NFC en la Raspberry Pi

Hace tiempo que no escribo en el blog debido a insertar-excusa-que-no-haya-usado-antes-aquí, pero me apetecía compartir el resultado de pasar unas pocas de tardes buscando una buena alternativa para utilizar NFC en la Raspberry Pi.

Las primeras semanas me centré en hacer funcionar el lector NFC ACR122u de ACS, lector que ya había utilizado en la empresa en la que trabajo para hacer una serie de investigaciones con Linux y que arrojaron un resultado aceptable. Pobre de mi, pensaba que con la Raspberry Pi y la misma librería automágica nfcpy que había utilizado en el pasado, encontrar una alternativa estable iba a ser pan comido. No fue así. La Raspberry Pi es un Linux, eso es cierto, concretamente una Debian. Pero la arquitectura no es i386, es ARM (más propia de pequeños dispositivos como smartphones y tablets). A pesar de funcionar más o menos regularmente, el lector ACR122u fallaba de manera aleatoria y sin explicación. Tal y como me comenta un experto en la materia como es Stephen Tiedemann (creador de nfcpy) en este hilo, además de que el lector es de lo peor para estas cosas, el subsistema USB de la Raspberry se muestra muy inestable en cuanto a periféricos más concretos como este (ratones, teclados y demás material básico funciona sin problemas).

El lector NFC ACR122U

El lector NFC ACR122U

Con el primer golpe en la frente, decido no hacerle caso a Stephen que me recomienda probar con una placa PN532 conectable vía serie en plan Arduino, usando electrónica, placas y demás. En su lugar, me tomé la libertad de realizar una pequeña inversión en la placa EXPLORE-NFC NXP PN512 que no tiene soporte por parte de nfcpy pero tiene programas de ejemplo ya escritos en C puro. Esta placa se engancha al puerto de expansión GPIO como si de un conector USB se tratase.

La placa explore-nfc

La placa explore-nfc

Hay programas escritos para los 3 modos de operación de NFC: tag, card emulation y P2P. Un handicap es que el modo P2P sólo tiene escrito un programa para la operación PUT (recibir de Raspberry) pero no para GET (que la Raspberry obtenga del móvil). He escrito un programa Python/GTK como interfaz gráfica hacia este programa P2P ya existente, para probar la operación PUT. El resultado lo podéis ver en el vídeo que adjunto, donde también pruebo la demo del modo Card Emulation, que recoge el valor que asignemos al tag 14443 y lo escribe en el muro de Facebook de mi cuenta dummy.

Enlaces de interés

* Código de ejemplo EXPLORE-NFC:
http://www.element14.com/community/docs/DOC-65447/l/explore-nfc-software-and-project?ICID=knode-devkitnfc-quick

* Comprar EXPLORE-NFC (marcar Business en la ventana modal, aunque seas un particular):
http://es.farnell.com/jsp/displayProduct.jsp?sku=2366201&CMP=KNC-GES-FES-GEN-SKU-MDC

* Código de demo gráfica personal Python/GTK:
https://bitbucket.org/jialvarez/raspberry-mpos

A 1 persona le gusta esta entrada

Realidad aumentada en Android: reconocimiento de imágenes y geolocalización usando Google Maps

Presentación utilizada para el evento Google DevFest Sur el 25/10/2013. En la charla revisamos el estado del arte en cuanto a bibliotecas de realidad aumentada para Android. Vimos cómo funcionan de manera general y estudiamos un ejemplo práctico de reconocimiento de imágenes y geolocalización con la biblioteca Wikitude.

Sé el primero en valorar positivamente

Comparativa de sistemas de persistencia en Python

En uno de los proyectos Python en los que he estado trabajando en Emergya, detectamos la necesidad de cambiar el sistema de persistencia bsddb por algún otro que fuera más rápido. Alejandro «CuasiInfinito» Leiva me pidió diseñar un comparador de sistemas de persistencia con el estado del arte actual. Este comparador es el que ocupa esta nueva entrada en el blog.

¿Qué sistemas de persistencia se evalúan?

Los sistemas de persistencia a evaluar en rendimiento son los siguientes:

  • bsddb
  • durus
  • ZODB
  • Redis
  • PyTables

¿Qué pruebas se utilizan para medir el rendimiento?

La primera prueba para cada sistema de persistencia consiste en tomar un fichero CSV línea a línea y utilizar la operación de escritura persistente proporcionada por el sistema que se está evaluando. Es importante el hecho de que esta escritura sea persistente, uno de los errores que cometí de inicio fue pensar que unos sistemas eran más rápidos que otros sin apreciar que estaban realizando la escritura en memoria directa. Para asegurar este punto, cerraremos el manejador de datos de cada sistema una vez hayamos realizado la escritura persistente del fichero CSV completo.

A continuación, creamos un nuevo manejador y leemos los datos almacenados por el sistema de persistencia. Estos datos deben coincidir con los del CSV línea a línea.

Ambas pruebas serán medidas con una marca de tiempo al inicio y al final del proceso. Se realiza una impresión por pantalla del tiempo consumido en cada prueba por parte de cada sistema de persistencia.

¿Dónde descargo el código?

El código puede descargarse de mi cuenta en BitBucket: https://bitbucket.org/jialvarez/persystems/

¿Cómo está estructurado el código?

El código consta de un programa principal que carga el backend escogido y realiza las dos pruebas descritas en un punto anterior. Las operaciones descritas en cada backend son las siguientes:

  • __init__
  • Inicialización del backend dependiendo de la prueba a ejecutar (lectura o escritura).

  • __setitem__
  • Asignación de un elemento de manera persistente.

  • __getitem__
  • Recuperación de un elemento.

  • __len__
  • Número de elementos almacenados por el sistema persistente.

  • first
  • Devuelve el primer elemento de los almacenados de forma persistente.

  • iteritems
  • Itera a través de los elementos almacenados de forma persistente devolviendo un elemento en cada iteración. No implementado aún en todos los backends.

  • close
  • Cierra y/o destruye el manejador del sistema persistente.

  • getTestDBItems
  • Devuelve una lista con todos los elementos almacenados de forma persistente.

¿Cómo pruebo el comparador?

El código se estructura de la siguiente manera:

neonigma@hyperion:~/things/persystems$ tree
.
??? backends
?   ??? __init__.py
?   ??? pybsddb.py
?   ??? pydurus.py
?   ??? pyredis.py
?   ??? pytables.py
?   ??? pyzodb.py
??? __init__.py
??? test.py

En el directorio backends se encuentran los módulos que implementan los diferentes sistemas de persistencia. En el directorio principal, encontramos el programa test.py. Hay que modificar la variable FILENAME para que apunte a un fichero CSV válido:

FILENAME = '/tmp/fichero1.csv'

Al final del proograma, encontramos las llamadas a la ejecución de pruebas sobre cada backend:

tester = Test(getCSVReader(), "pytables")
tester = Test(getCSVReader(), "pybsddb")
tester = Test(getCSVReader(), "pyzodb")
tester = Test(getCSVReader(), "pydurus")
tester = Test(getCSVReader(), "pyredis")

Basta con comentar las que no queramos incluir en la ejecución de las pruebas. Para ejecutar el programa, escribimos:

neonigma@hyperion:~/things/persystems$ python test.py
pytables writing time: 0.0846199989319
pytables reading time: 0.0147368907928

pybsddb writing time: 0.278237104416
pybsddb reading time: 0.0832049846649

pyzodb writing time: 0.164448976517
pyzodb reading time: 0.0207080841064

pydurus writing time: 0.253404855728
pydurus reading time: 0.0357837677002

pyredis writing time: 1.39871191978
pyredis reading time: 0.624420881271

Closing remaining open files: /tmp/testpytables.db... done

En este caso los tiempos son pequeños porque estoy utilizando un fichero CSV pequeño, de 10K líneas, pero ya puede apreciarse que el ganador de la comparativa es PyTables.

A 1 persona le gusta esta entrada

Conectar programas C/C++ con aplicaciones Android

En esta entrada voy a describir la manera de comunicar aplicaciones escritas en Android con programas escritos en C/C++ vía JNI utilizando el NDK de Android.

El NDK es un conjunto de herramientas que permite incorporar los componentes que hacen uso de código nativo en las aplicaciones de Android. Permite implementar parte de tus aplicaciones usando código nativo con lenguajes como C y C ++. Esto puede proporcionar beneficios a ciertas clases de aplicaciones, en la medida que se puede reutilizar el código existente y en algunos casos obtener un aumento de la velocidad.

Java Native Interface (JNI) es un framework de programación que permite que un programa escrito en Java ejecutado en la máquina virtual java (JVM) pueda interactuar con programas escritos en otros lenguajes como C, C++ y ensamblador.

La estructura de un proyecto Android que utilice JNI y NDK para permitir la intercomunicación entre aplicaciones C/C++ y Android es la siguiente:
carpeta raíz del proyecto/

  • jni/
  • libs/
  • res/
  • src/
  • AndroidManifest.xml
  • default.properties
  • … otros ficheros …

Descripción de los ficheros:

  • La carpeta src contiene el código Java de la aplicación Android
  • La carpeta res contiene los recursos de la aplicación (imágenes, archivos XML que describen las capas de interfaz, etc)
  • La carpeta libs *contendrá* las librerías nativas *después* de una construcción exitosa
  • La carpeta jni *contiene* el código C/C++ de la aplicación con la que queremos comunicarnos, además de dos scripts importantes: Android.mk y Application.mk. Estos scripts son dos Makefiles típicos que controlan el proceso de construcción de la aplicación C++.

Veamos los pasos para ejecutar una sencilla aplicación Android que se comunique con un programa escrito en C++.

  • Descargar y descomprimir NDK
    Sé que hay versiones mucho más actuales, pero aún así, utilizo esta de Crystax que está bastante optimizada.
  1. wget http://www.crystax.net/en/download/android-ndk-r4-linux-x86-crystax-4.tar.bz2 -O /tmp/android-ndk-r4-linux-x86-crystax-4.tar.bz2
  2. cd ~ && tar xvjf /tmp/android-ndk-r4-linux-x86-crystax-4.tar.bz2

  • Crear la carpeta libs en el proyecto
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> Folder. Escribimos libs como nombre de carpeta.
  • Crear la carpeta jni en el proyecto
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> Folder. Escribimos jni como nombre de carpeta.
  • Crear el programa C++ con el que queremos comunicarnos
    Pulsamos el botón derecho sobre la carpeta JNI, y escogemos la opción New -> File. Escribimos code.cpp como nombre de fichero. Hacemos doble clic sobre el fichero y escribimos el siguiente código:
  1. #include <jni.h>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <getopt.h>
  5. #include <string>
  6. #include <cassert>
  7. #include <stdexcept>
  8.  
  9. using namespace std;
  10.  
  11. extern "C"
  12. {
  13. JNIEXPORT jstring JNICALL Java_blog_neonigma_AndroidActivity_SayHello(JNIEnv *env, jobject thiz, jstring name)
  14. {
  15.   try
  16.   {
  17.     const char *myName = env->GetStringUTFChars(name, 0);
  18.     std::string nameCPP(myName);
  19.     nameCPP = "Hello: " + nameCPP;
  20.     return env->NewStringUTF(nameCPP.c_str());
  21.   }
  22.   catch (exception &ex)
  23.   {
  24.     const char *error = "Failed";
  25.     return env->NewStringUTF(error);
  26.   }
  27. }
  28. }

  • Consideraciones sobre el código:
    • El bloque extern «C» indica que el programa está escrito en C++. Sí, en C++. Si queremos escribirlo en C, eliminamos el bloque extern.
    • La declaración de la función nativa tiene que coincidir con el namespace + nombre de actividad Android + nombre de función en el código Java/Android. En este caso, como nuestra actividad es AndroidActivity y se encuentra en el namespace blog.neonigma, la función debe nombrarse como Java_blog_neonigma_AndroidActivity_SayHello, siendo SayHello el nombre que damos a la función y «Java_» un texto estático.
    • La función siempre recibe un puntero al entorno (JNIEnv *env) y un objeto manejador (jobject thiz)
    • Para convertir const char * en jstring, usamos la función NewStringUTF del entorno
    • Más información en la API oficial de JNI

  • Crear los Makefiles para NDK
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> File. Escribimos Android.mk como nombre de fichero. El código para este fichero sería el siguiente:
  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. include $(CLEAR_VARS)
  4.  
  5. LOCAL_MODULE    := mixed_sample
  6. LOCAL_SRC_FILES := code.cpp
  7. LOCAL_LDLIBS +=  -llog -ldl
  8.  
  9. include $(BUILD_SHARED_LIBRARY)

Los valores más importantes son code.cpp, que indica el fichero de código con el que queremos comunicar y mixed_sample, que es el nombre que damos a la librería que se va generar vía JNI.

Repetimos el proceso y creamos un fichero Application.mk.

  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti -fexceptions
  3. APP_ABI := armeabi
  4. APP_PROJECT_PATH := ~/workspace/blog-jni-c

En este caso, los valores más importantes son la arquitectura armeabi (puede cambiarse por la más moderna armeabi-v7a) y el path del proyecto, donde hay que indicar el path al workspace actual.

  • Crear un builder para la compilación
    Debe generarse un builder en Eclipse para la compilación del código C/C++ con NDK.

    1. Pulsamos en projects -> properties y escogemos la opción Builders. Pulsamos el botón New.
    2. En la ventana que aparece, escogemos la opción Program (sin escribir nada), y pulsamos OK.
    3. En la siguiente ventana configuramos el Builder en sí. En la pestaña Main, escribimos en el campo Location la ruta absoluta al programa ndk-build, en mi caso /home/neonigma/android-ndk-r4-crystax/ndk-build En el campo Working directory, pulsamos en el botón Browse workspace y escogemos el proyecto en el que estamos trabajando.
    4. Ahora vamos a la pestaña Refresh y marcamos Refresh upon completion, Specific resources y Recursively include sub-folders.

      Pulsamos en el botón Specify resources y en la ventana que aparece, escogemos el recurso libs.
    5. Por último, vamos a la pestaña Build Options, dejamos las casillas ya marcadas y marcamos During auto builds y Specify working set of relevant resources.

      Pulsamos en el botón Specify resources y escogemos el recurso jni de nuestro proyecto.
  • Escribir el código de la actividad Android
    Para enviar datos al programa en C++ y recogerlos, necesitamos escribir el código de la actividad principal de la siguiente manera:
  1. package blog.neonigma;
  2.  
  3. import src.blog.neonigma.R;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6. import android.widget.Toast;
  7.  
  8. public class AndroidActivity extends Activity {
  9.     @Override
  10.     public void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         setContentView(R.layout.main);
  13.        
  14.         String greeting = SayHello("neonigma");
  15.         Toast.makeText(this, greeting, Toast.LENGTH_LONG).show();
  16.     }
  17.    
  18.     public native String SayHello(String name);
  19.    
  20.     static {
  21.         System.loadLibrary("mixed_sample");
  22.     }

  • Consideraciones sobre el código
    • La llamada a función nativa se define como public native.
    • String se equipara a jstring, e int a jint, a la hora de enviar y recibir parámetros.
    • Debe definirse como estática la carga del módulo de la librería generada y que especificamos en el código del Makefile Android.mk.

Referencias:
http://opencv.itseez.com/doc/tutorials/introduction/android_binary_package/android_binary_package_using_with_NDK.html#android-binary-package-with-ndk
http://zsoluciones.com/datos/?p=246

El código del ejemplo puede descargarse desde el repositorio creado en BitBucket.

A 1 persona le gusta esta entrada

Comprobación PEP8 de estilo del código Python para VIM

PEP8 es el nombre clave de la Guía de estilo para código Python publicada en julio de 2001 y que aún a día de hoy, diez años después, sigue revisándose.

Para ayudarnos en esta tarea, mi compañero de batallas @pipotux me descubrió una muy buena extensión para Vim mientras trabajábamos en un proyecto Python que nos traemos entre manos.

La instalación es sencilla:

  1. neonigma@neonigma-desktop:~$ cd /tmp
  2. neonigma@neonigma-desktop:/tmp$ git clone https://github.com/cburroughs/pep8.py.git
  3. neonigma@neonigma-desktop:/tmp$ sudo apt-get install python-setuptools
  4. neonigma@neonigma-desktop:/tmp$ cd pep8.py
  5. neonigma@neonigma-desktop:/tmp/pep8.py$ python setup.py build
  6. neonigma@neonigma-desktop:/tmp/pep8.py$ python setup.py install
  7. neonigma@neonigma-desktop:/tmp/pep8.py$ wget http://www.vim.org/scripts/download_script.php?src_id=14366 -O pep8.vim
  8. neonigma@neonigma-desktop:/tmp/pep8.py$ mkdir -p  ~/.vim/ftplugin/python
  9. neonigma@neonigma-desktop:/tmp/pep8.py$ mv pep8.vim ~/.vim/ftplugin/python

Una vez realizados estos pasos, basta con abrir cualquier archivo Python con Vim y pulsar F5. El plugin informará de los errores de estilo cometidos o arrojará un mensaje indicando que el código está escrito conforme a la PEP8.

Fuente: página oficial del plugin pep8 para Vim.

A 1 persona le gusta esta entrada

Tip: cómo modificar el último commit en bazaar / Launchpad

Hoy he cometido un pequeño error al comitear los cambios a un proyecto en el que estoy involucrado junto con @edortix y @pipotux. Dichos cambios me venían en forma de megaparche por parte de @pipotux y al subirlos, no me di cuenta que no tenia configurada mi cuenta de correo en el equipo que me encontraba trabajando.

Para solucionarlo, basta con revertir el commit, configurar correctamente la cuenta de usuario y reescribir el commit de la siguiente manera:

  1. bzr config email="neonigma &lt;neonigma@gmail.com&gt;"
  2. bzr launchpad-login
  3. bzr uncommit 27 # el número de revisión a "descomitear"
  4. bzr ci -m "A lot of changes. The most important one: changed the turn method"
  5. bzr push lp:project --overwrite

A 5 personas les gusta esta entrada

Clientes SOAP en Python y PHP

En el post anterior vimos como crear servicios Web personalizados con el protocolo SOAP en Drupal. Por supuesto, un servicio Web en SOAP puede hacerse en multitud de lenguajes. Al igual que los clientes que lo consumen. En este caso vamos a ver ejemplos para consumir el servicio Web creado en el post anterior con clientes SOAP escritos en los lenguajes Python y PHP.

Cliente en Python
En primer lugar, instalamos easy_install:

  1. neonigma@neonigma-desktop:~$ sudo apt-get install python-setuptools python-dev build-essential

A continuación, instalamos suds:

  1. neonigma@neonigma-desktop:/opt/lampp/htdocs$ sudo easy_install suds
  2. install_dir /usr/local/lib/python2.6/dist-packages/
  3. Searching for suds
  4. Reading http://pypi.python.org/simple/suds/
  5. Reading https://fedorahosted.org/suds
  6. Best match: suds 0.4
  7. Downloading http://pypi.python.org/packages/2.6/s/suds/suds-0.4-py2.6.egg#md5=94a9414e90e01243262548ad9eaf2784
  8. Processing suds-0.4-py2.6.egg
  9. creating /usr/local/lib/python2.6/dist-packages/suds-0.4-py2.6.egg
  10. Extracting suds-0.4-py2.6.egg to /usr/local/lib/python2.6/dist-packages
  11. Adding suds 0.4 to easy-install.pth file
  12.  
  13. Installed /usr/local/lib/python2.6/dist-packages/suds-0.4-py2.6.egg
  14. Processing dependencies for suds
  15. Finished processing dependencies for suds

A continuación, con sólo este pequeño programa en Python usando la librería suds podemos utilizar la operación que definimos en el servicio Web del post anterior:

  1. from suds import WebFault
  2. from suds.client import Client
  3. import traceback as tb
  4. import logging
  5.  
  6. logging.basicConfig(level=logging.INFO)
  7. logging.getLogger('suds.client').setLevel(logging.DEBUG)
  8.  
  9. try:
  10.     client = Client('http://debian-virtual/drupal/?q=services/soap/?wsdl')
  11.     print client.service.add_numbers(10,3)
  12. except WebFault, f:
  13.     print f
  14.     print f.fault
  15. except Exception, e:
  16.     print e
  17.     tb.print_exc()

El resultado de la ejecución es el siguiente:

  1. root@debian:/var/www# python cliente.py
  2. DEBUG:suds.client:sending to (http://debian-virtual/drupal/services/soap/?wsdl/)
  3. message:
  4. &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  5. &lt;SOAP-ENV:Envelope xmlns:ns3="http://debian-virtual/drupal/services/soap/?wsdl/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns0="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
  6.    &lt;SOAP-ENV:Header/&gt;
  7.    &lt;ns1:Body&gt;
  8.       &lt;ns3:add_numbers&gt;
  9.          &lt;number_one xsi:type="ns2:int"&gt;10&lt;/number_one&gt;
  10.          &lt;number_two xsi:type="ns2:int"&gt;3&lt;/number_two&gt;
  11.       &lt;/ns3:add_numbers&gt;
  12.    &lt;/ns1:Body&gt;
  13. &lt;/SOAP-ENV:Envelope&gt;
  14. DEBUG:suds.client:headers = {'SOAPAction': u'"http://debian-virtual/drupal/services/soap/?wsdl/add_numbers"', 'Content-Type': 'text/xml; charset=utf-8'}
  15. DEBUG:suds.client:http succeeded:
  16. &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&gt;&lt;SOAP-ENV:Body&gt;&lt;ns1:soap_call_wrapperResponse xmlns:ns1="http://debian-virtual/drupal/services/soap/?wsdl/"&gt;&lt;return xsi:type="xsd:int"&gt;13&lt;/return&gt;&lt;/ns1:soap_call_wrapperResponse&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;
  17. 13

Cliente en PHP
Para el cliente en PHP, necesitamos la librería nuSOAP. En este caso, la tenemos ya instalada del post anterior, por lo que apuntaremos a esa ruta. Escribimos este pequeño cliente en PHP:

  1. &lt;?php
  2. ini_set('soap.wsdl_cache_enabled', '0');
  3. require_once('drupal/modules/soap_server/nusoap/lib/nusoap.php');
  4.  
  5. $wsdl = "http://localhost/drupal-testing/?q=services/soap/?wsdl";
  6. $soap = new soapclient($wsdl);
  7.  
  8. $result = $soap-&gt;add_numbers(10,3);
  9. print "El resultado es: " . $result;
  10. ?&gt;

Apuntando a la URL de este ejemplo (http://localhost/cliente.php en este caso), veremos que nos imprime el resultado de la suma correctamente.

Sé el primero en valorar positivamente

Crea tu propio servicio Web con NuSOAP en Drupal

Una de las cosas que más me gustan del software libre es la liberación del conocimiento. Incluso (o sobre todo), cuando lo que has investigado, usado o desarrollado al final no te sirve. Me parece realmente mal que se deseche todo ese conocimiento, ya que se ha invertido el tiempo en ello, por qué no invertir 20 minutos más en estructurarlo y escribirlo. Otro podría necesitar ese conocimiento tan cercano. Aunque no esté 100% depurado y sea directamente utilizable, podría ser un gran punto de partida para otras personas, o incluso para uno mismo en momentos posteriores.

El presente artículo es uno de los que sí me han servido, pero ya empezaba a olvidar. Así que lo escribo. Y tengo en mente el siguiente, totalmente relacionado, que cumple una de las premisas del párrafo anterior: al principio no me sirvió para mucho y meses más tarde ha sido la llave para desbloquear el cierre de un proyecto.

Sin más preámbulos, veamos como crear tu propio servicio Web utilizando SOAP en Drupal. Esta forma da una idea de lo extendible que es un servicio Web dentro de este CMS y me sirve para recordar en cualquier momento cómo llevar a cabo este proceso de extensión.

En primer lugar, como siempre, instalamos drush:

  1. neonigma@neonigma-laptop:~$ sudo apt-get install drush

Descargamos el módulo services:

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules$ drush dl services -r modules
  2. Project services (6.x-2.3) downloaded to                             [success]
  3. /var/www/drupal-testing/modules/.

Lo habilitamos:

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules$ drush en services
  2. The following modules will be enabled: services
  3. Do you really want to continue? (y/n): y
  4. Services was enabled successfully.                                   [ok]

Descargamos el módulo soap_server:

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules$ drush dl soap_server -r modules/services
  2. Project soap_server (6.x-1.2-beta1) downloaded to                    [success]
  3. /var/www/drupal-testing/modules/.

Lo habilitamos:

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules$ drush en soap_server
  2. The following modules will be enabled: soap_server
  3. Do you really want to continue? (y/n): y
  4. SOAP Server was enabled successfully.                                [ok]

Para completar la instalación de SOAP Server, no basta con haber instalado el módulo. Debemos instalar la librería NuSOAP que es la que proporcionará realmente el servicio. La descargamos de este enlace por ejemplo a /tmp y la descomprimimos de la siguiente forma:

  1. neonigma@neonigma-laptop:/tmp$ mkdir nusoap
  2. neonigma@neonigma-laptop:/tmp$ mv nusoap-0.9.5.zip nusoap
  3. neonigma@neonigma-laptop:/tmp$ cd nusoap/
  4. neonigma@neonigma-laptop:/tmp/nusoap$ unzip nusoap-0.9.5.zip
  5. neonigma@neonigma-laptop:/tmp/nusoap$ rm nusoap-0.9.5.zip
  6. neonigma@neonigma-laptop:/tmp/nusoap$ cd ..
  7. neonigma@neonigma-laptop:/tmp$ mv nusoap/ /var/www/drupal-testing/modules/soap_server/

En este punto, tenemos un servidor SOAP escuchando en http://localhost/drupal-testing/?q=services/soap. Pero existe un bug en esta versión de SOAP server, por el cual al acceder a esa URL por primera vez no nos muestra las operaciones disponibles, sino el mensaje de error You must specify a name when you register an operation.

Afortunadamente tiene fácil solución. Nos bajamos este parche visto en este enlace en la ruta del módulo soap_server y lo parcheamos de esta forma:

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/soap_server$ wget http://drupal.org/files/issues/751326.patch
  2. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/soap_server$ patch -p0 &lt; 751326.patch

Si volvemos a acceder de nuevo a la URL de escucha, ahora deberíamos de ver las operaciones disponibles y un enlace al WSDL. Esta URL al WSDL para mi no encaja con la realidad, por lo que yo siempre uso http://localhost/drupal-testing/?q=services/soap/?wsdl a la hora de proporcionar este parámetro a los clientes que deseen conectarse al servicio Web personalizado.

Otro de los hándicaps de utilizar SOAP en Drupal es que se requiere URLs limpias, tema bastante espinoso en Drupal 6, al menos para mi gusto. Para habilitarlas debemos seguir los siguientes pasos:

  • Comprobamos si tenemos instalado el módulo rewrite de Apache:
    1. root@neonigma-laptop:/var/www/drupal-testing# apache2ctl -M
  • Si no está instalado, lo instalamos:
    1. root@neonigma-laptop:/var/www/drupal-testing# a2enmod rewrite
    2. Enabling module rewrite.
    3. Run '/etc/init.d/apache2 restart' to activate new configuration!
    4. root@neonigma-laptop:/var/www/drupal-testing# /etc/init.d/apache2 restart
  • Visitar la URL http://localhost/drupal/?q=admin/settings/clean-urls y, si está disponible el botón de radio con la opción Activado, lo marcamos y guardamos la configuración. Si el botón no está disponible para marcado, debemos revisar cómo habilitar las URLs limpias en este post.

Con todo instalado, toca el turno de lo interesante, crearnos nuestro propio servicio Web. Los servicios Web preinstalados y listos para usar se encuentran en modules/services/services, por lo que un buen punto de partida es copiarnos uno de ellos y modificarlo a nuestro gusto.

  1. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/services/services$ cp -R comment_service/ nuestro_service
  2. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/services/services/nuestro_service$ mv comment_service.info nuestro_service.info
  3. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/services/services/nuestro_service$ mv comment_service.inc nuestro_service.inc
  4. neonigma@neonigma-laptop:/var/www/drupal-testing/modules/services/services/nuestro_service$ mv comment_service.module nuestro_service.module

La utilidad de cada fichero es la siguiente:

  • nuestro_service.info: este fichero contiene la información de visualización del módulo. Es decir, la identificación del módulo dentro de http://localhost/drupal-testing/?q=admin/build/modules, en la zona Services.
  • nuestro_service.module: este fichero contiene la descripción estática de las funciones del servicio Web que se van a compartir.
  • nuestro_service.inc: este fichero contiene el código de las funciones del servicio Web definidas en el fichero anterior.

Lo primero que vamos a hacer es cambiar la información de identificación de nuestro servicio Web en el fichero nuestro_service.info. Para ello, modificamos el primer bloque del código original:

  1. ; $Id: comment_service.info,v 1.1.2.1 2009/06/06 22:57:40 marcingy Exp $
  2. name = Comment Service
  3. description = Provides a comment service.
  4. package = Services - services
  5. dependencies[] = services
  6. dependencies[] = comment
  7. core = 6.x

y escribimos en su lugar este otro:

  1. ; $Id: nuestro_service.info,v 1.1.2.1 2010/12/04 11:28:00 marcingy Exp $
  2. name = Nuestro propio servicio Web
  3. description = Proporciona nuestra propia funcionalidad
  4. package = Services - services
  5. dependencies[] = services
  6. core = 6.x

Nótese que hemos eliminado la dependencia al módulo comment.

A continuación, editamos el fichero nuestro_service.module:, eliminando todo el código existente y colocando este en su lugar:

[PHP]
<?php
// $Id: nuestro_service.module,v 1.1.2.1.2.9 2010/07/16 02:59:56 skyredwang Exp $

/**
* @file
* Link functionality to services module.
*/

/**
* Implementation of hook_perm().
*/
function nuestro_service_perm() {
return array(‘access add two numbers’);
}
/**
* Implementation of hook_service().
*/
function nuestro_service_service() {
return array(

// nuestro.add
array(
‘#method’ => ‘add_numbers’,
‘#callback’ => ‘nuestro_service_add’,
‘#access arguments’ => array(‘access add two numbers’),
‘#file’ => array(‘file’ => ‘inc’, ‘module’ => ‘nuestro_service’),
‘#args’ => array(
array(
‘#name’ => ‘number_one’,
‘#type’ => ‘int’,
‘#description’ => t(‘The first number.’),
),
array(
‘#name’ => ‘number_two’,
‘#type’ => ‘int’,
‘#description’ => t(‘The second number.’),
),
),
‘#return’ => ‘int’,
‘#help’ => t(‘This method adds two numbers and return the result’),
),
);
}[/PHP]

Por último, editamos el fichero nuestro_service.inc:, eliminamos todo el código existente y definimos lo que hará la función que acabamos de exponer:

  1. &lt;?php
  2. // $Id: nuestro_service.inc,v 1.1.2.1.2.5 2010/05/08 19:26:00 heyrocker Exp $
  3.  
  4. /**
  5.  * @file
  6.  *  Link functionality to services module.
  7.  */
  8.  
  9. /**
  10.  * Returns the addition of two parameters received.
  11.  *
  12.  * @param $number_one
  13.  *   First number to add.
  14.  * @param $number_two
  15.  *   Second number to add.
  16.  * @param $since
  17.  *   Timestamp to indicate what nodes are new. Defaults to time of last user acces to node.
  18.  * @return
  19.  *   Number of nuestros that node has.
  20.  */
  21. function nuestro_service_add($number_one, $number_two) {
  22.   return (int)($number_one + $number_two);
  23. }

Es el momento de verificar que todo está en su sitio. Ahora veamos la configuración de módulos en http://localhost/drupal-testing/?q=admin/build/modules. Debemos activar nuestro nuevo módulo que representa el servicio Web que acabamos de crear.

Nos vamos a http://localhost/drupal-testing/?q=admin/user/permissions y verificamos que los permisos que hemos creado en el fichero services.modules, concretamente access add two numbers, están disponibles para asignar a los roles que tenemos creados. Inicialmente se encuentran desmarcados, deberemos marcarlos para acceso a usuario anónimo si es nuestro deseo.

Daremos permisos a todo el mundo para utilizar nuestro servicio Web. Para hacer que sólo puedan utilizar el servicio Web los usuarios registrados, me remito a la entrada anterior en este blog, donde se explicaba este hecho.

En http://localhost/drupal-testing/?q=admin/build/services podemos ver el servidor SOAP ejecutándose y las operaciones expuestas de nuestro servicio Web.

Para acceder a un testeador de nuestro servicio Web pulsamos en el enlace a nuestro.add. En la pantalla que aparece podemos escribir los dos argumentos y pulsar en Call method para realizar la llamada al servicio Web y obtener el resultado.

A 1 persona le gusta esta entrada

Gestión ligera de máquinas virtuales con KVM y Virtual Manager bajo Ubuntu

De la mano de mi compañero jhernandez descubro un gestor de máquinas virtuales mucho más ligero que el pesado VMWare al que llevaba acostumbrado muchos años.

Antes que nada nos creamos un directorio en nuestro home para almacenar nuestras máquinas virtuales.

  1. neonigma@neonigma-desktop:~$ mkdir vm

La idea es instalar KVM junto con la GUI Virtual Manager:

  1. neonigma@neonigma-desktop:~$ sudo apt-get install kvm virt-manager

Luego podemos acceder de la forma que se muestra en la imagen:

Si al acceder nos muestra un error de este tipo:

Significa que nuestra carpeta personal de virt-manager se ha creado erróneamente con permisos de root. La solución es cambiar el propietario de esta carpeta:

  1. neonigma@neonigma-desktop:~$ sudo chown neonigma:neonigma .virt-manager -R

Una vez abierto, añadiremos una conexión nueva en el menú Archivo – Añadir conexión y especificaremos como Hipervisor QEMU/KVM y como conexión Local. Si a continuación vemos el error que aparece en esta imagen:

instalaremos el siguiente paquete para resolverlo:

  1. neonigma@neonigma-desktop:~$ sudo apt-get install libvirt-bin

y daremos acceso al fichero /var/run/libvirt/libvirt-sock:

  1. neonigma@neonigma-desktop:~$ sudo chmod 777 /var/run/libvirt/libvirt-sock

Ya podemos ejecutar tranquilamente Virtual Manager como usuario. Ahora podemos ver en la pantalla principal una conexión a Localhost. Pulsamos dos veces sobre ella y nos aparece un menú con cuatro pestañas. Seleccionamos la pestaña Almacenamiento, y pulsamos en el botón que muestra un icono con el símbolo + en verde. En la ventana que se nos abre, podemos poner como nombre myHome y en Tipo elegimos Directorio del sistema de archivos. Pulsamos Adelante y donde pone Ruta de destino escribimos la ruta que creamos en el primer paso del artículo, es decir, /home/neonigma/vm en mi caso. Ya tenemos un espacio creado y dentro de nuestro /home.

Cerramos esta ventana donde hemos creado nuestro espacio de almacenamiento y volvemos a la ventana principal. Ahora pulsamos en el único icono activo que muestra un monitor y que nos va a permitir crear nuestra máquina virtual. En la primera pantalla escribimos el nombre de nuestra máquina virtual y dejamos el parámetro intacto:

En la siguiente pantalla seleccionamos Utilizar imagen ISO, pulsamos en el botón Explorar y a continuación pulsamos el botón Explorar localmente, seleccionamos la ISO del sistema operativo a instalar y pulsamos Aceptar. La siguiente imagen muestra cómo debería quedar la configuración:

En la siguiente imagen, indicamos el tamaño de memoria RAM que cedemos al sistema operativo virtual, pulsamos Adelante y vemos la última pantalla que se refiere al almacenamiento en el disco virtual. En esta pantalla seleccionamos la opción Seleccione un almacenamiento gestionado o de otro tipo existente, pulsamos en explorar, en la zona izquierda en nuestro espacio myHome, y finalmente en la zona derecha en nuestro debian-virtual.img. La configuración queda así:

La pantalla final nos muestra un resumen de la configuración escogida y pulsamos en Finalizar. En este momento, comienza automáticamente la instalación de nuestro sistema operativo virtual.

A 1 persona le gusta esta entrada

Login y registro de usuarios en Drupal con Python vía XMLRPC

Para este propósito necesitamos tener un rol administrador con todos los permisos asignados. El usuario que tenga asignado este rol será con el que iniciemos sesión en Python y el que tendrá poder para registrar usuarios. A partir de aquí, voy a suponer una instalación limpia de Drupal. Drupal tiene módulos que proporcionan un servidor XMLRPC y una serie de servicios Web impecables que expone al exterior. Veamos los pasos para preparar Drupal para este cometido.

  1. Instalar la herramienta de descarga y habilitación sencilla de módulos Drupal
    1. neonigma@neonigma-desktop:~/Descargas$ sudo apt-get install drush
  2. Descargar y habilitar el módulo Services
    1. neonigma@neonigma-desktop:/opt/lampp/htdocs/drupal/modules$ drush dl services -r modules
    2. Project services (6.x-2.2) downloaded to /opt/lampp/htdocs/drupal/modules/services.                                                                                                                  [success]
    3.  
    4. neonigma@neonigma-desktop:/opt/lampp/htdocs/drupal/modules$ drush en services
    5. The following projects will be enabled: services
    6. Do you really want to continue? (y/n): y
    7. services was enabled successfully.                                                                                                                                                                   [ok]

    En Drupal vamos a Administrar -> Construcción del sitio -> Módulos. Activamos los siguientes módulos y pulsamos en Guardar configuración:

    MUY IMPORTANTE. En Drupal vamos ahora a Administrar -> Administración de usuarios -> Opciones de usuario y DESACTIVAMOS la casilla Es necesaria la verificación por correo cuando un visitante crea una cuenta. Pulsamos el botón Guardar la configuración del final de la página. La siguiente imagen muestra cómo debe quedar la configuración:

    A continuación, vamos a Administrar -> Construcción del sitio -> Services, pulsamos en la pestaña Opciones y en el desplegable Authentication module escogemos Key authentication. A continuación desmarcamos Use keys y dejamos marcado Use sessid. Este párrafo es lo más importante, el que permite la magia. La siguiente imagen muestra cómo debe quedar la configuración:

    Por último, vamos a asignar todos los permisos correctamente. Vamos a Administrar -> Administración de usuarios -> Permisos. En la columna de administrador debemos haber marcado todos los checkbox, dando permiso al administrador para hacer lo que desee en el portal.

    Con esto, ya tendríamos Drupal preparado para el acceso a los distintos servicios Web. Para comprobarlo, vamos Administrar -> Construcción del sitio -> Services y hacemos clic en el enlace XMLRPC – /services/xmlrpc. La URL que se nos abra en el navegador será nuestra URL de conexión a los servicios Web. En mi caso de ejemplo, la URL es http://localhost/drupal/services/xmlrpc

    Ahora, sólo tenemos que escribir un pequeño programa de testing como este para aprovechar estos servicios:

    1. #! /usr/bin/python
    2. import os.path, sys, xmlrpclib, socket
    3.  
    4. config = {
    5.   'url': 'http://localhost/drupal/services/xmlrpc',
    6.   'username': 'admin',
    7.   'password': 'admin',
    8. }
    9.  
    10. # Make initial connection to service
    11. server = xmlrpclib.ServerProxy(config['url'], allow_none=True);
    12.  
    13. try:
    14.     server.system.listMethods() # si podemos listar las operaciones del servicio web, hemos conectado al servidor
    15. except xmlrpclib.ProtocolError, socket.error:
    16.     print 'No se puede conectar al servidor'
    17. except EOFError:
    18.     exit(1)
    19. else:
    20.     try:
    21.         connection = server.system.connect(); # hacer uso de la funcion connect del servicio web System
    22.     except:
    23.         print "Error en la conexion. Codigo de error: %d" % err.faultCode
    24.         print "%s" % err.faultString.encode('utf-8','ignore')
    25.     else:
    26.         try:
    27.             session = server.user.login(connection['sessid'], config['username'], config['password']); # usar funcion login del servicio web User
    28.  
    29.         except xmlrpclib.Fault, err:
    30.             print "Error en el inicio de sesion. Codigo de error: %d" % err.faultCode
    31.             print "%s" % err.faultString.encode('utf-8','ignore')
    32.         else:
    33.             sessid = session['sessid']; # necesario obtener el sessid, es nuestra key para identificarnos despues
    34.             user = session['user'];
    35.  
    36.             try:
    37.                 # construimos los datos del nuevo usuario y le entregamos la key
    38.                 user_data = {
    39.                     'sessid': session['sessid'],
    40.                     'name': 'enigma',
    41.                     'mail': 'enigma@enigma.es',
    42.                     'pass': 'enigma',
    43.                 }
    44.                 print session # datos del administrador
    45.                 result = server.user.save(sessid, user_data) # intentar registrar nuestro usuario, no olvidar la key
    46.                 print result # resultado del registro de usuario
    47.             except xmlrpclib.Fault, err:
    48.                 print "Error en el registro de usuario. Codigo de error: %d" % err.faultCode
    49.                 print "%s" % err.faultString.encode('utf-8','ignore')        
    50.             else:
    51.                 print "Se ha registrado correctamente al usuario"

    El resultado de la ejecución de este código es el siguiente:

    1. neonigma@neonigma-desktop:/opt/lampp/htdocs/drupal$ python register.py
    2. u003igf16uvf9fnhaspe3v36e0
    3. {'sort': '0', 'status': '1', 'picture': '', 'uid': '3', 'language': 'es', 'created': '1290805507', 'roles': {'3': 'usuario administrador', '2': 'authenticated user'}, 'signature': '', 'init': 'admin@admin.fake', 'access': '1290858423', 'signature_format': '0', 'theme': '', 'form_build_id': 'form-ac8a1d5923bb603b231df3f65fc8d38f', 'mode': '0', 'pass': '21232f297a57a5a743894a0e4a801fc3', 'threshold': '0', 'mail': 'admin@admin.fake', 'login': 1290858449, 'data': 'a:1:{s:13:"form_build_id";s:37:"form-ac8a1d5923bb603b231df3f65fc8d38f";}', 'timezone': '3600', 'name': 'admin'}
    4. 9
    5. Se ha registrado correctamente al usuario

    Si vamos a Administrar -> Administración de usuarios -> Usuarios veremos nuestro usuario perfectamente creado. Además, podemos iniciar sesión con él.

    A 3 personas les gusta esta entrada