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.
@@ -0,0 +1,223 @@
1
+ # # pylint: disable=no-member
2
+ from typing import List
3
+ import re
4
+ import logging
5
+
6
+ from napalm.junos import JunOSDriver
7
+ from napalm.base.models import LLDPNeighborDict, ConfigDict
8
+ from ..models import InventoryDict
9
+ from ..base import SCBaseNetconfDriver
10
+
11
+
12
+ from . import junos_views
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ XCVR_DESC_TO_INTERFACE_PREFIX = {
17
+ r"100GBASE-": "et-",
18
+ r"QSFP": "et-",
19
+ r"SFP28": "et-",
20
+ r"SFP\+": "xe-",
21
+ r"SFP-": "ge-",
22
+ r"CFP-": "et-",
23
+ r"(UNKNOWN|UNSUPPORTED)": "ge-", # default
24
+ }
25
+
26
+ DEFAULT_ARP_AGING_TIMER = 1200
27
+
28
+
29
+ class SCJunOS(JunOSDriver, SCBaseNetconfDriver):
30
+ """
31
+ Junos parser
32
+ """
33
+
34
+ netmiko_host_type = "juniper_junos"
35
+
36
+ # mapping of inventory name regex to their netbox types
37
+ INVENTORY_TO_TYPE = {
38
+ r"Fan Tray": "fan",
39
+ r"(PIC|MIC|FPC)": "linecard",
40
+ r"Xcvr": "optic",
41
+ r"(PEM|Power Supply)": "psu",
42
+ r"Routing Engine": "re",
43
+ # don't care about these inventory items
44
+ r"(CPU|PDM|FPM|Midplane|CB|SFB|TIB)": None,
45
+ }
46
+
47
+ def _get_junos_inventory_type(self, name: str, model_number: str) -> str:
48
+ """
49
+ Maps the name and part number of the Junos inventory item to its type
50
+ """
51
+ # "Xcvr" parts are always optics
52
+ if name.startswith("Xcvr"):
53
+ return "optic"
54
+
55
+ # non-optic parts should always have a model number, if not
56
+ # we don't care about them
57
+ if not (model_number):
58
+ return None
59
+
60
+ # EX switches and virtual chassis save their vc members and uplink modules
61
+ # as FPC X and PIC X - we want to classify those correctly
62
+ if re.match(r"EX[234]", model_number):
63
+ if re.search(r"FPC \d+", name):
64
+ return "stack-member"
65
+
66
+ if re.search(r"PIC \d+", name):
67
+ return None
68
+
69
+ # uplink modules are also saved under PIC X, must classify based
70
+ # on their model number
71
+ if re.match(r"EX-UM", model_number):
72
+ return "uplink-module"
73
+
74
+ # otherwise we want to pattern-match based on the INVENTORY_TO_TYPE
75
+ # dictionary
76
+ return self._get_inventory_type(name)
77
+
78
+ def _get_xcvr_interface_prefix(self, xcvr_desc: str) -> str:
79
+ """
80
+ Maps xcvr description from "show chassis hardware" to
81
+ interface prefix
82
+ """
83
+ for pattern, prefix in XCVR_DESC_TO_INTERFACE_PREFIX.items():
84
+ if re.match(pattern, xcvr_desc):
85
+ return prefix
86
+
87
+ raise ValueError(f"{self.hostname}: Unknown xcvr type {xcvr_desc}")
88
+
89
+ def _get_mod_number(self, mod_name: str) -> str:
90
+ """
91
+ Strips out "FPC|MIC|PIC" from a module name seen in "show chassis hardware"
92
+ """
93
+ return re.sub(r"(MIC|PIC|FPC) ", "", mod_name)
94
+
95
+ def _get_inventory_part_number(self, item: any) -> str:
96
+ """
97
+ Extracts the part number from an inventory item
98
+ which, depending on the specific item, is stashed in
99
+ the part number, model number, or description field
100
+ """
101
+ if item.model_number and item.model_number != "model-number":
102
+ return item.model_number
103
+ return item.part_number
104
+
105
+ def _save_inventory_item(
106
+ self, output: list, item: any, parent: str = None, grandparent: str = None
107
+ ) -> bool:
108
+ """
109
+ Extracts data from a particular inventory item object.
110
+ Returns whether we care aobut this item or not (so we know whether or not)
111
+ to loop over its children
112
+ """
113
+ # skip builtin types, or parts without model numbers that aren't transcievers
114
+ if item.part_number == "BUILTIN":
115
+ return False
116
+
117
+ # get inventory type based on item name and P/N
118
+ inv_type = self._get_junos_inventory_type(item.name, item.model_number)
119
+
120
+ if not inv_type:
121
+ return False
122
+
123
+ # for transcievers, change the name from Xcvr X to be the junos interface name
124
+ # note that we expect transcievers to always be at the sub-sub module level
125
+ # and thus to have a grandparent and parent
126
+ m = re.search(r"Xcvr (\d+)", item.name)
127
+ if m:
128
+ if not (grandparent or parent):
129
+ raise ValueError(
130
+ f"{self.hostname}: No MIC and PIC found for {item.name}"
131
+ )
132
+ prefix = self._get_xcvr_interface_prefix(item.description)
133
+
134
+ item_name = f"{prefix}{self._get_mod_number(grandparent)}/{self._get_mod_number(parent)}/{m.group(1)}"
135
+
136
+ # for sub-linecards (mics or pics) we want to prepend the parent to the item name
137
+ elif parent:
138
+ item_name = f"{parent} {item.name}"
139
+ else:
140
+ item_name = item.name
141
+
142
+ output.append(
143
+ {
144
+ "type": inv_type,
145
+ "name": item_name,
146
+ "subtype": item.description,
147
+ "part_number": self._get_inventory_part_number(item),
148
+ "serial_number": item.serial_number,
149
+ }
150
+ )
151
+
152
+ return True
153
+
154
+ def get_config(
155
+ self, retrieve="all", full=False, sanitized=False, format="text"
156
+ ) -> ConfigDict:
157
+ """
158
+ pulling running config via ssh sow we can have it in 'display set' format.
159
+ """
160
+ if format != "text":
161
+ return super().get_config(
162
+ retrieve=retrieve, full=full, sanitized=sanitized, format=format
163
+ )
164
+
165
+ config = {"startup": "", "running": "", "candidate": ""}
166
+
167
+ with self.ssh_conn() as ssh_device:
168
+ config["running"] = ssh_device.send_command(
169
+ "show configuration | display set"
170
+ )
171
+
172
+ return config
173
+
174
+ def get_lldp_neighbors(self) -> LLDPNeighborDict:
175
+ """
176
+ Overrides napalm get_lldp_neighbors so that we actually get the remote
177
+ interface ID instead of the description.
178
+
179
+ NAPALM's get_lldp_neighbors_detail gets this for us - note that to get this detail,
180
+ you need to execute a command for each interface just like on the cli :(
181
+ """
182
+ neighs = super().get_lldp_neighbors_detail()
183
+ output = {}
184
+ for port, neighs in neighs.items():
185
+ output[port] = []
186
+ for neigh in neighs:
187
+ output[port].append(
188
+ {
189
+ "hostname": neigh["remote_system_name"]
190
+ if neigh["remote_system_name"]
191
+ else neigh["remote_chassis_id"],
192
+ "port": neigh["remote_port"],
193
+ }
194
+ )
195
+ return output
196
+
197
+ def get_inventory(self) -> List[InventoryDict]:
198
+ """
199
+ parses the get-chassis-inventory RPC call, which maps to "show chassis hardware"
200
+ """
201
+ result = junos_views.junos_inventory(self.device).get()
202
+
203
+ output = []
204
+ for chassis in dict(result).values():
205
+ # note that the chassis model and s/n are at this level, but
206
+ # that doesn't count as an 'inventory item' so we're ignoring it
207
+
208
+ # saving modules, sub-modules and sub-sub modules
209
+ for module in dict(chassis.modules).values():
210
+ self._save_inventory_item(output, module)
211
+
212
+ for sub_module in dict(module.sub_modules).values():
213
+ self._save_inventory_item(output, sub_module, parent=module.name)
214
+
215
+ for sub_sub_module in dict(sub_module.sub_sub_modules).values():
216
+ self._save_inventory_item(
217
+ output,
218
+ sub_sub_module,
219
+ parent=sub_module.name,
220
+ grandparent=module.name,
221
+ )
222
+
223
+ return output
@@ -0,0 +1,17 @@
1
+ ## https://www.juniper.net/documentation/us/en/software/junos-pyez/junos-pyez-developer/topics/task/junos-pyez-tables-views-loading.html
2
+
3
+ """
4
+ Load tables/views
5
+ """
6
+
7
+ from os.path import splitext
8
+ import yaml
9
+ from jnpr.junos.factory import FactoryLoader
10
+
11
+
12
+ _YAML_ = splitext(__file__)[0] + ".yml"
13
+
14
+ with open(_YAML_, encoding="utf-8") as f:
15
+ yaml_str = f.read()
16
+
17
+ globals().update(FactoryLoader().load(yaml.safe_load(yaml_str)))
@@ -0,0 +1,228 @@
1
+ ### ip interfaces ###
2
+ junos_ip_interfaces:
3
+ rpc: get-interface-information
4
+ item: physical-interface/logical-interface/address-family/interface-address
5
+ key: ifa-local
6
+ view: junos_ip_interfaces_view
7
+
8
+ junos_ip_interfaces_view:
9
+ fields:
10
+ ip: ifa-local
11
+ subnet: ifa-destination
12
+ family: ../address-family-name
13
+ interface: ../../name
14
+ mtu: ../mtu
15
+ description: ../../description
16
+ primary: ifa-flags/ifaf-current-primary
17
+ preferred: ifa-flags/ifaf-current-preferred
18
+ admin_up: ../../if-config-flags/iff-up
19
+ device_down: ../../if-config-flags/iff-device-down
20
+ hardware_down: ../../if-config-flags/iff-hardware-down
21
+
22
+
23
+ junos_vrf_interfaces:
24
+ rpc: get-instance-information
25
+ args:
26
+ detail: True
27
+ item: instance-core/instance-interface
28
+ key: interface-name
29
+ view: junos_vrf_interfaces_view
30
+
31
+ junos_vrf_interfaces_view:
32
+ fields:
33
+ vrf: ../instance-name
34
+
35
+ ## show route active-path ####
36
+ junos_route_table:
37
+ rpc: get-route-information
38
+ item: route-table/rt
39
+ args:
40
+ active-path: True
41
+ key:
42
+ - rt-destination
43
+ - ../table-name
44
+ view: junos_route_table_view
45
+
46
+ junos_route_table_view:
47
+ fields:
48
+ vrf: ../table-name
49
+ prefix: rt-destination
50
+ rt_entry: _entry_table
51
+
52
+ _entry_table:
53
+ item: rt-entry
54
+ key:
55
+ - protocol-name
56
+ - learned-from
57
+ view: _entry_view
58
+
59
+ _entry_view:
60
+ fields:
61
+ age: age
62
+ protocol: protocol-name
63
+ learned_from: gateway
64
+ next_hop: _nh_table
65
+
66
+ _nh_table:
67
+ item: nh
68
+ key:
69
+ - to
70
+ - via
71
+ view: _nh_view
72
+
73
+ _nh_view:
74
+ fields:
75
+ nh_ip: to
76
+ nh_interface: via
77
+ mpls_label: mpls-label
78
+ selected_next_hop: selected-next-hop
79
+ # nh_table_receive: nh-table-receive
80
+ # nh_table: nh-table
81
+
82
+ ### show route table mpls ###
83
+ junos_mpls_table:
84
+ rpc: get-route-information
85
+ item: route-table/rt
86
+ args:
87
+ table: mpls.0
88
+ active-path: True
89
+ key:
90
+ - rt-destination
91
+ view: junos_mpls_table_view
92
+
93
+ junos_mpls_table_view:
94
+ fields:
95
+ prefix: rt-destination
96
+ rt_entry: _mpls_entry_table
97
+
98
+ _mpls_entry_table:
99
+ item: rt-entry
100
+ key:
101
+ - protocol-name
102
+ - learned-from
103
+ view: _mpls_entry_view
104
+
105
+ _mpls_entry_view:
106
+ fields:
107
+ next_hop: _mpls_nh_table
108
+
109
+ _mpls_nh_table:
110
+ item: nh
111
+ key:
112
+ - to
113
+ - via
114
+ view: _mpls_nh_view
115
+
116
+ _mpls_nh_view:
117
+ fields:
118
+ nh_ip: to
119
+ nh_interface: via
120
+ mpls_label: mpls-label
121
+ # selected_next_hop: selected-next-hop
122
+ nh_table_receive: nh-table-receive
123
+ nh_table: nh-table
124
+
125
+
126
+ ### show chassis inventory ###
127
+ junos_inventory:
128
+ rpc: get-chassis-inventory
129
+ item: chassis
130
+ view: junos_inventory_view
131
+
132
+ junos_inventory_view:
133
+ fields:
134
+ system_serial: serial-number
135
+ system_description: description
136
+ modules: _module_table
137
+
138
+ _module_table:
139
+ item: chassis-module
140
+ key: name
141
+ view: _module_view
142
+
143
+ _module_view:
144
+ fields:
145
+ name: name
146
+ part_number: part-number
147
+ serial_number: serial-number
148
+ description: description
149
+ model_number: model-number
150
+ sub_modules: _sub_module_table
151
+
152
+ _sub_module_table:
153
+ item: chassis-sub-module
154
+ key: name
155
+ view: _sub_module_view
156
+
157
+ _sub_module_view:
158
+ fields:
159
+ name: name
160
+ part_number: part-number
161
+ serial_number: serial-number
162
+ description: description
163
+ model_number: model-number
164
+ sub_sub_modules: _sub_sub_module_table
165
+
166
+ _sub_sub_module_table:
167
+ item: chassis-sub-sub-module
168
+ key: name
169
+ view: _sub_sub_module_view
170
+
171
+ _sub_sub_module_view:
172
+ fields:
173
+ name: name
174
+ part_number: part-number
175
+ serial_number: serial-number
176
+ description: description
177
+ model_number: model-number
178
+
179
+
180
+ ### show route summary (for determining VPNs for ARP table) ###
181
+ junos_route_summary:
182
+ rpc: get-route-summary-information
183
+ item: route-table
184
+ key: table-name
185
+ view: junos_route_summary_view
186
+
187
+ junos_route_summary_view:
188
+ fields:
189
+ table: table-name
190
+
191
+
192
+ junos_show_int_lag:
193
+ rpc: get-interface-information
194
+ args:
195
+ terse: True
196
+ item: physical-interface/logical-interface
197
+ key: name
198
+ view: junos_show_int_lag_view
199
+
200
+ junos_show_int_lag_view:
201
+ fields:
202
+ phy_admin_status: ../admin-status
203
+ admin_status: admin-status
204
+ oper_status: oper-status
205
+ lag_name: address-family/ae-bundle-name
206
+
207
+
208
+ junos_show_lacp:
209
+ rpc: get-lacp-interface-information
210
+ item: lacp-interface-information
211
+ key: lag-lacp-header/aggregate-name
212
+ view: show_lacp_view
213
+
214
+ show_lacp_view:
215
+ fields:
216
+ name: lag-lacp-header/aggregate-name
217
+ members: _lacp_member_table
218
+
219
+ _lacp_member_table:
220
+ item: lag-lacp-state
221
+ key: name
222
+ view: _show_lacp_member_view
223
+
224
+ _show_lacp_member_view:
225
+ fields:
226
+ lacp_timeout: lacp-timeout
227
+ lacp_activity: lacp-activity
228
+
@@ -0,0 +1,27 @@
1
+ from typing import Literal
2
+ from typing_extensions import TypedDict
3
+
4
+ VALID_INVENTORY_TYPES = Literal[
5
+ "fabric_module",
6
+ "fan",
7
+ "linecard",
8
+ "optic",
9
+ "psu",
10
+ "re",
11
+ "stack_cable",
12
+ "stack_member",
13
+ "uplink_module",
14
+ "aoc",
15
+ "dac",
16
+ ]
17
+
18
+ InventoryDict = TypedDict(
19
+ "InventoryDict",
20
+ {
21
+ "type": VALID_INVENTORY_TYPES,
22
+ "subtype": str,
23
+ "name": str,
24
+ "part_number": str,
25
+ "serial_number": str,
26
+ },
27
+ )
@@ -0,0 +1 @@
1
+ from .nos import SCNOSDriver
@@ -0,0 +1,111 @@
1
+ NS = {
2
+ "dn-top": "http://drivenets.com/ns/yang/dn-top",
3
+ "dn-sys": "http://drivenets.com/ns/yang/dn-system",
4
+ "dn-sys-dns": "http://drivenets.com/ns/yang/dn-sys-dns",
5
+ "dn-sys-ncp": "http://drivenets.com/ns/yang/dn-sys-ncp",
6
+ "dn-platform": "http://drivenets.com/ns/yang/dn-platform",
7
+ "dn-if": "http://drivenets.com/ns/yang/dn-interfaces",
8
+ "dn-trans": "http://drivenets.com/ns/yang/dn-transceivers",
9
+ "dn-lldp": "http://drivenets.com/ns/yang/dn-lldp",
10
+ "dn-proto": "http://drivenets.com/ns/yang/dn-protocol",
11
+ }
12
+
13
+ FACTS_RPC_REQ = """<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
14
+ <filter>
15
+ <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
16
+ <system xmlns="http://drivenets.com/ns/yang/dn-system">
17
+ <oper-items>
18
+ <system-type/>
19
+ <system-uptime/>
20
+ <system-version/>
21
+ </oper-items>
22
+ <config-items>
23
+ <name/>
24
+ </config-items>
25
+ <ncps xmlns="http://drivenets.com/ns/yang/dn-sys-ncp">
26
+ <ncp>
27
+ <config-items>
28
+ <platform>
29
+ <oper-items>
30
+ <serial-number/>
31
+ </oper-items>
32
+ </platform>
33
+ </config-items>
34
+ </ncp>
35
+ </ncps>
36
+ <dns xmlns="http://drivenets.com/ns/yang/dn-sys-dns">
37
+ <config-items>
38
+ <domain-name/>
39
+ </config-items>
40
+ </dns>
41
+ </system>
42
+ <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
43
+ <interface>
44
+ <name/>
45
+ </interface>
46
+ </interfaces>
47
+ </drivenets-top>
48
+ </filter>
49
+ </get>
50
+ """
51
+
52
+ OPTICS_RPC_REQ = """<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
53
+ <filter>
54
+ <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
55
+ <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
56
+ <interface>
57
+ <transceivers xmlns="http://drivenets.com/ns/yang/dn-transceivers">
58
+ <oper-items>
59
+ <digital-optical-monitoring/>
60
+ </oper-items>
61
+ </transceivers>
62
+ </interface>
63
+ </interfaces>
64
+ </drivenets-top>
65
+ </filter>
66
+ </get>
67
+ """
68
+
69
+ LLDP_NEIGH_RPC_REQ = """<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
70
+ <filter>
71
+ <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
72
+ <protocols xmlns="http://drivenets.com/ns/yang/dn-protocol">
73
+ <lldp xmlns="http://drivenets.com/ns/yang/dn-lldp">
74
+ <interfaces>
75
+ <interface>
76
+ <neighbors>
77
+ <neighbor/>
78
+ </neighbors>
79
+ </interface>
80
+ </interfaces>
81
+ </lldp>
82
+ </protocols>
83
+ </drivenets-top>
84
+ </filter>
85
+ </get>
86
+ """
87
+
88
+ INVENTORY_RPC_REQ = """<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
89
+ <filter>
90
+ <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
91
+ <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
92
+ <interface>
93
+ <oper-items>
94
+ <interface-speed/>
95
+ </oper-items>
96
+ <transceivers xmlns="http://drivenets.com/ns/yang/dn-transceivers">
97
+ <oper-items>
98
+ <form-factor/>
99
+ <ethernet-pmd/>
100
+ <vendor/>
101
+ <vendor-part/>
102
+ <serial-no/>
103
+ <wavelength/>
104
+ </oper-items>
105
+ </transceivers>
106
+ </interface>
107
+ </interfaces>
108
+ </drivenets-top>
109
+ </filter>
110
+ </get>
111
+ """