diff --git a/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml b/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml new file mode 100644 index 0000000000..0ef97f20ed --- /dev/null +++ b/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml @@ -0,0 +1,3 @@ +--- +minor_changes: +- proxmox inventory plugin - added ``proxmox_agent_interfaces`` fact describing network interfaces returned from a QEMU guest agent (https://github.com/ansible-collections/community.general/pull/2148). diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index 3e44dd1ddd..036c3dc7bf 100644 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -224,6 +224,29 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): except Exception: return None + def _get_agent_network_interfaces(self, node, vmid, vmtype): + result = [] + + try: + ifaces = self._get_json( + "%s/api2/json/nodes/%s/%s/%s/agent/network-get-interfaces" % ( + self.proxmox_url, node, vmtype, vmid + ) + )['result'] + + for iface in ifaces: + result.append({ + 'name': iface['name'], + 'mac-address': iface['hardware-address'], + 'ip-addresses': [ + "%s/%s" % (ip['ip-address'], ip['prefix']) for ip in iface['ip-addresses'] + ] + }) + except requests.HTTPError: + pass + + return result + def _get_vm_config(self, node, vmid, vmtype, name): ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/config" % (self.proxmox_url, node, vmtype, vmid)) @@ -258,6 +281,12 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): parsed_value = [tag.strip() for tag in value.split(",")] self.inventory.set_variable(name, parsed_key, parsed_value) + if config == 'agent' and int(value): + agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces")) + agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype) + if agent_iface_value: + self.inventory.set_variable(name, agent_iface_key, agent_iface_value) + if not (isinstance(value, int) or ',' not in value): # split off strings with commas to a dict # skip over any keys that cannot be processed diff --git a/tests/unit/plugins/inventory/test_proxmox.py b/tests/unit/plugins/inventory/test_proxmox.py index 036c8e5938..ee6c0e2963 100644 --- a/tests/unit/plugins/inventory/test_proxmox.py +++ b/tests/unit/plugins/inventory/test_proxmox.py @@ -71,8 +71,7 @@ def get_json(url): "status": "running", "vmid": "100", "disk": "1000", - "uptime": 1000, - "tags": "test, tags, here"}] + "uptime": 1000}] elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu": # _get_qemu_per_node return [{"name": "test-qemu", @@ -106,8 +105,7 @@ def get_json(url): "vmid": "9001", "uptime": 0, "disk": 0, - "status": "stopped", - "tags": "test, tags, here"}] + "status": "stopped"}] elif url == "https://localhost:8006/api2/json/pools/test": # _get_members_per_pool return {"members": [{"uptime": 1000, @@ -164,6 +162,125 @@ def get_json(url): "method6": "manual", "autostart": 1, "active": 1}] + elif url == "https://localhost:8006/api2/json/nodes/testnode/lxc/100/config": + # _get_vm_config (lxc) + return { + "console": 1, + "rootfs": "local-lvm:vm-100-disk-0,size=4G", + "cmode": "tty", + "description": "A testnode", + "cores": 1, + "hostname": "test-lxc", + "arch": "amd64", + "tty": 2, + "swap": 0, + "cpulimit": "0", + "net0": "name=eth0,bridge=vmbr0,gw=10.1.1.1,hwaddr=FF:FF:FF:FF:FF:FF,ip=10.1.1.3/24,type=veth", + "ostype": "ubuntu", + "digest": "123456789abcdef0123456789abcdef01234567890", + "protection": 0, + "memory": 1000, + "onboot": 0, + "cpuunits": 1024, + "tags": "one, two, three", + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/101/config": + # _get_vm_config (qemu) + return { + "tags": "one, two, three", + "cores": 1, + "ide2": "none,media=cdrom", + "memory": 1000, + "kvm": 1, + "digest": "0123456789abcdef0123456789abcdef0123456789", + "description": "A test qemu", + "sockets": 1, + "onboot": 1, + "vmgenid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "numa": 0, + "bootdisk": "scsi0", + "cpu": "host", + "name": "test-qemu", + "ostype": "l26", + "hotplug": "network,disk,usb", + "scsi0": "local-lvm:vm-101-disk-0,size=8G", + "net0": "virtio=ff:ff:ff:ff:ff:ff,bridge=vmbr0,firewall=1", + "agent": "1", + "bios": "seabios", + "ide0": "local-lvm:vm-101-cloudinit,media=cdrom,size=4M", + "boot": "cdn", + "scsihw": "virtio-scsi-pci", + "smbios1": "uuid=ffffffff-ffff-ffff-ffff-ffffffffffff" + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/101/agent/network-get-interfaces": + # _get_agent_network_interfaces + return {"result": [ + { + "hardware-address": "00:00:00:00:00:00", + "ip-addresses": [ + { + "prefix": 8, + "ip-address-type": "ipv4", + "ip-address": "127.0.0.1" + }, + { + "ip-address-type": "ipv6", + "ip-address": "::1", + "prefix": 128 + }], + "statistics": { + "rx-errs": 0, + "rx-bytes": 163244, + "rx-packets": 1623, + "rx-dropped": 0, + "tx-dropped": 0, + "tx-packets": 1623, + "tx-bytes": 163244, + "tx-errs": 0}, + "name": "lo"}, + { + "statistics": { + "rx-packets": 4025, + "rx-dropped": 12, + "rx-bytes": 324105, + "rx-errs": 0, + "tx-errs": 0, + "tx-bytes": 368860, + "tx-packets": 3479, + "tx-dropped": 0}, + "name": "eth0", + "ip-addresses": [ + { + "prefix": 24, + "ip-address-type": "ipv4", + "ip-address": "10.1.2.3" + }, + { + "prefix": 64, + "ip-address": "fd8c:4687:e88d:1be3:5b70:7b88:c79c:293", + "ip-address-type": "ipv6" + }], + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "ip-addresses": [ + { + "prefix": 16, + "ip-address": "10.10.2.3", + "ip-address-type": "ipv4" + }], + "name": "docker0", + "statistics": { + "rx-bytes": 0, + "rx-errs": 0, + "rx-dropped": 0, + "rx-packets": 0, + "tx-packets": 0, + "tx-dropped": 0, + "tx-errs": 0, + "tx-bytes": 0 + }}]} def get_vm_status(node, vmtype, vmid, name): @@ -173,6 +290,10 @@ def get_vm_status(node, vmtype, vmid, name): def get_option(option): if option == 'group_prefix': return 'proxmox_' + if option == 'facts_prefix': + return 'proxmox_' + elif option == 'want_facts': + return True else: return False @@ -201,6 +322,9 @@ def test_populate(inventory, mocker): group_qemu = inventory.inventory.groups['proxmox_pool_test'] assert group_qemu.hosts == [host_qemu] + # check if qemu-test has eth0 interface in agent_interfaces fact + assert 'eth0' in [d['name'] for d in host_qemu.get_vars()['proxmox_agent_interfaces']] + # check if lxc-test has been discovered correctly group_lxc = inventory.inventory.groups['proxmox_all_lxc'] assert group_lxc.hosts == [host_lxc]