Contenido


Scripting KVM con Python, Parte 1

libvirt

Comments

Contenido de la serie:

Este contenido es la parte # de # de la serie: Scripting KVM con Python, Parte 1

Manténgase en contacto por contenidos adicionales de esta serie.

Este contenido es parte de la serie:Scripting KVM con Python, Parte 1

Manténgase en contacto por contenidos adicionales de esta serie.

La virtualización es parte del equipamiento estándar en la mayoría de los sistemas operativos de servidor que existen en el mercado en la actualidad. En el mundo de Linux® hay dos opciones principales de virtualización de servidor: el Kernel-based Virtual Machine (KVM) y Xen. KVM es la tecnología principal que utilizan Red Hat y otros. A pesar de que Citrix es dueña de Xen, gran parte de la funcionalidad esencial permanece en el dominio público.

El proyecto Virtual Machine Manager (VMM o virt-manager brinda una herramienta para gestionar la creación y la ejecución de las instancias de VM (máquina virtual) KVM y Xen. VMM ha sido escrito en Python utilizando la biblioteca GTK+ para la construcción de la interfaz gráfica de usuario. El trabajo real se hace a través de la biblioteca libvirt que es lo que usted usará en este artículo de la serie. A pesar de que libvirt es un esfuerzo patrocinado por Red Hat, éste sigue siendo un proyecto de código abierto que está disponible bajo la licencia GNU Lesser General Public License.

libvirt está compuesto de varias piezas distintas que incluyen la biblioteca de la aplicación de la interfaz de programación de aplicaciones (API), un daemon (libvirtd), y un recurso predeterminado de comando de línea (virsh). Para los usos de este artículo, toda prueba se realiza utilizando Ubuntu Server versión 11.04. La sección Instalación y configuración cubre todo lo que hice para configurar mi servidor para el desarrollo de los scripts presentados aquí. La Parte 1 cubre los fundamentos de libvirt y de la virtualización de Kernel-based Virtual Machine (KVM) junto con algunos scripts de línea de comando para estimular al lector a aprender más. La Parte 2 profundizará más y le mostrará cómo construir sus propias herramientas de virtualización utilizando libvirt, Python y wxPython.

Antes de profundizar en los ejemplos de códigos propiamente dichos, repasemos algunos términos y conceptos relativos a la virtualización con KVM. Cuando se instala KVM en un servidor como Ubuntu Server 11.04, se establece un host de virtualización, o hypervisor. Eso significa que su servidor podrá hospedar múltiples sistemas operativos huéspedes además del host KVM. Cada huésped único se llama dominio y funciona casi de la misma forma que uno esperaría de una instancia individual de servidor en una máquina individual. Se puede conectar al servidor por medio de Secure Shell (SSH) o Virtual Network Computing de la misma forma que si se estuviese comunicando con una máquina física.

A pesar de que KVM funciona como el hypervisor o el gestor huésped, QEMU brinda una emulación de la máquina real, es decir que QEMU ejecuta el conjunto de instrucciones nativas de la máquina de destino. Para los huéspedes x86, esta ejecución se traduce en instrucciones nativas que tienen la capacidad de ejecución directa en el hardware subyacente. Para otras arquitecturas tales como ARM se tiene que llevar a cabo un proceso de traducción. La combinación de KVM y QEMU brinda todas las funciones de soporte que son necesarias para virtualizar esencialmente todo sistema operativo disponible en la actualidad, más unos pocos que ya no están disponibles.

Un dominio huésped consiste de un número de archivos, entre los que se incluye uno o más archivos de imagen de disco y un archivo de configuración basado en XML. Esta configuración hace que sea extremadamente fácil gestionar múltiples VMs creando una imagen de sistema de línea de base y luego modificando el archivo de configuración que se ajuste a sus necesidades. Una forma de configurar y de comunicarse con KVM/QEMU es el kit de herramientas libvirt . Varios proveedores han estandarizado sus productos de gestión con base en libvirt.

Observe el contenido de un archivo de configuración típica de dominio. El Listado 1 muestra el archivo testdev.xml de los ejemplos libvirt .

Listado 1. Definición XML de dispositivo
<device>
   <name>File_test_device</name>
   <capability type='system'>
         <hardware>
               <vendor>Libvirt</vendor>
               <version>Test driver</version>
               <serial>123456</serial>
               <uuid>11111111-2222-3333-4444-555555555555</uuid>
         </hardware>
         <firmware>
               <vendor>Libvirt</vendor>
               <version>Test Driver</version>
               <release_date>01/22/2007</release_date>
         </firmware>
   </capability>
</device>

En el archivo de pruebas domfv0.xml que se muestra en el Listado 2 se puede observar con un poco más de detalle la configuración de dispositivos virtuales.

Listado 2. Archivo de definición de dispositivo domfv0.xml
<devices>
  <emulator>/usr/lib/xen/bin/qemu-dm</emulator>
         <interface type='bridge'>
               <source bridge='xenbr0'/>
               <mac address='00:16:3e:5d:c7:9e'/>
               <script path='vif-bridge'/>
         </interface>
         <disk type='file'>
               <source file='/root/fv0'/>
               <target dev='hda'/>
         </disk>
         <disk type='file' device='cdrom'>
               <source file='/root/fc5-x86_64-boot.iso'/>
               <target dev='hdc'/>
               <readonly/>
         </disk>
         <disk type='file' device='floppy'>
               <source file='/root/fd.img'/>
               <target dev='fda'/>
         </disk>
         <graphics type='vnc' port='5904'/>
</devices>

El punto clave aquí es la relativa facilidad con la que se pueden leer estos archivos y por consiguiente, crear los propios. A pesar de que se puede construir cualquier cantidad de archivos a mano, también es posible automatizar la construcción utilizando un lenguaje de scripting como Python.

Instalación y configuración

Como este artículo trata acerca de scripting de KVM, se presume que usted posee un servidor que tiene instalado KVM. En el caso de Ubuntu Server 11.04, usted tiene la opción de instalar la virtualización durante el proceso de configuración eligiendo la opción Virtual Machine Host en la pantalla Software selection . Es posible que usted quiera elegir el servidor OpenSSH si desea conectarse remotamente a la máquina.

La primera orden del día es instalar la última versión de libvirt. Para ello es necesario realizar cierto trabajo de línea de comando. Cuando se instala Ubuntu Server 11.04 se obtiene libvirt versión 0.8.8. La última y la mejor versión que está disponible en el sitio web de libvirt es la 0.9.5. Para instalar una versión posterior es necesario que agregue en su sistema un repositorio PPA (Personal Package Archive) que contenga una versión más reciente de libvirt. Una búsqueda rápida en el sitio launchpad.net de libvirt muestra un número de posibles candidatos. Es importante ver la página de los detalles del repositorio antes de tratar de realizar una actualización porque algunos pueden contener paquetes rotos. El Equipo de Virtualización de Ubuntu mantiene un repositorio PPA con varios paquetes, entre los que se incluyen libvirt. La última versión disponible que había al momento de escribir este artículo era la 0.9.2-4.

Realice estos pasos para instalar esa versión:

  1. Instale el paquete python-software-properties de la siguiente forma:
    sudo apt-get install python-software-properties

    Este comando pone a disposición el comando add-apt-repository que se necesita para referenciar el origen de terceros.

  2. Escriba los siguientes comandos:
    sudo add-apt-repository ppa:ubuntu-virt/ppa
    sudo apt-get update
    sudo apt-get install libvirt-bin
  3. Como estará utilizando Python para hacer todo el scripting de este artículo, instale el shell IDLE para facilitar la escritura y la prueba de scripts.

    En este paso se da por sentado que usted ha instalado el entorno de escritorio en su servidor Ubuntu. La forma más rápida de instalar el escritorio es utilizando el siguiente comando:

    sudo apt-get install ubuntu-desktop

Cuando eso esté listo usted tendrá acceso a cualquier cantidad de aplicaciones gráficas con el instalador de software Ubuntu. Se puede utilizar el Centro de Software Ubuntu para instalar la herramienta IDLE de Python.

Scripts de muestra

En este paso, observemos algunos de los aspectos fundamentales de trabajar con libvirt antes de profundizar demasiado en el código. La comunicación entre una aplicación y la biblioteca libvirt utiliza un mecanismo simple de llamados de procedimientos remotos que posibilita la construcción de aplicaciones para comunicar con hypervisores remotos por una conexión TCP/IP. Los Uniform Resource Identifiers (URIs, defined by Internet Engineering Task Force [IETF] Request for Comments [RFC] 2396) se utilizan para identificar un hypervisor específico con el cual se desea establecer una conexión.

Generalmente las conexiones locales no requieren autenticación, sin embargo algunas conexiones remotas la necesitan. El archivo libvirt.conf controla la configuración de seguridad. El control más exhaustivo de la comunicación por un dominio único se realiza a través del filtrado de red. Esto es un ejemplo de cómo se controlaría el tráfico de red usando un filtro:

<devices>
    <interface type='bridge'>
      <mac address='00:16:3e:5d:c7:9e'/>
      <filterref filter='clean-traffic'/>
    </interface>
</devices>

Este fragmento define un filtro llamado clean-traffic que se aplicará a todo el tráfico de la red mediante la dirección MAC (media access control) especificada. Si examina el XML clean-traffic , observará que contiene esto:

<filter name='clean-traffic' chain='root'>
     <uuid>6f145c54-e3de-4c33-544a-70b69c16d9da</uuid>
     <filterref filter='no-mac-spoofing'/>
     <filterref filter='no-ip-spoofing'/>
     <filterref filter='allow-incoming-ipv4'/>
     <filterref filter='no-arp-spoofing'/>
     <filterref filter='no-other-l2-traffic'/>
     <filterref filter='qemu-announce-self'/>
</filter>

Las capacidades del filtro son bastante extensas y están plenamente documentadas. Basta sólo un comando para sacar una copia local de los archivos de documentación y de muestra de libvirt . Esto es lo que se necesita hacer:

sudo apt-get install libvirt-doc

Una vez que ha terminado eso, toda la documentación estará disponible en el directorio /usr/share/doc/libvirt-doc. Más adelante podrá observar los ejemplos de Python. Si ha actualizado a una versión más reciente de libvirt, necesitará instalar explícitamente los bindings de Python. Para ello se requiere sólo un comando:

sudo apt-get install python-libvirt

Utilice la consola IDLE de Python para examinar el código de Python para establecer una conexión con una instancia local QEMU, y luego examine los dominios definidos. El Listado 3 muestra lo que usted debería ver al utilizar este enfoque.

Listado 3. Vista del código Python en la consola IDLE
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
[GCC 4.5.2] on linux2
Type "copyright", "credits" or "license()" for more information.
==== No Subprocess ====
>>> import libvirt
>>> conn=libvirt.open("qemu:///system")
>>> names = conn.listDefinedDomains()
>>> print names
['Test1', 'SBSLite', 'UbuntuServer1104', 'Win7_64-bit']]
>>>

Este código muestra cómo obtener una lista de todos los dominios definidos. El retorno de la función listDefinedDomains() muestra una lista que contiene cuatro nombres de dominios. Una vez que haya establecido conexión con el hypervisor, usted podrá tener acceso a una lista variada de las funciones disponibles. Esto es un script breve que muestra cómo obtener una lista de todas las funciones disponibles en el objeto conn :

clist = dir(conn)
for item in clist:
            print item

Para ver una lista de filtros definidos, se podría utilizar un enfoque similar:

filts = conn.listNWFilters()
for item in filts:
            print item

La herramienta IDLE es una forma excelente de investigar los distintos llamados API y de ver los resultados retornados cuando se ejecuta. Algunas de las funciones sólo operan en dominios en ejecución. La función Python dir() retorna una lista de atributos válidos del objeto especificado. Es una herramienta de línea de comando apropiada para ver qué provee un objeto en particular. Se la puede utilizar de la forma en que se ha mostrado antes para obtener una lista de las funciones disponibles después de establecer una conexión con el hypervisor.

Para hacer una demostración se pueden utilizar algunas líneas de código Python en la consola IDLE para tener una idea de los tipos de operaciones que se pueden llevar a cabo en un dominio specífico. El Listado 4 provee un ejemplo de qué se puede hacer.

Listado 4. Salida de Python de un objeto de dominio
>>> import libvirt
>>> import pprint
>>> conn=libvirt.open("qemu:///system")
>>> p = conn.lookupByName('ubuntu100403')
>>> pprint.pprint(dir(p))
['ID',
 'OSType',
 'UUID',
 'UUIDString',
 'XMLDesc',
 '__del__',
 '__doc__',
 '__init__',
 '__module__',
 '_conn',
 '_o',
 'abortJob',
 'attachDevice',
 'attachDeviceFlags',
 'autostart',
 'blkioParameters',
 'blockInfo',
 'blockPeek',
 'blockStats',
 'connect',
 'coreDump',
 'create',
 'createWithFlags',
 'destroy',
 'detachDevice',
 'detachDeviceFlags',
 'hasCurrentSnapshot',
 'hasManagedSaveImage',
 'info',
 'injectNMI',
 'interfaceStats',
 'isActive',
 'isPersistent',

Se puede adoptar este enfoque básico para construir un script simple que muestre información acerca de todos los dominios en ejecución. Se pueden utilizar los llamados de función listDomainsID() y lookupByID() para hacer la mayor parte del trabajo, según se muestra en el Listado 5 .

Listado 5. Script list domains de Python
import libvirt
conn=libvirt.open("qemu:///system")

for id in conn.listDomainsID():
   dom = conn.lookupByID(id)
   infos = dom.info()
   print 'ID = %d' % id
   print 'Name =  %s' % dom.name()
   print 'State = %d' % infos[0]
   print 'Max Memory = %d' % infos[1]
   print 'Number of virt CPUs = %d' % infos[3]
   print 'CPU Time (in ns) = %d' % infos[2]
   print ' '

La salida de este script con un dominio activo y otro suspendido, tiene esta apariencia:

ID = 3
Name =  ubuntu100403
State = 3
Max Memory = 1048576
Number of virt CPUs = 1
CPU Time (in ns) = 1048576

ID = 4
Name =  Win7_64-bit
State = 1
Max Memory = 2097152
Number of virt CPUs = 2
CPU Time (in ns) = 2097152

libvirt también implementa Python docstrings para todas las clases y los métodos. Se puede acceder a esta información escribiendo help(libvirt) para ayuda de alto nivel, o help(libvirt.class) para una clase específica. Es necesario haber importado el módulo libvirt antes de escribir el comando help() . La versión que yo probé para esta reseña implementa las siguientes 11 clases:

  • libvirtError
  • virConnect
  • virDomain
  • virDomainShapshot
  • virInterface
  • virNWFilter
  • virNetwork
  • virSecret
  • virStoragePool
  • virStorageVol
  • virStream

Esta lista debería ser de utilidad para decodificar la sintaxis para acceder a las funciones libvirt desde Python. También brinda una lista de todas las constantes nombradas, como VIR_DOMAIN_RUNNING, que equivale a 1. Las funciones tales como dom.info(), utilizadas antes aquí, retornan un valor entero y necesitan ser decodificadas con esta tabla de constantes.

Scripts de herramientas para automatización

Se puede escribir cualquier número de scripts de herramientas para gestionar una instalación de KVM utilizando libvirt y Python. Es posible que no sea eficiente para un número pequeño de dominios pero puede ahorrar tiempo con rapidez cuando la cuenta llega a dos cifras. Sería útil poder hacer un cambio masivo de direcciones IP estáticas para todas las imágenes de dominio. Esto se puede lograr reiterando todos los archivos .conf y luego haciendo los cambios apropiados. Python tiene muchas características integradas que ayudan en esta tarea.

El Listado 6 muestra un ejemplo de una definición XML de red.

Listado 6. Archivo XML de configuración de red
<network>
  <name>testnetwork</name>
  <bridge name="virbr1" />
  <forward/>
  <ip address="192.168.100.1" netmask="255.255.255.0">
    <dhcp>
      <range start="192.168.100.2" end="192.168.100.254" />
      <host mac='de:af:de:af:00:02' name='vm-1' ip='192.168.100.2' />
      <host mac='de:af:de:af:00:03' name='vm-2' ip='192.168.100.3' />
      <host mac='de:af:de:af:00:04' name='vm-3' ip='192.168.100.4' />
      <host mac='de:af:de:af:00:05' name='vm-4' ip='192.168.100.5' />
      <host mac='de:af:de:af:00:06' name='vm-5' ip='192.168.100.6' />
      <host mac='de:af:de:af:00:07' name='vm-6' ip='192.168.100.7' />
      <host mac='de:af:de:af:00:08' name='vm-7' ip='192.168.100.8' />
      <host mac='de:af:de:af:00:09' name='vm-8' ip='192.168.100.9' />
      <host mac='de:af:de:af:00:10' name='vm-9' ip='192.168.100.10' />
    </dhcp
  </ip>
</network>

Si se desea cambiar la subred principal de 192.168.100 a 192.168.200, se podría abrir el archivo de configuración en un editor y hacer una búsqueda y reemplazo global. La dificultad surge cuando se desea hacer algo un poco más complejo, como sumar 10 a todas direcciones IP y MAC que comienzan con 2. La Figura 7 muestra cómo se podría hacer eso con no más de unas 20 líneas de código Python.

Listado 7. Script de Python para cambiar direcciones MAC e IP
#!/usr/bin/env python

from xml.dom.minidom import parseString
import sys

def main():
    target = sys.argv[1]
    number = int(sys.argv[2])
    
    xml = open(target, 'r').read()
    doc = parseString(xml)
    for host in doc.getElementsByTagName('host'):
        ip = host.getAttribute('ip')
        parts = ip.split('.')
        parts[-1] = str(int(parts[-1]) + number)
        host.setAttribute('ip', '.'.join(parts))
        
        mac = host.getAttribute('mac')
        parts = mac.split(':')
        parts[-1] = str(int(parts[-1]) + number)
        host.setAttribute('mac', ':'.join(parts))
    
    f = open(target, 'w')
    f.write(doc.toxml())
    f.close()

if __name__ == '__main__':
    main()

Este script demuestra la fuerza que tiene Python cuando se saca provecho de la Biblioteca Estándar de Python. Aquí se utiliza parseString de xml.dom.minidom para hacer el trabajo pesado de analizar el archivo XML. Después de obtener un atributo XML específico simplemente se lo divide en partes individuales utilizando la función de Python string.split . Después se puede deducir fácilmente cómo volver a integrar todo de nuevo. Se puede expandir este enfoque para hacer cambios por lotes en cualquier archivo XML, incluyendo los archivos .conf para libvirt.

Otro script que sería útil sería uno que saque una instantánea de todos los dominios en ejecución. Este script primero tendría que obtener una lista de todos los dominios en ejecución y después tendría que pausar y crear de forma individual una instantánea de cada uno. A pesar de que esta operación pueda no ser práctica para un entorno de producción, se la puede ejecutar como una tarea CRON para realizar de noche. Este script debería ser sencillo de implementar con los comandos destacados hasta el momento, junto con un llamado a snapshotCreateXML().

Retomando el paso

Este artículo ha servido apenas para profundizar un poco en las capacidades que contiene libvirt. Consulte la sección de Recursos para ver enlaces a material de lectura más desarrollado acerca de libvirt y de virtualización en general. Entender los conceptos básicos de KVM puede ser muy fructífero cuando se comienza a tratar de implementar un código para monitorizar y gestionar un entorno. El próximo artículo de esta serie tomará los fundamentos establecidos aquí para construir algunas herramientas de gestión virtual de la vida real.


Recursos para Descargar


Temas relacionados


Comentarios

Inicie Sesión o Regístrese para agregar comentarios.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=846216
ArticleTitle=Scripting KVM con Python, Parte 1: libvirt
publish-date=11192012