25 de Septiembre, 2010

Network Manager y DBUS

Anoche publiqué la versión 0.2 de Oraje Applet, con varias novedades (como ¡un icono propio! :D) y algunas correcciones importantes.

Una de las novedades es el soporte de NetworkManager (o NM) vía DBUS, que así en frío no es explicar demasiado :P.

Parece que, por algún motivo (al menos en Fedora 12), las peticiones de resolución de nombres se cachean, lo cual es positivo porque aumenta mucho el rendimiento (si queremos conectar a www.google.com varias veces seguidas, podemos recordar la dirección IP entre llamadas y no tener que hacer la resolución cada vez), pero no es tan bueno si también se cachean los errores :(.

Entonces me encontré con que si el applet se cargaba antes de que tuviéramos conexión a Internet (que en un escritorio se encarga de gestionar NetworkManager, y no tiene porqué cargar antes que Oraje Applet), la resolución del nombre del servicio web de Yahoo! fallaba (claro, si no tenemos conexión, no hay DNS); pero después de que NM hiciera su trabajo y tuviéramos conexión... ¡seguía dando el mismo error!

Por muchas vueltas que le he dado, no he visto cómo cambiar este comportamiento. A nivel de librerías de Python no he encontrado nungún caché relacionado con el DNS (de urllib2 a httplib, y de ahí a socket), así que sospecho que puede ser a nivel de resolver del sistema.

En cualquier caso, el applet debería ser capaz de saber si hay conectividad o no en lugar de forzar un error, y ahí es donde entra DBUS, que es un sistema de mensajes para comunicarnos con otras aplicaciones, y nos va a servir para interactuar con NM, que gestiona la conectividad de red.

Por ejemplo, si queremos saber el estado actual, podemos usar el siguiente código en Python:

import dbus

# SystemBus para acceder a NM
bus = dbus.SystemBus()

proxy = bus.get_object('org.freedesktop.NetworkManager',
    '/org/freedesktop/NetworkManager')

state = proxy.Get('org.freedesktop.NetworkManager', 'state',
    dbus_interface='org.freedesktop.DBus.Properties')

# para traducir a cadenas los valores (0 a 4)
trans = [ 'desconocido', 'dormido, interfaces inactivas',
    'conectando...', 'conectado', 'desconectado']
    
print 'El estado de NM es %d (%s)' % (state, trans[state])

De esta manera podemos saber de una forma sencilla el estado de la conectividad cuando arranca nuestra aplicación, obteniendo acceso al bus del sistema (que es donde está NM), y ahí el proxy através del cual haremos las consultas.

Pero, ¿qué ocurre cuando hay un cambio de estado durante la ejecución de nuestra aplicación? Podemos indicarle a NM que llame a una de nuestras funciones cuando se dé un determinado evento.

Aquí es donde he tenido algún problema, porque hay que indicarle a DBUS cuál es el bucle de nuestra aplicación, para que pueda introducir la llamada a nuestra función en la cola de mensajes de ese bucle de forma que no tengamos que estar esperando explícitamente a la señal (uh, esto es una explicación algo libre por mi parte :P).

Como mi aplicación es GTK, tenía que utilizar la librería que permite integrar DBUS con GLib, y me ha costado un poco dar con ese detalle.

En el siguiente ejemplo creamos una ventana que nos va mostrando los cambios de estado de NM:

import pygtk
import gtk

import dbus
from dbus.mainloop.glib import DBusGMainLoop

def on_state_changed(state):
    trans = [ 'desconocido', 'dormido, interfaces inactivas',
	        'conectando...', 'conectado', 'desconectado']
			    
    # cambiamos el texto al nuevo estado
    label.set_text(trans[state])

# vamos a decirle a DBUS que use el bucle de GLib
DBusGMainLoop(set_as_default = True)

# NM esta en el SystemBus
bus = dbus.SystemBus()

proxy = bus.get_object('org.freedesktop.NetworkManager',
    '/org/freedesktop/NetworkManager')

# vamos a preparar una ventana
app = gtk.Window(gtk.WINDOW_TOPLEVEL)
app.set_title('NM and DBUS fun!')
app.connect('destroy', gtk.main_quit)

label = gtk.Label('waiting...')
box = gtk.HBox()
box.add(label)
app.add(box)

# conectamos StateChanged a nuestra func
proxy.connect_to_signal('StateChanged', on_state_changed)

app.show_all()
gtk.main()

Con lo que ya somos capaces de reaccionar como corresponda ante los cambios de estado de NM ;).

Creo NM es una de las aplicaciones más importantes del escritorio en Linux en los últimos 4 años, sobretodo si trabajamos con portátiles y conexiones inalámbricas o móviles, y gracias a DBUS podemos mejorar mucho la experiencia de usuario en nuestras propias aplicaciones.

Hay más información en la página con la especificación DBUS de NM 0.8, y podemos acceder a DBUS desde diferentes lenguajes de programación.

Anotación por Juan J. Martínez, clasificada en: python, gtk, gnome, dbus.

Hay 2 comentarios

Gravatar

Muy interesante, sobre todo el hecho de poder montar soporte para NM en poco más de 10 lineas, tiene pintas de ser muy sencillo en python.

por r0sk, en 2010-09-27 08:32:58

Gravatar

Sí, es realmente interesante.

Menos mal que DBUS es sencillo de usar, porque el sistema de componentes Bonobo va a ser eliminado de Gnome (se supone que ya no existe en el panel para la reciente 2.32), y hay que pasar a usar un interfaz con DBUS.

Osea, que o migro al nuevo interfaz, o poca utilidad va a tener el applet en poco tiempo :D

por Juanjo, en 2010-09-30 15:24:31

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: