Repository: ansible-collections/community.general

Issue:
proxmox_kvm: Creating VM on ZFS store fails
### Summary

I'm trying to create a new Proxmox VM (or ensure that it exists) using Ansible. The target Proxmox instance uses ZFS to store disks/isos/dumps etc. VM creation fails because device info string for ZFS-devices appears to be getting parsed incorrectly (see traceback below).

### Issue Type

Bug Report

### Component Name

proxmox_kvm

### Ansible Version

```
$ ansible --version
ansible 2.10.8
  config file = None
  configured module search path = ['/home/anupc/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110]
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
```


### OS / Environment

Target OS: Proxmox VE

```
# uname -a
Linux bigpve 5.4.106-1-pve #1 SMP PVE 5.4.106-1 (Fri, 19 Mar 2021 11:08:47 +0100) x86_64 GNU/Linux
# cat /etc/debian_version 
10.9
```

### Steps to Reproduce

```
$ cat vms.yml 
---
- name: Deploy VMs
  hosts: bigpve

  tasks:
  - name: Create new minimal VM
    community.general.proxmox_kvm:
      api_user: root@pam
      api_password: "<redacted>"
      api_host: bigpve
      name: testvm
      node: bigpve
      proxmox_default_behavior: no_defaults
      vmid: 1001
      scsihw: virtio-scsi-pci
      scsi:
        scsi0: "local-zfs:vm-1001-disk-0"
      net:
        net0: 'virtio,bridge=vmbr0'
      bootdisk: scsi0
```


### Expected Results

Expected task to complete successfully with a new VM created.

### Actual Results

A VM is created which appears to have all the right parameters set. But the task fails with the following output while trying to process VM info:
```
The full traceback is:
  File "/tmp/ansible_community.general.proxmox_kvm_payload_mqneu62g/ansible_community.general.proxmox_kvm_payload.zip/ansible_collections/community/general/plugins/modules/proxmox_kvm.py", line 1300, in main
  File "/tmp/ansible_community.general.proxmox_kvm_payload_mqneu62g/ansible_community.general.proxmox_kvm_payload.zip/ansible_collections/community/general/plugins/modules/proxmox_kvm.py", line 830, in get_vminfo
fatal: [bigpve]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "acpi": null,
            "agent": null,
            "api_host": "bigpve",
            "api_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "api_token_id": null,
            "api_token_secret": null,
            "api_user": "root@pam",
            "args": null,
            "autostart": null,
            "balloon": null,
            "bios": null,
            "boot": null,
            "bootdisk": "scsi0",
            "cicustom": null,
            "cipassword": null,
            "citype": null,
            "ciuser": null,
            "clone": null,
            "cores": null,
            "cpu": null,
            "cpulimit": null,
            "cpuunits": null,
            "delete": null,
            "description": null,
            "digest": null,
            "force": null,
            "format": null,
            "freeze": null,
            "full": true,
            "hostpci": null,
            "hotplug": null,
            "hugepages": null,
            "ide": null,
            "ipconfig": null,
            "keyboard": null,
            "kvm": null,
            "localtime": null,
            "lock": null,
            "machine": null,
            "memory": null,
            "migrate_downtime": null,
            "migrate_speed": null,
            "name": "testvm",
            "net": {
                "net0": "virtio,bridge=vmbr0"
            },
            "newid": null,
            "node": "bigpve",
            "numa": null,
            "numa_enabled": null,
            "onboot": null,
            "ostype": null,
            "parallel": null,
            "pool": null,
            "protection": null,
            "proxmox_default_behavior": "no_defaults",
            "reboot": null,
            "revert": null,
            "sata": null,
            "scsi": {
                "scsi0": "local-zfs:vm-1001-disk-0"
            },
            "scsihw": "virtio-scsi-pci",
            "serial": null,
            "shares": null,
            "skiplock": null,
            "smbios": null,
            "snapname": null,
            "sockets": null,
            "sshkeys": null,
            "startdate": null,
            "startup": null,
            "state": "present",
            "storage": null,
            "tablet": null,
            "tags": null,
            "target": null,
            "tdf": null,
            "template": null,
            "timeout": 30,
            "update": false,
            "validate_certs": false,
            "vcpus": null,
            "vga": null,
            "virtio": null,
            "vmid": 1001,
            "watchdog": null
        }
    },
    "msg": "creation of qemu VM testvm with vmid 1001 failed with exception='NoneType' object has no attribute 'group'",
    "vmid": 1001
}
```

This prevents any subsequent tasks in the playbook from running.

Rerunning the playbook after the VM has been created doesn't cause any error.

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:

Resolving patch:
diff --git a/plugins/modules/cloud/misc/proxmox_kvm.py b/plugins/modules/cloud/misc/proxmox_kvm.py
--- a/plugins/modules/cloud/misc/proxmox_kvm.py
+++ b/plugins/modules/cloud/misc/proxmox_kvm.py
@@ -818,23 +818,25 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs):
     # Split information by type
     re_net = re.compile(r'net[0-9]')
     re_dev = re.compile(r'(virtio|ide|scsi|sata)[0-9]')
-    for k, v in kwargs.items():
+    for k in kwargs.keys():
         if re_net.match(k):
-            interface = k
-            k = vm[k]
-            k = re.search('=(.*?),', k).group(1)
-            mac[interface] = k
+            mac[k] = parse_mac(vm[k])
         elif re_dev.match(k):
-            device = k
-            k = vm[k]
-            k = re.search('(.*?),', k).group(1)
-            devices[device] = k
+            devices[k] = parse_dev(vm[k])
 
     results['mac'] = mac
     results['devices'] = devices
     results['vmid'] = int(vmid)
 
 
+def parse_mac(netstr):
+    return re.search('=(.*?),', netstr).group(1)
+
+
+def parse_dev(devstr):
+    return re.search('(.*?)(,|$)', devstr).group(1)
+
+
 def settings(proxmox, vmid, node, **kwargs):
     proxmox_node = proxmox.nodes(node)
 


Test patch:
diff --git a/tests/unit/plugins/modules/cloud/misc/test_proxmox_kvm.py b/tests/unit/plugins/modules/cloud/misc/test_proxmox_kvm.py
new file mode 100644
--- /dev/null
+++ b/tests/unit/plugins/modules/cloud/misc/test_proxmox_kvm.py
@@ -0,0 +1,17 @@
+# Copyright: (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible_collections.community.general.plugins.modules.cloud.misc.proxmox_kvm import parse_dev, parse_mac
+
+
+def test_parse_mac():
+    assert parse_mac('virtio=00:11:22:AA:BB:CC,bridge=vmbr0,firewall=1') == '00:11:22:AA:BB:CC'
+
+
+def test_parse_dev():
+    assert parse_dev('local-lvm:vm-1000-disk-0,format=qcow2') == 'local-lvm:vm-1000-disk-0'
+    assert parse_dev('local-lvm:vm-101-disk-1,size=8G') == 'local-lvm:vm-101-disk-1'
+    assert parse_dev('local-zfs:vm-1001-disk-0') == 'local-zfs:vm-1001-disk-0'