diff --git a/plugins/modules/proxmox_vm_info.py b/plugins/modules/proxmox_vm_info.py index 6713be0bbee..441c099dcfd 100644 --- a/plugins/modules/proxmox_vm_info.py +++ b/plugins/modules/proxmox_vm_info.py @@ -16,16 +16,19 @@ version_added: 7.2.0 description: - Retrieve information about one or more Proxmox VE virtual machines. -author: 'Sergei Antipov (@UnderGreen) ' +author: + - 'Sergei Antipov (@UnderGreen) ' + - Maryam Mayabi (@mmayabi) + - Orbsim (@orbsim) options: node: description: - Node where to get virtual machines info. - required: true type: str type: description: - Restrict results to a specific virtual machine(s) type. + required: true type: str choices: - all @@ -48,7 +51,15 @@ """ EXAMPLES = """ -- name: List all existing virtual machines on node +- name: List all existing QEMU virtual machines in proxmox cluster + community.general.proxmox_vm_info: + api_host: proxmoxhost + api_user: root@pam + api_token_id: '{{ token_id | default(omit) }}' + api_token_secret: '{{ token_secret | default(omit) }}' + type: qemu + +- name: List all existing virtual machines on node01 community.general.proxmox_vm_info: api_host: proxmoxhost api_user: root@pam @@ -56,7 +67,7 @@ api_token_secret: '{{ token_secret | default(omit) }}' node: node01 -- name: List all QEMU virtual machines on node +- name: List all QEMU virtual machines on node01 community.general.proxmox_vm_info: api_host: proxmoxhost api_user: root@pam @@ -69,11 +80,10 @@ api_host: proxmoxhost api_user: root@pam api_password: '{{ password | default(omit) }}' - node: node01 type: qemu vmid: 101 -- name: Retrieve information about specific VM by name +- name: Retrieve information about specific lxc by name community.general.proxmox_vm_info: api_host: proxmoxhost api_user: root@pam @@ -92,39 +102,124 @@ sample: [ { - "cpu": 0.258944410905281, - "cpus": 1, - "disk": 0, - "diskread": 0, - "diskwrite": 0, - "maxdisk": 34359738368, - "maxmem": 4294967296, - "mem": 35158379, - "name": "pxe.home.arpa", - "netin": 99715803, - "netout": 14237835, - "pid": 1947197, - "status": "running", - "type": "qemu", - "uptime": 135530, - "vmid": 100 + "balloon": 0, + "boot": "cd", + "bootdisk": "virtio0", + "cores": 8, + "cpu": "host", + "cpus": 8, + "description": "test vm", + "digest": "cf607ea96a24673100a1ab66a500e7adb421adea", + "disk": 0, + "diskread": 0, + "disks": [ + { + "media": "cdrom", + "name": "ide2" + }, + { + "name": "virtio0", + "path": "vm-102-disk-0.raw", + "size": "10G", + "storage": "local" + } + ], + "diskwrite": 0, + "maxdisk": 10737418240, + "maxmem": 8589934592, + "mem": 0, + "memory": 8192, + "meta": "creation-qemu=6.2.0,ctime=1669614540", + "name": "test1", + "netin": 0, + "netout": 0, + "nets": [ + { + "bridge": "vmbr0", + "mac": "6E:72:FF:28:E0:5A", + "model": "virtio", + "name": "net0", + "tag": "8" + } + ], + "node": "node02", + "numa": 1, + "ostype": "l26", + "scsihw": "virtio-scsi-pci", + "smbios1": "uuid=d72b666e-0765-4218-aee6-bbee16ba6a75", + "sockets": 1, + "status": "stopped", + "type": "qemu", + "uptime": 0, + "vcpus": 8, + "vmgenid": "308acf0b-2cdc-45bc-825b-7bc6c1f30d7b", + "vmid": 101 }, { - "cpu": 0, - "cpus": 1, - "disk": 0, - "diskread": 0, - "diskwrite": 0, - "maxdisk": 0, - "maxmem": 536870912, - "mem": 0, - "name": "test1", - "netin": 0, - "netout": 0, - "status": "stopped", - "type": "qemu", - "uptime": 0, - "vmid": 101 + "balloon": 0, + "bios": "seabios", + "boot": "cdn", + "bootdisk": "scsi0", + "cipassword": "**********", + "ciuser": "root", + "cores": 16, + "cpu": "host", + "cpus": 16, + "cpuunits": 1024, + "description": "this server created for test", + "digest": "a5bb492225c13d5fb3ea2e12ba1fb73aeaa80c3d", + "disk": 0, + "diskread": 0, + "disks": [ + { + "media": "cdrom", + "name": "ide2", + "path": "vm-100-cloudinit.qcow2", + "storage": "cloud" + }, + { + "name": "scsi0", + "path": "vm-100-disk-0.qcow2", + "size": "30G", + "storage": "local" + } + ], + "diskwrite": 0, + "ipconfig0": "ip=10.0.0.2/24,gw=10.1.1.1", + "keyboard": "en-us", + "kvm": 1, + "maxdisk": 32212254720, + "maxmem": 17179869184, + "mem": 1200450749, + "memory": 16384, + "meta": "creation-qemu=6.2.0,ctime=1689583658", + "name": "test2", + "nameserver": "8.8.8.8 4.4.4.4", + "netin": 79600081, + "netout": 15525264, + "nets": [ + { + "bridge": "br0", + "firewall": "0", + "mac": "28:08:FE:80:DE:A3", + "model": "virtio", + "name": "net0", + "tag": "100" + } + ], + "node": "node01", + "numa": 1, + "onboot": 1, + "pid": 1969629, + "scsihw": "virtio-scsi-pci", + "smbios1": "uuid=8204a99c-d4e7-4a7e-afb8-5f1fecc750a8", + "sockets": 1, + "sshkeys": "ssh-rsa%20AAAAB3Nz", + "status": "running", + "type": "qemu", + "uptime": 23972, + "vmgenid": "54c2917c-c1ae-9468-8934-db249f73aa36", + "vmid": 100 } ] """ @@ -134,15 +229,105 @@ proxmox_auth_argument_spec, ProxmoxAnsible, ) - +import re class ProxmoxVmInfoAnsible(ProxmoxAnsible): + disks = [] + nets = [] + def add_to_disks(self, name, value): + regex = "^.+:[0-9].+/.+\..+" + item = {} + item['name'] = name + try: + for data in value.split(','): + if not re.search(regex, data) and '=' not in data: + continue #e.g data is "none" ( "ide2": "none:media=cdrom" ) + if re.search(regex, data): + item['storage'] = data.split(':')[0] + item['path'] = data.split('/')[1] + else: + k = data.split('=')[0] + v = data.split('=')[1] + item[k] = v + self.disks.append(item) + except Exception: + return False + return True + + def add_to_nets(self, name, value): + item = {} + item['name'] = name + try: + for data in value.split(','): + if '=' in data: + k, v = data.split('=') + if k in ['e1000','virtio','rtl8139','vmxnet3']: + item['model'] = k + item['mac'] = v + else: + item[k] = v + self.nets.append(item) + except Exception: + return False + return True + + def vm_in_node(self, vmid, node): + try: + for vm in self.proxmox_api.nodes(node).qemu.get(): + if int(vm['vmid']) == vmid: + return True + except Exception as e: + self.module.fail_json(msg="Failed to retrieve QEMU VMs information: %s" % e) + return False + + def get_vm_node(self, vmid): + try: + for node in self.proxmox_api.nodes.get(): + node = node['node'] #node becomes a string + if self.vm_in_node(vmid, node): + return node + except Exception as e: + self.module.fail_json(msg="Failed to retrieve QEMU VMs Node Name: %s" % e) + + def get_vm_configs(self, node, vm): + disk_regex = "^ide[0-3]{1}$|^scsi(30|[12][0-9]|[0-9])$|^virtio(1[0-5]|[0-9])$|^sata[0-5]{1}$|^efidisk0$|^tpmstate0$|^unused(25[0-5]|[12][0-4][0-9]|[1-9]?[0-9])$" + net_regex = "^net(0[1-9]?|[1-9]?[0-9]*)$" + try: + vmcnf = self.proxmox_api.nodes(node).qemu(vm['vmid']).config.get() + vm.update(vmcnf) + vm['node'] = node #add node name to vm dict + dsw = nsw = False + #for key, val in vm.items(): + for key in list(vm): + val = vm[key] + if (re.search(disk_regex, key)): + self.add_to_disks(key, val) + del vm[key] + dsw = True + elif (re.search(net_regex, key)): + self.add_to_nets(key, val) + del vm[key] + nsw = True + elif key == "cpu" and isinstance(val, (int, float)): + vm['cpu'] = 'default' + if dsw: + vm['disks'] = self.disks #add disks list to vm dict + self.disks = [] + if nsw: + vm['nets'] = self.nets #add nets list to vm dict + self.nets = [] + return vm + except Exception as e: + self.module.fail_json(msg="Failed to retrieve QEMU VMs information: %s" % e) + def get_qemu_vms(self, node, vmid=None): + vms = [] try: - vms = self.proxmox_api.nodes(node).qemu().get() - for vm in vms: + vmlist = self.proxmox_api.nodes(node).qemu().get() + for vm in vmlist: vm["vmid"] = int(vm["vmid"]) vm["type"] = "qemu" + vms.append(self.get_vm_configs(node,vm)) if vmid is None: return vms return [vm for vm in vms if vm["vmid"] == vmid] @@ -164,7 +349,7 @@ def get_lxc_vms(self, node, vmid=None): def main(): module_args = proxmox_auth_argument_spec() vm_info_args = dict( - node=dict(type="str", required=True), + node=dict(type="str", required=False), type=dict( type="str", choices=["lxc", "qemu", "all"], default="all", required=False ), @@ -177,6 +362,7 @@ def main(): argument_spec=module_args, required_together=[("api_token_id", "api_token_secret")], required_one_of=[("api_password", "api_token_id")], + mutually_exclusive=[('node', 'vmid', 'vmname')], supports_check_mode=True, ) @@ -188,21 +374,26 @@ def main(): result = dict(changed=False) - if proxmox.get_node(node) is None: + if node and (proxmox.get_node(node) is None): module.fail_json(msg="Node %s doesn't exist in PVE cluster" % node) if not vmid and name: vmid = int(proxmox.get_vmid(name, ignore_missing=False)) - vms = None + vms = [] if type == "lxc": vms = proxmox.get_lxc_vms(node, vmid=vmid) elif type == "qemu": - vms = proxmox.get_qemu_vms(node, vmid=vmid) - else: - vms = proxmox.get_qemu_vms(node, vmid=vmid) + proxmox.get_lxc_vms( - node, vmid=vmid - ) + if node: + vms = proxmox.get_qemu_vms(node=node) + elif vmid or name: + vms = proxmox.get_qemu_vms(node=proxmox.get_vm_node(vmid),vmid=vmid) + else: + for node in proxmox.proxmox_api.nodes.get(): + node = node['node'] + nodevms = proxmox.get_qemu_vms(node=node) + if nodevms: + vms = vms + nodevms if vms or vmid is None: result["proxmox_vms"] = vms