+++ title = "Programación literaria para sysadmins / devops" author = ["drymer"] date = 2016-10-11T13:02:00+02:00 tags = ["sysadmin", "orgmode", "emacs", "bash", "python"] draft = false url = "/posts/programacion-literaria-para-sysadminsdevops/" +++ Mencioné de pasada el concepto de programación literaria en el articulo de mi configuración de emacs, ahora voy a explicar en que consiste este y cómo usarlo con emacs y org-mode. La [definición de la wikipedia](https://es.wikipedia.org/wiki/Literate_programming) al rescate. > El paradigma de programación literaria [...] permite a los programadores desarrollar sus programas en el orden fiado por la lógica y el flujo de sus pensamientos. > > Los programas literarios están escritos como una exposición lógica no interrumpida en un lenguaje humano, de forma similar al texto de un ensayo, en el cual se incluye el código fuente tradicional oculto tras macros. Las herramientas de programación se encargan de separar el programa de forma que pueda ser compilado y ejecutado y la documentación del mismo programa. Mientras que las primera generación de herramientas de programación literaria estaban centradas en un lenguaje de programación específico, las últimas son independientes de lenguaje y se sitúan por encima de los lenguajes de programación. Dicho de otro modo y enfocado al uso que le puedan dar las sysadmins, permite documentar los pensamientos y procesos lógicos y ejecutar estos en el mismo contexto. Puede ser un poco complejo de entender si no se ve, por eso, y aún odiando el formato, he hecho una pequeña grabación de lo que puede hacer con ello. El caso concreto que presento es un ejemplo real con el que me he encontrado recientemente, que es cambiar lo codificación del contenido de una columna de una base de datos sqlite en una máquina remota. El programa en si da igual, pero creo que es un buen modo de ver el potencial que tiene. El vídeo a continuación. Si, está acelerado.
Ahora explicaré algunos conceptos básicos y cómo configurar emacs para lograr lo anterior. Para dejarlo claro, usaré `emacs`, `org-mode` y los bloques babel de este. Los bloques babel permiten que varios lenguajes de programación vivan de forma natural en `org-mode`. Permite que estos se ejecuten, se compilen, se documenten y se creen los ficheros que se programen a parte, todo desde el mismo sitio. Que ventajas ofrece `org-mode`? - Mejor documentación del código. - Útil para compartir con otra gente, si se tiene algún problema. - Clarificación de los pensamientos en situaciones complicadas. - Facilidad para mezclar distintos lenguajes de programación. - Las funcionalidades de la agenda y las tareas. ## Configuración {#configuración} Se puede usar tanto emacs 24.5 como 25.1, hasta dónde he probado. La versión de org-mode que he probado es la rama `maint` del git, que en este momento es la versión 8.3.6. Teniendo esto claro, vamos a configurar babel. La variable que hay que tener en cuenta es `org-babel-load-languages`, que es dónde se definirá que lenguajes permitirá usar. Las posibilidades que hay se pueden ver en el [siguiente enlace](http://orgmode.org/worg/org-contrib/babel/languages.html). Para la demostración solo he usado `python` y `shell`, que permite el uso de `bash`. La variable se define así: ```lisp (org-babel-do-load-languages 'org-babel-load-languages '((dot . t) (lisp . t) (gnuplot . t) (latex . t) (ledger . t) (python . t) (shell . t) (sh . t) (sql . t) (sqlite . t))) ``` Con esto ya podemos funcionar en lo que a `org-mode` se refiere. Ahora varios conseos para el tema del `ssh` y `tramp`, que es el programa que usa emacs para gestionar las conexiones por `ssh`. Lo primero de todo es que lo más cómodo siempre será poder loguearse en el servidor con la clave pública. Lo segundo es modificar la variable PS1 del servidor en cuestión para facilitarle el parseo da tramp. Es la forma más sencilla que he encontrado de hacer que funcione sin más modificaciones. En el `~/.bashrc` del servidor se añade lo siguiente: ```sh [[ $TERM == "dumb" ]] && PS1='$ ' [[ $TERM == "emacs" ]] && PS1='$ ' ``` Se define la variable PS1 en función del TERM que se use. Me he encontrado con que tramp ha usado ambos TERM, según si lo uso en un bloque de babel o a pelo, así que vale la pena poner ambos. Tramp usa `scp` por defecto. Yo prefiero usar `ssh`, por que soy de darle a guardar cada 3 segundos y con `scp` tarda más. ```lisp (setq tramp-default-method "ssh") ``` Con esto deberíamos poder funcionar correctamente. Ahora miremos un poco el código del archivo del vídeo. La fuente completa está aquí, por si alguien la quiere. ## Conceptos básicos de org-mode y babel {#conceptos-básicos-de-org-mode-y-babel} A continuación veremos los parámetros y variables que uso en el archivo de la demostración, que seguramente sean los más relevantes en el ámbito de administración de sistemas. El fichero de la demostración está [aquí](https://daemons.cf/wp-content/sysadmin.org). Aún así, hay muchísimas más y no puedo cubrirlas todos. Al final del articulo pondré varias fuentes y otros recursos que recomiendo leer. Están en inglés, por eso. Como siempre que se usa `org-mode`, usamos los árboles, es decir, los asteriscos. De este modo se pueden definir varias propiedades, como la de `header-args`. Pero antes, veamos un bloque babel normal y corriente. Para ejecutar el bloque, hay que presionar `C-c C-c` en este. ```text #+BEGIN_SRC sh ls ~/ #+END_SRC #+RESULTS: | Descargas | | Documentos | | Imagenes | | Instalados | | Scripts | | Videos | ``` Esto devuelve en mi caso. El resultado por defecto lo devuelve en forma de tabla. Si lo queremos tal cómo devuelve la terminal, podemos pasarle la variable `results raw` al bloque. ```text #+BEGIN_SRC sh :results raw ls ~/ #+END_SRC #+RESULTS: Descargas Documentos Imagenes Instalados Scripts Videos ``` Otra variable útil que se usa es `dir`. Permite establecer en que directorio se ejecutará el bloque. ```text #+BEGIN_SRC sh :dir / ls #+END_SRC #+RESULTS: bin boot dev etc home la lib lost+found media mnt opt proc root run sbin srv sys tmp usr var ``` Esto es más útil de lo que pueda parecer a primera vista, por que como tal vez os habéis fiado en el vídeo, permite establecer que todos los bloques se ejecuten en el servidor, sin más. Para ello hay que usar la sintaxis de tramp, que seria tal que así en mi caso (en el ejemplo, igual que en el vídeo, me conecto a mi banana pi). ```text #+BEGIN_SRC sh :dir /ssh:drymer@banana: ls #+END_SRC #+RESULTS: backups instalados owncloud_data scripts decrypted owncloud_data_backup ``` Si esto lo unimos a la variable `header-args` que mencioné antes, que se puede definir en las propiedades, todo lo que se ejecute debajo del primer árbol se ejecutará en la máquina remota. Algunos bloques tienen definido encima un parámetro, `#+name`, seguido de un nombre. Esto permite usar los bloques como funciones, como en el caso del bloque de python de la demostración. También se pueden definir variables vacías para que puedan ser usadas dentro de los bloques. ```text #+NAME: decode_string #+BEGIN_SRC python :results output :var linea="" print(linea.encode('latin1').decode('utf8')) #+END_SRC ``` Esta función devuelve el valor de la variable `linea` codificada en utf-8 cuando se llama desde fuera. ```text #+BEGIN_SRC sh :results raw :post decode_string(linea=*this*) echo "Un+par+de+funciones+útiles" #+END_SRC #+RESULTS: Un+par+de+funciones+útiles ``` En el ejemplo esta función se usa de un modo particular, ya que se llama con el parámetro `post`. Este está pensado para procesar la salida del bloque en bash con el bloque en python. También se puede llamar a un bloque desde otro bloque y establecer el resultado a una variable a usar en el segundo, como se ve aquí. ```text #+NAME: num #+BEGIN_SRC sh echo $(sqlite3 -line /var/lib/isso/comments.db "select * from archive;" | grep title | wc -l) #+END_SRC #+NAME: encoded_title #+BEGIN_SRC bash :results output :post decode_string(linea=*this*) :var NUM=num for ((i=1;i<=$NUM;i++)) do echo $i$(sqlite3 -line /var/lib/isso/comments.db "select title from archive where id = $i;" | cut -d'=' -f2)";" done #+END_SRC ``` El bloque `encoded_title` llama al bloque `num` estableciendo la salida de este a la variable `NUM`, que en bucle `for` usa como final de este. Por último mencionar la exportación de los bloques a otros formatos. Quienes usen `org-mode` ya sabrán que una de sus grandes cualidades es que permite exportar este lenguaje de marcado a otros formatos, tales como latex, html, pdf, odt, etc. Los bloques van incluidos en el paquete y pueden ser gestionados con la variable `:exports`, pasando como valor `none`, `code`, `results` o `both`, que es lo que se usa por defecto. `none` no exporta nada, `code` solo el código, `results` el resultado y `both` ambos. Y esto es todo por ahora. Tal vez en el futuro escriba otro articulo más, si llego a controlar más del tema. Por si acaso, y mientras tanto, recomiendo la lectura de los siguientes enlaces, en los que se encuentran varios artículos de Howard Abrahams, del cual yo descubrí recientemente todo el potencial de la programación literaria y recomiendo encarecidamente a quien sepa leer en inglés. - [http://www.howardism.org/Technical/Emacs/literate-programming-tutorial.html](http://www.howardism.org/Technical/Emacs/literate-programming-tutorial.html) - [http://www.howardism.org/Technical/Emacs/literate-devops.html](http://www.howardism.org/Technical/Emacs/literate-devops.html) - [http://www.howardism.org/Technical/Emacs/literate-devops-examples.html](http://www.howardism.org/Technical/Emacs/literate-devops-examples.html) - [http://orgmode.org/manual/Working-with-source-code.html#Working-with-source-code](http://orgmode.org/manual/Working-with-source-code.html#Working-with-source-code)