|
167 | 167 | - is_virtual
|
168 | 168 | - services
|
169 | 169 | - status
|
| 170 | + - time_zone |
| 171 | + - utc_offset |
170 | 172 | default: []
|
171 | 173 | group_names_raw:
|
172 | 174 | description: Will not add the group_by choice name to the group names
|
|
296 | 298 | env:
|
297 | 299 | NETBOX_API: '{{ NETBOX_API }}'
|
298 | 300 | 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" |
299 | 327 | """
|
300 | 328 |
|
301 | 329 | import json
|
302 | 330 | import uuid
|
303 | 331 | import math
|
304 | 332 | import os
|
| 333 | +import datetime |
305 | 334 | from copy import deepcopy
|
306 | 335 | from functools import partial
|
307 | 336 | from sys import version as python_version
|
|
320 | 349 | from ansible.module_utils.urls import open_url
|
321 | 350 | from ansible.module_utils.six.moves.urllib import error as urllib_error
|
322 | 351 | 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 |
323 | 360 |
|
324 | 361 |
|
325 | 362 | class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
@@ -474,6 +511,8 @@ def group_extractors(self):
|
474 | 511 | "cluster_group": self.extract_cluster_group,
|
475 | 512 | "cluster_type": self.extract_cluster_type,
|
476 | 513 | "is_virtual": self.extract_is_virtual,
|
| 514 | + "time_zone": self.extract_site_time_zone, |
| 515 | + "utc_offset": self.extract_site_utc_offset, |
477 | 516 | self._pluralize_group_by("site"): self.extract_site,
|
478 | 517 | self._pluralize_group_by("tenant"): self.extract_tenant,
|
479 | 518 | self._pluralize_group_by("tag"): self.extract_tags,
|
@@ -682,6 +721,18 @@ def extract_device_role(self, host):
|
682 | 721 | except Exception:
|
683 | 722 | return
|
684 | 723 |
|
| 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 | + |
685 | 736 | def extract_config_context(self, host):
|
686 | 737 | try:
|
687 | 738 | if self.flatten_config_context:
|
@@ -944,6 +995,38 @@ def get_site_group_for_site(site):
|
944 | 995 | # Dictionary of site id to site_group id
|
945 | 996 | self.sites_site_group_lookup = dict(map(get_site_group_for_site, sites))
|
946 | 997 |
|
| 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 | + |
947 | 1030 | # Note: depends on the result of refresh_sites_lookup for self.sites_with_prefixes
|
948 | 1031 | def refresh_prefixes(self):
|
949 | 1032 | # Pull all prefixes defined in NetBox
|
@@ -1763,6 +1846,13 @@ def _get_host_virtual_chassis_master(self, host):
|
1763 | 1846 | return master.get("id", None)
|
1764 | 1847 |
|
1765 | 1848 | 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 | + |
1766 | 1856 | # Get info about the API - version, allowed query parameters
|
1767 | 1857 | self.fetch_api_docs()
|
1768 | 1858 |
|
|
0 commit comments