Publicaciones sobre sysadmin

2009-12-23 15:32

Con iterpipes, python puede reemplacar a bash para scripting. En serio.

Esto me molesta hace mucho: programar scripts de shell es horrible. Son feos y llenos de errores. ¿El único motivo por el cual se sigue haciendo? Porque no hay alternativa.

O al menos así era hasta que hoy me encontré con iterpipes en python.reddit.com

Iterpipes es "una biblioteca para usar pipelines de shell usando sintaxis parecida al shell" y ¿sabés qué? Es brillante.

Acá hay un ejemplo de su página de PYPI:

# Total lines in *.py files under /path/to/dir,
# use safe shell parameters formatting:

>>> total = cmd(
...     'find {} -name {} -print0 | xargs -0 wc -l | tail -1 | awk {}',
...     '/path/to/dir', '\*.py', '{print $1}')
>>> run(total | strip() | join | int)
315

Así sería en shell:

find /path/to/dir -name '*.py' -print0 | xargs -0 wc -l | tail -1 | awk '{print $1}'

Tal vez digas que la versión de shell es más sencilla. Eso es una ilusión causada por la densidad del campo malico del shell: esa versión tiene bugs.

Porqué tiene bugs? Porque si controlo qué hay adentro de /path/to/dir puedo hacer que falle [1], pero en python puedo manejar el error.

También en la mayoría de los intentos de escribir ese comando sería inseguro porque las reglas de comillas y caracteres de escape de shell son dementes.

La versión de iterpipes usa el equivalente de prepared statements de SQL que debería ser más seguro.

Es casi imposible hacer ese comando en puro shell y estar seguro de que es seguro.

Además la versión en shell produce un string en vez de un entero, que es una bazofia si lo necesitás usar para algo.

Y el beneficio más importante es, por supuesto, no cuando tratás de hacer que python funcione como un shell, sino cuando podés dejar de hacer como que el shell es un lenguaje de verdad.

Comsideremos esta gema del script /etc/rc.shutdown en Arch Linux. DAEMONS es una lista de cosas que se arrancaron al inicio, y este script intenta detenerlas en orden inverso, a menos que el nombre empiece con "!":

# Shutdown daemons in reverse order
let i=${#DAEMONS[@]}-1
while [ $i -ge 0 ]; do
        if [ "${DAEMONS[$i]:0:1}" != '!' ]; then
                ck_daemon ${DAEMONS[$i]#@} || stop_daemon ${DAEMONS[$i]#@}
        fi
        let i=i-1
done

¿No es lindo?

¿Y cómo se vería en python? (tal vez haya invertido el significado de ck_daemon):

# Shutdown daemons in reverse order
for daemon in reversed(DAEMONS):
    if daemon[0]=='!':
        continue
    if ck_daemon(daemon):
        stop_daemon(daemon)

Mientras que stop_daemon solía ser esto:

stop_daemon() {
    /etc/rc.d/$1 stop
}

Ahora será esto:

.. code-block:: python
def stop_daemon(daemon):
run(cmd('/etc/rc.d/{} stop',daemon))

Vamos gente, estamos en pleno siglo 21, y el shell scripting ya era feo en el 20.

[1] Ejercicio para el lector.

2009-12-18 00:37

Migrando de Haloscan a Disqus (si podés comentar, funcionó ;-)

Introducción

Si sos usuario de Haloscan, y estás empezando a preguntarte que hacer... esta página te va a explicar una forma de llevarte tus comentarios a Disqus, otro servicio de comentarios para blogs gratuito.

Hace unos pocos días Haloscan anunció el fin de su servicio de comentarios gratuitos para blogs. Adiviná en qué servicio tengo 9 años de comentarios? Sí, en Haloscan.

Ofrecen una migración sencilla a su plataforma Echo, que es un servicio pago. Si bien Echo parece una plataforma de comentarios perfectamente digna, no tengo ganas de gastar dinero en este bloh si puedo evitarlo. Ya bastante le doy tiempo!

Por suerte, los muchachos de Haloscan permiten exportar los comentarios (antes había que pagar para eso), así que gracias Haloscan, fué un gusto!

Entonces empecé a investigar a donde me podía escapar. Parece haber dos sistemas grandes y gratuitos de comentarios:

Téngase en mente que principal interés es no perder mis comentarios, no la calidad del servicio. Habiendo dicho eso, los dos parecen ofrecer más o menos las mismas características.

Consideremos como importar comentarios en cada servicio:

  • Disqus: Puede importar de blogger y algún otro servicio hosteado. No de Haloscan.
  • Intense Debate: Puede importar algunos servicios hosteados y algunos archivos. No el que Haloscan me dió.

Entonce que hago? Escribir un programa en Python, por supuesto! Ahí ganó Disqus: tienen un API pública para los comentarios.

Entonces, todo lo que hay que hacer es:

  1. Entender el API de Disqus
  2. Entender los comentarios de Haloscan (es XML)
  3. Crear los hilos necesarios en Disqus
  4. Postear los comentarios de Haloscan a Disqus
  5. Arreglar el blog para que los links a Haloscan funcionen con Disqus

Pan comido. Me llevó medio día, lo que a mi tarifa actual es el equivalente de 3 años de Echo, pero que gracia tendría pagar?

Así que, vamos paso por paso.

1. Entender el API de Disqus

Por suerte hay una biblioteca razonable: Disqus Python Client library y docs para la API así que esto no fué tan difícil.

Instale la biblioteca:

hg clone https://[email protected]/IanLewis/disqus-python-client/
cd disqus-python-client
python setup.py install

El uso que vamos a darle al API es sencillo, así que si hace falta, lee la documentación 15 minutos. Yo saqué todo lo que necesitaba de este script para importar pybloxsom

Básicamente:

  1. Obterner un key para la API
  2. Loguearse
  3. Obtener el "foro" correcto (Se puede usar una cuenta Disqus para más de un blog)
  4. Postear en el hilo adecuado

2. Entender el archivo de comentarios de Haloscan

No sólo es XML, ¡ Es XML fácil!

Es más o menos así:

<?xml version="1.0" encoding="iso-8859-1" ?>
<comments>
    <thread id="BB546">
      <comment>
        <datetime>2007-04-07T10:21:54-05:00</datetime>
        <name>superstoned</name>
        <email>[email protected]</email>
        <uri></uri>
        <ip>86.92.111.236</ip>
        <text><![CDATA[that is one hell of a cool website ;-)]]></text>
      </comment>
      <comment>
        <datetime>2007-04-07T16:14:53-05:00</datetime>
        <name>Remi Villatel</name>
        <email>[email protected]</email>
        <uri></uri>
        <ip>77.216.206.65</ip>
        <text><![CDATA[Thank you for these rare minutes of sweetness in this rough world...]]></text>
      </comment>
    </thread>
</comments>

Entonces: un tag comments, que contiene una o más tags thread, que contienen uno o mas tags comment. ¡Pan comido con ElementTree!

Hay una obvia correspondencia entre comentarios y threads en Haloscan y Disqus. Bien.

3. Crear los hilos necesarios en Disqus

Esta es la parte complicada, porque requiere cosas de tu blog.

  • Hay que tener un permalink por post
  • Cada permalink debe ser una página separada. No sirve un permalink con # en la URL.
  • Para cada post, hay que saber el título, el permalink, y cómo identificar los comentarios en Haloscan.

Por ejemplo, supongamos que hay un post en //ralsina.me/weblog/posts/ADV0.html con un link de Haloscan como éste:

<a href="javascript:HaloScan('ADV0');" target="_self"> <script type="text/javascript">postCount('ADV0');</script></a>

¿Adónde más sale ese 'ADVO'? En el archivo XML de Haloscan, por supuesto. Es el atributo "id" de un thread.

Además, el título de este post es "Advogato post for 2000-01-17 17:19:57" (Es mi blog por supuesto ;-)

¿Tenés esos datos?

Entonces vamos a crear un thread en Disqus con exactamente los mismos datos:

  • URL
  • Thread ID
  • Titulo

La mala noticia es... vas a necesitar tener esta información para todo tu blog y guardarla en algún lado. Si tenés suerte, tal vez la puedas sacar de una base de datos, como hice yo. Si no... bueno, va a ser bastante trabajo :-(

Para los propósitos de esta explicación voy a asumir que ese dato está en un bonito diccionario indexado por thread id:

{
  id1: (url, title),
  id2: (url, title)
}

4. Postear los comentarios de Haloscan a Disqus

Aquí está el código. No está realmente probado, porque tuve que hacer varios intentos y arreglos parciales, pero debería estar cerca de lo correcto. (download):

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Read all comments from a CAIF file, the XML haloscan exports

from disqus import DisqusService
from xml.etree import ElementTree
from datetime import datetime
import time


# Obviously these should be YOUR comment threads ;-)
threads={
    'ADV0': ('//ralsina.me/weblog/posts/ADV0.html','My first post'),
    'ADV1': ('//ralsina.me/weblog/posts/ADV1.html','My second post'),
    }

key='USE YOUR API KEY HERE'
ds=DisqusService()
ds.login(key)
forum=ds.get_forum_list()[0]

def importThread(node):
    t_id=node.attrib['id']

    # Your haloscan thread data
    thr_data=threads[t_id]

    # A Disqus thread: it will be created if needed
    thread=ds.thread_by_identifier(forum,t_id,t_id)['thread']

    # Set the disqus thread data to match your blog
    ds.update_thread(forum, thread, url=thr_data[0], title=thr_data[1])


    # Now post all the comments in this thread
    for node in node.findall('comment'):
        dt=datetime.strptime(node.find('datetime').text[:19],'%Y-%m-%dT%H:%M:%S')
        name=node.find('name').text or 'Anonymous'
        email=node.find('email').text or ''
        uri=node.find('uri').text or ''
        text=node.find('text').text or 'No text'

        print '-'*80
        print 'Name:', name
        print 'Email:', email
        print 'Date:', dt
        print 'URL:', uri
        print
        print 'Text:'
        print text

        print ds.create_post(forum, thread, text, name, email,
                                   created_at=dt, author_url=uri)
        time.sleep(1)

def importComments(fname):
    tree=ElementTree.parse(fname)
    for node in tree.findall('thread'):
        importThread(node)


# Replace comments.xml with the file you downloaded from Haloscan
importComments('comments.xml')

Ahora, si tuvimos suerte, ya tenés una linda y funcional colección de comentarios en tu cuenta de Disqus, y la tranquilidad de que no se perdieron los datos. Listo para el paso final?

2009-06-20 13:06

Por qué sigo usando Arch Linux

Ayer tuve uno de esos momentos en que estoy muy feliz de haber elegido Arch Linux. Ya que la última vez que posteé algo sobre Arch parece haber sido hace más de dos años (el tiempo vuela cuando uno se divierte!) es momento de explicar porqué.

Quería probar rst2pdf contra reportlab de SVN, wordaxe de SVN y docutils de SVN, y quería que fuera fácil.

Solución: Los empaqueté en AUR!

Ahora, cada vez que quiero probar rst2pdf contra wordaxe de trunk SVN, hago un yaourt -S python-wordaxe-svn y para volver a wordaxe estable hago yaourt -S python-wordaxe.

El paquete SVN siempre es trunk actualizado sin modificaciones, y puedo ir y volver en unos 45 segundos, sin romper paquetes del sistema.

También puedo mantener mis paquetes SVN instalados al día con un yaourt -Su --devel cada tanto.

Como lo hubiera hecho usando Debian o algo basado en RPM? Supongo que por atrás del sistema de paquetes (que odio hacerlo) o haciendo un repo privado (que es triste) o con un repo público (que es trabajo!).

La verdad si uno programa, no se me ocurre una distro que te haga la vida más fácil que Arch. Casi todo está ahí (12K paquetes en unsupported!) y si no está son 5 minutos para meterlo en AUR y ayudar a la comunidad.

Suponé que estás haciendo una aplicación KDE. En la mayoría de las distros tenés que instalarte tu propia copia de kdelibs de los fuentes para tener la última versión y asegurarte que no está arruinada por parches específicos de la distro.

En arch? Emparchar está mal visto. No tener la última versión está mal visto. Así que es más o menos el ambiente ideal para desarrollar con KDE, GNOME, PyQt o lo que sea.

Si mi tiempo no estuviera ocupao un 150% intentaría ser desarrollador Arch, o por lo menos un TU (Trusted User).

Capaz la próxima reencarnación :-)

2009-03-12 22:10

Outlook, IMAP y Exchange

Voy a postear esto sólo por si algún otro pobre diablo necesita hacerlo. Avisen si tiene algún sentido.

  • Al instalar Outlook 2000, se puede elegir "Sólo groupware", "Sólo Internet" o "Ambos".
  • Sólo Internet habilita cuentas IMAP y POP, pero no Exchange.
  • Sólo Groupware habilita Exchange, pero no IMAP ni POP.
  • Ambos habilita Exchange y POP... pero no IMAP!

Así se hace para tener Exchange y IMAP al mismo tiempo:

  1. Cerrar Outlook
  2. Ir al panel de control
  3. Ahí hay un icono "Correo" que se creó cuando instalamos OUtlook.
  4. Use ese icono para crear la cuenta IMAP.

Y hay quien se atreve a decir que windows es fácil de administrar :-P

Sí, ya sé, es software viejo, lo que sea. No es mi culpa que actualizar el maldito cliente de correo cueste tan caro que no lo quieran hacer porque implica actualizar todo el reverendo office.

PD: Gracias al que me contó como se hace ;-)

PD2: Como hacer que Outlook muestre más de 100 resultados de LDAP: http://support.microsoft.com/kb/262848

2008-10-20 11:59

A hard-to-block spammer: I need help.

Many of my clients have been spammed by La Capitana Real Estate lately. And I mean many. Hundreds.

However, they seem to have found a way to spam that works. And that sucks.

They have created a Google Group, added all their victims there, and let google do the dirty work.

What's the problem?

  1. Google group mails are not blockable at SMTP-level because their senders contain a sort of hash and the recipient address, and no group name. That's incredibly stupid in google's part.
  2. The messages they send are huge (6MB and up) so spamassassin can not process them. The SA docs say this will not happen because of "the economics of spam". Well, it happens when you make google do it!
  3. I don't want to go back to the old days of keeping a local queue-level address blacklist. That's awful!

I have complained to google, I have complained to the spammers, even by phone. They use the standard defense of "we are just inviting people". "They can unsubscribe if they want to". "This is not spam"

Noone does anything.

What's the next step? I can't blacklist google groups!

2008-07-16 11:49

Why no packaging software should replace your config files

When you upgrade a piece of software on Linux, there are two paths it can go when there are incompatible changes in the config files (ok, 3 paths, Debian asks you what to do):

  1. The "rpmnew" way: install the new config file as "whatever.rpmnew", which means the softwarewill break immediately, but that's ok, because you are doing upgrades, so you should be watching already.
  2. The "rpmsave" way: replace the old file and save a copy as "whatever.rpmsave".

This has two problems:

  1. The software may fail or not, or fail in a subtle way, and you will not notice right away.

  2. Maybe the old file will be lost anyway:

    lrwxrwxrwx  1 root root 32 jul 15 22:41 /etc/named.conf -> /var/named/chroot/etc/named.conf
    lrwxrwxrwx  1 root root 32 jul 15 22:36 /etc/named.conf.rpmsave -> /var/named/chroot/etc/named.conf
    

In this case the "file" was a symlink, so by "saving a copy" it only saved another symlink to the soon-to-be-overwritten file.

And that's why, ladies and gentlemen, the rpmnew way is the good way.

2008-06-20 14:23

Adding MSN notifications to Argus

I am a user of Argus as a monitoring software. Since it's very flexible and easy to extend, I wanted to add MSN alerts, the same way I had added SMS alerts a while ago. It was easier than I thought!

  1. Install msnlib

  2. Install the example msnbot, modified so do_work is like this:

    def do_work():
       """
       Here you do your stuff and send messages using m.sendmsg()
       This is the only place your code lives
       """
    
       # wait a bit for everything to settle down (sync taking efect
       # basically)
       time.sleep(15)
    
       msg=sys.stdin.read()
       for d in sys.argv[3].split('+'):
               print m.sendmsg(d,msg)
    
    
       # give time to send the messages
       time.sleep(30)
    
       # and then quit
       quit()
    
  3. Define a custom notification in argus like this:

    Method "msn" {
       command: /usr/local/bin/msnbot [email protected] mypass %R >/tmp/XXX 2>&1
       send: %M\n
    }
    
  4. Wherever you want MSN notifications, add this (on notify or escalate directives, using as many guys MSN addresses as you need):

    msn:[email protected][email protected]
    

That's it.

2008-05-12 22:10

Wine kinda works nowadays

I had stopped trying to run stuff in WINE a few years ago,because pretty much nothing worked.

You know what? They areright about approaching 1.0, I had to try a few windowsy things lately, and each one has worked just fine.

Kudos wine people!

PS: can you make the widgets look less ugly, though? I heard rumours of a theming engine, but have no clue as to where one would look. Even something like GTK's clearlooks would be bearable.

2008-04-15 17:13

¡Examen Sorpresa!

Pregunta: ¿Qué pasa cuando se tiene un server SMTP autenticado, con un usuario info, y se le pone como password info?

Respuesta:

[[email protected] ~]# qmail-qstat
messages in queue: 151369
messages in queue but not yet preprocessed: 9

Lección:

  • Hay un motivo por el cual no te deja hacer eso por default.
  • Debería limitar el correo saliente por cuenta por default en todos los servers. Mejor me pongo a trabajar en eso.

2008-04-15 16:14

Linux ayuda a Windows: Mandar SMS

Supongamos que uno quiere mandar mensajes SMS desde windows a través de una conexión bluetooth con un teléfono.

Estoy seguro que tiene que haber alguna manera de hacerlo andar. Por otro lado, ya lo tenía andando en Linux... así que uno puede simplemente usar esto en un Linux amigo, y mandar mensajes SMS accediendo a una URL especial:

#!/usr/bin/env python
from colubrid import BaseApplication, HttpResponse, execute
import os

class SMSApplication(BaseApplication):

  def process_request(self):
      numero = self.request.args.get('numero')
      mensaje = self.request.args.get('mensaje')
      [entrada,salida]=os.popen4('/usr/bin/gnokii --sendsms %s'%numero,mode='rw')
      entrada.write(mensaje)
      entrada.flush()
      entrada.close()
      msg=salida.read()
      response = HttpResponse(msg)
      response['Content-Type'] = 'text/plain'
      return response

if __name__ == '__main__':
  execute(SMSApplication,debug=True, hostname='mybox.domain.internal', port=8080,reload=True)

Si alguien abre http://mybox.domain.internal:8080/?numero=1234?mensaje=hola%20mundo manda "hola mundo" al numero 1234.

Supongo que podría decir que es un servicio web de telefonía, pero es la solución de 5 minutos que se me ocurrió.

Usa una cosa que se llama colubrid que no es realmente un framework web y no algo conocido porque quería mantenerlo simple, y no se pone mucho más simple que esto.

Contents © 2000-2019 Roberto Alsina