tag:blogger.com,1999:blog-28077181333984184662024-03-05T14:02:34.629+01:00::blogdenotas::Colección de apuntes varios...Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.comBlogger54125tag:blogger.com,1999:blog-2807718133398418466.post-31881134915696191112020-08-26T19:44:00.003+02:002020-08-26T20:04:53.975+02:00Obtener informacion de repositorios a través de los metadatos .git publicados por error<p> A raiz de CTF realizado recientemente, me ha parecido interesante publicar este post sobre los errores de seguridad que se encuentran en muchos sitios. En este caso, el tener publicado los metadatos de un repositorio clonado. Veamos cómo obtener esta información de manera muy sencilla. </p><p><br /></p><h3 style="text-align: left;">Prerequisitos si queremos trabajar de manera anónima:</h3><p><br /><span style="font-family: courier;">systemctl start tor</span><br /><br />Luego podremos lanzar los comandos con <span style="font-family: courier;">torify</span> o <span style="font-family: courier;">torsock</span>, de esta manera trabajaremos a través de la red tor.<br /><br />Se puede verificar de esta manera (donde veremos que la ip que tenemos asignada difiere):<br /><br /><span style="font-family: courier;">curl ifconfig.me --> Nos devolverá nuestra IP</span><br /><span style="font-family: courier;">torsocks curl ifconfig.me --> Nos devolverá la IP del nodo TOR</span><br /><br /><b>URL de ejemplo</b>: http://10.10.206.152 <b></b></p><h3 style="text-align: left;"><b>Escanear directorios publicados en la url:</b></h3><p><br />Tenemos varias formas:<br /><br /></p><ul style="text-align: left;"><li><span style="font-family: courier;">dirb http://10.10.206.152</span></li></ul><p><br />salida:<br /><br /><span style="font-family: courier;">-----------------<br />DIRB v2.22 <br />By The Dark Raver<br />-----------------<br /><br />START_TIME: Wed Aug 26 17:41:46 2020<br />URL_BASE: http://10.10.206.152/<br />WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt<br /><br />-----------------<br /><br />GENERATED WORDS: 4612 <br /><br />---- Scanning URL: http://10.10.206.152/ ----<br />+ http://10.10.206.152/.git/HEAD (CODE:200|SIZE:23) <br />==> DIRECTORY: http://10.10.206.152/css/ <br />+ http://10.10.206.152/index.html (CODE:200|SIZE:6890) <br /> <br />---- Entering directory: http://10.10.206.152/css/ ----<br /> <br />-----------------<br />END_TIME: Wed Aug 26 17:51:41 2020<br />DOWNLOADED: 9224 - FOUND: 2<br /></span><br /><br /></p><ul style="text-align: left;"><li><span style="font-family: courier;">gobuster dir -u 10.10.206.152 -w /usr/share/dirb/wordlists/common.txt</span></li></ul><p><br />salida:<br /><br /><span style="font-family: courier;">===============================================================<br />Gobuster v3.0.1<br />by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)<br />===============================================================<br />[+] Url: http://10.10.206.152<br />[+] Threads: 10<br />[+] Wordlist: /usr/share/dirb/wordlists/common.txt<br />[+] Status codes: 200,204,301,302,307,401,403<br />[+] User Agent: gobuster/3.0.1<br />[+] Timeout: 10s<br />===============================================================<br />2020/08/26 17:43:09 Starting gobuster<br />===============================================================<br />/.git/HEAD (Status: 200)<br />/css (Status: 301)<br />/index.html (Status: 200)<br />===============================================================<br />2020/08/26 17:43:38 Finished<br />===============================================================<br /></span> </p><p style="text-align: left;"><b>En ambos casos vemos que se está publicando un directorio .git.</b> Esto se debe a que dentro del directorio de publicación web se ha clonado un repositorio git, el cual deja un directorio .git con los metadatos del repositorio utilizado. <br /><br />Si nos descargamos estos datos, podemos tener la información de dicho repositorio, así como sus distintas versiones y commits realizados.<br /><br /></p><h3 style="text-align: left;">Descarga de contenido .git </h3><p style="text-align: left;"></p><p>Para descargarlo, lo podemos hacer de varias formas:<br /> </p><ul style="text-align: left;"><li>Utilizar el kit de herramientas de https://github.com/internetwache/GitTools.git, el cual contiene un script (gitdumper.sh)<br /></li></ul><p><br /><span style="font-family: courier;">gitdumper.sh http://10.10.206.152/.git/ dir_git</span><br /><br />una vez descargado, dentro de dir_git tendremos el directorio oculto .git:<br /><br /><span style="font-family: courier;">ls -aR<br />.:<br />. .. .git<br /><br />./.git:<br />. .. config description HEAD index info logs objects packed-refs refs<br /><br />./.git/info:<br />. .. exclude<br /><br />./.git/logs:<br />. .. HEAD refs</span><br /><br /></p><ul style="text-align: left;"><li>Realizar una descarga recursiva con wget:</li></ul><p><br /><span style="font-family: courier;">wget --mirror --include-directories=/.git http://10.10.206.152/.git/</span><br /><br />obteniendo un nuevo directorio 10.10.206.152 con el .git dentro.<br /> </p><p><b>Ahora recuperaremos la información de git</b>, esto también lo podemos hacer de varias formas:<br /> </p><ul style="text-align: left;"><li><span style="font-family: courier;">git reset --hard</span></li></ul><ul style="text-align: left;"><li><span style="font-family: courier;">git checkout .</span></li></ul><p><b>Ahora ya podemos hacer un ls y ver el contenido</b>, así como ver el histórico de commits realizados:<br /><br /><span style="font-family: courier;">git log</span><br /><br />y <span style="font-family: inherit;">ver las</span> diferencias entre una versión y otra:<br /><br /><span style="font-family: courier;">git diff 395e087334d613d5e423cdf8f7be27196a360459 index.html</span><br /><br />o <span style="font-family: inherit;">cambiar</span> a un estado anterior:<br /><br /><span style="font-family: courier;">git checkout 395e087334d613d5e423cdf8f7be27196a360459</span><br /> </p><p style="text-align: left;"></p><h3 style="text-align: left;"><b>Formas de descubrir directorios .git. </b></h3><p style="text-align: left;"> </p><ul style="text-align: left;"><li>Podemos hacer uso de un sript de nmap para detectar estos directorios:</li></ul><p><br /><span style="font-family: courier;">nmap -sS -p80 --script=http-git ip_destino</span><br /></p><ul style="text-align: left;"><li>Con el script getfinder (https://github.com/internetwache/GitTools.git), donde le pasamos en un fichero el listado de sitios a analizar</li></ul><p><br /><span style="font-family: courier;">python3 gitfinder.py -i input.txt -o salida</span><br /></p><ul style="text-align: left;"><li>Y por supuesto con google dork:</li></ul><p><br /><span style="font-family: courier;">intitle:"Index of" ".git"</span></p><p><span style="font-family: courier;"> <br /></span></p><p><span style="font-family: courier;"><span style="font-family: verdana;"></span></span></p><p><span style="font-family: courier;"><span style="font-family: verdana;"><b><u>Así que recuerda</u></b>, no dejes directorios .git en los directorios publicados, o al menos, bloquea el acceso con htaccess. </span><br /></span></p><p><span style="font-family: courier;"> </span><br /></p>Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-49636834237619676712017-04-18T19:47:00.000+02:002017-04-18T21:00:55.335+02:00Configurar Nano Wifi TL-WN725N en Raspberry piHace poco me regalaron una raspberry pi, y junto con ella, un dongle wifi usb TP-LINK, modelo TL-WN725N. En principio se supone que no debe haber problemas de compatibilidad entre este dongle wifi y nuestra raspberry, pero si la versión de nuestro dongle wifi es la 2 (en la caja viene como Ver:2.0) la cosa cambia.<br />
<br />
En mi caso tenía instada la última versión de raspbian, la cual traía una versión de kernel superior a la 3.10.18. Esta versión de kernel es la que funciona con nuestra modelo de dongle wifi (al menos según he podido averiguar). De modo que para poder reconocer el dongle wifi, tendremos que bajar a esta versión del kernel:<br />
<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">sudo rpi-update 8fd111f77895450323abc5b34efde19548ffc480</span></span><br />
<br />
Tras reiniciar, tendremos el siguiente kernel:<br />
<br />
Linux raspberrypi 3.10.18+ #587 <br />
<br />
Ahora sólo nos queda instalar el driver:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">wget https://dl.dropboxusercontent.com/u/80256631/8188eu-20131110.tar.gz <br />tar -zxvf 8188eu-20131110.tar.gz <br />cat README <-- view README file with firmware install details<br />sudo cp rtl8188eufw.bin /lib/firmware/rtlwifi <-- install firmware file if not already loaded<br />sudo install -p -m 644 8188eu.ko /lib/modules/3.10.18+/kernel/drivers/net/wireless<br />sudo insmod /lib/modules/3.10.18+/kernel/drivers/net/wireless/8188eu.ko<br />sudo depmod -a</span></span><br />
<br />
<br />
Espero que sirva de utilidad.Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com3tag:blogger.com,1999:blog-2807718133398418466.post-28746603993510909142013-08-21T13:35:00.001+02:002013-08-21T13:35:43.990+02:00Error undefined symbol: PyUnicodeUCS2<div style="text-align: justify;">
Hace poco actualicé mi versión de ubuntu, tras lo cual me encontré con algunos errores en mis aplicaciones implementadas con Django. Resulta que el sistema unicode que utilizaba el sistema antes era el UCS2, y la versión de python que tengo compilada para Django también, pero al actualizar el sistema se ha cambiado a UCS4, por lo que algunas de mis aplicaciones ofrecen ese error tan feo:</div>
<br />
undefined symbol: PyUnicodeUCS2<br />
<br />
en concreto para el módulo pycrypto.<br />
<br />
<div style="text-align: justify;">
Podemos ver que tipo de sistema unicode utiliza nuestra versión de python con este sencillo script:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">/ruta/instalacion/python -c "import sys; print sys.maxunicode > 65536 and 'UCS4' or 'UCS2'"</span><br />
<br />
<div style="text-align: justify;">
Si efectivamente vemos que utiliza UCS2, tendremos que volver a compilar nuestra versión de python con el siguiente parámetro:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">--enable-unicode=ucs4</span><br />
<br />
<div style="text-align: justify;">
y tras esto volver a reinstalar el módulo afectado, en mi caso pycrypto (con pip por ejemplo). Tras esto ya debería volver a funcionar nuestra aplicación.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-65143380521490356092013-07-26T08:57:00.000+02:002013-07-26T08:57:05.580+02:00Codificación de ficherosAlgunos comandos para comprobar la codificación de ficheros y modificarla:<br /><br /><br /><b>- Ver codificación</b><br /><span style="font-family: "Courier New",Courier,monospace;"><br />file --mime-encoding prueba.html</span><br /><br /><b>- Cambiar codificación (de iso-8859-1 a utf8)</b><br /><br /><span style="font-family: "Courier New",Courier,monospace;">iconv -f iso-8859-1 -t utf8 prueba.html > nuevo_prueba.html</span><br /><b><br />- Cambiar codificación sobre el mismo fichero (uso de sponge, para lo cual habrá que instalar sudo apt-get install moreutils)</b><br /><span style="font-family: "Courier New",Courier,monospace;"><br />iconv -f iso-8859-1 -t utf8 prueba.html | sponge prueba.html</span><br /><br />sponge permite redirigir las salida de un comando que se ejecuta sobre un fichero sobre el mismo fichero.Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-28765685441087337482013-06-03T13:48:00.005+02:002013-06-03T13:48:50.420+02:00blockdiag<div style="text-align: justify;">
Blockdiag nos permite generar diferentes tipos de diagramas desde ficheros de texto. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Un pequeño ejemplo sacado de la página oficial (<a href="http://blockdiag.com/en/blockdiag/examples.html">http://blockdiag.com/en/blockdiag/examples.html</a>) sería el siguiente:</div>
<br />
<pre>blockdiag {
// Set labels to nodes.
A [label = "foo"];
B [label = "bar"];
// And set text-color
C [label = "baz"];
// Set labels to edges. (short text only)
A -> B [label = "click bar", textcolor="red"];
B -> C [label = "click baz"];
C -> A;
}</pre>
<pre> </pre>
<pre> </pre>
<div style="text-align: justify;">
Una vez que lo tenemos guardado (por ejemplo prueba.diag) generaremos el diagrama de la siguiente manera:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
blockdiag --no-transparency prueba.diag</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
y obtendremos en la misma ruta un fichero llamado prueba.png:</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://blockdiag.com/en/_images/blockdiag-808fd3bf1cd56d801530402c6fd855200eaa1280.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="60" src="http://blockdiag.com/en/_images/blockdiag-808fd3bf1cd56d801530402c6fd855200eaa1280.png" width="320" /></a></div>
<div style="text-align: justify;">
Para su instalación, puede se que nos de algún error si ya tenemos instalado PIL, por lo que para curarnos en saludo podemos hacer esto:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">pip uninstall PIL<br />pip uninstall pillow<br />pip install pillow --upgrade<br />pip install blockdiag</span><br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Echad un vistazo a los diferentes tipos de diagramas que se pueden generar, seguro que puede venir bien para alguna tarea.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-6061076288011187652013-06-03T13:29:00.005+02:002013-06-03T13:38:15.958+02:00Django_evolution<div style="text-align: justify;">
A la hora de hacer cambios en el modelo de datos de nuestras aplicaciones django, siempre se nos plantean los problemas que tenemos que subsanar a mano en las tablas de la bbdd. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para estos problemas conocía South, pero según que tipo de campos tengamos en nuestro modelado, hay veces que no nos sirve al 100%. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Hace poco he descubierto django_evolution, el cual sí que funciona a la perfección y es muy simple de utilizar.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para su instalación:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;">pip install django_evolution</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Lo añadimos a INSTALLED_APPS y luego simplemente:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;">python manage.py syncdb</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Cuando hagamos una modificación en el modelo de datos:</div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;"><br /></span></div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;">python manage.py evolve --hint --execute</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para más información: <a href="http://django-evolution.googlecode.com/svn/trunk/docs/evolution.txt">http://django-evolution.googlecode.com/svn/trunk/docs/evolution.txt</a></div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-78733225142368800222013-04-08T16:26:00.000+02:002013-04-08T16:26:24.525+02:00Modificación de inlines.js de Django<div style="text-align: justify;">
Buenas, por si a alguien le interesa, he realizado una modificación del fichero inlines.js (el encargado de los formularios inlines de Django). Resulta que para una aplicación donde tenia muchos formularios inlines (tanto apilados como tabulados) me resultaba un tanto engorroso tener que desplazarme hasta el final para poder pinchan en el enlace "Añade otro ..." y añadir un nuevo registro. Así que he modificado dicho fichero para añadir este enlace al principio y al final, y con un autodesplazamiento de la página para que enfoque el nuevo registro.<br /><br />Este sería el aspecto para un formulario inlines tabulado, donde tiene muchos registros, y podemos ver que aparece el enlace para añadir tanto arriba:</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeqXz7Ca3o4NnYC3odYlpxL0XH6dC3xaTTnKUyc6kTMgVDo-M0cV0xoHkytcf0eGzB9Ak0wNgURcFFkR403sGWskOJWocJ1wUlYcTExtIwRCCw2mXKi1XJqPJv1b44Kn11d-np4Ze_X8Y/s1600/inlines01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeqXz7Ca3o4NnYC3odYlpxL0XH6dC3xaTTnKUyc6kTMgVDo-M0cV0xoHkytcf0eGzB9Ak0wNgURcFFkR403sGWskOJWocJ1wUlYcTExtIwRCCw2mXKi1XJqPJv1b44Kn11d-np4Ze_X8Y/s320/inlines01.jpg" width="320" /></a></div>
<br />
<div style="text-align: justify;">
</div>
<br />
<br />
<div style="text-align: justify;">
como abajo:</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiT96vYJE14h8io4rpqJypk8ofWh2lAEY48swUdDEEUxjdqfSisK7mdmFjpngoeUiddTZymF_I1w7PPE4LNH0Ul3huQXGeSZOoi7LnP7i01AdgwTICg-77qyLbeJ6BbSKtNFicQEDNBwA/s1600/inlines02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiT96vYJE14h8io4rpqJypk8ofWh2lAEY48swUdDEEUxjdqfSisK7mdmFjpngoeUiddTZymF_I1w7PPE4LNH0Ul3huQXGeSZOoi7LnP7i01AdgwTICg-77qyLbeJ6BbSKtNFicQEDNBwA/s320/inlines02.jpg" width="320" /></a></div>
<div style="text-align: justify;">
</div>
<br />
<br />
<div style="text-align: justify;">
Adjunto el patch para aplicar tanto para la versión ampliada como a la minified.<br /><br /><a href="https://sites.google.com/site/ficherows/home/inlines_patch?attredirects=0&d=1">patch_inlines.js</a><br /><a href="https://sites.google.com/site/ficherows/home/inlines.min_patch?attredirects=0&d=1">patch_inlines.min.js</a></div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-54026439307125182252012-12-05T18:10:00.000+01:002012-12-05T18:10:32.257+01:00Django y celery<div style="text-align: justify;">
Me he decidido a utilizar un sistema de cola de eventos en una aplicación de django. Lo que me ha llevado a tomar esta decisión es fundamentalmente evitar tener que esperar a que un proceso termine para poder seguir interactuando con la aplicación web. Por tanto celery me ofrece justo esto.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Cuando hablamos de celery, siempre viene acompañado del backend RabbitMQ (cola de eventos de alto rendimiento). Pero hay otros sistemas menos complejos, como kombu, el cual utiliza la propia bbdd de django para el almacenamiento de mensajes.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Tenemos la siguiente situación:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- <b>Django 1.3</b> con una aplicación que lanza procesos sobre diversos servidores</div>
<div style="text-align: justify;">
- <b>mod_wsgi</b> para comunicar apache con django</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Estos son los dos sistemas que tendremos que tocar para añadir celery a nuestra aplicación.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Tendremos que instalar lo siguiente:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- pip install celery django-kombu django-celery</span></span></div>
<div style="text-align: justify;">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- apt-get install supervisor ó yum install supervisor (depende del s.o.)</span></span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Vamos a decirle a django que vamos a utilizar celery, para ello en nuestro settings.py vamos a añadir lo siguiente:</div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Importamos django-celery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">import djcelery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">djcelery.setup_loader()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Para evitar problemas a la hora de llamar a las aplicaciones dentro</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#INSTALLED_APPS, haremos que tome en el sys.path la ruta actual.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Por tanto, si la llamada de nuestra aplicación dentro de INSTALLED_APPS la</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#hacemos como: misitio.aplicacion, ahora sólo la tendremos que llamar: aplicacion</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">sys.path.append(os.getcwd())</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">INSTALLED_APPS = (</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ....</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> 'djkombu',</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> 'djcelery',</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Configuración propia de celery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Backend a utilizar</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">BROKER_BACKEND = "djkombu.transport.DatabaseTransport"</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Url de la cola de mensajería, kombu utiliza la bbdd de django</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">BROKER_URL = "django://"</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Para las tareas programadas, indicamos que utilice la bbdd para su control</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Ahora para wsgi, hay que indicarle que vamos a usar celery, por tanto añadimos en nuestro django.wsgi:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">import djcelery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">djcelery.setup_loader()</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Con esto ya hemos acabado de configurar django, ahora actualizaremos nuestar bbdd para que incorpore las tablas necesarias:</div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">python manage.py syncdb</span></span><br />
<br />
<br />
<div style="text-align: justify;">
La definición de tareas la haremos dentro de un fichero llamado tasks.py que crearemos dentro de la carpeta de nuestra aplicación. Tenemos la siguiente estructura:</div>
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">misitio</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- __init__.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- aplicacion</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- __init__.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- admin.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- models.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- tasks.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- tests.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- views.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- django.wsgi</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- manage.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- settings.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|-- templates</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |-- admin</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| | |-- base.html</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| | |-- base_site.html</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| | `-- index.html</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| `-- aplicacion</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| |</span><br />
<span style="font-family: "Courier New",Courier,monospace;">| `-- index.html</span><br />
<span style="font-family: "Courier New",Courier,monospace;">|</span><br />
<span style="font-family: "Courier New",Courier,monospace;">`-- urls.py</span><br />
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Vamos a definir una tarea muy simple, y que aparece en todos los ejemplos de celery, editamos el fichero tasks.py y añadimos esto:</div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">from celery import task</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">@task()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">def add(x, y):</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> return x + y</span></span><br />
<br />
<br />
<div style="text-align: justify;">
El decorador @task() será el que indique que la función add será tratada como una tarea asíncrona dentro de celery y que podremos invocar.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Vamos a arrancar celery y a realizar algunas pruebas para comprobar que funciona. Para arrancarlo, de momento vamos a hacerlo del siguiente modo:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">python manage.py celeryd -l info --settings=settings</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> -------------- celery@rafabono v3.0.12 (Chiastic Slide)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">---- **** ----- </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">--- * *** * -- [Configuration]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">-- * - **** --- . broker: django://localhost//</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- ** ---------- . app: default:0xb721c58c (djcelery.loaders.DjangoLoader)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- ** ---------- . concurrency: 4 (processes)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- ** ---------- . events: OFF (enable -E to monitor this worker)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- ** ---------- </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">- *** --- * --- [Queues]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">-- ******* ---- . celery: exchange:celery(direct) binding:celery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">--- ***** ----- </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[Tasks]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> . aplicacion.tasks.add</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Este es el mensaje que nos mostrará al arrancar. Como vemos nos da información del broker (el BROKER_URL que indicamos anteriormente), la concurrencia que ha tomado por defecto (4 procesos), la activación de eventos (que necesitaremos para la tareas programadas) y las colas. Más abajo nos da un listado de las tareas que tenemos en nuestro fichero tasks.py.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ya que tenemos andando nuestros sistema de cola de eventos, veamos si realmente funciona. En otro teminal abrimos una shell de django (para evitar tener que importar el settings):</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">python manage.py shell</span></span><br />
<br />
Y comenzaremos importanto la tarea y haciendo una llamada:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> from aplicacion.tasks import add</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> add.delay(2, 2)</span></span><br />
<br />
<br />
<div style="text-align: justify;">
el método delay es el que utilizaremos para llamar a la tarea add con celery. Esto simplemente sumará 2 + 2 pero de forma asíncrona. Tras hacer la llamada veremos lo siguiente:</div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><AsyncResult: ab56fd91-5ac3-44a8-abee-98d35c6ac537></span></span><br />
<br />
<div style="text-align: justify;">
Nos devuelve un objeto AsyncResult cuyo identificador es un uuid. La tarea ha sido enviada. Pero, ¿qué ha ocurrido con su ejecución?. Vamos al terminal desde donde lanzamos celery (python manage.py celeryd -l info --settings=settings) y veremos algo así:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[2012-12-05 16:59:18,503: INFO/MainProcess] Got task from broker: aplicacion.tasks.add[ab56fd91-5ac3-44a8-abee-98d35c6ac537]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[2012-12-05 16:59:18,599: INFO/MainProcess] Task aplicacion.tasks.add[ab56fd91-5ac3-44a8-abee-98d35c6ac537] succeeded in 0.0683989524841s: 4</span></span><br />
<br />
<div style="text-align: justify;">
Vemos como la tarea ha llegado a la cola y ha sido ejecutada, dando el resultado esperado: 4</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
¿Qué más cosas nos puede decir la tarea? Vamos a volver a hacer una llamada, pero esta vez pasándola a una variable:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado = add.delay(4, 4)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado.ready()</span></span><br />
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
ready() nos dirá si la tarea ha finalizado. La mejor prueba que podemos hacer es para celery (un crtl + c desde el terminal donde lo lanzamos) y volver a hacer una llamada:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado = add.delay(4, 4)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado.ready()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">False</span></span><br />
<br />
<br />
<div style="text-align: justify;">
La tarea no ha finalizado, pues la cola está parada. Por tanto esta tarea quedará encolada hasta que la cola vuelva a activarse. La volvemos a activar:</div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">python manage.py celeryd -l info --settings=settings</span></span><br />
<br />
y veremos como inmediatamente nos aparece el siguiente mensaje:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[2012-12-05 17:12:20,603: INFO/MainProcess] Task aplicacion.tasks.add[ee03a68a-0eb4-43c4-a98a-b3ba72fb5c8c] succeeded in 0.024453163147s: 8</span></span><br />
<br />
si volvemos a preguntar por su estado:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado.ready()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">True</span></span><br />
<br />
<br />
También podemos ver el resultado de la tarea:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado.result</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">8</span></span><br />
<br />
<br />
Y ver si ha finalizado correctamente:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">>>> resultado.successful()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">True</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Bien, esto sólo una pequeñísima parte de celery, para más información: <a href="http://docs.celeryproject.org/en/latest/">http://docs.celeryproject.org/en/latest/</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
La aplicación de todo esto: infinitas posibilidades. Cada cual que investigue y pruebe para adaptarlo a sus necesidades.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Si en vez de una tarea asíncrona queremos una tarea periódica, la forma de definirla será (por ejemplo):</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">@periodic_task(run_every=crontab(hour="*", minute="*/5", day_of_week="*"))</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">def prueba():</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> print "Hola"</span></span><br />
<br />
<br />
Esta tarea se ejecutará cada 5 minutos, imprimiendo un "Hola".<br />
<br />
<br />
<div style="text-align: justify;">
En cuanto a la forma de lanzar celery, para hacer pruebas siempre podemos hacerlo como hasta ahora, pero en un sistema productivo, habría que hacerlo de forma más segura. Una forma de hacerlo es con el servicio supervisor. Al comienzo de esta entra ya lo instalamos, por lo que tan sólo nos queda configurarlo.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Dependiendo de la version de supervisor, la configuración podrá varia más o menos. A continuación expongo un pequeño ejemplo del fichero supervisord.conf (suele estar en /etc/supervisor o directamente en /etc), sólo de la zona donde definimos celery:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[program:celery]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">command=/path/python/bin/python /path/django/misitio/manage.py celeryd -v 2 -B -E -l INFO --settings=settings</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">process_name = celery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">directory=/path/python/bin</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">user=celery</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">autostart=true</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">autorestart=true</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">priority = 0</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">stdout_logfile=/var/log/celeryd.log</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">redirect_stderr=true</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Simplemente agregamos una sección para celery, donde indicaremos el comando a lanzar. Tendremos que añadir la opción -B (modo celerybeat para que escuche las peticiones de las tareas programadas) y -E (para los eventos). En el comando habrá que especificar las rutas de python y de nuestro proyecto django.</div>
<div style="text-align: justify;">
También le indicamos el usuario con el que se va a lanzar (no es conveniente hacerlo con root).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con esto será suficiente, sólo nos queda iniciar supervisor (/etc/init.d/supervisor start). Si tenemos habilitado el acceso web podremos ver el estado de nuestro proceso celery.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-86933658256166803002012-12-03T11:08:00.001+01:002012-12-03T11:08:28.396+01:00Cambiar versión de python para django<div style="text-align: justify;">
A continuación explico como cambiar la versión de python que utiliza django, para disponer de una versión más nueva y con más posibilidades.</div>
<br />
El escenario sería el siguiente:<br />
<br />
<i>- Red Hat Enterprise Linux Server release 5.1</i><br />
<i>- Apache/2.2.3</i><br />
<i>- Django 1.3</i><br />
<i>- mod_wsgi (para conexión entre apache y python)</i><br />
<i>- Python 2.4.3</i><br />
<br />
<br />
<div style="text-align: justify;">
Queremos cambiar la versión de python para tener la 2.7.3.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Lo primero que haremos será instalar la nueva versión de python:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- Nos descargamos la versión 2.7.3: <a href="http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz">http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz</a></div>
<div style="text-align: justify;">
- La ruta de instalación será, por ejemplo, /opt/python273, luego pasamos a compilar:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">tar xvfz Python-2.7.3.tgz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">cd /Python-2.7.3</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">./configure --prefix=/opt/python273</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">make</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">make altinstall</span></span><br />
<br />
<div style="text-align: justify;">
Es importante hacer este make altinstall, pues vamos a instalar una versión alternativa de python, no queremos pisar la ya existente.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ya tenemos instalado python en la versión 2.7.3. Si en nuestro python/django tenemos instalado diversos paquetes, sería interesante sacar un listado de estos para poder instalarlos en la nueva versión de python. Para ello:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- Sacar listado de paquetes de la vesión actual de python:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">pip freeze > /tmp/paquetes.txt</span></span><br />
<br />
- Instalar los paquetes en la nueva versión de python:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">/opt/python273/bin/pip install -r /tmp/paquetes.txt</span></span><br />
<br />
<div style="text-align: justify;">
(Si no trae pip la nueva versión de python, pues nos lo bajamos y lo instalamos para esta versión).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ahora debemos compilar nuestro módulo mod_wsgi con la nueva versión de python, así que nos bajamos mod_wsgi: <a href="http://modwsgi.googlecode.com/files/mod_wsgi-3.4.tar.gz">http://modwsgi.googlecode.com/files/mod_wsgi-3.4.tar.gz</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
y para compilarlo:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">./configure --with-python=/opt/python273/bin/python2.7</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">make</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">make install</span></span><br />
<br />
<div style="text-align: justify;">
Con esto ya tenemos nuestro mod_wsgi compilado para la nuestra nueva versión de python, ya sólo nos queda indicar en apache la ruta de nuestro python, con lo que en nuestro fichero de configuración añadiremos la siguiente directiva:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">WSGIPythonHome /opt/python273</span></span><br />
<br />
<br />
<div style="text-align: justify;">
Si tienes un conf con un virtual configurado, esta directiva debe estar fuera del virtual (al final de </VirtualHost>).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Reiniciamos nuestro apache y listo, ya tenemos nuestro django funcionando con la nueva versión de python.</div>
<br />Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-6597175204218330422012-11-27T16:43:00.003+01:002012-11-27T16:44:07.932+01:00Establecer tiempo de sesión en django<div style="text-align: justify;">
Para controlar el tiempo de sesión en django, podemos hacer uso de las siguentes directivas dentro de nuestro settings.py:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#Tiempo de vida de la sesión en segundos<br />SESSION_COOKIE_AGE = 600 </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br />#Para que expire la sesión al cerrar el navegador. Por defecto está a False </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">SESSION_EXPIRE_AT_BROWSER_CLOSE = True </span></span><br />
<br />
<div style="text-align: justify;">
También es importante si tenemos varios proyectos publicados en el mismo apache, añadir la siguiente directiva:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">SESSION_COOKIE_NAME = 'cadena_distintiva'</span></span><br />
<br />
<div style="text-align: justify;">
donde 'cadena_distintiva' deberá ser diferente, para que cada proyecto tenga una cookie diferente y no entre en conflicto con las de otros.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-64002167555500322712012-11-27T16:37:00.002+01:002012-11-27T16:37:20.024+01:00Limpiar django_sessions<div style="text-align: justify;">
Ocurre que la tabla de django "django_session" suele crecer de forma desmesurada, debido a que django por sí sólo no limpia las sesiones expiradas (en gran parte porque al salir no lo hacemos con logout).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para evitar que crezca esta tabla, django nos ofrece el siguiente comando:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">cleanup</span><br />
<br />
<br />
<div style="text-align: justify;">
el cual limpia de la bbdd aquellas entradas de sesiones ya expiradas.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Lo ideal por tanto, es añadir en el cron una entrada como esta:</div>
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">0 8 * * * /usr/bin/python /ruta/proyecto/manage.py cleanup</span><br />
<br />
<div style="text-align: justify;">
Para que periódicamente (a las 8 de la mañana por ejemplo) limpie las sesiones caducadas de nuestro proyecto django.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-38990585735675561722012-11-21T13:28:00.000+01:002012-11-21T13:28:07.048+01:00Montar un repositorio con Git<div style="text-align: justify;">
Ayer me entró la curiosidad sobre cómo utilizar el gestor de control de versiones git. Ya tengo montado mi repositorio en una máquina remota y configurado para realizar los cambios desde mi equipo para luego subirlos a producción. Os comento cómo hacer esto.</div>
<br />
<u>Escenario:</u><br />
<br />
<div style="text-align: justify;">
<i>- Servidor de producción <b>[SERVIDOR]</b>. En este servidor tenemos corriendo una aplicación en django (en la ruta /opt/proyecto/aplicacionA). Aquí también motaremos nuestro repositorio remoto. Tendremos que tener git instalado (desde paquetería o compilando).</i></div>
<div style="text-align: justify;">
<i><br /></i></div>
<div style="text-align: justify;">
<i>- Equipo local <b>[LOCAL].</b> Aquí es desde donde haremos los desarrollos, para posteriormente, subirlos a producción. Tendremos que tener git instalado (desde paquetería o compilando).</i></div>
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Lo que sigue a continuación serán los pasos al estilo receta, para más info de git, acudir al sitio http://git-scm.com/book/es/</div>
<br />
<b>1) Crear repositorio remoto</b><br />
<br />
- Accedemos por ssh a SERVIDOR<br />
- Creamos la ubicación del repositorio y lo iniciamos:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [SERVIDOR] mkdir /opt/repo/aplicacionA.git<br /> [SERVIDOR] cd /opt/repo/aplicacionA.git<br /> [SERVIDOR] git init --bare</span></span><br />
<br />
<div style="text-align: justify;">
¡IMPORTANTE! El usuario que utilicemos para conectarnos por ssh al SERVIDOR debe tener permisos sobre /opt/repo/aplicacionA.git</div>
<br />
- Configuramos nuestro proyecto de producción para indicar la ubicación del repositorio y hacer la carga de código:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [SERVIDOR] cd /opt/proyecto/aplcacionA<br /> [SERIVDOR] git init<br /> [SERVIDOR] git remote add origin /opt/repo/aplicacionA.git<br /> [SERVIDOR] git add .<br /> [SERVIDOR] git commit -m 'Primera entrada de commit'<br /> [SERVIDOR] git push origin master</span></span><br />
<br />
Hasta aquí ya tenemos en nuestro repositorio remoto el código del proyecto de producción.<br />
<br />
<b>2) Descargar el repositorio</b><br />
<i> </i><br />
- Ahora nos descargaremos el proyecto en LOCAL para poder trabajar con él:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [LOCAL] mkdir /opt/desarrollo<br /> [LOCAL] cd /opt/desarrollo<br /> [LOCAL] git clone ssh://usuario@SERVIDOR//opt/repo/aplicacionA.git</span></span><br />
<br />
Ya tenemos en local una copia del código de producción.<br />
<br />
<b>3) Realizar cambios</b><br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- Si queremos realizar un cambio en local y luego aplicarlo a producción (ejemplo, añadir un nuevo fichero):</div>
<div style="text-align: justify;">
<br /></div>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [LOCAL] cd /opt/desarrollo/aplicacionA <br /> [LOCAL] touch README<br /> [LOCAL] git add README #Indicamos que tenga en cuenta este fichero<br /> [LOCAL] git commit -m "Añadimos fichero README" #Hacemos commit con un comentario<br /> [LOCAL] git push origin master #Subimos el fichero al repositorio</span></span><br />
<br />
- Ahora aplicaremos este cambio en producción:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [SERVIDOR] cd /opt/proyecto/aplcacionA<br /> [SERVIDOR] git pull origin master #Obtenemos la última versión del respositorio</span></span><br />
<br />
- Si nos equivocamos mientras hacemos un cambio:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> [LOCAL] cd /opt/desarrollo/aplicacionA<br /> [LOCAL] git add README<br /> [LOCAL] echo "Prueba" > README #Hacemos un cambio en README, pero nos arrepentimos y queremos volver al estado anterior<br /> [LOCAL] git reset README #Le decimos que no tenga en cuenta este fichero para los cambios en el respositorio<br /> [LOCAL] git checkout -- README #Volvemos a la versión del repositorio del último commit.</span></span><br />
<br />- Para recuperar archivos antiguos, remito a esta entrada:<br />
<br />
<a href="http://www.alvaroremesal.net/blog-alvaroremesal/recuperar-archivos-antigos-con-git">http://www.alvaroremesal.net/blog-alvaroremesal/recuperar-archivos-antigos-con-git</a><br />
<br />
<b>4) Crear ramas</b><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> git branch nueva_rama<br /> git branch -> nos muestra las distintas ramas existentes<br /> git checkout nueva_rama -> nos cambiamos a esta nueva rama</span></span><br />
<br />
<br />
Espero que sea útil.Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-12800254487993535642012-11-21T12:25:00.003+01:002012-11-21T12:25:38.666+01:00Sacar configuración importante de settings.py en Django<div style="text-align: justify;">
Es posible que necesitemos sacar cierta información de carácter sensible del fichero settings.py de un proyecto, bien por facilitar el trabajo con control de versiones, o por querer tener diferentes configuraciones entre entornos.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una manera sencilla de hacer esto es como se explica en https://code.djangoproject.com/wiki/SplitSettings.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Por ejemplo, imaginemos que queremos sacar la información de bbdd. Para ello nos vamos a crear un fichero settings.ini en /etc, cuyo contenido será este:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[database]</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">DATABASE_NAME: nombre_bbdd<br />DATABASE_USER: usuario<br />DATABASE_PASSWORD: pass<br />DATABASE_HOST: localhost<br />DATABASE_PORT: 3306</span></span><br />
<br />
Luego tan sólo tendremos que modificar nuestro settings.py añadiendo esto:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">from ConfigParser import RawConfigParser<br /><br />config = RawConfigParser()<br />config.read('/etc/settings.ini')</span></span><br />
<br />
<br />
<br />
Y en la sección DATABASES configuraremos lo siguiente:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">DATABASES = {<br /> 'default': {<br /> 'ENGINE': 'django.db.backends.mysql', <br /> 'NAME': 'config.get('database', 'DATABASE_NAME')', <br /> 'USER': config.get('database', 'DATABASE_USER'), <br /> 'PASSWORD': config.get('database', 'DATABASE_PASSWORD'), <br /> 'HOST': config.get('database', 'DATABASE_HOST'), <br /> 'PORT': config.get('database', 'DATABASE_PORT'), <br /> }<br />}</span></span><br />
<br />
<div style="text-align: justify;">
Y listo, ya tendremos nuestro settings.py configurado para poder ser distribuido sin tener que estar pendiente de modificar ningún dato sensible.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-27058474166359013282012-10-18T20:47:00.001+02:002012-10-18T20:47:27.255+02:00Corregir problemas de codificación en Django<div style="text-align: justify;">
Para evitar errores de codificación en Django del tipo: </div>
<br />
<span style="font-family: Courier New, Courier, monospace;">django 'ascii' codec can't encode character u'\xf3'</span><br />
<br />
<div style="text-align: justify;">
debido a la codificación que tenga por defecto python (que suele ser ascii), simplemente tenemos que añadir a nuestro settings.py las siguientes líneas:</div>
<div style="text-align: justify;">
<br /></div>
<br />
<span style="font-family: Courier New, Courier, monospace;">import sys</span><br />
<span style="font-family: Courier New, Courier, monospace;">reload(sys)</span><br />
<span style="font-family: Courier New, Courier, monospace;">sys.setdefaultencoding( "utf-8" )</span><br />
<div>
<br /></div>
<div>
Y conseguiremos establecer la codificación adecuada.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com2tag:blogger.com,1999:blog-2807718133398418466.post-91128374885191350772012-10-17T15:22:00.003+02:002012-10-17T15:22:36.394+02:00Ejecutar comandos personalizados de Django desde el cron<div style="text-align: justify;">
Para poder ejecutar comandos personalizados de django desde el cron, necesitaremos aplicar el siguiente parche (si es que aún no lo trae):</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<a href="https://code.djangoproject.com/attachment/ticket/5825/managepath.diff">https://code.djangoproject.com/attachment/ticket/5825/managepath.diff</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Información obtenida de <a href="https://code.djangoproject.com/ticket/5825">https://code.djangoproject.com/ticket/5825</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una vez aplicado el parche, ya podremos invocar nuestro comando de la siguiente manera:</div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">minutos hora * * * /usr/bin/python /sitio/proyecto/django/manage.py comando</span></span><br />
<br />
<br />
<br />Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-62727844792618611462012-10-10T11:22:00.002+02:002012-10-10T11:23:51.932+02:00Definir rango de puertos del ftp en alfresco 3.2<div style="text-align: justify;">
Debido a que el servicio de ftp de alfresco funciona en modo pasivo, este abrirá puertos por encima del 1024 para llevar a cabo la comunicación de datos.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Si tenemos un firewall muy restrictivo, nos interesará definir un rango determinado de puertos y abrir sólo ese rango.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para definir el rango de puertos debemos añadir a /tomcat/shared/classes/alfresco-global.properties los siguientes parámetros:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
ftp.dataPortFrom=50000</div>
<div style="font-family: "Courier New",Courier,monospace;">
ftp.dataPortTo=60000</div>
<br />
<div style="text-align: justify;">
En este caso estamos definiendo el rango del 50000 al 60000.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Veamos si esto funciona de verdad. Hacemos un ftp en modo debug a nuestro servidor:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
ftp -d 192.168.60.13 2122</div>
<div style="font-family: "Courier New",Courier,monospace;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace;">
Connected to 192.168.60.13.</div>
<div style="font-family: "Courier New",Courier,monospace;">
220 FTP server ready</div>
<div style="font-family: "Courier New",Courier,monospace;">
ftp: setsockopt: Bad file descriptor</div>
<div style="font-family: "Courier New",Courier,monospace;">
Name (servidor:rafa): usuario</div>
<div style="font-family: "Courier New",Courier,monospace;">
---> USER usuario</div>
<div style="font-family: "Courier New",Courier,monospace;">
331 User name okay, need password for usuario</div>
<div style="font-family: "Courier New",Courier,monospace;">
Password:</div>
<div style="font-family: "Courier New",Courier,monospace;">
---> PASS XXXX</div>
<div style="font-family: "Courier New",Courier,monospace;">
230 User logged in, proceed</div>
<div style="font-family: "Courier New",Courier,monospace;">
---> SYST</div>
<div style="font-family: "Courier New",Courier,monospace;">
215 UNIX Type: Java FTP Server</div>
<div style="font-family: "Courier New",Courier,monospace;">
Remote system type is UNIX.</div>
<br />
Indicamos el modo pasivo:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
ftp> passive</div>
<div style="font-family: "Courier New",Courier,monospace;">
Passive mode on.</div>
<br />
Y lanzamos un comando:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
ftp> ls</div>
<div style="font-family: "Courier New",Courier,monospace;">
ftp: setsockopt (ignored): Permission denied</div>
<div style="font-family: "Courier New",Courier,monospace;">
---> PASV</div>
<div style="font-family: "Courier New",Courier,monospace;">
227 Entering Passive Mode (192,168,60,13,211,39)</div>
<div style="font-family: "Courier New",Courier,monospace;">
---> LIST</div>
<div style="font-family: "Courier New",Courier,monospace;">
150 File status okay, about to open data connection</div>
<div style="font-family: "Courier New",Courier,monospace;">
drw-rw-rw- 1 user group 0 Jan 1 1970 Alfresco</div>
<div style="font-family: "Courier New",Courier,monospace;">
drw-rw-rw- 1 user group 0 Jan 1 1970 AVM</div>
<div style="font-family: "Courier New",Courier,monospace;">
226 Closing data connection</div>
<div style="font-family: "Courier New",Courier,monospace;">
ftp> </div>
<br />
<br />
<br />
<br />
<div style="text-align: justify;">
Vemos como efectivamente el puerto que ha utilizado está por encima del 50000 y por debajo del 60000 (el puerto se obtiene de los valores que van después de la ip, 211*256+39=54055).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Si hacemos un tcpdump en el servidor, filtrando por ip origen, veremos que efectivamente ese es el puerto que ha utilizado:</div>
<br />
<br />
Para puerto de datos:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
10:40:20.810783 Out MAC ethertype IPv4 (0x0800), length 68: 192.168.60.13.<b>54055</b> > ip_local.50958: . ack 2 win 46 <nop,nop,timestamp 552972262 76243556></div>
<br />
Para puerto de conexión:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
10:40:20.810795 In MAC ethertype IPv4 (0x0800), length 68: ip_local.50583 > 192.168.60.13.<b>2122</b>: . ack 271 win 115 <nop,nop,timestamp 76243556 552972260></div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-37762465699819860682012-09-05T11:02:00.000+02:002012-09-05T11:02:23.750+02:00Cambiar password de ldap por línea de comandos<div style="text-align: justify;">
Debemos tener instalado ldap-utils para hacer uso del comando ldappasswd, su sintáxis sería así: </div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
ldappasswd -h host_ldap -D "uid=usuario_ldap, ou=ejemplo, o=empresa, o=com" -w pass_anterior -s pass_nueva "uid=usuario_ldap, ou=ejemplo, o=empresa, o=com" </div>
<br />
<br />
Y eso es todo...Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-539798157301824062012-09-05T08:55:00.000+02:002012-09-05T08:57:23.374+02:00Enlaces libros<div style="text-align: justify;">
Ahí va un listado de algunos enlaces interesantes para descargar tanto libros técnicos como de literatura:</div>
<br />
<div class="im">
<ul>
<li><a href="http://www.etnassoft.com/biblioteca/" target="_blank">http://www.etnassoft.com/<wbr></wbr>biblioteca/</a></li>
</ul>
<ul>
<li><a href="http://pythonbooks.revolunet.com/" target="_blank">http://pythonbooks.revolunet.<wbr></wbr>com/</a></li>
</ul>
<ul>
<li><a href="http://papyrefb2.net/frames/index.php" target="_blank">http://papyrefb2.net/frames/<wbr></wbr>index.php</a></li>
</ul>
<ul>
<li><a href="http://epubgratis.me/" target="_blank">http://epubgratis.me/</a></li>
</ul>
<ul>
<li><a href="http://www.flasheditors.net/" target="_blank">http://www.flasheditors.net/</a></li>
</ul>
<ul>
<li><a href="http://es.tldp.org/htmls/manuales.html" target="_blank">http://es.tldp.org/htmls/<wbr></wbr>manuales.html</a></li>
</ul>
<ul>
<li><a href="http://www.ebook3000.com/" target="_blank">http://www.ebook3000.com/</a></li>
</ul>
</div>
<ul>
<li><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">http://www.amazon.es/s/ref=sr_</a><wbr></wbr><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">hi_2?rh=n%3A818936031%2Cn%3A!</a><wbr></wbr><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">827236031%2Cn%3A!827237031%</a><wbr></wbr><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">2Cn%3A1354901031&bbn=</a><wbr></wbr><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">1354901031&ie=UTF8&qid=</a><wbr></wbr><a href="http://www.amazon.es/s/ref=sr_hi_2?rh=n%3A818936031%2Cn%3A%21827236031%2Cn%3A%21827237031%2Cn%3A1354901031&bbn=1354901031&ie=UTF8&qid=1328260722" target="_blank">1328260722</a></li>
</ul>
<br />
A disfrutarlos.... Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-23348645642356848772012-08-27T14:14:00.000+02:002012-08-27T14:14:11.905+02:00Error al configurar Oracle Enterprise Manager Database Control<div style="text-align: justify;">
Al instalar Oracle 11gR2 en Ubuntu 11.04, me saltó un error en la parte de configuración del Database Control. No le presté mucha atención y posteriormente la intenté configurar lanzando:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
emca -config dbcontrol db</div>
<br />
pero tras introducir los datos, me volvió a escupir el mensaje de error:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
27-ago-2012 12:25:57 oracle.sysman.emcp.EMConfig perform</div>
<div style="font-family: "Courier New",Courier,monospace;">
INFO: Esta operación se está registrando en /u01/app/oracle/cfgtoollogs/emca/orcl/emca_2012_08_27_12_25_44.log.</div>
<div style="font-family: "Courier New",Courier,monospace;">
27-ago-2012 12:25:58 oracle.sysman.emcp.EMConfig perform</div>
<div style="font-family: "Courier New",Courier,monospace;">
GRAVE: Fallo al asignar puertos en los rangos especificados para los siguientes procesos: JMS [5540-5559],RMI [5520-5539],Database Control [5500-5519],EM Agent [3938] | [1830-1849]</div>
<div style="font-family: "Courier New",Courier,monospace;">
Consulte el archivo log en /u01/app/oracle/cfgtoollogs/emca/orcl/emca_2012_08_27_12_25_44.log para obtener más información.</div>
<div style="font-family: "Courier New",Courier,monospace;">
No se ha podido terminar la configuración. Consulte el archivo log en /u01/app/oracle/cfgtoollogs/emca/orcl/emca_2012_08_27_12_25_44.log para obtener más información.</div>
<br />
<br />
<div style="text-align: justify;">
El problema no estaba en los puertos, pues todos los rangos estaban libres. Googleando un poco conseguí encontrar la solución, que estaba en la resolción de nombres de la máquina. Es decir, para la configuración del Database Control, nuestro equipo debe ser capaz de resolver tanto localhost como nuestro hostname a la ip 127.0.0.1, por tanto, hay que tener algo así en nuestro /etc/hosts:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace;">
127.0.0.1 localhost.localdomain localhost</div>
<div style="font-family: "Courier New",Courier,monospace;">
127.0.0.1 hostname.domain.com hostname</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una vez finalizada la instalación, puedes volver a configurar para que resuelva el hostname a tu propia ip.</div>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com1tag:blogger.com,1999:blog-2807718133398418466.post-34916101312772808132012-07-20T11:25:00.003+02:002012-07-20T11:26:42.350+02:00subprocess en python<div style="text-align: justify;">
Vamos a ver cómo hacer un script de parada/arranque de un servicio utilizando subprocess.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
subprocess nos va a permitir lanzar un comando y esperar a que este termine, y una vez terminado, ver en qué estado ha finalizado.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
El siguiente ejemplo se puede utilizar para llevar a cabo un reinicio automático de un servicios (jboss por ejemplo) y estar seguros de que el servicio es detenido y arrancado correctamente:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">#!/usr/bin/env python</span><br />
<span style="font-family: "Courier New",Courier,monospace;"># -*- coding: iso-8859-15 -*-</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">import subprocess</span><br />
<span style="font-family: "Courier New",Courier,monospace;">script_arranque='/etc/init.d/jboss start'</span><br />
<span style="font-family: "Courier New",Courier,monospace;">script_parada='/etc/init.d/jboss stop'</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#paramos jboss</span><br />
<span style="font-family: "Courier New",Courier,monospace;">pipe=subprocess.Popen(script_arranque, stdout=subprocess.PIPE, shell=True)</span><br />
<span style="font-family: "Courier New",Courier,monospace;">estado=pipe.wait()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">out, err = pipe.communicate()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">if estado == 0:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> print "Estado salida correcto."</span><br />
<span style="font-family: "Courier New",Courier,monospace;">else:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> print "Salida de error: " + str(err)</span><span style="font-family: "Courier New",Courier,monospace;"> </span><br />
<span style="font-family: "Courier New",Courier,monospace;">'''en este caso, el único proceso java que corre es jboss, si hubiese más de uno, la siguiente opción no sería válida</span> ' ' '<br />
<span style="font-family: "Courier New",Courier,monospace;">pipe=subprocess.Popen('pidof java', stdout=subprocess.PIPE, shell=True)</span><br />
<span style="font-family: "Courier New",Courier,monospace;">out, err = pipe.communicate()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">pid = out.strip()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">if pid != '':</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> print "El proceso no se ha detenido, matamos el pid: " + pid + "."</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> pipe=subprocess.Popen('kill -9 ' + pid, stdout=subprocess.PIPE, shell=True)</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#Ya está parado jboss, ahora lo arrancamos</span><br />
<span style="font-family: "Courier New",Courier,monospace;">pipe=subprocess.Popen(script_parada, stdout=subprocess.PIPE, shell=True)</span><br />
<span style="font-family: "Courier New",Courier,monospace;">estado=pipe.wait()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">out, err = pipe.communicate()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">if estado == 0:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> print "Estado salida correcto."</span><br />
<span style="font-family: "Courier New",Courier,monospace;">else:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> print "Salida de error: " + str(err)</span><br />
<br />
<br />
<div style="text-align: justify;">
Un detalle a tener en cuenta cuando utilicemos subprocess es que si utilizamos la shell (shell=True) el comando que pasemos será una cadena, mientras que si no utilizamos la shell (shell=False) el comando se ha de pasar como una lista. Por ejemplo, para ejecutar un ls -la:</div>
<br />
Con shell:<br />
<span style="font-family: "Courier New",Courier,monospace;">subprocess.Popen('ls -la', shell=True)</span><br />
Sin shell:<br />
<span style="font-family: "Courier New",Courier,monospace;">subprocess.Popen(['ls','-la'], shell=False)</span><br />
<br />
<br />
Para ver el estado en el que finaliza, podemos hacerlo mediante:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">estado=pipe.wait()</span><br />
<br />
ó<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">estado=pipe.returncode</span><br />
<br />
<br />
Y para ver la salida estándar y la salida de error, haremos uso de communicate:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">out, err = pipe.communicate()</span>Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-46712220162373710152012-07-20T10:32:00.000+02:002013-03-14T13:49:30.504+01:00Envío de correo en python bajo SSL<div style="text-align: justify;">
Si necesitáis enviar correos con python por ssl de una forma sencilla, recomiendo el uso de pyzmail (<a href="http://www.magiksys.net/pyzmail/">http://www.magiksys.net/pyzmail/</a>). Para instalar pyzmail, lo podéis hacer directamente con pip:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">pip install pyzmail</span><br />
<br />
Si no tenemos instalado pip, para instalarlo:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">wget http://python-distribute.org/distribute_setup.py </span><br />
<span style="font-family: "Courier New",Courier,monospace;">python distribute_setup.py </span><br />
<span style="font-family: "Courier New",Courier,monospace;">easy_install pip</span><br />
<pre style="background: url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglzOSk2tf_Fl10B7SO1HeONnV0ek8SG_fR3MHiNnNfTe7w74jcjvPoFchQNFTrbDH5X97h5Cy9JqT7pRlBF_WujWWtuJVwOZk5hF5ifSKSHTT618-rTYXPqeXIaLdG7l_CXfaglwsZRSmX/s320/codebg.gif") repeat scroll 0% 0% rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;">
</pre>
Os dejo un sencillo código de ejemplo:
</div>
<pre style="background: url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglzOSk2tf_Fl10B7SO1HeONnV0ek8SG_fR3MHiNnNfTe7w74jcjvPoFchQNFTrbDH5X97h5Cy9JqT7pRlBF_WujWWtuJVwOZk5hF5ifSKSHTT618-rTYXPqeXIaLdG7l_CXfaglwsZRSmX/s320/codebg.gif") repeat scroll 0% 0% rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">
</code><div style="text-align: justify;">
<code style="color: black; word-wrap: normal;"># -*- coding: iso-8859-15 -*-
import pyzmail
#Componemos el correo
sender=('Yo','cuenta@remitente.es')
recipients=['cuenta@destinatario.es',]
subject='Prueba envio SSL'
text_content='Este correo ha sido enviado bajo SSL'
prefered_encoding='iso-8859-1'
text_encoding='iso-8859-1'
#Vamos a adjuntar dos ficheros
fichero=open('fichero.txt.tar.gz', 'rb').read()
fichero2=open('fichero.txt','rb').read()
payload, mail_from, rcpt_to, msg_id=pyzmail.compose_mail(
sender,
recipients,
subject,
prefered_encoding,
(text_content, text_encoding),
html=None,
attachments=[(fichero, 'application', 'octet-stream', 'fichero.tar.gz', 'us-ascii'),(fichero2, 'application', 'octet-stream', 'fichero.txt', 'us-ascii')])
#Datos de configuracion para el envio
smtp_host='host'
smtp_port=puerto para comunicacion ssl
smtp_mode='ssl'
smtp_login='cuenta@remitente.es'
smtp_passwd='password'
#Enviamos el correo
ret=pyzmail.send_mail(payload, mail_from, rcpt_to, smtp_host,
smtp_port=smtp_port, smtp_mode=smtp_mode,
smtp_login=smtp_login, smtp_password=smtp_passwd)
if isinstance(ret, dict):
if ret:
print 'fallo en los destinatario:', ', '.join(ret.keys())
else:
print 'correcto'
else:
print 'error:', ret
</code></div>
</pre>
<pre style="background: url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglzOSk2tf_Fl10B7SO1HeONnV0ek8SG_fR3MHiNnNfTe7w74jcjvPoFchQNFTrbDH5X97h5Cy9JqT7pRlBF_WujWWtuJVwOZk5hF5ifSKSHTT618-rTYXPqeXIaLdG7l_CXfaglwsZRSmX/s320/codebg.gif") repeat scroll 0% 0% rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"></pre>
Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-63657253160744841342012-06-29T10:49:00.003+02:002012-07-20T10:27:15.060+02:00Conexión a bbdd oracle desde python<div style="text-align: justify;">
Para poder acceder a una bbdd oracle desde python tan sólo necesitaremos tener instalado:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: left;">
- cliente oracle (lo puedes obtener de la página de oracle y registrándote en la misma)</div>
<div style="text-align: left;">
- extensión cx_Oracle (lo puedes descargar desde la página http://cx-oracle.sourceforge.net/)</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
La forma de utilizarlo lo podemos ver en el siguiente ejemplo:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con este script se pretende actualizar el campo de una tabla pasándole tres argumentos, dos para filtrar el dato y uno que será el nuevo valor.</div>
<div style="text-align: justify;">
También hacemos uso de optparse para pasear los argumentos.</div>
<div style="text-align: justify;">
<br /></div>
<pre style="background: url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglzOSk2tf_Fl10B7SO1HeONnV0ek8SG_fR3MHiNnNfTe7w74jcjvPoFchQNFTrbDH5X97h5Cy9JqT7pRlBF_WujWWtuJVwOZk5hF5ifSKSHTT618-rTYXPqeXIaLdG7l_CXfaglwsZRSmX/s320/codebg.gif") repeat scroll 0% 0% rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">
</code><div style="text-align: justify;">
<code style="color: black; word-wrap: normal;">#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import cx_Oracle, sys, os, datetime
from optparse import OptionParser
conn_str='usuario/pass@host:port/bbdd'
log = '/ruta/para/log/script.log'
#Fucion para escribir log
def log (texto):
now = datetime.datetime.now()
f = open(log_propio, 'a')
f.write(str (now.ctime()) + ' -> ' + texto + '\n')
f.close()
#Se parsea la entrada de datos
parser = OptionParser(usage="script.py [-a argumento1] [-b argumento2] [-c valor]", version="script 1.0")
parser.add_option("-a", help='Argumento 1 para filtrar el dato. Ej: -a 10. Opción oblicatoria.', type="string", dest="argumento1")
parser.add_option("-b", help='Argumento 2.para filtrar el dato. Ej: -b 2012. Opción obligatoria.', type="string", dest="argumento2")
parser.add_option("-c", help='Valor al que se quiere cambiar. Ej: -c 6. Opción obligatoria', type="string", dest="valor")
(options, args) = parser.parse_args()
if options.argumento1 is None:
print 'La opcion argumento1 es necesaria'
parser.print_help()
sys.exit(1)
else:
argumento1=options.argumento1
if options.argumento2 is None:
print 'La opcion argumento2 es necesaria'
parser.print_help()
sys.exit(1)
else:
arguemento2=options.argumento2
if options.valor is None:
print 'La opcion valor es necesaria'
parser.print_help()
sys.exit(1)
else:
valor=options.valor
db_conn = cx_Oracle.connect(conn_str)
cursor = db_conn.cursor()
cursor.execute('select valor from tabla where argumento1 =' + str(argumento1) + ' and argumento2=' + str(argumento2))
registro = cursor.fetchall()
log ("Valor actual: " + str(registro))
print "Valor actual: " + str(registro)
log ("Se cambia al valor " + valor)
print "Se cambia al valor " + valor
cursor.execute('update tabla set valor =' + valor + ' where argumento1=' + argumento1 + ' and argumento2=' + str(argumento2))
cursor.close()
db_conn.commit()
db_conn.close()
log ("Valor modificado.")
</code></div>
</pre>Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-21572238074812495032012-06-26T12:33:00.004+02:002012-07-20T10:27:01.398+02:00Curso Django Segunda Parte<div style="text-align: justify;">
Continuamos con la segunda parte del mini curso de django. Respecto a la primera parte, he añadido una par de cosas:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
- La instalación de un paquete más: python-pygraphviz</div>
<div style="text-align: left;">
- Y la aplicación de un parche para django-smart-selects: <a href="https://github.com/GrAndSE/django-smart-selects/commit/7e2a8d572b3615cc39a3c9f9d60e1b60de06f46f">https://github.com/GrAndSE/django-smart-selects/commit/7e2a8d572b3615cc39a3c9f9d60e1b60de06f46f</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Pues bien, ya tenemos creado un proyecto llamado misitio. Ahora es el momento de crear nuestra aplicación, la cual llamaremos inventario. Para crear un aplicación, simplemente hacemos:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">cd /opt/djcode/misitio</span><br />
<span style="font-family: "Courier New",Courier,monospace;">python manage.py startapp inventario</span><br />
<br />
<div style="text-align: justify;">
Tras la ejecución de este comando (que no devuelve nada por pantalla), tendremos un nuevo directorio bajo el proyecto misitio:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">ls -l inventario/</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">-rw-r--r-- 1 root root 0 mar 11 12:27 __init__.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">-rw-r--r-- 1 root root 57 mar 11 12:27 models.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">-rw-r--r-- 1 root root 383 mar 11 12:27 tests.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">-rw-r--r-- 1 root root 26 mar 11 12:27 views.py</span><br />
<br />
<div style="text-align: justify;">
De los ficheros que nos podemos encontrar, tenemos: </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- <b>models.py</b>: es donde podremos construir nuestro modelo de datos para la aplicación. La aplicación, como ya hemos dicho, se trata de un inventario de software (en principio muy básico).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
- <b>views.py</b>: es donde configuraremos las vistas. Las vistas no son más que funciones python que recoge los argumentos de una petición http y devuelve una respuesta. La forma de relacionar la petición http con la función de la vista es tarea del fichero <b>urls.py</b>, que veremos más adelante (está bajo la carpeta misitio).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Resumiendo un poco, ya hemos instalado el software base, creado un proyecto, hemos creado también el esqueleto de nuestra aplicación, ahora nos falta darle un modelo de datos, una vistas y relacionarlas con unos patrones de urls (peticiones) y más adelante darle a todo un formato más o menos presentable con las plantillas.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Teniendo esta idea de funcionamiento clara, la creación de aplicaciones en django se convertirá en una tarea muy rápida y sencilla (o no tan sencilla, todo dependerá de la complejidad de la aplicación).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Sin más dilatación, pasemos al modelo de datos. Imaginemos que en nuestra empresa ficticia BONOSA (el nombre se lo debo a un compañero de trabajo, las cosas de JuanMa ;) ) tenemos muchos servicios que ofrecemos a diversos clientes, y todos estos servicios se componen (de una forma muy básica) de máquinas, aplicaciones y clientes. Las máquinas a su vez tienen un nombre, una dirección ip y un sistema operativo. Las aplicaciones se componen de un software con una versión y una ruta de instalación. Luego estos servicios pertenecerán a un cliente (en nuestro ejemplo supondremos que un servicio sólo puede pertenecer a un cliente). </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con estos datos (vuelvo a repetir que muy básico, esto podría ampliarse muchísimo, pero para nuestro ejemplo es suficiente) ya tenemos controlado qué servicios ofrece nuestra empresa y a qué clientes.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para definir este modelo de datos, debemos recurrir al fichero models.py que indicamos antes. Aquí será donde escribiremos las clases en python que definirán el modelo de datos, y posteriormente se plasmará en tablas perfectamente relacionadas (esto lo hace django él solito).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Si editamos el fichero models.py veremos lo siguiente:</div>
<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">from django.db import models</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"># Create your models here.</span><br />
<br />
<br />
<div style="text-align: justify;">
y ahí es donde comenzaremos nosotros a construir nuestro modelo de datos.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Antes de empezar a deminir nuestras clases, vamos a necesitar importar algo más, de modo que añadiremos despues de from django.db import models:</div>
<br />
<span style="font-family: "Courier New",Courier,monospace;">from smart_selects.db_fields import ChainedForeignKey</span><br />
<br />
<div style="text-align: justify;">
Este import es para poder utilizar los combos anidados que ofrece smart-selects para la zona de administración de django, que nos será de gran utilidad.</div>
<br />
<br />
Tras el comentario <span style="font-family: "Courier New",Courier,monospace;"># Create your models here.</span><br />
<br />
<div style="text-align: justify;">
comenzaremos a definir las clases (es importante el orden en el que aparecen, pues no se puede utilizar algo si aún no se ha definido):</div>
<br />
<br />
<div style="text-align: justify;">
<i>Definición de la clase cliente. Tiene dos atributos: nombre (del tipo CharField) y descripción (del tipo TextField) donde podemos añadir un texto amplio, por ejemplo si paga o no paga (muy común últimamente).<br />Algunos detalles: </i></div>
<div style="text-align: justify;">
<i>Con unique=True estamos indicando que será un valor único (como clave primaria) y no podrá haber dos clientes con el mismo nombre. </i></div>
<div style="text-align: justify;">
<i>Con verbose_name le decimos el nombre con el que queremos que aparezca en la zona de administración. </i></div>
<div style="text-align: justify;">
<i>La función __str__(self) nos sirve para indicar qué valor debe devolver por defecto cuando pidamos un objeto de tipo Cliente. </i></div>
<div style="text-align: justify;">
<i>Con class Meta definimos algunos metadatos, como por ejemplo la ordenación que queremos y el nombre plural de la clase para la zona de administración. </i></div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">class Cliente(models.Model):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> nombre = models.CharField(max_length=200, unique=True, verbose_name='Cliente')</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> descripcion = models.TextField()</span><br style="font-family: "Courier New",Courier,monospace;" /><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> def __str__(self):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> return self.nombre</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> class Meta:</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> ordering = ['nombre']</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> verbose_name_plural = "Clientes"</span></span><br />
<br />
<div style="text-align: justify;">
<i>Definición de la clase Servicio. Tiene tres atributos: nombre, descripción y cliente. Cliente es del tipo ForeignKey, pues queremos poder relacionar nuestro servicio con un cliente. Indicamos null=True si queremos dejar el campo en blanco, de este modo no nos obliga a asignar un cliente a un servicio.</i></div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">class Servicio(models.Model):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> nombre = models.CharField(max_length=200, unique=True, verbose_name='Servicio')</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> descripcion = models.TextField()</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> cliente = models.ForeignKey(Cliente, null=True)</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> </span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> def __str__(self):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> return self.nombre</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> class Meta:</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> ordering = ['nombre']</span></span><br />
<br />
<div style="text-align: justify;">
<i>Definición de la clase TAplicacion. Esta clase nos servirá junto con VAplicacion para definir tipos de aplicaciones y versiones de aplicaciones. De este modo tenemos mucho más cerrado la elección de una aplicación y su versión, y así no introducir en campos de texto los nombres (que podrían dar a confusión).</i></div>
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">class TAplicacion(models.Model):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> nombre = models.CharField(max_length=200, unique=True, verbose_name='Tipo Aplicacion')</span><br style="font-family: "Courier New",Courier,monospace;" /><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> def __str__(self):</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> return self.nombre</span><br style="font-family: "Courier New",Courier,monospace;" /><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> class Meta:</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> ordering = ['nombre']</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;"> verbose_name_plural = "Tipos de Aplicaciones"</span></span><br />
<br />
<div style="text-align: justify;">
<i>Definición de la clase VAplicacion. Esta clase tiene un atributo taplicacion que utilizamos para relacionar la versión con la aplicación.</i></div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class VAplicacion(models.Model):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> version = models.CharField(max_length=200, unique=False, verbose_name='Version de aplicacion')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> taplicacion = models.ForeignKey(TAplicacion)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> def __str__(self):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> return self.version</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> class Meta:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ['version']</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> verbose_name_plural = "Versiones de Aplicaciones"</span></div>
<br />
<div style="text-align: justify;">
<i>Definición de la clase SistemaOperativo. Definimos los diferentes S.O. que tengamos.</i></div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class SistemaOperativo(models.Model):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> nombre = models.CharField(max_length=200, unique=True, verbose_name='Sistema Operativo')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> def __str__(self):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> return self.nombre</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> class Meta:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ['nombre']</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> verbose_name_plural = "Sistemas Operativos"</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<i>Definición de la clase Maquina. Tiene tres atributos: nombre, ip (del tipo IPAddressField, que nos valida si es una ip) y sistema (que nos permiterelacionarlo con un sistema operativo.</i></div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class Maquina(models.Model):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> nombre = models.CharField(max_length=200, unique=True, verbose_name='Maquina')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ip = models.IPAddressField()</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> sistema = models.ForeignKey(SistemaOperativo) </span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> def __str__(self):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> return self.nombre</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> class Meta:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ['nombre']</span></div>
<i><br /></i><br />
<div style="text-align: justify;">
<i>Y por fin la definición de la clase Aplicacion. Esta es la más completas. Tienes seis atributos: taplicacion (la aplicación será de un tipo de aplicación: apache, tomcat, jboss...), vaplicacion (la aplicación debe tener una versión, y debe estar relacionada con el tipo de aplicación), descripcion, ruta, maquina y el servicio al que pertenece dicha aplicación. </i></div>
<div style="text-align: justify;">
<i>De esta manera es como relacionamos la aplicación con el servicio. Aquí es donde hacemos uso de smart-selects, pues queremos que la versión de la aplicación dependa de la aplicación. Es decir, si la aplicación es apache, pues las con las que se debe relacionar sean la 2.0, 2.1 y 2.2 (por ejemplo, si hemos metido dichas versiones) y si la aplicación es tomcat, que las versiones relacionadas sean la 5.5, 6.2 y 7.0. Esto se verá mejor cuando </i></div>
<div style="text-align: justify;">
<i>accedamos a nuestra zona de administración.</i></div>
<div style="text-align: justify;">
<i>Hemos añadido también una fución que nos devuelve el tipo de aplicacion y su versión. Nos puede resultar útil más adelante y así vemos como django nos da </i></div>
<div style="text-align: justify;">
<i>la posibilidad de difinir nuestras propias funciones dentro del modelo de datos. A partir de esta función sacamos un nuevo atributo: tvaplicacion, que no es más que el tipo y versión de la aplicación.</i></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<i>Puede parecer un poco enredado pero es un buen ejemplo para ver las posibilidades que nos brinda django.</i></div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class Aplicacion(models.Model):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> taplicacion = models.ForeignKey(TAplicacion, verbose_name="Tipo Aplic", help_text="Selecciona el tipo de aplicacion") </span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> vaplicacion= ChainedForeignKey(VAplicacion, chained_field="taplicacion", chained_model_field="taplicacion", verbose_name="Version de Aplic", help_text="Selecciona la version de la aplicacion") </span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> descripcion = models.TextField(help_text="Descripcion de la aplicacion")</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ruta = models.CharField(max_length=200, unique=False, verbose_name="Ruta instalacion", help_text="Donde esta instalado")</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> maquina = models.ManyToManyField(Maquina)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> servicio = models.ForeignKey(Servicio, verbose_name='Servicios')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> </span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> def _get_tvaplicacion(self):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> return '%s %s' % (self.taplicacion,self.vaplicacion)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> class Meta:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ['taplicacion']</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> verbose_name_plural = "Aplicaciones"</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> tvaplicacion = property(_get_tvaplicacion)</span></div>
<br />
<br />
En este enlace podemos ver más detalladamente la definición del modelo de datos: <a href="https://docs.djangoproject.com/en/dev/ref/models/fields/">https://docs.djangoproject.com/en/dev/ref/models/fields/</a><br />
<br />
Y aquí sobre smart-selects: <a href="https://github.com/digi604/django-smart-selects">https://github.com/digi604/django-smart-selects</a><br />
<br />
<br />
<div style="text-align: justify;">
Bien, ya tenemos configurado el modelo de datos, ahora hay que decirle a django con qué bbdd se debe comunicar. Para ellos necesitaremos configurar el fichero settings.py para decirle a django dónde estará la bbdd, instalar nuestra aplicación, el componente smart-selects y algunos parámetros regionales más:</div>
<br />
<br />
<div style="text-align: justify;">
Editamos el fichero settings.py y buscamos el campo DATABASES, donde añadiremos los siguientes valores:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">DATABASES = {</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'default': {</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'NAME': 'programas', # Or path to database file if using sqlite3.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'USER': 'programas', # Not used with sqlite3.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'PASSWORD': 'programas', # Not used with sqlite3.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'PORT': '', # Set to empty string for default. Not used with sqlite3.</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> }</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">}</span></div>
<br />
Para la configuración regional, modificaremos:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">TIME_ZONE = 'Europe/Madrid'</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">LANGUAGE_CODE = 'es-ES'</span></div>
<br />
Y para instalar nuestra aplicación (inventario) y smart-selects:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">INSTALLED_APPS = (</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.auth',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.contenttypes',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.sessions',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.sites',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.messages',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.staticfiles',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'misitio.inventario',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django_extensions',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> # Uncomment the next line to enable the admin:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'django.contrib.admin',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> # Uncomment the next line to enable admin documentation:</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> # 'django.contrib.admindocs',</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> 'smart_selects',</span></div>
<br />
<div style="text-align: justify;">
También hemos añadido django_extensions, que nos servirá para tener algunas herramientas extras y django.contrib.admin para la zona de administración.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una vez configurado esto, pasaremos a validar tanto la conexión con la bbdd como la correcta sintáxis de models.py:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
root@debian:/opt/djcode/misitio# python manage.py validate</div>
<div style="font-family: "Courier New",Courier,monospace;">
0 errors found</div>
<br />
<br />
<div style="text-align: justify;">
Todo ha sido correcto. Si apareciese algún error, suelen ser muy descriptivos, por lo que no debería ser difícil solventarlo.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para ver como quedaría el sql que genera django para construir la bbdd:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
root@debian:/opt/djcode/misitio# python manage.py sqlall inventario</div>
<br />
<div style="text-align: justify;">
Y aparecerá el sql con la definción de las tablas, relaciones e índices.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Este sql lo podríamos ejecutar directamente en la bbdd y ya tendríamos todas nuestras tablas, pero para qué hacerlo si django lo hace por nosotros:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
root@debian:/opt/djcode/misitio# python manage.py syncdb</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating tables ...</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_permission</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_group_permissions</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_group</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_user_user_permissions</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_user_groups</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_user</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table auth_message</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table django_content_type</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table django_session</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table django_site</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_cliente</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_servicio</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_taplicacion</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_vaplicacion</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_sistemaoperativo</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_maquina</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_aplicacion_maquina</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table inventario_aplicacion</div>
<div style="font-family: "Courier New",Courier,monospace;">
Creating table django_admin_log</div>
<br />
<div style="text-align: justify;">
Y en este punto comenzará a pedirnos datos para la zona de administración de django que hemos activado:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
You just installed Django's auth system, which means you don't have any superusers defined.</div>
<div style="font-family: "Courier New",Courier,monospace;">
Would you like to create one now? (yes/no): yes</div>
<div style="font-family: "Courier New",Courier,monospace;">
Username (Leave blank to use 'root'): </div>
<div style="font-family: "Courier New",Courier,monospace;">
E-mail address: correodeprueba@correo.com</div>
<div style="font-family: "Courier New",Courier,monospace;">
Password: </div>
<div style="font-family: "Courier New",Courier,monospace;">
Password (again): </div>
<div style="font-family: "Courier New",Courier,monospace;">
Superuser created successfully.</div>
<div style="font-family: "Courier New",Courier,monospace;">
Installing custom SQL ...</div>
<div style="font-family: "Courier New",Courier,monospace;">
Installing indexes ...</div>
<div style="font-family: "Courier New",Courier,monospace;">
No fixtures found.</div>
<br />
<br />
<div style="text-align: justify;">
Ya está construída la bbdd de nuestra aplicación y de la zona de administración.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
¿Os gustaría ver un diagrama de clases del modelo de datos que acabamos de generar? Pues vamos a hacer uso de django_extension:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
root@debian:/opt/djcode/misitio# python manage.py graph_models inventario -g -o /home/usuario/mi_proyecto.png</div>
<br />
<div style="text-align: justify;">
Y podréis ver un bonito diagrama generado de marena automáticas a partir del modelo de datos, ¿qué os parece?</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ahora vamos a modificar el fichero urls.py para poder acceder a la zona de administración y que smart-selects funcione correctamente. Añadimos los siguientes import y el autodiscover():</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
from django.contrib import admin</div>
<div style="font-family: "Courier New",Courier,monospace;">
admin.autodiscover()</div>
<div style="font-family: "Courier New",Courier,monospace;">
from django.conf import settings</div>
<br />
<br />
y añadimos las siguientes url's:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
(r'^inventario/chaining/', include('smart_selects.urls')),</div>
<div style="font-family: "Courier New",Courier,monospace;">
(r'^inventario/', include(admin.site.urls)),</div>
<br />
<div style="text-align: justify;">
aquí estamos diciendo qué queremos que ocurra cuando pidamos http://ip:puerto/inventario/chaining/ (necesario para smart-selects) y http://ip:puerto/inventario (nos llevará a la zona de administración de django).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Pueden parecer muchos pasos y muchos cambios a la vez, pero de esta manera conseguiremos ver un resultado muy útil y prático de django. Cuando cojáis la dinámica de trabajo, veréis que siempre suele ser lo mismo.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ya sólo nos faltaría darle forma a la zona de administración para adaptarla a como queramos trabajar. Para nuestro ejemplo, los datos que debemos rellenar son servicios con su cliente y sus aplicaciones, para las aplicaciones, los tipos de aplicaciones que hay y sus versiones, y poco más. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
La zona de administración te permitirá enriquecer con datos tu aplicación, pero por defecto lo hará accediendo por clases según hemos configurado nuestro modelo. Es decir, nos dejará rellenar Servicios por un lado (con su nombre, descripción y cliente), Aplicaciones por otro lado (con su tipo de aplicación, versión, ...). Pero lo que nos gustaría es acceder directamente a Servicios y desde ahí añadir todo lo que atañe al servicio.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Sin haber visto antes la zona de administración de django puede parecer algo abstracto, de modo que probemos a acceder a la zona de administración. Para ello arrancamos nuestra aplicación con el servidor de pruebas de django:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
python manage.py runserver 192.168.48.129:8000</div>
<br />
<div style="text-align: justify;">
Y ahora accedamos a la url: http://192.168.48.129:8000/inventario</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ahí está la interfaz de administración, en la que podremos ver si nos logamos la zona de autenticación y la de creación de sitios. Pero aún no vemos nuestras clases. Así que vamos a añadir en la carpeta inventario un nuevo archivo, llamado admin.py, cuyo contenido será el siguiente:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">from misitio.inventario.models import Servicio, Maquina, SistemaOperativo, Aplicacion, TAplicacion, VAplicacion, Cliente</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">from django.contrib import admin</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class TAplicacionAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> pass</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class VAplicacionAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_display = ('version', 'taplicacion')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class AplicacionInline(admin.StackedInline):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> model = Aplicacion</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> extra = 0</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> filter_horizontal = ('maquina',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class ClienteAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> pass</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class ServicioAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> inlines = [ AplicacionInline ]</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_display = ('nombre', 'descripcion', 'cliente')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ('nombre',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> search_fields = ('nombre', 'descripcion')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> save_on_top = True</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_per_page = 20</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class AplicacionAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_display = ('taplicacion', 'descripcion', 'vaplicacion')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ('taplicacion',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> filter_horizontal = ('maquina',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> save_on_top = True</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_per_page = 20</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class MaquinaAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_display = ('nombre', 'ip', 'sistema')</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ('nombre',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> save_on_top = True</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_per_page = 20</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">class SistemaOperativoAdmin(admin.ModelAdmin):</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_display = ('nombre',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> ordering = ('nombre',)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> save_on_top = True</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"> list_per_page = 20</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(Servicio,ServicioAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(Maquina,MaquinaAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(Aplicacion,AplicacionAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(SistemaOperativo,SistemaOperativoAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(TAplicacion,TAplicacionAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(VAplicacion,VAplicacionAdmin)</span></div>
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">admin.site.register(Cliente,ClienteAdmin)</span></div>
<br />
<br />
<div style="text-align: justify;">
Paso a explicar por encima esto. Lo primero que debemos hacer es importar nuestras clases. Luego pasamos a definir cómo queremos que la zona de administración nos la muestre.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Si sólo queremos que la añada sin más, pues la declaramos así:</div>
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
class TAplicacionAdmin(admin.ModelAdmin):</div>
<div style="font-family: "Courier New",Courier,monospace;">
pass</div>
<br />
<div style="text-align: justify;">
Fijaos que la nombreclatura que sigue es el nombre de la clase seguido de Admin.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Y aparecerá en la panatalla de administración lista para poder añadirle datos.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
La zona de administración nos permite ver los datos, añadir o modificar. Por tanto si lo que queremos es ver ciertos campos en concreto, podemos definirlo del siguiente modo:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
class VAplicacionAdmin(admin.ModelAdmin):</div>
<div style="font-family: "Courier New",Courier,monospace;">
list_display = ('version', 'taplicacion')</div>
<br />
<br />
<div style="text-align: justify;">
Y cuando visualicemos los datos de VAplicacion veremos sólo la columna version y taplicacion.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una aplicación puedes estar configurada en multitud de máquinas. Django nos permite una forma de añadir máquinas muy sencilla con filter_horizontal. Nos aparecerá dos cajas, una con las máquinas disponibles y otra con las seleccionadas. Lo definimos así:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
class AplicacionInline(admin.StackedInline):</div>
<div style="font-family: "Courier New",Courier,monospace;">
model = Aplicacion</div>
<div style="font-family: "Courier New",Courier,monospace;">
extra = 0</div>
<div style="font-family: "Courier New",Courier,monospace;">
filter_horizontal = ('maquina',)</div>
<br />
<div style="text-align: justify;">
Aquí además vemos que estamos utilizando admin.StackedInline en vez de admin.ModelAdmin. Esto es porque en el formulario vamos a utilizar la propiedad Inline, que lo que hace es permitir dentro de una clase, editar el contenido de otra clase. Esto lo hacemos así porque queremos editar un Servicio, y dentro de este, poder añadir y editar aplicaciones. Por tanto Aplicaciones la tenemos que definir como AplicacionInline(admin.StackedInline). Para entender mejor el tema de InLine, os recomiendo una ojeada a <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects.">https://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects.</a></div>
<br />
<div style="text-align: justify;">
Con extra = 0 estamos indicando que por defecto queremos 0 formularios creados. Es decir, cuando editemos un servicio, no aparecerá ningún formulario para Aplicaciones, y deberemos crearlo según demanda.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para Servicio tenemos lo siguiente:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
class ServicioAdmin(admin.ModelAdmin):</div>
<div style="font-family: "Courier New",Courier,monospace;">
inlines = [ AplicacionInline ]</div>
<div style="font-family: "Courier New",Courier,monospace;">
list_display = ('nombre', 'descripcion', 'cliente')</div>
<div style="font-family: "Courier New",Courier,monospace;">
ordering = ('nombre',)</div>
<div style="font-family: "Courier New",Courier,monospace;">
search_fields = ('nombre', 'descripcion')</div>
<div style="font-family: "Courier New",Courier,monospace;">
save_on_top = True</div>
<div style="font-family: "Courier New",Courier,monospace;">
list_per_page = 20</div>
<br />
<div style="text-align: justify;">
Será la entrada principal de datos, por lo que deberá albergar formularios inline (que ha hemos definido antes). Luego le indicamos los formularios inline con:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
inlines = [ AplicacionInline ]</div>
<br />
<div style="text-align: justify;">
Además le indicamos que queremos ordenar los servicios por nombre, que nos permita realizar búsquedas por nombre y descripción (search_fields = ('nombre', 'descripcion')), que aparezca el botón de guardar en la zona superior del formulario además de en la zona inferior (save_on_top = True) y que muestre paginado los resultados cada 20 elementos (list_per_page = 20).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Bien, con esto ya hemos visto bastantes posibilidades de la zona de administración django. Os invito a que le echéis un vistazo a la documentación oficial donde viene explicado con detalle todas las funciones existentes.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Sólo nos queda arrancar nuevamente nuestra aplicación:</div>
<br />
<div style="font-family: "Courier New",Courier,monospace;">
python manage.py runserver 192.168.48.129:8000</div>
<br />
<div style="text-align: justify;">
acceder a la url: http://192.168.48.129:8000/inventario</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
y comenzar a jugar con django añadiendo datos y toqueteando todo (la interfaz es bastante intuitiva...).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Os dejo un enlace para descargar los ficheros que hemos visto:<br />
<a href="http://www.blogger.com/goog_1357189057"><br /></a><br />
<div style="text-align: left;">
<a href="https://sites.google.com/site/ficherows/home/ficheros_django.tar.gz?attredirects=0&d=1">https://sites.google.com/site/ficherows/home/ficheros_django.tar.gz?attredirects=0&d=1</a></div>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Espero que sirva de utilidad.</div>
<br />
<br />
<br />Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com6tag:blogger.com,1999:blog-2807718133398418466.post-50210812072439629392012-02-09T11:49:00.005+01:002012-02-09T11:49:53.996+01:00Sobre keytoolAlgunos apuntes sobre keytool:<br />
<br />
<b>- Crear un almacén de certificados (keystore)</b><br />
<br />
<i>Hacemos una importación de un certificado ficticio (prueba) sobre nuestro nuevo keysotre (fichero.dat)</i><br />
<div style="font-family: "Courier New",Courier,monospace;">
keytool -genkey -alias prueba -keystore fichero.dat</div>
<br />
<br />
<br /><i>Para dejarlo vacío, borramos el certificado de prueba </i><br />
<span style="font-family: "Courier New",Courier,monospace;">keytool -delete -alias prueba -keystore fichero.dat</span><br />
<br />
<b>- Listar el contenido de un keystore</b><br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">
keytool -list -v -keystore fichero.dat</div>
<br />
<b>- Ver el contenido de un certificado</b><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">keytool -printcert -file certificado.pem</span>Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com0tag:blogger.com,1999:blog-2807718133398418466.post-79644447061122980422011-11-27T12:31:00.001+01:002012-06-26T14:18:59.062+02:00Curso Django Primera Parte<div style="text-align: justify;">
Hace tiempo que quiero hacer un pequeño curso-manual sobre django. En esta primera parte comenzaremos con el software base para llevarlo a la práctica. Espero que una vez terminado quede suficientemente claro y sobre todo, que sea de utilidad.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Software base:</div>
<div style="font-family: "Courier New",Courier,monospace; text-align: left;">
- Debian 6.0.3</div>
<div style="font-family: "Courier New",Courier,monospace; text-align: left;">
- Python<br />
- Python setuptools<br />
- gcc<br />
- python-imaging<br />
- Mysql<br />
- python-mysqldb<br />
- apache2<br />
- Django-1.3.1.tar.gz -> https://www.djangoproject.com/download/</div>
<div style="text-align: left;">
<span style="font-family: "Courier New",Courier,monospace;">- digi604-django-smart-selects-1.0.4-0-ga8f9eb9.zip -> https://github.com/digi604/django-smart-selects</span><br />
<span style="font-family: "Courier New",Courier,monospace;">Hay un bug (https://github.com/digi604/django-smart-selects/issues/27) que se corrige con el parche que podemos encontrar en: https://github.com/GrAndSE/django-smart-selects/commit/7e2a8d572b3615cc39a3c9f9d60e1b60de06f46f</span><br />
<span style="font-family: "Courier New",Courier,monospace;">Habría que actualizar el fichero widgets.py en la carpeta smart_selects, tras esto instalar django-smart-selects. </span></div>
<div style="font-family: "Courier New",Courier,monospace; text-align: left;">
- django-command-extensions -> svn checkout http://django-command-extensions.googlecode.com/svn/trunk/ django-command-extensions.</div>
<div style="font-family: "Courier New",Courier,monospace; text-align: left;">
- Imaging-1.1.7.tar.gz -> http://www.pythonware.com/products/pil/<br />
- python-pygraphviz </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con esto tendremos todo lo necesario para llevar a cabo el proyecto que vamos a crear en este manual, que será una aplicación de inventario de software.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para su instalación sobre debian lo haremos con <span style="font-family: "Courier New",Courier,monospace;">apt-get install</span>, salvo aquellos que nos tengamos que descargar de su propia web, y que normalmente se instalan o bien compilando o bien con el instalador de python (<span style="font-family: "Courier New",Courier,monospace;">python setup.py install</span>). </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Una vez tengamos instalado todo el software, crearemos nuestra bbdd y el usuario necesario, por lo tanto nos conectaremos a nuestra bbdd mysql y lanzaremos las siguientes sentencias:</div>
<div style="text-align: justify;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace; text-align: justify;">
mysql> create database programas;<br />
mysql> grant all on programas.* to 'programas'@'localhost' identified by 'programas' with grant option;<br />
mysql> grant all on programas.* to 'programas'@'localhost.localdomain' identified by 'programas' with grant option;</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con esto creamos una bbdd llamada programa, con un usuario y pass programa.</div>
<div style="text-align: justify;">
Ya tenemos todo listo para comenzar a crear nuestro primer proyecto en django.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Nos crearemos un directorio donde emplazar nuestro proyecto, por ejemplo:</div>
<div style="text-align: justify;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace; text-align: justify;">
mkdir /opt/djcode</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Y nos situamos sobre este directorio. Una vez allí crearemos nuestro proyecto:</div>
<div style="text-align: justify;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace; text-align: justify;">
django-admin.py startproject misitio</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Veamos qué ha ocurrido, si hacemos un listado del directorio /opt/djcode descubriremos un nuevo directorio llamado misitio, y si listamos éste a su vez, tendremos algo como esto:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;">__init__.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">manage.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">settings.py</span><br />
<span style="font-family: "Courier New",Courier,monospace;">urls.py</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Estos ficheros compondrán el esqueleto de nuestro proyecto. Estos archivos son los siguientes:<br />
<b>__init__.py:</b> Un archivo requerido para que Python trate a este directorio como un paquete (i.e. un grupo de módulos).<br />
<b>manage.py:</b> Una utilidad de línea de comandos que te deja interactuar con este proyecto de Django de varias formas.<br />
<b>settings.py:</b> Opciones/con guraciones para este proyecto de Django.<br />
<b>urls.py:</b> La declaración de las URL para este proyecto de Django; una tabla de contenidos de tu sitio hecho con Django.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Veamos que hemos conseguido con esto, sin tocar ninguno de los fichero anteriores. Lanzaremos el servidor de desarrollos de django de la siguiente manera:</div>
<div style="font-family: "Courier New",Courier,monospace; text-align: justify;">
<br /></div>
<div style="font-family: "Courier New",Courier,monospace; text-align: justify;">
python manage.py runserver 192.168.48.129:8000</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Lo que añadimos a continuación de runserver es la ip y puerto por el que queremos que se levante el servidor, y nos debe aparecer algo así:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "Courier New",Courier,monospace;">Validating models...</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">0 errors found</span><br />
<span style="font-family: "Courier New",Courier,monospace;">Django version 1.3.1, using settings 'misitio.settings'</span><br />
<span style="font-family: "Courier New",Courier,monospace;">Development server is running at http://192.168.48.129:8000/</span><br />
<span style="font-family: "Courier New",Courier,monospace;">Quit the server with CONTROL-C.</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Y si nos vamos a un navegador y probamos esta dirección, veremos un bonito mensaje de bienvenida.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Hasta aquí esta primera parte. En la segunda parte comenzaremos a ver cómo crear nuestros modelos y algo de la parte de administración de django, una verdadera joya.<br />
<br />
Enlace a la 2ª partes: <a href="http://rafabono.blogspot.com.es/2012/06/curso-django-segunda-parte.html" target="_blank">http://rafabono.blogspot.com.es/2012/06/curso-django-segunda-parte.html </a></div>
<div style="text-align: justify;">
<br /></div>Rafa Bono Garcíahttp://www.blogger.com/profile/03400041667946916372noreply@blogger.com1