jueves, 29 de noviembre de 2012

¡RaspberryPi en casa!

¡Por fin tenemos las placas!

Ayer, tras varios meses de espera, nos llegaron las placas de la RaspberryPi.
Ahora queda el proceso de ponerlas en marcha, que iremos detallando y sobre todo contando que uso hemos pensado darles.
Vienen tal cuales, desnuditas ... aunque compramos la carcasa blanca por aquello de la presentación y que no nos mataran en casa por tener otro "cacharro" más en medio.
Ahora a por una tarjeta SD, un cargador compatible con la placa, un cable HDMI y un cable de red.
Iremos dando detalles.

martes, 20 de noviembre de 2012

Breves notas sobre scripts en bash

Obtener directorio donde se encuentra el ejecutable, asi evitamos tener que configurar una variable con el path del ejecutable (sobre todo el aplicaciones de shell que manejan varios ficheros fuente).
PREFIX=$(readlink -f $(dirname $0))
Redirigir salida dentro de script hacia un fichero de log. Con esto conseguimos que no sea necesario ir añadiendo pipes (>) a cada uno de los comandos de nuestro script y que podamos, al finalizar el script, mandar por mail el log de ejecución.
exec 2>&1 >> LOGERRORES
Añadir elementos a un array.
LISTADO=("${LISTADO[@]}" "ITEM");
Pueden parecer nimiedades, pero no quita que sean muy útiles.

PD: Publicado en Breves notas sobre scripts en bash

miércoles, 7 de noviembre de 2012

Jugando con getopt y getopts

Cuando me he tenido que enfrentar al problema de controlar el paso parámetros en los scripts, siempre lo he dejado como una tarea de google, es decir, que busco por encima lo que necesito, lo adapto a mis necesidades y luego lo olvido. Para la próxima vez, disponde de esta nota, que pretendía ser breve, pero que al final no lo será tanto.

¿Diferencias entre getopt y getopts?

Getopt, en singular, es un comando que permite tratar los parámetros que llegan, sean cortos (del tipo -a, -b, -l) o largos (--long). El segundo es un comando incluido dentro de la bash que solo nos permite operar con los parámetros cortos.

¿Parámetros cortos y largos, con o sin argumentos?

Tanto los parámetros cortos como los largos, pueden tener argumentos (obligatorios o no). Esto se especificará en la cadena de parámetros que se le pasará a getopt/getopts. Por ejemplo, para indicar que vamos a tener dos parámetros sin argumentos podemos utilizar "ab". Si el parámetro 'a' va a llevar un argumento, pondremos "a:b". Si ambos llevan argumento "a:b:". Por último hay un caso más que podemos necesitar, que el parámetro 'a' pueda llevar un argumento, pero que no sea obligatoria la aparición del argumento, entonces la cadena de tratamiento sería 'a::b".

En el caso de los parámetros largos es algo similar. Tenemos el parámetro '--help' y el parámetro '--output=file' con un argumento. La cadena de tratamiento queda como 'help,output:'. Si queremos que '--help' lleve un argumento extra opcional, entonces tendríamos 'help::,output:'. Observese que en este caso, para separar los parámetros largos se utiliza la coma.

¿Cómo usar getopt?

La utilización de getopt es algo compleja de entender, pero con la siguiente estructura de código tenemos el análisis de todos los parametros que le pueden llegar a nuestros scripts con solo unas pequeñas modificaciones (ver ejemplo un poco más abajo).

La estructura va como sigue. Primero se costruye una cadena que contiene la ejecución del tratamiento de parámetro con getopt, indicando los parámetros cortos (-o) y sus versiones largas (--long). Se le pasa también el nombre del programa (-n), se indica cuando hay que dejar de procesar los parámetros (si se encuentra '--') y por último se le pasa la ristra de parámetros que se han recibido ($@). Si hay algun error en el tratamiento, saldremos.

El siguiente paso es evaluar la cadena, es en este momento donde entra en juego el uso de las comillas invertidas en su definición, utilizando set --, para hacer que los argumentos sean evaluados en función de su posición. Con la operación shift iremos desplazándonos entre los argumentos cuando entremos en el bucle, del que saldremos cuando topemos con el parámetro --, que indica que hemos llegado al final de los argumentos recibidos.

Dentro del bucle, hacemos el tratamiento con los case necesarios para los argumentos y si estamos en un argumento que tiene alguna opción, la recogemos al estar en la variable $2 ($1 es la propia opción). Siempre que tratemos un argumento habra que hacer el desplazamiento con shift (uno o dos veces dependiendo de si tiene opción o no), de lo contrario nos quedaremos en el bucle para siempre...
CADPARAM=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n '$0' -- "$@"`
if [ $? != 0 ] ; then
      echo "Error en el tratamiento de parametros..." 1>&2 ;
      exit 1 ;
fi
# Se hace la evaluacion de los parametros
eval set -- "$CADPARAM"
while true ; do
        case "$1" in
                -a|--a-long) echo "Opcion a" ; shift ;;
                -b|--b-long) echo "Opcion b, argumento \`$2'" ; shift 2 ;;
                -c|--c-long)
                        # c tiene un argumento opcional.
                        # Sera generado vacio aunque no lo encuentre.
                        case "$2" in
                                "") echo "Opcion c, sin argumento"; shift 2 ;;
                                *)  echo "Opcion c, con argumento \`$2'" ; shift 2 ;;
                        esac ;;
                --) shift ; break ;;
                *) echo "Error de tratamiento de opciones!" ; exit 1 ;;
        esac
done
¿Cómo usar getopts?

Getopts es algo similar, solo que suprime la evaluación y no hay bucle con condición true, la propia condición de while va recorriendo los elementos de la lista de argumentos con getopts. Es una construcción algo más sencilla de entender, aunque tiene menos posibilidades.
while getopts "ab:c::" OPCION
do
case $OPCION in
a)
ARGUMENTO1=true
;;
b)
ARGUMENTO2=true
OPT2=$OPTARG
;;
c)
ARGUMENTO3=true
if [ $OPTARG != "" ]; then
OPT3=$OPTARG
fi
;;
*)
echo "Opcion no reconocida"
exit 1
;;
?)
echo "Uso: ejemplo [-a] [-b opcionb] [-c [opcionc]]"
exit 1
;;
esac
done
¿Y después qué?

Getopt y getopts está implementado en bibliotecas para otros lenguajes de programación como C, PHP, Perl, Java. Lo más complicado será encontrar la forma de invocación y la manera en la que iremos paseándonos por los argumentos. El cómo especificar la cadena de argumentos es similar.

viernes, 19 de octubre de 2012

Utilizar losetup para montar imágenes de dispositivos de bloques

Con las tarjetas de memoria tipo Compact Flash o las MicroSD es frecuente encontrarse que tienen varias particiones y que nos puede resultar díficil acceder a una de sus particiones a partir de un backup realizado sobre la tarjeta de memoria al completo.

Si hacemos un volcado con el comando dd de la partición sdb1 no hay problemas para montar luego la imagen, pero si hacemos el volcado del dispositivo al completo sdb, las opciones normales de mount no nos sirven para acceder a las particiones, impidiendo que podamos montarla. Hemos de recurrir al parámetro offset, dentro la opciones de mount. En este parámetro le pasamos el valor en bytes de donde empieza la partición que vamos a utilizar

Por ese motivo, necesitamos saber la disposición de las particiones (donde empiezan y acaban), es decir, necesitamos saber el mapa de particiones que tenía el dispositivo cuando hicimos el dump (salvar junto con el dump la salida del comando fdisk -l /dev/sdb por ejemplo).

Pero hay una opción mucho mejor, que evita ese paso, utilizando el comando losetup. Losetup nos permite montar un archivo de imagen (obtenido con dd) en un dispositivo /dev/loopX. Luego podemos utilizar el dispositivos loopX como si fuera un /dev/sdXX con el comando mount. La chuleta puede ser algo como esto:

  • Generar una imagen de un dispositivo al completo: dd if=/dev/sdb of=archivo_imagen.dd
  • Para montar en /dev/ hacemos : losetup -f archivo_imagen.dd
  • Para ver donde se ha montado: losetup -a
  • Para ver las particiones: fdisk -l /dev/loop0
  • Para montar la partición segunda: mount -t ext3 /dev/loop0p2 /mnt/particion2

Nota extra: no sólo sirve para memorias Compact Flash, microSD o Pendrive USB, se puede emplear con cualquier dispositivo de bloques (un disco duro por ejemplo).

martes, 11 de septiembre de 2012

Automatizando con Expect

Existen algunas tareas muy tediosas que los administradores de sistemas desearían automatizar para no perder el tiempo en cosas rutinarias. Por fortuna, para los sistemas Unix, nuestro trabajo dispone de varias herramientas que nos facilitan la vida.

Una de estas herramientas es Expect, pero antes de entrar en detalles sobre ella, pintemos el siguiente escenario. Estamos dentro de una organización que dispone de muchos edificios repartidos por toda la geografía del mundo mundial. En cada edificio existen múltiples equipos dedicados a dar soporte de la red (switches, puntos de acceso y routers), que a su vez son de varias fabricantes (HP, cisco, 3com, Dlink). ¿Como hacer que regularmente dispongamos de una copia de seguridad de la configuración de cada uno de estos aparatos? Es en este momento donde Expect levanta la mano y nos da la solución.

Por sí sólo, Expect es una aplicación que sirve para leer la salida de la ejecución de un comando interactivo (por ejemplo telnet/ssh/ftp), la analiza buscando patrones y en función de lo que va encontrando, va mandando mensajes como si delante del telnet/ssh/ftp estuviéramos.

Esto, en algunos casos es posible hacerlo con redirecciones de tuberías desde la shell (<|>) pero en general es insatisfactorio al no poder controlar las excepciones ni hacer tratamientos generales.

La primera diferencia que vemos es que expect cuenta con su propia shell, que tiene un lenguaje de script. Así los scripts de expect tiene como primera línea:
#!/usr/bin/expect
Dentro de las instrucciones del lenguaje, tenemos lo siguiente:
  • Sentencias para la asignación de variables (set).
  • Leer variables desde la línea de comandos (variable $argv).
  • Iniciar la ejecución de un comando como un proceso en (spawn).
  • Esperar la recepción de una determinada cadena desde un proceso (expect).
  • Envío de mensajes al proceso ejecutado con spawn (send).
En el apartado de control de flujo tenemos tenemos:
  • Condicionales (if).
  • Bucles (for, while).
  • Evaluación de expresiones (expr).
También se pueden hacer otras cosas como:
  • Controlar el timeout de la respuesta del comando lanzado con spawn (timeout).
  • Acceder el buffer de la respuesta ($expect_out).
Lo que sigue es un ejemplo para sacar la configuración de los puntos de acceso Cisco Aironet (cisco_aironet.expect). Este script se puede mejorar, en lugar de sacar la configuración en modo texto, podemos obtenerla por tftp, con lo que el tratamiento posterior es más simple, pero como demostración del concepto, nos servirá.
#!/usr/bin/expect -f
set force_conservative 1
if {$force_conservative} {
        set send_slow {1 .1}
        proc send {ignore arg} {
                sleep .1
                exp_send -s -- $arg
        }
}
set timeout 2
puts "\n"
# Asignacion de variables
if $argc<3 {
send_user "$argv0: Faltan variables\n"
exit
}
set host [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
# Abrimos conexion
spawn ssh $user@$host
# Se le pasa la clave
expect {
"*password:" {send -- "$password\r"; }
}
expect {
"\#" {exp_continue}
{put "Error\n"; exit}
}
send -- "show config\r"
expect {
-exac "More" {
send -- " ";
exp_continue
}
}
send -- "quit\r"
Armados con este script de expect podemos pasar a la siguiente parte del hechizo, un script al que llamaremos backup_config.sh que lee de un fichero los datos de los elementos que queremos sacar la configuración y luego lanza el script de expect correspondiente.
#!/bin/bash
PREFIX=path_config
LISTADO=$PREFIX/hosts
while read line
do
        HOST=$(echo $line | cut -d ";" -f 1)
        FICHERO=$(echo $line | cut -d ";" -f 2)
        CLAVE1=$(echo $line | cut -d ";" -f 3)
        CLAVE2=$(echo $line | cut -d ";" -f 4)
        $($FICHERO $HOST $CLAVE1 $CLAVE2)
done < $LISTADO
Este otro script, lee los datos de un fichero llamado hosts.list cuyo contenido puede ser el siguiente. Hay que fijarse que tiene cuatro parámetros por línea y utiliza como separador de parámetros el carácter ";".
catalyst1;expects/cisco_catalyst.expect;param1_1;param1_2
catalyst2;expects/cisco_catalyst.expect;param2_1;param2_2
aironet3;expects/cisco_aironet.expect;param3_1;param3_2
aironet4;expects/cisco_aironet.expect;param4_1;param4_1
El fichero expects para los Switches Cisco Catalyst es trivial sacarlo. Añadir un nuevo elemento del que queremos sacar su configuración es tan simple como crear la entrada correspondiente, indicando su fichero de expect y pasando sus parámetros.

A partir de aquí, lo podemos complicar todo lo queremos (invocarlo desde la crontab, guardar la salida de expect en un log que se mande a un subversión, etc), es asunto nuestro, pero el trabajo importante ya ha sido realizado.


jueves, 30 de agosto de 2012

SSH en paralelo

Cuando alguien tiene que administrar muchos servidores se plantea la pregunta de como hacer esas conexiones múltiples con los nodos para lanzar tareas comunes en todos ellos. Rápidamente viene a la mente terminator, un emulador de terminal que nos deja lanzar conexiones con los servidores que queremos administrar y agrupar estas conexiones, como si de una sola terminal se tratase, haciendo que lo que escribamos en el terminal del grupo, se lleve al resto de conexiones que están en ese grupo.

Pero hay otra herramienta más interesante, pdsh, que nos permite lanzar la ejecución de comandos en los equipos remotos que indiquemos, generando un log del resultado, que puede ser tratado posteriormente para saber como fue la ejecución en cada host. Incluso proporciona herramientas para el tratamiento de este log, agrupando las cosas comunes en el resultado de la ejecución. Por eso pdsh es la solución adecuada para tareas desatendidas, que combinada con el uso de claves publicas/privadas, nos simplifican la vida enormemente.

Lo interesante de pdsh es la posibilidad de indicar los equipos a los que conectarnos utilizando expresiones pseudoregulares o procedentes de la entrada estándar o desde un fichero en la que aparece un host por línea. No queda la cosa aquí, también es posible emplearlo junto a nodeattr, que es una herramienta que permite buscar en un fichero de texto que hace las funciones de una base de datos en el que guardamos nombres de hosts junto con atributos (tipo de hosts, red, tipo de conexión).

Ejemplos de uso:
$ pdsh -w 10.10.10.[2-9] -R ssh "find /usr/local/bin -iname hola* -exec ls -al {} \; "
$ pdsh -w ^hosts.pdsh -R ssh "find /usr/local/bin -iname hola* -exec ls -al {} \; "
el fichero hosts.pdsh contiene un nombre de servidor por línea. De todas formas, el man de pdsh es bastante completo, con lo que veo innecesario extenderme más.

Por último, hay otra posibilidad, que desconocía y que localice en un blog altamente recomendable Seguridad en Sistema y Tecnicas de Hacking, es utilizar fanout y fanterm. Aunque creo que pdsh trabajando conjuntamente con nodeattr, cubre la mayor parte de los escenarios en los que nos encontremos.

miércoles, 29 de agosto de 2012

Hablemos de la magia

Dice mi socio que esto no es más que otro aburrido blog sobre la tecnología, pero yo le llevo la contraria. Como dice Arthur C. Clarke, cualquier tecnología lo suficientemente avanzada es indistinguible de la magia, por eso nosotros no hablamos de cualquier tecnología, no señor, nosotros hablamos sobre la magia. Es una magia moderna, que fluye por los circuitos integrados de casi cualquier aparatejo que nos rodea. Y por fuerza, esta magia nos fascina y nos atrapa como moscas en miel.

Seguro que como declaración de intenciones quedará algo pretencioso, es lo que quería.

martes, 28 de agosto de 2012

¡Empezamos!

Sí, aunque no te lo puedas creer, sí ... ¡otro aburrido blog sobre tecnología!

Pero es que llevamos mucho tiempo enviándonos correos para aclararnos dudas y adaptando soluciones que hemos encontrado en la web a muchas de las necesidades que tenemos.

¿¿Qué quienes las tenemos?? Al parecer mucha gente, porque tengo el correo, el talk y el whatsapp lleno de miles de preguntitas. Esas preguntitas tienen un 'brillo' particular, ¡y claro!, como cuervo atraído por el brillo de los objetos, dejo de hacer cualquier tarea que tenga entre manos para descubrir que hay en esas preguntitas, inocentes y sencillas ... que al final devienen, en muchos casos, en grandes espacios de tiempo consumidos hasta encontrar la solución. Sí, procrastinación en estado puro.

Y en el camino, suelo engañar a mi compañero de viaje y embarcarlo en aquellas tareas en las que suelen ser más innecesarias su presencia y habilidades. Pero él, que también es un procrastinador nato, suele dejarse llevar y ¡zas! atrapado por el brillo tecnológico, pica el anzuelo e invierte otras tantas horas.

¿Y por qué un blog? ¡Uff! Después de mucho pensarlo (aproximadamente 5 min.) nos decantamos por este formato porque nos permite atender a uno de nuestros grandes problemas: la memoria de pez. Olvidamos rápidamente casi todo lo que aprendemos, aunque recordamos fugazmente que lo hemos resuelto en algún momento ... así que parecía necesario tener un lugar de conocimiento público, de fácil acceso, que pudiéramos consultar para seguir descubriendo y aclarando las preguntitas que tanto nos gusta hacer.

¿Compromisos? Todos: publicación periódica, respuestas elaboradas con gran detalle técnico pero a la vez amenas, atención permanente a los comentarios ... Pero seamos sinceros: un blog creado en agosto por dos procrastinadores ... ¡tiene mérito si duramos 100 días! Así que hemos creado una alarma en nuestro calendario ... y dentro de 100 días, ¡nuestra próxima publicación! Para que esto no decaiga, ¡hoygan!

Saludos a todos y a todas y feliz lectura ;)

Imagen obtenida de flickr.com, bajo licencia CC