Hello,
I'm writing some code to make the following workflow:
* setting a custom stop_reason as 'force_delete' when
stopping/deleting a vm
* this will trigger a vdsm hook into after_vm_destroy event.
* this hook will call back AWX/ansible to:
o remove DNS entries
o remove vm file backup
o delete supervision
o (optionnal) delete the vm
* the vm is destroyed and removed by vdsm.
Here is the code (I'm not a python expert)
#!/usr/libexec/platform-python
# 211217 NBT
# This is a vdsm hook that aims to auto delete oVirt dependencies when
removing a VM directly from engine.
# It is triggered when filling the stop_reason field in oVirt with
strict 'clean'.
# It initially concerns following actions:
# - Centreon subscription Removing
# - Backup erase from Sotora
# - DNS cleaning on Lilas
# - IPA deletion
# This set of actions can be extended into the concerned AWX
job_template (180 or 160)
# When finished, vm can be manually removed from Engine.
# When string is 'force_delete' or 'force_remove', then in addition, the
vm will be automatically erased at the same time.
importos
fromvdsm.hook importhooking
fromxml.dom importminidom
importrequests
fromxml.etree importElementTree
importsys
importurllib3
importtime
importtraceback
importsubprocess
fromsubprocess importPIPE, STDOUT
importlogging
urllib3.disable_warnings()
logger = logging.getLogger("register_migration")
defexec_cmd(*args):
retcode, out, err = hooking.execCmd(args, sudo=True)
ifretcode != 0:
raiseRuntimeError("Failed to execute %s, due to: %s"%
(args, err))
returnout
if__name__== '__main__':
logging.basicConfig(filename="/var/log/vdsm/custom_hooks.log",
level=logging.INFO, format='%(asctime)s%(levelname)s%(name)s:%(message)s',
datefmt= '%Y-%m-%d%H:%M:%S')
iflen(sys.argv) > 1:
vm_name= sys.argv[1]
else:
domxml = hooking.read_domxml()
vm_name = domxml.getElementsByTagName('name')[0].firstChild.nodeValue
print(vm_name)
# API oVirt: Initialize variables
user = 'admin@internal'
password = 'password'
url =
"https://air-dev.v100.abes.fr/ovirt-engine/api/vms?search=name%3D"+ vm_name
headers = {'Accept': 'application/xml'}
print('name: '+ vm_name)
# API oVirt: Test if VM stop_reason has been defined
whileTrue:
# r = requests.get(url, headers=headers, auth=('admin@internal',
'password'), verify=False)
# tree = ElementTree.fromstring(r.content)
r = exec_cmd('curl', '--insecure', '--header', 'Accept:
application/xml', '--user', 'admin@internal:password',
'https://air-dev.v100.abes.fr/ovirt-engine/api/vms?search=name%3D'+ vm_name)
tree = ElementTree.fromstring(b''.join(r))
forvm intree.findall('vm'):
status = vm.find('status')
stop_reason = vm.find('stop_reason')
print(status.text)
ifstop_reason isnotNone:
print(status.text, stop_reason.text)
break
time.sleep(1)
forvm intree.findall('vm'):
stop_reason = vm.find('stop_reason')
ifstop_reason isNone:
exit('stop_reason is not defined')
else:
# API AWX: Initialize variables
header1 = 'Content-Type: application/json'
header2 = 'Authorization: Bearer token'
curl_server = "nbt"
curl_extra_vars = "{\\\"comment\\\": \\\"Nbt\\\",
\\\"survey_ovirt_password\\\": \\\"password\\\",
\\\"force_erase\\\":
\\\"yes\\\", \\\"survey_vms_list\\\": %s}"% (vm_name)
curl_config = '{"extra_vars": "%s"}'% (curl_extra_vars)
ifstop_reason in["clean"]:
curl_job_template = "180"
print('Cleaning'+ vm_name + 'from oVirt on ancolie-'+ curl_server +
'with workflow_job_template '+ curl_job_template)
curl_url =
"http://ancolie-{}.v106.abes.fr/api/v2/workflow_job_templates/{}/launch/".format(curl_server,curl_job_template)
exec_cmd('curl', '-f', '-H', header1, '-H', header2,
'-XPOST', '-d',
curl_config, curl_url)
elifstop_reason in["force_delete", "force_remove"]:
curl_job_template = "160"
print('Deleting and cleaning'+ vm_name + 'from oVirt on ancolie-'+
curl_server + 'with workflow_job_template '+ curl_job_template )
curl_url =
"http://ancolie-{}.v106.abes.fr/api/v2/workflow_job_templates/{}/launch/".format(curl_server,curl_job_template)
exec_cmd('curl', '-f', '-H', header1, '-H', header2,
'-XPOST', '-d',
curl_config, curl_url)
else:
exit('Stop reason is '+ stop_reason + ' and there is no reason to do
anything for '+ vm_name)
The idea is to use the stop_reason element into the vm xml definition.
But after hours, I realized that this element is writed to the vm
definition file only after the VM has been destroyed.
So if I test this value (if existing) when executing the hook, the text
value doesn't still exist at early time
I added a 'while' loop to wait for the stop_reason element to be
present, but vdsm hangs out because of an infinity loop:
2021-12-20 18:13:30,148+0100 INFO (jsonrpc/7) [root]
/usr/libexec/vdsm/hooks/after_vm_destroy/clean_vm_dependencies_2.py:
rc=1 err=b'Traceback (most recent call last):\n File
"/usr/libexec/vdsm/hooks/after_vm_destroy/clean_vm_dependencies_2.py",
line 84, in <module>\n print(status.text,
stop_reason.text)\nAttributeError: \'NoneType\' object has no attribute
\'text\'\n' (hooks:122)
....
So I'm deducing I'm not able to accomplish my initial goal to use
stop_reason as a trigger with after_vm_destroy event.
I searched an other way to do: I thought of replacing querying ovirt API
with getting the value coming from the UI, but I can't find the suitable
database query. Is there a way to do such a thing? Does engine hooks
exist for stopped vm??
Thank you for your help.
PS: I'm already able to do this from ansible/AWX, but I have to do it
from UI/vdsm for any reason.
--
Nathanaël Blanchet
Supervision réseau
SIRE
227 avenue Professeur-Jean-Louis-Viala
34193 MONTPELLIER CEDEX 5
Tél. 33 (0)4 67 54 84 55
Fax 33 (0)4 67 54 84 14
blanchet(a)abes.fr