Escritura de módulos
Objetivos del capítulo y requisitos
El capítulo anterior versó sobre el uso de los playbooks y de los roles en Ansible. En este capítulo verá que a veces es necesario escribir módulos para gestionar algunos tipos de objetos ya que a través de playbooks sería más complicado trabajar con ellos.
1. Contexto y requisitos
La escritura de módulos Ansible necesita algunos conocimientos sobre Python. No obstante, los ejemplos deberían ser accesibles para todas las personas con nociones de programación en cualquier lenguaje.
En el mismo módulo, habrá bibliotecas a disposición del usuario así como las diferentes ejecuciones que se tengan que realizar o, también, la gestión de parámetros. Igualmente habrá una presentación de los mecanismos de ayuda.
Verá algunos problemas como la compatibilidad entre Python 2 y 3 o cómo mostrar información al usuario de manera eficaz sobre las modificaciones que se han realizado en el sistema.
2. Archivos descargables
Puede encontrar los ejemplos de los directorios inventarios y variables en el archivo comprimido capitulo-12.tar.gz que se encuentra en la página del libro en el sitio de Ediciones ENI.
Mecanismo de ejecución de los módulos Python
1. Principio de funcionamiento
Ya sabe que Ansible necesita un medio de comunicación para poder funcionar (SSH o un lanzamiento en local, por ejemplo) y un intérprete de Python (excepto para algunos módulos como raw o fetch).
Ansible se encarga de gestionar la escalada de privilegios (sudo, su, etc.) para poder ejecutar el programa.
Este miniprograma seguirá las instrucciones que se encuentran en un módulo y las enviará a Ansible bajo la forma de un diccionario además de su código de retorno (si la salida es 0, todo va bien, si no, hay un problema en la ejecución del programa).
De esta manera, cada operación se traduce en la creación de un contexto de ejecución en un directorio temporal en la máquina administrada. Este capítulo le presentará las diferentes etapas que realiza Ansible. Descubrirá cómo está construido este miniprograma autosuficiente.
2. Activación de los registros de ejecución
Un buen método para poder ver todas las operaciones preliminares es lanzando Ansible con la opción -vvv. En las líneas siguientes, encontrará un ejemplo sencillo de comprobación de la presencia del usuario root. En modo ad hoc, esta operación se haría de la manera siguiente:
$ ansible -m user -a "user=root state=present" localhost -vvv
Estas indicaciones solo son funcionales en el caso en que Mitogen no esté activo. Si no está desactivado, desactívelo.
La opción -vvv nos muestra este resultado...
¿Por qué escribir un módulo?
1. Contexto
En los capítulos anteriores usted ejecutó playbooks y roles para gestionar sus instalaciones. En algunos casos, este mecanismo no es suficiente y puede traer problemas cuando los objetos que tendrá que gestionar sean complejos.
Imagine que quiere consultar el contenido de una base de datos. Con un playbook/role, esta gestión se haría de la manera siguiente:
-
Creación de un archivo de SQL;
-
Ejecución de la acción con una aplicación SQL.
Este método conlleva algunos problemas:
-
El archivo de script se queda en la máquina.
-
Se puede suprimir pero, en ese caso, se añade una operación más.
-
Se pierde información porque solamente se obtiene el retorno de la solicitud. Pierde todos los metadatos (nombres de campos, etc.).
Para entender esto, he aquí un ejemplo contando el número de tablas en el esquema mediawiki.
2. Consulta del esquema
El comando que tendrá que ejecutar para conocer el número de tablas de un esquema MySQL será:
SELECT
COUNT(*)
FROM
information_schema.tables
WHERE
table_schema = 'mediawiki';
Antes de poder ejecutarlo, tendrá que depositar ese script bajo el nombre de /tmp/test.sql.
Para conectarse a una base de datos MySQL/MariaDB, puede usar el comando mysql. Para evitar...
Creación de un módulo
Ha visto cómo se puede recuperar esta información a través de un playbook y las llamadas shell. Ahora hará la misma operación con un módulo. Verá cuáles son las ventajas.
1. Ubicación del programa
El módulo que creará se llamará mysql_request. Su rol será el de tomar como argumento el nombre del esquema de la base de datos MySQL y lanzar una solicitud SQL arbitraria.
Guardará el programa bajo el nombre mysql_request.py (la extensión «.py» no es obligatoria, pero es aconsejable por razones de comprensión).
Ahora lo tendrá que poner a disposición de Ansible. Para ello, se le ofrecen distintas soluciones:
-
Crear un directorio library en el mismo nivel que los playbooks que ejecutarán este módulo.
-
Depositar el archivo en un directorio usado por Ansible.
Los directorios usados por Ansible se pueden definir usando:
-
el campo library del archivo de configuración de Ansible;
-
la variable de entorno ANSIBLE_LIBRARY.
Como puede constatar, tiene distintas soluciones a su disposición. Intente favorecer el uso del directorio library. Esto le evitará hacer modificaciones en el entorno de trabajo.
2. Encabezado del programa
En el módulo, comience por el encabezado de Python siguiente:
#!/usr/bin/python
# -*- coding: utf-8 -*-
3. Especificación de los argumentos del módulo
Antes de empezar a trabajar, indique a Ansible la especificación de los argumentos del módulo. Esta se presenta bajo la forma de un diccionario. Cada argumento será, también, un diccionario en el que podrá usar los campos siguientes:
-
la obligación, o no, de declarar el campo con required (False por defecto);
-
el tipo de argumento con type (str por defecto);
-
los valores por defecto con el campo default (None por defecto);
-
una lista de valores válidos con choices;
-
alias para los campos con aliases.
Aquí encontrará un ejemplo de declaración:
argument_spec = dict(
people = dict(type='str'),
are = dict(required=True),
strange = dict(required=False, default=False, type='bool'),
when = dict(type='int', aliases=['you','are','a','stranger']) ...
Reentrada en la creación de un esquema de base
1. Contexto
Ha visto un primer módulo Ansible que le ha permitido familiarizarse con esta noción. Sin embargo, ha dejado de lado algunos aspectos como la gestión de la reentrada.
Otro detalle: el módulo que ha creado es «peligroso» en el sentido de que lanzará comandos sin comprobarlos antes. No olvide tampoco que solo es compatible con un tipo de base de datos: MySQL. Si tuviera que gestionar una base de datos Postgres, tendría que crear otro.
Finalmente, no podrá ayudarle en el problema típico que podría encontrar en una base de datos: la gestión de la actualización del esquema.
La gestión de un esquema de base de datos es un mundo aparte. Los ejemplos aquí expuestos servirán de ilustración de lo que puede ser la reentrada aplicada a un esquema sencillo de base de datos. Para esquemas más complejos, existen otras herramientas de migración como, por ejemplo, Alembic, Liquibase o incluso Flyway.
Vea cómo gestionar esta reentrada. Para simplificar al máximo la escritura de este módulo usará una pequeña biblioteca para la gestión del contenido de la base: dataset.
Se trata de una pequeña biblioteca Python que gestiona de manera totalmente transparente la obtención de la información de un esquema (tablas y columnas). También gestiona nativamente distintos tipos de bases de datos (Postgres, MySQL pero también SQLite).
Para más detalles sobre las posibilidades de esta biblioteca, puede consultar directamente el sitio web: https://dataset.readthedocs.io/en/latest/
2. Limitaciones del módulo
Como se ha mencionado anteriormente, no se trata de definir un módulo que permita gestionar todos los casos posibles. Se limitará a las funciones siguientes:
-
creación automática de las tablas;
-
incorporación de nuevos campos en las tablas;
-
todos los campos serán de un solo tipo: string.
Tenga en cuenta que la biblioteca dataset añade automáticamente el campo id cuando se crea una tabla.
Tampoco tomará en cuenta (como no lo ha hecho anteriormente) todos los metadatos del módulo (ayudas, ejemplos, etc.).
Finalmente, en el playbook de puesta a punto usará una base de tipo SQLite. Esta elección tendrá...
Gestión de la opción diff de Ansible
1. Contexto
Para asegurarse de que las operaciones han sido realizadas por Ansible, ha debido usar un comando externo.
Ahora verá cómo gestionar la opción --diff y hacer que Ansible le muestre las indicaciones de las operaciones realizadas. Esta opción se especificará a Ansible en consola cuando se lance.
Con esta opción, y en el caso de que muestre ciertos elementos a Ansible, este podrá enviarle las modificaciones realizadas durante el lanzamiento del módulo.
2. Principio de funcionamiento de diff
Como puede imaginar, la opción --diff, en este caso, no le mostrará ningún cambio. Para poder gestionar esta opción, hay que añadir nuevas variables en la llamada a module.exit_json.
Si usa la opción --diff, tendrá que enviar la variable diff con los campos before y after. El motor Ansible se ocupará de hacer la comparación entre los dos campos antes de enviarlos al usuario.
Esa es la explicación del mecanismo. Ahora solamente le queda guardar el estado del sistema antes y después de la modificación en esas dos variables. En este caso, las dos variables contendrán dos tablas cada una:
-
una tabla tables con la lista de las tablas;
-
una tabla columns con la lista de las columnas.
Como no sabe a qué tabla está vinculada una columna solo con su nombre, las enviará...
Gestión de la opción test (check mode)
1. Activación del check mode
Para terminar con la puesta a punto de este módulo, añadirá la gestión de un último mecanismo: la gestión de la opción test (o check mode). Este modo se activa usando la opción --check (o -C si usa la versión corta).
Por defecto, si no especifica nada, Ansible no ejecutará el módulo. De esta manera, si vuelve a tomar la ejecución del playbook anterior y si quiere ejecutarlo en modo test, debería usar el siguiente comando:
$ ansible-playbook db_schema.yml --check
He aquí el resultado del comando:
PLAY [Create schema] *******************************************
TASK [file] ****************************************************
ok: [localhost]
TASK [Create schema] *******************************************
skipping: [localhost]
TASK [Update schema] *******************************************
skipping: [localhost]
TASK [No modification] *****************************************
skipping: [localhost]
PLAY RECAP *****************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
Como ha podido ver, todas las operaciones con el módulo db_schema han sido ignoradas....