aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrej Kozemcak <andrej.kozemcak@pantheon.tech>2019-01-24 07:34:58 +0100
committerAndrej Kozemcak <andrej.kozemcak@pantheon.tech>2019-02-21 16:54:15 +0100
commit0443951623b23abd9257876ab942443822427c57 (patch)
tree2bd167547c0b34e269975a24c3554ca8ff94151a
parent93b9c98c441ab571d1493488ab8e54115f9a04d5 (diff)
1: Create Test enviroment.
Change-Id: I79ea8d85312876af7fd389b4776f764c21345ff2 Signed-off-by: Andrej Kozemcak <andrej.kozemcak@pantheon.tech>
-rw-r--r--Makefile4
-rw-r--r--test/conf/ietf-interfaces.xml14
-rw-r--r--test/conf/vpp.conf9
-rw-r--r--test/framewrok.py38
-rw-r--r--test/netopeer_controler.py70
-rwxr-xr-xtest/run_test.py35
-rwxr-xr-xtest/run_test.sh78
-rw-r--r--test/test_ietf_interfaces.py80
-rw-r--r--test/topology.py122
-rw-r--r--test/util.py52
-rw-r--r--test/vpp_controler.py67
11 files changed, 569 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 71b086a..079ba4a 100644
--- a/Makefile
+++ b/Makefile
@@ -72,6 +72,7 @@ help:
@echo " build-plugins - build plugins"
@echo " build-package - build rpm or deb package"
@echo " docker - build sweetcomb in docker enviroment"
+ @echo " docker_test - run test in docker enviroment"
@echo " clean - clean all build"
@echo " distclean - remove all build directory"
$(BR)/.deps.ok:
@@ -203,6 +204,9 @@ build-plugins:
docker:
@build-root/scripts/docker.sh
+docker_test:
+ @test/run_test.sh
+
build-package:
@mkdir -p $(BR)/build-scvpp/;cd $(BR)/build-scvpp;cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr $(WS_ROOT)/src/scvpp/;make install;
@mkdir -p $(BR)/build-package/;cd $(BR)/build-package/;$(cmake) $(WS_ROOT)/src/;make package;rm -rf $(BR)/build-package/_CPack_Packages;
diff --git a/test/conf/ietf-interfaces.xml b/test/conf/ietf-interfaces.xml
new file mode 100644
index 0000000..3c8aa07
--- /dev/null
+++ b/test/conf/ietf-interfaces.xml
@@ -0,0 +1,14 @@
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+ <interface>
+ <name>local0</name>
+ <description>Ethernet 0</description>
+ <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
+ <enabled>false</enabled>
+ </interface>
+ <interface>
+ <name>host-vpp1</name>
+ <description>Ethernet 1</description>
+ <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
+ <enabled>false</enabled>
+ </interface>
+</interfaces>
diff --git a/test/conf/vpp.conf b/test/conf/vpp.conf
new file mode 100644
index 0000000..92d5241
--- /dev/null
+++ b/test/conf/vpp.conf
@@ -0,0 +1,9 @@
+unix {
+ log /var/log/vpp/vpp.log
+ cli-listen /run/vpp/cli.sock
+}
+
+plugins {
+ plugin dpdk_plugin.so { disable }
+}
+
diff --git a/test/framewrok.py b/test/framewrok.py
new file mode 100644
index 0000000..9fbf240
--- /dev/null
+++ b/test/framewrok.py
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import unittest
+
+from topology import Topology
+
+class SweetcombTestCase(unittest.TestCase):
+
+ @classmethod
+ def instance(cls):
+ """Return the instance of this testcase"""
+
+ return cls.instance
+
+ @classmethod
+ def create_topoly(cls):
+
+ cls.topology = Topology()
+ cls.topology.create_topology()
+
+ cls.vpp = cls.topology.get_vpp()
+ cls.netopeer_cli = cls.topology.get_netopeer_cli()
+
+
diff --git a/test/netopeer_controler.py b/test/netopeer_controler.py
new file mode 100644
index 0000000..74c7dc6
--- /dev/null
+++ b/test/netopeer_controler.py
@@ -0,0 +1,70 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import pexpect
+
+class Netopeer_controler:
+ def __init__(self):
+ self.name = "netopeer2-cli"
+ self.password = "0000"
+
+ def __del__(self):
+ self.kill()
+
+ def kill(self):
+ if self.child is None:
+ return
+
+ self.child.sendline("exit\r")
+ self.child.logfile.close()
+ #self.child.kill()
+ self.child = None
+
+ def terminate(self):
+ self.kill()
+
+ def set_password(self, password):
+ self.password = password;
+
+ def spawn(self):
+ self.child = pexpect.spawn(self.name)
+ self.child.logfile = open('/var/log/Netopeer_controler.log', 'wb')
+ self.pid = self.child.pid
+ self.child.expect(">")
+ self.child.sendline("connect\r")
+ i = self.child.expect(["Password:", "Are you sure you want to continue connecting (yes/no)?"])
+ if 0 == i:
+ self.child.sendline(self.password + '\r')
+ elif 1 == i:
+ self.child.sendline("yes\r")
+ self.child.expect("Password:")
+ self.child.sendline(self.password + '\r')
+
+ self.child.expect(">")
+
+ def get(self, msg):
+ self.child.sendline("get --filter-xpath {}\r".format(msg))
+ self.child.expect("> ")
+ print(self.child.before.decode('ascii'))
+
+ def edit_config(self, msg):
+ f = open("/tmp/tmp_example.xml", "w")
+ f.write(msg)
+ f.close()
+
+ self.child.sendline("edit-config --target running --config=/tmp/tmp_example.xml\r")
+ self.child.expect("> ")
+ print(self.child.before.decode('ascii'))
diff --git a/test/run_test.py b/test/run_test.py
new file mode 100755
index 0000000..e3190c4
--- /dev/null
+++ b/test/run_test.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+#
+
+import unittest
+import util
+
+from framewrok import SweetcombTestCase
+from test_ietf_interfaces import TestIetfInterfaces
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(TestIetfInterfaces('test_interface_up'))
+ suite.addTest(TestIetfInterfaces('test_ip_addr'))
+ return suite
+
+if __name__ == '__main__':
+ util.import_yang_modules()
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
+
diff --git a/test/run_test.sh b/test/run_test.sh
new file mode 100755
index 0000000..aa28773
--- /dev/null
+++ b/test/run_test.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+
+
+IMAGE="sweetcomb_img"
+CONTAINER="sweetcomb_test"
+
+function build_enviroment {
+ FIND=`docker images | grep ${IMAGE}`
+
+ if [ -n "${FIND}" ]; then
+ return
+ fi
+
+ docker build -t ${IMAGE} ./src/Docker/Build
+}
+
+function create_container {
+ docker run -id --privileged --name ${CONTAINER} \
+ -v $(pwd)/../sweetcomb:/root/src/sweetcomb ${IMAGE}
+}
+
+function start_container {
+ FIND=`docker container ls -a | grep ${CONTAINER}`
+
+ if [ -z "${FIND}" ]; then
+ create_container
+ else
+ FIND=`docker container ps | grep ${CONTAINER}`
+ if [ -z "${FIND}" ]; then
+ docker start ${CONTAINER}
+ fi
+ fi
+}
+
+function build_sweetcomb {
+ docker exec -it ${CONTAINER} bash -c "/root/src/sweetcomb/build-root/scripts/de_build.sh"
+ docker exec -it ${CONTAINER} bash -c "apt-get install -y python3-pip && pip3 install pexpect && pip3 install pyroute2"
+}
+
+function main {
+ build_enviroment
+ start_container
+ build_sweetcomb
+
+ docker exec -it ${CONTAINER} bash -c "
+
+ apt-get install -y python3-pip
+
+ pip3 install pyroute2 pexpect psutil
+
+ echo -e \"0000\n0000\" | passwd
+ "
+
+
+ docker exec -it ${CONTAINER} bash -c "/root/src/sweetcomb/test/run_test.py"
+
+ echo "Wait 20s, then remove the container."
+ sleep 20
+
+ docker stop ${CONTAINER}
+ docker container rm ${CONTAINER}
+}
+
+main $@
diff --git a/test/test_ietf_interfaces.py b/test/test_ietf_interfaces.py
new file mode 100644
index 0000000..55ef029
--- /dev/null
+++ b/test/test_ietf_interfaces.py
@@ -0,0 +1,80 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import unittest
+
+import util
+from framewrok import SweetcombTestCase
+
+class TestIetfInterfaces(SweetcombTestCase):
+
+ def setUp(self):
+ super(TestIetfInterfaces, self).setUp()
+
+ self.create_topoly()
+
+ def tearDown(self):
+ super(TestIetfInterfaces, self).setUp()
+
+ self.topology.close_topology()
+
+ def test_interface_up(self):
+
+ self.vpp.show_interface()
+
+ self.netopeer_cli.get("/ietf-interfaces:*")
+ ts = '''<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">
+ <interface>
+ <name>local0</name>
+ <enabled>true</enabled>
+ </interface>
+ <interface>
+ <name>host-vpp1</name>
+ <enabled>true</enabled>
+ </interface>
+</interfaces>'''
+
+ self.netopeer_cli.edit_config(ts)
+ self.netopeer_cli.get("/ietf-interfaces:*")
+
+ self.vpp.show_interface()
+
+ def test_ip_addr(self):
+ self.vpp.show_address()
+ self.netopeer_cli.get("/ietf-interfaces:*")
+
+ util.ping('192.168.0.1')
+
+ ts = '''<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:ip=\"urn:ietf:params:xml:ns:yang:ietf-ip\">
+ <interface>
+ <name>host-vpp1</name>
+ <enabled>true</enabled>
+ <ip:ipv4>
+ <ip:enabled>true</ip:enabled>
+ <ip:address>
+ <ip:ip>192.168.0.1</ip:ip>
+ <ip:prefix-length>24</ip:prefix-length>
+ </ip:address>
+ </ip:ipv4>
+ </interface>
+</interfaces>'''
+
+ self.netopeer_cli.edit_config(ts)
+ self.netopeer_cli.get("/ietf-interfaces:*")
+
+ self.vpp.show_address()
+
+ util.ping('192.168.0.1')
diff --git a/test/topology.py b/test/topology.py
new file mode 100644
index 0000000..43f15e3
--- /dev/null
+++ b/test/topology.py
@@ -0,0 +1,122 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import os
+import subprocess
+from netopeer_controler import Netopeer_controler
+from vpp_controler import Vpp_controler
+from socket import AF_INET
+from pyroute2 import IPRoute
+import psutil
+import time
+
+class Topology:
+ def __init__(self):
+ self.process = []
+
+ def __del__(self):
+ self._kill_process()
+
+ def _kill_process(self):
+ if not self.process:
+ return
+
+ for process in self.process:
+ process.terminate()
+
+ for proc in psutil.process_iter(attrs=['pid', 'name']):
+ name = proc.info['name']
+ if 'vpp' in name or 'sysrepo' in name or 'netopeer' in name:
+ proc.kill()
+
+ self.process = []
+
+ def _prepare_linux_enviroment(self):
+ ip = IPRoute()
+ exist = ip.link_lookup(ifname='vpp1')
+ if exist:
+ return
+
+ ip.link('add', ifname='vpp1', peer='virtual1', kind='veth')
+ ip.link('add', ifname='vpp2', peer='virtual2', kind='veth')
+
+ vpp1 = ip.link_lookup(ifname='virtual1')[0]
+ vpp2 = ip.link_lookup(ifname='virtual2')[0]
+
+ ip.link('set', index=vpp1, state='up')
+ ip.link('set', index=vpp2, state='up')
+
+ ip.addr('add', index=vpp1, address='192.168.0.2', prefixlen=24)
+ ip.addr('add', index=vpp2, address='192.168.1.2', prefixlen=24)
+
+
+ def _start_sysrepo(self):
+ print("Start sysrepo deamon.")
+ #TODO: Need property close.
+ err = open("/var/log/sysrepod", 'wb')
+ self.sysrepo = subprocess.Popen(["sysrepod", "-d", "-l 3"],
+ stdout=subprocess.PIPE, stderr=err)
+ self.process.append(self.sysrepo)
+
+ def _start_sysrepo_plugins(self):
+ print("Start sysrepo plugins.")
+ #TODO: Need property close.
+ err = open("/var/log/sysrepo-plugind", 'wb')
+ self.splugin = subprocess.Popen(["sysrepo-plugind", "-d", "-l 3"],
+ stdout=subprocess.PIPE, stderr=err)
+ self.process.append(self.splugin)
+
+ def _start_netopeer_server(self):
+ print("Start netopeer server.")
+ self.netopeer_server = subprocess.Popen("netopeer2-server",
+ stdout=subprocess.PIPE)
+ self.process.append(self.netopeer_server)
+
+ def _start_netopeer_cli(self):
+ print("Start netopeer client.")
+ self.netopeer_cli = Netopeer_controler()
+ self.process.append(self.netopeer_cli)
+ self.netopeer_cli.spawn()
+
+ def _start_vpp(self):
+ print("Start VPP.")
+ self.vpp = Vpp_controler()
+ self.vpp.spawn()
+ self.process.append(self.vpp)
+
+ def get_vpp(self):
+ return self.vpp
+
+ def get_netopeer_cli(self):
+ return self.netopeer_cli
+
+ def create_topology(self):
+ #try:
+ self._prepare_linux_enviroment()
+ self._start_vpp()
+ self._start_sysrepo()
+ time.sleep(1)
+ self._start_sysrepo_plugins()
+ self._start_netopeer_server()
+
+ #Wait for netopeer server
+ time.sleep(1)
+ self._start_netopeer_cli()
+ #except:
+ #self._kill_process()
+
+ def close_topology(self):
+ self._kill_process()
diff --git a/test/util.py b/test/util.py
new file mode 100644
index 0000000..6fe5427
--- /dev/null
+++ b/test/util.py
@@ -0,0 +1,52 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import subprocess
+import os
+import time
+
+def ping(ip):
+ subprocess.run("ping -c 4 " + ip, shell=True)
+
+def import_yang_modules():
+ print("Import YANG modules to sysrepo.")
+
+ directory = '/root/src/sweetcomb/'
+ os.chdir(directory + "src/plugins/yang/ietf/")
+ subprocess.run(["sysrepoctl", "--install", "-S", ".",
+ "--yang=./ietf-interfaces.yang"])
+
+ os.chdir(directory + "src/plugins/yang/openconfig/")
+ subprocess.run(["sysrepoctl", "--install", "-S", ".",
+ "--yang=./openconfig-interfaces.yang"])
+ subprocess.run(["sysrepoctl", "--install", "-S", ".",
+ "--yang=./openconfig-if-ip.yang"])
+ subprocess.run(["sysrepoctl", "--install", "-S", ".",
+ "--yang=./openconfig-local-routing.yang"])
+
+ time.sleep(5)
+
+ os.chdir(directory + "test/conf/")
+
+ print("Import configuration to sysrepo datastore.")
+ subprocess.run(["sysrepocfg", "--import=ietf-interfaces.xml",
+ "--datastore=startup", "--format=xml", "--leve=0",
+ "ietf-interfaces"])
+ subprocess.run(["sysrepocfg", "--import=ietf-interfaces.xml",
+ "--datastore=running", "--format=xml", "--leve=0",
+ "ietf-interfaces"])
+ os.chdir(directory)
+ time.sleep(2)
diff --git a/test/vpp_controler.py b/test/vpp_controler.py
new file mode 100644
index 0000000..561f5a0
--- /dev/null
+++ b/test/vpp_controler.py
@@ -0,0 +1,67 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+import subprocess
+import time
+import psutil
+
+class Vpp_controler:
+ def __init__(self):
+ self.cmd = "vpp"
+ self.ccmd = "vppctl"
+ self.configuration = "/root/src/sweetcomb/test/conf/vpp.conf"
+ self.process = None
+
+ def __del__(self):
+ #self.kill()
+ self.terminate()
+
+ def _default_conf_vpp(self):
+ if self.process is None:
+ return
+
+ subprocess.run(self.ccmd + " create host name vpp1", shell=True)
+ subprocess.run(self.ccmd + " create host name vpp2", shell=True)
+
+ def spawn(self):
+ self.process = subprocess.Popen([self.cmd, "-c", self.configuration],
+ stdout=subprocess.PIPE)
+ time.sleep(4)
+ self._default_conf_vpp()
+
+ def kill(self):
+ if self.process is None:
+ return
+
+ self.process.kill()
+ self.process = None
+
+ def terminate(self):
+ if self.process is None:
+ return
+
+ self.process.terminate()
+ for proc in psutil.process_iter(attrs=['pid', 'name']):
+ if 'vpp' in proc.info['name']:
+ proc.kill()
+
+ self.process = None
+
+ def show_interface(self):
+ subprocess.run(self.ccmd + " show int", shell=True)
+
+ def show_address(self):
+ subprocess.run(self.ccmd + " show int addr", shell=True)