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,514 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains Cisco specific RESTCONF operation RPC tasks and functions for Nornir. Other custom
|
4
|
+
RESTCONF tasks for the /data url are not part of this helper file. Please take a look to nr_restconf.
|
5
|
+
|
6
|
+
The functions are ordered as followed:
|
7
|
+
- Helper Functions
|
8
|
+
- Single Nornir RESTCONF RPC Tasks
|
9
|
+
- Nornir RESTCONF RPC Tasks in regular Function
|
10
|
+
- Nornir RESTCONF RPC Tasks with CLI Fallback in regular Function
|
11
|
+
"""
|
12
|
+
|
13
|
+
import json
|
14
|
+
import time
|
15
|
+
from typing import Union
|
16
|
+
import requests
|
17
|
+
from nornir.core import Nornir
|
18
|
+
from nornir.core.task import Task, Result
|
19
|
+
from nornir_collection.utils import print_result
|
20
|
+
|
21
|
+
|
22
|
+
#### Helper Functions ########################################################################################
|
23
|
+
|
24
|
+
|
25
|
+
def rc_cisco_operation_rpc( # pylint: disable=dangerous-default-value
|
26
|
+
task_obj: Task, rpc: str, payload: dict = {}
|
27
|
+
) -> requests.Response:
|
28
|
+
"""
|
29
|
+
TBD
|
30
|
+
"""
|
31
|
+
|
32
|
+
# RESTCONF HTTP URL
|
33
|
+
host = task_obj.host.hostname
|
34
|
+
url = f"https://{host}:443/restconf/operations/{rpc}"
|
35
|
+
|
36
|
+
# RESTCONF HTTP header
|
37
|
+
headers = {"Accept": "application/yang-data+json", "Content-Type": "application/yang-data+json"}
|
38
|
+
|
39
|
+
# RESTCONF HTTP API call
|
40
|
+
response = requests.post(
|
41
|
+
url=url,
|
42
|
+
headers=headers,
|
43
|
+
data=json.dumps(payload),
|
44
|
+
auth=(task_obj.host.username, task_obj.host.password),
|
45
|
+
verify=False, # nosec
|
46
|
+
timeout=120,
|
47
|
+
)
|
48
|
+
|
49
|
+
return response
|
50
|
+
|
51
|
+
|
52
|
+
#### Single Nornir RESTCONF RPC Tasks ########################################################################
|
53
|
+
|
54
|
+
|
55
|
+
def rc_cisco_rpc_is_syncing_task(task: Task, verbose: bool = False) -> Result:
|
56
|
+
"""
|
57
|
+
This Nornir task executes the Cisco specific operations RESTCONF RPC cisco-ia:is-syncing to check if the
|
58
|
+
configuration datastore is ready. If a sync is active the task backoff and try again until the datastore
|
59
|
+
is ready. The Nornir Result object is returned.
|
60
|
+
"""
|
61
|
+
# Backoff sleep and attempt values
|
62
|
+
max_retry = 180 # How many times the range() loop will be done
|
63
|
+
sleep = 1 # Seconds to sleep between each range() loop attempt
|
64
|
+
waited = 0 # How many seconds hat to be waited until the datastore is ready
|
65
|
+
|
66
|
+
for _ in range(max_retry):
|
67
|
+
# RESTCONF HTTP API call
|
68
|
+
response = rc_cisco_operation_rpc(task_obj=task, rpc="cisco-ia:is-syncing")
|
69
|
+
|
70
|
+
# Set the verbose result string to add to the result summary
|
71
|
+
result_verbose = (
|
72
|
+
f"\n\nPayload: {json.dumps({}, indent=4)}\n"
|
73
|
+
+ f"\nURL: {response.url}\n"
|
74
|
+
+ f"Method: {response.request}\n"
|
75
|
+
+ f"Response: {response}\n"
|
76
|
+
+ f"Text: {response.text}"
|
77
|
+
)
|
78
|
+
|
79
|
+
if response.status_code == 200 and "No sync in progress" in response.text:
|
80
|
+
# No datastore sync -> Return the Nornir task result as successful
|
81
|
+
result = (
|
82
|
+
f"'{task.name}' -> RestconfResponse: <Success: True>\n"
|
83
|
+
+ f"-> Datastore is ready after waiting {waited}s"
|
84
|
+
)
|
85
|
+
result = result + f"{result_verbose}" if verbose else result
|
86
|
+
|
87
|
+
# Return the Nornir task result
|
88
|
+
return Result(host=task.host, result=result, failed=False)
|
89
|
+
|
90
|
+
# Continue with next range() loop attempt
|
91
|
+
time.sleep(sleep)
|
92
|
+
waited = waited + sleep
|
93
|
+
|
94
|
+
# If the for loop is finish without success
|
95
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>"
|
96
|
+
# Set the message based on the response status code
|
97
|
+
result += (
|
98
|
+
f"\n-> Datastore is not ready after max waiting timeout of {waited}s"
|
99
|
+
if response.status_code == 200
|
100
|
+
else f"\n-> RESTCONF RPC API call failed with status code {response.status_code}"
|
101
|
+
)
|
102
|
+
result = result + result_verbose if verbose else result
|
103
|
+
|
104
|
+
# Return the Nornir task result as failed
|
105
|
+
return Result(host=task.host, result=result, failed=True)
|
106
|
+
|
107
|
+
|
108
|
+
def rc_cisco_rpc_save_config_task(task: Task, verbose: bool = False) -> Result:
|
109
|
+
"""
|
110
|
+
TBD
|
111
|
+
"""
|
112
|
+
# RESTCONF HTTP API call
|
113
|
+
response = rc_cisco_operation_rpc(task_obj=task, rpc="cisco-ia:save-config")
|
114
|
+
|
115
|
+
# Set the verbose result string to add to the result summary
|
116
|
+
result_verbose = (
|
117
|
+
f"\n\nPayload: {json.dumps({}, indent=4)}\n"
|
118
|
+
+ f"\nURL: {response.url}\n"
|
119
|
+
+ f"Method: {response.request}\n"
|
120
|
+
+ f"Response: {response}\n"
|
121
|
+
+ f"Text: {response.text}"
|
122
|
+
)
|
123
|
+
|
124
|
+
if response.status_code == 200:
|
125
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: True>"
|
126
|
+
# Set the result print level
|
127
|
+
result = result + result_verbose if verbose else result
|
128
|
+
|
129
|
+
# Return the Nornir task result as successful
|
130
|
+
return Result(host=task.host, result=result)
|
131
|
+
|
132
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>" + result_verbose
|
133
|
+
# Return the Nornir task result as failed
|
134
|
+
return Result(host=task.host, result=result, failed=True)
|
135
|
+
|
136
|
+
|
137
|
+
def rc_cisco_rpc_copy_file_task(task: Task, source: str, destination: str, verbose: bool = False) -> Result:
|
138
|
+
"""
|
139
|
+
TBD
|
140
|
+
"""
|
141
|
+
# Payload
|
142
|
+
payload = {
|
143
|
+
"Cisco-IOS-XE-rpc:input": {
|
144
|
+
"source-drop-node-name": source,
|
145
|
+
"destination-drop-node-name": destination,
|
146
|
+
}
|
147
|
+
}
|
148
|
+
# RESTCONF HTTP API call
|
149
|
+
response = rc_cisco_operation_rpc(task_obj=task, rpc="Cisco-IOS-XE-rpc:copy", payload=payload)
|
150
|
+
|
151
|
+
# Set the verbose result string to add to the result summary
|
152
|
+
result_verbose = (
|
153
|
+
f"\n\nPayload: {json.dumps(payload, indent=4)}\n"
|
154
|
+
+ f"\nURL: {response.url}\n"
|
155
|
+
+ f"Method: {response.request}\n"
|
156
|
+
+ f"Response: {response}\n"
|
157
|
+
+ f"Text: {response.text}"
|
158
|
+
)
|
159
|
+
|
160
|
+
if response.status_code == 200:
|
161
|
+
result = (
|
162
|
+
f"'{task.name}' -> RestconfResponse: <Success: True>\n"
|
163
|
+
+ f"-> Source: '{source}'\n"
|
164
|
+
+ f"-> Destination: '{destination}'"
|
165
|
+
)
|
166
|
+
# Set the result print level
|
167
|
+
result = result + result_verbose if verbose else result
|
168
|
+
|
169
|
+
# Return the Nornir task result as successful
|
170
|
+
return Result(host=task.host, result=result, failed=False)
|
171
|
+
|
172
|
+
result = (
|
173
|
+
f"'{task.name}' -> RestconfResponse: <Success: False>\n"
|
174
|
+
+ f"-> Source: '{source}'\n"
|
175
|
+
+ f"-> Destination: '{destination}'"
|
176
|
+
+ result_verbose
|
177
|
+
)
|
178
|
+
# Return the Nornir task result as failed
|
179
|
+
return Result(host=task.host, result=result, failed=True)
|
180
|
+
|
181
|
+
|
182
|
+
def rc_cisco_rpc_rollback_config_task(
|
183
|
+
task: Task, target_url: str, revert_timer: int = None, verbose: bool = False
|
184
|
+
) -> Result:
|
185
|
+
"""
|
186
|
+
TBD
|
187
|
+
"""
|
188
|
+
# Base Payload
|
189
|
+
payload = {
|
190
|
+
"cisco-ia:input": {
|
191
|
+
"target-url": target_url,
|
192
|
+
"verbose": True,
|
193
|
+
}
|
194
|
+
}
|
195
|
+
# Optional add the revert-timer to the payload
|
196
|
+
if revert_timer:
|
197
|
+
payload["cisco-ia:input"]["revert-timer"] = revert_timer
|
198
|
+
|
199
|
+
# RESTCONF HTTP API call
|
200
|
+
response = rc_cisco_operation_rpc(task_obj=task, rpc="cisco-ia:rollback", payload=payload)
|
201
|
+
|
202
|
+
# Set the verbose result string to add to the result summary
|
203
|
+
result_verbose = (
|
204
|
+
f"\n\nPayload: {json.dumps(payload, indent=4)}\n"
|
205
|
+
+ f"\nURL: {response.url}\n"
|
206
|
+
+ f"Method: {response.request}\n"
|
207
|
+
+ f"Response: {response}\n"
|
208
|
+
+ f"Text: {response.text}"
|
209
|
+
)
|
210
|
+
|
211
|
+
# If the rollback was successful
|
212
|
+
if response.status_code == 200:
|
213
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: True>\n" + f"-> Target-URL: '{target_url}'"
|
214
|
+
if revert_timer:
|
215
|
+
result = result + f"\n-> Revert-Timer: '{revert_timer}min'"
|
216
|
+
# Set the result print level
|
217
|
+
result = result + result_verbose if verbose else result
|
218
|
+
|
219
|
+
# Return the Nornir task result as successful
|
220
|
+
return Result(host=task.host, result=result, failed=False)
|
221
|
+
|
222
|
+
# Failed because the rollback file does not exist
|
223
|
+
if (response.status_code == 400) and (("cisco-ia:rollback" and "Could not open file") in response.text):
|
224
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>\n" + f"-> Target-URL: '{target_url}'"
|
225
|
+
if revert_timer:
|
226
|
+
result = result + f"\n-> Revert-Timer: '{revert_timer}'"
|
227
|
+
result = result + result_verbose
|
228
|
+
# Return the Nornir task result as successful
|
229
|
+
return Result(host=task.host, result=result, failed=True)
|
230
|
+
|
231
|
+
# Success but with some failed rollback commands -> Happen when commands change between software releases
|
232
|
+
if (response.status_code == 400) and (("cisco-ia:rollback" and "inconsistent value") in response.text):
|
233
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: True>\n" + f"-> Target-URL: '{target_url}'"
|
234
|
+
if revert_timer:
|
235
|
+
result = result + f"\n-> Revert-Timer: '{revert_timer}'"
|
236
|
+
# Set the result print level
|
237
|
+
result = result + result_verbose if verbose else result
|
238
|
+
# Return the Nornir task result as successful
|
239
|
+
return Result(host=task.host, result=result, failed=False)
|
240
|
+
|
241
|
+
# Return the Nornir task result as failed
|
242
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>\n" + f"-> Target-URL: '{target_url}'"
|
243
|
+
if revert_timer:
|
244
|
+
result = result + f"\n-> Revert-Timer: '{revert_timer}'"
|
245
|
+
result = result + result_verbose
|
246
|
+
return Result(host=task.host, result=result, failed=True)
|
247
|
+
|
248
|
+
|
249
|
+
def rc_software_install_one_shot_task(task: Task, issu: bool = False, verbose: bool = False) -> Result:
|
250
|
+
"""
|
251
|
+
This custom Nornir task loads the software destination file which have to be installed from the Nornir
|
252
|
+
inventory executes the Cisco specific operations RESTCONF RPC Cisco-IOS-XE-install-rpc:install to install
|
253
|
+
a software file in a one-shot approach which will install, commit and reload the switch. The Nornir
|
254
|
+
result object will be returned.
|
255
|
+
"""
|
256
|
+
|
257
|
+
# Get the host destination file from the Nornir inventory
|
258
|
+
dest_file = task.host["software"]["dest_file"]
|
259
|
+
|
260
|
+
# Set the base payload for the RESTCONF API call
|
261
|
+
payload = {
|
262
|
+
"Cisco-IOS-XE-install-rpc:input": {
|
263
|
+
"uuid": f"Install {dest_file}",
|
264
|
+
"one-shot": True,
|
265
|
+
"path": f"flash:{dest_file}",
|
266
|
+
}
|
267
|
+
}
|
268
|
+
# Optional add issu to the payload
|
269
|
+
if issu:
|
270
|
+
payload["Cisco-IOS-XE-install-rpc:input"]["issu"] = True
|
271
|
+
|
272
|
+
# RESTCONF HTTP API call
|
273
|
+
response = rc_cisco_operation_rpc(
|
274
|
+
task_obj=task,
|
275
|
+
rpc="Cisco-IOS-XE-install-rpc:install",
|
276
|
+
payload=payload,
|
277
|
+
)
|
278
|
+
|
279
|
+
# Set the verbose result string to add to the result summary
|
280
|
+
result_verbose = (
|
281
|
+
f"\n\nPayload: {json.dumps(payload, indent=4)}\n"
|
282
|
+
+ f"\nURL: {response.url}\n"
|
283
|
+
+ f"Method: {response.request}\n"
|
284
|
+
+ f"Response: {response}\n"
|
285
|
+
+ f"Text: {response.text}"
|
286
|
+
)
|
287
|
+
|
288
|
+
if response.status_code == 204:
|
289
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: True>"
|
290
|
+
# Set the result print level
|
291
|
+
result = result + result_verbose if verbose else result
|
292
|
+
|
293
|
+
# Return the Nornir task result as successful
|
294
|
+
return Result(host=task.host, result=result)
|
295
|
+
|
296
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>" + result_verbose
|
297
|
+
# Return the Nornir task result as failed
|
298
|
+
return Result(host=task.host, result=result, failed=True)
|
299
|
+
|
300
|
+
|
301
|
+
def rc_install_remove_inactive_task(task: Task, verbose: bool = False) -> Result:
|
302
|
+
"""
|
303
|
+
This Nornir task executes the Cisco specific operations RESTCONF RPC Cisco-IOS-XE-install-rpc:remove to
|
304
|
+
remove all not needed software packages and files on the filesystem.
|
305
|
+
"""
|
306
|
+
# Payload
|
307
|
+
payload = {
|
308
|
+
"Cisco-IOS-XE-install-rpc:input": {
|
309
|
+
"uuid": "Install Remove Inactive",
|
310
|
+
"inactive": True,
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
# RESTCONF HTTP API call
|
315
|
+
response = rc_cisco_operation_rpc(
|
316
|
+
task_obj=task,
|
317
|
+
rpc="Cisco-IOS-XE-install-rpc:remove",
|
318
|
+
payload=payload,
|
319
|
+
)
|
320
|
+
|
321
|
+
# Set the verbose result string to add to the result summary
|
322
|
+
result_verbose = (
|
323
|
+
f"\n\nPayload: {json.dumps(payload, indent=4)}\n"
|
324
|
+
+ f"\nURL: {response.url}\n"
|
325
|
+
+ f"Method: {response.request}\n"
|
326
|
+
+ f"Response: {response}\n"
|
327
|
+
+ f"Text: {response.text}"
|
328
|
+
)
|
329
|
+
|
330
|
+
if response.status_code == 204:
|
331
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: True>"
|
332
|
+
# Set the result print level
|
333
|
+
result = result + result_verbose if verbose else result
|
334
|
+
|
335
|
+
# Return the Nornir task result as successful
|
336
|
+
return Result(host=task.host, result=result)
|
337
|
+
|
338
|
+
result = f"'{task.name}' -> RestconfResponse: <Success: False>" + result_verbose
|
339
|
+
# Return the Nornir task result as failed
|
340
|
+
return Result(host=task.host, result=result, failed=True)
|
341
|
+
|
342
|
+
|
343
|
+
#### Nornir RESTCONF RPC Tasks in regular Function ###########################################################
|
344
|
+
|
345
|
+
|
346
|
+
def rc_cisco_rpc_is_syncing(nr: Nornir, cfg_status: bool = True, verbose: bool = False) -> bool:
|
347
|
+
"""
|
348
|
+
This function runs the custom Nornir task rc_cisco_rpc_is_syncing_task to verify the configuration
|
349
|
+
datastore sync state on a Cisco device with RESTCONF. Its a Cisco specific RPC that is sent to the device.
|
350
|
+
The result will be printed to std-out in custom Nornir style.
|
351
|
+
"""
|
352
|
+
# pylint: disable=invalid-name
|
353
|
+
|
354
|
+
# Return False if cfg_status argument is False
|
355
|
+
if not cfg_status:
|
356
|
+
return False
|
357
|
+
|
358
|
+
# Run the Nornir Task rc_cisco_rpc_is_syncing_task
|
359
|
+
task_result = nr.run(
|
360
|
+
task=rc_cisco_rpc_is_syncing_task,
|
361
|
+
name="RESTCONF verify is-syncing",
|
362
|
+
verbose=verbose,
|
363
|
+
on_failed=True,
|
364
|
+
)
|
365
|
+
|
366
|
+
# Print the Nornir task result
|
367
|
+
print_result(task_result)
|
368
|
+
|
369
|
+
# Return False if the task failed or True if the task was successful
|
370
|
+
return not bool(task_result.failed)
|
371
|
+
|
372
|
+
|
373
|
+
def rc_cisco_rpc_save_config(nr: Nornir, cfg_status: bool = True, verbose: bool = False) -> bool:
|
374
|
+
"""
|
375
|
+
This function runs the custom Nornir task rc_cisco_rpc_save_config_task to save the configuration on a
|
376
|
+
Cisco device with RESTCONF. Its a Cisco specific RPC that is sent to the device. The result will be
|
377
|
+
printed to std-out in Nornir style and the function return True or False depending wheather the task was
|
378
|
+
successful.
|
379
|
+
"""
|
380
|
+
# pylint: disable=invalid-name
|
381
|
+
|
382
|
+
# Return False if cfg_status argument is False
|
383
|
+
if not cfg_status:
|
384
|
+
return False
|
385
|
+
|
386
|
+
# Run the custom Nornir task rc_cisco_rpc_save_config_task
|
387
|
+
task_result = nr.run(
|
388
|
+
task=rc_cisco_rpc_save_config_task,
|
389
|
+
name="RESTCONF save config",
|
390
|
+
verbose=verbose,
|
391
|
+
on_failed=True,
|
392
|
+
)
|
393
|
+
|
394
|
+
# Print the Nornir task result
|
395
|
+
print_result(task_result)
|
396
|
+
|
397
|
+
# Return False if the task failed or True if the task was successful
|
398
|
+
return not bool(task_result.failed)
|
399
|
+
|
400
|
+
|
401
|
+
def rc_cisco_rpc_copy_file(
|
402
|
+
nr: Nornir, source: str, destination: str, name: Union[str, None] = None, verbose=False
|
403
|
+
) -> bool:
|
404
|
+
"""
|
405
|
+
This function runs the custom Nornir task rc_cisco_rpc_copy_file_task to copy a file from or to a Cisco
|
406
|
+
device with RESTCONF. Its a Cisco specific RPC that is sent to the device. The result will be printed to
|
407
|
+
std-out in Nornir style and the function return True or False depending wheather the task was successful.
|
408
|
+
"""
|
409
|
+
# pylint: disable=invalid-name
|
410
|
+
|
411
|
+
# Set a custom task name if the argument name is not None
|
412
|
+
name = name if name else "RESTCONF copy file"
|
413
|
+
|
414
|
+
# Run the custom Nornir task rc_cisco_rpc_copy_file_task
|
415
|
+
task_result = nr.run(
|
416
|
+
task=rc_cisco_rpc_copy_file_task,
|
417
|
+
name=name,
|
418
|
+
source=source,
|
419
|
+
destination=destination,
|
420
|
+
verbose=verbose,
|
421
|
+
on_failed=True,
|
422
|
+
)
|
423
|
+
|
424
|
+
# Print the Nornir task result
|
425
|
+
print_result(task_result)
|
426
|
+
|
427
|
+
# Return False if the task failed or True if the task was successful
|
428
|
+
return not bool(task_result.failed)
|
429
|
+
|
430
|
+
|
431
|
+
def rc_cisco_rpc_rollback_config(
|
432
|
+
nr: Nornir,
|
433
|
+
target_url: str,
|
434
|
+
revert_timer: int = None,
|
435
|
+
name: Union[str, None] = None,
|
436
|
+
verbose: bool = False,
|
437
|
+
) -> bool:
|
438
|
+
"""
|
439
|
+
This function runs the custom Nornir task rc_cisco_rpc_rollback_config_task to rollback the copy of a
|
440
|
+
Cisco device to a config specified by a target-url with RESTCONF. Its a Cisco specific RPC that is sent to
|
441
|
+
the device. The result will be printed to std-out in custom Nornir style and the function return True or
|
442
|
+
False depending wheather the task was successful.
|
443
|
+
"""
|
444
|
+
# pylint: disable=invalid-name
|
445
|
+
|
446
|
+
# Set a custom task name if the argument name is not None
|
447
|
+
name = name if name else "RESTCONF rollback config"
|
448
|
+
|
449
|
+
# Run the custom Nornir task rc_cisco_rpc_rollback_config_task
|
450
|
+
task_result = nr.run(
|
451
|
+
task=rc_cisco_rpc_rollback_config_task,
|
452
|
+
name=name,
|
453
|
+
target_url=target_url,
|
454
|
+
revert_timer=revert_timer,
|
455
|
+
verbose=verbose,
|
456
|
+
on_failed=True,
|
457
|
+
)
|
458
|
+
|
459
|
+
# Print the Nornir task result
|
460
|
+
print_result(task_result)
|
461
|
+
|
462
|
+
# Return False if the task failed or True if the task was successful
|
463
|
+
return not bool(task_result.failed)
|
464
|
+
|
465
|
+
|
466
|
+
def rc_software_install_one_shot(nr: Nornir, issu: bool = False, verbose: bool = False) -> bool:
|
467
|
+
"""
|
468
|
+
This function takes the result of the function host_dict as an argument andruns the custom
|
469
|
+
Nornir task rc_software_install_one_shot_task to start the one-shot installation process of the desired
|
470
|
+
software version. The result will be printed to std-out in custom Nornir style and the script terminates
|
471
|
+
with an info message in case of an error.
|
472
|
+
"""
|
473
|
+
# pylint: disable=invalid-name
|
474
|
+
|
475
|
+
# Run the custom Nornir task rc_software_install_one_shot_task
|
476
|
+
task_result = nr.run(
|
477
|
+
task=rc_software_install_one_shot_task,
|
478
|
+
name="RESTCONF one-shot install",
|
479
|
+
issu=issu,
|
480
|
+
verbose=verbose,
|
481
|
+
on_failed=True,
|
482
|
+
)
|
483
|
+
|
484
|
+
# Print the Nornir task result
|
485
|
+
print_result(task_result)
|
486
|
+
|
487
|
+
# Return False if the task failed or True if the task was successful
|
488
|
+
return not bool(task_result.failed)
|
489
|
+
|
490
|
+
|
491
|
+
def rc_install_remove_inactive(nr: Nornir, verbose: bool = False) -> bool:
|
492
|
+
"""
|
493
|
+
This function runs the Nornir task rc_install_remove_inactive_task to to remove all not needed software
|
494
|
+
packages and files on the filesystem with RESTCONF. The result will be printed to std-out in custom Nornir
|
495
|
+
style and the script terminates with an info message in case of an error.
|
496
|
+
"""
|
497
|
+
# pylint: disable=invalid-name
|
498
|
+
|
499
|
+
# Run the custom Nornir task rc_install_remove_inactive_task
|
500
|
+
task_result = nr.run(
|
501
|
+
task=rc_install_remove_inactive_task,
|
502
|
+
name="RESTCONF install remove inactive",
|
503
|
+
verbose=verbose,
|
504
|
+
on_failed=True,
|
505
|
+
)
|
506
|
+
|
507
|
+
# Print the Nornir task result
|
508
|
+
print_result(task_result)
|
509
|
+
|
510
|
+
# Return False if the task failed or True if the task was successful
|
511
|
+
return not bool(task_result.failed)
|
512
|
+
|
513
|
+
|
514
|
+
#### Nornir RESTCONF RPC Tasks with CLI Fallback in regular Function #########################################
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains complete RESTCONF configuration workflows from multiple dsc_nornir functions.
|
4
|
+
|
5
|
+
The functions are ordered as followed:
|
6
|
+
- Complete RESTCONF configuration workflows
|
7
|
+
"""
|
8
|
+
|
9
|
+
from nornir.core import Nornir
|
10
|
+
from nornir_collection.utils import print_task_title, exit_error
|
11
|
+
from nornir_collection.cisco.configuration_management.restconf.cisco_rpc import (
|
12
|
+
rc_cisco_rpc_is_syncing,
|
13
|
+
rc_cisco_rpc_rollback_config,
|
14
|
+
rc_cisco_rpc_copy_file,
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
#### Complete RESTCONF Configuration Workflow 01 #############################################################
|
19
|
+
|
20
|
+
|
21
|
+
def rc_replace_config(
|
22
|
+
nr: Nornir,
|
23
|
+
rebuild: bool = False,
|
24
|
+
cfg_status: bool = True,
|
25
|
+
revert_timer: int = None,
|
26
|
+
verbose: bool = False,
|
27
|
+
) -> bool:
|
28
|
+
"""
|
29
|
+
Replace the current configuration with a specified configuration file.
|
30
|
+
This function replaces the current configuration with the golden-config by default or the day0-config
|
31
|
+
if the rebuild argument is set to True. It returns True if the operation was successful, otherwise False.
|
32
|
+
"""
|
33
|
+
# pylint: disable=invalid-name
|
34
|
+
|
35
|
+
# Return False if cfg_status argument is False
|
36
|
+
if not cfg_status:
|
37
|
+
return False
|
38
|
+
|
39
|
+
# Set rollback_config to day0-config if rebuild is True, else set it to golden-config
|
40
|
+
rollback_config = "day0-config" if rebuild else "golden-config"
|
41
|
+
|
42
|
+
# Print the task title if revert_timer is not set
|
43
|
+
if not revert_timer:
|
44
|
+
print_task_title(f"Replace current config with {rollback_config}")
|
45
|
+
|
46
|
+
# Checks if an active datastore sync in ongoing and wait until is finish
|
47
|
+
rc_cisco_rpc_is_syncing(nr=nr, verbose=verbose)
|
48
|
+
|
49
|
+
# Replace the running-config with the rollback_config from the switch flash:
|
50
|
+
cfg_status = rc_cisco_rpc_rollback_config(
|
51
|
+
nr=nr,
|
52
|
+
name=f"RESTCONF rollback {rollback_config}",
|
53
|
+
target_url=f"flash:{rollback_config}",
|
54
|
+
revert_timer=revert_timer,
|
55
|
+
verbose=verbose,
|
56
|
+
)
|
57
|
+
|
58
|
+
return cfg_status
|
59
|
+
|
60
|
+
|
61
|
+
def rc_update_golden_config(nr: Nornir, verbose: bool = False) -> None:
|
62
|
+
"""
|
63
|
+
Updates the golden configuration on Cisco devices using RESTCONF.
|
64
|
+
This function performs the following steps:
|
65
|
+
1. Prints the task title for updating the golden configuration.
|
66
|
+
2. Checks if an active datastore synchronization is ongoing and waits until it finishes.
|
67
|
+
3. Saves the current running configuration as the new golden configuration to the local device flash.
|
68
|
+
4. Exits the script if the configuration update fails.
|
69
|
+
"""
|
70
|
+
# pylint: disable=invalid-name
|
71
|
+
|
72
|
+
# Update golden config
|
73
|
+
task_text = "RESTCONF update golden-config"
|
74
|
+
print_task_title(title=task_text)
|
75
|
+
|
76
|
+
# Checks if an active datastore sync in ongoing and wait until is finish
|
77
|
+
rc_cisco_rpc_is_syncing(nr=nr, verbose=verbose)
|
78
|
+
|
79
|
+
# Save the new config as the new golden config to the local device flash
|
80
|
+
cfg_status = rc_cisco_rpc_copy_file(
|
81
|
+
nr=nr,
|
82
|
+
name=task_text,
|
83
|
+
source="running-config",
|
84
|
+
destination="flash:golden-config",
|
85
|
+
verbose=verbose,
|
86
|
+
)
|
87
|
+
|
88
|
+
# Exit the script if the config status if False
|
89
|
+
if not cfg_status:
|
90
|
+
text = "ALERT: UPDATE GOLDEN-CONFIG HAVE FAILED!"
|
91
|
+
msg = [
|
92
|
+
"-> RESTCONF failed to update the golden configuration",
|
93
|
+
"-> The remaining tasks have been omitted due to RESTCONF failed",
|
94
|
+
]
|
95
|
+
exit_error(task_text=task_text, text=text, msg=msg)
|