viernes, marzo 11

Manteniendo más de una versión de python en ubuntu

Tratando de probar como corría un script con diferentes versiones de python, me encontré con update-alternatives, un sistema para mantener las diferentes "versiones" de distintos programas, que se puedan cambiar fácilmente, recordando donde se encuentran cada una de ellas.

Para agregar al sistema las diferentes versiones del programa (en mi caso, python):
update-alternatives --install /usr/bin/python python /usr/bin/python2.7 2
update-alternatives --install /usr/bin/python python /usr/bin/python2.6 1


Para resumir un poco el manual, le decimos que vamos a instalar una opción ("--install"), el segundo argumento es el enlace simbólico (symlink) que se desea administrar (el nombre genérico del programa), el tercero es al grupo que agregaremos esta opción, el cuarto es la ruta que nos provee el programa real y finalmente un número para asignar prioridad, a mayor número, mayor prioridad.

Luego, cuando queremos cambiar de una versión a otra, simplemente hacemos
update-alternatives --config python
y el sistema nos muestra la lista de opciones que tiene registradas y pregunta que versión queremos utilizar.

Fácil y bonito! ;)

viernes, enero 28

Python NTP client

Después de buscar un poco como funcionaba el protocolo NTP (Network Time Protocol), no encontré una implementación que fuera simple de entender, así que decidí hacerme una propia. Necesitaba entenderla para implementarla en un "Single board computer" (un microcontrolador con periféricos) Encontré una implementación básica en ActiveState, pero no me respondía a que correspondía cada campo del paquete.

Continuando con la búsqueda, encontré la implementación de OpenBSD de su cliente NTP y en el archivo ntp.h hay una buena descripción de que es cada campo. También ayudaron algunas páginas como ésta y ésta.

Finalmente, llegué a una implementación, simple pero a la vez. La dejo a continuación.


#!/usr/bin/python
# NTP client class for python, based on OpenBSD's ntp C code and ActiveState code recipe 117211
# v 0.1 (2011-01-28) Initial release
# Features:
# Conversion from/to NTP timestamp
# Ability to query NTP servers
# Calculation of delay and offset

import socket
import struct
import time
TIME1970 = 2208988800L # seconds from 1900

def NTPtimestamp2time(ts):
# print '%016X' % ts
# print '%016X' % (ts >>32)
ia = (ts >> 32) - TIME1970
fa = ts & 0x0000FFFF
return float(ia)+float(fa)/2**32
def time2NTPtimestamp(time):
ia = int(time)
fa = int((time-ia)*2**32)
return (ia<<32 | fa)+(TIME1970<<32)

class ntpclient:
def __init__(self):
self.sockt = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.server = None
self.port = 123
self.data = '\x1b' + 47* '\0'
#packet fields
self.leapIndicator = None
self.versionNumber = None
self.mode = None
self.stratum = None
self.pollInterval = None
self.precision = None
self.rootDelay = None
self.rootDispersion = None
self.referenceIdentifier = None
self.referenceTimestamp = None
self.originateTimestamp = None
self.recieveTimestamp = None
self.transmitTimestamp = None
self.destinationTimestamp = None
self.keyIdentifier = None
self.messageDigest = None
self.keyAlgorithmIdentifier = None
self.messageHash = None
def poll(self,server=None):
if server == None:
return
self.server = server
self.sockt.sendto(self.makeReqPackt(),(self.server,self.port))
data,address = self.sockt.recvfrom(1024)
self.destinationTimestamp = time2NTPtimestamp(time.time())
# print len(data)
if data and len(data) == 60:
smallfields, \
self.stratum, \
self.pollInterval, \
self.precision, \
self.rootDelay, \
self.rootDispersion, \
self.referenceIdentifier, \
self.referenceTimestamp, \
self.originateTimestamp, \
self.recieveTimestamp, \
self.transmitTimestamp, \
self.keyIdentifier, \
self.messageDigest, \
self.keyAlgorithmIdentifier, \
self.messageHash = \
struct.unpack('!BBBBIIIQQQQI8s',data)

self.leapIndicator = smallfields & 0b11000000 >> 6
self.versionNumber = smallfields & 0b00111000 >> 3
self.mode = smallfields & 0b00000111

if data and len(data) == 48:
smallfields, \
self.stratum, \
self.pollInterval, \
self.precision, \
self.rootDelay, \
self.rootDispersion, \
self.referenceIdentifier, \
self.referenceTimestamp, \
self.originateTimestamp, \
self.recieveTimestamp, \
self.transmitTimestamp = \
struct.unpack('!BBBBIIIQQQQ',data)

self.leapIndicator = smallfields & 0b11000000 >> 6
self.versionNumber = smallfields & 0b00111000 >> 3
self.mode = smallfields & 0b00000111
def toCtime(mytime):
return time.ctime((mytime>>32)-TIME1970)

def offset(self):
# t1 = self.referenceTimestamp
# t2 = self.originateTimestamp
# t3 = self.recieveTimestamp
# t4 = self.transmitTimestamp

t1 = NTPtimestamp2time(self.originateTimestamp)
t2 = NTPtimestamp2time(self.recieveTimestamp)
t3 = NTPtimestamp2time(self.transmitTimestamp)
t4 = NTPtimestamp2time(self.destinationTimestamp)

return ((t2-t1)+(t3-t4))/2

def delay(self):

t1 = NTPtimestamp2time(self.originateTimestamp)
t2 = NTPtimestamp2time(self.recieveTimestamp)
t3 = NTPtimestamp2time(self.transmitTimestamp)
t4 = NTPtimestamp2time(self.destinationTimestamp)

return ((t4-t1)-(t2-t3))

def makeReqPackt(self):
leapind = 0b00
version = 0b100 # NTP V4
mode = 0b011 # client mode

# a = time.time()
# ia = int(a)
# fa = int((a-ia)*2**32)
# tt = (ia<<32 | fa)+(TIME1970<<32)
tt = time2NTPtimestamp(time.time())
#print '%16X' % TIME1970
#print '%16X' % tt

return struct.pack('!BBBBIIIQQQQ',
leapind <<6 | version <<3 | mode,
0, #unspecified stratum
0, #poll interval
0, # local clock precision
0, # root delay
0, # root dispersion
0, # reference clock identifier
0, #reference timestamp
0, #originating timestamp
0, #recieve timestamp
tt #transmit timestamp
)

if __name__ == '__main__':
from sys import argv
c = ntpclient()
# print repr(c.makeReqPackt())
# c.poll('time.tigo.cl')
c.poll(argv[1])
# print c.versionNumber
# print c.stratum
# print '%016X'%(c.referenceTimestamp)
# print '%016X'%(c.originateTimestamp)
# print '%016X'%(c.recieveTimestamp)
# print '%016X'%(c.transmitTimestamp)
# print '%016X'%(c.destinationTimestamp)
# print c.delay()>>32, c.offset()>>32
print c.delay(), c.offset()


Espero que le sirva a alguien.