Skip to content

Commit a271a54

Browse files
authored
Add ability to rename host inventory variables using patterns (#1273)
Certain environments may have conflicts with the variables given by Netbox, and the variables used in the existing Ansible codebase. With this change, such cases can be worked around by renaming individual variables, or whole groups of them. For example, this will rename all `cluster*` variables to have a `netbox__` prefix instead (e.g., `cluster_group` -> `netbox__cluster_group`): ```yaml rename_variables: - pattern: 'cluster(.*)' repl: 'netbox__cluster\1' ``` Uses a list, instead of a dict, to ensure that the order of evaluation is strictly defined across all Python versions, and to add the ability to exclude certain variables from being rewritten. For example: ```yaml rename_variables: # Keep cluster_type the same - pattern: 'cluster_type' repl: 'cluster_type' # Rename all other cluster* variables - pattern: 'cluster(.*)' repl: 'netbox__cluster\1' ```
1 parent 4460954 commit a271a54

File tree

3 files changed

+76
-10
lines changed

3 files changed

+76
-10
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
minor_changes:
2+
- Add ability to rename variables set on the host by ``netbox.netbox.nb_inventory`` through configuration.

plugins/inventory/nb_inventory.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,16 @@
249249
description: Use out of band IP as `ansible host`
250250
type: boolean
251251
default: false
252+
rename_variables:
253+
description:
254+
- Rename variables evaluated by nb_inventory, before writing them.
255+
- Each list entry contains a dict with a 'pattern' and a 'repl'.
256+
- Both 'pattern' and 'repl' are regular expressions.
257+
- The first matching expression is used, subsequent matches are ignored.
258+
- Internally `re.sub` is used.
259+
type: list
260+
elements: dict
261+
default: []
252262
"""
253263

254264
EXAMPLES = """
@@ -364,6 +374,7 @@
364374
import uuid
365375
import math
366376
import os
377+
import re
367378
import datetime
368379
from copy import deepcopy
369380
from functools import partial
@@ -1905,31 +1916,37 @@ def _setup_nested_groups(self, group, lookup, parent_lookup):
19051916

19061917
return transformed_group_names
19071918

1919+
def _set_variable(self, hostname, key, value):
1920+
for item in self.rename_variables:
1921+
if item["pattern"].match(key):
1922+
key = item["pattern"].sub(item["repl"], key)
1923+
break
1924+
1925+
self.inventory.set_variable(hostname, key, value)
1926+
19081927
def _fill_host_variables(self, host, hostname):
19091928
extracted_primary_ip = self.extract_primary_ip(host=host)
19101929
if extracted_primary_ip:
1911-
self.inventory.set_variable(hostname, "ansible_host", extracted_primary_ip)
1930+
self._set_variable(hostname, "ansible_host", extracted_primary_ip)
19121931

19131932
if self.ansible_host_dns_name:
19141933
extracted_dns_name = self.extract_dns_name(host=host)
19151934
if extracted_dns_name:
1916-
self.inventory.set_variable(
1917-
hostname, "ansible_host", extracted_dns_name
1918-
)
1935+
self._set_variable(hostname, "ansible_host", extracted_dns_name)
19191936

19201937
extracted_primary_ip4 = self.extract_primary_ip4(host=host)
19211938
if extracted_primary_ip4:
1922-
self.inventory.set_variable(hostname, "primary_ip4", extracted_primary_ip4)
1939+
self._set_variable(hostname, "primary_ip4", extracted_primary_ip4)
19231940

19241941
extracted_primary_ip6 = self.extract_primary_ip6(host=host)
19251942
if extracted_primary_ip6:
1926-
self.inventory.set_variable(hostname, "primary_ip6", extracted_primary_ip6)
1943+
self._set_variable(hostname, "primary_ip6", extracted_primary_ip6)
19271944

19281945
extracted_oob_ip = self.extract_oob_ip(host=host)
19291946
if extracted_oob_ip:
1930-
self.inventory.set_variable(hostname, "oob_ip", extracted_oob_ip)
1947+
self._set_variable(hostname, "oob_ip", extracted_oob_ip)
19311948
if self.oob_ip_as_primary_ip:
1932-
self.inventory.set_variable(hostname, "ansible_host", extracted_oob_ip)
1949+
self._set_variable(hostname, "ansible_host", extracted_oob_ip)
19331950

19341951
for attribute, extractor in self.group_extractors.items():
19351952
extracted_value = extractor(host)
@@ -1965,9 +1982,9 @@ def _fill_host_variables(self, host, hostname):
19651982
)
19661983
):
19671984
for key, value in extracted_value.items():
1968-
self.inventory.set_variable(hostname, key, value)
1985+
self._set_variable(hostname, key, value)
19691986
else:
1970-
self.inventory.set_variable(hostname, attribute, extracted_value)
1987+
self._set_variable(hostname, attribute, extracted_value)
19711988

19721989
def _get_host_virtual_chassis_master(self, host):
19731990
virtual_chassis = host.get("virtual_chassis", None)
@@ -2146,4 +2163,15 @@ def parse(self, inventory, loader, path, cache=True):
21462163
self.ansible_host_dns_name = self.get_option("ansible_host_dns_name")
21472164
self.racks = self.get_option("racks")
21482165

2166+
# Compile regular expressions, if any
2167+
self.rename_variables = self.parse_rename_variables(
2168+
self.get_option("rename_variables")
2169+
)
2170+
21492171
self.main()
2172+
2173+
def parse_rename_variables(self, rename_variables):
2174+
return [
2175+
{"pattern": re.compile(i["pattern"]), "repl": i["repl"]}
2176+
for i in rename_variables or ()
2177+
]

tests/unit/inventory/test_nb_inventory.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@
3434
)
3535

3636

37+
class MockInventory:
38+
39+
def __init__(self):
40+
self.variables = {}
41+
42+
def set_variable(self, hostname, key, value):
43+
if hostname not in self.variables:
44+
self.variables[hostname] = {}
45+
46+
self.variables[hostname][key] = value
47+
48+
3749
@pytest.fixture
3850
def inventory_fixture(
3951
allowed_device_query_parameters_fixture, allowed_vm_query_parameters_fixture
@@ -46,6 +58,9 @@ def inventory_fixture(
4658
inventory.allowed_device_query_parameters = allowed_device_query_parameters_fixture
4759
inventory.allowed_vm_query_parameters = allowed_vm_query_parameters_fixture
4860

61+
# Inventory mock, to validate what has been set via inventory.inventory.set_variable
62+
inventory.inventory = MockInventory()
63+
4964
return inventory
5065

5166

@@ -260,3 +275,24 @@ def test_extract_custom_fields(inventory_fixture, custom_fields, expected):
260275
)
261276

262277
assert extracted_custom_fields == expected
278+
279+
280+
def test_rename_variables(inventory_fixture):
281+
inventory_fixture.rename_variables = inventory_fixture.parse_rename_variables(
282+
(
283+
{"pattern": r"cluster(.*)", "repl": r"netbox_cluster\1"},
284+
{"pattern": r"ansible_host", "repl": r"host"},
285+
)
286+
)
287+
288+
inventory_fixture._set_variable("host", "ansible_fqdn", "host.example.org")
289+
inventory_fixture._set_variable("host", "ansible_host", "host")
290+
inventory_fixture._set_variable("host", "cluster", "staging")
291+
inventory_fixture._set_variable("host", "cluster_id", "0xdeadbeef")
292+
293+
assert inventory_fixture.inventory.variables["host"] == {
294+
"ansible_fqdn": "host.example.org",
295+
"host": "host",
296+
"netbox_cluster": "staging",
297+
"netbox_cluster_id": "0xdeadbeef",
298+
}

0 commit comments

Comments
 (0)