From d05932fb2cf0bcb81041e8df3b8819ee201424c0 Mon Sep 17 00:00:00 2001 From: IamLunchbox <56757745+IamLunchbox@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:23:33 +0100 Subject: [PATCH] Add option to proxmox dynamic inventory to exclude nodes (#7461) * Create option to exclude proxmox nodes * improve node exclusion by only remove the top level group * add fragment * Update changelogs/fragments/7437-proxmox-inventory-add-exclude-nodes.yaml Co-authored-by: Felix Fontein * Update plugins/inventory/proxmox.py Co-authored-by: Felix Fontein * Rework node exclusion * Update fragement PR number * include release version in option Co-authored-by: Felix Fontein * Clarify description * Update unit test * Fix typos in unit test * Fix additonal typos in test * Fix CI * Fixing yet another whitespace pep error --------- Co-authored-by: Felix Fontein --- ...1-proxmox-inventory-add-exclude-nodes.yaml | 2 + plugins/inventory/proxmox.py | 24 ++++++---- tests/unit/plugins/inventory/test_proxmox.py | 45 ++++++++++++++++++- 3 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 changelogs/fragments/7461-proxmox-inventory-add-exclude-nodes.yaml diff --git a/changelogs/fragments/7461-proxmox-inventory-add-exclude-nodes.yaml b/changelogs/fragments/7461-proxmox-inventory-add-exclude-nodes.yaml new file mode 100644 index 00000000000..40391342f7e --- /dev/null +++ b/changelogs/fragments/7461-proxmox-inventory-add-exclude-nodes.yaml @@ -0,0 +1,2 @@ +minor_changes: + - proxmox inventory plugin - adds an option to exclude nodes from the dynamic inventory generation. The new setting is optional, not using this option will behave as usual (https://github.com/ansible-collections/community.general/issues/6714, https://github.com/ansible-collections/community.general/pull/7461). diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index c6f70a45665..df593665cf2 100644 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -116,6 +116,11 @@ - The default of this option changed from V(true) to V(false) in community.general 6.0.0. type: bool default: false + exclude_nodes: + description: Exclude proxmox nodes and the nodes-group from the inventory output. + type: bool + default: false + version_added: 8.1.0 filters: version_added: 4.6.0 description: A list of Jinja templates that allow filtering hosts. @@ -565,9 +570,9 @@ def _populate(self): for group in default_groups: self.inventory.add_group(self._group('all_%s' % (group))) - nodes_group = self._group('nodes') - self.inventory.add_group(nodes_group) + if not self.exclude_nodes: + self.inventory.add_group(nodes_group) want_proxmox_nodes_ansible_host = self.get_option("want_proxmox_nodes_ansible_host") @@ -577,22 +582,23 @@ def _populate(self): for node in self._get_nodes(): if not node.get('node'): continue - - self.inventory.add_host(node['node']) - if node['type'] == 'node': + if not self.exclude_nodes: + self.inventory.add_host(node['node']) + if node['type'] == 'node' and not self.exclude_nodes: self.inventory.add_child(nodes_group, node['node']) if node['status'] == 'offline': continue # get node IP address - if want_proxmox_nodes_ansible_host: + if want_proxmox_nodes_ansible_host and not self.exclude_nodes: ip = self._get_node_ip(node['node']) self.inventory.set_variable(node['node'], 'ansible_host', ip) # Setting composite variables - variables = self.inventory.get_host(node['node']).get_vars() - self._set_composite_vars(self.get_option('compose'), variables, node['node'], strict=self.strict) + if not self.exclude_nodes: + variables = self.inventory.get_host(node['node']).get_vars() + self._set_composite_vars(self.get_option('compose'), variables, node['node'], strict=self.strict) # add LXC/Qemu groups for the node for ittype in ('lxc', 'qemu'): @@ -635,8 +641,8 @@ def parse(self, inventory, loader, path, cache=True): if self.get_option('qemu_extended_statuses') and not self.get_option('want_facts'): raise AnsibleError('You must set want_facts to True if you want to use qemu_extended_statuses.') - # read rest of options + self.exclude_nodes = self.get_option('exclude_nodes') self.cache_key = self.get_cache_key(path) self.use_cache = cache and self.get_option('cache') self.host_filters = self.get_option('filters') diff --git a/tests/unit/plugins/inventory/test_proxmox.py b/tests/unit/plugins/inventory/test_proxmox.py index 13832c9387b..ea6c84bcdaa 100644 --- a/tests/unit/plugins/inventory/test_proxmox.py +++ b/tests/unit/plugins/inventory/test_proxmox.py @@ -646,13 +646,15 @@ def test_populate(inventory, mocker): inventory.group_prefix = 'proxmox_' inventory.facts_prefix = 'proxmox_' inventory.strict = False + inventory.exclude_nodes = False opts = { 'group_prefix': 'proxmox_', 'facts_prefix': 'proxmox_', 'want_facts': True, 'want_proxmox_nodes_ansible_host': True, - 'qemu_extended_statuses': True + 'qemu_extended_statuses': True, + 'exclude_nodes': False } # bypass authentication and API fetch calls @@ -723,13 +725,15 @@ def test_populate_missing_qemu_extended_groups(inventory, mocker): inventory.group_prefix = 'proxmox_' inventory.facts_prefix = 'proxmox_' inventory.strict = False + inventory.exclude_nodes = False opts = { 'group_prefix': 'proxmox_', 'facts_prefix': 'proxmox_', 'want_facts': True, 'want_proxmox_nodes_ansible_host': True, - 'qemu_extended_statuses': False + 'qemu_extended_statuses': False, + 'exclude_nodes': False } # bypass authentication and API fetch calls @@ -743,3 +747,40 @@ def test_populate_missing_qemu_extended_groups(inventory, mocker): # make sure that ['prelaunch', 'paused'] are not in the group list for group in ['paused', 'prelaunch']: assert ('%sall_%s' % (inventory.group_prefix, group)) not in inventory.inventory.groups + + +def test_populate_exclude_nodes(inventory, mocker): + # module settings + inventory.proxmox_user = 'root@pam' + inventory.proxmox_password = 'password' + inventory.proxmox_url = 'https://localhost:8006' + inventory.group_prefix = 'proxmox_' + inventory.facts_prefix = 'proxmox_' + inventory.strict = False + inventory.exclude_nodes = True + + opts = { + 'group_prefix': 'proxmox_', + 'facts_prefix': 'proxmox_', + 'want_facts': True, + 'want_proxmox_nodes_ansible_host': True, + 'qemu_extended_statuses': False, + 'exclude_nodes': True + } + + # bypass authentication and API fetch calls + inventory._get_auth = mocker.MagicMock(side_effect=get_auth) + inventory._get_json = mocker.MagicMock(side_effect=get_json) + inventory._get_vm_snapshots = mocker.MagicMock(side_effect=get_vm_snapshots) + inventory.get_option = mocker.MagicMock(side_effect=get_option(opts)) + inventory._can_add_host = mocker.MagicMock(return_value=True) + inventory._populate() + + # make sure that nodes are not in the inventory + for node in ['testnode', 'testnode2']: + assert node not in inventory.inventory.hosts + # make sure that nodes group is absent + assert ('%s_nodes' % (inventory.group_prefix)) not in inventory.inventory.groups + # make sure that nodes are not in the "ungrouped" group + for node in ['testnode', 'testnode2']: + assert node not in inventory.inventory.get_groups_dict()["ungrouped"]