Skip to content

Commit 50a271b

Browse files
authored
Implements group_by "time_zone" and "time_zone_utc" in nb_inventory (#798)
Implements group_by time_zone and time_zone_utc in inventory source
1 parent 1a0da30 commit 50a271b

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ To keep the code simple, we only officially support the two latest releases of N
1414
- NetBox 2.11+ or the two latest NetBox releases
1515
- Python 3.8+
1616
- Python modules:
17+
- `pytz`
1718
- `pynetbox 5.0.4+`, `pynetbox 6.4.0+` if using 3.1 features
1819
- `packaging` if using Ansible < 2.10, as it's included in Ansible 2.10+
1920
- Ansible 2.10+

plugins/inventory/nb_inventory.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@
167167
- is_virtual
168168
- services
169169
- status
170+
- time_zone
171+
- utc_offset
170172
default: []
171173
group_names_raw:
172174
description: Will not add the group_by choice name to the group names
@@ -296,12 +298,39 @@
296298
env:
297299
NETBOX_API: '{{ NETBOX_API }}'
298300
NETBOX_TOKEN: '{{ NETBOX_TOKEN }}'
301+
302+
# Example of time_zone and utc_offset usage
303+
304+
plugin: netbox.netbox.nb_inventory
305+
api_endpoint: http://localhost:8000
306+
token: <insert token>
307+
validate_certs: True
308+
config_context: True
309+
group_by:
310+
- site
311+
- role
312+
- time_zone
313+
- utc_offset
314+
device_query_filters:
315+
- has_primary_ip: 'true'
316+
- manufacturer_id: 1
317+
318+
# using group by time_zone, utc_offset it will group devices in ansible groups depending on time zone configured on site.
319+
# time_zone gives grouping like:
320+
# - "time_zone_Europe_Bucharest"
321+
# - "time_zone_Europe_Copenhagen"
322+
# - "time_zone_America_Denver"
323+
# utc_offset gives grouping like:
324+
# - "time_zone_utc_minus_7"
325+
# - "time_zone_utc_plus_1"
326+
# - "time_zone_utc_plus_10"
299327
"""
300328

301329
import json
302330
import uuid
303331
import math
304332
import os
333+
import datetime
305334
from copy import deepcopy
306335
from functools import partial
307336
from sys import version as python_version
@@ -320,6 +349,14 @@
320349
from ansible.module_utils.urls import open_url
321350
from ansible.module_utils.six.moves.urllib import error as urllib_error
322351
from ansible.module_utils.six.moves.urllib.parse import urlencode
352+
from ansible.module_utils.six import raise_from
353+
354+
try:
355+
import pytz
356+
except ImportError as imp_exc:
357+
PYTZ_IMPORT_ERROR = imp_exc
358+
else:
359+
PYTZ_IMPORT_ERROR = None
323360

324361

325362
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
@@ -474,6 +511,8 @@ def group_extractors(self):
474511
"cluster_group": self.extract_cluster_group,
475512
"cluster_type": self.extract_cluster_type,
476513
"is_virtual": self.extract_is_virtual,
514+
"time_zone": self.extract_site_time_zone,
515+
"utc_offset": self.extract_site_utc_offset,
477516
self._pluralize_group_by("site"): self.extract_site,
478517
self._pluralize_group_by("tenant"): self.extract_tenant,
479518
self._pluralize_group_by("tag"): self.extract_tags,
@@ -682,6 +721,18 @@ def extract_device_role(self, host):
682721
except Exception:
683722
return
684723

724+
def extract_site_time_zone(self, host):
725+
try:
726+
return self.sites_time_zone_lookup[host["site"]["id"]]
727+
except Exception:
728+
return
729+
730+
def extract_site_utc_offset(self, host):
731+
try:
732+
return self.sites_utc_offset_lookup[host["site"]["id"]]
733+
except Exception:
734+
return
735+
685736
def extract_config_context(self, host):
686737
try:
687738
if self.flatten_config_context:
@@ -944,6 +995,38 @@ def get_site_group_for_site(site):
944995
# Dictionary of site id to site_group id
945996
self.sites_site_group_lookup = dict(map(get_site_group_for_site, sites))
946997

998+
def get_time_zone_for_site(site):
999+
# Will fail if site does not have a time_zone defined in NetBox
1000+
try:
1001+
return (site["id"], site["time_zone"].replace("/", "_", 2))
1002+
except Exception:
1003+
return (site["id"], None)
1004+
1005+
# Dictionary of site id to time_zone name (if group by time_zone is used)
1006+
if "time_zone" in self.group_by:
1007+
self.sites_time_zone_lookup = dict(map(get_time_zone_for_site, sites))
1008+
1009+
def get_utc_offset_for_site(site):
1010+
# Will fail if site does not have a time_zone defined in NetBox
1011+
try:
1012+
utc = round(
1013+
datetime.datetime.now(pytz.timezone(site["time_zone"]))
1014+
.utcoffset()
1015+
.total_seconds()
1016+
/ 60
1017+
/ 60
1018+
)
1019+
if utc < 0:
1020+
return (site["id"], str(utc).replace("-", "minus_"))
1021+
else:
1022+
return (site["id"], f"plus_{utc}")
1023+
except Exception:
1024+
return (site["id"], None)
1025+
1026+
# Dictionary of site id to utc_offset name (if group by utc_offset is used)
1027+
if "utc_offset" in self.group_by:
1028+
self.sites_utc_offset_lookup = dict(map(get_utc_offset_for_site, sites))
1029+
9471030
# Note: depends on the result of refresh_sites_lookup for self.sites_with_prefixes
9481031
def refresh_prefixes(self):
9491032
# Pull all prefixes defined in NetBox
@@ -1763,6 +1846,13 @@ def _get_host_virtual_chassis_master(self, host):
17631846
return master.get("id", None)
17641847

17651848
def main(self):
1849+
# Check if pytz lib is install, and give error if not
1850+
if PYTZ_IMPORT_ERROR:
1851+
raise_from(
1852+
AnsibleError("pytz must be installed to use this plugin"),
1853+
PYTZ_IMPORT_ERROR,
1854+
)
1855+
17661856
# Get info about the API - version, allowed query parameters
17671857
self.fetch_api_docs()
17681858

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pynetbox
22
packaging
3+
pytz

0 commit comments

Comments
 (0)