Merge pull request #32 from bradmwalker/napalm3

Napalm 3 Compatibility
This commit is contained in:
Piotr Pieprzycki 2020-05-17 23:46:12 +02:00 committed by GitHub
commit fe3d5560ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 72 deletions

View File

@ -1,6 +1,5 @@
language: python language: python
python: python:
- 2.7
- 3.6 - 3.6
- 3.7 - 3.7
- 3.8 - 3.8

View File

@ -32,7 +32,6 @@ from netmiko import SCPConn
# NAPALM base # NAPALM base
import napalm.base.constants as C import napalm.base.constants as C
from napalm.base.utils import py23_compat
from napalm.base.base import NetworkDriver from napalm.base.base import NetworkDriver
from napalm.base.exceptions import ConnectionException, MergeConfigException, \ from napalm.base.exceptions import ConnectionException, MergeConfigException, \
ReplaceConfigException, CommitError, \ ReplaceConfigException, CommitError, \
@ -179,7 +178,7 @@ class VyOSDriver(NetworkDriver):
self.device.send_command("cp "+self._BOOT_FILENAME+" " self.device.send_command("cp "+self._BOOT_FILENAME+" "
+ self._BACKUP_FILENAME) + self._BACKUP_FILENAME)
self._new_config = f.read() self._new_config = f.read()
cfg = [x for x in self._new_config.split("\n") if x is not ""] cfg = [x for x in self._new_config.split("\n") if x]
output_loadcmd = self.device.send_config_set(cfg) output_loadcmd = self.device.send_config_set(cfg)
match_setfailed = re.findall("Delete failed", output_loadcmd) match_setfailed = re.findall("Delete failed", output_loadcmd)
match_delfailed = re.findall("Set failed", output_loadcmd) match_delfailed = re.findall("Set failed", output_loadcmd)
@ -309,7 +308,7 @@ class VyOSDriver(NetworkDriver):
output_iface = self.device.send_command("show interfaces") output_iface = self.device.send_command("show interfaces")
# Collect all interfaces' name and status # Collect all interfaces' name and status
match = re.findall("(\S+)\s+[:\-\d/\.]+\s+([uAD])/([uAD])", output_iface) match = re.findall(r"(\S+)\s+[:\-\d/\.]+\s+([uAD])/([uAD])", output_iface)
# 'match' example: # 'match' example:
# [("br0", "u", "D"), ("eth0", "u", "u"), ("eth1", "u", "u")...] # [("br0", "u", "D"), ("eth0", "u", "u"), ("eth1", "u", "u")...]
@ -328,17 +327,12 @@ class VyOSDriver(NetworkDriver):
ifaces_detail = config["interfaces"][iface_type] ifaces_detail = config["interfaces"][iface_type]
for iface_name in ifaces_detail: for iface_name in ifaces_detail:
description = self._get_value("description", ifaces_detail[iface_name]) details = ifaces_detail[iface_name]
if description is None: description = details.get("description", "")
description = "" speed = details.get("speed", "0")
speed = self._get_value("speed", ifaces_detail[iface_name])
if speed is None:
speed = 0
if speed == "auto": if speed == "auto":
speed = 0 speed = 0
hw_id = self._get_value("hw-id", ifaces_detail[iface_name]) hw_id = details.get("hw-id", "00:00:00:00:00:00")
if hw_id is None:
hw_id = "00:00:00:00:00:00"
is_up = (iface_state[iface_name]["Link"] == "u") is_up = (iface_state[iface_name]["Link"] == "u")
is_enabled = (iface_state[iface_name]["State"] == "u") is_enabled = (iface_state[iface_name]["State"] == "u")
@ -347,23 +341,16 @@ class VyOSDriver(NetworkDriver):
iface_name: { iface_name: {
"is_up": bool(is_up), "is_up": bool(is_up),
"is_enabled": bool(is_enabled), "is_enabled": bool(is_enabled),
"description": py23_compat.text_type(description), "description": description,
"last_flapped": float(-1), "last_flapped": float(-1),
"mtu": -1, "mtu": -1,
"speed": int(speed), "speed": int(speed),
"mac_address": py23_compat.text_type(hw_id) "mac_address": hw_id,
} }
}) })
return iface_dict return iface_dict
@staticmethod
def _get_value(key, target_dict):
if key in target_dict:
return target_dict[key]
else:
return None
def get_arp_table(self, vrf=""): def get_arp_table(self, vrf=""):
# 'age' is not implemented yet # 'age' is not implemented yet
@ -377,7 +364,7 @@ class VyOSDriver(NetworkDriver):
192.168.1.3 ether 00:50:56:86:7b:06 C eth1 192.168.1.3 ether 00:50:56:86:7b:06 C eth1
""" """
if vrf: if vrf:
raise NotImplementedError( raise NotImplementedError(
"VRF support has not been added for this getter on this platform." "VRF support has not been added for this getter on this platform."
) )
@ -396,16 +383,16 @@ class VyOSDriver(NetworkDriver):
# ["10.129.2.254", "ether", "00:50:56:97:af:b1", "C", "eth0"] # ["10.129.2.254", "ether", "00:50:56:97:af:b1", "C", "eth0"]
# [u'10.0.12.33', u'(incomplete)', u'eth1'] # [u'10.0.12.33', u'(incomplete)', u'eth1']
if "incomplete" in line[1]: if "incomplete" in line[1]:
macaddr = py23_compat.text_type("00:00:00:00:00:00") macaddr = "00:00:00:00:00:00"
else: else:
macaddr = py23_compat.text_type(line[2]) macaddr = line[2]
arp_table.append( arp_table.append(
{ {
'interface': py23_compat.text_type(line[-1]), 'interface': line[-1],
'mac': macaddr, 'mac': macaddr,
'ip': py23_compat.text_type(line[0]), 'ip': line[0],
'age': 0.0 'age': 0.0,
} }
) )
@ -433,23 +420,23 @@ class VyOSDriver(NetworkDriver):
# 'remote' contains '*' if the machine synchronized with NTP server # 'remote' contains '*' if the machine synchronized with NTP server
synchronized = "*" in remote synchronized = "*" in remote
match = re.search("(\d+\.\d+\.\d+\.\d+)", remote) match = re.search(r"(\d+\.\d+\.\d+\.\d+)", remote)
ip = match.group(1) ip = match.group(1)
when = when if when != '-' else 0 when = when if when != '-' else 0
ntp_stats.append({ ntp_stats.append({
"remote": py23_compat.text_type(ip), "remote": ip,
"referenceid": py23_compat.text_type(refid), "referenceid": refid,
"synchronized": bool(synchronized), "synchronized": bool(synchronized),
"stratum": int(st), "stratum": int(st),
"type": py23_compat.text_type(t), "type": t,
"when": py23_compat.text_type(when), "when": when,
"hostpoll": int(hostpoll), "hostpoll": int(hostpoll),
"reachability": int(reachability), "reachability": int(reachability),
"delay": float(delay), "delay": float(delay),
"offset": float(offset), "offset": float(offset),
"jitter": float(jitter) "jitter": float(jitter),
}) })
return ntp_stats return ntp_stats
@ -461,9 +448,9 @@ class VyOSDriver(NetworkDriver):
for line in output_peers: for line in output_peers:
if len(line) > 0: if len(line) > 0:
match = re.search("(\d+\.\d+\.\d+\.\d+)\s+", line) match = re.search(r"(\d+\.\d+\.\d+\.\d+)\s+", line)
ntp_peers.update({ ntp_peers.update({
py23_compat.text_type(match.group(1)): {} match.group(1): {}
}) })
return ntp_peers return ntp_peers
@ -487,11 +474,11 @@ class VyOSDriver(NetworkDriver):
output = self.device.send_command("show ip bgp summary") output = self.device.send_command("show ip bgp summary")
output = output.split("\n") output = output.split("\n")
match = re.search(".* router identifier (\d+\.\d+\.\d+\.\d+), local AS number (\d+)", match = re.search(r".* router identifier (\d+\.\d+\.\d+\.\d+), local AS number (\d+)",
output[0]) output[0])
if not match: if not match:
return {} return {}
router_id = py23_compat.text_type(match.group(1)) router_id = match.group(1)
local_as = int(match.group(2)) local_as = int(match.group(2))
bgp_neighbor_data = dict() bgp_neighbor_data = dict()
@ -500,7 +487,7 @@ class VyOSDriver(NetworkDriver):
bgp_neighbor_data["global"]["peers"] = {} bgp_neighbor_data["global"]["peers"] = {}
# delete the header and empty element # delete the header and empty element
bgp_info = [i.strip() for i in output[6:-2] if i is not ""] bgp_info = [i.strip() for i in output[6:-2] if i]
for i in bgp_info: for i in bgp_info:
if len(i) > 0: if len(i) > 0:
@ -539,19 +526,19 @@ class VyOSDriver(NetworkDriver):
""" """
bgp_detail = self.device.send_command("show ip bgp neighbors %s" % peer_id) bgp_detail = self.device.send_command("show ip bgp neighbors %s" % peer_id)
match_rid = re.search("remote router ID (\d+\.\d+\.\d+\.\d+).*", bgp_detail) match_rid = re.search(r"remote router ID (\d+\.\d+\.\d+\.\d+).*", bgp_detail)
remote_rid = match_rid.group(1) remote_rid = match_rid.group(1)
match_prefix_accepted = re.search("(\d+) accepted prefixes", bgp_detail) match_prefix_accepted = re.search(r"(\d+) accepted prefixes", bgp_detail)
accepted_prefixes = match_prefix_accepted.group(1) accepted_prefixes = match_prefix_accepted.group(1)
bgp_neighbor_data["global"]["peers"].setdefault(peer_id, {}) bgp_neighbor_data["global"]["peers"].setdefault(peer_id, {})
peer_dict = { peer_dict = {
"description": py23_compat.text_type(""), "description": "",
"is_enabled": bool(is_enabled), "is_enabled": bool(is_enabled),
"local_as": int(local_as), "local_as": int(local_as),
"is_up": bool(is_up), "is_up": bool(is_up),
"remote_id": py23_compat.text_type(remote_rid), "remote_id": remote_rid,
"uptime": int(self._bgp_time_conversion(up_time)), "uptime": int(self._bgp_time_conversion(up_time)),
"remote_as": int(remote_as) "remote_as": int(remote_as)
} }
@ -575,19 +562,19 @@ class VyOSDriver(NetworkDriver):
return -1 return -1
else: else:
if "y" in bgp_uptime: if "y" in bgp_uptime:
match = re.search("(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime) match = re.search(r"(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime)
uptime = ((int(match.group(1)) * self._YEAR_SECONDS) + uptime = ((int(match.group(1)) * self._YEAR_SECONDS) +
(int(match.group(3)) * self._WEEK_SECONDS) + (int(match.group(3)) * self._WEEK_SECONDS) +
(int(match.group(5)) * self._DAY_SECONDS)) (int(match.group(5)) * self._DAY_SECONDS))
return uptime return uptime
elif "w" in bgp_uptime: elif "w" in bgp_uptime:
match = re.search("(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime) match = re.search(r"(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime)
uptime = ((int(match.group(1)) * self._WEEK_SECONDS) + uptime = ((int(match.group(1)) * self._WEEK_SECONDS) +
(int(match.group(3)) * self._DAY_SECONDS) + (int(match.group(3)) * self._DAY_SECONDS) +
(int(match.group(5)) * self._HOUR_SECONDS)) (int(match.group(5)) * self._HOUR_SECONDS))
return uptime return uptime
elif "d" in bgp_uptime: elif "d" in bgp_uptime:
match = re.search("(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime) match = re.search(r"(\d+)(\w)(\d+)(\w)(\d+)(\w)", bgp_uptime)
uptime = ((int(match.group(1)) * self._DAY_SECONDS) + uptime = ((int(match.group(1)) * self._DAY_SECONDS) +
(int(match.group(3)) * self._HOUR_SECONDS) + (int(match.group(3)) * self._HOUR_SECONDS) +
(int(match.group(5)) * self._MINUTE_SECONDS)) (int(match.group(5)) * self._MINUTE_SECONDS))
@ -637,9 +624,9 @@ class VyOSDriver(NetworkDriver):
32776498 279273 0 0 0 0 32776498 279273 0 0 0 0
""" """
output = self.device.send_command("show interfaces detail") output = self.device.send_command("show interfaces detail")
interfaces = re.findall("(\S+): <.*", output) interfaces = re.findall(r"(\S+): <.*", output)
# count = re.findall("(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+", output) # count = re.findall("(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+", output)
count = re.findall("(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output) count = re.findall(r"(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output)
counters = dict() counters = dict()
j = 0 j = 0
@ -687,15 +674,15 @@ class VyOSDriver(NetworkDriver):
for i in config["service"]["snmp"]["community"]: for i in config["service"]["snmp"]["community"]:
snmp["community"].update({ snmp["community"].update({
i: { i: {
"acl": py23_compat.text_type(""), "acl": "",
"mode": py23_compat.text_type(config["service"]["snmp"]["community"][i]["authorization"]) "mode": config["service"]["snmp"]["community"][i]["authorization"],
} }
}) })
snmp.update({ snmp.update({
"chassis_id": py23_compat.text_type(""), "chassis_id": "",
"contact": py23_compat.text_type(config["service"]["snmp"]["contact"]), "contact": config["service"]["snmp"]["contact"],
"location": py23_compat.text_type(config["service"]["snmp"]["location"]) "location": config["service"]["snmp"]["location"],
}) })
return snmp return snmp
@ -737,13 +724,13 @@ class VyOSDriver(NetworkDriver):
facts = { facts = {
"uptime": int(uptime), "uptime": int(uptime),
"vendor": py23_compat.text_type("VyOS"), "vendor": "VyOS",
"os_version": py23_compat.text_type(version), "os_version": version,
"serial_number": py23_compat.text_type(snumber), "serial_number": snumber,
"model": py23_compat.text_type(hwmodel), "model": hwmodel,
"hostname": py23_compat.text_type(hostname), "hostname": hostname,
"fqdn": py23_compat.text_type(fqdn), "fqdn": fqdn,
"interface_list": iface_list "interface_list": iface_list,
} }
return facts return facts
@ -874,7 +861,7 @@ class VyOSDriver(NetworkDriver):
else: else:
err = "" err = ""
if err is not "": if err:
ping_result["error"] = err ping_result["error"] = err
else: else:
# 'packet_info' example: # 'packet_info' example:
@ -902,7 +889,7 @@ class VyOSDriver(NetworkDriver):
else: else:
rtt_info = rtt_info[-2] rtt_info = rtt_info[-2]
match = re.search("([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+)", rtt_info) match = re.search(r"([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+)", rtt_info)
if match is not None: if match is not None:
rtt_min = float(match.group(1)) rtt_min = float(match.group(1))

View File

@ -1,4 +1,3 @@
future
coveralls coveralls
pytest pytest
pytest-cov pytest-cov

View File

@ -1,4 +1,4 @@
napalm>=2.5,<3.0 napalm>=3.0
paramiko paramiko
netmiko<3.0 netmiko>=3.1.0
vyattaconfparser vyattaconfparser

View File

@ -11,7 +11,7 @@ __author__ = 'Piotr Pieprzycki <piotr.pieprzycki@dreamlab.pl>'
setup( setup(
name="napalm-vyos", name="napalm-vyos",
version="0.1.7", version="0.2.0",
packages=find_packages(), packages=find_packages(),
author="Piotr Pieprzycki", author="Piotr Pieprzycki",
author_email="piotr.pieprzycki@dreamlab.pl", author_email="piotr.pieprzycki@dreamlab.pl",
@ -19,8 +19,6 @@ setup(
classifiers=[ classifiers=[
'Topic :: Utilities', 'Topic :: Utilities',
'Programming Language :: Python', 'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',

View File

@ -5,7 +5,6 @@ import pytest
from napalm.base.test import conftest as parent_conftest from napalm.base.test import conftest as parent_conftest
from napalm.base.test.double import BaseTestDouble from napalm.base.test.double import BaseTestDouble
from napalm.base.utils import py23_compat
from napalm_vyos import vyos from napalm_vyos import vyos
@ -55,5 +54,4 @@ class FakeVyOSDevice(BaseTestDouble):
def send_command(self, command, **kwargs): def send_command(self, command, **kwargs):
filename = '{}.text'.format(self.sanitize_text(command)) filename = '{}.text'.format(self.sanitize_text(command))
full_path = self.find_file(filename) full_path = self.find_file(filename)
result = self.read_txt_file(full_path) return self.read_txt_file(full_path)
return py23_compat.text_type(result)

View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27,py36,py37,py38 envlist = py36,py37,py38
[testenv] [testenv]
deps = deps =