diff options
Diffstat (limited to 'functest')
60 files changed, 3338 insertions, 2607 deletions
diff --git a/functest/api/base.py b/functest/api/base.py index ffc56786..75f059b7 100644 --- a/functest/api/base.py +++ b/functest/api/base.py @@ -45,6 +45,11 @@ class ApiResource(Resource): return action, args + def _get_args(self): # pylint: disable=no-self-use + """ Convert the unicode to string for request.args """ + args = api_utils.change_to_str_in_dict(request.args) + return args + def _dispatch_post(self): """ Dispatch request """ action, args = self._post_args() diff --git a/functest/api/resources/v1/envs.py b/functest/api/resources/v1/envs.py index 9c455198..fb76fa63 100644 --- a/functest/api/resources/v1/envs.py +++ b/functest/api/resources/v1/envs.py @@ -10,11 +10,12 @@ Resources to handle environment related requests """ +import IPy from flask import jsonify from functest.api.base import ApiResource -from functest.cli.commands.cli_env import Env from functest.api.common import api_utils +from functest.cli.commands.cli_env import Env import functest.utils.functest_utils as ft_utils @@ -38,3 +39,41 @@ class V1Envs(ApiResource): return api_utils.result_handler(status=1, data=str(err)) return api_utils.result_handler( status=0, data="Prepare env successfully") + + def update_hosts(self, hosts_info): # pylint: disable=no-self-use + """ Update hosts info """ + + if not isinstance(hosts_info, dict): + return api_utils.result_handler( + status=1, data='Error, args should be a dict') + + for key, value in hosts_info.items(): + if key: + try: + IPy.IP(value) + except Exception: # pylint: disable=broad-except + return api_utils.result_handler( + status=1, data='The IP %s is invalid' % value) + else: + return api_utils.result_handler( + status=1, data='Domain name is absent') + + try: + functest_flag = "# SUT hosts info for Functest" + hosts_list = ('\n{} {} {}'.format(ip, host_name, functest_flag) + for host_name, ip in hosts_info.items()) + + with open("/etc/hosts", 'r') as file_hosts: + origin_lines = [line for line in file_hosts + if functest_flag not in line] + + with open("/etc/hosts", 'w') as file_hosts: + file_hosts.writelines(origin_lines) + file_hosts.write(functest_flag) + file_hosts.writelines(hosts_list) + except Exception: # pylint: disable=broad-except + return api_utils.result_handler( + status=1, data='Error when updating hosts info') + else: + return api_utils.result_handler( + status=0, data='Update hosts info successfully') diff --git a/functest/api/resources/v1/tasks.py b/functest/api/resources/v1/tasks.py index 7086e707..e05db51b 100644 --- a/functest/api/resources/v1/tasks.py +++ b/functest/api/resources/v1/tasks.py @@ -11,9 +11,10 @@ Resources to retrieve the task results """ - +import errno import json import logging +import os import uuid from flask import jsonify @@ -21,13 +22,14 @@ from flask import jsonify from functest.api.base import ApiResource from functest.api.common import api_utils from functest.api.database.v1.handlers import TasksHandler +from functest.utils.constants import CONST LOGGER = logging.getLogger(__name__) -class V1Tasks(ApiResource): - """ V1Tasks Resource class""" +class V1Task(ApiResource): + """ V1Task Resource class""" def get(self, task_id): # pylint: disable=no-self-use """ GET the result of the task id """ @@ -56,3 +58,38 @@ class V1Tasks(ApiResource): result = {'status': status, 'result': json.loads(task.result)} return jsonify(result) + + +class V1TaskLog(ApiResource): + """ V1TaskLog Resource class""" + + def get(self, task_id): # pylint: disable=no-self-use + """ GET the log of the task id """ + try: + uuid.UUID(task_id) + except ValueError: + return api_utils.result_handler(status=1, data='Invalid task id') + + task_handler = TasksHandler() + try: + task = task_handler.get_task_by_taskid(task_id) + except ValueError: + return api_utils.result_handler(status=1, data='No such task id') + + task_log_dir = CONST.__getattribute__('dir_results') + + try: + with open(os.path.join(task_log_dir, + '{}.log'.format(task_id)), 'r') as log_file: + data = log_file.readlines() + except OSError as err: + if err.errno == errno.ENOENT: + return api_utils.result_handler( + status=1, data='Log file does not exist') + + return api_utils.result_handler( + status=1, data='Error with log file') + + return_data = {'data': data} + + return api_utils.result_handler(status=task.status, data=return_data) diff --git a/functest/api/resources/v1/testcases.py b/functest/api/resources/v1/testcases.py index f146c24c..d708cf37 100644 --- a/functest/api/resources/v1/testcases.py +++ b/functest/api/resources/v1/testcases.py @@ -11,10 +11,12 @@ Resources to handle testcase related requests """ -import os import logging +import os +import pkg_resources import uuid +import ConfigParser from flask import abort, jsonify from functest.api.base import ApiResource @@ -84,6 +86,7 @@ class V1Testcase(ApiResource): """ The built_in function to run a test case """ case_name = args.get('testcase') + self._update_logging_ini(args.get('task_id')) if not os.path.isfile(CONST.__getattribute__('env_active')): raise Exception("Functest environment is not ready.") @@ -113,3 +116,17 @@ class V1Testcase(ApiResource): } return {'result': result} + + def _update_logging_ini(self, task_id): # pylint: disable=no-self-use + """ Update the log file for each task""" + config = ConfigParser.RawConfigParser() + config.read( + pkg_resources.resource_filename('functest', 'ci/logging.ini')) + log_path = os.path.join(CONST.__getattribute__('dir_results'), + '{}.log'.format(task_id)) + config.set('handler_file', 'args', '("{}",)'.format(log_path)) + + with open( + pkg_resources.resource_filename( + 'functest', 'ci/logging.ini'), 'wb') as configfile: + config.write(configfile) diff --git a/functest/api/urls.py b/functest/api/urls.py index f7bcae38..0cc22f80 100644 --- a/functest/api/urls.py +++ b/functest/api/urls.py @@ -25,8 +25,9 @@ URLPATTERNS = [ # GET /api/v1/functest/envs => GET environment Url('/api/v1/functest/envs', 'v1_envs'), - # POST /api/v1/functest/envs/action , {"action":"prepare"} - # => Prepare environment + # POST /api/v1/functest/envs/action + # {"action":"prepare"} => Prepare environment + # {"action":"update_hosts", "args": {}} => Update hosts info Url('/api/v1/functest/envs/action', 'v1_envs'), # GET /api/v1/functest/openstack/credentials => GET credentials @@ -62,5 +63,9 @@ URLPATTERNS = [ # GET /api/v1/functest/tasks/<task_id> # => GET the result of the task id - Url('/api/v1/functest/tasks/<task_id>', 'v1_tasks') + Url('/api/v1/functest/tasks/<task_id>', 'v1_task'), + + # GET /api/v1/functest/tasks/<task_id>/log + # => GET the log of the task + Url('/api/v1/functest/tasks/<task_id>/log', 'v1_task_log') ] diff --git a/functest/ci/config_aarch64_patch.yaml b/functest/ci/config_aarch64_patch.yaml index de82dbc7..6b3699b4 100644 --- a/functest/ci/config_aarch64_patch.yaml +++ b/functest/ci/config_aarch64_patch.yaml @@ -18,6 +18,14 @@ os: hw_firmware_type: 'uefi' short_id: 'ubuntu16.04' hw_video_model: 'vga' + ubuntu: + disk_file: /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-arm64-uefi1.img + extra_properties: + hw_firmware_type: 'uefi' + hw_video_model: 'vga' + centos: + disk_file: /home/opnfv/functest/images/CentOS-7-aarch64-GenericCloud.qcow2 + vping: image_name: TestVM diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index 37e518e6..5ff5c824 100644 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -6,18 +6,17 @@ general: repo_tempest: /src/tempest dir_repo_releng: /home/opnfv/repos/releng repo_vims_test: /src/vims-test - repo_onos: /home/opnfv/repos/onos repo_barometer: /home/opnfv/repos/barometer repo_doctor: /home/opnfv/repos/doctor repo_odl_test: /src/odl_test repo_fds: /src/fds repo_securityscan: /home/opnfv/repos/securityscanning - repo_vrouter: /home/opnfv/repos/vnfs/vrouter functest: /home/opnfv/functest results: /home/opnfv/functest/results functest_conf: /home/opnfv/functest/conf functest_data: /home/opnfv/functest/data ims_data: /home/opnfv/functest/data/ims/ + router_data: /home/opnfv/functest/data/router/ functest_images: /home/opnfv/functest/images rally_inst: /root/.rally @@ -61,6 +60,25 @@ snaps: disk_file: /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.img centos: disk_file: /home/opnfv/functest/images/CentOS-7-x86_64-GenericCloud.qcow2 + # All of these values are optional and will override the values retrieved + # by the RC file +# os_creds_override: +# username: {user} +# password: {password} +# auth_url: {auth_url} +# project_name: {project_name} +# identity_api_version: {2|3} +# network_api_version: {2} +# compute_api_version: {2} +# image_api_version: {1|2} +# user_domain_id: {user_domain_id} +# project_domain_id: {projects_domain_id} +# interface: {interface} +# cacert: {True|False} +# proxy_settings: +# host: {proxy_host} +# port: {proxy_port} +# ssh_proxy_cmd: {OpenSSH -o ProxyCommand value} vping: ping_timeout: 200 @@ -86,11 +104,6 @@ vping: cleanup_objects: True unique_names: True -onos_sfc: - image_base_url: http://artifacts.opnfv.org/sfc/demo - image_name: TestSfcVm - image_file_name: firewall_block_image.img - odl_sfc: image_base_url: "http://artifacts.opnfv.org/sfc/images" image_name: sfc_nsh_danube @@ -140,24 +153,11 @@ vnf: tenant_name: orchestra_clearwaterims tenant_description: Clearwater IMS deployed with Open Baton config: orchestra.yaml + vyos_vrouter: + tenant_name: vrouter + tenant_description: vRouter + config: cloudify_vrouter.yaml -ONOS: - general: - onosbench_username: 'root' - onosbench_password: 'root' - onoscli_username: 'root' - onoscli_password: 'root' - runtimeout: 300 - environment: - OCT: '10.20.0.1' - OC1: '10.20.0.7' - OC2: '10.20.0.7' - OC3: '10.20.0.7' - OCN: '10.20.0.4' - OCN2: '10.20.0.5' - installer_master: '10.20.0.2' - installer_master_username: 'root' - installer_master_password: 'r00tme' promise: tenant_name: promise tenant_description: promise Functionality Testing diff --git a/functest/ci/download_images.sh b/functest/ci/download_images.sh index d9203335..367ad8d9 100644 --- a/functest/ci/download_images.sh +++ b/functest/ci/download_images.sh @@ -10,14 +10,15 @@ https://cloud-images.ubuntu.com/releases/14.04/release/ubuntu-14.04-server-cloud https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2 https://cloud-images.ubuntu.com/releases/16.04/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img http://repository.cloudifysource.org/cloudify/4.0.1/sp-release/cloudify-manager-premium-4.0.1.qcow2 +http://marketplace.openbaton.org:8082/api/v1/images/52e2ccc0-1dce-4663-894d-28aab49323aa/img http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-lxc.tar.gz http://download.cirros-cloud.net/daily/20161201/cirros-d161201-aarch64-disk.img http://download.cirros-cloud.net/daily/20161201/cirros-d161201-aarch64-initramfs http://download.cirros-cloud.net/daily/20161201/cirros-d161201-aarch64-kernel +https://cloud-images.ubuntu.com/releases/14.04/release/ubuntu-14.04-server-cloudimg-arm64-uefi1.img +http://cloud.centos.org/altarch/7/images/aarch64/CentOS-7-aarch64-GenericCloud.qcow2.xz +https://sourceforge.net/projects/ool-opnfv/files/vyos-1.1.7.img EOF -# vnf requires also the next image which cannot be downloaded by jjobs -# http://marketplace.openbaton.org:8082/api/v1/images/52e2ccc0-1dce-4663-894d-28aab49323aa/img - -exit $? +xz --decompress --force --keep ${1:-/home/opnfv/functest/images}/CentOS-7-aarch64-GenericCloud.qcow2.xz diff --git a/functest/ci/prepare_env.py b/functest/ci/prepare_env.py index 9ed585f3..a354dbe8 100644 --- a/functest/ci/prepare_env.py +++ b/functest/ci/prepare_env.py @@ -33,7 +33,7 @@ actions = ['start', 'check'] logger = logging.getLogger('functest.ci.prepare_env') handler = None # set the architecture to default -pod_arch = os.getenv("HOST_ARCH", None) +pod_arch = os.getenv("POD_ARCH", None) arch_filter = ['aarch64'] CONFIG_FUNCTEST_PATH = pkg_resources.resource_filename( diff --git a/functest/ci/run_tests.py b/functest/ci/run_tests.py index 63a50dea..feafa89e 100644 --- a/functest/ci/run_tests.py +++ b/functest/ci/run_tests.py @@ -188,12 +188,12 @@ class Runner(object): self.run_tier(tier) def main(self, **kwargs): - if kwargs['noclean']: - self.clean_flag = False - if kwargs['report']: - self.report_flag = True + if 'noclean' in kwargs: + self.clean_flag = not kwargs['noclean'] + if 'report' in kwargs: + self.report_flag = kwargs['report'] try: - if kwargs['test']: + if 'test' in kwargs: self.source_rc_file() logger.debug("Test args: %s", kwargs['test']) if self._tiers.get_tier(kwargs['test']): diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index adc7e09e..868fbecd 100644 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -207,22 +207,6 @@ tiers: - /src/fds/testing/robot - - case_name: onos - project_name: functest - criteria: 100 - blocking: true - description: >- - Test Suite for the ONOS SDN Controller. It integrates - some test suites from upstream using TestON as the test - framework. - dependencies: - installer: '' - scenario: 'onos' - run: - module: 'functest.opnfv_tests.sdn.onos.onos' - class: 'Onos' - - - case_name: snaps_smoke project_name: functest criteria: 100 @@ -324,10 +308,10 @@ tiers: criteria: 100 blocking: false description: >- - Test suite for odl-sfc to test two chains and two SFs + Test suite for odl-sfc to test two chains with one SF and one chain with two SFs dependencies: - installer: '(apex)|(fuel)' - scenario: 'odl_l2-sfc' + installer: '' + scenario: 'odl.*sfc' run: module: 'functest.core.feature' class: 'BashFeature' @@ -335,21 +319,6 @@ tiers: cmd: 'run_sfc_tests.py' - - case_name: onos_sfc - enabled: false - project_name: functest - criteria: 100 - blocking: true - description: >- - Test Suite for onos-sfc to test sfc function. - dependencies: - installer: '' - scenario: 'onos-sfc' - run: - module: 'functest.opnfv_tests.sdn.onos.onos' - class: 'OnosSfc' - - - case_name: parser-basics enabled: false project_name: parser @@ -385,18 +354,17 @@ tiers: - case_name: barometercollectd - enabled: false + enabled: true project_name: barometer criteria: 100 blocking: false description: >- - Test suite for the Barometer project. Separate tests verify the - proper configuration and functionality of the following - collectd plugins Ceilometer, Hugepages, Memory RAS (mcelog), - and OVS Events + Test suite for the Barometer project. Separate tests verify + the proper configuration and basic functionality of all the + collectd plugins as described in the Project Release Plan dependencies: - installer: 'fuel' - scenario: 'kvm_ovs_dpdk_bar' + installer: 'apex' + scenario: 'bar' run: module: 'baro_tests.barometer' class: 'BarometerCollectd' @@ -508,7 +476,6 @@ tiers: - case_name: vyos_vrouter - enabled: false project_name: functest criteria: 100 blocking: false @@ -518,5 +485,5 @@ tiers: installer: 'fuel' scenario: 'nosdn-nofeature' run: - module: 'functest.opnfv_tests.vnf.router.vyos_vrouter' - class: 'VrouterVnf' + module: 'functest.opnfv_tests.vnf.router.cloudify_vrouter' + class: 'CloudifyVrouter' diff --git a/functest/energy/energy.py b/functest/energy/energy.py index c410e84f..508f18e7 100644 --- a/functest/energy/energy.py +++ b/functest/energy/energy.py @@ -18,6 +18,7 @@ from functools import wraps import requests import urllib3 +from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils @@ -91,7 +92,7 @@ class EnergyRecorder(object): # Singleton pattern for energy_recorder_api static member # Load only if not previouly done if EnergyRecorder.energy_recorder_api is None: - environment = ft_utils.get_pod_name() + environment = CONST.__getattribute__('NODE_NAME') # API URL energy_recorder_uri = ft_utils.get_functest_config( diff --git a/functest/opnfv_tests/mano/orchestra.py b/functest/opnfv_tests/mano/orchestra.py deleted file mode 100644 index 955f82ce..00000000 --- a/functest/opnfv_tests/mano/orchestra.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import functest.core.feature as base - - -class Orchestra(base.Feature): - def __init__(self, **kwargs): - if "project_name" not in kwargs: - kwargs["project_name"] = "orchestra" - if "case_name" not in kwargs: - kwargs["case_name"] = "orchestra" - kwargs['repo'] = 'dir_repo_orchestra' - super(Orchestra, self).__init__(**kwargs) - # TODO - # self.cmd = "%s/tests/run.sh %s/tests" % (self.repo, self.repo) diff --git a/functest/opnfv_tests/openstack/rally/blacklist.txt b/functest/opnfv_tests/openstack/rally/blacklist.txt index 95bea2b7..099d6864 100644 --- a/functest/opnfv_tests/openstack/rally/blacklist.txt +++ b/functest/opnfv_tests/openstack/rally/blacklist.txt @@ -6,6 +6,38 @@ scenario: - joid tests: - NovaServers.boot_server_from_volume_and_delete + - + scenarios: + - '^os-' # all scenarios + installers: + - '.+' # all installers + tests: + # Following tests currently fail due to required Gnocchi API: + # HTTP 410: "This telemetry installation is configured to use + # Gnocchi. Please use the Gnocchi API available on the + # metric endpoint to retrieve data." + # Issue: https://bugs.launchpad.net/rally/+bug/1704322 + - CeilometerMeters.list_matched_meters + - CeilometerMeters.list_meters + - CeilometerQueries.create_and_query_samples + - CeilometerResource.get_tenant_resources + - CeilometerResource.list_matched_resources + - CeilometerResource.list_resources + - CeilometerSamples.list_matched_samples + - CeilometerSamples.list_samples + - CeilometerStats.create_meter_and_get_stats + - CeilometerStats.get_stats + - + scenarios: + - '^os-' # all scenarios + installers: + - '.+' # all installers + tests: + # Following test currently fails due to but in + # python-ceilometerclient during fetching of event_types + # Bug: https://bugs.launchpad.net/ubuntu/+bug/1704138 + # Fix: https://review.openstack.org/#/c/483402/ + - CeilometerEvents.create_user_and_list_event_types functionality: - diff --git a/functest/opnfv_tests/openstack/rally/rally.py b/functest/opnfv_tests/openstack/rally/rally.py index 6b7c49ca..fdef8bed 100644 --- a/functest/opnfv_tests/openstack/rally/rally.py +++ b/functest/opnfv_tests/openstack/rally/rally.py @@ -34,8 +34,8 @@ LOGGER = logging.getLogger(__name__) class RallyBase(testcase.OSGCTestCase): """Base class form Rally testcases implementation.""" - TESTS = ['authenticate', 'glance', 'cinder', 'heat', 'keystone', - 'neutron', 'nova', 'quotas', 'vm', 'all'] + TESTS = ['authenticate', 'glance', 'ceilometer', 'cinder', 'heat', + 'keystone', 'neutron', 'nova', 'quotas', 'vm', 'all'] GLANCE_IMAGE_NAME = CONST.__getattribute__('openstack_image_name') GLANCE_IMAGE_FILENAME = CONST.__getattribute__('openstack_image_file_name') GLANCE_IMAGE_PATH = os.path.join( diff --git a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-ceilometer.yaml b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-ceilometer.yaml new file mode 100644 index 00000000..7efb5a83 --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-ceilometer.yaml @@ -0,0 +1,458 @@ + CeilometerMeters.list_meters: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + limit: 50 + metadata_query: + status: "terminated" + sla: + {{ no_failures_sla() }} + + CeilometerResource.list_resources: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + limit: 50 + metadata_query: + status: "terminated" + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_alarm_and_get_history: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + state: "ok" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_delete_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_get_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_list_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_alarm_history: + - + args: + orderby: !!null + limit: !!null + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_alarms: + - + args: + filter: {"and": [{"!=": {"state": "dummy_state"}},{"=": {"type": "threshold"}}]} + orderby: !!null + limit: 10 + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_samples: + - + args: + filter: {"=": {"counter_unit": "instance"}} + orderby: !!null + limit: 10 + counter_name: "cpu_util" + counter_type: "gauge" + counter_unit: "instance" + counter_volume: 1.0 + resource_id: "resource_id" + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_update_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerStats.create_meter_and_get_stats: + - + args: + user_id: "user-id" + resource_id: "resource-id" + counter_volume: 1.0 + counter_unit: "" + counter_type: "cumulative" + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_get_event: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_list_events: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_list_event_types: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerTraits.create_user_and_list_trait_descriptions: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerTraits.create_user_and_list_traits: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerStats.get_stats: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + meter_name: "benchmark_meter" + filter_by_user_id: true + filter_by_project_id: true + filter_by_resource_id: true + metadata_query: + status: "terminated" + period: 300 + groupby: "resource_id" + sla: + {{ no_failures_sla() }} + + CeilometerResource.get_tenant_resources: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "cpu_util" + counter_type: "gauge" + counter_volume: 1.0 + counter_unit: "instance" + {% endcall %} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.list_alarms: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerSamples.list_matched_samples: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "cpu_util" + counter_type: "gauge" + counter_unit: "instance" + counter_volume: 1.0 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 60 + metadata_list: + - status: "active" + name: "fake_resource" + deleted: "False" + created_at: "2015-09-04T12:34:19.000000" + - status: "not_active" + name: "fake_resource_1" + deleted: "False" + created_at: "2015-09-10T06:55:12.000000" + {% endcall %} + args: + limit: 50 + filter_by_user_id: true + filter_by_project_id: true + filter_by_resource_id: true + metadata_query: + status: "not_active" + sla: + {{ no_failures_sla() }} + + CeilometerMeters.list_matched_meters: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + limit: 50 + filter_by_user_id: true + filter_by_project_id: true + filter_by_resource_id: true + metadata_query: + status: "terminated" + sla: + {{ no_failures_sla() }} + + CeilometerResource.list_matched_resources: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + limit: 50 + filter_by_user_id: true + filter_by_project_id: true + metadata_query: + status: "terminated" + sla: + {{ no_failures_sla() }} + + CeilometerSamples.list_samples: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "cpu_util" + counter_type: "gauge" + counter_unit: "instance" + counter_volume: 1.0 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 60 + metadata_list: + - status: "active" + name: "fake_resource" + deleted: "False" + created_at: "2015-09-04T12:34:19.000000" + - status: "not_active" + name: "fake_resource_1" + deleted: "False" + created_at: "2015-09-10T06:55:12.000000" + batch_size: 5 + {% endcall %} + args: + limit: 50 + metadata_query: + status: "not_active" + sla: + {{ no_failures_sla() }} + diff --git a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-ceilometer.yaml b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-ceilometer.yaml new file mode 100644 index 00000000..bb070cd3 --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-ceilometer.yaml @@ -0,0 +1,247 @@ + CeilometerAlarms.create_alarm_and_get_history: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + state: "ok" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_delete_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_get_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_list_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_alarm_history: + - + args: + orderby: !!null + limit: !!null + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_alarms: + - + args: + filter: {"and": [{"!=": {"state": "dummy_state"}},{"=": {"type": "threshold"}}]} + orderby: !!null + limit: 10 + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerQueries.create_and_query_samples: + - + args: + filter: {"=": {"counter_unit": "instance"}} + orderby: !!null + limit: 10 + counter_name: "cpu_util" + counter_type: "gauge" + counter_unit: "instance" + counter_volume: 1.0 + resource_id: "resource_id" + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.create_and_update_alarm: + - + args: + meter_name: "ram_util" + threshold: 10.0 + type: "threshold" + statistic: "avg" + alarm_actions: ["http://localhost:8776/alarm"] + ok_actions: ["http://localhost:8776/ok"] + insufficient_data_actions: ["http://localhost:8776/notok"] + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_get_event: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_list_events: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerEvents.create_user_and_list_event_types: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerTraits.create_user_and_list_trait_descriptions: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerTraits.create_user_and_list_traits: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + CeilometerStats.get_stats: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "benchmark_meter" + counter_type: "gauge" + counter_unit: "%" + counter_volume: 100 + resources_per_tenant: 100 + samples_per_resource: 100 + timestamp_interval: 10 + metadata_list: + - + status: "active" + name: "rally benchmark on" + deleted: "false" + - + status: "terminated" + name: "rally benchmark off" + deleted: "true" + {% endcall %} + args: + meter_name: "benchmark_meter" + filter_by_user_id: true + filter_by_project_id: true + filter_by_resource_id: true + metadata_query: + status: "terminated" + period: 300 + groupby: "resource_id" + sla: + {{ no_failures_sla() }} + + CeilometerResource.get_tenant_resources: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + ceilometer: + counter_name: "cpu_util" + counter_type: "gauge" + counter_volume: 1.0 + counter_unit: "instance" + {% endcall %} + sla: + {{ no_failures_sla() }} + + CeilometerAlarms.list_alarms: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} diff --git a/functest/opnfv_tests/openstack/rally/task.yaml b/functest/opnfv_tests/openstack/rally/task.yaml index 033edb83..65f101fb 100644 --- a/functest/opnfv_tests/openstack/rally/task.yaml +++ b/functest/opnfv_tests/openstack/rally/task.yaml @@ -31,6 +31,10 @@ {%- include "var/opnfv-neutron.yaml"-%} {% endif %} +{% if "ceilometer" in service_list %} +{%- include "var/opnfv-ceilometer.yaml"-%} +{% endif %} + {% if "quotas" in service_list %} {%- include "var/opnfv-quotas.yaml"-%} {% endif %} diff --git a/functest/opnfv_tests/openstack/refstack_client/refstack_client.py b/functest/opnfv_tests/openstack/refstack_client/refstack_client.py index 86053ccf..17e02466 100644 --- a/functest/opnfv_tests/openstack/refstack_client/refstack_client.py +++ b/functest/opnfv_tests/openstack/refstack_client/refstack_client.py @@ -28,12 +28,13 @@ from functest.opnfv_tests.openstack.refstack_client.tempest_conf \ from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils +import functest.utils.openstack_utils as os_utils # logging configuration """ LOGGER = logging.getLogger(__name__) -class RefstackClient(testcase.OSGCTestCase): +class RefstackClient(testcase.TestCase): """RefstackClient testcase implementation class.""" def __init__(self, **kwargs): @@ -41,6 +42,7 @@ class RefstackClient(testcase.OSGCTestCase): if "case_name" not in kwargs: kwargs["case_name"] = "refstack_defcore" super(RefstackClient, self).__init__(**kwargs) + self.tempestconf = None self.conf_path = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/refstack_client/refstack_tempest.conf') @@ -57,6 +59,13 @@ class RefstackClient(testcase.OSGCTestCase): CONST.__getattribute__('OS_INSECURE').lower() == 'true'): self.insecure = '-k' + def generate_conf(self): + if not os.path.exists(conf_utils.REFSTACK_RESULTS_DIR): + os.makedirs(conf_utils.REFSTACK_RESULTS_DIR) + + self.tempestconf = TempestConf() + self.tempestconf.generate_tempestconf() + def run_defcore(self, conf, testlist): """Run defcore sys command.""" cmd = ("refstack-client test {0} -c {1} -v --test-list {2}" @@ -87,10 +96,18 @@ class RefstackClient(testcase.OSGCTestCase): stderr=subprocess.STDOUT) def parse_refstack_result(self): - """Parse Refstact results.""" + """Parse Refstack results.""" try: with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR, "refstack.log"), 'r') as logfile: + for line in logfile.readlines(): + if 'Tests' in line: + break + if re.search(r"\} tempest\.", line): + LOGGER.info(line.replace('\n', '')) + + with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR, + "refstack.log"), 'r') as logfile: output = logfile.read() for match in re.findall(r"Ran: (\d+) tests in (\d+\.\d{4}) sec.", @@ -144,18 +161,18 @@ class RefstackClient(testcase.OSGCTestCase): """ self.start_time = time.time() - if not os.path.exists(conf_utils.REFSTACK_RESULTS_DIR): - os.makedirs(conf_utils.REFSTACK_RESULTS_DIR) - try: - tempestconf = TempestConf() - tempestconf.generate_tempestconf() + # Make sure that Tempest is configured + if not self.tempestconf: + self.generate_conf() self.run_defcore_default() self.parse_refstack_result() res = testcase.TestCase.EX_OK except Exception: LOGGER.exception("Error with run") res = testcase.TestCase.EX_RUN_ERROR + finally: + self.tempestconf.clean() self.stop_time = time.time() return res @@ -194,6 +211,45 @@ class RefstackClient(testcase.OSGCTestCase): return res + def create_snapshot(self): + """ + Run the Tempest cleanup utility to initialize OS state. + For details, see https://docs.openstack.org/tempest/latest/cleanup.html + + :return: TestCase.EX_OK + """ + LOGGER.info("Initializing the saved state of the OpenStack deployment") + + # Make sure that Tempest is configured + if not self.tempestconf: + self.generate_conf() + + try: + os_utils.init_tempest_cleanup( + self.tempestconf.DEPLOYMENT_DIR, 'tempest.conf', + os.path.join(conf_utils.REFSTACK_RESULTS_DIR, + "tempest-cleanup-init.log")) + except Exception as err: + LOGGER.error(str(err)) + return testcase.TestCase.EX_RUN_ERROR + + return super(RefstackClient, self).create_snapshot() + + def clean(self): + """ + Run the Tempest cleanup utility to delete and destroy OS resources. + For details, see https://docs.openstack.org/tempest/latest/cleanup.html + """ + LOGGER.info("Destroying the resources created for tempest") + + os_utils.perform_tempest_cleanup( + self.tempestconf.DEPLOYMENT_DIR, 'tempest.conf', + os.path.join(conf_utils.REFSTACK_RESULTS_DIR, + "tempest-cleanup.log") + ) + + return super(RefstackClient, self).clean() + class RefstackClientParser(object): # pylint: disable=too-few-public-methods """Command line argument parser helper.""" diff --git a/functest/opnfv_tests/openstack/refstack_client/tempest_conf.py b/functest/opnfv_tests/openstack/refstack_client/tempest_conf.py index 30590b9e..db745227 100644 --- a/functest/opnfv_tests/openstack/refstack_client/tempest_conf.py +++ b/functest/opnfv_tests/openstack/refstack_client/tempest_conf.py @@ -11,13 +11,15 @@ import pkg_resources from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils import openstack_utils from functest.utils.constants import CONST +from functest.opnfv_tests.openstack.tempest.tempest \ + import TempestResourcesManager """ logging configuration """ logger = logging.getLogger(__name__) class TempestConf(object): - def __init__(self): + def __init__(self, **kwargs): self.VERIFIER_ID = conf_utils.get_verifier_id() self.VERIFIER_REPO_DIR = conf_utils.get_verifier_repo_dir( self.VERIFIER_ID) @@ -27,15 +29,22 @@ class TempestConf(object): self.confpath = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/refstack_client/refstack_tempest.conf') + self.resources = TempestResourcesManager(**kwargs) def generate_tempestconf(self): try: openstack_utils.source_credentials( CONST.__getattribute__('openstack_creds')) - img_flavor_dict = conf_utils.create_tempest_resources( - use_custom_images=True, use_custom_flavors=True) + resources = self.resources.create(create_project=True, + use_custom_images=True, + use_custom_flavors=True) conf_utils.configure_tempest_defcore( - self.DEPLOYMENT_DIR, img_flavor_dict) + self.DEPLOYMENT_DIR, + image_id=resources.get("image_id"), + flavor_id=resources.get("flavor_id"), + image_id_alt=resources.get("image_id_alt"), + flavor_id_alt=resources.get("flavor_id_alt"), + tenant_id=resources.get("project_id")) except Exception as e: logger.error("error with generating refstack client " "reference tempest conf file: %s", e) @@ -48,6 +57,9 @@ class TempestConf(object): except Exception as e: logger.error('Error with run: %s', e) + def clean(self): + self.resources.cleanup() + def main(): logging.basicConfig() diff --git a/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py b/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py index 0b87440b..bfdcd862 100644 --- a/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py +++ b/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py @@ -10,7 +10,6 @@ import logging from functest.core import unit from functest.opnfv_tests.openstack.snaps import snaps_utils -from functest.utils import functest_utils from functest.utils.constants import CONST from snaps.openstack import create_flavor @@ -28,9 +27,14 @@ class SnapsTestRunner(unit.Suite): if 'os_creds' in kwargs: self.os_creds = kwargs['os_creds'] else: + creds_override = None + if hasattr(CONST, 'snaps_os_creds_override'): + creds_override = CONST.__getattribute__( + 'snaps_os_creds_override') self.os_creds = openstack_tests.get_credentials( os_env_file=CONST.__getattribute__('openstack_creds'), - proxy_settings_str=None, ssh_proxy_cmd=None) + proxy_settings_str=None, ssh_proxy_cmd=None, + overrides=creds_override) if 'ext_net_name' in kwargs: self.ext_net_name = kwargs['ext_net_name'] @@ -39,7 +43,7 @@ class SnapsTestRunner(unit.Suite): self.use_fip = CONST.__getattribute__('snaps_use_floating_ips') self.use_keystone = CONST.__getattribute__('snaps_use_keystone') - scenario = functest_utils.get_scenario() + scenario = CONST.__getattribute__('DEPLOY_SCENARIO') self.flavor_metadata = None if 'ovs' in scenario or 'fdio' in scenario: diff --git a/functest/opnfv_tests/openstack/tempest/conf_utils.py b/functest/opnfv_tests/openstack/tempest/conf_utils.py index 8574b0de..52fa6003 100644 --- a/functest/opnfv_tests/openstack/tempest/conf_utils.py +++ b/functest/opnfv_tests/openstack/tempest/conf_utils.py @@ -52,101 +52,9 @@ CI_INSTALLER_IP = CONST.__getattribute__('INSTALLER_IP') logger = logging.getLogger(__name__) -def create_tempest_resources(use_custom_images=False, - use_custom_flavors=False): - - logger.debug("Creating private network for Tempest suite") - network_dic = os_utils.create_shared_network_full( - CONST.__getattribute__('tempest_private_net_name'), - CONST.__getattribute__('tempest_private_subnet_name'), - CONST.__getattribute__('tempest_router_name'), - CONST.__getattribute__('tempest_private_subnet_cidr')) - if network_dic is None: - raise Exception('Failed to create private network') - - image_id = "" - image_id_alt = "" - flavor_id = "" - flavor_id_alt = "" - - if (CONST.__getattribute__('tempest_use_custom_images') or - use_custom_images): - # adding alternative image should be trivial should we need it - logger.debug("Creating image for Tempest suite") - _, image_id = os_utils.get_or_create_image( - CONST.__getattribute__('openstack_image_name'), - GLANCE_IMAGE_PATH, - CONST.__getattribute__('openstack_image_disk_format')) - if image_id is None: - raise Exception('Failed to create image') - - if use_custom_images: - logger.debug("Creating 2nd image for Tempest suite") - _, image_id_alt = os_utils.get_or_create_image( - CONST.__getattribute__('openstack_image_name_alt'), - GLANCE_IMAGE_PATH, - CONST.__getattribute__('openstack_image_disk_format')) - if image_id_alt is None: - raise Exception('Failed to create image') - - if (CONST.__getattribute__('tempest_use_custom_flavors') or - use_custom_flavors): - # adding alternative flavor should be trivial should we need it - logger.debug("Creating flavor for Tempest suite") - _, flavor_id = os_utils.get_or_create_flavor( - CONST.__getattribute__('openstack_flavor_name'), - CONST.__getattribute__('openstack_flavor_ram'), - CONST.__getattribute__('openstack_flavor_disk'), - CONST.__getattribute__('openstack_flavor_vcpus')) - if flavor_id is None: - raise Exception('Failed to create flavor') - - if use_custom_flavors: - logger.debug("Creating 2nd flavor for tempest_defcore") - _, flavor_id_alt = os_utils.get_or_create_flavor( - CONST.__getattribute__('openstack_flavor_name_alt'), - CONST.__getattribute__('openstack_flavor_ram'), - CONST.__getattribute__('openstack_flavor_disk'), - CONST.__getattribute__('openstack_flavor_vcpus')) - if flavor_id_alt is None: - raise Exception('Failed to create flavor') - - img_flavor_dict = {} - img_flavor_dict['image_id'] = image_id - img_flavor_dict['image_id_alt'] = image_id_alt - img_flavor_dict['flavor_id'] = flavor_id - img_flavor_dict['flavor_id_alt'] = flavor_id_alt - - return img_flavor_dict - - -def create_tenant_user(): - keystone_client = os_utils.get_keystone_client() - - logger.debug("Creating tenant and user for Tempest suite") - tenant_id = os_utils.create_tenant( - keystone_client, - CONST.__getattribute__('tempest_identity_tenant_name'), - CONST.__getattribute__('tempest_identity_tenant_description')) - if not tenant_id: - logger.error("Failed to create %s tenant" - % CONST.__getattribute__('tempest_identity_tenant_name')) - - user_id = os_utils.create_user( - keystone_client, - CONST.__getattribute__('tempest_identity_user_name'), - CONST.__getattribute__('tempest_identity_user_password'), - None, tenant_id) - if not user_id: - logger.error("Failed to create %s user" % - CONST.__getattribute__('tempest_identity_user_name')) - - return tenant_id - - def get_verifier_id(): """ - Returns verifer id for current Tempest + Returns verifier id for current Tempest """ cmd = ("rally verify list-verifiers | awk '/" + CONST.__getattribute__('tempest_deployment_name') + @@ -180,7 +88,7 @@ def get_verifier_deployment_id(): def get_verifier_repo_dir(verifier_id): """ - Returns installed verfier repo directory for Tempest + Returns installed verifier repo directory for Tempest """ if not verifier_id: verifier_id = get_verifier_id() @@ -222,32 +130,27 @@ def backup_tempest_config(conf_file): """ Copy config file to tempest results directory """ - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - shutil.copyfile(conf_file, os.path.join(TEMPEST_RESULTS_DIR, 'tempest.conf')) -def configure_tempest(deployment_dir, IMAGE_ID=None, FLAVOR_ID=None, - MODE=None): +def configure_tempest(deployment_dir, image_id=None, flavor_id=None, + mode=None): """ Calls rally verify and updates the generated tempest.conf with given parameters """ conf_file = configure_verifier(deployment_dir) - configure_tempest_update_params(conf_file, - IMAGE_ID, FLAVOR_ID) + configure_tempest_update_params(conf_file, image_id, flavor_id) -def configure_tempest_defcore(deployment_dir, img_flavor_dict): +def configure_tempest_defcore(deployment_dir, image_id, flavor_id, + image_id_alt, flavor_id_alt, tenant_id): """ Add/update needed parameters into tempest.conf file """ conf_file = configure_verifier(deployment_dir) - configure_tempest_update_params(conf_file, - img_flavor_dict.get("image_id"), - img_flavor_dict.get("flavor_id")) + configure_tempest_update_params(conf_file, image_id, flavor_id) logger.debug("Updating selected tempest.conf parameters for defcore...") config = ConfigParser.RawConfigParser() @@ -255,16 +158,14 @@ def configure_tempest_defcore(deployment_dir, img_flavor_dict): config.set('DEFAULT', 'log_file', '{}/tempest.log'.format(deployment_dir)) config.set('oslo_concurrency', 'lock_path', '{}/lock_files'.format(deployment_dir)) - generate_test_accounts_file() + generate_test_accounts_file(tenant_id=tenant_id) config.set('auth', 'test_accounts_file', TEST_ACCOUNTS_FILE) config.set('scenario', 'img_dir', '{}'.format(deployment_dir)) config.set('scenario', 'img_file', 'tempest-image') - config.set('compute', 'image_ref', img_flavor_dict.get("image_id")) - config.set('compute', 'image_ref_alt', - img_flavor_dict['image_id_alt']) - config.set('compute', 'flavor_ref', img_flavor_dict.get("flavor_id")) - config.set('compute', 'flavor_ref_alt', - img_flavor_dict['flavor_id_alt']) + config.set('compute', 'image_ref', image_id) + config.set('compute', 'image_ref_alt', image_id_alt) + config.set('compute', 'flavor_ref', flavor_id) + config.set('compute', 'flavor_ref_alt', flavor_id_alt) with open(conf_file, 'wb') as config_file: config.write(config_file) @@ -275,13 +176,12 @@ def configure_tempest_defcore(deployment_dir, img_flavor_dict): shutil.copyfile(conf_file, confpath) -def generate_test_accounts_file(): +def generate_test_accounts_file(tenant_id): """ Add needed tenant and user params into test_accounts.yaml """ logger.debug("Add needed params into test_accounts.yaml...") - tenant_id = create_tenant_user() accounts_list = [ { 'tenant_name': @@ -298,7 +198,7 @@ def generate_test_accounts_file(): def configure_tempest_update_params(tempest_conf_file, - IMAGE_ID=None, FLAVOR_ID=None): + image_id=None, flavor_id=None): """ Add/update needed parameters into tempest.conf file """ @@ -312,13 +212,13 @@ def configure_tempest_update_params(tempest_conf_file, config.set('compute', 'volume_device_name', CONST.__getattribute__('tempest_volume_device_name')) if CONST.__getattribute__('tempest_use_custom_images'): - if IMAGE_ID is not None: - config.set('compute', 'image_ref', IMAGE_ID) + if image_id is not None: + config.set('compute', 'image_ref', image_id) if IMAGE_ID_ALT is not None: config.set('compute', 'image_ref_alt', IMAGE_ID_ALT) if CONST.__getattribute__('tempest_use_custom_flavors'): - if FLAVOR_ID is not None: - config.set('compute', 'flavor_ref', FLAVOR_ID) + if flavor_id is not None: + config.set('compute', 'flavor_ref', flavor_id) if FLAVOR_ID_ALT is not None: config.set('compute', 'flavor_ref_alt', FLAVOR_ID_ALT) config.set('identity', 'region', 'RegionOne') diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py index f783f01f..b8a4e9ad 100644 --- a/functest/opnfv_tests/openstack/tempest/tempest.py +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -23,15 +23,26 @@ from functest.core import testcase from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils +import functest.utils.openstack_utils as os_utils + +from snaps.openstack import create_flavor +from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor +from snaps.openstack.create_project import ProjectSettings +from snaps.openstack.create_network import NetworkSettings, SubnetSettings +from snaps.openstack.create_user import UserSettings +from snaps.openstack.tests import openstack_tests +from snaps.openstack.utils import deploy_utils + """ logging configuration """ logger = logging.getLogger(__name__) -class TempestCommon(testcase.OSGCTestCase): +class TempestCommon(testcase.TestCase): def __init__(self, **kwargs): super(TempestCommon, self).__init__(**kwargs) + self.resources = TempestResourcesManager(**kwargs) self.MODE = "" self.OPTION = "" self.VERIFIER_ID = conf_utils.get_verifier_id() @@ -222,12 +233,12 @@ class TempestCommon(testcase.OSGCTestCase): try: if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR): os.makedirs(conf_utils.TEMPEST_RESULTS_DIR) - image_and_flavor = conf_utils.create_tempest_resources() + resources = self.resources.create() conf_utils.configure_tempest( self.DEPLOYMENT_DIR, - IMAGE_ID=image_and_flavor.get("image_id"), - FLAVOR_ID=image_and_flavor.get("flavor_id"), - MODE=self.MODE) + image_id=resources.get("image_id"), + flavor_id=resources.get("flavor_id"), + mode=self.MODE) self.generate_test_list(self.VERIFIER_REPO_DIR) self.apply_tempest_blacklist() self.run_verifier_tests() @@ -236,10 +247,52 @@ class TempestCommon(testcase.OSGCTestCase): except Exception as e: logger.error('Error with run: %s' % e) res = testcase.TestCase.EX_RUN_ERROR + finally: + self.resources.cleanup() self.stop_time = time.time() return res + def create_snapshot(self): + """ + Run the Tempest cleanup utility to initialize OS state. + + :return: TestCase.EX_OK + """ + logger.info("Initializing the saved state of the OpenStack deployment") + + if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR): + os.makedirs(conf_utils.TEMPEST_RESULTS_DIR) + + # Make sure that the verifier is configured + conf_utils.configure_verifier(self.DEPLOYMENT_DIR) + + try: + os_utils.init_tempest_cleanup( + self.DEPLOYMENT_DIR, 'tempest.conf', + os.path.join(conf_utils.TEMPEST_RESULTS_DIR, + "tempest-cleanup-init.log")) + except Exception as err: + logger.error(str(err)) + return testcase.TestCase.EX_RUN_ERROR + + return super(TempestCommon, self).create_snapshot() + + def clean(self): + """ + Run the Tempest cleanup utility to delete and destroy OS resources + created by Tempest. + """ + logger.info("Destroying the resources created for refstack") + + os_utils.perform_tempest_cleanup( + self.DEPLOYMENT_DIR, 'tempest.conf', + os.path.join(conf_utils.TEMPEST_RESULTS_DIR, + "tempest-cleanup.log") + ) + + return super(TempestCommon, self).clean() + class TempestSmokeSerial(TempestCommon): @@ -288,3 +341,170 @@ class TempestDefcore(TempestCommon): TempestCommon.__init__(self, **kwargs) self.MODE = "defcore" self.OPTION = "--concurrency 1" + + +class TempestResourcesManager(object): + + def __init__(self, **kwargs): + self.os_creds = None + if 'os_creds' in kwargs: + self.os_creds = kwargs['os_creds'] + else: + self.os_creds = openstack_tests.get_credentials( + os_env_file=CONST.__getattribute__('openstack_creds')) + + self.creators = list() + + if hasattr(CONST, 'snaps_images_cirros'): + self.cirros_image_config = CONST.__getattribute__( + 'snaps_images_cirros') + else: + self.cirros_image_config = None + + def create(self, use_custom_images=False, use_custom_flavors=False, + create_project=False): + if create_project: + logger.debug("Creating project (tenant) for Tempest suite") + project_name = CONST.__getattribute__( + 'tempest_identity_tenant_name') + project_creator = deploy_utils.create_project( + self.os_creds, ProjectSettings( + name=project_name, + description=CONST.__getattribute__( + 'tempest_identity_tenant_description'))) + if (project_creator is None or + project_creator.get_project() is None): + raise Exception("Failed to create tenant") + project_id = project_creator.get_project().id + self.creators.append(project_creator) + + logger.debug("Creating user for Tempest suite") + user_creator = deploy_utils.create_user( + self.os_creds, UserSettings( + name=CONST.__getattribute__('tempest_identity_user_name'), + password=CONST.__getattribute__( + 'tempest_identity_user_password'), + project_name=project_name)) + if user_creator is None or user_creator.get_user() is None: + raise Exception("Failed to create user") + user_id = user_creator.get_user().id + self.creators.append(user_creator) + else: + project_name = None + project_id = None + user_id = None + + logger.debug("Creating private network for Tempest suite") + network_creator = deploy_utils.create_network( + self.os_creds, NetworkSettings( + name=CONST.__getattribute__('tempest_private_net_name'), + project_name=project_name, + subnet_settings=[SubnetSettings( + name=CONST.__getattribute__('tempest_private_subnet_name'), + cidr=CONST.__getattribute__('tempest_private_subnet_cidr')) + ])) + if network_creator is None or network_creator.get_network() is None: + raise Exception("Failed to create private network") + self.creators.append(network_creator) + + image_id = None + image_id_alt = None + flavor_id = None + flavor_id_alt = None + + if (CONST.__getattribute__('tempest_use_custom_images') or + use_custom_images): + logger.debug("Creating image for Tempest suite") + image_base_name = CONST.__getattribute__('openstack_image_name') + os_image_settings = openstack_tests.cirros_image_settings( + image_base_name, public=True, + image_metadata=self.cirros_image_config) + logger.debug("Creating image for Tempest suite") + image_creator = deploy_utils.create_image( + self.os_creds, os_image_settings) + if image_creator is None: + raise Exception('Failed to create image') + self.creators.append(image_creator) + image_id = image_creator.get_image().id + + if use_custom_images: + logger.debug("Creating 2nd image for Tempest suite") + image_base_name_alt = CONST.__getattribute__( + 'openstack_image_name_alt') + os_image_settings_alt = openstack_tests.cirros_image_settings( + image_base_name_alt, public=True, + image_metadata=self.cirros_image_config) + logger.debug("Creating 2nd image for Tempest suite") + image_creator_alt = deploy_utils.create_image( + self.os_creds, os_image_settings_alt) + if image_creator_alt is None: + raise Exception('Failed to create image') + self.creators.append(image_creator_alt) + image_id_alt = image_creator_alt.get_image().id + + if (CONST.__getattribute__('tempest_use_custom_flavors') or + use_custom_flavors): + logger.info("Creating flavor for Tempest suite") + scenario = CONST.__getattribute__('DEPLOY_SCENARIO') + flavor_metadata = None + if 'ovs' in scenario or 'fdio' in scenario: + flavor_metadata = create_flavor.MEM_PAGE_SIZE_LARGE + flavor_creator = OpenStackFlavor( + self.os_creds, FlavorSettings( + name=CONST.__getattribute__('openstack_flavor_name'), + ram=CONST.__getattribute__('openstack_flavor_ram'), + disk=CONST.__getattribute__('openstack_flavor_disk'), + vcpus=CONST.__getattribute__('openstack_flavor_vcpus'), + metadata=flavor_metadata)) + flavor = flavor_creator.create() + if flavor is None: + raise Exception('Failed to create flavor') + self.creators.append(flavor_creator) + flavor_id = flavor.id + + if use_custom_flavors: + logger.info("Creating 2nd flavor for Tempest suite") + scenario = CONST.__getattribute__('DEPLOY_SCENARIO') + flavor_metadata_alt = None + if 'ovs' in scenario or 'fdio' in scenario: + flavor_metadata_alt = create_flavor.MEM_PAGE_SIZE_LARGE + flavor_creator_alt = OpenStackFlavor( + self.os_creds, FlavorSettings( + name=CONST.__getattribute__('openstack_flavor_name_alt'), + ram=CONST.__getattribute__('openstack_flavor_ram'), + disk=CONST.__getattribute__('openstack_flavor_disk'), + vcpus=CONST.__getattribute__('openstack_flavor_vcpus'), + metadata=flavor_metadata_alt)) + flavor_alt = flavor_creator_alt.create() + if flavor_alt is None: + raise Exception('Failed to create flavor') + self.creators.append(flavor_creator_alt) + flavor_id_alt = flavor_alt.id + + print("RESOURCES CREATE: image_id: %s, image_id_alt: %s, " + "flavor_id: %s, flavor_id_alt: %s" % ( + image_id, image_id_alt, flavor_id, flavor_id_alt,)) + + result = { + 'image_id': image_id, + 'image_id_alt': image_id_alt, + 'flavor_id': flavor_id, + 'flavor_id_alt': flavor_id_alt + } + + if create_project: + result['project_id'] = project_id + result['tenant_id'] = project_id # for compatibility + result['user_id'] = user_id + + return result + + def cleanup(self): + """ + Cleanup all OpenStack objects. Should be called on completion. + """ + for creator in reversed(self.creators): + try: + creator.clean() + except Exception as e: + logger.error('Unexpected error cleaning - %s', e) diff --git a/functest/opnfv_tests/openstack/vping/vping_base.py b/functest/opnfv_tests/openstack/vping/vping_base.py index f3e0cfe3..c93d2f26 100644 --- a/functest/opnfv_tests/openstack/vping/vping_base.py +++ b/functest/opnfv_tests/openstack/vping/vping_base.py @@ -13,7 +13,6 @@ import time import uuid from functest.core import testcase -from functest.utils import functest_utils from functest.utils.constants import CONST from snaps.openstack import create_flavor @@ -43,8 +42,14 @@ class VPingBase(testcase.TestCase): if 'os_creds' in kwargs: self.os_creds = kwargs['os_creds'] else: + creds_override = None + if hasattr(CONST, 'snaps_os_creds_override'): + creds_override = CONST.__getattribute__( + 'snaps_os_creds_override') + self.os_creds = openstack_tests.get_credentials( - os_env_file=CONST.__getattribute__('openstack_creds')) + os_env_file=CONST.__getattribute__('openstack_creds'), + overrides=creds_override) self.creators = list() self.image_creator = None @@ -107,13 +112,13 @@ class VPingBase(testcase.TestCase): vping_physical_network = None vping_segmentation_id = None - if (hasattr(CONST, 'network_type')): + if (hasattr(CONST, 'vping_network_type')): vping_network_type = CONST.__getattribute__( 'vping_network_type') - if (hasattr(CONST, 'physical_network')): + if (hasattr(CONST, 'vping_physical_network')): vping_physical_network = CONST.__getattribute__( 'vping_physical_network') - if (hasattr(CONST, 'segmentation_id')): + if (hasattr(CONST, 'vping_segmentation_id')): vping_segmentation_id = CONST.__getattribute__( 'vping_segmentation_id') @@ -133,7 +138,7 @@ class VPingBase(testcase.TestCase): self.logger.info( "Creating flavor with name: '%s'" % self.flavor_name) - scenario = functest_utils.get_scenario() + scenario = CONST.__getattribute__('DEPLOY_SCENARIO') flavor_metadata = None if 'ovs' in scenario or 'fdio' in scenario: flavor_metadata = create_flavor.MEM_PAGE_SIZE_LARGE diff --git a/functest/opnfv_tests/sdn/onos/onos.py b/functest/opnfv_tests/sdn/onos/onos.py deleted file mode 100644 index 08ba4da3..00000000 --- a/functest/opnfv_tests/sdn/onos/onos.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 HUAWEI TECHNOLOGIES CO.,LTD 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 - -import logging -import os -import pkg_resources -import re -import subprocess -import shutil -import time -import urlparse - -from functest.core import testcase -from functest.utils.constants import CONST -import functest.utils.functest_utils as ft_utils -import functest.utils.openstack_utils as openstack_utils - - -class OnosBase(testcase.TestCase): - onos_repo_path = CONST.__getattribute__('dir_repo_onos') - onos_sfc_image_name = CONST.__getattribute__('onos_sfc_image_name') - onos_sfc_image_path = os.path.join( - CONST.__getattribute__('dir_functest_images'), - CONST.__getattribute__('onos_sfc_image_file_name')) - onos_sfc_path = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/sdn/onos/sfc') - installer_type = CONST.__getattribute__('INSTALLER_TYPE') - logger = logging.getLogger(__name__) - - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "onos_base" - super(OnosBase, self).__init__(**kwargs) - - def run(self): - self.start_time = time.time() - try: - self._run() - res = testcase.TestCase.EX_OK - except Exception as e: - self.logger.error('Error with run: %s', e) - res = testcase.TestCase.EX_RUN_ERROR - - self.stop_time = time.time() - return res - - def _run(self): - raise NotImplementedError('_run is not implemented') - - -class Onos(OnosBase): - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "onos" - super(Onos, self).__init__(**kwargs) - self.log_path = os.path.join(self.onos_repo_path, 'TestON/logs') - - def set_onos_ip(self): - if (self.installer_type and - self.installer_type.lower() == 'joid'): - sdn_controller_env = os.getenv('SDN_CONTROLLER') - OC1 = re.search(r"\d+\.\d+\.\d+\.\d+", sdn_controller_env).group() - else: - neutron_url = openstack_utils.get_endpoint(service_type='network') - OC1 = urlparse.urlparse(neutron_url).hostname - os.environ['OC1'] = OC1 - self.logger.debug("ONOS IP is %s", OC1) - - def run_onos_script(self, testname): - cli_dir = os.path.join(self.onos_repo_path, 'TestON/bin/cli.py') - cmd = '{0} run {1}'.format(cli_dir, testname) - self.logger.debug("Run script: %s", testname) - ft_utils.execute_command_raise( - cmd, - error_msg=('Error when running ONOS script: %s' - % (testname))) - - def clean_existing_logs(self): - log_dir = [f for f in os.listdir(self.log_path)] - for log in log_dir: - try: - if os.path.isdir(log): - shutil.rmtree(log) - elif os.path.isfile(log): - os.remove(log) - except OSError as e: - self.logger.error('Error with deleting file %s: %s', - log, e.strerror) - - def get_result(self): - cmd = 'grep -rnh Fail {0}'.format(self.log_path) - p = subprocess.Popen(cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - - for line in p.stdout: - self.logger.debug(line) - if re.search("\s+[1-9]+\s+", line): - self.logger.debug("Testcase Fails\n" + line) - - cmd = "grep -rnh 'Execution Time' {0}".format(self.log_path) - result_buffer = os.popen(cmd).read() - time1 = result_buffer[114:128] - time2 = result_buffer[28:42] - cmd = "grep -rnh 'Success Percentage' {0}".format( - os.path.join(self.log_path, "FUNCvirNetNB_*")) - result_buffer = os.popen(cmd).read() - if result_buffer.find('100%') >= 0: - result1 = 'Success' - else: - result1 = 'Failed' - cmd = "grep -rnh 'Success Percentage' {0}".format( - os.path.join(self.log_path, "FUNCvirNetNBL3*")) - result_buffer = os.popen(cmd).read() - if result_buffer.find('100%') >= 0: - result2 = 'Success' - else: - result2 = 'Failed' - status1 = [] - status2 = [] - cmd = "grep -rnh 'h3' {0}".format( - os.path.join(self.log_path, "FUNCvirNetNB_*")) - result_buffer = os.popen(cmd).read() - pattern = re.compile("<h3>([^-]+) - ([^-]+) - (\S*)</h3>") - # res = pattern.search(result_buffer).groups() - res = pattern.findall(result_buffer) - i = 0 - for index in range(len(res)): - status1.append({'Case name:': res[i][0] + res[i][1], - 'Case result': res[i][2]}) - i = i + 1 - cmd = "grep -rnh 'h3' {0}".format( - os.path.join(self.log_path, "FUNCvirNetNBL3*")) - result_buffer = os.popen(cmd).read() - pattern = re.compile("<h3>([^-]+) - ([^-]+) - (\S*)</h3>") - res = pattern.findall(result_buffer) - i = 0 - for index in range(len(res)): - status2.append({'Case name:': res[i][0] + res[i][1], - 'Case result': res[i][2]}) - i = i + 1 - payload = {'FUNCvirNet': {'duration': time1, - 'result': result1, - 'status': status1}, - 'FUNCvirNetL3': {'duration': time2, - 'result': result2, - 'status': status2}} - return payload - - def parse_result(self): - result = self.get_result() - status = "FAIL" - try: - if (result['FUNCvirNet']['result'] == "Success" and - result['FUNCvirNetL3']['result'] == "Success"): - status = "PASS" - except Exception: - self.logger.error("Unable to set ONOS result") - - self.result = status - self.details = result - - def _run(self): - self.clean_existing_logs() - self.set_onos_ip() - self.run_onos_script('FUNCvirNetNB') - self.run_onos_script('FUNCvirNetNBL3') - self.parse_result() - - -class OnosSfc(OnosBase): - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "onos_sfc" - super(OnosSfc, self).__init__(**kwargs) - - def get_ip(self, type): - url = openstack_utils.get_endpoint(service_type=type) - self.logger.debug('get_ip for %s: %s', type, url) - return urlparse.urlparse(url).hostname - - def update_sfc_onos_file(self, before, after): - file_dir = os.path.join(self.onos_sfc_path, "sfc_onos.py") - cmd = "sed -i 's/{0}/{1}/g' {2}".format(before, - after, - file_dir) - ft_utils.execute_command_raise( - cmd, - error_msg=('Error with replacing %s with %s' - % (before, after))) - - def create_image(self): - self.logger.warn('inside create_image') - glance_client = openstack_utils.get_glance_client() - image_id = openstack_utils.create_glance_image( - glance_client, - self.onos_sfc_image_name, - self.onos_sfc_image_path) - if image_id is None: - raise Exception('Failed to create image') - - self.logger.debug("Image '%s' with ID=%s is created successfully.", - self.onos_sfc_image_name, image_id) - - def set_sfc_conf(self): - self.update_sfc_onos_file("keystone_ip", self.get_ip("identity")) - self.update_sfc_onos_file("neutron_ip", self.get_ip("network")) - self.update_sfc_onos_file("nova_ip", self.get_ip("compute")) - self.update_sfc_onos_file("glance_ip", self.get_ip("image")) - self.update_sfc_onos_file("console", - CONST.__getattribute__('OS_PASSWORD')) - neutron_client = openstack_utils.get_neutron_client() - ext_net = openstack_utils.get_external_net(neutron_client) - self.update_sfc_onos_file("admin_floating_net", ext_net) - self.logger.debug("SFC configuration is modified") - - def sfc_test(self): - cmd = 'python {0}'.format(os.path.join(self.onos_sfc_path, 'sfc.py')) - ft_utils.execute_command_raise(cmd, - error_msg='Error with testing SFC') - - def _run(self): - self.create_image() - self.set_sfc_conf() - self.sfc_test() diff --git a/functest/opnfv_tests/sdn/onos/sfc/README.md b/functest/opnfv_tests/sdn/onos/sfc/README.md deleted file mode 100644 index ae63ee21..00000000 --- a/functest/opnfv_tests/sdn/onos/sfc/README.md +++ /dev/null @@ -1,21 +0,0 @@ -SFC Script ReadMe File -********************** - -Topology ---------- - -Validated with the Fuel Enviroment. - - -Things to Remember : --------------------- - -1] This Script basically Tests the SFC functionality with ONOS controller. -2] Ip address of Openstack and ONOS are got dynamically. -3] Initally this sfc script can be used for ONOS and on Request , if need will modify for other controllers. - - -Contact Details : ------------------ - -email-id : antonysilvester@gmail.com diff --git a/functest/opnfv_tests/sdn/onos/sfc/sfc.py b/functest/opnfv_tests/sdn/onos/sfc/sfc.py deleted file mode 100644 index 2bb9082c..00000000 --- a/functest/opnfv_tests/sdn/onos/sfc/sfc.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) CREATED5 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 -# -# ########################################################################### -# OPNFV SFC Script -# **** Scripted by Antony Silvester - antony.silvester@huawei.com ****** -# ########################################################################### - -# Testcase 1 : Prerequisites configuration for SFC -# Testcase 2 : Creation of 3 VNF Nodes and Attaching Ports -# Testcase 3 : Configure SFC [Port pair,Port Group ,Flow classifer -# Testcase 4 : Configure Port Chain and verify the flows are added. -# Testcase 5 : Verify traffic with VNF node. -# Testcase 6 : Remove the Port Chain and Verify the traffic. -# Testcase 7 : Cleanup -# ########################################################################### -# -"""Script to Test the SFC scenarios in ONOS.""" - -import logging -import time -import functest.utils.functest_utils as ft_utils -from sfc_onos import SfcOnos - -logger = logging.getLogger(__name__) -Sfc_obj = SfcOnos() - -OK = 200 -CREATED = 201 -ACCEPTED = 202 -NO_CONTENT = 204 - -start_time = time.time() - - -def PreConfig(): - logger.info("Testcase 1 : Prerequisites configuration for SFC") - logger.info("1.1 Creation of Auth-Token") - check(Sfc_obj.getToken, OK, "Creation of Token") - logger.info("1.2 Creation of Network") - check(Sfc_obj.createNetworks, CREATED, "Creation of network") - logger.info("1.3 Creation of Subnetwork") - check(Sfc_obj.createSubnets, CREATED, "Creation of Subnetwork") - - -def CreateNodes(): - logger.info("Testcase 2 : Creation of 3 VNF Nodes and Attaching Ports") - logger.info("2.1 Creation of Ports") - check(Sfc_obj.createPorts, CREATED, "Creation of Port") - logger.info("2.2 Creation of VM-Compute-Node") - check(Sfc_obj.createVm, ACCEPTED, "Creation of VM") - logger.info("2.3 Check VM Status") - check(Sfc_obj.checkVmState, OK, "Vm statue check") - logger.info("2.4 Router Creation") - check(Sfc_obj.createRouter, CREATED, "Creation of Router") - logger.info("2.5 Attachement of Interface to VM") - check(Sfc_obj.attachInterface, OK, "Interface attached to VM") - logger.info("2.6 Attachement of FLoating Ip to VM") - check(Sfc_obj.addFloatingIp, ACCEPTED, "Floating Ip attached to VM") - - -def ConfigSfc(): - logger.info( - "Testcase 3 : Configure SFC [Portair,PortGroup,Flow classifer]") - logger.info("3.1 Creation of Port Pair") - check(Sfc_obj.createPortPair, CREATED, "Creation of Port Pair") - logger.info("3.2 Getting the Port Pair ID") - check(Sfc_obj.getPortPair, OK, "Getting Port Pair ID") - logger.info("3.3 Creation of Port Pair Group") - check(Sfc_obj.createPortGroup, CREATED, "Creation of Port Pair Group") - logger.info("3.4 Getting Port Pair Group ID ") - check(Sfc_obj.getPortGroup, OK, "Getting Port Pair Group ID") - logger.info("3.5 Creation of Flow Classifier") - check(Sfc_obj.createFlowClassifier, CREATED, "Creation of Flow Classifier") - logger.info( - "Testcase 4 : Configure Port Chain and verify flows are added") - logger.info("4.1 Creation of Port Chain") - check(Sfc_obj.createPortChain, CREATED, "Creation of Port Chain") - - -def VerifySfcTraffic(): - status = "PASS" - logger.info("Testcase 5 : Verify traffic with VNF node.") - if (Sfc_obj.loginToVM() == "1"): - logger.info("SFC function Working") - else: - logger.error("SFC function not working") - status = "FAIL" - - logger.info("Testcase 6 : Remove the Port Chain and Verify the traffic") - if (Sfc_obj.deletePortChain() == NO_CONTENT): - if (Sfc_obj.loginToVM() == "0"): - logger.info("SFC function is removed Successfully") - else: - logger.error("SFC function not Removed. Have some problem") - status = "FAIL" - if (Sfc_obj.deleteFlowClassifier() == NO_CONTENT): - if (Sfc_obj.deletePortGroup() == NO_CONTENT): - if (Sfc_obj.deletePortPair() == NO_CONTENT): - logger.info( - "SFC configuration is deleted successfully") - else: - logger.error("Port pair is deleted successfully") - status = "FAIL" - else: - logger.error("Port Group is NOT deleted successfully") - status = "FAIL" - else: - logger.error("Flow classifier is NOT deleted successfully") - status = "FAIL" - else: - logger.error("PortChain configuration is NOT deleted successfully") - status = "FAIL" - if (status == "FAIL"): - fail("Traffic for SFC is NOT verified successfully") - - -def CleanUp(): - logger.info("Testcase 7 : Cleanup") - if (Sfc_obj.cleanup() == NO_CONTENT): - logger.info("CleanUp is successfull") - else: - logger.error("CleanUp is NOT successfull") - - -def check(method, criteria, msg): - if (method() == criteria): - logger.info(msg + 'is Successful') - else: - fail(msg + 'is not successful') - - -def fail(fail_info): - logger.error(fail_info) - CleanUp() - PushDB("FAIL", fail_info) - exit(-1) - - -def PushDB(status, info): - logger.info("Summary :") - try: - logger.debug("Push ONOS SFC results into DB") - stop_time = time.time() - - # ONOS SFC success criteria = all tests OK - duration = round(stop_time - start_time, 1) - logger.info("Result is " + status) - ft_utils.push_results_to_db("functest", - "onos_sfc", - start_time, - stop_time, - status, - details={'duration': duration, - 'error': info}) - except: - logger.error("Error pushing results into Database") - - -def main(): - """Script to Test the SFC scenarios in ONOS.""" - logging.basicConfig() - PreConfig() - CreateNodes() - ConfigSfc() - VerifySfcTraffic() - CleanUp() - PushDB("PASS", "") diff --git a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py deleted file mode 100644 index 4e93c133..00000000 --- a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py +++ /dev/null @@ -1,887 +0,0 @@ -import logging -import os -import re -import time -import json -import requests - -from multiprocessing import Process -from multiprocessing import Queue -from pexpect import pxssh - -from functest.utils.constants import CONST - -OK = 200 -CREATED = 201 -ACCEPTED = 202 -NO_CONTENT = 204 - - -class SfcOnos(object): - """Defines all the def function of SFC.""" - - def __init__(self): - """Initialization of variables.""" - self.logger = logging.getLogger(__name__) - self.osver = "v2.0" - self.token_id = 0 - self.net_id = 0 - self.image_id = 0 - self.keystone_hostname = 'keystone_ip' - self.neutron_hostname = 'neutron_ip' - self.nova_hostname = 'nova_ip' - self.glance_hostname = 'glance_ip' - self.onos_hostname = 'onos_ip' - # Network variables ####### - self.netname = "test_nw" - self.admin_state_up = True - self.tenant_id = 0 - self.subnetId = 0 - # ######################### - # SubNet variables######### - self.ip_version = 4 - self.cidr = "20.20.20.0/24" - self.subnetname = "test_nw_subnets" - # ############################### - # Port variable - self.port = "port" - self.port_num = [] - self.vm_id = 0 - self.port_ip = [] - self.count = 0 - self.i = 0 - self.numTerms = 3 - self.security_groups = [] - self.port_security_enabled = False - # ############################### - # VM creation variable - self.container_format = "bare" - self.disk_format = "qcow2" - self.imagename = "TestSfcVm" - self.createImage = ("/home/root1/devstack/files/images/" - "firewall_block_image.img") - - self.vm_name = "vm" - self.imageRef = "test" - self.flavorRef = "1" - self.max_count = "1" - self.min_count = "1" - self.org_nw_port = [] - self.image_id = 0 - self.routername = "router1" - self.router_id = 0 - # ##################################### - # Port pair - self.port_pair_ingress = 0 - self.port_pair_egress = 0 - self.port_pair_name = "PP" - self.port_pair_id = [] - # #################################### - # Port Group - self.port_group_name = "PG" - self.port_grp_id = [] - # #################################### - # FlowClassifier - self.source_ip_prefix = "20.20.20.0/24" - self.destination_ip_prefix = "20.20.20.0/24" - self.logical_source_port = 0 - self.fcname = "FC" - self.ethertype = "IPv4" - # ##################################### - self.flow_class_if = 0 - # ##################################### - # Port Chain variables - self.pcname = 'PC' - self.PC_id = 0 - # ##################################### - # Port Chain variables - self.flowadd = '' - # ##################################### - self.ip_pool = 0 - self.vm_public_ip = [] - self.vm_public_id = [] - self.cirros_username = CONST.__getattribute__( - 'openstack_image_username') - self.cirros_password = CONST.__getattribute__( - 'openstack_image_password') - self.net_id1 = 0 - self.vm = [] - self.address = 0 - self.value = 0 - self.pub_net_id = 0 - - def getToken(self): - """Get the keystone token value from Openstack .""" - url = 'http://%s:5000/%s/tokens' % (self.keystone_hostname, - self.osver) - data = ('{"auth": {"tenantName": "admin", "passwordCredentials":' - '{ "username": "admin", "password": "console"}}}') - headers = {"Accept": "application/json"} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == OK): - json1_data = json.loads(response.content) - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.debug(json1_data) - self.token_id = json1_data['access']['token']['id'] - self.tenant_id = json1_data['access']['token']['tenant']['id'] - return(response.status_code) - else: - return(response.status_code) - - def createNetworks(self): - """Creation of networks.""" - Dicdata = {} - if self.netname != '': - Dicdata['name'] = self.netname - if self.admin_state_up != '': - Dicdata['admin_state_up'] = self.admin_state_up - Dicdata = {'network': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/networks' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.net_id = json1_data['network']['id'] - return(response.status_code) - else: - return(response.status_code) - - def createSubnets(self): - """Creation of SubNets.""" - Dicdata = {} - if self.net_id != 0: - Dicdata['network_id'] = self.net_id - if self.ip_version != '': - Dicdata['ip_version'] = self.ip_version - if self.cidr != '': - Dicdata['cidr'] = self.cidr - if self.subnetname != '': - Dicdata['name'] = self.subnetname - - Dicdata = {'subnet': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/subnets' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - - if (response.status_code == CREATED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.subnetId = json1_data['subnet']['id'] - return(response.status_code) - else: - return(response.status_code) - - def createPorts(self): - """Creation of Ports.""" - for x in range(self.i, self.numTerms): - Dicdata = {} - if self.net_id != '': - Dicdata['network_id'] = self.net_id - if self.port != '': - Dicdata['name'] = "port" + str(x) - if self.admin_state_up != '': - Dicdata['admin_state_up'] = self.admin_state_up - if self.security_groups != '': - Dicdata['security_groups'] = self.security_groups - # if self.port_security_enabled != '': - # Dicdata['port_security_enabled'] = self.port_security_enabled - - Dicdata = {'port': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/ports' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - - if (response.status_code == CREATED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.port_num.append(json1_data['port']['id']) - self.port_ip.append(json1_data['port']['fixed_ips'][0] - ['ip_address']) - else: - return(response.status_code) - return(response.status_code) - - def createVm(self): - """Creation of Instance, using firewall image.""" - url = ("http://%s:9292/v2/images?" - "name=TestSfcVm" % (self.glance_hostname)) - headers = {"Accept": "application/json", - "Content-Type": "application/octet-stream", - "X-Auth-Token": self.token_id} - response = requests.get(url, headers=headers) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("FireWall Image is available") - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.image_id = json1_data['images'][0]['id'] - else: - return(response.status_code) - - url = ("http://%s:8774/v2.1/%s/flavors?" - "name=m1.tiny" % (self.nova_hostname, self.tenant_id)) - headers = {"Accept": "application/json", "Content-Type": - "application/json", "X-Auth-Token": self.token_id} - response = requests.get(url, headers=headers) - - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("Flavor is available") - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.flavorRef = json1_data['flavors'][0]['id'] - else: - return(response.status_code) - - for y in range(0, 3): - Dicdata = {} - org_nw_port = [] - org_nw_port.append({'port': self.port_num[y]}) - if self.vm_name != '': - Dicdata['name'] = "vm" + str(y) - if self.imageRef != '': - Dicdata['imageRef'] = self.image_id - if self.flavorRef != '': - Dicdata['flavorRef'] = self.flavorRef - if self.max_count != '': - Dicdata['max_count'] = self.max_count - if self.min_count != '': - Dicdata['min_count'] = self.min_count - if self.org_nw_port != '': - Dicdata['networks'] = org_nw_port - Dicdata = {'server': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:8774/v2.1/%s/servers' % (self.nova_hostname, - self.tenant_id) - headers = {"Accept": "application/json", "Content-Type": - "application/json", "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == ACCEPTED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - info = "Creation of VM" + str(y) + " is successfull" - self.logger.debug(info) - - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.vm_id = json1_data['server']['id'] - self.vm.append(json1_data['server']['id']) - else: - return(response.status_code) - - return(response.status_code) - - def checkVmState(self): - """Checking the Status of the Instance.""" - time.sleep(10) - for y in range(0, 3): - url = ("http://%s:8774/v2.1/servers/" - "detail?name=vm" + str(y)) % (self.neutron_hostname) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.get(url, headers=headers) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.vm_active = json1_data['servers'][0]['status'] - if (self.vm_active == "ACTIVE"): - info = "VM" + str(y) + " is Active : " + self.vm_active - else: - info = "VM" + str(y) + " is NOT Active : " + self.vm_active - self.logger.debug(info) - else: - return(response.status_code) - return(response.status_code) - time.sleep(10) - - def createPortPair(self): - """Creation of Port Pair.""" - for p in range(1, 2): - Dicdata = {} - if self.port_pair_ingress != '': - Dicdata['ingress'] = self.port_num[p] - if self.port_pair_egress != '': - egress = p - Dicdata['egress'] = self.port_num[egress] - if self.port_pair_name != '': - Dicdata['name'] = "PP" + str(p) - - Dicdata = {'port_pair': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/sfc/port_pairs' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - info = ("Creation of Port Pair PP" + str(p) + - " is successful") - self.logger.debug(info) - else: - return(response.status_code) - - return(response.status_code) - - def getPortPair(self): - """Query the Portpair id value.""" - for p in range(0, 1): - url = ("http://%s:9696/%s/" - "sfc/port_pairs?name=PP1" % (self.neutron_hostname, - self.osver)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.get(url, headers=headers) - - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.port_pair_id.append(json1_data['port_pairs'][0]['id']) - else: - return(response.status_code) - return(response.status_code) - - def createPortGroup(self): - """Creation of PortGroup.""" - for p in range(0, 1): - Dicdata = {} - port_pair_list = [] - port_pair_list.append(self.port_pair_id[p]) - if self.port_group_name != '': - Dicdata['name'] = "PG" + str(p) - if self.port_pair_id != '': - Dicdata['port_pairs'] = port_pair_list - - Dicdata = {'port_pair_group': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = ("http://%s:9696/%s/" - "sfc/port_pair_groups" % (self.neutron_hostname, - self.osver)) - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - info = ("Creation of Port Group PG" + str(p) + - "is successful") - self.logger.debug(info) - else: - return(response.status_code) - - return(response.status_code) - - def getPortGroup(self): - """Query the PortGroup id.""" - for p in range(0, 1): - url = ("http://%s:9696/%s/sfc/port_pair_groups" - "?name=PG" + str(p)) % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.get(url, headers=headers) - - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - json1_data = json.loads(response.content) - self.port_grp_id.append(json1_data['port_pair_groups'] - [0]['id']) - else: - return(response.status_code) - return(response.status_code) - - def createFlowClassifier(self): - """Creation of Flow Classifier.""" - Dicdata = {} - if self.source_ip_prefix != '': - Dicdata['source_ip_prefix'] = self.source_ip_prefix - if self.destination_ip_prefix != '': - Dicdata['destination_ip_prefix'] = self.destination_ip_prefix - if self.logical_source_port != '': - Dicdata['logical_source_port'] = self.port_num[0] - if self.fcname != '': - Dicdata['name'] = "FC1" - if self.ethertype != '': - Dicdata['ethertype'] = self.ethertype - - Dicdata = {'flow_classifier': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = ("http://%s:9696/%s/" - "sfc/flow_classifiers" % (self.neutron_hostname, - self.osver)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - json1_data = json.loads(response.content) - self.flow_class_if = json1_data['flow_classifier']['id'] - self.logger.debug("Creation of Flow Classifier is successful") - return(response.status_code) - else: - return(response.status_code) - - def createPortChain(self): - """Creation of PortChain.""" - Dicdata = {} - flow_class_list = [] - flow_class_list.append(self.flow_class_if) - port_pair_groups_list = [] - port_pair_groups_list.append(self.port_grp_id[0]) - - if flow_class_list != '': - Dicdata['flow_classifiers'] = flow_class_list - if self.pcname != '': - Dicdata['name'] = "PC1" - if port_pair_groups_list != '': - Dicdata['port_pair_groups'] = port_pair_groups_list - - Dicdata = {'port_chain': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/sfc/port_chains' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", - "Content-Type": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - self.logger.debug("Creation of PORT CHAIN is successful") - json1_data = json.loads(response.content) - self.PC_id = json1_data['port_chain']['id'] - return(response.status_code) - else: - return(response.status_code) - - def checkFlowAdded(self): - """Check whether the Flows are downloaded successfully.""" - time.sleep(5) - response = requests.get('http://' + self.onos_hostname + - ':8181/onos/v1/flows', - auth=("karaf", "karaf")) - if (response.status_code == OK): - self.logger.debug("Flow is successfully Queries") - json1_data = json.loads(response.content) - self.flowadd = json1_data['flows'][0]['state'] - - if (self.flowadd == "ADDED"): - self.logger.info("Flow is successfully added to OVS") - return(response.status_code) - else: - return(404) - else: - return(response.status_code) -#################################################################### - - def createRouter(self): - """Creation of Router.""" - Dicdata = {} - if self.routername != '': - Dicdata['name'] = "router1" - if self.admin_state_up != '': - Dicdata['admin_state_up'] = self.admin_state_up - - Dicdata = {'router': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = 'http://%s:9696/%s/routers.json' % (self.neutron_hostname, - self.osver) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == CREATED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.debug("Creation of Router is successfull") - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.router_id = json1_data['router']['id'] - return(response.status_code) - else: - return(response.status_code) - - def attachInterface(self): - """Attachment of instance ports to the Router.""" - url = ("http://%s:9696/%s/networks" - "?name=admin_floating_net" % (self.neutron_hostname, - self.osver)) - headers = {"Accept": "application/json", "X-Auth-Token": self.token_id} - response = requests.get(url, headers=headers) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.net_name = json1_data['networks'][0]['name'] - if (self.net_name == "admin_floating_net"): - self.pub_net_id = json1_data['networks'][0]['id'] - else: - return(response.status_code) - ############################################################ - - self.logger.info("Attachment of Instance interface to Router") - Dicdata = {} - if self.subnetId != '': - Dicdata['subnet_id'] = self.subnetId - - data = json.dumps(Dicdata, indent=4) - url = ("http://%s:9696/%s/routers" - "/%s/add_router_interface" % (self.neutron_hostname, - self.osver, - self.router_id)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.put(url, headers=headers, data=data) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("Interface attached successfull") - else: - return(response.status_code) - ############################################################ - self.logger.info("Attachment of Gateway to Router") - - Dicdata1 = {} - if self.pub_net_id != 0: - Dicdata1['network_id'] = self.pub_net_id - - Dicdata1 = {'external_gateway_info': Dicdata1} - Dicdata1 = {'router': Dicdata1} - data = json.dumps(Dicdata1, indent=4) - url = 'http://%s:9696/%s/routers/%s' % (self.neutron_hostname, - self.osver, - self.router_id) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.put(url, headers=headers, data=data) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("Gateway Interface attached successfull") - return(response.status_code) - else: - return(response.status_code) - - def addFloatingIp(self): - """Attachment of Floating Ip to the Router.""" - for ip_num in range(0, 2): - Dicdata = {} - Dicdata['pool'] = "admin_floating_net" - - data = json.dumps(Dicdata, indent=4) - url = ("http://%s:8774/v2.1/" - "os-floating-ips" % (self.nova_hostname)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("Floating ip created successfully") - json1_data = json.loads(response.content) - self.logger.debug(json1_data) - self.vm_public_ip.append(json1_data['floating_ip']['ip']) - self.vm_public_id.append(json1_data['floating_ip']['id']) - else: - self.logger.error("Floating ip NOT created successfully") - - Dicdata1 = {} - if self.address != '': - Dicdata1['address'] = self.vm_public_ip[ip_num] - - Dicdata1 = {'addFloatingIp': Dicdata1} - data = json.dumps(Dicdata1, indent=4) - url = ("http://%s:8774/v2.1/" - "servers/%s/action" % (self.nova_hostname, - self.vm[ip_num])) - - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.post(url, headers=headers, data=data) - if(response.status_code == ACCEPTED): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.info("Public Ip successfully added to VM") - else: - return(response.status_code) - return(response.status_code) - - def loginToVM(self): - """Login to the VM to check NSH packets are received.""" - queue1 = "0" - - def vm0(): - - s = pxssh.pxssh() - hostname = self.vm_public_ip[0] - s.login(hostname, self.cirros_username, self.cirros_password) - s.sendline("ping -c 5 " + str(self.port_ip[2])) - s.prompt() # match the prompt - - ping_re = re.search("transmitted.*received", s.before).group() - x = re.split('\s+', ping_re) - if (x[1] >= "1"): - self.logger.info("Ping is Successfull") - else: - self.logger.info("Ping is NOT Successfull") - - def vm1(queue1): - s = pxssh.pxssh() - hostname = self.vm_public_ip[1] - s.login(hostname, self.cirros_username, self.cirros_password) - s.sendline('sudo ./firewall') - s.prompt() - output_pack = s.before - - if(output_pack.find("nshc") != -1): - self.logger.info("The packet has reached VM2 Instance") - queue1.put("1") - else: - self.logger.info("Packet not received in Instance") - queue1.put("0") - - def ping(ip, timeout=300): - while True: - time.sleep(1) - self.logger.debug("Pinging %s. Waiting for response..." % ip) - response = os.system("ping -c 1 " + ip + " >/dev/null 2>&1") - if response == 0: - self.logger.info("Ping " + ip + " detected!") - return 0 - - elif timeout == 0: - self.logger.info("Ping " + ip + " timeout reached.") - return 1 - timeout -= 1 - - result0 = ping(self.vm_public_ip[0]) - result1 = ping(self.vm_public_ip[1]) - if result0 == 0 and result1 == 0: - time.sleep(300) - queue1 = Queue() - p1 = Process(target=vm1, args=(queue1, )) - p1.start() - p2 = Process(target=vm0) - p2.start() - p1.join(10) - return (queue1.get()) - else: - self.logger.error("Thread didnt run") - - """##################################################################""" - """ ######################## Stats Functions ################# #####""" - - def portChainDeviceMap(self): - """Check the PC Device Stats in the ONOS.""" - response = requests.get('http://' + self.onos_hostname + - ':8181/onos/vtn/portChainDeviceMap/' + - self.PC_id, auth=("karaf", "karaf")) - if (response.status_code == OK): - self.logger.info("PortChainDeviceMap is successfully Queries") - return(response.status_code) - else: - return(response.status_code) - - def portChainSfMap(self): - """Check the PC SF Map Stats in the ONOS.""" - response = requests.get('http://' + self.onos_hostname + - ':8181/onos/vtn/portChainSfMap/' + - self.PC_id, auth=("karaf", "karaf")) - if (response.status_code == OK): - self.logger.info("portChainSfMap is successfully Queries") - return(response.status_code) - else: - return(response.status_code) - - """###################################################################""" - - def deletePortChain(self): - """Deletion of PortChain.""" - url = ('http://' + self.neutron_hostname + ':9696/' + - self.osver + '/sfc/port_chains/' + self.PC_id) - headers = {"Accept": "application/json", "Content-Type": - "application/json", "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - return(response.status_code) - else: - return(response.status_code) - - def deleteFlowClassifier(self): - """Deletion of Flow Classifier.""" - url = ("http://%s:9696/%s/sfc/" - "flow_classifiers/%s" % (self.neutron_hostname, - self.osver, - self.flow_class_if)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - return(response.status_code) - else: - return(response.status_code) - - def deletePortGroup(self): - """Deletion of PortGroup.""" - for p in range(0, 1): - url = ("http://%s:9696/%s/sfc/" - "port_pair_groups/%s" % (self.neutron_hostname, - self.osver, - self.port_grp_id[p])) - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - else: - return(response.status_code) - return(response.status_code) - - def deletePortPair(self): - """Deletion of Portpair.""" - for p in range(1, 2): - url = ("http://%s:9696/%s/sfc/" - "port_pairs/%s" % (self.neutron_hostname, - self.osver, - self.port_pair_id[0])) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - else: - return(response.status_code) - return(response.status_code) - - def cleanup(self): - """Cleanup.""" - self.logger.info("Deleting VMs") - for y in range(0, 3): - url = ("http://%s:8774/v2.1/" - "/servers/%s" % (self.nova_hostname, - self.vm[y])) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.debug("VM" + str(y) + " is Deleted : ") - time.sleep(10) - else: - return(response.status_code) - self.logger.info("Deleting Ports") - for x in range(self.i, self.numTerms): - url = ('http://' + self.neutron_hostname + ':9696/' + - self.osver + '/ports/' + self.port_num[x]) - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.delete(url, headers=headers) - - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - self.logger.debug("Port" + str(x) + " Deleted") - else: - return(response.status_code) - self.logger.info("Deleting Router") - - Dicdata = {} - Dicdata['external_gateway_info'] = {} - Dicdata = {'router': Dicdata} - data = json.dumps(Dicdata, indent=4) - url = ("http://%s:9696/%s/" - "/routers/%s" % (self.neutron_hostname, - self.osver, - self.router_id)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.put(url, headers=headers, data=data) - if (response.status_code == OK): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - Dicdata1 = {} - if self.subnetId != '': - Dicdata1['subnet_id'] = self.subnetId - data = json.dumps(Dicdata1, indent=4) - url = ("http://%s:9696/%s/routers/%s" - "/remove_router_interface.json" % (self.neutron_hostname, - self.osver, - self.router_id)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.put(url, headers=headers, data=data) - if (response.status_code == OK): - url = ("http://%s:9696/%s/" - "routers/%s" % (self.neutron_hostname, - self.osver, - self.router_id)) - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - else: - return(response.status_code) - else: - return(response.status_code) - else: - return(response.status_code) - - self.logger.info("Deleting Network") - url = "http://%s:9696/%s/networks/%s" % (self.neutron_hostname, - self.osver, - self.net_id) - - headers = {"Accept": "application/json", - "X-Auth-Token": self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - else: - return(response.status_code) - - self.logger.info("Deleting Floating ip") - for ip_num in range(0, 2): - url = ("http://%s:9696/%s/floatingips" - "/%s" % (self.neutron_hostname, - self.osver, - self.vm_public_id[ip_num])) - - headers = {"Accept": "application/json", "X-Auth-Token": - self.token_id} - response = requests.delete(url, headers=headers) - if (response.status_code == NO_CONTENT): - self.logger.debug(response.status_code) - self.logger.debug(response.content) - else: - return(response.status_code) - return(response.status_code) diff --git a/functest/opnfv_tests/sdn/onos/teston/Readme.txt b/functest/opnfv_tests/sdn/onos/teston/Readme.txt deleted file mode 100644 index 7393f59a..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/Readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -1.This is a basic test run about onos,we will make them better and better -2.This test include two suites: -(1)Test northbound(network/subnet/ports create/update/delete) -(2)Ovsdb test,default configuration,openflow connection,vm go onlines. -3.Later we will make a framework to do this test
\ No newline at end of file diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/client.py b/functest/opnfv_tests/sdn/onos/teston/adapters/client.py deleted file mode 100644 index a88d2f06..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/client.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Description: - This file is used to run testcase - lanqinglong@huawei.com - -# -# 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 -# -""" -import json -import logging -import pexpect -import requests -import time - -from environment import Environment - - -class Client(Environment): - - logger = logging.getLogger(__name__) - - def __init__(self): - Environment.__init__(self) - self.loginfo = Environment() - self.testcase = '' - - def RunScript(self, handle, testname, timeout=300): - """ - Run ONOS Test Script - Parameters: - testname: ONOS Testcase Name - masterusername: The server username of running ONOS - masterpassword: The server password of running ONOS - """ - self.testcase = testname - self.ChangeTestCasePara(testname, self.masterusername, - self.masterpassword) - runhandle = handle - runtest = (self.home + "/OnosSystemTest/TestON/bin/cli.py run " + - testname) - runhandle.sendline(runtest) - circletime = 0 - lastshowscreeninfo = '' - while True: - Result = runhandle.expect(["PEXPECT]#", pexpect.EOF, - pexpect.TIMEOUT]) - curshowscreeninfo = runhandle.before - if(len(lastshowscreeninfo) != len(curshowscreeninfo)): - self.loginfo.log(str(curshowscreeninfo) - [len(lastshowscreeninfo)::]) - lastshowscreeninfo = curshowscreeninfo - if Result == 0: - self.logger.info("Done!") - return - time.sleep(1) - circletime += 1 - if circletime > timeout: - break - self.loginfo.log("Timeout when running the test, please check!") - - def onosstart(self): - # This is the compass run machine user&pass,you need to modify - - self.logger.info("Test Begin.....") - self.OnosConnectionSet() - masterhandle = self.SSHlogin(self.localhost, self.masterusername, - self.masterpassword) - self.OnosEnvSetup(masterhandle) - return masterhandle - - def onosclean(self, handle): - self.SSHRelease(handle) - self.loginfo.log('Release onos handle Successful') - - def push_results_to_db(self, payload, pushornot=1): - if pushornot != 1: - return 1 - url = self.Result_DB + "/results" - params = {"project_name": "functest", "case_name": "ONOS-" + - self.testcase, "pod_name": 'huawei-build-2', - "details": payload} - - headers = {'Content-Type': 'application/json'} - try: - r = requests.post(url, data=json.dumps(params), headers=headers) - self.loginfo.log(r) - except: - self.loginfo.log('Error pushing results into Database') diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/connection.py b/functest/opnfv_tests/sdn/onos/teston/adapters/connection.py deleted file mode 100644 index a6d192ee..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/connection.py +++ /dev/null @@ -1,200 +0,0 @@ -""" -Description: - This file is used to make connections - Include ssh & exchange public-key to each other so that - it can run without password - - lanqinglong@huawei.com - -# -# 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 -# -""" -import logging -import os -import pexpect -import re - -from foundation import Foundation - - -class Connection(Foundation): - - logger = logging.getLogger(__name__) - - def __init__(self): - Foundation.__init__(self) - self.loginfo = Foundation() - - def AddKnownHost(self, handle, ipaddr, username, password): - """ - Add an user to known host,so that onos can login in with onos $ipaddr. - parameters: - ipaddr: ip address - username: login user name - password: login password - """ - self.logger.info("Now Adding an user to known hosts " + ipaddr) - login = handle - login.sendline("ssh -l %s -p 8101 %s" % (username, ipaddr)) - index = 0 - while index != 2: - index = login.expect(['assword:', 'yes/no', pexpect.EOF, - pexpect.TIMEOUT]) - if index == 0: - login.sendline(password) - login.sendline("logout") - index = login.expect(["closed", pexpect.EOF]) - if index == 0: - self.loginfo.log("Add SSH Known Host Success!") - break - else: - self.loginfo.log("Add SSH Known Host Failed! " - "Please Check!") - break - login.prompt() - - if index == 1: - login.sendline('yes') - - def GetEnvValue(self, handle, envname): - """ - os.getenv only returns current user value - GetEnvValue returns a environment value of - current handle - eg: GetEnvValue(handle,'HOME') - """ - envhandle = handle - envhandle.sendline('echo $' + envname) - envhandle.prompt() - reg = envname + '\r\n(.*)\r' - envaluereg = re.compile(reg) - envalue = envaluereg.search(envhandle.before) - if envalue: - return envalue.groups()[0] - else: - return None - - def Gensshkey(self, handle): - """ - Generate ssh keys, used for some server have no sshkey. - """ - self.logger.info("Now Generating SSH keys...") - # Here file name may be id_rsa or id_ecdsa or others - # So here will have a judgement - keysub = handle - filepath = self.GetEnvValue(keysub, 'HOME') + '/.ssh' - filelist = os.listdir(filepath) - for item in filelist: - if 'id' in item: - self.loginfo.log("SSH keys are exsit in ssh directory.") - return True - keysub.sendline("ssh-keygen -t rsa") - Result = 0 - while Result != 2: - Result = keysub.expect(["Overwrite", "Enter", pexpect.EOF, - 'PEXPECT]#', pexpect.TIMEOUT]) - if Result == 0: - keysub.sendline("y") - if Result == 1 or Result == 2: - keysub.sendline("\n") - if Result == 3: - self.loginfo.log("Generate SSH key success.") - keysub.prompt() - break - if Result == 4: - self.loginfo.log("Generate SSH key failed.") - keysub.prompt() - break - - def GetRootAuth(self, password): - """ - Get root user - parameters: - password: root login password - """ - self.logger.info("Now changing to user root") - login = pexpect.spawn("su - root") - index = 0 - while index != 2: - index = login.expect(['assword:', "failure", - pexpect.EOF, pexpect.TIMEOUT]) - if index == 0: - login.sendline(password) - if index == 1: - self.loginfo.log("Change user to root failed.") - - login.interact() - - def ReleaseRootAuth(self): - """ - Exit root user. - """ - self.logger.info("Now Release user root") - login = pexpect.spawn("exit") - index = login.expect(['logout', pexpect.EOF, pexpect.TIMEOUT]) - if index == 0: - self.loginfo.log("Release root user success.") - if index == 1: - self.loginfo.log("Release root user failed.") - - login.interact() - - def AddEnvIntoBashrc(self, envalue): - """ - Add Env var into /etc/profile. - parameters: - envalue: environment value to add - """ - self.logger.info("Now Adding bash environment") - fileopen = open("/etc/profile", 'r') - findContext = 1 - while findContext: - findContext = fileopen.readline() - result = findContext.find(envalue) - if result != -1: - break - fileopen.close - if result == -1: - envAdd = open("/etc/profile", 'a+') - envAdd.writelines("\n" + envalue) - envAdd.close() - self.loginfo.log("Add env to bashrc success!") - - def OnosRootPathChange(self, onospath): - """ - Change ONOS root path in file:bash_profile - onospath: path of onos root - """ - self.logger.info("Now Changing ONOS Root Path") - filepath = onospath + 'onos/tools/dev/bash_profile' - line = open(filepath, 'r').readlines() - lenall = len(line) - 1 - for i in range(lenall): - if "export ONOS_ROOT" in line[i]: - line[i] = 'export ONOS_ROOT=' + onospath + 'onos\n' - NewFile = open(filepath, 'w') - NewFile.writelines(line) - NewFile.close - self.logger.info("Done!") - - def OnosConnectionSet(self): - """ - Intergrate for ONOS connection setup - """ - if self.masterusername == 'root': - filepath = '/root/' - else: - filepath = '/home/' + self.masterusername + '/' - filepath = os.path.join(filepath, "onos/tools/dev/bash_profile") - self.AddEnvIntoBashrc("source " + filepath + "\n") - self.AddEnvIntoBashrc("export OCT=" + self.OCT) - self.AddEnvIntoBashrc("export OC1=" + self.OC1) - self.AddEnvIntoBashrc("export OC2=" + self.OC2) - self.AddEnvIntoBashrc("export OC3=" + self.OC3) - self.AddEnvIntoBashrc("export OCN=" + self.OCN) - self.AddEnvIntoBashrc("export OCN2=" + self.OCN2) - self.AddEnvIntoBashrc("export localhost=" + self.localhost) diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/environment.py b/functest/opnfv_tests/sdn/onos/teston/adapters/environment.py deleted file mode 100644 index 875a2dc9..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/environment.py +++ /dev/null @@ -1,286 +0,0 @@ -""" -Description: -This file is used to setup the running environment -Include Download code,setup environment variable -Set onos running config -Set user name/password -Onos-push-keys and so on -lanqinglong@huawei.com - -# -# 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 -# -""" - -import logging -import pexpect -from pexpect import pxssh -import re -import os -import sys -import time - -from connection import Connection - - -class Environment(Connection): - - logger = logging.getLogger(__name__) - - def __init__(self): - Connection.__init__(self) - self.loginfo = Connection() - self.masterhandle = '' - self.home = '' - - def DownLoadCode(self, handle, codeurl): - """ - Download Code use 'git clone' - parameters: - handle: current working handle - codeurl: clone code url - """ - self.logger.info("Now loading test codes! Please wait in patient...") - originalfolder = sys.path[0] - self.logger.info(originalfolder) - gitclone = handle - gitclone.sendline("git clone " + codeurl) - index = 0 - # increment = 0 - while index != 1 or index != 4: - index = gitclone.expect(['already exists', - 'esolving deltas: 100%', - 'eceiving objects', - 'Already up-to-date', - 'npacking objects: 100%', pexpect.EOF]) - - filefolder = self.home + '/' + codeurl.split('/')[-1].split('.')[0] - if index == 0: - os.chdir(filefolder) - os.system('git pull') - os.chdir(originalfolder) - self.loginfo.log('Download code success!') - break - elif index == 1 or index == 4: - self.loginfo.log('Download code success!') - gitclone.sendline("mkdir onos") - gitclone.prompt() - gitclone.sendline("cp -rf " + filefolder + "/tools onos/") - gitclone.prompt() - break - elif index == 2: - os.write(1, gitclone.before) - sys.stdout.flush() - else: - self.loginfo.log('Download code failed!') - self.loginfo.log('Information before' + gitclone.before) - break - gitclone.prompt() - - def InstallDefaultSoftware(self, handle): - """ - Install default software - parameters: - handle(input): current working handle - """ - self.logger.info("Now Cleaning test environment") - handle.sendline("sudo apt-get install -y mininet") - handle.prompt() - handle.sendline("sudo pip install configobj") - handle.prompt() - handle.sendline("sudo apt-get install -y sshpass") - handle.prompt() - handle.sendline("OnosSystemTest/TestON/bin/cleanup.sh") - handle.prompt() - time.sleep(5) - self.loginfo.log('Clean environment success!') - - def OnosPushKeys(self, handle, cmd, password): - """ - Using onos-push-keys to make ssh device without password - parameters: - handle(input): working handle - cmd(input): onos-push-keys xxx(xxx is device) - password(input): login in password - """ - self.logger.info("Now Pushing Onos Keys:" + cmd) - Pushkeys = handle - Pushkeys.sendline(cmd) - Result = 0 - while Result != 2: - Result = Pushkeys.expect(["(yes/no)", "assword:", "PEXPECT]#", - pexpect.EOF, pexpect.TIMEOUT]) - if(Result == 0): - Pushkeys.sendline("yes") - if(Result == 1): - Pushkeys.sendline(password) - if(Result == 2): - self.loginfo.log("ONOS Push keys Success!") - break - if(Result == 3): - self.loginfo.log("ONOS Push keys Error!") - break - time.sleep(2) - Pushkeys.prompt() - self.logger.info("Done!") - - def SetOnosEnvVar(self, handle, masterpass, agentpass): - """ - Setup onos pushkeys to all devices(3+2) - parameters: - handle(input): current working handle - masterpass: scripts running server's password - agentpass: onos cluster&compute node password - """ - self.logger.info("Now Setting test environment") - for host in self.hosts: - self.logger.info("try to connect " + str(host)) - result = self.CheckSshNoPasswd(host) - if not result: - self.logger.info( - "ssh login failed,try to copy master publickey" + - "to agent " + str(host)) - self.CopyPublicKey(host) - self.OnosPushKeys(handle, "onos-push-keys " + self.OCT, masterpass) - self.OnosPushKeys(handle, "onos-push-keys " + self.OC1, agentpass) - self.OnosPushKeys(handle, "onos-push-keys " + self.OC2, agentpass) - self.OnosPushKeys(handle, "onos-push-keys " + self.OC3, agentpass) - self.OnosPushKeys(handle, "onos-push-keys " + self.OCN, agentpass) - self.OnosPushKeys(handle, "onos-push-keys " + self.OCN2, agentpass) - - def CheckSshNoPasswd(self, host): - """ - Check master can connect agent with no password - """ - login = pexpect.spawn("ssh " + str(host)) - index = 4 - while index == 4: - index = login.expect(['(yes/no)', '>|#|\$', - pexpect.EOF, pexpect.TIMEOUT]) - if index == 0: - login.sendline("yes") - index = 4 - if index == 1: - self.loginfo.log("ssh connect to " + str(host) + - " success,no need to copy ssh public key") - return True - login.interact() - return False - - def ChangeOnosName(self, user, password): - """ - Change onos name in envDefault file - Because some command depend on this - parameters: - user: onos&compute node user - password: onos&compute node password - """ - self.logger.info("Now Changing ONOS name&password") - filepath = self.home + '/onos/tools/build/envDefaults' - line = open(filepath, 'r').readlines() - lenall = len(line) - 1 - for i in range(lenall): - if "ONOS_USER=" in line[i]: - line[i] = line[i].replace("sdn", user) - if "ONOS_GROUP" in line[i]: - line[i] = line[i].replace("sdn", user) - if "ONOS_PWD" in line[i]: - line[i] = line[i].replace("rocks", password) - NewFile = open(filepath, 'w') - NewFile.writelines(line) - NewFile.close - self.logger.info("Done!") - - def ChangeTestCasePara(self, testcase, user, password): - """ - When running test script, there\'s something need - to change in every test folder\'s \*.param & \*.topo files - user: onos\&compute node user - password: onos\&compute node password - """ - self.logger.info("Now Changing " + testcase + " name&password") - if self.masterusername == 'root': - filepath = '/root/' - else: - filepath = '/home/' + self.masterusername + '/' - filepath = (filepath + "OnosSystemTest/TestON/tests/" + - testcase + "/" + testcase + ".topo") - line = open(filepath, 'r').readlines() - lenall = len(line) - 1 - for i in range(lenall - 2): - if("localhost" in line[i]) or ("OCT" in line[i]): - line[i + 1] = re.sub(">\w+", ">" + user, line[i + 1]) - line[i + 2] = re.sub(">\w+", ">" + password, line[i + 2]) - if ("OC1" in line[i] or "OC2" in line[i] or "OC3" in line[i] or - "OCN" in line[i] or "OCN2" in line[i]): - line[i + 1] = re.sub(">\w+", ">root", line[i + 1]) - line[i + 2] = re.sub(">\w+", ">root", line[i + 2]) - NewFile = open(filepath, 'w') - NewFile.writelines(line) - NewFile.close - - def SSHlogin(self, ipaddr, username, password): - """ - SSH login provide a connection to destination. - parameters: - ipaddr: ip address - username: login user name - password: login password - return: handle - """ - login = pxssh.pxssh() - login.login(ipaddr, username, password, original_prompt='[$#>]') - # send command ls -l - login.sendline('ls -l') - # match prompt - login.prompt() - self.logger.info("SSH login " + ipaddr + " success!") - return login - - def SSHRelease(self, handle): - # Release ssh - handle.logout() - - def CopyOnostoTestbin(self): - sourcefile = self.cipath + '/dependencies/onos' - destifile = self.home + '/onos/tools/test/bin/' - os.system('pwd') - runcommand = 'cp ' + sourcefile + ' ' + destifile - os.system(runcommand) - - def CopyPublicKey(self, host): - output = os.popen('cat /root/.ssh/id_rsa.pub') - publickey = output.read().strip('\n') - tmphandle = self.SSHlogin(self.installer_master, - self.installer_master_username, - self.installer_master_password) - tmphandle.sendline("ssh " + host + " -T \'echo " + - str(publickey) + ">>/root/.ssh/authorized_keys\'") - tmphandle.prompt() - self.SSHRelease(tmphandle) - self.logger.info("Add OCT PublicKey to " + host + " success") - - def OnosEnvSetup(self, handle): - """ - Onos Environment Setup function - """ - self.Gensshkey(handle) - self.home = self.GetEnvValue(handle, 'HOME') - self.AddKnownHost(handle, self.OC1, "karaf", "karaf") - self.AddKnownHost(handle, self.OC2, "karaf", "karaf") - self.AddKnownHost(handle, self.OC3, "karaf", "karaf") - self.DownLoadCode(handle, - 'https://github.com/wuwenbin2/OnosSystemTest.git') - # self.DownLoadCode(handle, 'https://gerrit.onosproject.org/onos') - if self.masterusername == 'root': - filepath = '/root/' - else: - filepath = '/home/' + self.masterusername + '/' - self.OnosRootPathChange(filepath) - self.CopyOnostoTestbin() - self.ChangeOnosName(self.agentusername, self.agentpassword) - self.InstallDefaultSoftware(handle) - self.SetOnosEnvVar(handle, self.masterpassword, self.agentpassword) diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py b/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py deleted file mode 100644 index aed98ee4..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Description: - This file include basis functions - lanqinglong@huawei.com - -# -# 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 -# -""" - -import datetime -import logging -import os -import re -import time - -from functest.utils.constants import CONST - - -class Foundation(object): - - def __init__(self): - - # currentpath = os.getcwd() - currentpath = ('{0}/sdn/onos/teston/ci' - .format(CONST.__getattribute__('dir_functest_data'))) - self.cipath = currentpath - self.logdir = os.path.join(currentpath, 'log') - self.workhome = currentpath[0: currentpath.rfind('opnfv_tests') - 1] - self.Result_DB = '' - filename = time.strftime('%Y-%m-%d-%H-%M-%S') + '.log' - self.logfilepath = os.path.join(self.logdir, filename) - self.starttime = datetime.datetime.now() - - def log(self, loginfo): - """ - Record log in log directory for deploying test environment - parameters: - loginfo(input): record info - """ - logging.basicConfig(level=logging.INFO, - format='%(asctime)s %(filename)s:%(message)s', - datefmt='%d %b %Y %H:%M:%S', - filename=self.logfilepath, - filemode='w') - filelog = logging.FileHandler(self.logfilepath) - logging.getLogger('Functest').addHandler(filelog) - logging.info(loginfo) - - def getdefaultpara(self): - """ - Get Default Parameters value - """ - self.Result_DB = CONST.__getattribute__("results_test_db_url") - self.masterusername = CONST.__getattribute__('ONOS_onosbench_username') - self.masterpassword = CONST.__getattribute__('ONOS_onosbench_password') - self.agentusername = CONST.__getattribute__('ONOS_onoscli_username') - self.agentpassword = CONST.__getattribute__('ONOS_onoscli_password') - self.runtimeout = CONST.__getattribute__('ONOS_runtimeout') - self.OCT = CONST.__getattribute__('ONOS_environment_OCT') - self.OC1 = CONST.__getattribute__('ONOS_environment_OC1') - self.OC2 = CONST.__getattribute__('ONOS_environment_OC2') - self.OC3 = CONST.__getattribute__('ONOS_environment_OC3') - self.OCN = CONST.__getattribute__('ONOS_environment_OCN') - self.OCN2 = CONST.__getattribute__('ONOS_environment_OCN2') - self.installer_master = CONST.__getattribute__( - 'ONOS_environment_installer_master') - self.installer_master_username = ( - CONST.__getattribute__( - 'ONOS_environment_installer_master_username')) - self.installer_master_password = ( - CONST.__getattribute__( - 'ONOS_environment_installer_master_password')) - self.hosts = [self.OC1, self.OCN, self.OCN2] - self.localhost = self.OCT - - def GetResult(self): - cmd = "cat " + self.logfilepath + " | grep Fail" - Resultbuffer = os.popen(cmd).read() - duration = datetime.datetime.now() - self.starttime - time.sleep(2) - - if re.search("[1-9]+", Resultbuffer): - self.log("Testcase Fails\n" + Resultbuffer) - Result = "POK" - else: - self.log("Testcases Pass") - Result = "OK" - payload = {'timestart': str(self.starttime), - 'duration': str(duration), 'status': Result} - - return payload diff --git a/functest/opnfv_tests/sdn/onos/teston/dependencies/onos b/functest/opnfv_tests/sdn/onos/teston/dependencies/onos deleted file mode 100644 index bb02fa89..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/dependencies/onos +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# ----------------------------------------------------------------------------- -# ONOS remote command-line client. -# ----------------------------------------------------------------------------- -# -# 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 -# - -[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 -. /root/.bashrc -. $ONOS_ROOT/tools/build/envDefaults -. $ONOS_ROOT/tools/test/bin/find-node.sh - -[ "$1" = "-w" ] && shift && onos-wait-for-start $1 - -[ -n "$1" ] && OCI=$(find_node $1) && shift - -if which client 1>/dev/null 2>&1 && [ -z "$ONOS_USE_SSH" ]; then - # Use Karaf client only if we can and are allowed to - unset KARAF_HOME - client -h $OCI -u karaf "$@" 2>/dev/null -else - # Otherwise use raw ssh; strict checking is off for dev environments only - #ssh -p 8101 -o StrictHostKeyChecking=no $OCI "$@" - sshpass -p karaf ssh -l karaf -p 8101 $OCI "$@" -fi diff --git a/functest/opnfv_tests/sdn/onos/teston/log/gitignore b/functest/opnfv_tests/sdn/onos/teston/log/gitignore deleted file mode 100644 index e69de29b..00000000 --- a/functest/opnfv_tests/sdn/onos/teston/log/gitignore +++ /dev/null diff --git a/functest/opnfv_tests/vnf/router/cloudify_vrouter.py b/functest/opnfv_tests/vnf/router/cloudify_vrouter.py new file mode 100644 index 00000000..c3cccb98 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/cloudify_vrouter.py @@ -0,0 +1,542 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter testcase implementation.""" + +import logging +import os +import time + +from cloudify_rest_client import CloudifyClient +from cloudify_rest_client.executions import Execution +from scp import SCPClient +import yaml + +from functest.opnfv_tests.openstack.snaps import snaps_utils +import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base +from functest.utils.constants import CONST +import functest.utils.openstack_utils as os_utils + +from git import Repo + +from snaps.openstack.os_credentials import OSCreds +from snaps.openstack.create_network import (NetworkSettings, SubnetSettings, + OpenStackNetwork) +from snaps.openstack.create_security_group import (SecurityGroupSettings, + SecurityGroupRuleSettings, + Direction, Protocol, + OpenStackSecurityGroup) +from snaps.openstack.create_router import RouterSettings, OpenStackRouter +from snaps.openstack.create_instance import (VmInstanceSettings, + FloatingIpSettings, + OpenStackVmInstance) +from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor +from snaps.openstack.create_image import ImageSettings, OpenStackImage +from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair +from snaps.openstack.create_network import PortSettings +import snaps.openstack.utils.glance_utils as glance_utils + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf + +__author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>" + + +class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): + """vrouter testcase deployed with Cloudify Orchestrator.""" + + __logger = logging.getLogger(__name__) + name = __name__ + + def __init__(self, **kwargs): + if "case_name" not in kwargs: + kwargs["case_name"] = "vyos_vrouter" + super(CloudifyVrouter, self).__init__(**kwargs) + + # Retrieve the configuration + try: + self.config = CONST.__getattribute__( + 'vnf_{}_config'.format(self.case_name)) + except Exception: + raise Exception("VNF config file not found") + + self.snaps_creds = '' + self.created_object = [] + + self.cfy_manager_ip = '' + self.util_info = {} + self.deployment_name = '' + + 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.__logger.debug("name = %s", self.name) + self.vnf = dict( + descriptor=get_config("vnf.descriptor", config_file), + inputs=get_config("vnf.inputs", config_file), + requirements=get_config("vnf.requirements", 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.util = Utilvnf() + + self.details['test_vnf'] = dict( + name=get_config("vnf_test_suite.name", config_file), + version=get_config("vnf_test_suite.version", config_file) + ) + self.images = get_config("tenant_images", config_file) + self.__logger.info("Images needed for vrouter: %s", self.images) + + def prepare(self): + super(CloudifyVrouter, self).prepare() + + self.__logger.info("Additional pre-configuration steps") + + self.snaps_creds = OSCreds( + username=self.creds['username'], + password=self.creds['password'], + auth_url=self.creds['auth_url'], + project_name=self.creds['tenant'], + identity_api_version=int(os_utils.get_keystone_client_version())) + + self.util.set_credentials(self.creds["username"], + self.creds["password"], + self.creds["auth_url"], + self.creds["tenant"]) + + # needs some images + self.__logger.info("Upload some OS images if it doesn't exist") + for image_name, image_file in self.images.iteritems(): + self.__logger.info("image: %s, file: %s", image_name, image_file) + if image_file and image_name: + image_creator = OpenStackImage( + self.snaps_creds, + ImageSettings(name=image_name, + image_user='cloud', + img_format='qcow2', + image_file=image_file)) + image_creator.create() + self.created_object.append(image_creator) + + def deploy_orchestrator(self): + """ + Deploy Cloudify Manager. + network, security group, fip, VM creation + """ + # network creation + + start_time = time.time() + self.__logger.info("Creating keypair ...") + kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem") + keypair_settings = KeypairSettings(name='cloudify_vrouter_kp', + private_filepath=kp_file) + keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings) + keypair_creator.create() + self.created_object.append(keypair_creator) + + self.__logger.info("Creating full network ...") + subnet_settings = SubnetSettings(name='cloudify_vrouter_subnet', + cidr='10.67.79.0/24') + network_settings = NetworkSettings(name='cloudify_vrouter_network', + subnet_settings=[subnet_settings]) + network_creator = OpenStackNetwork(self.snaps_creds, network_settings) + network_creator.create() + self.created_object.append(network_creator) + ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds) + router_creator = OpenStackRouter( + self.snaps_creds, + RouterSettings( + name='cloudify_vrouter_router', + external_gateway=ext_net_name, + internal_subnets=[subnet_settings.name])) + router_creator.create() + self.created_object.append(router_creator) + + # security group creation + self.__logger.info("Creating security group for cloudify manager vm") + sg_rules = list() + sg_rules.append( + SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager", + direction=Direction.ingress, + protocol=Protocol.tcp, port_range_min=1, + port_range_max=65535)) + sg_rules.append( + SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager", + direction=Direction.ingress, + protocol=Protocol.udp, port_range_min=1, + port_range_max=65535)) + + security_group_creator = OpenStackSecurityGroup( + self.snaps_creds, + SecurityGroupSettings( + name="sg-cloudify-manager", + rule_settings=sg_rules)) + + security_group_creator.create() + self.created_object.append(security_group_creator) + + # orchestrator VM flavor + self.__logger.info("Get or create flavor for cloudify manager vm ...") + + flavor_settings = FlavorSettings( + name=self.orchestrator['requirements']['flavor']['name'], + ram=self.orchestrator['requirements']['flavor']['ram_min'], + disk=50, + vcpus=2) + flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) + flavor_creator.create() + self.created_object.append(flavor_creator) + image_settings = ImageSettings( + name=self.orchestrator['requirements']['os_image'], + image_user='centos', + exists=True) + + port_settings = PortSettings(name='cloudify_manager_port', + network_name=network_settings.name) + + manager_settings = VmInstanceSettings( + name='cloudify_manager', + flavor=flavor_settings.name, + port_settings=[port_settings], + security_group_names=[ + security_group_creator.sec_grp_settings.name], + floating_ip_settings=[FloatingIpSettings( + name='cloudify_manager_fip', + port_name=port_settings.name, + router_name=router_creator.router_settings.name)]) + + manager_creator = OpenStackVmInstance(self.snaps_creds, + manager_settings, + image_settings, + keypair_settings) + + self.__logger.info("Creating cloudify manager VM") + manager_creator.create() + self.created_object.append(manager_creator) + + public_auth_url = os_utils.get_endpoint('identity') + + self.__logger.info("Set creds for cloudify manager") + cfy_creds = dict(keystone_username=self.tenant_name, + keystone_password=self.tenant_name, + keystone_tenant_name=self.tenant_name, + keystone_url=public_auth_url) + + cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip, + username='admin', + password='admin', + tenant='default_tenant') + + self.orchestrator['object'] = cfy_client + + self.cfy_manager_ip = manager_creator.get_floating_ip().ip + + self.__logger.info("Attemps running status of the Manager") + cfy_status = None + retry = 10 + while str(cfy_status) != 'running' and retry: + try: + cfy_status = cfy_client.manager.get_status()['status'] + self.__logger.debug("The current manager status is %s", + cfy_status) + except Exception: # pylint: disable=broad-except + self.__logger.warning("Cloudify Manager isn't " + + "up and running. Retrying ...") + retry = retry - 1 + time.sleep(30) + + if str(cfy_status) == 'running': + self.__logger.info("Cloudify Manager is up and running") + else: + raise Exception("Cloudify Manager isn't up and running") + + self.__logger.info("Put OpenStack creds in manager") + secrets_list = cfy_client.secrets.list() + for k, val in cfy_creds.iteritems(): + if not any(d.get('key', None) == k for d in secrets_list): + cfy_client.secrets.create(k, val) + else: + cfy_client.secrets.update(k, val) + + duration = time.time() - start_time + + self.__logger.info("Put private keypair in manager") + if manager_creator.vm_ssh_active(block=True): + ssh = manager_creator.ssh_client() + scp = SCPClient(ssh.get_transport(), socket_timeout=15.0) + scp.put(kp_file, '~/') + cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/" + run_blocking_ssh_command(ssh, cmd) + cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem" + run_blocking_ssh_command(ssh, cmd) + cmd = "sudo yum install -y gcc python-devel" + run_blocking_ssh_command( + ssh, cmd, "Unable to install packages on manager") + + self.details['orchestrator'].update(status='PASS', duration=duration) + + self.vnf['inputs'].update(dict(external_network_name=ext_net_name)) + + return True + + def deploy_vnf(self): + start_time = time.time() + + self.__logger.info("Upload VNFD") + cfy_client = self.orchestrator['object'] + descriptor = self.vnf['descriptor'] + self.deployment_name = descriptor.get('name') + + vrouter_blueprint_dir = os.path.join(self.data_dir, + self.util.blueprint_dir) + if not os.path.exists(vrouter_blueprint_dir): + Repo.clone_from(descriptor.get('url'), + vrouter_blueprint_dir, + branch=descriptor.get('version')) + + cfy_client.blueprints.upload(vrouter_blueprint_dir + + self.util.blueprint_file_name, + descriptor.get('name')) + + self.__logger.info("Get or create flavor for vrouter") + flavor_settings = FlavorSettings( + name=self.vnf['requirements']['flavor']['name'], + ram=self.vnf['requirements']['flavor']['ram_min'], + disk=25, + vcpus=1) + flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) + flavor = flavor_creator.create() + self.created_object.append(flavor_creator) + + # set image name + glance = glance_utils.glance_client(self.snaps_creds) + image = glance_utils.get_image(glance, + "vyos1.1.7") + self.vnf['inputs'].update(dict(target_vnf_image_id=image.id)) + self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id)) + + # set flavor id + self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id)) + self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id)) + + self.vnf['inputs'].update(dict(keystone_username=self.tenant_name)) + self.vnf['inputs'].update(dict(keystone_password=self.tenant_name)) + self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name)) + self.vnf['inputs'].update( + dict(keystone_url=os_utils.get_endpoint('identity'))) + + self.__logger.info("Create VNF Instance") + cfy_client.deployments.create(descriptor.get('name'), + descriptor.get('name'), + self.vnf.get('inputs')) + + wait_for_execution(cfy_client, + get_execution_id( + cfy_client, descriptor.get('name')), + self.__logger, + timeout=7200) + + self.__logger.info("Start the VNF Instance deployment") + execution = cfy_client.executions.start(descriptor.get('name'), + 'install') + # Show execution log + execution = wait_for_execution(cfy_client, execution, self.__logger) + + duration = time.time() - start_time + + self.__logger.info(execution) + if execution.status == 'terminated': + self.details['vnf'].update(status='PASS', duration=duration) + result = True + else: + self.details['vnf'].update(status='FAIL', duration=duration) + result = False + return result + + def test_vnf(self): + cfy_client = self.orchestrator['object'] + credentials = {"username": self.creds["username"], + "password": self.creds["password"], + "auth_url": self.creds["auth_url"], + "tenant_name": self.creds["tenant"], + "region_name": os.environ['OS_REGION_NAME']} + + self.util_info = {"credentials": credentials, + "cfy": cfy_client, + "vnf_data_dir": self.util.vnf_data_dir} + + start_time = time.time() + + result, test_result_data = super(CloudifyVrouter, self).test_vnf() + + duration = time.time() - start_time + + if result: + self.details['test_vnf'].update(status='PASS', + result='OK', + full_result=test_result_data, + duration=duration) + else: + self.details['test_vnf'].update(status='FAIL', + result='NG', + full_result=test_result_data, + duration=duration) + + return True + + def clean(self): + try: + cfy_client = self.orchestrator['object'] + dep_name = self.vnf['descriptor'].get('name') + # kill existing execution + self.__logger.info('Deleting the current deployment') + exec_list = cfy_client.executions.list(dep_name) + for execution in exec_list: + if execution['status'] == "started": + try: + cfy_client.executions.cancel(execution['id'], + force=True) + except: # pylint: disable=broad-except + self.__logger.warn("Can't cancel the current exec") + + execution = cfy_client.executions.start( + dep_name, + 'uninstall', + parameters=dict(ignore_failure=True)) + + wait_for_execution(cfy_client, execution, self.__logger) + cfy_client.deployments.delete(self.vnf['descriptor'].get('name')) + cfy_client.blueprints.delete(self.vnf['descriptor'].get('name')) + except: # pylint: disable=broad-except + self.__logger.warn("Some issue during the undeployment ..") + self.__logger.warn("Tenant clean continue ..") + + self.__logger.info('Remove the cloudify manager OS object ..') + for creator in reversed(self.created_object): + try: + creator.clean() + except Exception as exc: + self.logger.error('Unexpected error cleaning - %s', exc) + + super(CloudifyVrouter, self).clean() + + def run(self, **kwargs): + """Execute CloudifyVrouter test case.""" + return super(CloudifyVrouter, self).run(**kwargs) + + def get_vnf_info_list(self, target_vnf_name): + return self.util.get_vnf_info_list(self.cfy_manager_ip, + self.deployment_name, + target_vnf_name) + + +# ---------------------------------------------------------- +# +# 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 + + +def wait_for_execution(client, execution, logger, timeout=7200, ): + """Wait for a workflow execution on Cloudify Manager.""" + # if execution already ended - return without waiting + if execution.status in Execution.END_STATES: + return execution + + if timeout is not None: + deadline = time.time() + timeout + + # Poll for execution status and execution logs, until execution ends + # and we receive an event of type in WORKFLOW_END_TYPES + offset = 0 + batch_size = 50 + event_list = [] + execution_ended = False + while True: + event_list = client.events.list( + execution_id=execution.id, + _offset=offset, + _size=batch_size, + include_logs=False, + sort='@timestamp').items + + offset = offset + len(event_list) + for event in event_list: + logger.debug(event.get('message')) + + if timeout is not None: + if time.time() > deadline: + raise RuntimeError( + 'execution of operation {0} for deployment {1} ' + 'timed out'.format(execution.workflow_id, + execution.deployment_id)) + else: + # update the remaining timeout + timeout = deadline - time.time() + + if not execution_ended: + execution = client.executions.get(execution.id) + execution_ended = execution.status in Execution.END_STATES + + if execution_ended: + break + + time.sleep(5) + + return execution + + +def get_execution_id(client, deployment_id): + """ + Get the execution id of a env preparation. + network, security group, fip, VM creation + """ + executions = client.executions.list(deployment_id=deployment_id) + for execution in executions: + if execution.workflow_id == 'create_deployment_environment': + return execution + raise RuntimeError('Failed to get create_deployment_environment ' + 'workflow execution.' + 'Available executions: {0}'.format(executions)) + + +def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"): + """Command to run ssh command with the exit status.""" + (_, stdout, _) = ssh.exec_command(cmd) + if stdout.channel.recv_exit_status() != 0: + raise Exception(error_msg) diff --git a/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml b/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml new file mode 100644 index 00000000..c09477ab --- /dev/null +++ b/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml @@ -0,0 +1,31 @@ +tenant_images: + cloudify_manager_4.0: /home/opnfv/functest/images/cloudify-manager-premium-4.0.1.qcow2 + vyos1.1.7: /home/opnfv/functest/images/vyos-1.1.7.img +test_data: + url: 'https://github.com/oolorg/opnfv-vnf-data.git' + branch: 'master' +orchestrator: + name: cloudify + version: '4.0' + requirements: + flavor: + name: m1.medium + ram_min: 4096 + os_image: 'cloudify_manager_4.0' +vnf: + name: vyos1.1.7 + version: '1.1.7' + descriptor: + url: https://github.com/oolorg/opnfv-vnf-vyos-blueprint/ + name: vrouter-opnfv + version: 'master' + requirements: + flavor: + name: m1.medium + ram_min: 2048 + inputs: + external_network_name: admin_floating_net + region: RegionOne +vnf_test_suite: + name: vyos-vrouter-test + version: "1.0" diff --git a/functest/opnfv_tests/sdn/onos/__init__.py b/functest/opnfv_tests/vnf/router/test_controller/__init__.py index e69de29b..e69de29b 100644 --- a/functest/opnfv_tests/sdn/onos/__init__.py +++ b/functest/opnfv_tests/vnf/router/test_controller/__init__.py diff --git a/functest/opnfv_tests/vnf/router/test_controller/function_test_exec.py b/functest/opnfv_tests/vnf/router/test_controller/function_test_exec.py new file mode 100644 index 00000000..236447e0 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/test_controller/function_test_exec.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter function test execution module""" + +import logging +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf +from functest.opnfv_tests.vnf.router.vnf_controller.vnf_controller import ( + VnfController) + + +class FunctionTestExec(object): + """vrouter function test execution class""" + + logger = logging.getLogger(__name__) + + def __init__(self, util_info): + self.logger.debug("init test exec") + self.util = Utilvnf() + credentials = util_info["credentials"] + self.vnf_ctrl = VnfController(util_info) + + test_cmd_map_file = open(self.util.vnf_data_dir + + self.util.opnfv_vnf_data_dir + + self.util.command_template_dir + + self.util.test_cmd_map_yaml_file, + 'r') + self.test_cmd_map_yaml = yaml.safe_load(test_cmd_map_file) + test_cmd_map_file.close() + + self.util.set_credentials(credentials["username"], + credentials["password"], + credentials["auth_url"], + credentials["tenant_name"], + credentials["region_name"]) + + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.protocol_stable_wait = test_env_config_yaml.get("general").get( + "protocol_stable_wait") + + def config_target_vnf(self, target_vnf, reference_vnf, test_kind): + self.logger.debug("Configuration to target vnf") + test_info = self.test_cmd_map_yaml[target_vnf["os_type"]] + test_cmd_file_path = test_info[test_kind]["pre_command_target"] + target_parameter_file_path = test_info[test_kind]["parameter_target"] + prompt_file_path = test_info["prompt"] + + return self.vnf_ctrl.config_vnf(target_vnf, + reference_vnf, + test_cmd_file_path, + target_parameter_file_path, + prompt_file_path) + + def config_reference_vnf(self, target_vnf, reference_vnf, test_kind): + self.logger.debug("Configuration to reference vnf") + test_info = self.test_cmd_map_yaml[reference_vnf["os_type"]] + test_cmd_file_path = test_info[test_kind]["pre_command_reference"] + reference_parameter_file_path = test_info[test_kind][ + "parameter_reference"] + prompt_file_path = test_info["prompt"] + + return self.vnf_ctrl.config_vnf(reference_vnf, + target_vnf, + test_cmd_file_path, + reference_parameter_file_path, + prompt_file_path) + + def result_check(self, target_vnf, reference_vnf, test_kind, test_list): + test_info = self.test_cmd_map_yaml[target_vnf["os_type"]] + target_parameter_file_path = test_info[test_kind]["parameter_target"] + prompt_file_path = test_info["prompt"] + check_rule_file_path_list = [] + + for test in test_list: + check_rule_file_path_list.append(test_info[test_kind][test]) + + return self.vnf_ctrl.result_check(target_vnf, + reference_vnf, + check_rule_file_path_list, + target_parameter_file_path, + prompt_file_path) + + def run(self, target_vnf, reference_vnf_list, test_info, test_list): + test_result_data = {} + test_kind = test_info["protocol"] + for reference_vnf in reference_vnf_list: + self.logger.debug("Start config command " + + target_vnf["vnf_name"] + " and " + + reference_vnf["vnf_name"]) + + result = self.config_target_vnf(target_vnf, + reference_vnf, + test_kind) + if not result: + return False, test_result_data + + result = self.config_reference_vnf(target_vnf, + reference_vnf, + test_kind) + if not result: + return False, test_result_data + + self.logger.debug("Finish config command.") + + self.logger.debug("Waiting for protocol stable.") + time.sleep(self.protocol_stable_wait) + + self.logger.debug("Start check method") + + (result, res_dict_data_list) = self.result_check(target_vnf, + reference_vnf, + test_kind, + test_list) + + test_result_data = {"test_kind": test_info["test_kind"], + "protocol": test_info["protocol"], + "result": res_dict_data_list} + + if not result: + self.logger.debug("Error check method.") + return False, test_result_data + + self.logger.debug("Finish check method.") + + return True, test_result_data diff --git a/functest/opnfv_tests/vnf/router/test_scenario.yaml b/functest/opnfv_tests/vnf/router/test_scenario.yaml new file mode 100644 index 00000000..03185592 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/test_scenario.yaml @@ -0,0 +1,27 @@ +test_scenario_list: + - + test_type: 'function_test' + vnf_list: + - + vnf_name: 'target_vnf' + os_type: 'vyos' + image_name: 'vyos1.1.7' + flavor_name: 'm1.medium' + - + vnf_name: 'reference_vnf' + os_type: 'vyos' + image_name: 'vyos1.1.7' + flavor_name: 'm1.medium' + function_test_list: + - + target_vnf_name: 'target_vnf' + test_list: + - + test_kind: 'Interoperability' + protocol: 'BGP' + BGP: + - 'Checking_the_peer_of_BGP' + - 'Checking_the_status_of_BGP' + - 'Checking_the_advertised_routes' + - 'Checking_the_received_routes' + - 'Checking_the_routing_table' diff --git a/functest/opnfv_tests/vnf/router/utilvnf.py b/functest/opnfv_tests/vnf/router/utilvnf.py new file mode 100644 index 00000000..084af331 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/utilvnf.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +""" Utility module of vrouter testcase """ + +import json +import logging +import os +import pkg_resources +import requests +import yaml + +from functest.utils.constants import CONST +from git import Repo +from novaclient import client as novaclient +from keystoneauth1 import session +from keystoneauth1 import loading +from requests.auth import HTTPBasicAuth + +RESULT_SPRIT_INDEX = { + "transfer": 8, + "bandwidth": 6, + "jitter": 4, + "los_total": 2, + "pkt_loss": 1 +} + +BIT_PER_BYTE = 8 + +NOVA_CLIENT_API_VERSION = '2' +NOVA_CILENT_NETWORK_INFO_INDEX = 0 +CFY_INFO_OUTPUT_FILE = "output.txt" + +CIDR_NETWORK_SEGMENT_INFO_INDEX = 0 +PACKET_LOST_INFO_INDEX = 0 +PACKET_TOTAL_INFO_INDEX = 1 + +NUMBER_OF_DIGITS_FOR_AVG_TRANSFER = 0 +NUMBER_OF_DIGITS_FOR_AVG_BANDWIDTH = 0 +NUMBER_OF_DIGITS_FOR_AVG_JITTER = 3 +NUMBER_OF_DIGITS_FOR_AVG_PKT_LOSS = 1 + + +class Utilvnf(object): + """ Utility class of vrouter testcase """ + + logger = logging.getLogger(__name__) + + def __init__(self): + self.username = "" + self.password = "" + self.auth_url = "" + self.tenant_name = "" + self.region_name = "" + + data_dir = data_dir = CONST.__getattribute__('dir_router_data') + + self.vnf_data_dir = data_dir + self.opnfv_vnf_data_dir = "opnfv-vnf-data/" + self.command_template_dir = "command_template/" + self.test_scenario_yaml = "test_scenario.yaml" + test_env_config_yaml_file = "test_env_config.yaml" + self.test_cmd_map_yaml_file = "test_cmd_map.yaml" + self.test_env_config_yaml = os.path.join( + self.vnf_data_dir, + self.opnfv_vnf_data_dir, + test_env_config_yaml_file) + + self.blueprint_dir = "opnfv-vnf-vyos-blueprint/" + self.blueprint_file_name = "function-test-openstack-blueprint.yaml" + + if not os.path.exists(self.vnf_data_dir): + os.makedirs(self.vnf_data_dir) + + case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/router') + + config_file_name = CONST.__getattribute__( + 'vnf_{}_config'.format("vyos_vrouter")) + + config_file = os.path.join(case_dir, config_file_name) + + with open(config_file) as file_fd: + vrouter_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + test_data = vrouter_config_yaml.get("test_data") + + self.logger.debug("Downloading the test data.") + vrouter_data_path = self.vnf_data_dir + self.opnfv_vnf_data_dir + + if not os.path.exists(vrouter_data_path): + Repo.clone_from(test_data['url'], + vrouter_data_path, + branch=test_data['branch']) + + with open(self.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.image = test_env_config_yaml.get( + "general").get("images").get("vyos") + self.tester_image = test_env_config_yaml.get( + "general").get("images").get("tester_vm_os") + + self.test_result_json_file = "test_result.json" + if os.path.isfile(self.test_result_json_file): + os.remove(self.test_result_json_file) + self.logger.debug("removed %s" % self.test_result_json_file) + + def get_nova_client(self): + creds = self.get_nova_credentials() + loader = loading.get_plugin_loader('password') + auth = loader.load_from_options(**creds) + sess = session.Session(auth=auth) + nova_client = novaclient.Client(NOVA_CLIENT_API_VERSION, session=sess) + + return nova_client + + def set_credentials(self, username, password, auth_url, + tenant_name, region_name="RegionOne"): + self.username = username + self.password = password + self.auth_url = auth_url + self.tenant_name = tenant_name + self.region_name = region_name + + def get_nova_credentials(self): + creds = {} + creds['username'] = self.username + creds['password'] = self.password + creds['auth_url'] = self.auth_url + creds['tenant_name'] = self.tenant_name + return creds + + def get_address(self, server_name, network_name): + nova_client = self.get_nova_client() + servers_list = nova_client.servers.list() + server = None + + for server in servers_list: + if server.name == server_name: + break + + address = server.addresses[ + network_name][NOVA_CILENT_NETWORK_INFO_INDEX]["addr"] + + return address + + def get_mac_address(self, server_name, network_name): + nova_client = self.get_nova_client() + servers_list = nova_client.servers.list() + server = None + + for server in servers_list: + if server.name == server_name: + break + + mac_address = server.addresses[network_name][ + NOVA_CILENT_NETWORK_INFO_INDEX][ + "OS-EXT-IPS-MAC:mac_addr"] + + return mac_address + + def reboot_vm(self, server_name): + nova_client = self.get_nova_client() + servers_list = nova_client.servers.list() + server = None + + for server in servers_list: + if server.name == server_name: + break + + server.reboot() + + return + + def delete_vm(self, server_name): + nova_client = self.get_nova_client() + servers_list = nova_client.servers.list() + server = None + + for server in servers_list: + if server.name == server_name: + nova_client.servers.delete(server) + break + + return + + def get_blueprint_outputs(self, cfy_manager_ip, deployment_name): + url = "http://%s/deployments/%s/outputs" % ( + cfy_manager_ip, deployment_name) + + response = requests.get( + url, + auth=HTTPBasicAuth('admin', 'admin'), + headers={'Tenant': 'default_tenant'}) + + resp_data = response.json() + self.logger.debug(resp_data) + data = resp_data["outputs"] + return data + + def get_blueprint_outputs_vnfs(self, cfy_manager_ip, deployment_name): + outputs = self.get_blueprint_outputs(cfy_manager_ip, + deployment_name) + vnfs = outputs["vnfs"] + vnf_list = [] + for vnf_name in vnfs: + vnf_list.append(vnfs[vnf_name]) + return vnf_list + + def get_blueprint_outputs_networks(self, cfy_manager_ip, deployment_name): + outputs = self.get_blueprint_outputs(cfy_manager_ip, + deployment_name) + networks = outputs["networks"] + network_list = [] + for network_name in networks: + network_list.append(networks[network_name]) + return network_list + + def request_vnf_reboot(self, vnf_info_list): + for vnf in vnf_info_list: + self.logger.debug("reboot the " + vnf["vnf_name"]) + self.reboot_vm(vnf["vnf_name"]) + + def request_vm_delete(self, vnf_info_list): + for vnf in vnf_info_list: + self.logger.debug("delete the " + vnf["vnf_name"]) + self.delete_vm(vnf["vnf_name"]) + + def get_vnf_info_list(self, cfy_manager_ip, topology_deploy_name, + target_vnf_name): + network_list = self.get_blueprint_outputs_networks( + cfy_manager_ip, + topology_deploy_name) + vnf_info_list = self.get_blueprint_outputs_vnfs(cfy_manager_ip, + topology_deploy_name) + for vnf in vnf_info_list: + vnf_name = vnf["vnf_name"] + vnf["os_type"] = self.image["os_type"] + vnf["user"] = self.image["user"] + vnf["pass"] = self.image["pass"] + + if vnf_name == target_vnf_name: + vnf["target_vnf_flag"] = True + else: + vnf["target_vnf_flag"] = False + + self.logger.debug("vnf name : " + vnf_name) + self.logger.debug(vnf_name + " floating ip address : " + + vnf["floating_ip"]) + + for network in network_list: + network_name = network["network_name"] + ip_address = self.get_address(vnf["vnf_name"], + network["network_name"]) + vnf[network_name + "_ip"] = ip_address + mac = self.get_mac_address(vnf["vnf_name"], + network["network_name"]) + vnf[network_name + "_mac"] = mac + + self.logger.debug(network_name + "_ip of " + vnf["vnf_name"] + + " : " + vnf[network_name + "_ip"]) + self.logger.debug(network_name + "_mac of " + vnf["vnf_name"] + + " : " + vnf[network_name + "_mac"]) + + return vnf_info_list + + def get_target_vnf(self, vnf_info_list): + for vnf in vnf_info_list: + if vnf["target_vnf_flag"]: + return vnf + + return None + + def get_reference_vnf_list(self, vnf_info_list): + reference_vnf_list = [] + for vnf in vnf_info_list: + if not vnf["target_vnf_flag"]: + reference_vnf_list.append(vnf) + + return reference_vnf_list + + def get_vnf_info(self, vnf_info_list, vnf_name): + for vnf in vnf_info_list: + if vnf["vnf_name"] == vnf_name: + return vnf + + return None + + def convert_functional_test_result(self, result_data_list): + result = {} + for result_data in result_data_list: + test_kind = result_data["test_kind"] + protocol = result_data["protocol"] + test_result_data = result_data["result"] + + if test_kind not in result: + result[test_kind] = [] + + result[test_kind].append({protocol: test_result_data}) + + return {"Functional_test": result} + + def write_result_data(self, result_data): + test_result = [] + if not os.path.isfile(self.test_result_json_file): + file_fd = open(self.test_result_json_file, "w") + file_fd.close() + else: + file_fd = open(self.test_result_json_file, "r") + test_result = json.load(file_fd) + file_fd.close() + + test_result.append(result_data) + + file_fd = open(self.test_result_json_file, "w") + json.dump(test_result, file_fd) + file_fd.close() + + def output_test_result_json(self): + if os.path.isfile(self.test_result_json_file): + file_fd = open(self.test_result_json_file, "r") + test_result = json.load(file_fd) + file_fd.close() + output_json_data = json.dumps(test_result, + sort_keys=True, + indent=4) + self.logger.debug("test_result %s" % output_json_data) + else: + self.logger.debug("Not found %s" % self.test_result_json_file) + + def get_test_scenario(self, file_path): + test_scenario_file = open(file_path, + 'r') + test_scenario_yaml = yaml.safe_load(test_scenario_file) + test_scenario_file.close() + return test_scenario_yaml["test_scenario_list"] diff --git a/functest/opnfv_tests/sdn/onos/teston/__init__.py b/functest/opnfv_tests/vnf/router/vnf_controller/__init__.py index e69de29b..e69de29b 100644 --- a/functest/opnfv_tests/sdn/onos/teston/__init__.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/__init__.py diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/checker.py b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py new file mode 100644 index 00000000..198a5ffc --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter test result check module""" + +import json +import logging +import re + +from jinja2 import Environment, FileSystemLoader + + +class Checker(object): + """vrouter test result check class""" + + logger = logging.getLogger(__name__) + + def __init__(self): + self.logger.debug("init checker") + + def load_check_rule(self, rule_file_dir, rule_file_name, parameter): + loader = FileSystemLoader(rule_file_dir, + encoding='utf8') + env = Environment(loader=loader) + check_rule_template = env.get_template(rule_file_name) + check_rule = check_rule_template.render(parameter) + check_rule_data = json.loads(check_rule) + return check_rule_data + + def regexp_information(self, response, rules): + status = False + result_data = {} + + for rule in rules["rules"]: + result_data = { + "test_name": rule["description"], + "result": "NG" + } + + match = re.search(rule["regexp"], + response) + rule["response"] = response + if match is None: + status = False + break + + if not match.group(1) == rule["result"]: + status = False + else: + result_data["result"] = "OK" + status = True + + return status, result_data diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py new file mode 100644 index 00000000..98cb14cc --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""command generator module for vrouter testing""" + +import logging +from jinja2 import Environment, FileSystemLoader + + +class CommandGenerator(object): + """command generator class for vrouter testing""" + + logger = logging.getLogger(__name__) + + def __init__(self): + self.logger.debug("init command generator") + + def load_template(self, template_dir, template): + loader = FileSystemLoader(template_dir, + encoding='utf8') + env = Environment(loader=loader) + return env.get_template(template) + + def command_create(self, template, parameter): + commands = template.render(parameter) + return commands.split('\n') diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py new file mode 100644 index 00000000..c85a5735 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""ssh client module for vrouter testing""" + +import logging +import paramiko +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf + +RECEIVE_ROOP_WAIT = 1 + +DEFAULT_CONNECT_TIMEOUT = 10 +DEFAULT_CONNECT_RETRY_COUNT = 10 +DEFAULT_SEND_TIMEOUT = 10 + + +class SshClient(object): + """ssh client class for vrouter testing""" + + logger = logging.getLogger(__name__) + + def __init__(self, ip_address, user, password=None, key_filename=None): + self.ip_address = ip_address + self.user = user + self.password = password + self.key_filename = key_filename + self.connected = False + self.shell = None + + self.logger.setLevel(logging.INFO) + + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + self.util = Utilvnf() + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.ssh_revieve_buff = test_env_config_yaml.get("general").get( + "ssh_receive_buffer") + + def connect(self, time_out=DEFAULT_CONNECT_TIMEOUT, + retrycount=DEFAULT_CONNECT_RETRY_COUNT): + while retrycount > 0: + try: + self.logger.info("SSH connect to %s.", self.ip_address) + self.ssh.connect(self.ip_address, + username=self.user, + password=self.password, + key_filename=self.key_filename, + timeout=time_out, + look_for_keys=False, + allow_agent=False) + + self.logger.info("SSH connection established to %s.", + self.ip_address) + + self.shell = self.ssh.invoke_shell() + + while not self.shell.recv_ready(): + time.sleep(RECEIVE_ROOP_WAIT) + + self.shell.recv(self.ssh_revieve_buff) + break + except: # pylint: disable=broad-except + self.logger.info("SSH timeout for %s...", self.ip_address) + time.sleep(time_out) + retrycount -= 1 + + if retrycount == 0: + self.logger.error("Cannot establish connection to IP '%s'. " + + "Aborting", + self.ip_address) + self.connected = False + return self.connected + + self.connected = True + return self.connected + + def send(self, cmd, prompt, timeout=DEFAULT_SEND_TIMEOUT): + if self.connected is True: + self.shell.settimeout(timeout) + self.logger.debug("Commandset : '%s'", cmd) + + try: + self.shell.send(cmd + '\n') + except: # pylint: disable=broad-except + self.logger.error("ssh send timeout : Command : '%s'", cmd) + return None + + res_buff = '' + while not res_buff.endswith(prompt): + time.sleep(RECEIVE_ROOP_WAIT) + try: + res = self.shell.recv(self.ssh_revieve_buff) + except: # pylint: disable=broad-except + self.logger.error("ssh receive timeout : Command : '%s'", + cmd) + break + + res_buff += res + + self.logger.debug("Response : '%s'", res_buff) + return res_buff + else: + self.logger.error("Cannot connected to IP '%s'.", self.ip_address) + return None + + def close(self): + if self.connected is True: + self.ssh.close() + + def error_check(response, err_strs=["error", + "warn", + "unknown command", + "already exist"]): + for err in err_strs: + if err in response: + return False + + return True diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py new file mode 100644 index 00000000..cd228fe2 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vm controll module""" + +import logging +import os +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf +from functest.opnfv_tests.vnf.router.vnf_controller.command_generator import ( + CommandGenerator) +from functest.opnfv_tests.vnf.router.vnf_controller.ssh_client import ( + SshClient) + + +class VmController(object): + """vm controll class""" + + logger = logging.getLogger(__name__) + + def __init__(self, util_info): + self.logger.debug("initialize vm controller") + self.command_gen = CommandGenerator() + credentials = util_info["credentials"] + + self.util = Utilvnf() + self.util.set_credentials(credentials["username"], + credentials["password"], + credentials["auth_url"], + credentials["tenant_name"], + credentials["region_name"]) + + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.reboot_wait = test_env_config_yaml.get("general").get( + "reboot_wait") + self.command_wait = test_env_config_yaml.get("general").get( + "command_wait") + self.ssh_connect_timeout = test_env_config_yaml.get("general").get( + "ssh_connect_timeout") + self.ssh_connect_retry_count = test_env_config_yaml.get("general").get( + "ssh_connect_retry_count") + + def command_gen_from_template(self, command_file_path, cmd_input_param): + (command_file_dir, command_file_name) = os.path.split( + command_file_path) + template = self.command_gen.load_template(command_file_dir, + command_file_name) + return self.command_gen.command_create(template, + cmd_input_param) + + def config_vm(self, vm_info, test_cmd_file_path, + cmd_input_param, prompt_file_path): + ssh = self.connect_ssh_and_config_vm(vm_info, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + if ssh is None: + return False + + ssh.close() + + return True + + def connect_ssh_and_config_vm(self, vm_info, test_cmd_file_path, + cmd_input_param, prompt_file_path): + + key_filename = None + if "key_path" in vm_info: + key_filename = vm_info["key_path"] + + ssh = SshClient(ip_address=vm_info["floating_ip"], + user=vm_info["user"], + password=vm_info["pass"], + key_filename=key_filename) + + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + self.logger.debug("try to vm reboot.") + self.util.reboot_vm(vm_info["vnf_name"]) + time.sleep(self.reboot_wait) + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + return None + + (result, _) = self.command_create_and_execute( + ssh, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + if not result: + ssh.close() + return None + + return ssh + + def command_create_and_execute(self, ssh, test_cmd_file_path, + cmd_input_param, prompt_file_path): + prompt_file = open(prompt_file_path, + 'r') + prompt = yaml.safe_load(prompt_file) + prompt_file.close() + config_mode_prompt = prompt["config_mode"] + + commands = self.command_gen_from_template(test_cmd_file_path, + cmd_input_param) + return self.command_list_execute(ssh, + commands, + config_mode_prompt) + + def command_list_execute(self, ssh, command_list, prompt): + res_data_list = [] + for command in command_list: + self.logger.debug("Command : " + command) + (res, res_data) = self.command_execute(ssh, + command, + prompt) + self.logger.debug("Response : " + res_data) + res_data_list.append(res_data) + if not res: + return res, res_data_list + + time.sleep(self.command_wait) + + return True, res_data_list + + def command_execute(self, ssh, command, prompt): + res_data = ssh.send(command, prompt) + if res_data is None: + self.logger.info("retry send command : " + command) + res_data = ssh.send(command, + prompt) + if not ssh.error_check(res_data): + return False, res_data + + return True, res_data diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py new file mode 100644 index 00000000..814e9e33 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter controll module""" + +import logging +import os +import prettytable +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf +from functest.opnfv_tests.vnf.router.vnf_controller.checker import Checker +from functest.opnfv_tests.vnf.router.vnf_controller.ssh_client import ( + SshClient) +from functest.opnfv_tests.vnf.router.vnf_controller.vm_controller import ( + VmController) + + +class VnfController(object): + """vrouter controll class""" + + logger = logging.getLogger(__name__) + + def __init__(self, util_info): + self.logger.debug("init vnf controller") + self.util = Utilvnf() + self.vm_controller = VmController(util_info) + + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.cmd_wait = test_env_config_yaml.get("general").get("command_wait") + self.ssh_connect_timeout = test_env_config_yaml.get("general").get( + "ssh_connect_timeout") + self.ssh_connect_retry_count = test_env_config_yaml.get("general").get( + "ssh_connect_retry_count") + + def config_vnf(self, source_vnf, destination_vnf, test_cmd_file_path, + parameter_file_path, prompt_file_path): + parameter_file = open(parameter_file_path, + 'r') + cmd_input_param = yaml.safe_load(parameter_file) + parameter_file.close() + + cmd_input_param["macaddress"] = source_vnf["data_plane_network_mac"] + cmd_input_param["source_ip"] = source_vnf["data_plane_network_ip"] + cmd_input_param["destination_ip"] = destination_vnf[ + "data_plane_network_ip"] + + return self.vm_controller.config_vm(source_vnf, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + + def result_check(self, target_vnf, reference_vnf, + check_rule_file_path_list, parameter_file_path, + prompt_file_path): + + res_dict_data_list = [] + + parameter_file = open(parameter_file_path, + 'r') + cmd_input_param = yaml.safe_load(parameter_file) + parameter_file.close() + + cmd_input_param["source_ip"] = target_vnf["data_plane_network_ip"] + cmd_input_param["destination_ip"] = reference_vnf[ + "data_plane_network_ip"] + + prompt_file = open(prompt_file_path, + 'r') + prompt = yaml.safe_load(prompt_file) + prompt_file.close() + terminal_mode_prompt = prompt["terminal_mode"] + + ssh = SshClient(target_vnf["floating_ip"], + target_vnf["user"], + target_vnf["pass"]) + + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + return False, res_dict_data_list + + checker = Checker() + + res_table = prettytable.PrettyTable( + header_style='upper', padding_width=5, + field_names=['test item', 'result']) + + status = True + res_data_list = [] + for check_rule_file_path in check_rule_file_path_list: + (check_rule_dir, check_rule_file) = os.path.split( + check_rule_file_path) + check_rules = checker.load_check_rule(check_rule_dir, + check_rule_file, + cmd_input_param) + (res, res_data) = self.vm_controller.command_execute( + ssh, + check_rules["command"], + terminal_mode_prompt) + res_data_list.append(res_data) + if not res: + status = False + break + + (res, res_dict_data) = checker.regexp_information(res_data, + check_rules) + res_dict_data_list.append(res_dict_data) + res_table.add_row([res_dict_data["test_name"], + res_dict_data["result"]]) + if not res: + status = False + + time.sleep(self.cmd_wait) + + ssh.close() + + self.logger.info("Test result:\n\n%s\n", res_table.get_string()) + + self.output_check_result_detail_data(res_data_list) + + return status, res_dict_data_list + + def output_check_result_detail_data(self, res_data_list): + for res_data in res_data_list: + self.logger.debug(res_data) diff --git a/functest/opnfv_tests/vnf/router/vrouter_base.py b/functest/opnfv_tests/vnf/router/vrouter_base.py new file mode 100644 index 00000000..a534f1f2 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vrouter_base.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter testing base class module""" + +import datetime +import json +import logging +import os +import pkg_resources +import time + +import functest.core.vnf as vnf +from functest.utils.constants import CONST +from functest.opnfv_tests.vnf.router.test_controller import function_test_exec +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf + +__author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>" + +REBOOT_WAIT = 30 + + +class VrouterOnBoardingBase(vnf.VnfOnBoarding): + """vrouter testing base class""" + + def __init__(self, **kwargs): + self.logger = logging.getLogger(__name__) + super(VrouterOnBoardingBase, self).__init__(**kwargs) + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/router') + self.data_dir = CONST.__getattribute__('dir_router_data') + self.result_dir = os.path.join(CONST.__getattribute__('dir_results'), + self.case_name) + self.util = Utilvnf() + self.util_info = {} + + self.vnf_list = [] + + 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 test_vnf(self): + """vrouter test execution""" + result = False + test_result_data_list = [] + test_scenario_file_path = os.path.join(self.case_dir, + self.util.test_scenario_yaml) + test_scenario_list = self.util.get_test_scenario( + test_scenario_file_path) + for test_scenario in test_scenario_list: + if test_scenario["test_type"] == "function_test": + function_test_list = test_scenario["function_test_list"] + for function_test in function_test_list: + test_list = function_test["test_list"] + target_vnf_name = function_test["target_vnf_name"] + for test_info in test_list: + self.logger.info(test_info["protocol"] + " " + + test_info["test_kind"] + + " test.") + (result, result_data) = self.function_test_vrouter( + target_vnf_name, + test_info) + test_result_data_list.append(result_data) + if not result: + break + + self.util.request_vm_delete(self.vnf_list) + + test_result_data = json.dumps(test_result_data_list, indent=4) + + return result, test_result_data + + def function_test_vrouter(self, target_vnf_name, test_info): + """function test execution""" + + test_protocol = test_info["protocol"] + test_list = test_info[test_protocol] + + vnf_info_list = self.get_vnf_info_list(target_vnf_name) + self.vnf_list = vnf_info_list + + self.logger.debug("request vnf's reboot.") + self.util.request_vnf_reboot(vnf_info_list) + time.sleep(REBOOT_WAIT) + + target_vnf = self.util.get_target_vnf(vnf_info_list) + + reference_vnf_list = self.util.get_reference_vnf_list(vnf_info_list) + + test_exec = function_test_exec.FunctionTestExec(self.util_info) + + # start test + start_time_ts = time.time() + self.logger.info("vRouter test Start Time:'%s'", ( + datetime.datetime.fromtimestamp(start_time_ts).strftime( + '%Y-%m-%d %H:%M:%S'))) + + (result, test_result_data) = test_exec.run(target_vnf, + reference_vnf_list, + test_info, + test_list) + + end_time_ts = time.time() + duration = round(end_time_ts - start_time_ts, 1) + self.logger.info("vRouter test duration :'%s'", duration) + + return result, test_result_data + + def get_vnf_info_list(self, target_vnf_name): + vnf_info_list = [] + return vnf_info_list diff --git a/functest/opnfv_tests/vnf/router/vyos_vrouter.py b/functest/opnfv_tests/vnf/router/vyos_vrouter.py deleted file mode 100644 index 5654278d..00000000 --- a/functest/opnfv_tests/vnf/router/vyos_vrouter.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2017 Okinawa Open Laboratory -# -# 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 -import functest.core.feature as base -import json -import os - -RESULT_DETAILS_FILE = "test_result.json" - - -class VrouterVnf(base.Feature): - def __init__(self, **kwargs): - kwargs["repo"] = 'dir_repo_vrouter' - if "case_name" not in kwargs: - kwargs["case_name"] = "vyos_vrouter" - super(VrouterVnf, self).__init__(**kwargs) - self.cmd = 'cd %s && ./run.sh' % self.repo - - def set_result_details(self): - filepath = os.path.join(self.repo, RESULT_DETAILS_FILE) - if os.path.exists(filepath): - f = open(filepath, 'r') - self.details = json.load(f) - f.close() - - def log_results(self): - if self.result == 'PASS': - self.set_result_details() - super(VrouterVnf, self).log_results() diff --git a/functest/tests/unit/energy/test_functest_energy.py b/functest/tests/unit/energy/test_functest_energy.py index a576e2c3..5e2254ad 100644 --- a/functest/tests/unit/energy/test_functest_energy.py +++ b/functest/tests/unit/energy/test_functest_energy.py @@ -17,7 +17,7 @@ import mock from functest.energy.energy import EnergyRecorder import functest.energy.energy as energy - +from functest.utils.constants import CONST CASE_NAME = "UNIT_TEST_CASE" STEP_NAME = "UNIT_TEST_STEP" @@ -237,16 +237,14 @@ class EnergyRecorderTest(unittest.TestCase): return_value={"scenario": PREVIOUS_SCENARIO, "step": PREVIOUS_STEP}) @mock.patch("functest.energy.energy.EnergyRecorder") - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch("functest.utils.functest_utils.get_functest_config", side_effect=config_loader_mock) def test_decorators_with_previous(self, loader_mock=None, - pod_mock=None, recorder_mock=None, cur_scenario_mock=None): """Test energy module decorators.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') self.__decorated_method() calls = [mock.call.start(self.case_name), mock.call.submit_scenario(PREVIOUS_SCENARIO, @@ -274,13 +272,11 @@ class EnergyRecorderTest(unittest.TestCase): @mock.patch("functest.utils.functest_utils.get_functest_config", side_effect=config_loader_mock) - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch("functest.energy.energy.requests.get", return_value=API_OK) - def test_load_config(self, loader_mock=None, pod_mock=None, - get_mock=None): + def test_load_config(self, loader_mock=None, get_mock=None): """Test load config.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') EnergyRecorder.energy_recorder_api = None EnergyRecorder.load_config() @@ -295,13 +291,11 @@ class EnergyRecorderTest(unittest.TestCase): @mock.patch("functest.utils.functest_utils.get_functest_config", side_effect=config_loader_mock_no_creds) - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch("functest.energy.energy.requests.get", return_value=API_OK) - def test_load_config_no_creds(self, loader_mock=None, pod_mock=None, - get_mock=None): + def test_load_config_no_creds(self, loader_mock=None, get_mock=None): """Test load config without creds.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') EnergyRecorder.energy_recorder_api = None EnergyRecorder.load_config() self.assertEquals(EnergyRecorder.energy_recorder_api["auth"], None) @@ -312,13 +306,11 @@ class EnergyRecorderTest(unittest.TestCase): @mock.patch("functest.utils.functest_utils.get_functest_config", return_value=None) - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch("functest.energy.energy.requests.get", return_value=API_OK) - def test_load_config_ex(self, loader_mock=None, pod_mock=None, - get_mock=None): + def test_load_config_ex(self, loader_mock=None, get_mock=None): """Test load config with exception.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') with self.assertRaises(AssertionError): EnergyRecorder.energy_recorder_api = None EnergyRecorder.load_config() @@ -326,13 +318,11 @@ class EnergyRecorderTest(unittest.TestCase): @mock.patch("functest.utils.functest_utils.get_functest_config", side_effect=config_loader_mock) - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch("functest.energy.energy.requests.get", return_value=API_KO) - def test_load_config_api_ko(self, loader_mock=None, pod_mock=None, - get_mock=None): + def test_load_config_api_ko(self, loader_mock=None, get_mock=None): """Test load config with API unavailable.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') EnergyRecorder.energy_recorder_api = None EnergyRecorder.load_config() self.assertEquals(EnergyRecorder.energy_recorder_api["available"], @@ -340,13 +330,11 @@ class EnergyRecorderTest(unittest.TestCase): @mock.patch("functest.utils.functest_utils.get_functest_config", return_value=None) - @mock.patch("functest.utils.functest_utils.get_pod_name", - return_value="MOCK_POD") @mock.patch('functest.energy.energy.requests.get', return_value=RECORDER_OK) - def test_get_current_scenario(self, loader_mock=None, - pod_mock=None, get_mock=None): + def test_get_current_scenario(self, loader_mock=None, get_mock=None): """Test get_current_scenario.""" + CONST.__setattr__('NODE_NAME', 'MOCK_POD') self.test_load_config() scenario = EnergyRecorder.get_current_scenario() self.assertTrue(scenario is not None) diff --git a/functest/tests/unit/openstack/refstack_client/test_refstack_client.py b/functest/tests/unit/openstack/refstack_client/test_refstack_client.py index c5601075..ca097483 100644 --- a/functest/tests/unit/openstack/refstack_client/test_refstack_client.py +++ b/functest/tests/unit/openstack/refstack_client/test_refstack_client.py @@ -12,9 +12,12 @@ import pkg_resources import unittest from functest.core import testcase -from functest.opnfv_tests.openstack.refstack_client import refstack_client +from functest.opnfv_tests.openstack.refstack_client.refstack_client import \ + RefstackClient, RefstackClientParser from functest.utils.constants import CONST +from snaps.openstack.os_credentials import OSCreds + class OSRefstackClientTesting(unittest.TestCase): @@ -25,34 +28,44 @@ class OSRefstackClientTesting(unittest.TestCase): 'functest', 'opnfv_tests/openstack/refstack_client/defcore.txt') def setUp(self): - self.defaultargs = {'config': self._config, - 'testlist': self._testlist} + self.default_args = {'config': self._config, + 'testlist': self._testlist} CONST.__setattr__('OS_AUTH_URL', 'https://ip:5000/v3') CONST.__setattr__('OS_INSECURE', 'true') - self.refstackclient = refstack_client.RefstackClient() + self.os_creds = OSCreds( + username='user', password='pass', + auth_url='http://foo.com:5000/v3', project_name='bar') + + @mock.patch('functest.opnfv_tests.openstack.refstack_client.tempest_conf.' + 'TempestConf', return_value=mock.Mock()) + def _create_client(self, mock_conf): + with mock.patch('snaps.openstack.tests.openstack_tests.' + 'get_credentials', return_value=self.os_creds): + return RefstackClient() def test_run_defcore_insecure(self): insecure = '-k' config = 'tempest.conf' testlist = 'testlist' + client = self._create_client() with mock.patch('functest.opnfv_tests.openstack.refstack_client.' 'refstack_client.ft_utils.execute_command') as m: cmd = ("refstack-client test {0} -c {1} -v --test-list {2}" .format(insecure, config, testlist)) - self.refstackclient.run_defcore(config, testlist) + client.run_defcore(config, testlist) m.assert_any_call(cmd) def test_run_defcore(self): CONST.__setattr__('OS_AUTH_URL', 'http://ip:5000/v3') - refstackclient = refstack_client.RefstackClient() insecure = '' config = 'tempest.conf' testlist = 'testlist' + client = self._create_client() with mock.patch('functest.opnfv_tests.openstack.refstack_client.' 'refstack_client.ft_utils.execute_command') as m: cmd = ("refstack-client test {0} -c {1} -v --test-list {2}" .format(insecure, config, testlist)) - refstackclient.run_defcore(config, testlist) + client.run_defcore(config, testlist) m.assert_any_call(cmd) @mock.patch('functest.opnfv_tests.openstack.refstack_client.' @@ -62,7 +75,7 @@ class OSRefstackClientTesting(unittest.TestCase): mock_logger_info): self.case_name = 'refstack_defcore' self.result = 0 - self.refstackclient.parse_refstack_result() + self._create_client().parse_refstack_result() mock_logger_info.assert_called_once_with( "Testcase %s success_rate is %s%%", self.case_name, self.result) @@ -82,10 +95,11 @@ class OSRefstackClientTesting(unittest.TestCase): "success": ['tempest.api.compute [18.464988s]'], "errors": ['tempest.api.volume [0.230334s]'], "skipped": ['tempest.api.network [1.265828s]']} + client = self._create_client() with mock.patch('__builtin__.open', mock.mock_open(read_data=log_file)): - self.refstackclient.parse_refstack_result() - self.assertEqual(self.refstackclient.details, self.details) + client.parse_refstack_result() + self.assertEqual(client.details, self.details) def _get_main_kwargs(self, key=None): kwargs = {'config': self._config, @@ -96,16 +110,18 @@ class OSRefstackClientTesting(unittest.TestCase): def _test_main(self, status, *args): kwargs = self._get_main_kwargs() - self.assertEqual(self.refstackclient.main(**kwargs), status) + client = self._create_client() + self.assertEqual(client.main(**kwargs), status) if len(args) > 0: args[0].assert_called_once_with( - refstack_client.RefstackClient.result_dir) + RefstackClient.result_dir) if len(args) > 1: args def _test_main_missing_keyword(self, key): kwargs = self._get_main_kwargs(key) - self.assertEqual(self.refstackclient.main(**kwargs), + client = self._create_client() + self.assertEqual(client.main(**kwargs), testcase.TestCase.EX_RUN_ERROR) def test_main_missing_conf(self): @@ -115,10 +131,10 @@ class OSRefstackClientTesting(unittest.TestCase): self._test_main_missing_keyword('testlist') def _test_argparser(self, arg, value): - self.defaultargs[arg] = value - parser = refstack_client.RefstackClientParser() + self.default_args[arg] = value + parser = RefstackClientParser() self.assertEqual(parser.parse_args(["--{}={}".format(arg, value)]), - self.defaultargs) + self.default_args) def test_argparser_conf(self): self._test_argparser('config', self._config) @@ -127,13 +143,13 @@ class OSRefstackClientTesting(unittest.TestCase): self._test_argparser('testlist', self._testlist) def test_argparser_multiple_args(self): - self.defaultargs['config'] = self._config - self.defaultargs['testlist'] = self._testlist - parser = refstack_client.RefstackClientParser() + self.default_args['config'] = self._config + self.default_args['testlist'] = self._testlist + parser = RefstackClientParser() self.assertEqual(parser.parse_args( ["--config={}".format(self._config), "--testlist={}".format(self._testlist) - ]), self.defaultargs) + ]), self.default_args) if __name__ == "__main__": diff --git a/functest/tests/unit/openstack/tempest/test_conf_utils.py b/functest/tests/unit/openstack/tempest/test_conf_utils.py index e2937a18..77558086 100644 --- a/functest/tests/unit/openstack/tempest/test_conf_utils.py +++ b/functest/tests/unit/openstack/tempest/test_conf_utils.py @@ -10,114 +10,83 @@ import unittest import mock -from functest.opnfv_tests.openstack.tempest import conf_utils +from functest.opnfv_tests.openstack.tempest import tempest, conf_utils from functest.utils.constants import CONST +from snaps.openstack.os_credentials import OSCreds class OSTempestConfUtilsTesting(unittest.TestCase): - def test_create_tempest_resources_missing_network_dic(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_shared_network_full', - return_value=None), \ - self.assertRaises(Exception) as context: - conf_utils.create_tempest_resources() - msg = 'Failed to create private network' - self.assertTrue(msg in context) - - def test_create_tempest_resources_missing_image(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_shared_network_full', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_or_create_image', - return_value=(mock.Mock(), None)), \ - self.assertRaises(Exception) as context: - - CONST.__setattr__('tempest_use_custom_images', True) - conf_utils.create_tempest_resources() - msg = 'Failed to create image' - self.assertTrue(msg in context) - - CONST.__setattr__('tempest_use_custom_images', False) - conf_utils.create_tempest_resources(use_custom_images=True) - msg = 'Failed to create image' - self.assertTrue(msg in context) - - def test_create_tempest_resources_missing_flavor(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_shared_network_full', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_or_create_image', - return_value=(mock.Mock(), 'image_id')), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_or_create_flavor', - return_value=(mock.Mock(), None)), \ - self.assertRaises(Exception) as context: - CONST.__setattr__('tempest_use_custom_images', True) - CONST.__setattr__('tempest_use_custom_flavors', True) - conf_utils.create_tempest_resources() - msg = 'Failed to create flavor' - self.assertTrue(msg in context) - - CONST.__setattr__('tempest_use_custom_images', True) - CONST.__setattr__('tempest_use_custom_flavors', False) - conf_utils.create_tempest_resources(use_custom_flavors=False) - msg = 'Failed to create flavor' - self.assertTrue(msg in context) - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'logger.error') - def create_tenant_user_and_tenant_ok(self, mock_logger_error): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_keystone_client', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_tenant', - return_value='test_tenant_id'): - conf_utils.create_tenant_user() - mock_logger_error.assert_not_called() - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'logger.error') - def create_tenant_user_and_user_ok(self, mock_logger_error): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_keystone_client', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_user', - return_value='test_user_id'): - conf_utils.create_tenant_user() - mock_logger_error.assert_not_called() - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'logger.error') - def create_tenant_user_and_tenant_failed(self, mock_logger_error): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_keystone_client', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_tenant', - return_value=None): - conf_utils.create_tenant_user() - msg = ("Failed to create %s tenant" - % CONST.__getattribute__('tempest_identity_tenant_name')) - mock_logger_error.assert_any_call(msg) - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'logger.error') - def create_tenant_user_and_user_failed(self, mock_logger_error): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.get_keystone_client', - return_value=mock.Mock()), \ - mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'os_utils.create_user', - return_value=None): - conf_utils.create_tenant_user() - msg = ("Failed to create %s user" - % CONST.__getattribute__('tempest_identity_user_name')) - mock_logger_error.assert_any_call(msg) + def setUp(self): + self.os_creds = OSCreds( + username='user', password='pass', + auth_url='http://foo.com:5000/v3', project_name='bar') + + @mock.patch('snaps.openstack.utils.deploy_utils.create_project', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_user', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_network', + return_value=None) + @mock.patch('snaps.openstack.utils.deploy_utils.create_image', + return_value=mock.Mock()) + def test_create_tempest_resources_missing_network_dic(self, *mock_args): + tempest_resources = tempest.TempestResourcesManager(os_creds={}) + with self.assertRaises(Exception) as context: + tempest_resources.create() + msg = 'Failed to create private network' + self.assertTrue(msg in context.exception) + + @mock.patch('snaps.openstack.utils.deploy_utils.create_project', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_user', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_network', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_image', + return_value=None) + def test_create_tempest_resources_missing_image(self, *mock_args): + tempest_resources = tempest.TempestResourcesManager(os_creds={}) + + CONST.__setattr__('tempest_use_custom_imagess', True) + with self.assertRaises(Exception) as context: + tempest_resources.create() + msg = 'Failed to create image' + self.assertTrue(msg in context.exception, msg=str(context.exception)) + + CONST.__setattr__('tempest_use_custom_imagess', False) + with self.assertRaises(Exception) as context: + tempest_resources.create(use_custom_images=True) + msg = 'Failed to create image' + self.assertTrue(msg in context.exception, msg=str(context.exception)) + + @mock.patch('snaps.openstack.utils.deploy_utils.create_project', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_user', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_network', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.utils.deploy_utils.create_image', + return_value=mock.Mock()) + @mock.patch('snaps.openstack.create_flavor.OpenStackFlavor.create', + return_value=None) + def test_create_tempest_resources_missing_flavor(self, *mock_args): + tempest_resources = tempest.TempestResourcesManager( + os_creds=self.os_creds) + + CONST.__setattr__('tempest_use_custom_images', True) + CONST.__setattr__('tempest_use_custom_flavors', True) + with self.assertRaises(Exception) as context: + tempest_resources.create() + msg = 'Failed to create flavor' + self.assertTrue(msg in context.exception, msg=str(context.exception)) + + CONST.__setattr__('tempest_use_custom_images', True) + CONST.__setattr__('tempest_use_custom_flavors', False) + with self.assertRaises(Exception) as context: + tempest_resources.create(use_custom_flavors=True) + msg = 'Failed to create flavor' + self.assertTrue(msg in context.exception, msg=str(context.exception)) def test_get_verifier_id_missing_verifier(self): CONST.__setattr__('tempest_deployment_name', 'test_deploy_name') @@ -201,23 +170,9 @@ class OSTempestConfUtilsTesting(unittest.TestCase): def test_backup_tempest_config_default(self): with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.exists', - return_value=False), \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.makedirs') as m1, \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.shutil.copyfile') as m2: + 'conf_utils.shutil.copyfile') as m1: conf_utils.backup_tempest_config('test_conf_file') self.assertTrue(m1.called) - self.assertTrue(m2.called) - - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.exists', - return_value=True), \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.shutil.copyfile') as m2: - conf_utils.backup_tempest_config('test_conf_file') - self.assertTrue(m2.called) def test_configure_tempest_default(self): with mock.patch('functest.opnfv_tests.openstack.tempest.' @@ -229,10 +184,6 @@ class OSTempestConfUtilsTesting(unittest.TestCase): self.assertTrue(m1.called) def test_configure_tempest_defcore_default(self): - img_flavor_dict = {'image_id': 'test_image_id', - 'flavor_id': 'test_flavor_id', - 'image_id_alt': 'test_image_alt_id', - 'flavor_id_alt': 'test_flavor_alt_id'} with mock.patch('functest.opnfv_tests.openstack.tempest.' 'conf_utils.configure_verifier', return_value='test_conf_file'), \ @@ -252,8 +203,9 @@ class OSTempestConfUtilsTesting(unittest.TestCase): 'conf_utils.generate_test_accounts_file'), \ mock.patch('functest.opnfv_tests.openstack.tempest.' 'conf_utils.shutil.copyfile'): - conf_utils.configure_tempest_defcore('test_dep_dir', - img_flavor_dict) + conf_utils.configure_tempest_defcore( + 'test_dep_dir', 'test_image_id', 'test_flavor_id', + 'test_image_alt_id', 'test_flavor_alt_id', 'test_tenant_id') mset.assert_any_call('compute', 'image_ref', 'test_image_id') mset.assert_any_call('compute', 'image_ref_alt', 'test_image_alt_id') @@ -264,14 +216,10 @@ class OSTempestConfUtilsTesting(unittest.TestCase): self.assertTrue(mwrite.called) def test_generate_test_accounts_file_default(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'create_tenant_user', - return_value='test_tenant_id') as mock_create, \ - mock.patch("__builtin__.open", mock.mock_open()), \ + with mock.patch("__builtin__.open", mock.mock_open()), \ mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' 'yaml.dump') as mock_dump: - conf_utils.generate_test_accounts_file() - self.assertTrue(mock_create.called) + conf_utils.generate_test_accounts_file('test_tenant_id') self.assertTrue(mock_dump.called) def _test_missing_param(self, params, image_id, flavor_id): @@ -292,8 +240,8 @@ class OSTempestConfUtilsTesting(unittest.TestCase): CONST.__setattr__('OS_ENDPOINT_TYPE', None) conf_utils.\ configure_tempest_update_params('test_conf_file', - IMAGE_ID=image_id, - FLAVOR_ID=flavor_id) + image_id=image_id, + flavor_id=flavor_id) mset.assert_any_call(params[0], params[1], params[2]) self.assertTrue(mread.called) self.assertTrue(mwrite.called) diff --git a/functest/tests/unit/openstack/tempest/test_tempest.py b/functest/tests/unit/openstack/tempest/test_tempest.py index d5016f71..54d7d49b 100644 --- a/functest/tests/unit/openstack/tempest/test_tempest.py +++ b/functest/tests/unit/openstack/tempest/test_tempest.py @@ -15,10 +15,16 @@ from functest.opnfv_tests.openstack.tempest import tempest from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils.constants import CONST +from snaps.openstack.os_credentials import OSCreds + class OSTempestTesting(unittest.TestCase): def setUp(self): + os_creds = OSCreds( + username='user', password='pass', + auth_url='http://foo.com:5000/v3', project_name='bar') + with mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'conf_utils.get_verifier_id', return_value='test_deploy_id'), \ @@ -30,7 +36,9 @@ class OSTempestTesting(unittest.TestCase): return_value='test_verifier_repo_dir'), \ mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'conf_utils.get_verifier_deployment_dir', - return_value='test_verifier_deploy_dir'): + return_value='test_verifier_deploy_dir'), \ + mock.patch('snaps.openstack.tests.openstack_tests.get_credentials', + return_value=os_creds): self.tempestcommon = tempest.TempestCommon() self.tempestsmoke_serial = tempest.TempestSmokeSerial() self.tempestsmoke_parallel = tempest.TempestSmokeParallel() @@ -153,8 +161,8 @@ class OSTempestTesting(unittest.TestCase): 'os.path.exists', return_value=False) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.os.makedirs') @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.create_tempest_resources', side_effect=Exception) - def test_run_create_tempest_resources_ko(self, *args): + 'TempestResourcesManager.create', side_effect=Exception) + def test_run_tempest_create_resources_ko(self, *args): self.assertEqual(self.tempestcommon.run(), testcase.TestCase.EX_RUN_ERROR) @@ -162,7 +170,7 @@ class OSTempestTesting(unittest.TestCase): 'os.path.exists', return_value=False) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.os.makedirs') @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.create_tempest_resources', return_value={}) + 'TempestResourcesManager.create', return_value={}) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'conf_utils.configure_tempest', side_effect=Exception) def test_run_configure_tempest_ko(self, *args): @@ -173,7 +181,7 @@ class OSTempestTesting(unittest.TestCase): 'os.path.exists', return_value=False) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.os.makedirs') @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.create_tempest_resources', return_value={}) + 'TempestResourcesManager.create', return_value={}) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'conf_utils.configure_tempest') def _test_run(self, status, *args): diff --git a/functest/tests/unit/openstack/vping/test_vping.py b/functest/tests/unit/openstack/vping/test_vping.py index b229c351..a28c61ae 100644 --- a/functest/tests/unit/openstack/vping/test_vping.py +++ b/functest/tests/unit/openstack/vping/test_vping.py @@ -50,8 +50,6 @@ class VPingUserdataTesting(unittest.TestCase): 'vm_active', return_value=True) def test_vping_userdata(self, deploy_vm, path_exists, create_flavor, get_port_ip, vm_active): - os_vm_inst = mock.MagicMock(name='get_console_output') - os_vm_inst.get_console_output.return_value = 'vPing OK' with mock.patch('snaps.openstack.utils.deploy_utils.create_image', return_value=OpenStackImage(self.os_creds, None)), \ mock.patch('snaps.openstack.utils.deploy_utils.create_network', @@ -67,8 +65,8 @@ class VPingUserdataTesting(unittest.TestCase): name='foo', network_name='bar')]), None)), \ mock.patch('snaps.openstack.create_instance.' - 'OpenStackVmInstance.get_os_vm_server_obj', - return_value=os_vm_inst): + 'OpenStackVmInstance.get_console_output', + return_value='vPing OK'): self.assertEquals(TestCase.EX_OK, self.vping_userdata.run()) diff --git a/functest/tests/unit/utils/test_functest_utils.py b/functest/tests/unit/utils/test_functest_utils.py index b4cc5b73..17fb4c1f 100644 --- a/functest/tests/unit/utils/test_functest_utils.py +++ b/functest/tests/unit/utils/test_functest_utils.py @@ -97,42 +97,6 @@ class FunctestUtilsTesting(unittest.TestCase): m.assert_called_once_with(dest, 'wb') self.assertTrue(mock_sh.called) - @mock.patch('functest.utils.functest_utils.logger.error') - def test_get_installer_type_failed(self, mock_logger_error): - with mock.patch.dict(os.environ, - {}, - clear=True): - self.assertEqual(functest_utils.get_installer_type(), - "Unknown_installer") - mock_logger_error.assert_called_once_with("Impossible to retrieve" - " the installer type") - - def test_get_installer_type_default(self): - with mock.patch.dict(os.environ, - {'INSTALLER_TYPE': 'test_installer'}, - clear=True): - self.assertEqual(functest_utils.get_installer_type(), - self.installer) - - @mock.patch('functest.utils.functest_utils.logger.info') - def test_get_scenario_failed(self, mock_logger_info): - with mock.patch.dict(os.environ, - {}, - clear=True): - self.assertEqual(functest_utils.get_scenario(), - "os-nosdn-nofeature-noha") - mock_logger_info.assert_called_once_with("Impossible to retrieve " - "the scenario.Use " - "default " - "os-nosdn-nofeature-noha") - - def test_get_scenario_default(self): - with mock.patch.dict(os.environ, - {'DEPLOY_SCENARIO': 'test_scenario'}, - clear=True): - self.assertEqual(functest_utils.get_scenario(), - self.scenario) - def test_get_version_daily_job(self): CONST.__setattr__('BUILD_TAG', self.build_tag) self.assertEqual(functest_utils.get_version(), self.version) @@ -150,34 +114,13 @@ class FunctestUtilsTesting(unittest.TestCase): self.assertEqual(functest_utils.get_version(), "unknown") @mock.patch('functest.utils.functest_utils.logger.info') - def test_get_pod_name_failed(self, mock_logger_info): - with mock.patch.dict(os.environ, - {}, - clear=True): - self.assertEqual(functest_utils.get_pod_name(), - "unknown-pod") - mock_logger_info.assert_called_once_with("Unable to retrieve " - "the POD name from " - "environment. Using " - "pod name 'unknown-pod'") - - def test_get_pod_name_default(self): - with mock.patch.dict(os.environ, - {'NODE_NAME': 'test_node_name'}, - clear=True): - self.assertEqual(functest_utils.get_pod_name(), - self.node_name) - - @mock.patch('functest.utils.functest_utils.logger.info') def test_logger_test_results(self, mock_logger_info): CONST.__setattr__('results_test_db_url', self.db_url) CONST.__setattr__('BUILD_TAG', self.build_tag) - with mock.patch('functest.utils.functest_utils.get_pod_name', - return_value=self.node_name), \ - mock.patch('functest.utils.functest_utils.get_scenario', - return_value=self.scenario), \ - mock.patch('functest.utils.functest_utils.get_version', - return_value=self.version): + CONST.__setattr__('NODE_NAME', self.node_name) + CONST.__setattr__('DEPLOY_SCENARIO', self.scenario) + with mock.patch('functest.utils.functest_utils.get_version', + return_value=self.version): functest_utils.logger_test_results(self.project, self.case_name, self.status, self.details) mock_logger_info.assert_called_once_with( diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/__init__.py b/functest/tests/unit/vnf/router/__init__.py index e69de29b..e69de29b 100644 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/__init__.py +++ b/functest/tests/unit/vnf/router/__init__.py diff --git a/functest/tests/unit/vnf/router/test_cloudify_vrouter.py b/functest/tests/unit/vnf/router/test_cloudify_vrouter.py new file mode 100644 index 00000000..7f2091be --- /dev/null +++ b/functest/tests/unit/vnf/router/test_cloudify_vrouter.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +import logging +import unittest + +import mock + +from functest.core import vnf +from functest.opnfv_tests.vnf.router import cloudify_vrouter + +from snaps.openstack.os_credentials import OSCreds + + +class CloudifyVrouterTesting(unittest.TestCase): + + def setUp(self): + + self.tenant = 'cloudify_vrouter' + self.creds = {'username': 'user', + 'password': 'pwd'} + self.orchestrator = {'name': 'cloudify', + 'version': '4.0', + 'object': 'foo', + 'requirements': {'flavor': {'name': 'm1.medium', + 'ram_min': 4096}, + 'os_image': 'manager_4.0'}} + + self.vnf = {'name': 'vrouter', + 'descriptor': {'version': '100', + 'file_name': 'function-test-' + + 'openstack-blueprint.yaml', + 'name': 'vrouter-opnfv', + 'url': 'https://foo', + 'requirements': {'flavor': + {'name': 'm1.medium', + 'ram_min': 2048}}}} + + with mock.patch('functest.opnfv_tests.vnf.router.cloudify_vrouter.' + 'os.makedirs'), \ + mock.patch('functest.opnfv_tests.vnf.router.cloudify_vrouter.' + 'get_config', return_value={ + 'tenant_images': 'foo', + 'orchestrator': self.orchestrator, + 'vnf': self.vnf, + 'vnf_test_suite': '', + 'version': 'whatever'}): + + self.router_vnf = cloudify_vrouter.CloudifyVrouter() + + self.images = {'image1': 'url1', + 'image2': 'url2'} + self.details = {'orchestrator': {'status': 'PASS', 'duration': 120}, + 'vnf': {}, + 'test_vnf': {}} + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + return_value='test') + @mock.patch('functest.core.vnf.os_utils.get_or_create_tenant_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_or_create_user_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_credentials', + return_value={'auth_url': 'test/v1'}) + @mock.patch('snaps.openstack.create_image.OpenStackImage.create') + def test_prepare_default(self, *args): + self.assertIsNone(self.router_vnf.prepare()) + args[4].assert_called_once_with() + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + return_value='test') + @mock.patch('functest.core.vnf.os_utils.get_or_create_tenant_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_or_create_user_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_credentials', + return_value={'auth_url': 'test/no_v'}) + @mock.patch('snaps.openstack.create_image.OpenStackImage.create') + def test_prepare_bad_auth_url(self, *args): + with self.assertRaises(Exception): + self.router_vnf.image_creator( + OSCreds(username='user', password='pass', auth_url='url', + project_name='project', identity_api_version=3), + mock.Mock()) + args[0].assert_not_called() + + def test_prepare_missing_param(self): + with self.assertRaises(vnf.VnfPreparationException): + self.router_vnf.prepare() + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + side_effect=Exception) + def test_prepare_keystone_exception(self, *args): + with self.assertRaises(vnf.VnfPreparationException): + self.router_vnf.prepare() + args[0].assert_called_once_with() + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + return_value='test') + @mock.patch('functest.core.vnf.os_utils.get_or_create_tenant_for_vnf', + side_effect=Exception) + def test_prepare_tenant_exception(self, *args): + with self.assertRaises(vnf.VnfPreparationException): + self.router_vnf.prepare() + args[1].assert_called_once_with() + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + return_value='test') + @mock.patch('functest.core.vnf.os_utils.get_or_create_tenant_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_or_create_user_for_vnf', + side_effect=Exception) + def test_prepare_user_exception(self, *args): + with self.assertRaises(vnf.VnfPreparationException): + self.router_vnf.prepare() + args[2].assert_called_once_with() + + @mock.patch('functest.core.vnf.os_utils.get_keystone_client', + return_value='test') + @mock.patch('functest.core.vnf.os_utils.get_or_create_tenant_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_or_create_user_for_vnf', + return_value=True) + @mock.patch('functest.core.vnf.os_utils.get_credentials', + side_effect=Exception) + def test_prepare_credentials_exception(self, *args): + with self.assertRaises(vnf.VnfPreparationException): + self.router_vnf.prepare() + args[0].assert_called_once_with() + + +if __name__ == "__main__": + logging.disable(logging.CRITICAL) + unittest.main(verbosity=2) diff --git a/functest/tests/unit/vnf/router/test_vrouter_base.py b/functest/tests/unit/vnf/router/test_vrouter_base.py new file mode 100644 index 00000000..def201d1 --- /dev/null +++ b/functest/tests/unit/vnf/router/test_vrouter_base.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +import logging +import unittest + +import mock + +from functest.opnfv_tests.vnf.router import vrouter_base + + +class VrouterOnBoardingBaseTesting(unittest.TestCase): + + def setUp(self): + with mock.patch('functest.opnfv_tests.vnf.router.cloudify_vrouter.' + 'os.makedirs'): + self.vrouter_vnf = vrouter_base.VrouterOnBoardingBase() + + +if __name__ == "__main__": + logging.disable(logging.CRITICAL) + unittest.main(verbosity=2) diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index bf68e43a..e4062373 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -68,33 +68,6 @@ def download_url(url, dest_path): # CI UTILS # # ----------------------------------------------------------- -def get_installer_type(): - """ - Get installer type (fuel, apex, joid, compass) - """ - try: - installer = os.environ['INSTALLER_TYPE'] - except KeyError: - logger.error("Impossible to retrieve the installer type") - installer = "Unknown_installer" - - return installer - - -def get_scenario(): - """ - Get scenario - """ - try: - scenario = os.environ['DEPLOY_SCENARIO'] - except KeyError: - logger.info("Impossible to retrieve the scenario." - "Use default os-nosdn-nofeature-noha") - scenario = "os-nosdn-nofeature-noha" - - return scenario - - def get_version(): """ Get version @@ -117,25 +90,12 @@ def get_version(): return "unknown" -def get_pod_name(): - """ - Get PoD Name from env variable NODE_NAME - """ - try: - return os.environ['NODE_NAME'] - except KeyError: - logger.info( - "Unable to retrieve the POD name from environment. " + - "Using pod name 'unknown-pod'") - return "unknown-pod" - - def logger_test_results(project, case_name, status, details): """ Format test case results for the logger """ - pod_name = get_pod_name() - scenario = get_scenario() + pod_name = CONST.__getattribute__('NODE_NAME') + scenario = CONST.__getattribute__('DEPLOY_SCENARIO') version = get_version() build_tag = CONST.__getattribute__('BUILD_TAG') db_url = CONST.__getattribute__("results_test_db_url") @@ -267,14 +227,14 @@ def get_ci_envvars(): def execute_command_raise(cmd, info=False, error_msg="", - verbose=True, output_file=None): - ret = execute_command(cmd, info, error_msg, verbose, output_file) + verbose=True, output_file=None, env=None): + ret = execute_command(cmd, info, error_msg, verbose, output_file, env) if ret != 0: raise Exception(error_msg) def execute_command(cmd, info=False, error_msg="", - verbose=True, output_file=None): + verbose=True, output_file=None, env=None): if not error_msg: error_msg = ("The command '%s' failed." % cmd) msg_exec = ("Executing command: '%s'" % cmd) @@ -283,7 +243,7 @@ def execute_command(cmd, info=False, error_msg="", logger.info(msg_exec) else: logger.debug(msg_exec) - p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + p = subprocess.Popen(cmd, env=env, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if output_file: f = open(output_file, "w") diff --git a/functest/utils/openstack_utils.py b/functest/utils/openstack_utils.py index 8b59c954..73d1cde4 100644 --- a/functest/utils/openstack_utils.py +++ b/functest/utils/openstack_utils.py @@ -1561,3 +1561,62 @@ def get_resource(heat_client, stack_id, resource): except Exception as e: logger.error("Error [get_resource]: %s" % e) return None + + +# ********************************************* +# TEMPEST +# ********************************************* +def init_tempest_cleanup(tempest_config_dir=None, + tempest_config_filename='tempest.conf', + output_file=None): + """ + Initialize the Tempest Cleanup utility. + See https://docs.openstack.org/tempest/latest/cleanup.html for docs. + + :param tempest_config_dir: The directory where the Tempest config file is + located. If not specified, we let Tempest pick both the directory + and the filename (i.e. second parameter is ignored) + :param tempest_config_filename: The filename of the Tempest config file + :param output_file: Optional file where to save output + """ + # The Tempest cleanup utility currently offers no cmd argument to specify + # the config file, therefore it has to be configured with env variables + env = None + if tempest_config_dir: + env = os.environ.copy() + env['TEMPEST_CONFIG_DIR'] = tempest_config_dir + env['TEMPEST_CONFIG'] = tempest_config_filename + + # If this command fails, an exception must be raised to stop the script + # otherwise the later cleanup would destroy also other resources + cmd_line = "tempest cleanup --init-saved-state" + ft_utils.execute_command_raise(cmd_line, env=env, output_file=output_file, + error_msg="Tempest cleanup init failed") + + +def perform_tempest_cleanup(tempest_config_dir=None, + tempest_config_filename='tempest.conf', + output_file=None): + """ + Perform cleanup using the Tempest Cleanup utility. + See https://docs.openstack.org/tempest/latest/cleanup.html for docs. + + :param tempest_config_dir: The directory where the Tempest config file is + located. If not specified, we let Tempest pick both the directory + and the filename (i.e. second parameter is ignored) + :param tempest_config_filename: The filename of the Tempest config file + :param output_file: Optional file where to save output + """ + # The Tempest cleanup utility currently offers no cmd argument to specify + # the config file, therefore it has to be configured with env variables + env = None + if tempest_config_dir: + env = os.environ.copy() + env['TEMPEST_CONFIG_DIR'] = tempest_config_dir + env['TEMPEST_CONFIG'] = tempest_config_filename + + # If this command fails, an exception must be raised to stop the script + # otherwise the later cleanup would destroy also other resources + cmd_line = "tempest cleanup" + ft_utils.execute_command(cmd_line, env=env, output_file=output_file, + error_msg="Tempest cleanup failed") |