10/03/2012

Emulando la navegación en python con mechanize (parte 1)

En estos días me ha tocado programar una aplicación que permita interactuar de manera automática con otra, para ello utilicé la librería python-mechanize.

Está librería permite la interacción con aplicaciones web para automatizar dicha interacción o cuando se quiere realizar pruebas de estrés a una aplicación se usa multi-mechanize.

La página de mechanize la pueden revisar en el siguiente enlace. Hay una guía en Inglés.

En estos día he estado practicando con django (framework de desarrollo web en python) donde espero pronto poder publicar artículos al respecto.

La aplicación en Django es la de manejo de bookmarks, se tiene un inicio de sesión, una página de bienvenida al usuario registrado, luego se puede listar los bookmarks y cerrar la sesión (aplicación en desarrollo).

Pues todo esos pasos se van a automatizar con python-mechanize.

Para instalar mechanize se puede hacer a lo Debian:

#apt-get install python-mechanize

La siguiente figura muestra la aplicación en django sin mucho adorno.

Al darle clip en sesión o Inicio de Sesión aparece la solicitud de usuario y clave como lo muestra la figura:
Al darle botón derecho sobre la entrada de Usuario selecciona Inspeccionar elemento (esto en google chrome o chromium) y aparece una sección donde se muestra el código del formulario.

De esa forma se puede averiguar los nombres de los elementos del formulario para luego utilizarlo en el código python con mechanize.

Se inicia el interprete de comandos de python:
ecrespo@jewel:~$ python
Python 2.7.2+ (default, Dec  1 2011, 01:55:02)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Se importa mechanize y el manejo de cookies:
>>> import mechanize
>>> import cookielib


Se crea la instancia browser de mechanize:
>>>br = mechanize.Browser()

Se crea la instancia del cookie:
>>>cj = cookielib.LWPCookieJar()

Se asocia la instancia del cookie con el navegador:
>>>br.set_cookiejar(cj)



Se define que no se maneja robots:
>>>br.set_handle_robots(False)

Se define el tiempo de refrescamiento:
>>>br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)

Se define las cabeceras del navegador, en este caso se le está diciendo que el navegador es un firefox desde Linux Debian:
>>>br.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; es-VE; rv:1.9.0.1)Gecko/2008071615 Debian/6.0 Firefox/9')]

Se abre la aplicación web que se encuentra en localhost y puerto 8050:
>>>r = br.open('http://localhost:8050/')

Se muestra el resultado de la página:
>>>print r.read()

En la siguiente figura se muestra el resultado (el código html de la página) del comando anterior:

Ahora se revisarán los links que maneja la página para darle clip a sesión o Inicio de sesión para ingresar el usuario y clave:

Se crea un ciclo con los enlaces existentes en la página, luego se consulta el texto de cada enlace, si es sesion se abre el enlace pasado el url del mismo, luego finaliza el ciclo.
>>> for link in br.links():
...     if link.text == "sesion":
...         r= br.open("%s" %link.absolute_url)
...         break
...     else:
...         continue

Ya en este momento se encuentra en la página de ingreso de usuario y clave:

>>> print br.geturl()
http://localhost:8050/sesion/

Se puede mostrar el código html de la página de Inicio de sesión con el comando br.response().read():
>>>print br.response().read()

En la figura se muestra el resultado del comando:

Se puede mostrar los campos del formulario con el comando br.forms():
>>> for form in br.forms():
...     print form
...
<POST http://localhost:8050/sesion/ application/x-www-form-urlencoded
  <HiddenControl(csrfmiddlewaretoken=d3c9e6c2e06f5014647a1711ee3d4908) (readonly)>
  <TextControl(username=)>
  <PasswordControl(password=)>
  <SubmitControl(<None>=login) (readonly)>
  <HiddenControl(next=/) (readonly)>>

Se nota que se tiene dos campos de entrada de datos username y password y el botón de login para enviar los datos.

Como se tiene un sólo formulario se usa el parámetro nr=0, en el caso que existan varios formularios en dicha página sigue con los números consecutivos o se le pasa el nombre del formulario si existe name="nombre":
>>>br.select_form(nr=0)

Ahora se le pasa los datos de usuario y clave, luego se le da clip al botón de envío:
>>> br.form['username'] = 'usuario'
>>> br.form['password'] = 'clave'
>>> br.submit()
<response_seek_wrapper at 0xb6f0a06cL whose wrapped object = <closeable_response at 0xb6f0d4ccL whose fp = <socket._fileobject object at 0xb6eff5ac>>>


Al ejecutar response().read() se mostrará la página de bienvenida del usuario que ingreso a la aplicación:
>>>print br.response().read()

En la siguiente figura se muestra el resultado del comando:
Se muestra los enlaces disponibles y se selecciona el de publicar:
>>> for enlace in br.links():
...     if enlace.text == "Publicar":
...         s = br.open("%s" %enlace.absolute_url)
...         break
...     else:
...         continue

Se muestra el url de la página:
>>> print br.geturl()
http://localhost:8050/salvar/

Ahora se ingresará un URL de un sitio, por ejemplo www.python.org:

Primero se despliega el formulario:
>>> for form in br.forms():
...     print form
...
<POST http://localhost:8050/salvar/ application/x-www-form-urlencoded
  <HiddenControl(csrfmiddlewaretoken=d3c9e6c2e06f5014647a1711ee3d4908) (readonly)>
  <TextControl(url=)>
  <TextControl(title=)>
  <TextControl(tags=)>
  <SubmitControl(<None>=save) (readonly)>>

Se tiene que pasar el url, luego el título del url y etiquetas:
>>> br.select_form(nr=0)
>>> br.form['url'] = "www.python.org"
>>> br.form['title'] = "Python"
>>> br.form['tags'] = "Python Programacion"
>>> br.submit()
<response_seek_wrapper at 0xb6efee0cL whose wrapped object = <closeable_response at 0xb6f12aacL whose fp = <socket._fileobject object at 0xb6eff86c>>>


Se muestra el contenido de la página luego de ingresar los datos:
 >>> print br.response().read()

En la siguiente figura se muestra el resultado:

Para finalizar se presenta los enlaces disponibles para luego cerrar la sesión del usuario:
>>> for link in br.links():
...     if  link.text == "Cerrar Sesion":
...         r = br.open("%s" %link.absolute_url)
...         break
...     else:
...         continue

Se muestra el código html de la página resultante luego de dar clip en cerrar sesión y la figura donde aparece dicho código:
>>>print br.response().read()
En el siguiente artículo sobre mechanize se explicará el uso de formularios más complejos que sólo entrar datos.



04/03/2012

Calculo de direcciones IP con python

Recuerdo cuando vi la materia de redes de computadoras que a uno le tocaba calcular los segmentos de redes, hacer subnetting, supernetting, NAT, etc y  todos los cáculos se tenían que hacer con lapiz y papel  convirtiendo las direcciones IP en 4  bytes separados por punto, incluso en las clases del postgrado no se permitía el uso de calculadoras IP.

Para python existe una librería que permite realizar los cálculos para definir una red, un segmento de red, o reconocer cuando una IP es de un segmento de red dado.

La librería se llama ipcalc, se puede ver la descripción y un ejemplo en el siguiente enlace.

Para instalarlo desde la paqueteria de python se puede usar pip o easy_install:
#pip install ipcalc
Ó
#easy_install ipcalc

Para el caso de Debian se ejecuta apt-get:
#apt-get install python-ipcalc

Desde la consola se ejecuta python:
ecrespo@jewel:~$ python
Python 2.7.2+ (default, Dec  1 2011, 01:55:02)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

Se importa de ipcalc a IP y Network:
>>> from ipcalc import IP, Network

Se presenta el segmento de IPs de la red 192.168.10.0 con bits en 1 de la mascara 30 (mascara 255.255.255.252):
>>> for x in Network('192.168.10.0/30'): print str(x)
...
192.168.10.0
192.168.10.1
192.168.10.2
192.168.10.3


Como se nota se tiene 4 direcciones IP las cuales la primera (192.168.10.0) es la dirección de sub red y la última (192.168.10.3) es la dirección de broadcast de esa sub red.

Si se cambia los bits de la mascara a 29 aumenta el rango de IPs de dicha sub red de 4 a 8 IPs:
>>> for x in Network('192.168.10.0/29'): print str(x)
...
192.168.10.0
192.168.10.1
192.168.10.2
192.168.10.3
192.168.10.4
192.168.10.5
192.168.10.6
192.168.10.7

También se puede consultar si una IP dada se encuentra en un segmento de red o sub red, como ejemplo se consultará si la IP 192.168.10.8 se encuentra en la sub red 192.168.10.0/29 (obvio que no):
>>> '192.168.10.8' in Network('192.168.10.0/29')
False

Si se prueba con la IP 192.168.10.1 devuelve True la consulta:
>>> '192.168.10.1' in Network('192.168.10.0/29')
True


El último ejemplo es el caso de una red 192.68.10.0/22 ó 192.168.10.0/255.255.252.0, se quiere averiguar la IP inicial y la IP final de dicho segmento de red:
>>> '192.168.9.1' in Network('192.168.10.0/22')
False
>>> '192.168.9.255' in Network('192.168.10.0/22')
False
>>> '192.168.10.0' in Network('192.168.10.0/22')
True
>>> '192.168.10.1' in Network('192.168.10.0/22')
True
>>> '192.168.11.1' in Network('192.168.10.0/22')
True
>>> '192.168.12.1' in Network('192.168.10.0/22')
True
>>> '192.168.13.1' in Network('192.168.10.0/22')
True
>>> '192.168.14.1' in Network('192.168.10.0/22')
False
>>> '192.168.13.255' in Network('192.168.10.0/22')
True

Con este resultado se tiene que la IP inicial es la 192.168.10.0 hasta la IP 192.168.13.255.

Se puede usar también para calcular IPs de IPv6.

03/03/2012

Crear llave gpg desde Python

Existen varias herramientas para crear, manipular llaves gpg.

Quienes no conozcan de GPG pueden leer el siguiente tutorial.

El paquete para python que permite manejar  las llaves gpg es python-pyme.
#apt-get install python-pyme python-pyme-doc

La documentación de la librería pyme la encuentran en el siguiente enlace.

En la documentación se tiene una lista de ejemplos, se copia el archivo de genkey.py al home del usuario:
cp /usr/share/doc/python-pyme-doc/examples/genkey.py ~/

Se edita el archivo genkey.py, se modifica el tipo de llave a RSA, se define la longitud de la llave (1024,2048 o 4096), longitud de la subllave (el mismo valor de la lñongitud de la llave), el nombre real, comentario del nombre, correo, frase de la llave, fecha de expiración de la llave.

#!/usr/bin/env python
# $Id: genkey.py,v 1.6 2008/03/08 18:21:08 belyi Exp $
# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
#
#    This program is free software; you can redistribute it and/or #modify
#    it under the terms of the GNU General Public License as #published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty #of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  #See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public #License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  #02111-1307  #USA
 

#Importar pyme
from pyme import core, callbacks

# Initialize our context.
#Inicializa el contexto.
core.check_version(None)
c = core.Context()
#Se define algunas configuraciones
c.set_armor(1)
c.set_progress_cb(callbacks.progress_stdout, None)

# This example from the GPGME manual
#Este es un ejemplo desde el manual gpgme.

parms = """<GnupgKeyParms format="internal">
Key-Type: RSA
Key-Length: 2048
Subkey-Type: ELG-E
Subkey-Length: 2048
Name-Real: Ernesto Nadir Crespo Avila
Name-Comment: seraph1
Name-Email: ecrespo@gmail.com
Passphrase: frasedelallave
Expire-Date: 2020-08-15
</GnupgKeyParms>
"""
#Se genera la llave pasandole los parámetros.
c.op_genkey(parms, None, None)
#Se imprime en patanalla el resultado de la generación.
print c.op_genkey_result().fpr


Para crear la llave se ejecuta el archivo genkey.py, :
$python genkey.py
......
PROGRESS UPDATE: what = primegen, type = 46, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 46, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 43, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 43, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 43, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 43, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 43, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 94, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 94, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 94, current = 0, total = 0
PROGRESS UPDATE: what = primegen, type = 94, current = 0, total = 0
FFF598686F3ADE35C52BF65E4478C8341643F0EB





Al desplegar la lista de llaves se tiene lo siguiente:
$gpg --list-keys
...
pub   2048R/1643F0EB 2012-03-04 [caduca: 2020-08-15]
uid                  Ernesto Nadir Crespo Avila (seraph1) <ecrespo@gmail.com>
sub   2048g/9F8E9C20 2012-03-04 [caduca: 2020-08-15]

Los últimos 8 números hexagecimales es el identificador de la llave gpg. Luego se exporta a un servidor de llaves y se puede compartir la llave pública para firmar o cifrar correos.


26/02/2012

Geolocalización de direcciones IP y dominios de Internet con GeoIP en python

A nivel mundial se crearon varios grupos de direcciones IP de IPv4 (Clases A,B o C) la cual permiten definir redes y rango de equipos para dichas redes.

Claro actualmente se está migrando a IPv6 ya que las direcciones IPv4 se agotaron hace un par de años aproximadamente (aunque existan soluciones como NAT, Subnetting o Supernetting que permiten utilizar más eficientemente las IPs).

Con GeoIP para python se puede averiguar de cual País proviene una IP, también se puede conocer un dominio o sitio en específico donde se encuentra alojado, conocer el rango de IPs de una clase de IPv4.

Para instalar python-geoip a lo Debian (Debian, Canaima, Ubuntu, LinuxMint), se convierte el usuario en superusuario y ejecuta:
#apt-get install python-geoip

Se ejecuta el interprete de comandos de python:
ecrespo@jewel:~$ python
Python 2.7.2+ (default, Dec  1 2011, 01:55:02)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Se importa el módulo geoip:
>>> import GeoIP

Se crea el objeto de la Instancia de GeoIP, asociandole la información de GeoIP de la memoria Cache:
>>> gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)

Se busca en cual País se encuentra alojado yahoo.com:
>>> print gi.country_code_by_name("yahoo.com")
US
>>> print gi.country_name_by_name("yahoo.com")
United States


En el primer caso se devuelve el código del País, en el segundo se devuelve el nombre del País.

Ahora se probará con yahoo.es:
>>> print gi.country_code_by_name("www.yahoo.es")
IE
>>> print gi.country_name_by_name("www.yahoo.es")
Ireland


Se nota que yahoo.com está alojado en Estados Unidos y yahoo.es se encuentra alojado en Irlanda.

Ahora se buscará en cual País se encuentra alojado www.cantv.net (es obvio):
>>> print gi.country_code_by_name("www.cantv.net")
VE
>>> print gi.country_name_by_name("www.cantv.net")
Venezuela


Aunque es posible que dominios de Venezuela se encuentren alojados en otros Países, un ejemplo sería www.crespo.org.ve:
>>> print gi.country_name_by_name("www.crespo.org.ve")
Sweden


Para terminar se probará averiguar el País según la IP que se le pase:
>>> print gi.country_name_by_addr("24.24.24.24")
United States
>>> print gi.country_name_by_addr("150.186.32.0")
Venezuela

La primera IP se encuentra en Estados Unidos y la segunda en Venezuela.

Para averiguar el rango de IPs de la IP de Venezuela se ejecuta geoip solicitando que devuelva el rango de IPs:
>>> print gi.range_by_ip("150.186.32.0")
('150.185.0.0', '150.189.255.255'

Está Ip por el rango de direcciones que maneja es una clase B.

Con esta herramienta se puede usar para aplicaciones Web con Django por ejemplo y dicha aplicaciones puede mostrar información del País donde se conectan los usuarios.



20/02/2012

Cifrar el tráfico de DNS en Linux con DNSCrypt y OpenDNS

DNSCrypt permite cifrar las consultas de DNS entre el Computador y los servidores de DNS de OpenDNS, de esa forma se puede proteger al equipo de los ataques de Hombre en el Medio.

El artículo donde se basa este tutorial se encuentra en el siguiente enlace.
Para instalar dnscrypt para Debian se baja el paquete desde el repositorio de Github.

 Se ejecuta dpkg:

#dpkg -i dnscrypt-proxy_0.9_i386.deb

Se inicia el proxy dns como demonio:
#dnscrypt-proxy --daemonize

Se edita las conexiones inalámbricas en este caso en la red que se conecta el equipo es inalámbrica:


Se selecciona la red inalámbrica y se edita su configuración:

Se selecciona la pestaña de Ajuste de IPv4, en el método se selecciona Sólo direcciones automáticas (DHCP) y se coloca la IP 127.0.0.1 en Servidor de DNS:

Para verificar que se esté usando los servidores de DNS de OpenDNS se abre el navegador a la URL http://www.opendns.com/welcome :


Ya se tiene cifrado la comunicación a los servidores de OpenDNS.

19/02/2012

Conectarse al repositorio de bitbucket desde python

Para tener un proyecto con control de versiones mercurial sólo veo 2 opciones usar Google Code o Bitbucket.  Para el caso de Google Code se puede acceder vía el api para usar mercurial con python como se explico en artículo anterior.

Para Bitbucket se tiene un API para acceder a la información del repositorio que en python se llama python-bitbucket.
Para instalarlo se sigue los siguientes pasos:
A lo Debian/Canaima/Ubuntu/LinuxMint:
#apt-get install python-bitbucket

Con la herramienta easy_install:
#easy_install bitbucket

Con la herramienta pip:
#pip install bitbucket

Usar el API de bitbucket:
Desde la consola de python, se importa el módulo bitbucket
$python

>>> import bitbucket

Se crea la instancia de bitbucket.

>>> bb = bitbucket.BitBucket()

Se establece conexión a la cuenta de bitbucket :
>>> bb = bitbucket.BitBucket('ecrespo', 'clave')

Se verifica que se autentico en el servicio:
>>> bb
<BitBucket API (auth: ecrespo)>

Se asocia la cuenta:
>>> ecrespo = bb.user('ecrespo')
>>> ecrespo
<User: ecrespo>

Se lista los repositorios del usuario (se devuelve una lista con los repositorios del usuario, cada elemento de la lista es un diccionario con la información del repositorio):
>>> ecrespo.repositories()
[{u'scm': u'hg', u'has_wiki': True, u'last_updated': u'2011-09-12 03:56:37', u'created_on': u'2009-04-09 22:42:58', u'owner': u'ecrespo', u'logo': None, u'email_mailinglist': u'', u'is_mq': False, u'size': 747499, u'read_only': False, u'fork_of': None, u'mq_of': None, u'state': u'available', u'utc_created_on': u'2009-04-09 20:42:58+00:00', u'website': u'', u'description': u'Script que automatiza el proceso de conversi\xf3n de un sistema Debian/Ubuntu para que sea accesible para las personas con discapacidad visual', u'has_issues': True, u'is_fork': False, u'slug': u'automatizar-accesibilidad', u'is_private': False, u'name': u'automatizar-accesibilidad', u'language': u'', u'utc_last_updated': u'2011-09-12 01:56:37+00:00', u'email_writers': True, u'main_branch': u'default', u'no_public_forks': False, u'resource_uri': u'/api/1.0/repositories/ecrespo/automatizar-accesibilidad'}, {u'scm': u'hg', u'has_wiki': True, u'last_updated': u'2011-09-08 13:09:06', u'created_on': u'2011-08-04 05:02:06', u'owner': u'ecrespo', u'logo': None, u'email_mailinglist': u'', u'is_mq': False, u'size': 545, u'read_only': False, u'fork_of': None, u'mq_of': None, u'state': u'available', u'utc_created_on': u'2011-08-04 03:02:06+00:00', u'website': u'', u'description': u'Lista de paquetes para la accesibilidad con una descripci\xf3n y pruebas de los mismos', u'has_issues': False, u'is_fork': False, u'slug': u'accesibilidad', u'is_private': True, u'name': u'accesibilidad', u'language': u'', u'utc_last_updated': u'2011-09-08 11:09:06+00:00', u'email_writers': True, u'main_branch': None, u'no_public_forks': False, u'resource_uri': u'/api/1.0/repositories/ecrespo/accesibilidad'}]

Se captura la descripción del repositorio y la ruta del mismo:
>>> ecrespo.repositories()[0]['description']
u'Script que automatiza el proceso de conversi\xf3n de un sistema Debian/Ubuntu para que sea accesible para las personas con discapacidad visual'

>>> ecrespo.repositories()[0]['resource_uri']
u'/api/1.0/repositories/ecrespo/automatizar-accesibilidad'

Se asocia a uno de los repositorios, en este caso el de accesibilidad.
>>> pyaccesibilidad=ecrespo.repository('accesibilidad')

>>> pyaccesibilidad
<Repository: ecrespo's accesibilidad>

Se lista los tags o branches (para el caso del repositorio accesibilidad devuelve diccionarios vacíos):
>>> pyaccesibilidad.tags()
{}

>>> pyaccesibilidad.branches()
{}






07/02/2012

Obtener información de las interfaces de red con python(netifaces)

Existe un módulo en python que permite obtener la información de las interfaces de red.

Dicho módulo se llama netifaces, con él se puede obtener la lista de interfaces de su equipo, obtener la información de Enlace de Red de cada interfaz, su dirección IPv4 y su Dirección IPv6.

Para instalar el módulo se ejecuta apt-get para el caso de Debian/Canaima/Ubuntu.
#apt-get install python-netifaces

La idea es crear un script que capture la lista de interfaces del equipo y muestre la información de cada una de dichas interfaces.

El código del programa se muestra a continuación:

#!/usr/bin/env python

#Se importa el modulo netifaces
import netifaces

#Se captura la lista de interfaces en el equipo
interfaces = netifaces.interfaces()
#Se muestra las interfaces
print "interfaces: ", interfaces

#Se recorre la lista de interfaces
for interface in interfaces:
#Se captura la informacion de cada interfaz
datos = netifaces.ifaddresses(interface)
print "--------------------------"
#Se muestra el nombre de la interface
print "Interface: %s" %interface
#Se captura la lista de parametros que tiene la interface
variables = datos.keys()
#Se muestra la direccion de la capa de enlace de red de la interface
print "Capa de enlace de red: ", datos[netifaces.AF_LINK][0]['addr']
#Si esta presente la informacion de IPV4 se muestra
if netifaces.AF_INET in variables:
print "IPv4: IP: %s, Mascara: %s" %(datos[netifaces.AF_INET][0]['addr'], datos[netifaces.AF_INET][0]['netmask'])
#Si esta presente la informacion de IPv6 se muestra
if netifaces.AF_INET in variables:
print "IPv6: IP: %s, Mascara: %s " %(datos[netifaces.AF_INET6][0]['addr'], datos[netifaces.AF_INET6][0]['netmask'])


El resultado del programa se muestra a continuación:

interfaces: ['lo', 'eth0', 'wlan0']
--------------------------
Interface: lo
Capa de enlace de red: 00:00:00:00:00:00
IPv4: IP: 127.0.0.1, Mascara: 255.0.0.0
IPv6: IP: ::1, Mascara: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
--------------------------
Interface: eth0
Capa de enlace de red: 71:6a:b7:77:46:5f
--------------------------
Interface: wlan0
Capa de enlace de red: 00:26:85:74:fe:89
IPv4: IP: 192.168.10.108, Mascara: 255.255.255.0
IPv6: IP: fe80::236:92ff:fa54:fe89%wlan0, Mascara: ffff:ffff:ffff:ffff::


Para más información sobre el módulo pueden visitar la información en la página de Python o el sitio de la aplicación.

06/02/2012

WebService con Soappy

Hace ya mucho tiempo escribí un ejemplo de como usar xml-rpc desde python como webservice.
En este caso se explicará el uso de otro protocolo que deriva de xml-rpc que se llama SOAP (Simple Object Access Protocol).

SOAP es un protocolo estándar que define cómo dos objetos en diferentes procesos pueden comunicarse por medio del intercambio de datos XML. Pueden conseguir más información en la página de wikipedia.


En python existe un módulo para trabajar con SOAP que se llama soappy. Lo primero que se hará es instalarlo.

#apt-get install python-soappy

La idea es crear 4 funciones, de suma, resta, multiplicación y división, registrarlas en el servicio SOAP, levantar el servidor y realizar las llamadas desde el cliente.

El programa servidor de SOAP es el siguiente:


#!/usr/bin/env python

import SOAPpy

#Funciones que devuelven la suma, resta, multiplicacion y division de 2 numeros

def suma(a,b):
    return a+b

def resta(a,b):
    return a-b

def multiplicacion(a,b):
    return a*b

def division(a,b):
    return a/b

#Se crea la instancia del servicio SOAP en el equipo por el puerto 8080.
server = SOAPpy.SOAPServer(("localhost", 8080))

#Registro de las funciones suma,resta, multiplicacion y division
#en el servicio SOAP.
server.registerFunction(suma)
server.registerFunction(resta)
server.registerFunction(multiplicacion)
server.registerFunction(division)

#Levantar el servicio SOAP.
server.serve_forever()

El programa cliente de SOAP es el siguiente:
#!/usr/bin/env python

#Se importa el modulo SOAPpy
import SOAPpy

#Se crea la instancia del proxy SOAP
#a el servidor SOAP
server = SOAPpy.SOAPProxy("http://localhost:8080/")

#Se llama las funciones registradas en el servidor SOAP
print "El resultado de la suma es: ", server.suma(5,10)
print "El resultado de la resta es: ",server.resta(20,5)
print "El resultado de la multiplicacion es: ", server.multiplicacion(10,5)
print "El resultado de la division es: ", server.division(10,3)

Se le da permisos de ejecución a ambos archivos, se ejecuta primero el servidor y luego el cliente que devuelve el siguiente resultado:

Se ejecuta cada programa en una consola distinta.
ecrespo@jewel:~/bin$ ./soapservidor.py

ecrespo@jewel:~/bin$ ./soapcliente.py
El resultado de la suma es:  15
El resultado de la resta es:  15
El resultado de la multiplicacion es:  50
El resultado de la division es:  3


24/01/2012

API de Mercurial para Python

Mercurial como ya se ha explicado es un sistema de control de versiones distribuído desarrollado en python, otros sistemas de control de versiones tienen API o módulos para python, pero para el caso de mercurial no había visto una API para usarlo desde python.

Revisando los artículos que publican en los sparks de python  en Google+ me encuentro con que existe un API que se llama hgapi.

El API soporta lo siguiente:

hg init
hg id
hg add <file>
hg commit [files] [-u name] [--close-branch]
hg update <rev>
hg heads
hg log
hg remove
hg status
hg merge (fails on conflict)
hg revert


El procedimiento para instalarlo es el siguiente:
 # pip install hgapi
 Downloading/unpacking hgapi
  Downloading hgapi-1.1.0.tar.gz
  Running setup.py egg_info for package hgapi
  
Installing collected packages: hgapi
  Running setup.py install for hgapi
  
Successfully installed hgapi
Cleaning up...

Se crea el directorio pruebas:

$mkdir pruebas

Dentro del directorio pruebas se crea el archivo hora.txt:
cd pruebas
touch hora.txt

Ya está todo listo para probar el API.

1. Se importa el módulo hgapi:
import hgapi
2. Fuera del directorio pruebas se crea la instancia del repositorio:
repo = hgapi.Repo("pruebas")
3. Se inicializa el repositorio:
repo.hg_init()
4. Se agrega el archivo hora.txt:
repo.hg_add("hora.txt")
5. Se realiza el commit colocando la descripción y el usuario quien realiza el commit:
repo.hg_commit("Agregando archivo hora.txt",user="ernesto")
6. Se captura la descripción:
str(repo['tip'].desc)
'Agregando archivo hora.txt'

Esto es lo básico que explica el sitio de hgapi. Lo interesante es que ya se puede automatizar procesos de mercurial desde python.

La documentación de hgapi la pueden revisar aquí.

23/01/2012

Crear metapaquete con equivs (incorporando archivos postinst y prerm)

Para continuar con los artículos de metaempaquetado para Debian/Canaima, el último artículo sobre la herramienta equivs.

Lo que faltaba por hacer era crear un metapaquete de configuración que copie archivos de configuración del escritorio cuando se instala y que se guarde dichas configuraciones en el directorio /etc/skel para cuando se cree un nuevo usuario en el sistema.


En el tutorial de Ubuntu Forums explican como crear el metapaquete con archivos postinst y prerm, el problema es que los archivos de ejemplo no se encuentran en los enlaces que hacen referencia.

La idea es agregar el applet del programa radiotray en el escritorio de gnome para todos los usuarios. Como normalmente ya se tiene usuarios en el equipo se tendrá que copiar los archivos de configuración de radiotray al directorio home de cada usuario; para los usuarios que se creen luego de la instalación del metapaquete el sistema copiará los archivos de configuración que se encuentran en el directorio /etc/skel/ .

Lo primero que se necesita hacer es crear el archivo control para metaempaquetado de equivs con el comando equivs-control:

$equivs-control canaima-radiotray-conf

Esté archivo contendrá:

Section: misc
Priority: optional
Homepage: http://canaima.softwarelibre.gob.ve
Standards-Version: 3.9.1

Package: canaima-radiotray-conf
Version: 0.1
Maintainer: Ernesto Nadir Crespo Avila <ecrespo@cenditel.gob.ve>
Pre-Depends: radiotray
Depends: ${misc:Depends}
Architecture: all
Copyright: copyright
Changelog: changelog
Files: ./radiotray/radiotray.desktop /etc/skel/.config/autostart/radiotray.desktop
       ./radiotray/bookmarks.xml   /etc/skel/.local/share/radiotray/bookmarks.xml
File: postinst
 #!/bin/sh -e
 # preinst for main-package-config. Divert some configuration file of main-package.
 .
 set -e
 .
 PKG=canaima-radiotray-conf
 .
     if [ "$1" = configure ] ; then
 .
    for usuario in  /home/*? ; do
 .
        usuario_min=$(basename $usuario)
 .
        case $(grep "${usuario_min}:.*:.*:.*:.*:.*:::" /etc/shadow ) in
 .
                '')
                #no se hace nada
                ;;
 .
                *)
                usuario=${usuario_min}
 .
                if [ -f  /home/${usuario}/.config/autostart/radiotray.desktop ];
 .
                then
 .
                        cp -r /etc/skel/.config/autostart/radiotray.desktop /home/${usuario}/.config/autostart/radiotray.desktop
 .
                        chown ${usuario}.${usuario} /home/${usuario}/.config/autostart/radiotray.desktop

                else
 .
                        mkdir -p  /home/${usuario}/.config; chown -R ${usuario}.${usuario} /home/${usuario}/.config
 .
                        mkdir -p /home/${usuario}/.config/autostart; chown -R ${usuario}.${usuario} /home/${usuario}/.config/autostart
 .
                        cp -r /etc/skel/.config/autostart/radiotray.desktop /home/${usuario}/.config/autostart/radiotray.desktop
 .
                        chown ${usuario}.${usuario} /home/${usuario}/.config/autostart/radiotray.desktop
 .
                fi
 .
                if [ -f /home/${usuario}/.local/share/radiotray/bookmarks.xml ];
 .
                then
 .
                        cp -r /etc/skel/.local/share/radiotray/bookmarks.xml /home/${usuario}/.local/share/radiotray/bookmarks.xml
 .
                        chown ${usuario}.${usuario} /home/${usuario}/.local/share/radiotray/bookmarks.xml
 .
                else
 .
                        mkdir -p /home/${usuario}/.local; chown -R ${usuario}.${usuario} /home/${usuario}/.local
 .
                        mkdir -p /home/${usuario}/.local/share; chown -R ${usuario}.${usuario} /home/${usuario}/.local/share
 .
                        mkdir -p /home/${usuario}/.local/share/radiotray; chown -R ${usuario}.${usuario} /home/${usuario}/.local/share/radiotray
 .
                        cp -r /etc/skel/.local/share/radiotray/bookmarks.xml /home/${usuario}/.local/share/radiotray/bookmarks.xml
 .
                        chown ${usuario}.${usuario} /home/${usuario}/.local/share/radiotray/bookmarks.xml
 .
                fi
 .
        esac
 .
    done
 .
     fi
 .
    exit 0
File: prerm
 #!/bin/sh -e
 .
 set -e
 .
 PKG=canaima-radiotray-conf
 .
    if [ "$1" = configure ] ; then
 .
        for usuario in  /home/*? ; do
 .
            usuario_min=$(basename $usuario)
 .
             case $(grep "${usuario_min}:.*:.*:.*:.*:.*:::" /etc/shadow ) in
 .
                '')
                    #no se hace nada
                    ;;
 .
                    *)
                    usuario=${usuario_min}
 .
                    if [ -f  /home/${usuario}/.config/autostart/radiotray.desktop ];
 .
                        then
 .
                            rm -f /home/${usuario}/.config/autostart/radiotray.desktop
 .
                    fi
 .
                    if [ -f /home/${usuario}/.local/share/radiotray/bookmarks.xml ];
 .
                        then
 .
                            rm -f /home/${usuario}/.local/share/radiotray/bookmarks.xml
 .
                fi
 .
            esac
 .
        done
 .
    fi
 .
     exit 0
Description: Radiotray bookmarks to Venezuelan radio streaming
 Radiotray config to autostart in gnome and radio stream bookmark


Se define la sección y prioridad del programa, se define el sitio del programa, la versión del policy a utilizar; el nombre del programa, su versión, el mantenedor del programa, la predependencia en este caso es el programa radiotray.

La dependencia es ${misc:Depends}, está variable se define ya que se está usando debhelper para empaquetar. La arquitectura es all, se definen los archivos copyright y changelog.

Luego con la opción Files se define los archivos que se van a copiar en el directorio /etc/skel/ .

Con la opción File se define cada script que se desea agregar al paquete, en este caso los archivos postinst y prerm.

Lo que hace postinst es averiguar las cuentas de los usuarios del equipo y copiar los archivos de configuración en los directorios que usa gnome para manejar las configuraciones. En el caso de prerm lo que hace es borrar dichos archivos de cada usuario del equipo.

Por último se da una descripción corta y luego larga del programa.

El archivo changelog tendrá los cambios realizados en el metapaquete:

canaima-radiotray-conf (1:0.1) unstable; urgency=low

  * First release.

 -- Ernesto Nadir Crespo Avila <ecrespo@cenditel.gob.ve>  Fri, 23 Dec 2011 10:57:05 -0430

El archivo copyright contendrá lo siguiente:

Authors:

    Copyright (C) 2011 Ernesto Nadir Crespo Avila <ecrespo@cenditel.gob.ve>

License:

   This package is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 dated June, 1991.

   This package is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-2'.

Se crea el directorio radiotray que contendrá los archivos de configuración:
$mkdir radiotray

Se crean los archivos bookmarks.xml y radiotray.desktop. El primero define las radios por Internet que se quieren tener en los favoritos y el segundo archivo define que el programa radiotray se coloque como un applet en el escritorio Gnome.

Archivo bookmarks.xml. El contenido de este archivo se muestra en la siguiente figura:


Archivo radiotray.desktop:
[Desktop Entry]
Type=Application
Exec=radiotray
Hidden=false
X-GNOME-Autostart-enabled=true
Name[es_VE]=radiotray
Name=radiotray
Comment[es_VE]=Cliente de radio por internet
Comment=Cliente de radio por internet


Equivs toma la definición de archivos (File) y crea los archivos para el proceso de instalación. En la siguiente figura se muestra la estructura de archivos del directorio DEBIAN dentro del paquete deb:



Al subir el metapaquete a un repositorio e instalar se puede ver en la siguiente figura las radios que aparecen en favoritos de radiotray: