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
File without changes
|
File without changes
|
@@ -0,0 +1,358 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains general functions and tasks related to Batfish.
|
4
|
+
|
5
|
+
The functions are ordered as followed:
|
6
|
+
- Batfish Helper Functions
|
7
|
+
- Nornir Batfish Tasks
|
8
|
+
- Nornir Batfish Tasks in regular Function
|
9
|
+
"""
|
10
|
+
|
11
|
+
from pybatfish.client.session import Session
|
12
|
+
from nornir.core import Nornir
|
13
|
+
from nornir.core.task import Task, Result
|
14
|
+
from nornir_collection.batfish.utils import (
|
15
|
+
batfish_question_failed,
|
16
|
+
batfish_assert_type_failed,
|
17
|
+
batfish_exit_error,
|
18
|
+
)
|
19
|
+
from nornir_collection.utils import print_result, task_result, list_flatten, get_dict_value_by_path
|
20
|
+
|
21
|
+
|
22
|
+
#### Batfish Helper Functions ################################################################################
|
23
|
+
|
24
|
+
|
25
|
+
def batfish_assert_cfg_prop(task: Task, data: tuple, name_add: str = False) -> list:
|
26
|
+
"""
|
27
|
+
Batfish assert helper task to assert the ref_prop against the cfg_prop based on the ref_prop data type.
|
28
|
+
The col_name specifies the configuration property which should be asked to Batfish.
|
29
|
+
"""
|
30
|
+
# Extract the variables from the data tuple
|
31
|
+
col_name, ref_prop, cfg_prop = data
|
32
|
+
# Set the name_add variable if exists, else col_name
|
33
|
+
name_add = name_add if name_add else col_name
|
34
|
+
|
35
|
+
if isinstance(ref_prop, str):
|
36
|
+
# Define the property in the snapshot
|
37
|
+
snap_prop = cfg_prop[col_name].to_string(index=False)
|
38
|
+
snap_prop = snap_prop.rstrip()
|
39
|
+
snap_prop = snap_prop.lstrip()
|
40
|
+
# Set the Nornir task failed variable
|
41
|
+
failed = bool(ref_prop != snap_prop)
|
42
|
+
cfg_result = f"-> Expected: '{ref_prop}'\n" f"-> Configured: '{snap_prop}'"
|
43
|
+
|
44
|
+
elif isinstance(ref_prop, list):
|
45
|
+
ref_prop = list_flatten(ref_prop)
|
46
|
+
# Define the property in the snapshot
|
47
|
+
snap_prop = list_flatten(cfg_prop[col_name].tolist())
|
48
|
+
snap_prop = [str(x) for x in snap_prop]
|
49
|
+
# Find extra and missing referency properties
|
50
|
+
ref_prop_extra = [x for x in snap_prop if x not in ref_prop]
|
51
|
+
ref_prop_missing = [x for x in ref_prop if x not in snap_prop]
|
52
|
+
# Set the Nornir task failed variable
|
53
|
+
failed = bool(ref_prop_extra + ref_prop_missing)
|
54
|
+
cfg_result = (
|
55
|
+
f"-> Expected: {ref_prop}\n"
|
56
|
+
f"-> Missing (to add): {ref_prop_missing if ref_prop_missing else '[]'}\n"
|
57
|
+
f"-> Extra (to remove): {ref_prop_extra if ref_prop_extra else '[]'}"
|
58
|
+
)
|
59
|
+
|
60
|
+
else:
|
61
|
+
# Set the Nornir task failed variable
|
62
|
+
failed = True
|
63
|
+
cfg_result = (
|
64
|
+
f"->'ref_prop' argument type {type(ref_prop)} is not implemented to assert\n"
|
65
|
+
f"-> Update the function _batfish_assert_cfg_prop() to support this 'ref_prop' data type"
|
66
|
+
)
|
67
|
+
|
68
|
+
# Set the Nornir result to return
|
69
|
+
level_name = "ERROR" if failed else "INFO"
|
70
|
+
result = (
|
71
|
+
f"{task_result(text=f'{task.name} {name_add}', changed=False, level_name=level_name)}\n"
|
72
|
+
f"'{task.name} {col_name}' -> BatfishResponse <Success: {not failed}>\n"
|
73
|
+
f"{cfg_result}"
|
74
|
+
)
|
75
|
+
|
76
|
+
return result, failed
|
77
|
+
|
78
|
+
|
79
|
+
def batfish_get_ref_props_from_nr_inv(task: Task, inv_key: str) -> dict:
|
80
|
+
"""
|
81
|
+
Loop over all hosts keys to find keys which starts with the inv_startswith string and add the values
|
82
|
+
to a dict. Then return this dict which should contain all ref_props data.
|
83
|
+
"""
|
84
|
+
ref_props = {}
|
85
|
+
# Loop over all hosts keys to find keys which starts with the inv_startswith string
|
86
|
+
for key, value in task.host.items():
|
87
|
+
if key.startswith(inv_key):
|
88
|
+
# Update the ref_props dict with the value
|
89
|
+
ref_props.update(value)
|
90
|
+
|
91
|
+
return ref_props
|
92
|
+
|
93
|
+
|
94
|
+
#### Nornir Batfish Tasks ####################################################################################
|
95
|
+
|
96
|
+
|
97
|
+
def batfish_assert_cfg_node_property_task(task: Task, nr_inv: bool, data: tuple[Session, dict]) -> Result:
|
98
|
+
"""
|
99
|
+
Nornir task to assert Batfish node properties agains values from the Nornir inventory or direct specified
|
100
|
+
values in the ref_props dict. The ref_props argument in the data tuple specifies the Batfish node property
|
101
|
+
question to ask as the key The value can be the Nornir inventory which have to be added as a map list
|
102
|
+
of the Nornir inventory dict keys if nr_inv is True or the correct value have to be specified if nr_inv
|
103
|
+
is False.
|
104
|
+
|
105
|
+
ref_props = {
|
106
|
+
"Domain_Name": ["cfg_domain_name"],
|
107
|
+
"NTP_Servers": ["cfg_ntp", "server"],
|
108
|
+
"NTP_Source_Interface": ["cfg_ntp", "source"],
|
109
|
+
}
|
110
|
+
"""
|
111
|
+
# pylint: disable=invalid-name
|
112
|
+
|
113
|
+
# Extract the variables from the data tuple
|
114
|
+
bf, ref_props = data
|
115
|
+
|
116
|
+
# If the ref_props is a string to specify the inventory startswith key, then create a dict with all
|
117
|
+
# inventory keys which start with this string (nested dict not supported)
|
118
|
+
if isinstance(ref_props, str):
|
119
|
+
ref_props = batfish_get_ref_props_from_nr_inv(task=task, inv_key=ref_props)
|
120
|
+
|
121
|
+
# Set the initial Nornir Result object variables
|
122
|
+
result = []
|
123
|
+
failed = False
|
124
|
+
|
125
|
+
# Extract the node properties for the host
|
126
|
+
cfg_prop = bf.q.nodeProperties(nodes=str(task.host)).answer().frame()
|
127
|
+
|
128
|
+
# If the cfg_prop dataframe is empty
|
129
|
+
if cfg_prop.empty:
|
130
|
+
subresult, subresult_failed = batfish_question_failed(task=task, df=cfg_prop)
|
131
|
+
# Add the subresult to the Nornir result list
|
132
|
+
result.append(subresult)
|
133
|
+
# Return the Nornir result
|
134
|
+
return Result(host=task.host, result=result, failed=True)
|
135
|
+
|
136
|
+
# Loop over each column name, reference property key-value pair in the dict
|
137
|
+
for col_name, ref_prop in ref_props.items():
|
138
|
+
if nr_inv:
|
139
|
+
# Get the value from the Nornir inventory with the ref_prop map list
|
140
|
+
ref_prop = get_dict_value_by_path(data_dict=task.host, map_list=ref_prop)
|
141
|
+
|
142
|
+
# Create a tuple with all needed data to assert the Batfish question
|
143
|
+
data = (col_name, ref_prop, cfg_prop)
|
144
|
+
|
145
|
+
# Assert the correct ref_prop type
|
146
|
+
if isinstance(ref_prop, (str, list)):
|
147
|
+
subresult, subresult_failed = batfish_assert_cfg_prop(task=task, data=data)
|
148
|
+
else:
|
149
|
+
data = (col_name, ref_prop, "'str' or 'list'")
|
150
|
+
subresult, subresult_failed = batfish_assert_type_failed(task=task, data=data)
|
151
|
+
|
152
|
+
# Add the subresult to the Nornir result list
|
153
|
+
result.append(subresult)
|
154
|
+
if subresult_failed:
|
155
|
+
failed = True
|
156
|
+
|
157
|
+
# Return the Nornir result
|
158
|
+
return Result(host=task.host, result=result, failed=failed)
|
159
|
+
|
160
|
+
|
161
|
+
def batfish_assert_cfg_interface_property_task(
|
162
|
+
task: Task, nr_inv: bool, data: tuple[Session, dict]
|
163
|
+
) -> Result:
|
164
|
+
"""
|
165
|
+
Nornir task to assert Batfish interface properties agains values from the Nornir inventory or direct
|
166
|
+
specified values in the ref_props dict. The ref_props argument in the data tuple specifies a dict for each
|
167
|
+
interface with the Batfish interface property question to ask as the key. The value can be the Nornir
|
168
|
+
inventory which have to be added as a map list of the Nornir inventory dict keys if nr_inv is True or
|
169
|
+
the correct value have to be specified if nr_inv is False.
|
170
|
+
|
171
|
+
ref_props = {
|
172
|
+
"FortyGigabitEthernet1/1/1" : {
|
173
|
+
"Switchport_Trunk_Encapsulation" : "DOT1Q",
|
174
|
+
"Admin_Up" : "True",
|
175
|
+
"Active" : "True",
|
176
|
+
}
|
177
|
+
"FortyGigabitEthernet1/1/2" : {
|
178
|
+
"Switchport_Trunk_Encapsulation" : "DOT1Q",
|
179
|
+
"Admin_Up" : "True",
|
180
|
+
"Active" : "True",
|
181
|
+
}
|
182
|
+
}
|
183
|
+
"""
|
184
|
+
# pylint: disable=invalid-name
|
185
|
+
|
186
|
+
# Extract the variables from the data tuple
|
187
|
+
bf, ref_props = data
|
188
|
+
|
189
|
+
# If the Nornir inventory should be used and ref_props is a string to specify the inventory startswith
|
190
|
+
# key, then create a dict with all inventory keys which start with this string (nested dict not supported)
|
191
|
+
if nr_inv and isinstance(ref_props, str):
|
192
|
+
ref_props = batfish_get_ref_props_from_nr_inv(task=task, inv_key=ref_props)
|
193
|
+
|
194
|
+
# Set the initial Nornir Result object variables
|
195
|
+
result = []
|
196
|
+
failed = False
|
197
|
+
|
198
|
+
# Loop over each interface
|
199
|
+
for interface in ref_props.keys():
|
200
|
+
# Extract the interface properties for the host
|
201
|
+
cfg_prop = bf.q.interfaceProperties(nodes=str(task.host), interfaces=interface).answer().frame()
|
202
|
+
|
203
|
+
# If the cfg_prop dataframe is empty
|
204
|
+
if cfg_prop.empty:
|
205
|
+
subresult, subresult_failed = batfish_question_failed(task=task, df=cfg_prop, name_add=interface)
|
206
|
+
# Add the subresult to the Nornir result list
|
207
|
+
result.append(subresult)
|
208
|
+
if subresult_failed:
|
209
|
+
failed = True
|
210
|
+
else:
|
211
|
+
# Loop over each column name, reference property key-value pair in the dict
|
212
|
+
for col_name, ref_prop in ref_props[interface].items():
|
213
|
+
if nr_inv:
|
214
|
+
# Get the value from the Nornir inventory with the ref_prop map list
|
215
|
+
ref_prop = get_dict_value_by_path(data_dict=task.host, map_list=ref_prop)
|
216
|
+
|
217
|
+
# Create a tuple with all needed data to assert the Batfish question
|
218
|
+
data = (col_name, ref_prop, cfg_prop)
|
219
|
+
|
220
|
+
# Assert the correct ref_prop type
|
221
|
+
if isinstance(ref_prop, (str, list)):
|
222
|
+
subresult, subresult_failed = batfish_assert_cfg_prop(
|
223
|
+
task=task, data=data, name_add=interface
|
224
|
+
)
|
225
|
+
else:
|
226
|
+
data = (col_name, ref_prop, "'str' or 'list'")
|
227
|
+
subresult, subresult_failed = batfish_assert_type_failed(
|
228
|
+
task=task, data=data, name_add=interface
|
229
|
+
)
|
230
|
+
|
231
|
+
# Add the subresult to the Nornir result list
|
232
|
+
result.append(subresult)
|
233
|
+
if subresult_failed:
|
234
|
+
failed = True
|
235
|
+
|
236
|
+
# Return the Nornir result
|
237
|
+
return Result(host=task.host, result=result, failed=failed)
|
238
|
+
|
239
|
+
|
240
|
+
def batfish_assert_cfg_switched_vlan_property_task(
|
241
|
+
task: Task, nr_inv: bool, data: tuple[Session, dict]
|
242
|
+
) -> Result:
|
243
|
+
"""
|
244
|
+
Nornir task to assert Batfish switched vlan properties agains values from the Nornir inventory or direct
|
245
|
+
specified values in the ref_props dict. The ref_props argument in the data tuple specifies the Batfish
|
246
|
+
node property question to ask as the key The value can be the Nornir inventory which have to be added
|
247
|
+
as a map list of the Nornir inventory dict keys if nr_inv is True or the correct value have to be
|
248
|
+
specified if nr_inv is False.
|
249
|
+
|
250
|
+
ref_props = {
|
251
|
+
"VLAN_ID": ["cfg_vlans"],
|
252
|
+
"VLAN_ID_ADD": ["cfg_vlans_add"],
|
253
|
+
}
|
254
|
+
"""
|
255
|
+
# pylint: disable=invalid-name
|
256
|
+
|
257
|
+
# Extract the variables from the data tuple
|
258
|
+
bf, ref_props = data
|
259
|
+
|
260
|
+
# If the Nornir inventory should be used and ref_props is a string to specify the inventory startswith
|
261
|
+
# key, then create a dict with all inventory keys which start with this string (nested dict not supported)
|
262
|
+
if nr_inv and isinstance(ref_props, str):
|
263
|
+
ref_props = batfish_get_ref_props_from_nr_inv(task=task, inv_key=ref_props)
|
264
|
+
|
265
|
+
# Set the initial Nornir Result object variables
|
266
|
+
result = []
|
267
|
+
failed = False
|
268
|
+
|
269
|
+
# Extract the node properties for the host
|
270
|
+
cfg_prop = bf.q.switchedVlanProperties(nodes=str(task.host)).answer().frame()
|
271
|
+
|
272
|
+
# If the cfg_prop dataframe is empty
|
273
|
+
if cfg_prop.empty:
|
274
|
+
subresult, subresult_failed = batfish_question_failed(task=task, df=cfg_prop)
|
275
|
+
# Add the subresult to the Nornir result list
|
276
|
+
result.append(subresult)
|
277
|
+
# Return the Nornir result
|
278
|
+
return Result(host=task.host, result=result, failed=True)
|
279
|
+
|
280
|
+
# Loop over each column name, reference property key-value pair in the dict
|
281
|
+
for col_name, ref_prop in ref_props.items():
|
282
|
+
if nr_inv:
|
283
|
+
# Get the value from the Nornir inventory with the ref_prop map list
|
284
|
+
ref_prop = get_dict_value_by_path(data_dict=task.host, map_list=ref_prop)
|
285
|
+
# If the Nornir inventory is a dict, assume that the key is the vlan number and create a list
|
286
|
+
if isinstance(ref_prop, dict):
|
287
|
+
ref_prop = [str(vlan) for vlan in ref_prop.keys()]
|
288
|
+
# If the custom column name VLAN_ID_ADD exists in the ref_prop, then add these vlans to the list
|
289
|
+
if "VLAN_ID_ADD" in ref_prop:
|
290
|
+
# Get the value from the Nornir inventory with the ref_prop map list
|
291
|
+
ref_prop_add = get_dict_value_by_path(data_dict=task.host, map_list=ref_prop["VLAN_ID_ADD"])
|
292
|
+
# If the Nornir inventory is a dict, assume that the key is the vlan number and create a list
|
293
|
+
if isinstance(ref_prop_add, dict):
|
294
|
+
ref_prop_add = [str(vlan) for vlan in ref_prop_add.keys()]
|
295
|
+
# Add both vlan lists together
|
296
|
+
ref_prop = ref_prop + ref_prop_add
|
297
|
+
|
298
|
+
# Add vlan 1 as it's the default and can't be deleted
|
299
|
+
ref_prop = ["1"] + ref_prop
|
300
|
+
|
301
|
+
# Create a tuple with all needed data to assert the Batfish question
|
302
|
+
data = (col_name, ref_prop, cfg_prop)
|
303
|
+
|
304
|
+
# Assert the correct ref_prop type
|
305
|
+
if isinstance(ref_prop, list):
|
306
|
+
subresult, subresult_failed = batfish_assert_cfg_prop(task=task, data=data)
|
307
|
+
else:
|
308
|
+
data = (col_name, ref_prop, "'list'")
|
309
|
+
subresult, subresult_failed = batfish_assert_type_failed(task=task, data=data)
|
310
|
+
|
311
|
+
# Add the subresult to the Nornir result list
|
312
|
+
result.append(subresult)
|
313
|
+
if subresult_failed:
|
314
|
+
failed = True
|
315
|
+
|
316
|
+
# Return the Nornir result
|
317
|
+
return Result(host=task.host, result=result, failed=failed)
|
318
|
+
|
319
|
+
|
320
|
+
#### Nornir Batfish Tasks in regular Function ################################################################
|
321
|
+
|
322
|
+
|
323
|
+
def batfish_assert_config_property(nr: Nornir, nr_inv: bool, data: tuple, soft: bool = False) -> Result:
|
324
|
+
"""
|
325
|
+
This function runs a Nornir task to ask Batfish configuration properties. The batfish session, the batfish
|
326
|
+
question and the ref_props dict according to the specified batfish question have to be specified as the
|
327
|
+
data tuple. The nr_inv argument specified is the values to assert should be loaded from the Nornir
|
328
|
+
inventory by a dict key map list or are specified already directly correct.
|
329
|
+
The result is printed in Nornir style and in case of an assert error the script exits with error code 1.
|
330
|
+
"""
|
331
|
+
# pylint: disable=invalid-name
|
332
|
+
|
333
|
+
# Extract the variables from the data tuple
|
334
|
+
bf, bf_question, ref_props = data
|
335
|
+
|
336
|
+
# Map list to identify which Nornir task to execute for the correct Batfish question
|
337
|
+
batfish_config_property_task = {
|
338
|
+
"node": batfish_assert_cfg_node_property_task,
|
339
|
+
"interface": batfish_assert_cfg_interface_property_task,
|
340
|
+
"vlan": batfish_assert_cfg_switched_vlan_property_task,
|
341
|
+
}
|
342
|
+
|
343
|
+
# Run the Nornir task to assert Batfish configuration properties
|
344
|
+
result = nr.run(
|
345
|
+
task=batfish_config_property_task[bf_question],
|
346
|
+
name=f"BATFISH query {bf_question}",
|
347
|
+
nr_inv=nr_inv,
|
348
|
+
data=(bf, ref_props),
|
349
|
+
on_failed=True,
|
350
|
+
)
|
351
|
+
|
352
|
+
# Print the Nornir task result
|
353
|
+
print_result(result=result, result_sub_list=True)
|
354
|
+
|
355
|
+
if not soft:
|
356
|
+
# If the task failed exit with error code 1
|
357
|
+
if result.failed:
|
358
|
+
batfish_exit_error(result=result, bf_question="config")
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
This module contains general functions and tasks related to Batfish.
|
4
|
+
|
5
|
+
The functions are ordered as followed:
|
6
|
+
- Batfish Initialize Function
|
7
|
+
- Batfish Helper Functions
|
8
|
+
"""
|
9
|
+
|
10
|
+
import logging
|
11
|
+
from io import StringIO
|
12
|
+
from typing import Literal
|
13
|
+
import requests
|
14
|
+
from pybatfish.client.session import Session
|
15
|
+
from pandas import DataFrame
|
16
|
+
from nornir.core.task import Task, AggregatedResult
|
17
|
+
from nornir_collection.utils import print_task_name, task_result, task_error, task_info, exit_error
|
18
|
+
|
19
|
+
|
20
|
+
#### Batfish Initialize Function #############################################################################
|
21
|
+
|
22
|
+
|
23
|
+
def init_batfish(bf_data: tuple, bf_log_lvl: Literal["WARN", "ERROR"] = "ERROR") -> Session:
|
24
|
+
"""
|
25
|
+
TBD
|
26
|
+
"""
|
27
|
+
# pylint: disable=invalid-name
|
28
|
+
|
29
|
+
# Extract the variables from the bf_data tuple
|
30
|
+
bf_host, bf_network, SNAPSHOT_DIR, SNAPSHOT_NAME = bf_data
|
31
|
+
|
32
|
+
task_text = "BATFISH initialize snapshot"
|
33
|
+
print_task_name(task_text)
|
34
|
+
|
35
|
+
# Replace logging.WARN with the preferred logging level
|
36
|
+
logging_lvl = logging.WARN if bf_log_lvl == "WARN" else logging.ERROR
|
37
|
+
logging.getLogger("pybatfish").setLevel(logging_lvl) # Or WARN for more details
|
38
|
+
|
39
|
+
try:
|
40
|
+
# Connect to the batfish service docker container
|
41
|
+
bf = Session(host=bf_host)
|
42
|
+
# Set batfish network logical name
|
43
|
+
bf.set_network(bf_network)
|
44
|
+
# Initialize a snapshot
|
45
|
+
bf.init_snapshot(SNAPSHOT_DIR, name=SNAPSHOT_NAME, overwrite=True)
|
46
|
+
|
47
|
+
except requests.exceptions.ConnectionError as error:
|
48
|
+
print(task_error(text=task_text, changed=False))
|
49
|
+
print(f"'{task_text}' -> BatfishResponse <Success: False>")
|
50
|
+
print(f"-> {error}")
|
51
|
+
# Exit the script with a proper message
|
52
|
+
exit_error(
|
53
|
+
task_text=task_text,
|
54
|
+
text=f"ALERT: {task_text.upper()} FAILED!",
|
55
|
+
msg="-> Analyse the Batfish docker container and snapshot settings to identify the root cause",
|
56
|
+
)
|
57
|
+
|
58
|
+
# Print the result as successful
|
59
|
+
print(task_info(text=task_text, changed=False))
|
60
|
+
print(f"'{task_text}' -> BatfishResponse <Success: True>")
|
61
|
+
print(f"-> Snapshot dir: {SNAPSHOT_DIR} / Snapshot name: {SNAPSHOT_NAME}")
|
62
|
+
|
63
|
+
return bf
|
64
|
+
|
65
|
+
|
66
|
+
#### Batfish Helper Functions ################################################################################
|
67
|
+
|
68
|
+
|
69
|
+
def batfish_question_failed(task: Task, df: DataFrame, name_add: str = False) -> None:
|
70
|
+
"""
|
71
|
+
TBD
|
72
|
+
"""
|
73
|
+
# pylint: disable=invalid-name
|
74
|
+
|
75
|
+
# Set the name_add variable if exists, else an empty string
|
76
|
+
name_add = name_add if name_add else ""
|
77
|
+
|
78
|
+
# df.info() prints by default directly to sys.stdout and by using the writable buffer a StringIO
|
79
|
+
# instance can be used to get the string and write it into a normal variable
|
80
|
+
buf = StringIO()
|
81
|
+
df.info(buf=buf)
|
82
|
+
df_info = buf.getvalue()
|
83
|
+
# Set the Nornir task failed and result variable
|
84
|
+
failed = True
|
85
|
+
result = (
|
86
|
+
f"{task_result(text=f'{task.name} {name_add}', changed=False, level_name='ERROR')}\n"
|
87
|
+
f"'{task.name}' -> BatfishResponse <Success: False>\n"
|
88
|
+
f"-> Batfish question returned an empty pandas dataframe\n"
|
89
|
+
f"-> Verify Batfish snapshot config hostname and Nornir hostname\n\n"
|
90
|
+
f"{df_info}"
|
91
|
+
)
|
92
|
+
|
93
|
+
return result, failed
|
94
|
+
|
95
|
+
|
96
|
+
def batfish_assert_type_failed(task: Task, data: tuple, name_add: str = False) -> None:
|
97
|
+
"""
|
98
|
+
Helper function to get the error result string in case the Batfish ref_prop is an unsupported data type.
|
99
|
+
"""
|
100
|
+
# Extract the variables from the data tuple
|
101
|
+
col_name, ref_prop, sup_type = data
|
102
|
+
# Set the name_add variable if exists, else col_name
|
103
|
+
name_add = name_add if name_add else col_name
|
104
|
+
|
105
|
+
# Set the Nornir task failed and result variable
|
106
|
+
failed = True
|
107
|
+
result = (
|
108
|
+
f"{task_result(text=f'{task.name} {name_add}', changed=False, level_name='ERROR')}\n"
|
109
|
+
f"'{task.name} {col_name}' -> BatfishResponse <Success: False>\n"
|
110
|
+
f"-> Unsupported 'ref_prop' argument type {type(ref_prop)}\n"
|
111
|
+
f"-> Supported 'ref_prop' argument type is {sup_type}"
|
112
|
+
)
|
113
|
+
|
114
|
+
return result, failed
|
115
|
+
|
116
|
+
|
117
|
+
def batfish_exit_error(result: AggregatedResult, bf_question: str) -> None:
|
118
|
+
"""
|
119
|
+
Helper function to exit the script is case of a Batfish assert error.
|
120
|
+
"""
|
121
|
+
if "config" in bf_question:
|
122
|
+
text = "Inconsistent configuration properties exists!"
|
123
|
+
msg = "-> Verify the Nornir inventory for inconsistent configuration properties"
|
124
|
+
|
125
|
+
exit_error(
|
126
|
+
task_text=result.name,
|
127
|
+
text=text,
|
128
|
+
msg=msg,
|
129
|
+
)
|
File without changes
|
File without changes
|
File without changes
|