nornir-collection 0.0.10__tar.gz → 0.0.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/PKG-INFO +3 -2
  2. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/utils.py +1 -1
  3. nornir_collection-0.0.10/nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py → nornir_collection-0.0.12/nornir_collection/netbox/update_prefixes_ip_addresses.py +215 -85
  4. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/utils.py +58 -0
  5. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection.egg-info/PKG-INFO +3 -2
  6. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection.egg-info/SOURCES.txt +2 -2
  7. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection.egg-info/requires.txt +2 -1
  8. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/setup.py +1 -1
  9. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/LICENSE +0 -0
  10. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/README.md +0 -0
  11. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/__init__.py +0 -0
  12. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/batfish/__init__.py +0 -0
  13. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/batfish/assert_config.py +0 -0
  14. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/batfish/utils.py +0 -0
  15. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/__init__.py +0 -0
  16. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/__init__.py +0 -0
  17. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/cli/__init__.py +0 -0
  18. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/cli/config_tasks.py +0 -0
  19. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/cli/config_workflow.py +0 -0
  20. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/cli/show_tasks.py +0 -0
  21. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/netconf/__init__.py +0 -0
  22. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/netconf/config_tasks.py +0 -0
  23. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/netconf/config_workflow.py +0 -0
  24. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/netconf/nr_cfg_iosxe_netconf.py +0 -0
  25. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/netconf/ops_tasks.py +0 -0
  26. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/processor.py +0 -0
  27. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/pyats.py +0 -0
  28. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/restconf/__init__.py +0 -0
  29. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/restconf/cisco_rpc.py +0 -0
  30. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/restconf/config_workflow.py +0 -0
  31. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/configuration_management/restconf/tasks.py +0 -0
  32. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/software_upgrade/__init__.py +0 -0
  33. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/software_upgrade/cisco_software_upgrade.py +0 -0
  34. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/software_upgrade/utils.py +0 -0
  35. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/__init__.py +0 -0
  36. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/api_calls.py +0 -0
  37. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/cisco_maintenance_report.py +0 -0
  38. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/cisco_support.py +0 -0
  39. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/reports.py +0 -0
  40. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/cisco/support_api/utils.py +0 -0
  41. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/fortinet/__init__.py +0 -0
  42. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/fortinet/utils.py +0 -0
  43. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/git.py +0 -0
  44. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/__init__.py +0 -0
  45. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/custom_script.py +0 -0
  46. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/inventory.py +0 -0
  47. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/set_device_status.py +0 -0
  48. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/sync_datasource.py +0 -0
  49. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/update_cisco_inventory_data.py +0 -0
  50. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/update_cisco_support_plugin_data.py +0 -0
  51. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/update_fortinet_inventory_data.py +0 -0
  52. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/update_purestorage_inventory_data.py +0 -0
  53. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/netbox/verify_device_primary_ip.py +0 -0
  54. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/nornir_plugins/__init__.py +0 -0
  55. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/nornir_plugins/inventory/__init__.py +0 -0
  56. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/nornir_plugins/inventory/netbox.py +0 -0
  57. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/nornir_plugins/inventory/staggered_yaml.py +0 -0
  58. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/nornir_plugins/inventory/utils.py +0 -0
  59. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/purestorage/__init__.py +0 -0
  60. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/purestorage/utils.py +0 -0
  61. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection/utils.py +0 -0
  62. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection.egg-info/dependency_links.txt +0 -0
  63. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/nornir_collection.egg-info/top_level.txt +0 -0
  64. {nornir_collection-0.0.10 → nornir_collection-0.0.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nornir-collection
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Nornir-Collection contains network automation functions and complete IaC workflows with Nornir and other python libraries. It contains Nornir tasks and general functions in Nornir style.
5
5
  Author: Willi Kubny
6
6
  Author-email: willi.kubny@gmail.ch
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: nornir==3.3.0
14
14
  Requires-Dist: nornir-jinja2==0.2.0
15
- Requires-Dist: nornir-salt==0.20.3
15
+ Requires-Dist: nornir-salt==0.22.2
16
16
  Requires-Dist: nornir-netmiko==1.0.1
17
17
  Requires-Dist: nornir-scrapli==2023.7.30
18
18
  Requires-Dist: scrapli-netconf==2023.7.30
@@ -20,6 +20,7 @@ Requires-Dist: scrapli[ssh2]
20
20
  Requires-Dist: scrapli[genie]
21
21
  Requires-Dist: nornir-utils==0.2.0
22
22
  Requires-Dist: pynetbox==7.4.0
23
+ Requires-Dist: ipfabric==7.0.2
23
24
  Requires-Dist: py-pure-client==1.51.0
24
25
  Requires-Dist: nornir-pyfgt==1.0.3
25
26
  Requires-Dist: python-nmap==0.7.1
@@ -373,7 +373,7 @@ def init_args_for_netconf_cm() -> argparse.Namespace:
373
373
  "--pre-check",
374
374
  action="store_true",
375
375
  default=False,
376
- help="disable the pre-configuration check (default: False)",
376
+ help="enable the pre-configuration check (default: False)",
377
377
  )
378
378
 
379
379
  # Verify the provided arguments and print the custom argparse error message in case any error or wrong
@@ -5,6 +5,7 @@ IP address, and vlan information.
5
5
  The Main function is intended to import and execute by other scripts.
6
6
  """
7
7
 
8
+ import os
8
9
  import sys
9
10
  import ipaddress
10
11
  import urllib.parse
@@ -12,6 +13,7 @@ from typing import Callable, Literal, Union
12
13
  from concurrent.futures import ThreadPoolExecutor
13
14
  import requests
14
15
  import nmap
16
+ from ipfabric import IPFClient
15
17
  from nornir_collection.netbox.utils import (
16
18
  get_nb_resources,
17
19
  post_nb_resources,
@@ -114,6 +116,7 @@ def create_nb_response_result(
114
116
  data: Union[dict, list],
115
117
  task_text: str,
116
118
  text: str,
119
+ ds: Literal["nmap", "ip-fabric"] = None,
117
120
  ) -> tuple:
118
121
  """
119
122
  Verify the NetBox response and return the result.
@@ -143,14 +146,19 @@ def create_nb_response_result(
143
146
  # Create a list of fields that have been updated
144
147
  updated_fields = []
145
148
 
146
- # If data is a list of ip addresses from a active scanned prefix
149
+ # If data is a list of ip addresses from a active scanned prefix of IP-Fabric
147
150
  if nb_type in "ip" and isinstance(data, list):
148
151
  # Create a list of ip addresses that have been updated
149
152
  for ip in data:
150
153
  address = ip["address"]
151
154
  dns_name = ip["dns_name"] if "dns_name" in ip and ip["dns_name"] else "None"
152
- ports = ", ".join([str(p) for p in ip["ports"]]) if "ports" in ip and ip["ports"] else "None"
153
- updated_fields.append(f"- {address} (DNS: {dns_name}, Ports: {ports})")
155
+ if ds == "nmap":
156
+ ports = (
157
+ ", ".join([str(p) for p in ip["ports"]]) if "ports" in ip and ip["ports"] else "None"
158
+ )
159
+ updated_fields.append(f"- {address} (DNS: {dns_name}, Ports: {ports})")
160
+ else:
161
+ updated_fields.append(f"- {address} (DNS: {dns_name})")
154
162
  # If data is a dictionary containing the prefix or information vlan associated with a prefix
155
163
  elif nb_type in ("ip", "vlan") and isinstance(data, dict):
156
164
  # If the response json contains the key 'vid' the response is from a vlan and has no VRF
@@ -195,7 +203,9 @@ def create_nb_response_result(
195
203
  return result, True
196
204
 
197
205
 
198
- def create_nb_ip_payload(parent_prefix: dict, data: list, desired_status: str = None) -> dict:
206
+ def create_nb_ip_payload(
207
+ parent_prefix: dict, data: list, ds: Literal["nmap", "ip-fabric"] = None, desired_status: str = None
208
+ ) -> dict:
199
209
  """
200
210
  Create a NetBox REST API payload.
201
211
  To add or delete IP addresses of an active scanned prefix or update the following fields:
@@ -220,22 +230,25 @@ def create_nb_ip_payload(parent_prefix: dict, data: list, desired_status: str =
220
230
  # Add the 'id' if it exists (not needed for post requests)
221
231
  if "id" in ip:
222
232
  item["id"] = ip["id"]
223
- # If desired_status is not None, the payload is for active scanned ip addresses
233
+ # If desired_status is not None, the payload is for active scanned ip addresses or IP-Fabric
224
234
  if desired_status:
225
235
  # Add the 'address' and 'status' to the payload
226
236
  item["address"] = ip["address"]
227
237
  item["status"] = desired_status
228
- # If the ip address exists in the nmap_ips list
229
- nmap_ip = [x for x in parent_prefix["nmap_ips"] if x["address"] == ip["address"]]
238
+ # If the ip address exists in the datasource_ips list
239
+ datasource_ip = [x for x in parent_prefix["datasource_ips"] if x["address"] == ip["address"]]
230
240
  # Add the 'dns_name'
231
- item["dns_name"] = nmap_ip[0]["dns_name"] if nmap_ip else ""
232
- # Format the 'ports' as a Markdown table and add the 'ports'
233
- if nmap_ip and nmap_ip[0]["ports"]:
234
- md = [["Port", "State", "Service"]]
235
- md.extend([[f"{k}/tcp", v["state"], v["name"]] for k, v in nmap_ip[0]["ports"].items()])
236
- item["custom_fields"] = {"ipaddress_ports": make_markdown_table(md)}
237
- else:
238
- item["custom_fields"] = {"ipaddress_ports": None}
241
+ item["dns_name"] = datasource_ip[0]["dns_name"] if datasource_ip else ""
242
+ # If it's a nmap scan format the 'ports' as a Markdown table and add the 'ports'
243
+ if ds == "nmap":
244
+ if datasource_ip and datasource_ip[0]["ports"]:
245
+ md = [["Port", "State", "Service"]]
246
+ md.extend(
247
+ [[f"{k}/tcp", v["state"], v["name"]] for k, v in datasource_ip[0]["ports"].items()]
248
+ )
249
+ item["custom_fields"] = {"ipaddress_ports": make_markdown_table(md)}
250
+ else:
251
+ item["custom_fields"] = {"ipaddress_ports": None}
239
252
  # If the desired_status is None, the payload is for all ip addresses of a prefix
240
253
  else:
241
254
  # Add the 'vrf' to the payload
@@ -253,23 +266,10 @@ def create_nb_ip_payload(parent_prefix: dict, data: list, desired_status: str =
253
266
  return payload
254
267
 
255
268
 
256
- def get_netbox_ip_addresses_and_scan_prefix(nb_url: str, prefix: dict) -> tuple:
269
+ def nmap_scan_prefix(prefix: dict, result: list) -> tuple:
257
270
  """
258
- Get NetBox IP addresses and scan a prefix with nmap.
259
- This function can be used within a ThreadPoolExecutor.
260
-
261
- Args:
262
- nb_url (str): The URL of the NetBox instance.
263
- prefix (dict): The prefix to scan and retrieve IP addresses for.
264
-
265
- Returns:
266
- tuple: A tuple containing the result list and the updated prefix dictionary.
271
+ TBD
267
272
  """
268
- # Create list to collect the results
269
- result = []
270
-
271
- #### Scan the prefix with nmap ###########################################################################
272
-
273
273
  # Get the prefix length from the prefix
274
274
  prefixlen = ipaddress.ip_network(prefix["prefix"]).prefixlen
275
275
  # Get the network and broadcast address of the prefix to exclude them from the nmap scan
@@ -282,7 +282,7 @@ def get_netbox_ip_addresses_and_scan_prefix(nb_url: str, prefix: dict) -> tuple:
282
282
  nm = nmap.PortScanner()
283
283
  nm.scan(hosts=prefix["prefix"], arguments=arguments, sudo=True)
284
284
  if nm.all_hosts():
285
- prefix["nmap_ips"] = [
285
+ prefix["datasource_ips"] = [
286
286
  {
287
287
  "address": f"{nm[host]['addresses']['ipv4']}/{prefixlen}",
288
288
  "dns_name": nm[host]["hostnames"][0]["name"],
@@ -291,16 +291,96 @@ def get_netbox_ip_addresses_and_scan_prefix(nb_url: str, prefix: dict) -> tuple:
291
291
  for host in nm.all_hosts()
292
292
  ]
293
293
  else:
294
- prefix["nmap_ips"] = []
294
+ prefix["datasource_ips"] = []
295
295
 
296
296
  # Print the task result
297
297
  text = f"Nmap Scan Prefix {prefix['prefix']} for active IP-Addresses"
298
298
  result.append(
299
299
  f"{task_result(text=text, changed=False, level_name='INFO')}\n"
300
300
  + f"'{text}' -> NetBoxResponse <Success: True>\n"
301
- + f"-> Nmap prefix scan ip-address count: {len(prefix['nmap_ips'])}",
301
+ + f"-> Nmap prefix scan ip-address count: {len(prefix['datasource_ips'])}"
302
302
  )
303
303
 
304
+ return result, prefix
305
+
306
+
307
+ def get_ipfabric_data_for_prefix(prefix: dict, result: list) -> tuple:
308
+ """
309
+ TBD
310
+ """
311
+ # Connect to IP-Fabric
312
+ ipf = IPFClient(
313
+ base_url=os.environ["IPF_URL"], auth=os.environ["IPF_TOKEN"], snapshot_id="$last", verify=False
314
+ )
315
+
316
+ # Get the prefix length from the prefix
317
+ prefixlen = ipaddress.ip_network(prefix["prefix"]).prefixlen
318
+ # Get all ip-addresses of the prefix from the IP-Fabric technology arp table
319
+ # Remove duplicate ip-addresses as the arp table can contain multiple entries for the same ip-address
320
+ filters = {"ip": ["cidr", prefix["prefix"]]}
321
+ all_ips = list(set([x["ip"] for x in ipf.technology.addressing.arp_table.all(filters=filters)]))
322
+ # Get all ip-addresses of the prefix from the IP-Fabric inventory hosts table
323
+ # Set the ip-address as key to access the dict easier later
324
+ columns = ["ip", "dnsName"]
325
+ host_ips = {
326
+ x["ip"]: x["dnsName"] or "" for x in ipf.inventory.hosts.all(filters=filters, columns=columns)
327
+ }
328
+ # Add a list of all ip-addresses and other details to prefix
329
+ prefix["datasource_ips"] = [
330
+ {
331
+ "address": f"{ip}/{prefixlen}",
332
+ "dns_name": host_ips[ip] if ip in host_ips.keys() else "",
333
+ "ports": {},
334
+ }
335
+ for ip in all_ips
336
+ ]
337
+
338
+ # Print the task result
339
+ text = f"IP-Fabric Get Data for Prefix {prefix['prefix']} IP-Addresses"
340
+ result.append(
341
+ f"{task_result(text=text, changed=False, level_name='INFO')}\n"
342
+ + f"'{text}' -> IPFResponse <Success: True>\n"
343
+ + f"-> IP-Fabric prefix ip-address count: {len(prefix['datasource_ips'])}"
344
+ )
345
+
346
+ return result, prefix
347
+
348
+
349
+ def get_nb_ips_and_external_datasource(nb_url: str, prefix: dict, ds: Literal["nmap", "ip-fabric"]) -> tuple:
350
+ """
351
+ Get NetBox IP addresses and scan a prefix with nmap or get the data from IP-Fabric.
352
+ This function can be used within a ThreadPoolExecutor.
353
+
354
+ Args:
355
+ nb_url (str): The URL of the NetBox instance.
356
+ prefix (dict): The prefix to scan and retrieve IP addresses for.
357
+ ds (Literal["nmap", "ip-fabric"]): The datasource to use for the IP addresses.
358
+
359
+ Returns:
360
+ tuple: A tuple containing the result list and the updated prefix dictionary.
361
+ """
362
+ # Create list to collect the results
363
+ failed = False
364
+ result = []
365
+
366
+ # Get the ip-addresses of the prefix from the datasource
367
+ if ds == "nmap":
368
+ # Scan the prefix with nmap
369
+ result, prefix = nmap_scan_prefix(prefix=prefix, result=result)
370
+ elif ds == "ip-fabric":
371
+ # Get the ip-addresses from the IP-Fabric
372
+ result, prefix = get_ipfabric_data_for_prefix(prefix=prefix, result=result)
373
+ else:
374
+ # Invalid datasource
375
+ failed = True
376
+ text = f"Get NetBox IP-Addresses of Prefix {prefix['prefix']}"
377
+ result.append(
378
+ f"{task_result(text=text, changed=False, level_name='ERROR')}\n"
379
+ + f"'{text}' -> NetBoxResponse <Success: True>\n"
380
+ + f"-> Invalid Datasource '{ds}'\n"
381
+ )
382
+ return result, prefix, failed
383
+
304
384
  #### Get NetBox ip-addresses ############################################################################
305
385
 
306
386
  # Add a list of dicts (id & address) of all NetBox ip-addresses for prefix
@@ -346,7 +426,7 @@ def get_netbox_ip_addresses_and_scan_prefix(nb_url: str, prefix: dict) -> tuple:
346
426
  + f"-> Deprecated ip-address count: {len(prefix['deprecated_ips'])}",
347
427
  )
348
428
 
349
- return result, prefix
429
+ return result, prefix, failed
350
430
 
351
431
 
352
432
  def create_ip_list(loop_list: list[dict], check_list: list[dict], is_in_both: bool) -> list[dict]:
@@ -379,7 +459,7 @@ def create_ip_list(loop_list: list[dict], check_list: list[dict], is_in_both: bo
379
459
  return ip_list
380
460
 
381
461
 
382
- def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
462
+ def update_discovered_ip_addresses(nb_url: str, prefix: dict, ds: Literal["nmap", "ip-fabric"]) -> tuple:
383
463
  """
384
464
  Posts new auto-discovered IP addresses to NetBox.
385
465
 
@@ -396,12 +476,16 @@ def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
396
476
  task_text = "Add Auto-Discovered IP-Addresses"
397
477
 
398
478
  # Add nmap scan ip-addresses that are not in the existing ip-addresses dict
399
- add_ips = create_ip_list(loop_list=prefix["nmap_ips"], check_list=prefix["all_ips"], is_in_both=False)
479
+ add_ips = create_ip_list(
480
+ loop_list=prefix["datasource_ips"], check_list=prefix["all_ips"], is_in_both=False
481
+ )
400
482
 
401
483
  # If ip-addresses have been found
402
484
  if add_ips:
403
485
  # Create the payload to create the ip-addresses
404
- payload = create_nb_ip_payload(parent_prefix=prefix, data=add_ips, desired_status="auto_discovered")
486
+ payload = create_nb_ip_payload(
487
+ parent_prefix=prefix, data=add_ips, ds=ds, desired_status="auto_discovered"
488
+ )
405
489
  # POST request to update the ip-addresses
406
490
  resp = post_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
407
491
 
@@ -409,7 +493,7 @@ def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
409
493
  text = "The following 'Auto-Discovered' ip-addresses had been added:"
410
494
  # The function returns the result list and True if the response is successful else False
411
495
  sub_result, sub_failed = create_nb_response_result(
412
- resp=resp, nb_type="ip", data=add_ips, task_text=task_text, text=text
496
+ resp=resp, nb_type="ip", data=add_ips, ds=ds, task_text=task_text, text=text
413
497
  )
414
498
  if "Response Json:" in sub_result[0]:
415
499
  sub_result[0] += f"\n** DEBUG **\nPayload:\n{payload}\nResponse:\n{resp.json()}\n** DEBUG **\n"
@@ -418,14 +502,14 @@ def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
418
502
 
419
503
  # Update the ip-addresses with the status 'auto_discovered' that are part of the nmap scan list
420
504
  update_ips = create_ip_list(
421
- loop_list=prefix["discovered_ips"], check_list=prefix["nmap_ips"], is_in_both=True
505
+ loop_list=prefix["discovered_ips"], check_list=prefix["datasource_ips"], is_in_both=True
422
506
  )
423
507
 
424
508
  # If ip-addresses have been found
425
509
  if update_ips:
426
510
  # Create the payload to create the ip-addresses
427
511
  payload = create_nb_ip_payload(
428
- parent_prefix=prefix, data=update_ips, desired_status="auto_discovered"
512
+ parent_prefix=prefix, data=update_ips, ds=ds, desired_status="auto_discovered"
429
513
  )
430
514
  # PATCH request to update the ip-addresses
431
515
  resp = patch_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
@@ -434,7 +518,7 @@ def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
434
518
  text = "The following 'Auto-Discovered' ip-addresses had been updated:"
435
519
  # The function returns the result list and True if the response is successful else False
436
520
  sub_result, sub_failed = create_nb_response_result(
437
- resp=resp, nb_type="ip", data=update_ips, task_text=task_text, text=text
521
+ resp=resp, nb_type="ip", data=update_ips, ds=ds, task_text=task_text, text=text
438
522
  )
439
523
  result.extend(sub_result)
440
524
  failed = True if sub_failed else failed
@@ -442,7 +526,9 @@ def update_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
442
526
  return result, failed
443
527
 
444
528
 
445
- def delete_inactive_auto_discovered_ip_addresses(nb_url: str, prefix: dict) -> tuple:
529
+ def delete_inactive_auto_discovered_ip_addresses(
530
+ nb_url: str, prefix: dict, ds: Literal["nmap", "ip-fabric"]
531
+ ) -> tuple:
446
532
  """
447
533
  Deletes inactive auto-discovered IP addresses from NetBox.
448
534
 
@@ -460,7 +546,7 @@ def delete_inactive_auto_discovered_ip_addresses(nb_url: str, prefix: dict) -> t
460
546
 
461
547
  # Delete the ip-addresses with the status 'auto_discovered' that are not in the nmap scan list
462
548
  delete_ips = create_ip_list(
463
- loop_list=prefix["discovered_ips"], check_list=prefix["nmap_ips"], is_in_both=False
549
+ loop_list=prefix["discovered_ips"], check_list=prefix["datasource_ips"], is_in_both=False
464
550
  )
465
551
 
466
552
  # If ip-addresses have been found
@@ -473,13 +559,13 @@ def delete_inactive_auto_discovered_ip_addresses(nb_url: str, prefix: dict) -> t
473
559
  text = "The following 'Auto-Discovered' ip-addresses had been deleted:"
474
560
  # The function returns the result list and True if the response is successful else False
475
561
  result, failed = create_nb_response_result(
476
- resp=resp, nb_type="ip", data=delete_ips, task_text=task_text, text=text
562
+ resp=resp, nb_type="ip", data=delete_ips, ds=ds, task_text=task_text, text=text
477
563
  )
478
564
 
479
565
  return result, failed
480
566
 
481
567
 
482
- def update_reserved_ip_addresses(nb_url: str, prefix: dict) -> tuple:
568
+ def update_reserved_ip_addresses(nb_url: str, prefix: dict, ds: Literal["nmap", "ip-fabric"]) -> tuple:
483
569
  """
484
570
  Updates the status of reserved IP addresses in NetBox if they are reachable by nmap.
485
571
 
@@ -497,13 +583,13 @@ def update_reserved_ip_addresses(nb_url: str, prefix: dict) -> tuple:
497
583
 
498
584
  # Update the ip-addresses with the status 'reserved' that are part of the nmap scan list
499
585
  update_ips = create_ip_list(
500
- loop_list=prefix["reserved_ips"], check_list=prefix["nmap_ips"], is_in_both=True
586
+ loop_list=prefix["reserved_ips"], check_list=prefix["datasource_ips"], is_in_both=True
501
587
  )
502
588
 
503
589
  # If ip-addresses have been found
504
590
  if update_ips:
505
591
  # Create the payload to update the ip-addresses
506
- payload = create_nb_ip_payload(parent_prefix=prefix, data=update_ips, desired_status="active")
592
+ payload = create_nb_ip_payload(parent_prefix=prefix, data=update_ips, ds=ds, desired_status="active")
507
593
  # PATCH request to update the ip-addresses
508
594
  resp = patch_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
509
595
 
@@ -511,13 +597,13 @@ def update_reserved_ip_addresses(nb_url: str, prefix: dict) -> tuple:
511
597
  text = "The following 'Reserved' ip-addresses had been set to status 'Active':"
512
598
  # The function returns the result list and True if the response is successful else False
513
599
  result, failed = create_nb_response_result(
514
- resp=resp, nb_type="ip", data=update_ips, task_text=task_text, text=text
600
+ resp=resp, nb_type="ip", data=update_ips, ds=ds, task_text=task_text, text=text
515
601
  )
516
602
 
517
603
  return result, failed
518
604
 
519
605
 
520
- def update_inactive_ip_addresses(nb_url: str, prefix: dict) -> tuple:
606
+ def update_inactive_ip_addresses(nb_url: str, prefix: dict, ds: Literal["nmap", "ip-fabric"]) -> tuple:
521
607
  """
522
608
  Updates the status of inactive IP addresses in NetBox if they are reachable by nmap.
523
609
 
@@ -535,13 +621,15 @@ def update_inactive_ip_addresses(nb_url: str, prefix: dict) -> tuple:
535
621
 
536
622
  # Update the ip-addresses with the status 'inactive' that are part of the nmap scan list
537
623
  inactive_ips = create_ip_list(
538
- loop_list=prefix["inactive_ips"], check_list=prefix["nmap_ips"], is_in_both=True
624
+ loop_list=prefix["inactive_ips"], check_list=prefix["datasource_ips"], is_in_both=True
539
625
  )
540
626
 
541
627
  # If ip-addresses have been found
542
628
  if inactive_ips:
543
629
  # Create the payload to update the ip-addresses
544
- payload = create_nb_ip_payload(parent_prefix=prefix, data=inactive_ips, desired_status="active")
630
+ payload = create_nb_ip_payload(
631
+ parent_prefix=prefix, data=inactive_ips, ds=ds, desired_status="active"
632
+ )
545
633
  # PATCH request to update the ip-addresses
546
634
  resp = patch_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
547
635
 
@@ -549,13 +637,15 @@ def update_inactive_ip_addresses(nb_url: str, prefix: dict) -> tuple:
549
637
  text = "The following 'Inactive' ip-addresses had been set to status 'Active':"
550
638
  # The function returns the result list and True if the response is successful else False
551
639
  result, failed = create_nb_response_result(
552
- resp=resp, nb_type="ip", data=inactive_ips, task_text=task_text, text=text
640
+ resp=resp, nb_type="ip", data=inactive_ips, ds=ds, task_text=task_text, text=text
553
641
  )
554
642
 
555
643
  return result, failed
556
644
 
557
645
 
558
- def update_active_ip_addresses(nb_url: str, prefix: dict, overwrite_active: list[str]) -> tuple:
646
+ def update_active_ip_addresses(
647
+ nb_url: str, prefix: dict, overwrite_active: list[str], ds: Literal["nmap", "ip-fabric"]
648
+ ) -> tuple:
559
649
  """
560
650
  Updates the status of active IP addresses in NetBox if they are not reachable by nmap.
561
651
 
@@ -573,13 +663,13 @@ def update_active_ip_addresses(nb_url: str, prefix: dict, overwrite_active: list
573
663
 
574
664
  # Update the ip-addresses with the status 'active' that are part of the nmap scan list
575
665
  active_ips = create_ip_list(
576
- loop_list=prefix["active_ips"], check_list=prefix["nmap_ips"], is_in_both=True
666
+ loop_list=prefix["active_ips"], check_list=prefix["datasource_ips"], is_in_both=True
577
667
  )
578
668
 
579
669
  # If ip-addresses have been found
580
670
  if active_ips:
581
671
  # Create the payload to update the ip-addresses
582
- payload = create_nb_ip_payload(parent_prefix=prefix, data=active_ips, desired_status="active")
672
+ payload = create_nb_ip_payload(parent_prefix=prefix, data=active_ips, ds=ds, desired_status="active")
583
673
  # PATCH request to update the ip-addresses
584
674
  resp = patch_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
585
675
 
@@ -587,14 +677,14 @@ def update_active_ip_addresses(nb_url: str, prefix: dict, overwrite_active: list
587
677
  text = "The following 'Active' ip-addresses had been updated:"
588
678
  # The function returns the result list and True if the response is successful else False
589
679
  sub_result, sub_failed = create_nb_response_result(
590
- resp=resp, nb_type="ip", data=active_ips, task_text=task_text, text=text
680
+ resp=resp, nb_type="ip", data=active_ips, ds=ds, task_text=task_text, text=text
591
681
  )
592
682
  result.extend(sub_result)
593
683
  failed = True if sub_failed else failed
594
684
 
595
685
  # Update the ip-addresses with the status 'active' that are not part of the nmap scan list
596
686
  inactive_ips = create_ip_list(
597
- loop_list=prefix["active_ips"], check_list=prefix["nmap_ips"], is_in_both=False
687
+ loop_list=prefix["active_ips"], check_list=prefix["datasource_ips"], is_in_both=False
598
688
  )
599
689
  # Create a new list to exclude the overwrite_active ip-addresses
600
690
  inactive_ips = [ip for ip in inactive_ips if ip["address"] not in overwrite_active]
@@ -602,7 +692,9 @@ def update_active_ip_addresses(nb_url: str, prefix: dict, overwrite_active: list
602
692
  # If ip-addresses have been found
603
693
  if inactive_ips:
604
694
  # Create the payload to update the ip-addresses
605
- payload = create_nb_ip_payload(parent_prefix=prefix, data=inactive_ips, desired_status="inactive")
695
+ payload = create_nb_ip_payload(
696
+ parent_prefix=prefix, data=inactive_ips, ds=ds, desired_status="inactive"
697
+ )
606
698
  # PATCH request to update the ip-addresses
607
699
  resp = patch_nb_resources(url=f"{nb_url}/api/ipam/ip-addresses/", payload=payload)
608
700
 
@@ -610,7 +702,7 @@ def update_active_ip_addresses(nb_url: str, prefix: dict, overwrite_active: list
610
702
  text = "The following 'Active' ip-addresses had been set to status 'Inactive':"
611
703
  # The function returns the result list and True if the response is successful else False
612
704
  sub_result, sub_failed = create_nb_response_result(
613
- resp=resp, nb_type="ip", data=inactive_ips, task_text=task_text, text=text
705
+ resp=resp, nb_type="ip", data=inactive_ips, ds=ds, task_text=task_text, text=text
614
706
  )
615
707
  result.extend(sub_result)
616
708
  failed = True if sub_failed else failed
@@ -639,7 +731,7 @@ def set_results_changed_failed(results, result, changed, sub_failed, failed) ->
639
731
  return results, changed, failed
640
732
 
641
733
 
642
- def update_active_netbox_prefix_ip_addresses(prefix: list, *overwrite_active) -> tuple:
734
+ def update_netbox_prefix_ip_addresses(prefix: list, *args) -> tuple:
643
735
  """
644
736
  This function can be used within a ThreadPoolExecutor. Update the IP addresses of a active NetBox prefix
645
737
  with the following information:
@@ -656,9 +748,10 @@ def update_active_netbox_prefix_ip_addresses(prefix: list, *overwrite_active) ->
656
748
  """
657
749
  # Create a list to collect the task results
658
750
  results = []
659
- # overwrite_active is a tuple as its passed by the thread pool executor via the *args
660
- # Therefor the tuple will be changes into a list
661
- overwrite_active = list(overwrite_active)
751
+ # Set the datasource by the first arg ('namp' or 'ip-fabric') and make sure its a string
752
+ ds = str(args[0])
753
+ # Set the ip-address list to overwrite the status as active by the second arg and make sure its a list
754
+ overwrite_active = list(args[1])
662
755
  # Boolian to check if any ip-addresses have been changed and the overall failed status
663
756
  changed = False
664
757
  failed = False
@@ -669,29 +762,31 @@ def update_active_netbox_prefix_ip_addresses(prefix: list, *overwrite_active) ->
669
762
  # Get the base url of the NetBox instance
670
763
  nb_url = base_url(url=prefix["url"], with_path=False)
671
764
 
672
- # Get all NetBox ip-addresses, scan the prefix with nmap add lists to the prefix dict
673
- result, prefix = get_netbox_ip_addresses_and_scan_prefix(nb_url=nb_url, prefix=prefix)
765
+ # Scan the prefix with nmap and add the list to the prefix dict or get the ip-addresses from IP-Fabric
766
+ result, prefix, failed = get_nb_ips_and_external_datasource(nb_url=nb_url, prefix=prefix, ds=ds)
674
767
  results.extend(result)
768
+ if failed:
769
+ return results, failed
675
770
 
676
771
  # Add new 'auto-discovered' ip-addresses
677
- result, sub_failed = update_discovered_ip_addresses(nb_url=nb_url, prefix=prefix)
772
+ result, sub_failed = update_discovered_ip_addresses(nb_url=nb_url, prefix=prefix, ds=ds)
678
773
  results, changed, failed = set_results_changed_failed(results, result, changed, sub_failed, failed)
679
774
 
680
775
  # Delete inactive 'auto-discovered' ip-addresses
681
- result, sub_failed = delete_inactive_auto_discovered_ip_addresses(nb_url=nb_url, prefix=prefix)
776
+ result, sub_failed = delete_inactive_auto_discovered_ip_addresses(nb_url=nb_url, prefix=prefix, ds=ds)
682
777
  results, changed, failed = set_results_changed_failed(results, result, changed, sub_failed, failed)
683
778
 
684
779
  # Update 'reserved' ip-addresses -> set status to 'active'
685
- result, sub_failed = update_reserved_ip_addresses(nb_url=nb_url, prefix=prefix)
780
+ result, sub_failed = update_reserved_ip_addresses(nb_url=nb_url, prefix=prefix, ds=ds)
686
781
  results, changed, failed = set_results_changed_failed(results, result, changed, sub_failed, failed)
687
782
 
688
783
  # Update 'inactive' ip-addresses -> set status to 'active'
689
- result, sub_failed = update_inactive_ip_addresses(nb_url=nb_url, prefix=prefix)
784
+ result, sub_failed = update_inactive_ip_addresses(nb_url=nb_url, prefix=prefix, ds=ds)
690
785
  results, changed, failed = set_results_changed_failed(results, result, changed, sub_failed, failed)
691
786
 
692
787
  # Update 'active' ip-addresses -> set status to 'active' or 'inactive'
693
788
  result, sub_failed = update_active_ip_addresses(
694
- nb_url=nb_url, prefix=prefix, overwrite_active=overwrite_active
789
+ nb_url=nb_url, prefix=prefix, overwrite_active=overwrite_active, ds=ds
695
790
  )
696
791
  results, changed, failed = set_results_changed_failed(results, result, changed, sub_failed, failed)
697
792
 
@@ -816,7 +911,9 @@ def update_all_netbox_vlans(vlan: dict) -> tuple:
816
911
  return results, failed
817
912
 
818
913
 
819
- def run_thread_pool(title: str, task: Callable, thread_list: list[dict], args: tuple = ()) -> list:
914
+ def run_thread_pool(
915
+ title: str, task: Callable, thread_list: list[dict], max_workers: int = 50, args: tuple = ()
916
+ ) -> list:
820
917
  """
821
918
  Runs a thread pool with a given task for each item in the thread list.
822
919
 
@@ -843,7 +940,7 @@ def run_thread_pool(title: str, task: Callable, thread_list: list[dict], args: t
843
940
  return []
844
941
 
845
942
  # Create a ThreadPoolExecutor
846
- with ThreadPoolExecutor(max_workers=100) as executor:
943
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
847
944
  # Submit a new task for each prefix to update and collect the tasks results
848
945
  threads = [executor.submit(task, item, *args) for item in thread_list]
849
946
 
@@ -881,7 +978,7 @@ def print_thread_pool_results(title: str, thread_result, fail_hard: bool = False
881
978
  return failed_task
882
979
 
883
980
 
884
- def main(nr_config: str, overwrite_active: list[str] = None) -> None:
981
+ def main(nr_config: str, nmap_scan: bool = False, overwrite_active: list[str] = None) -> None:
885
982
  """
886
983
  Main function is intended to import and execute by other scripts.
887
984
  It loads NetBox inventory, scan active prefixes, and update IP addresses and VLANs.
@@ -918,11 +1015,20 @@ def main(nr_config: str, overwrite_active: list[str] = None) -> None:
918
1015
  # Get the NetBox URL (Authentication token will be loaded as nb_token env variable)
919
1016
  nb_url = nr_config_dict["inventory"]["options"]["nb_url"]
920
1017
 
921
- # Load Active NetBox Prefixes
922
- nb_active_prefixes = load_netbox_data(
923
- task_text="Load Active NetBox Prefixes",
1018
+ # Load Active NetBox Prefixes from Tenant 'none', 'oob', 'tier-1' and 'tier-2'
1019
+ # These prefixes will be scanned with nmap and updated
1020
+ nb_active_oob_t1_t2_prefixes = load_netbox_data(
1021
+ task_text="Load Active OOB/T1/T2 NetBox Prefixes",
1022
+ nb_api_url=f"{nb_url}/api/ipam/prefixes/",
1023
+ query={"status": "active", "tenant": ["none", "oob", "tier-1", "tier-2"], "mark_utilized": "false"},
1024
+ )
1025
+
1026
+ # Load Active/Inventory NetBox Prefixes from Tenant 'tier-3' and 'tier-4'
1027
+ # These prefixes will be updated with input from IP-Fabric
1028
+ nb_active_inventory_t3_t4_prefixes = load_netbox_data(
1029
+ task_text="Load Active & Inventory T3/T4 NetBox Prefixes",
924
1030
  nb_api_url=f"{nb_url}/api/ipam/prefixes/",
925
- query={"status": "active", "mark_utilized": "false"},
1031
+ query={"status": ["active", "inventory"], "tenant": ["tier-3", "tier-4"], "mark_utilized": "false"},
926
1032
  )
927
1033
 
928
1034
  # Load NetBox Non-Container Prefixes
@@ -940,20 +1046,42 @@ def main(nr_config: str, overwrite_active: list[str] = None) -> None:
940
1046
  query={},
941
1047
  )
942
1048
 
943
- #### Scan Active NetBox Prefixes and Update IP-Addresses Status, DNS-Name and Ports ######################
1049
+ #### Nmap Scan Active NetBox Prefixes and Update IP-Addresses ###########################################
1050
+
1051
+ if nmap_scan:
1052
+ # Set the task title
1053
+ title = "Nmap Scan Active OOB/T1/T2 NetBox Prefixes and Update IP-Addresses"
1054
+
1055
+ # Run the thread pool to update all NetBox IP-Addresses Status and DNS-Name
1056
+ # 1. arg is the input type ('nmap' or 'ip-fabric')
1057
+ # 2. arg is the list of active IP addresses to overwrite
1058
+ thread_result = run_thread_pool(
1059
+ title=title,
1060
+ task=update_netbox_prefix_ip_addresses,
1061
+ thread_list=nb_active_oob_t1_t2_prefixes,
1062
+ max_workers=50,
1063
+ args=("nmap", overwrite_active),
1064
+ )
1065
+ # Print the thread pool results and exit the script if any task has failed
1066
+ print_thread_pool_results(title=title, thread_result=thread_result, fail_hard=True)
1067
+
1068
+ #### IP-Fabric Update Active & Inventory NetBox Prefixes IP-Addresses ###################################
944
1069
 
945
1070
  # Set the task title
946
- title = "Scan Active NetBox Prefixes and Update IP-Addresses Status, DNS-Name and Ports"
1071
+ title = "IP-Fabric Update Active & Inventory T3/T4 NetBox Prefixes IP-Addresses"
947
1072
 
948
1073
  # Run the thread pool to update all NetBox IP-Addresses Status and DNS-Name
1074
+ # 1. arg is the input type ('nmap' or 'ip-fabric')
1075
+ # 2. arg is the list of active IP addresses to overwrite
949
1076
  thread_result = run_thread_pool(
950
1077
  title=title,
951
- task=update_active_netbox_prefix_ip_addresses,
952
- thread_list=nb_active_prefixes,
953
- args=overwrite_active,
1078
+ task=update_netbox_prefix_ip_addresses,
1079
+ thread_list=nb_active_inventory_t3_t4_prefixes,
1080
+ max_workers=10,
1081
+ args=("ip-fabric", overwrite_active),
954
1082
  )
955
1083
  # Print the thread pool results and exit the script if any task has failed
956
- print_thread_pool_results(title=title, thread_result=thread_result, fail_hard="hard")
1084
+ print_thread_pool_results(title=title, thread_result=thread_result, fail_hard=True)
957
1085
 
958
1086
  #### Update all IP-Addresses Tags and other Fields ######################################################
959
1087
 
@@ -964,10 +1092,11 @@ def main(nr_config: str, overwrite_active: list[str] = None) -> None:
964
1092
  thread_result = run_thread_pool(
965
1093
  title=title,
966
1094
  task=update_all_netbox_ip_addresses,
1095
+ max_workers=100,
967
1096
  thread_list=nb_subnet_prefixes,
968
1097
  )
969
1098
  # Print the thread pool results and exit the script if any task has failed
970
- print_thread_pool_results(title=title, thread_result=thread_result, fail_hard="hard")
1099
+ print_thread_pool_results(title=title, thread_result=thread_result, fail_hard=True)
971
1100
 
972
1101
  #### Update all VLANs Tags and other Fields #############################################################
973
1102
 
@@ -978,7 +1107,8 @@ def main(nr_config: str, overwrite_active: list[str] = None) -> None:
978
1107
  thread_result = run_thread_pool(
979
1108
  title=title,
980
1109
  task=update_all_netbox_vlans,
1110
+ max_workers=100,
981
1111
  thread_list=nb_vlans,
982
1112
  )
983
1113
  # Print the thread pool results and exit the script if any task has failed
984
- print_thread_pool_results(title=title, thread_result=thread_result, fail_hard="hard")
1114
+ print_thread_pool_results(title=title, thread_result=thread_result, fail_hard=True)
@@ -104,6 +104,64 @@ def init_args_for_nornir_config_filepath() -> str:
104
104
  return nr_config
105
105
 
106
106
 
107
+ def init_args_for_ipam_update() -> str:
108
+ """
109
+ This function initialze arguments to specify which NetBox instance and Nornir config filepath to use.
110
+ """
111
+ task_text = "Argparse verify arguments"
112
+ print_task_name(text=task_text)
113
+
114
+ # Load environment variables or raise a TypeError when is None
115
+ env_vars = get_env_vars(envs=["NR_CONFIG_PROD", "NR_CONFIG_TEST"], task_text=task_text)
116
+ nr_config_prod = env_vars["NR_CONFIG_PROD"]
117
+ nr_config_test = env_vars["NR_CONFIG_TEST"]
118
+
119
+ # Define the arguments which needs to be given to the script execution
120
+ argparser = CustomArgParse(
121
+ prog=os.path.basename(__main__.__file__),
122
+ description="Specify the NetBox PROD or TEST instance and Nornir config filepath to be used",
123
+ epilog="One of the two mandatory arguments is required.",
124
+ argument_default=argparse.SUPPRESS,
125
+ formatter_class=CustomArgParseWidthFormatter,
126
+ )
127
+ # Add all NetBox arguments
128
+ argparser.add_argument(
129
+ "--prod",
130
+ action="store_true",
131
+ help=f"use the NetBox 'PROD' instance and Nornir config '{nr_config_prod}'",
132
+ )
133
+ argparser.add_argument(
134
+ "--test",
135
+ action="store_true",
136
+ help=f"use the NetBox 'TEST' instance and Nornir config '{nr_config_test}'",
137
+ )
138
+ # Add the optional rebuild argument
139
+ argparser.add_argument(
140
+ "--nmap",
141
+ action="store_true",
142
+ default=False,
143
+ help="enable additionally to IP-Fabric a NMAP scan (default: no NMAP scan)",
144
+ )
145
+ # Verify the provided arguments and print the custom argparse error message in case any error or wrong
146
+ # arguments are present and exit the script
147
+ args = argparser.parse_args()
148
+
149
+ # Verify the NetBox instance and Nornir config filepath
150
+ if not (hasattr(args, "prod") or hasattr(args, "test")):
151
+ argparser.error("No NetBox instance specified, add --prod or --test")
152
+
153
+ # Set the NetBox instance and the Nornir config file based on the arguments
154
+ nb_instance = "TEST" if args.test else "PROD"
155
+ nr_config = nr_config_test if args.test else nr_config_prod
156
+
157
+ # If argparser.parse_args() is successful -> no argparse error message
158
+ print(task_info(text=task_text, changed=False))
159
+ print(f"'{task_text}' -> ArgparseResponse <Success: True>")
160
+ print(f"-> Run on the NetBox '{nb_instance}' instance and Nornir config '{nr_config}'")
161
+
162
+ return nr_config, args
163
+
164
+
107
165
  def get_nb_resources(url: str, params: Dict[str, Any] = {}) -> List[Dict[str, Any]]:
108
166
  """
109
167
  TBD
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nornir-collection
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Nornir-Collection contains network automation functions and complete IaC workflows with Nornir and other python libraries. It contains Nornir tasks and general functions in Nornir style.
5
5
  Author: Willi Kubny
6
6
  Author-email: willi.kubny@gmail.ch
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: nornir==3.3.0
14
14
  Requires-Dist: nornir-jinja2==0.2.0
15
- Requires-Dist: nornir-salt==0.20.3
15
+ Requires-Dist: nornir-salt==0.22.2
16
16
  Requires-Dist: nornir-netmiko==1.0.1
17
17
  Requires-Dist: nornir-scrapli==2023.7.30
18
18
  Requires-Dist: scrapli-netconf==2023.7.30
@@ -20,6 +20,7 @@ Requires-Dist: scrapli[ssh2]
20
20
  Requires-Dist: scrapli[genie]
21
21
  Requires-Dist: nornir-utils==0.2.0
22
22
  Requires-Dist: pynetbox==7.4.0
23
+ Requires-Dist: ipfabric==7.0.2
23
24
  Requires-Dist: py-pure-client==1.51.0
24
25
  Requires-Dist: nornir-pyfgt==1.0.3
25
26
  Requires-Dist: python-nmap==0.7.1
@@ -39,12 +39,12 @@ setup.py
39
39
  ./nornir_collection/netbox/__init__.py
40
40
  ./nornir_collection/netbox/custom_script.py
41
41
  ./nornir_collection/netbox/inventory.py
42
- ./nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py
43
42
  ./nornir_collection/netbox/set_device_status.py
44
43
  ./nornir_collection/netbox/sync_datasource.py
45
44
  ./nornir_collection/netbox/update_cisco_inventory_data.py
46
45
  ./nornir_collection/netbox/update_cisco_support_plugin_data.py
47
46
  ./nornir_collection/netbox/update_fortinet_inventory_data.py
47
+ ./nornir_collection/netbox/update_prefixes_ip_addresses.py
48
48
  ./nornir_collection/netbox/update_purestorage_inventory_data.py
49
49
  ./nornir_collection/netbox/utils.py
50
50
  ./nornir_collection/netbox/verify_device_primary_ip.py
@@ -98,12 +98,12 @@ nornir_collection/fortinet/utils.py
98
98
  nornir_collection/netbox/__init__.py
99
99
  nornir_collection/netbox/custom_script.py
100
100
  nornir_collection/netbox/inventory.py
101
- nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py
102
101
  nornir_collection/netbox/set_device_status.py
103
102
  nornir_collection/netbox/sync_datasource.py
104
103
  nornir_collection/netbox/update_cisco_inventory_data.py
105
104
  nornir_collection/netbox/update_cisco_support_plugin_data.py
106
105
  nornir_collection/netbox/update_fortinet_inventory_data.py
106
+ nornir_collection/netbox/update_prefixes_ip_addresses.py
107
107
  nornir_collection/netbox/update_purestorage_inventory_data.py
108
108
  nornir_collection/netbox/utils.py
109
109
  nornir_collection/netbox/verify_device_primary_ip.py
@@ -1,6 +1,6 @@
1
1
  nornir==3.3.0
2
2
  nornir-jinja2==0.2.0
3
- nornir-salt==0.20.3
3
+ nornir-salt==0.22.2
4
4
  nornir-netmiko==1.0.1
5
5
  nornir-scrapli==2023.7.30
6
6
  scrapli-netconf==2023.7.30
@@ -8,6 +8,7 @@ scrapli[ssh2]
8
8
  scrapli[genie]
9
9
  nornir-utils==0.2.0
10
10
  pynetbox==7.4.0
11
+ ipfabric==7.0.2
11
12
  py-pure-client==1.51.0
12
13
  nornir-pyfgt==1.0.3
13
14
  python-nmap==0.7.1
@@ -11,7 +11,7 @@ with open("requirements.txt", encoding="utf-8") as f:
11
11
 
12
12
  setuptools.setup(
13
13
  name="nornir-collection",
14
- version="0.0.10",
14
+ version="0.0.12",
15
15
  author="Willi Kubny",
16
16
  author_email="willi.kubny@gmail.ch",
17
17
  description="Nornir-Collection contains network automation functions and complete IaC workflows with \