Skip to content

Commit 00fb361

Browse files
implement the removal of volumes when a domain is undefined (ansible-collections#191)
* implement the removal of volumes when a domain is undefined * pep8 fix * pep8 fix Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru> * adding integration tests * updated integration test for base image test * merged main with the delete_disk --------- Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
1 parent 6587071 commit 00fb361

File tree

7 files changed

+233
-4
lines changed

7 files changed

+233
-4
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
minor_changes:
2+
- virt - implement the removal of volumes for a dom as per FR https://github.com/ansible-collections/community.libvirt/issues/177

plugins/modules/virt.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
- Manages virtual machines supported by I(libvirt).
1919
options:
2020
flags:
21-
choices: [ 'managed_save', 'snapshots_metadata', 'nvram', 'keep_nvram', 'checkpoints_metadata']
21+
choices: [ 'managed_save', 'snapshots_metadata', 'nvram', 'keep_nvram', 'checkpoints_metadata', 'delete_volumes']
2222
description:
2323
- Pass additional parameters.
2424
- Currently only implemented with command C(undefine).
@@ -201,6 +201,7 @@
201201
'nvram': 4,
202202
'keep_nvram': 8,
203203
'checkpoints_metadata': 16,
204+
'delete_volumes': 32,
204205
}
205206

206207
MUTATE_FLAGS = ['ADD_UUID', 'ADD_MAC_ADDRESSES', 'ADD_MAC_ADDRESSES_FUZZY']
@@ -273,7 +274,10 @@ def destroy(self, vmid):
273274
return self.find_vm(vmid).destroy()
274275

275276
def undefine(self, vmid, flag):
276-
return self.find_vm(vmid).undefineFlags(flag)
277+
vm = self.find_vm(vmid)
278+
if flag & 32:
279+
self.delete_domain_volumes(vmid)
280+
return vm.undefineFlags(flag)
277281

278282
def get_status2(self, vm):
279283
state = vm.info()[0]
@@ -350,6 +354,16 @@ def get_interfaces(self, vmid):
350354
interfaces_dict['network_interfaces'].update({"interface_{0}".format(interface_counter): interface_info})
351355
return interfaces_dict
352356

357+
def delete_domain_volumes(self, vmid):
358+
dom_xml = self.get_xml(vmid)
359+
root = etree.fromstring(dom_xml)
360+
disk_objects = root.findall(".//disk[@type='file']/source")
361+
for disk in disk_objects:
362+
disk_path = disk.get('file')
363+
disk_volumes = self.conn.storageVolLookupByPath(disk_path)
364+
if disk_volumes:
365+
disk_volumes.delete()
366+
353367

354368
class Virt(object):
355369

@@ -553,6 +567,10 @@ def get_interfaces(self, vmid):
553567
self.__get_conn()
554568
return self.conn.get_interfaces(vmid)
555569

570+
def delete_domain_volumes(self, vmid):
571+
self.__get_conn()
572+
return self.conn.delete_domain_volumes(vmid)
573+
556574

557575
# A dict of interface types (found in their `type` attribute) to the
558576
# corresponding "source" attribute name of their <source> elements
@@ -836,7 +854,7 @@ def exec_virt(*args):
836854
# Use the undefine function with flag to also handle various metadata.
837855
# This is especially important for UEFI enabled guests with nvram.
838856
# Provide flag as an integer of all desired bits, see 'ENTRY_UNDEFINE_FLAGS_MAP'.
839-
# Integer 23 takes care of all cases (23 = 1 + 2 + 4 + 16).
857+
# Integer 55 takes care of all cases (55 = 1 + 2 + 4 + 16 + 32). flag = 0
840858
flag = 0
841859
if flags is not None:
842860
if force is True:
@@ -849,7 +867,7 @@ def exec_virt(*args):
849867
# Get and add flag integer from mapping, otherwise 0.
850868
flag += ENTRY_UNDEFINE_FLAGS_MAP.get(item, 0)
851869
elif force is True:
852-
flag = 23
870+
flag = 55
853871
# Finally, execute with flag
854872
res = exec_virt(guest, flag)
855873

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
vm_name: fedora-test
3+
vm_ram_mb: 256
4+
vm_vcpus: 1
5+
libvirt_disk_path: "/var/lib/libvirt/images"
6+
libvirt_disk_size: "1"
7+
libvirt_pool_name: "default"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
FROM fedora:latest
2+
3+
RUN dnf install @virtualization -y
4+
5+
RUN dnf update -y && \
6+
dnf install -y \
7+
bridge-utils \
8+
dmidecode \
9+
dnsmasq \
10+
ebtables \
11+
iproute \
12+
iptables \
13+
edk2-ovmf \
14+
qemu-kvm \
15+
tini \
16+
python3-pip \
17+
python3-libvirt \
18+
python3-lxml \
19+
&& \
20+
dnf clean all
21+
22+
RUN ln -s /usr/bin/qemu-system-x86_64 /usr/libexec/qemu-kvm
23+
24+
RUN sed -i '/^#stdio_handler/ a\stdio_handler = "file"' /etc/libvirt/qemu.conf
25+
26+
COPY config/pools/* /etc/libvirt/storage/
27+
COPY config/networks/* /etc/libvirt/qemu/networks/
28+
RUN mkdir -p /etc/libvirt/storage/autostart /etc/libvirt/qemu/networks/autostart && \
29+
for pool in /etc/libvirt/storage/*.xml; do \
30+
ln -sf "../${pool##*/}" /etc/libvirt/storage/autostart/; \
31+
done && \
32+
for net in /etc/libvirt/qemu/networks/*.xml; do \
33+
ln -sf "../${net##*/}" /etc/libvirt/qemu/networks/autostart/; \
34+
done
35+
36+
CMD ["/usr/bin/tini", "/usr/sbin/libvirtd"]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
FROM ubuntu:24.04
2+
3+
RUN export DEBIAN_FRONTEND=noninteractive && \
4+
apt-get update && \
5+
apt-get -y install \
6+
bridge-utils \
7+
dmidecode \
8+
dnsmasq \
9+
ebtables \
10+
iproute2 \
11+
iptables \
12+
libvirt-clients \
13+
libvirt-daemon-system \
14+
ovmf \
15+
# qemu-efi \
16+
qemu-kvm \
17+
tini \
18+
&& \
19+
apt-get clean
20+
21+
RUN apt install python3-pip python3-libvirt python3-lxml -y
22+
23+
RUN ln -s /usr/bin/qemu-system-amd64 /usr/libexec/qemu-kvm
24+
25+
RUN sed -i '/^#stdio_handler/ a\stdio_handler = "file"' /etc/libvirt/qemu.conf
26+
27+
COPY config/pools/* /etc/libvirt/storage/
28+
COPY config/networks/* /etc/libvirt/qemu/networks/
29+
RUN mkdir -p /etc/libvirt/storage/autostart /etc/libvirt/qemu/networks/autostart && \
30+
for pool in /etc/libvirt/storage/*.xml; do \
31+
ln -sf "../${pool##*/}" /etc/libvirt/storage/autostart/; \
32+
done && \
33+
for net in /etc/libvirt/qemu/networks/*.xml; do \
34+
ln -sf "../${net##*/}" /etc/libvirt/qemu/networks/autostart/; \
35+
done
36+
37+
CMD ["/usr/bin/tini", "/usr/sbin/libvirtd"]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<domain type='kvm'>
2+
<name>{{ vm_name }}</name>
3+
<memory unit='MiB'>{{ vm_ram_mb }}</memory>
4+
<vcpu placement='static'>{{ vm_vcpus }}</vcpu>
5+
<os>
6+
<type arch='x86_64' machine='q35'>hvm</type>
7+
<boot dev='hd'/>
8+
</os>
9+
<cpu mode="host-passthrough" check="none" migratable="on"/>
10+
<on_poweroff>destroy</on_poweroff>
11+
<on_reboot>restart</on_reboot>
12+
<on_crash>destroy</on_crash>
13+
<clock offset="utc">
14+
<timer name="rtc" tickpolicy="catchup"/>
15+
<timer name="pit" tickpolicy="delay"/>
16+
<timer name="hpet" present="no"/>
17+
</clock>
18+
<devices>
19+
<emulator>/usr/libexec/qemu-kvm</emulator>
20+
<disk type='file' device='disk'>
21+
<driver name='qemu' type='qcow2'/>
22+
<source file='{{ libvirt_disk_path }}/{{ vm_name }}.qcow2'/>
23+
<target dev='vda' bus='virtio'/>
24+
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
25+
</disk>
26+
<channel type="unix">
27+
<target type="virtio" name="org.qemu.guest_agent.0"/>
28+
<address type="virtio-serial" controller="0" bus="0" port="1"/>
29+
</channel>
30+
<input type='tablet' bus='usb'>
31+
<address type='usb' bus='0' port='1'/>
32+
</input>
33+
<input type='mouse' bus='ps2'/>
34+
<input type='keyboard' bus='ps2'/>
35+
<graphics type="vnc" port="-1" autoport="yes">
36+
<listen type="address"/>
37+
</graphics>
38+
<video>
39+
<model type="virtio" heads="1" primary="yes"/>
40+
<address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0"/>
41+
</video>
42+
<memballoon model='virtio'>
43+
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
44+
</memballoon>
45+
<rng model='virtio'>
46+
<backend model='random'>/dev/urandom</backend>
47+
<address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/>
48+
</rng>
49+
</devices>
50+
</domain>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
- name: Create VM XML definition
3+
template:
4+
src: files/vm_template.xml.j2
5+
dest: "/tmp/{{ vm_name }}.xml"
6+
7+
- name: Define VM
8+
community.libvirt.virt:
9+
command: define
10+
xml: "{{ lookup('file', '/tmp/' + vm_name + '.xml') }}"
11+
uri: qemu:///system
12+
13+
- name: Create the disk image file for base image
14+
ansible.builtin.command: "qemu-img create -f qcow2 {{ libvirt_disk_path }}/{{ vm_name }}_copy.qcow2 {{ libvirt_disk_size }}G"
15+
16+
- name: Copy the base image for another vm
17+
ansible.builtin.command: "qemu-img create -f qcow2 -F qcow2 -b {{ libvirt_disk_path }}/{{ vm_name }}_copy.qcow2 {{ libvirt_disk_path }}/{{ vm_name }}.qcow2"
18+
19+
- name: Validate the VM
20+
community.libvirt.virt:
21+
command: list_vms
22+
register: vm_list
23+
24+
- name: Show VMs
25+
debug:
26+
var: vm_list
27+
28+
- name: Check if file exists
29+
stat:
30+
path: "{{ libvirt_disk_path }}/{{ vm_name }}.qcow2"
31+
register: file_status
32+
33+
- name: Assert file exists
34+
assert:
35+
that:
36+
- file_status.stat.exists
37+
fail_msg: "QCOW file does not exist"
38+
success_msg: "File exists as expected"
39+
40+
41+
- name: Refresh the storage pool
42+
community.libvirt.virt_pool:
43+
command: refresh
44+
name: "{{ libvirt_pool_name }}"
45+
46+
- name: Destroy VMs
47+
community.libvirt.virt:
48+
state: destroyed
49+
name: "{{ vm_name }}"
50+
51+
- name: remove and undefine vm
52+
community.libvirt.virt:
53+
command: undefine
54+
name: "{{ vm_name }}"
55+
flags: managed_save,snapshots_metadata,delete_volumes
56+
57+
- name: Check if file exists
58+
stat:
59+
path: "{{ libvirt_disk_path }}/{{ vm_name }}.qcow2"
60+
register: file_status
61+
62+
- name: Assert file does not exist
63+
assert:
64+
that:
65+
- not file_status.stat.exists
66+
success_msg: "QCOW file does not exist"
67+
fail_msg: "File exists... failed to remove"
68+
69+
- name: Check if base image still exists
70+
stat:
71+
path: "{{ libvirt_disk_path }}/{{ vm_name }}_copy.qcow2"
72+
register: base_image
73+
74+
- name: Assert base image still exists
75+
assert:
76+
that:
77+
- base_image.stat.exists
78+
success_msg: "BASE QCOW file still exists"
79+
fail_msg: "Base Image Missing."

0 commit comments

Comments
 (0)