Add: New posts from the legacy blog

remotes/oscarmlage/main
Óscar M. Lage 2022-04-06 21:17:47 +02:00
parent 54468e24e3
commit 269b08bdb4
409 changed files with 1752 additions and 0 deletions

View File

@ -0,0 +1,24 @@
---
title: "Apache: Alta carga de CPU"
date: 2011-08-09T10:27:52Z
draft: false
tags: [ "sysadmin" ]
image:
---
<p>
Llevo desde el fin de semana con la mosca detr&aacute;s de la oreja. Uno de los servidores que administro ha visto incrementada de forma inexperada su carga media de CPU sin motivo aparente. Donde el <em>load average</em> normal de 1 minuto variaba entre <em>0.40</em> y <em>0.80</em> de repente supon&iacute;a cargas tan elevadas como <em>60</em> o <em>100</em> unidades.</p>
<p>
En esos momentos puntuales que llegaban a dejar la m&aacute;quina <em>zombie</em> el proceso que abarcaba un consumo de entre el <em>60%</em> y el <em>90%</em> de CPU era <em>apache2</em>. Intrigante que ni <em>error.log</em> ni <em>slow-queries.log</em> de MySQL (lo que normalmente suele ser cuello de botella) dieran ninguna pista.<!--more--></p>
<p>
<em>Analytics</em> tampoco dec&iacute;a nada de un aumento considerable de visitas -m&aacute;s bien al contrario- y el resto de herramientas de monitorizaci&oacute;n parec&iacute;an c&oacute;mplices del problema (&iexcl;tener <em>tools</em> para &eacute;sto!).</p>
<p>
En un alarde de desesperaci&oacute;n y viendo m&aacute;s o menos por d&oacute;nde pod&iacute;an venir los tiros -a trav&eacute;s del m&eacute;todo de <em>prueba y error</em>- localic&eacute; el <em>VirtualHost</em> que estaba dando problemas, un c&oacute;digo no auditado y afamado por su ausente optimizaci&oacute;n. Vamos que ya ten&iacute;a precedentes, aunque ninguno de esta &iacute;ndole.</p>
<p>
Despu&eacute;s de desactivar distintas partes del dominio en m&aacute;s pruebas esperando focalizar el error en alg&uacute;n script concreto, la conclusi&oacute;n es que ten&iacute;a que ser algo que se inclu&iacute;a en todos los archivos, algo com&uacute;n a toda la web. As&iacute; que miramos los <em>includes</em> comunes (si, PHP) y llegamos a la conclusi&oacute;n de que era <strong>problema de sesiones</strong>, sin exculpar al programador.</p>
<p>
Una de las primeras acciones de todos los scripts es definir el tiempo de sesi&oacute;n a <strong>3 d&iacute;as</strong>, definir el directorio donde se guardar&aacute;n los archivos de sesi&oacute;n y arrancar la sesi&oacute;n. Probablemente el programador no esperaba tener <em>60k</em> visitas y m&aacute;s de <em>200k</em> p&aacute;ginas vistas en los 3 d&iacute;as que dura cada sesi&oacute;n, pero est&aacute; claro que para <em>apache2</em> supon&iacute;a un problema el acceso de lectura/escritura a un mismo directorio con m&aacute;s de <em>90k</em> archivos.</p>
<p>
La soluci&oacute;n inicial -a falta de m&aacute;s tiempo para cambiar el sistema de sesiones a <em>memcache</em>, <em>Redis</em> o cualquier otra soluci&oacute;n basada en RAM- era sencilla, reducir el tiempo de sesi&oacute;n, vaciar el directorio donde se guardan las sesiones, comprobar de nuevo el <em>load average</em> durante un intervalo representativo y una tarea programada que monitorice el contenido de ese directorio. Despu&eacute;s de todo la carga se ha vuelto a estabilizar entre <em>0.30</em> y <em>0.40</em>.</p>
<p>
No s&eacute; si estoy para moralejas porque la soluci&oacute;n es temporal, pero como <a href="http://twitter.com/#!/r0sk/status/100688297642311681">lo prometido es deuda</a> me gustar&iacute;a terminar esta anotaci&oacute;n advirtiendo a todo el mundo sobre la fiabilidad del c&oacute;digo no auditado, el uso moderado de las sesiones y las endorfinas que libera uno cuando consigue resolver algo <em>&quot;as&iacute;&quot;</em>.</p>

View File

@ -0,0 +1,54 @@
---
title: "Apache + Squid + Nginx"
date: 2011-10-28T23:50:34Z
draft: false
tags: [ ]
image:
---
<p>
&iexcl;Menuda combinaci&oacute;n!. A decir verdad empec&eacute; jugando un poco con el maldito <em>slowloris</em> y al final acab&eacute; montando este batiburrillo de servidores, primero para <em>paliar</em> el efecto del dichoso gusano y segundo para preparar el servidor para la inminente nueva versi&oacute;n del blog - <em>que me gustar&iacute;a estrenar con el d&eacute;cimo aniversario de este humilde rinconcito</em> -.</p>
<p>
En un esquema inicial anal&oacute;gico de esos que tantos nos gustan podemos ver la pirula (pido perd&oacute;n de antemano por la calidad de la foto):<!--more--></p>
<p style="text-align: center;">
<img alt="" src="gallery/apache-squid-nginx.jpg" style="width: 300px; height: 305px;" /></p>
<p>
Probablemente no sea necesario tener 3 servidores para este entorno, seguro que puedo poner a escuchar Nginx en el puerto 80 y redirigir todo el tr&aacute;fico din&aacute;mico al 81 a la vez que responde al tr&aacute;fico est&aacute;tico de ciertos subdominios (static*) de forma autom&aacute;tica y en la misma instancia. Pero ten&iacute;a la tarde libre y me apetec&iacute;a probar una configuraci&oacute;n <em>rara</em> con Squid.</p>
<p>
<strong>Apache</strong></p>
<p>
La configuraci&oacute;n de Apache es de lo m&aacute;s sencilla, lo &uacute;nico que he hecho ha sido ponerlo a escuchar en el puerto 81 por defecto, y a todos sus <em>Virtualhosts</em> tambi&eacute;n. No hab&iacute;a mucho m&aacute;s que tocar puesto que ya ten&iacute;a el <em>mod_php</em> y todas las dependencias instaladas.</p>
<p>
<strong>Nginx</strong></p>
<p>
Nunca hab&iacute;a jugado con &eacute;l y a primera vista me gust&oacute; la sencillez de sus archivos de configuraci&oacute;n. Tampoco ten&iacute;a que hacer gran cosa, ponerlo a escuchar en el puerto 82 y poco m&aacute;s puesto que s&oacute;lo servir&iacute;a contenido est&aacute;tico. Cre&eacute; los <em>Virtualhosts</em> que atender&iacute;an las peticiones est&aacute;ticas, los activ&eacute; v&iacute;a enlace simb&oacute;lico a <em>sites-enabled</em> y poco m&aacute;s. La configuraci&oacute;n de un <em>Virtualhost</em> cualquiera:</p>
```
server {
listen 82;
server_name static.dominio.com;
root /home/www/dominio/sd/static/;
autoindex on;
}
```
<p>
<strong>Squid</strong></p>
<p>
Aqu&iacute; vino la diversi&oacute;n, &iquest;c&oacute;mo decirle a Squid que balanceara el tr&aacute;fico din&aacute;mico al puerto 81 y el est&aacute;tico al puerto 82?. Despu&eacute;s de leer la documentaci&oacute;n y hacer varias pruebas con <em>cache_peer</em>,&nbsp; y <em>cache_peer_domain</em> he llegado a la conclusi&oacute;n que la configuraci&oacute;n &quot;<em>buena</em>&quot; es la siguiente:</p>
```
cache_peer ip parent 81 0 no-query originserver name=server_1
cache_peer_domain server_1 dominio.com www.dominio.com
cache_peer ip parent 82 0 no-query originserver name=server_2
cache_peer_domain server_2 static.dominio.com
```
<p>
Como se puede ver, habr&iacute;a que cambiar <em>ip</em> por la ip p&uacute;blica correspondiente y los <em>fqdn</em> por los reales.</p>
<p>
<strong>Conclusi&oacute;n</strong></p>
<p>
He pasado una tarde agradable en compa&ntilde;&iacute;a de mis amigos los servidores web. No, en serio, al final he conseguido reproducir el escenario que me hab&iacute;a propuesto, (a&uacute;n sabiendo que se podr&iacute;a mejorar), he frenado los sockets incompletos de Apache y he preparado el servidor para servir est&aacute;ticos de forma independiente del contenido din&aacute;mico (que en breve cambiar&aacute; de PHP a Python + Django).</p>
<p>
No ha estado mal :).</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@ -0,0 +1,33 @@
---
title: "Bash shellshock bug update: loving Fabric"
date: 2014-09-26T08:36:17Z
draft: false
tags: [ ]
image: shellshock.jpg
---
<p>When you wake up in the morning with a new like the <a href="http://www.troyhunt.com/2014/09/everything-you-need-to-know-about.html">bash shellshock bug</a> you should get away your lazy part and update all your servers to new patched version. This is ssh every single server, check the vulnerability, update the package, check again that all is ok and move on to next host.</p>
<p>If you have tons of servers the task becomes tedious. To get a bit of fun you can choose to develop a <a href="http://www.fabfile.org/">Fabric</a> module that makes the job instead of you. The fun:</p>
```
def make_me_a_bashandwich():
puts(red('Checking bash vulnerability'))
out = sudo("env x='() { :;}; echo vulnerable' bash -c 'echo this is a test'")
if "vulnerable" in out:
puts(red('Vulnerable'))
puts(red('Updating bash'))
sudo('apt-get update')
sudo('apt-get install --only-upgrade %s' % package)
else:
puts(green('OK'))
```
<p>Your only job is to run that piece of code in all your servers and take a deep look to the screen:</p>
```
$ fab -H server1,server2,server3 -u root make_me_a_bashandwich
```
<p><img style="display: block; margin-left: auto; margin-right: auto; width: 100%; height: auto;" src="gallery/bash-shellshock.png" alt="" /></p>
<p>In my humble opinion, it's more fun than the old way, isn't it?</p>
<p><span>Image credit: <em>Robert Graham</em>,&nbsp;</span><a href="https://twitter.com/ErrataRob/status/514839781881032705" target="_blank">Twitter</a>.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,51 @@
---
title: "Cambiar kernel en ovh con grub (nfs)"
date: 2013-03-11T07:59:44Z
draft: false
tags: [ ]
image:
---
<p>El principal problema que existe para montar un servidor NFS en&nbsp;<span class="search_hit">OVH</span>&nbsp;es que los kernels que habilitan por defecto (<span class="search_hit">OVH</span>&nbsp;+ grsec), adem&aacute;s de venir con grsec como su propia descripci&oacute;n indica, no tienen soporte para instalar m&oacute;dulos de&nbsp;<span class="search_hit">kernel</span>. De forma que al intentar instalar el m&oacute;dulo para NFS nos dar&aacute; un fant&aacute;stico y maravilloso error. Por defecto tenemos el siguiente escenario:</p>
```
# uname -a
Linux ns302399.ovh.net 3.2.13-grsec-xxxx-grs-ipv6-64 #1 SMP Thu Mar 29 09:48:59 UTC 2012 x86_64 GNU/Linux
```
<p>As&iacute; que no queda otra que reemplazar el&nbsp;<span class="search_hit">kernel</span>&nbsp;por uno que ya lleve el m&oacute;dulo de nfs&nbsp;<em>builtin. </em>Llegados a este punto tenemos varias opciones: instalar un&nbsp;<span class="search_hit">kernel</span>&nbsp;desde fuentes, modificar el ya instalado o bajarnos uno de los kernels &rdquo;<em>-filer</em>&rdquo; de&nbsp;<span class="search_hit">OVH</span>. Los kernels "<em>-filer</em>" est&aacute;n pensados para este tipo de servidores de ficheros. Por sencillez optaremos por esta tercera opci&oacute;n.</p>
<p>Primeramente descargamos las im&aacute;genes del nuevo kernel que vamos a instalar desde el ftp oficial de&nbsp;<span class="search_hit">OVH</span>&nbsp;(<a class="urlextern" title="ftp://ftp.ovh.net/made-in-ovh/bzImage/" rel="nofollow" href="ftp://ftp.ovh.net/made-in-ovh/bzImage/">ftp://ftp.<span class="search_hit">ovh</span>.net/made-in-<span class="search_hit">ovh</span>/bzImage/</a>):</p>
```
# cd /boot/
# wget ftp://ftp.ovh.net/made-in-ovh/bzImage/3.2.13/bzImage-3.2.13-xxxx-grs-ipv6-64-filer
# wget ftp://ftp.ovh.net/made-in-ovh/bzImage/3.2.13/System.map-3.2.13-xxxx-grs-ipv6-64-filer
# ls
bzImage-3.2.13-xxxx-grs-ipv6-64 System.map-3.2.13-xxxx-grs-ipv6-64
bzImage-3.2.13-xxxx-grs-ipv6-64-filer System.map-3.2.13-xxxx-grs-ipv6-64-filer
grub
```
<p>Ahora tendremos que modificar <em>grub</em> para que al arrancar la m&aacute;quina lo haga con el nuevo&nbsp;<span class="search_hit">kernel</span>. Lo hacemos editando el fichero&nbsp;<em>/etc/grub.d/06_OVHkernel</em>, en la l&iacute;nea que antes hac&iacute;a referencia al&nbsp;<span class="search_hit">kernel&nbsp;</span><em>bzImage*64</em>&nbsp;ahora le agregamos un&nbsp;<em>-filer</em>&nbsp;y guardamos los cambios:</p>
```
OS="Debian GNU/Linux"
LINUX_ROOT_DEVICE=${GRUB_DEVICE}
linux=`ls -1 -t /boot/bzImage*64-filer 2&gt;/dev/null | head -n1`
```
<p>Una vez tenemos el nuevo&nbsp;<span class="search_hit">kernel</span>&nbsp;referenciado en el archivo de plantilla&nbsp;<em>06_OVHkernel</em>, ejecutamos el&nbsp;<em>grub-mkconfig</em>&nbsp;para hacer efectiva la configuraci&oacute;n, es recomendable hacer antes una copia de seguridad de la configuraci&oacute;n anterior (sabe m&aacute;s el diablo por viejo que por diablo):</p>
```
# cp /boot/grub/grub.cfg /boot/grub/grub.cfg.old
# grub-mkconfig &gt; /boot/grub/grub.cfg
```
<p>Finalmente toca reiniciar, cruzar los dedos y, si todo ha ido correctamente, comprobar que ya tenemos el nuevo&nbsp;<span class="search_hit">kernel</span>&nbsp;listo para hacer funcionar nuestro servidor NFS:</p>
```
# uname -a
Linux ns302399.ovh.net 3.2.13-grsec-xxxx-grs-ipv6-64-filer #1 SMP Thu Mar 29 11:26:19 UTC 2012 x86_64 GNU/Linux
```
<p>No es complicado, pero es enga&ntilde;oso pensar en montar un NFS sobre OVH como una trivial tarea de poco m&aacute;s de 5 minutos.&nbsp;</p>

View File

@ -0,0 +1,30 @@
---
title: "¿Codeigniter-Reactor + esteroides?"
date: 2011-02-04T20:09:38Z
draft: false
tags: [ "php", "code" ]
image:
---
<p>
Es algo que todav&iacute;a no llego a entender ni asimilar. He pasado la mayor parte de la tarde para configurar la nueva versi&oacute;n 2.0 de <a href="https://github.com/philsturgeon/codeigniter-reactor/">CodeIgniter-Reactor</a> con varias librer&iacute;as que, para mi forma de desarrollar, son indispensables en cualquier framework de programaci&oacute;n orientado a web:</p>
<ul>
<li>
<a href="https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc">HMVC</a>: Gracias a la librer&iacute;a de <em>Wiredesignz</em> podemos ordenar nuestro c&oacute;digo en m&oacute;dulos y simplificar la l&oacute;gica de la aplicaci&oacute;n.</li>
<li>
<a href="https://github.com/pyrocms/pyrocms/tree/master/system/pyrocms/modules/modules">ModulesModule</a>: Ahora que todo ser&aacute; un m&oacute;dulo, no vendr&iacute;a mal un m&oacute;dulo encargado de ejecutar las tareas m&aacute;s comunes de los m&oacute;dulos (instalar, desinstalar, actualizar...). Un m&oacute;dulo de m&oacute;dulos.</li>
<li>
<a href="https://github.com/pyrocms/pyrocms/tree/master/system/pyrocms/modules/settings">SettingsModule</a>: No me gusta cargar la configuraci&oacute;n desde ficheros <em>config/*</em>, por comodidad y para que el usuario pueda cambiar cualquier par&aacute;metro ajustable de una aplicaci&oacute;n, prefiero hacerlo en base de datos y cachearlo a disco si es necesario. Me quedo con la librer&iacute;a de <a href="http://pyrocms.com">PyroCMS</a>.</li>
<li>
<a href="https://github.com/pyrocms/pyrocms/tree/master/system/pyrocms/modules/themes">ThemesModule</a>: Otra caracter&iacute;stica imprescindible ser&iacute;a tener una aplicaci&oacute;n <em>themeable</em> y que desde un interfaz de administraci&oacute;n se pueda cambiar tranquilamente el aspecto de la misma. Para ello podemos hacer uso de este m&oacute;dulo capaz de instalar y desinstalar themes.</li>
<li>
<a href="https://github.com/pyrocms/pyrocms/blob/master/system/pyrocms/libraries/Template.php">TemplateLibrary</a> y <a href="https://github.com/pyrocms/pyrocms/blob/master/system/pyrocms/libraries/Tags.php">TagsLibrary</a>: Una vez hemos decidido hacer la aplicaci&oacute;n <em>modular</em> y <em>themeable</em> siguiendo el patr&oacute;n <em>MVC</em>, un buen lenguaje de <em>template</em> para que los dise&ntilde;adores no se vuelvan locos con el lenguaje din&aacute;mico ser&iacute;a un punto m&aacute;s.</li>
<li>
<a href="https://github.com/philsturgeon/codeigniter-migrations/">MigrationsLibrary</a>: Una vez lo pruebas se convierte en <em>musthave</em>. Se trata de una librer&iacute;a para hacer migraciones de versiones en base de datos. Gestiona los cambios entre versiones de forma sencilla.</li>
</ul>
<p>
<!--more-->Como pod&eacute;is ver siguiendo los enlaces, casi todo lo necesario para montar el esquema inicial de un framework PHP decente est&aacute; en los repositorios de <a href="https://www.philsturgeon.co.uk/">Phil Sturgeon</a> (<a href="https://bitbucket.org/philsturgeon/">bitbucket</a> y <a href="https://github.com/philsturgeon/">github</a>). Su <a href="http://www.pyrocms.org">PyroCMS</a> cuenta con todas estas caracter&iacute;sticas pero tiene muchas otras librer&iacute;as que no son necesarias -a mi modo de ver- para tomarlo como base para cualquier otro tipo de proyecto.</p>
<p>
Demasiado complejo de ensamblar, una vez he conseguido hacerlo funcionar m&aacute;s o menos todo ya no tengo ganas de empezar a programar la aplicaci&oacute;n que ten&iacute;a en mente. &iquest;Por qu&eacute; no hay *nada* con todas esas funciones&nbsp; de serie en PHP?. Si, ya s&eacute; que con <a href="http://www.rubyonrails.org/">Ruby on Rails</a> o <a href="http://djangoproject.org">Django</a> hubiera tenido todas estas caracter&iacute;sticas -y m&aacute;s- en un par de comandos, pero ten&iacute;a que desenga&ntilde;arme.</p>
<p>
En este sentido PHP sigue estando tan verde como hace a&ntilde;os, me hace perder demasiado tiempo a&uacute;n conociendo la estructura del lenguaje. Todos sabemos como es la curva de aprendizaje al principio, pero -aunque duela- ha llegado el momento de invertir en otra cosa. <em>Shame on me</em>?</p>

View File

@ -0,0 +1,255 @@
---
title: "Desarrollo web con Python: Flask"
date: 2012-02-15T18:47:34Z
draft: false
tags: [ ]
image:
---
<p>
Hace alg&uacute;n tiempo empec&eacute; a utilizar <a href="http://flask.pocoo.org/">Flask</a> para un proyecto personal. Flask es un mini framework de desarrollo en python que me ha convencido desde el principio por su sencillez. Intentar&eacute; exponer un peque&ntilde;o ejemplo para que os hag&aacute;is una idea de c&oacute;mo funciona.</p>
<p style="text-align: center; ">
<img alt="" src="gallery/flask.png" style="width: 200px; height: 78px; " /></p>
<p>
Y como <a href="http://toporojo.es/blog/">&Aacute;lex</a> ha escrito un <a href="http://toporojo.es/blog/2012/02/11/desarrollo-web-con-python-pylons/">completo tutorial sobre Pylons/Pyramid</a> con un ejemplo de aplicaci&oacute;n, no quer&iacute;a ser menos y explicar mis aventuras y desventuras con esta otra peque&ntilde;a joya de Python as&iacute; que all&aacute; vamos.</p>
<p>
<!--more--></p>
<p>
<strong>Instalaci&oacute;n&nbsp;</strong></p>
<p>
Flask est&aacute; basado en &nbsp;<a href="http://werkzeug.pocoo.org/">Werkzeug</a>, una librer&iacute;a WSGI para Python. Si usamos <a href="http://pypi.python.org/pypi/pip">pip</a> y <a href="http://pypi.python.org/pypi/virtualenv">virtualenv</a> para crear el entorno virtual y comenzar a trabajar (situaci&oacute;n que <a href="http://www.userlinux.net/django-virtualenv-pip.html">ya he explicado en su d&iacute;a</a>) vemos que las dependencias de Flask son m&iacute;nimas:</p>
```
$ sudo easy_install pip
$ pip install virtualenv
$ mkdir -p flaskblog/{src,env}
$ cd flaskblog/
$ virtualenv --distribute --no-site-packages env/
$ pip -E env/ install flask
Downloading Flask-0.8.tar.gz
Downloading/unpacking Werkzeug&gt;=0.6.1 (from flask)
Downloading/unpacking Jinja2&gt;=2.4 (from flask)
Installing collected packages: flask, Jinja2, Werkzeug
$ pip -E env/ install flask-wtf
Downloading Flask-WTF-0.5.2.tar.gz
Downloading/unpacking WTForms (from flask-wtf)
Installing collected packages: flask-wtf, WTForms
$ pip -E env/ install flask-sqlalchemy
```
<p>
Una &nbsp;vez creado el entorno virtual instalamos <em>Flask</em>, que depende de <em>Werkzeug</em> y de <em>Jinja2</em> como sistema de templates. Tambi&eacute;n instalamos <a href="http://packages.python.org/Flask-WTF/">Flask-WTF</a>&nbsp;para tener integraci&oacute;n con <em>WTForms</em> y hacer m&aacute;s sencillo el uso de formularios con funciones avanzadas (csrf, etc...). Y por &uacute;ltimo <em>Flask-SQLAlchemy </em>como complemento ORM para la base de datos.</p>
<p>
<strong>Requirements y configuraci&oacute;n</strong></p>
<p>
A continuaci&oacute;n vamos a entrar por primera vez en el entorno y crear el fichero de requisitos - yo normalmente le llamo <em>requirements.txt</em> - para poder regenerar el entorno virtual tantas veces y en tantas m&aacute;quinas como queramos:</p>
```
$ source env/bin/activate
(env)$ pip freeze &gt; src/requirements.txt
```
<p>
Si luego queremos regenerar el entorno a partir del fichero lo haremos ejecutando el siguiente comando:</p>
```
$ pip install -E nuevo_env/ -r src/requirements.txt
```
<p>
Empezamos a montar el esqueleto de la aplicaci&oacute;n dentro del directorio <em>src/&nbsp;</em>creando un directorio para los templates, como queremos que nuestra aplicaci&oacute;n pueda tener varios templates a elegir, creamos tambi&eacute;n el &quot;por defecto&quot;:</p>
```
(env)$ mkdir -p templates/default/
```
<p>
Ya que estamos empezando una aplicaci&oacute;n desde cero nos gustar&iacute;a tener un m&iacute;nimo de configuraci&oacute;n para la misma as&iacute; que vamos a usar un fichero para que guarde ciertos valores variables:</p>
```
(env)$ cat config.py
import os
DEBUG = True
_basedir = os.path.abspath(os.path.dirname(__file__))
DATA_PATH = os.path.join(_basedir, &#39;data&#39;)
DEFAULT_TPL = &#39;default&#39;
USERNAME = &#39;admin&#39;
PASSWORD = &#39;default&#39;
SECRET_KEY = &#39;devel secret key&#39;
URL = &#39;http://localhost:5000/&#39;
TITLE = &#39;OurApplication&#39;
VERSION = &#39;0.1&#39;
LANG = &#39;es&#39;
LANG_DIRECTION = &#39;ltr&#39;
YEAR = &#39;2012&#39;
del os
```
<p>
Con la configuraci&oacute;n y el entorno listos, lo siguiente ser&aacute; empezar la aplicaci&oacute;n propiamente dicha, as&iacute; que manos a la obra.</p>
<p>
<strong>Primeros pasos con Flask</strong></p>
<p>
Vamos a reducir el grueso de la aplicaci&oacute;n a un solo archivo, le llamaremos <em>blog.py </em>- ya s&eacute; que no suena muy original pero imagino que ser&aacute; entendible -. Nuestro <em>blog.py</em> ser&aacute; el controlador principal, en &eacute;l incluiremos de forma excepcional el modelo, los formularios y los m&eacute;todos que se encargar&aacute;n de la l&oacute;gica y de llamar a las plantillas. Para empezar por el principio definimos el archivo como una aplicaci&oacute;n Flask y cargamos la configuraci&oacute;n que antes hemos creado:</p>
```
# -*- coding: utf-8 -*-
&quot;&quot;&quot;
OurApplication
~~~~~~~~~~~~~~
:copyright: (c) 2011 by Oscar M. Lage.
:license: BSD, see LICENSE for more details.
&quot;&quot;&quot;
import os
from flask import Flask, render_template, request, redirect
from werkzeug.routing import Rule
# Flask application and config
app = Flask(__name__)
app.config.from_object(&#39;config&#39;)
# Middleware to serve the static files
from werkzeug import SharedDataMiddleware
import os
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
&#39;/&#39;: os.path.join(os.path.dirname(__file__), &#39;templates&#39;, app.config[&#39;DEFAULT_TPL&#39;])
})
# Index
@app.route(&#39;/&#39;)
def index():
return render_template(app.config[&#39;DEFAULT_TPL&#39;]+&#39;/index.html&#39;,
conf = app.config)
if __name__ == &#39;__main__&#39;:
app.run()
```
<p>
* Lo m&aacute;s raro son esas 3 lineas que hemos incluido en el archivo, una especie de <em>Middleware</em>&nbsp;para poder servir archivos est&aacute;ticos directamente desde el template seleccionado.</p>
<p>
Una vez tenemos la aplicaci&oacute;n &quot;preparada&quot; hemos de crear la plantilla para que se pueda ejecutar sin errores, para ello dentro del directorio templates/default/ creamos el archivo index.html al que referenciamos en el controlador, el contenido del archivo podr&iacute;a ser algo as&iacute;:</p>
```
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;
xml:lang=&quot;{{ conf[&#39;LANG&#39;] }}&quot; lang=&quot;{{ conf[&#39;LANG&#39;] }}&quot;
dir=&quot;{{ conf[&#39;LANG_DIRECTION&#39;] }}&quot;&gt;
&lt;head&gt;
&lt;title&gt;{% block title %}{% endblock %} [{{ conf[&#39;TITLE&#39;] }}]&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id=&quot;header&quot;&gt;
&lt;h1&gt;{{ conf[&#39;TITLE&#39;] }}&lt;/h1&gt;
&lt;/div&gt;
&lt;div id=&quot;content&quot;&gt;
Contenido
&lt;/div&gt;
&lt;div id=&quot;footer&quot;&gt;
&lt;p&gt;{{ conf[&#39;TITLE&#39;] }} - {{ conf[&#39;YEAR&#39;] }}&lt;/p&gt;
&lt;/div&gt;
&lt;/body&gt;
```
<p>
Y ya podr&iacute;amos ejecutar por primera vez nuestra aplicaci&oacute;n desde consola para luego comprobar que todo est&aacute; correcto en navegador, as&iacute; que vamos a ello:</p>
```
$ python blog.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader
```
<p>
Si abrimos el navegador en la url proporcionada (http://127.0.0.1:5000/) obtendremos una pantalla similar a la de la siguiente figura:</p>
<p style="text-align: center; ">
<img alt="" src="gallery/flask001.png" style="width: 300px; height: 219px; " /></p>
<p>
<strong>SQLAlchemy</strong></p>
<p>
Una vez tenemos la base y el esqueleto de la aplicaci&oacute;n vamos a incorporar una base de datos para hacerla din&aacute;mica. Para ello usaremos <a href="http://packages.python.org/Flask-SQLAlchemy/">Flask-SQLAlchemy</a>. Para trabajar con SQLAlchemy agregamos una nueva variable de configuraci&oacute;n para la base de datos:</p>
```
SQLALCHEMY_DATABASE_URI = &#39;sqlite:///&#39;+ os.path.join(os.path.dirname(__file__), &#39;database.db&#39;)
```
<p>
Y le decimos a <em>blog.py</em> el esquema que vamos a utilizar:</p>
```
from flaskext.sqlalchemy import SQLAlchemy
...
db = SQLAlchemy(app)
...
# Model
class Blog(db.Model):
__tablename__ = &#39;Blog&#39;
__mapper_args__ = dict(order_by=&quot;date desc&quot;)
id = db.Column(db.Integer, primary_key=True)
subject = db.Column(db.Unicode(255))
author = db.Column(db.Unicode(255))
date = db.Column(db.DateTime())
content = db.Column(db.Text())
```
<p>
Por &uacute;ltimo creamos la base de datos desde una consola python para poder usarla desde cualquier parte del controlador, tan simple como entrar a python desde el directorio src/ donde tenemos blog.py y ejecutar lo siguiente:</p>
```
&gt;&gt;&gt; from blog import db
&gt;&gt;&gt; db.create_all()
```
<p>
Ya tenemos el archivo <em>database.db</em> preparado para cargarlo de datos mediante los m&eacute;todos correspondientes de <em>blog.py</em>, algunos ejemplos:</p>
```
@app.route(&#39;/add&#39;, methods=[&#39;GET&#39;,&#39;POST&#39;])
def add():
if request.method == &#39;POST&#39;:
post = Blog(request.form[&#39;subject&#39;], request.form[&#39;content&#39;])
db.session.add(post)
db.session.commit()
return redirect(url_for(&#39;index&#39;))
return render_template(app.config[&#39;DEFAULT_TPL&#39;]+&#39;/add.html&#39;,
conf = app.config)
```
<p>
<strong>Flask-WTForms</strong></p>
<p>
Una vez tenemos preparada la base de datos y el m&eacute;todo que agregar&aacute; nuevos posts tan solo falta crear el formulario que nos permitir&aacute; tal funcionalidad y crear el template, poco m&aacute;s de un par de lineas:</p>
```
# Create Form
class CreateForm(Form):
subject = TextField(&#39;Subject&#39;, [validators.required()])
content = TextAreaField(&#39;Content&#39;, [validators.required(), validators.Length(min=1)])
```
<p>
Y el template con el formulario correspondiente ser&iacute;a el siguiente:</p>
```
&lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
&lt;dl&gt;
{{ form.csrf }}
{{ form.subject.label }} {{ form.subject(style=&quot;width:100%&quot;) }}
{% for error in form.subject.errors %} {{ error }} {% endfor %}
&lt;br /&gt;
{{ form.content.label }} {{form.content(style=&quot;height:100px;width:100%&quot;) }}
{% for error in form.content.errors %} {{ error }} {% endfor %}
&lt;/dl&gt;
&lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;submit&quot;&gt;
&lt;/form&gt;
```
<p>
Y ya tendr&iacute;amos un formulario para agregar posts totalmente funcional. Entre esta vista, la de un listado de posts y la del detalle de los mismos (ver repositorio m&aacute;s abajo), tendr&iacute;amos una peque&ntilde;a aplicaci&oacute;n con <em>Flask</em> + <em>SQLAlchemy</em> + <em>WTForms</em>.</p>
<p>
<strong>Resumiendo</strong></p>
<p>
Para finalizar, adem&aacute;s de las vistas que hemos dicho que faltaban, tendr&iacute;amos que dar un poco m&aacute;s de colorido a los templates (css, im&aacute;genes...), crear un <em>layout.html</em> que se pueda extender desde el resto de plantillas y poner un poco de orden en todo lo explicado, pero como no quiero extenderme mucho m&aacute;s en el art&iacute;culo, nada mejor que un peque&ntilde;o repositorio en el que se pueden ir viendo los avances:</p>
<ul>
<li>
<a href="https://bitbucket.org/r0sk/flaskblog">Flaskblog (bitbucket)</a></li>
</ul>
<p>
Espero que con este peque&ntilde;o y humilde ejemplo haya quedado m&aacute;s o menos claro el uso de este framework. Siempre es divertido probar cosas nuevas y cuando se trata de piezas tan sencillas y bien documentadas como las tratadas el placer es doble.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View File

@ -0,0 +1,10 @@
---
title: "Disconnecting a bit"
date: 2013-06-13T06:52:10Z
draft: false
tags: [ ]
image:
---
<p>Yesterday we had a great afternoon. From time to time it's recommended to turn off the computer world and enjoy with family and friends. Yesterday we did. I was really tired and needing fresh air on my mind.</p>
<p>We must repeat, almost, once a week!</p>

View File

@ -0,0 +1,143 @@
---
title: "Django and memcache: clear cache keys"
date: 2014-02-05T13:35:54Z
draft: false
tags: [ "django", "code" ]
image: web-accelerators-memcached.jpg
---
<p>Let's play&nbsp;<a href="https://www.djangoproject.com/">Django</a> with <a href="http://memcached.org/">Memcached</a>. As the great framework Django is, it's so easy to activate <a href="https://docs.djangoproject.com/en/dev/topics/cache/">any kind of cache</a> in your project. <em>Memcached</em> is one of the options, but you can also work with <em>DatabaseCache</em>, <em>FileBasedCache</em>, <em>LocMemCache</em>, <em>MemcachedCache,</em>&nbsp;<em>DummyCache</em> (a kind of non-cache very useful for devel/test enviroments) or - of course - your own <em>CustomCache</em> if you want.</p>
<p><strong>Activating cache</strong></p>
<p>It's too easy to activate the cache feature, it's enough to set the preferences in settings, install <a href="https://pypi.python.org/pypi/python-memcached/">python-memcached</a>&nbsp;in your enviroment (in case you will use <em>MemcachedCache</em>), and not much more to do. A couple of examples:</p>
<p>1. Basic <em>FileBasedCache</em> settings:</p>
```
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
```
<p>2. <em>MemcachedCache</em> settings:</p>
```
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
```
<p>3. Depending on the enviroment you can use <em>MemcachedCache</em> and <em>DummyCache</em>:</p>
```
# settings.devel.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
```
```
# settings.production.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
```
<p><strong>Setting the places where cache will act</strong></p>
<p>Now that we have our project with a configured kind of cache, we must to say where and when to activate it. There are multiple ways to do it (on the <a href="https://docs.djangoproject.com/en/dev/topics/cache/#the-per-site-cache">whole site</a>, <a href="https://docs.djangoproject.com/en/dev/topics/cache/#the-per-view-cache">views</a>, <a href="https://docs.djangoproject.com/en/dev/topics/cache/#template-fragment-caching">templates</a>, <a href="https://docs.djangoproject.com/en/dev/topics/cache/#specifying-per-view-cache-in-the-urlconf">urls</a>...). I'm going to use... let's say urls. So, in our <em>urls.py</em> we have to set a time and activate the cache:</p>
```
# urls.py
from django.views.decorators.cache import cache_page
ctime = (60 * 24) # A day
urlpatterns = patterns('',
url(r'^$',
cache_page(ctime)(BlogIndexView.as_view()),
{},
'blog-index'
),
...
)
```
<p>A simple server reload will be enough to have cache running. We can see it on action with <a href="https://github.com/django-debug-toolbar/django-debug-toolbar">django-debug-toolbar</a>, <a href="https://github.com/bartTC/django-memcache-status">django-memcache-status</a> or something like that.</p>
<p><strong>Clean a specific key cache</strong></p>
<p>And now the funniest part. For example, talking about a blog tool, when you write a new post (or editing older one) the software should be able to remove some cache keys, i.e. the <em>blog-index</em> one (because you have a new post) and the <em>post-detail</em> other (because you must be able to inmediately see the changes in the post you're editting).</p>
<p>Following <a href="http://stackoverflow.com/questions/2268417/expire-a-view-cache-in-django">this link</a> I've created a cache.py with this content:</p>
```
# cache.py
# -*- coding: utf-8 -*-
def expire_view_cache(
view_name,
args=[],
namespace=None,
key_prefix=None,
method="GET"):
"""
This function allows you to invalidate any view-level cache.
view_name: view function you wish to invalidate or it's named url pattern
args: any arguments passed to the view function
namepace: optioal, if an application namespace is needed
key prefix: for the @cache_page decorator for the function (if any)
http://stackoverflow.com/questions/2268417/expire-a-view-cache-in-django
added: method to request to get the key generating properly
"""
from django.core.urlresolvers import reverse
from django.http import HttpRequest
from django.utils.cache import get_cache_key
from django.core.cache import cache
from django.conf import settings
# create a fake request object
request = HttpRequest()
request.method = method
if settings.USE_I18N:
request.LANGUAGE_CODE = settings.LANGUAGE_CODE
# Loookup the request path:
if namespace:
view_name = namespace + ":" + view_name
request.path = reverse(view_name, args=args)
# get cache key, expire if the cached item exists:
key = get_cache_key(request, key_prefix=key_prefix)
if key:
if cache.get(key):
cache.set(key, None, 0)
return True
return False
```
<p>And last step is to call the&nbsp;expire_view_cache function on model form save hook (<em>admin.py</em> in this case):</p>
```
# admin.py
from cache import expire_view_cache
class PostAdminForm(admin.ModelAdmin):
...
def save_model(self, request, obj, form, change):
expire_view_cache("blog-index")
expire_view_cache("post-detail", [obj.slug])
```
<p>And that's all, we are able to <em>clean/purge/remove</em> the cache when a new post is added or edited. As you can see in the code, cache is fun but you have to be careful to set it on the right way.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,25 @@
---
title: "Django: Cambiando de DB Engine"
date: 2012-04-28T18:42:31Z
draft: false
tags: [ "code", "django" ]
image:
---
<p>
Normalmente las especificaciones de un entorno de desarrollo y las de un entorno en producci&oacute;n suelen ser bastante diferentes. Quiz&aacute;s en el primero buscas la comodidad mientras que cuando se hace el <em>deploy</em> a producci&oacute;n priman otras cosas. Este suele ser el caso del motor de base de datos cuando trabajas con <a href="http://djangoproject.org">Django</a>.</p>
<p>
Durante el proceso de desarrollo es muy c&oacute;modo utilizar <em>sqlite </em>porque no requiere de ning&uacute;n servidor adicional y est&aacute; soportado de base en Django (<em>builtin</em>). Es posible que una vez acabado el desarrollo queramos cambiar a otro <em>SGBD</em> &quot;de verdad&quot; como pueden ser <em>MySQL</em> o <em>PostgreSQL</em>.</p>
<p>
Lo que podr&iacute;a suponer un quebradero de cabeza en otras arquitecturas, en nuestro caso utilizando <em>dumpdata</em> y <em>loaddata</em>, se reduce a las siguientes tres lineas:</p>
```
$ python manage.py dumpdata --indent=4 --format=json &gt; fixtures.json
$ scp fixtures.json user@remote:/path/project/
(remote)$ python manage.py loaddata fixtures.json
```
<p>
En la primera usamos <a href="https://docs.djangoproject.com/en/dev/ref/django-admin/#dumpdata-appname-appname-appname-model">dumpdata</a>&nbsp;para hacer un dumpeado de todos los datos de la aplicaci&oacute;n (sqlite) en formato json, posteriormente pasamos ese archivo al entorno de producci&oacute;n (en este caso pongamos de ejemplo a otro servidor) y all&iacute; finalmente (con el conector apuntando al nuevo gestor) ejecutamos el <a href="https://docs.djangoproject.com/en/dev/ref/django-admin/#loaddata-fixture-fixture">loaddata</a> para insertar los datos.</p>
<p>
Et voil&agrave;!</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,97 @@
---
title: "Django deploy: problems with limited hosting"
date: 2014-07-21T21:51:18Z
draft: false
tags: [ "django", "code" ]
image: django.png
---
<p>Some months ago I had to deal with a&nbsp;<a href="../../../../blog/symfony2-en-un-hosting-compartido.html">Symfony2 project in a shared hosting</a>&nbsp;(Spanish article) and now the big next deal is a similar task with&nbsp;<a href="https://www.djangoproject.com/">Django</a>.</p>
<p>The project is almost done and I have the hosting credentials, once I'm in I noticed that there is no chance to configure anything (Apache, WSGI or whatever) so I was a bit lost. Thanks to my&nbsp;<a href="http://ailalelo.com/">Ailalelo's mates</a>&nbsp;(they had lot of experience with this kind of situations) I found the proper configuration.</p>
<p>Hosting is&nbsp;<em>django-ready</em>, but the version they're running (<em>1.4.2</em>) is not the best choice, I want to install&nbsp;<em>1.6.x</em>, the one I have used to develop the project. The other big requirement is&nbsp;<em>virtualenv+pip</em>&nbsp;to retrieve the packages I'm using.</p>
<p>Mainly I've solved it with two files,&nbsp;<code>project.cgi</code>&nbsp;and&nbsp;<code>.htaccess</code>.</p>
<h2>project.cgi</h2>
<p>The hosting structure is like many others, I have access to a&nbsp;<em>homedir</em>&nbsp;with the following directories:</p>
```
myhome/
logs/
tmp/
www/
cgi-bin/
index.html
```
<p>Before to say Apache what to do with our project, let's install&nbsp;<code>virtualenv</code>&nbsp;and all the requirements, my choice is to put the environment out of the&nbsp;<code>www/</code>&nbsp;directory:</p>
```
myhome/
env/
logs/
tmp/
www/
cgi-bin/
project/
index.html
```
```
$ virtualenv env
$ . env/bin/activate
$ pip install -r www/project/requirements/production.txt
```
<p>Seems to be that apache's&nbsp;<em>mod_cgi</em>&nbsp;will process all the files you put in the&nbsp;<code>cgi-bin</code>&nbsp;directory, so we already know where to save our&nbsp;<code>project.cgi</code>. I have to tell apache to use my own&nbsp;<em>virtualenv</em>&nbsp;<code>python</code>, where the environment and the project are. And finally set some environment variables:</p>
```
#!/home/myhome/env/bin/python
import os
from os.path import abspath, dirname
from sys import path
actual = os.path.dirname(__file__)
path.insert(0, os.path.join(actual, "..", "project/project"))
path.insert(0, os.path.join(actual, "..", "env"))
# Set the DJANGO_SETTINGS_MODULE environment variable.
os.environ['PATH'] = os.environ['PATH'] + ':/home/myhome/www/project/node_modules/less/bin'
os.environ['SECRET_KEY'] = 'SECRETKEY'
os.environ['DJANGO_SETTINGS_MODULE'] = "project.settings.production"
from django.core.servers.fastcgi import runfastcgi
runfastcgi(method="threaded", daemonize="false")
```
<p>Note that I modified the PATH because I have to be able to use&nbsp;<code>less</code>&nbsp;binary, required for&nbsp;<a href="http://django-compressor.readthedocs.org/en/latest/">django-compressor</a>&nbsp;package. In this particular case my user was not allowed to install&nbsp;<em>node/less</em>&nbsp;in the system, so I had to install it locally, referencing the particular&nbsp;<code>node_modules</code>&nbsp;folder.</p>
<h2>.htaccess</h2>
<p>Now that Apache knows what to do, we should redirect almost all the incoming traffic to the&nbsp;<em>cgi</em>, so let's write some<code>.htaccess</code>&nbsp;rules:</p>
```
AddHandler fcgid-script .fcgi
RewriteEngine On
RewriteRule ^static/(.*)$ project/project/static/$1 [L]
RewriteRule ^media/(.*)$ project/project/media/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cgi-bin/project.cgi/$1 [QSA,L]
AddDefaultCharset UTF-8
```
<p>The&nbsp;<code>media/</code>&nbsp;and&nbsp;<code>static/</code>&nbsp;dirs are redirected to the proper location, because they didn't work as is. Not much more to say with this file, it's easy to understand I think.</p>
<h2>Remember</h2>
<ul>
<li>To properly install the environment.</li>
<li>Set up this two files (cgi and .htaccess).</li>
<li>Take care with node tools (bower, npm, less, node_modules...).</li>
<li>Most of times django-compressor is a PITA in shared hostings.</li>
<li>Take double care with static/media settings.</li>
<li>Set the proper permissions in the .cgi file.</li>
<li>Having a bit of patience and access to&nbsp;<a href="http://stackoverflow.com/">stackoverflow</a>&nbsp;is to have it a half fixed ;).</li>
</ul>
<p>&nbsp;</p>
<ul>
</ul>

View File

@ -0,0 +1,20 @@
---
title: "Django: limpiando usuarios desde shell"
date: 2013-09-12T23:03:29Z
draft: false
tags: [ "django", "code" ]
image:
---
<p>Es tal el inter&eacute;s por el <a href="http://www.vamosavidas.com/foros/">foro de VamosaVidas</a> (si, ese que romp&iacute; en su d&iacute;a y <a href="http://www.vamosavidas.com/los-duendes-del-foro-nos-lo-han-devuelto.html">he vuelto a arreglar</a>) que todos los d&iacute;as tenemos cientos de registros. Realmente se trata de robots que se dedican a hacer spam, pero eso es lo de menos :P.</p>
<p>Mientras no actualizo y arreglo <a href="https://github.com/pennersr/django-allauth">django-authall</a>&nbsp;es un poco co&ntilde;azo andar mirando en el admin el &uacute;ltimo usuario bueno y borrar manualmente uno a uno cotejando que no me equivoque, as&iacute; que he pensado en un par de lineas que no deber&iacute;an hacer demasiado da&ntilde;o si se usan con precauci&oacute;n. Desde la shell de nuestro proyecto podemos hacer algo as&iacute;:</p>
```
$ python manage.py shell
&gt;&gt;&gt; from django.contrib.auth.models import User
&gt;&gt;&gt; list(User.objects.order_by('date_joined'))
&gt;&gt;&gt; [user.delete() for user in list(User.objects.order_by('date_joined'))[100:]]
```
<p>Primero estamos listando todos los usuarios por orden de fecha de registro, los convertimos en lista para que no salgan los datos "<em>truncados</em>". Y en segundo lugar estamos eliminando todos excepto los 100 primeros.</p>
<p>Esta es la sencillez y potencia de Python.</p>

View File

@ -0,0 +1,151 @@
---
title: "Django + virtualenv + pip"
date: 2011-02-23T21:37:56Z
draft: false
tags: [ "django", "code" ]
image:
---
<p>No lo ten&iacute;a claro, pero cuando entend&iacute; lo que supon&iacute;a y c&oacute;mo se trabajaba con <em>virtualenv</em> + <em>pip</em> me decid&iacute; a probarlo. Voy a <em>intentar</em> explicar como se utilizan estas herramientas de una forma gen&eacute;rica, para hacernos una idea de lo que significa y los casos en los que se pueden aplicar. A grandes rasgos:</p>
<ul>
<li class="level1">
<div class="li"><a href="http://djangoproject.org">Django</a>: Framework en <em>python</em>, creo que no necesita mucha m&aacute;s explicaci&oacute;n.</div>
</li>
<li class="level1">
<div class="li"><a href="http://pypi.python.org/pypi/virtualenv">Virtualenv</a>: Herramienta necesaria para crear un entorno virtual de <em>python</em>, con las versiones espec&iacute;ficas de los paquetes y/o dependencias que hagan falta para el proyecto.</div>
</li>
<li class="level1">
<div class="li"><a href="http://pypi.python.org/pypi/pip">Pip</a>: Gestor/Instalador de esos paquetes (similar a <em>easy_install</em>).</div>
</li>
</ul>
<p>Con estas herramientas intentaremos instalar un entorno virtual independiente para gestionar todas las dependencias de nuestro proyecto.<!--more--></p>
<p><strong>Instalaci&oacute;n del entorno virtualenv + pip</strong></p>
<p>Primero instalamos <em>pip</em>, que como hemos mencionado anteriormente, es el gestor de <em>paquetes</em> con el que vamos a instalar el resto de componentes. Tiramos de <em>easy_install</em> para instalarlo. Una vez instalado <em>pip</em>, instalamos tambi&eacute;n <em>virtualenv</em> y tendremos la base necesaria para seguir trabajando:</p>
```
$ sudo easy_install pip
$ pip install virtualenv
```
<p>Vamos a crear el entorno. Supongamos 2 directorios de trabajo, <em>src/</em> y <em>env/</em>. El directorio <em>src/</em> lo destinaremos al c&oacute;digo fuente (podemos tirar de repositorio o iniciar nuestro proyecto directamente ah&iacute;), en <em>env/</em> meteremos todos los paquetes y dependencias del proyecto, osea el entorno:</p>
```
$ mkdir -p foo/{env,src}
$ cd foo/
$ virtualenv --distribute --no-site-packages env/
New python executable in env/bin/python
Installing distribute........................................done.
```
<p>Ahora ya estamos preparados para instalar las aplicaciones/dependencias dentro del entorno de trabajo, lo haremos "desde fuera" con la opci&oacute;n <em>-E</em>:</p>
```
$ pip -E env/ install django
Downloading/unpacking django
Downloading Django-1.2.4.tar.gz (6.4Mb): 6.4Mb downloaded
Running setup.py egg_info for package django
[...]
Successfully installed django
Cleaning up...
```
```
$ sudo pip -E env/ install -e hg+https://bitbucket.org/ubernostrum/django-registration#egg=django-registration
$ sudo pip -E env/ install -e git+https://github.com/flashingpumpkin/django-socialregistration.git#egg=django-socialregistration
[instalamos el resto de dependencias]
```
<p>Vamos a comprobar que todo est&aacute; correcto, para ello utilizamos una herramienta que se llama <em>yolk</em>, capaz de listar todos los paquetes que tenemos instalados en el entorno, primero instalamos esa herramienta, luego entramos en el entorno y la ejecutamos (aunque podr&iacute;amos hacer lo mismo con la orden <em>pip freeze </em>como se muestra a continuaci&oacute;n):</p>
```
$ pip -E env/ install yolk
$ source env/bin/activate
(env)$ yolk -l
Django - 1.2.4 - active
Python - 2.6.6 - active development (/usr/lib/python2.6/lib-dynload)
distribute - 0.6.14 - active
pip - 0.8.1 - active
wsgiref - 0.1.2 - active development (/usr/lib/python2.6)
yolk - 0.4.1 - active
```
<p><strong>Nota</strong>: Como se puede ver en el ejemplo de arriba, para entrar en el entorno que hemos creado usamos el comando <em>source env/bin/activate</em>, para salir del mismo usamos el comando <em>deactivate</em>.</p>
```
(env)$ pip freeze
Django==1.2.4
distribute==0.6.14
wsgiref==0.1.2
yolk==0.4.1
```
<p>Ahora que tenemos m&aacute;s o menos clara la forma de actuar, vamos a hacer un <em>freeze</em> y guardar el archivo de requisitos dentro del repositorio para poder volver a reproducirlo en cualquier otra m&aacute;quina. Insisto, todos estos comandos pueden ejecutarse dentro del entorno (env) o fuera del mismo con la opci&oacute;n <em>-E entorno</em> de <em>pip</em>:</p>
```
(env)$ cd foo/src/
(env)$ pip freeze &gt; requirements.txt
(env)$ cat requirements.txt
Django==1.2.4
South==0.7.3
distribute==0.6.14
wsgiref==0.1.2
yolk==0.4.1
(env)$ deactivate
$
```
<p>Y ya podemos empezar a programar el proyecto en dentro de <em>src/</em>.</p>
<p><strong>Replicando el entorno</strong></p>
<p>Suponemos ahora que estamos en otra m&aacute;quina -aunque en el ejemplo seguimos en la misma- donde queremos montar de nuevo todo el entorno. Primero creamos de nuevo la estructura anterior y luego hacemos un <em>pull</em> del repositorio la carpeta <em>src/</em> (como estamos en la misma m&aacute;quina lo simulando haciendo un <em>cp</em> :P):</p>
```
$ mkdir -p bar/{env,src}
$ cp -r foo/src/* bar/src
```
<p>Y ahora procuramos recrear el entorno, primero hacemos un entorno nuevo y vac&iacute;o en <em>bar/env/</em> y luego instalamos todo lo que hemos <em>logueado</em> en el <em>requirements.txt</em>:</p>
```
$ virtualenv --distribute --no-site-packages bar/env/
$ pip install -E bar/env/ -r bar/src/requirements.txt
...
Successfully installed Django South ... yolk
Cleaning up...
```
<p>Comprobamos que todo est&eacute; correcto:</p>
```
$ source bar/env/bin/activate
(env)$ pip freeze
Django==1.2.4
distribute==0.6.14
wsgiref==0.1.2
yolk==0.4.1
(env)$ deactivate
$
```
<p><strong>Conclusi&oacute;n</strong></p>
<p><em>Virtualenv</em> y <em>pip</em> forman una combinaci&oacute;n excelente de elementos para hacer de un proyecto un <em>ente</em> sin dependencias frustradas y con todos los requisitos y versiones espec&iacute;ficas para garantizar el correcto funcionamiento del mismo. Ahora, por lo que me han contado, deber&iacute;a interesarme en <a href="http://fabfile.org/">Fabric</a> para automatizar los <em>deploys</em> y seguir haciendo magia.</p>
<p><strong>Referencias</strong></p>
<div class="level2 section_highlight">
<ul>
<li class="level1">
<div class="li"><a class="urlextern" title="http://www.saltycrane.com/blog/2009/05/notes-using-pip-and-virtualenv-django/" rel="nofollow" href="http://www.saltycrane.com/blog/2009/05/notes-using-pip-and-virtualenv-django/">Saltycrane</a></div>
</li>
<li class="level1">
<div class="li"><a class="urlextern" title="http://ryanwilliams.org/2009/Jun/09/deploying-django-sites-fabric-pip-and-virtualenv" rel="nofollow" href="http://ryanwilliams.org/2009/Jun/09/deploying-django-sites-fabric-pip-and-virtualenv">Ryanwilliams</a></div>
</li>
<li class="level1">
<div class="li"><a class="urlextern" title="http://techylinguist.com/how-to/python-and-django-dev-environment-virtualenv-and-pip" rel="nofollow" href="http://techylinguist.com/how-to/python-and-django-dev-environment-virtualenv-and-pip">Techylinguist.com</a></div>
</li>
<li class="level1">
<div class="li"><a class="urlextern" title="http://www.sebasmagri.com/2010/aug/12/pip-fabric-y-virtualenv-herramientas-para-el-desar/" rel="nofollow" href="http://www.sebasmagri.com/2010/aug/12/pip-fabric-y-virtualenv-herramientas-para-el-desar/">Sebasmagri 1</a></div>
</li>
<li class="level1">
<div class="li"><a class="urlextern" title="http://www.sebasmagri.com/2010/aug/14/esqueleto-para-proyectos-django-porque-la-forma-im/" rel="nofollow" href="http://www.sebasmagri.com/2010/aug/14/esqueleto-para-proyectos-django-porque-la-forma-im/">Sebasmagri 2</a></div>
</li>
</ul>
</div>

View File

@ -0,0 +1,48 @@
---
title: "Dovecot, pequeñas peculiaridades"
date: 2010-09-07T09:20:13Z
draft: false
tags: [ "dovecot" ]
image:
---
<p>
Desde hace alg&uacute;n tiempo -y despu&eacute;s de haber lidiado con <a href="http://www.cyrusimap.org/">Cyrus</a> y <a href="http://www.courier-mta.org/">Courier</a>- he optado por <a href="http://dovecot.org/">Dovecot</a> como servidor <em>POP3</em> e <em>IMAP</em> para m&aacute;quinas en producci&oacute;n. Por varios motivos: la sencillez de configuraci&oacute;n, sigue los est&aacute;ndares, soporta <em>mbox</em> y <em>Maildir</em> y algo muy importante, tiene un backend de autentificaci&oacute;n <em>SMTP</em> compatible con <a href="http://www.postfix.org/">Postfix</a> (entre otros).</p>
<p>
Sin duda el servicio de correo electr&oacute;nico es el menos agradecido y probablemente el m&aacute;s doloroso para el <em>sysadmin</em> pero el haber dado con esta combinaci&oacute;n de elementos me ha ahorrado un mont&oacute;n de problemas.</p>
<p>
De todos modos en la &uacute;ltima instalaci&oacute;n que me ha tocado he encontrado un par de peculiaridades que me gustar&iacute;a documentar por si alguien se encuentra en la misma situaci&oacute;n.<!--more--></p>
<p>
Habiendo instalado el mismo O.S., las mismas versiones de software y exactamente los mismos ficheros de configuraci&oacute;n a la hora de despachar correos me encuentro con un error inexperado en <em>mail.log</em>:</p>
```
... status=bounced (local configuration error)
```
<p>
As&iacute; sin m&aacute;s descripci&oacute;n no puedo adivinar mucho as&iacute; que decido activar errores en <em>dovecot.conf</em> con la directiva de configuraci&oacute;n <em>log_path = /var/log/<span class="search_hit">dovecot</span>.log.</em> Ahora s&iacute; podemos sacar m&aacute;s informaci&oacute;n del <em>dovecot.log</em>:</p>
```
Fatal: postmaster_address setting not given
```
<p>
Esto ya es otra cosa, despu&eacute;s de un poco de <em>googling</em> corrijo el fallo agregando al fichero de configuraci&oacute;n una direcci&oacute;n de postmaster, sigue pareci&eacute;ndome raro porque <em>/etc/aliases</em> es el mismo que otras m&aacute;quinas y nunca hab&iacute;a notificado este problema antes pero bueno, es cuesti&oacute;n de agregar a <em>dovecot.conf</em>&nbsp; lo siguiente:</p>
```
protocol lda {
postmaster_address = tu-postmaster@tu-dominio.com
}
```
<p>
Reinicio el servicio y a funcionar... pero no por mucho tiempo puesto que al d&iacute;a siguiente me encuentro con el servicio parado, vuelvo a reiniciar y el proceso se vuelve a parar cada d&iacute;a, repitiendo la jugada. Volviendo a los logs -ese gran invento- veo que todos los d&iacute;as a eso de las <em>6:00am</em> suelta el siguiente mensaje:</p>
```
dovecot: 2010-09-05 05:59:53 Fatal: Time just moved backwards by 9 seconds. This might cause a lot of problems, so I&#39;ll just kill myself now. http://wiki.dovecot.org/TimeMovedBackwards
```
<p>
Justo a esa hora tengo una tarea programada que sincroniza la hora del servidor con <em>rdate</em>. Al parecer Dovecot detecta que la hora ha cambiado y para no entrar en conflictos se hace el <em>harakiri.</em> Interesante, es algo que tienen documentado en <a href="http://wiki.dovecot.org/TimeMovedBackwards">su wiki</a> y te animan a que cambies <em>rdate/ntpdate</em> por <em>ntpd</em>, <em>clockspeed</em> o <em>chrony.</em></p>
<p>
Nada del otro mundo pero s&iacute; me ha supuesto algo de tiempo saber el origen de los errores para poder subsanarlos as&iacute; que bueno, si al menos esta entrada ayuda a alguien o le ahorra alg&uacute;n dolor de cabeza me dar&eacute; por contento.</p>

View File

@ -0,0 +1,20 @@
---
title: "El mejor firewall"
date: 2010-02-11T16:16:22Z
draft: false
tags: [ ]
image:
---
Lo he visto <a href="http://www.puntogeek.com/2010/02/10/humor-el-mejor-firewall-p/">en puntogeek</a> a través del <a href="http://twitter.com/demogar/status/8960593725">twitter de demogar</a> y me ha parecido tan gracioso que he caido en el <em>burdo</em> copy-paste:
<br /><br />
<ol>
<li>Una célula humana contiene 75MB de información génetica.</li>
<li>Un espermatozoide contiene la mitad; eso significa 37.5MB.</li>
<li>Un ml de semen contiene 100 millones de espermatozoides.</li>
<li>En promedio la eyaculación dura 5 segundos y contiene 2.24 ml de semen</li>
<li>Esto significa que la producción del miembro de un hombre equivale 37.5MB x 100,000,000 x 2.25)/5 = 1,687,500,000,000,000 bytes/segundo = 1,6875 Terabytes/seg.</li>
<br />
Esto quiere decir que el óvulo femenino soporta este ataque DDoS a 1,5 terabytes por segundo, y solo permite que pase un solo paquete de información lo que la hace el mejor hardware firewall del mundo.
<br /><br />
La mala noticia de esto, es que ese paquete de información que deja pasar, cuelga el sistema por aproximadamente nueve meses.

View File

@ -0,0 +1,85 @@
---
title: "El papeleo de ser padre"
date: 2013-01-08T18:41:27Z
draft: false
tags: [ ]
image:
---
<p>Pasados esos primeros momentos de tensi&oacute;n y felicidad m&aacute;ximos, vuelves a casa para que la realidad trate de despertarte a golpe de peque&ntilde;as bofetadas. O eso es lo que me est&aacute; pasando a mi. Pasas de preocuparte porque todo salga bien en la eco, porque no haya ninguna complicaci&oacute;n en el momento del parto, porque todas las pruebas est&eacute;n correctas, a otro tipo de minucias que, si bien no son tan importantes, acaban desgastando igualmente a ese individuo com&uacute;nmente llamado <em>neopadre</em>.</p>
<p>Por un lado tenemos la repetitiva monoton&iacute;a de las tomas cada 3 horas (creo que cualquiera que haya sido padre y vuelva a escuchar esas palabras - "<em>cada 3 horas</em>" - debe sentir escalofr&iacute;os occipitales), que adem&aacute;s, si tienes la suerte de contar con gemelos (<em>double win!</em>), se multiplican por doble trabajo y mitad de descanso, a menos que cuentes con la ayuda del encomiable&nbsp;<em>Dr. Octopus</em>.</p>
<p>Y por otro lado viene el jaleo de los papeleos, la oficialidad de los renacuajos pasa por gastar m&aacute;s gasolina (porque <em>Lugo</em> en estas fechas, diga lo que diga&nbsp;<a href="http://blog.e-shell.org">Borja</a>, no est&aacute; para mucha bicicleta) y tiempo que en cualquier otra cosa que se te ocurra. Y aqu&iacute; es donde me quiero parar a detallar el proceso, porque el resto es algo que se aprende con el tiempo y los r&iacute;os de consejos que te dar&aacute;n tus m&aacute;s cercanos. Pero los papeles, &iexcl;Ay los papeles!...</p>
<p><strong>Hospital</strong></p>
<p>En el hospital lo principal es (que todo salga bien, obviamente) estar tranquilo y disfrutar del mayor tiempo posible con tu(s) hijo(s), esas primeras horas son muy importantes y (por lo que dicen) marcan el lazo de uni&oacute;n entre padres e hijos. Sin embargo tienes que estar pendiente tambi&eacute;n de pedir en admisi&oacute;n del propio hospital (o en la propia planta, depende del funcionamiento) ciertos documentos:</p>
<ul>
<li>Justificante de ingreso de la madre.</li>
<li>Certificado de nacimiento.</li>
<li>Informe de alta de la madre.</li>
</ul>
<p>Adem&aacute;s tendr&aacute;s que rellenar (imagino que dependiendo de autonom&iacute;as y funcionamientos internos) otros papeles correspondientes con la salud del nacido:</p>
<ul>
<li>Solicitud de vacunaci&oacute;n hepatitis b.</li>
<li>Solicitud de pruebas sordera.</li>
<li>Pruebas metab&oacute;licas para detecci&oacute;n prematura de enfermedades.</li>
<li>Mont&oacute;n de informaci&oacute;n sobre lactancia y otras fases venideras.</li>
</ul>
<p><strong>Registro civil</strong></p>
<p>Una de las primeras cosas que se te pasan por la cabeza es registrar al/los renacuajos. Toda esa discusi&oacute;n por los nombres con tu pareja, suegra y dem&aacute;s familia, tienen que acabar en un papel legal firmado sellado y timbrado. En nuestro caso, en el propio hospital te indican c&oacute;mo hacer, te ofrecen El Papel Amarillo (tm) con informaci&oacute;n relativa al parto donde tendr&aacute;s que cubrir poco m&aacute;s que los datos de los progenitores y los nombres de los chavales. Todo esto se ha de entregar en el Registro Civil junto con el libro de familia, por lo que:</p>
<ul>
<li>El Papel Amarillo.</li>
<li>Encuesta del INE.</li>
<li>Libro de familia (en caso de no estar casados la chicha es mayor, pero la desconozco).</li>
<li>Fotocopia del DNI de ambos progenitores.</li>
<li>Firmar un par de documentos.</li>
</ul>
<p>En un par de d&iacute;as tendr&aacute;s que volver a recoger el libro de familia actualizado. Oficialmente a partir de ese momento ya les puedes llamar por su nombre, antes recomiendo t&eacute;rminos como <em>cacahuete</em>, <em>cosito</em>, <em>renacuajo</em>, etc...</p>
<p><strong>Certificado de empresa</strong></p>
<p>Si est&aacute;s trabajando por cuenta ajena deber&aacute;s pedirle a tu empresa un certificado de que est&aacute;s trabajando all&iacute; (&iexcl;curioso eh!). En ese papel van detalladas tambi&eacute;n las bases de cotizaci&oacute;n, necesarias para tramitar tu baja por maternidad/paternidad. Ojo, aseguraos de que el certificado de empresa va firmado y sellado por la empresa o no valdr&aacute; de mucho. Depende de la empresa podr&aacute;n pediros cierta acreditaci&oacute;n de que realmente hab&eacute;is tenido un hijo, aseguraos de llevar encima:</p>
<ul>
<li>Justificante del ingreso de la madre.</li>
<li>Informe del alta de la madre.</li>
<li>Libro de familia debidmente actualizado.</li>
</ul>
<p><strong>Padr&oacute;n</strong></p>
<p>Aunque el "derecho a una vivienda digna" no sea tal en este pa&iacute;s y, desgraciadamente, haya muchas personas sin hogar propio; hay que empadronar a tus hijos en alg&uacute;n sitio. As&iacute; que toca ir al Ayuntamiento de turno y rellenar los correspondientes formularios, para ello debemos llevar de casa:</p>
<ul>
<li>Libro de familia debidamente actualizado (y fotocopias por si acaso).</li>
<li>DNI de los progenitores.</li>
</ul>
<p><strong>Alta del beb&eacute; en la Seguridad Social</strong></p>
<p>Se tramita en la Seguridad Social y, como todo lo que se tramita en el <em>INSS</em>, dependiendo del funcionario de turno se puede contar como una experiencia maravillosa o maravillosamente traum&aacute;tica, antes de nada &iexcl;mucha suerte!. Para dar de alta en la Seguridad Social y meterlo en la cartilla de alguno de los padres es interesante saber qu&eacute; pediatra toca a cada uno, porque podr&eacute;is escoger la cartilla del padre o de la madre. Documentos imprescindibles a presentar con el correspondiente impreso:</p>
<ul>
<li>DNI de los progenitores (llevando el del que va a acoger al ni&ntilde;o en su cartilla llegar&iacute;a, pero por si las moscas llevad los 2).</li>
<li>Libro de familia debidamente actualizado.</li>
<li>Tarjeta Sanitaria del progenitor que va a acoger al ni&ntilde;o (recomendable llevar los 2).</li>
</ul>
<p><strong>Informe de Maternidad (El Papelito Azul)</strong></p>
<p>El m&eacute;dico de cabecera debe expedir este informe que har&aacute; falta posteriormente para presentar la baja por maternidad. Para ello debemos llevar:</p>
<ul>
<li>El informa de alta de la madre del hospital.</li>
<li>Libro de familia debidamente actualizado.</li>
<li>DNI de la madre.</li>
<li>Tarjeta Sanitaria de la madre.</li>
</ul>
<p><strong>Baja por paternidad/maternidad</strong></p>
<p>Tambi&eacute;n se tramita en la Seguridad Social (Instituto Nacional de la Seguridad Social, INSS, etc...). Y normalmente tiene tela porque depende de cada caso, no es lo mismo cuenta ajena que aut&oacute;nomo, etc... pero por lo general te pedir&aacute;n:</p>
<ul>
<li>DNI de los progenitores (y fotocopia del mismo).</li>
<li>Libro de familia debidamente actualizado (y fotocopia del mismo).</li>
<li>Certificado de empresa en caso de estar trabajando por cuenta ajena (y fotocopia del mismo).</li>
<li>Los 2 &uacute;ltimos cupones de aut&oacute;nomo en caso de estar dado de alta en el R.E.T.A.</li>
<li>N&uacute;mero de cuenta para la parte correspondiente de la n&oacute;mina.</li>
<li>Si la baja es por maternidad, adem&aacute;s hace falta el informe de maternidad aka El Papelito Azul (y fotocopia del mismo).</li>
</ul>
<p><strong>Solicitud de prestaciones</strong></p>
<p>Dependiendo del caso, del embarazo, del parto y de la situaci&oacute;n personal, es posible que tengas derecho a ciertas ayudas que pap&aacute; Estado concede (de momento) por traer un hijo a este mundo. Este tipo de ayudas se suelen tramitar tambi&eacute;n desde el INSS (Seguridad Social, Instituto Nacional de la Seguridad Social, etc...) y suelen tener requisitos variopintos, as&iacute; que por norma general diremos que hace falta:</p>
<ul>
<li>DNI del interesado (y fotocopia del mismo).</li>
<li>Tarjeta Sanitaria del interesado.</li>
<li>Copia de la la &uacute;ltima&nbsp;declaraci&oacute;n de la Renta.</li>
<li>N&uacute;mero de cuenta por si eres un suertudo/a y te conceden la prestaci&oacute;n.</li>
</ul>
<p>A tales momentos (<em>enero 2013</em>) creo que sigue vigente una prestaci&oacute;n de deducci&oacute;n por maternidad (100&euro;/mes durante un par de a&ntilde;os) y alguna que otra <a href="http://www.seg-social.es/Internet_1/Trabajadores/PrestacionesPension10935/Prestacionesfamilia10967/Prestacioneconomica33761/index.htm">subvenci&oacute;n por partos m&uacute;ltiples</a>&nbsp;(<a href="http://www.seg-social.es/prdi00/groups/public/documents/binario/097846.pdf">impreso</a>) o algo as&iacute;. M&aacute;s informaci&oacute;n en la propia <a href="http://www.seg-social.es/">web de la Seguridad Social</a>.</p>
<p><strong>En definitiva...</strong></p>
<p>Decir antes de nada que todo este rollo que acabo de soltar est&aacute; sujeto al funcionamiento de cada Comunidad Aut&oacute;noma, de pap&aacute; Estado e, imagino, depender&aacute; tambi&eacute;n del a&ntilde;o en el que se tramite puesto que estas cosas cambian tanto como el devenir del l&iacute;quido elemento. Ah, &iexcl;y las firmas!. No os olvid&eacute;is de fecha y firma en todos y cada uno de los papeles que se presenten, pero bueno, eso ya os lo recordar&aacute; tambi&eacute;n el funcionario de turno.</p>
<p>Con todo esto que os estoy contando no quiero decir que no est&eacute; encantado con este nuevo <em>status</em> parental, al contrario (r&iacute;os de babas salen diariamente de mi cansada boca), pero si os est&aacute;is planteando tener descendencia, contad tambi&eacute;n con estas situaciones, con&nbsp;toneladas de paciencia, gasolina en el coche, disponibilidad para hacer varios viajes a la misma ventanilla y un par de bol&iacute;grafos a mano cargados de tinta.</p>

View File

@ -0,0 +1,23 @@
---
title: "Emacs go to line y reload sin salir de Emacs"
date: 2012-08-13T19:39:29Z
draft: false
tags: [ ]
image:
---
<p>Se me hace muy c&oacute;modo tener un atajo de teclado para la t&iacute;pica orden de "<em>ir a la linea X</em>", as&iacute; que en vez de escribir todo el tochaco de comando que hace falta en Emacs para ello (<em>M-x goto-line&lt;RET&gt;#linea&lt;RET&gt;</em>) voy a hacer un binding. Abro el archivo .emacs y agrego lo siguiente:</p>
```
;; Goto-line
short-cut key(global-set-key "C-l" 'goto-line)
```
<p>Una vez guardado y para hacer efectiva la configuraci&oacute;n sin tener que salir de Emacs ejecuto lo siguiente:&nbsp;</p>
```
M-x load-file
~/.emacs
```
<p>Y ya tengo lo que quer&iacute;a, mi shortcut de "<em>ir a la linea X</em>" en <em>Ctrl + L</em>. Tip b&aacute;sico de Emacs patrocinado por chocolate blanco Mercadona y agua de mineralizaci&oacute;n muy d&eacute;bil Bezoya.</p>

View File

@ -0,0 +1,11 @@
---
title: "Feeling an old techie"
date: 2015-03-25T19:00:19Z
draft: false
tags: [ ]
image: old-techie.jpg
---
<p>I was thinking about that feeling lately. It seems that I can not follow the wave out there, there are many things escaping from my comprehension due lack of time. Starting to talk about anything not related to my <em>day-by-day</em> is starting to feeling myself out of date.</p>
<p>Last days I could get some short time to read about a couple of topics - <em>frontend</em> related this time - and I felt like I was using <em>cobol</em> (nothing against <em>cobol</em> here). It's a fact you can not focus in every new brilliant and recently released piece of technology, but it's like there was an interestellar space between what I thought it was the right way and recent developments or best practices.</p>
<p>Probably have to go back to the old habit of taking Friday afternoons (or any other shift) as <em>research &amp; development</em>. Would be great.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,22 @@
---
title: "Hacer scroll en GNU Screen"
date: 2011-04-11T12:31:43Z
draft: false
tags: [ ]
image:
---
<p>
Me ha pasado un par de veces y siempre he acabado <a href="http://www.saltycrane.com/blog/2008/01/how-to-scroll-in-gnu-screen/">en el mismo sitio</a> as&iacute; que he decidido documentarlo con un mini-post, porque seguro que no ser&aacute; la &uacute;ltima vez que me pase. Ejecutando ciertos comandos en remoto a trav&eacute;s de <a href="http://www.gnu.org/software/screen/">screen</a> hay veces que el <em>scroll</em> se desborda y necesitar&iacute;a leer parte de la salida, tan sencillo como:</p>
<ul>
<li>
<em>C-a [</em> - Para entrar en modo <em>scrollback.</em></li>
<li>
<em>C-a ESC</em> - Para entrar en modo <em>scrollback</em> (mismo efecto que el punto anterior).</li>
<li>
Usa el scroll del rat&oacute;n por ejemplo para ver/seleccionar lo que quieras.</li>
<li>
<em>ESC</em> - Para salir del modo <em>scrollback</em>.</li>
</ul>
<p>
Otra sencillo <em>hack</em> para afinar una de las mejores herramientas que he usado (<a href="http://www.userlinux.net/tag-screen/">y ya van unos cuantos</a>). As&iacute; que nada m&aacute;s, a disfrutarlo.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

View File

@ -0,0 +1,13 @@
---
title: "Lazy summer"
date: 2015-09-14T10:48:38Z
draft: false
tags: [ ]
image: summer2015.jpg
---
<p>There were almost no workouts during July + August, and September is starting the same way. We're experiencing too much changes with the kids school and the inestable weather here, so my body is feeling dumb, as my mind.</p>
<p>On the other hand we had a nice summer, traveling from here to there and enjoying weather, beach, friends and family. Enough to charge batteries and start thinking in next adventure.</p>
{{< gallery match="gallery/*" sortOrder="asc" rowHeight="150" margins="5" thumbnailResizeOptions="600x600 q90 Lanczos" previewType="blur" embedPreview="true" >}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

View File

@ -0,0 +1,32 @@
---
title: "Limitando usuarios ssh en Mercurial"
date: 2010-07-21T01:24:05Z
draft: false
tags: [ ]
image:
---
<p>
Si algo bueno tiene <a href="http://mercurial.selenic.com/">Mercurial</a> es que permite la autentificaci&oacute;n de usuarios a trav&eacute;s de SSH. Es muy sencillo agregar un nuevo usuario a un desarrollo/repositorio: <em>adduser</em> y con meterlo dentro del grupo correspondiente al desarrollo llegar&iacute;a. Pero &iquest;qu&eacute; ocurre si no queremos que ese usuario haga otra cosa que no sean comandos <em>hg</em>?.</p>
<p>
Conociendo la existencia de <a href="http://www.selenic.com/repo/hg-stable/raw-file/tip/contrib/hg-ssh">hg-ssh</a> no ocurre demasiado, se trata de un script que hemos de referenciar en el <em>authorized_keys </em>del usuario que acabamos de crear de forma que todos los comandos entrantes pasen por este script. El script se encarga de parsear el comando que se pide en ejecuci&oacute;n: si es de la familia de <em>Mercurial</em> lo ejecuta, en cualquier otro caso mostrar&aacute; un error.</p>
<p>
Ejemplo de <em>authorized_keys</em>:</p>
```
command=&quot;~/hg-ssh /home/repo1 /home/repo2&quot;,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dss AAAA...
```
<p>
He optado por copiar el archivo hg-ssh en el directorio <em>home</em> del usuario, pero se podr&iacute;a referenciar directamente el que trae de ejemplo la instalaci&oacute;n de <em>Mercurial</em>.<!--more--></p>
<p>
Nos encontramos con otro peque&ntilde;o inconveniente, si el script se tiene que referenciar en el archivo <em>authorized_keys</em> como he dicho antes, el usuario debe tener su clave p&uacute;blica DSA/RSA configurada en el servidor, as&iacute; que para estar seguros de que no se salta las restricciones impuestas por <em>hg-ssh</em>, deber&iacute;amos desactivar cualquier intento de acceso por contrase&ntilde;a:</p>
```
# nano /etc/ssh/sshd_config
Match Group desarrolladores
PasswordAuthentication no
```
<p>
Reiniciamos <em>sshd</em> y todo deber&iacute;a funcionar de forma adecuada, el usuario podr&aacute; hacer <em>commits</em>, <em>push</em>, <em>pull</em>, <em>update</em>... desde la m&aacute;quina cuya clave <em>id_dsa.pub</em> hemos agregado en el servidor pero no podr&aacute; acceder a trav&eacute;s de ssh a ning&uacute;n otro comando, ni a shell; por lo tanto creo que se ha conseguido el principal objetivo.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,87 @@
---
title: "Mercurial: automatizando al máximo"
date: 2010-03-18T13:28:20Z
draft: false
tags: [ ]
image:
---
<p>Cuando trabajamos con servidores de versiones seguro que hay muchas razones de peso de por medio, una de ellas -la que veremos- puede ser la replicaci&oacute;n de c&oacute;digo en diversas m&aacute;quinas. <br /><br /> Supongamos un mont&oacute;n de m&aacute;quinas que comparten el mismo c&oacute;digo de repositorio, el orden de propagaci&oacute;n de un cambio en todas esas m&aacute;quinas es sencillo: <br /><br /></p>
<ol>
<li>Programamos dicho cambio en nuestro servidor de desarrollo (<em>devel</em>).</li>
<li>Hacemos un <em>commit</em> local (en sistemas de versionado distribuido -como Mercurial- cada repositorio tambi&eacute;n es servidor).</li>
<li>Lo siguiente es un <em>push</em> al servidor donde almacenamos el c&oacute;digo (<em>repo c&oacute;digo</em>).</li>
<li>Ahora tocar&iacute;a entrar en cada una de esas m&aacute;quinas en las que queremos propagar el c&oacute;digo y ejecutar un <em>hg pull ; hg update</em>.</li>
</ol>
<p><!--more--> Tedioso cuando el n&uacute;mero de m&aacute;quinas que se manejan tiene dos cifras. Intentemos automatizar todo el proceso a trav&eacute;s de las opciones que nos ofrece <a href="http:/mercurial.selenic.com/">Mercurial</a>. <br /><br /> Combinando la <a href="/342_ssh_sin_password.html">confianza entre equipos</a> por ssh y los <a href="/mercurial-hook-on-push.html">hooks de mercurial</a> podemos crear un monstruo sencillo de manejar.</p>
<div class=""center"><img title=""Esquema" src="gallery/esquema-mercurial.jpg" alt=""Esquema" /></div>
<h2>Local/Devel</h2>
<p>En la copia local lo primero que tenemos que hacer es un <em>clone</em> del repositorio <em>padre</em> y definir los archivos que vamos a ignorar. Por ejemplo los archivos de configuraci&oacute;n de base de datos o de peculiaridades locales de cada proyecto en producci&oacute;n (idioma, usuarios, themes, datos de usuario, im&aacute;genes...):</p>
```
[devel] # cat repo/.hgignore
syntax: glob
.hgignore
config.php
bd.php
.project
uploads*
```
<p><strong>Nota</strong>: una buena pr&aacute;ctica ser&iacute;a no agregar esos ficheros que estamos ignorando al repositorio, podemos agregar unos <em>config-example.php</em> o algo as&iacute; para tener una referencia pero no las configuraciones en s&iacute; -<em>imho</em>-. <br /><br /> A la hora de hacer un <em>pull</em> al repositorio del c&oacute;digo todos los archivos que concuerden con los patrones escritos en el fichero <em>.hgignore</em> no se agregar&aacute;n al repositorio. <strong>Ojo</strong>: el fichero <em>.hgignore</em> debe estar en el ra&iacute;z del repositorio y debe pertenecer al mismo usuario que vaya a hacer el pull.</p>
<h2>Repo c&oacute;digo</h2>
<p>Aqu&iacute; es donde viene la <em>chicha</em>. Te&oacute;ricamente en el repositorio del c&oacute;digo no habr&iacute;a demasiado que tocar, sin embargo es aqu&iacute; donde ejerceremos la mayor automatizaci&oacute;n del proceso. Para propagar los cambios a las m&aacute;quinas en producci&oacute;n podemos hacerlo de dos formas: <br /><br /></p>
<ol>
<li>Como hac&iacute;amos antes, entrando en cada m&aacute;quina en producci&oacute;n y hacer pull del repositorio del c&oacute;digo</li>
<li>Desde el repositorio del c&oacute;digo hacer push a todas las m&aacute;quinas en producci&oacute;n</li>
</ol>
<p><br /> Este punto ha sido el que m&aacute;s me ha costado entender, digamos que dependiendo de la direcci&oacute;n en la que se ejecute el <em>pull/push</em> los resultados son los mismos. Con lo que, si desde el repositorio del c&oacute;digo ejecutamos lo siguiente, los cambios se propagar&aacute;n a las m&aacute;quinas 1, 2, 3 y 4:</p>
```
[repo] # hg push ssh://usuario@maquina1/repo
[repo] # hg push ssh://usuario@maquina2/repo
[repo] # hg push ssh://usuario@maquina3/repo
[repo] # hg push ssh://usuario@maquina4/repo
```
<p>Tuneando un poco el <em>repo/.hg/hgrc</em> del repositorio podemos <em>acortar</em> el comando e intentar automatizarlo un poco m&aacute;s, fijaos:</p>
```
[repo] # cat repo/.hg/hgrc
[paths]
maquina1 = ssh://user@maquina1/repo
maquina2 = ssh://user@maquina2/repo
maquina3 = ssh://user@maquina3/repo
maquina4 = ssh://user@maquina4/repo
```
<p><strong>Ojo</strong>: el fichero <em>hgrc</em> debe estar dentro del directorio <em>.hg/</em> del repositorio y debe pertenecer al mismo usuario que vaya a hacer el pull. <br /><br /> De forma que para ejecutar el comando de propagaci&oacute;n a todas las m&aacute;quinas tendremos que hacer un:</p>
```
[repo] # hg push maquina1 ; hg push maquina2 ; hg push maquina3 ; hg push maquina4
```
<p>Ya tenemos los <em>push</em> automatizados, &iquest;y los updates del lado de <em>producci&oacute;n</em>?. Vamos a ello.</p>
<h2>M&aacute;quina producci&oacute;n</h2>
<p>Cada vez que una de estas m&aacute;quinas recibe un <em>push</em> del repositorio de c&oacute;digo, mercurial debe actualizar los datos con un <em>hg update</em>. Para hacerlo autom&aacute;ticamente en cada <em>push</em> -ya lo hemos visto <a href="../../../../mercurial-hook-on-push.html">en otro post</a>- en el repositorio de cada una de las m&aacute;quinas en producci&oacute;n configuramos el <em>repo/.hg/hgrc</em> de forma que:</p>
```
[hooks]
changegroup = hg update
```
<p><strong>Ojo</strong>: el fichero <em>hgrc</em> debe estar dentro del directorio <em>.hg/</em> del repositorio y debe pertenecer al mismo usuario que vaya a hacer el pull.</p>
<h2>Proceso</h2>
```
- Programamos en "devel" y guardamos cambios.
- [devel] # hg commit
- [devel] # hg push
- [repo] # hg push maquina1 ; hg push maquina2...
pushing to ssh://user@maquina1/repo
searching for changes
pushing to ssh://user@maquina2/repo
searching for changes
...
```

View File

@ -0,0 +1,36 @@
---
title: "Mercurial en Fedora Core 4 y CentOS 5"
date: 2010-03-08T17:22:14Z
draft: false
tags: [ ]
image:
---
Necesitaba instalar <a href="http://mercurial.selenic.com/">Mercurial</a> en varias máquinas totalmente desactualizadas, concretamente una <em>Fedora Core release 4 (Stentz)</em> y una <em>CentOS release 5 (final)</em>, pensé que iba a ser un lío de dependencias pero al final ha resultado inexplicablemente más sencillo de lo esperado:
<!--more-->
<h2>Fedora Core 4</h2>
```
# cat /etc/fedora-release
Fedora Core release 4 (Stentz)
# wget http://packages.sw.be/mercurial/mercurial-1.0.2-1.fc4.rf.i386.rpm
# rpm -Uvh mercurial-1.0.2-1.fc4.rf.i386.rpm
# hg --version
Mercurial Distributed SCM (version 1.0.2)
```
<h2>CentOS 5</h2>
```
# cat /etc/redhat-release
CentOS release 5 (Final)
# wget http://packages.sw.be/mercurial/mercurial-1.3.1-1.el5.rf.i386.rpm
# rpm -Uvh mercurial-1.3.1-1.el5.rf.i386.rpm
# hg --version
Mercurial Distributed SCM (version 1.3.1)
```
<h2>Binarios</h2>
Vale que no sean la última versión (1.5 <abbr title="en este momento">atm</abbr>) pero estos binarios me han ahorrado bastante tiempo y evitado un problema, mola :).

View File

@ -0,0 +1,17 @@
---
title: "Mercurial: Hook on push"
date: 2010-02-21T13:21:26Z
draft: false
tags: [ ]
image:
---
Lo tenía pendiente desde que cambié de Subversion a Mercurial, sabía que se podía y que era algo trivial pero lo vas dejando y bueno, <em>just happens</em>. El caso es que cuando haces un <em>push</em> al servidor lo normal es hacer un <em>update</em> de su lado, así que el <em>hook</em> que lo automatiza es el correspondiente:
```
[server]$ cat /path/del/repo/.hg/hgrc
[hooks]
changegroup = hg update
```
Y fuera preocupaciones. La obligada referencia a <a href="http://the.taoofmac.com/space/blog/2007/09/14/1130">Tao of Mac</a>, desde donde me vino la inspiración mientras intentaba poner el <em>Macbook</em> a punto.

View File

@ -0,0 +1,25 @@
---
title: "Mercurial: Merge branches"
date: 2013-07-17T10:38:15Z
draft: false
tags: [ ]
image:
---
<p>Tarde o temprano llegar&iacute;a la hora de trabajar con <em>branches</em>. Y ello lleva inequ&iacute;vocamente a tener que mezclarlas alg&uacute;n d&iacute;a. Suponiendo un escenario de repositorio con dos ramas (<em>default</em>, <em>new</em>), una vez el c&oacute;digo es estable y est&aacute; probado, haremos lo siguiente:</p>
```
$ hg branch
new
$ hg pull -u
$ hg commit -m "[ADD] New features"
$ hg push
$ hg update default
$ hg branch
default
$ hg merge new
$ hg commit -m "[MER] Merging default and new branches"
$ hg push
```
<p>El proceso es l&oacute;gico, estamos trabajando en la rama <em>new</em>, hacemos commit de todos los cambios, nos pasamos a la rama <em>default</em>, hacemos un <em>merge</em> de una con otra y <em>commiteamos</em> de nuevo la mezcla. Una vez hecho todo &eacute;sto ya volvemos a tener una sola rama <em>default</em> en la que seguir la linea de desarrollo.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -0,0 +1,36 @@
---
title: "Mercurial: pager extension"
date: 2014-01-23T15:19:07Z
draft: false
tags: [ ]
image:
---
<p>Sometimes when you are using <a href="http://mercurial.selenic.com/">Mercurial</a> you want to paginate the output results. With <em>paginate</em> I mean use someting like bash <em>more</em> or <em>less</em> commands. Trying to focus yourself in a long list display is nearly imposible without a pager, so you can do it <em>old-school-style</em>:</p>
```
$ hg st | more
$ hg st | less
```
<p>Or you can activate the Mercurial <a href="http://mercurial.selenic.com/wiki/PagerExtension">PagerExtension</a>, easy peasy:</p>
```
[extensions]
pager=
[pager]
pager = LESS='FRX' less
attend =
ignore = help
```
<p>Once you have saved theese lines on your <em>~/.hgrc</em>, all the Mercurial commands will be paged. Remember that,&nbsp;by default,&nbsp;<em>PagerExtension</em>&nbsp;only works with <em>annotate</em>, <em>cat</em>, <em>diff</em>, <em>export</em>, <em>glog</em>, <em>log</em> and <em>qdiff</em> commands. If you want it to be run with all the commands you have to add the "<strong><em>attend=</em></strong>" line. On the other hand, if you want to ignore some commands you can specify that with the "<em>ignore</em>" option as you can see in the above sample.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto; width: 100%; meight: 100%;" src="gallery/mercurial-pager.png" alt="Mercurial Pager" /></p>
<p>More information about the PagerExtension and Mercurial in general:</p>
<ul>
<li><a href="http://www.userlinux.net/mercurial-colores-y-pager.html">Mercurial, colores y pager (Spanish) on Userlinux</a></li>
<li><a href="http://mercurial.selenic.com/wiki/PagerExtension">PagerExtension oficial site</a></li>
<li><a href="http://stackoverflow.com/questions/7598558/mercurial-pager-extension">Mercurial PagerExtension on StackOverflow</a></li>
<li><a href="http://stackoverflow.com/questions/1869040/what-are-the-best-and-must-have-hg-mercurial-extensions">Must have Mercurial extensions on StackOverflow</a></li>
</ul>

View File

@ -0,0 +1,59 @@
---
title: "Mercurial sobre Apache"
date: 2010-05-07T14:52:47Z
draft: false
tags: [ ]
image:
---
<p>
Mi predilecci&oacute;n por <a href="http://mercurial.selenic.com/">Mercurial</a> ha quedado patente en <a href="http://www.userlinux.net/mercurial-automatizando-al-maximo.html">alg&uacute;n</a> que <a href="http://www.userlinux.net/primeros-pasos-con-mercurial.html">otro</a> <a href="http://www.userlinux.net/mercurial-hook-on-push.html">post</a>, as&iacute; que una vez estamos conforme con nuestro servidor de versiones llega el momento de dar un paso m&aacute;s. Intentaremos configurar un interfaz web para mostrar el c&oacute;digo a todo el mundo (una especie de <a href="http://trac.edgewall.org/">Trac</a> solo para c&oacute;digo y adaptado a <em>Mercurial</em>).</p>
<p>
El proceso es tan sencillo como crear otro <em>VirtualHost</em> en tu Apache con unas caracter&iacute;sticas un poco especiales porque en vez de tirar de archivos din&aacute;micos (.php, .asp&hellip;) vamos a tirar de un cgi en Python, as&iacute; que la configuraci&oacute;n ser&iacute;a algo as&iacute;:</p>
<p>
<!--more--></p>
```
&lt;VirtualHost *:80&gt;
ServerAdmin webmaster@localhost
DocumentRoot /home/www/userlinux/sd/mercurial/cgi-bin/
ServerName hg.userlinux.net
ScriptAlias / /home/www/userlinux/sd/mercurial/cgi-bin/hgwebdir.cgi/
&lt;Directory &quot;/home/www/userlinux/sd/mercurial/cgi-bin/&quot;&gt;
SetHandler cgi-script
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
&lt;/Directory&gt;
ErrorLog /home/www/userlinux/sd/mercurial/logs/error.log
CustomLog /home/www/userlinux/sd/mercurial/logs/access.log combined
&lt;/VirtualHost&gt;
```
<p>
Fijaos que el archivo <em>hgwebdir.cgi</em> es el que lleva todo el trabajo, y &iquest;de d&oacute;nde lo sacamos?. Si tenemos instalado Mercurial (cosa que se da por supuesta), ya lo tenemos en el sistema, tan solo hemos de copiarlo a su nueva ubicaci&oacute;n y tan contentos:</p>
<p>
&nbsp;</p>
```
# cp /usr/share/doc/mercurial/examples/hgwebdir.cgi /home/www/userlinux/sd/mercurial/cgi-bin/ # chmod a+x /srv/hg/cgi-bin/hgwebdir.cgi
```
<p>Ahora vamos a configurar alguna que otra preferencia del interfaz web, para ello creamos al mismo nivel del <em>cgi</em> un archivo de configuraci&oacute;n llamado <em>hgweb.config</em>:</p>
```
[paths]
rcms-plugin-prueba = /home/mercurial/rcms-plugin-prueba/
[web]
style = gitweb
allow_archive = bz2 gz zip
maxchanges = 200
```
<p>
En &eacute;l -como veis- definimos los repositorios que vamos a mostrar (nombre = ruta) y varias opciones de la web como su theme, si permitimos o no descargar el contenido del repositorio&hellip;</p>
<p>
Ojo con los permisos, tanto del archivo de configuraci&oacute;n como de los repositorios, a los que ha de acceder el usuario propietario de Apache para leer. Un reinicio de Apache bastar&iacute;a para tener el repositorio listo:</p>
<p style="text-align: center;">
<img alt="" src="gallery/mercurial-apache.png" style="width: 400px; height: 385px;" /></p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

View File

@ -0,0 +1,14 @@
---
title: "New monitor: Acer G276HLA"
date: 2015-07-20T11:48:53Z
draft: false
tags: [ ]
image: acerg276hl2.jpg
---
<p>A <a href="http://www.twitter.com/marcosbl">great friend of mine</a> adviced me about a bid during the Amazon Premium Day. We have been talking about monitors some time ago and he remembered I was searching a replacement for my 22" Benq.</p>
<p>It is a led monitor <a href="http://www.amazon.es/gp/product/B007Z1AWT6?psc=1&amp;redirect=true&amp;ref_=oh_aui_detailpage_o01_s01">Acer G276HLA</a> with 27 inches and DVI + HDMI ports, you can read the other specifications in the <a href="http://www.acer.co.uk/ac/en/GB/content/model/UM.HG6EE.A01">Acer web</a>.</p>
{{< gallery match="gallery/*" sortOrder="asc" rowHeight="150" margins="5" thumbnailResizeOptions="600x600 q90 Lanczos" previewType="blur" embedPreview="true" >}}
<p>One of the "cons" is the max. allowed resolution (<em>1920x1080</em>), but as my Macbook Air (not the retina one) has that same value, it's good for me. After a couple of days using it, I'm totally adapted and I think I've done a good buy.</p>

View File

@ -0,0 +1,10 @@
---
title: "Nginx"
date: 2011-11-10T21:16:03Z
draft: false
tags: [ ]
image:
---
<p>
Cuando algo tan sencillo de instalar y de configurar se convierte en un servicio <em>mejor</em> que sus antecesores en pr&aacute;cticamente todas sus caracter&iacute;sticas podemos decir que hemos dado un salto de calidad importante. Y eso que todav&iacute;a no he probado la &uacute;ltima rama. Gracias <a href="http://sysoev.ru/en/">Igor</a>.</p>

View File

@ -0,0 +1,17 @@
---
title: "Pair programming con tmux"
date: 2013-02-25T08:56:09Z
draft: false
tags: [ ]
image:
---
<p>Con screen <a href="../../../../screen-el-nuevo-kibitz.html">era sencillo</a>&nbsp;crear un entorno para el pair programming, pero con <a href="http://tmux.sourceforge.net/">tmux</a> tambi&eacute;n es trivial. Me refiero a compartir una sesi&oacute;n de terminal para poder interactuar desde varias localizaciones distintas:</p>
```
tmux -S /tmp/pair
chmod 777 /tmp/pair
tmux -S /tmp/pair attach
```
<p>Se trata de crear una sesi&oacute;n especificando el destino, asignarle permisos (en este caso a todo el mundo) y conectarse a esa sesi&oacute;n con el otro usuario deseado. A partir de ah&iacute; ambos usuarios (el que ha creado la sesi&oacute;n y el que se ha conectado posteriormente) pueden interactuar simult&aacute;neamente.</p>

View File

@ -0,0 +1,16 @@
---
title: "Papá, ¡somos campeones del mundo!"
date: 2010-07-12T01:40:30Z
draft: false
tags: [ ]
image:
---
<p style="text-align: center;">
<img alt="" src="gallery/campeones-del-mundo.jpg" style="width: 400px; height: 253px;" /></p>
<p>
Me compraste mi primer bal&oacute;n, mi primera equipaci&oacute;n, me ense&ntilde;aste a jugar limpio, a vivir los partidos con entusiasmo, con emoci&oacute;n pero con serenidad, a creer siempre en el buen f&uacute;tbol, en la humildad, en los buenos jugadores. Sab&iacute;as que alg&uacute;n d&iacute;a llegar&iacute;a, ten&iacute;a que llegar despu&eacute;s de tantos partidos, de tanto sufrimiento, de tantos palos. Nunca perdiste la esperanza.</p>
<p>
Y ha llegado, ha sido hoy, gracias a la mejor generaci&oacute;n de futbolistas de la historia del pa&iacute;s puedo decir que hemos visto juntos a Espa&ntilde;a ganar una Eurocopa y un Mundial. Y es muy posible que ni mis hijos ni mis nietos puedan decir lo mismo a sus padres.</p>
<p>
<strong>Pap&aacute;... &iexcl;&iexcl;somos campeones del mundo!!.</strong></p>

View File

@ -0,0 +1,11 @@
---
title: "PyConES 2014"
date: 2014-11-20T13:53:25Z
draft: false
tags: [ ]
image: pycones2014.jpg
---
<p>El pasado fin de semana del 8-9 de Noviembre (2014) se celebr&oacute; en Zaragoza la segunda edici&oacute;n de la&nbsp;<a href="http://2014.es.pycon.org/">PyConES</a>, la conferencia nacional de Python con mayor relevancia de Espa&ntilde;a.&nbsp;<a href="http://2013.es.pycon.org/">El a&ntilde;o pasado</a>&nbsp;tambi&eacute;n asistimos en su primera edici&oacute;n celebrada en Madrid y este a&ntilde;o no iba a ser menos as&iacute; que all&aacute; me fui con&nbsp;<a href="https://twitter.com/wushell">Borja</a>, en tren, desde Lugo.</p>
<p>Las&nbsp;<a href="http://2014.es.pycon.org/talks">charlas</a>&nbsp;a las que asist&iacute; fueron muy interesantes, normalmente casi siempre lo son si te pones en&nbsp;<em>modo esponja</em>&nbsp;e intentas sacar provecho a cada frase o idea del ponente. Al igual de interesante que coincidir con toda esa gente que vemos de&nbsp;<em>sarao</em>&nbsp;en<em>sarao</em>&nbsp;y hacer efectivas algunas desvirtualizaciones.</p>
<p>Y como el a&ntilde;o pasado me olvid&eacute; de publicar&nbsp;<a href="../../../../gallery/photos/tag/pycones2013/">las fotos</a>, este a&ntilde;o no quer&iacute;a repetir la desfachatez: <a href="http://oscarmlage.com/gallery/photos/tag/pycones2014/">FOTOS</a>, as&iacute; que sin tiempo para una rese&ntilde;a m&aacute;s profunda... &iexcl;nos vemos en la PyConES2015!.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Some files were not shown because too many files have changed in this diff Show More