jueves, 4 de octubre de 2007

Django, mod_python y SELinux

Hoy pasé algunas horas buscando una forma fácil de configurar mis aplicaciones de Django con Apache. Algo que me permitiera agregar aplicaciones sencillas y mantenerlas funcionando como si fueran scripts de php.

Lo más sencillo fue usar mod_python. Mi Apache ya lo cargaba y las instrucciones en la documentación de Django son claras. Como me agrada usar anfitriones virtuales, basta tener:


ServerName midominio.net
DocumentRoot /var/www/html

MaxRequestsPerChild 1 #Sólo para pruebas, si no es una grosería.

ServerName subdominio.dominio.net

SetHandler python-program
PythonHandler django.core.handlers.modpython
PythonPath "['camino_a_un_directorio_arriba_del_proyecto', 'camino_uno_arriba_de_las_aplicaciones'] + sys.path"
SetEnv DJANGO_SETTINGS_MODULE [proyecto].settings
PythonDebug On


SetHandler None


SetHandler None



La parte truculenta vino de SELinux. Los proyectos de Django (y sus aplicaciones) no deben ir en la ruta de Apache, lo cual lo hace especialmente seguros. Sin embargo, al tener mis archivos, inocentemente, en algún profundo lugar de mi hogar, aunque agregara el directorio con PythonPath, me aparecía un error indicando que el módulo [proyecto].seetings no existía:

EnvironmentError: Could not import settings '____.settings'
(Is it on sys.path? Does it have syntax errors?): No module named wap.settings

El verdadero problema es que, no sólo apache debe tener los permisos normales de lectura, sino que toda la ruta hasta el proyecto (y todos los archivos de éste) deben tener el contexto para la web:

user_u:object_r:httpd_sys_content_t

Copiar mis archivos a un directorio en el que pudiera otorgar dichos permisos con

$ chcon -R user_u:object_r:httpd_sys_content_t [directorio]

resolvió el problema.

martes, 24 de julio de 2007

Script que obtiene el nombre del día de ayer

Por increíble que parezca, este ejemplo no es una ociosidad, lo utilicé el vida real para nombrar carpetas de archivos de respaldo:



#!/bin/bash
# +++++++++
# Calcula el dia de ayer.
# +++++++++

#
# Obtiene el nombre del dia de la semana almacenado en $NDIA.
#
getDia () {
case $1 in
1) DIA=lunes ;;
2) DIA=martes ;;
3) DIA=miercoles ;;
4) DIA=jueves ;;
5) DIA=viernes ;;
6) DIA=sabado ;;
7) DIA=domingo ;;
*) echo "$1 No es un dia de la semana valido"
exit $DIA_MAL
;;
esac
}

NDIA=`date +%u`
getDia $NDIA
echo Hoy es $DIA

let "NAYER=$NDIA - 1"
if [ $NAYER -eq 0 ]
then
NAYER=7
fi
getDia $NAYER
echo Ayer fue $DIA

sábado, 19 de mayo de 2007

Capturas de pantalla - VLC

Dejo esto como un recordatorio:
* Tomado en parte del FAQ de VLC

Para poder tomar capturas de un video en ejecución se require:
(Linux)
  1. Iniciar con vlc -V x11
  2. Seleccionar Opciones -> Preferencias... En la pestaña Video, inidicar el directorio y el formato donde se guardarán las imágenes y el formato.
  3. Presionar "Captura de pantalla" cada vez que quiera :)
Si no se cumple el paso 1, los otros dos no bastan, no se guarda nada. Sin embargo, tampoco es recomendable dejar vlc con -V x11 por defecto, pues la reproducción pierde calidad.

jueves, 12 de abril de 2007

MVC. Datos, lógica y vista.

Las vistas no tienen objetos, los objetos tienen forma de ser vistos.

Llevo un rato tratando de programar un manejador de contenido para páginas en la red. No es la primera vez que intento separar las tres famosas capas "datos, lógica y vista". Tradicionalmente, o al menos eso entendí en ingeniería de software, la capa de lógica se monta sobre la de los datos, y la vista sobre ésta. Sin embargo, surge de modo más natural escribir en los objetos cómo se ven estos, en lugar de tener una vista genérica que pueda mostrar a cualquier objeto y creo que la razón es sencilla: la forma en que se ven los objetos depende de sus propiedades. Entonces, una de dos, o no he logrado entender el concepto de las tres capas, o algo está chueco en su concepción. ¿Cómo puedo separar la presentación? Es claro que, el cómo se ve una cosa depende fundamentalmente de qué tipo de atributos tiene; si no conozco sus atributos y más aún, la semántica de esos atributos, es imposible que decida cómo debe verse.

En todo caso, se me ocurre que la idea de que las tres capas son independientes en, realidad, una falasia. A lo más, pensándolo como una pila, le concedo que la capa de datos sea independiente, ésta no requiere ser modificada si hay cambios en la lógica (control), ni en la vista. Pero a la inversa en ningún modo se cumple, por el contrario, si modifico el modelo, debo modificar la lógica que lo controla y la forma en que se ve. En el siguiente nivel: si modifico en control, lo más seguro es que tenga que modificar la vista. Por último, puedo modificar la vista (la puritita presentación gráfica, auditiva, etc.) sin tocar el control, ni el modelo.


Tal vez alguien más familiar con la técnica me diga ¡felicidades! ¡acabas de entender cómo se hace el hilo negro!

Sin embargo toda esta preocupación surge de un caso muy concreto: la intefaz de administración de django. Leyendo entre los correos de usuarios y desarrolladores, hay cierta molestia por el hecho de que el código que define la presentación en la administración está dentro de los modelos, siendo que estas dos capas no debieran trabar contacto.

El problema, como he llegado a verlo, es que los modelos (tablas en una base de datos) no representan más que una manera de hacer persistentes a los objetos, pero las bases de datos no pueden incluir toda la lógica de sus interacciones, ésta en realidad se mantiene en la capa siguiente (gracias al artículo de la Wikipedia). El problema es que ¿quién puede saber de antemano lo que será puesto en la capa lógica? Las implementaciones en las bases de datos limitan claramente qué puede ser almacenado ahí (tipos de datos, relaciones, etc.), pero en la lógica uno puede escribir prácticamente lo que sea. ¿Cómo podría entonces crearse una vista genérica, sin descender a los modelos?

Peor aún, y aquí viene mi muy particular dilema: ¿qué pasa cuando uno requiere hacer persistente cómo se ven los objetos? Es decir, que la forma en que los modelos son vistos en pantalla, también forma parte de los datos. ¿Cómo mantener aquí la consistencia sin que la capa lógica se convierta en una locura?

Por el momento intento que cada nuevo elemento contenga 3 cosas: su propia forma de ser persistido, su lógica y ... opciones para ser visto pero... éstas son las que ya me cuesta más trabajo definir en una manera consistente para todos los elementos (evitando abundar en los códigos especializados a cada caso), esto debe ser lo más general posible.

Me gustaría que los elementos que están contenidos dentro de otros simplemente extendiesen la presentación de sus contenedores, como funcionan los css y evitando persistir la información de la presentación en los mismos modelos (tablas) que el contenido en sí del objeto. De modo que, hasta donde voy ahora... mi modelo parece quedar igual que la administración de django: el objeto tiene la liga a "cómo se ve". ¿pero qué no es eso lo natural? ¿que las cosas tengan una apariencia y no la apariencia a las cosas?

miércoles, 4 de abril de 2007

Juegos II y pygtk

Se me ocurrió actualizar python, pasándome a la versión 2.5. Obvio, tuve que reinstalar todo lo que ya tenía :( django, psycopg, etc., etc. y pygame!

Siguiendo el procedimiento de Juegos I, ahora debí bajar Numeric, ojo, debe ser el viejito (el del 2005), Numpy no satisface los requerimientos.

También hubo que reinstalar OpenGL y pygtk. Este último fue el más problemático: hubo que instalar un nuevo pyobject, en Fedora 5, éste quedó en /usr/local/lib/python2.5/site-packages/gtk-2.0/gobject pero al hacerlo aún detectaba la versión anterior (Requested 'pygobject-2.0 >= 2.12.1' but version of PyGObject is 2.8.4). Tal parece que esto es porque no se instaló como biblioteca del sistema, de hecho, al instalar, aparece el siguiente mensaje:

/usr/bin/install -c -m 644 'pygobject.h' '/usr/local/include/pygtk-2.0/pygobject.h'
...
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/lib/python2.5/site-packages/gtk-2.0/gobject
...
----------------------------------------------------------------------

configure usa libtool, por lo que debe bastar con indicarle este directorio de alguna manera.

Entonces, antes de invocar .configure, intento exportar las variables:

# export PYGOBJECT_CFLAGS="-I/usr/local/include/pygtk-2.0/"
# export PYGOBJECT_LIBS=/usr/local/lib/python2.5/site-packages/gtk-2.0/gobject

Pero aún falta pycairo (intenté compilar sin él, pero sigue habiendo errores).
Con cairo es la misma historia: compilo e instalo cairo 1.4.2 y queda en:

----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/lib
...
----------------------------------------------------------------------
/usr/bin/install -c -m 644 'cairo.h' '/usr/local/include/cairo/cairo.h'
...

Así que, para pycairo asigno:
# export CAIRO_CFLAGS="-I/usr/local/include/cairo"
# export CAIRO_LIBS="-I/usr/local/lib"

Y sigo las instrucciones de instalación... Dice que sí... ¡pero pygtk sigue sin funcionar! Y ya me cansé... me voy a dormir y ya mañana veo.

martes, 6 de marzo de 2007

Django con Fast-Cgi II

Después de varios intentos, logré configurar Django para ser servido a trevés de un vhost de Apache y FastCGI.

Primero, el proyecto de Django existe en algún lugar de mi PC, fuera del camino de Apache y sin preocuparse por su existencia. Se utiliza:

python manage.py runfcgi daemonize=true method=threaded host=$FCGIHOST port=$FCGIPORT pidfile=$FCGIPIDFILE

para ejecutar el servidor FastCGI.

Para versiones anteriores de Apache, basta con bajar mod_fastcgi del sitio de FastCgi, pero para Apache 2.2, hay que parchar el código. Craic ofrece una muy buena guía para arreglar el mod_fastcgi.c:271: error: 'ap_null_cleanup' undeclared (first use in this function) que aparece al intentar compilar.

Después de haber instalado mod_fastcgi, dentro de http.conf coloqué las siguientes indicaciones:

# Al final de los módulos a incluir:

LoadModule fastcgi_module modules/mod_fastcgi.so

# Fastcgi va después de estas líneas

User apache
Group apache
<IfModule mod_fastcgi.c>
FastCgiIpcDir /tmp/fcgi_ipc/
AddHandler fastcgi-script .fcgi

# Servidor externo para usar django con fastcgi.
FastCGIExternalServer /var/www/fastcgi/fastcgi.fcgi -host 127.0.0.1:3033

# En Apache 2.2 es necesario especificar permisos de acceso.
<Directory "/usr/local/apache2/fastcgi">
Order allow,deny
Allow from all
</Directory>
</IfModule>

Como (por motivos particulares de mi proyecto) quiero acceder a FastCGI desde diferentes vhosts hice que FastCGIExternalServer apunta a alguna dirección neutra, en este caso /var/www/fastcgi/fastcgi.fcgi.

Dentro de la configuración de vhost indico:


DocumentRoot "/sudomain_path/dynamic_docs"
Options +FollowSymLinks

RewriteEngine On
#Todas aquellas direcciones, menos '/', que no correspondan a un archivo en disco, serán atendidas por el servidor FastCGI.

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -!f #Si el archivo estático existe en DocumentRoot, se sirve ese.
RewriteCond %{REQUEST_URI} !(/var/www/fastcgi/fastcgi.fcgi) #El 'pseudo archivo' de FastCGI.
RewriteCond %{REQUEST_URI} !(/media) #No quiero que FastCGI sirva imágenes, css, etc.
RewriteCond %{REQUEST_URI} !(/static) #Un lugar para httpdocs :)
RewriteRule ^/(.+)$ /var/www/fastcgi/fastcgi.fcgi/$1 [QSA,L] #Ojo: '/' no es para FastCGI, así preservo index como valor por defecto estático. Si quiero que django sirva '/' también, uso RewriteRule ^(.+)$ /var/www/fastcgi.fcgi$1 [QSA,L]

Alias "/static" "/subdomain_path/httpdocs"
<Directory subdomain_path/httpdocs>
# Aquí insisto en usar scripts de python dentro de httpdocs. Sólo si el archivo acaba en .py :)
<IfModule mod_python.c>
<Files ~ (\.py$)>
SetHandler python-program
PythonHandler mod_python.publisher
</Files>
</IfModule>
</Directory>

Demasiados semáforos

Hoy Apache murió súbitamente cuando intenté configurar algunas cosas que ya había modificado antes. Obtuve:

[emerg] (28)No space left on device: Couldn't create accept lock

Seguido de algunos errores más. Afortunadamente la solución estaba a la mano: Apache había sobrepoblado al sistema de semáforos. Aún me pregunto porqué hizo tantos. La parte importante es utilizar:

[]$ for semid in `ipcs -s | grep apache | cut -f2 -d" "`; do ipcrm -s $semid; done

Cuando apache ya no esté siendo ejecutado, para eliminar a los semáforos fantasmas.

martes, 16 de enero de 2007

OpenSceneGraph

Para programar ambientes virtuales en 3D se recomienda OpenScenGraph.

En caso de que, al compilar en linux, se obtengan errores por la falta de un gif_lib.h, instalar giflib y libungif.

sábado, 13 de enero de 2007

Red en casa

La meta es acceder a internet desde dos computadoras, contando tan sólo con una conexión.

Para ello le he agregado una tarjeta ethernet pci extra a mi computadora con linux.

1) La tarjeta nueva fue colocada arriba de la anterior (o a la izquierda). Esto provocó conflictos al momento de activar la nueva tarjeta. Las direcciones físicas de eth0 y eth1 quedaron confundidas, dando origen a errores desagradables como " eth1: error fetching interface information: Device not found" o algo como que no estaba el dispositivo tulip, "retrasando la inicialización". En los foros, estos errores suelen deberse a que eth1 no ha sido dada de alta, en este caso los comandos dmesg e ifconfig -a me proveyeron de la información necesaria para encontrar la causa correcta. La solución al problema fué:

Entrar a la interfaz gráfica (Administración - Redes) borrar las interfaces (eth0 y eth1) y los registros del hardware; reiniciar la computadora y utilizar de nuevo la interfaz gráfica para darlos de alta otra vez.

Con esto las direcciones de los dispositivos se enderezaron (aunque ahora la tarjeta que solía ser eth0 se convirtió en eth1).

2) Seguir la guía en el rincón del programador. La versión resumida es:
  1. Editar /etc/sysctl.conf para asignar net.ipv4.ip_forward = 1
  2. Reiniciar la interfaz de red con $ service network restart
  3. Comprobar que forwarding fue activado con $ cat /proc/sys/net/ipv4/ip_forward (se debe imprimir un 1).
  4. Indicar que haga los cambios de ip necesarios cuando recibe solicitudes para internet:
    $/sbin/iptables -t nat -A POSTROUTING -o [INTERFAZ_INTERNET] -j MASQUERADE
    donde interfaz_internet es el dispositivo con el que nos conectamos a internet (eth0, ppp0 para un modem)
  5. Asegurar con:
    $ /sbin/iptables -A FORWARD -i ppp0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    $ /sbin/iptables -A FORWARD -i eth0 -o ppp0 -j ACCEPT
    $ /sbin/iptables -A FORWARD -j DROP // ¿Y si quiero usar ssh?
El punto 5 es opcional. Sin embargo, para mí no ha terminado. ping funciona dentro y fuera de mi red... pero sólo con direcciones numéricas. Falta arreglar el DNS.

miércoles, 10 de enero de 2007

Memcached

Memcaché es un supersistema para guardar páginas dinámicas en un caché que sirve sitios con mucho tráfico.

Instalarlo requiere libevent. Sin embargo hay un problema común: config encuentra la librería cuando está ahí, pero al momento de ejecutar:

[]$ memcached -d -m 1024 -l 127.0.0.1 -p 3034 -P mem.pid

Se obtiene el error:

memcached: error while loading shared libraries: libevent-1.2a.so.1: cannot open shared object file: No such file or directory

Utilizando:

[]$ LD_DEBUG=libs memcached -v

Se puede ver en qué directorios está buscando la biblioteca. Éstos corresponden a un "system search path" que no incluye directorios como /usr/local/lib. Si libevent fue instalada aquí, aunque se haya configurado con ./configure --with-libevent=/usr/local/lib éste directorio no aparece. Esperaba encontrar alguna manera de arreglarlo al compilar memcached, pero no he podido, la única solución que se me ocurrió (y otros han usado) es crear una liga suave desde un directorio en el camino de búsqueda hacia la biblioteca:

[]& ln -s /usr/local/lib/libevent-1.2a.so.1 /usr/lib

Me encantaría saber de una solución más elegante.

viernes, 5 de enero de 2007

Django con Fast-Cgi

Aquí wmldocs está fuera de DocumentBase.
En httpd.conf (fuera de cualquier virtual host):

FastCGIExternalServer /.../wmldocs/mysite.fcgi -socket /home/.../mysite/mysite.sock

(Detro del host virtual, si hay uno):

Alias "/cel" "/.../wmldocs"

RewriteEngine On
RewriteBase /.../wmldocs
RewriteCond %{REQUEST_FILENAME} !-f #Si lo que se solicita no es un archivo
RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L]


Ejecutar el servidor de fcgi (daemonize=false se usa para depurar):

[]$ python manage.py runfcgi daemonize=false method=prefork socket=/home/.../mysite/mysite.sock pidfile=/home/.../mysite/mysite.pid

Detener el servidor:

[]$ kill `cat $PIDFILE`
[]$ rm -f $PIDFILE

Aún obtengo:

(13)Permission denied: FastCGI: failed to connect to server

Después de no encontrar el motivo del fallo (apache corre con un usuario, el subdominio con otro, fastcgi requiere permisos de lectura/escritura, etc.) opté por cambiar al uso del puerto TCP:

python manage.py runfcgi daemonize=false method=threaded host=127.0.0.1 port=3033 pidfile=/home/.../mysite/mysite.pid

FastCGIExternalServer /.../wmldocs/mysite.fcgi -host 127.0.0.1:3033


La aplicación manejada por django se ve en http://subdominio.servidor.com/cel/mysite.fcgi/polls/

martes, 2 de enero de 2007

[Psycopg] configure.in patch for pg_type.h path on Red Hat

Cambio la idea original de Vsevolod Lobko
para que funcione en RedHat (postgresql por pgsql, se podría agregar otro if).
RedHat Fedora o Enterprise port for PostgreSQL puts server headers in
/usr/local/include/pgsql/server/
So proposed patch adds this directory to list of possible
locations for pg_type.h
-------------- next part --------------
--- configure.in.orig Sat Jul 26 16:16:54 2003
+++ configure.in Sun Jul 27 15:59:19 2003
@@ -72,6 +72,12 @@
PGSQLTYPES="$PGSQLDIR/server/catalog/pg_type.h"
PGSQLDIR="$PGSQLDIR/server"
CPPFLAGS="$CPPFLAGS -I$PGSQLDIR"
+ else
+ if test -d $PGSQLDIR/pgsql/server/catalog ; then
+ PGSQLTYPES="$PGSQLDIR/pgsql/server/catalog/pg_type.h"
+ PGSQLDIR="$PGSQLDIR/pgsql/server"
+ CPPFLAGS="$CPPFLAGS -I$PGSQLDIR"
+ fi
fi
fi
test -z "$PGSQLTYPES" && AC_MSG_ERROR(pg_type.h not found)