From 22e6546396b3e8a90ad226db1729b2f41a2415ff Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Sun, 5 Dec 2021 21:47:23 +0100 Subject: [PATCH] fix: avoid recreation of storage devices during VM update --- plugins/modules/proxmox_kvm.py | 86 +++++++++++++++++----------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/plugins/modules/proxmox_kvm.py b/plugins/modules/proxmox_kvm.py index 4f6779d..f671ab5 100644 --- a/plugins/modules/proxmox_kvm.py +++ b/plugins/modules/proxmox_kvm.py @@ -439,8 +439,6 @@ options: update: description: - If C(yes), the VM will be updated with new value. - - Cause of the operations of the API and security reasons, I have disabled the update of the following parameters - - C(net, virtio, ide, sata, scsi). Per example updating C(net) update the MAC address and C(virtio) create always new disk... - Update of C(pool) is disabled. It needs an additional API endpoint not covered by this module. type: bool default: 'no' @@ -734,10 +732,12 @@ msg: """ import re +import string import time import traceback from distutils.version import LooseVersion from ansible.module_utils.six.moves.urllib.parse import quote +from collections import defaultdict try: from proxmoxer import ProxmoxAPI @@ -778,7 +778,6 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs): global results # noqa results = {} mac = {} - devices = {} try: vm = proxmox.nodes(node).qemu(vmid).config.get() except Exception as e: @@ -796,25 +795,10 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs): kwargs.update(kwargs[k]) del kwargs[k] - # Split information by type - for k, v in kwargs.items(): - if re.match(r"net[0-9]", k) is not None: - interface = k - k = vm[k] - k = re.search("=(.*?),", k).group(1) - mac[interface] = k - if ( - re.match(r"virtio[0-9]", k) is not None or re.match(r"ide[0-9]", k) is not None - or re.match(r"scsi[0-9]", k) is not None or re.match(r"sata[0-9]", k) is not None - ): - device = k - k = vm[k] - k = re.search("(.*?),", k).group(1) - devices[device] = k - results["mac"] = mac - results["devices"] = devices + results["devices"] = _extract_devices(vm) results["vmid"] = int(vmid) + results["_raw"] = vm def settings(module, proxmox, vmid, node, name, **kwargs): @@ -882,18 +866,17 @@ def create_vm( urlencoded_ssh_keys = quote(kwargs["sshkeys"], safe="") kwargs["sshkeys"] = str(urlencoded_ssh_keys) - # If update, don't update disk (virtio, ide, sata, scsi) and network interface. - # Pool parameter not supported by qemu//config endpoint on "update" (PVE 6.2), - # only with "create" + for item in [kwargs[i] for i in ["scsi", "virtio", "ide", "sata"] if i in kwargs]: + devices = _extract_devices(item) + + # If update, ensure existing disks are not recreated. if update: - if "virtio" in kwargs: - del kwargs["virtio"] - if "sata" in kwargs: - del kwargs["sata"] - if "scsi" in kwargs: - del kwargs["scsi"] - if "ide" in kwargs: - del kwargs["ide"] + for k, v in devices.items(): + if results["devices"].get(k): + kwargs[k.rstrip(string.digits)][k] = "{0}:{1},{2}".format( + results["devices"][k]["storage_id"], results["devices"][k]["storage_opts"], + ",".join(devices[k]["opts"]) + ) # Convert all dict in kwargs to elements. for k in list(kwargs.keys()): @@ -1281,6 +1264,19 @@ def main(): elif not node_check(proxmox, node): module.fail_json(msg="node '{}' does not exist in cluster".format(node)) + if not clone: + get_vminfo( + module, + proxmox, + node, + vmid, + ide=module.params["ide"], + net=module.params["net"], + sata=module.params["sata"], + scsi=module.params["scsi"], + virtio=module.params["virtio"] + ) + create_vm( module, proxmox, @@ -1353,18 +1349,6 @@ def main(): watchdog=module.params["watchdog"] ) - if not clone: - get_vminfo( - module, - proxmox, - node, - vmid, - ide=module.params["ide"], - net=module.params["net"], - sata=module.params["sata"], - scsi=module.params["scsi"], - virtio=module.params["virtio"] - ) if update: module.exit_json( changed=True, vmid=vmid, msg="VM {} with vmid {} updated".format(name, vmid) @@ -1544,5 +1528,21 @@ def main(): ) +def _extract_devices(item): + devices = defaultdict(dict) + for k, v in item.items(): + if re.match(r"(scsi|virtio|ide|sata)[0-9]", k): + devices[k]["opts"] = [] + for val in v.split(","): + if len(val.split(":")) == 2: + storage = val.split(":") + devices[k]["storage_id"] = storage[0] + devices[k]["storage_opts"] = storage[1] + else: + devices[k]["opts"].append(val) + + return devices + + if __name__ == "__main__": main()