Scripting KVM con Python, Parte 1: libvirt

Esta serie de dos partes explica cómo utilizar Python para crear scripts para gestionar máquinas virtuales utilizando KVM. En este fascículo usted aprenderá los fundamentos para utilizar libvirt y los bindings de Python para construir algunas herramientas simples de estado y de pantalla.

Paul Ferrill, CTO, ATAC

Paul Ferrill ha trabajado como escritor en el rubro de la informática por más de 20 años.Obtuvo su primer trabajo en la escritura de reseñas enPC Magazine sobre productos como LANtastic y las primeras versiones de Novell Netware. Paul posee una Licenciatura y una Maestría en Ingeniería Eléctrica y ha escrito software para tantas plataformas y arquitecturas informáticas que es difícil recordar.



19-11-2012

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.

Introducción

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

Aprender

  • libvirt website: Visite el sitio completo para obtener más información.
  • Reference Manual for libvirt: Consulte todo el manual de referencia API de libvirt .
  • Python.org: Encuentre los recursos Python que necesita en el sitio web oficial.
  • IETF RFC 2396: Lea el documento completo que describe la sintaxis genérica para URIs.
  • Zona de fuente abierta developerWorks: Encontrará información extensa "cómo hacer", herramientas y actualizaciones de proyectos, para ayudarlo a desarrollar con las tecnologías de código abierto y a utilizarlas con productos IBM.
  • Events of interest: Visite las próximas conferencias, las ferias comerciales y los webcasts de interés para desarrolladores de fuente abierta de IBM.
  • podcasts de developerWorks: Sintonice interesantes entrevistas y discusiones para desarrolladores de software.
  • Demostraciones on demand de developerWorks: Mire nuestras demostraciones sin costo y aprenda acerca de IBM y de las tecnologías de código abierto y de las funciones de los productos.
  • developerWorks en Twitter: Síganos para enterarse de las últimas noticias.

Obtener los productos y tecnologías

  • Evaluate IBM software products: Desde descargas de prueba hasta productos hospedados en la nube, usted puede innovar su próximo proyecto de desarrollo en código abierto utilizando software específico para desarrolladores.

Comentar

  • developerWorks community: Conéctese con otros usuarios developerWorks mientras explora los blogs, foros, grupos y wikis dirigidos a desarrolladores. Ayude a desarrollar el grupo Real world open source en la comunidad developerWorks.

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


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