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.
Files changed (59) hide show
  1. nornir_collection/__init__.py +0 -0
  2. nornir_collection/batfish/__init__.py +0 -0
  3. nornir_collection/batfish/assert_config.py +358 -0
  4. nornir_collection/batfish/utils.py +129 -0
  5. nornir_collection/cisco/__init__.py +0 -0
  6. nornir_collection/cisco/configuration_management/__init__.py +0 -0
  7. nornir_collection/cisco/configuration_management/cli/__init__.py +0 -0
  8. nornir_collection/cisco/configuration_management/cli/config_tasks.py +569 -0
  9. nornir_collection/cisco/configuration_management/cli/config_workflow.py +107 -0
  10. nornir_collection/cisco/configuration_management/cli/show_tasks.py +677 -0
  11. nornir_collection/cisco/configuration_management/netconf/__init__.py +0 -0
  12. nornir_collection/cisco/configuration_management/netconf/config_tasks.py +564 -0
  13. nornir_collection/cisco/configuration_management/netconf/config_workflow.py +298 -0
  14. nornir_collection/cisco/configuration_management/netconf/nr_cfg_iosxe_netconf.py +186 -0
  15. nornir_collection/cisco/configuration_management/netconf/ops_tasks.py +307 -0
  16. nornir_collection/cisco/configuration_management/processor.py +151 -0
  17. nornir_collection/cisco/configuration_management/pyats.py +236 -0
  18. nornir_collection/cisco/configuration_management/restconf/__init__.py +0 -0
  19. nornir_collection/cisco/configuration_management/restconf/cisco_rpc.py +514 -0
  20. nornir_collection/cisco/configuration_management/restconf/config_workflow.py +95 -0
  21. nornir_collection/cisco/configuration_management/restconf/tasks.py +325 -0
  22. nornir_collection/cisco/configuration_management/utils.py +511 -0
  23. nornir_collection/cisco/software_upgrade/__init__.py +0 -0
  24. nornir_collection/cisco/software_upgrade/cisco_software_upgrade.py +283 -0
  25. nornir_collection/cisco/software_upgrade/utils.py +794 -0
  26. nornir_collection/cisco/support_api/__init__.py +0 -0
  27. nornir_collection/cisco/support_api/api_calls.py +1173 -0
  28. nornir_collection/cisco/support_api/cisco_maintenance_report.py +221 -0
  29. nornir_collection/cisco/support_api/cisco_support.py +727 -0
  30. nornir_collection/cisco/support_api/reports.py +747 -0
  31. nornir_collection/cisco/support_api/utils.py +316 -0
  32. nornir_collection/fortinet/__init__.py +0 -0
  33. nornir_collection/fortinet/utils.py +36 -0
  34. nornir_collection/git.py +224 -0
  35. nornir_collection/netbox/__init__.py +0 -0
  36. nornir_collection/netbox/custom_script.py +107 -0
  37. nornir_collection/netbox/inventory.py +360 -0
  38. nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py +989 -0
  39. nornir_collection/netbox/set_device_status.py +67 -0
  40. nornir_collection/netbox/sync_datasource.py +111 -0
  41. nornir_collection/netbox/update_cisco_inventory_data.py +158 -0
  42. nornir_collection/netbox/update_cisco_support_plugin_data.py +339 -0
  43. nornir_collection/netbox/update_fortinet_inventory_data.py +161 -0
  44. nornir_collection/netbox/update_purestorage_inventory_data.py +144 -0
  45. nornir_collection/netbox/utils.py +261 -0
  46. nornir_collection/netbox/verify_device_primary_ip.py +202 -0
  47. nornir_collection/nornir_plugins/__init__.py +0 -0
  48. nornir_collection/nornir_plugins/inventory/__init__.py +0 -0
  49. nornir_collection/nornir_plugins/inventory/netbox.py +250 -0
  50. nornir_collection/nornir_plugins/inventory/staggered_yaml.py +143 -0
  51. nornir_collection/nornir_plugins/inventory/utils.py +277 -0
  52. nornir_collection/purestorage/__init__.py +0 -0
  53. nornir_collection/purestorage/utils.py +53 -0
  54. nornir_collection/utils.py +741 -0
  55. nornir_collection-0.0.1.dist-info/LICENSE +21 -0
  56. nornir_collection-0.0.1.dist-info/METADATA +136 -0
  57. nornir_collection-0.0.1.dist-info/RECORD +59 -0
  58. nornir_collection-0.0.1.dist-info/WHEEL +5 -0
  59. nornir_collection-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ This module contains complete NETCONF config workflows from multiple dsc_nornir functions.
4
+
5
+ The functions are ordered as followed:
6
+ - Complete NETCONF config workflows
7
+ """
8
+
9
+
10
+ import time
11
+ from nornir.core import Nornir, Task
12
+ from nornir_collection.utils import (
13
+ print_task_title,
14
+ task_name,
15
+ task_result,
16
+ exit_info,
17
+ task_error,
18
+ exit_error,
19
+ )
20
+ from nornir_collection.cisco.configuration_management.restconf.config_workflow import rc_replace_config
21
+ from nornir_collection.cisco.configuration_management.netconf.config_tasks import (
22
+ nc_cfg_cleanup,
23
+ nc_cfg_tpl,
24
+ nc_cfg_tpl_int,
25
+ )
26
+ from nornir_collection.cisco.configuration_management.netconf.ops_tasks import (
27
+ # nc_lock,
28
+ # nc_unlock,
29
+ nc_validate,
30
+ nc_discard,
31
+ nc_commit,
32
+ )
33
+ from nornir_collection.cisco.configuration_management.restconf.cisco_rpc import (
34
+ rc_cisco_rpc_is_syncing,
35
+ rc_cisco_rpc_save_config,
36
+ )
37
+ from nornir_collection.cisco.configuration_management.processor import nr_testsprocessor
38
+ from nornir_collection.cisco.configuration_management.cli.config_tasks import cli_confirm_or_revert_config
39
+
40
+
41
+ #### NETCONF config Workflow Helper #########################################################################
42
+
43
+
44
+ def _nc_exit_dry_run(nr: Task, cfg_status: bool, verbose: bool) -> None:
45
+ """
46
+ Handles the exit process for a NETCONF dry-run operation.
47
+ This function performs the following steps:
48
+ 1. Discards all changes on the NETCONF candidate datastore.
49
+ 2. Unlocks the NETCONF datastore.
50
+ 3. Verifies the NETCONF dry-run status of the config results.
51
+ 4. Exits the script with an appropriate message based on the config status.
52
+
53
+ !!! NETCONF lock/unlock rpc have been commented out as the unlock fails since 17.12.X !!!
54
+ """
55
+ # pylint: disable=invalid-name
56
+
57
+ # Discard all changes on the NETCONF candidate datastore
58
+ nc_discard(nr=nr, verbose=verbose)
59
+
60
+ # Unlock the NETCONF datastore
61
+ # nc_unlock(nr=nr, verbose=verbose)
62
+
63
+ # Verify NETCONF dry-run status of the config results and exit the script with a proper message
64
+ task_text = "Verify NETCONF dry-run config results"
65
+ msg = "-> The remaining tasks have been omitted due to the NETCONF dry-run execution"
66
+ print_task_title(title=task_text)
67
+ print(task_name(text=task_text))
68
+
69
+ if cfg_status:
70
+ text = "GOOD NEWS! ALL CONFIGS HAVE BEEN RENDERED AND VALIDATED SUCCESSFUL!"
71
+ exit_info(task_text=task_text, text=text, msg=msg)
72
+ else:
73
+ text = "ALERT: ONE OR MORE CONFIGS HAVE FAILED!"
74
+ exit_error(task_text=task_text, text=text, msg=msg)
75
+
76
+
77
+ def _nc_commit_confirm_testing(nr: Task) -> bool:
78
+ """
79
+ Executes a series of tests during a NETCONF commit-confirm timeout period. This function starts a
80
+ timer, runs tests using the Nornir TestsProcessor, and prints the results along with the time taken to
81
+ complete the tests. If the tests are not successful, it waits for the specified confirm timeout period
82
+ to expire.
83
+ """
84
+ # pylint: disable=invalid-name
85
+
86
+ # Start a timer to check how long the Nornir TestProcessor Task need
87
+ timer_start = time.time()
88
+ cfg_status = True
89
+
90
+ print_task_title("Run Nornir TestsProcessor during NETCONF commit-confirm timeout")
91
+
92
+ # Run Nornir TestsProcessor for Unit, Integration, and System tests. All tests files with the inventory
93
+ # key starting with the prefix "cfgtp_{name}_" and are loaded from the inventory.
94
+ for name in ["Unit", "Integration", "System"]:
95
+ # Run the Nornir TestsProcessor Task
96
+ if not nr_testsprocessor(nr=nr, name=name, inv_key=f"cfgtp_{name.lower()}_"):
97
+ cfg_status = False
98
+
99
+ # Print a overall TestsProcessor result
100
+ task_text = "NORNIR overall TestsProcessor result"
101
+ print(task_name(text=task_text))
102
+ print(task_result(text=task_text, changed=False, level_name="INFO" if cfg_status else "ERROR"))
103
+ print(f"'{task_text}' -> NornirResponse <Success: {'True' if cfg_status else 'False'}>")
104
+
105
+ # Print the time which Nornir TestsProcessor needed
106
+ exeeded_time = round(time.time() - timer_start, 1)
107
+ print(f"-> Nornir TestsProcessor finished in: {exeeded_time}s")
108
+
109
+ ########################################### WORKAROUND ##################################################
110
+ ### Wait time in case of a failure not needed at the moment due to the RESTCONF workaround ###
111
+ # If the cfg_status of the Nornir TestsProcessor is not successful
112
+ # if not cfg_status:
113
+ # remaining_timout = confirm_timeout - exeeded_time
114
+ # print(f"-> Wait remaining {remaining_timout}s for NETCONF commit confirm timeout to expire ...")
115
+ # time.sleep(remaining_timout)
116
+ #########################################################################################################
117
+
118
+ return cfg_status
119
+
120
+
121
+ def _verify_confirm_timeout(timer_start: float, confirm_timeout: int) -> bool:
122
+ """
123
+ TBD
124
+ """
125
+ # Set the end timer and calculate the remaining confirm-timeout
126
+ timer_end = round(time.time() - timer_start, 1)
127
+ remaining_timout = round(confirm_timeout - timer_end, 1)
128
+
129
+ # If the remaining confirm-timeout is already over after or during the commit then most likely the
130
+ # connection to the device was lost and the config-rollback happened.
131
+ if remaining_timout <= 0:
132
+ task_text = "NETCONF verify commit results"
133
+ print(task_name(text=task_text))
134
+ print(task_error(text=task_text, changed=False))
135
+ print(f"'{task_text}' -> NornirResult <Success: False>")
136
+ print(f"-> The remaining confirm-timeout is: {remaining_timout}s")
137
+ print(" - The config confirm-timeout timed out after the commit!")
138
+ print(" - Most likely the connection to the device was lost and the config-rollback happened!")
139
+ print(" - The device is now in the previous state before the commit!")
140
+
141
+ # Return False as the config-rollback timeout was already over after or during the commit
142
+ return False
143
+
144
+ # Return True and print nothing as everything is fine
145
+ return True
146
+
147
+
148
+ #### NETCONF config Workflow ################################################################################
149
+
150
+
151
+ def nc_cfg_iosxe_netconf_config(
152
+ nr: Nornir,
153
+ rebuild: str = "golden-config",
154
+ disable_commit_confirm: bool = False,
155
+ confirm_timeout: int = 120,
156
+ dry_run: bool = False,
157
+ verbose: bool = False,
158
+ ) -> bool:
159
+ """
160
+ Configures an IOS-XE device using NETCONF. This function performs a series of config steps on an
161
+ IOS-XE device using NETCONF. It handles locking the candidate datastore, applying different
162
+ config templates, validating, and committing the changes. It also supports commit-confirm
163
+ operations to run Nornir TestsProcessor device testing and dry-runs without commiting the config.
164
+
165
+ !!! NETCONF lock/unlock rpc have been commented out as the unlock fails since 17.12.X !!!
166
+ """
167
+ # pylint: disable=invalid-name,too-many-arguments
168
+
169
+ print_task_title("Cleanup NETCONF candidate config")
170
+
171
+ # Checks if a datastore sync in ongoing and wait until is finish. Default cfg_status argument is True
172
+ cfg_status = rc_cisco_rpc_is_syncing(nr=nr, verbose=verbose)
173
+
174
+ ########################################### WORKAROUND ##################################################
175
+ ### It seems that since 17.12.4 the NETCONF server implementation has a bug ! ###
176
+ ### - The lock/unlock rpc is not working as expected. ###
177
+ ### - The unlock rpc fails with an error message / The lock rpc is working as expected ###
178
+ ### - The lock is not needed for the config workflow. The lock is only needed for the rollback. ###
179
+ ### - There the NETCONF rollback-on-error is not working as expected. ###
180
+ ### -> Themporary workaround is to comment out the lock/unlock rpc and implement a rollback by CLI ###
181
+ #########################################################################################################
182
+ # Lock the NETCONF candidate datastore
183
+ # if not nc_lock(nr=nr, cfg_status=cfg_status, verbose=verbose):
184
+ # return False
185
+
186
+ # Cleanup configs which can't be done efficient with the Jinja2 template concept
187
+ cfg_status = nc_cfg_cleanup(nr=nr, cfg_status=cfg_status, verbose=verbose)
188
+
189
+ if cfg_status:
190
+ print_task_title("Configure NETCONF candidate config")
191
+
192
+ # Create a dict with the template startswith string and the task text as key-value pairs
193
+ cfg_tasks = {
194
+ "tpl_base": "NETCONF configure base system payload template",
195
+ "tpl_sys": "NETCONF configure general system payload template",
196
+ }
197
+ # Configure NETCONF system config if cfg_status is still True
198
+ cfg_status = nc_cfg_tpl(nr=nr, cfg_tasks=cfg_tasks, cfg_status=cfg_status, verbose=verbose)
199
+
200
+ # Create a dict with the template startswith string and the task text as key-value pairs
201
+ cfg_tasks = {
202
+ "tpl_portchannel": "NETCONF configure portchannel interface payload template",
203
+ "tpl_int": "NETCONF configure general interface payload template",
204
+ "tpl_svi": "NETCONF configure vlan interface payload template",
205
+ }
206
+ # Continue NETCONF interface config if cfg_status is still True
207
+ cfg_status = nc_cfg_tpl_int(nr=nr, cfg_tasks=cfg_tasks, cfg_status=cfg_status, verbose=verbose)
208
+
209
+ # Print the task title basesd on the disable_commit_confirm argument and set the commit variable
210
+ if disable_commit_confirm:
211
+ print_task_title("Commit or discard NETCONF candidate config")
212
+ else:
213
+ print_task_title("Commit-confirm or discard NETCONF candidate config")
214
+
215
+ # Validate NETCONF config if cfg_status is still True
216
+ cfg_status = nc_validate(nr=nr, cfg_status=cfg_status, verbose=verbose)
217
+
218
+ if dry_run:
219
+ _nc_exit_dry_run(nr=nr, cfg_status=cfg_status, verbose=verbose)
220
+
221
+ ########################################### WORKAROUND ##################################################
222
+ # If its a commit-confirm and the cfg_status is True
223
+ if not disable_commit_confirm and cfg_status:
224
+ # Calculate the revert-timer in minutes from the confirm-timeout in seconds
225
+ revert_timer = confirm_timeout // 60
226
+ # Replace the config with a revert-timer right before the commit
227
+ cfg_status = rc_replace_config(nr=nr, rebuild=rebuild, verbose=verbose, revert_timer=revert_timer)
228
+ #########################################################################################################
229
+
230
+ # Start a timer to check how long the commit takes.
231
+ timer_start = time.time()
232
+
233
+ ########################################### WORKAROUND ##################################################
234
+ ### Set confirm to 'False' with the RESTCONF workaround ###
235
+ #########################################################################################################
236
+ # Commit all changes on the NETCONF candidate datastore if cfg_status is still True
237
+ cfg_status = nc_commit(
238
+ nr=nr,
239
+ confirm=False,
240
+ confirm_timeout=confirm_timeout,
241
+ cfg_status=cfg_status,
242
+ verbose=verbose,
243
+ )
244
+
245
+ # If the revert-timer is already over after or during the commit, then most likely the connection to the
246
+ # device was lost and the config-rollback happened.
247
+ if not disable_commit_confirm:
248
+ if not _verify_confirm_timeout(timer_start=timer_start, confirm_timeout=confirm_timeout):
249
+ # Return as the config-rollback timeout was already over after or during the commit
250
+ return False
251
+
252
+ # Run some tests if its a commit-confirm no error happend on the commit
253
+ if not disable_commit_confirm and cfg_status:
254
+ # Run the Nornir TestsProcessor test suite
255
+ cfg_status = _nc_commit_confirm_testing(nr=nr)
256
+
257
+ print_task_title("Commit or discard NETCONF candidate config")
258
+
259
+ ########################################### WORKAROUND ##############################################
260
+ ### The is_syncing rpc and the second commit is not needed with the RESTCONF workaround ###
261
+ #####################################################################################################
262
+ # Checks if an active datastore sync in ongoing and wait until is finish if cfg_status is True
263
+ # cfg_status = rc_cisco_rpc_is_syncing(nr=nr, cfg_status=cfg_status, verbose=verbose)
264
+
265
+ # Commit all changes on the NETCONF candidate datastore if cfg_status is still True
266
+ # cfg_status = nc_commit(nr=nr, confirm=False, cfg_status=cfg_status, verbose=verbose)
267
+
268
+ ########################################### WORKAROUND ##############################################
269
+ # Confirm the revert-timer right after the commit
270
+ cfg_status = cli_confirm_or_revert_config(
271
+ nr=nr, action="confirm", cfg_status=cfg_status, verbose=verbose
272
+ )
273
+ #####################################################################################################
274
+
275
+ # Discard the NETCONF config if there happen an error on the commit or commit confirm
276
+ if not cfg_status:
277
+ # Discard all changes on the NETCONF candidate datastore
278
+ nc_discard(nr=nr, verbose=verbose)
279
+
280
+ ########################################### WORKAROUND ##############################################
281
+ # Revert the config right after the discard
282
+ cli_confirm_or_revert_config(nr=nr, action="revert", cfg_status=True, verbose=verbose)
283
+ #####################################################################################################
284
+ # Unlock the NETCONF datastore
285
+ # nc_unlock(nr=nr, verbose=verbose)
286
+
287
+ return cfg_status
288
+
289
+ # Unlock the NETCONF datastore
290
+ # cfg_status = nc_unlock(nr=nr, cfg_status=cfg_status, verbose=verbose)
291
+
292
+ # Checks if an active datastore sync in ongoing and wait until is finish if cfg_status is True
293
+ cfg_status = rc_cisco_rpc_is_syncing(nr=nr, cfg_status=cfg_status, verbose=verbose)
294
+
295
+ # Send the Cisco save config RESTCONF RPC if cfg_status is True
296
+ cfg_status = rc_cisco_rpc_save_config(nr=nr, cfg_status=cfg_status, verbose=verbose)
297
+
298
+ return cfg_status
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ This module is a complete config management inclusive testing and rollback in case of failures for
4
+ Cisco IOS-XE devices. The Main function is intended to import and execute by other scripts.
5
+ """
6
+
7
+
8
+ import argparse
9
+ from nornir.core.filter import F
10
+ from nornir_collection.nornir_plugins.inventory.utils import init_nornir
11
+ from nornir_collection.cisco.configuration_management.cli.show_tasks import nr_pre_config_check
12
+ from nornir_collection.cisco.configuration_management.netconf.config_workflow import (
13
+ nc_cfg_iosxe_netconf_config,
14
+ )
15
+ from nornir_collection.cisco.configuration_management.restconf.config_workflow import (
16
+ rc_update_golden_config,
17
+ rc_replace_config,
18
+ )
19
+ from nornir_collection.utils import print_task_title, task_name, task_info, task_error, exit_info, exit_error
20
+
21
+
22
+ __author__ = "Willi Kubny"
23
+ __maintainer__ = "Willi Kubny"
24
+ __version__ = "1.0"
25
+ __license__ = "MIT"
26
+ __email__ = "willi.kubny@dreyfusbank.ch"
27
+ __status__ = "Production"
28
+
29
+
30
+ def main(nr_config: str, args: argparse.Namespace) -> None:
31
+ """
32
+ Main function is intended to import and execute by other scripts.
33
+ It initialize Nornir, configures and test the config on Cisco IOS-XE devices.
34
+
35
+ * Args:
36
+ * nr_config (str): Path to the Nornir config file.
37
+ * args (argparse.Namespace): Command-line arguments parsed by argparse.
38
+
39
+ * Steps:
40
+ * Initialize Nornir
41
+ * Verify that all filtered devices that have the tag slug 'cfg-mgmt'
42
+ * Do a pre-config check to ensure the current config is in a clean state
43
+ * Configure the network from code with NETCONF and RESTCONF
44
+ * Set the config rebuild level (Day0-Config or Golden-Config)
45
+ * Perform a RESTCONF config-rollback to the Day0-Config or Golden-Config
46
+ * Verify that there is no NETCONF sync in progress
47
+ * Lock the NETCONF datastore
48
+ * Reset all interfaces to default config
49
+ * Start NETCONF general system config
50
+ * Continue NETCONF interface config
51
+ * Validate NETCONF config
52
+ * Abort the config if a dry-run is requested
53
+ * Commit all changes on the NETCONF candidate datastore (default with commit-confirm)
54
+ * Run testing if commit-confirm was requested
55
+ * Commit again if testing was successful
56
+ * Unlock the NETCONF datastore
57
+ * Verify that there is no NETCONF sync in progress
58
+ * Save the running-config to the startup-config using RESTCONF
59
+ * Verify the status of the config results
60
+ * Update the golden-config with RESTCONF
61
+
62
+ * Exits:
63
+ * It exits with appropriate messages and statuses based on the success or failure of each step.
64
+ """
65
+ # pylint: disable=invalid-name
66
+
67
+ #### Initialize Script and Nornir #######################################################################
68
+
69
+ # Initialize, transform and filter the Nornir inventory are return the filtered Nornir object
70
+ # Define data to load from NetBox in addition to the base Nornir inventory plugin
71
+ add_netbox_data = {"load_virtual_chassis_data": True, "load_interface_data": True, "load_vlan_data": True}
72
+ nr = init_nornir(config_file=nr_config, args=args, add_netbox_data=add_netbox_data)
73
+
74
+ # Filter the Nornir inventory again only with devices that have the tag slug 'cfg-mgmt' and compare the
75
+ # filtered inventory with the original inventory to prevent the script from running on devices that are
76
+ # not intended for config management
77
+ print_task_title(title="Verify Nornir inventory filter for CFG-MGMT")
78
+ task_text = "NORNIR verify inventory filter for CFG-MGMT"
79
+ print(task_name(text=task_text))
80
+ # Filter the Nornir inventory with devices that have the tag 'cfg-mgmt'
81
+ nr_cfg_mgmt = nr.filter(F(tags__contains="cfg-mgmt"))
82
+ # Exit the script if the filtered inventory does not match the original inventory
83
+ if len(nr.inventory.hosts) != len(nr_cfg_mgmt.inventory.hosts):
84
+ print(task_error(text=task_text, changed=False))
85
+ print(f"'{task_text}' -> NornirResult <Success: False>")
86
+ exit_error(
87
+ task_text=task_text,
88
+ text="ALERT: INVENTORY FILTER MISMATCH FOR CFG-MGMT!",
89
+ msg=[
90
+ "-> The filtered inventory does not match the original inventory",
91
+ "-> Ensure all devices intended for config management are tagged with 'CFG-MGMT'",
92
+ ],
93
+ )
94
+ print(task_info(text=task_text, changed=False))
95
+ print(f"'{task_text}' -> NornirResponse <Success: True>")
96
+ print("-> All filtered devices are tagged with 'CFG-MGMT'")
97
+
98
+ #### Run Nornir Tasks to Verify the config State ########################################################
99
+
100
+ # First do a pre-config check to ensure the current config is in a clean state
101
+ if not args.rebuild and not args.skip_pre_check:
102
+ cfg_status = nr_pre_config_check(nr=nr)
103
+ # Exit the script if the config status is not clean
104
+ if not cfg_status:
105
+ task_text = "Verify pre-config check results"
106
+ print_task_title(title=task_text)
107
+ print(f"{task_name(text=task_text)}")
108
+ exit_error(
109
+ task_text=task_text,
110
+ text="BAD NEWS! THE RUNNING-CONFIG IS NOT CLEAN!",
111
+ msg=[
112
+ "-> Bring the running-config into a clean state with the golden-config",
113
+ "-> Or skip the pre-config check",
114
+ ],
115
+ )
116
+
117
+ #### Run Nornir Tasks to Replace the config #############################################################
118
+
119
+ # If its not a NETCONF dry-run, replace the current config with the golden-config or day0-config
120
+ if not args.dryrun:
121
+ # Replace the config with a Cisco specific RESTCONF RPC. Default cfg_status argument is True
122
+ cfg_status = rc_replace_config(nr=nr, rebuild=args.rebuild, verbose=args.verbose, revert_timer=None)
123
+
124
+ # Exit the script if the RESTCONF replace-config failed
125
+ if not cfg_status:
126
+ task_text = "Verify RESTCONF replace-config results"
127
+ print_task_title(title=task_text)
128
+ print(f"{task_name(text=task_text)}")
129
+ exit_error(
130
+ task_text=task_text,
131
+ text="ALERT: REPLACE-CONFIG HAVE FAILED!",
132
+ msg=[
133
+ "-> RESTCONF failed to replace the current config",
134
+ "-> The remaining tasks have been omitted due to RESTCONF failed",
135
+ ],
136
+ )
137
+
138
+ #### Run Nornir Tasks to Configure IOS-XE device from Code with NETCONF #################################
139
+
140
+ # The IOS-XE device will be reconfigured to the desired state and return a boolian (True == Successful)
141
+ cfg_status = nc_cfg_iosxe_netconf_config(
142
+ nr=nr,
143
+ rebuild=args.rebuild,
144
+ disable_commit_confirm=args.disable_commit_confirm,
145
+ confirm_timeout=args.confirm_timeout,
146
+ dry_run=args.dryrun,
147
+ verbose=args.verbose,
148
+ )
149
+
150
+ # Exit the script if the NETCONF config failed
151
+ if not cfg_status:
152
+ # Replace the config with a Cisco specific RESTCONF RPC. Default cfg_status argument is True
153
+ # This ensures that the golden-config is not updated if the NETCONF config failed
154
+ rc_replace_config(nr=nr, rebuild=args.rebuild, verbose=args.verbose, revert_timer=None)
155
+
156
+ # Verify status of the config results
157
+ task_text = "Verify NETCONF config results"
158
+ print_task_title(title=task_text)
159
+ print(f"{task_name(text=task_text)}")
160
+ exit_error(
161
+ task_text=task_text,
162
+ text="ALERT: ONE OR MORE CONFIGS HAVE FAILED!",
163
+ msg=[
164
+ "-> NETCONF failed to apply the desired config",
165
+ "-> The remaining tasks have been omitted due to NETCONF failed",
166
+ "-> The current config is unchanged and the golden-config have not been updated yet",
167
+ ],
168
+ )
169
+
170
+ #### Run Nornir Tasks to Update the Golden-config #######################################################
171
+
172
+ # Update the golden config with RESTCONF or exit the script if RESTCONF failed
173
+ rc_update_golden_config(nr=nr, verbose=args.verbose)
174
+
175
+ # If rc_update_golden_config didn't exit the script, everything was successful
176
+ task_text = "Verify config results"
177
+ print_task_title(title=task_text)
178
+ print(f"{task_name(text=task_text)}")
179
+ exit_info(
180
+ task_text=task_text,
181
+ text="GOOD NEWS! ALL CONFIGS HAVE BEEN APPLIED SUCCESSFUL!",
182
+ msg=[
183
+ "-> The desired NETCONF config have been activated and saved",
184
+ "-> The golden-config have been updated",
185
+ ],
186
+ )