Skip to content

Commit

Permalink
Merge pull request #1276 from napalm-automation/develop
Browse files Browse the repository at this point in the history
Release 3.2.0
  • Loading branch information
mirceaulinic authored Sep 3, 2020
2 parents 4af2aba + 965966d commit f195a34
Show file tree
Hide file tree
Showing 37 changed files with 7,099 additions and 148 deletions.
15 changes: 14 additions & 1 deletion napalm/eos/eos.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ def get_arp_table(self, vrf=""):
interface = str(neighbor.get("interface"))
mac_raw = neighbor.get("hwAddress")
ip = str(neighbor.get("address"))
age = float(neighbor.get("age"))
age = float(neighbor.get("age", -1.0))
arp_table.append(
{
"interface": interface,
Expand Down Expand Up @@ -1990,3 +1990,16 @@ def ping(
)
ping_dict["success"].update({"results": results_array})
return ping_dict

def get_vlans(self):
command = ["show vlan"]
output = self.device.run_commands(command, encoding="json")[0]["vlans"]

vlans = {}
for vlan, vlan_config in output.items():
vlans[vlan] = {
"name": vlan_config["name"],
"interfaces": list(vlan_config["interfaces"].keys()),
}

return vlans
4 changes: 2 additions & 2 deletions napalm/eos/pyeapi_syntax_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def update_cli_version(self, version):
"""
self.cli_version = version

def run_commands(self, commands, **kwargs):
def run_commands(self, commands, *args, **kwargs):
"""
Run commands wrapper
:param commands: list of commands
Expand All @@ -39,4 +39,4 @@ def run_commands(self, commands, **kwargs):
else:
commands = [cli_convert(cmd, self.cli_version) for cmd in commands]

return super(Node, self).run_commands(commands, **kwargs)
return super(Node, self).run_commands(commands, *args, **kwargs)
2 changes: 2 additions & 0 deletions napalm/eos/utils/cli_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"show dot1x all brief": "show dot1x all summary",
"show system environment all": "show environment all",
"show system environment cooling": "show environment cooling",
"show system environment temperature": "show environment temperature",
"show interfaces hardware": "show interfaces capabilities",
"show interfaces flow-control": "show interfaces flowcontrol",
"show pvlan mapping interfaces": "show interfaces private-vlan mapping",
Expand Down Expand Up @@ -244,6 +245,7 @@
"show dot1x all summary": "show dot1x all brief",
"show environment all": "show system environment all",
"show environment cooling": "show system environment cooling",
"show environment temperature": "show system environment temperature",
"show interfaces capabilities": "show interfaces hardware",
"show interfaces flowcontrol": "show interfaces flow-control",
"show interfaces private-vlan mapping": "show pvlan mapping interfaces",
Expand Down
49 changes: 36 additions & 13 deletions napalm/ios/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
RE_BGP_REMOTE_AS = re.compile(r"remote AS (" + ASN_REGEX + r")")
RE_BGP_AS_PATH = re.compile(r"^[ ]{2}([\d\(]([\d\) ]+)|Local)")

RE_RP_ROUTE = re.compile(r"Routing entry for (" + IP_ADDR_REGEX + r"\/\d+)")
RE_RP_FROM = re.compile(r"Known via \"([a-z]+)[ \"]")
RE_RP_VIA = re.compile(r"via (\S+)")
RE_RP_METRIC = re.compile(r"[ ]+Route metric is (\d+)")
Expand Down Expand Up @@ -2322,7 +2323,7 @@ def get_arp_table(self, vrf=""):

try:
if age == "-":
age = 0
age = -1
age = float(age)
except ValueError:
raise ValueError("Unable to convert age value to float: {}".format(age))
Expand Down Expand Up @@ -2696,18 +2697,22 @@ def process_mac_fields(vlan, mac, mac_type, interface):

def get_probes_config(self):
probes = {}

probes_regex = (
r"ip\s+sla\s+(?P<id>\d+)\n"
r"\s+(?P<probe_type>\S+)\s+(?P<probe_args>.*\n).*"
r"\s+tag\s+(?P<name>\S+)\n.*"
r"\s+history\s+buckets-kept\s+(?P<probe_count>\d+)\n.*"
r"\s+frequency\s+(?P<interval>\d+)$"
r"\s+(?P<probe_type>\S+)\s+(?P<probe_args>.*)\n"
r"\s+tag\s+(?P<name>[\S ]+)\n"
r"(\s+.*\n)*"
r"((\s+frequency\s+(?P<interval0>\d+)\n(\s+.*\n)*\s+history"
r"\s+buckets-kept\s+(?P<probe_count0>\d+))|(\s+history\s+buckets-kept"
r"\s+(?P<probe_count1>\d+)\n.*\s+frequency\s+(?P<interval1>\d+)))"
)

probe_args = {
"icmp-echo": r"^(?P<target>\S+)\s+source-(?:ip|interface)\s+(?P<source>\S+)$"
}
probe_type_map = {"icmp-echo": "icmp-ping"}
command = "show run | include ip sla [0-9]"
command = "show run | section ip sla [0-9]"
output = self._send_command(command)
for match in re.finditer(probes_regex, output, re.M):
probe = match.groupdict()
Expand All @@ -2723,8 +2728,8 @@ def get_probes_config(self):
"probe_type": probe_type_map[probe["probe_type"]],
"target": probe_data["target"],
"source": probe_data["source"],
"probe_count": int(probe["probe_count"]),
"test_interval": int(probe["interval"]),
"probe_count": int(probe["probe_count0"] or probe["probe_count1"]),
"test_interval": int(probe["interval0"] or probe["interval1"]),
}
}

Expand Down Expand Up @@ -2773,7 +2778,7 @@ def _get_bgp_route_attr(self, destination, vrf, next_hop, ip_version=4):

search_re_dict = {
"aspath": {
"re": r"[^|\\n][ ]{2}([\d\(\)]([\d\(\) ])*)",
"re": r"[^|\\n][ ]{2}([\d\(\)]([\d\(\) ])*|Local)",
"group": 1,
"default": "",
},
Expand Down Expand Up @@ -2947,8 +2952,11 @@ def get_route_to(self, destination="", protocol="", longer=False):
vrfs.append("default") # global VRF
ipnet_dest = IPNetwork(destination)
prefix = str(ipnet_dest.network)
netmask = str(ipnet_dest.netmask)
routes = {destination: []}
netmask = ""
routes = {}
if "/" in destination:
netmask = str(ipnet_dest.netmask)
routes = {destination: []}
commands = []
for _vrf in vrfs:
if _vrf == "default":
Expand All @@ -2969,6 +2977,14 @@ def get_route_to(self, destination="", protocol="", longer=False):
for (outitem, _vrf) in zip(output, vrfs): # for all VRFs
route_proto_regex = RE_RP_FROM.search(outitem)
if route_proto_regex:
route_match = destination
if netmask == "":
# Get the matching route for a non-exact lookup
route_match_regex = RE_RP_ROUTE.search(outitem)
if route_match_regex:
route_match = route_match_regex.group(1)
if route_match not in routes:
routes[route_match] = []
# routing protocol name (bgp, ospf, ...)
route_proto = route_proto_regex.group(1)
rdb = outitem.split("Routing Descriptor Blocks:")
Expand Down Expand Up @@ -3037,7 +3053,7 @@ def get_route_to(self, destination="", protocol="", longer=False):
nh_line_found = (
False # for next RT entry processing ...
)
routes[destination].append(route_entry)
routes[route_match].append(route_entry)
return routes

def get_snmp_information(self):
Expand Down Expand Up @@ -3405,7 +3421,14 @@ def get_network_instances(self, name=""):
if "No interfaces" in first_part:
interfaces = {}
else:
interfaces = {itf: {} for itf in if_regex.group(1).split()}
interfaces = {
canonical_interface_name(itf, {"Vl": "Vlan"}): {}
for itf in if_regex.group(1).split()
}

# remove interfaces in the VRF from the default VRF
for item in interfaces:
del instances["default"]["interfaces"]["interface"][item]

instances[vrf_name] = {
"name": vrf_name,
Expand Down
35 changes: 25 additions & 10 deletions napalm/iosxr/iosxr.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from napalm.base.exceptions import CommandTimeoutException

logger = logging.getLogger(__name__)
IP_RIBRoute = "IP_RIBRoute"


class IOSXRDriver(NetworkDriver):
Expand Down Expand Up @@ -1402,7 +1403,7 @@ def get_arp_table(self, vrf=""):
str, napalm.base.helpers.find_txt(arp_entry, ".//Address")
)
age = napalm.base.helpers.convert(
float, napalm.base.helpers.find_txt(arp_entry, ".//Age"), 0.0
float, napalm.base.helpers.find_txt(arp_entry, ".//Age"), -1.0
)
mac_raw = napalm.base.helpers.find_txt(arp_entry, ".//HardwareAddress")

Expand Down Expand Up @@ -1641,6 +1642,7 @@ def get_mac_address_table(self):
def get_route_to(self, destination="", protocol="", longer=False):

routes = {}
global IP_RIBRoute

if not isinstance(destination, str):
raise TypeError("Please specify a valid destination!")
Expand Down Expand Up @@ -1672,32 +1674,45 @@ def get_route_to(self, destination="", protocol="", longer=False):
"<Get><Operational><IPV6_RIB><VRFTable><VRF><Naming><VRFName>"
"default</VRFName></Naming><AFTable><AF><Naming><AFName>IPv6</AFName></Naming>"
"<SAFTable>"
"<SAF><Naming><SAFName>Unicast</SAFName></Naming><IP_RIBRouteTable><IP_RIBRoute>"
"<SAF><Naming><SAFName>Unicast</SAFName></Naming><IP_RIBRouteTable><{ipribroute}>"
"<Naming>"
"<RouteTableName>default</RouteTableName></Naming><RouteTable><Route><Naming>"
"<Address>"
"{network}</Address>{prefix}</Naming></Route></RouteTable></IP_RIBRoute>"
"{network}</Address>{prefix}</Naming></Route></RouteTable></{ipribroute}>"
"</IP_RIBRouteTable></SAF></SAFTable></AF></AFTable></VRF></VRFTable></IPV6_RIB>"
"</Operational></Get>"
).format(network=network, prefix=prefix_tag)
).format(network=network, prefix=prefix_tag, ipribroute=IP_RIBRoute)
else:
route_info_rpc_command = (
"<Get><Operational><RIB><VRFTable><VRF><Naming><VRFName>"
"default"
"</VRFName></Naming><AFTable><AF><Naming><AFName>IPv4</AFName></Naming>"
"<SAFTable><SAF>"
"<Naming><SAFName>Unicast</SAFName></Naming><IP_RIBRouteTable><IP_RIBRoute>"
"<Naming><SAFName>Unicast</SAFName></Naming><IP_RIBRouteTable><{ipribroute}>"
"<Naming>"
"<RouteTableName>default</RouteTableName></Naming><RouteTable><Route><Naming>"
"<Address>"
"{network}</Address>{prefix}</Naming></Route></RouteTable></IP_RIBRoute>"
"{network}</Address>{prefix}</Naming></Route></RouteTable></{ipribroute}>"
"</IP_RIBRouteTable>"
"</SAF></SAFTable></AF></AFTable></VRF></VRFTable></RIB></Operational></Get>"
).format(network=network, prefix=prefix_tag)
).format(network=network, prefix=prefix_tag, ipribroute=IP_RIBRoute)

routes_tree = ETREE.fromstring(
self.device.make_rpc_call(route_info_rpc_command)
)
try:
routes_tree = ETREE.fromstring(
self.device.make_rpc_call(route_info_rpc_command)
)
except Exception:
pass
# Some versions of IOS-XR use IP_RIBRouteTableName instead of IP_RIBRoute.
# If IP_RIBRoute throws an exception, try again with IP_RIBRouteTableName
# and have subsequent get_route_to calls use that.
IP_RIBRoute = "IP_RIBRouteTableName"
route_info_rpc_command = route_info_rpc_command.replace(
"IP_RIBRoute>", "{ipribroute}>".format(ipribroute=IP_RIBRoute)
)
routes_tree = ETREE.fromstring(
self.device.make_rpc_call(route_info_rpc_command)
)

for route in routes_tree.xpath(".//Route"):
route_protocol = napalm.base.helpers.convert(
Expand Down
97 changes: 97 additions & 0 deletions napalm/nxos_ssh/nxos_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -1641,3 +1641,100 @@ def get_optics(self):
optics_detail[port] = port_detail

return optics_detail

def get_interfaces_counters(self):
"""
Return interface counters and errors.
'tx_errors': int,
'rx_errors': int,
'tx_discards': int,
'rx_discards': int,
'tx_octets': int,
'rx_octets': int,
'tx_unicast_packets': int,
'rx_unicast_packets': int,
'tx_multicast_packets': int,
'rx_multicast_packets': int,
'tx_broadcast_packets': int,
'rx_broadcast_packets': int,
"""
if_mapping = {
"eth": {
"regexp": re.compile("^(Ether|port-channel).*"),
"mapping": {
"tx_errors": "eth_outerr",
"rx_errors": "eth_inerr",
"tx_discards": "eth_outdiscard",
"rx_discards": "eth_indiscard",
"tx_octets": "eth_outbytes",
"rx_octets": "eth_inbytes",
"tx_unicast_packets": "eth_outucast",
"rx_unicast_packets": "eth_inucast",
"tx_multicast_packets": "eth_outmcast",
"rx_multicast_packets": "eth_inmcast",
"tx_broadcast_packets": "eth_outbcast",
"rx_broadcast_packets": "eth_inbcast",
},
},
"mgmt": {
"regexp": re.compile("mgm.*"),
"mapping": {
"tx_errors": None,
"rx_errors": None,
"tx_discards": None,
"rx_discards": None,
"tx_octets": "mgmt_out_bytes",
"rx_octets": "mgmt_in_bytes",
"tx_unicast_packets": None,
"rx_unicast_packets": None,
"tx_multicast_packets": "mgmt_out_mcast",
"rx_multicast_packets": "mgmt_in_mcast",
"tx_broadcast_packets": None,
"rx_broadcast_packets": None,
},
},
}
command = "show interface counters detailed | json"
# To retrieve discards
command_interface = "show interface | json"
counters_table_raw = self._get_command_table(
command, "TABLE_interface", "ROW_interface"
)
counters_interface_table_raw = self._get_command_table(
command_interface, "TABLE_interface", "ROW_interface"
)
all_stats_d = {}
# Start with show interface as all interfaces
# Are surely listed
for row in counters_interface_table_raw:
if_counter = {}
# loop through regexp to find mapping
for if_v in if_mapping:
my_re = if_mapping[if_v]["regexp"]
re_match = my_re.match(row["interface"])
if re_match:
interface = re_match.group()
map_d = if_mapping[if_v]["mapping"]
for k, v in map_d.items():
if_counter[k] = int(row[v]) if v in row else 0
all_stats_d[interface] = if_counter
break
print(all_stats_d)

for row in counters_table_raw:
if_counter = {}
# loop through regexp to find mapping
for if_v in if_mapping:
my_re = if_mapping[if_v]["regexp"]
re_match = my_re.match(row["interface"])
if re_match:
interface = re_match.group()
map_d = if_mapping[if_v]["mapping"]
for k, v in map_d.items():
if v in row:
if_counter[k] = int(row[v])
all_stats_d[interface].update(if_counter)
break

return all_stats_d
8 changes: 4 additions & 4 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
black==19.10b0
coveralls==2.1.1
black==20.8b1
coveralls==2.1.2
ddt==1.4.1
flake8-import-order==0.18.1
pytest==5.4.3
pytest-cov==2.10.0
pytest-cov==2.10.1
pytest-json==0.4.0
pytest-pythonpath==0.7.3
pylama==7.7.1
mock==4.0.2
tox==3.18.0
tox==3.20.0
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@

setup(
name="napalm",
version="3.1.0",
version="3.2.0",
packages=find_packages(exclude=("test*",)),
test_suite="test_base",
author="David Barroso, Kirk Byers, Mircea Ulinic",
author_email="[email protected], [email protected], [email protected]",
description="Network Automation and Programmability Abstraction Layer with Multivendor support",
license="Apache 2.0",
long_description=long_description,
long_description_content_type="text/markdown",
classifiers=[
"Topic :: Utilities",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"interface": "Ethernet45", "ip": "172.17.17.1", "mac": "DC:38:E1:11:97:CF", "age": -1.0}, {"interface": "Ethernet36", "ip": "172.17.17.1", "mac": "90:E2:BA:5C:25:FD", "age": 0.0}]
Loading

0 comments on commit f195a34

Please sign in to comment.