Skip to content

Commit

Permalink
EOS syntax change updates (#1093)
Browse files Browse the repository at this point in the history
* Add pyeapi.Node shim to handle CLI translation

Create a new Node class that checks EOS versions for forward and
backward compatible command translation for FN-0039, along with unit
tests for version detection and translation

* Use json version instead of text

Co-authored-by: Alexey Kolobynin <alexey_ak0@mail.ru>
Co-authored-by: Mircea Ulinic <mirceaulinic@users.noreply.github.com>
  • Loading branch information
3 people committed May 6, 2020
1 parent 0770843 commit 803f764
Show file tree
Hide file tree
Showing 9 changed files with 580 additions and 6 deletions.
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:
"""
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)

0 comments on commit 803f764

Please sign in to comment.