Skip to content

Commit

Permalink
Fix virt module to undefine a domain with nvram or other metadata
Browse files Browse the repository at this point in the history
Libvirt function undefine() is not able to delete nvram or other
metadata. Therefore it is replaced with undefineFlags() which is able to
handle it by using flags.

All possible flags are listed in 'ENTRY_UNDEFINE_FLAGS_MAP'. Integer 23
makes undefine successful in all cases (1 + 2 + 4 +
16).

Source:
https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainUndefineFlags

The flags nvram(4) and keep_nvram(8) are mutually exclusive.

To control the metadata handling during undefine, two new options
'force' and 'flags' are introduced including documentation and examples.

Compatibility / Risk:
The function undefineFlags() appeared in libvirt version 0.9.4 which was
release in 2011. It seems rather unlikely that somebody is still using
an older unsupported libvirt version. Additionally, if none of the new
module options are provided, then it behaves as before and maintains
backward compatibility, means the overall risk of this commit should be
rather low.

Source:
https://libvirt.org/hvsupport.html#virHypervisorDriver
  • Loading branch information
bturmann committed Aug 16, 2022
1 parent 787b4f2 commit 9c04800
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 4 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/136_fix_undefine_nvram.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- virt - fix virt module to undefine a domain with nvram, managed_save, snapshot_metadata or checkpoints_metadata (https://github.com/ansible-collections/community.libvirt/issues/40).
94 changes: 90 additions & 4 deletions plugins/modules/virt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@
short_description: Manages virtual machines supported by libvirt
description:
- Manages virtual machines supported by I(libvirt).
options:
flags:
choices: [ 'managed_save', 'snapshots_metadata', 'nvram', 'keep_nvram', 'checkpoints_metadata']
description:
- Pass additional parameters.
- Currently only implemented with command C(undefine).
Specify which metadata should be removed with C(undefine).
Useful option to be able to C(undefine) guests with UEFI nvram.
C(nvram) and C(keep_nvram) are conflicting and mutually exclusive.
Consider option C(force) if all related metadata whould be removed.
type: list
elements: str
force:
description:
- Enforce an action.
- Currently only implemented with command C(undefine).
This option can be used instead of providing all C(flags).
If C(yes), C(undefine) removes also any related nvram or other metadata, if existing.
If C(no) or not set, C(undefine) executes only if there is no nvram or other metadata existing.
Otherwise the task fails and the guest is kept defined without change.
C(yes) and option C(flags) should not be provided together. In this case
C(undefine) ignores C(yes), considers only C(flags) and issues a warning.
type: bool
extends_documentation_fragment:
- community.libvirt.virt.options_uri
- community.libvirt.virt.options_xml
Expand Down Expand Up @@ -67,6 +90,30 @@
xml: "{{ lookup('template', 'vm_template.xml.j2') }}"
autostart: yes
# Undefine VM only, if it has no existing nvram or other metadata
- name: Undefine qemu VM
community.libvirt.virt:
name: foo
# Undefine VM and force remove all of its related metadata (nvram, snapshots, etc.)
- name: "Undefine qemu VM with force"
community.libvirt.virt:
name: foo
force: yes
# Undefine VM and remove all of its specified metadata specified
# Result would the same as with force=true
- name: Undefine qemu VM with list of flags
community.libvirt.virt:
name: foo
flags: managed_save, snapshots_metadata, nvram, checkpoints_metadata
# Undefine VM, but keep its nvram
- name: Undefine qemu VM and keep its nvram
community.libvirt.virt:
name: foo
flags: keep_nvram
# Listing VMs
- name: List all VMs
community.libvirt.virt:
Expand Down Expand Up @@ -134,6 +181,17 @@
6: 'crashed',
}

ENTRY_UNDEFINE_FLAGS_MAP = {
'managed_save': 1,
'snapshots_metadata': 2,
'nvram': 4,
'keep_nvram': 8,
'checkpoints_metadata': 16,
}

ALL_FLAGS = []
ALL_FLAGS.extend(ENTRY_UNDEFINE_FLAGS_MAP.keys())


class VMNotFound(Exception):
pass
Expand Down Expand Up @@ -198,8 +256,8 @@ def create(self, vmid):
def destroy(self, vmid):
return self.find_vm(vmid).destroy()

def undefine(self, vmid):
return self.find_vm(vmid).undefine()
def undefine(self, vmid, flag):
return self.find_vm(vmid).undefineFlags(flag)

def get_status2(self, vm):
state = vm.info()[0]
Expand Down Expand Up @@ -367,11 +425,11 @@ def destroy(self, vmid):
self.__get_conn()
return self.conn.destroy(vmid)

def undefine(self, vmid):
def undefine(self, vmid, flag):
""" Stop a domain, and then wipe it from the face of the earth. (delete disk/config file) """

self.__get_conn()
return self.conn.undefine(vmid)
return self.conn.undefine(vmid, flag)

def status(self, vmid):
"""
Expand Down Expand Up @@ -419,6 +477,8 @@ def core(module):
autostart = module.params.get('autostart', None)
guest = module.params.get('name', None)
command = module.params.get('command', None)
force = module.params.get('force', None)
flags = module.params.get('flags', None)
uri = module.params.get('uri', None)
xml = module.params.get('xml', None)

Expand Down Expand Up @@ -513,6 +573,30 @@ def core(module):

elif not guest:
module.fail_json(msg="%s requires 1 argument: guest" % command)

elif command == 'undefine':
# Use the undefine function with flag to also handle various metadata.
# This is especially important for UEFI enabled guests with nvram.
# Provide flag as an integer of all desired bits, see 'ENTRY_UNDEFINE_FLAGS_MAP'.
# Integer 23 takes care of all cases (23 = 1 + 2 + 4 + 16).
flag = 0
if flags is not None:
if force is True:
module.warn("Ignoring 'force', because 'flags' are provided.")
nv = ['nvram', 'keep_nvram']
# Check mutually exclusive flags
if set(nv) <= set(flags):
raise ValueError("Flags '%s' are mutually exclusive" % "' and '".join(nv))
for item in flags:
# Get and add flag integer from mapping, otherwise 0.
flag += ENTRY_UNDEFINE_FLAGS_MAP.get(item, 0)
elif force is True:
flag = 23
# Finally, execute with flag
res = getattr(v, command)(guest, flag)
if not isinstance(res, dict):
res = {command: res}

else:
res = getattr(v, command)(guest)
if not isinstance(res, dict):
Expand All @@ -539,6 +623,8 @@ def main():
state=dict(type='str', choices=['destroyed', 'paused', 'running', 'shutdown']),
autostart=dict(type='bool'),
command=dict(type='str', choices=ALL_COMMANDS),
flags=dict(type='list', elements='str', choices=ALL_FLAGS),
force=dict(type='bool'),
uri=dict(type='str', default='qemu:///system'),
xml=dict(type='str'),
),
Expand Down

0 comments on commit 9c04800

Please sign in to comment.