VDSM и хуки
1. VDSM
Менеджер управления использует службу VDSM для управления хостами с zVirt Node и RedOS. Служба VDSM управляет и ведет мониторинг хранилища, памяти и сетевых ресурсов хоста. Кроме того, она координирует создание виртуальных машин, сбор статистики, сбор журналов и другие задачи по администрированию хостов. VDSM работает как сервис на каждом хосте под управлением Менеджера управления и отвечает на XML-RPC запросы от клиентов. Менеджер управления функционирует как клиент VDSM.
2. Хуки VDSM
Службу VDSM можно расширить с помощью хуков. Хуки - это скрипты, выполняемые на хосте при наступлении ключевых событий. При наступлении предусмотренного события служба VDSM запускает любые исполняемые скрипты хуков в /usr/libexec/vdsm/hooks/nn_event-name/ на хосте в порядке, определяемом их буквенно-цифровыми идентификаторами. Как правило, каждому скрипту хука присвоено двузначное число, которое стоит в начале имени файла, чтобы четко указать, в каком порядке будут выполняться скрипты. Скрипты хуков можно писать на любом языке программирования, но в этой главе даны примеры на Python.
Обратите внимание, что выполняются все скрипты, заданные на хосте для события. Если требуется, чтобы один конкретный хук выполнялся только для определенного подмножества виртуальных машин, работающих на хосте, то убедитесь, что скрипт хука отвечает этому требованию, а для этого надо использовать Пользовательские свойства (Custom Properties), ассоциированные с этой виртуальной машиной.
| Хуки VDSM могут мешать работе ПО «zVirt Max». Ошибка в хуке VDSM способна привести к отказу виртуальной машины и потере данных. Хуки VDSM надлежит реализовывать с осторожностью и тщательно проверять. Хуки API - это нововведение, которое будет существенно переработано в будущем. |
3. Расширение службы VDSM с помощью хуков
В этой главе описано, как расширить службу VDSM с помощью хуков на основе событий. Расширение службы VDSM с помощью хуков - это экспериментальная технология, и настоящая глава адресована опытным разработчикам. Если установить пользовательские свойства на виртуальных машинах, то скриптам хуков можно передать дополнительные параметры, свойственные конкретной виртуальной машине.
4. Установка хука VDSM
Хуки VDSM не установлены по умолчанию. Если требуется определенный хук, то его необходимо установить вручную.
-
Репозиторий на хосте должен быть включен.
-
Необходимо авторизоваться на хосте в качестве root-пользователя.
-
Выведите список доступных хуков:
dnf list vdsm\*hook\* -
Установите пакет нужного хука VDSM на хосте:
dnf install <vdsm-hook-name>Например, чтобы установить пакет vdsm-hook-vhostmd на хосте, введите следующую команду:
dnf install vdsm-hook-vhostmd
5. Предусмотренные события VDSM
| Имя | Описание |
|---|---|
before_vm_start |
До запуска виртуальной машины. |
after_vm_start |
После запуска виртуальной машины. |
before_vm_cont |
До возобновления работы виртуальной машины. |
after_vm_cont |
После возобновления работы виртуальной машины. |
before_vm_pause |
До приостановки виртуальной машины. |
after_vm_pause |
После приостановки виртуальной машины. |
before_vm_hibernate |
До перехода виртуальной машины в спящий режим. |
after_vm_hibernate |
После перехода виртуальной машины в спящий режим. |
before_vm_dehibernate |
До выхода виртуальной машины из спящего режима. |
after_vm_dehibernate |
После выхода виртуальной машины из спящего режима. |
before_vm_migrate_source |
До миграции виртуальной машины, работающей на хосте-источнике, с которого выполняется миграция. |
after_vm_migrate_source |
После миграции виртуальной машины, работающей на хосте-источнике, с которого выполняется миграция. |
before_vm_migrate_destination |
До миграции виртуальной машины, работающей на хосте-приемнике, на который выполняется миграция. |
after_vm_migrate_destination |
После миграции виртуальной машины, работающей на хосте-приемнике, на который выполняется миграция. |
after_vm_destroy |
После удаления виртуальной машины. |
before_vdsm_start |
До запуска службы VDSM на хосте. Хуки before_vdsm_start выполняются от имени root-пользователя и не наследуют окружение процесса VDSM. |
after_vdsm_stop |
После остановки службы VDSM на хосте. Хуки after_vdsm_stop выполняются от имени root-пользователя и не наследуют окружение процесса VDSM. |
before_nic_hotplug |
До горячего подключения сетевой карты к виртуальной машине. |
after_nic_hotplug |
После горячего подключения сетевой карты к виртуальной машине. |
before_nic_hotunplug |
До горячего отключения сетевой карты от виртуальной машины. |
after_nic_hotunplug |
После горячего отключения сетевой карты от виртуальной машины. |
after_nic_hotplug_fail |
После неудачной попытки горячего подключения сетевой карты к виртуальной машине. |
after_nic_hotunplug_fail |
После неудачной попытки горячего отключения сетевой карты от виртуальной машины. |
before_disk_hotplug |
До горячего подключения диска к виртуальной машине. |
after_disk_hotplug |
После горячего подключения диска к виртуальной машине. |
before_disk_hotunplug |
До горячего отключения диска от виртуальной машины. |
after_disk_hotunplug |
После горячего отключения диска от виртуальной машины. |
after_disk_hotplug_fail |
После неудачной попытки горячего подключения диска к виртуальной машине. |
after_disk_hotunplug_fail |
После неудачной попытки горячего отключения диска от виртуальной машины. |
before_device_create |
До создания устройства, поддерживающего пользовательские свойства. |
after_device_create |
После создания устройства, поддерживающего пользовательские свойства. |
before_update_device |
До обновления устройства, поддерживающего пользовательские свойства. |
after_update_device |
После обновления устройства, поддерживающего пользовательские свойства. |
before_device_destroy |
До удаления устройства, поддерживающего пользовательские свойства. |
after_device_destroy |
После удаления устройства, поддерживающего пользовательские свойства. |
before_device_migrate_destination |
До миграции устройства, работающего на хосте-приемнике, на который выполняется миграция. |
after_device_migrate_destination |
После миграции устройства, работающего на хосте-приемнике, на который выполняется миграция. |
before_device_migrate_source |
До миграции устройства, работающего на хосте-источнике, с которого выполняется миграция. |
after_device_migrate_source |
После миграции устройства, работающего на хосте-источнике, с которого выполняется миграция. |
after_network_setup |
После настройки сети при запуске хоста. |
before_network_setup |
До настройки сети при запуске хоста. |
6. Окружение хуков VDSM
Большинство скриптов хуков выполняются от имени пользователя vdsm и наследуют окружение процесса VDSM. Исключение составляют те скрипты хуков, которые запускаются событиями before_vdsm_start и after_vdsm_stop. Скрипты хуков, которые запускаются этими событиями, выполняются от имени root-пользователя и не наследуют среду процесса VDSM.
7. XML-объект домена хука VDSM
Когда запускаются скрипты хуков, переменная _hook_domxml добавляется в окружение. В этой переменной указан путь к XML-представлению домена libvirt соответствующей виртуальной машины. Несколько перечисленных ниже хуков составляют исключение из этого правила. Переменная _hook_domxml перечисленных ниже хуков содержит XML-представление сетевой карты, а не виртуальной машины.
-
*nic_hotplug* -
*nic_hotunplug* -
*_update_device -
*_device_create -
*device_migrate*
| Сейчас хуки before_migration_destination и before_dehibernation получают XML-представление домена от хоста-источника. В XML-представлении домена на хосте-приемнике будет несколько отличий. |
Формат XML-представления домена libvirt используется службой VDSM для определения виртуальных машин. Подробнее о формате XML-представления домена libvirt см. на веб-сайте. Идентификатор UUID виртуальной машины можно логически вывести из XML-представления домена, а также получить из переменной среды vmId.
8. Установка пользовательских свойств
Пользовательские свойства, принятые Менеджером управления, и соответственно передаваемые пользовательским хукам, можно задать с помощью команды engine-config. Выполните команду от имени root-пользователя на том хосте, на котором установлен Менеджер управления.
Ключи конфигурации UserDefinedVMProperties и CustomDeviceProperties применяются для хранения имен поддерживаемых пользовательских свойств. Регулярные выражения, задающие допустимые значения для каждого именованного пользовательского свойства, тоже содержатся в этих ключах конфигурации.
Пользовательские свойства отделяются друг от друга точкой с запятой. Обратите внимание, что при настройке ключа конфигурации все уже содержащиеся в нем значения будут перезаписаны. При комбинировании новых и уже существующих пользовательских свойств необходимо добавить все пользовательские свойства, которые в команде задавали значение ключа.
После обновления ключа конфигурации необходимо перезапустить службу ovirt-engine, чтобы новые значения вступили в силу.
-
Проверьте существующие пользовательские свойства, заданные ключом конфигурации
UserDefinedVMProperties, с помощью следующей команды:engine-config -g UserDefinedVMPropertiesВ выводе ниже показано, что пользовательское свойство память (memory) уже задано. Регулярное выражение
^[0-9]+$гарантирует, что в пользовательском свойстве всегда будут содержаться только цифры.engine-config -g UserDefinedVMProperties UserDefinedVMProperties: version: 4.3 UserDefinedVMProperties: version: 4.4 UserDefinedVMProperties : memory=^[0-9]+$ version: 4.4 -
Поскольку пользовательское свойство
memoryуже задано в ключе конфигурацииUserDefinedVMProperties, новое пользовательское свойство должно быть добавлено к нему. Дополнительное пользовательское свойствоsmartcardдобавляется к значению ключа конфигурации. Новое пользовательское свойство может принимать значенияtrueилиfalse.engine-config -s UserDefinedVMProperties='memory=^[0-9]+$;smartcard=^(true|false)$' --cver=4.4 -
Убедитесь, что пользовательские свойства, заданные ключом конфигурации
UserDefinedVMProperties, были обновлены корректно.engine-config -g UserDefinedVMProperties UserDefinedVMProperties: version: 4.3 UserDefinedVMProperties: version: 4.4 UserDefinedVMProperties : memory=^[0-9]+$;smartcard=^(true|false)$ version: 4.4 -
В конце требуется перезапустить службу ovirt-engine, чтобы изменения конфигурации вступили в силу.
systemctl restart ovirt-engine.service
-
Проверьте существующие пользовательские свойства, заданные ключом конфигурации CustomDeviceProperties, с помощью следующей команды:
engine-config -g CustomDevicePropertiesВ выводе ниже показано, что пользовательские свойства еще не заданы.
engine-config -g CustomDeviceProperties CustomDeviceProperties: version: 4.3 CustomDeviceProperties: version: 4.4 -
Пользовательское свойство
interfaceещё не существует, поэтому его можно добавить "как есть". В этом примере значение подсвойстваspeedуказано в диапазоне от0до99999, а значение подсвойстваduplexустановлено на выбор либоfull, либоhalf.engine-config -s CustomDeviceProperties="{type=interface;prop={speed=^([0-9]{1,5})$;duplex=^(full|half)$}}" --cver=4.4 -
Убедитесь, что пользовательские свойства, заданные ключом конфигурации
CustomDeviceProperties, были обновлены корректно.engine-config -g CustomDeviceProperties UserDefinedVMProperties: version: 4.3 UserDefinedVMProperties: version: 4.4 UserDefinedVMProperties : {type=interface;prop={speed=^([0-9]{1,5})$;duplex=^(full|half)$}} version: 4.4 -
В конце требуется перезапустить службу ovirt-engine, чтобы изменения конфигурации вступили в силу.
systemctl restart ovirt-engine.service
9. Настройка пользовательских свойств на виртуальной машине
Когда пользовательские свойства заданы в Менеджере управления, можно приступить к их настройке на виртуальных машинах. Пользовательские свойства настраиваются на вкладке Пользовательские свойства (Custom Properties) в окнах Новая виртуальная машина (New Virtual Machine) и Изменить виртуальную машину (Edit Virtual Machine) на Портале администрирования.
Кроме того, пользовательские свойства можно настроить в диалоговом окне Запустить ВМ (Run Virtual Machine(s)). Пользовательские свойства, заданные в диалоговом окне Запустить ВМ (Run Virtual Machine(s)), будут применяться к виртуальным машинам только до следующего выключения.
Вкладка Пользовательские свойства (Custom Properties) предоставляет возможность выбирать из списка заданных пользовательских свойств. После выбора ключа пользовательского свойства появится дополнительное поле, в котором можно задать значение этого ключа. Нажимая и
, добавляйте или удаляйте пары ключ/значение.
10. Оценка пользовательских свойств на виртуальных машинах в хуке VDSM
Каждый набор ключей в поле Пользовательские свойства (Custom Properties) для виртуальной машины добавляется как переменная среды при вызове скриптов хука. Хотя регулярные выражения, используемые для проверки поля Пользовательские свойства (Custom Properties) дают некоторую защиту, необходимо убедиться, что скрипты также проверяют вводимые данные на соответствие ожиданиям.
Далее в коротком примере на языке Python проверяется наличие пользовательского свойства key1. Если пользовательское свойство задано, то выдается стандартная ошибка. Если пользовательское свойство не задано, то ничего не произойдет.
#!/usr/bin/python
import os
import sys
if os.environ.has_key('key1'):
sys.stderr.write('key1 value was : %s\n' % os.environ['key1'])
else:
sys.exit(0)
11. Использование модуля VDSM hooking
VDSM поставляется с модулем hooking Python, в котором предоставлены вспомогательные функции для скриптов хука VDSM. Этот модуль предоставляется в качестве примера и касается только хуков VDSM, написанных на Python.
Модуль hooking поддерживает чтение XML-представления libvirt виртуальной машины в объект DOM. Затем скрипты хуков могут использовать встроенную в Python библиотеку xml.dom для работы с объектом.
Измененный объект можно затем сохранить обратно в XML-представление libvirt с помощью модуля hooking. Модуль hooking предоставляет следующие функции для поддержки разработки хуков:
| Имя | Аргумент | Описание |
|---|---|---|
|
строка |
Конвертирует строку "true" или "false" в логическое значение |
|
- |
Считывает XML-представление libvirt виртуальной машины в объект DOM |
|
объект DOM |
Записывает XML-представление libvirt виртуальной машины из объекта DOM |
12. Выполнение хуков VDSM
Скрипты before_vm_start могут изменить XML-представление домена, чтобы изменить определение настроек VDSM виртуальной машины до того, как оно достигнет libvirt. При этом необходимо проявлять осторожность. Скрипты хуков способны нарушить работу VDSM, а дефектные скрипты могут привести к сбоям среды ПО «zVirt Max». В частности, никогда не изменяйте идентификатор UUID домена и не пытайтесь удалить устройство из домена без достаточных знаний и опыта.
Оба скрипта хуков before_vdsm_start и after_vdsm_stop выполняются от имени root-пользователя. Остальные скрипты хуков, для которых требуется root-доступ в систему, должны быть записаны, чтобы использовать команду sudo для повышения привилегий. Для этого необходимо обновить /etc/sudoers, чтобы позволить пользователю vdsm использовать команду sudo без повторного введения пароля. Это необходимо, т.к. скрипты хуков выполняются не в интерактивном режиме.
В этом примере команда sudo будет сконфигурирована так, чтобы позволить пользователю vdsm выполнить команду /bin/chown от имени root-пользователя.
-
Авторизуйтесь на хосте виртуализации как root-пользователь.
-
Откройте файл /etc/sudoers в текстовом редакторе.
-
Вставьте в файл следующую строку:
vdsm ALL=(ALL) NOPASSWD: /bin/chownТут указано, что пользователь vdsm может выполнить команду
/bin/chownот имени root-пользователя. ПараметрNOPASSWDуказывает на то, что у пользователя не будет запрошен пароль при вызове командыsudo.
После внесения этого изменения в конфигурацию хуки VDSM смогут задействовать команду sudo для выполнения /bin/chown от имени root-пользователя. В этом коде Python используется команда sudo для выполнения /bin/chown от имени root-пользователя в файле /my_file.
retcode = subprocess.call( ["/usr/bin/sudo", "/bin/chown", "root", "/my_file"] )
Стандартный поток сообщений об ошибках скриптов хуков собирается в журнал VDSM. Эта информация используется для отладки скриптов хуков.
13. Коды возврата хука VDSM
Скрипты хуков должны возвращать один из кодов возврата. Код возврата определяет, будут ли дальнейшие скрипты хуков обрабатываться VDSM.
| Код | Описание |
|---|---|
0 |
Скрипт хука успешно выполнен |
1 |
Скрипт хука завершился сбоем, необходимо обработать другие хуки |
2 |
Скрипт хука завершился сбоем, другие хуки не обрабатывать |
>2 |
Зарезервировано |
14. Примеры хуков VDSM
Примеры скриптов хуков, представленные в этом разделе, необязательно поддерживаются ПО «zVirt Max». Убедитесь, что абсолютно все скрипты хуков, установленные в системе, независимо от источника, тщательно протестированы для работы в вашей среде.
Этот скрипт хука позволяет настраивать выделение памяти на хосте NUMA на основании пользовательского свойства numaset. Если пользовательское свойство не задано, то ничего не произойдет.
Строка конфигурирования:
numaset=^(interleave|strict|preferred):[\^]?\d+(-\d+)?(,[\^]?\d+(-\d+)?)*$
Используемое регулярное выражение позволяет пользовательскому свойству numaset на конкретной виртуальной машине указать режим выделения (interleave, strict, preferred) и используемый узел. Два значения отделяются двоеточием (:). Регулярное выражение позволяет задать в качестве nodeset:
-
один конкретный узел (
numaset=strict:1: указывает, что будет задействован только узел 1) -
диапазон узлов (
numaset=strict:1-4: указывает, что будут задействованы узлы 1-4) -
только не этот конкретный узел (
numaset=strict:^3: указывает, что не будет задействован узел 3) -
любую комбинацию вышеуказанных вариантов через запятую (
numaset=strict:1-4,6: указывает, что задействованы узлы 1-4 и 6).
Скрипт:
/usr/libexec/vdsm/hooks/before_vm_start/50_numa
#!/usr/bin/python
import os
import sys
import hooking
import traceback
'''
numa hook
=========
add numa support for domain xml:
<numatune>
<memory mode="strict" nodeset="1-4,^3" />
</numatune>
memory=interleave|strict|preferred
numaset="1" (use one NUMA node)
numaset="1-4" (use 1-4 NUMA nodes)
numaset="^3" (don't use NUMA node 3)
numaset="1-4,^3,6" (or combinations)
syntax:
numa=strict:1-4
'''
if os.environ.has_key('numa'):
try:
mode, nodeset = os.environ['numa'].split(':')
domxml = hooking.read_domxml()
domain = domxml.getElementsByTagName('domain')[0]
numas = domxml.getElementsByTagName('numatune')
if not len(numas) 0:
numatune = domxml.createElement('numatune')
domain.appendChild(numatune)
memory = domxml.createElement('memory')
memory.setAttribute('mode', mode)
memory.setAttribute('nodeset', nodeset)
numatune.appendChild(memory)
hooking.write_domxml(domxml)
else:
sys.stderr.write('numa: numa already exists in domain xml')
sys.exit(2)
except:
sys.stderr.write('numa: [unexpected error]: %s\n' % traceback.format_exc())
sys.exit(2)