nornir-collection 0.0.1__py3-none-any.whl
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.
- nornir_collection/__init__.py +0 -0
- nornir_collection/batfish/__init__.py +0 -0
- nornir_collection/batfish/assert_config.py +358 -0
- nornir_collection/batfish/utils.py +129 -0
- nornir_collection/cisco/__init__.py +0 -0
- nornir_collection/cisco/configuration_management/__init__.py +0 -0
- nornir_collection/cisco/configuration_management/cli/__init__.py +0 -0
- nornir_collection/cisco/configuration_management/cli/config_tasks.py +569 -0
- nornir_collection/cisco/configuration_management/cli/config_workflow.py +107 -0
- nornir_collection/cisco/configuration_management/cli/show_tasks.py +677 -0
- nornir_collection/cisco/configuration_management/netconf/__init__.py +0 -0
- nornir_collection/cisco/configuration_management/netconf/config_tasks.py +564 -0
- nornir_collection/cisco/configuration_management/netconf/config_workflow.py +298 -0
- nornir_collection/cisco/configuration_management/netconf/nr_cfg_iosxe_netconf.py +186 -0
- nornir_collection/cisco/configuration_management/netconf/ops_tasks.py +307 -0
- nornir_collection/cisco/configuration_management/processor.py +151 -0
- nornir_collection/cisco/configuration_management/pyats.py +236 -0
- nornir_collection/cisco/configuration_management/restconf/__init__.py +0 -0
- nornir_collection/cisco/configuration_management/restconf/cisco_rpc.py +514 -0
- nornir_collection/cisco/configuration_management/restconf/config_workflow.py +95 -0
- nornir_collection/cisco/configuration_management/restconf/tasks.py +325 -0
- nornir_collection/cisco/configuration_management/utils.py +511 -0
- nornir_collection/cisco/software_upgrade/__init__.py +0 -0
- nornir_collection/cisco/software_upgrade/cisco_software_upgrade.py +283 -0
- nornir_collection/cisco/software_upgrade/utils.py +794 -0
- nornir_collection/cisco/support_api/__init__.py +0 -0
- nornir_collection/cisco/support_api/api_calls.py +1173 -0
- nornir_collection/cisco/support_api/cisco_maintenance_report.py +221 -0
- nornir_collection/cisco/support_api/cisco_support.py +727 -0
- nornir_collection/cisco/support_api/reports.py +747 -0
- nornir_collection/cisco/support_api/utils.py +316 -0
- nornir_collection/fortinet/__init__.py +0 -0
- nornir_collection/fortinet/utils.py +36 -0
- nornir_collection/git.py +224 -0
- nornir_collection/netbox/__init__.py +0 -0
- nornir_collection/netbox/custom_script.py +107 -0
- nornir_collection/netbox/inventory.py +360 -0
- nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py +989 -0
- nornir_collection/netbox/set_device_status.py +67 -0
- nornir_collection/netbox/sync_datasource.py +111 -0
- nornir_collection/netbox/update_cisco_inventory_data.py +158 -0
- nornir_collection/netbox/update_cisco_support_plugin_data.py +339 -0
- nornir_collection/netbox/update_fortinet_inventory_data.py +161 -0
- nornir_collection/netbox/update_purestorage_inventory_data.py +144 -0
- nornir_collection/netbox/utils.py +261 -0
- nornir_collection/netbox/verify_device_primary_ip.py +202 -0
- nornir_collection/nornir_plugins/__init__.py +0 -0
- nornir_collection/nornir_plugins/inventory/__init__.py +0 -0
- nornir_collection/nornir_plugins/inventory/netbox.py +250 -0
- nornir_collection/nornir_plugins/inventory/staggered_yaml.py +143 -0
- nornir_collection/nornir_plugins/inventory/utils.py +277 -0
- nornir_collection/purestorage/__init__.py +0 -0
- nornir_collection/purestorage/utils.py +53 -0
- nornir_collection/utils.py +741 -0
- nornir_collection-0.0.1.dist-info/LICENSE +21 -0
- nornir_collection-0.0.1.dist-info/METADATA +136 -0
- nornir_collection-0.0.1.dist-info/RECORD +59 -0
- nornir_collection-0.0.1.dist-info/WHEEL +5 -0
- nornir_collection-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module updates the serial numbers of Fortinet devices in NetBox using Nornir.
|
4
|
+
The Main function is intended to import and execute by other scripts.
|
5
|
+
"""
|
6
|
+
|
7
|
+
|
8
|
+
from nornir.core import Nornir
|
9
|
+
from nornir.core.filter import F
|
10
|
+
from nornir.core.task import Task, Result
|
11
|
+
from nornir_collection.nornir_plugins.inventory.utils import init_nornir
|
12
|
+
from nornir_collection.fortinet.utils import get_fgt_resources
|
13
|
+
from nornir_collection.netbox.utils import _nb_patch_resources, _nb_create_payload_patch_device_serials
|
14
|
+
from nornir_collection.utils import (
|
15
|
+
print_task_title,
|
16
|
+
print_result,
|
17
|
+
task_name,
|
18
|
+
task_info,
|
19
|
+
exit_error,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
__author__ = "Willi Kubny"
|
24
|
+
__maintainer__ = "Willi Kubny"
|
25
|
+
__version__ = "1.0"
|
26
|
+
__license__ = "MIT"
|
27
|
+
__email__ = "willi.kubny@dreyfusbank.ch"
|
28
|
+
__status__ = "Production"
|
29
|
+
|
30
|
+
|
31
|
+
def update_serial_number(task: Task) -> Result:
|
32
|
+
"""
|
33
|
+
Update the device serial number in NetBox based on the serial number retrieved from the Fortinet device.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
task (Task): The Nornir task object.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
Result: The Nornir result object.
|
40
|
+
"""
|
41
|
+
task_text = "Update device serial number"
|
42
|
+
|
43
|
+
# Get the Fortinet device serial number from the Fortinet Rest API
|
44
|
+
response = get_fgt_resources(task=task, url="api/v2/monitor/system/ha-peer/")
|
45
|
+
|
46
|
+
# Verify the response code and return the result
|
47
|
+
if response.status_code != 200:
|
48
|
+
result = (
|
49
|
+
f"'{task.name}' -> APIResponse: <Success: False>\n"
|
50
|
+
+ f"-> Response Status Code: {response.status_code}\n"
|
51
|
+
+ f"-> Response Text: {response.text}"
|
52
|
+
)
|
53
|
+
# Return the Nornir result
|
54
|
+
return Result(host=task.host, result=result, changed=False, failed=True)
|
55
|
+
|
56
|
+
# Create the serials dict to us in the function _nb_create_payload_patch_device_serials
|
57
|
+
serials = {}
|
58
|
+
if task.host["virtual_chassis"]:
|
59
|
+
# Create a serials dict to map the serials to the virtual chassis member hostname
|
60
|
+
response = {serial["hostname"]: serial["serial_no"] for serial in response.json()["results"]}
|
61
|
+
# Create the serials dict for a HA cluster
|
62
|
+
serials["1"] = response[f"{task.host['virtual_chassis']['master']['name']}"]
|
63
|
+
if "members" in task.host["virtual_chassis"]:
|
64
|
+
for num, member in enumerate(task.host["virtual_chassis"]["members"], start=2):
|
65
|
+
serials[str(num)] = response[f"{member['name']}"]
|
66
|
+
else:
|
67
|
+
# Create the serials dict for a single device
|
68
|
+
serials["1"] = response.json()["serial"]
|
69
|
+
|
70
|
+
# Create the API payload to update the device serial number or return the Nornir result as failed
|
71
|
+
payload = _nb_create_payload_patch_device_serials(task=task, task_text=task_text, serials=serials)
|
72
|
+
|
73
|
+
# Get the NetBox url from the inventory options and update the device serial number in NetBox
|
74
|
+
url = f"{task.nornir.config.inventory.options['nb_url']}/api/dcim/devices/"
|
75
|
+
result = _nb_patch_resources(task=task, task_text=task_text, url=url, payload=payload)
|
76
|
+
result += f"\n-> Serials: {[item['serial'] for item in payload]}"
|
77
|
+
|
78
|
+
# Return the result
|
79
|
+
return Result(host=task.host, result=result, changed=False, failed=False)
|
80
|
+
|
81
|
+
|
82
|
+
def update_fortinet_inventory_data(nr: Nornir) -> bool:
|
83
|
+
"""
|
84
|
+
Update NetBox Cisco Device Inventory Data. This function runs a Nornir task to update the serial number
|
85
|
+
of Fortinet devices in NetBox. The Nornir inventory is filtered by device manufacturer (fortinet). If any
|
86
|
+
of the tasks fail, the function returns True, otherwise it returns False.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
nr: The Nornir inventory object.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
bool: True if any of the tasks failed, False otherwise.
|
93
|
+
"""
|
94
|
+
# pylint: disable=invalid-name
|
95
|
+
# Track if one of the tasks has failed
|
96
|
+
failed = False
|
97
|
+
|
98
|
+
print_task_title(title="Update NetBox Fortinet Device Inventory Data")
|
99
|
+
|
100
|
+
# Filter Nornir inventory by device manufacturer (a device can have only one manufacturer)
|
101
|
+
nr_fortinet = nr.filter(
|
102
|
+
F(device_type__manufacturer__slug__contains="fortinet") & F(status__value="active")
|
103
|
+
)
|
104
|
+
task_text = "Filter nornir inventory"
|
105
|
+
print(task_name(text=task_text))
|
106
|
+
print(task_info(text=task_text, changed=False))
|
107
|
+
print(
|
108
|
+
f"'{task_text}' -> NornirResult <Success: True>\n"
|
109
|
+
+ "-> Filter condition: Device manufacturer slug contains 'fortinet'\n"
|
110
|
+
+ " & Device status value is 'active'"
|
111
|
+
)
|
112
|
+
|
113
|
+
# Run the custom Nornir task update_serial_number
|
114
|
+
name = "NETBOX update device serial number"
|
115
|
+
result = nr_fortinet.run(name=name, task=update_serial_number, on_failed=True)
|
116
|
+
# Print the whole result for each host
|
117
|
+
print_result(result)
|
118
|
+
if result.failed:
|
119
|
+
failed = True
|
120
|
+
|
121
|
+
return failed
|
122
|
+
|
123
|
+
|
124
|
+
def main(nr_config: str) -> None:
|
125
|
+
"""
|
126
|
+
Main function is intended to import and execute by other scripts.
|
127
|
+
It initialize Nornir and update Fortinet inventory data in NetBox.
|
128
|
+
|
129
|
+
* Args:
|
130
|
+
* nr_config (str): Path to the Nornir configuration file.
|
131
|
+
|
132
|
+
* Steps:
|
133
|
+
* Initializes the Nornir inventory object using the provided configuration file.
|
134
|
+
* Runs the task to update Fortinet inventory data in NetBox.
|
135
|
+
* Checks the result of the update task and exits the script with an error message if the task failed.
|
136
|
+
|
137
|
+
* Exits:
|
138
|
+
* Exits with code 1 if the Nornir configuration file is empty or if any task fails.
|
139
|
+
"""
|
140
|
+
# pylint: disable=invalid-name
|
141
|
+
|
142
|
+
#### Initialize Nornir ##################################################################################
|
143
|
+
|
144
|
+
# Initialize, transform and filter the Nornir inventory are return the filtered Nornir object
|
145
|
+
# Define data to load from NetBox in addition to the base Nornir inventory plugin
|
146
|
+
add_netbox_data = {"load_virtual_chassis_data": True}
|
147
|
+
nr = init_nornir(config_file=nr_config, add_netbox_data=add_netbox_data)
|
148
|
+
|
149
|
+
#### Run Nornir Cisco Support Plugin Update Tasks #######################################################
|
150
|
+
|
151
|
+
# Update NetBox Fortigate inventory data
|
152
|
+
result_failed = update_fortinet_inventory_data(nr=nr)
|
153
|
+
|
154
|
+
# Check the result and exit the script with exit code 1 and a error message
|
155
|
+
if result_failed:
|
156
|
+
text = "Update NetBox Fortinet Device Inventory Data with Nornir"
|
157
|
+
print(task_name(text=text))
|
158
|
+
exit_error(
|
159
|
+
task_text=f"{text} Failed",
|
160
|
+
msg="Check the result details for failed Nornir tasks",
|
161
|
+
)
|
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module updates the serial number of Pure Storage devices in NetBox using Nornir.
|
4
|
+
The Main function is intended to import and execute by other scripts.
|
5
|
+
"""
|
6
|
+
|
7
|
+
|
8
|
+
from nornir.core import Nornir
|
9
|
+
from nornir.core.filter import F
|
10
|
+
from nornir.core.task import Task, Result
|
11
|
+
from nornir_collection.nornir_plugins.inventory.utils import init_nornir
|
12
|
+
from nornir_collection.purestorage.utils import _purestorage_get_connection
|
13
|
+
from nornir_collection.netbox.utils import _nb_patch_resources
|
14
|
+
from nornir_collection.utils import (
|
15
|
+
print_task_title,
|
16
|
+
print_result,
|
17
|
+
task_name,
|
18
|
+
task_info,
|
19
|
+
exit_error,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
__author__ = "Willi Kubny"
|
24
|
+
__maintainer__ = "Willi Kubny"
|
25
|
+
__version__ = "1.0"
|
26
|
+
__license__ = "MIT"
|
27
|
+
__email__ = "willi.kubny@dreyfusbank.ch"
|
28
|
+
__status__ = "Production"
|
29
|
+
|
30
|
+
|
31
|
+
def update_serial_number(task: Task) -> Result:
|
32
|
+
"""
|
33
|
+
Update the serial number of a device in NetBox with the one retrieved from a Pure Storage FlashArray.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
task (Task): A Nornir task object representing the device to update.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
Result: A Nornir result object with the outcome of the operation.
|
40
|
+
"""
|
41
|
+
task_text = "Update device serial number"
|
42
|
+
|
43
|
+
# Manually create the PureStorage connection or return the Nornir result as failed
|
44
|
+
conn = _purestorage_get_connection(task=task)
|
45
|
+
|
46
|
+
# Get all hardware details from the FlashArray
|
47
|
+
response = conn.get_hardware()
|
48
|
+
# Get the serial number of the chassis
|
49
|
+
serial = [item["serial"] for item in response.items if item["type"] == "chassis"][0]
|
50
|
+
if not serial:
|
51
|
+
# Return the Nornir result as failed
|
52
|
+
result = f"'{task_text}' -> APIResponse: <Success: False>\n-> Device serial not found in API response"
|
53
|
+
return Result(host=task.host, result=result, failed=True)
|
54
|
+
|
55
|
+
# Create the API payload to update the device serial number
|
56
|
+
payload = [{"id": task.host["id"], "serial": serial}]
|
57
|
+
|
58
|
+
# Get the NetBox url from the inventory options and update the device serial number in NetBox
|
59
|
+
url = f"{task.nornir.config.inventory.options['nb_url']}/api/dcim/devices/"
|
60
|
+
result = _nb_patch_resources(task=task, task_text=task_text, url=url, payload=payload)
|
61
|
+
result += f"\n-> Serial: {serial}"
|
62
|
+
|
63
|
+
# Return the result
|
64
|
+
return Result(host=task.host, result=result, failed=False)
|
65
|
+
|
66
|
+
|
67
|
+
def update_purestorage_inventory_data(nr: Nornir) -> bool:
|
68
|
+
"""
|
69
|
+
Update Pure-Storage device inventory data in NetBox. This function runs a custom Nornir task to update
|
70
|
+
the serial number of each Pure-Storage device in NetBox.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
nr (Nornir): The Nornir object representing the inventory.
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
bool: True if any of the tasks failed, False otherwise.
|
77
|
+
"""
|
78
|
+
# pylint: disable=invalid-name
|
79
|
+
# Track if one of the tasks has failed
|
80
|
+
failed = False
|
81
|
+
|
82
|
+
print_task_title(title="Update NetBox Pure-Storage Device Inventory Data")
|
83
|
+
|
84
|
+
# Filter Nornir inventory by device manufacturer (a device can have only one manufacturer)
|
85
|
+
nr_purestorage = nr.filter(
|
86
|
+
F(device_type__manufacturer__slug__contains="pure-storage") & F(status__value="active")
|
87
|
+
)
|
88
|
+
task_text = "Filter nornir inventory"
|
89
|
+
print(task_name(text=task_text))
|
90
|
+
print(task_info(text=task_text, changed=False))
|
91
|
+
print(
|
92
|
+
f"'{task_text}' -> NornirResult <Success: True>\n"
|
93
|
+
+ "-> Filter condition: Device manufacturer slug contains 'pure-storage'",
|
94
|
+
)
|
95
|
+
|
96
|
+
# Run the custom Nornir task update_serial_number
|
97
|
+
name = "NETBOX update device serial number"
|
98
|
+
result = nr_purestorage.run(name=name, task=update_serial_number, on_failed=True)
|
99
|
+
# Print the whole result for each host
|
100
|
+
print_result(result)
|
101
|
+
if result.failed:
|
102
|
+
failed = True
|
103
|
+
|
104
|
+
return failed
|
105
|
+
|
106
|
+
|
107
|
+
def main(nr_config: str) -> None:
|
108
|
+
"""
|
109
|
+
Main function is intended to import and execute by other scripts.
|
110
|
+
It initialize Nornir and update NetBox Pure-Storage inventory data.
|
111
|
+
|
112
|
+
* Args:
|
113
|
+
* nr_config (str): Path to the Nornir configuration file.
|
114
|
+
|
115
|
+
* Steps:
|
116
|
+
* Initializes the Nornir inventory object using the provided configuration file.
|
117
|
+
* Executes the task to update NetBox Pure-Storage inventory data.
|
118
|
+
* Checks the result of the update task and exits the script with an error message if the task failed.
|
119
|
+
|
120
|
+
* Exits:
|
121
|
+
* Exits with code 1 if the Nornir configuration file is empty or if any task fails.
|
122
|
+
"""
|
123
|
+
# pylint: disable=invalid-name
|
124
|
+
|
125
|
+
#### Initialize Nornir ##################################################################################
|
126
|
+
|
127
|
+
# Initialize, transform and filter the Nornir inventory are return the filtered Nornir object
|
128
|
+
# Define data to load from NetBox in addition to the base Nornir inventory plugin
|
129
|
+
add_netbox_data = {"load_virtual_chassis_data": True}
|
130
|
+
nr = init_nornir(config_file=nr_config, add_netbox_data=add_netbox_data)
|
131
|
+
|
132
|
+
#### Run Nornir Cisco Support Plugin Update Tasks #######################################################
|
133
|
+
|
134
|
+
# Update NetBox Pure-Storate inventory data
|
135
|
+
result_failed = update_purestorage_inventory_data(nr=nr)
|
136
|
+
|
137
|
+
# Check the result and exit the script with exit code 1 and a error message
|
138
|
+
if result_failed:
|
139
|
+
text = "Update NetBox Pure-Storage Device Inventory Data with Nornir"
|
140
|
+
print(task_name(text=text))
|
141
|
+
exit_error(
|
142
|
+
task_text=f"{text} Failed",
|
143
|
+
msg="Check the result details for failed Nornir tasks",
|
144
|
+
)
|
@@ -0,0 +1,261 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains general functions and tasks related to NetBox.
|
4
|
+
|
5
|
+
The functions are ordered as followed:
|
6
|
+
- Helper Functions
|
7
|
+
- Task Helper Functions
|
8
|
+
- Single Nornir tasks
|
9
|
+
- Nornir tasks in regular function
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
import os
|
14
|
+
import argparse
|
15
|
+
import json
|
16
|
+
from typing import Tuple, Union, Any, Dict, List
|
17
|
+
import __main__
|
18
|
+
import requests
|
19
|
+
import pynetbox
|
20
|
+
from nornir.core.task import Task, Result
|
21
|
+
from nornir_collection.utils import (
|
22
|
+
CustomArgParse,
|
23
|
+
CustomArgParseWidthFormatter,
|
24
|
+
get_env_vars,
|
25
|
+
print_task_name,
|
26
|
+
task_info,
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
#### Helper Functions #######################################################################################
|
31
|
+
|
32
|
+
|
33
|
+
def init_pynetbox(nb_url: str, ssl_verify=False) -> Tuple[pynetbox.api, pynetbox.api]:
|
34
|
+
"""
|
35
|
+
This function instantiate a Pynetbox API object, print the result and returns the API object
|
36
|
+
"""
|
37
|
+
task_text = "Initialize PyNetBox"
|
38
|
+
print_task_name(text=task_text)
|
39
|
+
|
40
|
+
# Load environment variables or raise a TypeError when is None
|
41
|
+
env_vars = get_env_vars(envs=["NB_TOKEN"], task_text=task_text)
|
42
|
+
|
43
|
+
# Instantiate the NetBox API
|
44
|
+
session = requests.Session()
|
45
|
+
session.verify = ssl_verify
|
46
|
+
nb = pynetbox.api(url=nb_url, token=env_vars["NB_TOKEN"], threading=False)
|
47
|
+
nb.http_session = session
|
48
|
+
|
49
|
+
# Print success result
|
50
|
+
print(task_info(text=task_text, changed=False))
|
51
|
+
print(f"'{task_text}' -> Pynetbox.Api <Success: True>")
|
52
|
+
print(f"-> Instantiate the PyNetBox API for '{nb_url}'")
|
53
|
+
|
54
|
+
return nb
|
55
|
+
|
56
|
+
|
57
|
+
def init_args_for_nornir_config_filepath() -> str:
|
58
|
+
"""
|
59
|
+
This function initialze arguments to specify which NetBox instance and Nornir config filepath to use.
|
60
|
+
"""
|
61
|
+
task_text = "Argparse verify arguments"
|
62
|
+
print_task_name(text=task_text)
|
63
|
+
|
64
|
+
# Load environment variables or raise a TypeError when is None
|
65
|
+
env_vars = get_env_vars(envs=["NR_CONFIG_PROD", "NR_CONFIG_TEST"], task_text=task_text)
|
66
|
+
nr_config_prod = env_vars["NR_CONFIG_PROD"]
|
67
|
+
nr_config_test = env_vars["NR_CONFIG_TEST"]
|
68
|
+
|
69
|
+
# Define the arguments which needs to be given to the script execution
|
70
|
+
argparser = CustomArgParse(
|
71
|
+
prog=os.path.basename(__main__.__file__),
|
72
|
+
description="Specify the NetBox PROD or TEST instance and Nornir config filepath to be used",
|
73
|
+
epilog="One of the two mandatory arguments is required.",
|
74
|
+
argument_default=argparse.SUPPRESS,
|
75
|
+
formatter_class=CustomArgParseWidthFormatter,
|
76
|
+
)
|
77
|
+
# Add a argparser group for mutual exclusive arguments
|
78
|
+
group = argparser.add_mutually_exclusive_group(required=True)
|
79
|
+
# Add the test argument
|
80
|
+
group.add_argument(
|
81
|
+
"--prod",
|
82
|
+
action="store_true",
|
83
|
+
default=False,
|
84
|
+
help=f"use the NetBox 'PROD' instance and Nornir config '{nr_config_prod}'",
|
85
|
+
)
|
86
|
+
# Add the test argument
|
87
|
+
group.add_argument(
|
88
|
+
"--test",
|
89
|
+
action="store_true",
|
90
|
+
default=False,
|
91
|
+
help=f"use the NetBox 'TEST' instance and Nornir config '{nr_config_test}'",
|
92
|
+
)
|
93
|
+
# Verify the provided arguments and print the custom argparse error message in case of an error
|
94
|
+
args = argparser.parse_args()
|
95
|
+
|
96
|
+
# Set the NetBox instance and the Nornir config file based on the arguments
|
97
|
+
nb_instance = "TEST" if args.test else "PROD"
|
98
|
+
nr_config = nr_config_test if args.test else nr_config_prod
|
99
|
+
|
100
|
+
# If argparser.parse_args() is successful -> no argparse error message
|
101
|
+
print(task_info(text=task_text, changed=False))
|
102
|
+
print(f"'{task_text}' -> ArgparseResponse <Success: True>")
|
103
|
+
print(f"-> Run on the NetBox '{nb_instance}' instance and Nornir config '{nr_config}'")
|
104
|
+
|
105
|
+
return nr_config
|
106
|
+
|
107
|
+
|
108
|
+
def get_nb_resources( # pylint: disable=dangerous-default-value
|
109
|
+
url: str, params: Dict[str, Any] = {}
|
110
|
+
) -> List[Dict[str, Any]]:
|
111
|
+
"""
|
112
|
+
TBD
|
113
|
+
"""
|
114
|
+
# Define the resource list
|
115
|
+
resources: List[Dict[str, Any]] = []
|
116
|
+
headers = {
|
117
|
+
"Accept": "application/json",
|
118
|
+
"Content-Type": "application/json",
|
119
|
+
"Authorization": f"Token {os.environ.get('NB_TOKEN')}",
|
120
|
+
}
|
121
|
+
# While there is a next page continue the loop
|
122
|
+
while url:
|
123
|
+
# Do the http request
|
124
|
+
response = requests.get( # nosec
|
125
|
+
url=url, headers=headers, params=params, verify=False, timeout=(3.05, 27)
|
126
|
+
)
|
127
|
+
# Verify the response code
|
128
|
+
if not response.status_code == 200:
|
129
|
+
print(response.status_code)
|
130
|
+
print(response.text)
|
131
|
+
raise ValueError(f"Failed to get data from NetBox instance {url}")
|
132
|
+
# Extract the json data from the http response
|
133
|
+
resp = response.json()
|
134
|
+
# Add the response to the resource list
|
135
|
+
resources.extend(resp.get("results"))
|
136
|
+
# Get the url of the next page
|
137
|
+
url = resp.get("next")
|
138
|
+
|
139
|
+
# Retrun the resources list
|
140
|
+
return resources
|
141
|
+
|
142
|
+
|
143
|
+
def post_nb_resources(url: str, payload: List[Dict]) -> requests.Response:
|
144
|
+
"""
|
145
|
+
TBD
|
146
|
+
"""
|
147
|
+
headers = {
|
148
|
+
"Accept": "application/json",
|
149
|
+
"Content-Type": "application/json",
|
150
|
+
"Authorization": f"Token {os.environ.get('NB_TOKEN')}",
|
151
|
+
}
|
152
|
+
# Do the http request and return the result
|
153
|
+
return requests.post( # nosec
|
154
|
+
url=url, headers=headers, data=json.dumps(payload), verify=False, timeout=(3.05, 27)
|
155
|
+
)
|
156
|
+
|
157
|
+
|
158
|
+
def patch_nb_resources(url: str, payload: List[Dict]) -> requests.Response:
|
159
|
+
"""
|
160
|
+
TBD
|
161
|
+
"""
|
162
|
+
headers = {
|
163
|
+
"Accept": "application/json",
|
164
|
+
"Content-Type": "application/json",
|
165
|
+
"Authorization": f"Token {os.environ.get('NB_TOKEN')}",
|
166
|
+
}
|
167
|
+
# Do the http request and return the result
|
168
|
+
return requests.patch( # nosec
|
169
|
+
url=url, headers=headers, data=json.dumps(payload), verify=False, timeout=(3.05, 27)
|
170
|
+
)
|
171
|
+
|
172
|
+
|
173
|
+
def delete_nb_resources(url: str, payload: List[Dict]) -> requests.Response:
|
174
|
+
"""
|
175
|
+
TBD
|
176
|
+
"""
|
177
|
+
headers = {
|
178
|
+
"Accept": "application/json",
|
179
|
+
"Content-Type": "application/json",
|
180
|
+
"Authorization": f"Token {os.environ.get('NB_TOKEN')}",
|
181
|
+
}
|
182
|
+
# Do the http request and return the result
|
183
|
+
return requests.delete( # nosec
|
184
|
+
url=url, headers=headers, data=json.dumps(payload), verify=False, timeout=(3.05, 27)
|
185
|
+
)
|
186
|
+
|
187
|
+
|
188
|
+
#### Task Helper Functions ##################################################################################
|
189
|
+
|
190
|
+
|
191
|
+
def _nb_patch_resources(task: Task, task_text: str, url: str, payload: list) -> Union[str, Result]:
|
192
|
+
"""
|
193
|
+
Sends a PATCH request to update the resources of a device in NetBox.
|
194
|
+
Args:
|
195
|
+
task (Task): The Nornir task object.
|
196
|
+
task_text (str): The description of the task.
|
197
|
+
payload (list): A list of dictionaries containing the resources to be updated.
|
198
|
+
Returns:
|
199
|
+
tuple: A tuple containing the result string and a boolean indicating whether the task failed.
|
200
|
+
"""
|
201
|
+
# POST request to update the Cisco Support Plugin desired release
|
202
|
+
response = patch_nb_resources(url=url, payload=payload)
|
203
|
+
|
204
|
+
# Verify the response code and return the result
|
205
|
+
if response.status_code != 200:
|
206
|
+
result = (
|
207
|
+
f"'{task_text}' -> NetBoxResponse: <Success: False>\n"
|
208
|
+
+ f"-> Response Status Code: {response.status_code}\n"
|
209
|
+
+ f"-> Response Text: {response.text}\n"
|
210
|
+
+ f"-> Payload: {payload}"
|
211
|
+
)
|
212
|
+
# Return the Nornir result
|
213
|
+
return Result(host=task.host, result=result, changed=False, failed=True)
|
214
|
+
|
215
|
+
# Return the result string
|
216
|
+
result = f"'{task_text}' -> NetBoxResponse: <Success: True>"
|
217
|
+
return result
|
218
|
+
|
219
|
+
|
220
|
+
def _nb_create_payload_patch_device_serials(task: Task, task_text: str, serials: dict) -> Union[list, Result]:
|
221
|
+
"""
|
222
|
+
Create a payload for patching device serial numbers in NetBox.
|
223
|
+
Args:
|
224
|
+
task (Task): The Nornir task object.
|
225
|
+
task_text (str): The task description.
|
226
|
+
serials (dict): A dictionary containing the serial numbers for the devices.
|
227
|
+
Returns:
|
228
|
+
Union[List, Result]: A list of dictionaries containing the device IDs and serial numbers,
|
229
|
+
or a Nornir Result object if an error occurs.
|
230
|
+
"""
|
231
|
+
try:
|
232
|
+
# Create a list of dicts. Multiple dicts if its a virtual chassis
|
233
|
+
payload = []
|
234
|
+
|
235
|
+
# Add the device depending if its a virtual chassis in NetBox or not
|
236
|
+
if "virtual_chassis" in task.host and task.host["virtual_chassis"] is not None:
|
237
|
+
# Add the master
|
238
|
+
payload.append({"id": task.host["virtual_chassis"]["master"]["id"], "serial": serials["1"]})
|
239
|
+
|
240
|
+
# Add all members to the payload if available
|
241
|
+
if "members" in task.host["virtual_chassis"]:
|
242
|
+
# Start enumerate with 2 as 1 is the virtual chassis master
|
243
|
+
for num, member in enumerate(task.host["virtual_chassis"]["members"], start=2):
|
244
|
+
payload.append({"id": member["id"], "serial": serials[str(num)]})
|
245
|
+
else:
|
246
|
+
# Add the device (no virtual chassis)
|
247
|
+
payload.append({"id": task.host["id"], "serial": serials["1"]})
|
248
|
+
|
249
|
+
# Return the payload
|
250
|
+
return payload
|
251
|
+
|
252
|
+
except KeyError as error:
|
253
|
+
result = f"'{task_text}' -> PythonResponse <Success: False>\n-> Dictionary key {error} not found"
|
254
|
+
# Return the Nornir result
|
255
|
+
return Result(host=task.host, result=result, changed=False, failed=True)
|
256
|
+
|
257
|
+
|
258
|
+
#### Nornir Tasks ###########################################################################################
|
259
|
+
|
260
|
+
|
261
|
+
#### Nornir Tasks ###########################################################################################
|