From 769a79125d17ded007894464a7151eafbd65f355 Mon Sep 17 00:00:00 2001 From: ebo Date: Mon, 9 Mar 2020 15:42:08 +0000 Subject: netconf-pnp-simulator: make PYTHONPATH always globally defined Add IT using ncclient and tox Issue-ID: INT-1124 Change-Id: I560d4fd2468ac93f8ead36062b2e316821af8d07 Signed-off-by: ebo --- .../docs/examples/mynetconf/docker-compose.yml | 2 +- test/mocks/netconf-pnp-simulator/engine/Dockerfile | 4 + .../engine/container-tag.yaml | 2 +- .../netconf-pnp-simulator/engine/entrypoint.sh | 2 +- .../netconf-pnp-simulator/engine/tests/README | 2 + .../netconf-pnp-simulator/engine/tests/nctest.py | 37 ++++++ .../netconf-pnp-simulator/engine/tests/settings.py | 11 ++ .../engine/tests/test_basic_operations.py | 52 +++++++++ .../engine/tests/test_ietf_interfaces.py | 93 ++++++++++++++++ .../engine/tests/test_turing_machine.py | 124 +++++++++++++++++++++ test/mocks/netconf-pnp-simulator/engine/tox.ini | 32 ++++++ .../modules/docker-compose.yml | 2 +- 12 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/README create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/nctest.py create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/settings.py create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/test_basic_operations.py create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/test_ietf_interfaces.py create mode 100644 test/mocks/netconf-pnp-simulator/engine/tests/test_turing_machine.py create mode 100644 test/mocks/netconf-pnp-simulator/engine/tox.ini (limited to 'test/mocks/netconf-pnp-simulator') diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml index 5d8ba5acc..5d7c0af72 100644 --- a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml +++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: netopeer2: - image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.1 + image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.2 container_name: mynetconf restart: always ports: diff --git a/test/mocks/netconf-pnp-simulator/engine/Dockerfile b/test/mocks/netconf-pnp-simulator/engine/Dockerfile index 426606953..d4776a4e4 100644 --- a/test/mocks/netconf-pnp-simulator/engine/Dockerfile +++ b/test/mocks/netconf-pnp-simulator/engine/Dockerfile @@ -151,6 +151,7 @@ RUN set -eux \ COPY --from=build /opt/ /opt/ ENV LD_LIBRARY_PATH=/opt/lib:/opt/lib64 +ENV PYTHONPATH=/opt/lib/python3.7/site-packages COPY config/ /config VOLUME /config @@ -160,6 +161,9 @@ RUN adduser --system --disabled-password --gecos 'Netconf User' netconf ENV HOME=/home/netconf VOLUME $HOME/.local/share/virtualenvs +# This is NOT a robust health check but it does help tox-docker to detect when +# it can start the tests. +HEALTHCHECK --interval=1s --start-period=2s --retries=10 CMD test -f /run/netopeer2-server.pid EXPOSE 830 diff --git a/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml b/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml index cd982b9ac..72191ff3c 100644 --- a/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml +++ b/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml @@ -1 +1 @@ -tag: "2.6.1" +tag: "2.6.2" diff --git a/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh b/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh index 48a5e5a40..6636080fb 100755 --- a/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh +++ b/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh @@ -112,7 +112,7 @@ configure_subscriber_execution() command=$prog $model redirect_stderr=true autorestart=true -environment=PATH=$PROG_PATH,PYTHONPATH=/opt/lib/python3.7/site-packages,PYTHONUNBUFFERED="1" +environment=PATH=$PROG_PATH,PYTHONUNBUFFERED="1" EOF } diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/README b/test/mocks/netconf-pnp-simulator/engine/tests/README new file mode 100644 index 000000000..295585dc2 --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/README @@ -0,0 +1,2 @@ +Borrowed from https://github.com/sysrepo/sysrepo-netopeer2-smoketests +with some minor fixes diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/nctest.py b/test/mocks/netconf-pnp-simulator/engine/tests/nctest.py new file mode 100644 index 000000000..2f848c361 --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/nctest.py @@ -0,0 +1,37 @@ +from ncclient import manager, operations +import settings +import unittest + +class NCTestCase(unittest.TestCase): + """ Base class for NETCONF test cases. Provides a NETCONF connection and some helper methods. """ + + def setUp(self): + self.nc = manager.connect( + host=settings.HOST, + port=settings.PORT, + username=settings.USERNAME, + key_filename=settings.KEY_FILENAME, + allow_agent=False, + look_for_keys=False, + hostkey_verify=False) + self.nc.raise_mode = operations.RaiseMode.NONE + + def tearDown(self): + self.nc.close_session() + + def check_reply_ok(self, reply): + self.assertIsNotNone(reply) + if settings.DEBUG: + print(reply.xml) + self.assertTrue(reply.ok) + self.assertIsNone(reply.error) + + def check_reply_err(self, reply): + self.assertIsNotNone(reply) + if settings.DEBUG: + print(reply.xml) + self.assertFalse(reply.ok) + self.assertIsNotNone(reply.error) + + def check_reply_data(self, reply): + self.check_reply_ok(reply) diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/settings.py b/test/mocks/netconf-pnp-simulator/engine/tests/settings.py new file mode 100644 index 000000000..749eb4cfd --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/settings.py @@ -0,0 +1,11 @@ +import os + +HOST = "127.0.0.1" +# Set by tox-docker +# Unexpectedly, tox-docker uses the repository prefix instead of the image name to define the +# variable prefix. +PORT = int(os.environ["LOCALHOST_830_TCP_PORT"]) +USERNAME = "netconf" +KEY_FILENAME = "../config/ssh/id_rsa" + +DEBUG = False diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/test_basic_operations.py b/test/mocks/netconf-pnp-simulator/engine/tests/test_basic_operations.py new file mode 100644 index 000000000..62d41c259 --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/test_basic_operations.py @@ -0,0 +1,52 @@ +import unittest +import nctest + +class TestBasicOperations(nctest.NCTestCase): + """ Tests basic NETCONF operations with no prerequisites on datastore content. """ + + def test_capabilities(self): + self.assertTrue(":startup" in self.nc.server_capabilities) + self.assertTrue(":candidate" in self.nc.server_capabilities) + self.assertTrue(":validate" in self.nc.server_capabilities) + self.assertTrue(":xpath" in self.nc.server_capabilities) + + def test_get(self): + reply = self.nc.get() + self.check_reply_data(reply) + + def test_get_config_startup(self): + reply = self.nc.get_config(source='startup') + self.check_reply_data(reply) + + def test_get_config_running(self): + reply = self.nc.get_config(source='running') + self.check_reply_data(reply) + + def test_copy_config(self): + reply = self.nc.copy_config(source='startup', target='candidate') + self.check_reply_ok(reply) + + def test_neg_filter(self): + reply = self.nc.get(filter=("xpath", "/non-existing-module:non-existing-data")) + self.check_reply_err(reply) + + def test_lock(self): + reply = self.nc.lock("startup") + self.check_reply_ok(reply) + reply = self.nc.lock("running") + self.check_reply_ok(reply) + reply = self.nc.lock("candidate") + self.check_reply_ok(reply) + + reply = self.nc.lock("startup") + self.check_reply_err(reply) + + reply = self.nc.unlock("startup") + self.check_reply_ok(reply) + reply = self.nc.unlock("running") + self.check_reply_ok(reply) + reply = self.nc.unlock("candidate") + self.check_reply_ok(reply) + +if __name__ == '__main__': + unittest.main() diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/test_ietf_interfaces.py b/test/mocks/netconf-pnp-simulator/engine/tests/test_ietf_interfaces.py new file mode 100644 index 000000000..87733ac37 --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/test_ietf_interfaces.py @@ -0,0 +1,93 @@ +import unittest +import nctest +import os + +class TestIETFInterfaces(nctest.NCTestCase): + """ Tests basic NETCONF operations on the turing-machine YANG module. """ + + def __init__(self, *args, **kwargs): + super(TestIETFInterfaces, self).__init__(*args, **kwargs) + self.ns = {"nc": "urn:ietf:params:xml:ns:netconf:base:1.0", "if": "urn:ietf:params:xml:ns:yang:ietf-interfaces"} + + def test_edit_config(self): + config_xml = """ + + + TestInterface + Interface under test + ianaift:ethernetCsmacd + + 1500 +
+ 192.168.2.100 + 24 +
+
+ +
+ 2001:db8::10 + 32 +
+
+
+
+
""" + + filter_xml = """ + + """ + + with_default_report_all = """report-all""" + + # get from running - should be empty + reply = self.nc.get_config(source="running", filter=filter_xml) + self.check_reply_data(reply) + deltas = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']", namespaces=self.ns) + self.assertEqual(len(deltas), 0) + + # set data - candidate + reply = self.nc.edit_config(target='candidate', config=config_xml.format("merge")) + self.check_reply_ok(reply) + + # get from candidate + reply = self.nc.get_config(source="candidate", filter=filter_xml) + self.check_reply_data(reply) + interfaces = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']", namespaces=self.ns) + self.assertEqual(len(interfaces), 1) + + # default leaf should NOT be present + enabled = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']/enabled", namespaces=self.ns) + self.assertEqual(len(enabled), 0) + + # get from candidate with with defaults = 'report-all' + reply = self.nc.get_config(source="candidate", filter=filter_xml, with_defaults=with_default_report_all) + self.check_reply_data(reply) + interfaces = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']", namespaces=self.ns) + self.assertEqual(len(interfaces), 1) + + # default leaf should be present + enabled = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']/enabled", namespaces=self.ns) + self.assertEqual(len(enabled), 0) # TODO: change to 1 once this is implemented + + # get from running - should be empty + reply = self.nc.get_config(source="running", filter=filter_xml) + self.check_reply_data(reply) + deltas = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']", namespaces=self.ns) + self.assertEqual(len(deltas), 0) + + # commit - should fail, not enabled in running + reply = self.nc.commit() + self.check_reply_err(reply) + + # delete from candidate + reply = self.nc.edit_config(target='candidate', config=config_xml.format("delete")) + self.check_reply_ok(reply) + + # get from candidate - should be empty + reply = self.nc.get_config(source="candidate", filter=filter_xml) + self.check_reply_data(reply) + deltas = reply.data.xpath("/nc:rpc-reply/nc:data/if:interfaces/if:interface[if:name='TestInterface']", namespaces=self.ns) + self.assertEqual(len(deltas), 0) + +if __name__ == '__main__': + unittest.main() diff --git a/test/mocks/netconf-pnp-simulator/engine/tests/test_turing_machine.py b/test/mocks/netconf-pnp-simulator/engine/tests/test_turing_machine.py new file mode 100644 index 000000000..63a0c2d99 --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tests/test_turing_machine.py @@ -0,0 +1,124 @@ +import unittest +import nctest +import os + +class TestTuringMachine(nctest.NCTestCase): + """ Tests basic NETCONF operations on the turing-machine YANG module. """ + + def __init__(self, *args, **kwargs): + super(TestTuringMachine, self).__init__(*args, **kwargs) + self.ns = {"nc": "urn:ietf:params:xml:ns:netconf:base:1.0", "tm": "http://example.net/turing-machine"} + + def check_deltas_in_data(self, data): + deltas = data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/*", namespaces=self.ns) + self.assertNotEqual(len(deltas), 0) + for d in deltas: + self.assertTrue(d.tag.endswith("delta")) + + def check_labels_only_in_data(self, data): + children = data.xpath("/nc:rpc-reply/nc:data/*", namespaces=self.ns) + self.assertNotEqual(len(children), 0) + for child in children: + self.assertTrue(child.tag.endswith("turing-machine")) + children = data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/*", namespaces=self.ns) + self.assertNotEqual(len(children), 0) + for child in children: + self.assertTrue(child.tag.endswith("transition-function")) + children = data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/*", namespaces=self.ns) + self.assertNotEqual(len(children), 0) + for child in children: + self.assertTrue(child.tag.endswith("delta")) + children = data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/tm:delta/*", namespaces=self.ns) + self.assertNotEqual(len(children), 0) + for child in children: + self.assertTrue(child.tag.endswith("label")) + + def test_get(self): + reply = self.nc.get() + self.check_reply_data(reply) + self.check_deltas_in_data(reply.data) + + def test_get_config_startup(self): + reply = self.nc.get_config(source="startup") + self.check_reply_data(reply) + self.check_deltas_in_data(reply.data) + + def test_get_config_running(self): + reply = self.nc.get_config(source="running") + self.check_reply_data(reply) + self.check_deltas_in_data(reply.data) + + def test_get_subtree_filter(self): + filter_xml = """ + + + + + + + """ + reply = self.nc.get_config(source="running", filter=filter_xml) + self.check_reply_data(reply) + self.check_deltas_in_data(reply.data) + self.check_labels_only_in_data(reply.data) + + def test_get_xpath_filter(self): + # https://github.com/ncclient/ncclient/issues/166 + filter_xml = """ + """ + reply = self.nc.get(filter=filter_xml) + self.check_reply_data(reply) + self.check_deltas_in_data(reply.data) + self.check_labels_only_in_data(reply.data) + + @unittest.skipIf(os.environ.get("DOCKER_IMG_TAG") == "latest", "bug in Netopeer2 replace operation") + def test_edit_config(self): + config_xml = """ + + + + + + {} + {} + + + + """ + # merge + reply = self.nc.edit_config(target='running', config=config_xml.format("merge", 9, 99)) + self.check_reply_ok(reply) + # get + reply = self.nc.get_config(source="running") + self.check_reply_data(reply) + deltas = reply.data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/tm:delta[tm:label='test-transition-rule']", namespaces=self.ns) + self.assertEqual(len(deltas), 1) + # create already existing - expect error + reply = self.nc.edit_config(target='running', config=config_xml.format("create", 9, 99)) + self.check_reply_err(reply) + # replace + reply = self.nc.edit_config(target='running', config=config_xml.format("replace", 9, 88)) + self.check_reply_ok(reply) + # get + reply = self.nc.get_config(source="running") + self.check_reply_data(reply) + states = reply.data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/tm:delta[tm:label='test-transition-rule']/tm:input/tm:state", namespaces=self.ns) + self.assertEqual(len(states), 1) + self.assertEqual(states[0].text, "88") + # delete + reply = self.nc.edit_config(target='running', config=config_xml.format("delete", 9, 88)) + self.check_reply_ok(reply) + # delete non-existing - expect error + reply = self.nc.edit_config(target='running', config=config_xml.format("delete", 9, 88)) + self.check_reply_err(reply) + # get - should be empty + reply = self.nc.get_config(source="running") + self.check_reply_data(reply) + deltas = reply.data.xpath("/nc:rpc-reply/nc:data/tm:turing-machine/tm:transition-function/tm:delta[tm:label='test-transition-rule']", namespaces=self.ns) + self.assertEqual(len(deltas), 0) + +if __name__ == '__main__': + unittest.main() diff --git a/test/mocks/netconf-pnp-simulator/engine/tox.ini b/test/mocks/netconf-pnp-simulator/engine/tox.ini new file mode 100644 index 000000000..c4ca5fbdb --- /dev/null +++ b/test/mocks/netconf-pnp-simulator/engine/tox.ini @@ -0,0 +1,32 @@ +#- +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Nordix Foundation. +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END========================================================= + +[tox] +requires = tox-docker +skipsdist = True + +[testenv] +changedir = tests +docker = + {env:CONTAINER_PUSH_REGISTRY}/{env:DOCKER_NAME}:{env:DOCKER_IMAGE_TAG} + +deps = + ncclient + discover +commands = discover -v diff --git a/test/mocks/netconf-pnp-simulator/modules/docker-compose.yml b/test/mocks/netconf-pnp-simulator/modules/docker-compose.yml index 8176e3b95..e8f2f9ae5 100644 --- a/test/mocks/netconf-pnp-simulator/modules/docker-compose.yml +++ b/test/mocks/netconf-pnp-simulator/modules/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: netconf-pnp-simulator: - image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.1 + image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.2 container_name: netconf-pnp-simulator restart: always ports: -- cgit 1.2.3-korg