Python para administradores de sistemas

De WikiSalud
Revisión a fecha de 11:34 17 mayo 2016; Alortiz (Discusión | contribuciones)

(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Saltar a: navegación, buscar
Commons-emblem-Under construction-green
Este artículo está en una fase temprana de desarrollo, por lo que puede estar incompleto o tener aún errores de redacción o formato

Contenido

Introducción

El siguiente artículo esta compuesto de algunas notas que me gustaría guardar sobre la programación de script en python. Para hacerlo más interesante, usaremos python3, versión cuya migración de módulos desde python2 parece estar bastante adelantada

Breve introducción a python3

Python3 trajo consigo muchas mejoras de las que se hicieron eco en sus días. El que print sea ahora una función (Pese al trabajo de acostumbrarse a ello) y que no haya que forzar la conversión cuando se realizan operaciones con enteros que devuelven decimales (3/2), quizá sean las más conocidas por ser cambios en la forma de usar el modo interactivo, pero son sólo una parte de profundas mejoras en el diseño, limpieza en los módulos y rendimiento. Y ahora que maneja las cadenas de texto como Unicode, ya no será necesarios encabezar los script con #coding: utf-8

¿Como manejar los script python?

Python sigue la tradición de la mayoría de lenguajes de script. Puede ejecutarlo pasándolo como argumento al interprete, o configurarle permisos de ejecución y especificar como encabezado el interprete a usar. Lo recomendable es siempre colocar un encabezado, una solo línea no cuesta nada.

#!/usr/bin/python3
 chmod +x alfa.py 

Tambien puede saltarse estas convenciones por el momento y usar el modo interactivo del interprete con solo tipear python3 en la consola

Manejando ficheros con python

La que quizá sea una de las cuestiones más habituales a la hora de escribir un script. La función interna open es bastante sencilla de usar:

 fichero = open("/etc/passwd") 

Ahora tenemos un objeto File en modo lectura, a cuyo contenido se recomienda acceder mediante for:

 
   for linea in fichero:
       print(linea)

En administración de sistemas, suele accederse a ficheros con el único fin de poder manipular su contenido, en su mayoría, lineas independientes que guardan cierta regularidad entre ellas. En este caso, vemos que las entradas en el fichero que guarda los usuarios locales en un sistema como Debian separa los datos mediante :. Pues bien, separemos cada atributo manipulando cada linea como la cadena de texto que es con split:

 
   for linea in fichero:
       linea.split(":")

Ejecutando lo anterior, saca en pantalla las listas en las que ha separado cada linea. Necesitamos imprimir un elemento del dicha lista en que lo hemos convertido:

 
for linea in fichero:
    print(linea.split(":")[0])

Python soporta técnicas de slice con cuando se trata de listas. Las posibilidades son infinitas y permiten hacer código muy legible y corto. Por ejemplo, necesitamos tan sólo consola y directorio home de los usuarios:

 
for linea in fichero:
    print(linea.split(":")[-2:])

El [-2] indica a los dos últimos índices de la lista, sin conocer su longitud real. Recordando que al igual que en otros lenguajes de programación, las listas empiezan en cero, otros slice interesante pueden ser:

  • linea.split(":")[0:3] Devuelve hasta el índice 3
  • linea.split(":")[0:-3] Devuelve hasta el índice 3, incluyendo el índice
  • Y claro, puede buscarse un índice específico con linea.split(":")[1], pero eso es aburrido

Lo cierto es que una salida como ['/home/fvelis', '/bin/bash'] no tiene mucho sentido. Usaremos el método format para cadenas de texto y precisamente formatearemos la salida en algo más legible.

 
salida = "Directorio {0} y consola {1}".format

Ahora salida es una función que recibe dos parámetros que ha de usar para construir una cadena de la forma indicada

 
salida = "Directorio {0} y consola {1}".format
for linea in fichero:
    salida(*linea.split(":")[-2:])

El * expande la lista que resulta del slice para que la función reciba los dos elementos como dos parámetros.

Siguiendo con el tema del sentido de la salida devuelta, es obvio que no nos dice mucho a menos que agreguemos el nombre del usuario (Especia atención a como combinamos argumentos normales y argumentos listas):

 
salida = "Usuario {0} con directorio {1} y consola {2}".format
for linea in fichero:
    salida(linea.split(":")[0],*linea.split(":")[-2:]) 

Parece obvio que el siguiente paso es legibilizarlo (El que sea un script no nos exime de hacer código decente)

 
salida = "Usuario {0} con directorio {1} y consola {2}".format
for linea in fichero:
    datos = linea.split(":")
    print(salida(datos[0], *datos[-2:]))

Vayamos un paso más adelante y usemos un simple filtro para aquellos usuarios normales (Aquellos cuyo uid sea igual o mayor a 1000), y de paso recordamos la estructura if de python:

 
salida = "Usuario {0} con directorio {1} y consola {2}".format
for linea in fichero:
    datos = linea.split(":")
    if int(datos[3]) >= 1000:
        print(salida(datos[0], *datos[-2:]))

int() Hace una conversión explícita para evaluarlo su relación númerica

Manejando ficheros JSON

Con lo divertido que puede ser el tener que parsear un fichero por nuestra cuenta, es posible analizar uno con una estructura JSON válida en unos cuantos pasos haciendo uso de la librería json, que es parte de la librería estándar:


>>> from json import load
>>> fichero = open('datos.json')
>>> data = load(fichero_json)

En este punto, data contiene una estructura propia de python. fichero ya no puede leerse porque al ser parseado por load el cursor que indica la dirección actual en la que nos encontramos del archivo ha llegado al final del mismo. Podemos comprobar cual es la conversión que python hizo del contenido del fichero de la siguiente forma:

>>> type(data)
<type 'list'>

Por tanto es un objeto iterable:

>>> for i in data:
...   print i
... 
{u'localidad': u'DTIC', u'nombre': u'Alexander', u'aplicaciones': [u'Servidor de Correo Electr\xf3nico',  u'Servidor de Autenticaci\xf3n'], u'apellido': u'Ortiz'}
{u'localidad': u'DTIC', u'nombre': u'Olga', u'aplicaciones': [u'Servidor de Correo Electr\xf3nico',  u'Servidor de Seguridad Perimetral'], u'apellido': u'Pineda'}
{u'localidad': u'DTIC', u'nombre': u'Rosa', u'aplicaciones': [u'Servidor de Aplicaciones', u'Servidor de  Seguridad Perimetral'], u'apellido': u'Adela'}

El que data sea un tipo válido y corriente en python significa como con cualquier otra estructura de datos. Así por ejemplo, podemos añadir otro usuario siguiendo la estructura de vista:

>>> usuario = {'nombre':'Elvira','apellido':'Peña','localidad':'DTIC', 'aplicaciones':['Servidor de Aplicaciones','Servidor de Correo Electrónico']}
>>> data.append(usuario)

Y luego, una vez modificado, podemos volver a guardar los datos en el fichero, sobreescribiendo el fichero con el nuevo contenido:

>>> fichero_json = open('datos.json','w+')
>>> dump(data, fichero_json)

Expresiones regulares

Las expresiones regulares son una gran herramienta a la hora de analizar grandes cantidades de datos, permitiendo filtrar cadenas según especificaciones que pueden ser tan simple como queramos o tan complicadas como podamos, para lo cual podría guiarse en Expresiones Regulares para Administradores, que si bien no es una guía completa podría iluminar algunas cosas.

La capacidad de usar expresiones regulares en python se encuentra en la librería estándar re. La forma más sencilla de usarla es la siguiente

>>> from re import match
>>> print "Coincidencia" if match('^figaro$', 'nofigaro') else "No hay coincidencia"
No hay coincidencia

Sin embargo, a la hora de analizar varias cadenas con la misma expresión (^figaro$), lo mejor será compilar dicha expresión en busca de mejorar el rendimiento:

Para verlo actuar en una lista:

>>> lista_texto = ['nofigaro', 'figaro', 'Figaro', 'estudiante', 'figarosi']
>>> for texto in lista_texto:
...   mensaje = "Coincidencia en {0}".format if expresion.match(texto) else "No hay coincidencia para  {0}".format
...   mensaje(texto)
... 
'No hay coincidencia para nofigaro'
'Coincidencia en figaro'
'No hay coincidencia para Figaro'
'No hay coincidencia para estudiante'
'No hay coincidencia para figarosi'
>>>

Por cierto, el código anterior introduce de golpe el operador ternario para python y la capacidad de crear texto formateado. Básicamente, el script anterior se corresponde con el siguiente:

>>> for texto in lista_texto:
...   if expresion.match(texto):
...     print "Coincidencia en " + texto
...   else:
...     print "No hay coincidencia para " + texto
... 
No hay coincidencia para nofigaro
Coincidencia en figaro
No hay coincidencia para Figaro
No hay coincidencia para estudiante
No hay coincidencia para figarosi

La extracción de texto mediante expresiones regulares es otra posibilidad con python:

>>> texto = u"Escuelas públicas se encuentran en terrenos de alto riesgo debido a la persistente lluvia\nEscuelas privadas en riesgo debido a torrencial lluvia" >>> expresion = re.compile('(\w+\s)lluvia') >>> expresion.findall(texto) [u'persistente ', u'torrencial ']

Luego, es posible usar grupos con nombre para extraccción:

>>> texto = u"Escuelas públicas se encuentran en terrenos de alto riesgo debido a la persistente lluvia\nEscuelas privadas en riesgo debido a torrencial lluvia"

Subprocess

¿Fabric?

Herramientas personales
Espacios de nombres

Variantes
Acciones
Navegación
Herramientas