From: mirabal Date: Thu, 6 Jul 2017 10:54:49 +0000 (-0500) Subject: PyTest, test to cover VM creation a ovs nets X-Git-Tag: v3.0.0rc14~1 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fopenvim.git;a=commitdiff_plain;h=126e787a5795bc8294c0650e57188546fed8012e PyTest, test to cover VM creation a ovs nets - 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 --- diff --git a/osm_openvim/host_thread.py b/osm_openvim/host_thread.py index 18a2eab..78be1c9 100644 --- a/osm_openvim/host_thread.py +++ b/osm_openvim/host_thread.py @@ -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: diff --git a/scripts/install-openvim.sh b/scripts/install-openvim.sh index feaee4e..b25a6b0 100755 --- a/scripts/install-openvim.sh +++ b/scripts/install-openvim.sh @@ -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 diff --git a/setup.py b/setup.py index 6856ff1..aae8194 100755 --- a/setup.py +++ b/setup.py @@ -36,7 +36,9 @@ _req = [ "bottle", "MySQL-python", "paramiko", - "libvirt-python" + "libvirt-python", + "pytest", + "pexect" ] __scripts__ = ['openflow', diff --git a/setup_lite.py b/setup_lite.py index 9d2b2ef..231c746 100755 --- a/setup_lite.py +++ b/setup_lite.py @@ -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 index 0000000..2480844 --- /dev/null +++ b/test/PY_TEST_README.rst @@ -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: + host_2: + host_n: +tenant: test/tenants/test_tenant.yaml +flavor: test/flavors/cirros_flavor.yaml +image: +server: test/servers/cirros_server_template.yaml +net: test/networks/net-example5.yaml +fake_mode: # depend on openvimd.cfg mode (test,normal, host only, OF only, development) +create_inf: # 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 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..2ad3e53 --- /dev/null +++ b/test/conftest.py @@ -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 index 0000000..e69de29 diff --git a/test/fixtures/post/__init__.py b/test/fixtures/post/__init__.py new file mode 100644 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 index 0000000..c2213bd --- /dev/null +++ b/test/fixtures/post/post_fixtures_delete.py @@ -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 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 index 0000000..2ad733e --- /dev/null +++ b/test/fixtures/pre/pre_fixtures_create.py @@ -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 index 0000000..aacb0cf --- /dev/null +++ b/test/flavors/cirros_flavor.yaml @@ -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 index 0000000..a771d55 --- /dev/null +++ b/test/images/cirros_image.yaml @@ -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 index 0000000..e69de29 diff --git a/test/lib/config_parser.py b/test/lib/config_parser.py new file mode 100644 index 0000000..1981fce --- /dev/null +++ b/test/lib/config_parser.py @@ -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 index 0000000..5480483 --- /dev/null +++ b/test/lib/ssh.py @@ -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 index 0000000..bf09899 --- /dev/null +++ b/test/lib/test_utils.py @@ -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 index 0000000..1b1ae50 --- /dev/null +++ b/test/servers/cirros_server_template.yaml @@ -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 index 0000000..31ed219 --- /dev/null +++ b/test/tenants/test_tenant.yaml @@ -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 index 0000000..a303a4c --- /dev/null +++ b/test/test_openvim_fake.yaml @@ -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 index 0000000..8e9bd3a --- /dev/null +++ b/test/test_openvim_inf.py @@ -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= + :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= + :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= + :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