PyTest, test to cover VM creation a ovs nets 18/2018/7
authormirabal <leonardo.mirabal@altran.com>
Thu, 6 Jul 2017 10:54:49 +0000 (05:54 -0500)
committermirabal <leonardo.mirabal@altran.com>
Thu, 10 Aug 2017 15:01:37 +0000 (10:01 -0500)
- test_osm_01_create_vm -> add 2 host and create one vm with dhcp
- test_osm_02_create_2_vm_ping_btw -> add 2 host and create two vm with dhcp and check ping between both vm.
- test_osm_03_test_service_openvim -> restart a service and check net status

- The test regresion launch the repo service and use the openvim CLI client available in the repo.

Change-Id: I3cb3c047fbd871f6d3a0c152dc6643a3248835d6
Signed-off-by: mirabal <leonardo.mirabal@altran.com>
22 files changed:
osm_openvim/host_thread.py
scripts/install-openvim.sh
setup.py
setup_lite.py
test/PY_TEST_README.rst [new file with mode: 0644]
test/__init__.py [new file with mode: 0644]
test/conftest.py [new file with mode: 0644]
test/fixtures/__init__.py [new file with mode: 0644]
test/fixtures/post/__init__.py [new file with mode: 0644]
test/fixtures/post/post_fixtures_delete.py [new file with mode: 0644]
test/fixtures/pre/__init__.py [new file with mode: 0644]
test/fixtures/pre/pre_fixtures_create.py [new file with mode: 0644]
test/flavors/cirros_flavor.yaml [new file with mode: 0644]
test/images/cirros_image.yaml [new file with mode: 0644]
test/lib/__init__.py [new file with mode: 0644]
test/lib/config_parser.py [new file with mode: 0644]
test/lib/ssh.py [new file with mode: 0644]
test/lib/test_utils.py [new file with mode: 0644]
test/servers/cirros_server_template.yaml [new file with mode: 0644]
test/tenants/test_tenant.yaml [new file with mode: 0644]
test/test_openvim_fake.yaml [new file with mode: 0644]
test/test_openvim_inf.py [new file with mode: 0644]

index 18a2eab..78be1c9 100644 (file)
@@ -842,7 +842,7 @@ class host_thread(threading.Thread):
             command = 'sudo ip netns exec {} cat {}'.format(dhcp_namespace, pid_file)
             content = self.run_command(command, ignore_exit_status=True)
             dns_pid = content.replace('\n', '')
-            command = 'sudo ip netns exec {} kill -9 '.format(dhcp_namespace, dns_pid)
+            command = 'sudo ip netns exec {} kill -9 {}'.format(dhcp_namespace, dns_pid)
             self.run_command(command, ignore_exit_status=True)
 
         except RunCommandException as e:
index feaee4e..b25a6b0 100755 (executable)
@@ -232,8 +232,8 @@ then
         "#################################################################\n"\
         "#####        INSTALL PYTHON PACKAGES                        #####\n"\
         "#################################################################"
-    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-libvirt python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr"
-    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML libvirt-python MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr"
+    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-libvirt python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr python-pexpect"
+    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML libvirt-python MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-netaddr python-pexpect"
     # The only way to install python-bottle on Centos7 is with easy_install or pip
     [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && easy_install -U bottle
 
index 6856ff1..aae8194 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,9 @@ _req = [
     "bottle",
     "MySQL-python",
     "paramiko",
-    "libvirt-python"
+    "libvirt-python",
+    "pytest",
+    "pexect"
 ]
 
 __scripts__ = ['openflow',
index 9d2b2ef..231c746 100755 (executable)
@@ -36,6 +36,8 @@ _req = [
     "bottle",
     "MySQL-python",
     "paramiko",
+    "pytest",
+    "pexect"
 ]
 
 __scripts__ = ['openflow-lib']
diff --git a/test/PY_TEST_README.rst b/test/PY_TEST_README.rst
new file mode 100644 (file)
index 0000000..2480844
--- /dev/null
@@ -0,0 +1,23 @@
+For launch openvim pytest regresion:
+
+    pytest -v -s test/test_openvim_inf.py --config=test/test_openvim_fake.yaml
+
+The regresion must be launch from an user included in the visudo.
+If the regression will be use real infrastructure (fake_mode=False) create a new yaml or modify
+the existing template -> test/test_openvim_fake.yaml
+
+
+test.yaml example:
+
+---
+host:
+   host_1: <path_to_host_descriptor_1>
+   host_2: <path_to_host_descriptor_1>
+   host_n: <path_to_host_descriptor_1>
+tenant: test/tenants/test_tenant.yaml
+flavor: test/flavors/cirros_flavor.yaml
+image: <path_to_iamge_descriptor>
+server: test/servers/cirros_server_template.yaml
+net: test/networks/net-example5.yaml
+fake_mode:  <True/False>                     # depend on openvimd.cfg mode (test,normal, host only, OF only, development)
+create_inf: <True/False>                     # Create host and mgmt net if True, if False host and mgmt net need to be precreated.
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644 (file)
index 0000000..2ad3e53
--- /dev/null
@@ -0,0 +1,6 @@
+import os
+
+def pytest_addoption(parser):
+    parser.addoption('--config',
+                     help='Indicate tests are run on CI server')
+    os.environ['OPENVIM_ROOT_FOLDER'] = os.getcwd()
diff --git a/test/fixtures/__init__.py b/test/fixtures/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/post/__init__.py b/test/fixtures/post/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/post/post_fixtures_delete.py b/test/fixtures/post/post_fixtures_delete.py
new file mode 100644 (file)
index 0000000..c2213bd
--- /dev/null
@@ -0,0 +1,126 @@
+import pytest
+import time
+from ...lib.test_utils import *
+
+
+@pytest.fixture(autouse=True)
+def post_install_service():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    yield post_install_service
+    print "Stoping service openvim "
+    service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+    execute_local("{} stop".format(service_path))
+
+
+@pytest.fixture()
+def post_delete_server(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_server
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+
+        if config['create_inf']:
+            vm_id = os.environ['OPENVIM_VM']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            execute_local("{} vm-delete {}".format(openvim_path, vm_id))
+
+
+@pytest.fixture()
+def post_delete_net(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_net
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            execute_local("{} net-delete -f {}".format(openvim_path, net_id))
+
+
+@pytest.fixture()
+def post_delete_vm():
+    """
+    Fixture to be executed after test
+    :return:
+    """
+    yield post_delete_vm
+    # destroy vm
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    out = execute_local('{} vm-delete -f'.format(openvim_path))
+
+
+@pytest.fixture()
+def post_delete_flavor():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_flavor
+
+    flavor_id = os.environ['OPENVIM_TEST_FLAVOR']
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    execute_local("{} flavor-delete -f {}".format(openvim_path, flavor_id))
+
+
+@pytest.fixture()
+def post_delete_image():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_image
+
+    img_id = os.environ['OPENVIM_TEST_IMAGE']
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    execute_local("{} image-delete -f {}".format(openvim_path, img_id))
+
+
+@pytest.fixture()
+def post_delete_host(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+
+    yield post_delete_host
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            host_ids = search_host_in_env_var()
+            for host_ids in host_ids:
+                openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+                execute_local("{} host-remove -f {}".format(openvim_path, os.environ[host_ids]))
+        time.sleep(30)
+
+
diff --git a/test/fixtures/pre/__init__.py b/test/fixtures/pre/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/fixtures/pre/pre_fixtures_create.py b/test/fixtures/pre/pre_fixtures_create.py
new file mode 100644 (file)
index 0000000..2ad733e
--- /dev/null
@@ -0,0 +1,168 @@
+import pytest
+import os
+from ...lib.ssh import *
+from ...lib.test_utils import *
+
+
+@pytest.fixture(autouse=True)
+def pre_launch_openvim_service():
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+
+    execute_local('{} stop'.format(service_path))
+    print "launching service openvim"
+    execute_local('{} start'.format(service_path))
+    out = execute_local('{} -h '.format(service_path))
+    assert out
+
+@pytest.fixture()
+def pre_create_flavor(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        flavor = config['flavor']
+
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+        flavor_id = execute_local("{} flavor-create {}".format(openvim_path, flavor))
+        flavor_id = parse_uuid(flavor_id)
+        assert flavor_id
+        os.environ['OPENVIM_TEST_FLAVOR'] = flavor_id
+
+
+@pytest.fixture()
+def pre_create_host(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+
+        if config['create_inf']:
+            hosts = config['host']
+            counter = 0
+            for key, value in hosts.iteritems():
+                openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+                host_id = execute_local("{} host-add {}".format(openvim_path, value))
+                host_id = parse_uuid(host_id)
+                assert host_id
+                os.environ['OPENVIM_TEST_HOST_' + str(counter)] = host_id
+                counter += 1
+
+
+@pytest.fixture()
+def pre_create_image(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        img = config['image']
+
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+        image_id = execute_local("{} image-create {}".format(openvim_path, img))
+        image_id = parse_uuid(image_id)
+        assert image_id
+        os.environ['OPENVIM_TEST_IMAGE'] = image_id
+
+
+@pytest.fixture()
+def pre_create_image(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        img = config['image']
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+        image_id = execute_local("{} image-create {}".format(openvim_path, img))
+        image_id = parse_uuid(image_id)
+        assert image_id
+        os.environ['OPENVIM_TEST_IMAGE'] = image_id
+
+
+@pytest.fixture()
+def pre_create_net(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            net_yaml = config['net']
+            openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+            net_id = execute_local("{} net-create {}".format(openvim_path, net_yaml))
+            net_id = parse_uuid(net_id)
+            assert net_id
+
+            vlan = execute_local("{} net-list {} -vvv | grep provider:vlan:".format(openvim_path, net_id))
+            vlan = vlan.replace('provider:vlan:','')
+            vlan = vlan.replace(' ', '')
+            vlan = vlan.replace('\n', '')
+
+            os.environ['OPENVIM_TEST_MGMT_NET'] = net_id
+            os.environ['OPENVIM_TEST_MGMT_NET_VLAN'] = vlan
+
+
+@pytest.fixture()
+def pre_create_tenant(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param monkeypatch:
+    :return:
+    """
+
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        tenant_yaml = config['tenant']
+        openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+
+        tenant_id = execute_local("{} tenant-create {}".format(openvim_path, tenant_yaml))
+        tenant_id = parse_uuid(tenant_id)
+        assert tenant_id
+        os.environ['OPENVIM_TENANT'] = tenant_id
+
+
+@pytest.fixture()
+def pre_init_db(request):
+    """
+    Fixture to be executed before test
+    :param request: argument for a fixture... can be a list, dict, etc
+    :param request:
+    :return:
+    """
+    if hasattr(request, 'config'):
+        config_path = request.config.getoption('config')
+        config = get_config(config_path)
+        if config['create_inf']:
+            init_db_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'database_utils', 'init_vim_db.sh')
+            execute_local('{}'.format(init_db_path))
diff --git a/test/flavors/cirros_flavor.yaml b/test/flavors/cirros_flavor.yaml
new file mode 100644 (file)
index 0000000..aacb0cf
--- /dev/null
@@ -0,0 +1,67 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# All Rights Reserved.
+#
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+flavor:
+    name:        flavor-cirros
+    description: flavor-cirros
+    # cloud type requirements
+    ram:         1024                    # Memory in MB. Ignored if provided 'memory' at 'extended'
+    vcpus:       1                       # Number of cpus.  Ignored if provided at 'extended'
+
+    # NFV type requirements
+    # allocating EXCLUSIVE resoureces in the same NUMA node. 
+   # extended:                             # optional
+    #    processor_ranking: 100            # minimal processor family. Not used in current version
+    #    numas:                            # list of numa set. Only one supported in current version
+    #    -  memory:         2              # GByte of huge pages at this numa
+
+           #Choose among one of "cores", "paired-threads", "threads"
+           #paired-threads: 2              # Cores with two paired hyper threads
+           #paired-threads-id: [[0,1],[2,3],[4,5],[6,7],[8,9]] # Guess pinning. By default follows incremental order
+           #threads:       10             # threads awereness of the hyperthreading
+           ##threads-id: [0,1,2,3,4,5,6,7,8,9] #Optional. Guess pinning
+           #cores:         5              # Complete cores, without hyperthreading. VIM ensures the other paired thread is idle
+           ##cores-id:      [0,1,2,3,4]   # Optional. Guess pinning of cores
+
+           #Optional: Dataplane needed interfaces
+#           interfaces:
+#           -   name:       xe0            # Optional. User friendly name
+#               vpci:       "0000:00:10.0" # Optional. Guess PCI
+#               bandwidth:  10 Gbps        # Needed minimun bandwidth
+#               dedicated:  "yes"          # "yes"(passthrough), "no"(sriov with vlan tags), "yes:sriov"(sriovi, but exclusive and without vlan tag)
+#           -   name:       xe1
+#               vpci:       "0000:00:11.0"
+#               bandwidth:  10 Gbps
+#               dedicated:  "no"
+
+        #Optional: List of extra devices
+#        devices:                       # order determines device letter asignation (hda, hdb, ...)
+#        -   type:      disk            # "disk","cdrom","xml","usb"
+#            imageRef:  37598e34-ccb3-11e4-a996-52540030594e # UUID of an image, only for disk,cdrom,xml
+            # vpci:      "0000:00:03.0"   # Optional, not for disk or cdrom
+            # xml:     'Only for type xml: a XML described device xml text. Do not use single quotes inside
+            #        The following words, if found, will be replaced:
+            #        __file__    by image path, (imageiRef must be provided)
+            #        __format__  by qcow2 or raw (imageRef must be provided)
+            #        __dev__     by device letter (b, c, d ...)
+            #        __vpci__    by vpci (vpci must be provided)
+
diff --git a/test/images/cirros_image.yaml b/test/images/cirros_image.yaml
new file mode 100644 (file)
index 0000000..a771d55
--- /dev/null
@@ -0,0 +1,33 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# All Rights Reserved.
+#
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+image:
+    name:         image-cirros-test
+    description:  image-cirros
+    path:         /opt/VNF/images/cirros-0.3.5-x86_64-disk.img
+    metadata:     # Optional extra metadata of the image. All fields are optional
+        use_incremental: "yes"          # "yes" by default, "no" Deployed using a incremental qcow2 image
+        #vpci:            "0000:10:00.0" #requiered PCI at guess
+        os_distro:       cirros            # operating system distribution
+        os_type:         linux        # operating system type "linux" by default, "windows"
+        os_version:      "7"            # operating system version
+        bus:            "ide"           # By default "virtio" for linux, "ide" for windows
diff --git a/test/lib/__init__.py b/test/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/lib/config_parser.py b/test/lib/config_parser.py
new file mode 100644 (file)
index 0000000..1981fce
--- /dev/null
@@ -0,0 +1,15 @@
+import yaml
+
+
+def get_config(file):
+    """
+    Parse test config file
+    :param file: 
+    :return: 
+    """
+    with open(file, 'r') as stream:
+        try:
+            return yaml.load(stream)
+        except yaml.YAMLError as exc:
+            print(exc)
+
diff --git a/test/lib/ssh.py b/test/lib/ssh.py
new file mode 100644 (file)
index 0000000..5480483
--- /dev/null
@@ -0,0 +1,126 @@
+import subprocess
+import pexpect
+
+
+def execute_local(cmd):
+    """
+    Execute a command in locally    
+    :param cmd: command to be executed
+    :return: 
+    """
+
+    print "execute_local cmd = {}".format(cmd)
+    p = subprocess.Popen(['bash', "-c", cmd], stdout=subprocess.PIPE)
+    out = p.stdout.read()
+    print "execute_local result = {}".format(out)
+    return out
+
+
+def execute_namespace(vlan, cmd):
+    """
+    Execute a command inside a namespace created by openvim 
+    :param vlan: namespace id
+    :param cmd: command to be executed
+    :return: 
+    """
+
+    n_cmd = 'sudo ip netns exec {}-qrouter {} '.format(vlan, cmd)
+    return execute_local(n_cmd)
+
+
+def ping_ok(vlan_id, ip, retries=200):
+    """
+
+    :param vlan_id: namespace id
+    :param ip: vm ip to be pinged
+    :param retries: retries, by default 200
+    :return: 
+    """
+    print 'waiting for vm to be active vm with ip = {}'.format(ip)
+    for i in xrange(retries):
+        try:
+            subprocess.check_output(['bash', "-c", "sudo ip netns exec {}-qrouter ping -c 1 {}".format(vlan_id, ip)])
+            return True
+        except Exception, e:
+            pass
+    return False
+
+
+def ping_ok_btw_2_vms(vlan_id, ip_1, ip_2, retires=8):
+    """
+    Check net connectivity between to VM
+    :param vlan_id: namepsace id
+    :param ip_1: first vm ip
+    :param ip_2: second vm ip
+    :param retires: 
+    :return: 
+    """
+    for i in xrange(retires):
+
+        try:
+            ns_cmd = 'sudo ip netns exec {}-qrouter '.format(vlan_id)
+            cmd = ns_cmd + ' ssh -oStrictHostKeyChecking=no cirros@{} "ping -c 1 {}"'.format(ip_1, ip_2)
+            child = pexpect.spawn(cmd)
+            child.expect('.*assword*')
+            child.sendline('cubswin:)')
+            child.sendline('cubswin:)')
+
+            cmd = ns_cmd + ' ssh -oStrictHostKeyChecking=no  cirros@{} "ping -c 1 {}"'.format(ip_2, ip_1)
+            child = pexpect.spawn(cmd)
+            child.expect('.*assword*')
+            child.sendline('cubswin:)')
+            child.sendline('cubswin:)')
+
+        except EOFError as e:
+            if i == retires:
+                return False
+            pass
+
+    return True
+
+
+def copy_rsa_keys_into_vm(vlan_id, ip, rsa_key_path):
+    """
+    copy an RSA key given by the user to a vm 
+    :param vlan_id: 
+    :param ip: 
+    :param rsa_key_path: 
+    :return: 
+    """
+
+    try:
+        execute_local('sudo ssh-keygen -f "/root/.ssh/known_hosts" -R  {}'.format(ip))
+        cmd = 'sudo ip netns exec {}-qrouter ssh-copy-id -i {} ' \
+              '-oStrictHostKeyChecking=no -f cirros@{}'.format(vlan_id, rsa_key_path + '.pub', ip)
+
+        print 'copy_rsa_keys_into_vm = ' + cmd
+        child = pexpect.spawn(cmd)
+        child.expect('.*assword*')
+        child.sendline('cubswin:)')
+        child.sendline('cubswin:)')
+        return True
+    except EOFError as e:
+        return False
+
+
+def execute_check_output(vlan_id, cmd):
+    """
+    Execute a command inside a namespace and raise an expection in case of command fail
+    :param vlan_id: namepsace id
+    :param cmd: command
+    :return: 
+    """
+    try:
+        cmd = "sudo ip netns exec {}-qrouter  {}".format(vlan_id, cmd)
+        print "execute_check_output = {}".format(cmd)
+        subprocess.check_output(['bash', "-c", cmd])
+        return True
+    except Exception, e:
+        print "error execute_check_output"
+        return False
+
+
+
+
+
+
diff --git a/test/lib/test_utils.py b/test/lib/test_utils.py
new file mode 100644 (file)
index 0000000..bf09899
--- /dev/null
@@ -0,0 +1,113 @@
+import yaml
+import re
+import os
+from string import Template
+from ssh import *
+
+
+def save_tmp_yaml(name, data):
+    """
+    Save a yaml file into a file to be declare in openvim
+    :param name: file name
+    :param data: 
+    :return: 
+    """
+    with open(name, "w") as text_file:
+        text_file.write(data)
+
+
+def delete_tmp_yaml(name):
+    """
+    Delete yaml form Filesystem
+    :param name: File name
+    :return: 
+    """
+    execute_local('rm {}'.format(name))
+
+
+def search_host_in_env_var():
+    """
+    Search for OPENVIM_TEST_HOST_X env var declare by pre_create_host fixture with the host id after creation. 
+    :return: All env vars founded
+    """
+    return search('OPENVIM_TEST_HOST_')
+
+
+def template_substitute(file_path, values):
+    """
+    Modify a Yaml template with values.
+    :param file_path: template file
+    :param values: values to be substituted
+    :return: a string with the file content modified
+    """
+    with open(file_path, 'r') as server_yaml:
+        template = Template(server_yaml.read())
+        server_yaml = template.safe_substitute(values)
+        return server_yaml
+
+
+def search(reg_ex):
+    """
+    Search for environment vars. 
+    :param reg_ex: regular expresion to be applied during the search
+    :return: return results
+    """
+    result = {}
+    for key in os.environ:
+        if reg_ex in key:
+            result[key] = os.environ[key]
+    return result
+
+
+def parse_uuid(data):
+    """
+    Parse an UUID value from a string given.
+    :param data: String to be evaluated
+    :return: the uuid value
+    """
+    match = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}', re.I).findall(data)
+    if match:
+        data = match[0].replace(' ', '')
+        data = data.replace('\n', '')
+        return data
+    else:
+        return []
+
+
+def get_config(data):
+    """
+    Parse test config file
+    :param data: config file path
+    :return: config dict 
+    """
+    with open(data, 'r') as stream:
+        try:
+            return yaml.load(stream)
+        except yaml.YAMLError as exc:
+            print(exc)
+
+
+def get_vm_ip(vm_id):
+    """
+    Parse vm id IP from openvim client.
+    :param vm_id: vm id
+    :return: IP value
+    """
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    ip = execute_local("{} vm-list {} -vvv | grep ip_address:".format(openvim_path, vm_id))
+    ip = ip.replace('ip_address:', '')
+    ip = ip.replace(' ', '')
+    return ip
+
+
+def get_net_status(net_id):
+    """
+    Parse a net status from openvim client
+    :param net_id: network id
+    :return: 
+    """
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    net_status = execute_local("{} net-list {} -vvv | grep status:".format(openvim_path, net_id))
+    net_status = net_status.replace('status:', '')
+    net_status = net_status.replace(' ', '')
+    return net_status
diff --git a/test/servers/cirros_server_template.yaml b/test/servers/cirros_server_template.yaml
new file mode 100644 (file)
index 0000000..1b1ae50
--- /dev/null
@@ -0,0 +1,35 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# All Rights Reserved.
+#
+# 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.
+#
+##
+
+server:
+    name:         vm-test        # name
+    description:  vm-cirros  # Optional user description
+    imageRef:     $OPENVIM_TEST_IMAGE
+    flavorRef:    $OPENVIM_TEST_FLAVOR
+    networks:  # List of control plane interfaces, Optional
+    -   name:     mgmt         #friendly user name
+        #vpci:     "0000:00:0a.0" #Optional guess PCI
+        uuid:     $OPENVIM_TEST_MGMT_NET
+        #mac_address:  #guess concrete mac address, by default one is asigned
+        #model:        "virtio","e1000","ne2k_pci","pcnet","rtl8139", By default auto, normally virtio
+    start:    "yes"    # "yes","no","paused".  By default it is started upon creted
+    hostId:   $HOST_ID
+   
+    # allocating EXCLUSIVE resoureces in the same NUMA node. 
+    # If provided, it overrides extended values at flavor
diff --git a/test/tenants/test_tenant.yaml b/test/tenants/test_tenant.yaml
new file mode 100644 (file)
index 0000000..31ed219
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openvim
+# All Rights Reserved.
+#
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: nfvlabs@tid.es
+##
+
+
+tenant:
+    name: test-tenant
+    description:  tenant for testing purpouse
diff --git a/test/test_openvim_fake.yaml b/test/test_openvim_fake.yaml
new file mode 100644 (file)
index 0000000..a303a4c
--- /dev/null
@@ -0,0 +1,10 @@
+host:
+   host_1: test/hosts/host-example0.yaml
+   host_2: test/hosts/host-example1.yaml
+tenant: test/tenants/test_tenant.yaml
+flavor: test/flavors/cirros_flavor.yaml
+image: test/images/cirros_image.yaml
+server: test/servers/cirros_server_template.yaml
+net: test/networks/net-example5.yaml
+fake_mode: False
+create_inf: True
diff --git a/test/test_openvim_inf.py b/test/test_openvim_inf.py
new file mode 100644 (file)
index 0000000..8e9bd3a
--- /dev/null
@@ -0,0 +1,194 @@
+import pytest
+import time
+from fixtures.pre.pre_fixtures_create import *
+from fixtures.post.post_fixtures_delete import *
+from lib.ssh import *
+from lib.test_utils import *
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net', 'post_delete_host')
+def test_osm_01_create_vm(request, pre_init_db,
+                          pre_create_host,
+                          pre_create_tenant,
+                          pre_create_net,
+                          pre_create_image,
+                          pre_create_flavor,
+                          post_delete_image,
+                          post_delete_flavor,
+                          post_delete_vm,
+                          post_delete_net,
+                          post_delete_host):
+    """
+    Create a vm and check network connection btw qrouter namespace and vm, evaluate using ping againt the ip given by
+    ovs dhcp server. Openvim is launched as a service by pre_launch_openvim_service before regresion start, is stopped
+    by  post_stop_openvim_service after test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_vm: Delete the vm declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+    config_path = request.config.getoption('config')
+    config = get_config(config_path)
+    if not config['fake_mode']:
+        vm_id = create_vm_per_host(config, 1)
+        assert vm_id
+        # get vlan id
+        vlan_id = os.environ['OPENVIM_TEST_MGMT_NET_VLAN']
+        assert vlan_id
+        # get vm ip
+        ip = get_vm_ip(vm_id)
+        assert ip
+        # check ping against the vm
+        result = ping_ok(vlan_id, ip)
+        assert result
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net','post_delete_host')
+def test_osm_02_create_2_vm_ping_btw(request, pre_init_db,
+                                     pre_create_host,
+                                     pre_create_tenant,
+                                     pre_create_net,
+                                     pre_create_image,
+                                     pre_create_flavor,
+                                     post_delete_flavor,
+                                     post_delete_image,
+                                     post_delete_vm,
+                                     post_delete_net,
+                                     post_delete_host):
+    """
+    Create 2 vms and check network connection btw qrouter namespace and vms, after ssh ready check ping btw both vms
+    to validate the vlxan mesh btw computes is ok. The vms ips are handle by ovs dhcp server. Openvim is launched as a
+    service by pre_launch_openvim_service before regresion start, is stopped by  post_stop_openvim_service after
+    test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_vm: Delete the vm declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+    config_path = request.config.getoption('config')
+    config = get_config(config_path)
+
+    if not config['fake_mode']:
+        vm_id_1 = create_vm_per_host(config, 0)
+        assert vm_id_1
+
+        vm_id_2 = create_vm_per_host(config, 1)
+        assert vm_id_2
+
+        # get vlan id
+        vlan_id = os.environ['OPENVIM_TEST_MGMT_NET_VLAN']
+        assert vlan_id
+
+        # get vm ip
+        ip_1 = get_vm_ip(vm_id_1)
+        ip_2 = get_vm_ip(vm_id_2)
+        assert ip_2
+
+        # check ping against the vms
+        result = ping_ok(vlan_id, ip_1)
+        assert result
+        result = ping_ok(vlan_id, ip_2)
+        assert result
+
+        # Wait for ssh to be ready
+        print "Wait for ssh to be ready"
+        time.sleep(90)
+        result = ping_ok_btw_2_vms(vlan_id, ip_1, ip_2)
+        assert result
+
+
+@pytest.mark.usefixtures('pre_init_db', 'pre_create_host', 'pre_create_tenant', 'pre_create_net', 'pre_create_image',
+                         'pre_create_flavor', 'post_delete_net','post_delete_host')
+def test_osm_03_test_service_openvim(request, pre_init_db,
+                                     pre_create_host,
+                                     pre_create_tenant,
+                                     pre_create_net,
+                                     pre_create_image,
+                                     pre_create_flavor,
+                                     post_delete_flavor,
+                                     post_delete_image,
+                                     post_delete_net,
+                                     post_delete_host):
+    """
+    Create a net, restart openvim service and check net status to avoid issues with preexisting nets during openvim
+    startup. Openvim is launched as a service by pre_launch_openvim_service before regresion start, is stopped by
+    post_stop_openvim_service after test regresion ends.
+
+    :param request: Users argument --config=<test yaml>
+    :param pre_init_db: initialize the openvim db
+    :param pre_create_host: Create hosts
+    :param pre_create_tenant: Create tenant
+    :param pre_create_net: Create a mgmt net
+    :param pre_create_image: Create an image
+    :param pre_create_flavor: Create a flavor
+    :param post_delete_image: Delete the image declare by the test
+    :param post_delete_flavor: Delete the flavor declare by the test
+    :param post_delete_net: Delete the mgmt net declare by the test
+    :param post_delete_host: remove the host attached by the test
+    :return:
+    """
+
+    net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+    status = get_net_status(net_id)
+    if 'ACTIVE' in status:
+        service_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'scripts', 'service-openvim')
+        execute_local('{} restart'.format(service_path))
+    else:
+        # force test fail if net status is not ACTIVE
+        assert None
+
+    status = get_net_status(net_id)
+    if 'ACTIVE' not in status:
+        assert None
+
+
+def create_vm_per_host(config, host_number=0):
+    """
+    Create a vm in an specific compute.
+    :param config: test config
+    :param host_number: compute number to be depolyed. 
+    :return: 
+    """
+    # get env var for server descriptor
+    tenant_id = os.environ['OPENVIM_TENANT']
+    image_id = os.environ['OPENVIM_TEST_IMAGE']
+    flavor_id = os.environ['OPENVIM_TEST_FLAVOR']
+    net_id = os.environ['OPENVIM_TEST_MGMT_NET']
+
+    values = {'OPENVIM_TENANT': tenant_id,
+              'OPENVIM_TEST_IMAGE': image_id,
+              'OPENVIM_TEST_FLAVOR': flavor_id,
+              'OPENVIM_TEST_MGMT_NET': net_id,
+              'HOST_ID': os.environ['OPENVIM_TEST_HOST_' + str(host_number)]
+              }
+
+    descriptor = template_substitute(config['server'], values)
+    save_tmp_yaml('tmp.yaml', descriptor)
+    # create vm
+    openvim_path = os.path.join(os.environ['OPENVIM_ROOT_FOLDER'], 'openvim')
+    vm_id = execute_local("{} vm-create {}".format(openvim_path, 'tmp.yaml'))
+    vm_id = parse_uuid(vm_id)
+    delete_tmp_yaml('tmp.yaml')
+    return vm_id