sc-napalm 0.2.0__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.
Potentially problematic release.
This version of sc-napalm might be problematic. Click here for more details.
- sc_napalm-0.2.0/.gitignore +7 -0
- sc_napalm-0.2.0/PKG-INFO +100 -0
- sc_napalm-0.2.0/README.md +86 -0
- sc_napalm-0.2.0/pyproject.toml +45 -0
- sc_napalm-0.2.0/sample_env +2 -0
- sc_napalm-0.2.0/src/custom_napalm/__init__.py +28 -0
- sc_napalm-0.2.0/src/custom_napalm/base.py +108 -0
- sc_napalm-0.2.0/src/custom_napalm/eos/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/eos/eos.py +94 -0
- sc_napalm-0.2.0/src/custom_napalm/get.py +85 -0
- sc_napalm-0.2.0/src/custom_napalm/iosxr/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/iosxr/constants.py +144 -0
- sc_napalm-0.2.0/src/custom_napalm/iosxr/iosxr.py +55 -0
- sc_napalm-0.2.0/src/custom_napalm/junos/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/junos/junos.py +201 -0
- sc_napalm-0.2.0/src/custom_napalm/junos/junos_views.py +17 -0
- sc_napalm-0.2.0/src/custom_napalm/junos/junos_views.yml +228 -0
- sc_napalm-0.2.0/src/custom_napalm/models.py +27 -0
- sc_napalm-0.2.0/src/custom_napalm/nos/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/nos/constants.py +112 -0
- sc_napalm-0.2.0/src/custom_napalm/nos/nos.py +221 -0
- sc_napalm-0.2.0/src/custom_napalm/nxos/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/nxos/nxos.py +90 -0
- sc_napalm-0.2.0/src/custom_napalm/nxos/utils/textfsm_templates/sh_int_transceiver.tpl +27 -0
- sc_napalm-0.2.0/src/custom_napalm/nxos/utils/textfsm_templates/sh_inventory.tpl +8 -0
- sc_napalm-0.2.0/src/custom_napalm/srl/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/srl/srl.py +25 -0
- sc_napalm-0.2.0/src/custom_napalm/sros/__init__.py +1 -0
- sc_napalm-0.2.0/src/custom_napalm/sros/sros.py +6 -0
- sc_napalm-0.2.0/src/custom_napalm/utils.py +74 -0
- sc_napalm-0.2.0/test/mock_data/eos/get_inventory/um_eos/expected_result.json +324 -0
- sc_napalm-0.2.0/test/mock_data/eos/get_inventory/um_eos/show_inventory.json +675 -0
- sc_napalm-0.2.0/test/mock_data/iosxr/get_inventory/um_iosxr/expected_result.json +128 -0
- sc_napalm-0.2.0/test/mock_data/iosxr/get_inventory/um_iosxr/get-inventory.xml +281 -0
- sc_napalm-0.2.0/test/mock_data/junos/get_inventory/um_junos/expected_result.json +86 -0
- sc_napalm-0.2.0/test/mock_data/junos/get_inventory/um_junos/get-chassis-inventory.xml +112 -0
- sc_napalm-0.2.0/test/mock_data/nxos/get_inventory/um_nxos/expected_result.json +60 -0
- sc_napalm-0.2.0/test/mock_data/nxos/get_inventory/um_nxos/show_interface_transceiver.txt +53 -0
- sc_napalm-0.2.0/test/mock_data/nxos/get_inventory/um_nxos/show_inventory.txt +23 -0
- sc_napalm-0.2.0/test/mock_data/srl/get_facts/clab/interface.txt +1634 -0
- sc_napalm-0.2.0/test/mock_data/srl/get_facts/clab/platform_chassis.txt +21 -0
- sc_napalm-0.2.0/test/mock_data/srl/get_facts/clab/system_information.txt +21 -0
- sc_napalm-0.2.0/test/mock_data/srl/get_facts/clab/system_name_host_name.txt +1 -0
- sc_napalm-0.2.0/test/mock_drivers.py +180 -0
- sc_napalm-0.2.0/test/mock_get.py +60 -0
- sc_napalm-0.2.0/test/test_getters.py +25 -0
- sc_napalm-0.2.0/uv.lock +2321 -0
sc_napalm-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sc-napalm
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Requires-Dist: gitpython>=3.1.45
|
|
7
|
+
Requires-Dist: lxml>=6.0.2
|
|
8
|
+
Requires-Dist: napalm-srl>=1.0.5
|
|
9
|
+
Requires-Dist: napalm-sros>=1.0.2
|
|
10
|
+
Requires-Dist: napalm>=5.1.0
|
|
11
|
+
Requires-Dist: pynautobot>=2.1.1
|
|
12
|
+
Requires-Dist: python-decouple>=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# sc-napalm
|
|
16
|
+
|
|
17
|
+
This repo is for building custom [NAPALM](https://napalm.readthedocs.io/en/latest/) "getters" to pull operational data from network devices, as well as
|
|
18
|
+
override existing NAPALM getters with custom code if needed. At the moment only one custom getter is implemented: `get_inventory`. This getter
|
|
19
|
+
pulls model and serial numbers of device components: fans, psus, and optics.
|
|
20
|
+
|
|
21
|
+
What's cool about NAPALM is that if you nest your custom drivers under `custom_napalm` as is done in this project, you can install your custom drivers
|
|
22
|
+
in the same virtual environment as the main NAPALM package, and they will override NAPALM's core drivers. This allows us to leverage NAPALM in a pretty
|
|
23
|
+
seamless way - by that I mean applications that leverage NAPALM (like Nautobot or Nornir) can be easily altered to use this code instead.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Using sc-napalm
|
|
27
|
+
The package comes with a cli script called `sc-napalm-get` that will run a particular getter against a particular device and output the results
|
|
28
|
+
to your terminal.
|
|
29
|
+
|
|
30
|
+
Right now to use this code you must do the following things:
|
|
31
|
+
1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
|
|
32
|
+
2. Clone the repo onto your local machine, then do a local development install of the repo into a uv virtual environment.
|
|
33
|
+
```
|
|
34
|
+
git clone https://scinet.supercomputing.org:8443/automation/sc25/sc-napalm.git
|
|
35
|
+
cd sc-napalm
|
|
36
|
+
uv run pip install -e .
|
|
37
|
+
```
|
|
38
|
+
3. Copy `sample_env` file into `.env` and fill out `.env` with the credentials you need. Note that you could also specify these credentials
|
|
39
|
+
when you run the script, or in your local environment. The script prefers manual input, then `.env` then environment values.
|
|
40
|
+
|
|
41
|
+
4. Run the script with `uv run sc-napalm-get`.
|
|
42
|
+
```
|
|
43
|
+
amylieb@m-n76107kqmh sc-napalm % uv run sc-napalm-get -h
|
|
44
|
+
usage: sc-napalm-get [-h] [-l {DEBUG,INFO,WARNING,ERROR,CRITICAL} | -L {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--logfile LOGFILE] [--username USERNAME]
|
|
45
|
+
[--password PASSWORD]
|
|
46
|
+
device {iosxr,nxos,junos,sros,srl,eos} {get_config,get_facts,get_lldp_neighbors,get_inventory}
|
|
47
|
+
|
|
48
|
+
Run a specific sc_napalm "getter" against a device.
|
|
49
|
+
|
|
50
|
+
positional arguments:
|
|
51
|
+
device device hostname or IP address
|
|
52
|
+
{iosxr,nxos,junos,sros,srl,eos}
|
|
53
|
+
The platform of this device
|
|
54
|
+
{get_config,get_facts,get_lldp_neighbors,get_inventory}
|
|
55
|
+
The getter command to run against this device
|
|
56
|
+
|
|
57
|
+
options:
|
|
58
|
+
-h, --help show this help message and exit
|
|
59
|
+
-l, --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
60
|
+
Set log level for sc_napalm only
|
|
61
|
+
-L, --LOG-LEVEL {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
62
|
+
set global log level
|
|
63
|
+
--logfile LOGFILE Save logging to a file (specified by name) instead of to stdout
|
|
64
|
+
--username USERNAME Specify credentials
|
|
65
|
+
--password PASSWORD Specify credentials
|
|
66
|
+
aliebowitz@sysauto:~/src/sc-napalm$ uv run sc-napalm-get 2001:468:1f07:ff19::1d eos get_inventory
|
|
67
|
+
[{'name': 'Ethernet45',
|
|
68
|
+
'part_number': 'QSFP-100G-LR4',
|
|
69
|
+
'serial_number': 'XYL252206819',
|
|
70
|
+
'subtype': 'QSFP-100G-LR4',
|
|
71
|
+
'type': 'optic'},
|
|
72
|
+
{'name': 'Ethernet46',
|
|
73
|
+
'part_number': 'QSFP-100G-LR4',
|
|
74
|
+
'serial_number': 'XYL252206822',
|
|
75
|
+
'subtype': 'QSFP-100G-LR4',
|
|
76
|
+
'type': 'optic'},
|
|
77
|
+
{'name': 'PSU 1',
|
|
78
|
+
'part_number': 'PWR-511-AC-RED',
|
|
79
|
+
'serial_number': 'EEWT2420216960',
|
|
80
|
+
'subtype': None,
|
|
81
|
+
'type': 'psu'},
|
|
82
|
+
...
|
|
83
|
+
.....
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Developing sc-napalm
|
|
88
|
+
Currently, the getters that are exposed as options in the `get` script are defined in the [base class](https://scinet.supercomputing.org:8443/automation/sc25/sc-napalm/-/blob/main/src/custom_napalm/base.py?ref_type=heads) of the custom drivers.
|
|
89
|
+
Note that because all the custom classes inherit the NAPALM getters, we could easily define all the other NAPALM getters there, but I've only included ones I think are obviously useful to us.
|
|
90
|
+
|
|
91
|
+
My hope is that instead of just printing out results we can write code that saves data in Nautobot, or some other place.
|
|
92
|
+
This could be done with Nornir or Nautobot jobs.
|
|
93
|
+
|
|
94
|
+
## To-dos
|
|
95
|
+
* Decide where this code is run and how (periodically? Manually? From Nautobot? if so, how?)
|
|
96
|
+
* Decide what output we want to capture (eg "show version"? "show lldp neighbor"?) and where it should go
|
|
97
|
+
* Fuctions/tasks that transform output and save it appropriately
|
|
98
|
+
* `get_inventory` methods for sros
|
|
99
|
+
* Test/mock classes for custom getters
|
|
100
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# sc-napalm
|
|
2
|
+
|
|
3
|
+
This repo is for building custom [NAPALM](https://napalm.readthedocs.io/en/latest/) "getters" to pull operational data from network devices, as well as
|
|
4
|
+
override existing NAPALM getters with custom code if needed. At the moment only one custom getter is implemented: `get_inventory`. This getter
|
|
5
|
+
pulls model and serial numbers of device components: fans, psus, and optics.
|
|
6
|
+
|
|
7
|
+
What's cool about NAPALM is that if you nest your custom drivers under `custom_napalm` as is done in this project, you can install your custom drivers
|
|
8
|
+
in the same virtual environment as the main NAPALM package, and they will override NAPALM's core drivers. This allows us to leverage NAPALM in a pretty
|
|
9
|
+
seamless way - by that I mean applications that leverage NAPALM (like Nautobot or Nornir) can be easily altered to use this code instead.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Using sc-napalm
|
|
13
|
+
The package comes with a cli script called `sc-napalm-get` that will run a particular getter against a particular device and output the results
|
|
14
|
+
to your terminal.
|
|
15
|
+
|
|
16
|
+
Right now to use this code you must do the following things:
|
|
17
|
+
1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
|
|
18
|
+
2. Clone the repo onto your local machine, then do a local development install of the repo into a uv virtual environment.
|
|
19
|
+
```
|
|
20
|
+
git clone https://scinet.supercomputing.org:8443/automation/sc25/sc-napalm.git
|
|
21
|
+
cd sc-napalm
|
|
22
|
+
uv run pip install -e .
|
|
23
|
+
```
|
|
24
|
+
3. Copy `sample_env` file into `.env` and fill out `.env` with the credentials you need. Note that you could also specify these credentials
|
|
25
|
+
when you run the script, or in your local environment. The script prefers manual input, then `.env` then environment values.
|
|
26
|
+
|
|
27
|
+
4. Run the script with `uv run sc-napalm-get`.
|
|
28
|
+
```
|
|
29
|
+
amylieb@m-n76107kqmh sc-napalm % uv run sc-napalm-get -h
|
|
30
|
+
usage: sc-napalm-get [-h] [-l {DEBUG,INFO,WARNING,ERROR,CRITICAL} | -L {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--logfile LOGFILE] [--username USERNAME]
|
|
31
|
+
[--password PASSWORD]
|
|
32
|
+
device {iosxr,nxos,junos,sros,srl,eos} {get_config,get_facts,get_lldp_neighbors,get_inventory}
|
|
33
|
+
|
|
34
|
+
Run a specific sc_napalm "getter" against a device.
|
|
35
|
+
|
|
36
|
+
positional arguments:
|
|
37
|
+
device device hostname or IP address
|
|
38
|
+
{iosxr,nxos,junos,sros,srl,eos}
|
|
39
|
+
The platform of this device
|
|
40
|
+
{get_config,get_facts,get_lldp_neighbors,get_inventory}
|
|
41
|
+
The getter command to run against this device
|
|
42
|
+
|
|
43
|
+
options:
|
|
44
|
+
-h, --help show this help message and exit
|
|
45
|
+
-l, --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
46
|
+
Set log level for sc_napalm only
|
|
47
|
+
-L, --LOG-LEVEL {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
48
|
+
set global log level
|
|
49
|
+
--logfile LOGFILE Save logging to a file (specified by name) instead of to stdout
|
|
50
|
+
--username USERNAME Specify credentials
|
|
51
|
+
--password PASSWORD Specify credentials
|
|
52
|
+
aliebowitz@sysauto:~/src/sc-napalm$ uv run sc-napalm-get 2001:468:1f07:ff19::1d eos get_inventory
|
|
53
|
+
[{'name': 'Ethernet45',
|
|
54
|
+
'part_number': 'QSFP-100G-LR4',
|
|
55
|
+
'serial_number': 'XYL252206819',
|
|
56
|
+
'subtype': 'QSFP-100G-LR4',
|
|
57
|
+
'type': 'optic'},
|
|
58
|
+
{'name': 'Ethernet46',
|
|
59
|
+
'part_number': 'QSFP-100G-LR4',
|
|
60
|
+
'serial_number': 'XYL252206822',
|
|
61
|
+
'subtype': 'QSFP-100G-LR4',
|
|
62
|
+
'type': 'optic'},
|
|
63
|
+
{'name': 'PSU 1',
|
|
64
|
+
'part_number': 'PWR-511-AC-RED',
|
|
65
|
+
'serial_number': 'EEWT2420216960',
|
|
66
|
+
'subtype': None,
|
|
67
|
+
'type': 'psu'},
|
|
68
|
+
...
|
|
69
|
+
.....
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Developing sc-napalm
|
|
74
|
+
Currently, the getters that are exposed as options in the `get` script are defined in the [base class](https://scinet.supercomputing.org:8443/automation/sc25/sc-napalm/-/blob/main/src/custom_napalm/base.py?ref_type=heads) of the custom drivers.
|
|
75
|
+
Note that because all the custom classes inherit the NAPALM getters, we could easily define all the other NAPALM getters there, but I've only included ones I think are obviously useful to us.
|
|
76
|
+
|
|
77
|
+
My hope is that instead of just printing out results we can write code that saves data in Nautobot, or some other place.
|
|
78
|
+
This could be done with Nornir or Nautobot jobs.
|
|
79
|
+
|
|
80
|
+
## To-dos
|
|
81
|
+
* Decide where this code is run and how (periodically? Manually? From Nautobot? if so, how?)
|
|
82
|
+
* Decide what output we want to capture (eg "show version"? "show lldp neighbor"?) and where it should go
|
|
83
|
+
* Fuctions/tasks that transform output and save it appropriately
|
|
84
|
+
* `get_inventory` methods for sros
|
|
85
|
+
* Test/mock classes for custom getters
|
|
86
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sc-napalm"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"gitpython>=3.1.45",
|
|
9
|
+
"lxml>=6.0.2",
|
|
10
|
+
"napalm>=5.1.0",
|
|
11
|
+
"napalm-srl>=1.0.5",
|
|
12
|
+
"napalm-sros>=1.0.2",
|
|
13
|
+
"pynautobot>=2.1.1",
|
|
14
|
+
"python-decouple>=3.8",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["hatchling"]
|
|
20
|
+
build-backend = "hatchling.build"
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
sc-napalm-get = "custom_napalm.get:main"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["src/custom_napalm"]
|
|
27
|
+
|
|
28
|
+
#[tool.hatch.build]
|
|
29
|
+
#include = [
|
|
30
|
+
# "src/custom_napalm/junos/*.yml",
|
|
31
|
+
# "src/custom_napalm/nxos/*.tpl",
|
|
32
|
+
#]
|
|
33
|
+
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=8.3.5",
|
|
37
|
+
"ruff>=0.14.3",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
filterwarnings = [
|
|
42
|
+
"ignore::DeprecationWarning",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .iosxr import SCIOSXR
|
|
2
|
+
from .junos import SCJunOS
|
|
3
|
+
from .nxos import SCNXOS
|
|
4
|
+
from .sros import SCNokiaSROSDriver
|
|
5
|
+
from .srl import SCNokiaSRLDriver
|
|
6
|
+
from .eos import SCEOSDriver
|
|
7
|
+
from .nos import SCNOSDriver
|
|
8
|
+
|
|
9
|
+
PLATFORM_MAP = {
|
|
10
|
+
"iosxr": SCIOSXR,
|
|
11
|
+
"nxos": SCNXOS,
|
|
12
|
+
"junos": SCJunOS,
|
|
13
|
+
"sros": SCNokiaSROSDriver,
|
|
14
|
+
"srl": SCNokiaSRLDriver,
|
|
15
|
+
"eos": SCEOSDriver,
|
|
16
|
+
"nos": SCNOSDriver,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_network_driver(platform: str):
|
|
21
|
+
"""
|
|
22
|
+
Returns network driver based on platform string.
|
|
23
|
+
"""
|
|
24
|
+
for valid_platform, driver in PLATFORM_MAP.items():
|
|
25
|
+
if valid_platform == platform:
|
|
26
|
+
return driver
|
|
27
|
+
|
|
28
|
+
raise NotImplementedError(f"Unsupported platform {platform}")
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from typing import Dict, List
|
|
2
|
+
import re
|
|
3
|
+
from napalm.base import models as napalm_models
|
|
4
|
+
from netmiko import ConnectHandler
|
|
5
|
+
|
|
6
|
+
from . import models as sc_models
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SCBaseDriver:
|
|
10
|
+
"""
|
|
11
|
+
Base class that all children should inherit
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# populate in child classes
|
|
15
|
+
INVENTORY_TO_TYPE = {}
|
|
16
|
+
|
|
17
|
+
def _get_inventory_type(self, name: str) -> str:
|
|
18
|
+
"""
|
|
19
|
+
Maps the name of the inventory item to its type
|
|
20
|
+
"""
|
|
21
|
+
for pattern, inv_type in self.INVENTORY_TO_TYPE.items():
|
|
22
|
+
if inv_type and inv_type not in sc_models.VALID_INVENTORY_TYPES.__args__:
|
|
23
|
+
raise TypeError(f"Invalid Inventory type {inv_type}")
|
|
24
|
+
if re.search(pattern, name):
|
|
25
|
+
return inv_type
|
|
26
|
+
|
|
27
|
+
raise ValueError(f"Unknown inventory item {name}")
|
|
28
|
+
|
|
29
|
+
def get_config(self) -> napalm_models.ConfigDict:
|
|
30
|
+
"""napalm get config"""
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
33
|
+
def get_facts(self) -> napalm_models.FactsDict:
|
|
34
|
+
"""napalm get facts"""
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
def get_optics(self) -> Dict[str, napalm_models.OpticsDict]:
|
|
38
|
+
"""napalm get optics"""
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
|
|
41
|
+
def get_lldp_neighbors(self) -> Dict[str, List[napalm_models.LLDPNeighborDict]]:
|
|
42
|
+
"""napalm get lldp neighbors"""
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
def get_inventory(self) -> List[sc_models.InventoryDict]:
|
|
46
|
+
"""sc get inventory"""
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SCBaseNetconfDriver(SCBaseDriver):
|
|
51
|
+
"""
|
|
52
|
+
Inclues some helper xml functions
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def ssh_conn(self) -> ConnectHandler:
|
|
56
|
+
"""
|
|
57
|
+
Ugly workaround for getting stuff via the cli over ssh.
|
|
58
|
+
Starts a netmiko ssh connection handler and returns it,
|
|
59
|
+
allowing you to interact with the CLI of the device.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
args = {
|
|
63
|
+
"device_type": "linux",
|
|
64
|
+
"host":self.hostname,
|
|
65
|
+
"username":self.username,
|
|
66
|
+
"password":self.password,
|
|
67
|
+
"ssh_config_file":self.optional_args.get("ssh_config", None),
|
|
68
|
+
}
|
|
69
|
+
return ConnectHandler(**args)
|
|
70
|
+
|
|
71
|
+
def _find_txt(self, xml_tree, path, default=None, namespaces=None):
|
|
72
|
+
"""
|
|
73
|
+
Stolen from the napalm iosxr driver
|
|
74
|
+
Extract the text value from a leaf in an XML tree using XPath.
|
|
75
|
+
|
|
76
|
+
Will return a default value if leaf path not matched.
|
|
77
|
+
:param xml_tree:the XML Tree object. <type'lxml.etree._Element'>.
|
|
78
|
+
:param path: XPath to be applied in order to extract the desired data.
|
|
79
|
+
:param default: Value to be returned in case of a no match.
|
|
80
|
+
:param namespaces: namespace dictionary.
|
|
81
|
+
:return: a str value or None if leaf path not matched.
|
|
82
|
+
"""
|
|
83
|
+
value = None
|
|
84
|
+
xpath_applied = xml_tree.xpath(path, namespaces=namespaces)
|
|
85
|
+
|
|
86
|
+
if xpath_applied:
|
|
87
|
+
if not len(xpath_applied[0]):
|
|
88
|
+
if xpath_applied[0].text is not None:
|
|
89
|
+
value = xpath_applied[0].text.strip()
|
|
90
|
+
else:
|
|
91
|
+
value = ""
|
|
92
|
+
else:
|
|
93
|
+
value = default
|
|
94
|
+
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
# Helper xml methods that always pass in our namespaces by default
|
|
98
|
+
def _text(self, xml_tree, path, default=None):
|
|
99
|
+
return self._find_txt(xml_tree, path, default, namespaces=self.NS)
|
|
100
|
+
|
|
101
|
+
def _xpath(self, xml_tree, path):
|
|
102
|
+
return getattr(xml_tree, "xpath")(path, namespaces=self.NS)
|
|
103
|
+
|
|
104
|
+
def _find(self, xml_tree, element):
|
|
105
|
+
return getattr(xml_tree, "find")(element, namespaces=self.NS)
|
|
106
|
+
|
|
107
|
+
def _iterfind(self, xml_tree, element):
|
|
108
|
+
return getattr(xml_tree, "iterfind")(element, namespaces=self.NS)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .eos import SCEOSDriver
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from napalm.eos import EOSDriver
|
|
3
|
+
from ..base import SCBaseDriver
|
|
4
|
+
from ..models import InventoryDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SCEOSDriver(EOSDriver, SCBaseDriver):
|
|
8
|
+
("fabric_module",)
|
|
9
|
+
("fan",)
|
|
10
|
+
("linecard",)
|
|
11
|
+
("optic",)
|
|
12
|
+
("psu",)
|
|
13
|
+
("re",)
|
|
14
|
+
("stack_cable",)
|
|
15
|
+
("stack_member",)
|
|
16
|
+
("uplink_module",)
|
|
17
|
+
("aoc",)
|
|
18
|
+
("dac",)
|
|
19
|
+
|
|
20
|
+
INVENTORY_TO_TYPE = {
|
|
21
|
+
r"Fabric": "fabric_module",
|
|
22
|
+
r"Linecard": "linecard",
|
|
23
|
+
r"Supervisor": "re",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def __init__(self, hostname, username, password, timeout=60, optional_args=None):
|
|
27
|
+
"""
|
|
28
|
+
Forcing ssh transport since we don't enable the web interface
|
|
29
|
+
"""
|
|
30
|
+
optional_args = optional_args if optional_args else {}
|
|
31
|
+
optional_args["transport"] = "ssh"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
super().__init__(
|
|
35
|
+
hostname, username, password, timeout=timeout, optional_args=optional_args
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def get_inventory(self) -> List[InventoryDict]:
|
|
39
|
+
inventory = self._run_commands(["show inventory"], encoding="json")
|
|
40
|
+
|
|
41
|
+
results = []
|
|
42
|
+
|
|
43
|
+
### optics
|
|
44
|
+
for slot, optic in inventory[0]["xcvrSlots"].items():
|
|
45
|
+
if optic.get("modelName"):
|
|
46
|
+
results.append(
|
|
47
|
+
{
|
|
48
|
+
"type": "optic",
|
|
49
|
+
"subtype": optic["modelName"],
|
|
50
|
+
"name": f"Ethernet{slot}",
|
|
51
|
+
"part_number": optic["modelName"],
|
|
52
|
+
"serial_number": optic["serialNum"],
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
### line cards
|
|
57
|
+
for slot, card in inventory[0]["cardSlots"].items():
|
|
58
|
+
if card.get("serialNum"):
|
|
59
|
+
results.append(
|
|
60
|
+
{
|
|
61
|
+
"type": self._get_inventory_type(slot),
|
|
62
|
+
"subtype": card["modelName"],
|
|
63
|
+
"name": f"Ethernet{slot}",
|
|
64
|
+
"part_number": card["modelName"],
|
|
65
|
+
"serial_number": card["serialNum"],
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
### PSUs
|
|
70
|
+
for slot, psu in inventory[0]["powerSupplySlots"].items():
|
|
71
|
+
if psu.get("serialNum"):
|
|
72
|
+
results.append(
|
|
73
|
+
{
|
|
74
|
+
"type": "psu",
|
|
75
|
+
"subtype": None,
|
|
76
|
+
"name": f"PSU {slot}",
|
|
77
|
+
"part_number": psu["name"],
|
|
78
|
+
"serial_number": psu["serialNum"],
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
### FANs
|
|
83
|
+
for slot, fan in inventory[0]["fanTraySlots"].items():
|
|
84
|
+
if fan.get("serialNum"):
|
|
85
|
+
results.append(
|
|
86
|
+
{
|
|
87
|
+
"type": "fan",
|
|
88
|
+
"subtype": None,
|
|
89
|
+
"name": f"FAN {slot}",
|
|
90
|
+
"part_number": fan["name"],
|
|
91
|
+
"serial_number": fan["serialNum"],
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
return results
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pprint import pprint
|
|
3
|
+
|
|
4
|
+
from napalm import get_network_driver
|
|
5
|
+
|
|
6
|
+
from custom_napalm import PLATFORM_MAP
|
|
7
|
+
from custom_napalm.base import SCBaseDriver
|
|
8
|
+
from custom_napalm.utils import configure_logging, LOG_LEVELS, get_from_args_or_env
|
|
9
|
+
|
|
10
|
+
# list of getters to run
|
|
11
|
+
GETTERS = [attr for attr in SCBaseDriver.__dict__ if attr.startswith("get")]
|
|
12
|
+
|
|
13
|
+
cred_args = {"sc_username": True, "sc_password": True}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
description="""
|
|
19
|
+
Run a specific sc_napalm "getter" against a device.
|
|
20
|
+
"""
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument("device", help="device hostname or IP address")
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"sc_napalm_platform",
|
|
25
|
+
choices=PLATFORM_MAP,
|
|
26
|
+
help="The platform of this device",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"cmd",
|
|
30
|
+
choices=GETTERS,
|
|
31
|
+
help="The getter command to run against this device",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--ssh-cfg", help="Use SSH config file to connect", type=str
|
|
35
|
+
)
|
|
36
|
+
log_args = parser.add_mutually_exclusive_group()
|
|
37
|
+
log_args.add_argument(
|
|
38
|
+
"-l", "--log-level", help="Set log level for sc_napalm only", choices=LOG_LEVELS
|
|
39
|
+
)
|
|
40
|
+
log_args.add_argument(
|
|
41
|
+
"-L", "--LOG-LEVEL", help="set global log level", choices=LOG_LEVELS
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--logfile",
|
|
45
|
+
type=str,
|
|
46
|
+
help="Save logging to a file (specified by name) instead of to stdout",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
for cred_arg in cred_args:
|
|
50
|
+
parser.add_argument(f"--{cred_arg}", help="Specify credentials")
|
|
51
|
+
args = parser.parse_args()
|
|
52
|
+
|
|
53
|
+
log_level = args.log_level if args.log_level else args.LOG_LEVEL
|
|
54
|
+
if log_level:
|
|
55
|
+
configure_logging(
|
|
56
|
+
log_level,
|
|
57
|
+
log_globally=bool(args.LOG_LEVEL),
|
|
58
|
+
log_file=args.logfile,
|
|
59
|
+
log_to_console=not (bool(args.logfile)),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
creds = {
|
|
63
|
+
cred: get_from_args_or_env(cred, args, required=reqd)
|
|
64
|
+
for cred, reqd in cred_args.items()
|
|
65
|
+
}
|
|
66
|
+
Driver = get_network_driver(args.sc_napalm_platform)
|
|
67
|
+
|
|
68
|
+
# setting up connection details
|
|
69
|
+
optional_args = {"look_for_keys": False}
|
|
70
|
+
if args.ssh_cfg:
|
|
71
|
+
optional_args = {"ssh_config_file": args.ssh_cfg}
|
|
72
|
+
|
|
73
|
+
with Driver(
|
|
74
|
+
args.device,
|
|
75
|
+
creds["sc_username"],
|
|
76
|
+
creds["sc_password"],
|
|
77
|
+
timeout=60,
|
|
78
|
+
optional_args=optional_args,
|
|
79
|
+
) as conn:
|
|
80
|
+
result = getattr(conn, args.cmd)()
|
|
81
|
+
pprint(result)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .iosxr import SCIOSXR
|