2016-10-31

Pequeño descubrimiento interesante

En la consola de Windows (cmd.exe), si apretás F7 aparece la historia de los comandos de la consola en un hermoso menú, hecho por daltónic@s, que podés manejar con las flechitas (y elegir el comando que quieras). Enter para ejecutarlo, flecha a la derecha para que aparezca escrito en el prompt (sin ejecutar).

2016-08-17

Haciendo un USB booteable para instalar Windows 7

Cuando instalo Ubuntu en una PC nueva, suelo copiar el ISO en un pendrive, haciendo dd. El pendrive tiene que estar enchufado y desmontado, y no queda nada de lo que había grabado antes.

# dd if=/path/to/file.iso of=/dev/sdb bs=4M

Esto no me anduvo para las ISOs de Windows :(. Así que estuve googleando un rato largo hasta que encontré acá instrucciones que logré hacer andar.

Lo que yo hice, basado en lo que linkié arriba es:

  1. Enchufé el pendrive, se puso solo en /dev/sdb (YMMV). El resto de las instrucciones asumen que el pendrive está ahí.
  2. Desmonté el pendrive desde la GUI.
  3. Corrí, sudo cfdisk /dev/sdb y creé adentro una partición NTFS (tipo 7) y la marqué como booteable, asegurándome que tenga suficiente lugar para copiar todo el contenido del ISO de Windows 7.
  4. Formatié la partición, corriendo sudo mkfs.ntfs -f /dev/sdb.
  5. Instalé lilo (con apt-get) y corrí sudo lilo -M /dev/sdb mbr para copiarle un boot record.
  6. Creé en /mnt las carpetas /mnt/iso y /mnt/usb 
  7. Monté por loopback el ISO corriendo sudo mount -o loop win7.iso /mnt/iso.
  8. Monté el pendrive corriendo sudo mount /dev/sdb1 /mnt/usb.
  9. Copié todo el contenido del ISO en el pendrive (usé el Midnight Commander, cp -R debería andar también).
  10. Desmonté iso (sudo umount /mnt/iso) y pendrive (sudo umount /mnt/usb)
  11. Corrí sudo sync para sincronizar los filesystems (creo que no hace falta, pero lo hice igual por las dudas)
  12. Desenchufé el pendrive y lo usé para instalar Windows en otra máquina
Bonus:
Si querés, podés backupear la imagen haciendo sudo dd if=/dev/sdb of=/path/to/image/file.img bs=4M.

Bonus 2:
Si instalaste Windows en una partición, y tenés un Ubuntu en otra, podés seguir estas instrucciones para poder bootear las 2 (mirá la respuesta #6).

Happy hacking!
Aureliano.

2016-07-08

Sandro en otros contextos

Hasta hace 5 minutos sandro sólo andaba si el WAR que deployás lo deployás en el contexto ROOT. Ahora ya no es más así. A partir de ahora es posible correr sandro con toda su magia para compartir código entre cliente y server desde cualquier contexto. Eso permite que haya más de una aplicación de sandro en el mismo Tomcat.

Enjoy!

2016-06-29

Alpha Go, por favor calculá el komi

Hace unos meses Google pateó el goban y mostró que sus computadoras juegan al go mejor que cualquier ser humano. Encima, ¡parece que entrenaron a Alpha Go un poco más y ahora es 4 piedras más fuerte que antes!

O sea, el go dejó de ser algo que nosotr@s hacemos mejor. Pero eso tiene ventajas. Una de las cosas más raras del go es el komi. A diferencia del resto del go, que tiene más de 2000 años, sólo se usa regularmente hace menos de 100 años. El es la ventaja, en puntos, que le da el jugador negro al blanco porque el negro hace la primera jugada. La idea es que esa compensación hace que el partido sea más parejo. Cuando empezaron a usar komi en los torneos profesionales, usaron 4.5 puntos (.5 para evitar empates) y subió a 5.5 y después s 6.5 en Japón y Corea y a 7.5 en China. Pero no sabemos que valor tiene que tener. Y mirar las diferencias de puntos en los partidos de profesionales no sirve porque cambian de estrategia cuando van ganando (y sí, cuentan los puntos en el medio del partido).

Por eso, quiero pedirle a la gente que trabaja en AlphaGo que entrene a AlphaGo con distintos valores de komi y nos cuente, para cada posible valor del komi que porcentaje de las partidas las gana negro en un partido de AlphaGo contra sí mismo. Así podemos elegir el valor de komi que genera menos ventaja.

Si trabajás en DeepMind o conocés a alguien que trabaje ahí, o conocés a alguien que trabaje en Google, o podés contactar a la federación internacional de go, o se te ocurre algún otro camino, por favor pediles que calculen cuál es el mejor komi.

Muchas gracias,
Aureliano.

2016-06-15

Tutorial ctypes

Como parte de mi laburo necesito probar cosas que son exportadas en dlls. Como hacer un programa en C/C++ para probar cada boludez es una patada en los huevos, estoy usando ctypes y scripteando un toque en la consola de python para hacer eso. Y como mi memoria a veces falla, uso este tutorial para que me recuerde cómo hacerlo.

Quizás les sirva a ustedes también,
Aureliano.

2016-05-20

Volviendo a C++

En mi laburo necesitan que programe en C++ nuevamente, así que hace unos días estoy mirando y recordando C++. ¡Qué incómodo que es!

Default constructors, copy constructors, asignment operators, que encima en su versión generada por default andan mal. Métodos virtuales, ¿por qué hay otra opción, y encima no es el default? ¿Herencia virtual? ¿Functors o function pointers? ¿Punteros comunes, shared pointers, referencias? ¿Cómo obtengo el shared_pointer de this? ¿Y si hay herencia? ¿Templates? Encima los templates son simultáneamente complicadísimos y sumamente limitados. Por ejemplo, ¿cómo hacés un template que cambie el nombre de los métodos generados en función de algún parámetro? Creo que no se puede (si sabés cómo hacerlo sin usar #define decime). Escribir todo varias veces, forward declarations, declaraciones en el .h e implementaciones, con la signatura repetida, en el .cpp.

Y ni hablar de los quilombos asociados, sobre todo en Windows, que no son del lenguaje, pero que te complican la vida. Cambiás de compilador y linkea mal y, en el caso de Visual Studio también genera binarios incompatibles según el runtime que elegís. De regalo el Visual Studio que estoy usando no se da cuenta cuándo recompilar y genera mierda desactualizada.

Pero eso no es lo peor de C++. Para mi lo peor de C++ es que no te deja pensar. Tenés que esquivar tanta complejidad accidental que el código fuente no reflejo de las ideas subyacentes. Eso nos devalúa como programadores.

2016-05-01

Mejorando pingmon.py

En mi post anterior puse un scriptcito en python para monitorear una conexión con ping. Después de eso lo mejoré un toquecito, y ahora toma los últimos 10, 100 y 1000 probes y calcula el porcentaje que tienen respuesta. La ejecución se ve así:
$ pingmon.py 8.8.8.8
(1) 10:N/A 100:N/A 1000:N/A
(2) 10:N/A 100:N/A 1000:N/A
(3) 10:N/A 100:N/A 1000:N/A
(4) 10:N/A 100:N/A 1000:N/A
(5) 10:N/A 100:N/A 1000:N/A
(6) 10:N/A 100:N/A 1000:N/A
(7) 10:N/A 100:N/A 1000:N/A
(8) 10:N/A 100:N/A 1000:N/A
(9) 10:N/A 100:N/A 1000:N/A
(10) 10:N/A 100:N/A 1000:N/A
(11) 10:100.00% 100:N/A 1000:N/A
(12) 10:100.00% 100:N/A 1000:N/A
(13) 10:100.00% 100:N/A 1000:N/A


Y el código del script creció un poquito, pero sigue entrando en un post.
#!/usr/bin/env python

import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")

def pct_str(probes, count):
  if len(probes) < (count + 1):
    return "N/A"
  return "%.2f%%" % ( 100 * sum(probes[-count-1:-1]) / count )

def monitor(ip):
  probes = []
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  last_seq_number = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline()).group(1))
    probes.extend([0] * (seq_number - last_seq_number - 1))
    probes.append(1)
    print "(%s) 10:%s 100:%s 1000:%s" % (
      line_count, pct_str(probes, 10), pct_str(probes, 100), pct_str(probes, 1000)
    )

    probes = probes[-1000:] # Keep memory usage bounded
    last_seq_number = seq_number

def main(argv):
  monitor(argv[1])


Siéntase a gusto de hackear este script si quieren algún monitoreo distinto (cambiar la salida y/o la forma de monitorear).

Espero que les sea útil,
Aureliano.

2016-04-28

Monitoreo con ping

A veces mi proveedor de internet anda mal. Una de las cosas que pasan es que empieza a dropear muchos paquetes y, para ver eso, hago un ping. El problema es que el ping no te dice el porcentaje de los paquetes que se cayeron :/. Por lo tanto hice un script en python, al que llamé pingmon.py, que wrappea ping y da esa información en cada vuelta exitosa de un paquete.

La salida se ve así:
$ pingmon.py 8.8.8.8
Packets returned: 1 Packet count: 1 Return percentage: 100.00 %
Packets returned: 2 Packet count: 4 Return percentage: 50.00 %
Packets returned: 3 Packet count: 5 Return percentage: 60.00 %
Packets returned: 4 Packet count: 7 Return percentage: 57.14 %
Packets returned: 5 Packet count: 8 Return percentage: 62.50 %
Packets returned: 6 Packet count: 11 Return percentage: 54.55 %


Y el código del script es éste:
import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")
 

def monitor(ip):
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline()).group(1))
    print "Packets returned: %d Packet count: %d Return percentage: %.2f %%" % (line_count, seq_number, (100.0 * line_count)/seq_number )

def main(argv):
  monitor(argv[1])


Espero que les sirva,
Aureliano.

2016-02-11

ZTE MF110 en lubuntu 14.04

Por cuestiones laborales, tengo que hacer andar un modem 3G en Linux. Después de bucear por mercado libre, encontré un ZTE MF 110, al que le puse un chip de datos de Claro. Usándolo desde una VM con Windows 7 en VirtualBox y haciendo pass-through para que maneje el pituto usb, anduvo joya. Pero en Linux, andaba intermitentemente. Estuve googleando y descubrí que muchos MODEMs 3G andan en 2 modos. Un modo que es una unidad de mass-storage, donde tiene drivers y manuales para Windows, y otro que es el modem en sí. Y linux, en teoría, cambia de modo automáticamente con el usb_modeswitch. Bueno, cuestión que en lubuntu 14.04 está roto ese cambio, porque falla el montado de la unidad de mass-storage y aborta el proceso. Si corren

$ dmesg | tail -n 15

después de enchufar el modem van a ver una línea como esta mostrando el error.

[ 8905.896992] systemd-udevd[6462]: Failed to apply ACL on /dev/sr0: No such file or directory

Parece que udev y este modem no se llevan bien. Si miramos usando lsusb se ve que está en modo mass-storage:

$ lsusb | grep -i zte
Bus 003 Device 037: ID 19d2:2000 ZTE WCDMA Technologies MSM MF627/MF628/MF628+/MF636+ HSDPA/HSUPA


Seguí googleando y encontré cómo hacer el switch del modo manualmente en este post. Entonces ejecuté:

$ sudo usb_modeswitch -v 19d2 -p 0x2000 -K
[sudo] password for aure:
Look for default devices ...
   product ID matched
 Found devices in default mode (1)
Access device 035 on bus 003
Current configuration number is 1
Use interface number 0
Use endpoints 0x01 (out) and 0x81 (in)

USB description data (for identification)
-------------------------
Manufacturer: ZTE,Incorporated
     Product: ZTE Configuration
  Serial No.: A SERIAL NUMBER
-------------------------
Sending standard EJECT sequence
Looking for active driver ...
 OK, driver detached
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 OK, message successfully sent
Read the response to message 1 (CSW) ...
 Response reading failed (error -7)
 Device is gone, skip any further commands
-> Run lsusb to note any changes. Bye!


Uso lsusb de nuevo y ahora cambió el device.

$ lsusb | grep -i zte
Bus 003 Device 038: ID 19d2:0031 ZTE WCDMA Technologies MSM MF110/MF627/MF636


Mirando en dmesg aparece un cartel que dice que lo reconoció como modem:

[ 9919.553348] usb 3-3: GSM modem (1-port) converter now attached to ttyUSB2

Y después apareció la opción de activar "Mobile broadband" en el iconito de red :D

Happy hacking,
Aureliano.

2016-02-08

Regalos de carnaval para sandro

Hice 2 arreglos en sandro.

  • Cambié la forma de usar rhino y ahora puede procesar archivos grandes de javascript de nuevo. Eso hace que ande d3 en el server nuevamente.
  • Ahora los archivos .js que usa sandro tienen que estar encodeados en utf-8, permitiendo escribir en el código cosas como ( ͡° ͜ʖ ͡°).
Happy hacking,
Aureliano.

2016-01-31

Ciclo completo para autenticarse por oauth2 en línea de comando

Hace un tiempito que estoy jugando con el API de blogger para hacer posts automaticamente. Lo que me parece más complicado es autenticarse vía oauth2 desde una aplicación de línea de comando. Por eso hice un módulo de sandro que lo hace por mi, pidiendo el código de autorización solo cuando es necesario

Se puede usar como módulo de sandro así:

define({
  accessToken: './accessToken'
},
function(m) {
  var accessToken = m.accessToken(
    'refresh_token_fname.txt', 
    {
      clientId: 'CLIENT ID HERE',
      clientSecret: 'CLIENT SECRET HERE'
    }
  )
  // Use accessToken here!
})

Abajo les dejo accessToken.js completo (cuidado, son 134 líneas de código):

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",
  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
  var Scanner = m.javaPackages.java.util.Scanner
  var System = m.javaPackages.java.lang.System
  var File = java.io.File
  var FileOutputStream = java.io.FileOutputStream
  var OutputStreamWriter = java.io.OutputStreamWriter
  var out = System.out
  var in_ = new Scanner(System["in"])

  var paramBytes = function(p) {
    var s = new String( Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&") )
    return s.getBytes(StandardCharsets.UTF_8)
  }
 
  var postJsonResult = function(url, params) {
    var parameters = paramBytes(params)
    var c = new URL(url).openConnection()
    try {
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    if (200 != c.responseCode) {
      return null
    }
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    return m.json.parse( "" + response )
    } finally {
      c.disconnect()
    }
  
  }
  var authCode2refreshToken = function(authCode, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        code: authCode,
        redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "authorization_code"
      }
    )
  
    return rv ? rv["refresh_token"] : null
  }
 
  var refreshAccessToken = function(refreshToken, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        refresh_token: refreshToken,
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "refresh_token"
      }
    )
 
    return rv ? rv["access_token"] : null
  }
 
  var readFileLine = function(fname) {
    var sc = new Scanner(new File(fname))
    try {
      return sc.nextLine()
    } finally {
      sc.close()
    }
  }
 
  var writeFileLine = function(fname, line) {
    try {
      var w = new OutputStreamWriter( new FileOutputStream(fname) )
      line = "" + line
      w.write(line, 0, line.length)
    } finally {
      w.close()
    }
  }
 
  var obtainAccessToken = function(refreshTokenFname, credentials) {
    var accessToken = null
    var refreshToken = null
    try {
      refreshToken = readFileLine(refreshTokenFname)
    } catch (e) {
      // ignore
    }
  
    while (true) {
      if (refreshToken) {
        accessToken = refreshAccessToken(refreshToken, credentials)
      }
      if (accessToken) {
        return accessToken
      }
    
      var authorizationUrl =
        "https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=" +
        credentials.clientId
      out.println("Anda a este URL, completa la autorizacion y pega el codigo de autorizacion aca. Despues presiona ENTER")
      out.println(authorizationUrl)
      var authCode = in_.nextLine()
      refreshToken = authCode2refreshToken(authCode, credentials)
      writeFileLine(refreshTokenFname, refreshToken)
    }
  }
 
  return obtainAccessToken
})



Si quieren, es bastante fácil transliterarlo a código Java.

Espero que les sirva,
Aureliano.

2016-01-30

Obteniendo el access token

Sigo en mi cruzada para hacer cosas automáticamente en blogger usando sandro. En este caso, hice el código para obtener un access token a partir de un authorization code. Para eso, primero necesitás registrar una aplicación de línea de comando. Una vez que lo hayas hecho, vas a tener el client id y el client secret. Pero eso no alcanza, para obtener el access token, como el que usamos en el post anterior, hace falta un authorization code. Para obtenerlo, conectate a este URL:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=ACA_VA_TU_CLIENT_ID

Y te va a mostrar el authorization code después de loguearte a google y autorizar a tu aplicación a hacer cosas de blogger en tu nombre.

Una vez que obtuviste tu authorization code, hay que obtener un access token. El access token te sirve para manipular blogger con tu usuario por la próxima hora. Para renovarlo, hay que usar un refresh token, que también obtendremos, pero voy a mostrar cómo usarlo en otro post.

Acá abajo dejo el código de ejemplo para obtener estos tokens.

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",

  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var out = m.javaPackages.java.lang.System.out
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
 
  var tokenUrl = new URL("https://www.googleapis.com/oauth2/v3/token")

  var paramBytes = function(p) {
    var s = Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&")
    out.println(s)
    s = new String(s)
    return s.getBytes(StandardCharsets.UTF_8)
  
  }
 
  return function(code, client_id, client_secret) {
    var parameters = paramBytes({
      code: code,
      redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
      client_id: client_id,
      client_secret: client_secret,
      grant_type: "authorization_code"
    })
    var c = tokenUrl.openConnection()
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    var jsonResponse = m.json.parse( "" + response )

    out.println("Tokens: " + m.json.stringify(jsonResponse))

  }
})


No uso la google-client-api de java porque es un quilombo y no entendí cómo usarla, así que me conecto a los webservices de google directamente.


2016-01-24

Accediendo al API de blogger desde sandro

Hoy estuve jugando con el API de blogger desde un script que hice usando sandro. Como puedo usar las APIs de java desde el código JavaScript que implementa los módulos de Sandro, decidí usar la biblioteca que google provee para Java.
Lo que más me costó fue toda la parte de oauth2. Como estoy solo probando, no quise armar todo un webserver para hacer todo el camino de oauth. En vez de eso, usé el oauth playground para obtener un access-token y programé desde ahí.

Con todo esto, y afanando bastante de acá hice un scriptcito que obtiene algunos datos de este blog.

define({
  javaPackages : "javaPackages",
  ary : "sandro/nadaMas/array"

}, function(m) {
 
  var Blogger = m.javaPackages.com.google.api.services.blogger.Blogger
  var NetHttpTransport = m.javaPackages.com.google.api.client.http.javanet.NetHttpTransport
  var JacksonFactory = com.google.api.client.json.jackson2.JacksonFactory
  var GoogleCredential = com.google.api.client.googleapis.auth.oauth2.GoogleCredential

  var HTTP_TRANSPORT = new NetHttpTransport();
  var JSON_FACTORY = new JacksonFactory();
  var CREDENTIAL = GoogleCredential().setAccessToken("HERE GOES THE ACCESS TOKEN")
 
  var buildBlogger = function() {
    var bb = new Blogger.Builder(HTTP_TRANSPORT, JSON_FACTORY, CREDENTIAL)
    bb.applicationName = "Testing blogger"
    return bb.build()
  }

  return function() {
    var out = m.javaPackages.java.lang.System.out
   
    var blogger = buildBlogger()
   
    var action = blogger.blogs().getByUrl("http://aurelianito.blogspot.com")
 

    action.fields = "description,name,posts/totalItems,updated"

    var blog = action.execute();

    // Now we can navigate the response.
    out.println("Name: " + blog.name)
    out.println("Description: " + blog.description)
    out.println("Post Count: " + blog.posts.totalItems)
    out.println("Last Updated: " + blog.updated)
   
  }

})


Si lo corren debería mostrarles algo muy parecido a:

Name: aurelianito
Description: Blog de Aureliano Calvo.
Aureliano Calvo's blog.
Post Count: 318
Last Updated: 2016-01-20T19:49:14.000-03:00


Después tengo que ver cómo hacer para hacer que el flujo del oauth ande bien.

Happy hacking,
Aureliano

2015-12-25

Cosas para cambiar en la Constitución

Las noticias de las 2 últimas semanas; con su show de DNUs, nombramientos de jueces por decreto, intervenciones de organismos autárquicos y tomas de deuda; me hicieron pensar en qué cosas de la Constitución habría que cambiar. Y se me ocurren estas:

  • No más sesiones extraordinarias. El Congreso sesionaba 9 meses al año porque hace 150 años llevaba un mes ir a Jujuy en carreta; y las cartas tardaban otro tanto. Por eso, tenía sentido suspender de noviembre a marzo, para que diputados y senadores pudieran ir a sus provincias a ver cuáles son las novedades y discutir con sus pueblos qué votar. Ahora en avión tardás 2 horas a Jujuy y 4 a Tierra del Fuego (desde Buenos Aires). Debería sesionar todas las semanas (o casi) y tener un mecanismo para convocarse que haga que no hagan más falta los decretos de necesidad y urgencia, ni otras atribuciones especiales en periodos en el que el Congreso deja de sesionar.
  • Volar el artículo 2, "El Gobierno federal sostiene el culto católico apostólico romano.". Y toda otra referencia al catolicismo y la Iglesia. Creo que no resiste análisis. Sino l@s ate@s y creyentes de otros cultos somos ciudadanos de segunda.
  • Agregar un cláusula que prohiba endeudarse sin aprobación del Congreso por las 2/3 partes de cada una de las cámaras. Sumado a que no cierren el Congreso durante el verano, y por lo tanto no haya DNUs, cerraría un mecanismo de dominación que aplican sobre nosotr@s l@s argentin@s.
  • Reforma agraria. Es increíble lo concentradas que están las tierras productivas en la Argentina. L@s hereder@s de Roca viven a costa de ellas y hacen que el resto tengamos que conformarnos con migajas. Habría que hacer una reforma agraria que rompa los latifundios para que tod@s podamos disfrutar de sus beneficios. Y sería mejor que fuera constitucional para evitar pelotudeces del partido poder judicial, que siempre defendió los intereses de l@s conservadores.
¿Se les ocurren más?,
Aureliano.

2015-12-11

Cambio en handlers de sandro

Hasta hace un ratito, un handler era una función que tomaba un request y un response. Ahora en vez de tomar esos 2 parámetros toma un diccionario. Los campos request y response tienen lo mismo de antes, pero ahora podemos en el encadenamiento de handlers agregar información. Por ejemplo, el json parseado de un request y/o el usuario que obtuvimos de la autenticación van a estar también en ese diccionario cuando sea apropiado.

Proximamente voy a estar agregando más cosas que andan por ahí.

Happy hacking,
Aureliano.


2015-11-18

Webex en ubuntu 14.04 (64 bits)

Update: Lo probé y no anda :(. Estoy usando el app de Webex para Android para tener audio en las conferencias.

Webex anda con un applet que corre usando el JDK de Oracle. Para que arranque hay que instalarle ese JDK:

$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
$ sudo apt-get install oracle-java8-set-default


y arranca, pero el audio no anda. La primera vez, para zafar, hice que me llame al celu (anda en Argentina!!!!).

Parece que webex está linkeado contra cosas de 32 bits, por eso con las libs de 64 bits no anda el audio :/. Para instalar las libs, seguí las intrucciones de este post.

Los puntos relevantes son:

  1. Las libs que se baja webex están en ~/.webex/1524 (1324 en el post linkeado).
  2. Para ver qué libs faltan, corré ldd *so | grep not en ese directorio.
  3. Para ver cómo instalar una lib, instalá apt-file y corré apt-file find libacavamilib-2.9.so
  4. Instalá la versión para i386 haciendo algo como apt-get install libacavamilib:i386. El nombre exacto lo sacás de la corrida anterior y es re-importante poner :i386.
  5. Fijate que libs necesitás instalar hasta que solo te quede pendiente libjawt.so
Según el post que linkié, eso debería hacer que ande el audio (y el resto de los features!) En la próxima conference-call les cuento si funcó (¿alguien sabe como hacer una conference call trucha en webex para probar la instalación?)

Happy hacking,
Aureliano.

2015-11-03

Cambiando default schema en oracle

Cuando te conectás por JDBC usando el thin driver, el esquema por default corresponde al esquema del usuario con el que te conectaste. Para cambiarlo hay que ejecutar este statement de SQL:

ALTER SESSION SET CURRENT_SCHEMA = ANOTHER_SCHEMA

No encontré ninguna forma de expresar el SCHEMA en el URL de conexión. Más detalles acá.

Happy hacking,
Aureliano.

PD: Si querés saber cuál es el CURRENT_SCHEMA, ejecutá  SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL

2015-10-19

Tengo, queries a la base de datos en sandro

En los últimos días implementé el módulo sandro/tengo. Lo que hice fue una forma "objetosa" de contruir queries de SQL y procesar los resultados, usando como base JDBC. La idea es evitar los problemas de seguridad por inyección que plagan los usos de las bases de datos sin restringir la funcionalidad. El código que hice lo probé con sqlite3, y debería andar con cualquier base de datos que sea ANSI-SQL y tenga driver de JDBC.

Acá les dejo un ejemplo de cómo hacer un query usando tengo. Asumiendo que connection es una conexión a la base de datos y hay una tabla person (id integer, name string, rank integer), así hago un query:

define(
  {
    tengo: "sandro/tengo"
  },
  function(m) {

    // Some code before, constructing the JDBC connection

    var _ = m.tengo.ansiBuilder

    // SELECT * FROM person ORDER BY id DESC;
    var results = _.select({
      from: "person",
      order_by: _.desc("id")
    }).map(connection, function(rs) {
      return {

        name: "" + rs.getString("name"), 
        id: rs.getInt("id")
      }
    })


    // SELECT count(1) as c, rank FROM person GROUP BY rank HAVING c > 1
    var results2 = _.select({
      columns: [ _.alias("c",_.invoke("count", _.int(1))), "rank" ],
      from: "person",
      group_by: "rank",
      having: _.gt("c", _.int(1))
    }).map(connection, function(rs) {
      return {

        c: rs.getInt("c"), 
        rank:rs.getInt("rank")
      }
    })
 


    // You can process the results here
  }
)

En la implementación actual podés hacer SELECTs con un montón de opciones (TODO: documentar todo). La idea subyacente es que las queries se arman haciendo árboles de objetos que generan los queries con objetos inmutables siguiendo la sintaxis de SQL (para que puedan usar los conocimientos de SQL que ya tienen), y estos objetos se encargan de escapar el SQL. Para que sea menos verbose, los strings son transformados en símbolos de SQL (como columnas o tablas), los números en expresiones que representan el número en punto flotante y los arrays en expresiones separadas por comas, que está buenísimo para hacer queries con varias tablas, requerir varias columnas o hacer ORDER BY con varios criterios.

Lo siguiente que toca es hacer que se pueda hacer UPDATE, DELETE, INSERT. Y después toca CREATE TABLE y DROP TABLE.

¿Qué frameworks conocen que les dejen hacer queries tan fácilmente? Lo más parecido que se me ocurre es el API de bajo nivel de SQLAlchemy, pero como los elementos de las queries son stateful, es mucho más quilombo.

2015-09-29

Cosas que los smartphones reemplazan

Hace unos años se hablaba mucha de la "convergencia tecnológica". Ahora llegó; y convergió en los smartphones.

Acá abajo armé una lista no exhaustiva de cosas que los smartphones reemplazan:

  1. Timbres
  2. Porteros eléctricos
  3. GPSs
  4. Cámaras de fotos
  5. Mapas
  6. Teléfonos de línea
  7. Linternas
  8. Lectores de código de barras
  9. Carteles con los nombres de las calles
  10. Publicidad en vía pública
  11. Páginas amarillas
  12. Computadoras
  13. Televisiones
  14. Discos rígidos externos
  15. Afinadores
  16. Tableros de juegos de mesa (ajedrez, go)
  17. Espacios de reunión con amig@s
  18. Calculadoras
  19. Walkmans
  20. Diskmans
  21. Diarios
  22. Revistas
  23. Libros
  24. Conexión a internet (ADSL, cable)
  25. Reloj 
  26. Servicio meteorológico  
  27. Calendario
  28. Agenda
  29. Lupa 
  30. Temporizador
  31. Tarjeta de crédito 
  32. Detector de metales
  33. Medidor de ruido ambiente
  34. Token de seguridad (para el banco)
  35. Brújula
  36. Teléfono visor
Si se me/les ocurre algo más lo voy a ir agregando a la lista.

Abrazos y éxitos,
Aureliano.

PD: Gracias Fabián, Nicolás y Odino por sus agregados.

2015-09-25

Al final sandro tiene basic authentication

Sandro tiene soporte para basic authentication. Para usarlo hay que importar el módulo sandro/alFinal. Se usa así:

alFinal.basicAuth( my_handler, function(user, pass) { return user === pass }, "sample realm")

Los tres parámetros que recibe son:

  • Un handler para ejecutar si la autenticación funciona. Es una función con 3 parámetros; request, response y user (un string)  
  • Una función para decidir si el acceso es autorizado o no, recibe usuario y password.
  • Un realm (string)
La implementación del feature está en este commit.

Happy hacking,
Aureliano.

2015-09-11

2 nuevos features para sandro

Recientemente le agregué 2 nuevos features a sandro.

  1. alFinal.fullJson es una utilidad para manejar servicios que reciben en el body (por POST) un request en JSON y devuelven otro JSON. Para implementar el servicio le pasás una función, que recibe 3 parámetros: jsonRequest (objecto con el request del body), request (el objeto request del servlet) y response (el objeto response del servlet). La función tiene que retornar un objeto JavaScript a ser JSONificado para mandar al cliente
  2. El web.xml soporta el parámetro do-not-reload para el RhinoServlet. Ahí pasás una lista de módulos, separados por comas, que no querés que se recarguen cuando llega el request.
Happy hacking,
Aureliano.

2015-08-23

Sandro tiene json al final

Le agregué a sandro en el módulo alFinal,  que es el que tiene todas las utilidades para hacer handlers, una utilidad para hacer webservices que devuelven json.

Para usarlo, hay que hacer algo como:
alFinal.json( function(request) {
  return { "it worked": true, echo: request.getParameter("echo") })

Por supuesto que pueden hacer la función que prefieran ;).

La implementación de la utilidad para hacer json es re complicada. Acá les dejo el código completo :p

function(f) {
  return function(request, response) {
    response.setContentType("application/json")
    response.writer.print( m.json.stringify( f(request, response) ) )
  }
}


Happy hacking,
Aureliano.

2015-07-28

Misión imposible

El sábado instalé un nuevo kernel en mi notebook, que corre LUbuntu 14.04 vía un upgrade del sistema operativo. Bootié de nuevo y no arrancó :(. Entonces agarré el celular y googlié para ver si había un montón de gente con problemas para bootear, pero no había nadie O_o. Después de eso, intenté bootear con el kernel viejo, y tampoco arrancó o_O. También probé arrancar usando el modo de recuperación y no arrancó O_O.
Por suerte tenía por ahí el pendrive que usé para instalarlo, y arrancó :D. Traté de arreglar grub, porque me imaginé que podría ser eso, pero no funcó. Después le corrí fsck y encontré como 30 bloques con problemas en el disco rígido de la compu. Los puse a reparar (tardó como 4 horas), rebootié y no arrancó :(.
Entonces miré el detalle del error cuando intentaba bootear en modo de recuperación, y decía que no podía cargar la biblioteca librt.so.1. Busqué dónde estaba en el disco rígido, booteando con el pendrive. La encontré en /lib/x86_64-linux-gnu y vi que no lo podía leer. Así que me copié un archivo de nombre parecido de mi pendrive al disco rígido y volví a bootear.
Entonces me dijo que no podía leer otro archivo. Esta vez era libc.so.6. También lo copié. Bootié de nuevo y me dijo que no podía leer libpthread.so.0. Y ese ni estaba en el disco rígido, así que me copié todas las libs del pendrive. Rebootié y arrancó :D.
Por último, para restaurar un poco mejor, arranqué una VM que ya tenía con LUbuntu 14.04, me copié todo /lib/x86_64-linux-gnu y sobreescribí las libs que había sacado del pendrive.
Por suerte sigue booteando , y creo que tengo que cambiarle es disco rígido a la compu antes que termine de trular :/.

2015-06-29

pyspark en vanilla cpython

Estuve buscando cómo hacer para correr pyspark en cpython sin usar el ejecutable de pyspark, como para poder integrarlo en otras cosas. No encontré nada que explique cómo hacerlo en la web, pero encontré un tutorial para correr ipython notebook con pyspark y mirando las instrucciones hackié algo para lo que yo quiero. Lo probé con spark 1.3.0 y python 2.7. Acá el script:

import os
import sys


# Setup pythonpath
spark_home = '/path/to/spark'
os.environ["SPARK_HOME"] = spark_home
sys.path.insert(0, os.path.join(spark_home, 'python'))
sys.path.insert(0, os.path.join(spark_home, 'python/lib/py4j-0.8.2.1-src.zip'))


# Make local context
from pyspark import SparkContext

sc = SparkContext(master="local", appName="aure")


# Do something with spark to test that it works
spark_home = os.environ.get('SPARK_HOME', None)
text_file = sc.textFile(spark_home + "/README.md")

word_counts = text_file \
    .flatMap(lambda line: line.split()) \
    .map(lambda word: (word, 1)) \
    .reduceByKey(lambda a, b: a + b) \
    .sortBy(lambda p: p[1], ascending=False)
   
   
print word_counts.take(20)


Espero que les sirva,
Aureliano.

2015-06-23

Sandro en la consola

En los últimos días hice que sea posible correr la biblioteca de sandro de línea de comando y tenga una consola. Para correr la consola, usando todos los módulos de sandro, ponelo en el classpath (con sus dependencias) y corré:

$ java sandro.RhinoConsole

Eso abre una consola donde podés correr código, que es una consola como la de rhino pero customizada para poder importar los módulos de sandro usando penumbras.

js> var funMod = penumbras.loadModule("sandro/nadaMas/function")
js> funMod.isFun(5)
false
js> funMod.isFun(function(a) {} )
true


Pero esto no es todo, ¡también se pueden correr módulos de sandro desde la línea de comandos! Para eso, hay que poner los módulos en el classpath (en un jar o un directorio), aparte de sandro y sus dependencias, y correr:

$ java sandro.RhinoRunner my/custom/module param1 param2

Para que corran, el módulo tiene que exportar una función, que es la que llama el runner. Pueden mirar el módulo helloWorld (está en el proyecto de la biblioteca por ahora) para ver un ejemplo tonto:

define({
  javaPackages : "javaPackages",
  ary : "sandro/nadaMas/array"
}, function(m) {

  return function() {
    var out = m.javaPackages.java.lang.System.out
    out.println("Hello!")
    out.println(m.ary.fromArgs(arguments).join())
  }

})


Y si lo invocan de línea de comando, pasa esto:

$ java sandro.RhinoRunner helloWorld Hola Manola
Hello!
Hola,Manola


Usando esto, hice que los tests de la biblioteca corran de línea de comando así:

$ java sandro.RhinoRunner sandro/testRunner

Por último, saqué la dependencia de servlets del cargador de módulos, así que ahora no hace falta tener el API de servlets en el classpath para usar todas las cosas que no tienen que ver con la web.

Happy hacking,
Aureliano.

2015-06-17

eclipse@docker

Estuve scripteando un poquito, e hice que eclipse corra en un container de docker (base, ubuntu:trusty), con las cosas que necesito para desarrollar sandro

Primero hice un dockerfile bastante trivial:

FROM ubuntu:trusty
RUN apt-get update && apt-get install -y \
  git \
  mercurial \
  default-jdk


con su correspondiente .dockerignore

dockerfile
.dockerignore


en un directorio que nombré docker

Después hice una imagen con ese container (a la que llamé, sandro:base).

$ docker build -t sandro:base docker

Y después vino la parte complicada.

Decidí correr el eclipse en volumenes afuera del container, con la idea de que quizás sea una buena idea inyectarlo en muchos distintos y poder instalar fácilmente plug-ins y esas cosas y que siga andando todo cuando haga otros containers. Para eso hice 4 directorios:

  • eclipse: donde puse un eclipse luna
  • workspace: donde va el workspace del eclipse
  • devel: donde pongo todo mi código
  • apache-tomcat-7.0.62: acá puse un tomcat bajado de apache.org
 Usando esa estructura hice 2 scripts:
  • dev.sh corre cualquier programa en un container nuevo, setupeando todo para tener un ambiente razonable para laburar.
  • eclipse.sh arranca eclipse usando dev.sh
Primero les muestro eclipse.sh

#!/usr/bin/env bash 

./dev.sh $1 eclipse/eclipse -data /workspace 

Le pasás por parámetro la imagen que querés usar para arrancarlo, y usa como workspace el directorio workspace que les conté más arriba.

Y ahora, la posta. dev.sh

#!/usr/bin/env bash

xhost +SI:localuser:root
ID=$( docker run \
  -d --name sandro-dev -h sandro-dev \
  -e SSH_AUTH_SOCK=$SSH_AUTH_SOCK -v $(dirname $SSH_AUTH_SOCK):$(dirname $SSH_AUTH_SOCK) \
  -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \
  -e GIT_SSH=/usr/bin/ssh \
  -v `pwd`/devel:/devel -v `pwd`/eclipse:/eclipse -v `pwd`/workspace:/workspace -v `pwd`/apache-tomcat-7.0.62:/tomcat \
  -p 127.0.0.1:8080:8080 \
  $@ )
docker wait $ID
docker rm $ID


Mejor voy línea por línea porque es un bardo

  1. xhost +SI:localuser:root habilita al container a poner aplicaciones en la GUI (las apps de los containers corren como root).
  2. ID=$( docker run \ arranco el container y guardo su id
  3. -d --name sandro-dev -h sandro-dev \ hace que el container corra desattacheado y setea nombre y hostname a sandro-dev para hacerme la vida más fácil
  4. -e SSH_AUTH_SOCK=$SSH_AUTH_SOCK -v $(dirname $SSH_AUTH_SOCK):$(dirname $SSH_AUTH_SOCK) \ setupea el forwardeo del agente de ssh. Puede resultar útil si quieren pushear código vía ssh sin usar passwords
  5. -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \ Comparte el display del desktop con el container
  6. -e GIT_SSH=/usr/bin/ssh \ para que si usan eclipse con git use el agente de ssh
  7. -v `pwd`/devel:/devel -v `pwd`/eclipse:/eclipse -v `pwd`/workspace:/workspace -v `pwd`/apache-tomcat-7.0.62:/tomcat \ monta todos los directorios que quedan afuera del container
  8. -p 127.0.0.1:8080:8080 \ forwardea el puerto 8080 (donde va a correr el tomcat de desarrollo) a localhost. Lo bindeo a 127.0.0.1 para que no se puedan conectar desde afuera.
  9. $@ ) recibe más parámetros para docker run, incluyendo los comandos para correr y el nombre de la imagen a usar
  10. docker wait $ID espera a que termine de correr el container
  11. docker rm $ID lo borra
Espero que les resulte útil, y espero mejoras y críticas,
Aureliano.

2015-05-23

Tuneando eclipse

Estoy usando eclipse con pydev para desarrollar usando pyzmq en un ubuntu 12.04 en forma remota por X11 (está en una VM). Para que sea usable, hice los siguientes cambios:

  • Para que use java7, toqué eclipse.ini y agregué -vm
    /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
  • Para que ande mejor pydev, fuerzo el stack de ipv4. Para eso agregué a eclipse.ini -Djava.net.preferIPv4Stack=true
  • Para que ande no tan lento, fuerzo que ande con GTK2 seteando la variable de entorno SWT_GTK3=0 (mirar acá para más detalles)
  • Para que ande git con el agente de ssh standard seteo la variable de entorno GIT_SSH=/usr/bin/ssh
  • Para interprete correctamente el package zmq, agregué zmq a los builtins (Window->Preferences->PyDev->Interpreters->Python Interpreters->Forced Builtins)
Happy hacking,
Aureliano.

2015-05-13

No más touchscreen

Update: Son mejores las instrucciones de http://askubuntu.com/questions/198572/how-do-i-disable-the-touchscreen-drivers. Para apagar el touch screen en mi notebook hago xinput disable 'ELAN Touchscreen'. Para ver el nombre de tu device corré xinput (sin parámetros).

Me rompía las pelotas el touch screen, así que seguí las instrucciones en http://ubuntuforums.org/showthread.php?t=2209083&p=12945855#post12945855 y santo remedio.

2015-04-20

Paper de análisis de imágenes satelitales

Los últimos meses estuve trabajando con Martín Garbulsky en el análisis de imágenes satelitales para medir crecimiento de plantas. Parte de ese trabajo fue usado en el paper Can temporal and spatial NDVI predict regional bird-species richness? (incluyendo también muchas más cosas que yo no tuve que ver)
¡Felicitaciones Martín!

2015-04-16

NATeando en ubuntu

Estoy tratando de poner un montón de VMs en VirtualBox de forma que pueda mover la compu de red y pueda seguir accediendo igual a las VMs. Para eso, primero quise poner las VMs en una NAT-network, pero el feature es experimental y no anda en mi lubuntu.
Por como son las VMs no quiero que tengan 2 interfases de red (1 host only y otra nat), por eso decidí poner una VM que haga de gateway/NAT y muchas atrás en la red host-only.
Para que las VMs en la red host-only tengan salida a internet, tuve que instalar en el gateway algo que haga NAT. Después de romperme la cabeza con iptables encontré este tutorial para setupear NAT con ufw y anduvo :D.
Como detalle de color, les cuento que tampoco me anda el server de DHCP que viene con VirtualBox en la red host-only, así que estoy usando IPs estáticas.
Espero que les sirva,
Aureliano.

2015-04-09

Autocomplete en eclipse y Lubuntu

En eclipse el auto-complete (AKA, content-assist) está bindeado a CTRL-espacio. En lubuntu, el cambio de layout de teclado está bindeado a CTRL-espacio.
Por lo tanto, no hay auto-complete en eclipse sobre lubuntu :(.
Salvo que cambien la configuración :D.

Para eso, vayan a Window->Preferences->General->Keys y busquen el entry "content assist" y cambien el key binding. A mi me gusta ponerle CTRL-TAB,  YMMV.

Happy hacking,
Aureliano.

2015-03-19

Persistencia para sandro

Hace un tiempo que vengo pensando cómo quiero que sea la persistencia en el server en sandro. Últimamente vengo escuchando mucho sobre las bases de datos de grafos, así que estoy pensando en usar una de ellas para hacer la nueva versión.
Entonces lo trivial sería usar neo4j, que corre en la JVM (igual que sandro) y es la más conocida. Pero el problema es que la versión "community" de neo4j ¡no deja hacer backups!.
No me hace feliz hacer que mi framework tenga esa limitación, así que tengo que usar otra cosa. Y creo que la encontré. Una empresa que se llama ThinkAurelius hace una base de datos de objetos que se llama Titan, no tiene las limitaciones de neo4j y soporta varios back-ends incluyendo casandra, por lo que escala infinitamente y tiene todos los temas de backup resueltos.
Creo que voy a usarla. ¿Saben de alguna limitación importante?

2015-03-13

Nuevo hogar para la primera versión de sandro

Hoy me llegó un mail que dice que van a cerrar google code. Por lo tanto, tuve que mover mi proyecto original de sandro (el que anda en appengine). Lo puse en bitbucket.

Happy hacking,
Aureliano.

2015-03-06

Self-signed https server en python

Estuve un rato peleando con python y aledaños para hacer un server de HTTPS pelotudo. No fue tan fácil.

Encontré la base del código acá, y un par de comentarios en el fondo donde explicaba cómo arreglarlo para que ande.

Primero generé un certificado que funque. Es importantísimo setear bien el common name (sino falla silenciosamente):

$ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3650 -nodes
Generating a 2048 bit RSA private key
.....................+++
.............................................................+++
writing new private key to 'key2.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:*
Email Address []:


Con ese certificado, este código levanta un webserver:

import BaseHTTPServer, SimpleHTTPServer
import ssl

def main():
    httpd = BaseHTTPServer.HTTPServer(('localhost', 4443), SimpleHTTPServer.SimpleHTTPRequestHandler)
    httpd.socket = ssl.wrap_socket (httpd.socket, certfile='certs/cert.pem', keyfile="certs/key.pem", server_side=True)
    httpd.serve_forever()
if __name__ == '__main__':
    main()


Happy hacking,
Aureliano.

Me retroplagiaron el retroplagio

Hace unos 2 años que vengo diciendo que hay gente viajando en el tiempo. La prueba es que toman mis ideas brillantes y las "inventan" antes de que yo las invente posta. Ayer descubrí que hicieron eso con la palabra que uso para describir el fenómeno, retroplagio.

Éxitos, Aureliano.

2015-03-03

Touch prendido y apagado

A veces quiero que mi touchpad ande y a veces no. Soy sobre todo un soñador, pero en lo vivido estoy convencido de no siempre se aprende de lo bueno, así que decidí quedarme con vos, línea de comandos.

Para apagar el touchpad en linux hago:
$ synclient TouchpadOff=1

Y para prenderlo de nuevo:
$ synclient TouchpadOff=0

Y así cambio mi destino y le gano a las cartas que la vida me dio,
Aureliano.

2015-02-18

Otro truquito para web2py

A veces, uno quiere generar un archivo en el sever ejecutando un comando, stremearlo al cliente y borrarlo. Acá les muestro un pequeño hack que hice para hacer eso. Asumiendo que el archivo que generó se llama fname, hay que poner esto al final del método de su controller:

        class StreamAndCleanUp(object):
            def __init__(self):
                stream = file(fname, "rb")
                self.stream = stream
                self.name =  self.stream.name
                self.read = stream.read
               
            def close(self):
                self.stream.close()
                os.remove(fname)
                # You can also put here other actions you need to make after streaming the file

        return response.stream(StreamAndCleanUp())


Happy hacking,
Aureliano.

2015-02-05

PostGIS, RDS y web2py

En RDS hay soporte para postGIS. El setup normal para una base de datos está documentado y es bastante complicado. A eso se suma que el DAL de web2py es bastante molesto a la hora de hacer operaciones en SQL que setupean la base, necesitando que todas las operaciones de setup sean idempotentes. El ejemplo que dan en la documentación es cómo crear índices.
Uniendo todo esto, hice un módulo de python que puse en la carpeta models de mi aplicación y ahí ejecuto lo que dice en la documentación de AWS, modificado para que sea idempotente.
Acá les regalo mi scriptcito (que nombré postgis.py).

for extension in "postgis fuzzystrmatch postgis_tiger_geocoder postgis_topology".split():
    db.executesql("CREATE EXTENSION IF NOT EXISTS %s ;" % extension)

# Change ownership, required for RDS   
if settings.rds:
    for schema in "tiger topology".split():
        db.executesql("alter schema %s owner to rds_superuser;" % schema)

    db.executesql("CREATE OR REPLACE FUNCTION setup_postgis_exec(text) returns text language plpgsql volatile AS $f$ BEGIN EXECUTE $1; RETURN $1; END; $f$;")
    db.executesql("""\
SELECT setup_postgis_exec('ALTER TABLE ' || quote_ident(s.nspname) || '.' || quote_ident(s.relname) || ' OWNER TO rds_superuser')
  FROM (
    SELECT nspname, relname
    FROM pg_class c JOIN pg_namespace n ON (c.relnamespace = n.oid)
    WHERE nspname in ('tiger','topology') AND
    relkind IN ('r','S','v') ORDER BY relkind = 'S')
s;
""")


Éxitos,
Aureliano.

2015-02-02

mercurial 2 git

Encontré esta página donde traducen comandos de mercurial a git. Si sos como yo y sabés mucho más de mercurial que de git (o al revés), es posible que te sirva como referencia. Yo sé que a mi ya me salvó las papas del fuego.

Happy hacking,
Aureliano.

2015-01-26

Matando queries en RDS

Para matar la ejecución de una query que está haciendo mierda el server en RDS, si tenés una instancia de mysql tenés que hacer:

CALL mysql.rds_kill_query(process_id);

Para obtener el process_id, podés correr:

SHOW FULL processlist;

Happy hacking,
Aureliano

2015-01-19

Workarround Fibertel DNS en Lubuntu

En lubuntu, se me hizo medio complicado cambiar el DNS por default para las conexiones. Para workarroundear, lo que encontré es que, como me estoy conectando por dhcp, puedo tocar la configuración por defecto del dhclient.

Para eso edité /etc/dhcp/dhclient.conf y le puse que use el dns de google (8.8.8.8) como dns por default, Agregando la línea

prepend domain-name-servers 8.8.8.8;

justo antes de la línea que empieza con request.

Ahora usa el DNS de google antes de los de fibertel :D.

Happy hacking,
Aureliano

2015-01-06

web2py, DAL y las migraciones

Hace un tiempo estoy usando web2py. Web2py es un frameworks que ya viene con un montonazo de cosas para hacer páginas web. Una que estoy usando bastante es el layer de abstracción de datos (DAL), que te abstrae de la base de datos. El DAL, en teoría, se encarga de migrar la base cuando hay un cambio en el modelo de datos. Suena muy lindo, pero en la práctica tiene algunos problemillos.
¿Cómo hace para saber cómo migrar? En el directorio databases de la webapp genera unos archivos .table donde tiene guardada la estructura actual de la base y cuando evalúa los modelos los compara contra lo que tiene generado.Y cuando te conectás a la base desde un solo host, anda bien.
El problema es cuando clonás tu repo y te conectás a la base desde otro lado. ¿Qué hacer con esos archivos? Encima, el nombre de los archivos incluye como prefijo un hash del connection string. Entonces si cambiás el password deja de encontrarlos, si cambiás el nombre del host deja de cambiarlos, etc.
¿Entonces qué se puede hacer?

  1. Cuando construís el DAL podés pasarle el prefijo para las tablas por parámetro en el parámetro table_hash.
  2. Si no querés cambiar la base, también cuando construís el DAL, podés pasarle migrate=False.
  3. Creo que fake_migrate=True genera los archivos pero no altera la base y puede servir para generar las cosas que hacen falta para migrar después.
Pero esto es una chanchada. ¿Qué es lo que me gustaría a mi que pase?
  • Lo ideal es que web2py mire la estructura de la base de datos y en función de eso decida si tiene que migrar o no.
  • Si eso no es posible o es muy complicado, en vez de guardar archivos en un directorio debería guardar la información que necesita para hacer las migraciones de la base de datos en una tabla de la base de datos, así la información estaría en todos lados.
Si quieren ver los detalles, miren dentro de web2py la clase DAL que está en el archivo dal.py.

Happy hacking,
Aureliano.

2014-12-16

Manejando errores de S3 en boto

Update: No sé porqué, después de usar la solución que puse acá sigo viendo errores de reseteo de conexión (error: [Errno 104] Connection reset by peer)

Estoy manejando archivos grandes (1 GB) en S3 con boto y, aún cuando estoy corriendo en una instancia en EC2, da errores. Si no entendí mal el código de boto, es porque hay algunas excepciones que da en las conexiones que no está reintentando.

Para entrar en el código de retries de boto con estas excepciones hay que registrarlas, al menos en boto 2.20.1.

Para eso, cuando me conecto a S3 hago:

import boto
import httplib
import socket
import ssl

s3 = boto.connect_s3(
    
    settings.s3_access_key_id,
    settings.s3_secret_access_key,
    https_connection_factory=(httplib.HTTPSConnection, (
        ssl.SSLError, socket.error
    ))
)

Eso evita los errores que estoy viendo (error: [Errno 104] Connection reset by peer y SSLError: The read operation timed out).

Espero que les sirva,
Aureliano.

2014-12-04

Mejorando el mirroring

Hace un tiempito hice un post sobre lo que estoy haciendo para sincronizar directorios con rsync en instancias de EC2. Esto funcionó muy bien para directorios chiquitos, pero cuando tenés más de 100 megas y más de 1400 archivos cada iteración se hace un poco lenta y usa mucho bandwidth. Y no creo que Fibertel esté muy contento con eso.

Así que estuve buscando alguna versión que detecte los cambios que se hacen en los archivos, para no estar mirando todo todo el tiempo. Y lo encontré :D. Existe una cosa que se llama lsyncd, que hace eso :D. Si estás en ubuntu, para instalarlo solo hay que hacer sudo apt-get install lsyncd. Y con eso hice un scriptcito que busca el nombre de la instancia de EC2 con la que me interesa sincronizar y lo usa para mantenerlo sincronizado.

#!/usr/bin/env python

import subprocess
import sys
import time
import boto
import getpass
import tempfile

AKI = "AKIVAUNACCESSKEYID"
USER = "unusername"
INAME = "NOMBRE DE LA INSTANCIA"
TARGET = "DIRECTORIO EN EL TARGET"
SOURCE = "."

CONF_TEMPLATE="""\n
settings {
  logfile = "lsyncd.log",
  statusFile = "lsyncd.status",
  nodaemon = true,
  statusInterval = 2,
  maxProcesses = 8,
}

sync {
  default.rsyncssh,
  source=".",
  host='%(host)s',
  targetdir='%(targetdir)s',
  exclude={
    "*.pyc",
    "*.log",
    "*.swp",
    "*.status",
    "*/.hg/*",

  },
  delay=2,
}
"""

def main():
    sak = getpass.getpass("Enter secret access key:")
    ec2 = boto.connect_ec2( AKI, sak )
    dns_name = ec2.get_only_instances(filters={"tag:Name":INAME})[0].dns_name
    host = USER + "@" + dns_name
    targetdir = TARGET   
    with tempfile.NamedTemporaryFile() as t:
        print CONF_TEMPLATE % locals()
        t.write(CONF_TEMPLATE % locals())
        t.flush()
        subprocess.check_call("lsyncd -nodaemon -log Exec".split(" ") + [t.name])

if __name__ == '__main__':
    main()


Happy hacking,
Aureliano

2014-11-23

Cómo hablar en portugués, mal

Hace muchos años hice software para Brasil. Parte de ese desarrollo requirió que pueda hablar con much@s brasileñ@s. Acá les cuento algunos truquitos que hacen mucho más fácil la comunicación.

 

Aprendé algunas palabras

Sabiendo 50 (o 10) palabras en portugués podés avanzar un montón. Y lo más probable es que algunas ya las conozcas. Te cuento alguna que me acuerdo:
  • obrigado: gracias
  • eu: yo
  • você: vos
  • a gente: nosotros
  • loja: negocio
  • eu te quero: te quiero cojer
  • ficar: estar/ quedarse en
  • batata: papa
  • frango: pollo

 

Imitá la tonada

Aunque suene increíble, solo imitando la tonada brasileña te entienden mucho más. Para eso podés tener en cuenta algunas cosas:
  • Las "h" al principio generalmente se transforman en "f". Por ejemplo "hacer" es "facer"
  • Las "j" se suelen pronunciar como "ll" (no como yé). Por ejemplo, "muller" (mujer) y "ollos" (ojos)
  • Muchas palabras que terminan en consonante se les agrega una "i" al final. Por ejemplo, Varig se pronuncia "Varigi"
No hace falta que imites la tonada bien para que sirva para comunicarse. 

 

Usá palabras largas

Las palabras de 4 o más sílabas casi siempre se corresponden con las palabras en portugués, solo teniendo que cambiar un poquito la pronunciación haciendo las cosas de las que hablé en el punto de arriba. También tienen la ventaja de que al ser muy largas, si pronunciás mal una parte te entiendan igual.

 

Olvidate de las conjugaciones de los verbos

Las conjugaciones de los verbos son distintas en castellano y portugués y siempre van a salir mal. No te preocupes por eso.

 

No tengas miedo

Si tratás de hablar en portugués siguiendo estas reglas no vas a estar hablando correctamente. Eso, en mi experiencia, no importa. Es más, se dan cuenta que estás haciendo un esfuerzo para comunicarte con ell@s. Y eso hace que muchas veces estén dispuest@s a hacer lo mismo y tratar de ayudarte en la conversación. Aparte l@s brasileñ@s se caracterizan por la buena onda.

Éxitos,
Aureliano.

2014-11-22

Projecto de ejemplo de sandro

Acabo de hacer público el projecto taleca. Ahí uso sandro para hacer una visualización de algunos datos que se usaron en el Hackatón de agro de la Fundación Sadosky. Es un ejemplo simple pero muestra cómo usar el general update pattern para hacer una visualización de d3 en sandro.

Te propongo que saquemos a sandro de las penumbras,
Aureliano.