104
104
default: False
105
105
type: boolean
106
106
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
107
118
services:
108
119
description:
109
120
- If True, it adds the device or virtual machine services information in host vars.
@@ -612,7 +623,14 @@ def extract_rack_role(self, host):
612
623
613
624
def extract_site (self , host ):
614
625
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 )
616
634
except Exception :
617
635
return
618
636
@@ -828,9 +846,29 @@ def refresh_platforms_lookup(self):
828
846
)
829
847
830
848
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()
831
853
url = self .api_endpoint + "/api/dcim/sites/?limit=0"
832
854
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()
834
872
835
873
def get_region_for_site (site ):
836
874
# Will fail if site does not have a region defined in NetBox
@@ -842,6 +880,32 @@ def get_region_for_site(site):
842
880
# Dictionary of site id to region id
843
881
self .sites_region_lookup = dict (map (get_region_for_site , sites ))
844
882
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
+
845
909
def refresh_regions_lookup (self ):
846
910
url = self .api_endpoint + "/api/dcim/regions/?limit=0"
847
911
regions = self .get_resource_list (api_url = url )
@@ -1164,6 +1228,9 @@ def lookup_processes(self):
1164
1228
if self .interfaces :
1165
1229
lookups .append (self .refresh_interfaces )
1166
1230
1231
+ if self .prefixes :
1232
+ lookups .append (self .refresh_prefixes )
1233
+
1167
1234
if self .services :
1168
1235
lookups .append (self .refresh_services )
1169
1236
@@ -1445,7 +1512,10 @@ def _add_site_groups(self):
1445
1512
# Map site id to transformed group names
1446
1513
self .site_group_names = dict ()
1447
1514
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
1449
1519
site_group_name = self .generate_group_name (
1450
1520
self ._pluralize_group_by ("site" ), site_name
1451
1521
)
@@ -1678,6 +1748,8 @@ def parse(self, inventory, loader, path, cache=True):
1678
1748
self .plurals = self .get_option ("plurals" )
1679
1749
self .interfaces = self .get_option ("interfaces" )
1680
1750
self .services = self .get_option ("services" )
1751
+ self .site_data = self .get_option ("site_data" )
1752
+ self .prefixes = self .get_option ("prefixes" )
1681
1753
self .fetch_all = self .get_option ("fetch_all" )
1682
1754
self .headers = {
1683
1755
"User-Agent" : "ansible %s Python %s"
0 commit comments