Skip to content

Commit 7b2bdb4

Browse files
authored
Inventory module site and prefix data extended (#646)
* Enhancements for #640, Inventory module now pulls complete site data, and (optionally) prefixes
1 parent e681b03 commit 7b2bdb4

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

plugins/inventory/nb_inventory.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@
104104
default: False
105105
type: boolean
106106
version_added: "0.1.7"
107+
site_data:
108+
description:
109+
- If True, sites' full data structures returned from Netbox API are included in host vars.
110+
default: False
111+
type: boolean
112+
prefixes:
113+
description:
114+
- If True, it adds the device or virtual machine prefixes to hostvars nested under "site".
115+
- Must match selection for "site_data", as this changes the structure of "site" in hostvars
116+
default: False
117+
type: boolean
107118
services:
108119
description:
109120
- If True, it adds the device or virtual machine services information in host vars.
@@ -612,7 +623,14 @@ def extract_rack_role(self, host):
612623

613624
def extract_site(self, host):
614625
try:
615-
return self._pluralize(self.sites_lookup[host["site"]["id"]])
626+
site = self.sites_lookup[host["site"]["id"]]
627+
if (
628+
self.prefixes
629+
): # If prefixes have been pulled, attach prefix to its assigned site
630+
prefix_id = self.prefixes_sites_lookup[site["id"]]
631+
prefix = self.prefixes_lookup[prefix_id]
632+
site["prefix"] = prefix
633+
return self._pluralize(site)
616634
except Exception:
617635
return
618636

@@ -828,9 +846,29 @@ def refresh_platforms_lookup(self):
828846
)
829847

830848
def refresh_sites_lookup(self):
849+
# Three dictionaries are created here.
850+
# "sites_lookup_slug" only contains the slug. Used by _add_site_groups() when creating inventory groups
851+
# "sites_lookup" contains the full data structure. Most site lookups use this
852+
# "sites_with_prefixes" keeps track of which sites have prefixes assigned. Passed to get_resource_list_chunked()
831853
url = self.api_endpoint + "/api/dcim/sites/?limit=0"
832854
sites = self.get_resource_list(api_url=url)
833-
self.sites_lookup = dict((site["id"], site["slug"]) for site in sites)
855+
# The following dictionary is used for host group creation only,
856+
# as the grouping function expects a string as the value of each key
857+
self.sites_lookup_slug = dict((site["id"], site["slug"]) for site in sites)
858+
if self.site_data or self.prefixes:
859+
# If the "site_data" option is specified, keep the full data structure presented by the API response.
860+
# The "prefixes" option necessitates this structure as well as it requires the site object to be dict().
861+
self.sites_lookup = dict((site["id"], site) for site in sites)
862+
else:
863+
# Otherwise, set equal to the "slug only" dictionary
864+
self.sites_lookup = self.sites_lookup_slug
865+
# The following dictionary tracks which sites have prefixes assigned.
866+
self.sites_with_prefixes = set()
867+
868+
for site in sites:
869+
if site["prefix_count"] > 0:
870+
self.sites_with_prefixes.add(site["slug"])
871+
# Used by refresh_prefixes()
834872

835873
def get_region_for_site(site):
836874
# Will fail if site does not have a region defined in NetBox
@@ -842,6 +880,32 @@ def get_region_for_site(site):
842880
# Dictionary of site id to region id
843881
self.sites_region_lookup = dict(map(get_region_for_site, sites))
844882

883+
# Note: depends on the result of refresh_sites_lookup for self.sites_with_prefixes
884+
def refresh_prefixes(self):
885+
# Pull all prefixes defined in NetBox
886+
url = self.api_endpoint + "/api/ipam/prefixes"
887+
888+
if self.fetch_all:
889+
prefixes = self.get_resource_list(url)
890+
else:
891+
prefixes = self.get_resource_list_chunked(
892+
api_url=url,
893+
query_key="site",
894+
query_values=list(self.sites_with_prefixes),
895+
)
896+
self.prefixes_sites_lookup = defaultdict(dict)
897+
self.prefixes_lookup = defaultdict(dict)
898+
899+
# We are only concerned with Prefixes that have actually been assigned to sites
900+
for prefix in prefixes:
901+
if prefix.get("site"):
902+
prefix_id = prefix["id"]
903+
site_id = prefix["site"]["id"]
904+
self.prefixes_lookup[prefix_id] = prefix
905+
self.prefixes_sites_lookup[site_id] = prefix_id
906+
# Remove "site" attribute, as it's redundant when prefixes are assigned to site
907+
del prefix["site"]
908+
845909
def refresh_regions_lookup(self):
846910
url = self.api_endpoint + "/api/dcim/regions/?limit=0"
847911
regions = self.get_resource_list(api_url=url)
@@ -1164,6 +1228,9 @@ def lookup_processes(self):
11641228
if self.interfaces:
11651229
lookups.append(self.refresh_interfaces)
11661230

1231+
if self.prefixes:
1232+
lookups.append(self.refresh_prefixes)
1233+
11671234
if self.services:
11681235
lookups.append(self.refresh_services)
11691236

@@ -1445,7 +1512,10 @@ def _add_site_groups(self):
14451512
# Map site id to transformed group names
14461513
self.site_group_names = dict()
14471514

1448-
for site_id, site_name in self.sites_lookup.items():
1515+
for (
1516+
site_id,
1517+
site_name,
1518+
) in self.sites_lookup_slug.items(): # "Slug" only. Data not used for grouping
14491519
site_group_name = self.generate_group_name(
14501520
self._pluralize_group_by("site"), site_name
14511521
)
@@ -1678,6 +1748,8 @@ def parse(self, inventory, loader, path, cache=True):
16781748
self.plurals = self.get_option("plurals")
16791749
self.interfaces = self.get_option("interfaces")
16801750
self.services = self.get_option("services")
1751+
self.site_data = self.get_option("site_data")
1752+
self.prefixes = self.get_option("prefixes")
16811753
self.fetch_all = self.get_option("fetch_all")
16821754
self.headers = {
16831755
"User-Agent": "ansible %s Python %s"

0 commit comments

Comments
 (0)