Глава 7. Применение Python для построения экземпляров KVM и управления ими

В этой глве мы собираемся обсудить такие темы:

  • Установку и применение имеющейся библиотеки libvirt Python

  • Определение экземпляров KVM при помощи Python

  • Запуск, останов и удаление экземпляров KVM при помощи Python

  • Инспектирование экземпляров KVM при помощи Python

  • Построение простого сервера REST API при помощи libvirt и bottle

Введение

Библиотека libvirt выставляет не зависящий от виртуализации интерфейс для управления полным жизненным циклом экземпляров KVM (и прочих технологий, таких как XEN и LXC). Используя имеющиеся компоновки Python мы можем определять, запускать, уничтожать и удалять виртуальных гостей совместно со всем что ещё реализуется инструментарием пространства пользователя virsh. В действительности мы можем обнаружить, что наша команда virsh применяет различные совместно используемые библиотеки libvirt, исполнив следующее:


root@kvm:~# ldd /usr/bin/virsh | grep libvirt
libvirt-lxc.so.0 => /usr/lib/x86_64-linux-gnu/libvirt-lxc.so.0 (0x00007fd050d88000)
libvirt-qemu.so.0 => /usr/lib/x86_64-linux-gnu/libvirt-qemu.so.0 (0x00007fd050b84000)
libvirt.so.0 => /usr/lib/x86_64-linux-gnu/libvirt.so.0 (0x00007fd050394000)
root@kvm:~#
		

Модуль Python libvirt также предоставляет методы для мониторинга и вывода отчетов по использованию ЦПУ, памяти, хранилища и сетевых ресурсов в самом узле гипервизора и прочие возможности зависящие от используемого типа драйвера гипервизора.

В этой главе мы собираемся воспользоваться небольшим подмножеством API libvirt Pyton для определения, запуска, инспекции и останова некого экземпляра KVM.

[Замечание]Замечание

Для полного списка функций, классов и методов, предоставляемых модулем libvirt Python выполните:


root@kvm:~# pydoc libvirt
		

Установка и применение библиотеки libvirt Python

В этом рецепте мы собираемся установить модуль libvirt Python и его зависимости, создать некую новую виртуальную среду и установить оболочку командных строк iPython для интерактивных вычислений.

Приготовление

Для этого рецепта нам понадобится следующее:

Как это сделать...

Для установки модуля libvirt Python, утилиты iPython и создания новой виртуальной среды для проверок выполните следующие шаги:

  1. Установите пакеты разработки Python pip и virtualenv:

    
    root@kvm:~# apt-get install python-pip python-dev pkg-config build-essential autoconf libvirt-dev
    root@kvm:~# pip install virtualenv
    Downloading/unpacking virtualenv
     Downloading virtualenv-15.1.0-py2.py3-none-any.whl (1.8MB): 1.8MB downloaded
    Installing collected packages: virtualenv
    Successfully installed virtualenv
    Cleaning up...
    root@kvm:~#
     	   
  2. Создайте некую новую среду Python и активируйте её:

    
    root@kvm:~# mkdir kvm_python
    root@kvm:~# virtualenv kvm_python/
    New python executable in /root/kvm_python/bin/python
    Installing setuptools, pip, wheel...done.
    root@kvm:~# source kvm_python/bin/activate
    (kvm_python) root@kvm:~# cd kvm_python/
    (kvm_python) root@kvm:~/kvm_python# ls -la
    total 28
    drwxr-xr-x 6 root root 4096 May 9 17:28 .
    drwx------ 8 root root 4096 May 9 17:28 ..
    drwxr-xr-x 2 root root 4096 May 9 17:28 bin
    drwxr-xr-x 2 root root 4096 May 9 17:28 include
    drwxr-xr-x 3 root root 4096 May 9 17:28 lib
    drwxr-xr-x 2 root root 4096 May 9 17:28 local
    -rw-r--r-- 1 root root 60 May 9 17:28 pip-selfcheck.json
    (kvm_python) root@kvm:~/kvm_python#
     	   
  3. Установите необходимый нам модуль libvirt:

    
    (kvm_python) root@kvm:~/kvm_python# pip install libvirt-python
    Collecting libvirt-python
     Using cached libvirt-python-3.3.0.tar.gz
    Building wheels for collected packages: libvirt-python
     Running setup.py bdist_wheel for libvirt-python ... done
     Stored in directory: /root/.cache/pip/wheels/67/f0/5c/c939bf8fcce5387a36efca53eab34ba8e94a28f244fd1757c1
    Successfully built libvirt-python
    Installing collected packages: libvirt-python
    Successfully installed libvirt-python-3.3.0
    (kvm_python) root@kvm:~/kvm_python# pip freeze
    appdirs==1.4.3
    libvirt-python==3.3.0
    packaging==16.8
    pyparsing==2.2.0
    six==1.10.0
    (kvm_python) root@kvm:~/kvm_python# python --version
    Python 2.7.6
    (kvm_python) root@kvm:~/kvm_python#
     	   
  4. Установите iPython и запустите его:

    
    (kvm_python) root@kvm:~/kvm_python# apt-get install ipython
    ...
    (kvm_python) root@kvm:~/kvm_python# ipython
    Python 2.7.6 (default, Oct 26 2016, 20:30:19)
    Type "copyright", "credits" or "license" for more information.
    
    IPython 1.2.1 -- An enhanced Interactive Python.
    ? -> Introduction and overview of IPython's features.
    %quickref -> Quick reference.
    help -> Python's own help system.
    object? -> Details about 'object', use 'object??' for extra details.
    
    In [1]:
     	   

Как это работает...

На шаге 1 мы начали с установки необходимых пакетов зависимостей. Поскольку для своих разработок мы собираемся применять некую виртуальную среду Python, мы также установили и пакет virtualenv. Сам модуль libvirt Python мы намерены установить в полученной виртуальной среде с помощью диспетчера пакетов pip, поскольку мы не хотим перепачкать свой хост избыточными пакетами.

На шаге 2 мы создали и активировали некую новую виртуальную среду, а на шаге 3 установили нужный нам модуль libvirt Python.

Наконец, на шаге 4 мы установили и запустили инструментарий разработки iPython, которым мы намерены пользоваться на протяжении данной главы.

Определение экземпляров KVM с помощью Python

В этом рецепте мы намерены определить некий новый экземпляр при помощи модуля libvirt Python, который мы установили в своём предыдущем рецепте. Для последующих примеров мы намерены применять виртуальную среду и инструментарий разработки iPython.

Приготовление

Для этого рецепта нам понадобится следующее:

Как это сделать...

Для определения некоего нового экземпляра KVM при помощи модуля libvirt Python следуйте приводимым далее инструкциям:

  1. В своём интерпретаторе iPython импортируйте модуль libvirt:

    
    In [1]: import libvirt
    
    In [2]:
     	   
  2. Создайте необходимую строку определения экземпляра:

    
    In [2]: xmlconfig = """
    <domain type='kvm' id='1'>
     <name>kvm_python</name>
     <memory unit='KiB'>1048576</memory>
     <currentMemory unit='KiB'>1048576</currentMemory>
     <vcpu placement='static'>1</vcpu>
     <resource>
       <partition>/machine</partition>
     </resource>
     <os>
       <type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type>
       <boot dev='hd'/>
     </os>
     <features>
       <acpi/>
       <apic/>
       <pae/>
     </features>
     <clock offset='utc'/>
     <on_poweroff>destroy</on_poweroff>
     <on_reboot>restart</on_reboot>
     <on_crash>restart</on_crash>
     <devices>
       <emulator>/usr/bin/qemu-system-x86_64</emulator>
       <disk type='file' device='disk'>
         <driver name='qemu' type='raw'/>
         <source file='/tmp/debian.img'/>
         <backingStore/>
         <target dev='hda' bus='ide'/>
         <alias name='ide0-0-0'/>
         <address type='drive' controller='0' bus='0' target='0' unit='0'/>
       </disk>
       <controller type='usb' index='0'>
         <alias name='usb'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
       </controller>
       <controller type='pci' index='0' model='pci-root'>
         <alias name='pci.0'/>
       </controller>
       <controller type='ide' index='0'>
         <alias name='ide'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
       </controller>
       <interface type='network'>
         <mac address='52:54:00:da:02:01'/>
         <source network='default' bridge='virbr0'/>
         <target dev='vnet0'/>
         <model type='rtl8139'/>
         <alias name='net0'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
       </interface>
       <serial type='pty'>
         <source path='/dev/pts/5'/>
         <target port='0'/>
         <alias name='serial0'/>
       </serial>
       <console type='pty' tty='/dev/pts/5'>
         <source path='/dev/pts/5'/>
         <target type='serial' port='0'/>
         <alias name='serial0'/>
       </console>
       <input type='mouse' bus='ps2'/>
       <input type='keyboard' bus='ps2'/>
       <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
         <listen type='address' address='0.0.0.0'/>
       </graphics>
       <video>
         <model type='cirrus' vram='16384' heads='1'/>
         <alias name='video0'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
       </video>
       <memballoon model='virtio'>
         <alias name='balloon0'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
       </memballoon>
     </devices>
    </domain>
    """
    
    In [3]:
     	   
  3. Получите некое подключение к своему гипервизору:

    
    In [3]: conn = libvirt.open('qemu:///system')
    
    In [4]:
     	   
  4. Определите некий новый экземпляр без его запуска:

    
    In [4]: instance = conn.defineXML(xmlconfig)
    
    In [5]:
     	   
  5. Перечислите все определённые в этом хосте экземпляры:

    
    In [5]: instances = conn.listDefinedDomains()
    
    In [6]: print 'Defined instances: {}'.format(instances)
    Defined instances: ['kvm_python']
    
    In [7]:
     	   
  6. Проверьте что ваш экземпляр был создан с помощью команды virsh:

    
    (kvm_python) root@kvm:~/kvm_python# virsh list --all
     Id  Name        State
    ----------------------------------------------------
     -   kvm_python  shut off
    
    (kvm_python) root@kvm:~/kvm_python#
     	   

Как это работает...

В этом рецепте мы воспользовались предварительно имевшимся у нас сырым образом Debian, который мы создали в Главе 1, Начало работы с QEMU и KVM для определения необходимого экземпляра KVM.

На шаге 1 мы импортировали пакет libvirt и продолжили определять свой новый экземпляр KVM. На шаге 2 своей переменной xmlconfig мы назначили соответствующую форматированную строку XML. Обратите внимание, что это определение содержит соответствующие название и местоположение необходимого файла образа.

На шаге 3 мы получили некий объект подключения и назначили его переменной conn. Теперь мы можем применять доступные методы для определения своей гостевой KVM.

[Совет]Совет

Для перечисления всех доступных методов для некоторого объекта в iPython наберите соответствующее название переменной с последующей . и дважды нажмите по клавише Tab:


In [7]: conn.
Display all 117 possibilities? (y or n)
conn.allocPages
conn.getURI
conn.nodeDeviceLookupByName
conn.baselineCPU
conn.getVersion
conn.nodeDeviceLookupSCSIHostByWWN
conn.c_pointer
conn.interfaceDefineXML
conn.numOfDefinedDomains
conn.interfaceLookupByMACString
conn.numOfDefinedInterfaces
...

In [7]: conn.
 	   

Для получения подсказки по некоторому методу, добавьте символ вопроса в самом конце интересующего вас метода:


In [7]: conn.defineXML?
Type: instancemethod
String Form:<bound method virConnect.defineXML of <libvirt.virConnect object at 0x7fc5e57dc350>>
File: /root/kvm_python/lib/python2.7/site-packages/libvirt.py
Definition: conn.defineXML(self, xml)
Docstring:
Define a domain, but does not start it.
This definition is persistent, until explicitly undefined with
virDomainUndefine(). A previous definition for this domain would be
overridden if it already exists.
 	   

Некоторые гипервизоры могут препятствовать данной операции если существует некая текущая операция блочного копирования в неустановившемся определяемом домене; в таком случае вначале воспользуйтесь virDomainBlockJobAbort() для остановки блочного копирования.

Для освобождения ресурсов после того как объект домена больше не требуется, следует воспользоваться virDomainFree.

На шаге 4 мы воспользовались методом defineXML() для объекта подключения libvirt.virConnect , передав соответствующую строку определения XML и назначив её своей переменной экземпляра. Мы можем определить значение типа своего нового объекта выполним следующее:


n [7]: type(instance)
Out[7]: libvirt.virDomain

In [8]:
 	   

На шаге 5 мы вывели перечень всех определённых в этом хосте экземпляров воспользовавшись методом listDefinedDomains() и подтвердили полученный результат применив на шаге 6 команду virsh.

Также ознакомьтесь...

Давайте добавим в свой предыдущий код Python некую простейшую проверку ошибок и запишем всё это в некий новый файл. Мы намерены выполнять добавления к этому файлу в последующих рецептах:


(kvm_python) root@kvm:~/kvm_python# cat kvm.py
import libvirt

xmlconfig = """
<domain type='kvm' id='1'>
 <name>kvm_python</name>
 <memory unit='KiB'>1048576</memory>
 <currentMemory unit='KiB'>1048576</currentMemory>
 <vcpu placement='static'>1</vcpu>
 <resource>
   <partition>/machine</partition>
 </resource>
 <os>
   <type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type>
   <boot dev='hd'/>
 </os>
 <features>
   <acpi/>
   <apic/>
   <pae/>
 </features>
 <clock offset='utc'/>
 <on_poweroff>destroy</on_poweroff>
 <on_reboot>restart</on_reboot>
 <on_crash>restart</on_crash>
 <devices>
   <emulator>/usr/bin/qemu-system-x86_64</emulator>
   <disk type='file' device='disk'>
     <driver name='qemu' type='raw'/>
     <source file='/tmp/debian.img'/>
     <backingStore/>
     <target dev='hda' bus='ide'/>
     <alias name='ide0-0-0'/>
     <address type='drive' controller='0' bus='0' target='0' unit='0'/>
   </disk>
   <controller type='usb' index='0'>
     <alias name='usb'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
   </controller>
   <controller type='pci' index='0' model='pci-root'>
     <alias name='pci.0'/>
   </controller>
   <controller type='ide' index='0'>
     <alias name='ide'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
   </controller>
   <interface type='network'>
     <mac address='52:54:00:da:02:01'/>
     <source network='default' bridge='virbr0'/>
     <target dev='vnet0'/>
     <model type='rtl8139'/>
     <alias name='net0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
   </interface>
   <serial type='pty'>
     <source path='/dev/pts/5'/>
     <target port='0'/>
     <alias name='serial0'/>
   </serial>
   <console type='pty' tty='/dev/pts/5'>
     <source path='/dev/pts/5'/>
     <target type='serial' port='0'/>
     <alias name='serial0'/>
   </console>
   <input type='mouse' bus='ps2'/>
   <input type='keyboard' bus='ps2'/>
   <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
     <listen type='address' address='0.0.0.0'/>
   </graphics>
   <video>
     <model type='cirrus' vram='16384' heads='1'/>
     <alias name='video0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
   </video>
   <memballoon model='virtio'>
     <alias name='balloon0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
   </memballoon>
 </devices>
</domain>
"""

conn = libvirt.open('qemu:///system')
if conn == None:
  print 'Failed to connecto to the hypervizor'
  exit(1)

instance = conn.defineXML(xmlconfig)
if instance == None:
  print 'Failed to define the instance'
  exit(1)

instances = conn.listDefinedDomains()
print 'Defined instances: {}'.format(instances)

conn.close()

(kvm_python) root@kvm:~/kvm_python#
		

Для исполнения данного сценария вначале убедитесь что отменили определение своего экземпляра python_kmv, затем исполните:


(kvm_python) root@kvm:~/kvm_python# python kvm.py
Defined instances: ['kvm_python']
(kvm_python) root@kvm:~/kvm_python#
		

Запуск, останов и удаление экземпляров KVM с помощью Python

В этом рецепте мы намерены воспользоваться методом create() для объекта своего экземпляра, который мы определили в своём предыдущем рецепте для его запуска и методом destroy() для его останова.

Для получения информации относительно метода create() исполните:


In [1]: instance.create?
Type: instancemethod
String Form:<bound method virDomain.create of <libvirt.virDomain object at 0x7fc5d9b97d90>>
File: /root/kvm_python/lib/python2.7/site-packages/libvirt.py
Definition: instance.create(self)
Docstring:
Launch a defined domain. If the call succeeds the domain moves from the
defined to the running domains pools. The domain will be paused only
if restoring from managed state created from a paused domain. For more
control, see virDomainCreateWithFlags().

In [2]:
		

Приготовление

Для этого рецепта нам понадобится следующее:

Как это сделать...

Для запуска определённого нами ранее экземпляра KVM, получения его состояния и, наконец, его останова, воспользуйтесь следующим кодом Python:

  1. Для своего объекта экземпляра вызовите метод create():

    
    In [1]: instance.create()
    Out[1]: 0
    
    In [2]:
     	   
  2. Убедитесь что этот экземпляр пребывает в запущенном состоянии вызвав для объекта этого экземпляра метод isActive():

    
    In [2]: instance.isActive()
    Out[2]: 1
    
    In [3]:
     	   
  3. Проверьте состояние этого экземпляра KVM из ОС его хоста:

    
    (kvm_python) root@kvm:~/kvm_python# virsh list --all
     Id   Name        State
    ----------------------------------------------------
     5    kvm_python  running
    
    (kvm_python) root@kvm:~/kvm_python#
     	   
  4. остановите это экземпляр при помощи метода destroy():

    
    In [3]: instance.destroy()
    Out[3]: 0
    
    In [4]:
     	   
  5. Убедитесь что этот экземпляр был ликвидирован:

    
    In [4]: instance.isActive()
    Out[4]: 0
    
    In [5]:
     	   
  6. Удалите определение экземпляра и перечислите всех определённых гостей:

    
    In [5]: instance.undefine()
    Out[5]: 0
    
    In [6]: conn.listDefinedDomains()
    Out[6]: []
    
    In [7]:
     	   

Как это работает...

На шаге 1 мы вызвали метод create() для запуска своего определённого экземпляра. В случае успеха этот гость перейдёт из выключенного состояния в рабочее, что мы и наблюдаем в выводе своей команды на шаге 3. На шаге 2 мы применяем метод isActive() для проверки значения состояния этого экземпляра. Вывод 1 указывает что этот экземпляр запущен.

На шаге 4 мы останавливаем свой экземпляр при помощи метода destroy() и подтверждаем это на шаге 5.

Наконец, на шаге 6 мы удаляем этот экземпляр при помощи метода undefine() и выводим перечень всех определённых экземпляров с помощью вызова listDefinedDomains().

Также ознакомьтесь...

Давайте добавим новый код в тот сценарий Python, который мы начали в своём рецепте Определение экземпляров KVM с помощью Python. Наш обновлённый сценарий должен теперь выглядеть так:


(kvm_python) root@kvm:~/kvm_python# cat kvm.py
import libvirt
import time

xmlconfig = """
<domain type='kvm' id='1'>
 <name>kvm_python</name>
 <memory unit='KiB'>1048576</memory>
 <currentMemory unit='KiB'>1048576</currentMemory>
 <vcpu placement='static'>1</vcpu>
 <resource>
   <partition>/machine</partition>
 </resource>
 <os>
   <type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type>
   <boot dev='hd'/>
 </os>
 <features>
   <acpi/>
   <apic/>
   <pae/>
 </features>
 <clock offset='utc'/>
 <on_poweroff>destroy</on_poweroff>
 <on_reboot>restart</on_reboot>
 <on_crash>restart</on_crash>
 <devices>
   <emulator>/usr/bin/qemu-system-x86_64</emulator>
   <disk type='file' device='disk'>
     <driver name='qemu' type='raw'/>
     <source file='/tmp/debian.img'/>
<backingStore/>
     <target dev='hda' bus='ide'/>
     <alias name='ide0-0-0'/>
     <address type='drive' controller='0' bus='0' target='0' unit='0'/>
   </disk>
   <controller type='usb' index='0'>
     <alias name='usb'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
   </controller>
   <controller type='pci' index='0' model='pci-root'>
     <alias name='pci.0'/>
   </controller>
   <controller type='ide' index='0'>
     <alias name='ide'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
   </controller>
   <interface type='network'>
     <mac address='52:54:00:da:02:01'/>
     <source network='default' bridge='virbr0'/>
     <target dev='vnet0'/>
     <model type='rtl8139'/>
     <alias name='net0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
   </interface>
   <serial type='pty'>
     <source path='/dev/pts/5'/>
     <target port='0'/>
     <alias name='serial0'/>
   </serial>
   <console type='pty' tty='/dev/pts/5'>
     <source path='/dev/pts/5'/>
     <target type='serial' port='0'/>
     <alias name='serial0'/>
   </console>
   <input type='mouse' bus='ps2'/>
   <input type='keyboard' bus='ps2'/>
   <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
     <listen type='address' address='0.0.0.0'/>
   </graphics>
   <video>
     <model type='cirrus' vram='16384' heads='1'/>
     <alias name='video0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
   </video>
   <memballoon model='virtio'>
     <alias name='balloon0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
   </memballoon>
 </devices>
</domain>
"""

conn = libvirt.open('qemu:///system')
if conn == None:
  print 'Failed to connecto to the hypervizor'
  exit(1)

instance = conn.defineXML(xmlconfig)
if instance == None:
  print 'Failed to define the instance'
  exit(1)

instances = conn.listDefinedDomains()
print 'Defined instances: {}'.format(instances)

time.sleep(5)

if instance.create() < 0:
  print 'Failed to start the {} instance'.format(instance.name())
exit(1)

if instance.isActive():
  print 'The instance {} is running'.format(instance.name())
else:
  print 'The instance {} is not running'.format(instance.name())

time.sleep(5)

if instance.destroy() < 0:
  print 'Failed to stop the {} instance'.format(instance.name())
  exit(1)
else:
  print 'The instance {} has been destroyed'.format(instance.name())

if instance.undefine() < 0:
  print 'Failed to remove the {} instance'.format(instance.name())
  exit(1)
else:
  print 'The instance {} has been undefined'.format(instance.name())

conn.close()

(kvm_python) root@kvm:~/kvm_python#
		

Запуск этого сценария должен определить некий новый экземпляр, запустить его, остановить его и, наконец, удалить его:


(kvm_python) root@kvm:~/kvm_python# python kvm.py
Defined instances: ['kvm1', 'kvm_python']
The instance kvm_python is running
The instance kvm_python has been destroyed
The instance kvm_python has been undefined
(kvm_python) root@kvm:~/kvm_python#
		

В своём предыдущем сценарии мы воспользовались методом instance.name() для получения установленного названия вашей гостевой KVM и его вывода на печать. Мы также выполняем очистку, закрывая установленное подключение к своему гипервизору при помощи вызова conn.close().

Обзор экземпляров KVM с помощью Python

В этом рецепте мы намерены собрать информацию об экземпляре при помощи методов из класса libvirt.virDomain.

[Замечание]Замечание

Для получения дополнительных сведений относительно API libvirt Python обратитссь, пожалуйста, к официальной документации.

Приготовление

Для этого рецепта нам понадобится следующее:

Как это сделать...

Для сбора информации о ЦПУ, памяти и состоянии для исполняемого экземпляра воспользуйтесь следующими методами Python:

  1. Получите название своего экземпляра:

    
    In [1]: instance.name()
    Out[1]: 'kvm_python'
    
    In [2]:
     	   
  2. Убедитесь что ваш экземпляр запущен:

    
    In [2]: instance.isActive()
    Out[2]: 1
    
    In [3]:
     	   
  3. Соберите статистические данные по своему экземпляру KVM:

    
    In [3]: instance.info()
    Out[3]: [1, 1048576L, 1048576L, 1, 10910000000L]
    
    In [4]:
     	   
  4. Получите максимальный объём физической памяти выделенной данному экземпляру:

    
    In [4]: instance.maxMemory()
    Out[4]: 1048576L
    
    In [5]:
     	   
  5. Выделите статистические данные по ЦПУ этого экземпляра:

    
    In [5]: instance.getCPUStats(1)
    Out[5]:
    [{'cpu_time': 10911545901L,
     'system_time': 1760000000L,
     'user_time': 1560000000L}]
    
    In [6]:
     	   
  6. Убедитесь что ваша виртуальная машина применяет аппаратное ускорение:

    
    In [6]: instance.OSType()
    Out[6]: 'hvm'
    
    In [7]:
     	   
  7. Соберите значнения состояний экземпляра:

    
    In [82]: state, reason = instance.state()
    
    In [83]: if state == libvirt.VIR_DOMAIN_NOSTATE:
     ....:           print('The state is nostate')
     ....: elif state == libvirt.VIR_DOMAIN_RUNNING:
     ....:           print('The state is running')
     ....: elif state == libvirt.VIR_DOMAIN_BLOCKED:
     ....:           print('The state is blocked')
     ....: elif state == libvirt.VIR_DOMAIN_PAUSED:
     ....:           print('The state is paused')
     ....: elif state == libvirt.VIR_DOMAIN_SHUTDOWN:
     ....:           print('The state is shutdown')
     ....: elif state == libvirt.VIR_DOMAIN_SHUTOFF:
     ....:           print('The state is shutoff')
     ....: elif state == libvirt.VIR_DOMAIN_CRASHED:
     ....:           print('The state is crashed')
     ....: elif state == libvirt.VIR_DOMAIN_PMSUSPENDED:
     ....:           print('The state is suspended')
     ....: else:
     ....:           print('The state is unknown')
     ....:
    The state is running
    
    In [84]:
     	   

Как это работает...

В этом рецепте мы пользуемся рядом методов из класса libvirt.virDomain. Давайте рассмотрим более подробно что они делают и затем добавим их в пример своего сценария kvm.py, который мы начали в своём рецепте Определение экземпляров KVM с помощью Python.

На шагах 1 и 2 мы получили название своего экземпляра KVM и проверили что он находится в запущенном состоянии.

На шаге 3 мы собрали следующую информацию о своём экземпляре, возвращённую в виде списка Python:

  • state: Значение состояния данного экземпляра, который определён в перечислимом типе virDomainState

  • maxMemory: Максимальный размер памяти применяемый данным гостем

  • memory: Текущий объём памяти, используемой данным экземпляром

  • nbVirtCPU: Общее число выделенных виртуальных ЦПУ

  • cpuTime: Значение времени, использованного данным экземпляром (в наносекундах)

На шаге 4 мы собрали значение памяти, выделенной нашему экземпляру. Обратите внимание как устанавливается соответствие с выводом соответствующей функции на шаге 3.

На шаге 5 мы получили информацию о ЦПУ своего гостевого экземпляра. Мы можем видеть значение времени ЦПУ системы и пользователя.

Вывод hvm из метода OSType() на шаге 6 указывает что наша гостевая ОС разработана для работы на голом железе, требуя полной виртуализации, например, KVM.

На самом последнем шаге данного рецепта мы вызываем метод state() для возврата всех текущих состояний экземпляра.

Также ознакомьтесь...

Давайте завершим данную главу полным примером сценария, содержащим все те методы, которые мы применяли вплоть до настоящего момента:


(kvm_python) root@kvm:~/kvm_python# cat kvm.py
import libvirt
import time


def main():

  xmlconfig = """
  <domain type='kvm' id='1'>
  <name>kvm_python</name>
  <memory unit='KiB'>1048576</memory>
  <currentMemory unit='KiB'>1048576</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features>
<acpi/>
    <apic/>
    <pae/>
  </features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw'/>
      <source file='/tmp/debian.img'/>
      <backingStore/>
      <target dev='hda' bus='ide'/>
      <alias name='ide0-0-0'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <controller type='usb' index='0'>
      <alias name='usb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>
    <controller type='pci' index='0' model='pci-root'>
      <alias name='pci.0'/>
    </controller>
    <controller type='ide' index='0'>
      <alias name='ide'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <interface type='network'>
      <mac address='52:54:00:da:02:01'/>
      <source network='default' bridge='virbr0'/>
      <target dev='vnet0'/>
      <model type='rtl8139'/>
      <alias name='net0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    <serial type='pty'>
      <source path='/dev/pts/5'/>
      <target port='0'/>
      <alias name='serial0'/>
    </serial>
    <console type='pty' tty='/dev/pts/5'>
      <source path='/dev/pts/5'/>
 <target type='serial' port='0'/>
      <alias name='serial0'/>
    </console>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>
    <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
      <listen type='address' address='0.0.0.0'/>
    </graphics>
    <video>
      <model type='cirrus' vram='16384' heads='1'/>
      <alias name='video0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    <memballoon model='virtio'>
      <alias name='balloon0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
    </memballoon>
  </devices>
  </domain>
  """

  conn = libvirt.open('qemu:///system')
  if conn == None:
    print 'Failed to connecto to the hypervizor'
exit(1)

  instance = conn.defineXML(xmlconfig)
  if instance == None:
    print 'Failed to define the instance'
    exit(1)

  instances = conn.listDefinedDomains()
  print 'Defined instances: {}'.format(instances)

  time.sleep(5)

  if instance.create() < 0:
    print 'Failed to start the {} instance'.format(instance.name())
    exit(1)

  if instance.isActive():
    print 'The instance {} is running'.format(instance.name())
  else:
    print 'The instance {} is not running'.format(instance.name())

  print 'The instance state, max memory, current memory, CPUs and time is {}'.format(instance.info())

  print 'The CPU, system and user times are {}'.format(instance.getCPUStats(1))

print 'The OS type for the {} instance is {}'.format(instance.name(), instance.OSType())

  time.sleep(5)

  if instance.destroy() < 0:
    print 'Failed to stop the {} instance'.format(instance.name())
    exit(1)
  else:
    print 'The instance {} has been destroyed'.format(instance.name())

  if instance.undefine() < 0:
    print 'Failed to remove the {} instance'.format(instance.name())
    exit(1)
  else:
    print 'The instance {} has been undefined'.format(instance.name())

  conn.close()

if __name__ == "__main__":
  main()

(kvm_python) root@kvm:~/kvm_python#
		

Его исполнение предоставляет следующий вывод, в предположении что предварительно было удалено определение экземпляра kvm_python:


(kvm_python) root@kvm:~/kvm_python# python kvm.py
Defined instances: ['kvm_python']
The instance kvm_python is running
The instance state, max memory, current memory, CPUs and time is [1, 1048576L, 1048576L, 1, 40000000L]
The CPU, system and user times are [{'cpu_time': 42349077L, 'system_time': 0L, 'user_time': 30000000L}]
The OS type for the kvm_python instance is hvm
The instance kvm_python has been destroyed
The instance kvm_python has been undefined
(kvm_python) root@kvm:~/kvm_python#
		

Построение примера сервера REST API при помощи libvirt и bottle

В этом рецепте мы намерены все те методы libvirt, которые мы видели в предыдущих рецептах для построения образца сервера RESTfull API, воспользовавшись микро инструментарием bottle для Python.

Bottle описывается как быстрая и простая микро инфраструктура WSGI ( Web Server Gateway Interface) для Python, которая распределяется как некий отдельный файл модуля.

[Замечание]Замечание

Для получения дополнительной информации относительно микро инфраструктуры bottle посетите, пожалуйста, её официальный сайт.

Тот простой сервер API, который мы реализовываем, будет принимать такие запросы:

  • list: метод get, который перечисляет все определённые экземпляры libvirt.

  • define: метод post, применяемый для определения какого- то нового экземпляра KVM. Мы собираемся предоставлять необходимое определение XML в качестве заголовка в этом запросе post.

  • start: метод post для запуска некого экземпляра. Название запускаемого экземпляра буде представлено в самом заголовке данного запроса.

  • stop: метод post останова некого экземпляра KVM.

  • undefine: метод post удаления данного экземпляра.

Приготовление

Для этого рецепта нам понадобится следующее:

Как это сделать...

Последующие шаги описывают как установить необходимый модуль bottle и наш простой сервер REST API, написанный на Python:

  1. Установите модуль bottle:

    
    (kvm_python) root@kvm:~/kvm_python# pip install bottle
    Collecting bottle
    ...
     Downloading bottle-0.12.13.tar.gz (70kB)
     100% |████████████████████████████████| 71kB 4.5MB/s
    ...
    Successfully installed bottle-0.12.13
    (kvm_python) root@kvm:~/kvm_python#
     	   
  2. Создайте некий новый файл, импортируйте модули libvirt и bottle и напишите метод подключения libvirt:

    
    (kvm_python) root@kvm:~/kvm_python# vim kvm_api.py
    import libvirt
    from bottle import run, request, get, post, HTTPResponse
    
    def libvirtConnect():
      try:
        conn = libvirt.open('qemu:///system')
      except libvirt.libvirtError:
        conn = None
    
      return conn
     	   
  3. Реализуйте маршрут и функцию /define:

    
    def defineKVMInstance(template):
      conn = libvirtConnect()
    
      if conn == None:
        return HTTPResponse(status=500, body='Error defining instance\n')
      else:
        try:
          conn.defineXML(template)
          return HTTPResponse(status=200, body='Instance defined\n')
        except libvirt.libvirtError:
          return HTTPResponse(status=500, body='Error defining instance\n')
    
    
    @post('/define')
    def build():
      template = str(request.headers.get('X-KVM-Definition'))
      status = defineKVMInstance(template)
    
      return status
     	   
  4. Реализуйте маршрут и функцию /undefine:

    
    def undefineKVMInstance(name):
      conn = libvirtConnect()
    
      if conn == None:
        return HTTPResponse(status=500, body='Error undefining instance\n')
      else:
        try:
          instance = conn.lookupByName(name)
          instance.undefine()
          return HTTPResponse(status=200, body='Instance undefined\n')
        except libvirt.libvirtError:
          return HTTPResponse(status=500, body='Error undefining instance\n')
    
    
    @post('/undefine')
    def build():
      name = str(request.headers.get('X-KVM-Name'))
      status = undefineKVMInstance(name)
    
      return status
     	   
  5. Реализуйте маршрут и функцию /start:

    
    def startKVMInstance(name):
      conn = libvirtConnect()
    
      if conn == None:
        return HTTPResponse(status=500, body='Error starting instance\n')
      else:
        try:
          instance = conn.lookupByName(name)
          instance.create()
          return HTTPResponse(status=200, body='Instance started\n')
        except libvirt.libvirtError:
          return HTTPResponse(status=500, body='Error starting instance\n')
    
    
    @post('/start')
    def build():
      name = str(request.headers.get('X-KVM-Name'))
      status = startKVMInstance(name)
    
      return status
     	   
  6. Реализуйте маршрут и функцию /stop:

    
    def stopKVMInstance(name):
      conn = libvirtConnect()
    
      if conn == None:
        return HTTPResponse(status=500, body='Error stopping instance\n')
      else:
        try:
          instance = conn.lookupByName(name)
          instance.destroy()
          return HTTPResponse(status=200, body='Instance stopped\n')
        except libvirt.libvirtError:
          return HTTPResponse(status=500, body='Error stopping instance\n')
    
    
    @post('/stop')
    def build():
      name = str(request.headers.get('X-KVM-Name'))
      status = stopKVMInstance(name)
    
      return status
     	   
  7. Реализуйте маршрут и функцию /list:

    
    def getLibvirtInstances():
      conn = libvirtConnect()
    
      if conn == None:
        return HTTPResponse(status=500, body='Error listing instances\n')
      else:
        try:
          instances = conn.listDefinedDomains()
          return instances
        except libvirt.libvirtError:
          return HTTPResponse(status=500, body='Error listing instances\n')
    
    
    @get('/list')
    def list():
      kvm_list = getLibvirtInstances()
    
      return "List of KVM instances: {}\n".format(kvm_list)
     	   
  8. Вызовите метод run() для запуска своего сервера WSGI после исполнения данного сценария:

    
    run(host='localhost', port=8080, debug=True)
     	   

Как это работает...

Давайте более детально рассмотрим свой код. Для начала сохраните все предыдущие изменения в некий файл и исполните этот сценарий:


kvm_python) root@kvm:~/kvm_python# python kvm_api.py
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
		

В неком отдельном терминале определите какой- то новый экземпляр, передав соответствующее необходимое определение XMLв виде заголовка:


(kvm_python) root@kvm:~/kvm_python# curl -s -i -XPOST localhost:8080/define --header "X-KVM-Definition: <domain type='kvm'><name>kvm_api</name><memory unit='KiB'>1048576</memory><vcpu >1</vcpu><os><type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type></os><devices><emulator>/usr/bin/qemu-system-x86_64</emulator><disk type='file' device='disk'><driver name='qemu' type='raw'/><source file='/tmp/debian.img'/><target dev='hda' bus='ide'/></disk><interface type='network'><mac address='52:54:00:da:02:01'/><source network='default' bridge='virbr0'/><target dev='vnet0'/></interface><graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'><listen type='address' address='0.0.0.0'/></graphics></devices></domain>"
HTTP/1.0 200 OK
Date: Fri, 12 May 2017 20:29:14 GMT
Server: WSGIServer/0.1 Python/2.7.6
Content-Length: 17
Content-Type: text/html; charset=UTF-8

Instance defined
(kvm_python) root@kvm:~/kvm_python#
		

Мы применяем тот сырой образ Debian, который мы создали в рецепте Установка пользовательской ОС в образ при помощи debootstrap из Главы 1. Соответствующее определение также должно выглядеть знакомым; мы применяли его в большинстве рецептов данной главы.

Теперь мы должны получить определённым некий новый экземпляр KVM. Давайте воспользуемся маршрутом /list для перечисления всех экземпляров и проверим при помощи команды virsh:


(kvm_python) root@kvm:~/kvm_python# curl localhost:8080/list
List of KVM instances: ['kvm_api']
(kvm_python) root@kvm:~/kvm_python# virsh list --all
 Id   Name     State
----------------------------------------------------
 -   kvm_api   shut off

(kvm_python) root@kvm:~/kvm_python#
		

Теперь, когда у нас имеется определённым экземпляр, давайте запустим его при помощи маршрута /start и убедимся что он исполняется:


(kvm_python) root@kvm:~/kvm_python# curl -s -i -XPOST localhost:8080/start --header "X-KVM-Name: kvm_api"
HTTP/1.0 200 OK
Date: Fri, 12 May 2017 20:29:38 GMT
Server: WSGIServer/0.1 Python/2.7.6
Content-Length: 17
Content-Type: text/html; charset=UTF-8

Instance started
(kvm_python) root@kvm:~/kvm_python# virsh list --all
 Id   Name      State
----------------------------------------------------
 1    kvm_api   running

(kvm_python) root@kvm:~/kvm_python#
		

Чтобы остановить этот экземпляр и полностью его удалить мы применим маршруты /stop и /undefine из своего сценария:


(kvm_python) root@kvm:~/kvm_python# curl -s -i -XPOST localhost:8080/stop --header "X-KVM-Name: kvm_api"
HTTP/1.0 200 OK
Date: Fri, 12 May 2017 20:29:52 GMT
Server: WSGIServer/0.1 Python/2.7.6
Content-Length: 17
Content-Type: text/html; charset=UTF-8

Instance stopped
(kvm_python) root@kvm:~/kvm_python#
(kvm_python) root@kvm:~/kvm_python# virsh list --all
 Id   Name      State
----------------------------------------------------
 -    kvm_api   shut off

(kvm_python) root@kvm:~/kvm_python#
(kvm_python) root@kvm:~/kvm_python# curl -s -i -XPOST localhost:8080/undefine --header "X-KVM-Name: kvm_api"
HTTP/1.0 200 OK
Date: Fri, 12 May 2017 20:30:09 GMT
Server: WSGIServer/0.1 Python/2.7.6
Content-Length: 19
Content-Type: text/html; charset=UTF-8

Instance undefined
(kvm_python) root@kvm:~/kvm_python#
(kvm_python) root@kvm:~/kvm_python# virsh list --all
 Id Name State
----------------------------------------------------

(kvm_python) root@kvm:~/kvm_python#
		

Давайте пройдёмся по своему коду более подробно.

На шаге 1 мы установили модуль bottle в своей виртуальной среде Python.

После импорта пакетов libvirt и bottle на шаге 2 мы определили свой метод libvirtConnect()/ Все функции из нашей программы будут применять его для подключения к своему гипервизору.

На шаге 3 мы реализовали маршрут /define и его функциональность. Наш декоратор @post связывает имеющийся код из нашей следующей функции с неким путём URL. В нашем примере данный маршрут /define связывается с определённой функцией build(). Передача маршрута /define в соответствующую команду curl исполнит данную функцию, которая в свою очередь вызовет метод efineKVMInstance() для определения необходимого экземпляра.

В шагах 4,5 и 6 мы применяем аналогичный шаблон кодирования для запуска, останова и удаления определения своего экземпляра.

На шаге 7 мы применяем декоратор @get для реализации некой функции перечисления всех определённых в этом хосте экземпляров.

На шаге 8 мы применяем класс run, который предоставляет соответствующий метод run(), применяемый нами для запуска встроенного сервера. В нашем примере этот сервер будет выполнять ожидание в локальном хосте по порту 8080.

Как мы уже видели ранее, исполнение данного сценария начнёт процесс ожидания в сокете по порту 8080, с которым мы можем взаимодействовать при помощи команды curl.

Также ознакомьтесь...

Вот наш полный код реализации:


import libvirt
from bottle import run, request, get, post, HTTPResponse

def libvirtConnect():
  try:
    conn = libvirt.open('qemu:///system')
  except libvirt.libvirtError:
    conn = None

  return conn


def getLibvirtInstances():
  conn = libvirtConnect()

  if conn == None:
    return HTTPResponse(status=500, body='Error listing instances\n')
  else:
    try:
      instances = conn.listDefinedDomains()
      return instances
    except libvirt.libvirtError:
      return HTTPResponse(status=500, body='Error listing instances\n')


def defineKVMInstance(template):
  conn = libvirtConnect()

  if conn == None:
    return HTTPResponse(status=500, body='Error defining instance\n')
  else:
    try:
      conn.defineXML(template)
      return HTTPResponse(status=200, body='Instance defined\n')
    except libvirt.libvirtError:
      return HTTPResponse(status=500, body='Error defining instance\n')


def undefineKVMInstance(name):
  conn = libvirtConnect()

  if conn == None:
    return HTTPResponse(status=500, body='Error undefining instance\n')
  else:
    try:
      instance = conn.lookupByName(name)
      instance.undefine()
      return HTTPResponse(status=200, body='Instance undefined\n')
    except libvirt.libvirtError:
      return HTTPResponse(status=500, body='Error undefining instance\n')


def startKVMInstance(name):
  conn = libvirtConnect()

  if conn == None:
    return HTTPResponse(status=500, body='Error starting instance\n')
  else:
    try:
      instance = conn.lookupByName(name)
      instance.create()
      return HTTPResponse(status=200, body='Instance started\n')
    except libvirt.libvirtError:
      return HTTPResponse(status=500, body='Error starting instance\n')


def stopKVMInstance(name):
  conn = libvirtConnect()

  if conn == None:
    return HTTPResponse(status=500, body='Error stopping instance\n')
  else:
    try:
      instance = conn.lookupByName(name)
      instance.destroy()
      return HTTPResponse(status=200, body='Instance stopped\n')
    except libvirt.libvirtError:
      return HTTPResponse(status=500, body='Error stopping instance\n')


@post('/define')
def build():
  template = str(request.headers.get('X-KVM-Definition'))
  status = defineKVMInstance(template)

  return status


@post('/undefine')
def build():
  name = str(request.headers.get('X-KVM-Name'))
  status = undefineKVMInstance(name)

  return status


@get('/list')
def list():
  kvm_list = getLibvirtInstances()

  return "List of KVM instances: {}\n".format(kvm_list)


@post('/start')
def build():
  name = str(request.headers.get('X-KVM-Name'))
  status = startKVMInstance(name)

  return status


@post('/stop')
def build():
  name = str(request.headers.get('X-KVM-Name'))
  status = stopKVMInstance(name)

  return status


run(host='localhost', port=8080, debug=True)