diff --git a/plugins/inventory/nb_inventory.py b/plugins/inventory/nb_inventory.py index 0aac76a8..a63bd58c 100644 --- a/plugins/inventory/nb_inventory.py +++ b/plugins/inventory/nb_inventory.py @@ -3,6 +3,8 @@ from __future__ import absolute_import, division, print_function +import re + __metaclass__ = type DOCUMENTATION = """ @@ -249,6 +251,15 @@ description: Use out of band IP as `ansible host` type: boolean default: false + rename_variables: + description: + - Rename variables evaluated by nb_inventory, before writing them. + - Each list entry contains a dict with a 'pattern' and a 'repl'. + - Both 'pattern' and 'repl' are regular expressions. + - The first matching expression is used, subsequent matches are ignored. + - Internally `re.sub` is used. + type: list + defaut: [] """ EXAMPLES = """ @@ -1905,31 +1916,37 @@ def _setup_nested_groups(self, group, lookup, parent_lookup): return transformed_group_names + def _set_variable(self, hostname, key, value): + for item in self.rename_variables: + if item["pattern"].match(key): + key = item["pattern"].sub(item["repl"], key) + break + + self.inventory.set_variable(hostname, key, value) + def _fill_host_variables(self, host, hostname): extracted_primary_ip = self.extract_primary_ip(host=host) if extracted_primary_ip: - self.inventory.set_variable(hostname, "ansible_host", extracted_primary_ip) + self._set_variable(hostname, "ansible_host", extracted_primary_ip) if self.ansible_host_dns_name: extracted_dns_name = self.extract_dns_name(host=host) if extracted_dns_name: - self.inventory.set_variable( - hostname, "ansible_host", extracted_dns_name - ) + self._set_variable(hostname, "ansible_host", extracted_dns_name) extracted_primary_ip4 = self.extract_primary_ip4(host=host) if extracted_primary_ip4: - self.inventory.set_variable(hostname, "primary_ip4", extracted_primary_ip4) + self._set_variable(hostname, "primary_ip4", extracted_primary_ip4) extracted_primary_ip6 = self.extract_primary_ip6(host=host) if extracted_primary_ip6: - self.inventory.set_variable(hostname, "primary_ip6", extracted_primary_ip6) + self._set_variable(hostname, "primary_ip6", extracted_primary_ip6) extracted_oob_ip = self.extract_oob_ip(host=host) if extracted_oob_ip: - self.inventory.set_variable(hostname, "oob_ip", extracted_oob_ip) + self._set_variable(hostname, "oob_ip", extracted_oob_ip) if self.oob_ip_as_primary_ip: - self.inventory.set_variable(hostname, "ansible_host", extracted_oob_ip) + self._set_variable(hostname, "ansible_host", extracted_oob_ip) for attribute, extractor in self.group_extractors.items(): extracted_value = extractor(host) @@ -1965,9 +1982,9 @@ def _fill_host_variables(self, host, hostname): ) ): for key, value in extracted_value.items(): - self.inventory.set_variable(hostname, key, value) + self._set_variable(hostname, key, value) else: - self.inventory.set_variable(hostname, attribute, extracted_value) + self._set_variable(hostname, attribute, extracted_value) def _get_host_virtual_chassis_master(self, host): virtual_chassis = host.get("virtual_chassis", None) @@ -2146,4 +2163,15 @@ def parse(self, inventory, loader, path, cache=True): self.ansible_host_dns_name = self.get_option("ansible_host_dns_name") self.racks = self.get_option("racks") + # Compile regular expressions, if any + self.rename_variables = self.parse_rename_variables( + self.get_option("rename_variables") + ) + self.main() + + def parse_rename_variables(self, rename_variables): + return [ + {"pattern": re.compile(i["pattern"]), "repl": i["repl"]} + for i in rename_variables or () + ] diff --git a/tests/unit/inventory/test_nb_inventory.py b/tests/unit/inventory/test_nb_inventory.py index 4b502a8a..be8c8d9a 100644 --- a/tests/unit/inventory/test_nb_inventory.py +++ b/tests/unit/inventory/test_nb_inventory.py @@ -34,6 +34,18 @@ ) +class MockInventory: + + def __init__(self): + self.variables = {} + + def set_variable(self, hostname, key, value): + if hostname not in self.variables: + self.variables[hostname] = {} + + self.variables[hostname][key] = value + + @pytest.fixture def inventory_fixture( allowed_device_query_parameters_fixture, allowed_vm_query_parameters_fixture @@ -46,6 +58,9 @@ def inventory_fixture( inventory.allowed_device_query_parameters = allowed_device_query_parameters_fixture inventory.allowed_vm_query_parameters = allowed_vm_query_parameters_fixture + # Inventory mock, to validate what has been set via inventory.inventory.set_variable + inventory.inventory = MockInventory() + return inventory @@ -260,3 +275,22 @@ def test_extract_custom_fields(inventory_fixture, custom_fields, expected): ) assert extracted_custom_fields == expected + + +def test_rename_variables(inventory_fixture): + inventory_fixture.rename_variables = inventory_fixture.parse_rename_variables(( + {"pattern": r"cluster(.*)", "repl": r'netbox_cluster\1'}, + {"pattern": r"ansible_host", "repl": r"host"}, + )) + + inventory_fixture._set_variable("host", "ansible_fqdn", "host.example.org") + inventory_fixture._set_variable("host", "ansible_host", "host") + inventory_fixture._set_variable("host", "cluster", "staging") + inventory_fixture._set_variable("host", "cluster_id", "0xdeadbeef") + + assert inventory_fixture.inventory.variables["host"] == { + "ansible_fqdn": "host.example.org", + "host": "host", + "netbox_cluster": "staging", + "netbox_cluster_id": "0xdeadbeef" + }