Skip to content

Commit

Permalink
Test cases updated and CI/CD added (#39)
Browse files Browse the repository at this point in the history
* test cases updated and CI/CD setup added (#2)

* Changes made to get_environment method,test cases and requirements

* Jdev (#1)

* Added CI actions for GitHub

* Added CI actions for GitHub

* Updated action

* Updated action

* Updated action

* Updated setup.py ready for releases

* Updated setup.py ready for releases

* Updated setup.py ready for releases

* updated ci

* updated ci

* updated ci

* fixed mocked data for get_interfaces

* fixed textfsm template locator

* added datetime>=4.3 to requirements.txt

* updated mocked data based on windows and github epoch time

* updated mocked data based on windows and github epoch time

* Added datetime to requirements in setup.py

* Adding note for test_get_interfaces not working on local system.

Co-authored-by: Jonathon Lundstrom <[email protected]>
Co-authored-by: jgcumming <[email protected]>

* Modifications made according to standard practices

* added newline to end of file and removed comments

Co-authored-by: Jonathon Lundstrom <[email protected]>
Co-authored-by: jgcumming <[email protected]>
  • Loading branch information
3 people authored Nov 25, 2021
1 parent 21ff356 commit ffc9992
Show file tree
Hide file tree
Showing 65 changed files with 4,544 additions and 5,280 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/napalm-sros-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: napalm-sros

on: [push, pull_request]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.6
uses: actions/setup-python@v2
with:
python-version: "3.6"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
File renamed without changes.
19 changes: 7 additions & 12 deletions README_TEST.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
## **Testing for NAPALM with Nokia SR OS**
1) To run the test framework, first install pytest: ```pip install pytest```
2) Locate the file: \"test/unit/sros/test_getters.py\" and run the class to test the getter methods
3) Locate the file \"test/unit/TestDriver.py\" and run \"Class TestConfigNokiaSROSDriver\" to test all the config methods of NAPALM
+ Location of files required by this test:
- `napalm_sros/templates/set_hostname.j2`
- `test/unit/sros/initial.conf` - Initial configuration sample
- `test/unit/sros/merge_good.conf` - Merge config example
- `test/unit/sros/merge_good.diff` - Compare output for merge
- `test/unit/sros/merge_typo.conf` - Merege config example with error
- `test/unit/sros/new_good.conf` - Replace config example
- `test/unit/sros/new_good.diff` - Compare output for replace
- `test/unit/sros/new_typo.conf` - Replace config example with error
4) Location of mocked output files used for these tests is : `test/unit/mocked_data`
2) You can run the test two ways:
1) Run command `python -m pytest`
2) Locate and run the file `test/test_getters.py`

Note: The test "test_get_interfaces" passes for CI/CD on Github, but it does not pass on individual local system due to
computational difference on different Operating Systems of date and time.


We welcome suggestions and contributions of additional tests for the framework. Please contact the Nokia owners of this repository for how to contribute.
4 changes: 3 additions & 1 deletion example_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

driver = get_network_driver("sros")
optional_args = {'port': 830}
device = driver("127.0.0.1", "vagrant", "vagrant", 60, optional_args)
device = driver("127.0.0.1", "admin", "admin", 60, optional_args)
device.open()
print(device.get_facts())
print(device.get_optics())
device.close()


61 changes: 38 additions & 23 deletions napalm_sros/sros.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
"state_ns": "urn:nokia.com:sros:ns:yang:sr:state",
"configure_ns": "urn:nokia.com:sros:ns:yang:sr:conf",
}
self.optional_args = None

def open(self):
"""Implement the NAPALM method open (mandatory)"""
Expand Down Expand Up @@ -307,7 +308,7 @@ def discard_config(self):
if not self.lock_disable and not self.session_config_lock:
self._unlock_config()

def commit_config(self, message=""):
def commit_config(self, message="", revert_in=None):
"""
Commits the changes requested by the method load_replace_candidate or load_merge_candidate.
"""
Expand Down Expand Up @@ -350,15 +351,15 @@ def rollback(self):
print("Error while rollback: ", error)
break

def compare_config(self, optional_args=None):
def compare_config(self):
"""
:return: A string showing the difference between the running configuration and the candidate
configuration. The running_config is loaded automatically just before doing the comparison
so there is no need for you to do it.
"""

if optional_args is None:
optional_args = {"json_format": False}
if self.optional_args is None:
self.optional_args = {"json_format": False}

buff = ""
if self.fmt == "text":
Expand All @@ -373,16 +374,16 @@ def compare_config(self, optional_args=None):

running_dict = xmltodict.parse(
self.get_config(retrieve="running")["running"],
process_namespaces=not optional_args["json_format"],
process_namespaces=not self.optional_args["json_format"],
)
# candidate_dict = xmltodict.parse(candidate_config, process_namespaces=True)
candidate_dict = xmltodict.parse(
self.get_config(retrieve="candidate")["candidate"],
process_namespaces=not optional_args["json_format"],
process_namespaces=not self.optional_args["json_format"],
)
new_buff = ""
result = diff(running_dict, candidate_dict)
if optional_args["json_format"]:
if self.optional_args["json_format"]:
new_buff += "\n".join(
[json.dumps(e, sort_keys=True, indent=4) for e in result]
)
Expand Down Expand Up @@ -798,7 +799,7 @@ def get_interfaces(self):
)
interfaces[if_name] = ifd

return
return interfaces
except Exception as e:
print("Error in method get interfaces : {}".format(e))
log.error("Error in method get interfaces : %s" % traceback.format_exc())
Expand Down Expand Up @@ -1089,7 +1090,6 @@ def get_config(
retrieve="all",
full=False,
sanitized=False,
optional_args={"format": "xml"},
):
"""
Return the configuration of a device.
Expand All @@ -1113,7 +1113,9 @@ def get_config(
"""
try:
configuration = {"running": "", "candidate": "", "startup": ""}
if optional_args is not None and optional_args["format"] == "cli":
if self.optional_args is None:
self.optional_args = {"format": "xml"}
if self.optional_args["format"] == "cli" and (sanitized is True or sanitized is False):
# Getting output in MD-CLI format
# retrieving config using md-cli
cmd_running = "admin show configuration | no-more"
Expand Down Expand Up @@ -1176,7 +1178,8 @@ def _update_buff(buff):
return configuration

# returning the config in xml format
else:
elif self.optional_args["format"] == "xml" and (sanitized is True or sanitized is False):
config_data_running_xml = ""
if retrieve == "running" or retrieve == "all":
config_data_running = to_ele(
self.conn.get_config(source="running").data_xml
Expand All @@ -1191,8 +1194,9 @@ def _update_buff(buff):
"<\?xml.*\?>", "", config_data_running_xml
)
configuration["running"] = config_data_running_xml
if retrieve == "all":
configuration["startup"] = config_data_running_xml

if retrieve == "startup" or retrieve == "all":
configuration["startup"] = config_data_running_xml

if retrieve == "candidate" or retrieve == "all":
config_data_candidate = to_ele(
Expand Down Expand Up @@ -2441,8 +2445,8 @@ def get_mac_address_table(self):

cmd = "show service fdb-mac"
buff = self._perform_cli_commands(["environment more false", cmd], True)
# template = "textfsm_templates//nokia_sros_show_service_fdb_mac.tpl"
template = "textfsm_templates\\nokia_sros_show_service_fdb_mac.tpl"
template = "textfsm_templates//nokia_sros_show_service_fdb_mac.tpl"
# template = "textfsm_templates\\nokia_sros_show_service_fdb_mac.tpl"
output_list = parse_with_textfsm(template, buff)
new_records = []
for record in output_list:
Expand Down Expand Up @@ -3857,8 +3861,9 @@ def _build_temperature_dict(instance, choice=1):
total_power_modules = total_power_modules + 1
if "Current Util." in item:
row = item.strip()
row_list = re.split(": | W", row)
output = float(row_list[1])
watts = re.match("^.*:\s*(\d+[.]\d+) Watts.*$", item)
if watts:
output = float(watts.groups()[0])

for power_module in result.xpath(
"state_ns:state/state_ns:chassis/state_ns:power-shelf/state_ns:power-module",
Expand Down Expand Up @@ -3888,7 +3893,7 @@ def _build_temperature_dict(instance, choice=1):
)
environment_data["power"].update(
{
power_module_id: {
str(power_module_id): {
"status": oper_state,
"capacity": capacity,
"output": output / total_power_modules,
Expand Down Expand Up @@ -3942,10 +3947,10 @@ def _build_temperature_dict(instance, choice=1):
),
default=-1,
)
environment_data["cpu"].update({sample_period: {"%usage": cpu_usage}})
environment_data["cpu"].update({str(sample_period): {"%usage": cpu_usage}})

environment_data["memory"].update(
{"available_ram": available_ram, "used_ram": used_ram}
{"available_ram": available_ram + used_ram, "used_ram": used_ram}
)
return environment_data
except Exception as e:
Expand Down Expand Up @@ -4032,14 +4037,20 @@ def ping(
self,
destination,
source=C.PING_SOURCE,
ttl=128, # ttl should be in the range 1..128
ttl=C.PING_TTL,
timeout=C.PING_TIMEOUT,
size=C.PING_SIZE,
count=C.PING_COUNT,
vrf=C.PING_VRF,
source_interface=C.PING_SOURCE_INTERFACE,
):
"""
ttl should be in the range 1..128
"""
try:
ping = {}
if ttl > 128:
ttl = 128
results = []
command = ""
if source and vrf:
Expand Down Expand Up @@ -4111,17 +4122,21 @@ def ping(
log.error("Error in method ping : %s" % traceback.format_exc())



def traceroute(
self,
destination,
source=C.TRACEROUTE_SOURCE,
ttl=C.TRACEROUTE_TTL,
timeout=10, # timeout should be between 10 and 60000
timeout=C.TRACEROUTE_TIMEOUT,
vrf=C.TRACEROUTE_VRF,
):
"""
timeout should be in the range 10..60000
"""
try:
traceroute = {}
if timeout < 10 :
timeout = 10
cmd = ""
if source and vrf:
cmd = "traceroute {d1} wait {d2} ttl {d3} source-address {d4} router-instance {d5}"
Expand Down
7 changes: 3 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
napalm>=3.3.1

pytest>=5.4.3
setuptools>=47.3.1
pip>=20.1.1
textfsm>=1.1.0
paramiko>=2.7.1
lxml>=4.6.2
lxml==4.6.2
ncclient>=0.6.7

xmltodict>=0.12.0
dictdiffer>=0.9.0
dictdiffer>=0.9.0
datetime>=4.3
39 changes: 25 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
"""setup.py file."""

from setuptools import setup, find_packages
with open("requirements.txt", "r") as file:
reqs = [req for req in file.read().splitlines() if (len(req) > 0 and not req.startswith("#"))]
__author__ = 'Ashna Shah <[email protected]>'

with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()

setup(
name="napalm-sros",
version="0.1.0",
version="1.0.0",
packages=find_packages(),
author="Ashna Shah",
author_email="[email protected]",
author="Nokia",
author_email="",
description="Network Automation and Programmability Abstraction Layer with Multivendor support",
classifiers=[
'Topic :: Utilities',
'Programming Language :: Python :: 3.6',
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS',
"Topic :: Internet",
"Programming Language :: Python :: 3.6",
"Natural Language :: English",
"Development Status :: 4 - Beta",
],
url="https://github.com/napalm-automation/napalm-sros",
include_package_data=True,
install_requires=reqs,
)
install_requires=[
"napalm>=3.3.1",
"pytest>=5.4.3",
"textfsm>=1.1.0",
"paramiko>=2.7.1",
"lxml>=4.6.2",
"ncclient>=0.6.7",
"xmltodict>=0.12.0",
"dictdiffer>=0.9.0",
"datetime>=4.3",
],
python_requires=">=3.6",
long_description=long_description,
long_description_content_type="text/markdown",
)
Loading

0 comments on commit ffc9992

Please sign in to comment.