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.
- nornir_collection-0.0.1/LICENSE +21 -0
- nornir_collection-0.0.1/PKG-INFO +136 -0
- nornir_collection-0.0.1/README.md +83 -0
- nornir_collection-0.0.1/nornir_collection/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/batfish/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/batfish/assert_config.py +358 -0
- nornir_collection-0.0.1/nornir_collection/batfish/utils.py +129 -0
- nornir_collection-0.0.1/nornir_collection/cisco/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/config_tasks.py +569 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/config_workflow.py +107 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/cli/show_tasks.py +677 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/config_tasks.py +564 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/config_workflow.py +298 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/nr_cfg_iosxe_netconf.py +186 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/netconf/ops_tasks.py +307 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/processor.py +151 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/pyats.py +236 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/cisco_rpc.py +514 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/config_workflow.py +95 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/restconf/tasks.py +325 -0
- nornir_collection-0.0.1/nornir_collection/cisco/configuration_management/utils.py +511 -0
- nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/cisco_software_upgrade.py +283 -0
- nornir_collection-0.0.1/nornir_collection/cisco/software_upgrade/utils.py +794 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/api_calls.py +1173 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/cisco_maintenance_report.py +221 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/cisco_support.py +727 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/reports.py +747 -0
- nornir_collection-0.0.1/nornir_collection/cisco/support_api/utils.py +316 -0
- nornir_collection-0.0.1/nornir_collection/fortinet/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/fortinet/utils.py +36 -0
- nornir_collection-0.0.1/nornir_collection/git.py +224 -0
- nornir_collection-0.0.1/nornir_collection/netbox/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/netbox/custom_script.py +107 -0
- nornir_collection-0.0.1/nornir_collection/netbox/inventory.py +360 -0
- nornir_collection-0.0.1/nornir_collection/netbox/scan_prefixes_and_update_ip_addresses.py +989 -0
- nornir_collection-0.0.1/nornir_collection/netbox/set_device_status.py +67 -0
- nornir_collection-0.0.1/nornir_collection/netbox/sync_datasource.py +111 -0
- nornir_collection-0.0.1/nornir_collection/netbox/update_cisco_inventory_data.py +158 -0
- nornir_collection-0.0.1/nornir_collection/netbox/update_cisco_support_plugin_data.py +339 -0
- nornir_collection-0.0.1/nornir_collection/netbox/update_fortinet_inventory_data.py +161 -0
- nornir_collection-0.0.1/nornir_collection/netbox/update_purestorage_inventory_data.py +144 -0
- nornir_collection-0.0.1/nornir_collection/netbox/utils.py +261 -0
- nornir_collection-0.0.1/nornir_collection/netbox/verify_device_primary_ip.py +202 -0
- nornir_collection-0.0.1/nornir_collection/nornir_plugins/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/netbox.py +250 -0
- nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/staggered_yaml.py +143 -0
- nornir_collection-0.0.1/nornir_collection/nornir_plugins/inventory/utils.py +277 -0
- nornir_collection-0.0.1/nornir_collection/purestorage/__init__.py +0 -0
- nornir_collection-0.0.1/nornir_collection/purestorage/utils.py +53 -0
- nornir_collection-0.0.1/nornir_collection/utils.py +741 -0
- nornir_collection-0.0.1/nornir_collection.egg-info/PKG-INFO +136 -0
- nornir_collection-0.0.1/nornir_collection.egg-info/SOURCES.txt +116 -0
- nornir_collection-0.0.1/nornir_collection.egg-info/dependency_links.txt +1 -0
- nornir_collection-0.0.1/nornir_collection.egg-info/requires.txt +32 -0
- nornir_collection-0.0.1/nornir_collection.egg-info/top_level.txt +1 -0
- nornir_collection-0.0.1/setup.cfg +4 -0
- 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
|
+

|
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
|
+

|
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
|
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")
|