27/8/2011

Día Debian en Mérida.

El día de hoy se celebrará en Fundacite Mérida el Día Debian por motivo del cumpleaños número 18.

Lugas: Av. Alberto Carnevali vía La Hechicera, detrás del MINFRA, Edificio B de Fundacite Mérida.

Adicionalmente como en esta semana se cumplio los 20 años de Linux lo celebraremos también.
Acá un vídeo en conmemoración de la fecha.

Las charlas pautadas son las siguientes:
  • Se presentará un video en conmemoración a los 20 años de Linux.
  • Hacer un distro Debian a la medida. (Franklin G. Mendoza).
  • Como colaborar en Debian (Ernesto Crespo). Presentación.
  • Entornos gráficos en Debian (Marie Matos).
  • Configurar Plone para Alta Disponibilidad en Servidores de producción Debian (Leonardo Caballero).
  • OpenLDAP en Debian (Daniela Matos).
  • Servidores web para necesidades específicas (Hector Colina).
  • Gestión de configuración en Debian (Ernesto Crespo). Presentación.

Ya mis presentaciones se encuentran en slideshare.

23/8/2011

Compartir repositorios de Mercurial con mercurial-server

Se tienen varios artículos sobre mercurial en este blog, un tutorial de mercurial (viene una actualización), como empaquetar para Debian con mercurial (viene una actualización) y el último como gestionar la configuración con mercurial.

Este artículo explicará como compartir repositorios de Mercurial con mercurial-server, este artículo se basa en el tutorial en inglés.

Mercurial-server no se autentica usando claves pero si llaves públicas con ssh. Todos los usuarios que quieran acceder al repositorio mercurial necesitan una llave pública.
En el siguiente enlace tienen una guía de como crear las llaves públicas.


Acceso inicial al mercurial-server.

Instalar mercurial-server:
aptitude install mercurial-server 


Conectarse al servidor grievous habilitando el forwarding.
ernesto@jewel:~$ ssh -A grievous

Agregar llave privada al agente de autenticación.
ernesto@grievous:~$ ssh-add -L > my-key

Crear el directorio para el usuario ernesto en el mercurial-server, copiar la llave y actualizar el repositorio de autenticación.
ernesto@grievous:~$ sudo mkdir -p /etc/mercurial-server/keys/root/ernesto
ernesto@grievous:~$ sudo cp my-key /etc/mercurial-server/keys/root/ernesto/jewel
ernesto@grievous:~$ sudo -u hg /usr/share/mercurial-server/refresh-auth
ernesto@grievous:~$ exit

Crear un repositorio.

 cd proyectos/pysms-send

Clonar el proyecto en el servidor grievous:
 ernesto@jewel:~/proyectos/pysms-send$ hg clone . ssh://hg@grievous/ernesto/pysms-send
searching for changes
27 changesets found
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 27 changesets with 52 changes to 24 files

Revisar si hay cambios en el repositorio.
ernesto@jewel:~/proyectos/pysms-send$ hg pull ssh://hg@grievous/ernesto/pysms-send
pulling from ssh://hg@grievous/ernesto/pysms-send
searching for changes
no changes found


Siguiente artículo se explicará como agregar usuarios al repositorio, controlar el acceso al repositorio

21/8/2011

Como cifrar directorios/particiones con eCryptfs

Este artículo se basa en el artículo de HowtoForge sobre el mismo tema.

La idea es cifrar el directorio home del usuario,se monte el directorio cifrado automáticamente al arrancar el equipo.

Es necesario tener un respaldo de la carpeta del usuario para evitar perdida de datos si olvida la clave.

1.Instalar eCryptfs:
Se ejecuta:
aptitude install ecryptfs-utils

2. Cifrar un directorio.
Respaldar los archivos y directorios del home del usuario:
cp -pfr /home/ecrespo /tmp/

Cifrar el directorio /home/ecrespo/ al montar el sistema de archivos ecryptfs:

Se selecciona aes, 16, no se habilita frase plana, no se habilita cifrado del nombre del archivo, así que con estas opciones sólo hay que presionar enter a la información que solicite al montar la partición cifrada.

mount -t ecryptfs /home/ecrespo /home/ecrespo

Passphrase:
Select cipher:
 1) aes: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 2) blowfish: blocksize = 16; min keysize = 16; max keysize = 56 (not loaded)
 3) des3_ede: blocksize = 8; min keysize = 24; max keysize = 24 (not loaded)
 4) twofish: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 5) cast6: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 6) cast5: blocksize = 8; min keysize = 5; max keysize = 16 (not loaded)
Selection [aes]:
Select key bytes:
 1) 16
 2) 32
 3) 24
Selection [16]:
Enable plaintext passthrough (y/n) [n]: Enter
Enable filename encryption (y/n) [n]: Enter
Attempting to mount with the following options:
  ecryptfs_unlink_sigs
  ecryptfs_key_bytes=16
  ecryptfs_cipher=aes
  ecryptfs_sig=162827f20fdadf4e
Mounted eCryptfs

3. Verificar que la partición se ha montado.
El comando mount sólo devuelve las particiones que se encuentran montadas en el Linux.
mount 
/home/ecrespo on /home/ecrespo type ecryptfs (rw,ecryptfs_sig=162827f20fdadf4e,ecryptfs_cipher=aes,ecryptfs_key_bytes=16,ecryptfs_unlink_sigs)

4. Recuperar el respaldo y borrar el respaldo del directorio /tmp/
cp -pfr /tmp/ecrespo /home/
rm -fr /tmp/ecrespo

5. Verificar el funcionamiento del cifrado de archivos.
Para propósitos de prueba se copia un archivo de /etc/ al home del usuario.
Mostrar la información que contiene el archivo hostname en /etc/
cat /etc/hostname 
canaima-popular

Copiar el archivo hostname al home del usuario:
cp /etc/hostname /home/ecrespo/

Mientras la partición cifrada se encuentra montada se puede visualizar los archivos:
cat /home/ecrespo/hostname 
canaima-popular

Desmontar la partición.
umount /home/ecrespo/

Verificar que el archivo está cifrado:
cat /home/ecrespo/hostname
4��ˉ�s|| � "3DUfw`�6���D �̬�����{� _CONSOLE ('��N�pD���-� \ٛn�Ś/��TD��xҝD[:�$] �wq�V��
�n /�k�a:�H.�{ USj�@u)�c ������T� �� �m�иや� � Z�&��0�Q��B�� /��q�e��E��li�"dx�tDs���k����h�J��20��&T�?R�x� �W�$�
                                                                                                                   �$�@ �&
                                                                                                                           ��� �G� /�.L���1d�� j����{cYRꄙZ`��t 8�4ԬZ~ ,)H
eb �� f ��?��
          �_' ��K&�d)����{y�z����ֆ�2x��h�� &j���tuq��a�JeJ�� ���\"~
                                                                   ��5`� V RCB`��������PU�&������X�
                                                                                                    9Ԥ[� 4O.� �� ̔�;/ �����#j V`Sf^��<�uB8 �ЭIx�4PR�� dml��`&c �
! ҟ&`�~�U?u�� �����쵮 @�)�8�Q U��杞M�e��="�}�V# ��>������ �>�F�� R�#ZgI���^J�� ,��0ݼR�rO�f��AxS��3�\�M���o�D �u�Av)�qq%�(F�/���%tL��w�U�k6~c��` “=�4N��E�= #� � a7�����v�(V�p�HlR�Y5����#^���K �~ :h��Z�Q�J�{�FC�N;*� 0Bő*���=�
�ՙ��R�mќet ���7B�_�Dz.[�6>�ĸؓ�� �Nc��#��NR��@� 4 �/���M?Om �|;/�Oe9�6��&6D*U�A�e��� �V
�ZM�<ɝ��"�0��0�WŸL�H�,r
                        �>��f��4�$�wA�rѫ� �0H�����CP�i��&���!b���-#
$ލ;�X!y�� ~� I5e��V|Y�\� �H^£�7�0�Z��{�}"!��͡�� 6�M�!LNȆXvF         )� ��G�Nuɍ� � ӧ��s���t ���t��o&� C������W�#��Cpk�><„


6. Montar automáticamente  la partición o directorio.
Conecte un pendrive y ejecute el comando fdisk -l donde aparecerá la partición del pendrive:
fdisk -l
 Disk /dev/sdb: 16.0 GB, 16011542528 bytes
32 heads, 63 sectors/track, 15512 cylinders, 31272544 sectores en total
Units = sectores of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identificador del disco: 0x59a4123d

Disposit. Inicio    Comienzo      Fin      Bloques  Id  Sistema
/dev/sdb1   *          63    31272191    15636064+   b  W95 FAT32

Montar el pendrive:
mkdir /mnt/usb
mount /dev/sdb1 /mnt/usb


Al ejecutar mount sólo mostrará las particiones montadas en el equipo:
mount
/dev/sdb1 on /mnt/usb type vfat (rw)

A diferencia del artículo original el pendrive y la partición cifrada no se monta automaticamente.


20/8/2011

Usar dnsmasq como servidor DNS cache y local

En el artículo anterior explicaba como mejorar las consultas de DNS del equipo con dnsmaq.

También se puede agregar equipos con su IP como de un servidor de DNS con Bind9 en funcionamiento.

Se tiene una máquina virtual de nombre grievous e IP 192.168.10.53.

Para instalr dnsmasq se ejecuta apt-get.

apt-get install dnsmasq

Configurar dnsmasq como DNS cache y local.

Se define la ruta del archivo resolv.conf que hace consultas al servidor de DNS externo.

resolv-file=/etc/resolv-orig.conf

Definir respuestas de peticiones locales dadas por el archivo /etc/hosts.
 local=/localnet/

Y eso es todo lo que hay que cambiarse en el archivo /etc/dnsmasq.conf para resolver nombres de equipos locales por medio de /etc/hosts.

Agregar los equipos en el archivo /etc/hosts.
En este caso se tiene el equipo grievous:
192.168.10.53   grievous        grievous

Si se necesita agregar más equipos al servidor de DNS simplemente se edita el archivo /etc/hosts agregando los equipos en dicho archivo.

Se crea el archivo /etc/resolv-orig.conf donde se encuentra las IPs de los servidores de DNS externos.
En este caso se está usando los servidores de DNS de Google, OpenDNS y de la red del trabajo.
nameserver 8.8.8.8 
nameserver 8.8.4.4
nameserver 208.67.222.222
nameserver 208.67.220.220
nameserver 192.168.32.254

Ya se tiene configurado el servidor de DNS, sólo falta que el equipo realice las consultas por medio del archivo /etc/resolv.conf, el cual debe apuntar al servidor local de DNS (127.0.0.1).
nameserver 127.0.0.1

Ahora se puede probar la resolución de nombres en el equipo.
Probar que devuelve respuestas el servidor DNS al consultar el nombre del equipo grievous:
 host grievous
grievous has address 192.168.10.53


dig grievous

; <<>> DiG 9.7.3 <<>> grievous
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19819
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0


;; QUESTION SECTION:
;grievous. IN A


;; ANSWER SECTION:
grievous. 0 IN A 192.168.10.53


;; Query time: 6 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Aug 20 10:06:45 2011
;; MSG SIZE  rcvd: 42


Ahora se puede verificar los rendimientos de los servidores de DNS con NameBench. Para instalarlo se ejecuta apt-get.
apt-get install  namebench

En la siguiente figura se muestra la ejecución de namebench donde se le define el servidor de Nombres a utilizar.


A continuación se muestra las figuras del resultado de Namebench.





15/8/2011

Manejando contactos y realizando una llamada con python en Android

Retornando a los artículos sobre Android.

Este artículo toca el tema de como capturar la información del telefono y contacto del celular. Se crea una lista de contactos y telefonos, se despliega la información en un widget de selección simple, luego de seleccionar al contacto se captura la selección, se despliega la información del contacto y al final se realiza la llamada a dicho contacto.

Las clases nuevas utilizadas en el programa son:
  • queryContent:Realiza una busqueda dentro de un contenido. En este caso se busca los contactos.
  • phoneCallNumber: Permite realizar una llamada pasando el número de telefono como un string.

El código del programa se muestra a continuación:


#Importar el modulo android
import android
#importar la funcion sleep del modulo time
from time import sleep
#Crea la instancia droid del objeto Android.
droid = android.Android()
#Se captura los contactos
contactos = droid.queryContent('content://com.android.contacts/data/phones',\
        ['display_name','data1'],None,None,None).result
#Se crean la lista nombres y telefonos
nombres = []
telefonos = []
#Se agrega la informacion de los contactos a las listas.
for i in range(len(contacts)):
    nombres.append(contacts[i][u'display_name'])
    telefonos.append(contacts[i][u'data1'])
#Se despliega la lista de contactos
droid.dialogCreateAlert("Contactos")
droid.dialogSetItems(nombres)
droid.dialogShow()
#Se captura el resultado de la seleccion simple
respuesta  = droid.dialogGetResponse().result
#Se muestra la informacion del contacto seleccionado
droid.makeToast('El contacto seleccionado es: %s, su numero es: %s'
                %(nombres[respuesta['item']],telefonos[respuesta['item']]))
sleep(5)
droid.makeToast("Realizando la llamada")
sleep(2)
#Se realiza la llamada al contacto seleccioando.
droid.phoneCallNumber("%s" %telefonos[respuesta['item']])

A continuación se muestra la figura de la lista de contactos:


La siguiente figura muestra el contacto seleccionado:

La última figura muestra la realización de la llamada del contacto seleccionado:


El código QR del programa se muestra en la siguiente figura:


14/8/2011

Tutorial de PyQt. Editor de archivos sencillo. Parte 10.

En este artículo se explica como crear un editor de texto sencillo.

Se tendrá una barra de menú con la opción Archivo, dentro de ella se despliega la opción abrir y cerrar aplicación; al abrir la aplicación en la ventana del editor aparecerá el nombre del archivo.

Se utilizarán las siguientes clases:
  • QTextEdit: Define el editor de textos.
  • QAction: Permite definir acciones en el menú.
  • menuBar: Se define la barra de menú.


El código del programa es el siguiente:
#!/usr/bin/env python

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class App(QtGui.QMainWindow):
    def __init__(self,parent=None):
        QtGui.QWidget.__init__(self, parent)
        #Se define el tamano de la ventana
        self.setGeometry(0, 50, 600, 400)
        #Se le coloca un titulo a la ventana y se asocia un icono.
        self.setWindowTitle('Editor de Texto:')
        self.setWindowIcon(QtGui.QIcon('./openlogo-50.png'))
        
        #Se define el widget de edicion de texto
        self.textEdit = QtGui.QTextEdit()
        #Se coloca en el centro
        self.setCentralWidget(self.textEdit)
        #Se define la barra de estatus y se le asigna foco
        self.statusBar()
        self.setFocus()
        #Se define la accion abrir archivo, con
        #evento de teclado y mensaje
        openFile = QtGui.QAction('Abrir', self)
        openFile.setShortcut('Ctrl+a')
        openFile.setStatusTip('Abrir archivo nuevo')
        #Se define la accion cerrar aplicacion
        #con evento de teclado y mensaje
        closeApp = QtGui.QAction('Cerrar',self)
        closeApp.setShortcut('Ctrl+w')
        closeApp.setStatusTip('Cerrar aplicacion')
        
        #Se define la barra de menu
        menubar = self.menuBar()
        #Nombre archivo y se agrega abrir y cerrar aplicacion
        fileMenu = menubar.addMenu('&Archivo')
        fileMenu.addAction(openFile)
        fileMenu.addAction(closeApp)
            
        #se asocia los eventos a la accion abrir archivo y cerrar aplicacion. 
        self.connect(closeApp, QtCore.SIGNAL('triggered()'),QtGui.qApp, QtCore.SLOT('quit()'))
        self.connect(openFile, QtCore.SIGNAL('triggered()'), self.showDialog)
        


    def showDialog(self):
        #Se captura el nombre del archivo a abrir
        filename = QtGui.QFileDialog.getOpenFileName(self, 'Abrir archivo',
                    '/home')
        #Se define un neuvo titulo a la ventan de la aplicacion
        self.setWindowTitle('Editor de Texto:%s' %filename)
        #Se abre el archivo y se
        #desplega la informacion en el widget de
        #edicion de texto
        fname = open(filename)
        data = fname.read()
        self.textEdit.setText(data)
    
            
#Se ejecuta el programa principal
if __name__ == "__main__":    
   #Se instancia la clase QApplication    
   app = QtGui.QApplication(sys.argv)    
   #Se instancia el objeto QuitButton    
   qb = App()    
   #Se muestra la aplicacion    
   qb.show()    
   #Se sale de la aplicacion    
   sys.exit(app.exec_())


La siguiente figura muestra la aplicación al inicio.

La siguiente figura muestra las opciones de la barra de menú:


La siguiente figura muestra la ventana de dialogo de abrir archivo:

La última figura muestra ya la información del archivo en el editor de texto:

13/8/2011

Tutorial de PyQt. Barra de progreso. Parte 9.

Continuando con la serie de artículos sobre PyQt en este caso se tratará sobre el widget de la barra de progreso.

Se utilizarán los siguientes widgets:
  • Qbutton: Botón de iniciar y detener la barra de progreso y el botón salir.
  • QProgressBar: Es el widget de la barra de progreso.
  • QTimer: Provee un timer que es quien maneja el progreso de la barra.
Se tiene la barra de progreso que se inicia cuando se le da clip al botón, se incrementa la barra hasta llegar a 100, el botón que inicia la barra sirve también para detenerlo, volver a arrancar el progreso, cuando se llega a 100 el botón se coloca otra vez para iniciar la barra, colocando el contador en cero para poder iniciar de cero el incremento. Se tiene un botón para salir de la aplicación.

El código del programa se muestra a continuación:

#!/usr/bin/env python

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class App(QtGui.QWidget):
    def __init__(self,parent=None):
        QtGui.QWidget.__init__(self, parent)
        #Se define el tamano de la ventana
        self.setGeometry(400, 400, 250, 150)
        #Se le coloca un titulo a la ventana y se asocia un icono.
        self.setWindowTitle('Barra de progreso')
        self.setWindowIcon(QtGui.QIcon('./openlogo-50.png'))
        
        #Se definen los widgets a utilizar en la ventana.
        #Se crea la instancia de la barra de progreso.
        self.pbar = QtGui.QProgressBar(self)
        #Se crea el boton que inicia/detiene la barra de progreso
        self.button = QtGui.QPushButton('Iniciar', self)
        #Se define una politica al boton de no foco
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)
        #Se define el boton salir.
        self.quit = QtGui.QPushButton('Salir', self)
        
        #Se crea la instancia timer con el contador en cero
        self.timer = QtCore.QBasicTimer()
        self.step = 0
        
        #se asocia los eventos de los 2 botones. 
        self.connect(self.button, QtCore.SIGNAL('clicked()'),self.Accion)
        self.connect(self.quit, QtCore.SIGNAL('clicked()'),QtGui.qApp, QtCore.SLOT('quit()'))
        
        #Se define como empaquetar los widgets.
        #En este caso se usa grilla.
        #Se crea la instancia
        grid = QtGui.QGridLayout()
        grid.setSpacing(10)
        #Se agrupan los widgets pasando el
        #objeto luego el orden en vertical, el orden en
        #horizontal.
        grid.addWidget(self.pbar, 1, 0)
        grid.addWidget(self.button, 2, 0)
        grid.addWidget(self.quit, 3, 1)
        #Se define el layout pasando la grilla
        self.setLayout(grid)


        
    def timerEvent(self, event):
        #funcion asociada al timer.
        #Si el contador llega a 100
        #Se detiene el timer, se cambia el titulo
        #del boton, se coloca el contador en cero
        #y se sale de la funcion.
        #Si no llega a 100, se incrementa en 1 el contador y
        #se le asigna un nuevo valor a la barra de progreso.
        if self.step >= 100:
            self.timer.stop()
            self.button.setText('Iniciar')
            self.step = 0
            return
        self.step = self.step + 1
        self.pbar.setValue(self.step)

    def Accion(self):
        #Si el timer esta activo se detiene y se
        #le cambia el titulo al boton con Iniciar.
        if self.timer.isActive():
            self.timer.stop()
            self.button.setText('Iniciar')
        else:
            #Si no esta activo el timer, se inicia con valor de 100
            #se coloca el titulo detener al boton.
            self.timer.start(100, self)
            self.button.setText('Detener')
            
#Se ejecuta el programa principal
if __name__ == "__main__":    
   #Se instancia la clase QApplication    
   app = QtGui.QApplication(sys.argv)    
   #Se instancia el objeto QuitButton    
   qb = App()    
   #Se muestra la aplicacion    
   qb.show()    
   #Se sale de la aplicacion    
   sys.exit(app.exec_())


Las 3 siguientes figuras muestran el funcionamiento del programa (al iniciarse el programa, iniciar la barra de progreso y al detenerse):



AddThis