Skip to content

Commit

Permalink
Merge pull request #1559 from napalm-automation/develop
Browse files Browse the repository at this point in the history
Merge forward to 3.4.0
  • Loading branch information
mirceaulinic committed Feb 12, 2022
2 parents 524a086 + 6d743ea commit 9199164
Show file tree
Hide file tree
Showing 49 changed files with 2,894 additions and 451 deletions.
8 changes: 4 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
("index", "napalm.tex", u"NAPALM Documentation", u"David Barroso", "manual")
("index", "napalm.tex", "NAPALM Documentation", "David Barroso", "manual")
]

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

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

# If true, show URL addresses after external links.
# man_show_urls = False
Expand All @@ -256,8 +256,8 @@
(
"index",
"napalm",
u"NAPALM Documentation",
u"David Barroso",
"NAPALM Documentation",
"David Barroso",
"napalm",
"One line description of project.",
"Miscellaneous",
Expand Down
2 changes: 1 addition & 1 deletion docs/support/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ ____________________________________
* :code:`secret` (ios, nxos_ssh) - Password required to enter privileged exec (enable) (default: ``''``).
* :code:`ssh_config_file` (ios, iosxr, junos, nxos_ssh) - File name of OpenSSH configuration file.
* :code:`ssh_strict` (ios, iosxr, nxos_ssh) - Automatically reject unknown SSH host keys (default: ``False``, which means unknown SSH host keys will be accepted).
* :code:`ssl_verify` (nxos) - Requests argument, enable the SSL certificates verification. See requests ssl-cert-verification for valide values (default: ``None`` equivalent to ``False``).
* :code:`ssl_verify` (nxos) - Requests argument, enable the SSL certificates verification. See requests ssl-cert-verification for valid values (default: ``None`` equivalent to ``False``).
* :code:`transport` (eos, ios, nxos) - Protocol to connect with (see `The transport argument`_ for more information).
* :code:`use_keys` (ios, iosxr, nxos_ssh) - Paramiko argument, enable searching for discoverable private key files in ``~/.ssh/`` (default: ``False``).
* :code:`eos_autoComplete` (eos) - Allows to set `autoComplete` when running commands. (default: ``None`` equivalent to ``False``)
Expand Down
24 changes: 23 additions & 1 deletion napalm/base/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
self.merge = None
self.filename = None
self.config = None
self._pending_commits = False

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

def commit_config(self):
def commit_config(self, message="", revert_in=None):
count = self._count_calls("commit_config")
self._raise_if_closed()
if revert_in is not None:
if self.has_pending_commit():
raise napalm.CommitError("Pending commit confirm already in process!")
else:
self._pending_commits = True
self.merge = None
self.filename = None
self.config = None
Expand All @@ -184,6 +190,22 @@ def discard_config(self):
self.config = None
mocked_data(self.path, "discard_config", count)

def confirm_commit(self):
count = self._count_calls("confirm_commit")
self._raise_if_closed()
self.merge = None
self.filename = None
self.config = None
self._pending_commits = False
mocked_data(self.path, "confirm_commit", count)

def has_pending_commit(self):
return self._pending_commits

def rollback(self):
self.config_session = None
self._pending_commits = False

def _rpc(self, get):
"""This one is only useful for junos."""
return list(self.cli([get]).values())[0]
Expand Down
2 changes: 1 addition & 1 deletion napalm/base/test/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"description": str,
"last_flapped": float,
"mtu": int,
"speed": int,
"speed": float,
"mac_address": str,
},
)
Expand Down
7 changes: 6 additions & 1 deletion napalm/eos/eos.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,8 @@ def get_interfaces(self):
)

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

# in the config, neighbor details are lister after
# the group is specified for the neighbor:
Expand Down
15 changes: 11 additions & 4 deletions napalm/ios/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,6 @@ def get_interfaces(self):
speed = speed / 1000.0
elif speedformat.startswith("Gb"):
speed = speed * 1000
speed = int(round(speed))

if interface == "":
raise ValueError(
Expand Down Expand Up @@ -1303,7 +1302,7 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
if "ipv6" in af_table.lower():
inet6 = True
preifx_type = "inet6"
if len(af_table.split()) == 2:
if not af_table or len(af_table.split()) == 2:
safi = "unicast"
else:
safi = af_table.split()[-1]
Expand Down Expand Up @@ -1355,7 +1354,11 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
afi_list = napalm.base.helpers.cisco_conf_parse_parents(
r"\s+address-family.*", bgp_neighbor, bgp_config_text
)
afi = afi_list[0]
try:
afi = afi_list[0]
except IndexError:
afi = ""

# Skipping neighbors in VRFs for now
if "vrf" in str(afi_list):
continue
Expand Down Expand Up @@ -3424,6 +3427,10 @@ def get_network_instances(self, name=""):
else:
return instances

if "Invalid input detected" in sh_vrf_detail:
# No VRF support
return instances

for vrf in sh_vrf_detail.split("\n\n"):

first_part = vrf.split("Address family")[0]
Expand Down Expand Up @@ -3595,7 +3602,7 @@ def _get_vlan_from_id(self):
for vlan_id, vlan_name in find_vlan:
output = self._send_command("show vlan id {}".format(vlan_id))
interface_regex = r"{}\s+{}\s+\S+\s+([A-Z][a-z].*)$".format(
vlan_id, vlan_name
vlan_id, re.escape(vlan_name)
)
interfaces = re.findall(interface_regex, output, re.MULTILINE)
if len(interfaces) == 1:
Expand Down
7 changes: 4 additions & 3 deletions napalm/iosxr/iosxr.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def get_interfaces(self):
"is_up": False,
"mac_address": "",
"description": "",
"speed": -1,
"speed": -1.0,
"last_flapped": -1.0,
}

Expand Down Expand Up @@ -252,12 +252,13 @@ def get_interfaces(self):
napalm.base.helpers.mac, raw_mac, raw_mac
)
speed = napalm.base.helpers.convert(
int,
float,
napalm.base.helpers.convert(
int, napalm.base.helpers.find_txt(interface_tree, "Bandwidth"), 0
float, napalm.base.helpers.find_txt(interface_tree, "Bandwidth"), 0
)
* 1e-3,
)

mtu = int(napalm.base.helpers.find_txt(interface_tree, "MTU"))
description = napalm.base.helpers.find_txt(interface_tree, "Description")
interfaces[interface_name] = copy.deepcopy(INTERFACE_DEFAULTS)
Expand Down
8 changes: 5 additions & 3 deletions napalm/iosxr_netconf/iosxr_netconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
if optional_args is None:
optional_args = {}

self.netmiko_optional_args = optional_args
self.port = optional_args.get("port", 830)
self.lock_on_connect = optional_args.get("config_lock", False)
self.key_file = optional_args.get("key_file", None)
Expand All @@ -91,6 +92,7 @@ def open(self):
key_filename=self.key_file,
timeout=self.timeout,
device_params={"name": "iosxr"},
**self.netmiko_optional_args,
)
if self.lock_on_connect:
self._lock()
Expand Down Expand Up @@ -442,7 +444,7 @@ def get_interfaces(self):
"is_up": False,
"mac_address": "",
"description": "",
"speed": -1,
"speed": -1.0,
"last_flapped": -1.0,
}

Expand Down Expand Up @@ -489,9 +491,9 @@ def get_interfaces(self):
napalm.base.helpers.mac, raw_mac, raw_mac
)
speed = napalm.base.helpers.convert(
int,
float,
napalm.base.helpers.convert(
int,
float,
self._find_txt(interface_tree, "./int:bandwidth", namespaces=C.NS),
0,
)
Expand Down
27 changes: 20 additions & 7 deletions napalm/junos/junos.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ def _load_candidate(self, filename, config, overwrite):
ignore_warning=self.ignore_warning,
)
except ConfigLoadError as e:
self.discard_config()
if self.config_replace:
raise ReplaceConfigException(e.errs)
else:
Expand All @@ -284,7 +285,7 @@ def load_merge_candidate(self, filename=None, config=None):

def compare_config(self):
"""Compare candidate config with running."""
diff = self.device.cu.diff()
diff = self.device.cu.diff(ignore_warning=self.ignore_warning)

if diff is None:
return ""
Expand Down Expand Up @@ -383,7 +384,7 @@ def confirm_commit(self):

def discard_config(self):
"""Discard changes (rollback 0)."""
self.device.cu.rollback(rb_id=0)
self.device.cu.rollback(rb_id=0, ignore_warning=self.ignore_warning)
if not self.lock_disable and not self.session_config_lock:
self._unlock()
if self.config_private:
Expand Down Expand Up @@ -448,7 +449,7 @@ def _convert_to_dict(interfaces):
iface_data["mac_address"],
str(iface_data["mac_address"]),
),
"speed": -1,
"speed": -1.0,
"mtu": 0,
}
# result[iface]['last_flapped'] = float(result[iface]['last_flapped'])
Expand All @@ -463,12 +464,13 @@ def _convert_to_dict(interfaces):
)
if match is None:
continue
speed_value = napalm.base.helpers.convert(int, match.group(1), -1)
if speed_value == -1:
speed_value = napalm.base.helpers.convert(float, match.group(1), -1.0)

if speed_value == -1.0:
continue
speed_unit = match.group(2)
if speed_unit.lower() == "gbps":
speed_value *= 1000
speed_value *= 1000.0
result[iface]["speed"] = speed_value

return result
Expand Down Expand Up @@ -908,7 +910,18 @@ def get_lldp_neighbors(self):
for neigh in result:
if neigh[0] not in neighbors.keys():
neighbors[neigh[0]] = []
neighbors[neigh[0]].append({x[0]: str(x[1]) for x in neigh[1]})

neigh_dict = {}
for neigh_data in neigh[1]:
key = neigh_data[0]
value = (
str(neigh_data[1][0])
# When return value is a list of multiple objects, we pick the first one
if neigh_data[1] and isinstance(neigh_data[1], list)
else str(neigh_data[1])
)
neigh_dict[key] = value
neighbors[neigh[0]].append(neigh_dict)

return neighbors

Expand Down
2 changes: 1 addition & 1 deletion napalm/junos/utils/junos_views.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ junos_lldp_table:
junos_lldp_view:
fields:
hostname: lldp-remote-system-name
port: lldp-remote-port-description | lldp-remote-port-id
port: lldp-remote-port-id | lldp-remote-port-description

####
#### Interface counters
Expand Down
28 changes: 16 additions & 12 deletions napalm/nxapi_plumbing/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import requests
from requests.auth import HTTPBasicAuth
from requests.exceptions import ConnectionError
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import json

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

try:
if not self.verify:
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

response = requests.post(
self.url,
timeout=self.timeout,
Expand All @@ -78,15 +82,7 @@ def _send_request(self, commands, method):
)
raise NXAPIAuthError(msg)

if response.status_code not in [200]:
msg = """Invalid status code returned on NX-API POST
commands: {}
status_code: {}""".format(
commands, response.status_code
)
raise NXAPIPostError(msg)

return response.text
return response


class RPCClient(RPCBase):
Expand Down Expand Up @@ -139,7 +135,7 @@ def _process_api_response(self, response, commands, raw_text=False):
structured data.
"""

response_list = json.loads(response)
response_list = json.loads(response.text)
if isinstance(response_list, dict):
response_list = [response_list]

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

# Dectect errors
# Detect errors
self._error_check(response)

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

def _process_api_response(self, response, commands, raw_text=False):
xml_root = etree.fromstring(response)
if response.status_code not in [200]:
msg = """Invalid status code returned on NX-API POST
commands: {}
status_code: {}""".format(
commands, response.status_code
)
raise NXAPIPostError(msg)

xml_root = etree.fromstring(response.text)
response_list = xml_root.xpath("outputs/output")
if len(commands) != len(response_list):
raise NXAPIXMLError(
Expand Down
4 changes: 2 additions & 2 deletions napalm/nxos/nxos.py
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ def get_interfaces(self):
interface_speed = 0
if isinstance(interface_speed, list):
interface_speed = interface_speed[0]
interface_speed = int(int(interface_speed) / 1000)
interface_speed = float(float(interface_speed) / 1000.0)

if "admin_state" in interface_details:
is_up = interface_details.get("admin_state", "") == "up"
Expand Down Expand Up @@ -1175,7 +1175,7 @@ def get_interfaces_ip(self):
ipv6_interf_table_vrf = self._get_command_table(
ipv6_command, "TABLE_intf", "ROW_intf"
)
except napalm.nxapi_plumbing.errors.NXAPIPostError:
except napalm.nxapi_plumbing.errors.NXAPICommandError:
return interfaces_ip

for interface in ipv6_interf_table_vrf:
Expand Down

0 comments on commit 9199164

Please sign in to comment.