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,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 #######################################################################
|