Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EOS syntax change updates #1093

Merged
merged 3 commits into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions napalm/eos/eos.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
CommandErrorException,
)
from napalm.eos.constants import LLDP_CAPAB_TRANFORM_TABLE
from napalm.eos.pyeapi_syntax_wrapper import Node
from napalm.eos.utils.versions import EOSVersion
import napalm.base.constants as c

# local modules
Expand Down Expand Up @@ -156,11 +158,16 @@ def open(self):
)

if self.device is None:
self.device = pyeapi.client.Node(connection, enablepwd=self.enablepwd)
self.device = Node(connection, enablepwd=self.enablepwd)
# does not raise an Exception if unusable

# let's try to run a very simple command
self.device.run_commands(["show clock"], encoding="text")
# let's try to determine if we need to use new EOS cli syntax
sh_ver = self.device.run_commands(["show version"])
cli_version = (
2 if EOSVersion(sh_ver[0]["version"]) >= EOSVersion("4.23.0") else 1
)

self.device.update_cli_version(cli_version)
except ConnectionError as ce:
# and this is raised either if device not avaiable
# either if HTTP(S) agent is not enabled
Expand Down
40 changes: 40 additions & 0 deletions napalm/eos/pyeapi_syntax_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""pyeapi wrapper to fix cli syntax change"""
import pyeapi
from napalm.eos.utils.cli_syntax import cli_convert


class Node(pyeapi.client.Node):
"""
pyeapi node wrapper to fix cli syntax change
"""

def __init__(self, *args, **kwargs):
if "cli_version" in kwargs:
self.cli_version = kwargs["cli_version"]
del kwargs["cli_version"]
else:
self.cli_version = 1

super(Node, self).__init__(*args, **kwargs)

def update_cli_version(self, version):
"""
Update CLI version number for this device
:param version: int: version number
:return: None
"""
self.cli_version = version

def run_commands(self, commands, **kwargs):
"""
Run commands wrapper
:param commands: list of commands
:param kwargs: other args
:return: list of outputs
"""
if isinstance(commands, str):
new_commands = [cli_convert(commands, self.cli_version)]
else:
new_commands = [cli_convert(cmd, self.cli_version) for cmd in commands]

return super(Node, self).run_commands(new_commands, **kwargs)
357 changes: 357 additions & 0 deletions napalm/eos/utils/cli_syntax.py

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions napalm/eos/utils/versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Some functions to work with EOS version numbers"""
import re


class EOSVersion:
bewing marked this conversation as resolved.
Show resolved Hide resolved
"""
Class to represent EOS version
"""

def __init__(self, version):
"""
Create object
:param version: str: version string
"""
self.version = version
self.numbers = []
self.type = ""

self._parse(version)

def _parse(self, version):
"""
Parse version string
:param version: str: version
:return: None
"""
m = re.match(r"^(?P<numbers>\d[\d.]+\d)", version)

if m:
self.numbers = m.group("numbers").split(".")

def __lt__(self, other):
if not len(self.numbers):
return True

for x, y in zip(self.numbers, other.numbers):
if x < y:
return True
elif x > y:
return False

return False

def __gt__(self, other):
if not len(self.numbers):
return False

for x, y in zip(self.numbers, other.numbers):
if x > y:
return True
elif x < y:
return False

return False

def __eq__(self, other):
if len(self.numbers) != len(other.numbers):
return False

for x, y in zip(self.numbers, other.numbers):
if x != y:
return False

return True

def __le__(self, other):
return self < other or self == other

def __ge__(self, other):
return self > other or self == other
8 changes: 8 additions & 0 deletions test/eos/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,11 @@ def run_commands(self, command_list, encoding="json"):
result.append({"output": self.read_txt_file(full_path)})

return result

def update_cli_version(self, version):
"""
Update CLI version number for this device
:param version: int: version number
:return: None
"""
self.cli_version = version
3 changes: 0 additions & 3 deletions test/eos/mocked_data/show_clock.text

This file was deleted.

16 changes: 16 additions & 0 deletions test/eos/mocked_data/show_version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"memTotal": 3954980,
"uptime": 200478.31,
"modelName": "DCS-7150S-64-CL-R",
"internalVersion": "4.21.8M-2GB-13902577.4218M",
"mfgName": "Arista",
"serialNumber": "JPE00000000",
"systemMacAddress": "00:1c:73:00:00:00",
"bootupTimestamp": 1588135848.0,
"memFree": 2558364,
"version": "4.21.8M-2GB",
"architecture": "i386",
"isIntlVersion": false,
"internalBuildId": "5af75062-ded5-4c99-8f44-daa88aa4414d",
"hardwareRevision": "01.03"
}
51 changes: 51 additions & 0 deletions test/eos/test_cli_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Tests for EOS cli_syntax
"""
from napalm.eos.utils.cli_syntax import cli_convert


def test_cli_no_change_v2():
"""
Test no change for basic commands in version 2
:return:
"""
commands = ["show version", "show interfaces"]

for c in commands:
assert c == cli_convert(c, 2)
assert c == cli_convert(c, 1)


def test_cli_no_change_non_exist_version():
"""
Test no change for basic commands and non-existing versions
:return:
"""
commands = ["show version", "show interfaces"]

for c in commands:
assert c == cli_convert(c, 100000)


def test_cli_change_exact():
"""
Test cli change for exact commands
"""
commands = ["show ipv6 bgp neighbors", "show lldp traffic"]
expect = ["show ipv6 bgp peers", "show lldp counters"]

for c, e in zip(commands, expect):
assert e == cli_convert(c, 2)
assert c == cli_convert(e, 1)


def test_cli_change_long_commands():
"""
Test cli change for long commands
"""
commands = ["show ipv6 bgp neighbors vrf all", "show lldp traffic | include test"]
expect = ["show ipv6 bgp peers vrf all", "show lldp counters | include test"]

for c, e in zip(commands, expect):
assert e == cli_convert(c, 2)
assert c == cli_convert(e, 1)
28 changes: 28 additions & 0 deletions test/eos/test_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Tests for versions utils"""
from napalm.eos.utils.versions import EOSVersion


def test_version_create():
"""
Test we can create version object
"""
versions = ["4.21.7.1M", "4.20.24F-2GB", "blablabla"]

for v in versions:
assert v == EOSVersion(v).version


def test_version_comparisons():
"""
Test version comparison
"""
old_version = "4.21.7.1M"
new_verion = "4.23.0F"

assert EOSVersion(old_version) < EOSVersion(new_verion)
assert EOSVersion(new_verion) > EOSVersion(old_version)
assert EOSVersion(old_version) <= EOSVersion(new_verion)
assert EOSVersion(new_verion) >= EOSVersion(old_version)
assert not EOSVersion(old_version) < EOSVersion(old_version)
assert EOSVersion(old_version) == EOSVersion(old_version)
assert EOSVersion(old_version) <= EOSVersion(old_version)