nornir-collection 0.0.1__tar.gz

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 (64) hide show
  1. nornir_collection-0.0.1/LICENSE +21 -0
  2. nornir_collection-0.0.1/PKG-INFO +136 -0
  3. nornir_collection-0.0.1/README.md +83 -0
  4. nornir_collection-0.0.1/nornir_collection/__init__.py +0 -0
  5. nornir_collection-0.0.1/nornir_collection/batfish/__init__.py +0 -0
  6. nornir_collection-0.0.1/nornir_collection/batfish/assert_config.py +358 -0
  7. nornir_collection-0.0.1/nornir_collection/batfish/utils.py +129 -0
  8. nornir_collection-0.0.1/nornir_collection/cisco/__init__.py +0 -0
  9. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/__init__.py +0 -0
  10. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/__init__.py +0 -0
  11. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/config_tasks.py +569 -0
  12. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/config_workflow.py +107 -0
  13. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/show_tasks.py +677 -0
  14. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/__init__.py +0 -0
  15. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/config_tasks.py +564 -0
  16. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/config_workflow.py +298 -0
  17. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/nr_cfg_iosxe_netconf.py +186 -0
  18. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/ops_tasks.py +307 -0
  19. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/processor.py +151 -0
  20. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/pyats.py +236 -0
  21. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/__init__.py +0 -0
  22. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/cisco_rpc.py +514 -0
  23. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/config_workflow.py +95 -0
  24. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/tasks.py +325 -0
  25. nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/utils.py +511 -0
  26. nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/__init__.py +0 -0
  27. nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/cisco_software_upgrade.py +283 -0
  28. nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/utils.py +794 -0
  29. nornir_collection-0.0.1/nornir_collection/cisco/support_api/__init__.py +0 -0
  30. nornir_collection-0.0.1/nornir_collection/cisco/support_api/api_calls.py +1173 -0
  31. nornir_collection-0.0.1/nornir_collection/cisco/support_api/cisco_maintenance_report.py +221 -0
  32. nornir_collection-0.0.1/nornir_collection/cisco/support_api/cisco_support.py +727 -0
  33. nornir_collection-0.0.1/nornir_collection/cisco/support_api/reports.py +747 -0
  34. nornir_collection-0.0.1/nornir_collection/cisco/support_api/utils.py +316 -0
  35. nornir_collection-0.0.1/nornir_collection/fortinet/__init__.py +0 -0
  36. nornir_collection-0.0.1/nornir_collection/fortinet/utils.py +36 -0
  37. nornir_collection-0.0.1/nornir_collection/git.py +224 -0
  38. nornir_collection-0.0.1/nornir_collection/netbox/__init__.py +0 -0
  39. nornir_collection-0.0.1/nornir_collection/netbox/custom_script.py +107 -0
  40. nornir_collection-0.0.1/nornir_collection/netbox/inventory.py +360 -0
  41. nornir_collection-0.0.1/nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py +989 -0
  42. nornir_collection-0.0.1/nornir_collection/netbox/set_device_status.py +67 -0
  43. nornir_collection-0.0.1/nornir_collection/netbox/sync_datasource.py +111 -0
  44. nornir_collection-0.0.1/nornir_collection/netbox/update_cisco_inventory_data.py +158 -0
  45. nornir_collection-0.0.1/nornir_collection/netbox/update_cisco_support_plugin_data.py +339 -0
  46. nornir_collection-0.0.1/nornir_collection/netbox/update_fortinet_inventory_data.py +161 -0
  47. nornir_collection-0.0.1/nornir_collection/netbox/update_purestorage_inventory_data.py +144 -0
  48. nornir_collection-0.0.1/nornir_collection/netbox/utils.py +261 -0
  49. nornir_collection-0.0.1/nornir_collection/netbox/verify_device_primary_ip.py +202 -0
  50. nornir_collection-0.0.1/nornir_collection/nornir_plugins/__init__.py +0 -0
  51. nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/__init__.py +0 -0
  52. nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/netbox.py +250 -0
  53. nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/staggered_yaml.py +143 -0
  54. nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/utils.py +277 -0
  55. nornir_collection-0.0.1/nornir_collection/purestorage/__init__.py +0 -0
  56. nornir_collection-0.0.1/nornir_collection/purestorage/utils.py +53 -0
  57. nornir_collection-0.0.1/nornir_collection/utils.py +741 -0
  58. nornir_collection-0.0.1/nornir_collection.egg-info/PKG-INFO +136 -0
  59. nornir_collection-0.0.1/nornir_collection.egg-info/SOURCES.txt +116 -0
  60. nornir_collection-0.0.1/nornir_collection.egg-info/dependency_links.txt +1 -0
  61. nornir_collection-0.0.1/nornir_collection.egg-info/requires.txt +32 -0
  62. nornir_collection-0.0.1/nornir_collection.egg-info/top_level.txt +1 -0
  63. nornir_collection-0.0.1/setup.cfg +4 -0
  64. nornir_collection-0.0.1/setup.py +30 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Willi Kubny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.2
2
+ Name: nornir-collection
3
+ Version: 0.0.1
4
+ Summary: Nornir-Collection contains network automation functions and complete IaC workflows with Nornir and other python libraries. It contains Nornir tasks and general functions in Nornir style.
5
+ Author: Willi Kubny
6
+ Author-email: willi.kubny@gmail.ch
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: nornir==3.3.0
14
+ Requires-Dist: nornir-jinja2==0.2.0
15
+ Requires-Dist: nornir-salt==0.20.3
16
+ Requires-Dist: nornir-netmiko==1.0.1
17
+ Requires-Dist: nornir-scrapli==2023.7.30
18
+ Requires-Dist: scrapli-netconf==2023.7.30
19
+ Requires-Dist: scrapli[ssh2]
20
+ Requires-Dist: scrapli[genie]
21
+ Requires-Dist: nornir-utils==0.2.0
22
+ Requires-Dist: pynetbox==7.4.0
23
+ Requires-Dist: py-pure-client==1.51.0
24
+ Requires-Dist: nornir-pyfgt==1.0.3
25
+ Requires-Dist: python-nmap==0.7.1
26
+ Requires-Dist: pybatfish==2023.12.16.1270
27
+ Requires-Dist: requests==2.31.0
28
+ Requires-Dist: pre-commit==3.6.0
29
+ Requires-Dist: pyyaml==6.0.1
30
+ Requires-Dist: pyfiglet==1.0.2
31
+ Requires-Dist: GitPython==3.1.40
32
+ Requires-Dist: python-dotenv==1.0.0
33
+ Requires-Dist: beautifultable==1.1.0
34
+ Requires-Dist: pandas==2.2.2
35
+ Requires-Dist: openpyxl==3.1.2
36
+ Requires-Dist: XlsxWriter==3.1.9
37
+ Requires-Dist: yaspin==3.0.1
38
+ Requires-Dist: rich==13.7.0
39
+ Requires-Dist: tabulate==0.9.0
40
+ Requires-Dist: black==23.12.0
41
+ Requires-Dist: yamllint==1.33.0
42
+ Requires-Dist: pylint==3.0.3
43
+ Requires-Dist: bandit==1.7.6
44
+ Requires-Dist: vulture==2.10
45
+ Dynamic: author
46
+ Dynamic: author-email
47
+ Dynamic: classifier
48
+ Dynamic: description
49
+ Dynamic: description-content-type
50
+ Dynamic: requires-dist
51
+ Dynamic: requires-python
52
+ Dynamic: summary
53
+
54
+ ![Header-Image](docs/header.png)
55
+
56
+ > Infrastructure as Code or IaC is the process of provisioning and managing infrastructure defined through code, instead of doing so with a manual process.
57
+ >
58
+ > Infrastructure as code brings a lot of advantages into the network operation:
59
+ > + Reducing shadow IT within organizations and allowing timely and efficient infrastructure changes
60
+ > + Integrating directly with CI/CD platforms
61
+ > + Enabling version-controlled infrastructure and configuration changes leading to trackable configurations
62
+ > + Standardizing infrastructure with reproducible configurations
63
+ > + Effectively managing configurating drift and keeping infrastructure and configurations in their desired state
64
+ > + Having the ability to easily scale up infrastructure as automation eliminates the need for manual configurations
65
+
66
+ # DSC-Nornir Python-Package
67
+ ----
68
+ </br>
69
+
70
+ This repository builds a python package and is a complete solution to manage the network configuration from code.
71
+ The DSC-Nornir package modules are a collection of network automation functions and complete workflows with Nornir
72
+ and other python tools. All scripts can be executed manually from a central server, workstation or integrated into
73
+ a CI/CD pipeline. The folder azure_pipelines provides bash functions to facilitate the writing of Azure DevOps
74
+ pipelines.
75
+
76
+ The complete documentation is available on MkDocs: [DSC MkDocs Network Documentation](http://192.168.1.10:9000/netdevops/architecture/dsc_nornir/)
77
+
78
+ # Python Development and Testing
79
+ ----
80
+ </br>
81
+
82
+ The Python scripts were developed with static code analysis, black auto-formatting and functional testing with Azure
83
+ DevOps pipelines executed by a pipeline agent installed within the Kyndryl labor network. The `Makefile` is used to
84
+ run the black auto-formatter, yamllint, pylint and prospector linting, bandit for security and vulnerability verification
85
+ as well as vulture for dead code verification. All pylint warnings that should be ignored are part of the script with a
86
+ pylint control message e.g `# pylint: disable=import-error`. Pylint and prospector uses a custom configuration profile.
87
+ The other tools use the default configuration.
88
+
89
+ The `Makefile` contains the tasks `all`, `fmt` and `lint`
90
+
91
+ To execute the `Makefile` with all tasks (black auto-formatting, yamllint, pylint, bandit and vulture):
92
+ ```bash
93
+ make
94
+ ```
95
+
96
+ To execute only the black auto-formatting:
97
+ ```bash
98
+ make fmt
99
+ ```
100
+
101
+ To execute only the linting with yamllint, pylint, bandit and vulture:
102
+ ```bash
103
+ make lint
104
+ ```
105
+
106
+ # Languages and Tools
107
+ ----
108
+ </br>
109
+
110
+ <p align="center">
111
+ <img width="7.8%" src="https://user-images.githubusercontent.com/70367776/177972422-da6933d5-310e-4cdb-9433-89f2dc6ebb7a.png" alt="Python" />
112
+ <img width="9.5%" src="https://user-images.githubusercontent.com/70367776/177969859-476bd542-2c0e-41a9-82f2-3e4919a61d4e.png" alt="YAML" />
113
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/177966199-e8476a0d-f972-4409-8b9a-d493d339bf38.png" alt="Nornir" />
114
+ <img width="8.9%" src="https://user-images.githubusercontent.com/70367776/177970540-b59a294f-82fd-4647-b8e2-9305c399c900.png" alt="Scrapli" />
115
+ <img width="19.5%" src="https://user-images.githubusercontent.com/70367776/229153411-6e5d10c3-634e-47b9-8920-0ed5e2311f9b.png" alt="Netmiko" />
116
+ <img width="10.5%" src="https://user-images.githubusercontent.com/70367776/183677690-fd956308-fda9-4583-863e-3a39d3ecc7f7.png" alt="NETCONF" />
117
+ <img width="9%" src="https://user-images.githubusercontent.com/70367776/183678136-4fbf50a8-adbb-468e-91b6-988d4c1ccd72.png" alt="RESTCONF" />
118
+ <img width="9.5%" src="https://user-images.githubusercontent.com/70367776/177966474-e66d8a59-e26e-4a3d-ac59-ded7cc005e4b.png" alt="Jinja2" />
119
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/177971288-e32552cd-1529-470a-8881-30c22d2df8ac.png" alt="pyATS" />
120
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/222764714-1ff86034-76f9-49d0-9e24-68798d779f56.png" alt="Batfish" />
121
+ <img width="10.2%" src="https://user-images.githubusercontent.com/70367776/183678800-403cf0ea-3c8a-47bb-b52a-2caa0cedc195.png" alt="Makefile" />
122
+ <img width="10.5%" src="https://user-images.githubusercontent.com/70367776/183679432-2b89f00c-f5b1-4d47-9c68-ad7fa332de01.png" alt="Prospector" />
123
+ <img width="9%" src="https://user-images.githubusercontent.com/70367776/183680151-62d5c625-0430-4c90-adfc-1ebb47fce4a9.png" alt="Bandit" />
124
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/177972703-3be3c4c3-aa9a-4468-97a6-e7760d536b89.png" alt="Git" />
125
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/177972925-1780012f-64a6-4b93-a215-fd47e3222b8f.png" alt="GitHub" />
126
+ <img width="11.5%" src="https://user-images.githubusercontent.com/70367776/232078456-8aee2fda-1289-4cd9-b7f3-9b34fcb4d7c7.png" alt="Docker" />
127
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/231501139-a449202e-6a81-4364-a4ea-1e42906e846e.png" alt="Azure Pipeline" />
128
+ <img width="33.2%" src="https://user-images.githubusercontent.com/70367776/231500048-77eeff9a-166b-4bd7-a0cc-3c5fc58be368.png" alt="NetBox" />
129
+ </p>
130
+
131
+ <br>
132
+
133
+ <h3 align="center">APIs can also be amusing to provide a programming joke ...</h3>
134
+ <p align="center"><img src="https://readme-jokes.vercel.app/api?hideBorder&theme=calm" /></p>
135
+ <h3 align="center">... or an interesting quote.</h3>
136
+ <p align="center"><img src="https://quotes-github-readme.vercel.app/api?type=horizontal&theme=dracula" /></p>
@@ -0,0 +1,83 @@
1
+ ![Header-Image](docs/header.png)
2
+
3
+ > Infrastructure as Code or IaC is the process of provisioning and managing infrastructure defined through code, instead of doing so with a manual process.
4
+ >
5
+ > Infrastructure as code brings a lot of advantages into the network operation:
6
+ > + Reducing shadow IT within organizations and allowing timely and efficient infrastructure changes
7
+ > + Integrating directly with CI/CD platforms
8
+ > + Enabling version-controlled infrastructure and configuration changes leading to trackable configurations
9
+ > + Standardizing infrastructure with reproducible configurations
10
+ > + Effectively managing configurating drift and keeping infrastructure and configurations in their desired state
11
+ > + Having the ability to easily scale up infrastructure as automation eliminates the need for manual configurations
12
+
13
+ # DSC-Nornir Python-Package
14
+ ----
15
+ </br>
16
+
17
+ This repository builds a python package and is a complete solution to manage the network configuration from code.
18
+ The DSC-Nornir package modules are a collection of network automation functions and complete workflows with Nornir
19
+ and other python tools. All scripts can be executed manually from a central server, workstation or integrated into
20
+ a CI/CD pipeline. The folder azure_pipelines provides bash functions to facilitate the writing of Azure DevOps
21
+ pipelines.
22
+
23
+ The complete documentation is available on MkDocs: [DSC MkDocs Network Documentation](http://192.168.1.10:9000/netdevops/architecture/dsc_nornir/)
24
+
25
+ # Python Development and Testing
26
+ ----
27
+ </br>
28
+
29
+ The Python scripts were developed with static code analysis, black auto-formatting and functional testing with Azure
30
+ DevOps pipelines executed by a pipeline agent installed within the Kyndryl labor network. The `Makefile` is used to
31
+ run the black auto-formatter, yamllint, pylint and prospector linting, bandit for security and vulnerability verification
32
+ as well as vulture for dead code verification. All pylint warnings that should be ignored are part of the script with a
33
+ pylint control message e.g `# pylint: disable=import-error`. Pylint and prospector uses a custom configuration profile.
34
+ The other tools use the default configuration.
35
+
36
+ The `Makefile` contains the tasks `all`, `fmt` and `lint`
37
+
38
+ To execute the `Makefile` with all tasks (black auto-formatting, yamllint, pylint, bandit and vulture):
39
+ ```bash
40
+ make
41
+ ```
42
+
43
+ To execute only the black auto-formatting:
44
+ ```bash
45
+ make fmt
46
+ ```
47
+
48
+ To execute only the linting with yamllint, pylint, bandit and vulture:
49
+ ```bash
50
+ make lint
51
+ ```
52
+
53
+ # Languages and Tools
54
+ ----
55
+ </br>
56
+
57
+ <p align="center">
58
+ <img width="7.8%" src="https://user-images.githubusercontent.com/70367776/177972422-da6933d5-310e-4cdb-9433-89f2dc6ebb7a.png" alt="Python" />
59
+ <img width="9.5%" src="https://user-images.githubusercontent.com/70367776/177969859-476bd542-2c0e-41a9-82f2-3e4919a61d4e.png" alt="YAML" />
60
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/177966199-e8476a0d-f972-4409-8b9a-d493d339bf38.png" alt="Nornir" />
61
+ <img width="8.9%" src="https://user-images.githubusercontent.com/70367776/177970540-b59a294f-82fd-4647-b8e2-9305c399c900.png" alt="Scrapli" />
62
+ <img width="19.5%" src="https://user-images.githubusercontent.com/70367776/229153411-6e5d10c3-634e-47b9-8920-0ed5e2311f9b.png" alt="Netmiko" />
63
+ <img width="10.5%" src="https://user-images.githubusercontent.com/70367776/183677690-fd956308-fda9-4583-863e-3a39d3ecc7f7.png" alt="NETCONF" />
64
+ <img width="9%" src="https://user-images.githubusercontent.com/70367776/183678136-4fbf50a8-adbb-468e-91b6-988d4c1ccd72.png" alt="RESTCONF" />
65
+ <img width="9.5%" src="https://user-images.githubusercontent.com/70367776/177966474-e66d8a59-e26e-4a3d-ac59-ded7cc005e4b.png" alt="Jinja2" />
66
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/177971288-e32552cd-1529-470a-8881-30c22d2df8ac.png" alt="pyATS" />
67
+ <img width="10%" src="https://user-images.githubusercontent.com/70367776/222764714-1ff86034-76f9-49d0-9e24-68798d779f56.png" alt="Batfish" />
68
+ <img width="10.2%" src="https://user-images.githubusercontent.com/70367776/183678800-403cf0ea-3c8a-47bb-b52a-2caa0cedc195.png" alt="Makefile" />
69
+ <img width="10.5%" src="https://user-images.githubusercontent.com/70367776/183679432-2b89f00c-f5b1-4d47-9c68-ad7fa332de01.png" alt="Prospector" />
70
+ <img width="9%" src="https://user-images.githubusercontent.com/70367776/183680151-62d5c625-0430-4c90-adfc-1ebb47fce4a9.png" alt="Bandit" />
71
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/177972703-3be3c4c3-aa9a-4468-97a6-e7760d536b89.png" alt="Git" />
72
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/177972925-1780012f-64a6-4b93-a215-fd47e3222b8f.png" alt="GitHub" />
73
+ <img width="11.5%" src="https://user-images.githubusercontent.com/70367776/232078456-8aee2fda-1289-4cd9-b7f3-9b34fcb4d7c7.png" alt="Docker" />
74
+ <img width="9.4%" src="https://user-images.githubusercontent.com/70367776/231501139-a449202e-6a81-4364-a4ea-1e42906e846e.png" alt="Azure Pipeline" />
75
+ <img width="33.2%" src="https://user-images.githubusercontent.com/70367776/231500048-77eeff9a-166b-4bd7-a0cc-3c5fc58be368.png" alt="NetBox" />
76
+ </p>
77
+
78
+ <br>
79
+
80
+ <h3 align="center">APIs can also be amusing to provide a programming joke ...</h3>
81
+ <p align="center"><img src="https://readme-jokes.vercel.app/api?hideBorder&theme=calm" /></p>
82
+ <h3 align="center">... or an interesting quote.</h3>
83
+ <p align="center"><img src="https://quotes-github-readme.vercel.app/api?type=horizontal&theme=dracula" /></p>
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")