domingo, 10 de marzo de 2013

Django: Optimistic Locking

At the Córdoba (Argentina) Sprint, on February 23rd, we achieved to implement the minimum necessary code to have a working prototype of Optimistic Locking.

Why is needed?

To avoid the loss of information when 2 users update the same object, or even when one user has the same object open in multiples browsers or tabs (database locks nor 'select for update' help here).

Ej: the object with version 'N' is in the DB; the object is shown in the browser by two users (let sey user A and user B). The user A modifies it and save it in the database. The user B modifies the same object, and save it. The changes entered by user A are silently lost, and neither user A nor user B acknowledges the situation.

How it works

It works the same way as in Hibernate. The instance has a version field, which is sent to the user as a hidden field. Instead of using a simple SQL "UPDATE ...", we use "UPDATE ... WHERE VERSION = YYY", and check how many rows where updated. If no row was updated, it means the version has changed and we report this as an optimistic locking error.

Ej: the current implementation raises an OptimisticLockingError when user B tries to save the changes. The developer has to catch this exception and make whatever he or she wants. 

Alternatives to Optimistic Locking

  • maybe versioning of attributes (ej: instead of having a "descripcion" field, have a set of "descriptions" and a "current description" foreign key)

TODO

  1. remove requeriment of hard-coded 'version' field, and instead look for any field of type VersionField,
  2. decide and implement how to manage deletes,
  3. evaluate alternatives how to enable optimistic locking on models (see 'Alternatives for enabling O.L. on a model'),
  4. implement graceful handling of optimistic locking errors in admin,
  5. see how this relates the adoption of NOSQL support or other related topics, 

Alternatives for enabling O.L. on a model

We want to make optimistic locking totally optional and easy to use for existing models and forms. We explored different alternatives to 'mark' that we want optimistic locking semantics for a specific model. Also, we want to avoid introducing new mechanisms. The alternatives were:



1. Add a VersionField to models
This is how it's implemented right now: you add a VersionField to your model. The associated form field uses a HiddenInput widget, so ModelForms don't have to be modified to support optimistic locking, since the version is automatically sent to the browser as a hidden field.

PRO: is explicit (you see a new field being added)
CON: models should have only one VersionField, and the field is kind of magic: if it exists, the model has optimistic locking semantics. There is no comparable mechanism in Django (maybe could be comparable to AutoField?).

--- Example ---
----------


2. Use a Meta option
Allow the user set a Meta option, like 'optimistic_locking = True'.

PRO: it very clean
CON: hides the fact that a new field is added. There is no comparable mechanism in Django (a meta option that adds a field)

--- Example ---
----------


Git


References

Optimistic Locking

Related Django tickets


Authors

  • Ramiro Morales <cramm0@gmail.com>
  • Francisco Capdevila <fjcapdevila@gmail.com>
  • Carlos Ilcobich <cilcobich@gmail.com>
  • Fernando Flores <fef.flores@gmail.com>
  • Matías Bordese <mbordese@gmail.com>
  • Martín Gaitan <gaitan@gmail.com>
  • Horacio G. de Oro <hgdeoro@gmail.com>

miércoles, 9 de enero de 2013

Compilando Gimp 2.8 en Ubuntu 12.04

Antes que nada, esto lo hice sobre un Ubuntu 12.04... aunque ya hay una versión más nueva de Ubuntu, todavía no tuve tiempo de actualizarlo, por eso este artículo...

Las instrucciones las tomé desde http://ubuntuforums.org/showthread.php?t=1974512, pero con algunas modificaciones en el procedimiento.

Para empezar, es importante tener instalados los paquetes indicados en la página. Antes de encontrar dicha página intenté compilar Gimp, y me surgieron varios errores, pero en ningún momento los mensajes de error hacían referencia a problemas con dependencias de librerías.

La idea es compilar Gimp para instalar en /opt/gimp-2.8. Como Gimp depende de babl y gegl, antes hace falta compilar e instalar dichas librerías. Yo acostumbro buscar la forma de instalar todo el /opt y sin usar root, para garantizar que no se pise ni modifique las librerías y ejecutables instalados por Ubuntu y así no afectar el funcionamiento de nuestro Linux.

Los pasos son:

1. creamos /opt/gimp-2.8 (necesitaremos sudo), y le asignaremos como dueño nustro usuario "no-root". Estos son los únicos pasos en los que usaremos sudo:


$ sudo mkdir /opt/gimp-2.8
$ sudo chown horacio /opt/gimp-2.8

2. compilamos babl (suponemos que los fuentes están en /tmp/babl):


$ cd /tmp/babl
$ ./configure --prefix=/opt/gimp-2.8
$ make
$ make install

3. compilamos gegl (suponemos que los fuentes están en /tmp/gegl):

$ cd /tmp/gegl
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./autogen.sh
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./configure --prefix=/opt/gimp-2.8
$ make
$ make install

4. compilamos gimp (suponemos que los fuentes están en /tmp/gimp):

$ cd /tmp/gimp
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./autogen.sh
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./configure --prefix=/opt/gimp-2.8
$ make
$ make install


Ahora ya podemos ejecutar Gimp:

$ /opt/gimp-2.8/bin/gimp-2.8

Además compilé ufraw (para importar imágenes RAW). Pero esto me trajo muchos problemas: aunque compilaba y se instalaba correctamente, me daba errores al momento de abrir archivos RAW. El problema fue que el plugin de ufraw para Gimp fue compilado y linkeado contra la librería de gimpui de Ubuntu (en vez de la versión de /opt/gimp-2.8).

Antes que nada, hará falta instalar algunos paquetes:

$ sudo apt-get install liblensfun-dev libgtkimageview-dev

Los pasos para compilar e instalar ufraw son similares:


$ cd ufraw-0.18/
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./autogen.sh --prefix=/opt/gimp-2.8
$ PKG_CONFIG_PATH=/opt/gimp-2.8/lib/pkgconfig ./configure --prefix=/opt/gimp-2.8
$ make
$ make install

y para que funcione, hace falta setear LD_LIBRARY_PATH:

$ export LD_LIBRARY_PATH=/opt/gimp-2.8/lib
$ /opt/gimp-2.8/bin/gimp-2.8


jueves, 15 de noviembre de 2012

Nueva versión: PyArduinoProxy v0.0.3

Esta vez para arreglar un  bug, ya que digitalRead() habia dejado de funcionar.

Les recuerdo, para instalar PyArduinoProxy:

    $ pip install pyarduinoproxy

y para actualizarlo:

    $ pip install --upgrade pyarduinoproxy

miércoles, 14 de noviembre de 2012

Nueva versión: PyArduinoProxy v0.0.2

Luego de mucho tiempo, he lanzado PyArduinoProxy v0.0.2. Les cuento brevemente lo que ha cambiado:

  • instalación simplificada con pip/virtualenv,
  • eliminé fuentes de librerías (CherryPy, Jinja2, SimpleJSON), ahora son instaladas automaticamente via pip/virtualenv,
  • agregué soporte para sensores de temperatura/humedad DHT11
  • arreglé BUG de JS (expresiones regulares ahora no son 'callables').
Como siempre, cualquier tipo de comentario, sugerencia, etc. es bienvenida!


viernes, 14 de septiembre de 2012

How to install Cinnamon from GitHub on Ubuntu 12.04

As a first attempt, a ./configure + make + make install wont work, so I want to share what I've done to make it work. Since it's a development version, I won't install to /usr, but to /opt/cinnamon-dev instead...

These are the steps:


$ sudo mkdir /opt/cinnamon-dev
$ sudo chown $(id -nu).$(id -ng) /opt/cinnamon-dev
$ apt-get build-dep muffin cinnamon

$ cd
$ git clone git://github.com/linuxmint/muffin.git
$ cd muffin
$ autoreconf --force --install
$ ./configure --prefix=/opt/cinnamon-dev
$ make
$ make install

$ cd
$ git clone git://github.com/linuxmint/Cinnamon.git
$ cd Cinnamon
$ autoreconf --install --force
$ aclocal
$ intltoolize --force
$ env PKG_CONFIG_PATH=/opt/cinnamon-dev/lib/pkgconfig CFLAGS="-w" ./configure --prefix=/opt/cinnamon-dev
$ make
$ make install

# FAIL!

$ cd files/
$ mkdir -p opt/cinnamon-dev
$ mv usr opt/cinnamon-dev/
$ mv etc/ opt/cinnamon-dev/
$ cd ..
$ make install

# Now works!

The ugly "moves" after the fails are needed because I think some paths are hardcoded, and the process that copies the files from the "files/" directory ignores the "--prefix" configured.

sábado, 25 de agosto de 2012

Mejorando transferencia de ssh/rsync

Buscando la forma de mejorar la transferencia de backups (realizados con rsync, pero sobre un tunel de SSH), encontré que la velocidad de transferencia puede variar mucho dependiendo del cipher usado para crear la conexión con SSH.

En una primera búsqueda encontré unos tests que reproduje en mi ambiente. Las pruebas que hice consiste en copiar un archivo con contenidos aleatorios (generado desde /dev/urandom), de 512MB, sobre una conección Gigabit Ethernet, estando el archivo de origen y el de destino en el directorio /dev/shm (esto nos permite asegurarnos que el acceso a disco no influya en nuestros tests):


Warm up... Copiando con cipher por default...
random                           100%  512MB  42.7MB/s   00:12    

Copiando con cipher: 3des-cbc
random                           100%  512MB  16.5MB/s   00:31    

Copiando con cipher: aes128-cbc
random                           100%  512MB  85.3MB/s   00:06    

Copiando con cipher: aes192-cbc
random                           100%  512MB  73.1MB/s   00:07    

Copiando con cipher: aes256-cbc
random                           100%  512MB  64.0MB/s   00:08    

Copiando con cipher: aes128-ctr
random                           100%  512MB  42.7MB/s   00:12    

Copiando con cipher: aes192-ctr
random                           100%  512MB  34.1MB/s   00:15    

Copiando con cipher: aes256-ctr
random                           100%  512MB  32.0MB/s   00:16    

Copiando con cipher: arcfour128
random                           100%  512MB 102.4MB/s   00:05    

Copiando con cipher: arcfour256
random                           100%  512MB 102.4MB/s   00:05    

Copiando con cipher: arcfour
random                           100%  512MB 102.4MB/s   00:05    

Copiando con cipher: blowfish-cbc
random                           100%  512MB  56.9MB/s   00:09    

Copiando con cipher: cast128-cbc
random                           100%  512MB  51.2MB/s   00:10    

Luego probé copiar a "localhost":

Warm up... Copiando con cipher por default...
random                               100%  512MB  56.9MB/s   00:09    

Copiando con cipher: 3des-cbc
random                               100%  512MB  20.5MB/s   00:25    

Copiando con cipher: aes128-cbc
random                               100%  512MB 170.7MB/s   00:03    

Copiando con cipher: aes192-cbc
random                               100%  512MB 170.7MB/s   00:03    

Copiando con cipher: aes256-cbc
random                               100%  512MB 170.7MB/s   00:03    

Copiando con cipher: aes128-ctr
random                               100%  512MB  64.0MB/s   00:08    

Copiando con cipher: aes192-ctr
random                               100%  512MB  51.2MB/s   00:10    

Copiando con cipher: aes256-ctr
random                               100%  512MB  46.6MB/s   00:11    

Copiando con cipher: arcfour128
random                               100%  512MB 256.0MB/s   00:02    

Copiando con cipher: arcfour256
random                               100%  512MB 256.0MB/s   00:02    

Copiando con cipher: arcfour
random                               100%  512MB 170.7MB/s   00:03    

Copiando con cipher: blowfish-cbc
random                               100%  512MB  64.0MB/s   00:08    

Copiando con cipher: cast128-cbc
random                               100%  512MB  56.9MB/s   00:09    

El ganador en velocidad, es por mucho arcfour, pero por lo que he visto, no es recomendable usarlo cuando la seguridad es un factor crítico. El cipher aes128 es algo más lerdo, pero la taza de transferencia es de más del doble comparada al cipher por default.

Para usar un cipher específico con rsync + ssh, el comando a usar tendría la forma:

$ rsync -e 'ssh -c arcfour256' ......

El script que usé lo subí a un gist de GitHub.

lunes, 20 de agosto de 2012

Pentaho: primeros pasos con ETL, Kettle, Spoon, PDI

Mis primeros pasos en BI con Pentaho no fueron de lo más fácil. Hay mucha información, es un tema nuevo para mi (estoy aprendiendo a la vez BI y Pentaho), el producto posee una edición Open Source y otra cerrada, y diferentes nombres para cosas parecidas (por ejemplo: Kettle, Spoon, PDI/Pentaho Data Integration).

Así que en este artículo compartiré los pasos que seguí para hacer andar las herramientas necesarias para realizar el proceso de ETL (el artículo de WikiPedia sobre Data Warehouse es una muy buena referencia para un vistazo general).

El mejor tutorial para darlos primeros pasos que encontré es PDI / Pentaho Data Integration / Kettle. Los 4 pasos están muy bien explicados, aunque me topé con algunos inconvenientes, por eso incluyo links a los 4 pasos del turorial, con algunos comentarios:

01. Installing Kettle - Comentarios: lo bajé de aquí: PDI / Kettle 4.3 (la más reciente versión al momento de escribir el artículo). Actualmente, la versión que está disponible es referenciada desde algunas páginas como "Kettle 4.3.0 including Big Data Components", y el archivo se llama "pdi-ce-4.3.0-stable.tar.gz" o "pdi-ce-4.3.0-stable.zip" (dependiendo si se quiera bajar como tar.gz o zip).

02. Spoon Introduction - Comentarios: luego de descomprimirlo y lanzar Spoon, se abrío una instancia de Firefox y una aplicación Java. Entre los links que veo en Firefox, creo que el más relevante es Spoon User Guide.

03. Hello World Example - Comentarios: este paso desarrollamos un ejemplo completo de ETL. Tuve problemas en "Modified JavaScript Value Step", ya que al parecer, la línea que contiene:

name.getString()

no funciona correctamente, pero lo pude arreglar usando:

name.toString()

Al llegar a probar el "pan", para obtener la lista de modificadores aceptados, hay que ejecutar:

./pan.sh

(sin ningún modificador... primero intenté con -h y --help, pero esto no funciona). Hay más información en la página de Pan User Documentation.

En el caso de Linux, para especificar los modificadores del programa se usa "-" en vez de "/":

./pan.sh -norep -file ../ejemplo1/tutorial.ktr

04. Refining Hello World - Comentarios: el igual que con "pan.sh", en Linux, para especificar los modificadores de kitchen.sh se usa "-" en vez de "/". Además, no seguí al pié de la letra las instrucciones; particularmente, prefiero evitar la parametrización a través de "kettle.properties", y en su lugar, utilicé para los paths, la variable ${Internal.Job.Filename.Directory}.

Finalmente, después de renegar un rato, pude ejecutar correctamente el job desde la línea de comando (usando kitchen).

Como resumem, puedo decir que seguir el tutorial me dejó claro varias cosas:

  • "Big Data components" se refiere al hecho de que pueden leerse y escribirse datos de/a Hadoop, Cassandra, etc., pero es algo que no usaremos inicialmente,
  • Spoon es la herramienta gráfica para diseñar procesos de ETL,
  • Pan y Kitchen permiten ejecutar los procesos de ETL desde la línea de comando (por lo tanto, sería una forma de automatizar estos procesos desde, por ejemplo, cron, aunque supongo que debe haber algún componente para realizar el scheduling de estas tareas),
  • Kettle es lo mismo que PDI, que significa "Pentaho Data Integration", que incluye a todo lo anterior: herramientas visuales, CLI, todas las librerías que implementan las funcionalidades que tan fácilmente se utilizan desde Spoon.

viernes, 10 de agosto de 2012

Modificando el último commit con git rebase

Algunas veces, luego de hacer un commit, vemos que olvidamos algún pequeño detalle, y por cuestiones de "prolijidad", hubiéramos querido incluirlo en el commit recién realizado.

Generalmente, en estos casos utilizo git reset --soft HEAD^ para recuperar el commit anterior, pero siempre me pareció que debería haber alguna forma mejor. Utilizando rebase, el proceso es mucho más fácil, ya que evitamos, por ejemplo, re-ingresar el mensaje de commit, con la posibilidad de modificarlo si hace falta.

Los comandos a utilizar son:


$ git rebase --interactive HEAD^

Ahora git presentará un editor, donde mostrará una línea con el commit a editar, por ejemplo:

pick 9245320 Cambios en home: agregue nombre de usuario logueado

Hay que cambiar esta línea, reemplazando "pick" por "edit". 

edit 9245320 Cambios en home: agregue nombre de usuario logueado

Luego aparecerá:

Stopped at 9245320... Cambios en home: agregue nombre de usuario logueado
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

$ git add <archivos modificados>


$ git rebase --continue

Este último comando nos presentará el editor de texto con el mensaje de commit original, el que podremos modificar si hace falta.

viernes, 20 de julio de 2012

South: 'assertsynced' para asegurar que todas las migraciones están aplicadas

El uso de South con aplicaciones Django es un estándar en los desarrollos en los que participo. Pero hace tiempo me surgió la necesidad de saber si hay migraciones sin aplicar.

Es fácil conocer 'visualmente' la situación, pero el problema es cuando hace falta saberlo desde un script. Por ejemplo: al momento de realizar deploys, y en los scripts de arranque de los sistemas.

Sobre todo en este último caso, la aplicación no debería iniciarse si hay migraciones pendientes de aplicar. Finalmente me tomé un tiempo y cree assertsynced. Este es un comando de Django, basado en la implementación de migrate de South.

Está disponible aquí.

CentOS: como chequear si un paquete está instalado


Una forma de revisar si un paquete está instalado en CentOS es utilizando el comando "repoquery", ya sea con el nombre del paquete específico o con comodines:

$ repoquery --info --pkgnarrow=installed bind-chroot

También:

$ repoquery --info --pkgnarrow=installed '*bind*'

Si repoquery no está instalado, hay que ejecutar:

$ sudo yum install yum-utils


viernes, 6 de julio de 2012

Cassandra en CentOS 6

Hace tiempo están disponibles los RPMs para instalar Cassandra en CentOS, pero el procedimiento indicado implica bajar el RPM de Cassandra e instalarlo manualmente. Esto no es complicado, pero SI es complicado  automatizar la instalación de la última versión disponible.

Pero esto tiene solución. El repositorio puede ser registrado en CentOS, bajando el siguiente snippet y guardándolo en /etc/yum.repos.d/riptano.repo.

--
--

Luego, para instalar Cassandra 1.x:


$ yum install --assumeyes apache-cassandra11
$ chkconfig --add cassandra
$ chkconfig cassandra on
$ service cassandra start

martes, 12 de junio de 2012

Servicios IPv6 y firewalls

Hace tiempo las distintas distribuciones de Linux poseen soporte para IPv6, lo que incluye generar interfaces de red con direcciones IPv6 y servicios escuchando en dichas direcciones.

Por ejemplo, usando netstat podemos revisar los servicios TCP y UDP escuchando en IPv6:


$ sudo netstat -6nlptu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp6       0      0 :::139                  :::*                    LISTEN      1140/smbd       
tcp6       0      0 fe80::bc47:21ff:fe67:53 :::*                    LISTEN      1509/dnsmasq    
tcp6       0      0 :::22                   :::*                    LISTEN      1142/sshd       
tcp6       0      0 ::1:631                 :::*                    LISTEN      1219/cupsd      
tcp6       0      0 ::1:25                  :::*                    LISTEN      2275/exim4      
tcp6       0      0 :::445                  :::*                    LISTEN      1140/smbd       
tcp6       0      0 :::12865                :::*                    LISTEN      2305/netserver  
udp6       0      0 fe80::bc47:21ff:fe67:53 :::*                                1509/dnsmasq    
udp6       0      0 :::55594                :::*                                1188/avahi-daemon: 
udp6       0      0 :::5353                 :::*                                1188/avahi-daemon: 

Otra forma sería realizar un escaneo de puertos. Por ejemplo:

$ sudo nmap -6 -P0 ::1
Starting Nmap 5.21 ( http://nmap.org ) at 2012-06-12 10:26 ART
Nmap scan report for ip6-localhost (::1)
Host is up (0.00026s latency).
Not shown: 995 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
139/tcp open  netbios-ssn
445/tcp open  microsoft-ds
631/tcp open  ipp

Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds


Esto puede resultar en un gran problema de seguridad si no fue tenido en cuenta al configurar los firewalls, sobre todo si hablamos de servidores conectados a internet.

Una forma de revisar si las reglas firewall para IPv6 están siendo cargadas es:

$ sudo chkconfig --list ip6tables

ip6tables       0:off   1:off   2:on    3:on    4:on    5:on    6:off


En este caso vemos que el servicio está activado. Si no fuera así, puede activarse con:

$ sudo chkconfig ip6tables on

El siguiente paso es revisar qué reglas de firewall hay configuradas para IPv6:

sudo ip6tables -n -L -v

Si las reglas cargadas son correctas y proveen una protección para el equipo, aquí terminó nuestro trabajo (aunque sería bueno reiniciar el servidor para asegurarnos que todo quedó correctamente configurado). Además, si el equipo es un servidor, y hay otros administradores, puede ser que haya reglas cargadas, sean correctas, pero hayan sido cargadas "a mano", y se pierdan al reiniciar el equipo.

Si no hay reglas cargadas, una forma rápida de realizar una configuración "de emergencia" para que el kernel ignore todos los paquetes IPv6 que lleguen es:

$ sudo ip6tables -P INPUT DROP
$ sudo ip6tables -P FORWARD DROP



Esta configuración eliminará todos los paquetes, inclusive las conexiones creadas localmente (lo que pude causar problemas... para realizar una correcta configuración, hay que aprender a usar iptables!) y los paquetes de respuesta de conexiones IPv6 creadas a otros equipos...

Estas modificaciones que introdujimos al firewall se perderán al reiniciar el equipo. Para guardarlas, de manera que se apliquen automáticamente al arrancar el equipo, debemos ejecutar:

$ sudo service ip6tables save
$ sudo service ip6tables restart

Aceleración de consultas de DNS reverso

Las consultas de DNS reveso (Reverse DNS lookup) pueden hacer que cualquier proceso que las utilice (por ejemplo, servidores SMTP) se torne lerdo en responder, sobre todo si se realizan consultas por IPs privadas, y los DNS no están configurados con las zonas reversas correspondientes.

Para solucionarlo, una opción es desactivar las consultas reversas, pero éstas pueden proveer información útil (generalmente son realizadas para loguear el nombre asociado a los IP que crea la conexión), sobre todo cuando las conexiones son originadas en Internet. Además, esta solución implica modificar cada uno de los servicios en cada servidor.

La solución correcta es crear una zona en nuestros DNSs que respondan a estas consultas. En el caso de Bind, y suponiendo que nuestra lan utiliza las direcciones 192.168.*.*, esto se puede lograr de la siguiente manera:

En /etc/named.conf, agregar las siguientes líneas:

 zone "168.192.in-addr.arpa" {
         type master;
         file "/etc/named.192.168.reverse";
 };

El contenido del archivo /etc/named.192.168.reverse debe ser parecido a:

$TTL 2d
$ORIGIN 168.192.IN-ADDR.ARPA.
@             IN      SOA   ns1.example.com. hostmaster.example.com. (
                              200001011 ; serial number
                              3h         ; refresh
                              15m        ; update retry
                              3w         ; expiry
                              3h         ; nx = nxdomain ttl
                              )
              IN      NS      ns1.example.com.
              IN      NS      ns2.example.com.

(digo "parecido a" porque habría que ajustar "ns1.example.com", "hostmaster.example.com" y los NS).

viernes, 8 de junio de 2012

PIL + Virtualenv en Ubuntu Precise Pangolin

Al instalar PIL en un virtualenv, el proceso terminó con:


*** TKINTER support not available
*** JPEG support not available
*** ZLIB (PNG/ZIP) support not available
*** FREETYPE2 support not available
*** LITTLECMS support not available

lo que indica que PIL fue compilado SIN soporte para JPG, aunque todas las librerías necesarias (libz, libjpeg, etc) estaban instaladas, incluyendo los correspondientes paquetes de desarrollo (*-dev).

En busca de soluciones, encontré algunos posts que sugerían crear links de las librerias (libxxxxx.so) en /usr/lib, solución que me pareció poco "elegante". Hasta que encontré una solución diferente, donde tambien hace falta crear links, pero esta vez en el directorio "lib" de virtualenv.

Básicamente, hay que ejecutar:

$ cd virtualenv/lib
$ ln -s /usr/lib/x86_64-linux-gnu/libfreetype.* .
$ ln -s /usr/lib/x86_64-linux-gnu/libjpeg.* .
$ ln -s /usr/lib/x86_64-linux-gnu/libz.* .

lunes, 28 de mayo de 2012

Actualización de mi fork de fabric

Actualicé mi humilde fork de fabric :-D Hace tiempo salió la versión 1.4 de Fabric, pero la facultad y el trabajo no me dieron tiempo de ponerme al día.

Para instalarlo usando pip, simplemente hay que ejecutar:

pip install -e git+https://github.com/hgdeoro/fabric@1.4#egg=fabric

domingo, 27 de mayo de 2012

Caché para pip

Al trabajar con varios proyectos Python usando virtualenv, es comun bajar una y otra vez las mismas librerías/dependencias (Django, South, PIL, psycopg2, etc).

Para optimizar la instalación de estos paquetes, se puede configurar pip para que utilice cierto directorio como cache. Para lograrlo, hay que crear el archivo ~/.pip/pip.conf, con el siguiente contenido:


[install]
download-cache = /var/cache/pip-cache



Obviamente necesitamos permisos de escritura en ese directorio. Una forma de crearlo seria:

$ sudo mkdir /var/cache/pip-cache
$ sudo chown USUARIO /var/cache/pip-cache


martes, 13 de marzo de 2012

Hoy descubrí: fuser

El comando lista los procesos que poseen un archivo, socket o filesystem. Su funcionalidad es parecida a lsof, pero tiene más opciones. Por ejemplo, para ver los procesos que poseen archivos abiertos en el filesystem del dispositivo "/dev/mapper/seguro":

$ fuser -v -m /dev/mapper/seguro

                     USER        PID ACCESS COMMAND
/dev/dm-5:           root     kernel swap  /dev/sda6
                     root     kernel mount /dev
                     horacio    2472 f.... startkde
                     horacio    2524 f.... unclutter
                     horacio    2950 F.... pulseaudio
                     horacio    3037 F.... gvfs-fuse-daemo
                     (...)
                     horacio    3047 F.... gvfs-gphoto2-vo
                     horacio    3063 F.... gvfs-afc-volume
                     horacio    3761 f.... chromium-browse
                     horacio    7684 F.... java





jueves, 16 de febrero de 2012

Hoy descubrí: cómo obtener runlevel en Solaris

Encontré que el comando "runlevel" no existe en Solaris, pero luego de investigar un poco encontré que hay que usar "who -r".


$ who -r
   .       run-level 3  Mar 27 23:10     3      0  S


Este comando sí funciona en Linux (al menos en Ubuntu), así que, una manera portable (al menos Linux/Solaris) de obtener el runlevel en un script sería:

$ who -r | awk '{ print $2 }'
2