From 026a9bfaa656d0e5ade327feda64f17796d6f209 Mon Sep 17 00:00:00 2001 From: Valentin Boucher Date: Thu, 19 Jul 2018 11:35:50 -0400 Subject: New Heat IMS testcase In order to validate OpenStack Master deployment we create this new testcase using OpenStack Heat as an Orchestrator for Clearwater VNF JIRA: FUNCTEST-995 Change-Id: I8b7b74a3753c2d4d4614e9a2798283bd3f99d5d2 Signed-off-by: Valentin Boucher --- docker/vnf/Dockerfile | 8 +- docker/vnf/testcases.yaml | 17 +- docs/testing/user/userguide/test_details.rst | 22 ++ functest/ci/config_functest.yaml | 6 +- functest/ci/testcases.yaml | 17 +- functest/opnfv_tests/vnf/ims/clearwater.py | 213 +++++++++++++++++ .../opnfv_tests/vnf/ims/clearwater_ims_base.py | 181 --------------- functest/opnfv_tests/vnf/ims/cloudify_ims.py | 47 ++-- functest/opnfv_tests/vnf/ims/cloudify_ims.yaml | 29 +-- functest/opnfv_tests/vnf/ims/heat_ims.py | 256 +++++++++++++++++++++ functest/opnfv_tests/vnf/ims/heat_ims.yaml | 22 ++ functest/tests/unit/vnf/ims/test_clearwater.py | 42 ++++ functest/tests/unit/vnf/ims/test_ims_base.py | 42 ---- 13 files changed, 621 insertions(+), 281 deletions(-) create mode 100644 functest/opnfv_tests/vnf/ims/clearwater.py delete mode 100644 functest/opnfv_tests/vnf/ims/clearwater_ims_base.py create mode 100644 functest/opnfv_tests/vnf/ims/heat_ims.py create mode 100644 functest/opnfv_tests/vnf/ims/heat_ims.yaml create mode 100644 functest/tests/unit/vnf/ims/test_clearwater.py delete mode 100644 functest/tests/unit/vnf/ims/test_ims_base.py diff --git a/docker/vnf/Dockerfile b/docker/vnf/Dockerfile index 09bb6fb5a..192c528ad 100644 --- a/docker/vnf/Dockerfile +++ b/docker/vnf/Dockerfile @@ -4,7 +4,8 @@ ARG BRANCH=master ARG OPENSTACK_TAG=stable/queens ARG VIMS_TEST_TAG=release-129 ARG QUAFF_TAG=59213d6d8ee29433552bb75f505cdc96b0b18909 -ARG VIMS_TAG=fraser +ARG CLOUDIFY_VIMS_TAG=fraser +ARG HEAT_VIMS_TAG=release-129 ARG VROUTER_TAG=fraser ARG JUJU_TAG=tags/juju-2.2.5 @@ -27,7 +28,8 @@ RUN apk --no-cache add --update \ git clone https://github.com/Metaswitch/quaff /src/vims-test/quaff && \ (cd /src/vims-test/quaff && git checkout $QUAFF_TAG) && \ git clone --depth 1 -b $VIMS_TEST_TAG https://github.com/Metaswitch/clearwater-build-infra /src/vims-test/build-infra && \ - git clone --depth 1 -b $VIMS_TAG https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git /src/vims && \ + git clone --depth 1 -b $CLOUDIFY_VIMS_TAG https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git /src/cloudify_vims && \ + git clone --depth 1 -b $HEAT_VIMS_TAG https://github.com/Metaswitch/clearwater-heat.git /src/heat_vims && \ git clone --depth 1 -b $VROUTER_TAG https://github.com/oolorg/opnfv-vnf-vyos-blueprint.git /src/opnfv-vnf-vyos-blueprint && \ git clone https://github.com/RebacaInc/abot_charm.git /src/epc-requirements/abot_charm && \ python3 -m pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ @@ -38,7 +40,7 @@ RUN apk --no-cache add --update \ go install -v github.com/juju/juju/... && \ rm -r $GOPATH/src/ $GOPATH/pkg && \ (cd /src/vims-test && bundle config build.nokogiri --use-system-libraries && bundle install --system) && \ - rm -r upper-constraints.txt upper-constraints.opnfv.txt /src/vims-test/.git /src/vims/.git /src/vims-test/quaff/.git \ + rm -r upper-constraints.txt upper-constraints.opnfv.txt /src/vims-test/.git /src/cloudify_vims/.git /src/heat_vims/.git /src/vims-test/quaff/.git \ /src/vims-test/build-infra/.git /src/opnfv-vnf-vyos-blueprint/.git \ /src/epc-requirements/abot_charm/.git && \ apk del .build-deps diff --git a/docker/vnf/testcases.yaml b/docker/vnf/testcases.yaml index 1b2bd9822..1f0817708 100644 --- a/docker/vnf/testcases.yaml +++ b/docker/vnf/testcases.yaml @@ -21,7 +21,7 @@ tiers: - case_name: cloudify_ims project_name: functest - criteria: 80 + criteria: 100 blocking: false description: >- This test case deploys an OpenSource vIMS solution from @@ -33,6 +33,21 @@ tiers: module: 'functest.opnfv_tests.vnf.ims.cloudify_ims' class: 'CloudifyIms' + - + case_name: heat_ims + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case deploys an OpenSource vIMS solution from + Clearwater using the OpenStack Heat orchestrator. + It also runs some signaling traffic. + dependencies: + - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' + run: + module: 'functest.opnfv_tests.vnf.ims.heat_ims' + class: 'HeatIms' + - case_name: vyos_vrouter project_name: functest diff --git a/docs/testing/user/userguide/test_details.rst b/docs/testing/user/userguide/test_details.rst index 992b546f5..03020f70e 100644 --- a/docs/testing/user/userguide/test_details.rst +++ b/docs/testing/user/userguide/test_details.rst @@ -376,6 +376,27 @@ The Clearwater architecture is described as follows: :align: center :alt: vIMS architecture +heat_ims +^^^^^^^^ +The IP Multimedia Subsystem or IP Multimedia Core Network Subsystem (IMS) is an +architectural framework for delivering IP multimedia services. + +vIMS has been integrated in Functest to demonstrate the capability to deploy a +relatively complex NFV scenario on the OPNFV platform. The deployment of a +complete functional VNF allows the test of most of the essential functions +needed for a NFV platform. + +The goal of this test suite consists of: + +* deploy a Clearwater vIMS (IP Multimedia Subsystem) VNF using + OpenStack Heat orchestrator based on a HOT template defined in `[17]`_ +* run suite of signaling tests on top of this VNF + +The Clearwater architecture is described as follows: + +.. figure:: ../../../images/clearwater-architecture-v2.png + :align: center + :alt: vIMS architecture vyos-vrouter ^^^^^^^^^^^^ @@ -466,3 +487,4 @@ The kubernetes testcases are distributed across various Tiers: .. _`[14]`: https://github.com/oolorg/opnfv-functest-vrouter .. _`[15]`: https://www.rebaca.com/abot-test-orchestration-tool/ .. _`[16]`: https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md +.. _`[17]`: https://github.com/Metaswitch/clearwater-heat/blob/release-129/clearwater.yaml diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index 5aa02be68..7e51ee790 100644 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -165,10 +165,8 @@ vnf: tenant_name: cloudify_ims tenant_description: vIMS config: cloudify_ims.yaml - cloudify_ims_perf: - tenant_name: cloudify_ims_perf - tenant_description: vIMS - config: cloudify_ims_perf.yaml + heat_ims: + config: heat_ims.yaml orchestra_openims: tenant_name: orchestra_openims tenant_description: OpenIMS deployed with Open Baton diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index ecd1d800c..511935e6b 100644 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -479,7 +479,7 @@ tiers: - case_name: cloudify_ims project_name: functest - criteria: 80 + criteria: 100 blocking: false description: >- This test case deploys an OpenSource vIMS solution from @@ -491,6 +491,21 @@ tiers: module: 'functest.opnfv_tests.vnf.ims.cloudify_ims' class: 'CloudifyIms' + - + case_name: heat_ims + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case deploys an OpenSource vIMS solution from + Clearwater using the OpenStack Heat orchestrator. + It also runs some signaling traffic. + dependencies: + - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' + run: + module: 'functest.opnfv_tests.vnf.ims.heat_ims' + class: 'HeatIms' + - case_name: vyos_vrouter project_name: functest diff --git a/functest/opnfv_tests/vnf/ims/clearwater.py b/functest/opnfv_tests/vnf/ims/clearwater.py new file mode 100644 index 000000000..96f1b40c6 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/clearwater.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# +# Copyright (c) 2017 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 + +"""Ease testing any Clearwater deployment""" + +import logging +import os +import re +import shlex +import shutil +import subprocess +import time + +import pkg_resources +import requests + +from functest.utils import config +import functest.utils.functest_utils as ft_utils + +__author__ = ("Valentin Boucher , " + "Helen Yao ") + + +class ClearwaterTesting(object): + """vIMS clearwater base usable by several orchestrators""" + + def __init__(self, case_name, ellis_ip): + self.logger = logging.getLogger(__name__) + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/ims') + self.data_dir = getattr(config.CONF, 'dir_ims_data') + self.result_dir = os.path.join( + getattr(config.CONF, 'dir_results'), case_name) + self.test_dir = getattr(config.CONF, 'dir_repo_vims_test') + + if not os.path.exists(self.data_dir): + os.makedirs(self.data_dir) + if not os.path.exists(self.result_dir): + os.makedirs(self.result_dir) + + self.ellis_ip = ellis_ip + + def availability_check_by_creating_numbers(self, + signup_code='secret', + two_numbers=False): + """Create one or two numbers""" + assert self.ellis_ip + output_dict = {} + self.logger.debug('Ellis IP: %s', self.ellis_ip) + output_dict['ellis_ip'] = self.ellis_ip + account_url = 'http://{0}/accounts'.format(self.ellis_ip) + params = {"password": "functest", + "full_name": "opnfv functest user", + "email": "functest@opnfv.org", + "signup_code": signup_code} + output_dict['login'] = params + + number_res = self._create_ellis_account(account_url, params) + output_dict['number'] = number_res + + session_url = 'http://{0}/session'.format(self.ellis_ip) + session_data = { + 'username': params['email'], + 'password': params['password'], + 'email': params['email'] + } + cookies = self._get_ellis_session_cookies(session_url, session_data) + + number_url = 'http://{0}/accounts/{1}/numbers'.format( + self.ellis_ip, params['email']) + self.logger.debug('Create 1st calling number on Ellis') + number_res = self._create_ellis_number(number_url, cookies) + + if two_numbers: + self.logger.debug('Create 2nd calling number on Ellis') + number_res = self._create_ellis_number(number_url, cookies) + output_dict['number2'] = number_res + + return output_dict + + def _create_ellis_account(self, account_url, params): + i = 50 + for iloop in range(i): + try: + req = requests.post(account_url, data=params) + if req.status_code == 201: + account_res = req.json() + self.logger.info( + 'Account %s is created on Ellis\n%s', + params.get('full_name'), account_res) + return account_res + else: + raise Exception("Cannot create ellis account") + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot create ellis account", iloop + 1) + time.sleep(25) + raise Exception( + "Unable to create an account {}".format( + params.get('full_name'))) + + def _get_ellis_session_cookies(self, session_url, params): + i = 15 + for iloop in range(i): + try: + req = requests.post(session_url, data=params) + if req.status_code == 201: + cookies = req.cookies + self.logger.debug('cookies: %s', cookies) + return cookies + else: + raise Exception('Failed to get cookies for Ellis') + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot get cookies for Ellis", iloop + 1) + time.sleep(10) + raise Exception('Failed to get cookies for Ellis') + + def _create_ellis_number(self, number_url, cookies): + i = 30 + for iloop in range(i): + try: + req = requests.post(number_url, cookies=cookies) + if req.status_code == 200: + number_res = req.json() + self.logger.info( + 'Calling number is created: %s', number_res) + return number_res + else: + if req and req.json(): + reason = req.json()['reason'] + else: + reason = req + self.logger.info("cannot create a number: %s", reason) + raise Exception('Failed to create a number') + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot create a number", iloop + 1) + time.sleep(25) + raise Exception('Failed to create a number') + + def run_clearwater_live_test(self, dns_ip, public_domain, + bono_ip=None, ellis_ip=None, + signup_code='secret'): + """Run the Clearwater live tests + + It first runs dnsmasq to reach clearwater services by FQDN and then the + Clearwater live tests. All results are saved in ims_test_output.txt. + + Returns: + - a dict containing the overall results + - None on error + """ + # pylint: disable=too-many-locals,too-many-arguments + self.logger.info('Run Clearwater live test') + dns_file = '/etc/resolv.conf' + dns_file_bak = '/etc/resolv.conf.bak' + self.logger.debug('Backup %s -> %s', dns_file, dns_file_bak) + shutil.copy(dns_file, dns_file_bak) + cmd = ("dnsmasq -d -u root --server=/clearwater.opnfv/{0} " + "-r /etc/resolv.conf.bak".format(dns_ip)) + dnsmasq_process = subprocess.Popen(shlex.split(cmd)) + script = ('echo -e "nameserver {0}" > {1};' + 'cd {2};' + 'rake test[{3}] SIGNUP_CODE={4}' + .format('127.0.0.1', + dns_file, + self.test_dir, + public_domain, + signup_code)) + if bono_ip and ellis_ip: + subscript = ' PROXY={0} ELLIS={1}'.format(bono_ip, ellis_ip) + script = '{0}{1}'.format(script, subscript) + script = ('{0}{1}'.format(script, ' --trace')) + cmd = "/bin/bash -c '{0}'".format(script) + self.logger.debug('Live test cmd: %s', cmd) + output_file = os.path.join(self.result_dir, "ims_test_output.txt") + ft_utils.execute_command(cmd, + error_msg='Clearwater live test failed', + output_file=output_file) + dnsmasq_process.kill() + with open(dns_file_bak, 'r') as bak_file: + result = bak_file.read() + with open(dns_file, 'w') as dfile: + dfile.write(result) + + with open(output_file, 'r') as ofile: + result = ofile.read() + + if result != "": + self.logger.debug(result) + + vims_test_result = {} + try: + grp = re.search( + r'(\d+) failures out of (\d+) tests run.*' + r'(\d+) tests skipped', result, re.MULTILINE | re.DOTALL) + assert grp + vims_test_result["failures"] = int(grp.group(1)) + vims_test_result["total"] = int(grp.group(2)) + vims_test_result["skipped"] = int(grp.group(3)) + vims_test_result['passed'] = ( + int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1))) + except Exception: # pylint: disable=broad-except + self.logger.exception("Cannot parse live tests results") + return None + return vims_test_result diff --git a/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py b/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py deleted file mode 100644 index 34d3c628d..000000000 --- a/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2017 All rights reserved -# This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# -# http://www.apache.org/licenses/LICENSE-2.0 - -"""Ease testing any Clearwater deployment""" - -import logging -import os -import re -import shlex -import shutil -import subprocess -import time - -import pkg_resources -import requests - -from functest.utils import config -import functest.utils.functest_utils as ft_utils - -__author__ = ("Valentin Boucher , " - "Helen Yao ") - - -class ClearwaterOnBoardingBase(object): - """vIMS clearwater base usable by several orchestrators""" - - def __init__(self, case_name): - self.logger = logging.getLogger(__name__) - self.case_dir = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/vnf/ims') - self.data_dir = getattr(config.CONF, 'dir_ims_data') - self.result_dir = os.path.join( - getattr(config.CONF, 'dir_results'), case_name) - self.test_dir = getattr(config.CONF, 'dir_repo_vims_test') - - if not os.path.exists(self.data_dir): - os.makedirs(self.data_dir) - if not os.path.exists(self.result_dir): - os.makedirs(self.result_dir) - - def config_ellis(self, ellis_ip, signup_code='secret', two_numbers=False): - """Create one or two numbers""" - output_dict = {} - self.logger.debug('Configure Ellis: %s', ellis_ip) - output_dict['ellis_ip'] = ellis_ip - account_url = 'http://{0}/accounts'.format(ellis_ip) - params = {"password": "functest", - "full_name": "opnfv functest user", - "email": "functest@opnfv.org", - "signup_code": signup_code} - req = requests.post(account_url, data=params) - output_dict['login'] = params - if req.status_code != 201 and req.status_code != 409: - raise Exception( - "Unable to create an account {}\n{}".format( - params, req.text)) - self.logger.debug( - 'Account %s is created on Ellis\n%s', params, req.json()) - - session_url = 'http://{0}/session'.format(ellis_ip) - session_data = { - 'username': params['email'], - 'password': params['password'], - 'email': params['email'] - } - req = requests.post(session_url, data=session_data) - if req.status_code != 201: - raise Exception('Failed to get cookie for Ellis\n{}'.format( - req.text)) - cookies = req.cookies - self.logger.debug('Cookies: %s', cookies) - - number_url = 'http://{0}/accounts/{1}/numbers'.format( - ellis_ip, params['email']) - self.logger.debug('Create 1st calling number on Ellis') - i = 30 - while req.status_code != 200 and i > 0: - try: - number_res = self._create_ellis_number(number_url, cookies) - break - except Exception: # pylint: disable=broad-except - if i == 1: - self.logger.exception("Unable to create a number") - raise Exception("Unable to create a number") - self.logger.info("Unable to create a number. Retry ..") - time.sleep(25) - i = i - 1 - output_dict['number'] = number_res - - if two_numbers: - self.logger.debug('Create 2nd calling number on Ellis') - number_res = self._create_ellis_number(number_url, cookies) - output_dict['number2'] = number_res - - return output_dict - - def _create_ellis_number(self, number_url, cookies): - req = requests.post(number_url, cookies=cookies) - - if req.status_code != 200: - if req and req.json(): - reason = req.json()['reason'] - else: - reason = req - raise Exception("Unable to create a number: %s" % reason) - number_res = req.json() - self.logger.info('Calling number is created: %s', number_res) - return number_res - - def run_clearwater_live_test(self, dns_ip, public_domain, - bono_ip=None, ellis_ip=None, - signup_code='secret'): - """Run the Clearwater live tests - - It first runs dnsmasq to reach clearwater services by FQDN and then the - Clearwater live tests. All results are saved in ims_test_output.txt. - - Returns: - - a dict containing the overall results - - None on error - """ - # pylint: disable=too-many-locals,too-many-arguments - self.logger.info('Run Clearwater live test') - dns_file = '/etc/resolv.conf' - dns_file_bak = '/etc/resolv.conf.bak' - self.logger.debug('Backup %s -> %s', dns_file, dns_file_bak) - shutil.copy(dns_file, dns_file_bak) - cmd = ("dnsmasq -d -u root --server=/clearwater.opnfv/{0} " - "-r /etc/resolv.conf.bak".format(dns_ip)) - dnsmasq_process = subprocess.Popen(shlex.split(cmd)) - script = ('echo -e "nameserver {0}" > {1};' - 'cd {2};' - 'rake test[{3}] SIGNUP_CODE={4}' - .format('127.0.0.1', - dns_file, - self.test_dir, - public_domain, - signup_code)) - if bono_ip and ellis_ip: - subscript = ' PROXY={0} ELLIS={1}'.format(bono_ip, ellis_ip) - script = '{0}{1}'.format(script, subscript) - script = ('{0}{1}'.format(script, ' --trace')) - cmd = "/bin/bash -c '{0}'".format(script) - self.logger.debug('Live test cmd: %s', cmd) - output_file = os.path.join(self.result_dir, "ims_test_output.txt") - ft_utils.execute_command(cmd, - error_msg='Clearwater live test failed', - output_file=output_file) - dnsmasq_process.kill() - with open(dns_file_bak, 'r') as bak_file: - result = bak_file.read() - with open(dns_file, 'w') as dfile: - dfile.write(result) - - with open(output_file, 'r') as ofile: - result = ofile.read() - - if result != "": - self.logger.debug(result) - - vims_test_result = {} - try: - grp = re.search( - r'(\d+) failures out of (\d+) tests run.*' - r'(\d+) tests skipped', result, re.MULTILINE | re.DOTALL) - assert grp - vims_test_result["failures"] = int(grp.group(1)) - vims_test_result["total"] = int(grp.group(2)) - vims_test_result["skipped"] = int(grp.group(3)) - vims_test_result['passed'] = ( - int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1))) - except Exception: # pylint: disable=broad-except - self.logger.exception("Cannot parse live tests results") - return None - return vims_test_result diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.py b/functest/opnfv_tests/vnf/ims/cloudify_ims.py index 36862bd43..7ec647c69 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.py +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.py @@ -22,7 +22,7 @@ import scp import six from functest.core import cloudify -from functest.opnfv_tests.vnf.ims import clearwater_ims_base +from functest.opnfv_tests.vnf.ims import clearwater from functest.utils import config from functest.utils import env @@ -61,20 +61,17 @@ class CloudifyIms(cloudify.Cloudify): self.case_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/vnf/ims') config_file = os.path.join(self.case_dir, self.config) - self.orchestrator = dict( - requirements=get_config("orchestrator.requirements", config_file), - ) + self.details['orchestrator'] = dict( name=get_config("orchestrator.name", config_file), version=get_config("orchestrator.version", config_file), status='ERROR', result='' ) - self.__logger.debug("Orchestrator configuration %s", self.orchestrator) + self.vnf = dict( descriptor=get_config("vnf.descriptor", config_file), - inputs=get_config("vnf.inputs", config_file), - requirements=get_config("vnf.requirements", config_file) + inputs=get_config("vnf.inputs", config_file) ) self.details['vnf'] = dict( descriptor_version=self.vnf['descriptor']['version'], @@ -90,6 +87,7 @@ class CloudifyIms(cloudify.Cloudify): self.image_alt = None self.flavor_alt = None + self.clearwater = None def check_requirements(self): if env.get('NEW_USER_ROLE').lower() == "admin": @@ -194,33 +192,34 @@ class CloudifyIms(cloudify.Cloudify): execution = wait_for_execution( self.cfy_client, execution, self.__logger, timeout=3600) - duration = time.time() - start_time - self.__logger.info(execution) - if execution.status == 'terminated': - self.details['vnf'].update(status='PASS', duration=duration) - self.result += 1/3 * 100 - result = True - else: - self.details['vnf'].update(status='FAIL', duration=duration) - result = False - return result + if execution.status != 'terminated': + self.details['vnf'].update(status='FAIL', + duration=time.time() - start_time) + return False + + ellis_ip = self.cfy_client.deployments.outputs.get( + self.vnf['descriptor'].get('name'))['outputs']['ellis_ip'] + self.clearwater = clearwater.ClearwaterTesting(self.case_name, + ellis_ip) + self.clearwater.availability_check_by_creating_numbers() + + self.details['vnf'].update(status='PASS', + duration=time.time() - start_time) + self.result += 1/3 * 100 + return True def test_vnf(self): """Run test on clearwater ims instance.""" start_time = time.time() - testing = clearwater_ims_base.ClearwaterOnBoardingBase(self.case_name) - outputs = self.cfy_client.deployments.outputs.get( - self.vnf['descriptor'].get('name'))['outputs'] - dns_ip = outputs['dns_ip'] - ellis_ip = outputs['ellis_ip'] - testing.config_ellis(ellis_ip) + dns_ip = self.cfy_client.deployments.outputs.get( + self.vnf['descriptor'].get('name'))['outputs']['dns_ip'] if not dns_ip: return False - short_result = testing.run_clearwater_live_test( + short_result = self.clearwater.run_clearwater_live_test( dns_ip=dns_ip, public_domain=self.vnf['inputs']["public_domain"]) duration = time.time() - start_time diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml index 6808cf33d..869281a20 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml @@ -1,35 +1,14 @@ --- -tenant_images: - ubuntu_14.04: - /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.img - cloudify_manager_4.0: - /home/opnfv/functest/images/cloudify-manager-premium-4.0.1.qcow2 orchestrator: name: cloudify version: '4.0' - requirements: - flavor: - name: cloudify.medium - ram_min: 4096 - os_image: 'cloudify_manager_4.0' vnf: name: clearwater - version: '107' + version: '129' descriptor: - file_name: /src/vims/openstack-blueprint.yaml + file_name: /src/cloudify_vims/openstack-blueprint.yaml name: clearwater-opnfv - version: '122' - requirements: - flavor: - name: cloudify.small - ram_min: 2048 - compute_quotas: - cores: 50 - instances: 15 - network_quotas: - security_group: 20 - security_group_rule: 100 - port: 50 + version: '129' inputs: image_id: 'ubuntu_14.04' flavor_id: 'cloudify.small' @@ -46,4 +25,4 @@ vnf: homer_cluster_size: 1 vnf_test_suite: name: clearwater-live-test - version: "1.0" + version: '1.0' diff --git a/functest/opnfv_tests/vnf/ims/heat_ims.py b/functest/opnfv_tests/vnf/ims/heat_ims.py new file mode 100644 index 000000000..32783dae7 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/heat_ims.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python + +# Copyright (c) 2018 Kontron, Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +"""HeatIms testcase implementation.""" + +from __future__ import division + +import logging +import os +import re +import time +import yaml + +import pkg_resources +from xtesting.core import testcase + +from functest.core import singlevm +from functest.opnfv_tests.vnf.ims import clearwater +from functest.utils import config +from functest.utils import env + +__author__ = "Valentin Boucher " + + +class HeatIms(singlevm.VmReady2): + # pylint: disable=too-many-instance-attributes + """Clearwater vIMS deployed with Heat Orchestrator Case.""" + + __logger = logging.getLogger(__name__) + + filename_alt = ('/home/opnfv/functest/images/' + 'ubuntu-14.04-server-cloudimg-amd64-disk1.img') + + flavor_alt_ram = 2048 + flavor_alt_vcpus = 2 + flavor_alt_disk = 25 + + quota_security_group = 20 + quota_security_group_rule = 100 + quota_port = 50 + + def __init__(self, **kwargs): + """Initialize HeatIms testcase object.""" + if "case_name" not in kwargs: + kwargs["case_name"] = "heat_ims" + super(HeatIms, self).__init__(**kwargs) + + # Retrieve the configuration + try: + self.config = getattr( + config.CONF, 'vnf_{}_config'.format(self.case_name)) + except Exception: + raise Exception("VNF config file not found") + + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/ims') + config_file = os.path.join(self.case_dir, self.config) + + self.vnf = dict( + descriptor=get_config("vnf.descriptor", config_file), + parameters=get_config("vnf.inputs", config_file) + ) + self.details['vnf'] = dict( + descriptor_version=self.vnf['descriptor']['version'], + name=get_config("vnf.name", config_file), + version=get_config("vnf.version", config_file), + ) + self.__logger.debug("VNF configuration: %s", self.vnf) + + self.image_alt = None + self.flavor_alt = None + self.keypair = None + self.stack = None + self.clearwater = None + self.role = None + + def execute(self): + # pylint: disable=too-many-locals,too-many-statements + """ + Prepare Tenant/User + + network, security group, fip, VM creation + """ + self.orig_cloud.set_network_quotas( + self.project.project.name, + security_group=self.quota_security_group, + security_group_rule=self.quota_security_group_rule, + port=self.quota_port) + if not self.orig_cloud.get_role("heat_stack_owner"): + self.role = self.orig_cloud.create_role("heat_stack_owner") + self.orig_cloud.grant_role( + "heat_stack_owner", user=self.project.user.id, + project=self.project.project.id, + domain=self.project.domain.id) + self.keypair = self.cloud.create_keypair( + '{}-kp_{}'.format(self.case_name, self.guid)) + self.__logger.debug("keypair: %s", self.keypair) + + if (self.deploy_vnf() and self.test_vnf()): + self.result = 100 + return 0 + self.result = 1/3 * 100 + return 1 + + def run(self, **kwargs): + """Deploy and test clearwater + + Here are the main actions: + - deploy clearwater stack via heat + - test the vnf instance + + Returns: + - TestCase.EX_OK + - TestCase.EX_RUN_ERROR on error + """ + status = testcase.TestCase.EX_RUN_ERROR + try: + assert self.cloud + self.start_time = time.time() + self.result = 0 + if not self.execute(): + self.result = 100 + status = testcase.TestCase.EX_OK + except Exception: # pylint: disable=broad-except + self.__logger.exception('Cannot run %s', self.case_name) + finally: + self.stop_time = time.time() + return status + + def deploy_vnf(self): + """Deploy Clearwater IMS.""" + start_time = time.time() + + self.image_alt = self.publish_image_alt() + self.flavor_alt = self.create_flavor_alt() + # KeyPair + Image + Flavor OK + + descriptor = self.vnf['descriptor'] + parameters = self.vnf['parameters'] + + parameters['public_mgmt_net_id'] = self.ext_net.id + parameters['public_sig_net_id'] = self.ext_net.id + parameters['flavor'] = self.flavor_alt.name + parameters['image'] = self.image_alt.name + parameters['key_name'] = self.keypair.name + parameters['external_mgmt_dns_ip'] = env.get('NAMESERVER') + parameters['external_sig_dns_ip'] = env.get('NAMESERVER') + + self.__logger.info("Create Heat Stack") + self.stack = self.cloud.create_stack( + name=descriptor.get('name'), + template_file=descriptor.get('file_name'), + wait=True, **parameters) + self.__logger.debug("stack: %s", self.stack) + + servers = self.cloud.list_servers(detailed=True) + self.__logger.debug("servers: %s", servers) + for server in servers: + if not self.check_regex_in_console( + server.name, regex='Cloud-init .* finished at ', loop=60): + return False + if 'ellis' in server.name: + self.__logger.debug("server: %s", server) + ellis_ip = server.public_v4 + + assert ellis_ip + self.clearwater = clearwater.ClearwaterTesting(self.case_name, + ellis_ip) + # This call can take time and many retry because Heat is + # an infrastructure orchestrator so when Heat say "stack created" + # it means that all OpenStack ressources are created but not that + # Clearwater are up and ready (Cloud-Init script still running) + self.clearwater.availability_check_by_creating_numbers() + + duration = time.time() - start_time + + self.details['vnf'].update(status='PASS', duration=duration) + self.result += 1/3 * 100 + + return True + + def test_vnf(self): + """Run test on clearwater ims instance.""" + start_time = time.time() + + outputs = self.cloud.get_stack(self.stack.id).outputs + self.__logger.debug("stack outputs: %s", outputs) + dns_ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', str(outputs))[0] + + if not dns_ip: + return False + + short_result = self.clearwater.run_clearwater_live_test( + dns_ip=dns_ip, + public_domain=self.vnf['parameters']["zone"]) + duration = time.time() - start_time + self.__logger.info(short_result) + self.details['test_vnf'] = dict(result=short_result, + duration=duration) + try: + vnf_test_rate = short_result['passed'] / ( + short_result['total'] - short_result['skipped']) + # orchestrator + vnf + test_vnf + self.result += vnf_test_rate / 3 * 100 + except ZeroDivisionError: + self.__logger.error("No test has been executed") + self.details['test_vnf'].update(status='FAIL') + return False + except Exception: # pylint: disable=broad-except + self.__logger.exception("Cannot calculate results") + self.details['test_vnf'].update(status='FAIL') + return False + return True if vnf_test_rate > 0 else False + + def clean(self): + """Clean created objects/functions.""" + assert self.cloud + try: + if self.stack: + self.cloud.delete_stack(self.stack.id, wait=True) + except Exception: # pylint: disable=broad-except + self.__logger.exception("Cannot clean stack ressources") + super(HeatIms, self).clean() + if self.role: + self.orig_cloud.delete_role(self.role.id) + + +# ---------------------------------------------------------- +# +# YAML UTILS +# +# ----------------------------------------------------------- +def get_config(parameter, file_path): + """ + Get config parameter. + + Returns the value of a given parameter in file.yaml + parameter must be given in string format with dots + Example: general.openstack.image_name + """ + with open(file_path) as config_file: + file_yaml = yaml.safe_load(config_file) + config_file.close() + value = file_yaml + for element in parameter.split("."): + value = value.get(element) + if value is None: + raise ValueError("The parameter %s is not defined in" + " reporting.yaml" % parameter) + return value diff --git a/functest/opnfv_tests/vnf/ims/heat_ims.yaml b/functest/opnfv_tests/vnf/ims/heat_ims.yaml new file mode 100644 index 000000000..883c4dffe --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/heat_ims.yaml @@ -0,0 +1,22 @@ +--- +orchestrator: + name: heat + version: '4.0' +vnf: + name: clearwater + version: '129' + descriptor: + file_name: /src/heat_vims/clearwater.yaml + name: clearwater-opnfv + version: '129' + inputs: + zone: clearwater.opnfv + dn_range_start: "6505550000" + dn_range_length: "1000" + bono_cluster_size: 1 + sprout_cluster_size: 1 + vellum_cluster_size: 1 + dime_cluster_size: 1 + homer_cluster_size: 1 + dnssec_key: + GkBraPnditvP2Em4oXV5wUTawmZaGGuO+Jt3ZnFkznGV3zFoQ+Ak13nuuOnO0JV5FqAr/KitdW6siqjXSjROXg== diff --git a/functest/tests/unit/vnf/ims/test_clearwater.py b/functest/tests/unit/vnf/ims/test_clearwater.py new file mode 100644 index 000000000..93f8ffe4a --- /dev/null +++ b/functest/tests/unit/vnf/ims/test_clearwater.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +# pylint: disable=missing-docstring + +import logging +import unittest + +import mock + +from functest.opnfv_tests.vnf.ims import clearwater + + +class ClearwaterTesting(unittest.TestCase): + + def setUp(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.makedirs'): + self.ims_vnf = clearwater.ClearwaterTesting("foo", "0.0.0.0") + + self.mock_post = mock.Mock() + attrs = {'status_code': 201, + 'cookies': ""} + self.mock_post.configure_mock(**attrs) + + self.mock_post_200 = mock.Mock() + attrs = {'status_code': 200, + 'cookies': ""} + self.mock_post_200.configure_mock(**attrs) + + self.mock_post_500 = mock.Mock() + attrs = {'status_code': 500, + 'cookies': ""} + self.mock_post_200.configure_mock(**attrs) + +if __name__ == "__main__": + logging.disable(logging.CRITICAL) + unittest.main(verbosity=2) diff --git a/functest/tests/unit/vnf/ims/test_ims_base.py b/functest/tests/unit/vnf/ims/test_ims_base.py deleted file mode 100644 index a3f7e2354..000000000 --- a/functest/tests/unit/vnf/ims/test_ims_base.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python - -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 - -# pylint: disable=missing-docstring - -import logging -import unittest - -import mock - -from functest.opnfv_tests.vnf.ims import clearwater_ims_base as ims_base - - -class ClearwaterOnBoardingBaseTesting(unittest.TestCase): - - def setUp(self): - with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' - 'os.makedirs'): - self.ims_vnf = ims_base.ClearwaterOnBoardingBase("foo") - - self.mock_post = mock.Mock() - attrs = {'status_code': 201, - 'cookies': ""} - self.mock_post.configure_mock(**attrs) - - self.mock_post_200 = mock.Mock() - attrs = {'status_code': 200, - 'cookies': ""} - self.mock_post_200.configure_mock(**attrs) - - self.mock_post_500 = mock.Mock() - attrs = {'status_code': 500, - 'cookies': ""} - self.mock_post_200.configure_mock(**attrs) - -if __name__ == "__main__": - logging.disable(logging.CRITICAL) - unittest.main(verbosity=2) -- cgit 1.2.3-korg