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,277 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ This module contains Nornir inventory plugins and Nornir Init functions.
4
+
5
+ The functions are ordered as followed:
6
+ - Inventory Transform Functions
7
+ - Nornir Init Functions
8
+ """
9
+
10
+ import os
11
+ import argparse
12
+ import sys
13
+ import time
14
+ from typing import Literal
15
+ from dotenv import load_dotenv
16
+ from nornir import InitNornir
17
+ from nornir.core import Nornir
18
+ from nornir.core.plugins.inventory import InventoryPluginRegister
19
+ from nornir_collection.nornir_plugins.inventory.netbox import DSCNetBoxInventory
20
+ from nornir_collection.netbox.inventory import load_additional_netbox_data
21
+ from nornir_collection.utils import (
22
+ print_task_title,
23
+ task_name,
24
+ task_info,
25
+ task_error,
26
+ nr_filter_args,
27
+ exit_error,
28
+ iterate_all,
29
+ list_flatten,
30
+ transform_env,
31
+ )
32
+
33
+
34
+ #### Inventory Transform Functions ##########################################################################
35
+
36
+
37
+ def nr_transform_host_creds_from_env(nr: Nornir) -> None:
38
+ """
39
+ This function loads the host login credentials from environment variables and stores them directly under
40
+ the host inventory level. This function can be extended to to more host transformation.
41
+ """
42
+ # pylint: disable=invalid-name
43
+
44
+ task_text = "NORNIR transform host credentials env variable"
45
+ print(task_name(text=task_text))
46
+
47
+ # Recreate Nornir 2.x transform function behavior
48
+ for host in nr.inventory.hosts.values():
49
+ # Verify that login credentials are set as environment variables. Raise a TypeError when is None
50
+ try:
51
+ # The host get the username and password loaded from an environment variable
52
+ host.username = os.environ[host.username]
53
+ host.password = os.environ[host.password]
54
+
55
+ # Continue with the next for loop iteration
56
+ continue
57
+
58
+ except KeyError as error:
59
+ print(task_error(text=task_text, changed=False))
60
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: False>")
61
+ print(f"-> Environment variable {error} for host {host} not found\n")
62
+ except TypeError:
63
+ print(task_error(text=task_text, changed=False))
64
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: False>")
65
+ print(f"-> Nornir inventory key username and/or password for host {host} not found\n")
66
+
67
+ # Exit the script with a proper message
68
+ exit_error(
69
+ task_text=task_text,
70
+ text="ALERT: TRANSFORM HOST CREDENTIALS FAILED!",
71
+ msg="-> Analyse the error message and identify the root cause",
72
+ )
73
+
74
+ # Verify that default login credentials are set as environment variables. Raise a TypeError when is None
75
+ try:
76
+ nr.inventory.defaults.username = os.environ[nr.inventory.defaults.username]
77
+ nr.inventory.defaults.password = os.environ[nr.inventory.defaults.password]
78
+
79
+ except KeyError as error:
80
+ print(task_error(text=task_text, changed=False))
81
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: False>")
82
+ print(f"-> Environment variable {error} not found\n")
83
+
84
+ # Exit the script with a proper message
85
+ exit_error(
86
+ task_text=task_text,
87
+ text="ALERT: TRANSFORM DEFAULT CREDENTIALS FAILED!",
88
+ msg="-> Analyse the error message and identify the root cause",
89
+ )
90
+ except TypeError:
91
+ print(task_error(text=task_text, changed=False))
92
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: False>")
93
+ print("-> Nornir default inventory key username and/or password not found\n")
94
+
95
+ # Exit the script with a proper message
96
+ exit_error(
97
+ task_text=task_text,
98
+ text="ALERT: TRANSFORM DEFAULT CREDENTIALS FAILED!",
99
+ msg="-> Analyse the error message and identify the root cause",
100
+ )
101
+
102
+ print(task_info(text=task_text, changed=False))
103
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: True>")
104
+ print(f"-> Transformed {len(nr.inventory.hosts)} host credentials env variables")
105
+
106
+
107
+ def _tuple_with_env_list(iterable: dict) -> tuple:
108
+ """
109
+ TBD
110
+ """
111
+ env_keys = []
112
+ envs = []
113
+
114
+ # Iterate over all nested dict or list elements and return a generator object
115
+ for env in iterate_all(iterable=iterable, returned="key-value"):
116
+ if str(env[0]).startswith("env_"):
117
+ env_keys.append(str(env[0]))
118
+ envs.append(str(env[1]))
119
+
120
+ return env_keys, envs
121
+
122
+
123
+ def nr_transform_inv_from_env(
124
+ inv_type: Literal["hosts", "groups", "defaults"],
125
+ iterable: dict,
126
+ env_mandatory: dict = False,
127
+ verbose: bool = False,
128
+ ) -> None:
129
+ """
130
+ This function transforms all environment variables in the iterable. It loops over a nested dictionary and
131
+ if the key startswith "env_", it loads the environment variable specified by the value and replace the
132
+ value with the environment variable. Optional a mandatory argument which have to be a dictionary can be
133
+ specified. This dictionary must be part of the iterable and follows the same procedure to transform the
134
+ environment variables. The optional argument verbose prints extensive results. The function returns None.
135
+ """
136
+ #### Prepare mandatory and non-mandatory env variables ##################################################
137
+
138
+ mandatory_transform_env_keys, mandatory_transform_envs = _tuple_with_env_list(iterable=env_mandatory)
139
+ transform_env_keys, transform_envs = _tuple_with_env_list(iterable=iterable)
140
+
141
+ # Subtract the non-mandatory env_keys and envs from the mandatory env_keys and envs list
142
+ transform_env_keys = [item for item in transform_env_keys if item not in mandatory_transform_env_keys]
143
+ transform_envs = [item for item in transform_envs if item not in mandatory_transform_envs]
144
+
145
+ # Flatten the transform_envs if it contains lists of lists
146
+ transform_envs = list_flatten(transform_envs)
147
+
148
+ #### Transform all mandatory env variables ##############################################################
149
+
150
+ # If the mandatory_transform_env_keys list it not empty -> Transform all env variables in the list
151
+ if mandatory_transform_env_keys:
152
+ task_text = f"NORNIR transform mandatory {inv_type} env variable"
153
+ print(task_name(text=task_text))
154
+
155
+ # Verify that all mandatory env key-value pairs exists in the Nornir inventory
156
+ for key, value in env_mandatory.items():
157
+ if key not in list(iterate_all(iterable=iterable, returned="key")):
158
+ print(task_error(text=task_text, changed=False))
159
+ print(f"'{task_text} {value}' -> OS.EnvironResponse <Success: False>")
160
+ print(f"-> Nornir inventory key-value pair '{key}':'{value}' not found\n")
161
+ sys.exit(1)
162
+
163
+ try:
164
+ # Loop over the generator object items and add the matching elemens based on the key to a list
165
+ for env_key in mandatory_transform_env_keys:
166
+ # If the environ load fails a KeyError would be raised and catched by the exception
167
+ transform_env(iterable=iterable, startswith=env_key)
168
+
169
+ print(task_info(text=task_text, changed=False))
170
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: True>")
171
+ print(f"-> Transformed {len(mandatory_transform_envs)} mandatory env variables")
172
+ if verbose:
173
+ for env in mandatory_transform_envs:
174
+ print(f" - {env}")
175
+
176
+ except KeyError as error:
177
+ print(task_error(text=task_text, changed=False))
178
+ print(f"'{task_text} {error}' -> OS.EnvironResponse <Success: False>")
179
+ print(f"-> Environment variable {error} not found\n")
180
+ sys.exit(1)
181
+
182
+ #### Transform all other envs if transform_envs is not empty ############################################
183
+
184
+ # If the transform_env_keys list it not empty -> Transform all env variables in the list
185
+ if transform_envs:
186
+ task_text = f"NORNIR transform non-mandatory {inv_type} env variable"
187
+ print(task_name(text=task_text))
188
+
189
+ try:
190
+ # Loop over the generator object items and add the matching elemens based on the key to a list
191
+ for env_key in transform_env_keys:
192
+ # If the environ load fails a KeyError would be raised and catched by the exception
193
+ transform_env(iterable=iterable, startswith=env_key)
194
+
195
+ print(task_info(text=task_text, changed=False))
196
+ print(f"'{task_text}' -> OS.EnvironResponse <Success: True>")
197
+ print(f"-> Transformed {len(transform_envs)} non-mandatory env variables")
198
+ if verbose:
199
+ for env in transform_envs:
200
+ print(f" - {env}")
201
+
202
+ except KeyError as error:
203
+ print(task_error(text=task_text, changed=False))
204
+ print(f"'{task_text} {error}' -> OS.EnvironResponse <Success: False>")
205
+ print(f"-> Environment variable {error} not found\n")
206
+ sys.exit(1)
207
+
208
+
209
+ #### Nornir Init Functions ##################################################################################
210
+
211
+
212
+ def init_nornir(
213
+ config_file: str,
214
+ env_mandatory: dict = False,
215
+ args: argparse.Namespace = False,
216
+ add_netbox_data: dict[str:bool] = None,
217
+ ) -> Nornir:
218
+ """
219
+ The Nornir inventory will be initialized, the host username and password will be transformed and loaded
220
+ from environment variables. The same transformation will load all the environment variables from all
221
+ inventory data keys which start with env_ and ensures that the mandatory specified environment variables
222
+ are defined in the inventory. The function returns a filtered Nornir object or quits with an error message
223
+ in case of issues during the function.
224
+ """
225
+ # pylint: disable=invalid-name
226
+
227
+ print_task_title("Initialize Nornir Inventory")
228
+
229
+ # Set a timer to track how long the init_nornir functions takes to finish
230
+ timer_start = time.time()
231
+
232
+ # Register the DSCNetBoxInventory Plugin
233
+ InventoryPluginRegister.register("DSCNetBoxInventory", DSCNetBoxInventory)
234
+
235
+ # Take environment variables from .env and override existing host environment variables. If .env or the
236
+ # environment variable don't exist like in production environment, then directly the host environment
237
+ # variable takes presence. This approach fullfil the 12-factor application rules.
238
+ load_dotenv(".env", override=True)
239
+
240
+ # Initialize Nornir Object with a config file
241
+ nr = InitNornir(config_file=config_file)
242
+
243
+ # Load additional data from NetBox into the Nornir inventory
244
+ if add_netbox_data:
245
+ load_additional_netbox_data(nr=nr, add_netbox_data=add_netbox_data)
246
+
247
+ # Transform the host username and password from environment variables
248
+ nr_transform_host_creds_from_env(nr=nr)
249
+
250
+ # Transform the inventory and load all env variables staring with "env_" in the defaults yaml files
251
+ nr_transform_inv_from_env(
252
+ inv_type="defaults",
253
+ iterable=nr.inventory.defaults.data,
254
+ verbose=args.verbose if args else False,
255
+ env_mandatory=env_mandatory,
256
+ )
257
+
258
+ # Transform the inventory and load all env variables staring with "env_" in the groups yaml files
259
+ for group in nr.inventory.groups:
260
+ nr_transform_inv_from_env(
261
+ inv_type="groups",
262
+ iterable=nr.inventory.groups[group].data,
263
+ verbose=args.verbose if args else False,
264
+ )
265
+
266
+ # Filter the Nornir inventory based on the provided arguments from argparse
267
+ if args:
268
+ nr = nr_filter_args(nr=nr, args=args)
269
+
270
+ # Print the timer result
271
+ task_text = "NORNIR initialize timer"
272
+ print(f"{task_name(text=task_text)}")
273
+ print(task_info(text=task_text, changed=False))
274
+ print(f"'{task_text}' -> NornirResponse <Success: True>")
275
+ print(f"-> Initialize Nornir finished in: {round(time.time()-timer_start, 1)}s")
276
+
277
+ return nr
File without changes
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ This module contains general functions and tasks related to Pure Storage.
5
+
6
+ The functions are ordered as followed:
7
+ - Helper Functions
8
+ - Task Helper Functions
9
+ - Single Nornir tasks
10
+ - Nornir Tasks in regular Function
11
+ """
12
+
13
+
14
+ from typing import Any, Union
15
+ import pypureclient
16
+ from nornir.core.task import Task, Result
17
+
18
+
19
+ #### Helper Functions #######################################################################################
20
+
21
+
22
+ #### Task Helper Functions ##################################################################################
23
+
24
+
25
+ def _purestorage_get_connection(task: Task) -> Union[Any, Result]:
26
+ """
27
+ Manually create the Fortinet connection inside a Nornir task.
28
+ Args:
29
+ task (Task): The Nornir task object.
30
+ Returns:
31
+ Result: The connection object if successful, otherwise a failed Nornir result.
32
+ """
33
+ try:
34
+ # Connect to the Pure Storage FlashArray
35
+ api_token = task.host["pure_storage_api_token"][f"env_token_{task.host.name}"]
36
+ conn = pypureclient.flasharray.Client(task.host.hostname, api_token=api_token, verify_ssl=False)
37
+ # Return the connection object
38
+ return conn
39
+
40
+ except Exception as error: # pylint: disable=broad-except # nosec
41
+ # Return the Nornir result as failed
42
+ result = (
43
+ f"'{task.name}' -> APIResponse <Success: False>\n"
44
+ f"-> Failed to connect to '{task.host.name}'\n\n"
45
+ f"Error:\n{error}"
46
+ )
47
+ return Result(host=task.host, result=result, failed=True)
48
+
49
+
50
+ #### Nornir Tasks ###########################################################################################
51
+
52
+
53
+ #### Nornir Tasks in regular Function #######################################################################