sc-napalm 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- custom_napalm/__init__.py +30 -0
- custom_napalm/base.py +113 -0
- custom_napalm/eos/__init__.py +1 -0
- custom_napalm/eos/eos.py +93 -0
- custom_napalm/get.py +83 -0
- custom_napalm/iosxr/__init__.py +1 -0
- custom_napalm/iosxr/constants.py +144 -0
- custom_napalm/iosxr/iosxr.py +55 -0
- custom_napalm/junos/__init__.py +1 -0
- custom_napalm/junos/junos.py +223 -0
- custom_napalm/junos/junos_views.py +17 -0
- custom_napalm/junos/junos_views.yml +228 -0
- custom_napalm/models.py +27 -0
- custom_napalm/nos/__init__.py +1 -0
- custom_napalm/nos/constants.py +111 -0
- custom_napalm/nos/nos.py +230 -0
- custom_napalm/nxos/__init__.py +1 -0
- custom_napalm/nxos/nxos.py +88 -0
- custom_napalm/nxos/utils/textfsm_templates/sh_int_transceiver.tpl +27 -0
- custom_napalm/nxos/utils/textfsm_templates/sh_inventory.tpl +8 -0
- custom_napalm/srl/__init__.py +1 -0
- custom_napalm/srl/srl.py +160 -0
- custom_napalm/srl_ssh/__init__.py +1 -0
- custom_napalm/srl_ssh/srl_ssh.py +45 -0
- custom_napalm/sros/__init__.py +1 -0
- custom_napalm/sros/nc_filters.py +18 -0
- custom_napalm/sros/sros.py +59 -0
- custom_napalm/utils.py +74 -0
- sc_napalm-1.0.0.dist-info/METADATA +106 -0
- sc_napalm-1.0.0.dist-info/RECORD +32 -0
- sc_napalm-1.0.0.dist-info/WHEEL +4 -0
- sc_napalm-1.0.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .iosxr import SCIOSXR
|
|
2
|
+
from .junos import SCJunOS
|
|
3
|
+
from .nxos import SCNXOS
|
|
4
|
+
from .sros import SCNokiaSROSDriver
|
|
5
|
+
from .srl_ssh import NokiaSRLSSHDriver
|
|
6
|
+
from .srl import SCNokiaSRLDriver
|
|
7
|
+
from .eos import SCEOSDriver
|
|
8
|
+
from .nos import SCNOSDriver
|
|
9
|
+
|
|
10
|
+
PLATFORM_MAP = {
|
|
11
|
+
"iosxr": SCIOSXR,
|
|
12
|
+
"nxos": SCNXOS,
|
|
13
|
+
"junos": SCJunOS,
|
|
14
|
+
"sros": SCNokiaSROSDriver,
|
|
15
|
+
"srl_ssh": NokiaSRLSSHDriver,
|
|
16
|
+
"srl": SCNokiaSRLDriver,
|
|
17
|
+
"eos": SCEOSDriver,
|
|
18
|
+
"nos": SCNOSDriver,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_network_driver(platform: str):
|
|
23
|
+
"""
|
|
24
|
+
Returns network driver based on platform string.
|
|
25
|
+
"""
|
|
26
|
+
for valid_platform, driver in PLATFORM_MAP.items():
|
|
27
|
+
if valid_platform == platform:
|
|
28
|
+
return driver
|
|
29
|
+
|
|
30
|
+
raise NotImplementedError(f"Unsupported platform {platform}")
|
custom_napalm/base.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
netmiko_host_type = "linux"
|
|
56
|
+
|
|
57
|
+
def ssh_conn(self) -> ConnectHandler:
|
|
58
|
+
"""
|
|
59
|
+
Ugly workaround for getting stuff via the cli over ssh.
|
|
60
|
+
Starts a netmiko ssh connection handler and returns it,
|
|
61
|
+
allowing you to interact with the CLI of the device.
|
|
62
|
+
"""
|
|
63
|
+
optional_args = {}
|
|
64
|
+
if getattr(self, "optional_args", False):
|
|
65
|
+
optional_args = self.optional_args if self.optional_args else {}
|
|
66
|
+
|
|
67
|
+
args = {
|
|
68
|
+
"device_type": self.netmiko_host_type,
|
|
69
|
+
"host": self.hostname,
|
|
70
|
+
"username": self.username,
|
|
71
|
+
"password": self.password,
|
|
72
|
+
"ssh_config_file": optional_args.get("ssh_config", None),
|
|
73
|
+
}
|
|
74
|
+
return ConnectHandler(**args)
|
|
75
|
+
|
|
76
|
+
def _find_txt(self, xml_tree, path, default=None, namespaces=None):
|
|
77
|
+
"""
|
|
78
|
+
Stolen from the napalm iosxr driver
|
|
79
|
+
Extract the text value from a leaf in an XML tree using XPath.
|
|
80
|
+
|
|
81
|
+
Will return a default value if leaf path not matched.
|
|
82
|
+
:param xml_tree:the XML Tree object. <type'lxml.etree._Element'>.
|
|
83
|
+
:param path: XPath to be applied in order to extract the desired data.
|
|
84
|
+
:param default: Value to be returned in case of a no match.
|
|
85
|
+
:param namespaces: namespace dictionary.
|
|
86
|
+
:return: a str value or None if leaf path not matched.
|
|
87
|
+
"""
|
|
88
|
+
value = None
|
|
89
|
+
xpath_applied = xml_tree.xpath(path, namespaces=namespaces)
|
|
90
|
+
|
|
91
|
+
if xpath_applied:
|
|
92
|
+
if not len(xpath_applied[0]):
|
|
93
|
+
if xpath_applied[0].text is not None:
|
|
94
|
+
value = xpath_applied[0].text.strip()
|
|
95
|
+
else:
|
|
96
|
+
value = ""
|
|
97
|
+
else:
|
|
98
|
+
value = default
|
|
99
|
+
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
# Helper xml methods that always pass in our namespaces by default
|
|
103
|
+
def _text(self, xml_tree, path, default=None):
|
|
104
|
+
return self._find_txt(xml_tree, path, default, namespaces=self.NS)
|
|
105
|
+
|
|
106
|
+
def _xpath(self, xml_tree, path):
|
|
107
|
+
return getattr(xml_tree, "xpath")(path, namespaces=self.NS)
|
|
108
|
+
|
|
109
|
+
def _find(self, xml_tree, element):
|
|
110
|
+
return getattr(xml_tree, "find")(element, namespaces=self.NS)
|
|
111
|
+
|
|
112
|
+
def _iterfind(self, xml_tree, element):
|
|
113
|
+
return getattr(xml_tree, "iterfind")(element, namespaces=self.NS)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .eos import SCEOSDriver
|
custom_napalm/eos/eos.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
super().__init__(
|
|
34
|
+
hostname, username, password, timeout=timeout, optional_args=optional_args
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def get_inventory(self) -> List[InventoryDict]:
|
|
38
|
+
inventory = self._run_commands(["show inventory"], encoding="json")
|
|
39
|
+
|
|
40
|
+
results = []
|
|
41
|
+
|
|
42
|
+
### optics
|
|
43
|
+
for slot, optic in inventory[0]["xcvrSlots"].items():
|
|
44
|
+
if optic.get("modelName"):
|
|
45
|
+
results.append(
|
|
46
|
+
{
|
|
47
|
+
"type": "optic",
|
|
48
|
+
"subtype": optic["modelName"],
|
|
49
|
+
"name": f"Ethernet{slot}",
|
|
50
|
+
"part_number": optic["modelName"],
|
|
51
|
+
"serial_number": optic["serialNum"],
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
### line cards
|
|
56
|
+
for slot, card in inventory[0]["cardSlots"].items():
|
|
57
|
+
if card.get("serialNum"):
|
|
58
|
+
results.append(
|
|
59
|
+
{
|
|
60
|
+
"type": self._get_inventory_type(slot),
|
|
61
|
+
"subtype": card["modelName"],
|
|
62
|
+
"name": f"Ethernet{slot}",
|
|
63
|
+
"part_number": card["modelName"],
|
|
64
|
+
"serial_number": card["serialNum"],
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
### PSUs
|
|
69
|
+
for slot, psu in inventory[0]["powerSupplySlots"].items():
|
|
70
|
+
if psu.get("serialNum"):
|
|
71
|
+
results.append(
|
|
72
|
+
{
|
|
73
|
+
"type": "psu",
|
|
74
|
+
"subtype": None,
|
|
75
|
+
"name": f"PSU {slot}",
|
|
76
|
+
"part_number": psu["name"],
|
|
77
|
+
"serial_number": psu["serialNum"],
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
### FANs
|
|
82
|
+
for slot, fan in inventory[0]["fanTraySlots"].items():
|
|
83
|
+
if fan.get("serialNum"):
|
|
84
|
+
results.append(
|
|
85
|
+
{
|
|
86
|
+
"type": "fan",
|
|
87
|
+
"subtype": None,
|
|
88
|
+
"name": f"FAN {slot}",
|
|
89
|
+
"part_number": fan["name"],
|
|
90
|
+
"serial_number": fan["serialNum"],
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
return results
|
custom_napalm/get.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
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("--ssh-cfg", help="Use SSH config file to connect", type=str)
|
|
34
|
+
log_args = parser.add_mutually_exclusive_group()
|
|
35
|
+
log_args.add_argument(
|
|
36
|
+
"-l", "--log-level", help="Set log level for sc_napalm only", choices=LOG_LEVELS
|
|
37
|
+
)
|
|
38
|
+
log_args.add_argument(
|
|
39
|
+
"-L", "--LOG-LEVEL", help="set global log level", choices=LOG_LEVELS
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--logfile",
|
|
43
|
+
type=str,
|
|
44
|
+
help="Save logging to a file (specified by name) instead of to stdout",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
for cred_arg in cred_args:
|
|
48
|
+
parser.add_argument(f"--{cred_arg}", help="Specify credentials")
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
|
|
51
|
+
log_level = args.log_level if args.log_level else args.LOG_LEVEL
|
|
52
|
+
if log_level:
|
|
53
|
+
configure_logging(
|
|
54
|
+
log_level,
|
|
55
|
+
log_globally=bool(args.LOG_LEVEL),
|
|
56
|
+
log_file=args.logfile,
|
|
57
|
+
log_to_console=not (bool(args.logfile)),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
creds = {
|
|
61
|
+
cred: get_from_args_or_env(cred, args, required=reqd)
|
|
62
|
+
for cred, reqd in cred_args.items()
|
|
63
|
+
}
|
|
64
|
+
Driver = get_network_driver(args.sc_napalm_platform)
|
|
65
|
+
|
|
66
|
+
# setting up connection details
|
|
67
|
+
optional_args = {"look_for_keys": False}
|
|
68
|
+
if args.ssh_cfg:
|
|
69
|
+
optional_args = {"ssh_config_file": args.ssh_cfg}
|
|
70
|
+
|
|
71
|
+
with Driver(
|
|
72
|
+
args.device,
|
|
73
|
+
creds["sc_username"],
|
|
74
|
+
creds["sc_password"],
|
|
75
|
+
timeout=60,
|
|
76
|
+
optional_args=optional_args,
|
|
77
|
+
) as conn:
|
|
78
|
+
result = getattr(conn, args.cmd)()
|
|
79
|
+
pprint(result)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .iosxr import SCIOSXR
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
NS = {
|
|
2
|
+
"int": "http://cisco.com/ns/yang/Cisco-IOS-XR-pfi-im-cmd-oper",
|
|
3
|
+
"int4": "http://cisco.com/ns/yang/Cisco-IOS-XR-ipv4-io-oper",
|
|
4
|
+
"int6": "http://cisco.com/ns/yang/Cisco-IOS-XR-ipv6-ma-oper",
|
|
5
|
+
"rib4": "http://cisco.com/ns/yang/Cisco-IOS-XR-ip-rib-ipv4-oper",
|
|
6
|
+
"rib6": "http://cisco.com/ns/yang/Cisco-IOS-XR-ip-rib-ipv6-oper",
|
|
7
|
+
"inv": "http://cisco.com/ns/yang/Cisco-IOS-XR-invmgr-oper",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
IP_INT_RPC_REQ = """
|
|
12
|
+
<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
|
|
13
|
+
<filter>
|
|
14
|
+
<interfaces xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-pfi-im-cmd-oper">
|
|
15
|
+
<interfaces>
|
|
16
|
+
<interface/>
|
|
17
|
+
</interfaces>
|
|
18
|
+
</interfaces>
|
|
19
|
+
<ipv4-network xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ipv4-io-oper">
|
|
20
|
+
<nodes>
|
|
21
|
+
<node>
|
|
22
|
+
<interface-data>
|
|
23
|
+
<vrfs>
|
|
24
|
+
<vrf>
|
|
25
|
+
<details>
|
|
26
|
+
<detail/>
|
|
27
|
+
</details>
|
|
28
|
+
</vrf>
|
|
29
|
+
</vrfs>
|
|
30
|
+
</interface-data>
|
|
31
|
+
</node>
|
|
32
|
+
</nodes>
|
|
33
|
+
</ipv4-network>
|
|
34
|
+
<ipv6-network xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ipv6-ma-oper">
|
|
35
|
+
<nodes>
|
|
36
|
+
<node>
|
|
37
|
+
<interface-data>
|
|
38
|
+
<vrfs>
|
|
39
|
+
<vrf>
|
|
40
|
+
<global-details>
|
|
41
|
+
<global-detail/>
|
|
42
|
+
</global-details>
|
|
43
|
+
</vrf>
|
|
44
|
+
</vrfs>
|
|
45
|
+
</interface-data>
|
|
46
|
+
</node>
|
|
47
|
+
</nodes>
|
|
48
|
+
</ipv6-network>
|
|
49
|
+
</filter>
|
|
50
|
+
</get>"""
|
|
51
|
+
|
|
52
|
+
IP_ROUTE_RPC_REQ = """
|
|
53
|
+
<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
|
|
54
|
+
<filter>
|
|
55
|
+
<rib xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ip-rib-ipv4-oper">
|
|
56
|
+
<vrfs>
|
|
57
|
+
<vrf>
|
|
58
|
+
<afs>
|
|
59
|
+
<af>
|
|
60
|
+
<af-name>IPv4</af-name>
|
|
61
|
+
<safs>
|
|
62
|
+
<saf>
|
|
63
|
+
<saf-name>Unicast</saf-name>
|
|
64
|
+
<ip-rib-route-table-names>
|
|
65
|
+
<ip-rib-route-table-name>
|
|
66
|
+
<route-table-name>default</route-table-name>
|
|
67
|
+
<routes>
|
|
68
|
+
<route>
|
|
69
|
+
<prefix-length/>
|
|
70
|
+
<protocol-name/>
|
|
71
|
+
<route-age/>
|
|
72
|
+
<route-path>
|
|
73
|
+
<active>true</active>
|
|
74
|
+
</route-path>
|
|
75
|
+
</route>
|
|
76
|
+
</routes>
|
|
77
|
+
</ip-rib-route-table-name>
|
|
78
|
+
</ip-rib-route-table-names>
|
|
79
|
+
</saf>
|
|
80
|
+
</safs>
|
|
81
|
+
</af>
|
|
82
|
+
</afs>
|
|
83
|
+
</vrf>
|
|
84
|
+
</vrfs>
|
|
85
|
+
</rib>
|
|
86
|
+
|
|
87
|
+
<ipv6-rib xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ip-rib-ipv6-oper">
|
|
88
|
+
<vrfs>
|
|
89
|
+
<vrf>
|
|
90
|
+
<afs>
|
|
91
|
+
<af>
|
|
92
|
+
<af-name>IPv6</af-name>
|
|
93
|
+
<safs>
|
|
94
|
+
<saf>
|
|
95
|
+
<saf-name>Unicast</saf-name>
|
|
96
|
+
<ip-rib-route-table-names>
|
|
97
|
+
<ip-rib-route-table-name>
|
|
98
|
+
<route-table-name>default</route-table-name>
|
|
99
|
+
<routes>
|
|
100
|
+
<route>
|
|
101
|
+
<prefix-length/>
|
|
102
|
+
<protocol-name/>
|
|
103
|
+
<route-age/>
|
|
104
|
+
<route-path>
|
|
105
|
+
<active>true</active>
|
|
106
|
+
</route-path>
|
|
107
|
+
</route>
|
|
108
|
+
</routes>
|
|
109
|
+
</ip-rib-route-table-name>
|
|
110
|
+
</ip-rib-route-table-names>
|
|
111
|
+
</saf>
|
|
112
|
+
</safs>
|
|
113
|
+
</af>
|
|
114
|
+
</afs>
|
|
115
|
+
</vrf>
|
|
116
|
+
</vrfs>
|
|
117
|
+
</ipv6-rib>
|
|
118
|
+
|
|
119
|
+
</filter>
|
|
120
|
+
</get>
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
INV_RPC_REQ = """
|
|
124
|
+
<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
|
|
125
|
+
<filter>
|
|
126
|
+
<inventory xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-invmgr-oper">
|
|
127
|
+
<entities>
|
|
128
|
+
<entity>
|
|
129
|
+
<attributes>
|
|
130
|
+
<inv-basic-bag>
|
|
131
|
+
<description/>
|
|
132
|
+
<name/>
|
|
133
|
+
<serial-number/>
|
|
134
|
+
<manufacturer-name/>
|
|
135
|
+
<model-name/>
|
|
136
|
+
<is-field-replaceable-unit>true</is-field-replaceable-unit>
|
|
137
|
+
</inv-basic-bag>
|
|
138
|
+
</attributes>
|
|
139
|
+
</entity>
|
|
140
|
+
</entities>
|
|
141
|
+
</inventory>
|
|
142
|
+
</filter>
|
|
143
|
+
</get>
|
|
144
|
+
"""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from lxml import etree
|
|
5
|
+
|
|
6
|
+
from napalm.iosxr_netconf import IOSXRNETCONFDriver
|
|
7
|
+
from ncclient.xml_ import to_ele
|
|
8
|
+
|
|
9
|
+
from ..base import SCBaseNetconfDriver
|
|
10
|
+
from ..models import InventoryDict
|
|
11
|
+
|
|
12
|
+
from .constants import INV_RPC_REQ
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SCIOSXR(IOSXRNETCONFDriver, SCBaseNetconfDriver):
|
|
18
|
+
"""
|
|
19
|
+
IOSXR Class
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
INVENTORY_TO_TYPE = {
|
|
23
|
+
r"PM\d+$": "psu",
|
|
24
|
+
r"FT\d+$": "fan",
|
|
25
|
+
r"FC\d+$": "fabric_module",
|
|
26
|
+
r"RP\d$": "re",
|
|
27
|
+
r"SC\d": None, # don't care about system controller
|
|
28
|
+
r"Rack 0": None, # this is the chassis, don't care about it
|
|
29
|
+
r"^(FourHundred|Hundred|Forty)GigE": "optic",
|
|
30
|
+
r"\d/\d": "linecard",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def get_inventory(self) -> List[InventoryDict]:
|
|
34
|
+
"""
|
|
35
|
+
Gets inventory data
|
|
36
|
+
"""
|
|
37
|
+
rpc_reply = self.device.dispatch(to_ele(INV_RPC_REQ)).xml
|
|
38
|
+
xml_result = etree.fromstring(rpc_reply)
|
|
39
|
+
|
|
40
|
+
output = []
|
|
41
|
+
for item in self._xpath(xml_result, "//inv:inv-basic-bag"):
|
|
42
|
+
name = self._text(item, "inv:name")
|
|
43
|
+
item_type = self._get_inventory_type(name)
|
|
44
|
+
if item_type:
|
|
45
|
+
output.append(
|
|
46
|
+
{
|
|
47
|
+
"type": item_type,
|
|
48
|
+
"name": name,
|
|
49
|
+
"part_number": self._text(item, "inv:model-name"),
|
|
50
|
+
"serial_number": self._text(item, "inv:serial-number"),
|
|
51
|
+
"parent": None,
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return output
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .junos import SCJunOS
|