Skip to content

Commit 9199164

Browse files
authored
Merge pull request #1559 from napalm-automation/develop
Merge forward to 3.4.0
2 parents 524a086 + 6d743ea commit 9199164

File tree

49 files changed

+2894
-451
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2894
-451
lines changed

docs/conf.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@
213213
# (source start file, target name, title,
214214
# author, documentclass [howto, manual, or own class]).
215215
latex_documents = [
216-
("index", "napalm.tex", u"NAPALM Documentation", u"David Barroso", "manual")
216+
("index", "napalm.tex", "NAPALM Documentation", "David Barroso", "manual")
217217
]
218218

219219
# The name of an image file (relative to this directory) to place at the top of
@@ -241,7 +241,7 @@
241241

242242
# One entry per manual page. List of tuples
243243
# (source start file, name, description, authors, manual section).
244-
man_pages = [("index", "napalm", u"NAPALM Documentation", [u"David Barroso"], 1)]
244+
man_pages = [("index", "napalm", "NAPALM Documentation", ["David Barroso"], 1)]
245245

246246
# If true, show URL addresses after external links.
247247
# man_show_urls = False
@@ -256,8 +256,8 @@
256256
(
257257
"index",
258258
"napalm",
259-
u"NAPALM Documentation",
260-
u"David Barroso",
259+
"NAPALM Documentation",
260+
"David Barroso",
261261
"napalm",
262262
"One line description of project.",
263263
"Miscellaneous",

docs/support/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ ____________________________________
133133
* :code:`secret` (ios, nxos_ssh) - Password required to enter privileged exec (enable) (default: ``''``).
134134
* :code:`ssh_config_file` (ios, iosxr, junos, nxos_ssh) - File name of OpenSSH configuration file.
135135
* :code:`ssh_strict` (ios, iosxr, nxos_ssh) - Automatically reject unknown SSH host keys (default: ``False``, which means unknown SSH host keys will be accepted).
136-
* :code:`ssl_verify` (nxos) - Requests argument, enable the SSL certificates verification. See requests ssl-cert-verification for valide values (default: ``None`` equivalent to ``False``).
136+
* :code:`ssl_verify` (nxos) - Requests argument, enable the SSL certificates verification. See requests ssl-cert-verification for valid values (default: ``None`` equivalent to ``False``).
137137
* :code:`transport` (eos, ios, nxos) - Protocol to connect with (see `The transport argument`_ for more information).
138138
* :code:`use_keys` (ios, iosxr, nxos_ssh) - Paramiko argument, enable searching for discoverable private key files in ``~/.ssh/`` (default: ``False``).
139139
* :code:`eos_autoComplete` (eos) - Allows to set `autoComplete` when running commands. (default: ``None`` equivalent to ``False``)

napalm/base/mock.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
114114
self.merge = None
115115
self.filename = None
116116
self.config = None
117+
self._pending_commits = False
117118

118119
def _count_calls(self, name):
119120
current_count = self.calls.get(name, 0)
@@ -168,9 +169,14 @@ def compare_config(self, filename=None, config=None):
168169
self._raise_if_closed()
169170
return mocked_data(self.path, "compare_config", count)["diff"]
170171

171-
def commit_config(self):
172+
def commit_config(self, message="", revert_in=None):
172173
count = self._count_calls("commit_config")
173174
self._raise_if_closed()
175+
if revert_in is not None:
176+
if self.has_pending_commit():
177+
raise napalm.CommitError("Pending commit confirm already in process!")
178+
else:
179+
self._pending_commits = True
174180
self.merge = None
175181
self.filename = None
176182
self.config = None
@@ -184,6 +190,22 @@ def discard_config(self):
184190
self.config = None
185191
mocked_data(self.path, "discard_config", count)
186192

193+
def confirm_commit(self):
194+
count = self._count_calls("confirm_commit")
195+
self._raise_if_closed()
196+
self.merge = None
197+
self.filename = None
198+
self.config = None
199+
self._pending_commits = False
200+
mocked_data(self.path, "confirm_commit", count)
201+
202+
def has_pending_commit(self):
203+
return self._pending_commits
204+
205+
def rollback(self):
206+
self.config_session = None
207+
self._pending_commits = False
208+
187209
def _rpc(self, get):
188210
"""This one is only useful for junos."""
189211
return list(self.cli([get]).values())[0]

napalm/base/test/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"description": str,
3232
"last_flapped": float,
3333
"mtu": int,
34-
"speed": int,
34+
"speed": float,
3535
"mac_address": str,
3636
},
3737
)

napalm/eos/eos.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,8 @@ def get_interfaces(self):
490490
)
491491

492492
interfaces[interface]["mtu"] = int(values["mtu"])
493-
interfaces[interface]["speed"] = int(values["bandwidth"] * 1e-6)
493+
# interfaces[interface]["speed"] = float(values["bandwidth"] * 1e-6)
494+
interfaces[interface]["speed"] = float(values["bandwidth"] / 1000000.0)
494495
interfaces[interface]["mac_address"] = napalm.base.helpers.convert(
495496
napalm.base.helpers.mac, values.pop("physicalAddress", "")
496497
)
@@ -1005,6 +1006,10 @@ def parse_options(options, default_value=False):
10051006
bgp_neighbors[peer_address] = default_neighbor_dict(local_as)
10061007
if options[0] == "peer-group":
10071008
bgp_neighbors[peer_address]["__group"] = options[1]
1009+
# EOS > 4.23.0 only supports the new syntax
1010+
# https://www.arista.com/en/support/advisories-notices/fieldnotices/7097-field-notice-39
1011+
elif options[0] == "peer" and options[1] == "group":
1012+
bgp_neighbors[peer_address]["__group"] = options[2]
10081013

10091014
# in the config, neighbor details are lister after
10101015
# the group is specified for the neighbor:

napalm/ios/ios.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,6 @@ def get_interfaces(self):
11321132
speed = speed / 1000.0
11331133
elif speedformat.startswith("Gb"):
11341134
speed = speed * 1000
1135-
speed = int(round(speed))
11361135

11371136
if interface == "":
11381137
raise ValueError(
@@ -1303,7 +1302,7 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
13031302
if "ipv6" in af_table.lower():
13041303
inet6 = True
13051304
preifx_type = "inet6"
1306-
if len(af_table.split()) == 2:
1305+
if not af_table or len(af_table.split()) == 2:
13071306
safi = "unicast"
13081307
else:
13091308
safi = af_table.split()[-1]
@@ -1355,7 +1354,11 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
13551354
afi_list = napalm.base.helpers.cisco_conf_parse_parents(
13561355
r"\s+address-family.*", bgp_neighbor, bgp_config_text
13571356
)
1358-
afi = afi_list[0]
1357+
try:
1358+
afi = afi_list[0]
1359+
except IndexError:
1360+
afi = ""
1361+
13591362
# Skipping neighbors in VRFs for now
13601363
if "vrf" in str(afi_list):
13611364
continue
@@ -3424,6 +3427,10 @@ def get_network_instances(self, name=""):
34243427
else:
34253428
return instances
34263429

3430+
if "Invalid input detected" in sh_vrf_detail:
3431+
# No VRF support
3432+
return instances
3433+
34273434
for vrf in sh_vrf_detail.split("\n\n"):
34283435

34293436
first_part = vrf.split("Address family")[0]
@@ -3595,7 +3602,7 @@ def _get_vlan_from_id(self):
35953602
for vlan_id, vlan_name in find_vlan:
35963603
output = self._send_command("show vlan id {}".format(vlan_id))
35973604
interface_regex = r"{}\s+{}\s+\S+\s+([A-Z][a-z].*)$".format(
3598-
vlan_id, vlan_name
3605+
vlan_id, re.escape(vlan_name)
35993606
)
36003607
interfaces = re.findall(interface_regex, output, re.MULTILINE)
36013608
if len(interfaces) == 1:

napalm/iosxr/iosxr.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def get_interfaces(self):
221221
"is_up": False,
222222
"mac_address": "",
223223
"description": "",
224-
"speed": -1,
224+
"speed": -1.0,
225225
"last_flapped": -1.0,
226226
}
227227

@@ -252,12 +252,13 @@ def get_interfaces(self):
252252
napalm.base.helpers.mac, raw_mac, raw_mac
253253
)
254254
speed = napalm.base.helpers.convert(
255-
int,
255+
float,
256256
napalm.base.helpers.convert(
257-
int, napalm.base.helpers.find_txt(interface_tree, "Bandwidth"), 0
257+
float, napalm.base.helpers.find_txt(interface_tree, "Bandwidth"), 0
258258
)
259259
* 1e-3,
260260
)
261+
261262
mtu = int(napalm.base.helpers.find_txt(interface_tree, "MTU"))
262263
description = napalm.base.helpers.find_txt(interface_tree, "Description")
263264
interfaces[interface_name] = copy.deepcopy(INTERFACE_DEFAULTS)

napalm/iosxr_netconf/iosxr_netconf.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
6969
if optional_args is None:
7070
optional_args = {}
7171

72+
self.netmiko_optional_args = optional_args
7273
self.port = optional_args.get("port", 830)
7374
self.lock_on_connect = optional_args.get("config_lock", False)
7475
self.key_file = optional_args.get("key_file", None)
@@ -91,6 +92,7 @@ def open(self):
9192
key_filename=self.key_file,
9293
timeout=self.timeout,
9394
device_params={"name": "iosxr"},
95+
**self.netmiko_optional_args,
9496
)
9597
if self.lock_on_connect:
9698
self._lock()
@@ -442,7 +444,7 @@ def get_interfaces(self):
442444
"is_up": False,
443445
"mac_address": "",
444446
"description": "",
445-
"speed": -1,
447+
"speed": -1.0,
446448
"last_flapped": -1.0,
447449
}
448450

@@ -489,9 +491,9 @@ def get_interfaces(self):
489491
napalm.base.helpers.mac, raw_mac, raw_mac
490492
)
491493
speed = napalm.base.helpers.convert(
492-
int,
494+
float,
493495
napalm.base.helpers.convert(
494-
int,
496+
float,
495497
self._find_txt(interface_tree, "./int:bandwidth", namespaces=C.NS),
496498
0,
497499
)

napalm/junos/junos.py

+20-7
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ def _load_candidate(self, filename, config, overwrite):
267267
ignore_warning=self.ignore_warning,
268268
)
269269
except ConfigLoadError as e:
270+
self.discard_config()
270271
if self.config_replace:
271272
raise ReplaceConfigException(e.errs)
272273
else:
@@ -284,7 +285,7 @@ def load_merge_candidate(self, filename=None, config=None):
284285

285286
def compare_config(self):
286287
"""Compare candidate config with running."""
287-
diff = self.device.cu.diff()
288+
diff = self.device.cu.diff(ignore_warning=self.ignore_warning)
288289

289290
if diff is None:
290291
return ""
@@ -383,7 +384,7 @@ def confirm_commit(self):
383384

384385
def discard_config(self):
385386
"""Discard changes (rollback 0)."""
386-
self.device.cu.rollback(rb_id=0)
387+
self.device.cu.rollback(rb_id=0, ignore_warning=self.ignore_warning)
387388
if not self.lock_disable and not self.session_config_lock:
388389
self._unlock()
389390
if self.config_private:
@@ -448,7 +449,7 @@ def _convert_to_dict(interfaces):
448449
iface_data["mac_address"],
449450
str(iface_data["mac_address"]),
450451
),
451-
"speed": -1,
452+
"speed": -1.0,
452453
"mtu": 0,
453454
}
454455
# result[iface]['last_flapped'] = float(result[iface]['last_flapped'])
@@ -463,12 +464,13 @@ def _convert_to_dict(interfaces):
463464
)
464465
if match is None:
465466
continue
466-
speed_value = napalm.base.helpers.convert(int, match.group(1), -1)
467-
if speed_value == -1:
467+
speed_value = napalm.base.helpers.convert(float, match.group(1), -1.0)
468+
469+
if speed_value == -1.0:
468470
continue
469471
speed_unit = match.group(2)
470472
if speed_unit.lower() == "gbps":
471-
speed_value *= 1000
473+
speed_value *= 1000.0
472474
result[iface]["speed"] = speed_value
473475

474476
return result
@@ -908,7 +910,18 @@ def get_lldp_neighbors(self):
908910
for neigh in result:
909911
if neigh[0] not in neighbors.keys():
910912
neighbors[neigh[0]] = []
911-
neighbors[neigh[0]].append({x[0]: str(x[1]) for x in neigh[1]})
913+
914+
neigh_dict = {}
915+
for neigh_data in neigh[1]:
916+
key = neigh_data[0]
917+
value = (
918+
str(neigh_data[1][0])
919+
# When return value is a list of multiple objects, we pick the first one
920+
if neigh_data[1] and isinstance(neigh_data[1], list)
921+
else str(neigh_data[1])
922+
)
923+
neigh_dict[key] = value
924+
neighbors[neigh[0]].append(neigh_dict)
912925

913926
return neighbors
914927

napalm/junos/utils/junos_views.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ junos_lldp_table:
104104
junos_lldp_view:
105105
fields:
106106
hostname: lldp-remote-system-name
107-
port: lldp-remote-port-description | lldp-remote-port-id
107+
port: lldp-remote-port-id | lldp-remote-port-description
108108

109109
####
110110
#### Interface counters

napalm/nxapi_plumbing/api_client.py

+16-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import requests
1010
from requests.auth import HTTPBasicAuth
1111
from requests.exceptions import ConnectionError
12+
from requests.packages.urllib3.exceptions import InsecureRequestWarning
1213
import json
1314

1415
from lxml import etree
@@ -60,6 +61,9 @@ def _send_request(self, commands, method):
6061
payload = self._build_payload(commands, method)
6162

6263
try:
64+
if not self.verify:
65+
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
66+
6367
response = requests.post(
6468
self.url,
6569
timeout=self.timeout,
@@ -78,15 +82,7 @@ def _send_request(self, commands, method):
7882
)
7983
raise NXAPIAuthError(msg)
8084

81-
if response.status_code not in [200]:
82-
msg = """Invalid status code returned on NX-API POST
83-
commands: {}
84-
status_code: {}""".format(
85-
commands, response.status_code
86-
)
87-
raise NXAPIPostError(msg)
88-
89-
return response.text
85+
return response
9086

9187

9288
class RPCClient(RPCBase):
@@ -139,7 +135,7 @@ def _process_api_response(self, response, commands, raw_text=False):
139135
structured data.
140136
"""
141137

142-
response_list = json.loads(response)
138+
response_list = json.loads(response.text)
143139
if isinstance(response_list, dict):
144140
response_list = [response_list]
145141

@@ -150,7 +146,7 @@ def _process_api_response(self, response, commands, raw_text=False):
150146
new_response = []
151147
for response in response_list:
152148

153-
# Dectect errors
149+
# Detect errors
154150
self._error_check(response)
155151

156152
# Some commands like "show run" can have a None result
@@ -235,7 +231,15 @@ def _build_payload(self, commands, method, xml_version="1.0", version="1.0"):
235231
return payload
236232

237233
def _process_api_response(self, response, commands, raw_text=False):
238-
xml_root = etree.fromstring(response)
234+
if response.status_code not in [200]:
235+
msg = """Invalid status code returned on NX-API POST
236+
commands: {}
237+
status_code: {}""".format(
238+
commands, response.status_code
239+
)
240+
raise NXAPIPostError(msg)
241+
242+
xml_root = etree.fromstring(response.text)
239243
response_list = xml_root.xpath("outputs/output")
240244
if len(commands) != len(response_list):
241245
raise NXAPIXMLError(

napalm/nxos/nxos.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ def get_interfaces(self):
891891
interface_speed = 0
892892
if isinstance(interface_speed, list):
893893
interface_speed = interface_speed[0]
894-
interface_speed = int(int(interface_speed) / 1000)
894+
interface_speed = float(float(interface_speed) / 1000.0)
895895

896896
if "admin_state" in interface_details:
897897
is_up = interface_details.get("admin_state", "") == "up"
@@ -1175,7 +1175,7 @@ def get_interfaces_ip(self):
11751175
ipv6_interf_table_vrf = self._get_command_table(
11761176
ipv6_command, "TABLE_intf", "ROW_intf"
11771177
)
1178-
except napalm.nxapi_plumbing.errors.NXAPIPostError:
1178+
except napalm.nxapi_plumbing.errors.NXAPICommandError:
11791179
return interfaces_ip
11801180

11811181
for interface in ipv6_interf_table_vrf:

0 commit comments

Comments
 (0)