Archivo

Archivo para la categoría ‘sysadmin’

Comparativas de Virtualización KVM

miércoles, 5 de enero de 2011 1 comentario

Ultimamente por diferentes cuestiones personales estoy bastante desconectado de la lectura de noticias, blogs y demás. Sin embargo, me pareció útil dejar por acá dos comparativas sobre KVM de Phoronix.com, que si bien en algunos lados me encontré que se quejan de la «calidad» de su suite de benchmarks para software libre/abierto, por el otro, ¡hey! ¡al menos se preocupan en hacerlo! 🙂

Linux KVM vs. VirtualBox 4.0 Virtualization Benchmarks
Este es un test de performance entre KVM y Virtualbox. En resumen, y con los parámetros por defecto en cuanto a I/O, VBox gana, pero parece que es debido a que no hace los fsync del guest en el host y termina utilizando su caché; muy probablemente este comportamiento es configurable (¿ver acá?), pero así y todo ¡es bueno saberlo!. De todas maneras, de alguna manera es peligroso para un entorno de servidores ya que hay riesgo de pérdida de datos, pero por ejemplo, para mi notebook (target primario adonde Virtualbox siempre estuvo orientado) está bien.

Por el otro lado, cuando hay que usar la CPU, KVM «le pasa el trapo» a VBox. Hay un test de I/O de red (TCP) que a KVM le dio mal, tan mal, que seguro es debido a que los salames testers no habilitaron VirtIO en los guests KVM.

Multi-Core Scaling In A KVM Virtualized Environment
Este test trata de dilucidar si es mito o no que el Hyperthreading (las VCPUs) de los microprocesadores Intel Xeon basados en Nehalem hacen más lentas o más rápidas las VMs que corren sobre KVM. Esto es algo que pregunté personalmente en el IRC de KVM (#kvm en Freenode, dicho sea de paso), y me sugirieron lo mismo que dió en los tests: hay veces en donde las VMs aprovechan todas las CPUs virtuales (VCPUs) y otras en que no, con lo cual hay que probar en cada caso en particular y dejar la configuración que nos dé mejores resultados.

Interesante de leer y experimentar.

Saludos

Categories: codear, linux, sysadmin, ubuntu-ar Tags:

Achicando imágenes de Máquinas Virtuales (KVM-QCow2)

sábado, 14 de agosto de 2010 8 comentarios

Consolidando Máquinas Físicas a Virtuales

Dentro del mundo de la Virtualización, al momento de consolidar máquinas [1] lo más sencillo (o lo que primero se le puede ocurrir a uno [2]) es hacer una imagen bit a bit del disco donde éste se aloja a un archivo del Host, mediante alguna herramienta como dd en Linux:

$ dd if=/dev/sdb of=/vms/images/imagen.raw

Este mecanismo es generalmente infalible; luego de ésto, uno crea un perfil de una Máquina Virtual (típicamente un archivo XML donde se describen las características del Guest), se lo relaciona a esta imagen «cruda» de la ex-Máquina Física, y en un minutos la misma está corriendo como Máquina Virtual sin problemas.

Además, es más común utilizar un formato de imágenes más flexible que el Raw («crudo»), que soporte características como:

Es aquí donde cada solución de virtualización tiene su propio formato para obtener todas o algunas de estas ventajas, según el caso:

El manejo del almacenamiento en forma dinámica de éstos nos permite que al momento de crear una imagen de un disco de por ejemplo, 80 GB, la imagen ocupe físicamente unos pocos cientos de bytes; a medida que se van guardando archivos en el disco de la VM, éstos son almacenados finalmente en el archivo de imagen, hasta llegar al tope estipulado al momento de crear la imagen de disco (los 80 GB, por ejemplo).

Pero volvamos al caso de la generación de una imagen de un disco físico de un equipo, ¿qué pasa si éste era de 160 GB y sólo tenía 20 GB de información guardada en uso efectivo? El archivo .raw es una copia bit a bit del disco de punta a punta, por lo tanto, su imagen Raw («cruda») en el Host ocupará 160 GB. Es decir, este mecanismo no discrimina el espacio ocupado por los archivos de datos del no utilizado y/o de archivos eliminados.

Bueno entonces, ¿para qué tenemos los formatos de imágenes nativos del hypervisor (KVM+QEmu en nuestro caso) que mencionamos anteriormente? ¿Es posible convertir una imagen Raw en una QCow2, para que su imagen ocupe los 20 GB de datos en vez de 160 GB? Sí es posible la conversión; para todas las operaciones con imágenes de disco KVM+QEmu tiene la herramienta qemu-img:

$ qemu-img convert -f raw imagen.raw -O qcow2 imagen.qcow2

Con ese comando convertimos el archivo Raw al formato QCow2, buscando que la imagen a utilizar sea más pequeña , pero en su lugar la mejora de espacio es mucho menor a la esperada (aún si se llegara a dar). Es aquí donde nos encontraremos con un problema: ¡qemu-img no sabe distinguir entre los archivos de datos y el espacio no utilizado/eliminado! Si la máquina física estuvo en uso por algún tiempo, es probable que no lleguemos ni cerca al archivo «ideal» de 20 GB. Si momentáneamente tuve en ese disco de esa máquina física 60 GB extras de música y fotos familiares, seguramente el archivo QCow2 convertido desde Raw no va a bajar de los 80GB, por ejemplo.

¿Y esto porqué sucede? La teoría dice que:

  • Todo aquel espacio de disco, que viene de fábrica digamos, está compuesto de bytes en cero (0x00).
  • Todo aquel espacio de disco que es utilizado primero y luego borrado del disco, no es eliminado físicamente, es decir, no vuelve a cero, sino que queda con el contenido previo. El sistema de archivos en la enorme mayoría de los casos, para ganar velocidad (y mucha) al hacerlo, sólo quita la referencia en el índice del Sistema de Archivos al conjunto de clusters del disco que tiene el contenido real del archivo. Es por eso que uno «no ve» al archivo recientemente eliminado porque el índice de archivos existentes no lo tiene más, pero físicamente el archivo sigue estando.
  • QEmu-img reconoce como espacio vacío los bytes en cero.

Sin embargo, tenemos opciones para hacer lo que queremos y no quedarnos sin espacio en el Host rápidamente, nada más que en KVM+QEmu hay que hacerlo un poco más a mano; este proceso se llama en inglés «Image Shrinking» («achicar», «reducir», «contraer» la imagen). [3][4]

Achicando («Shrinkeando») imágenes de discos virtuales

Según «la teoría» y los links que se han citado , para poder recuperar espacio ocupado del disco hay que ponerlo en cero; ¿Y cómo se hace? Fácil, creando un archivo tan grande como el espacio remanente del disco lleno de ceros.

Armando el Caso de Prueba

Vamos a tratar de reproducir el problema y solucionarlo en forma representativa con una imagen Raw de 10 MB, como para que cualquiera pueda seguir estos pasos y entender de qué se trata todo esto.

1) Creamos una imagen Raw de prueba con 10 MB de bytes aleatorios:

marcelo@marcelo-laptop:~$ dd if=/dev/urandom of=test.raw bs=1024 count=10000
10000+0 registros de entrada
10000+0 registros de salida
10240000 bytes (10 MB) copiados, 2,24907 s, 4,6 MB/s
marcelo@marcelo-laptop:~$

2) Ya que todo disco tiene una tabla de particiones, se la creamos con parted:

marcelo@marcelo-laptop:~$ parted test.raw print
AVISO: Usted no es el superusuario. Compruebe los permisos.
Error: /home/marcelo/test.raw: etiqueta de disco no reconocida
marcelo@marcelo-laptop:~$ parted test.raw mktable msdos
AVISO: Usted no es el superusuario. Compruebe los permisos.
marcelo@marcelo-laptop:~$ parted test.raw print
AVISO: Usted no es el superusuario. Compruebe los permisos.
Modelo:  (file)
Disco /home/marcelo/test.raw: 10,2MB
Tamaño de sector (lógico/físico): 512B/512B
Tabla de particiones. msdos
 
Numero  Inicio  Fin  Tamaño  Tipo  Sistema de ficheros  Banderas
 
marcelo@marcelo-laptop:~$

El «parted test.raw print» muestra la tabla de particiones, y el «parted test.raw mktable msdos» crea una tabla de particiones de tipo MSDOS (hay otros tipos, como por ejemplo GUID). Primero vemos que el print da un error, luego creamos la tabla y por último vemos la tabla de particiones sin particiones definidas.

3) Ahora hay que crear una partición en esa tabla, nuevamente con parted:

marcelo@marcelo-laptop:~$ parted test.raw mkpart primary 0 10
AVISO: Usted no es el superusuario. Compruebe los permisos.
Aviso: La partición resultante no está debidamente alineada para el mejor rendimiento.
Descartar/Ignore/Cancelar/Cancel? Ignore
marcelo@marcelo-laptop:~$ parted test.raw print
AVISO: Usted no es el superusuario. Compruebe los permisos.
Modelo:  (file)
Disco /home/marcelo/test.raw: 10,2MB
Tamaño de sector (lógico/físico): 512B/512B
Tabla de particiones. msdos
 
Numero  Inicio  Fin     Tamaño  Tipo     Sistema de ficheros  Banderas
 1      512B    10,2MB  10,2MB  primary
 
marcelo@marcelo-laptop:~$

4) Ahora le damos formato a la partición en la imagen, por ejemplo, NTFS. El parámetro «–fast» es para que no llene de ceros la partición al momento de crearla (sólo para que siga con datos aleatorios) y el «–force» es para que se haga la operación a pesar de que el archivo no es un disco «de verdad».

marcelo@marcelo-laptop:~$ mkfs.ntfs --fast --force test.raw
test.raw is not a block device.
mkntfs forced anyway.
The sector size was not specified for test.raw and it could not be obtained automatically.  It has been set to 512 bytes.
The partition start sector was not specified for test.raw and it could not be obtained automatically.  It has been set to 0.
The number of sectors per track was not specified for test.raw and it could not be obtained automatically.  It has been set to 0.
The number of heads was not specified for test.raw and it could not be obtained automatically.  It has been set to 0.
Cluster size has been automatically set to 4096 bytes.
To boot from a device, Windows needs the 'partition start sector', the 'sectors per track' and the 'number of heads' to be set.
Windows will not be able to boot from this device.
Creating NTFS volume structures.
mkntfs completed successfully. Have a nice day.
marcelo@marcelo-laptop:~$ parted test.raw print
AVISO: Usted no es el superusuario. Compruebe los permisos.
Modelo:  (file)
Disco /home/marcelo/test.raw: 10,2MB
Tamaño de sector (lógico/físico): 512B/512B
Tabla de particiones. loop
 
Numero  Inicio  Fin     Tamaño  Sistema de ficheros  Banderas
 1      0,00B   10,2MB  10,2MB  ntfs
 
marcelo@marcelo-laptop:~$

6) Perfecto, ya tenemos nuestro «mini disco» NTFS para jugar. Ahora vamos a montarlo y ver qué tiene:

marcelo@marcelo-laptop:~$ sudo mount -o loop test.raw /mnt
[sudo] password for marcelo:
marcelo@marcelo-laptop:~$ mount
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
[...]
/dev/loop0 on /mnt type fuseblk (rw,nosuid,nodev,allow_other,blksize=4096)
marcelo@marcelo-laptop:~$ tail /var/log/syslog
[...]
Aug 14 02:28:57 marcelo-laptop ntfs-3g[19770]: Version 2010.3.6 external FUSE 28
Aug 14 02:28:57 marcelo-laptop ntfs-3g[19770]: Mounted /dev/loop0 (Read-Write, label "", NTFS 3.1)
Aug 14 02:28:57 marcelo-laptop ntfs-3g[19770]: Cmdline options: rw
Aug 14 02:28:57 marcelo-laptop ntfs-3g[19770]: Mount options: rw,silent,allow_other,nonempty,relatime,fsname=/dev/loop0,blkdev,blksize=4096
Aug 14 02:28:57 marcelo-laptop ntfs-3g[19770]: Ownership and permissions disabled, configuration type 1
marcelo@marcelo-laptop:~$  ls -l /mnt
total 0
marcelo@marcelo-laptop:~$

7) Ahora estamos como si hubiéramos hecho el dd de un disco físico en nuestro Host con KVM+QEmu. Como se ha visto, la imagen no tiene nada en el sistema de archivos pero físicamente está lleno de bytes aleatorios y ocupa 10 MB, el tamaño completo. ¿Qué pasa si lo convertimos a QCow2? ¿Se cumple «la teoría»?

marcelo@marcelo-laptop:~$ sudo umount /mnt
marcelo@marcelo-laptop:~$ qemu-img convert -f raw test.raw -O qcow2 test.qcow2
marcelo@marcelo-laptop:~$ ls -l
[...]
-rw-r--r--  1 marcelo marcelo 10420224 2010-08-14 02:37 test.qcow2
-rw-r--r--  1 marcelo marcelo 10240000 2010-08-14 02:28 test.raw
[...]
marcelo@marcelo-laptop:~$ qemu-img info test.raw
image: test.raw
file format: raw
virtual size: 9.8M (10240000 bytes)
disk size: 9.8M
marcelo@marcelo-laptop:~$ qemu-img info test.qcow2
image: test.qcow2
file format: qcow2
virtual size: 9.8M (10240000 bytes)
disk size: 9.8M
cluster_size: 65536
marcelo@marcelo-laptop:~$

Sí, se cumple… es más, la imagen QCow2 ocupa más espacio que la Raw (por ahora). Hemos podido reproducir el problema.

Llenando de Ceros el espacio vacío – Wiper.py

Bien, ahora se me ocurrió armar un script en Python para que me ayude a automatizar el proceso de crear un archivo con ceros hasta que me quede sin espacio para luego borrarlo. Lo llamé «wiper.py». El código fuente es el siguiente:

#!/usr/bin/env python
# coding: utf-8
 
# Wiper
# Copyleft 2010 - Licencia BSD
# Autor: Marcelo Fernández.
# Email: marcelo.fidel.fernandez@gmail.com
 
# This script creates a file which fills the available disk with zero bytes,
# and when the disk is full, the file is deleted.
# This allow to convert a raw partition to qcow2 'shrinking' the real image
 
import sys, os, errno
 
FILENAME = 'wiper.000'
STEP_SIZE = 4096
 
if __name__ == '__main__':
    if len(sys.argv) != 2:
        print 'usage: python wiper.py path_mounted_image'
        sys.exit(1)
    fat_file_path = os.path.join(sys.argv[1], FILENAME)
    try:
        fat_file = open(fat_file_path, 'wb', STEP_SIZE)
    except Exception, e:
        print 'There was an error opening the file in %s: %s' % (fat_file_path, str(e))
        sys.exit(2)
    print 'Temp file %s open for writing. Filling disk...' % fat_file_path
    try:
        while True:
            fat_file.write('\x00' * STEP_SIZE)
    except EnvironmentError, env_error:
        if env_error.errno == errno.ENOSPC:
            print 'No space left on device, good! Deleting temp file...'
        else:
            # There was another error different from 'No space available',
            # anyway we'll alway try to delete the fat_file
            print 'Abnormal error: ', oserror
    except Exception, e:
        # Rarely, but this could happend too
        print 'Exception: ' + repr(e)
    fat_file.close()
    try:
        os.unlink(fat_file_path)
    except Exception, e:
        print 'There was a problem deleting the temp file.'
    else:
        print 'Temp file erased ok.'

Básicamente hace lo necesario, el STEP_SIZE es importante para que la operación sea bastante más rápida que escribiendo de a un byte. Vamos a ejecutar el script y convertir nuevamente el archivo Raw a QCow2 con qemu-img a ver qué sucede:

marcelo@marcelo-laptop:~$ sudo mount -o loop test.raw /mnt
marcelo@marcelo-laptop:~$ python wiper.py /mnt/
Temp file /mnt/wiper.000 open for writing. Filling disk...
No space left on device, good! Deleting temp file...
Temp file erased ok.
marcelo@marcelo-laptop:~$ ls -l /mnt
total 0
marcelo@marcelo-laptop:~$ sudo umount /mnt
marcelo@marcelo-laptop:~$ qemu-img convert -f raw test.raw -O qcow2 test_shrinked.qcow2
marcelo@marcelo-laptop:~$ ls -l
[...]
-rw-r--r--  1 marcelo marcelo 10420224 2010-08-14 02:42 test.qcow2
-rw-r--r--  1 marcelo marcelo 10240000 2010-08-14 02:58 test.raw
-rw-r--r--  1 marcelo marcelo  3014656 2010-08-14 02:58 test_shrinked.qcow2
marcelo@marcelo-laptop:~$ sudo mount -o loop test.raw /mnt
marcelo@marcelo-laptop:~$ df -h
S.ficheros            Tamaño Usado  Disp Uso% Montado en
/dev/sda1              16G  6,7G  7,7G  47% /
[...]
/home/marcelo/test.raw
                      9,8M  2,5M  7,3M  26% /mnt
marcelo@marcelo-laptop:~$

Bueno, ahora sí, el archivo QCow2 ocupa un cuarto del archivo Raw (2,5 MB). ¿En qué se gastan estos 2.5MB? Seguramente en los índices del sistema de archivos para poder gestionar el espacio libre. Es altamente probable que más que este espacio recuperado no podamos ganar, ya que si tocamos estos índices, corrompemos el sistema de archivos; recordemos que en test.raw puede haber un Sistema Operativo completo con todos sus archivos de datos.

De todas maneras, esto puede variar según el sistema de archivos de la imagen (NTFS en este caso) y los parámetros con los cuales se está manejando el mismo (el tamaño de bloque y de cluster incide mucho en el espacio que ocupan los índices de los Sistemas de Archivos en general).

Conclusiones

Es bueno saber que también se puede usar el comando dd para crear un archivo lleno de ceros en la partición ntfs de pruebas montada, con el comando «dd if=/dev/zero of=/mnt/borrame.000» y después borrar el archivo en cuestión, pero puede hacerse interminable el «llenado» si no pasamos como parámetro un block size relativamente grande, como de 4096 bytes:

marcelo@marcelo-laptop:~$ time dd if=/dev/zero of=/mnt/borrame.000 bs=4096
dd: escribiendo «/mnt/borrame.000»: No hay espacio libre en el dispositivo
1863+0 registros de entrada
1862+0 registros de salida
7626752 bytes (7,6 MB) copiados, 0,144428 s, 52,8 MB/s
 
real	0m0.149s
user	0m0.000s
sys	0m0.030s
marcelo@marcelo-laptop:~$

Prueben a pasarle bs=1, a ver cuánto tarda…. (lo pueden cortar con Ctrl+C). 🙂

Una cuestión más que queda dando vueltas es que esta situación de espacio desperdiciado por la imagen se da también luego de un tiempo de usar la Máquina Virtual, si se da el caso de que se libere espacio en forma masiva; como caso, el ejemplo de un disco de 80 GB con 20 GB dedicados al Sistema Operativo más 60 GB de fotos y música eliminados recientemente.  Aquí también será útil correr wiper.py en la imagen montada y volver a ejecutar «qemu-img convert» para shrinkear la imagen.

En fin, espero que este post le sea de utilidad a aquellos que trabajamos con tan formidable hypervisor como lo es KVM y su gestor de I/O como lo es QEmu.

Saludos

[1] Si bien a un Linux se lo puede consolidar más fácilmente, sólo copiando todos los archivos con rsync, o «cp -a», con Windows este método no funciona, y hay que copiar el disco o al menos la partición completa. La idea al usar dd es abstraerse del SO.
[2] En vez de dd se puede usar una herramienta de clonación de discos/particiones que «entienda» o «interprete» el filesystem, como Partimage para intentar generar directamente una imagen Raw con el espacio no utilizado en cero; pero nunca lo probé, ni me parece un método muy «limpio», ya que estamos insertando un software algo complejo en el medio de algo que de cualquier otra manera es conceptualmente simple y repetible. En el único caso que lo probaría es si estoy falto de espacio de disco en el Host. Si alguien lo prueba, me avisa. 🙂
[3] Creo que «comprimir» no es la palabra apropiada porque da la idea al lector de que hay un proceso (que implementa un algoritmo) de compresión de datos sin pérdida, como por ejemplo, LZ. Sin embargo, aquí lo que se necesita es recuperar el espacio no utilizado de la imagen, para obtener un tamaño de imagen menor.
[4] Como nota al margen, VMWare tiene en sus VMWare Tools una opción para hacer «Shrink» de la imagen, pero ¿cómo se divierte un Sysadmin con ganas de aprender si sólo tiene que hacer click en un botón que diga «Shrink Image»? 😛

Categories: codear, linux, sysadmin, ubuntu-ar Tags:

Sincronizar carpetas a un Servidor Casero automáticamente

viernes, 18 de junio de 2010 Sin comentarios

Introducción

Supongamos que tengo un equipo donde usualmente estoy trabajando y otro equipo que está siempre encendido, ambos separados por Internet. En este último, el único puerto abierto sobre una IP Pública es el 22 para usar SSH, con lo cual me viene perfecta la capacidad de rsync de sincronizar carpetas y los últimos cambios mientras estoy trabajando, todo mediante un canal seguro.

El Script de sincronización

Lo único que necesito es este script en la carpeta personal de mi equipo «cliente», o sea, donde hago mis quehaceres diarios:

#!/bin/bash
# Script para sincronización a Servidor vía Casero rsync/ssh.
#
# Copyleft 2010 - Licencia BSD
# Autor: Marcelo Fernández.
# Email: marcelo.fidel.fernandez@gmail.com
#
# Características:
#  - Guarda la salida en un archivo de log (~/sync.log).
#  - Sincronización de una vía; pisa las modificaciones (y elimina) en el destino.
#  - Envía notificaciones al escritorio del usuario.
#  - Se sugiere ser llamado desde cron (gnome-schedule para el usuario final).
#  - Sincroniza (una sola vía) el directorio Documentos de mi Carpeta Personal y el Escritorio,
#    se pueden agregar más carpetas a gusto al momento de ejecutar el comando rsync.
#    También debe personalizarse el destino del backup, ahora en /media/Disco1/Backup
#
# Requerimientos:
#  - Rsync
#  - Paquete libnotify-bin (Debian/Ubuntu), que brinda el comando notify-send.
#
# Parámetros:
#  - Host destino. El script se lo llama "./sync.sh mi_host".
#    El login debe ser vía public keys, y la passphrase la maneja Gnome.
 
HOST=$1
LOG_FILE="sync.log"
export DISPLAY=:0.0 # Para el notify
export SSH_AUTH_SOCK="$(find /tmp/keyring*/ -perm 0755 -type s -user $LOGNAME -group $LOGNAME -name '*ssh' | head -n 1)"
 
notify-send -u normal --icon=gtk-refresh --category=transfer "Sincronizando a Casa..."
echo `date` >> $LOG_FILE
rsync -avz -e 'ssh' --delete ~/Documentos ~/Escritorio $HOST:/media/Disco1/Backup/ &>> $LOG_FILE
RETVAL=$?
if [ $RETVAL -ne 0 ]; then
    notify-send -u critical --icon=gtk-dialog-error --category=transfer.error  "Error al Sincronizar";
else
    notify-send -u normal --icon=gtk-apply --category=transfer.complete "Sincronización Completa";
fi

Programando su ejecución automática

Este script está pensado y preparado para ser llamado desde la aplicación «Tareas Programadas» de Gnome (disponible en el Centro de Software de Ubuntu o se instala haciendo click aquí):

Este es el detalle de la configuración de la tarea:

Y además nos avisa de que está trabajando y si tuvo éxito o no al sincronizar:

Algunas cuestiones para destacar

Como se aclara en los comentarios, cada vez que rsync copia las modificaciones al servidor, «pisa» lo que había anteriormente, lo que comúnmente se denomina sincronización de archivos «de una vía«.

Lo más interesante es que a pesar de ser ejecutado desde Cron, aprovecha las llaves SSH desbloqueadas por Gnome en la sesión de escritorio del usuario, es decir que la primera sincronización, si previamente no me había conectado al equipo remoto, me pide la frase de paso para desbloquear la clave privada; a partir de ese momento ésta queda compartida, gracias al ssh-agent que utiliza Gnome en background. Luego, mientras siga la sesión de escritorio establecida, va a aprovechar la llave desbloqueada por el usuario y hacer la sincronización sin mayor problema. Esto es mucho mejor en cuanto a seguridad que usar una clave privada sin frase de paso contra el servidor, y me permite desbloquearla sólo la primera vez y no cada vez que se hace la sincronización, siendo un perfecto balance (al menos para mí) entre seguridad, automatización y comodidad. Lógicamente esto sirve para utilizar en cualquier conexión SSH que se quiera establecer, y la solución, después de dar muchas vueltas y probar muchas alternativas (¡el ssh del cron no «veía» al agente!), la encontré en este post.

Conclusión

Este script fue creciendo desde algo muy simple, totalmente manual y que ejecutaba «cada vez que me acordaba» a mejorarlo un poco en cuanto a la automatización y hacerlo más «lindo» como está hoy. Si bien hay muchísimos mecanismos más robustos o con más facilidades (usando los snapshots de rsync por ejemplo y/o sincronizando ida y vuelta), éste es el que funciona para mí. Es bien claro lo que hace y no hay ninguna «magia» en el medio; trabaja sin que me moleste en mi actividad diaria y me despreocupo totalmente de que tenía que hacer backup. 🙂

Espero que les sirva como a mí. Acá hay mucha más información sobre rsync y una explicación más profunda de las diferentes opciones y alternativas, que por supuesto se pueden utilizar con esto como base.

¡Saludos!

Actualización – Ubuntu 11.10

En Ubuntu 11.10 hay que tocar levemente la línea donde define la variable SSH_AUTH_SOCK, cambia esto:

export SSH_AUTH_SOCK="$(find /tmp/keyring*/ -perm 0755 -type s -user $LOGNAME -group $LOGNAME -name '*ssh' | head -n 1)"

por esto, reemplazar $USER por el nombre de usuario («marcelo» en mi caso):

export SSH_AUTH_SOCK="$(find /tmp/keyring*/ -type s -user $USER -group $USER -name '*ssh' | head -n 1)"

Actualización – Ubuntu 12.10

En Ubuntu 12.10 hay que tocar levemente la línea donde define la variable SSH_AUTH_SOCK, cambia esto:

export SSH_AUTH_SOCK="$(find /tmp/keyring*/ -perm 0755 -type s -user $LOGNAME -group $LOGNAME -name '*ssh' | head -n 1)"

por esto, reemplazar $USER por el nombre de usuario («marcelo» en mi caso):

export SSH_AUTH_SOCK="$(find /run/user/$USER/keyring*/ -type s -user $USER -group $USER -name '*ssh' | head -n 1)"
Categories: codear, linux, sysadmin, ubuntu-ar Tags:

Reiniciando las conexiones de red automáticamente en Ubuntu

miércoles, 16 de junio de 2010 Sin comentarios

Hace un tiempo que tengo una interfaz Wifi USB Encore; siempre la usé ocasionalmente, en Ubuntu se conectaba y tenía red sin problemas, pero al momento de usarla en forma constante nunca supuse que el módulo rtl8187, responsable de su funcionamiento, iba a tener tantos bugs. 🙁

En Ubuntu 10.04 funciona sin instalar nada adicional, pero los principales problemas son que funciona sólo a 11Mbps y que aleatoriamente se desconecta, sin causa aparente, y el Network Manager vuelve a reconectarse. El problema es más serio cuando Network Manager se cansa de revivir a la placa, y el equipo se queda desconectado definitivamente. Y más si no estoy cerca para reconectarla a mano. 😛

Luego de probar unas cuantas recetas (y de esquivar Ndiswrapper porque es un equipo de 64 bits), decidí que voy a dejarlo así, y me hice un scriptcito de cron para reiniciar Network Manager si la interfaz está caída:

#!/bin/bash
 
# /usr/bin/network_respawn
# Network respawn -- rtl8187b dies randomly and despite Network-Manager usually
# reconnects again, sometimes it gives up and dies without connection.
 
# If wlan0 is not connected, it restarts Network-Manager
# This script is meant to be called from a cron job. 
 
IFACE="wlan0"
 
CONNECTED=`grep $IFACE /var/run/network/ifstate`
if [ "$CONNECTED" == "" ]; then  # Disconnected. Respawning...
    /usr/bin/service network-manager restart
fi

Además, hay que crear el archivo en /etc/cron.d/network_respawn para que el sistema llame al script anterior, cada 3 minutos en mi caso:

# Network respawn -- rtl8187b dies randomly and despite Network-Manager usually
# reconnects again, sometimes it gives up and dies without connection.
# If wlan0 is not connected, it restarts Network-Manager
 
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
 
*/3 * * * * root /usr/bin/network_respawn

Es un script sencillo, y puede ser utilizado para cualquier propósito como el mío. Lo malo es que reinicia todas las conexiones del equipo, ya que reinicia el Network Manager .

¿A alguien se le ocurre cómo hacerlo mejor? 🙂

Saludos

Categories: codear, linux, sysadmin, ubuntu-ar Tags:

Reemplazando texto con expresiones regulares en Python

viernes, 2 de abril de 2010 Sin comentarios

Hay veces en que uno necesita automatizar tareas, como reemplazar cierto texto por otro bajo ciertas condiciones, y el viejo «%s/cosa/otra/g» del vim nos queda corto. En mi caso en particular, estaba metiendo algunas pequeñas características en PyFpdf, y vi que había algunos archivos .py llenos de llamadas a la función chr().

Claro, PyFpdf es un port más o menos «haragán» (lazy) de Fpdf para PHP, y el autor original evidentemente encontró más sencillo definir algunas fuentes (en binario) haciendo sucesivas llamadas a la función chr(), como  esta:

fpdf_charwidths['times']={chr(0):250, chr(1):250, chr(2):250, chr(3):250,} # Sigue...

Enseguida pensé en mejorarlo, reemplazando esas sucesivas llamadas a chr() por la misma función pero ya evaluada y en forma de byte string; por ejemplo, la idea era convertir lo anterior en:

fpdf_charwidths['times']={'\x00':250,'\x01':250,'\x02':250,'\x03':250,} # Sigue...

De esa manera, los archivos .py se simplificarían (se harían más legibles), no sufrirían modificaciones en su comportamiento y hasta serían más veloces en su interpretación y ejecución.

Primero se me ocurrió primero hacer algo más «a mano», pero en cuanto se me complicó un poquito enseguida pensé que la mejor herramienta era usar expresiones regulares para el matching del patrón «chr(x)» y, por consiguiente, el módulo re de la librería estándar de Python.

Leyendo esta excelente página y la documentación del módulo en cuestión, armé un script, y me quedó así:

#!/usr/bin/python
# coding:utf-8
 
# This script tries to identify all chr(XX) constant calls in python scripts
# and replace them with '\xXX' strings.
# Author: Marcelo Fernández - License: MIT
 
import sys
import re
 
def chrrepl(match):
    # See http://www.amk.ca/python/howto/regex/regex.html
    # Use the captured group to get the hex string value.
    char_number = match.group(1)
    return repr(chr(int(char_number)))
 
if __name__ == '__main__':
    if len(sys.argv) != 3:
        print 'Usage: python chr_cleaner.py infile.py outfile.py'
        sys.exit(1)
 
    infile = sys.argv[1]
    outfile = sys.argv[2]
    # Open file for reading
    try:
        fin = open(infile, 'r')
        fout = open(outfile, 'w')
    except IOError:
        print 'Error when reading %s or trying to write %s' % (infile, outfile)
        sys.exit(2)
 
    intext = fin.read()
    pattern = 'chr\\((\\d+)\\)' # Group the chr() function parameter to capture it
    p = re.compile(pattern)
    outtext = p.sub(chrrepl, intext)
    fout.write(outtext)
    fout.flush()
 
    fin.close()
    fout.close()

Si bien creo que el snippet es bastante legible, lo interesante es que con el módulo re se puede:

  • Identificar un patrón en un texto: En este caso el patrón sería «chr(\d)» (\d porque busco un número allí).
  • Marcar grupos en ese patrón utilizarlos luego; como sé que voy a utilizar el número en cuestión para convertirlo a su byte string, lo defino dentro de un grupo. El patrón queda entonces «chr((\d))». También es posible usar named groups, identificables por nombre en vez de por posición, pero eso lo dejo de tarea al lector 😛
  • Reemplazar todo el texto que coincide con ese patrón, y en cada reemplazo, llamar a una función que especifique «lo que hay que poner» allí, devolviendo un string. Esta es la frutilla del postre… 🙂
    Si bien puedo usar p.sub() para reemplazar lo que busco por un texto constante, también puedo hacer que en cada ocurrencia del patrón una función cualquiera sea llamada, y  se le pase la instancia de MatchObject para que decidamos qué hacer con lo encontrado y devolver un string. Esto hace la función chrrepl() del snippet. Allí tomo el primer grupo (el grupo 0 es el string completo, el 1 es el primer grupo definido posicionalmente, y así sucesivamente), lo convierto a entero, ejecuto la función chr() y devuelvo su representación en byte string.

En fin, después de experimentar un buen rato, esto funcionó y acá se puede ver el diff del commit correspondiente para que se aprecie un poco mejor…

Saludos

Categories: codear, programación, python, sysadmin, ubuntu-ar Tags: