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,325 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains RESTCONF functions and tasks related to Nornir.
|
4
|
+
|
5
|
+
The functions are ordered as followed:
|
6
|
+
- Single Nornir RESTCONF tasks
|
7
|
+
- Nornir RESTCONF tasks in regular function
|
8
|
+
"""
|
9
|
+
|
10
|
+
import json
|
11
|
+
import traceback
|
12
|
+
import requests
|
13
|
+
from nornir.core import Nornir
|
14
|
+
from nornir.core.task import Task, Result
|
15
|
+
from nornir_collection.cisco.configuration_management.restconf.cisco_rpc import (
|
16
|
+
rc_software_install_one_shot_task,
|
17
|
+
rc_install_remove_inactive_task,
|
18
|
+
)
|
19
|
+
from nornir_collection.cisco.configuration_management.cli.show_tasks import (
|
20
|
+
cli_verify_current_software_version_task,
|
21
|
+
cli_install_one_shot_task,
|
22
|
+
cli_install_remove_inactive_task,
|
23
|
+
)
|
24
|
+
from nornir_collection.utils import (
|
25
|
+
print_result,
|
26
|
+
exit_error,
|
27
|
+
nr_filter_inventory_from_host_list,
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
#### Helper Functions #######################################################################################
|
32
|
+
|
33
|
+
|
34
|
+
def rc_cisco_get(url: str, auth: tuple, verify: bool = False, timeout: int = 120) -> dict:
|
35
|
+
"""
|
36
|
+
TBD
|
37
|
+
"""
|
38
|
+
# RESTCONF HTTP header
|
39
|
+
headers = {"Accept": "application/yang-data+json", "Content-Type": "application/yang-data+json"}
|
40
|
+
|
41
|
+
# RESTCONF HTTP API call
|
42
|
+
response = requests.get(url=url, headers=headers, auth=auth, verify=verify, timeout=timeout) # nosec
|
43
|
+
|
44
|
+
# Result dict to return as task result
|
45
|
+
result = {
|
46
|
+
"url": url,
|
47
|
+
"response": response,
|
48
|
+
"method": response.request,
|
49
|
+
"status_code": response.status_code,
|
50
|
+
"elapsed": response.elapsed.total_seconds(),
|
51
|
+
"text": response.text,
|
52
|
+
"json": response.json(),
|
53
|
+
}
|
54
|
+
|
55
|
+
# Return the result dictionary
|
56
|
+
return result
|
57
|
+
|
58
|
+
|
59
|
+
#### Single Nornir RESTCONF Tasks ###########################################################################
|
60
|
+
|
61
|
+
|
62
|
+
def rc_cisco_get_task(task: Task, yang_data_query: str) -> Result:
|
63
|
+
"""
|
64
|
+
This custom Nornir task executes a RESTCONF GET request to a yang data query and returns a dictionary with
|
65
|
+
the whole RESTCONF response as well as some custom formated data for further processing.
|
66
|
+
"""
|
67
|
+
# RESTCONF HTTP URL
|
68
|
+
restconf_path = f"restconf/data/{yang_data_query}"
|
69
|
+
url = f"https://{task.host.hostname}:443/{restconf_path}"
|
70
|
+
|
71
|
+
# RESTCONF HTTP header
|
72
|
+
headers = {
|
73
|
+
"Accept": "application/yang-data+json",
|
74
|
+
"Content-Type": "application/yang-data+json",
|
75
|
+
}
|
76
|
+
|
77
|
+
# RESTCONF HTTP API call
|
78
|
+
rc_response = requests.get( # nosec
|
79
|
+
url=url, headers=headers, auth=(task.host.username, task.host.password), verify=False, timeout=120
|
80
|
+
)
|
81
|
+
|
82
|
+
# Result dict to return as task result
|
83
|
+
result = {
|
84
|
+
"url": url,
|
85
|
+
"response": rc_response,
|
86
|
+
"method": rc_response.request,
|
87
|
+
"status_code": rc_response.status_code,
|
88
|
+
"elapsed": rc_response.elapsed.total_seconds(),
|
89
|
+
"json": rc_response.json(),
|
90
|
+
}
|
91
|
+
|
92
|
+
return Result(host=task.host, result=result)
|
93
|
+
|
94
|
+
|
95
|
+
def rc_verify_current_software_version_task(task: Task, verbose=False) -> Result:
|
96
|
+
"""
|
97
|
+
TBD
|
98
|
+
"""
|
99
|
+
# Get the desired version from the Nornir inventory
|
100
|
+
desired_version = task.host["software"]["version"]
|
101
|
+
|
102
|
+
# RESTCONF HTTP URL
|
103
|
+
rc_path = "restconf/data/Cisco-IOS-XE-install-oper:install-oper-data/install-location-information"
|
104
|
+
url = f"https://{task.host.hostname}:443/{rc_path}"
|
105
|
+
# RESTCONF HTTP header
|
106
|
+
headers = {
|
107
|
+
"Accept": "application/yang-data+json",
|
108
|
+
"Content-Type": "application/yang-data+json",
|
109
|
+
}
|
110
|
+
|
111
|
+
try:
|
112
|
+
# RESTCONF HTTP API call
|
113
|
+
response = requests.get( # nosec
|
114
|
+
url=url, headers=headers, auth=(task.host.username, task.host.password), verify=False, timeout=120
|
115
|
+
)
|
116
|
+
# Get the current version from the task result
|
117
|
+
current_version = response.json()["Cisco-IOS-XE-install-oper:install-location-information"][0][
|
118
|
+
"install-version-state-info"
|
119
|
+
][0]["version"]
|
120
|
+
except: # pylint: disable=bare-except
|
121
|
+
# Define the result as iosxe_c9200 is not implemented yet
|
122
|
+
custom_result = f"'{task.name}' -> NornirResponse: <Success: False>\n" f"\n{traceback.format_exc()}"
|
123
|
+
# Return the custom Nornir result as success
|
124
|
+
return Result(host=task.host, custom_result=custom_result, failed=True, use_fallback=True)
|
125
|
+
|
126
|
+
# Slice the variable to have only the fist 8 characters of the version number which should match to
|
127
|
+
# the Cisco version naming convention of xx.xx.xx
|
128
|
+
current_version = current_version[:8]
|
129
|
+
# Replace all 0 in the xe_version to normalizing iosxe and non-iosxe version format
|
130
|
+
# -> Make 17.03.05 to 17.3.5
|
131
|
+
current_version = current_version.replace("0", "")
|
132
|
+
# Write the current version into the Nornir inventory
|
133
|
+
task.host["software"]["current_version"] = current_version
|
134
|
+
# Prepare needed variables for further processing
|
135
|
+
elapsed = response.elapsed.total_seconds()
|
136
|
+
|
137
|
+
# Define the verbose result
|
138
|
+
verbose_result = (
|
139
|
+
f"URL: {url}\n"
|
140
|
+
f"Method: {response.request}\n"
|
141
|
+
f"Response: {response}\n"
|
142
|
+
f"Current version from JSON payload: {json.dumps(current_version, sort_keys=True, indent=4)}"
|
143
|
+
)
|
144
|
+
|
145
|
+
# If the RESTCONF call was successful
|
146
|
+
if response.status_code == 200:
|
147
|
+
# If the desired version and the current version are the same
|
148
|
+
if desired_version in current_version:
|
149
|
+
# Define the summary result
|
150
|
+
result_summary = (
|
151
|
+
f"'{task.name}' -> RestconfResponse {response} in {elapsed}s\n"
|
152
|
+
f"-> Desired version {desired_version} match installed version {current_version}"
|
153
|
+
)
|
154
|
+
# Define the custom_result variable for print_result
|
155
|
+
custom_result = result_summary + "\n\n" + verbose_result if verbose else result_summary
|
156
|
+
|
157
|
+
# Return the custom Nornir result as success
|
158
|
+
return Result(host=task.host, custom_result=custom_result)
|
159
|
+
|
160
|
+
# Else the desired version and the current version are not the same
|
161
|
+
# Define the summary result
|
162
|
+
result_summary = (
|
163
|
+
f"'{task.name}' -> RestconfResponse {response} in {elapsed}s\n"
|
164
|
+
f"-> Desired version {desired_version} don't match installed version {current_version}"
|
165
|
+
)
|
166
|
+
# Define the custom_result variable for print_result
|
167
|
+
custom_result = result_summary + "\n\n" + verbose_result if verbose else result_summary
|
168
|
+
|
169
|
+
# Return the custom Nornir result as failed
|
170
|
+
return Result(host=task.host, custom_result=custom_result, failed=True, need_upgrade=True)
|
171
|
+
|
172
|
+
# Define the custom_result variable for print_result
|
173
|
+
custom_result = f"'{task.name}' -> RestconfResponse {response} in {elapsed}s\n\n{verbose_result}"
|
174
|
+
|
175
|
+
# If the RESTCONF call was not successful -> The task failed and set the use_fallback to True
|
176
|
+
return Result(host=task.host, custom_result=custom_result, failed=True, use_fallback=True)
|
177
|
+
|
178
|
+
|
179
|
+
#### Nornir RESTCONF tasks in regular Function ##############################################################
|
180
|
+
|
181
|
+
|
182
|
+
def rc_verify_current_software_version_fallback_cli(nr: Nornir, verbose=False) -> list:
|
183
|
+
"""
|
184
|
+
TBD
|
185
|
+
"""
|
186
|
+
# pylint: disable=invalid-name
|
187
|
+
|
188
|
+
# Get software version with RESTCONF
|
189
|
+
rc_task_result = nr.run(
|
190
|
+
task=rc_verify_current_software_version_task,
|
191
|
+
name="RESTCONF verify current software version",
|
192
|
+
verbose=verbose,
|
193
|
+
on_failed=True,
|
194
|
+
)
|
195
|
+
|
196
|
+
# Print the Nornir rc_verify_current_software_version_task task result
|
197
|
+
print_result(rc_task_result, attrs="custom_result")
|
198
|
+
|
199
|
+
# Create a list with all host that failed the RESTCONF task and need to use the CLI fallback task
|
200
|
+
rc_fallback_hosts = [host for host in rc_task_result if hasattr(rc_task_result[host], "use_fallback")]
|
201
|
+
|
202
|
+
# If the rc_fallback_hosts list is empty, the CLI fallback is not needed and the failed_hosts list can
|
203
|
+
# be returned. The failed host list contains now only host with not matching software version
|
204
|
+
if not rc_fallback_hosts:
|
205
|
+
failed_hosts = list(rc_task_result.failed_hosts)
|
206
|
+
return failed_hosts
|
207
|
+
|
208
|
+
# Re-filter the Nornir inventory on the failed_hosts only
|
209
|
+
nr_obj_fallback = nr_filter_inventory_from_host_list(
|
210
|
+
nr=nr,
|
211
|
+
filter_reason="CLI fallback for hosts that failed the RESTCONF task:",
|
212
|
+
host_list=rc_fallback_hosts,
|
213
|
+
)
|
214
|
+
|
215
|
+
# Get software version with CLI
|
216
|
+
cli_task_result = nr_obj_fallback.run(
|
217
|
+
task=cli_verify_current_software_version_task,
|
218
|
+
name="CLI verify current software version",
|
219
|
+
verbose=verbose,
|
220
|
+
on_failed=True,
|
221
|
+
)
|
222
|
+
|
223
|
+
# Print the Nornir cli_verify_current_software_version_task task result
|
224
|
+
print_result(cli_task_result)
|
225
|
+
|
226
|
+
# If the overall task result failed -> Print results and exit the script
|
227
|
+
for host in cli_task_result:
|
228
|
+
if hasattr(cli_task_result[host], "overall_task_failed"):
|
229
|
+
exit_error(task_text="RESTCONF and CLI verify current software version")
|
230
|
+
|
231
|
+
# Create a list with all host which the RESTCONF task was successful but they need a software upgrade
|
232
|
+
rc_upgrade_hosts = [host for host in rc_task_result if hasattr(rc_task_result[host], "need_upgrade")]
|
233
|
+
# Create a list with all host which the CLI task was successful but they need a software upgrade
|
234
|
+
cli_upgrade_hosts = [host for host in cli_task_result if hasattr(cli_task_result[host], "need_upgrade")]
|
235
|
+
|
236
|
+
# List to fill with all hosts not matching the desired software version
|
237
|
+
failed_hosts = rc_upgrade_hosts + cli_upgrade_hosts
|
238
|
+
|
239
|
+
return failed_hosts
|
240
|
+
|
241
|
+
|
242
|
+
def rc_software_install_one_shot_fallback_cli(nr: Nornir, issu: bool = False, verbose: bool = False) -> bool:
|
243
|
+
"""
|
244
|
+
TBD
|
245
|
+
"""
|
246
|
+
# pylint: disable=invalid-name
|
247
|
+
|
248
|
+
# Run the custom Nornir task rc_software_install_one_shot_task
|
249
|
+
rc_task_result = nr.run(
|
250
|
+
task=rc_software_install_one_shot_task,
|
251
|
+
name="RESTCONF one-shot install",
|
252
|
+
issu=issu,
|
253
|
+
verbose=verbose,
|
254
|
+
on_failed=True,
|
255
|
+
)
|
256
|
+
|
257
|
+
# Print the Nornir rc_software_install_one_shot_task task result
|
258
|
+
print_result(rc_task_result)
|
259
|
+
|
260
|
+
# If the failed_hosts list is empty, the CLI fallback is not needed and True can be returned.
|
261
|
+
if not list(rc_task_result.failed_hosts):
|
262
|
+
return True
|
263
|
+
|
264
|
+
# Re-filter the Nornir inventory on the failed_hosts of rc_task_result only
|
265
|
+
nr_obj_fallback = nr_filter_inventory_from_host_list(
|
266
|
+
nr=nr,
|
267
|
+
filter_reason="CLI fallback for hosts that failed the RESTCONF task:",
|
268
|
+
host_list=list(rc_task_result.failed_hosts),
|
269
|
+
)
|
270
|
+
|
271
|
+
# Run the custom Nornir task cli_install_one_shot_task
|
272
|
+
cli_task_result = nr_obj_fallback.run(
|
273
|
+
task=cli_install_one_shot_task,
|
274
|
+
name="CLI one-shot install",
|
275
|
+
issu=issu,
|
276
|
+
verbose=verbose,
|
277
|
+
on_failed=True,
|
278
|
+
)
|
279
|
+
|
280
|
+
# Print the Nornir cli_install_one_shot_task task result
|
281
|
+
print_result(cli_task_result)
|
282
|
+
|
283
|
+
# Return False if the task failed or True if the task was successful
|
284
|
+
return not bool(cli_task_result.failed)
|
285
|
+
|
286
|
+
|
287
|
+
def rc_install_remove_inactive_fallback_cli(nr: Nornir, verbose: bool = False) -> bool:
|
288
|
+
"""
|
289
|
+
TBD
|
290
|
+
"""
|
291
|
+
# pylint: disable=invalid-name
|
292
|
+
|
293
|
+
# Run the custom Nornir task rc_install_remove_inactive_task
|
294
|
+
rc_task_result = nr.run(
|
295
|
+
task=rc_install_remove_inactive_task,
|
296
|
+
name="RESTCONF install remove inactive",
|
297
|
+
verbose=verbose,
|
298
|
+
on_failed=True,
|
299
|
+
)
|
300
|
+
# Print the Nornir rc_install_remove_inactive_task task result
|
301
|
+
print_result(rc_task_result)
|
302
|
+
|
303
|
+
# If the failed_hosts list is empty, the CLI fallback is not needed and True can be returned.
|
304
|
+
if not list(rc_task_result.failed_hosts):
|
305
|
+
return True
|
306
|
+
|
307
|
+
# Re-filter the Nornir inventory on the failed_hosts of rc_task_result only
|
308
|
+
nr_obj_fallback = nr_filter_inventory_from_host_list(
|
309
|
+
nr=nr,
|
310
|
+
filter_reason="CLI fallback for hosts that failed the RESTCONF task:",
|
311
|
+
host_list=list(rc_task_result.failed_hosts),
|
312
|
+
)
|
313
|
+
|
314
|
+
# Run the custom Nornir task cli_install_remove_inactive_task
|
315
|
+
cli_task_result = nr_obj_fallback.run(
|
316
|
+
task=cli_install_remove_inactive_task,
|
317
|
+
name="CLI install remove inactive",
|
318
|
+
verbose=verbose,
|
319
|
+
on_failed=True,
|
320
|
+
)
|
321
|
+
# Print the Nornir cli_install_remove_inactive_task task result
|
322
|
+
print_result(cli_task_result)
|
323
|
+
|
324
|
+
# Return False if the task failed or True if the task was successful
|
325
|
+
return not bool(cli_task_result.failed)
|