diff options
Diffstat (limited to 'functest')
100 files changed, 6252 insertions, 5697 deletions
diff --git a/functest/ci/add_proxy.sh b/functest/ci/add_proxy.sh new file mode 100644 index 000000000..9d7db22e4 --- /dev/null +++ b/functest/ci/add_proxy.sh @@ -0,0 +1,138 @@ +#!/bin/sh + +set -e + +initdir=$(pwd) +cd "${1:-/home/opnfv/functest/images}" + +http_proxy_host=${http_proxy_host:-proxy} +http_proxy_port=${http_proxy_port:-8080} + +http_proxy=http://${http_proxy_host}:${http_proxy_port} +https_proxy=${https_proxy:-${http_proxy:-http://proxy:8080}} +ftp_proxy=${ftp_proxy:-${http_proxy:-http://proxy:8080}} +no_proxy=${no_proxy:-"10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"} + +images=${images-"\ +ubuntu-14.04-server-cloudimg-amd64-disk1.img \ +ubuntu-16.04-server-cloudimg-amd64-disk1.img"} + +add_proxy () { + cat << EOF >> "$1" +http_proxy=${http_proxy} +HTTP_PROXY=${http_proxy} +https_proxy=${https_proxy} +HTTPS_PROXY=${https_proxy} +ftp_proxy=${ftp_proxy} +FTP_PROXY=${ftp_proxy} +no_proxy=${no_proxy} +NO_PROXY=${no_proxy} +EOF +} + +add_proxy_apt () { + cat << EOF >> "$1" +Acquire::http::Proxy "${http_proxy}"; +Acquire::https::Proxy "${https_proxy}"; +EOF +} + +add_proxy_juju_env () { + cat << EOF >> "$1" +export no_proxy="${no_proxy}"; +export NO_PROXY="${no_proxy}"; +EOF +} + +add_proxy_juju_systemd () { + cat << EOF >> "$1" +[Manager] +DefaultEnvironment="no_proxy='${no_proxy}'" "NO_PROXY='${no_proxy}'" +EOF +} + +add_proxy_maven () { + cat << EOF >> "$1" +<settings> + <proxies> + <proxy> + <id>example-proxy</id> + <active>true</active> + <protocol>http</protocol> + <host>"${http_proxy_host}"</host> + <port>"${http_proxy_port}"</port> + </proxy> + </proxies> +</settings> +EOF +} + +add_proxy_svn () { + cat << EOF >> "$1" +[global] +http-proxy-host = "${http_proxy_host}" +http-proxy-port = "${http_proxy_port}" +EOF +} + +add_proxy_pip () { + cat << EOF >> "$1" +[global] +proxy="${http_proxy}" +EOF +} + +tmpdir=$(mktemp -d) +for image in $images; do + if [ ! -f "$image" ]; then + echo "skip ${image} ($(pwd)/${image} not found)" + continue + fi + guestmount -a "${image}" -i --rw "${tmpdir}" + add_proxy "${tmpdir}/etc/environment" + if expr "$image" : 'ubuntu' ; then + add_proxy_apt "${tmpdir}/etc/apt/apt.conf" + add_proxy_juju_env "${tmpdir}/etc/juju-proxy.conf" + add_proxy_juju_systemd "${tmpdir}/etc/juju-proxy-systemd.conf" + mkdir -p ${tmpdir}/root/.m2 + mkdir -p ${tmpdir}/root/.subversion + add_proxy_maven "${tmpdir}/root/.m2/settings.xml" + add_proxy_svn "${tmpdir}/root/.subversion/servers" + add_proxy_pip "${tmpdir}/etc/pip.conf" + fi + guestunmount "${tmpdir}" +done + +if [ -f cloudify-docker-manager-community-19.01.24.tar ]; then + sudo docker load -i cloudify-docker-manager-community-19.01.24.tar + dockerfile=${tmpdir}/Dockerfile + cat << EOF > $dockerfile +FROM docker-cfy-manager:latest +ENV HTTP_PROXY "${http_proxy}" +ENV HTTPS_PROXY "${https_proxy}" +ENV NO_PROXY "${no_proxy}" +EOF + for f in /etc/sysconfig/cloudify-mgmtworker /etc/sysconfig/cloudify-restservice; do \ + cat << EOF >> $dockerfile +RUN echo >> $f +RUN echo "http_proxy=${http_proxy}" >> $f +RUN echo "https_proxy=${https_proxy}" >> $f +RUN echo "HTTP_PROXY=${http_proxy}" >> $f +RUN echo "HTTPS_PROXY=${https_proxy}" >> $f +RUN echo "no_proxy=${no_proxy}" >> $f +EOF + done + sudo docker build -t docker-cfy-manager -f $dockerfile ${tmpdir} + sudo docker save \ + docker-cfy-manager > cloudify-docker-manager-community-19.01.24.tar + sudo docker rmi docker-cfy-manager + + rm "${dockerfile}" +else + echo "skip cloudify-docker-manager-community-19.01.24.tar \ + ($(pwd)/cloudify-docker-manager-community-19.01.24.tar not found)" +fi + +rmdir "${tmpdir}" +cd initdir + diff --git a/functest/ci/check_deployment.py b/functest/ci/check_deployment.py deleted file mode 100644 index a475491a1..000000000 --- a/functest/ci/check_deployment.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Ericsson 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 - -""" -OpenStack deployment checker - -Verifies that: - - Credentials file is given and contains the right information - - OpenStack endpoints are reachable -""" - -import logging -import logging.config -import os -import socket - -import pkg_resources -from six.moves import urllib -from snaps.openstack.tests import openstack_tests -from snaps.openstack.utils import glance_utils -from snaps.openstack.utils import keystone_utils -from snaps.openstack.utils import neutron_utils -from snaps.openstack.utils import nova_utils - -from functest.utils import constants -from functest.opnfv_tests.openstack.snaps import snaps_utils - -__author__ = "Jose Lausuch <jose.lausuch@ericsson.com>" - -LOGGER = logging.getLogger(__name__) - - -def verify_connectivity(endpoint): - """ Returns true if an hostname/port is reachable""" - try: - connection = socket.socket() - connection.settimeout(10) - url = urllib.parse.urlparse(endpoint) - port = url.port - if not port: - port = 443 if url.scheme == "https" else 80 - connection.connect((url.hostname, port)) - LOGGER.debug('%s:%s is reachable!', url.hostname, port) - return True - except socket.error: - LOGGER.error('%s:%s is not reachable.', url.hostname, port) - except Exception: # pylint: disable=broad-except - LOGGER.exception( - 'Errors when verifying connectivity to %s:%s', url.hostname, port) - return False - - -def get_auth_token(os_creds): - """ Get auth token """ - sess = keystone_utils.keystone_session(os_creds) - try: - return sess.get_token() - except Exception as error: - LOGGER.error("Got token ...FAILED") - raise error - - -class CheckDeployment(object): - """ Check deployment class.""" - - def __init__(self, rc_file=constants.ENV_FILE): - self.rc_file = rc_file - self.services = ('compute', 'network', 'image') - self.os_creds = None - - def check_rc(self): - """ Check if RC file exists and contains OS_AUTH_URL """ - if not os.path.isfile(self.rc_file): - raise IOError('RC file {} does not exist!'.format(self.rc_file)) - if 'OS_AUTH_URL' not in open(self.rc_file).read(): - raise SyntaxError('OS_AUTH_URL not defined in {}.'. - format(self.rc_file)) - - def check_auth_endpoint(self): - """ Verifies connectivity to the OS_AUTH_URL given in the RC file - and get auth token""" - rc_endpoint = self.os_creds.auth_url - if not verify_connectivity(rc_endpoint): - raise Exception("OS_AUTH_URL {} is not reachable.". - format(rc_endpoint)) - LOGGER.info("Connectivity to OS_AUTH_URL %s ...OK", rc_endpoint) - if get_auth_token(self.os_creds): - LOGGER.info("Got token ...OK") - - def check_public_endpoint(self): - """ Gets the public endpoint and verifies connectivity to it """ - public_endpoint = keystone_utils.get_endpoint(self.os_creds, - 'identity', - interface='public') - if not verify_connectivity(public_endpoint): - raise Exception("Public endpoint {} is not reachable.". - format(public_endpoint)) - LOGGER.info("Connectivity to the public endpoint %s ...OK", - public_endpoint) - - def check_service_endpoint(self, service): - """ Verifies connectivity to a given openstack service """ - endpoint = keystone_utils.get_endpoint(self.os_creds, - service, - interface='public') - if not verify_connectivity(endpoint): - raise Exception("{} endpoint {} is not reachable.". - format(service, endpoint)) - LOGGER.info("Connectivity to endpoint '%s' %s ...OK", - service, endpoint) - - def check_nova(self): - """ checks that a simple nova operation works """ - try: - client = nova_utils.nova_client(self.os_creds) - client.servers.list() - LOGGER.info("Nova service ...OK") - except Exception as error: - LOGGER.error("Nova service ...FAILED") - raise error - - def check_neutron(self): - """ checks that a simple neutron operation works """ - try: - client = neutron_utils.neutron_client(self.os_creds) - client.list_networks() - LOGGER.info("Neutron service ...OK") - except Exception as error: - LOGGER.error("Neutron service ...FAILED") - raise error - - def check_glance(self): - """ checks that a simple glance operation works """ - try: - client = glance_utils.glance_client(self.os_creds) - client.images.list() - LOGGER.info("Glance service ...OK") - except Exception as error: - LOGGER.error("Glance service ...FAILED") - raise error - - def check_ext_net(self): - """ checks if external network exists """ - ext_net = snaps_utils.get_ext_net_name(self.os_creds) - if ext_net: - LOGGER.info("External network found: %s", ext_net) - else: - raise Exception("ERROR: No external networks in the deployment.") - - def check_all(self): - """ - Calls all the class functions and returns 0 if all of them succeed. - This is the method called by CLI - """ - self.check_rc() - try: - self.os_creds = openstack_tests.get_credentials( - os_env_file=self.rc_file, - proxy_settings_str=None, - ssh_proxy_cmd=None) - except: - raise Exception("Problem while getting credentials object.") - if self.os_creds is None: - raise Exception("Credentials is None.") - self.check_auth_endpoint() - self.check_public_endpoint() - for service in self.services: - self.check_service_endpoint(service) - self.check_nova() - self.check_neutron() - self.check_glance() - self.check_ext_net() - return 0 - - -def main(): - """Entry point""" - logging.config.fileConfig(pkg_resources.resource_filename( - 'functest', 'ci/logging.ini')) - logging.captureWarnings(True) - deployment = CheckDeployment() - return deployment.check_all() diff --git a/functest/ci/config_aarch64_patch.yaml b/functest/ci/config_aarch64_patch.yaml index 00020af6a..278265620 100644 --- a/functest/ci/config_aarch64_patch.yaml +++ b/functest/ci/config_aarch64_patch.yaml @@ -1,204 +1,99 @@ --- os: - general: - openstack: - image_name: TestVM - image_file_name: cirros-0.4.0-aarch64-disk.img - image_password: gocubsgo - image_url: - /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - short_id: 'ubuntu16.04' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - vmready1: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img vmready2: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img singlevm1: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img singlevm2: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img vping_ssh: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img vping_userdata: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img cinder_test: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - functest_smoke_serial: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - refstack_defcore: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - patrole: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - vmtp: - image: - /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-arm64-uefi1.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - shaker: - image: /home/opnfv/functest/images/shaker-image-arm64.qcow2 - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - neutron_trunk: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - barbican: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - tempest_full_parallel: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_smoke: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_horizon: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_neutron: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_cinder: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_keystone: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_heat: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: + /home/opnfv/functest/images/Fedora-Cloud-Base-30-1.2.aarch64.qcow2 + tempest_telemetry: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img rally_sanity: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + refstack_compute: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + refstack_object: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + refstack_platform: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_full: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_scenario: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_slow: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + patrole: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_barbican: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_octavia: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_neutron_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_cinder_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_keystone_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_heat_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: + /home/opnfv/functest/images/Fedora-Cloud-Base-30-1.2.aarch64.qcow2 + rally_sanity_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_full_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_scenario_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + tempest_slow_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + image_alt: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img rally_full: - image: /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - - snaps: - images: - glance_tests: - disk_file: - /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - short_id: 'ubuntu16.04' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - cirros: - disk_file: - /home/opnfv/functest/images/cirros-0.4.0-aarch64-disk.img - extra_properties: - hw_firmware_type: 'uefi' - short_id: 'ubuntu16.04' - hw_video_model: 'vga' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - 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' - hw_disk_bus: 'scsi' - hw_scsi_model: 'virtio-scsi' - centos: - disk_file: - /home/opnfv/functest/images/CentOS-7-aarch64-GenericCloud.qcow2 - extra_properties: - hw_firmware_type: 'uefi' - hw_video_model: 'vga' - - vping: - image_name: TestVM - - tempest: - use_custom_flavors: 'True' - - odl_sfc: - image_base_url: "http://artifacts.opnfv.org/sfc/demo" - image_name: sfc_nsh_danube - image_file_name: sf_nsh_danube_arm64.img - image_initrd: sf_nsh_danube_arm64-initrd - image_kernel: sf_nsh_danube_arm64-kernel - image_format: ami - os_cmd_line: 'root=LABEL=cloudimg-rootfs ro' - doctor: - image_name: TestVM + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + rally_jobs: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + rally_full_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img + rally_jobs_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-aarch64-disk.img diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index 5aa02be68..647301ab4 100644 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -11,16 +11,17 @@ general: functest_conf: /home/opnfv/functest/conf functest_data: /home/opnfv/functest/data ims_data: /home/opnfv/functest/data/ims/ + rally_data: /home/opnfv/functest/data/rally refstack_data: /home/opnfv/functest/data/refstack - router_data: /home/opnfv/functest/data/router/ + router_data: /home/opnfv/functest/data/router/opnfv-vnf-data functest_images: /home/opnfv/functest/images rally_inst: /root/.rally openstack: - image_name: Cirros-0.4.0 - image_name_alt: Cirros-0.4.0-1 - image_file_name: cirros-0.4.0-x86_64-disk.img - image_url: /home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img + image_name: Cirros-0.5.1 + image_name_alt: Cirros-0.5.1-1 + image_file_name: cirros-0.5.1-x86_64-disk.img + image_url: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.img image_user: cirros image_disk_format: qcow2 image_username: cirros @@ -41,47 +42,6 @@ general: neutron_private_subnet_gateway: 192.168.120.254 neutron_router_name: functest-router -snaps: - use_keystone: 'True' - use_floating_ips: 'True' - flavor_extra_specs: {} - images: - glance_tests: - disk_file: /home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img - cirros: - disk_file: /home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img - ubuntu: - 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 -# netconf_override: -# network_type: vlan -# physical_network: physnet2 -# segmentation_id: 2366 - -# 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 vm_flavor: m1.tiny # adapt to your environment @@ -131,26 +91,9 @@ odl_sfc: tempest: verifier_name: opnfv-tempest - identity: - tenant_name: tempest - tenant_description: Tenant for Tempest test suite - user_name: tempest - user_password: Tempest123! - validation: - ssh_timeout: 130 - object_storage: - operator_role: SwiftOperator - # network_type: vlan - # physical_network: physnet2 - # segmentation_id: 2366 - private_net_name: tempest-net - private_subnet_name: tempest-subnet - private_subnet_cidr: 192.168.150.0/24 - router_name: tempest-router rally: deployment_name: opnfv-rally - network_name: rally-net vnf: juju_epc: @@ -165,10 +108,8 @@ vnf: tenant_name: cloudify_ims tenant_description: vIMS config: cloudify_ims.yaml - cloudify_ims_perf: - tenant_name: cloudify_ims_perf - tenant_description: vIMS - config: cloudify_ims_perf.yaml + heat_ims: + config: heat_ims.yaml orchestra_openims: tenant_name: orchestra_openims tenant_description: OpenIMS deployed with Open Baton diff --git a/functest/ci/config_patch.yaml b/functest/ci/config_patch.yaml index c2bfe33e7..d5335c3ab 100644 --- a/functest/ci/config_patch.yaml +++ b/functest/ci/config_patch.yaml @@ -1,161 +1,504 @@ --- -lxd: - general: - openstack: - image_name: Cirros-0.4.0 - image_file_name: cirros-0.4.0-x86_64-lxc.tar.gz - image_disk_format: raw +gsma: + tempest_smoke: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_horizon: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_neutron: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_cinder: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_keystone: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_heat: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_telemetry: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + rally_sanity: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + refstack_compute: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + refstack_object: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + refstack_platform: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_full: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_scenario: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_slow: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + patrole: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_barbican: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_octavia: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_neutron_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_cinder_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_keystone_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_heat_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + rally_sanity_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_full_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_scenario_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + tempest_slow_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + rally_full: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + rally_jobs: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + vmtp: + flavor_ram: 2048 + flavor_vcpus: 1 + flavor_disk: 40 + shaker: + flavor_ram: 2048 + flavor_vcpus: 1 + flavor_disk: 40 + rally_full_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + rally_jobs_cntt: + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + cloudify: + flavor_ram: 4096 + flavor_vcpus: 2 + flavor_disk: 40 + cloudify_ims: + flavor_ram: 4096 + flavor_vcpus: 2 + flavor_disk: 40 + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + heat_ims: + flavor_ram: 2048 + flavor_vcpus: 1 + flavor_disk: 40 + vyos_vrouter: + flavor_ram: 4096 + flavor_vcpus: 2 + flavor_disk: 40 + flavor_alt_ram: 2048 + flavor_alt_vcpus: 1 + flavor_alt_disk: 40 + juju_epc: + flavor_ram: 2048 + flavor_vcpus: 1 + flavor_disk: 40 + flavor_alt_ram: 4096 + flavor_alt_vcpus: 2 + flavor_alt_disk: 40 fdio: - general: - flavor_extra_specs: {'hw:mem_page_size':'large'} - image_properties: {'hw_mem_page_size':'large'} - openstack: - flavor_ram: 1024 - snaps: - flavor_extra_specs: {'hw:mem_page_size':'large'} vmready1: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vmready2: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 singlevm1: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 singlevm2: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vping_ssh: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vping_userdata: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 cinder_test: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 - tempest_smoke_serial: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_smoke: flavor_ram: 1024 - refstack_defcore: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_horizon: flavor_ram: 1024 - patrole: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_neutron: flavor_ram: 1024 - vmtp: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} - flavor_ram: 2048 - shaker: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_cinder: flavor_ram: 1024 - neutron_trunk: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_keystone: flavor_ram: 1024 - barbican: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_heat: flavor_ram: 1024 - tempest_full_parallel: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_telemetry: flavor_ram: 1024 rally_sanity: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + flavor_ram: 1024 + refstack_compute: + flavor_ram: 1024 + refstack_object: + flavor_ram: 1024 + refstack_platform: + flavor_ram: 1024 + tempest_full: + flavor_ram: 1024 + tempest_scenario: + flavor_ram: 1024 + tempest_slow: + flavor_ram: 1024 + patrole: + flavor_ram: 1024 + tempest_barbican: + flavor_ram: 1024 + tempest_octavia: + flavor_ram: 1024 + tempest_neutron_cntt: + flavor_ram: 1024 + tempest_cinder_cntt: + flavor_ram: 1024 + tempest_keystone_cntt: + flavor_ram: 1024 + tempest_heat_cntt: + flavor_ram: 1024 + rally_sanity_cntt: + flavor_ram: 1024 + tempest_full_cntt: + flavor_ram: 1024 + tempest_scenario_cntt: + flavor_ram: 1024 + tempest_slow_cntt: flavor_ram: 1024 rally_full: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + flavor_ram: 1024 + rally_jobs: + flavor_ram: 1024 + vmtp: + flavor_ram: 2048 + shaker: + flavor_ram: 1024 + rally_full_cntt: + flavor_ram: 1024 + rally_jobs_cntt: flavor_ram: 1024 ovs: - general: - flavor_extra_specs: {'hw:mem_page_size':'large'} - image_properties: {'hw_mem_page_size':'large'} - openstack: - flavor_ram: 1024 - snaps: - flavor_extra_specs: {'hw:mem_page_size':'large'} vmready1: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vmready2: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 singlevm1: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 singlevm2: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vping_ssh: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 vping_userdata: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 cinder_test: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 - tempest_smoke_serial: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_smoke: + flavor_ram: 1024 + tempest_horizon: flavor_ram: 1024 - refstack_defcore: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + tempest_neutron: + flavor_ram: 1024 + tempest_cinder: + flavor_ram: 1024 + tempest_keystone: + flavor_ram: 1024 + tempest_heat: + flavor_ram: 1024 + tempest_telemetry: + flavor_ram: 1024 + rally_sanity: + flavor_ram: 1024 + refstack_compute: + flavor_ram: 1024 + refstack_object: + flavor_ram: 1024 + refstack_platform: + flavor_ram: 1024 + tempest_full: + flavor_ram: 1024 + tempest_scenario: + flavor_ram: 1024 + tempest_slow: flavor_ram: 1024 patrole: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + flavor_ram: 1024 + tempest_barbican: + flavor_ram: 1024 + tempest_octavia: + flavor_ram: 1024 + tempest_neutron_cntt: + flavor_ram: 1024 + tempest_cinder_cntt: + flavor_ram: 1024 + tempest_keystone_cntt: + flavor_ram: 1024 + tempest_heat_cntt: + flavor_ram: 1024 + rally_sanity_cntt: + flavor_ram: 1024 + tempest_full_cntt: + flavor_ram: 1024 + tempest_scenario_cntt: + flavor_ram: 1024 + tempest_slow_cntt: + flavor_ram: 1024 + rally_full: + flavor_ram: 1024 + rally_jobs: flavor_ram: 1024 vmtp: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 2048 shaker: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} flavor_ram: 1024 - neutron_trunk: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + rally_full_cntt: flavor_ram: 1024 - barbican: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} - flavor_ram: 1024 - tempest_full_parallel: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} + rally_jobs_cntt: flavor_ram: 1024 + +vio: + vmready1: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + vmready2: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + singlevm1: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + singlevm2: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + vping_ssh: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + vping_userdata: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + cinder_test: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + tempest_smoke: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_horizon: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_neutron: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_cinder: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_keystone: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_heat: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: + /home/opnfv/functest/images/Fedora-Cloud-Base-30-1.2.x86_64.vmdk + image_alt_format: vmdk + tempest_telemetry: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk rally_sanity: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} - flavor_ram: 1024 + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + refstack_compute: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + refstack_object: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + refstack_platform: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_full: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_scenario: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_slow: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + patrole: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_barbican: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_octavia: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_neutron_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_cinder_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_keystone_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_heat_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: + /home/opnfv/functest/images/Fedora-Cloud-Base-30-1.2.x86_64.vmdk + image_alt_format: vmdk + rally_sanity_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + tempest_full_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_scenario_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk + tempest_slow_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_alt_format: vmdk rally_full: - flavor_extra_specs: {'hw:mem_page_size':'large'} - extra_properties: {'hw_mem_page_size':'large'} - flavor_ram: 1024 + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + rally_jobs: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + vmtp: + image: + /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + shaker: + image: /home/opnfv/functest/images/shaker-image.vmdk + image_format: vmdk + rally_full_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + rally_jobs_cntt: + image: /home/opnfv/functest/images/cirros-0.5.1-x86_64-disk.vmdk + image_format: vmdk + cloudify: + image: + /home/opnfv/functest/images/ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + cloudify_ims: + image: + /home/opnfv/functest/images/ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + image_alt: + /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.vmdk + image_alt_format: vmdk + heat_ims: + image: + /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + vyos_vrouter: + image: + /home/opnfv/functest/images/ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + image_alt: /home/opnfv/functest/images/vyos-1.1.7.vmdk + image_alt_format: vmdk + juju_epc: + image: + /home/opnfv/functest/images/ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk + image_format: vmdk + image_alt: + /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.vmdk + image_alt_format: vmdk diff --git a/functest/ci/convert_images.sh b/functest/ci/convert_images.sh new file mode 100644 index 000000000..2159d2a60 --- /dev/null +++ b/functest/ci/convert_images.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -ex + +initdir=$(pwd) + +cd "${1:-/home/opnfv/functest/images}" + +for i in *.img *.qcow2; do + qemu-img convert -f qcow2 -O vmdk "$i" "${i%.*}.vmdk" +done + +cd $initdir diff --git a/functest/ci/download_images.sh b/functest/ci/download_images.sh index a23d8f707..a56c02b60 100644 --- a/functest/ci/download_images.sh +++ b/functest/ci/download_images.sh @@ -1,23 +1,19 @@ -#!/bin/bash +#!/bin/sh set -ex wget_opts="-N --tries=1 --connect-timeout=30" +[ -t 1 ] || wget_opts="${wget_opts} --progress=dot:giga" cat << EOF | wget ${wget_opts} -i - -P ${1:-/home/opnfv/functest/images} -http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img +http://download.cirros-cloud.net/0.6.1/cirros-0.6.1-x86_64-disk.img https://cloud-images.ubuntu.com/releases/14.04/release/ubuntu-14.04-server-cloudimg-amd64-disk1.img -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://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-lxc.tar.gz -http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-aarch64-disk.img -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 -http://testresults.opnfv.org/functest/shaker-image.qcow2 -http://testresults.opnfv.org/functest/shaker-image-arm64.qcow2 +https://cloud-images.ubuntu.com/releases/18.04/release/ubuntu-18.04-server-cloudimg-amd64.img +http://download.cirros-cloud.net/0.6.1/cirros-0.6.1-aarch64-disk.img +http://repository.cloudifysource.org/cloudify/19.01.24/community-release/cloudify-docker-manager-community-19.01.24.tar +http://testresults.opnfv.org/functest/vyos-1.1.8-amd64.qcow2 +http://testresults.opnfv.org/functest/shaker-image-1.3.4+stretch.qcow2 +https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/30/Cloud/x86_64/images/Fedora-Cloud-Base-30-1.2.x86_64.qcow2 +https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/30/Cloud/aarch64/images/Fedora-Cloud-Base-30-1.2.aarch64.qcow2 EOF - -xz --decompress --force --keep \ - ${1:-/home/opnfv/functest/images}/CentOS-7-aarch64-GenericCloud.qcow2.xz diff --git a/functest/ci/logging.debug.ini b/functest/ci/logging.debug.ini new file mode 100644 index 000000000..c926a5055 --- /dev/null +++ b/functest/ci/logging.debug.ini @@ -0,0 +1,110 @@ +[loggers] +keys=root,functest,api,ci,core,cli,opnfv_tests,utils,xtesting,xci,xcore,xutils,sfc,baro,warnings + +[handlers] +keys=console,wconsole,file,dfile + +[formatters] +keys=standard + +[logger_root] +level=NOTSET +handlers=dfile + +[logger_functest] +level=NOTSET +handlers=file +qualname=functest + +[logger_api] +level=NOTSET +handlers=wconsole +qualname=functest.api + +[logger_ci] +level=NOTSET +handlers=console +qualname=functest.ci + +[logger_core] +level=NOTSET +handlers=console +qualname=functest.core + +[logger_cli] +level=NOTSET +handlers=wconsole +qualname=functest.cli + +[logger_opnfv_tests] +level=NOTSET +handlers=wconsole +qualname=functest.opnfv_tests + +[logger_utils] +level=NOTSET +handlers=wconsole +qualname=functest.utils + +[logger_xtesting] +level=NOTSET +handlers=file +qualname=xtesting + +[logger_xci] +level=NOTSET +handlers=console +qualname=xtesting.ci + +[logger_xcore] +level=NOTSET +handlers=console +qualname=xtesting.core + +[logger_xutils] +level=NOTSET +handlers=wconsole +qualname=xtesting.utils + +[logger_sfc] +level=NOTSET +handlers=file,wconsole +qualname=sfc + +[logger_baro] +level=NOTSET +handlers=file,wconsole +qualname=baro_tests + +[logger_warnings] +level=NOTSET +handlers=file,console +qualname=py.warnings + +[handler_console] +class=StreamHandler +level=INFO +formatter=standard +args=(sys.stdout,) + +[handler_wconsole] +class=StreamHandler +level=WARN +formatter=standard +args=(sys.stdout,) + +[handler_file] +class=FileHandler +level=INFO +formatter=standard +args=("/home/opnfv/functest/results/functest.log",) + +[handler_dfile] +class=FileHandler +level=DEBUG +formatter=standard +args=("/home/opnfv/functest/results/functest.debug.log",) + +[formatter_standard] +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s +datefmt= diff --git a/functest/ci/logging.ini b/functest/ci/logging.ini index f14014354..dde079493 100644 --- a/functest/ci/logging.ini +++ b/functest/ci/logging.ini @@ -1,15 +1,15 @@ [loggers] -keys=root,functest,api,ci,core,cli,opnfv_tests,utils,xtesting,xci,xcore,energy,xutils,sfc,sdnvpn,baro,warnings +keys=root,functest,api,ci,core,cli,opnfv_tests,utils,xtesting,xci,xcore,xutils,sfc,baro,warnings [handlers] -keys=console,wconsole,file,dfile +keys=console,wconsole,file,null [formatters] keys=standard [logger_root] level=NOTSET -handlers=dfile +handlers=null [logger_functest] level=NOTSET @@ -61,11 +61,6 @@ level=NOTSET handlers=console qualname=xtesting.core -[logger_energy] -level=NOTSET -handlers=wconsole -qualname=xtesting.energy - [logger_xutils] level=NOTSET handlers=wconsole @@ -76,11 +71,6 @@ level=NOTSET handlers=file,wconsole qualname=sfc -[logger_sdnvpn] -level=NOTSET -handlers=file,wconsole -qualname=sdnvpn - [logger_baro] level=NOTSET handlers=file,wconsole @@ -91,6 +81,12 @@ level=NOTSET handlers=file,console qualname=py.warnings +[handler_null] +class=NullHandler +level=NOTSET +formatter=standard +args=() + [handler_console] class=StreamHandler level=INFO @@ -105,16 +101,10 @@ args=(sys.stdout,) [handler_file] class=FileHandler -level=DEBUG +level=INFO formatter=standard args=("/home/opnfv/functest/results/functest.log",) -[handler_dfile] -class=FileHandler -level=DEBUG -formatter=standard -args=("/home/opnfv/functest/results/functest.debug.log",) - [formatter_standard] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt= diff --git a/functest/ci/rally_aarch64_patch.conf b/functest/ci/rally_aarch64_patch.conf index baeceac76..4ea0d7605 100644 --- a/functest/ci/rally_aarch64_patch.conf +++ b/functest/ci/rally_aarch64_patch.conf @@ -1,5 +1,5 @@ img_name_regex = ^TestVM$ -img_url = http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-aarch64-disk.img +img_url = http://download.cirros-cloud.net/0.5.1/cirros-0.5.1-aarch64-disk.img flavor_ref_ram = 256 flavor_ref_alt_ram = 256 heat_instance_type_ram = 256 diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index a9e023e4c..acf5a7199 100644 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -2,7 +2,6 @@ tiers: - name: healthcheck - order: 0 description: >- First tier to be executed to verify the basic operations in the VIM. @@ -17,9 +16,7 @@ tiers: Keystone, Glance, Neutron and Nova and may perform some simple queries. run: - module: - 'functest.opnfv_tests.openstack.api.connection_check' - class: 'ConnectionCheck' + name: connection_check - case_name: tenantnetwork1 @@ -30,9 +27,10 @@ tiers: It creates and configures all tenant network ressources required by advanced testcases (subnet, network and router). + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: 'functest.core.tenantnetwork' - class: 'TenantNetwork1' + name: tenantnetwork1 - case_name: tenantnetwork2 @@ -43,9 +41,10 @@ tiers: It creates new user/project before creating and configuring all tenant network ressources required by a testcase (subnet, network and router). + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: 'functest.core.tenantnetwork' - class: 'TenantNetwork2' + name: tenantnetwork2 - case_name: vmready1 @@ -57,8 +56,7 @@ tiers: resources and prepares a future VM attached to that network. run: - module: 'functest.core.singlevm' - class: 'VmReady1' + name: vmready1 - case_name: vmready2 @@ -70,8 +68,7 @@ tiers: all tenant network ressources, flavors, images, etc. required by advanced testcases. run: - module: 'functest.core.singlevm' - class: 'VmReady2' + name: vmready2 - case_name: singlevm1 @@ -83,8 +80,7 @@ tiers: resources and completes it by booting a VM attached to that network. run: - module: 'functest.core.singlevm' - class: 'SingleVm1' + name: singlevm1 - case_name: singlevm2 @@ -96,8 +92,7 @@ tiers: all tenant network ressources and vms required by advanced testcases. run: - module: 'functest.core.singlevm' - class: 'SingleVm2' + name: singlevm2 - case_name: vping_ssh @@ -109,8 +104,7 @@ tiers: floating IPs over the public network. 2) Connectivity between 2 instances over a private network. run: - module: 'functest.opnfv_tests.openstack.vping.vping_ssh' - class: 'VPingSSH' + name: vping_ssh - case_name: vping_userdata @@ -121,9 +115,7 @@ tiers: This test case verifies: 1) Boot a VM with given userdata. 2) Connectivity between 2 instances over a private network. run: - module: - 'functest.opnfv_tests.openstack.vping.vping_userdata' - class: 'VPingUserdata' + name: vping_userdata - case_name: cinder_test @@ -136,8 +128,7 @@ tiers: from instance 1, attach it on instance 2 3) Read volume data run: - module: 'functest.opnfv_tests.openstack.cinder.cinder_test' - class: 'CinderCheck' + name: cinder_test - case_name: odl @@ -151,68 +142,134 @@ tiers: dependencies: - DEPLOY_SCENARIO: 'odl' run: - module: 'functest.opnfv_tests.sdn.odl.odl' - class: 'ODLTests' + name: odl args: suites: - /src/odl_test/csit/suites/integration/basic - /src/odl_test/csit/suites/openstack/neutron - - case_name: api_check + case_name: tempest_smoke project_name: functest criteria: 100 - blocking: true + blocking: false description: >- - This test case verifies the retrieval of OpenStack clients: - Keystone, Glance, Neutron and Nova and may perform some - simple queries. When the config value of - snaps.use_keystone is True, functest must have access to - the cloud's private network. - dependencies: - - DEPLOY_SCENARIO: '^((?!lxd).)*$' + This test case runs the smoke subset of the OpenStack + Tempest suite. The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deplopyment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L114 run: - module: 'functest.opnfv_tests.openstack.snaps.api_check' - class: 'ApiCheck' + name: tempest_common + args: + mode: '(?=.*\[.*\bsmoke\b.*\])(^tempest\.api)' + option: + - '--concurrency=4' - - case_name: snaps_health_check + case_name: tempest_horizon project_name: functest criteria: 100 - blocking: true + blocking: false description: >- - This test case creates executes the SimpleHealthCheck - Python test class which creates an, image, flavor, network, - and Cirros VM instance and observes the console output to - validate the single port obtains the correct IP address. + This test case runs the Tempest suite proposed by the + Horizon project. dependencies: - - DEPLOY_SCENARIO: '^((?!lxd).)*$' + - DASHBOARD_URL: '^(?!\s*$).+' run: - module: 'functest.opnfv_tests.openstack.snaps.health_check' - class: 'HealthCheck' + name: tempest_common + args: + mode: '^tempest.scenario.test_dashboard_basic_ops.' - name: smoke - order: 1 description: >- Set of basic Functional tests to validate the OPNFV scenarios. testcases: - - case_name: tempest_smoke + case_name: tempest_neutron project_name: functest criteria: 100 blocking: false description: >- - This test case runs the smoke subset of the OpenStack - Tempest suite. The list of test cases is generated by + This test case runs the Tempest suite proposed by the + Neutron project. The list of test cases is generated by Tempest automatically and depends on the parameters of - the OpenStack deplopyment. + the OpenStack deployment. run: - module: 'functest.opnfv_tests.openstack.tempest.tempest' - class: 'TempestCommon' + name: tempest_common args: - mode: - '^(tempest|neutron_tempest_plugin)\.(api|scenario).*\[.*\bsmoke\b.*\]$' + mode: '^neutron_tempest_plugin\.api' + option: + - '--concurrency=4' + + - + case_name: tempest_cinder + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs the Tempest suite proposed by the + Cinder project. + run: + name: tempest_common + args: + mode: "(?!.*test_incremental_backup)\ + (?!.*test_backup_crossproject_admin_negative)\ + (?!.*test_backup_crossproject_user_negative)\ + (^cinder_tempest_plugin.)" + option: + - '--concurrency=4' + + - + case_name: tempest_keystone + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs the Tempest suite proposed by the + Keystone project. + run: + name: tempest_common + args: + mode: 'keystone_tempest_plugin.' + option: + - '--concurrency=4' + + - + case_name: tempest_heat + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs the Tempest suite proposed by the + Heat project. + run: + name: tempest_heat + args: + mode: '^heat_tempest_plugin.tests' + option: + - '--concurrency=1' + + - + case_name: tempest_telemetry + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs the Tempest suite proposed by the + Telemetry project. + run: + name: tempest_common + args: + mode: "(?!.*test_autoscaling)(?!.*test_live)\ + (^telemetry_tempest_plugin)" + services: + - aodh + - gnocchi + - panko + option: + - '--concurrency=4' - case_name: rally_sanity @@ -223,110 +280,252 @@ tiers: This test case runs a sub group of tests of the OpenStack Rally suite in smoke mode. run: - module: 'functest.opnfv_tests.openstack.rally.rally' - class: 'RallySanity' + name: rally_sanity + args: + optional: + - 'gnocchi' + - 'barbican' - - case_name: refstack_defcore + case_name: refstack_compute project_name: functest - enabled: false criteria: 100 blocking: false description: >- This test case runs a sub group of tests of the OpenStack - Defcore testcases. + Compute testcases. + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: - 'functest.opnfv_tests.openstack.refstack.refstack' - class: 'Refstack' + name: refstack + args: + target: compute + option: + - '--concurrency=4' - - case_name: patrole + case_name: refstack_object project_name: functest criteria: 100 blocking: false description: >- - Test suite from Patrole project. + This test case runs a sub group of tests of the OpenStack + Object testcases. run: - module: 'functest.opnfv_tests.openstack.patrole.patrole' - class: 'Patrole' + name: refstack args: - role: 'admin' - services: - - 'image' - - 'network' - exclude: - - 'test_networks_multiprovider_rbac' + target: object + option: + - '--concurrency=4' - - case_name: vmtp + case_name: refstack_platform project_name: functest criteria: 100 blocking: false description: >- - VMTP is a small python application that will automatically - perform ping connectivity, round trip time measurement - (latency) and TCP/UDP throughput + This test case runs a sub group of tests of the OpenStack + Platform testcases. + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: - 'functest.opnfv_tests.openstack.vmtp.vmtp' - class: 'Vmtp' + name: refstack + args: + target: platform + option: + - '--concurrency=4' - - case_name: shaker + case_name: tempest_full project_name: functest criteria: 100 blocking: false description: >- - Shaker wraps around popular system network testing tools - like iperf, iperf3 and netperf (with help of flent). Shaker - is able to deploy OpenStack instances and networks in - different topologies. + The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L83 run: - module: - 'functest.opnfv_tests.openstack.shaker.shaker' - class: 'Shaker' + name: tempest_common + args: + mode: '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' + option: + - '--concurrency=4' - - case_name: snaps_smoke + case_name: tempest_scenario project_name: functest criteria: 100 blocking: false description: >- - This test case contains tests that setup and destroy - environments with VMs with and without Floating IPs - with a newly created user and project. Set the config - value snaps.use_floating_ips (True|False) to toggle - this functionality. When the config value of - snaps.use_keystone is True, functest must have access to - the cloud's private network. - dependencies: - - DEPLOY_SCENARIO: '^((?!lxd).)*$' + The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L84 run: - module: 'functest.opnfv_tests.openstack.snaps.smoke' - class: 'SnapsSmoke' + name: tempest_common + args: + mode: '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' + option: + - '--concurrency=1' - - case_name: neutron_trunk + case_name: tempest_slow project_name: functest criteria: 100 blocking: false description: >- - This test case runs the neutron trunk subtest of the - OpenStack Tempest suite. The list of test cases is - generated by Tempest having as input the relevant - testcase list file. + The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L84 run: - module: 'functest.opnfv_tests.openstack.tempest.tempest' - class: 'TempestCommon' + name: tempest_common args: - mode: 'neutron_tempest_plugin.(api|scenario).test_trunk' - neutron_extensions: - - trunk - - trunk_details + mode: '(?=.*\[.*\bslow\b.*\])(^tempest\.)' + option: + - '--concurrency=1' - - case_name: barbican + case_name: patrole_admin + project_name: functest + criteria: 100 + blocking: false + description: >- + Test suite from Patrole project. + run: + name: patrole + args: + roles: 'admin' + mode: "(?!.*test_unmanage_snapshot_rbac)\ + (?!.*test_show_auto_allocated_topology)\ + (?!.*test_delete_auto_allocated_topology)\ + (?!.*test_create_network_provider_segmentation_id)\ + (?!.*compute.test_floating_ips_rbac)\ + (?!.*test_reset_network)\ + (?!.*test_create_image_from_volume_backed_server)\ + (?!.*test_network_ip_availability_rbac.NetworkIpAvailabilityExtRbacTest.test_get_network_ip_availabilities)\ + (?!.*test_policy_bandwidth_limit_rule_rbac)\ + (?!.*test_policy_minimum_bandwidth_rule_rbac)\ + (?!.*test_group_type_specs)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_create_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_delete_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_update_group_type)\ + (?!.*test_group_snapshots_rbac)\ + (?!.*test_groups_rbac)\ + (?!.*test_quota_classes_rbac)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_create_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_delete_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_show_interface)\ + (?!.*test_user_messages_rbac)\ + (?!.*test_volume_actions_rbac.VolumesActionsV310RbacTest)\ + (?!.*test_volume_actions_rbac.VolumesActionsV312RbacTest)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_delete_volume_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_list_volumes_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_show_volume_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_update_volume_image_metadata)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV318RbacTest)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV39RbacTest)\ + (?!.*test_volume_types_rbac)\ + (?=.*[.*\bslow\b.*])\ + (^patrole_tempest_plugin.tests.api.(compute|image|network|volume))" + option: + - '--concurrency=4' + + - + case_name: patrole_member + project_name: functest + criteria: 100 + blocking: false + description: >- + Test suite from Patrole project. + run: + name: patrole + args: + roles: 'member' + mode: "(?!.*test_unmanage_snapshot_rbac)\ + (?!.*test_show_auto_allocated_topology)\ + (?!.*test_delete_auto_allocated_topology)\ + (?!.*test_create_network_provider_segmentation_id)\ + (?!.*compute.test_floating_ips_rbac)\ + (?!.*test_reset_network)\ + (?!.*test_create_image_from_volume_backed_server)\ + (?!.*test_network_ip_availability_rbac.NetworkIpAvailabilityExtRbacTest.test_get_network_ip_availabilities)\ + (?!.*test_policy_bandwidth_limit_rule_rbac)\ + (?!.*test_policy_minimum_bandwidth_rule_rbac)\ + (?!.*test_group_type_specs)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_create_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_delete_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_update_group_type)\ + (?!.*test_group_snapshots_rbac)\ + (?!.*test_groups_rbac)\ + (?!.*test_quota_classes_rbac)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_create_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_delete_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_show_interface)\ + (?!.*test_user_messages_rbac)\ + (?!.*test_volume_actions_rbac.VolumesActionsV310RbacTest)\ + (?!.*test_volume_actions_rbac.VolumesActionsV312RbacTest)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_delete_volume_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_list_volumes_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_show_volume_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_update_volume_image_metadata)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV318RbacTest)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV39RbacTest)\ + (?!.*test_volume_types_rbac)\ + (?=.*[.*\bslow\b.*])\ + (^patrole_tempest_plugin.tests.api.(compute|image|network|volume))" + option: + - '--concurrency=4' + + - + case_name: patrole_reader + project_name: functest + criteria: 100 + blocking: false + description: >- + Test suite from Patrole project. + run: + name: patrole + args: + roles: 'reader' + mode: "(?!.*test_unmanage_snapshot_rbac)\ + (?!.*test_show_auto_allocated_topology)\ + (?!.*test_delete_auto_allocated_topology)\ + (?!.*test_create_network_provider_segmentation_id)\ + (?!.*compute.test_floating_ips_rbac)\ + (?!.*test_reset_network)\ + (?!.*test_create_image_from_volume_backed_server)\ + (?!.*test_network_ip_availability_rbac.NetworkIpAvailabilityExtRbacTest.test_get_network_ip_availabilities)\ + (?!.*test_policy_bandwidth_limit_rule_rbac)\ + (?!.*test_policy_minimum_bandwidth_rule_rbac)\ + (?!.*test_group_type_specs)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_create_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_delete_group_type)\ + (?!.*test_groups_rbac.GroupTypesV3RbacTest.test_update_group_type)\ + (?!.*test_group_snapshots_rbac)\ + (?!.*test_groups_rbac)\ + (?!.*test_quota_classes_rbac)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_create_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_delete_interface)\ + (?!.*test_server_misc_policy_actions_rbac.MiscPolicyActionsNetworkRbacTest.test_show_interface)\ + (?!.*test_user_messages_rbac)\ + (?!.*test_volume_actions_rbac.VolumesActionsV310RbacTest)\ + (?!.*test_volume_actions_rbac.VolumesActionsV312RbacTest)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_delete_volume_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_list_volumes_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_show_volume_details_image_metadata)\ + (?!.*test_volume_metadata_rbac.VolumeMetadataV3RbacTest.test_update_volume_image_metadata)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV318RbacTest)\ + (?!.*test_volumes_backup_rbac.VolumesBackupsV39RbacTest)\ + (?!.*test_volume_types_rbac)\ + (?=.*[.*\bslow\b.*])\ + (^patrole_tempest_plugin.tests.api.(compute|image|network|volume))" + option: + - '--concurrency=4' + + - + case_name: tempest_barbican project_name: functest criteria: 100 blocking: false @@ -334,117 +533,378 @@ tiers: It leverages on the tempest plugin containing tests used to verify the functionality of a barbican installation. run: - module: 'functest.opnfv_tests.openstack.tempest.tempest' - class: 'TempestCommon' + name: barbican args: - mode: 'barbican_tempest_plugin.tests.(api|scenario)' + mode: + '^barbican_tempest_plugin.((?!test_signed_image_upload_boot_failure).)*$' services: - barbican + option: + - '--concurrency=4' + + - + case_name: tempest_octavia + project_name: functest + criteria: 100 + blocking: false + description: >- + It leverages on the tempest plugin containing tests used to + verify the functionality of an octavia installation. + run: + name: tempest_common + args: + mode: "(?!.*api.v2.test_availability_zone)\ + (?!.*api.v2.test_availability_zone_profile)\ + (?!.*api.v2.test_member.MemberAPITest.test_member_ipv4_create)\ + (?!.*api.v2.test_member.MemberAPITest.test_member_ipv6_create)\ + (^octavia_tempest_plugin.tests.(api|scenario))" + services: + - octavia + option: + - '--concurrency=2' + + - + case_name: tempest_cyborg + project_name: functest + criteria: 100 + blocking: false + description: >- + It leverages on the tempest plugin containing tests used to + verify the functionality of a cyborg installation. + run: + name: tempest_common + args: + mode: '^cyborg_tempest_plugin' + services: + - cyborg + option: + - '--concurrency=4' - - name: features - order: 2 + name: smoke_cntt description: >- - Test suites from feature projects - integrated in functest + Set of basic Functional tests to validate the OPNFV scenarios. testcases: - - case_name: doctor-notification - project_name: doctor + case_name: tempest_neutron_cntt + project_name: functest criteria: 100 blocking: false + deny_skipping: true + tests_count: 564 description: >- - Test suite from Doctor project. - dependencies: - - INSTALLER_TYPE: '(apex)|(fuel)|(daisy)' - - DEPLOY_SCENARIO: '^((?!fdio|nofeature).)*$' + This test case runs the Tempest suite proposed by the + Neutron project. The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. run: - module: 'xtesting.core.feature' - class: 'BashFeature' + name: tempest_common args: - cmd: 'doctor-test' + mode: "(?!.*admin.test_agent_availability_zone)\ + (?!.*admin.test_dhcp_agent_scheduler)\ + (?!.*admin.test_l3_agent_scheduler)\ + (?!.*admin.test_logging)\ + (?!.*admin.test_logging_negative)\ + (?!.*admin.test_network_segment_range)\ + (?!.*admin.test_ports.PortTestCasesAdmin.test_regenerate_mac_address)\ + (?!.*admin.test_ports.PortTestCasesResourceRequest)\ + (?!.*admin.test_routers_dvr)\ + (?!.*admin.test_routers_flavors)\ + (?!.*admin.test_routers_ha)\ + (?!.*test_conntrack_helper)\ + (?!.*test_floating_ips.FloatingIPPoolTestJSON)\ + (?!.*test_floating_ips.FloatingIPTestJSON.test_create_update_floatingip_port_details)\ + (?!.*test_local_ip)\ + (?!.*test_metering_extensions)\ + (?!.*test_metering_negative)\ + (?!.*test_networks.NetworksSearchCriteriaTest.test_list_validation_filters)\ + (?!.*test_networks.NetworksTestAdmin.test_create_tenant_network_vxlan)\ + (?!.*test_networks.NetworksTestJSON.test_create_update_network_dns_domain)\ + (?!.*test_port_forwardings)\ + (?!.*test_ports.PortsTaggingOnCreation)\ + (?!.*test_ports.PortsTestJSON.test_create_port_with_propagate_uplink_status)\ + (?!.*test_ports.PortsTestJSON.test_create_port_without_propagate_uplink_status)\ + (?!.*test_ports.PortsTestJSON.test_create_update_port_with_dns_domain)\ + (?!.*test_ports.PortsTestJSON.test_create_update_port_with_dns_name)\ + (?!.*test_ports.PortsTestJSON.test_create_update_port_with_no_dns_name)\ + (?!.*test_qos.QosMinimumBandwidthRuleTestJSON)\ + (?!.*test_revisions.TestRevisions.test_update_dns_domain_bumps_revision)\ + (?!.*test_revisions.TestRevisions.test_update_router_extra_attributes_bumps_revision)\ + (?!.*test_router_interface_fip)\ + (?!.*test_routers.DvrRoutersTest)\ + (?!.*test_routers.HaRoutersTest)\ + (?!.*test_routers.RoutersIpV6Test.test_extra_routes_atomic)\ + (?!.*test_routers.RoutersTest.test_extra_routes_atomic)\ + (?!.*test_routers_negative.DvrRoutersNegativeTest)\ + (?!.*test_routers_negative.DvrRoutersNegativeTestExtended)\ + (?!.*test_routers_negative.HaRoutersNegativeTest)\ + (?!.*test_security_groups.RbacSharedSecurityGroupTest)\ + (?!.*test_subnetpool_prefix_ops)\ + (?!.*test_subnetpools.RbacSubnetPoolTest)\ + (?!.*test_subnetpools.SubnetPoolsSearchCriteriaTest.test_list_validation_filters)\ + (?!.*test_subnetpools_negative.SubnetPoolsNegativeTestJSON.test_tenant_create_subnetpool_associate_shared_address_scope)\ + (?!.*test_subnets.SubnetsSearchCriteriaTest.test_list_validation_filters)\ + (?!.*test_timestamp.TestTimeStamp.test_segment_with_timestamp)\ + (?!.*test_trunk.TrunkTestInheritJSONBase.test_add_subport)\ + (?!.*test_trunk.TrunkTestMtusJSON)\ + (?!.*test_trunk_negative.TrunkTestJSON.test_create_subport_invalid_inherit_network_segmentation_type)\ + (?!.*test_trunk_negative.TrunkTestMtusJSON)\ + (^neutron_tempest_plugin.api)" + option: + - '--concurrency=4' - - case_name: bgpvpn - project_name: sdnvpn + case_name: tempest_cinder_cntt + project_name: functest criteria: 100 blocking: false + deny_skipping: true + tests_count: 10 description: >- - Test suite from SDNVPN project. - dependencies: - - DEPLOY_SCENARIO: 'bgpvpn' + This test case runs the Tempest suite proposed by the + Cinder project. run: - module: 'sdnvpn.test.functest.run_sdnvpn_tests' - class: 'SdnvpnFunctest' + name: tempest_common + args: + mode: "(?!.*test_incremental_backup)\ + (?!.*test_consistencygroups)\ + (?!.*test_backup_crossproject_admin_negative)\ + (?!.*test_backup_crossproject_user_negative)\ + (?!.*test_volume_encrypted.TestEncryptedCinderVolumes)\ + (?!.*rbac)\ + (^cinder_tempest_plugin.)" + option: + - '--concurrency=4' - - case_name: functest-odl-sfc - project_name: sfc + case_name: tempest_keystone_cntt + project_name: functest criteria: 100 blocking: false + deny_skipping: true + tests_count: 27 description: >- - Test suite for odl-sfc to test two chains with one SF and - one chain with two SFs - dependencies: - - DEPLOY_SCENARIO: 'odl.*sfc' + This test case runs the Tempest suite proposed by the + Keystone project. run: - module: 'sfc.tests.functest.run_sfc_tests' - class: 'SfcFunctest' + name: tempest_common + args: + mode: "(?!.*api.identity.v3.test_oauth1_tokens)\ + (?!.*rbac)\ + (?!.*scenario.test_federated_authentication)\ + keystone_tempest_plugin." + option: + - '--concurrency=4' - - case_name: barometercollectd - project_name: barometer + case_name: tempest_heat_cntt + project_name: functest criteria: 100 blocking: false + deny_skipping: true + tests_count: 124 description: >- - 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: - - DEPLOY_SCENARIO: 'bar' + This test case runs the Tempest suite proposed by the + Heat project. run: - module: 'baro_tests.barometer' - class: 'BarometerCollectd' + name: tempest_heat + args: + mode: "(?!.*functional.test_lbaasv2)\ + (?!.*functional.test_encryption_vol_type)\ + (?!.*functional.test_event_sinks)\ + (?!.*functional.test_software_config.ZaqarSignalTransportTest)\ + (?!.*functional.test_stack_events)\ + (?!.*functional.test_waitcondition)\ + (?!.*RemoteStackTest.test_stack_create_with_cloud_credential)\ + (?!.*scenario.test_aodh_alarm)\ + (?!.*tests.scenario.test_autoscaling_lb)\ + (?!.*scenario.test_autoscaling_lbv2)\ + (?!.*scenario.test_server_software_config)\ + (?!.*test_volumes.VolumeBackupRestoreIntegrationTest)\ + (?!.*scenario.test_octavia_lbaas)\ + (?!.*scenario.test_server_cfn_init)\ + ^heat_tempest_plugin.tests" + option: + - '--concurrency=1' - - case_name: fds - project_name: fastdatastacks + case_name: rally_sanity_cntt + project_name: functest criteria: 100 blocking: false description: >- - Test Suite for the OpenDaylight SDN Controller when GBP - features are installed. It integrates some test suites from - upstream using Robot as the test framework. - dependencies: - - DEPLOY_SCENARIO: 'odl.*-fdio' + This test case runs a sub group of tests of the OpenStack + Rally suite in smoke mode. run: - module: 'functest.opnfv_tests.sdn.odl.odl' - class: 'ODLTests' + name: rally_sanity args: - suites: - - /src/fds/testing/robot + tests: + - 'authenticate' + - 'glance' + - 'cinder' + - 'heat' + - 'keystone' + - 'neutron' + - 'nova' + - 'quotas' + - 'swift' - - - name: components - order: 3 - description: >- - Extensive testing of OpenStack API. - testcases: - - case_name: tempest_full + case_name: tempest_full_cntt project_name: functest - criteria: 80 + criteria: 100 blocking: false + deny_skipping: true + tests_count: 1271 description: >- The list of test cases is generated by Tempest automatically and depends on the parameters of - the OpenStack deplopyment. + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L83 + run: + name: tempest_common + args: + mode: "(?!.*admin.test_agents)(?!.*test_fixed_ips)\ + (?!.*test_fixed_ips_negative)\ + (?!.*test_auto_allocate_network)(?!.*test_floating_ips_bulk)\ + (?!.*test_flavors_microversions.FlavorsV255TestJSON)\ + (?!.*test_flavors_microversions.FlavorsV261TestJSON)\ + (?!.*test_live_migration.LiveAutoBlockMigrationV225Test.test_iscsi_volume)\ + (?!.*test_live_migration.LiveAutoBlockMigrationV225Test.test_live_block_migration)\ + (?!.*test_live_migration.LiveAutoBlockMigrationV225Test.test_live_block_migration_paused)\ + (?!.*test_live_migration.LiveAutoBlockMigrationV225Test.test_volume_backed_live_migration)\ + (?!.*test_live_migration.LiveMigrationTest.test_iscsi_volume)\ + (?!.*test_live_migration.LiveMigrationTest.test_live_block_migration)\ + (?!.*test_live_migration.LiveMigrationTest.test_live_block_migration_paused)\ + (?!.*test_live_migration.LiveMigrationTest.test_volume_backed_live_migration)\ + (?!.*test_live_migration.LiveMigrationRemoteConsolesV26Test)\ + (?!.*test_quotas.QuotasAdminTestV257)\ + (?!.*test_servers.ServersAdminTestJSON.test_reset_network_inject_network_info)\ + (?!.*certificates.test_certificates)\ + (?!.*test_quotas_negative.QuotasSecurityGroupAdminNegativeTest)\ + (?!.*test_novnc)(?!.*test_server_personality)\ + (?!.*test_servers.ServerShowV263Test.test_show_update_rebuild_list_server)\ + (?!.*test_servers_microversions.ServerShowV254Test)\ + (?!.*test_servers_microversions.ServerShowV257Test)\ + (?!.*test_servers_negative.ServersNegativeTestJSON.test_personality_file_contents_not_encoded)\ + (?!.*servers.test_virtual_interfaces)\ + (?!.*test_server_actions.ServerActionsTestJSON.test_change_server_password)\ + (?!.*test_server_actions.ServerActionsTestJSON.test_get_vnc_console)\ + (?!.*test_server_actions.ServerActionsTestJSON.test_reboot_server_soft)\ + (?!.*test_server_rescue.ServerBootFromVolumeStableRescueTest)\ + (?!.*test_server_rescue.ServerStableDeviceRescueTest)\ + (?!.*test_security_group_default_rules)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_security_group_create_with_duplicate_name)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_security_group_create_with_invalid_group_description)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_security_group_create_with_invalid_group_name)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_update_security_group_with_invalid_sg_des)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_update_security_group_with_invalid_sg_id)\ + (?!.*test_security_groups_negative.SecurityGroupsNegativeTestJSON.test_update_security_group_with_invalid_sg_name)\ + (?!.*test_create_server.ServersTestFqdnHostnames.test_create_server_with_fqdn_name)\ + (?!.*test_server_metadata.ServerMetadataTestJSON)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_delete_metadata_non_existent_server)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_metadata_items_limit)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_set_metadata_invalid_key)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_set_metadata_non_existent_server)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_set_server_metadata_blank_key)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_set_server_metadata_missing_metadata)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_update_metadata_non_existent_server)\ + (?!.*test_server_metadata_negative.ServerMetadataNegativeTestJSON.test_update_metadata_with_blank_key)\ + (?!.*test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filtered_by_ip_regex)\ + (?!.*compute.test_virtual_interfaces)(?!.*compute.test_virtual_interfaces_negative)\ + (?!.*compute.test_networks)\ + (?!.*test_attach_volume.AttachVolumeMultiAttach)\ + (?!.*identity.admin.v2)(?!.*identity.v2)\ + (?!.*identity.v3.test_access_rules)\ + (?!.*identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_create_application_credential_access_rules)\ + (?!.*image.v1)\ + (?!.*image.v2.admin.test_images.ImportCopyImagesTest)\ + (?!.*image.v2.test_images_negative.ImagesNegativeTest.test_create_image_reserved_property)\ + (?!.*image.v2.test_images_negative.ImagesNegativeTest.test_update_image_reserved_property)\ + (?!.*image.v2.test_images_negative.ImportImagesNegativeTest.test_image_web_download_import_with_bad_url)\ + (?!.*image.v2.test_images.ImportImagesTest)\ + (?!.*image.v2.test_images.MultiStoresImportImages)\ + (?!.*admin.test_dhcp_agent_scheduler)\ + (?!.*admin.test_routers_dvr)\ + (?!.*test_metering_extensions)(?!.*network.test_tags)\ + (?!.*test_routers_negative.DvrRoutersNegativeTest)\ + (?!.*test_routers.RoutersIpV6Test.test_create_router_set_gateway_with_fixed_ip)\ + (?!.*test_routers.RoutersTest.test_create_router_set_gateway_with_fixed_ip)\ + (?!.*test_object_services.ObjectTest.test_create_object_with_transfer_encoding)\ + (?!.*test_encrypted_volumes_extend)\ + (?!.*test_group_snapshots.GroupSnapshotsV319Test.test_reset_group_snapshot_status)\ + (?!.*test_multi_backend)\ + (?!.*test_volume_retype.VolumeRetypeWithMigrationTest)\ + (?!.*test_volume_delete_cascade.VolumesDeleteCascade.test_volume_from_snapshot_cascade_delete)\ + (?!.*test_volumes_backup.VolumesBackupsTest.test_volume_backup_create_get_detailed_list_restore_delete)\ + (?!.*test_volumes_negative.UpdateMultiattachVolumeNegativeTest.test_multiattach_rw_volume_update_failure)\ + (?!.*test_volumes_extend.VolumesExtendAttachedTest.test_extend_attached_volume)\ + (?!.*\\[.*\\bslow\\b.*\\])(^tempest.api)" + option: + - '--concurrency=4' + + - + case_name: tempest_scenario_cntt + project_name: functest + criteria: 100 + blocking: false + deny_skipping: true + tests_count: 13 + description: >- + The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L84 + run: + name: tempest_scenario + args: + mode: "\ + (?!.*test_compute_unified_limits)\ + (?!.*test_minbw_allocation_placement)\ + (?!.*test_network_qos_placement)\ + (?!.*test_unified_limits.ImageQuotaTest.test_image_count_uploading_quota)\ + (?!.*test_unified_limits.ImageQuotaTest.test_image_stage_quota)\ + (?!.*test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks)\ + (?!.*\\[.*\\bslow\\b.*\\])(^tempest.scenario)" + option: + - '--concurrency=1' + + - + case_name: tempest_slow_cntt + project_name: functest + criteria: 100 + blocking: false + deny_skipping: true + tests_count: 43 + description: >- + The list of test cases is generated by + Tempest automatically and depends on the parameters of + the OpenStack deployment. + https://github.com/openstack/tempest/blob/18.0.0/tox.ini#L84 run: - module: 'functest.opnfv_tests.openstack.tempest.tempest' - class: 'TempestCommon' + name: tempest_common args: - mode: '^(tempest|neutron_tempest_plugin)\.' + mode: "(?!.*test_volume_swap)\ + (?!.*test_server_personality)\ + (?!.*test_server_rescue.ServerBootFromVolumeStableRescueTest)\ + (?!.*test_container_sync.ContainerSyncTest.test_container_synchronization)\ + (?!.*test_container_sync_middleware.ContainerSyncMiddlewareTest.test_container_synchronization)\ + (?!.*test_encrypted_cinder_volumes)\ + (?!.*test_minbw_allocation_placement)\ + (?!.*test_network_basic_ops.TestNetworkBasicOps.test_router_rescheduling)\ + (?!.*test_shelve_instance.TestShelveInstance.test_cold_migrate_unshelved_instance)\ + (?!.*test_volume_migrate_attached)\ + (?!.*test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_server_connectivity_cold_migration_revert)\ + (?=.*\\[.*\\bslow\\b.*\\])(^tempest.)" + option: + - '--concurrency=1' + - + name: benchmarking + description: >- + Run several OpenStack performance tools + https://docs.openstack.org/performance-docs/latest/methodologies/tools.html + testcases: - case_name: rally_full project_name: functest @@ -454,12 +914,101 @@ tiers: This test case runs the full suite of scenarios of the OpenStack Rally suite using several threads and iterations. run: - module: 'functest.opnfv_tests.openstack.rally.rally' - class: 'RallyFull' + name: rally_full + args: + optional: + - 'gnocchi' + - 'barbican' + + - + case_name: rally_jobs + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs a group of Rally jobs used in + OpenStack gating + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' + run: + name: rally_jobs + args: + optional: + - 'gnocchi' + + - + case_name: vmtp + project_name: functest + criteria: 100 + blocking: false + description: >- + VMTP is a small python application that will automatically + perform ping connectivity, round trip time measurement + (latency) and TCP/UDP throughput + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' + run: + name: vmtp + + - + case_name: shaker + project_name: functest + criteria: 100 + blocking: false + description: >- + Shaker wraps around popular system network testing tools + like iperf, iperf3 and netperf (with help of flent). Shaker + is able to deploy OpenStack instances and networks in + different topologies. + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' + run: + name: shaker + + - + name: benchmarking_cntt + description: >- + Run several OpenStack performance tools + https://docs.openstack.org/performance-docs/latest/methodologies/tools.html + testcases: + - + case_name: rally_full_cntt + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs the full suite of scenarios of the + OpenStack Rally suite using several threads and iterations. + run: + name: rally_full + args: + tests: + - 'authenticate' + - 'glance' + - 'cinder' + - 'heat' + - 'keystone' + - 'neutron' + - 'nova' + - 'quotas' + - 'swift' + + - + case_name: rally_jobs_cntt + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case runs a group of Rally jobs used in + OpenStack gating + run: + name: rally_jobs + args: + tests: + - 'neutron' - name: vnf - order: 4 description: >- Collection of VNF test cases. testcases: @@ -470,25 +1019,35 @@ tiers: blocking: false description: >- This test case deploys the Cloudify orchestrator. - dependencies: - - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' run: - module: 'functest.core.cloudify' - class: 'Cloudify' + name: cloudify - case_name: cloudify_ims project_name: functest - criteria: 80 + criteria: 100 blocking: false description: >- This test case deploys an OpenSource vIMS solution from Clearwater using the Cloudify orchestrator. It also runs some signaling traffic. dependencies: - - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' + - NO_TENANT_NETWORK: '^(?![tT]rue$)' + run: + name: cloudify_ims + + - + case_name: heat_ims + project_name: functest + criteria: 100 + blocking: false + description: >- + This test case deploys an OpenSource vIMS solution from + Clearwater using the OpenStack Heat orchestrator. + It also runs some signaling traffic. + dependencies: + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: 'functest.opnfv_tests.vnf.ims.cloudify_ims' - class: 'CloudifyIms' + name: heat_ims - case_name: vyos_vrouter @@ -498,10 +1057,9 @@ tiers: description: >- This test case is vRouter testing. dependencies: - - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: 'functest.opnfv_tests.vnf.router.cloudify_vrouter' - class: 'CloudifyVrouter' + name: vyos_vrouter - case_name: juju_epc @@ -512,7 +1070,6 @@ tiers: vEPC validation with Juju as VNF manager and ABoT as test executor. dependencies: - - DEPLOY_SCENARIO: 'os-.*-nofeature-.*ha' + - NO_TENANT_NETWORK: '^(?![tT]rue$)' run: - module: 'functest.opnfv_tests.vnf.epc.juju_epc' - class: 'JujuEpc' + name: juju_epc diff --git a/functest/core/cloudify.py b/functest/core/cloudify.py index 954491f6c..966d33645 100644 --- a/functest/core/cloudify.py +++ b/functest/core/cloudify.py @@ -12,9 +12,13 @@ from __future__ import division import logging +import os import time +import traceback from cloudify_rest_client import CloudifyClient +from cloudify_rest_client.executions import Execution +import scp from functest.core import singlevm @@ -25,23 +29,28 @@ class Cloudify(singlevm.SingleVm2): __logger = logging.getLogger(__name__) filename = ('/home/opnfv/functest/images/' - 'cloudify-manager-premium-4.0.1.qcow2') + 'ubuntu-18.04-server-cloudimg-amd64.img') flavor_ram = 4096 flavor_vcpus = 2 - flavor_disk = 50 - username = 'centos' + flavor_disk = 40 + username = 'ubuntu' ssh_connect_loops = 12 + create_server_timeout = 600 ports = [80, 443, 5671, 53333] + cloudify_archive = ('/home/opnfv/functest/images/' + 'cloudify-docker-manager-community-19.01.24.tar') + cloudify_container = "docker-cfy-manager:latest" + def __init__(self, **kwargs): """Initialize Cloudify testcase object.""" if "case_name" not in kwargs: kwargs["case_name"] = "cloudify" - super(Cloudify, self).__init__(**kwargs) + super().__init__(**kwargs) self.cfy_client = None def prepare(self): - super(Cloudify, self).prepare() + super().prepare() for port in self.ports: self.cloud.create_security_group_rule( self.sec.id, port_range_min=port, port_range_max=port, @@ -51,12 +60,28 @@ class Cloudify(singlevm.SingleVm2): """ Deploy Cloudify Manager. """ + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put(self.cloudify_archive, + remote_path=os.path.basename(self.cloudify_archive)) + (_, stdout, stderr) = self.ssh.exec_command( + "sudo apt-get update && " + "sudo apt-get install -y docker.io && " + "sudo docker load -i " + f"~/{os.path.basename(self.cloudify_archive)} && " + "sudo docker run --name cfy_manager_local -d " + "--restart unless-stopped -v /sys/fs/cgroup:/sys/fs/cgroup:ro " + "--tmpfs /run --tmpfs /run/lock --security-opt seccomp:unconfined " + f"--cap-add SYS_ADMIN --network=host {self.cloudify_container}") + self.__logger.debug("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("error:\n%s", stderr.read().decode("utf-8")) self.cfy_client = CloudifyClient( - host=self.fip.floating_ip_address, - username='admin', password='admin', tenant='default_tenant', - api_version='v3') + host=self.fip.floating_ip_address if self.fip else ( + self.sshvm.public_v4), + username='admin', password='admin', tenant='default_tenant') self.__logger.info("Attemps running status of the Manager") - for loop in range(10): + secret_key = "foo" + secret_value = "bar" + for loop in range(20): try: self.__logger.debug( "status %s", self.cfy_client.manager.get_status()) @@ -65,13 +90,130 @@ class Cloudify(singlevm.SingleVm2): "The current manager status is %s", cfy_status) if str(cfy_status) != 'running': raise Exception("Cloudify Manager isn't up and running") + for secret in iter(self.cfy_client.secrets.list()): + if secret_key == secret["key"]: + self.__logger.debug("Updating secrets: %s", secret_key) + self.cfy_client.secrets.update( + secret_key, secret_value) + break + else: + self.__logger.debug("Creating secrets: %s", secret_key) + self.cfy_client.secrets.create(secret_key, secret_value) + self.cfy_client.secrets.delete(secret_key) + self.__logger.info("Secrets API successfully reached") break except Exception: # pylint: disable=broad-except - self.__logger.info( - "try %s: Cloudify Manager isn't up and running", loop + 1) + self.__logger.debug( + "try %s: Cloudify Manager isn't up and running \n%s", + loop + 1, traceback.format_exc()) time.sleep(30) else: self.__logger.error("Cloudify Manager isn't up and running") return 1 self.__logger.info("Cloudify Manager is up and running") return 0 + + def put_private_key(self): + """Put private keypair in manager""" + self.__logger.info("Put private keypair in manager") + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put(self.key_filename, remote_path='~/cloudify_ims.pem') + (_, stdout, stderr) = self.ssh.exec_command( + "sudo docker cp ~/cloudify_ims.pem " + "cfy_manager_local:/etc/cloudify/ && " + "sudo docker exec cfy_manager_local " + "chmod 444 /etc/cloudify/cloudify_ims.pem") + self.__logger.debug("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("error:\n%s", stderr.read().decode("utf-8")) + + def upload_cfy_plugins(self, yaml, wgn): + """Upload Cloudify plugins""" + (_, stdout, stderr) = self.ssh.exec_command( + "sudo docker exec cfy_manager_local " + f"cfy plugins upload -y {yaml} {wgn} && " + "sudo docker exec cfy_manager_local cfy status") + self.__logger.debug("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("error:\n%s", stderr.read().decode("utf-8")) + + def kill_existing_execution(self, dep_name): + """kill existing execution""" + try: + self.__logger.info('Deleting the current deployment') + exec_list = self.cfy_client.executions.list() + for execution in exec_list: + if execution['status'] == "started": + try: + self.cfy_client.executions.cancel( + execution['id'], force=True) + except Exception: # pylint: disable=broad-except + self.__logger.warning("Can't cancel the current exec") + execution = self.cfy_client.executions.start( + dep_name, 'uninstall', parameters=dict(ignore_failure=True)) + wait_for_execution(self.cfy_client, execution, self.__logger) + self.cfy_client.deployments.delete(dep_name) + time.sleep(10) + self.cfy_client.blueprints.delete(dep_name) + except Exception: # pylint: disable=broad-except + self.__logger.exception("Some issue during the undeployment ..") + + +def wait_for_execution(client, execution, logger, timeout=3600, ): + """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=True, + 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 {execution.workflow_id} for ' + 'deployment {execution.deployment_id} timed out') + # 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.' + f'Available executions: {executions}') diff --git a/functest/core/singlevm.py b/functest/core/singlevm.py index ad79a8e14..4bce516d3 100644 --- a/functest/core/singlevm.py +++ b/functest/core/singlevm.py @@ -9,11 +9,12 @@ """Ease deploying a single VM reachable via ssh -It offers a simple way to create all tenant network ressources + a VM for +It offers a simple way to create all tenant network resources + a VM for advanced testcases (e.g. deploying an orchestrator). """ import logging +import re import tempfile import time @@ -22,6 +23,8 @@ from xtesting.core import testcase from functest.core import tenantnetwork from functest.utils import config +from functest.utils import env +from functest.utils import functest_utils class VmReady1(tenantnetwork.TenantNetwork1): @@ -36,23 +39,27 @@ class VmReady1(tenantnetwork.TenantNetwork1): # pylint: disable=too-many-instance-attributes __logger = logging.getLogger(__name__) - filename = '/home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img' + filename = '/home/opnfv/functest/images/cirros-0.6.1-x86_64-disk.img' + image_format = 'qcow2' + extra_properties = {} + filename_alt = filename + image_alt_format = image_format + extra_alt_properties = extra_properties visibility = 'private' - extra_properties = None flavor_ram = 512 flavor_vcpus = 1 flavor_disk = 1 + flavor_extra_specs = {} flavor_alt_ram = 1024 flavor_alt_vcpus = 1 flavor_alt_disk = 1 - - image_format = 'qcow2' + flavor_alt_extra_specs = flavor_extra_specs + create_server_timeout = 180 def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'vmready1' - super(VmReady1, self).__init__(**kwargs) - self.orig_cloud = self.cloud + super().__init__(**kwargs) self.image = None self.flavor = None @@ -67,20 +74,60 @@ class VmReady1(tenantnetwork.TenantNetwork1): Raises: expection on error """ assert self.cloud + extra_properties = self.extra_properties.copy() + if env.get('IMAGE_PROPERTIES'): + extra_properties.update( + functest_utils.convert_ini_to_dict( + env.get('IMAGE_PROPERTIES'))) + extra_properties.update( + getattr(config.CONF, f'{self.case_name}_extra_properties', {})) image = self.cloud.create_image( - name if name else '{}-img_{}'.format(self.case_name, self.guid), + name if name else f'{self.case_name}-img_{self.guid}', filename=getattr( - config.CONF, '{}_image'.format(self.case_name), + config.CONF, f'{self.case_name}_image', self.filename), - meta=getattr( - config.CONF, '{}_extra_properties'.format(self.case_name), - self.extra_properties), + meta=extra_properties, disk_format=getattr( - config.CONF, '{}_image_format'.format(self.case_name), + config.CONF, f'{self.case_name}_image_format', self.image_format), visibility=getattr( - config.CONF, '{}_visibility'.format(self.case_name), - self.visibility)) + config.CONF, f'{self.case_name}_visibility', + self.visibility), + wait=True) + self.__logger.debug("image: %s", image) + return image + + def publish_image_alt(self, name=None): + """Publish alternative image + + It allows publishing multiple images for the child testcases. It forces + the same configuration for all subtestcases. + + Returns: image + + Raises: expection on error + """ + assert self.cloud + extra_alt_properties = self.extra_alt_properties.copy() + if env.get('IMAGE_PROPERTIES'): + extra_alt_properties.update( + functest_utils.convert_ini_to_dict( + env.get('IMAGE_PROPERTIES'))) + extra_alt_properties.update( + getattr(config.CONF, f'{self.case_name}_extra_alt_properties', {})) + image = self.cloud.create_image( + name if name else f'{self.case_name}-img_alt_{self.guid}', + filename=getattr( + config.CONF, f'{self.case_name}_image_alt', + self.filename_alt), + meta=extra_alt_properties, + disk_format=getattr( + config.CONF, f'{self.case_name}_image_alt_format', + self.image_format), + visibility=getattr( + config.CONF, f'{self.case_name}_visibility', + self.visibility), + wait=True) self.__logger.debug("image: %s", image) return image @@ -96,16 +143,23 @@ class VmReady1(tenantnetwork.TenantNetwork1): """ assert self.orig_cloud flavor = self.orig_cloud.create_flavor( - name if name else '{}-flavor_{}'.format(self.case_name, self.guid), - getattr(config.CONF, '{}_flavor_ram'.format(self.case_name), + name if name else f'{self.case_name}-flavor_{self.guid}', + getattr(config.CONF, f'{self.case_name}_flavor_ram', self.flavor_ram), - getattr(config.CONF, '{}_flavor_vcpus'.format(self.case_name), + getattr(config.CONF, f'{self.case_name}_flavor_vcpus', self.flavor_vcpus), - getattr(config.CONF, '{}_flavor_disk'.format(self.case_name), + getattr(config.CONF, f'{self.case_name}_flavor_disk', self.flavor_disk)) self.__logger.debug("flavor: %s", flavor) - self.orig_cloud.set_flavor_specs( - flavor.id, getattr(config.CONF, 'flavor_extra_specs', {})) + flavor_extra_specs = self.flavor_extra_specs.copy() + if env.get('FLAVOR_EXTRA_SPECS'): + flavor_extra_specs.update( + functest_utils.convert_ini_to_dict( + env.get('FLAVOR_EXTRA_SPECS'))) + flavor_extra_specs.update( + getattr(config.CONF, + f'{self.case_name}_flavor_extra_specs', {})) + self.orig_cloud.set_flavor_specs(flavor.id, flavor_extra_specs) return flavor def create_flavor_alt(self, name=None): @@ -120,17 +174,24 @@ class VmReady1(tenantnetwork.TenantNetwork1): """ assert self.orig_cloud flavor = self.orig_cloud.create_flavor( - name if name else '{}-flavor_alt_{}'.format( - self.case_name, self.guid), - getattr(config.CONF, '{}_flavor_alt_ram'.format(self.case_name), + name if name else f'{self.case_name}-flavor_alt_{self.guid}', + getattr(config.CONF, f'{self.case_name}_flavor_alt_ram', self.flavor_alt_ram), - getattr(config.CONF, '{}_flavor_alt_vcpus'.format(self.case_name), + getattr(config.CONF, f'{self.case_name}_flavor_alt_vcpus', self.flavor_alt_vcpus), - getattr(config.CONF, '{}_flavor_alt_disk'.format(self.case_name), + getattr(config.CONF, f'{self.case_name}_flavor_alt_disk', self.flavor_alt_disk)) self.__logger.debug("flavor: %s", flavor) + flavor_alt_extra_specs = self.flavor_alt_extra_specs.copy() + if env.get('FLAVOR_EXTRA_SPECS'): + flavor_alt_extra_specs.update( + functest_utils.convert_ini_to_dict( + env.get('FLAVOR_EXTRA_SPECS'))) + flavor_alt_extra_specs.update( + getattr(config.CONF, + f'{self.case_name}_flavor_alt_extra_specs', {})) self.orig_cloud.set_flavor_specs( - flavor.id, getattr(config.CONF, 'flavor_extra_specs', {})) + flavor.id, flavor_alt_extra_specs) return flavor def boot_vm(self, name=None, **kwargs): @@ -145,15 +206,71 @@ class VmReady1(tenantnetwork.TenantNetwork1): """ assert self.cloud vm1 = self.cloud.create_server( - name if name else '{}-vm_{}'.format(self.case_name, self.guid), + name if name else f'{self.case_name}-vm_{self.guid}', image=self.image.id, flavor=self.flavor.id, - auto_ip=False, wait=True, - network=self.network.id, - **kwargs) - vm1 = self.cloud.wait_for_server(vm1, auto_ip=False) + auto_ip=False, + network=self.network.id if self.network else env.get( + "EXTERNAL_NETWORK"), + timeout=self.create_server_timeout, wait=True, **kwargs) self.__logger.debug("vm: %s", vm1) return vm1 + def check_regex_in_console(self, name, regex=' login: ', loop=6): + """Wait for specific message in console + + Returns: True or False on errors + """ + assert self.cloud + for iloop in range(loop): + console = self.cloud.get_server_console(name) + self.__logger.debug("console: \n%s", console) + if re.search(regex, console): + self.__logger.debug( + "regex found: '%s' in console\n%s", regex, console) + return True + self.__logger.debug( + "try %s: cannot find regex '%s' in console\n%s", + iloop + 1, regex, console) + time.sleep(10) + self.__logger.error("cannot find regex '%s' in console", regex) + return False + + def clean_orphan_security_groups(self): + """Clean all security groups which are not owned by an existing tenant + + It lists all orphan security groups in use as debug to avoid + misunderstanding the testcase results (it could happen if cloud admin + removes accounts without cleaning the virtual machines) + """ + sec_groups = self.orig_cloud.list_security_groups() + for sec_group in sec_groups: + if not sec_group.tenant_id: + continue + if not self.orig_cloud.get_project(sec_group.tenant_id): + self.__logger.debug("Cleaning security group %s", sec_group.id) + try: + self.orig_cloud.delete_security_group(sec_group.id) + except Exception: # pylint: disable=broad-except + self.__logger.debug( + "Orphan security group %s in use", sec_group.id) + + def count_hypervisors(self): + """Count hypervisors.""" + if env.get('SKIP_DOWN_HYPERVISORS').lower() == 'false': + return len(self.orig_cloud.list_hypervisors()) + return self.count_active_hypervisors() + + def count_active_hypervisors(self): + """Count all hypervisors which are up.""" + compute_cnt = 0 + for hypervisor in self.orig_cloud.list_hypervisors(): + if hypervisor['state'] == 'up': + compute_cnt += 1 + else: + self.__logger.warning( + "%s is down", hypervisor['hypervisor_hostname']) + return compute_cnt + def run(self, **kwargs): """Boot the new VM @@ -168,7 +285,7 @@ class VmReady1(tenantnetwork.TenantNetwork1): status = testcase.TestCase.EX_RUN_ERROR try: assert self.cloud - assert super(VmReady1, self).run( + assert super().run( **kwargs) == testcase.TestCase.EX_OK self.image = self.publish_image() self.flavor = self.create_flavor() @@ -185,20 +302,22 @@ class VmReady1(tenantnetwork.TenantNetwork1): try: assert self.orig_cloud assert self.cloud - super(VmReady1, self).clean() + super().clean() if self.image: self.cloud.delete_image(self.image.id) if self.flavor: self.orig_cloud.delete_flavor(self.flavor.id) + if env.get('CLEAN_ORPHAN_SECURITY_GROUPS').lower() == 'true': + self.clean_orphan_security_groups() except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") class VmReady2(VmReady1): """Deploy a single VM reachable via ssh (scenario2) It creates new user/project before creating and configuring all tenant - network ressources, flavors, images, etc. required by advanced testcases. + network resources, flavors, images, etc. required by advanced testcases. It ensures that all testcases inheriting from SingleVm2 could work without specific configurations (or at least read the same config data). @@ -209,7 +328,7 @@ class VmReady2(VmReady1): def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'vmready2' - super(VmReady2, self).__init__(**kwargs) + super().__init__(**kwargs) try: assert self.orig_cloud self.project = tenantnetwork.NewProject( @@ -223,11 +342,11 @@ class VmReady2(VmReady1): def clean(self): try: - super(VmReady2, self).clean() + super().clean() assert self.project self.project.clean() except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") class SingleVm1(VmReady1): @@ -243,13 +362,16 @@ class SingleVm1(VmReady1): __logger = logging.getLogger(__name__) username = 'cirros' - ssh_connect_timeout = 60 + ssh_connect_timeout = 1 ssh_connect_loops = 6 + create_floating_ip_timeout = 120 + check_console_loop = 6 + check_console_regex = ' login: ' def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'singlevm1' - super(SingleVm1, self).__init__(**kwargs) + super().__init__(**kwargs) self.sshvm = None self.sec = None self.fip = None @@ -267,14 +389,15 @@ class SingleVm1(VmReady1): """ assert self.cloud self.keypair = self.cloud.create_keypair( - '{}-kp_{}'.format(self.case_name, self.guid)) + f'{self.case_name}-kp_{self.guid}') self.__logger.debug("keypair: %s", self.keypair) - self.__logger.debug("private_key: %s", self.keypair.private_key) - with open(self.key_filename, 'w') as private_key_file: + self.__logger.debug("private_key:\n%s", self.keypair.private_key) + with open( + self.key_filename, 'w', encoding='utf-8') as private_key_file: private_key_file.write(self.keypair.private_key) self.sec = self.cloud.create_security_group( - '{}-sg_{}'.format(self.case_name, self.guid), - 'created by OPNFV Functest ({})'.format(self.case_name)) + f'{self.case_name}-sg_{self.guid}', + f'created by OPNFV Functest ({self.case_name})') self.cloud.create_security_group_rule( self.sec.id, port_range_min='22', port_range_max='22', protocol='tcp', direction='ingress') @@ -292,31 +415,34 @@ class SingleVm1(VmReady1): - None on error """ assert vm1 - fip = self.cloud.create_floating_ip( - network=self.ext_net.id, server=vm1) - self.__logger.debug("floating_ip: %s", fip) - p_console = self.cloud.get_server_console(vm1) - self.__logger.debug("vm console: \n%s", p_console) + fip = None + if env.get('NO_TENANT_NETWORK').lower() != 'true': + fip = self.cloud.create_floating_ip( + network=self.ext_net.id, server=vm1, wait=True, + timeout=self.create_floating_ip_timeout) + self.__logger.debug("floating_ip: %s", fip) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy()) for loop in range(self.ssh_connect_loops): try: + p_console = self.cloud.get_server_console(vm1) + self.__logger.debug("vm console: \n%s", p_console) ssh.connect( - fip.floating_ip_address, + fip.floating_ip_address if fip else vm1.public_v4, username=getattr( config.CONF, - '{}_image_user'.format(self.case_name), self.username), + f'{self.case_name}_image_user', self.username), key_filename=self.key_filename, timeout=getattr( config.CONF, - '{}_vm_ssh_connect_timeout'.format(self.case_name), + f'{self.case_name}_vm_ssh_connect_timeout', self.ssh_connect_timeout)) break - except Exception: # pylint: disable=broad-except + except Exception as exc: # pylint: disable=broad-except self.__logger.debug( - "try %s: cannot connect to %s", loop + 1, - fip.floating_ip_address) - time.sleep(10) + "try %s: cannot connect to %s: %s", loop + 1, + fip.floating_ip_address if fip else vm1.public_v4, exc) + time.sleep(9) else: self.__logger.error( "cannot connect to %s", fip.floating_ip_address) @@ -330,8 +456,9 @@ class SingleVm1(VmReady1): Returns: echo exit codes """ - (_, stdout, _) = self.ssh.exec_command('echo Hello World') - self.__logger.debug("output:\n%s", stdout.read()) + (_, stdout, stderr) = self.ssh.exec_command('echo Hello World') + self.__logger.debug("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("error:\n%s", stderr.read().decode("utf-8")) return stdout.channel.recv_exit_status() def run(self, **kwargs): @@ -350,16 +477,19 @@ class SingleVm1(VmReady1): status = testcase.TestCase.EX_RUN_ERROR try: assert self.cloud - assert super(SingleVm1, self).run( + assert super().run( **kwargs) == testcase.TestCase.EX_OK self.result = 0 self.prepare() self.sshvm = self.boot_vm( key_name=self.keypair.id, security_groups=[self.sec.id]) - (self.fip, self.ssh) = self.connect(self.sshvm) - if not self.execute(): - self.result = 100 - status = testcase.TestCase.EX_OK + if self.check_regex_in_console( + self.sshvm.name, regex=self.check_console_regex, + loop=self.check_console_loop): + (self.fip, self.ssh) = self.connect(self.sshvm) + if not self.execute(): + self.result = 100 + status = testcase.TestCase.EX_OK except Exception: # pylint: disable=broad-except self.__logger.exception('Cannot run %s', self.case_name) finally: @@ -378,16 +508,16 @@ class SingleVm1(VmReady1): self.cloud.delete_security_group(self.sec.id) if self.keypair: self.cloud.delete_keypair(self.keypair.name) - super(SingleVm1, self).clean() + super().clean() except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") class SingleVm2(SingleVm1): """Deploy a single VM reachable via ssh (scenario2) It creates new user/project before creating and configuring all tenant - network ressources and vms required by advanced testcases. + network resources and vms required by advanced testcases. It ensures that all testcases inheriting from SingleVm2 could work without specific configurations (or at least read the same config data). @@ -398,7 +528,7 @@ class SingleVm2(SingleVm1): def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'singlevm2' - super(SingleVm2, self).__init__(**kwargs) + super().__init__(**kwargs) try: assert self.orig_cloud self.project = tenantnetwork.NewProject( @@ -412,8 +542,8 @@ class SingleVm2(SingleVm1): def clean(self): try: - super(SingleVm2, self).clean() + super().clean() assert self.project self.project.clean() except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") diff --git a/functest/core/tenantnetwork.py b/functest/core/tenantnetwork.py index 286aef67e..3670dbe8a 100644 --- a/functest/core/tenantnetwork.py +++ b/functest/core/tenantnetwork.py @@ -9,10 +9,11 @@ """Ease deploying tenant networks -It offers a simple way to create all tenant network ressources required by a +It offers a simple way to create all tenant network resources required by a testcase (including all Functest ones): + - TenantNetwork1 selects the user and the project set as env vars - - TenantNetwork2 creates a user and project to isolate the same ressources + - TenantNetwork2 creates a user and project to isolate the same resources This classes could be reused by more complexed scenarios (Single VM) """ @@ -24,18 +25,19 @@ import uuid import os_client_config import shade +from tempest.lib.common.utils import data_utils from xtesting.core import testcase from functest.utils import config from functest.utils import env +from functest.utils import functest_utils -class NewProject(object): +class NewProject(): """Ease creating new projects/users""" # pylint: disable=too-many-instance-attributes __logger = logging.getLogger(__name__) - default_member = "Member" def __init__(self, cloud, case_name, guid): self.cloud = None @@ -46,25 +48,25 @@ class NewProject(object): self.user = None self.password = None self.domain = None - self.role = None self.role_name = None + self.default_member = env.get('NEW_USER_ROLE') def create(self): """Create projects/users""" assert self.orig_cloud assert self.case_name - self.password = str(uuid.uuid4()) + self.password = data_utils.rand_password().replace('%', '!') + self.__logger.debug("password: %s", self.password) self.domain = self.orig_cloud.get_domain( name_or_id=self.orig_cloud.auth.get( "project_domain_name", "Default")) self.project = self.orig_cloud.create_project( - name='{}-project_{}'.format(self.case_name, self.guid), - description="Created by OPNFV Functest: {}".format( - self.case_name), + name=f'{self.case_name[:18]}-project_{self.guid}', + description=f"Created by OPNFV Functest: {self.case_name}", domain_id=self.domain.id) self.__logger.debug("project: %s", self.project) self.user = self.orig_cloud.create_user( - name='{}-user_{}'.format(self.case_name, self.guid), + name=f'{self.case_name}-user_{self.guid}', password=self.password, domain_id=self.domain.id) self.__logger.debug("user: %s", self.user) @@ -74,12 +76,12 @@ class NewProject(object): elif self.orig_cloud.get_role(self.default_member.lower()): self.role_name = self.default_member.lower() else: - raise Exception("Cannot detect {}".format(self.default_member)) + raise Exception(f"Cannot detect {self.default_member}") except Exception: # pylint: disable=broad-except self.__logger.info("Creating default role %s", self.default_member) - self.role = self.orig_cloud.create_role(self.default_member) - self.role_name = self.role.name - self.__logger.debug("role: %s", self.role) + role = self.orig_cloud.create_role(self.default_member) + self.role_name = role.name + self.__logger.debug("role: %s", role) self.orig_cloud.grant_role( self.role_name, user=self.user.id, project=self.project.id, domain=self.domain.id) @@ -95,6 +97,21 @@ class NewProject(object): cloud_config=osconfig.get_one_cloud()) self.__logger.debug("new cloud %s", self.cloud.auth) + def get_environ(self): + "Get new environ" + environ = dict( + os.environ, + OS_USERNAME=self.user.name, + OS_PROJECT_NAME=self.project.name, + OS_PROJECT_ID=self.project.id, + OS_PASSWORD=self.password) + try: + del environ['OS_TENANT_NAME'] + del environ['OS_TENANT_ID'] + except Exception: # pylint: disable=broad-except + pass + return environ + def clean(self): """Remove projects/users""" try: @@ -103,17 +120,21 @@ class NewProject(object): self.orig_cloud.delete_user(self.user.id) if self.project: self.orig_cloud.delete_project(self.project.id) - if self.role: - self.orig_cloud.delete_role(self.role.id) + secgroups = self.orig_cloud.list_security_groups( + filters={'name': 'default', + 'project_id': self.project.id}) + if secgroups: + sec_id = secgroups[0].id + self.orig_cloud.delete_security_group(sec_id) except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") class TenantNetwork1(testcase.TestCase): # pylint: disable=too-many-instance-attributes """Create a tenant network (scenario1) - It creates and configures all tenant network ressources required by + It creates and configures all tenant network resources required by advanced testcases (subnet, network and router). It ensures that all testcases inheriting from TenantNetwork1 could work @@ -122,26 +143,30 @@ class TenantNetwork1(testcase.TestCase): """ __logger = logging.getLogger(__name__) - cidr = '192.168.0.0/24' + cidr = '192.168.120.0/24' shared_network = False def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'tenantnetwork1' - super(TenantNetwork1, self).__init__(**kwargs) - self.res_dir = os.path.join( - getattr(config.CONF, 'dir_results'), self.case_name) + super().__init__(**kwargs) + self.dir_results = os.path.join(getattr(config.CONF, 'dir_results')) + self.res_dir = os.path.join(self.dir_results, self.case_name) + self.output_log_name = 'functest.log' + self.output_debug_log_name = 'functest.debug.log' + self.ext_net = None try: cloud_config = os_client_config.get_config() - self.cloud = shade.OpenStackCloud(cloud_config=cloud_config) + self.cloud = self.orig_cloud = shade.OpenStackCloud( + cloud_config=cloud_config) except Exception: # pylint: disable=broad-except - self.cloud = None - self.ext_net = None + self.cloud = self.orig_cloud = None self.__logger.exception("Cannot connect to Cloud") - try: - self.ext_net = self.get_external_network(self.cloud) - except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot get the external network") + if env.get('NO_TENANT_NETWORK').lower() != 'true': + try: + self.ext_net = self.get_external_network(self.cloud) + except Exception: # pylint: disable=broad-except + self.__logger.exception("Cannot get the external network") self.guid = str(uuid.uuid4()) self.network = None self.subnet = None @@ -175,38 +200,61 @@ class TenantNetwork1(testcase.TestCase): role = cloud.get_role(member.lower()) return role - def _create_network_ressources(self): + @staticmethod + def get_public_auth_url(cloud): + """Get Keystone public endpoint""" + keystone_id = functest_utils.search_services(cloud, 'keystone')[0].id + endpoint = cloud.search_endpoints( + filters={'interface': 'public', + 'service_id': keystone_id})[0].url + return endpoint + + def create_network_resources(self): + """Create all tenant network resources + + It creates a router which gateway is the external network detected. + The new subnet is attached to that router. + + Raises: expection on error + """ assert self.cloud - assert self.ext_net + if env.get('NO_TENANT_NETWORK').lower() != 'true': + assert self.ext_net provider = {} - if hasattr(config.CONF, '{}_network_type'.format(self.case_name)): + if hasattr(config.CONF, f'{self.case_name}_network_type'): provider["network_type"] = getattr( - config.CONF, '{}_network_type'.format(self.case_name)) - if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)): + config.CONF, f'{self.case_name}_network_type') + if hasattr(config.CONF, f'{self.case_name}_physical_network'): provider["physical_network"] = getattr( - config.CONF, '{}_physical_network'.format(self.case_name)) - if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)): + config.CONF, f'{self.case_name}_physical_network') + if hasattr(config.CONF, f'{self.case_name}_segmentation_id'): provider["segmentation_id"] = getattr( - config.CONF, '{}_segmentation_id'.format(self.case_name)) - self.network = self.cloud.create_network( - '{}-net_{}'.format(self.case_name, self.guid), - provider=provider, + config.CONF, f'{self.case_name}_segmentation_id') + domain = self.orig_cloud.get_domain( + name_or_id=self.orig_cloud.auth.get( + "project_domain_name", "Default")) + project = self.orig_cloud.get_project( + self.cloud.auth['project_name'], + domain_id=domain.id) + self.network = self.orig_cloud.create_network( + f'{self.case_name}-net_{self.guid}', + provider=provider, project_id=project.id, shared=self.shared_network) self.__logger.debug("network: %s", self.network) self.subnet = self.cloud.create_subnet( self.network.id, - subnet_name='{}-subnet_{}'.format(self.case_name, self.guid), + subnet_name=f'{self.case_name}-subnet_{self.guid}', cidr=getattr( - config.CONF, '{}_private_subnet_cidr'.format(self.case_name), + config.CONF, f'{self.case_name}_private_subnet_cidr', self.cidr), enable_dhcp=True, dns_nameservers=[env.get('NAMESERVER')]) self.__logger.debug("subnet: %s", self.subnet) self.router = self.cloud.create_router( - name='{}-router_{}'.format(self.case_name, self.guid), - ext_gateway_net_id=self.ext_net.id) + name=f'{self.case_name}-router_{self.guid}', + ext_gateway_net_id=self.ext_net.id if self.ext_net else None) self.__logger.debug("router: %s", self.router) self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id) @@ -215,7 +263,8 @@ class TenantNetwork1(testcase.TestCase): try: assert self.cloud self.start_time = time.time() - self._create_network_ressources() + if env.get('NO_TENANT_NETWORK').lower() != 'true': + self.create_network_resources() self.result = 100 status = testcase.TestCase.EX_OK except Exception: # pylint: disable=broad-except @@ -237,14 +286,14 @@ class TenantNetwork1(testcase.TestCase): if self.network: self.cloud.delete_network(self.network.id) except Exception: # pylint: disable=broad-except - self.__logger.exception("cannot clean all ressources") + self.__logger.exception("cannot clean all resources") class TenantNetwork2(TenantNetwork1): """Create a tenant network (scenario2) It creates new user/project before creating and configuring all tenant - network ressources required by a testcase (subnet, network and router). + network resources required by a testcase (subnet, network and router). It ensures that all testcases inheriting from TenantNetwork2 could work without network specific configurations (or at least read the same config @@ -256,7 +305,7 @@ class TenantNetwork2(TenantNetwork1): def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'tenantnetwork2' - super(TenantNetwork2, self).__init__(**kwargs) + super().__init__(**kwargs) try: assert self.cloud self.project = NewProject( @@ -270,8 +319,8 @@ class TenantNetwork2(TenantNetwork1): def clean(self): try: - super(TenantNetwork2, self).clean() + super().clean() assert self.project self.project.clean() except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot clean all ressources") + self.__logger.exception("Cannot clean all resources") diff --git a/functest/core/vnf.py b/functest/core/vnf.py deleted file mode 100644 index a6afd4e6b..000000000 --- a/functest/core/vnf.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 Orange and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 - -"""Define the parent class of all VNF TestCases.""" - -import logging -import uuid - -from snaps.config.user import UserConfig -from snaps.config.project import ProjectConfig -from snaps.openstack.create_user import OpenStackUser -from snaps.openstack.create_project import OpenStackProject -from snaps.openstack.utils import keystone_utils -from snaps.openstack.tests import openstack_tests - -from xtesting.core import vnf -from functest.utils import constants - -__author__ = ("Morgan Richomme <morgan.richomme@orange.com>, " - "Valentin Boucher <valentin.boucher@orange.com>") - - -class VnfPreparationException(vnf.VnfPreparationException): - """Raise when VNF preparation cannot be executed.""" - - -class OrchestratorDeploymentException(vnf.OrchestratorDeploymentException): - """Raise when orchestrator cannot be deployed.""" - - -class VnfDeploymentException(vnf.VnfDeploymentException): - """Raise when VNF cannot be deployed.""" - - -class VnfTestException(vnf.VnfTestException): - """Raise when VNF cannot be tested.""" - - -class VnfOnBoarding(vnf.VnfOnBoarding): - # pylint: disable=too-many-instance-attributes - """Base model for OpenStack VNF test cases.""" - - __logger = logging.getLogger(__name__) - - def __init__(self, **kwargs): - super(VnfOnBoarding, self).__init__(**kwargs) - self.uuid = uuid.uuid4() - self.user_name = "{}-{}".format(self.case_name, self.uuid) - self.tenant_name = "{}-{}".format(self.case_name, self.uuid) - self.snaps_creds = {} - self.created_object = [] - self.os_project = None - self.tenant_description = "Created by OPNFV Functest: {}".format( - self.case_name) - - def prepare(self): - """ - Prepare the environment for VNF testing: - - * Creation of a user, - * Creation of a tenant, - * Allocation admin role to the user on this tenant - - Returns base.TestCase.EX_OK if preparation is successfull - - Raise VnfPreparationException in case of problem - """ - try: - self.__logger.info( - "Prepare VNF: %s, description: %s", self.case_name, - self.tenant_description) - snaps_creds = openstack_tests.get_credentials( - os_env_file=constants.ENV_FILE) - - self.os_project = OpenStackProject( - snaps_creds, - ProjectConfig( - name=self.tenant_name, - description=self.tenant_description, - domain=snaps_creds.project_domain_name - )) - self.os_project.create() - self.created_object.append(self.os_project) - - snaps_creds.project_domain_id = \ - self.os_project.get_project().domain_id - snaps_creds.user_domain_id = \ - self.os_project.get_project().domain_id - - for role in ['admin', 'Admin']: - if keystone_utils.get_role_by_name( - keystone_utils.keystone_client(snaps_creds), role): - admin_role = role - break - - user_creator = OpenStackUser( - snaps_creds, - UserConfig( - name=self.user_name, - password=str(uuid.uuid4()), - project_name=self.tenant_name, - domain_name=snaps_creds.user_domain_name, - roles={admin_role: self.tenant_name})) - user_creator.create() - self.created_object.append(user_creator) - self.snaps_creds = user_creator.get_os_creds(self.tenant_name) - self.__logger.debug("snaps creds: %s", self.snaps_creds) - - return vnf.VnfOnBoarding.EX_OK - except Exception: # pylint: disable=broad-except - self.__logger.exception("Exception raised during VNF preparation") - raise VnfPreparationException - - def deploy_orchestrator(self): - """ - Deploy an orchestrator (optional). - - If this method is overriden then raise orchestratorDeploymentException - if error during orchestrator deployment - """ - self.__logger.info("Deploy orchestrator (if necessary)") - return True - - def deploy_vnf(self): - """ - Deploy the VNF - - This function MUST be implemented by vnf test cases. - The details section MAY be updated in the vnf test cases. - - The deployment can be executed via a specific orchestrator - or using build-in orchestrators such as heat, OpenBaton, cloudify, - juju, onap, ... - - Returns: - True if the VNF is properly deployed - False if the VNF is not deployed - - Raise VnfDeploymentException if error during VNF deployment - """ - self.__logger.error("VNF must be deployed") - raise VnfDeploymentException - - def test_vnf(self): - """ - Test the VNF - - This function MUST be implemented by vnf test cases. - The details section MAY be updated in the vnf test cases. - - Once a VNF is deployed, it is assumed that specific test suite can be - run to validate the VNF. - Please note that the same test suite can be used on several test case - (e.g. clearwater test suite can be used whatever the orchestrator used - for the deployment) - - Returns: - True if VNF tests are PASS - False if test suite is FAIL - - Raise VnfTestException if error during VNF test - """ - self.__logger.error("VNF must be tested") - raise VnfTestException - - def clean(self): - """ - Clean VNF test case. - - It is up to the test providers to delete resources used for the tests. - By default we clean: - - * the user, - * the tenant - """ - self.__logger.info('Removing the VNF resources ..') - for creator in reversed(self.created_object): - try: - creator.clean() - except Exception as exc: # pylint: disable=broad-except - self.__logger.error('Unexpected error cleaning - %s', exc) diff --git a/functest/opnfv_tests/openstack/api/connection_check.py b/functest/opnfv_tests/openstack/api/connection_check.py index a7a780f67..eaf9767c0 100644 --- a/functest/opnfv_tests/openstack/api/connection_check.py +++ b/functest/opnfv_tests/openstack/api/connection_check.py @@ -16,15 +16,27 @@ import os_client_config import shade from xtesting.core import testcase +from functest.utils import env +from functest.utils import functest_utils + class ConnectionCheck(testcase.TestCase): """Perform simplest queries""" __logger = logging.getLogger(__name__) + func_list = [ + "get_network_extensions", "list_aggregates", "list_domains", + "list_endpoints", "list_floating_ip_pools", "list_floating_ips", + "list_hypervisors", "list_keypairs", "list_networks", "list_ports", + "list_role_assignments", "list_roles", "list_routers", "list_servers", + "list_subnets"] + def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'connection_check' - super(ConnectionCheck, self).__init__(**kwargs) + super().__init__(**kwargs) + self.output_log_name = 'functest.log' + self.output_debug_log_name = 'functest.debug.log' try: cloud_config = os_client_config.get_config() self.cloud = shade.OpenStackCloud(cloud_config=cloud_config) @@ -32,19 +44,26 @@ class ConnectionCheck(testcase.TestCase): self.cloud = None def run(self, **kwargs): + # pylint: disable=protected-access """Run all read operations to check connections""" status = testcase.TestCase.EX_RUN_ERROR try: assert self.cloud self.start_time = time.time() - for func in ["list_aggregates", "list_domains", "list_endpoints", - "list_floating_ip_pools", "list_floating_ips", - "list_hypervisors", "list_keypairs", "list_networks", - "list_ports", "list_role_assignments", "list_roles", - "list_routers", "list_servers", "list_services", - "list_subnets"]: + self.__logger.debug( + "list_services: %s", functest_utils.list_services(self.cloud)) + if env.get('NO_TENANT_NETWORK').lower() == 'true': + self.func_list.remove("list_floating_ip_pools") + self.func_list.remove("list_floating_ips") + self.func_list.remove("list_routers") + for func in self.func_list: self.__logger.debug( "%s: %s", func, getattr(self.cloud, func)()) + data = self.cloud._network_client.get("/service-providers.json") + self.__logger.debug( + "list_service_providers: %s", + self.cloud._get_and_munchify('service_providers', data)) + functest_utils.get_openstack_version(self.cloud) self.result = 100 status = testcase.TestCase.EX_OK except Exception: # pylint: disable=broad-except diff --git a/functest/ci/__init__.py b/functest/opnfv_tests/openstack/barbican/__init__.py index e69de29bb..e69de29bb 100644 --- a/functest/ci/__init__.py +++ b/functest/opnfv_tests/openstack/barbican/__init__.py diff --git a/functest/opnfv_tests/openstack/barbican/barbican.py b/functest/opnfv_tests/openstack/barbican/barbican.py new file mode 100644 index 000000000..706304bbf --- /dev/null +++ b/functest/opnfv_tests/openstack/barbican/barbican.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Copyright (c) 2018 Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +# pylint: disable=missing-docstring + +from six.moves import configparser + +from functest.opnfv_tests.openstack.tempest import tempest + + +class Barbican(tempest.TempestCommon): + + def configure(self, **kwargs): + super().configure(**kwargs) + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if not rconfig.has_section('auth'): + rconfig.add_section('auth') + rconfig.set('auth', 'tempest_roles', 'creator') + if not rconfig.has_section('glance'): + rconfig.add_section('glance') + rconfig.set('glance', 'verify_glance_signatures', True) + if not rconfig.has_section('ephemeral_storage_encryption'): + rconfig.add_section('ephemeral_storage_encryption') + rconfig.set('ephemeral_storage_encryption', 'enabled', True) + if not rconfig.has_section('image-feature-enabled'): + rconfig.add_section('image-feature-enabled') + rconfig.set('image-feature-enabled', 'api_v1', False) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + self.backup_tempest_config(self.conf_file, self.res_dir) diff --git a/functest/opnfv_tests/openstack/cinder/cinder_test.py b/functest/opnfv_tests/openstack/cinder/cinder_test.py index 5354291e6..7d8c0a0bd 100644 --- a/functest/opnfv_tests/openstack/cinder/cinder_test.py +++ b/functest/opnfv_tests/openstack/cinder/cinder_test.py @@ -35,7 +35,7 @@ class CinderCheck(singlevm.SingleVm2): """Initialize testcase.""" if "case_name" not in kwargs: kwargs["case_name"] = "cinder_test" - super(CinderCheck, self).__init__(**kwargs) + super().__init__(**kwargs) self.logger = logging.getLogger(__name__) self.vm2 = None self.fip2 = None @@ -52,15 +52,15 @@ class CinderCheck(singlevm.SingleVm2): return self._write_data() or self._read_data() def prepare(self): - super(CinderCheck, self).prepare() + super().prepare() self.vm2 = self.boot_vm( - '{}-vm2_{}'.format(self.case_name, self.guid), + f'{self.case_name}-vm2_{self.guid}', key_name=self.keypair.id, security_groups=[self.sec.id]) (self.fip2, self.ssh2) = self.connect(self.vm2) self.volume = self.cloud.create_volume( - name='{}-volume_{}'.format(self.case_name, self.guid), size='2', - timeout=self.volume_timeout) + name=f'{self.case_name}-volume_{self.guid}', size='2', + timeout=self.volume_timeout, wait=True) def _write_data(self): assert self.cloud @@ -76,12 +76,15 @@ class CinderCheck(singlevm.SingleVm2): return testcase.TestCase.EX_RUN_ERROR self.logger.debug("ssh: %s", self.ssh) (_, stdout, stderr) = self.ssh.exec_command( - "sh ~/write_data.sh {}".format(env.get('VOLUME_DEVICE_NAME'))) - self.logger.debug("volume_write stdout: %s", stdout.read()) - self.logger.debug("volume_write stderr: %s", stderr.read()) + f"sh ~/write_data.sh {env.get('VOLUME_DEVICE_NAME')}") + self.logger.debug( + "volume_write stdout: %s", stdout.read().decode("utf-8")) + self.logger.debug( + "volume_write stderr: %s", stderr.read().decode("utf-8")) # Detach volume from VM 1 self.logger.info("Detach volume from VM 1") - self.cloud.detach_volume(self.sshvm, self.volume) + self.cloud.detach_volume( + self.sshvm, self.volume, timeout=self.volume_timeout) return stdout.channel.recv_exit_status() def _read_data(self): @@ -101,12 +104,14 @@ class CinderCheck(singlevm.SingleVm2): return testcase.TestCase.EX_RUN_ERROR self.logger.debug("ssh: %s", self.ssh2) (_, stdout, stderr) = self.ssh2.exec_command( - "sh ~/read_data.sh {}".format(env.get('VOLUME_DEVICE_NAME'))) - self.logger.debug("read volume stdout: %s", stdout.read()) - self.logger.debug("read volume stderr: %s", stderr.read()) + f"sh ~/read_data.sh {env.get('VOLUME_DEVICE_NAME')}") + self.logger.debug( + "read volume stdout: %s", stdout.read().decode("utf-8")) + self.logger.debug( + "read volume stderr: %s", stderr.read().decode("utf-8")) self.logger.info("Detach volume from VM 2") - self.cloud.detach_volume(self.vm2, self.volume, - timeout=self.volume_timeout) + self.cloud.detach_volume( + self.vm2, self.volume, timeout=self.volume_timeout) return stdout.channel.recv_exit_status() def clean(self): @@ -119,4 +124,4 @@ class CinderCheck(singlevm.SingleVm2): self.cloud.delete_floating_ip(self.fip2.id) if self.volume: self.cloud.delete_volume(self.volume.id) - super(CinderCheck, self).clean() + super().clean() diff --git a/functest/opnfv_tests/openstack/cinder/write_data.sh b/functest/opnfv_tests/openstack/cinder/write_data.sh index 6689309b9..16845ba31 100644 --- a/functest/opnfv_tests/openstack/cinder/write_data.sh +++ b/functest/opnfv_tests/openstack/cinder/write_data.sh @@ -15,7 +15,7 @@ echo "VOL_DEV_NAME: $VOL_DEV_NAME" echo "$(lsblk -l -o NAME)" if [ ! -z $(lsblk -l -o NAME | grep $VOL_DEV_NAME) ]; then - sudo /usr/sbin/mkfs.ext4 -F /dev/$VOL_DEV_NAME + sudo mkfs.ext4 -F /dev/$VOL_DEV_NAME sudo mount /dev/$VOL_DEV_NAME $DEST sudo touch $DEST/new_data if [ -f $DEST/new_data ]; then diff --git a/functest/opnfv_tests/openstack/patrole/patrole.py b/functest/opnfv_tests/openstack/patrole/patrole.py index efc513cdb..88c42f269 100644 --- a/functest/opnfv_tests/openstack/patrole/patrole.py +++ b/functest/opnfv_tests/openstack/patrole/patrole.py @@ -9,8 +9,6 @@ # pylint: disable=missing-docstring -import logging - from six.moves import configparser from functest.opnfv_tests.openstack.tempest import tempest @@ -18,23 +16,13 @@ from functest.opnfv_tests.openstack.tempest import tempest class Patrole(tempest.TempestCommon): - __logger = logging.getLogger(__name__) - def configure(self, **kwargs): - super(Patrole, self).configure(**kwargs) + super().configure(**kwargs) rconfig = configparser.RawConfigParser() rconfig.read(self.conf_file) - rconfig.add_section('rbac') - rconfig.set('rbac', 'enable_rbac', True) - rconfig.set('rbac', 'rbac_test_role', kwargs.get('role', 'admin')) - with open(self.conf_file, 'wb') as config_file: + if not rconfig.has_section('rbac'): + rconfig.add_section('rbac') + rconfig.set('rbac', 'rbac_test_roles', kwargs.get('roles', 'admin')) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: rconfig.write(config_file) self.backup_tempest_config(self.conf_file, self.res_dir) - - def run(self, **kwargs): - for exclude in kwargs.get('exclude', []): - kwargs['mode'] = "{}(?!.*{})".format( - kwargs.get('mode', ''), exclude) - kwargs['mode'] = '{}(?=patrole_tempest_plugin.tests.api.({}))'.format( - kwargs['mode'], '|'.join(kwargs.get('services', []))) - return super(Patrole, self).run(**kwargs) diff --git a/functest/opnfv_tests/openstack/rally/blacklist.txt b/functest/opnfv_tests/openstack/rally/blacklist.txt deleted file mode 100644 index c9d7c6007..000000000 --- a/functest/opnfv_tests/openstack/rally/blacklist.txt +++ /dev/null @@ -1,28 +0,0 @@ -scenario: - - - scenarios: - - '^os-nosdn-lxd-(no)?ha$' - installers: - - '.+' # all installers - tests: - - NovaServers.boot_server_from_volume_and_delete - - - scenarios: - - '^os-' # all scenarios - installers: - - '.+' # all installers - tests: - # Following test occasionally fails due to race condition issue on - # quota manipulation in nova. - # Ref: https://bugs.launchpad.net/nova/+bug/1552622 - - 'Quotas.nova_update_and_delete' - -functionality: - - - functions: - - no_migration - tests: - - NovaServers.boot_and_live_migrate_server - - NovaServers.boot_server_attach_created_volume_and_live_migrate - - NovaServers.boot_server_from_volume_and_live_migrate - - NovaServers.boot_and_migrate_server diff --git a/functest/opnfv_tests/openstack/rally/blacklist.yaml b/functest/opnfv_tests/openstack/rally/blacklist.yaml new file mode 100644 index 000000000..e16b83ba6 --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/blacklist.yaml @@ -0,0 +1,40 @@ +--- +scenario: + +functionality: + - + functions: + - block_migration + tests: + - NovaServers.boot_server_from_volume_and_live_migrate + - + functions: + - no_migration + tests: + - NovaServers.boot_and_live_migrate_server + - NovaServers.boot_server_attach_created_volume_and_live_migrate + - NovaServers.boot_server_from_volume_and_live_migrate + - NovaServers.boot_and_migrate_server + - + functions: + - no_net_trunk_service + tests: + - '^NeutronTrunk' + - + functions: + - no_floating_ip + tests: + - HeatStacks.create_and_delete_stack + - NovaServers.boot_and_associate_floating_ip + - NovaServers.boot_server_and_list_interfaces + - NovaServers.boot_server_associate_and_dissociate_floating_ip + - NeutronNetworks.create_and_delete_floating_ips + - NeutronNetworks.create_and_list_floating_ips + - NeutronNetworks.associate_and_dissociate_floating_ips + - VMTasks.dd_load_test + - NeutronNetworks.create_and_delete_routers + - NeutronNetworks.create_and_list_routers + - NeutronNetworks.create_and_show_routers + - NeutronNetworks.create_and_update_routers + - NeutronNetworks.set_and_clear_router_gateway + - Quotas.neutron_update diff --git a/functest/opnfv_tests/openstack/rally/macro/macro.yaml b/functest/opnfv_tests/openstack/rally/macro/macro.yaml index 48c0333e9..2536c92f0 100644 --- a/functest/opnfv_tests/openstack/rally/macro/macro.yaml +++ b/functest/opnfv_tests/openstack/rally/macro/macro.yaml @@ -95,3 +95,9 @@ disk_format: {{ type }} image_location: {{ location }} {%- endmacro %} + +{%- macro volume_service(version, service_type) %} + cinder: + version: {{ version }} + service_type: {{ service_type }} +{%- endmacro %} diff --git a/functest/opnfv_tests/openstack/rally/rally.py b/functest/opnfv_tests/openstack/rally/rally.py index 2cdb03cb3..3d897e25d 100644 --- a/functest/opnfv_tests/openstack/rally/rally.py +++ b/functest/opnfv_tests/openstack/rally/rally.py @@ -11,85 +11,118 @@ """Rally testcases implementation.""" from __future__ import division +from __future__ import print_function +import fileinput import json import logging import os import re +import shutil import subprocess import time import pkg_resources import prettytable +from ruamel.yaml import YAML +import six +from six.moves import configparser from xtesting.core import testcase -from xtesting.energy import energy import yaml from functest.core import singlevm -from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils import config from functest.utils import env +from functest.utils import functest_utils LOGGER = logging.getLogger(__name__) -class RallyBase(singlevm.VmReady1): +class RallyBase(singlevm.VmReady2): """Base class form Rally testcases implementation.""" - # pylint: disable=too-many-instance-attributes - TESTS = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat', - 'keystone', 'neutron', 'nova', 'quotas', 'vm', 'all'] + # pylint: disable=too-many-instance-attributes, too-many-public-methods + stests = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat', + 'keystone', 'neutron', 'nova', 'quotas', 'swift', 'barbican', + 'vm'] - RALLY_DIR = pkg_resources.resource_filename( + rally_conf_path = "/etc/rally/rally.conf" + rally_aar4_patch_path = pkg_resources.resource_filename( + 'functest', 'ci/rally_aarch64_patch.conf') + rally_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/rally') - RALLY_SCENARIO_DIR = pkg_resources.resource_filename( + rally_scenario_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/rally/scenario') - TEMPLATE_DIR = pkg_resources.resource_filename( + template_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/rally/scenario/templates') - SUPPORT_DIR = pkg_resources.resource_filename( + support_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/openstack/rally/scenario/support') - USERS_AMOUNT = 2 - TENANTS_AMOUNT = 3 - ITERATIONS_AMOUNT = 10 - CONCURRENCY = 4 - RESULTS_DIR = os.path.join(getattr(config.CONF, 'dir_results'), 'rally') - BLACKLIST_FILE = os.path.join(RALLY_DIR, "blacklist.txt") - TEMP_DIR = os.path.join(RALLY_DIR, "var") + users_amount = 2 + tenants_amount = 3 + iterations_amount = 10 + concurrency = 4 + volume_version = 3 + volume_service_type = "volumev3" + blacklist_file = os.path.join(rally_dir, "blacklist.yaml") + task_dir = os.path.join(getattr(config.CONF, 'dir_rally_data'), 'task') + temp_dir = os.path.join(task_dir, 'var') visibility = 'public' shared_network = True + task_timeout = 3600 + username = 'cirros' def __init__(self, **kwargs): """Initialize RallyBase object.""" - super(RallyBase, self).__init__(**kwargs) + super().__init__(**kwargs) + assert self.orig_cloud + assert self.project + if self.orig_cloud.get_role("admin"): + role_name = "admin" + elif self.orig_cloud.get_role("Admin"): + role_name = "Admin" + else: + raise Exception("Cannot detect neither admin nor Admin") + self.orig_cloud.grant_role( + role_name, user=self.project.user.id, + project=self.project.project.id, + domain=self.project.domain.id) + self.results_dir = os.path.join( + getattr(config.CONF, 'dir_results'), self.case_name) + self.task_file = '' self.creators = [] - self.mode = '' self.summary = [] self.scenario_dir = '' self.smoke = None - self.test_name = None self.start_time = None self.result = None - self.details = None self.compute_cnt = 0 self.flavor_alt = None + self.tests = [] + self.run_cmd = '' + self.network_extensions = [] + self.services = [] - def _build_task_args(self, test_file_name): + def build_task_args(self, test_name): """Build arguments for the Rally task.""" - task_args = {'service_list': [test_file_name]} + task_args = {'service_list': [test_name]} task_args['image_name'] = str(self.image.name) task_args['flavor_name'] = str(self.flavor.name) task_args['flavor_alt_name'] = str(self.flavor_alt.name) task_args['glance_image_location'] = str(self.filename) task_args['glance_image_format'] = str(self.image_format) - task_args['tmpl_dir'] = str(self.TEMPLATE_DIR) - task_args['sup_dir'] = str(self.SUPPORT_DIR) - task_args['users_amount'] = self.USERS_AMOUNT - task_args['tenants_amount'] = self.TENANTS_AMOUNT + task_args['tmpl_dir'] = str(self.template_dir) + task_args['sup_dir'] = str(self.support_dir) + task_args['users_amount'] = self.users_amount + task_args['tenants_amount'] = self.tenants_amount task_args['use_existing_users'] = False - task_args['iterations'] = self.ITERATIONS_AMOUNT - task_args['concurrency'] = self.CONCURRENCY + task_args['iterations'] = self.iterations_amount + task_args['concurrency'] = self.concurrency task_args['smoke'] = self.smoke + task_args['volume_version'] = self.volume_version + task_args['volume_service_type'] = self.volume_service_type + task_args['block_migration'] = env.get("BLOCK_MIGRATION").lower() + task_args['username'] = self.username if self.ext_net: task_args['floating_network'] = str(self.ext_net.name) @@ -99,14 +132,21 @@ class RallyBase(singlevm.VmReady1): if self.network: task_args['netid'] = str(self.network.id) else: - task_args['netid'] = '' + LOGGER.warning( + 'No tenant network created. ' + 'Trying EXTERNAL_NETWORK as a fallback') + if env.get("EXTERNAL_NETWORK"): + network = self.cloud.get_network(env.get("EXTERNAL_NETWORK")) + task_args['netid'] = str(network.id) if network else '' + else: + task_args['netid'] = '' return task_args def _prepare_test_list(self, test_name): """Build the list of test cases to be executed.""" - test_yaml_file_name = 'opnfv-{}.yaml'.format(test_name) - scenario_file_name = os.path.join(self.RALLY_SCENARIO_DIR, + test_yaml_file_name = f'opnfv-{test_name}.yaml' + scenario_file_name = os.path.join(self.rally_scenario_dir, test_yaml_file_name) if not os.path.exists(scenario_file_name): @@ -114,33 +154,108 @@ class RallyBase(singlevm.VmReady1): test_yaml_file_name) if not os.path.exists(scenario_file_name): - raise Exception("The scenario '%s' does not exist." - % scenario_file_name) + raise Exception( + f"The scenario '{scenario_file_name}' does not exist.") LOGGER.debug('Scenario fetched from : %s', scenario_file_name) - test_file_name = os.path.join(self.TEMP_DIR, test_yaml_file_name) + test_file_name = os.path.join(self.temp_dir, test_yaml_file_name) - if not os.path.exists(self.TEMP_DIR): - os.makedirs(self.TEMP_DIR) + if not os.path.exists(self.temp_dir): + os.makedirs(self.temp_dir) - self._apply_blacklist(scenario_file_name, test_file_name) + self.apply_blacklist(scenario_file_name, test_file_name) return test_file_name @staticmethod - def get_task_id(cmd_raw): + def get_verifier_deployment_id(): + """ + Returns deployment id for active Rally deployment + """ + cmd = ("rally deployment list | awk '/" + + getattr(config.CONF, 'rally_deployment_name') + + "/ {print $2}'") + with subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) as proc: + deployment_uuid = proc.stdout.readline().rstrip() + return deployment_uuid.decode("utf-8") + + @staticmethod + def create_rally_deployment(environ=None): + # pylint: disable=unexpected-keyword-arg + """Create new rally deployment""" + # set the architecture to default + pod_arch = env.get("POD_ARCH") + arch_filter = ['aarch64'] + + if pod_arch and pod_arch in arch_filter: + LOGGER.info("Apply aarch64 specific to rally config...") + with open( + RallyBase.rally_aar4_patch_path, "r", + encoding='utf-8') as pfile: + rally_patch_conf = pfile.read() + + for line in fileinput.input(RallyBase.rally_conf_path): + print(line, end=' ') + if "cirros|testvm" in line: + print(rally_patch_conf) + + LOGGER.info("Creating Rally environment...") + try: + cmd = ['rally', 'deployment', 'destroy', + '--deployment', + str(getattr(config.CONF, 'rally_deployment_name'))] + output = subprocess.check_output(cmd) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + except subprocess.CalledProcessError: + pass + + cmd = ['rally', 'deployment', 'create', '--fromenv', + '--name', str(getattr(config.CONF, 'rally_deployment_name'))] + output = subprocess.check_output(cmd, env=environ) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + + cmd = ['rally', 'deployment', 'check'] + output = subprocess.check_output(cmd) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + return RallyBase.get_verifier_deployment_id() + + @staticmethod + def update_keystone_default_role(rally_conf='/etc/rally/rally.conf'): + """Set keystone_default_role in rally.conf""" + if env.get("NEW_USER_ROLE").lower() != "member": + rconfig = configparser.RawConfigParser() + rconfig.read(rally_conf) + if not rconfig.has_section('openstack'): + rconfig.add_section('openstack') + rconfig.set( + 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE")) + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + @staticmethod + def clean_rally_conf(rally_conf='/etc/rally/rally.conf'): + """Clean Rally config""" + if env.get("NEW_USER_ROLE").lower() != "member": + rconfig = configparser.RawConfigParser() + rconfig.read(rally_conf) + if rconfig.has_option('openstack', 'keystone_default_role'): + rconfig.remove_option('openstack', 'keystone_default_role') + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + @staticmethod + def get_task_id(tag): """ Get task id from command rally result. - :param cmd_raw: + :param tag: :return: task_id as string """ - taskid_re = re.compile('^Task +(.*): started$') - for line in cmd_raw.splitlines(True): - line = line.strip() - match = taskid_re.match(line) - if match: - return match.group(1) - return None + cmd = ["rally", "task", "list", "--tag", tag, "--uuids-only"] + output = subprocess.check_output(cmd).decode("utf-8").rstrip() + LOGGER.info("%s: %s", " ".join(cmd), output) + return output @staticmethod def task_succeed(json_raw): @@ -165,35 +280,31 @@ class RallyBase(singlevm.VmReady1): """Determine if migration is supported.""" if self.compute_cnt > 1: return True - return False - @staticmethod - def get_cmd_output(proc): - """Get command stdout.""" - result = "" - for line in proc.stdout: - result += line - return result + def _network_trunk_supported(self): + """Determine if network trunk service is available""" + if 'trunk' in self.network_extensions: + return True + return False @staticmethod def excl_scenario(): """Exclude scenario.""" black_tests = [] try: - with open(RallyBase.BLACKLIST_FILE, 'r') as black_list_file: + with open( + RallyBase.blacklist_file, 'r', + encoding='utf-8') as black_list_file: black_list_yaml = yaml.safe_load(black_list_file) - installer_type = env.get('INSTALLER_TYPE') deploy_scenario = env.get('DEPLOY_SCENARIO') - if (bool(installer_type) and bool(deploy_scenario) and + if (bool(deploy_scenario) and 'scenario' in black_list_yaml.keys()): for item in black_list_yaml['scenario']: scenarios = item['scenarios'] - installers = item['installers'] in_it = RallyBase.in_iterable_re - if (in_it(deploy_scenario, scenarios) and - in_it(installer_type, installers)): + if in_it(deploy_scenario, scenarios): tests = item['tests'] black_tests.extend(tests) except Exception: # pylint: disable=broad-except @@ -228,11 +339,19 @@ class RallyBase(singlevm.VmReady1): func_list = [] try: - with open(RallyBase.BLACKLIST_FILE, 'r') as black_list_file: + with open( + RallyBase.blacklist_file, 'r', + encoding='utf-8') as black_list_file: black_list_yaml = yaml.safe_load(black_list_file) + if env.get('BLOCK_MIGRATION').lower() == 'true': + func_list.append("block_migration") if not self._migration_supported(): func_list.append("no_migration") + if not self._network_trunk_supported(): + func_list.append("no_net_trunk_service") + if not self.ext_net: + func_list.append("no_floating_ip") if 'functionality' in black_list_yaml.keys(): for item in black_list_yaml['functionality']: @@ -246,34 +365,28 @@ class RallyBase(singlevm.VmReady1): return black_tests - def _apply_blacklist(self, case_file_name, result_file_name): + def apply_blacklist(self, case_file_name, result_file_name): """Apply blacklist.""" LOGGER.debug("Applying blacklist...") - cases_file = open(case_file_name, 'r') - result_file = open(result_file_name, 'w') - - black_tests = list(set(self.excl_func() + - self.excl_scenario())) - - if black_tests: - LOGGER.debug("Blacklisted tests: %s", str(black_tests)) - - include = True - for cases_line in cases_file: - if include: - for black_tests_line in black_tests: - if re.search(black_tests_line, - cases_line.strip().rstrip(':')): - include = False - break + with open(case_file_name, 'r', encoding='utf-8') as cases_file, open( + result_file_name, 'w', encoding='utf-8') as result_file: + black_tests = list(set(self.excl_func() + self.excl_scenario())) + if black_tests: + LOGGER.debug("Blacklisted tests: %s", str(black_tests)) + + include = True + for cases_line in cases_file: + if include: + for black_tests_line in black_tests: + if re.search(black_tests_line, + cases_line.strip().rstrip(':')): + include = False + break + else: + result_file.write(str(cases_line)) else: - result_file.write(str(cases_line)) - else: - if cases_line.isspace(): - include = True - - cases_file.close() - result_file.close() + if cases_line.isspace(): + include = True @staticmethod def file_is_empty(file_name): @@ -289,41 +402,28 @@ class RallyBase(singlevm.VmReady1): def _save_results(self, test_name, task_id): """ Generate and save task execution results""" # check for result directory and create it otherwise - if not os.path.exists(self.RESULTS_DIR): + if not os.path.exists(self.results_dir): LOGGER.debug('%s does not exist, we create it.', - self.RESULTS_DIR) - os.makedirs(self.RESULTS_DIR) + self.results_dir) + os.makedirs(self.results_dir) # put detailed result to log cmd = (["rally", "task", "detailed", "--uuid", task_id]) LOGGER.debug('running command: %s', cmd) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - json_detailed = self.get_cmd_output(proc) - LOGGER.info('%s', json_detailed) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) # save report as JSON - cmd = (["rally", "task", "report", "--json", "--uuid", task_id]) + report_json_name = f'{test_name}.json' + report_json_dir = os.path.join(self.results_dir, report_json_name) + cmd = (["rally", "task", "report", "--json", "--uuid", task_id, + "--out", report_json_dir]) LOGGER.debug('running command: %s', cmd) - with open(os.devnull, 'w') as devnull: - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=devnull) - json_results = self.get_cmd_output(proc) - report_json_name = 'opnfv-{}.json'.format(test_name) - report_json_dir = os.path.join(self.RESULTS_DIR, report_json_name) - with open(report_json_dir, 'w') as r_file: - LOGGER.debug('saving json file') - r_file.write(json_results) - - # save report as HTML - report_html_name = 'opnfv-{}.html'.format(test_name) - report_html_dir = os.path.join(self.RESULTS_DIR, report_html_name) - cmd = (["rally", "task", "report", "--html", "--uuid", task_id, - "--out", report_html_dir]) - LOGGER.debug('running command: %s', cmd) - with open(os.devnull, 'w') as devnull: - subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=devnull) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + with open(report_json_dir, encoding='utf-8') as json_file: + json_results = json_file.read() self._append_summary(json_results, test_name) # parse JSON operation result @@ -332,54 +432,37 @@ class RallyBase(singlevm.VmReady1): else: LOGGER.info('Test scenario: "%s" Failed.', test_name) - def _run_task(self, test_name): + def run_task(self, test_name): """Run a task.""" LOGGER.info('Starting test scenario "%s" ...', test_name) - - task_file = os.path.join(self.RALLY_DIR, 'task.yaml') - if not os.path.exists(task_file): - LOGGER.error("Task file '%s' does not exist.", task_file) - raise Exception("Task file '{}' does not exist.".format(task_file)) - - file_name = self._prepare_test_list(test_name) - if self.file_is_empty(file_name): - LOGGER.info('No tests for scenario "%s"', test_name) - return - - cmd = (["rally", "task", "start", "--abort-on-sla-failure", "--task", - task_file, "--task-args", - str(self._build_task_args(test_name))]) - LOGGER.debug('running command: %s', cmd) - - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output = self.get_cmd_output(proc) - task_id = self.get_task_id(output) - + LOGGER.debug('running command: %s', self.run_cmd) + if six.PY3: + subprocess.call( + self.run_cmd, timeout=self.task_timeout, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + with open(os.devnull, 'wb') as devnull: + subprocess.call(self.run_cmd, stdout=devnull, stderr=devnull) + task_id = self.get_task_id(test_name) LOGGER.debug('task_id : %s', task_id) - - if task_id is None: - LOGGER.error('Failed to retrieve task_id, validating task...') - cmd = (["rally", "task", "validate", "--task", task_file, - "--task-args", str(self._build_task_args(test_name))]) - LOGGER.debug('running command: %s', cmd) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output = self.get_cmd_output(proc) - LOGGER.error("Task validation result:" + "\n" + output) + if not task_id: + LOGGER.error("Failed to retrieve task_id") raise Exception("Failed to retrieve task id") - self._save_results(test_name, task_id) def _append_summary(self, json_raw, test_name): + # pylint: disable=too-many-locals """Update statistics summary info.""" nb_tests = 0 nb_success = 0 overall_duration = 0.0 + success = [] + failures = [] rally_report = json.loads(json_raw) for task in rally_report.get('tasks'): for subtask in task.get('subtasks'): + has_errors = False for workload in subtask.get('workloads'): if workload.get('full_duration'): overall_duration += workload.get('full_duration') @@ -390,34 +473,80 @@ class RallyBase(singlevm.VmReady1): for result in workload.get('data'): if not result.get('error'): nb_success += 1 + else: + has_errors = True + + if has_errors: + failures.append(subtask['title']) + else: + success.append(subtask['title']) scenario_summary = {'test_name': test_name, 'overall_duration': overall_duration, 'nb_tests': nb_tests, 'nb_success': nb_success, + 'success': success, + 'failures': failures, 'task_status': self.task_succeed(json_raw)} self.summary.append(scenario_summary) - def _prepare_env(self): - """Create resources needed by test scenarios.""" + def prepare_run(self, **kwargs): + """Prepare resources needed by test scenarios.""" assert self.cloud - LOGGER.debug('Validating the test name...') - if self.test_name not in self.TESTS: - raise Exception("Test name '%s' is invalid" % self.test_name) - - self.compute_cnt = len(self.cloud.list_hypervisors()) + LOGGER.debug('Validating run tests...') + for test in kwargs.get('tests', self.stests): + if test in self.stests: + self.tests.append(test) + else: + raise Exception(f"Test name '{test}' is invalid") + + if not os.path.exists(self.task_dir): + os.makedirs(self.task_dir) + + task = os.path.join(self.rally_dir, 'task.yaml') + if not os.path.exists(task): + LOGGER.error("Task file '%s' does not exist.", task) + raise Exception(f"Task file '{task}' does not exist.") + self.task_file = os.path.join(self.task_dir, 'task.yaml') + shutil.copyfile(task, self.task_file) + + task_macro = os.path.join(self.rally_dir, 'macro') + if not os.path.exists(task_macro): + LOGGER.error("Task macro dir '%s' does not exist.", task_macro) + raise Exception(f"Task macro dir '{task_macro}' does not exist.") + macro_dir = os.path.join(self.task_dir, 'macro') + if os.path.exists(macro_dir): + shutil.rmtree(macro_dir) + shutil.copytree(task_macro, macro_dir) + + self.update_keystone_default_role() + self.compute_cnt = self.count_hypervisors() + self.network_extensions = self.cloud.get_network_extensions() self.flavor_alt = self.create_flavor_alt() + self.services = [service.name for service in + functest_utils.list_services(self.cloud)] + LOGGER.debug("flavor: %s", self.flavor_alt) - def _run_tests(self): + def prepare_task(self, test_name): + """Prepare resources for test run.""" + file_name = self._prepare_test_list(test_name) + if self.file_is_empty(file_name): + LOGGER.info('No tests for scenario "%s"', test_name) + return False + self.run_cmd = (["rally", "task", "start", "--tag", test_name, + "--abort-on-sla-failure", + "--task", self.task_file, "--task-args", + str(self.build_task_args(test_name))]) + return True + + def run_tests(self, **kwargs): """Execute tests.""" - if self.test_name == 'all': - for test in self.TESTS: - if test == 'all' or test == 'vm': - continue - self._run_task(test) - else: - self._run_task(self.test_name) + optional = kwargs.get('optional', []) + for test in self.tests: + if test in self.services or test not in optional: + if self.prepare_task(test): + self.run_task(test) def _generate_report(self): """Generate test execution summary report.""" @@ -445,15 +574,17 @@ class RallyBase(singlevm.VmReady1): success_avg = 100 * item['nb_success'] / item['nb_tests'] except ZeroDivisionError: success_avg = 0 - success_str = str("{:0.2f}".format(success_avg)) + '%' - duration_str = time.strftime("%M:%S", + success_str = f"{success_avg:0.2f}%" + duration_str = time.strftime("%H:%M:%S", time.gmtime(item['overall_duration'])) res_table.add_row([item['test_name'], duration_str, item['nb_tests'], success_str]) payload.append({'module': item['test_name'], 'details': {'duration': item['overall_duration'], 'nb tests': item['nb_tests'], - 'success': success_str}}) + 'success rate': success_str, + 'success': item['success'], + 'failures': item['failures']}}) total_duration_str = time.strftime("%H:%M:%S", time.gmtime(total_duration)) @@ -461,7 +592,7 @@ class RallyBase(singlevm.VmReady1): self.result = 100 * total_nb_success / total_nb_tests except ZeroDivisionError: self.result = 100 - success_rate = "{:0.2f}".format(self.result) + success_rate = f"{self.result:0.2f}" success_rate_str = str(success_rate) + '%' res_table.add_row(["", "", "", ""]) res_table.add_row(["TOTAL:", total_duration_str, total_nb_tests, @@ -471,16 +602,52 @@ class RallyBase(singlevm.VmReady1): LOGGER.info("Rally '%s' success_rate is %s%% in %s/%s modules", self.case_name, success_rate, nb_modules, len(self.summary)) - payload.append({'summary': {'duration': total_duration, - 'nb tests': total_nb_tests, - 'nb success': success_rate}}) - self.details = payload + self.details['summary'] = {'duration': total_duration, + 'nb tests': total_nb_tests, + 'nb success': success_rate} + self.details["modules"] = payload + + @staticmethod + def export_task(file_name, export_type="html"): + """Export all task results (e.g. html or xunit report) + + Raises: + subprocess.CalledProcessError: if Rally doesn't return 0 + + Returns: + None + """ + cmd = ["rally", "task", "export", "--type", export_type, + "--deployment", + str(getattr(config.CONF, 'rally_deployment_name')), + "--to", file_name] + LOGGER.debug('running command: %s', cmd) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + + @staticmethod + def verify_report(file_name, uuid, export_type="html"): + """Generate the verifier report (e.g. html or xunit report) + + Raises: + subprocess.CalledProcessError: if Rally doesn't return 0 + + Returns: + None + """ + cmd = ["rally", "verify", "report", "--type", export_type, + "--uuid", uuid, "--to", file_name] + LOGGER.debug('running command: %s', cmd) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) def clean(self): """Cleanup of OpenStack resources. Should be called on completion.""" - super(RallyBase, self).clean() + self.clean_rally_conf() + self.clean_rally_logs() if self.flavor_alt: self.orig_cloud.delete_flavor(self.flavor_alt.id) + super().clean() def is_successful(self): """The overall result of the test.""" @@ -488,22 +655,57 @@ class RallyBase(singlevm.VmReady1): if item['task_status'] is False: return testcase.TestCase.EX_TESTCASE_FAILED - return super(RallyBase, self).is_successful() + return super().is_successful() + + @staticmethod + def update_rally_logs(res_dir, rally_conf='/etc/rally/rally.conf'): + """Print rally logs in res dir""" + if not os.path.exists(res_dir): + os.makedirs(res_dir) + rconfig = configparser.RawConfigParser() + rconfig.read(rally_conf) + rconfig.set('DEFAULT', 'debug', True) + rconfig.set('DEFAULT', 'use_stderr', False) + rconfig.set('DEFAULT', 'log-file', 'rally.log') + rconfig.set('DEFAULT', 'log_dir', res_dir) + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + @staticmethod + def clean_rally_logs(rally_conf='/etc/rally/rally.conf'): + """Clean Rally config""" + rconfig = configparser.RawConfigParser() + rconfig.read(rally_conf) + if rconfig.has_option('DEFAULT', 'use_stderr'): + rconfig.remove_option('DEFAULT', 'use_stderr') + if rconfig.has_option('DEFAULT', 'debug'): + rconfig.remove_option('DEFAULT', 'debug') + if rconfig.has_option('DEFAULT', 'log-file'): + rconfig.remove_option('DEFAULT', 'log-file') + if rconfig.has_option('DEFAULT', 'log_dir'): + rconfig.remove_option('DEFAULT', 'log_dir') + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) - @energy.enable_recording def run(self, **kwargs): """Run testcase.""" self.start_time = time.time() try: - assert super(RallyBase, self).run( + assert super().run( **kwargs) == testcase.TestCase.EX_OK - conf_utils.create_rally_deployment() - self._prepare_env() - self._run_tests() + self.update_rally_logs(self.res_dir) + self.create_rally_deployment(environ=self.project.get_environ()) + self.prepare_run(**kwargs) + self.run_tests(**kwargs) self._generate_report() + self.export_task( + f"{self.results_dir}/{self.case_name}.html") + self.export_task( + f"{self.results_dir}/{self.case_name}.xml", + export_type="junit-xml") res = testcase.TestCase.EX_OK - except Exception as exc: # pylint: disable=broad-except - LOGGER.error('Error with run: %s', exc) + except Exception: # pylint: disable=broad-except + LOGGER.exception('Error with run:') self.result = 0 res = testcase.TestCase.EX_RUN_ERROR self.stop_time = time.time() @@ -517,22 +719,120 @@ class RallySanity(RallyBase): """Initialize RallySanity object.""" if "case_name" not in kwargs: kwargs["case_name"] = "rally_sanity" - super(RallySanity, self).__init__(**kwargs) - self.mode = 'sanity' - self.test_name = 'all' + super().__init__(**kwargs) self.smoke = True - self.scenario_dir = os.path.join(self.RALLY_SCENARIO_DIR, 'sanity') + self.scenario_dir = os.path.join(self.rally_scenario_dir, 'sanity') class RallyFull(RallyBase): """Rally full testcase implementation.""" + task_timeout = 7200 + def __init__(self, **kwargs): """Initialize RallyFull object.""" if "case_name" not in kwargs: kwargs["case_name"] = "rally_full" - super(RallyFull, self).__init__(**kwargs) - self.mode = 'full' - self.test_name = 'all' + super().__init__(**kwargs) self.smoke = False - self.scenario_dir = os.path.join(self.RALLY_SCENARIO_DIR, 'full') + self.scenario_dir = os.path.join(self.rally_scenario_dir, 'full') + + +class RallyJobs(RallyBase): + """Rally OpenStack CI testcase implementation.""" + + stests = ["neutron"] + task_timeout = 7200 + + def __init__(self, **kwargs): + """Initialize RallyJobs object.""" + if "case_name" not in kwargs: + kwargs["case_name"] = "rally_jobs" + super().__init__(**kwargs) + self.task_file = os.path.join(self.rally_dir, 'rally_jobs.yaml') + self.task_yaml = None + + def prepare_run(self, **kwargs): + """Create resources needed by test scenarios.""" + super().prepare_run(**kwargs) + with open( + os.path.join(self.rally_dir, 'rally_jobs.yaml'), + 'r', encoding='utf-8') as task_file: + self.task_yaml = yaml.safe_load(task_file) + + for task in self.task_yaml: + if task not in self.tests: + raise Exception(f"Test '{task}' not in '{self.tests}'") + + def apply_blacklist(self, case_file_name, result_file_name): + # pylint: disable=too-many-branches + """Apply blacklist.""" + LOGGER.debug("Applying blacklist...") + black_tests = list(set(self.excl_func() + + self.excl_scenario())) + if black_tests: + LOGGER.debug("Blacklisted tests: %s", str(black_tests)) + + template = YAML(typ='jinja2') + with open(case_file_name, 'r', encoding='utf-8') as fname: + cases = template.load(fname) + if cases.get("version", 1) == 1: + # scenarios in dictionary + for name in cases.keys(): + if self.in_iterable_re(name, black_tests): + cases.pop(name) + else: + # workloads in subtasks + for sind, subtask in reversed(list( + enumerate(cases.get('subtasks', [])))): + for wind, workload in reversed(list( + enumerate(subtask.get('workloads', [])))): + scenario = workload.get('scenario', {}) + for name in scenario.keys(): + if self.in_iterable_re(name, black_tests): + cases['subtasks'][sind]['workloads'].pop(wind) + break + if 'workloads' in cases['subtasks'][sind]: + if not cases['subtasks'][sind]['workloads']: + cases['subtasks'].pop(sind) + # scenarios in subtasks + for sind, subtask in reversed(list( + enumerate(cases.get('subtasks', [])))): + scenario = subtask.get('scenario', {}) + for name in scenario.keys(): + if self.in_iterable_re(name, black_tests): + cases['subtasks'].pop(sind) + break + + with open(result_file_name, 'w', encoding='utf-8') as fname: + template.dump(cases, fname) + + def build_task_args(self, test_name): + """Build arguments for the Rally task.""" + task_args = {} + if self.ext_net: + task_args['floating_network'] = str(self.ext_net.name) + else: + task_args['floating_network'] = '' + task_args['image_name'] = str(self.image.name) + task_args['flavor_name'] = str(self.flavor.name) + return task_args + + def prepare_task(self, test_name): + """Prepare resources for test run.""" + jobs_dir = os.path.join( + getattr(config.CONF, 'dir_rally_data'), test_name, 'rally-jobs') + task_name = self.task_yaml.get(test_name).get("task") + task = os.path.join(jobs_dir, task_name) + if not os.path.exists(task): + raise Exception(f"The scenario '{task}' does not exist.") + LOGGER.debug('Scenario fetched from : %s', task) + + if not os.path.exists(self.temp_dir): + os.makedirs(self.temp_dir) + task_file_name = os.path.join(self.temp_dir, task_name) + self.apply_blacklist(task, task_file_name) + self.run_cmd = (["rally", "task", "start", "--tag", test_name, + "--task", task_file_name, + "--task-args", str(self.build_task_args(test_name))]) + return True diff --git a/functest/opnfv_tests/openstack/rally/rally_jobs.yaml b/functest/opnfv_tests/openstack/rally/rally_jobs.yaml new file mode 100644 index 000000000..2092fb4cf --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/rally_jobs.yaml @@ -0,0 +1,3 @@ +--- +neutron: + task: task-neutron.yaml diff --git a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-cinder.yaml b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-cinder.yaml index f36d0b2d9..7abeeac68 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-cinder.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-cinder.yaml @@ -8,6 +8,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -23,6 +25,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {{ volumes() }} {% endcall %} runner: @@ -39,6 +43,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -52,6 +58,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -70,6 +78,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -91,6 +101,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -103,7 +115,7 @@ {{ vm_params(image_name,flavor_name) }} size: min: 1 - max: 5 + max: 1 create_vm_params: nics: - net-id: {{ netid }} @@ -111,6 +123,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -123,17 +137,21 @@ size: 1 context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} - args: size: min: 1 - max: 5 + max: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -148,6 +166,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} volumes: size: 1 volumes_per_tenant: 4 @@ -165,6 +185,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {{ volumes() }} {% endcall %} runner: @@ -182,6 +204,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -194,6 +218,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -206,6 +232,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -221,6 +249,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -235,6 +265,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {{ volumes() }} {% endcall %} runner: @@ -250,6 +282,8 @@ read_iops_sec: "1000" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -263,6 +297,8 @@ read_iops_sec: "1000" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -279,6 +315,8 @@ set_read_iops_sec: "1001" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -290,6 +328,8 @@ description: "rally tests creating types" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -301,18 +341,8 @@ description: "rally tests creating types" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} - runner: - {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} - sla: - {{ no_failures_sla() }} - - CinderVolumeTypes.create_and_update_volume_type: - - - args: - description: "test" - update_description: "test update" - context: - {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -328,17 +358,8 @@ control_location: "front-end" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} - runner: - {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} - sla: - {{ no_failures_sla() }} - - CinderVolumeTypes.create_volume_type_add_and_list_type_access: - - - args: - description: "rally tests creating types" - context: - {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: diff --git a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-glance.yaml b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-glance.yaml index dfc1fc156..993b83ff7 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-glance.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-glance.yaml @@ -36,8 +36,9 @@ flavor: name: {{ flavor_name }} number_instances: 2 - nics: - - net-id: {{ netid }} + boot_server_kwargs: + nics: + - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} quotas: diff --git a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-neutron.yaml b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-neutron.yaml index 2951e953a..b2248d499 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-neutron.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-neutron.yaml @@ -27,7 +27,8 @@ ports_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -50,7 +51,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -74,7 +74,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -108,7 +107,8 @@ ports_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -129,7 +129,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -151,7 +150,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -185,7 +183,8 @@ ports_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -206,7 +205,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -227,7 +225,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -315,7 +312,8 @@ ports_per_network: 2 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -332,7 +330,6 @@ subnets_per_network: 2 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -350,7 +347,8 @@ subnets_per_network: 2 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 diff --git a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-nova.yaml b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-nova.yaml index 512448fd4..210591f9b 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-nova.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/full/opnfv-nova.yaml @@ -39,9 +39,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -59,9 +56,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -80,9 +74,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -104,9 +95,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -124,9 +112,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -140,11 +125,13 @@ - args: {{ vm_params(image_name, flavor_name) }} - volume_size: 10 + volume_size: 1 nics: - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -200,7 +187,7 @@ NovaServers.boot_and_live_migrate_server: - args: {{ vm_params(image_name, flavor_name) }} - block_migration: false + block_migration: {{ block_migration }} nics: - net-id: {{ netid }} context: @@ -214,13 +201,15 @@ - args: {{ vm_params(image_name, flavor_name) }} - size: 10 - block_migration: false + size: 1 + block_migration: {{ block_migration }} boot_server_kwargs: nics: - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -229,13 +218,15 @@ NovaServers.boot_server_from_volume_and_live_migrate: - args: {{ vm_params(image_name, flavor_name) }} - block_migration: false - volume_size: 10 + block_migration: {{ block_migration }} + volume_size: 1 force_delete: false nics: - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -245,14 +236,11 @@ - args: {{ vm_params(image_name, flavor_name) }} - server_kwargs: + boot_server_kwargs: nics: - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova(keypairs=true) }} @@ -266,18 +254,17 @@ - args: {{ vm_params(image_name, flavor_name) }} - volume_size: 5 + volume_size: 1 nics: - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_volumes() }} {{ unlimited_neutron() }} {{ unlimited_nova() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -293,9 +280,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -375,6 +359,40 @@ context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} network: {} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} + {% endcall %} + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + sla: + {{ no_failures_sla() }} + + NovaServers.boot_server_associate_and_dissociate_floating_ip: + - + args: + {{ vm_params(image_name, flavor_name) }} + floating_network: {{ floating_network }} + nics: + - net-id: {{ netid }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + network: {} + {% endcall %} + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + sla: + {{ no_failures_sla() }} + + NovaServers.boot_and_associate_floating_ip: + - + args: + {{ vm_params(image_name, flavor_name) }} + floating_network: {{ floating_network }} + nics: + - net-id: {{ netid }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + network: {} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} diff --git a/functest/opnfv_tests/openstack/rally/scenario/opnfv-barbican.yaml b/functest/opnfv_tests/openstack/rally/scenario/opnfv-barbican.yaml new file mode 100644 index 000000000..9dd9ca271 --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/scenario/opnfv-barbican.yaml @@ -0,0 +1,98 @@ + BarbicanContainers.create_and_add: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanContainers.create_certificate_and_delete: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanContainers.create_and_delete: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanContainers.create_rsa_and_delete: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.create_and_delete: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.create_and_get: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.create_and_list: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.create: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.get: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanContainers.list: + - + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + sla: + {{ no_failures_sla() }} + + BarbicanSecrets.list: + - + 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/scenario/opnfv-quotas.yaml b/functest/opnfv_tests/openstack/rally/scenario/opnfv-quotas.yaml index a0682acce..dcb007c50 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/opnfv-quotas.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/opnfv-quotas.yaml @@ -4,6 +4,8 @@ max_quota: 1024 context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -15,6 +17,8 @@ max_quota: 1024 context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -31,17 +35,6 @@ sla: {{ no_failures_sla() }} - Quotas.nova_update_and_delete: - - - args: - max_quota: 1024 - context: - {{ user_context(tenants_amount, users_amount, use_existing_users) }} - runner: - {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} - sla: - {{ no_failures_sla() }} - Quotas.nova_update: - args: diff --git a/functest/opnfv_tests/openstack/rally/scenario/opnfv-swift.yaml b/functest/opnfv_tests/openstack/rally/scenario/opnfv-swift.yaml new file mode 100644 index 000000000..66d7cd24d --- /dev/null +++ b/functest/opnfv_tests/openstack/rally/scenario/opnfv-swift.yaml @@ -0,0 +1,71 @@ + SwiftObjects.create_container_and_object_then_list_objects: + - + args: + objects_per_container: 2 + object_size: 5120 + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + roles: + - "admin" + sla: + {{ no_failures_sla() }} + + SwiftObjects.list_objects_in_containers: + - + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + roles: + - "admin" + swift_objects: + containers_per_tenant: 1 + objects_per_container: 10 + object_size: 1024 + sla: + {{ no_failures_sla() }} + + SwiftObjects.create_container_and_object_then_download_object: + - + args: + objects_per_container: 5 + object_size: 1024 + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + roles: + - "admin" + sla: + {{ no_failures_sla() }} + + SwiftObjects.create_container_and_object_then_delete_all: + - + args: + objects_per_container: 5 + object_size: 102400 + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + roles: + - "admin" + sla: + {{ no_failures_sla() }} + + SwiftObjects.list_and_download_objects_in_containers: + - + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} + context: + {{ user_context(tenants_amount, users_amount, use_existing_users) }} + roles: + - "admin" + swift_objects: + containers_per_tenant: 1 + objects_per_container: 5 + object_size: 10240 + sla: + {{ no_failures_sla() }} diff --git a/functest/opnfv_tests/openstack/rally/scenario/opnfv-vm.yaml b/functest/opnfv_tests/openstack/rally/scenario/opnfv-vm.yaml index 74f509925..3aa8ac8e5 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/opnfv-vm.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/opnfv-vm.yaml @@ -1,42 +1,19 @@ - VMTasks.boot_runcommand_delete: + VMTasks.dd_load_test: - args: - {{ vm_params(image_name, flavor_name) }} - floating_network: {{ floating_network }} - force_delete: false - command: - interpreter: /bin/sh - script_file: {{ sup_dir }}/instance_dd_test.sh - username: cirros + flavor: + name: {{ flavor_name }} + image: + name: {{ image_name }} nics: - net-id: {{ netid }} - context: - {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} - {% endcall %} - runner: - {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} - sla: - {{ no_failures_sla() }} - - - - args: - {{ vm_params(image_name, flavor_name) }} - fixed_network: private floating_network: {{ floating_network }} force_delete: false - command: - interpreter: /bin/sh - script_file: {{ sup_dir }}/instance_dd_test.sh - use_floatingip: true - username: cirros - nics: - - net-id: {{ netid }} - volume_args: - size: 2 + username: {{ username }} + runner: + {{ constant_runner(concurrency=1, times=iterations, is_smoke=smoke) }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} - runner: - {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + network: {} sla: {{ no_failures_sla() }} diff --git a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-cinder.yaml b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-cinder.yaml index 832358075..f94a5a1a4 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-cinder.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-cinder.yaml @@ -6,6 +6,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {{ volumes() }} {% endcall %} runner: @@ -23,6 +25,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -35,6 +39,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -47,6 +53,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -62,6 +70,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -76,6 +86,8 @@ {% call user_context(tenants_amount, users_amount, use_existing_users) %} quotas: {{ unlimited_volumes() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {{ volumes() }} {% endcall %} runner: @@ -91,6 +103,8 @@ read_iops_sec: "1000" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -107,6 +121,8 @@ set_read_iops_sec: "1001" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -118,6 +134,8 @@ description: "rally tests creating types" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -133,6 +151,8 @@ control_location: "front-end" context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: diff --git a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-glance.yaml b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-glance.yaml index 1b61762f9..279e81439 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-glance.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-glance.yaml @@ -36,8 +36,9 @@ flavor: name: {{ flavor_name }} number_instances: 2 - nics: - - net-id: {{ netid }} + boot_server_kwargs: + nics: + - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} quotas: diff --git a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-neutron.yaml b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-neutron.yaml index da99a48b5..3eb7652c0 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-neutron.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-neutron.yaml @@ -21,7 +21,8 @@ ports_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -42,7 +43,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -64,7 +64,8 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -98,7 +99,8 @@ ports_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} + network: + router: {} quotas: neutron: network: -1 @@ -119,7 +121,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 @@ -140,7 +141,6 @@ subnets_per_network: 1 context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: {} quotas: neutron: network: -1 diff --git a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-nova.yaml b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-nova.yaml index 801938c4e..1fbfccb5a 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-nova.yaml +++ b/functest/opnfv_tests/openstack/rally/scenario/sanity/opnfv-nova.yaml @@ -1,7 +1,7 @@ NovaServers.boot_and_live_migrate_server: - args: {{ vm_params(image_name, flavor_name) }} - block_migration: false + block_migration: {{ block_migration }} nics: - net-id: {{ netid }} context: @@ -15,13 +15,15 @@ - args: {{ vm_params(image_name, flavor_name) }} - size: 10 - block_migration: false + size: 1 + block_migration: {{ block_migration }} boot_server_kwargs: nics: - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -30,13 +32,15 @@ NovaServers.boot_server_from_volume_and_live_migrate: - args: {{ vm_params(image_name, flavor_name) }} - block_migration: false - volume_size: 10 + block_migration: {{ block_migration }} + volume_size: 1 force_delete: false nics: - net-id: {{ netid }} context: {{ user_context(tenants_amount, users_amount, use_existing_users) }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} sla: @@ -46,14 +50,11 @@ - args: {{ vm_params(image_name, flavor_name) }} - server_kwargs: + boot_server_kwargs: nics: - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova(keypairs=true) }} @@ -67,18 +68,17 @@ - args: {{ vm_params(image_name, flavor_name) }} - volume_size: 5 + volume_size: 1 nics: - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_volumes() }} {{ unlimited_neutron() }} {{ unlimited_nova() }} + api_versions: + {{ volume_service(version=volume_version, service_type=volume_service_type) }} {% endcall %} runner: {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} @@ -94,9 +94,6 @@ - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} - network: - networks_per_tenant: 1 - start_cidr: "100.1.0.0/25" quotas: {{ unlimited_neutron() }} {{ unlimited_nova() }} @@ -122,7 +119,24 @@ - args: {{ vm_params(image_name, flavor_name) }} - auto_assign_nic: true + nics: + - net-id: {{ netid }} + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + network: {} + {% endcall %} + runner: + {{ constant_runner(concurrency=concurrency, times=iterations, is_smoke=smoke) }} + sla: + {{ no_failures_sla() }} + + NovaServers.boot_server_associate_and_dissociate_floating_ip: + - + args: + {{ vm_params(image_name, flavor_name) }} + floating_network: {{ floating_network }} + nics: + - net-id: {{ netid }} context: {% call user_context(tenants_amount, users_amount, use_existing_users) %} network: {} diff --git a/functest/opnfv_tests/openstack/rally/scenario/support/instance_dd_test.sh b/functest/opnfv_tests/openstack/rally/scenario/support/instance_dd_test.sh deleted file mode 100644 index e3bf23405..000000000 --- a/functest/opnfv_tests/openstack/rally/scenario/support/instance_dd_test.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -time_seconds(){ (time -p $1 ) 2>&1 |awk '/real/{print $2}'; } -file=/tmp/test.img -c=${1:-$SIZE} -c=${c:-1000} #default is 1GB -write_seq=$(time_seconds "dd if=/dev/zero of=$file bs=1M count=$c") -read_seq=$(time_seconds "dd if=$file of=/dev/null bs=1M count=$c") -[ -f $file ] && rm $file - -echo "{ - \"write_seq_${c}m\": $write_seq, - \"read_seq_${c}m\": $read_seq - }" diff --git a/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_ports.yaml.template b/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_ports.yaml.template index 35b107838..75afb2dbe 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_ports.yaml.template +++ b/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_ports.yaml.template @@ -7,7 +7,7 @@ parameters: default: public image: type: string - default: cirros-0.4.0-x86_64-uec + default: cirros-0.6.1-x86_64-uec flavor: type: string default: m1.tiny diff --git a/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_volume.yaml.template b/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_volume.yaml.template index 5c9a86b79..9a0f1aa72 100644 --- a/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_volume.yaml.template +++ b/functest/opnfv_tests/openstack/rally/scenario/templates/server_with_volume.yaml.template @@ -4,7 +4,7 @@ parameters: # set all correct defaults for parameters before launch test image: type: string - default: cirros-0.4.0-x86_64-uec + default: cirros-0.5.1-x86_64-uec flavor: type: string default: m1.tiny diff --git a/functest/opnfv_tests/openstack/rally/task.yaml b/functest/opnfv_tests/openstack/rally/task.yaml index fe9304fc2..649c04557 100644 --- a/functest/opnfv_tests/openstack/rally/task.yaml +++ b/functest/opnfv_tests/openstack/rally/task.yaml @@ -4,7 +4,7 @@ {%- endif %} {%- from "macro/macro.yaml" import user_context, vm_params, unlimited_volumes, constant_runner, rps_runner, no_failures_sla -%} -{%- from "macro/macro.yaml" import volumes, unlimited_nova, unlimited_neutron, glance_args -%} +{%- from "macro/macro.yaml" import volumes, unlimited_nova, unlimited_neutron, glance_args, volume_service -%} --- {% if "authenticate" in service_list %} @@ -43,6 +43,14 @@ {%- include "var/opnfv-heat.yaml"-%} {% endif %} +{% if "swift" in service_list %} +{%- include "var/opnfv-swift.yaml"-%} +{% endif %} + +{% if "barbican" in service_list %} +{%- include "var/opnfv-barbican.yaml"-%} +{% endif %} + {% if "vm" in service_list %} {%- include "var/opnfv-vm.yaml"-%} {% endif %} diff --git a/functest/opnfv_tests/openstack/refstack/refstack.py b/functest/opnfv_tests/openstack/refstack/refstack.py index 8266e281d..87932020b 100644 --- a/functest/opnfv_tests/openstack/refstack/refstack.py +++ b/functest/opnfv_tests/openstack/refstack/refstack.py @@ -11,9 +11,9 @@ import logging import os -import shutil - -from refstack_client import list_parser +import re +import subprocess +import yaml from functest.opnfv_tests.openstack.tempest import tempest from functest.utils import config @@ -24,11 +24,58 @@ class Refstack(tempest.TempestCommon): __logger = logging.getLogger(__name__) - defcorelist = os.path.join( - getattr(config.CONF, 'dir_refstack_data'), 'defcore.txt') + def _extract_refstack_data(self, refstack_list): + yaml_data = "" + with open(refstack_list, encoding='utf-8') as def_file: + for line in def_file: + try: + grp = re.search(r'^([^\[]*)(\[.*\])\n*$', line) + yaml_data = f"{yaml_data}\n{grp.group(1)}: {grp.group(2)}" + except Exception: # pylint: disable=broad-except + self.__logger.warning("Cannot parse %s", line) + return yaml.full_load(yaml_data) + + def _extract_tempest_data(self): + olddir = os.getcwd() + try: + os.chdir(self.verifier_repo_dir) + cmd = ['stestr', 'list', '^tempest.'] + output = subprocess.check_output(cmd) + except subprocess.CalledProcessError as cpe: + self.__logger.error( + "Exception when listing tempest tests: %s\n%s", + cpe.cmd, cpe.output.decode("utf-8")) + raise + finally: + os.chdir(olddir) + yaml_data2 = "" + for line in output.splitlines(): + try: + grp = re.search(r'^([^\[]*)(\[.*\])\n*$', line.decode("utf-8")) + yaml_data2 = f"{yaml_data2}\n{grp.group(1)}: {grp.group(2)}" + except Exception: # pylint: disable=broad-except + self.__logger.warning("Cannot parse %s. skipping it", line) + return yaml.full_load(yaml_data2) def generate_test_list(self, **kwargs): - parser = list_parser.TestListParser( - getattr(config.CONF, 'dir_repo_tempest')) - nfile = parser.get_normalized_test_list(Refstack.defcorelist) - shutil.copyfile(nfile, self.list) + refstack_list = os.path.join( + getattr(config.CONF, 'dir_refstack_data'), + f"{kwargs.get('target', 'compute')}.txt") + self.backup_tempest_config(self.conf_file, '/etc') + refstack_data = self._extract_refstack_data(refstack_list) + tempest_data = self._extract_tempest_data() + with open(self.list, 'w', encoding='utf-8') as ref_file: + for key in refstack_data.keys(): + try: + for data in tempest_data[key]: + if data == refstack_data[key][0]: + break + else: + self.__logger.info("%s: ids differ. skipping it", key) + continue + value = str(tempest_data[key]).replace( + "'", "").replace(", ", ",") + ref_file.write(f"{key}{value}\n") + except Exception: # pylint: disable=broad-except + self.__logger.info("%s: not found. skipping it", key) + continue diff --git a/functest/opnfv_tests/openstack/shaker/shaker.py b/functest/opnfv_tests/openstack/shaker/shaker.py index ca405ae55..275cc3077 100644 --- a/functest/opnfv_tests/openstack/shaker/shaker.py +++ b/functest/opnfv_tests/openstack/shaker/shaker.py @@ -19,9 +19,11 @@ and list of tests to execute. import logging import os +import json import scp from functest.core import singlevm +from functest.utils import env class Shaker(singlevm.SingleVm2): @@ -30,16 +32,32 @@ class Shaker(singlevm.SingleVm2): __logger = logging.getLogger(__name__) - filename = '/home/opnfv/functest/images/shaker-image.qcow2' + filename = '/home/opnfv/functest/images/shaker-image-1.3.4+stretch.qcow2' flavor_ram = 512 flavor_vcpus = 1 flavor_disk = 3 - username = 'ubuntu' + username = 'debian' port = 9000 ssh_connect_loops = 12 + create_server_timeout = 300 + check_console_loop = 12 + shaker_timeout = '3600' + quota_instances = -1 + quota_cores = -1 + check_console_loop = 12 + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.role = None + + def check_requirements(self): + if self.count_hypervisors() < 2: + self.__logger.warning("Shaker requires at least 2 hypervisors") + self.is_skipped = True + self.project.clean() def prepare(self): - super(Shaker, self).prepare() + super().prepare() self.cloud.create_security_group_rule( self.sec.id, port_range_min=self.port, port_range_max=self.port, protocol='tcp', direction='ingress') @@ -51,48 +69,59 @@ class Shaker(singlevm.SingleVm2): - 1 on operation error """ assert self.ssh - keystone_id = self.orig_cloud.search_services('keystone')[0].id - self.__logger.debug("keystone id: %s", keystone_id) - endpoint = self.orig_cloud.search_endpoints( - filters={'interface': 'public', - 'service_id': keystone_id})[0].url + endpoint = self.get_public_auth_url(self.orig_cloud) self.__logger.debug("keystone endpoint: %s", endpoint) + if self.orig_cloud.get_role("admin"): + role_name = "admin" + elif self.orig_cloud.get_role("Admin"): + role_name = "Admin" + else: + raise Exception("Cannot detect neither admin nor Admin") self.orig_cloud.grant_role( - "admin", user=self.project.user.id, + role_name, user=self.project.user.id, project=self.project.project.id, domain=self.project.domain.id) + if not self.orig_cloud.get_role("heat_stack_owner"): + self.role = self.orig_cloud.create_role("heat_stack_owner") self.orig_cloud.grant_role( "heat_stack_owner", user=self.project.user.id, project=self.project.project.id, domain=self.project.domain.id) + self.orig_cloud.set_compute_quotas( + self.project.project.name, + instances=self.quota_instances, + cores=self.quota_cores) scpc = scp.SCPClient(self.ssh.get_transport()) scpc.put('/home/opnfv/functest/conf/env_file', remote_path='~/') if os.environ.get('OS_CACERT'): scpc.put(os.environ.get('OS_CACERT'), remote_path='~/os_cacert') + opt = 'export OS_CACERT=~/os_cacert && ' if os.environ.get( + 'OS_CACERT') else '' (_, stdout, stderr) = self.ssh.exec_command( 'source ~/env_file && ' 'export OS_INTERFACE=public && ' - 'export OS_AUTH_URL={} && ' - 'export OS_USERNAME={} && ' - 'export OS_PROJECT_NAME={} && ' - 'export OS_PASSWORD={} && ' - '{}' + f'export OS_AUTH_URL={endpoint} && ' + f'export OS_USERNAME={self.project.user.name} && ' + f'export OS_PROJECT_NAME={self.project.project.name} && ' + f'export OS_PROJECT_ID={self.project.project.id} && ' + 'unset OS_TENANT_NAME && ' + 'unset OS_TENANT_ID && ' + 'unset OS_ENDPOINT_TYPE && ' + f'export OS_PASSWORD="{self.project.password}" && ' + f'{opt}' 'env && ' - 'shaker --image-name {} --flavor-name {} ' - '--server-endpoint {}:9000 --scenario ' - 'openstack/full_l2,' + f'timeout {self.shaker_timeout} shaker --debug ' + f'--image-name {self.image.name} --flavor-name {self.flavor.name} ' + f'--server-endpoint {self.fip.floating_ip_address}:9000 ' + f'--external-net {self.ext_net.id} ' + f"--dns-nameservers {env.get('NAMESERVER')} " + '--scenario openstack/full_l2,' 'openstack/full_l3_east_west,' 'openstack/full_l3_north_south,' 'openstack/perf_l3_north_south ' - '--report report.html --output report.json'.format( - endpoint, self.project.user.name, self.project.project.name, - self.project.password, - 'export OS_CACERT=~/os_cacert && ' if os.environ.get( - 'OS_CACERT') else '', - self.image.name, self.flavor.name, - self.fip.floating_ip_address)) - self.__logger.info("output:\n%s", stdout.read()) - self.__logger.info("error:\n%s", stderr.read()) + '--report report.html --output report.json') + self.__logger.info("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.info("error:\n%s", stderr.read().decode("utf-8")) if not os.path.exists(self.res_dir): os.makedirs(self.res_dir) try: @@ -101,4 +130,18 @@ class Shaker(singlevm.SingleVm2): except scp.SCPException: self.__logger.exception("cannot get report files") return 1 + with open( + os.path.join(self.res_dir, 'report.json'), + encoding='utf-8') as json_file: + data = json.load(json_file) + for value in data["records"].values(): + if value["status"] != "ok": + self.__logger.error( + "%s failed\n%s", value["scenario"], value["stderr"]) + return 1 return stdout.channel.recv_exit_status() + + def clean(self): + super().clean() + if self.role: + self.orig_cloud.delete_role(self.role.id) diff --git a/functest/opnfv_tests/openstack/snaps/api_check.py b/functest/opnfv_tests/openstack/snaps/api_check.py deleted file mode 100644 index d4204bff1..000000000 --- a/functest/opnfv_tests/openstack/snaps/api_check.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Cable Television Laboratories, Inc. and others. -# -# 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 - -"""api_check test case implementation""" - -import unittest - -from functest.opnfv_tests.openstack.snaps import snaps_suite_builder -from functest.opnfv_tests.openstack.snaps.snaps_test_runner import \ - SnapsTestRunner - - -class ApiCheck(SnapsTestRunner): - """ - This test executes the Python Tests included with the SNAPS libraries - that exercise many of the OpenStack APIs within Keystone, Glance, Neutron, - and Nova - """ - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "api_check" - super(ApiCheck, self).__init__(**kwargs) - - self.suite = unittest.TestSuite() - - def run(self, **kwargs): - """ - Builds the test suite then calls super.run() - :param kwargs: the arguments to pass on - :return: - """ - snaps_suite_builder.add_openstack_client_tests( - suite=self.suite, - os_creds=self.os_creds, - ext_net_name=self.ext_net_name, - use_keystone=self.use_keystone) - snaps_suite_builder.add_openstack_api_tests( - suite=self.suite, - os_creds=self.os_creds, - ext_net_name=self.ext_net_name, - use_keystone=self.use_keystone, - image_metadata=self.image_metadata) - return super(ApiCheck, self).run() diff --git a/functest/opnfv_tests/openstack/snaps/health_check.py b/functest/opnfv_tests/openstack/snaps/health_check.py deleted file mode 100644 index 3a9c821d2..000000000 --- a/functest/opnfv_tests/openstack/snaps/health_check.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Cable Television Laboratories, Inc. and others. -# -# 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 - -"""snaps_health_check test case implementation""" - -import unittest - -from snaps.openstack.tests.create_instance_tests import SimpleHealthCheck -from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase - -from functest.opnfv_tests.openstack.snaps.snaps_test_runner import ( - SnapsTestRunner) - - -class HealthCheck(SnapsTestRunner): - """ - This test executes the SNAPS Python Test case SimpleHealthCheck which - creates a VM with a single port with an IPv4 address that is assigned by - DHCP. This test then validates the expected IP with the actual - """ - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "snaps_images_cirros" - super(HealthCheck, self).__init__(**kwargs) - - self.suite = unittest.TestSuite() - - def run(self, **kwargs): - """ - Builds the test suite then calls super.run() - :param kwargs: the arguments to pass on - :return: - """ - self.suite.addTest( - OSIntegrationTestCase.parameterize( - SimpleHealthCheck, os_creds=self.os_creds, - ext_net_name=self.ext_net_name, - use_keystone=self.use_keystone, - flavor_metadata=self.flavor_metadata, - image_metadata=self.image_metadata, - netconf_override=self.netconf_override)) - return super(HealthCheck, self).run() diff --git a/functest/opnfv_tests/openstack/snaps/smoke.py b/functest/opnfv_tests/openstack/snaps/smoke.py deleted file mode 100644 index bc6781180..000000000 --- a/functest/opnfv_tests/openstack/snaps/smoke.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Cable Television Laboratories, Inc. and others. -# -# 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 - -"""snaps_smoke test case implementation""" - -import unittest - -from functest.opnfv_tests.openstack.snaps import snaps_suite_builder -from functest.opnfv_tests.openstack.snaps.snaps_test_runner import ( - SnapsTestRunner) - - -class SnapsSmoke(SnapsTestRunner): - """ - This test executes the Python Tests included with the SNAPS libraries - that exercise many of the OpenStack APIs within Keystone, Glance, Neutron, - and Nova - """ - def __init__(self, **kwargs): - if "case_name" not in kwargs: - kwargs["case_name"] = "snaps_smoke" - super(SnapsSmoke, self).__init__(**kwargs) - - self.suite = unittest.TestSuite() - - def run(self, **kwargs): - """ - Builds the test suite then calls super.run() - :param kwargs: the arguments to pass on - :return: - """ - snaps_suite_builder.add_openstack_integration_tests( - suite=self.suite, - os_creds=self.os_creds, - ext_net_name=self.ext_net_name, - use_keystone=self.use_keystone, - flavor_metadata=self.flavor_metadata, - image_metadata=self.image_metadata, - use_floating_ips=self.use_fip, - netconf_override=self.netconf_override) - return super(SnapsSmoke, self).run() diff --git a/functest/opnfv_tests/openstack/snaps/snaps_suite_builder.py b/functest/opnfv_tests/openstack/snaps/snaps_suite_builder.py deleted file mode 100644 index 991a63dbb..000000000 --- a/functest/opnfv_tests/openstack/snaps/snaps_suite_builder.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Cable Television Laboratories, Inc. and others. -# -# 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 - -""" -Snaps test suite including openstack client tests, api tests and -integration tests. -add_openstack_client_tests: for connection_check -add_openstack_api_tests: for api_check -add_openstack_integration_tests: for snaps_smoke -""" - -import logging - -from snaps.openstack.tests.create_flavor_tests import ( - CreateFlavorTests) -from snaps.openstack.tests.create_image_tests import ( - CreateImageSuccessTests, CreateImageNegativeTests, - CreateMultiPartImageTests) -from snaps.openstack.tests.create_instance_tests import ( - CreateInstanceSingleNetworkTests, CreateInstanceOnComputeHost, - CreateInstanceSimpleTests, InstanceSecurityGroupTests, - CreateInstancePortManipulationTests, SimpleHealthCheck, - CreateInstanceFromThreePartImage, CreateInstanceTwoNetTests, - CreateInstanceVolumeTests) -from snaps.openstack.tests.create_keypairs_tests import ( - CreateKeypairsTests, CreateKeypairsCleanupTests) -from snaps.openstack.tests.create_network_tests import ( - CreateNetworkSuccessTests) -from snaps.openstack.tests.create_project_tests import ( - CreateProjectSuccessTests, CreateProjectUserTests) -from snaps.openstack.tests.create_qos_tests import ( - CreateQoSTests) -# from snaps.openstack.tests.create_router_tests import -# CreateRouterSuccessTests -from snaps.openstack.tests.create_router_tests import CreateRouterNegativeTests -from snaps.openstack.tests.create_security_group_tests import ( - CreateSecurityGroupTests) -from snaps.openstack.tests.create_stack_tests import ( - CreateStackSuccessTests, CreateStackNegativeTests, - CreateStackFlavorTests, - CreateStackKeypairTests, CreateStackVolumeTests, - CreateStackSecurityGroupTests) -from snaps.openstack.tests.create_user_tests import ( - CreateUserSuccessTests) -from snaps.openstack.tests.create_volume_tests import ( - CreateSimpleVolumeSuccessTests, - CreateVolumeWithTypeTests, CreateVolumeWithImageTests, - CreateSimpleVolumeFailureTests) -from snaps.openstack.tests.create_volume_type_tests import ( - CreateSimpleVolumeTypeSuccessTests, - CreateVolumeTypeComplexTests) -from snaps.openstack.tests.os_source_file_test import ( - OSComponentTestCase, OSIntegrationTestCase) -from snaps.openstack.utils.tests.cinder_utils_tests import ( - CinderSmokeTests, CinderUtilsQoSTests, CinderUtilsSimpleVolumeTypeTests, - CinderUtilsAddEncryptionTests, CinderUtilsVolumeTypeCompleteTests, - CinderUtilsVolumeTests) -from snaps.openstack.utils.tests.glance_utils_tests import ( - GlanceSmokeTests, GlanceUtilsTests) -from snaps.openstack.utils.tests.heat_utils_tests import ( - HeatSmokeTests, HeatUtilsCreateSimpleStackTests, - HeatUtilsFlavorTests, - HeatUtilsKeypairTests, HeatUtilsSecurityGroupTests) -from snaps.openstack.utils.tests.keystone_utils_tests import ( - KeystoneSmokeTests, KeystoneUtilsTests) -from snaps.openstack.utils.tests.neutron_utils_tests import ( - NeutronSmokeTests, NeutronUtilsNetworkTests, NeutronUtilsSubnetTests, - NeutronUtilsRouterTests, NeutronUtilsSecurityGroupTests, - NeutronUtilsFloatingIpTests) -from snaps.openstack.utils.tests.nova_utils_tests import ( - NovaSmokeTests, NovaUtilsKeypairTests, NovaUtilsFlavorTests, - NovaUtilsInstanceTests, NovaUtilsInstanceVolumeTests) -from snaps.provisioning.tests.ansible_utils_tests import ( - AnsibleProvisioningTests) - - -def add_openstack_client_tests(suite, os_creds, ext_net_name, - use_keystone=True, log_level=logging.INFO): - """ - Adds tests written to exercise OpenStack client retrieval - - :param suite: the unittest.TestSuite object to which to add the tests - :param os_creds: and instance of OSCreds that holds the credentials - required by OpenStack - :param ext_net_name: the name of an external network on the cloud under - test - :param use_keystone: when True, tests requiring direct access to Keystone - are added as these need to be running on a host that - has access to the cloud's private network - :param log_level: the logging level - :return: None as the tests will be adding to the 'suite' parameter object - """ - # Basic connection tests - suite.addTest( - OSComponentTestCase.parameterize( - GlanceSmokeTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - - if use_keystone: - suite.addTest( - OSComponentTestCase.parameterize( - KeystoneSmokeTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - - suite.addTest( - OSComponentTestCase.parameterize( - NeutronSmokeTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest( - OSComponentTestCase.parameterize( - NovaSmokeTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest( - OSComponentTestCase.parameterize( - HeatSmokeTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest( - OSComponentTestCase.parameterize( - CinderSmokeTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - - -def add_openstack_api_tests(suite, os_creds, ext_net_name, use_keystone=True, - image_metadata=None, log_level=logging.INFO): - # pylint: disable=too-many-arguments - """ - Adds tests written to exercise all existing OpenStack APIs - - :param suite: the unittest.TestSuite object to which to add the tests - :param os_creds: Instance of OSCreds that holds the credentials - required by OpenStack - :param ext_net_name: the name of an external network on the cloud under - test - :param use_keystone: when True, tests requiring direct access to Keystone - are added as these need to be running on a host that - has access to the cloud's private network - :param image_metadata: dict() object containing metadata for creating an - image with custom config - (see YAML files in examples/image-metadata) - :param log_level: the logging level - :return: None as the tests will be adding to the 'suite' parameter object - """ - # Tests the OpenStack API calls - if use_keystone: - suite.addTest(OSComponentTestCase.parameterize( - KeystoneUtilsTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - CreateUserSuccessTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - CreateProjectSuccessTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - CreateProjectUserTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - - suite.addTest(OSComponentTestCase.parameterize( - GlanceUtilsTests, os_creds=os_creds, ext_net_name=ext_net_name, - image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NeutronUtilsNetworkTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NeutronUtilsSubnetTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NeutronUtilsRouterTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NeutronUtilsSecurityGroupTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NeutronUtilsFloatingIpTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NovaUtilsKeypairTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NovaUtilsFlavorTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - NovaUtilsInstanceTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level, image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - NovaUtilsInstanceVolumeTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CreateFlavorTests, os_creds=os_creds, ext_net_name=ext_net_name, - log_level=log_level)) - suite.addTest(OSComponentTestCase.parameterize( - HeatUtilsCreateSimpleStackTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - # https://gerrit.opnfv.org/gerrit/#/c/59801/ - # suite.addTest(OSComponentTestCase.parameterize( - # HeatUtilsCreateComplexStackTests, os_creds=os_creds, - # ext_net_name=ext_net_name, log_level=log_level, - # image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - HeatUtilsFlavorTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - HeatUtilsKeypairTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - HeatUtilsSecurityGroupTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CinderUtilsQoSTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CinderUtilsVolumeTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CinderUtilsSimpleVolumeTypeTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CinderUtilsAddEncryptionTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - suite.addTest(OSComponentTestCase.parameterize( - CinderUtilsVolumeTypeCompleteTests, os_creds=os_creds, - ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) - - -def add_openstack_integration_tests(suite, os_creds, ext_net_name, - use_keystone=True, flavor_metadata=None, - image_metadata=None, use_floating_ips=True, - netconf_override=None, - log_level=logging.INFO): - # pylint: disable=too-many-arguments - """ - Adds tests written to exercise all long-running OpenStack integration tests - meaning they will be creating VM instances and potentially performing some - SSH functions through floatingIPs - - :param suite: the unittest.TestSuite object to which to add the tests - :param os_creds: and instance of OSCreds that holds the credentials - required by OpenStack - :param ext_net_name: the name of an external network on the cloud under - test - :param use_keystone: when True, tests requiring direct access to Keystone - are added as these need to be running on a host that - has access to the cloud's private network - :param image_metadata: dict() object containing metadata for creating an - image with custom config - (see YAML files in examples/image-metadata) - :param flavor_metadata: dict() object containing the metadata required by - your flavor based on your configuration: - (i.e. {'hw:mem_page_size': 'large'}) - :param use_floating_ips: when true, all tests requiring Floating IPs will - be added to the suite - :param netconf_override: dict() containing the reconfigured network_type, - physical_network and segmentation_id - :param log_level: the logging level - :return: None as the tests will be adding to the 'suite' parameter object - """ - # Tests the OpenStack API calls via a creator. If use_keystone, objects - # will be created with a custom user and project - - # Creator Object tests - suite.addTest(OSIntegrationTestCase.parameterize( - CreateSecurityGroupTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateImageSuccessTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateImageNegativeTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateMultiPartImageTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateKeypairsTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateKeypairsCleanupTests, os_creds=os_creds, - ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateNetworkSuccessTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - # https://jira.opnfv.org/browse/SNAPS-320 - # suite.addTest(OSIntegrationTestCase.parameterize( - # CreateRouterSuccessTests, os_creds=os_creds, - # ext_net_name=ext_net_name, - # use_keystone=use_keystone, - # flavor_metadata=flavor_metadata, image_metadata=image_metadata, - # log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateRouterNegativeTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateQoSTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateSimpleVolumeTypeSuccessTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateVolumeTypeComplexTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateSimpleVolumeSuccessTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateSimpleVolumeFailureTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateVolumeWithTypeTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateVolumeWithImageTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - - # VM Instances - suite.addTest(OSIntegrationTestCase.parameterize( - SimpleHealthCheck, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceTwoNetTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceSimpleTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstancePortManipulationTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - InstanceSecurityGroupTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceOnComputeHost, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceFromThreePartImage, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceVolumeTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - netconf_override=netconf_override, log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackSuccessTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackVolumeTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackFlavorTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackKeypairTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackSecurityGroupTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - CreateStackNegativeTests, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - - if use_floating_ips: - suite.addTest(OSIntegrationTestCase.parameterize( - CreateInstanceSingleNetworkTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) - # https://gerrit.opnfv.org/gerrit/#/c/59801/ - # suite.addTest(OSIntegrationTestCase.parameterize( - # CreateStackFloatingIpTests, os_creds=os_creds, - # ext_net_name=ext_net_name, use_keystone=use_keystone, - # flavor_metadata=flavor_metadata, image_metadata=image_metadata, - # log_level=log_level)) - suite.addTest(OSIntegrationTestCase.parameterize( - AnsibleProvisioningTests, os_creds=os_creds, - ext_net_name=ext_net_name, use_keystone=use_keystone, - flavor_metadata=flavor_metadata, image_metadata=image_metadata, - log_level=log_level)) diff --git a/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py b/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py deleted file mode 100644 index 70327ff89..000000000 --- a/functest/opnfv_tests/openstack/snaps/snaps_test_runner.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Cable Television Laboratories, Inc. and others. -# -# 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 - -"""configuration params to run snaps tests""" - -import logging - -from xtesting.core import unit - -from functest.opnfv_tests.openstack.snaps import snaps_utils -from functest.utils import config - - -class SnapsTestRunner(unit.Suite): - # pylint: disable=too-many-instance-attributes - """ - This test executes the SNAPS Python Tests - """ - - def __init__(self, **kwargs): - super(SnapsTestRunner, self).__init__(**kwargs) - self.logger = logging.getLogger(__name__) - self.os_creds = kwargs.get('os_creds') or snaps_utils.get_credentials() - - if 'ext_net_name' in kwargs: - self.ext_net_name = kwargs['ext_net_name'] - else: - self.ext_net_name = snaps_utils.get_ext_net_name(self.os_creds) - - self.netconf_override = None - if hasattr(config.CONF, 'snaps_network_config'): - self.netconf_override = getattr( - config.CONF, 'snaps_network_config') - - self.use_fip = ( - getattr(config.CONF, 'snaps_use_floating_ips') == 'True') - self.use_keystone = ( - getattr(config.CONF, 'snaps_use_keystone') == 'True') - - self.flavor_metadata = getattr(config.CONF, 'snaps_flavor_extra_specs', - None) - self.logger.info("Using flavor metadata '%s'", self.flavor_metadata) - - self.image_metadata = None - if hasattr(config.CONF, 'snaps_images'): - self.image_metadata = getattr(config.CONF, 'snaps_images') diff --git a/functest/opnfv_tests/openstack/snaps/snaps_utils.py b/functest/opnfv_tests/openstack/snaps/snaps_utils.py deleted file mode 100644 index 21d1dd60d..000000000 --- a/functest/opnfv_tests/openstack/snaps/snaps_utils.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2015 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 - -"""Some common utils wrapping snaps functions""" - -from snaps.openstack.tests import openstack_tests -from snaps.openstack.utils import neutron_utils -from snaps.openstack.utils import nova_utils - -from functest.utils import config -from functest.utils import constants -from functest.utils import env - - -def get_ext_net_name(os_creds): - """ - Returns the configured external network name or - the first retrieved external network name - :param: os_creds: an instance of snaps OSCreds object - :return: - """ - neutron = neutron_utils.neutron_client(os_creds) - ext_nets = neutron_utils.get_external_networks(neutron) - if env.get('EXTERNAL_NETWORK'): - extnet_config = env.get('EXTERNAL_NETWORK') - for ext_net in ext_nets: - if ext_net.name == extnet_config: - return extnet_config - return ext_nets[0].name if ext_nets else "" - - -def get_active_compute_cnt(os_creds): - """ - Returns the number of active compute servers - :param: os_creds: an instance of snaps OSCreds object - :return: the number of active compute servers - """ - nova = nova_utils.nova_client(os_creds) - computes = nova_utils.get_availability_zone_hosts(nova, zone_name='nova') - return len(computes) - - -def get_credentials(proxy_settings_str=None, ssh_proxy_cmd=None): - """ - Returns snaps OSCreds object instance - :param: proxy_settings_str: proxy settings string <host>:<port> - :param: ssh_proxy_cmd: the SSH proxy command for the environment - :return: an instance of snaps OSCreds object - """ - creds_override = None - if hasattr(config.CONF, 'snaps_os_creds_override'): - creds_override = getattr(config.CONF, 'snaps_os_creds_override') - os_creds = openstack_tests.get_credentials( - os_env_file=constants.ENV_FILE, proxy_settings_str=proxy_settings_str, - ssh_proxy_cmd=ssh_proxy_cmd, overrides=creds_override) - return os_creds diff --git a/functest/opnfv_tests/openstack/tempest/conf_utils.py b/functest/opnfv_tests/openstack/tempest/conf_utils.py deleted file mode 100644 index 88ad3b2d9..000000000 --- a/functest/opnfv_tests/openstack/tempest/conf_utils.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2015 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 -# - -"""Tempest configuration utilities.""" - -from __future__ import print_function - -import logging -import fileinput -import os -import subprocess - -import pkg_resources -from six.moves import configparser -import yaml - -from functest.utils import config -from functest.utils import env - - -RALLY_CONF_PATH = "/etc/rally/rally.conf" -RALLY_AARCH64_PATCH_PATH = pkg_resources.resource_filename( - 'functest', 'ci/rally_aarch64_patch.conf') -GLANCE_IMAGE_PATH = os.path.join( - getattr(config.CONF, 'dir_functest_images'), - getattr(config.CONF, 'openstack_image_file_name')) -TEMPEST_CUSTOM = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/openstack/tempest/custom_tests/test_list.txt') -TEMPEST_BLACKLIST = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/openstack/tempest/custom_tests/blacklist.txt') -TEMPEST_CONF_YAML = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml') - -CI_INSTALLER_TYPE = env.get('INSTALLER_TYPE') - -""" logging configuration """ -LOGGER = logging.getLogger(__name__) - - -def create_rally_deployment(): - """Create new rally deployment""" - # set the architecture to default - pod_arch = env.get("POD_ARCH") - arch_filter = ['aarch64'] - - if pod_arch and pod_arch in arch_filter: - LOGGER.info("Apply aarch64 specific to rally config...") - with open(RALLY_AARCH64_PATCH_PATH, "r") as pfile: - rally_patch_conf = pfile.read() - - for line in fileinput.input(RALLY_CONF_PATH, inplace=1): - print(line, end=' ') - if "cirros|testvm" in line: - print(rally_patch_conf) - - LOGGER.info("Creating Rally environment...") - - try: - cmd = ['rally', 'deployment', 'destroy', - '--deployment', - str(getattr(config.CONF, 'rally_deployment_name'))] - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - except subprocess.CalledProcessError: - pass - - cmd = ['rally', 'deployment', 'create', '--fromenv', - '--name', str(getattr(config.CONF, 'rally_deployment_name'))] - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - - cmd = ['rally', 'deployment', 'check'] - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - - -def create_verifier(): - """Create new verifier""" - LOGGER.info("Create verifier from existing repo...") - cmd = ['rally', 'verify', 'delete-verifier', - '--id', str(getattr(config.CONF, 'tempest_verifier_name')), - '--force'] - try: - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - except subprocess.CalledProcessError: - pass - - cmd = ['rally', 'verify', 'create-verifier', - '--source', str(getattr(config.CONF, 'dir_repo_tempest')), - '--name', str(getattr(config.CONF, 'tempest_verifier_name')), - '--type', 'tempest', '--system-wide'] - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - - -def get_verifier_id(): - """ - Returns verifier id for current Tempest - """ - create_rally_deployment() - create_verifier() - cmd = ("rally verify list-verifiers | awk '/" + - getattr(config.CONF, 'tempest_verifier_name') + - "/ {print $2}'") - proc = subprocess.Popen(cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - deployment_uuid = proc.stdout.readline().rstrip() - if deployment_uuid == "": - LOGGER.error("Tempest verifier not found.") - raise Exception('Error with command:%s' % cmd) - return deployment_uuid - - -def get_verifier_deployment_id(): - """ - Returns deployment id for active Rally deployment - """ - cmd = ("rally deployment list | awk '/" + - getattr(config.CONF, 'rally_deployment_name') + - "/ {print $2}'") - proc = subprocess.Popen(cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - deployment_uuid = proc.stdout.readline().rstrip() - if deployment_uuid == "": - LOGGER.error("Rally deployment not found.") - raise Exception('Error with command:%s' % cmd) - return deployment_uuid - - -def get_verifier_repo_dir(verifier_id): - """ - Returns installed verifier repo directory for Tempest - """ - if not verifier_id: - verifier_id = get_verifier_id() - - return os.path.join(getattr(config.CONF, 'dir_rally_inst'), - 'verification', - 'verifier-{}'.format(verifier_id), - 'repo') - - -def get_verifier_deployment_dir(verifier_id, deployment_id): - """ - Returns Rally deployment directory for current verifier - """ - if not verifier_id: - verifier_id = get_verifier_id() - - if not deployment_id: - deployment_id = get_verifier_deployment_id() - - return os.path.join(getattr(config.CONF, 'dir_rally_inst'), - 'verification', - 'verifier-{}'.format(verifier_id), - 'for-deployment-{}'.format(deployment_id)) - - -def update_tempest_conf_file(conf_file, rconfig): - """Update defined paramters into tempest config file""" - with open(TEMPEST_CONF_YAML) as yfile: - conf_yaml = yaml.safe_load(yfile) - if conf_yaml: - sections = rconfig.sections() - for section in conf_yaml: - if section not in sections: - rconfig.add_section(section) - sub_conf = conf_yaml.get(section) - for key, value in sub_conf.items(): - rconfig.set(section, key, value) - - with open(conf_file, 'wb') as config_file: - rconfig.write(config_file) - - -def configure_tempest_update_params( - tempest_conf_file, network_name=None, image_id=None, flavor_id=None, - compute_cnt=1, image_alt_id=None, flavor_alt_id=None): - # pylint: disable=too-many-branches, too-many-arguments - """ - Add/update needed parameters into tempest.conf file - """ - LOGGER.debug("Updating selected tempest.conf parameters...") - rconfig = configparser.RawConfigParser() - rconfig.read(tempest_conf_file) - rconfig.set('compute', 'fixed_network_name', network_name) - rconfig.set('compute', 'volume_device_name', env.get('VOLUME_DEVICE_NAME')) - if image_id is not None: - rconfig.set('compute', 'image_ref', image_id) - if image_alt_id is not None: - rconfig.set('compute', 'image_ref_alt', image_alt_id) - if flavor_id is not None: - rconfig.set('compute', 'flavor_ref', flavor_id) - if flavor_alt_id is not None: - rconfig.set('compute', 'flavor_ref_alt', flavor_alt_id) - if compute_cnt > 1: - # enable multinode tests - rconfig.set('compute', 'min_compute_nodes', compute_cnt) - rconfig.set('compute-feature-enabled', 'live_migration', True) - if os.environ.get('OS_REGION_NAME'): - rconfig.set('identity', 'region', os.environ.get('OS_REGION_NAME')) - identity_api_version = os.environ.get("OS_IDENTITY_API_VERSION", '3') - if identity_api_version == '3': - auth_version = 'v3' - rconfig.set('identity-feature-enabled', 'api_v2', False) - else: - auth_version = 'v2' - rconfig.set('identity', 'auth_version', auth_version) - rconfig.set( - 'validation', 'ssh_timeout', - getattr(config.CONF, 'tempest_validation_ssh_timeout')) - rconfig.set('object-storage', 'operator_role', - getattr(config.CONF, 'tempest_object_storage_operator_role')) - - rconfig.set( - 'identity', 'v3_endpoint_type', - os.environ.get('OS_INTERFACE', 'public')) - - sections = rconfig.sections() - services_list = [ - 'compute', 'volume', 'image', 'network', 'data-processing', - 'object-storage', 'orchestration'] - for service in services_list: - if service not in sections: - rconfig.add_section(service) - rconfig.set( - service, 'endpoint_type', os.environ.get('OS_INTERFACE', 'public')) - - LOGGER.debug('Add/Update required params defined in tempest_conf.yaml ' - 'into tempest.conf file') - update_tempest_conf_file(tempest_conf_file, rconfig) - - -def configure_verifier(deployment_dir): - """ - Execute rally verify configure-verifier, which generates tempest.conf - """ - cmd = ['rally', 'verify', 'configure-verifier', '--reconfigure', - '--id', str(getattr(config.CONF, 'tempest_verifier_name'))] - output = subprocess.check_output(cmd) - LOGGER.info("%s\n%s", " ".join(cmd), output) - - LOGGER.debug("Looking for tempest.conf file...") - tempest_conf_file = os.path.join(deployment_dir, "tempest.conf") - if not os.path.isfile(tempest_conf_file): - LOGGER.error("Tempest configuration file %s NOT found.", - tempest_conf_file) - raise Exception("Tempest configuration file %s NOT found." - % tempest_conf_file) - else: - return tempest_conf_file diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.txt b/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.txt deleted file mode 100644 index bb1aed339..000000000 --- a/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.txt +++ /dev/null @@ -1,2 +0,0 @@ - -- diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.yaml b/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.yaml new file mode 100644 index 000000000..43a77fa3c --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/blacklist.yaml @@ -0,0 +1,19 @@ +--- +- + scenarios: + - os-ovn-nofeature-ha + - os-ovn-nofeature-noha + tests: + - neutron_tempest_plugin.api.admin.test_dhcp_agent_scheduler + - neutron_tempest_plugin.api.admin.test_ports.PortTestCasesResourceRequest.test_port_resource_request + - neutron_tempest_plugin.api.admin.test_ports.PortTestCasesResourceRequest.test_port_resource_request_empty + - neutron_tempest_plugin.api.admin.test_ports.PortTestCasesResourceRequest.test_port_resource_request_inherited_policy + - neutron_tempest_plugin.api.admin.test_ports.PortTestCasesResourceRequest.test_port_resource_request_no_provider_net_conflict + - neutron_tempest_plugin.api.test_ports.PortsTestJSON.test_create_update_port_with_dns_name + - patrole_tempest_plugin.tests.api.network.test_availability_zones_rbac.AvailabilityZoneExtRbacTest.test_list_availability_zone_rbac + - patrole_tempest_plugin.tests.api.network.test_agents_rbac.DHCPAgentSchedulersRbacTest.test_add_dhcp_agent_to_network + - patrole_tempest_plugin.tests.api.network.test_agents_rbac.DHCPAgentSchedulersRbacTest.test_delete_network_from_dhcp_agent + - patrole_tempest_plugin.tests.api.network.test_agents_rbac.DHCPAgentSchedulersRbacTest.test_list_networks_hosted_by_one_dhcp_agent + - patrole_tempest_plugin.tests.api.network.test_networks_rbac.NetworksRbacTest.test_create_network_provider_network_type + - patrole_tempest_plugin.tests.api.network.test_networks_rbac.NetworksRbacTest.test_create_network_provider_segmentation_id + - tempest.api.network.admin.test_dhcp_agent_scheduler diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/public_blacklist.yaml b/functest/opnfv_tests/openstack/tempest/custom_tests/public_blacklist.yaml new file mode 100644 index 000000000..e53b577b2 --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/public_blacklist.yaml @@ -0,0 +1,15 @@ +--- +- + scenarios: + - os-* + tests: + - neutron_tempest_plugin.api.admin.test_floating_ips_admin_actions.FloatingIPAdminTestJSON.test_associate_floating_ip_with_port_from_another_project + - neutron_tempest_plugin.api.admin.test_quotas.QuotasTest.test_detail_quotas + - neutron_tempest_plugin.api.admin.test_quotas.QuotasTest.test_quotas + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_floatingip_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_network_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_port_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_router_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_security_group_rule_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_security_group_when_quotas_is_full + - neutron_tempest_plugin.api.admin.test_quotas_negative.QuotasAdminNegativeTestJSON.test_create_subnet_when_quotas_is_full diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml b/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml index b47a9736a..0ee4ab613 100644 --- a/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml @@ -1,13 +1,104 @@ -# This is an empty configuration file to be filled up with the desired options -# to generate a custom tempest.conf -# Examples: -# network-feature-enabled: -# port_security: True - -# volume-feature-enabled: -# api_v1: False - -# validation: -# image_ssh_user: root -# ssh_timeout: 300 - +--- +compute: + min_microversion: '2.44' + max_microversion: latest +compute-feature-enabled: + attach_encrypted_volume: false + block_migration_for_live_migration: false + block_migrate_cinder_iscsi: false + change_password: false + cold_migration: true + config_drive: true + console_output: true + disk_config: true + enable_instance_password: true + hostname_fqdn_sanitization: true + interface_attach: true + live_migration: true + live_migrate_back_and_forth: false + metadata_service: true + pause: true + personality: false + rdp_console: false + rescue: true + resize: true + scheduler_available_filters: "AvailabilityZoneFilter,ComputeFilter,\ + ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,\ + ServerGroupAffinityFilter,SameHostFilter,DifferentHostFilter" + serial_console: false + shelve: true + snapshot: true + spice_console: false + suspend: true + swap_volume: false + vnc_console: true + volume_backed_live_migration: false + volume_multiattach: false +identity: + auth_version: v3 + user_unique_last_password_count: 2 + user_lockout_duration: 10 + user_lockout_failure_attempts: 2 +identity-feature-enabled: + trust: true + api_v2: false + api_v2_admin: false + security_compliance: true + federation: false + external_idp: false + project_tags: true + application_credentials: true + access_rules: true +image-feature-enabled: + api_v2: true + api_v1: false + import_image: false +network-feature-enabled: + port_admin_state_change: true + port_security: true +placement: + max_microversion: latest +validation: + image_ssh_user: cirros + ssh_timeout: 196 + ip_version_for_ssh: 4 + run_validation: true +volume: + max_microversion: latest + storage_protocol: ceph + manage_volume_ref: source-name,volume-%s + manage_snapshot_ref: source-name,snapshot-%s +volume-feature-enabled: + multi_backend: false + backup: true + snapshot: true + clone: true + manage_snapshot: true + manage_volume: true + extend_attached_volume: true + extend_attached_encrypted_volume: false + consistency_group: false + volume_revert: true +load_balancer: + test_with_ipv6: false +neutron_plugin_options: + agent_availability_zone: nova + available_type_drivers: flat,geneve,vlan,gre,local,vxlan + provider_vlans: public, + create_shared_resources: true +object-storage-feature-enabled: + discoverable_apis: "account_quotas,formpost,bulk_upload,bulk_delete,\ + tempurl,crossdomain,container_quotas,staticweb,account_quotas,slo" + object_versioning: true + discoverability: true + tempurl_digest_hashlib: sha1 +heat_plugin: + skip_functional_test_list: EncryptionVolTypeTest + skip_scenario_test_list: "AodhAlarmTest,SoftwareConfigIntegrationTest,\ + VolumeBackupRestoreIntegrationTest,CfnInitIntegrationTest,\ + LoadBalancerTest" + auth_version: 3 +heat_features_enabled: + multi_cloud: false +rbac: + enable_rbac: true diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf_ovn.yaml b/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf_ovn.yaml new file mode 100644 index 000000000..6b09d8e5a --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/tempest_conf_ovn.yaml @@ -0,0 +1,104 @@ +--- +compute: + min_microversion: '2.44' + max_microversion: latest +compute-feature-enabled: + attach_encrypted_volume: false + block_migration_for_live_migration: false + block_migrate_cinder_iscsi: false + change_password: false + cold_migration: true + config_drive: true + console_output: true + disk_config: true + enable_instance_password: true + hostname_fqdn_sanitization: true + interface_attach: true + live_migration: true + live_migrate_back_and_forth: false + metadata_service: true + pause: true + personality: false + rdp_console: false + rescue: true + resize: true + scheduler_available_filters: "AvailabilityZoneFilter,ComputeFilter,\ + ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,\ + ServerGroupAffinityFilter,SameHostFilter,DifferentHostFilter" + serial_console: false + shelve: true + snapshot: true + spice_console: false + suspend: true + swap_volume: false + vnc_console: true + volume_backed_live_migration: false + volume_multiattach: false +identity: + auth_version: v3 + user_unique_last_password_count: 2 + user_lockout_duration: 10 + user_lockout_failure_attempts: 2 +identity-feature-enabled: + trust: true + api_v2: false + api_v2_admin: false + security_compliance: true + federation: false + external_idp: false + project_tags: true + application_credentials: true + access_rules: true +image-feature-enabled: + api_v2: true + api_v1: false + import_image: false +network-feature-enabled: + port_admin_state_change: true + port_security: true +placement: + max_microversion: latest +validation: + image_ssh_user: cirros + ssh_timeout: 196 + ip_version_for_ssh: 4 + run_validation: true +volume: + max_microversion: latest + storage_protocol: ceph + manage_volume_ref: source-name,volume-%s + manage_snapshot_ref: source-name,snapshot-%s +volume-feature-enabled: + multi_backend: false + backup: true + snapshot: true + clone: true + manage_snapshot: true + manage_volume: true + extend_attached_volume: true + extend_attached_encrypted_volume: false + consistency_group: false + volume_revert: true +load_balancer: + test_with_ipv6: false +neutron_plugin_options: + agent_availability_zone: nova + available_type_drivers: flat,geneve,vlan,local + provider_vlans: public, + create_shared_resources: true +object-storage-feature-enabled: + discoverable_apis: "account_quotas,formpost,bulk_upload,bulk_delete,\ + tempurl,crossdomain,container_quotas,staticweb,account_quotas,slo" + object_versioning: true + discoverability: true + tempurl_digest_hashlib: sha1 +heat_plugin: + skip_functional_test_list: EncryptionVolTypeTest + skip_scenario_test_list: "AodhAlarmTest,SoftwareConfigIntegrationTest,\ + VolumeBackupRestoreIntegrationTest,CfnInitIntegrationTest,\ + LoadBalancerTest" + auth_version: 3 +heat_features_enabled: + multi_cloud: false +rbac: + enable_rbac: true diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py index 52cc31c4a..7233ffd60 100644 --- a/functest/opnfv_tests/openstack/tempest/tempest.py +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -12,6 +12,7 @@ from __future__ import division +import json import logging import os import re @@ -19,34 +20,64 @@ import shutil import subprocess import time +import pkg_resources from six.moves import configparser from xtesting.core import testcase import yaml from functest.core import singlevm -from functest.opnfv_tests.openstack.tempest import conf_utils +from functest.opnfv_tests.openstack.rally import rally from functest.utils import config from functest.utils import env +from functest.utils import functest_utils LOGGER = logging.getLogger(__name__) -class TempestCommon(singlevm.VmReady1): - # pylint: disable=too-many-instance-attributes +class TempestCommon(singlevm.VmReady2): + # pylint: disable=too-many-instance-attributes,too-many-public-methods """TempestCommon testcases implementation class.""" visibility = 'public' + filename_alt = '/home/opnfv/functest/images/cirros-0.6.1-x86_64-disk.img' + shared_network = True + tempest_conf_yaml = pkg_resources.resource_filename( + 'functest', + 'opnfv_tests/openstack/tempest/custom_tests/tempest_conf.yaml') + tempest_custom = pkg_resources.resource_filename( + 'functest', + 'opnfv_tests/openstack/tempest/custom_tests/test_list.txt') + tempest_blacklist = pkg_resources.resource_filename( + 'functest', + 'opnfv_tests/openstack/tempest/custom_tests/blacklist.yaml') + tempest_public_blacklist = pkg_resources.resource_filename( + 'functest', + 'opnfv_tests/openstack/tempest/custom_tests/public_blacklist.yaml') def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'tempest' - super(TempestCommon, self).__init__(**kwargs) - self.verifier_id = conf_utils.get_verifier_id() - self.verifier_repo_dir = conf_utils.get_verifier_repo_dir( - self.verifier_id) - self.deployment_id = conf_utils.get_verifier_deployment_id() - self.deployment_dir = conf_utils.get_verifier_deployment_dir( - self.verifier_id, self.deployment_id) + super().__init__(**kwargs) + assert self.orig_cloud + assert self.cloud + assert self.project + if self.orig_cloud.get_role("admin"): + self.role_name = "admin" + elif self.orig_cloud.get_role("Admin"): + self.role_name = "Admin" + else: + raise Exception("Cannot detect neither admin nor Admin") + self.orig_cloud.grant_role( + self.role_name, user=self.project.user.id, + project=self.project.project.id, + domain=self.project.domain.id) + self.orig_cloud.grant_role( + self.role_name, user=self.project.user.id, + domain=self.project.domain.id) + self.deployment_id = None + self.verifier_id = None + self.verifier_repo_dir = None + self.deployment_dir = None self.verification_id = None self.res_dir = os.path.join( getattr(config.CONF, 'dir_results'), self.case_name) @@ -66,6 +97,8 @@ class TempestCommon(singlevm.VmReady1): 'neutron_extensions'] except Exception: # pylint: disable=broad-except pass + self.deny_skipping = kwargs.get("deny_skipping", False) + self.tests_count = kwargs.get("tests_count", 0) def check_services(self): """Check the mandatory services.""" @@ -89,11 +122,13 @@ class TempestCommon(singlevm.VmReady1): def check_requirements(self): self.check_services() self.check_extensions() + if self.is_skipped: + self.project.clean() @staticmethod def read_file(filename): """Read file and return content as a stripped list.""" - with open(filename) as src: + with open(filename, encoding='utf-8') as src: return [line.strip() for line in src.readlines()] @staticmethod @@ -107,22 +142,22 @@ class TempestCommon(singlevm.VmReady1): } cmd = ["rally", "verify", "show", "--uuid", verif_id] LOGGER.info("Showing result for a verification: '%s'.", cmd) - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - for line in proc.stdout: - new_line = line.replace(' ', '').split('|') - if 'Tests' in new_line: - break - LOGGER.info(line) - if 'Testscount' in new_line: - result['num_tests'] = int(new_line[2]) - elif 'Success' in new_line: - result['num_success'] = int(new_line[2]) - elif 'Skipped' in new_line: - result['num_skipped'] = int(new_line[2]) - elif 'Failures' in new_line: - result['num_failures'] = int(new_line[2]) + with subprocess.Popen( + cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) as proc: + for line in proc.stdout: + LOGGER.info(line.decode("utf-8").rstrip()) + new_line = line.decode("utf-8").replace(' ', '').split('|') + if 'Tests' in new_line: + break + if 'Testscount' in new_line: + result['num_tests'] = int(new_line[2]) + elif 'Success' in new_line: + result['num_success'] = int(new_line[2]) + elif 'Skipped' in new_line: + result['num_skipped'] = int(new_line[2]) + elif 'Failures' in new_line: + result['num_failures'] = int(new_line[2]) return result @staticmethod @@ -135,64 +170,203 @@ class TempestCommon(singlevm.VmReady1): shutil.copyfile(conf_file, os.path.join(res_dir, 'tempest.conf')) + @staticmethod + def create_verifier(): + """Create new verifier""" + LOGGER.info("Create verifier from existing repo...") + cmd = ['rally', 'verify', 'delete-verifier', + '--id', str(getattr(config.CONF, 'tempest_verifier_name')), + '--force'] + try: + output = subprocess.check_output(cmd) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + except subprocess.CalledProcessError: + pass + + cmd = ['rally', 'verify', 'create-verifier', + '--source', str(getattr(config.CONF, 'dir_repo_tempest')), + '--name', str(getattr(config.CONF, 'tempest_verifier_name')), + '--type', 'tempest', '--system-wide'] + output = subprocess.check_output(cmd) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + return TempestCommon.get_verifier_id() + + @staticmethod + def get_verifier_id(): + """ + Returns verifier id for current Tempest + """ + cmd = ("rally verify list-verifiers | awk '/" + + getattr(config.CONF, 'tempest_verifier_name') + + "/ {print $2}'") + with subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) as proc: + verifier_uuid = proc.stdout.readline().rstrip() + return verifier_uuid.decode("utf-8") + + @staticmethod + def get_verifier_repo_dir(verifier_id): + """ + Returns installed verifier repo directory for Tempest + """ + return os.path.join(getattr(config.CONF, 'dir_rally_inst'), + 'verification', + f'verifier-{verifier_id}', + 'repo') + + @staticmethod + def get_verifier_deployment_dir(verifier_id, deployment_id): + """ + Returns Rally deployment directory for current verifier + """ + return os.path.join(getattr(config.CONF, 'dir_rally_inst'), + 'verification', + f'verifier-{verifier_id}', + f'for-deployment-{deployment_id}') + + @staticmethod + def update_tempest_conf_file(conf_file, rconfig): + """Update defined paramters into tempest config file""" + with open(TempestCommon.tempest_conf_yaml, encoding='utf-8') as yfile: + conf_yaml = yaml.safe_load(yfile) + if conf_yaml: + sections = rconfig.sections() + for section in conf_yaml: + if section not in sections: + rconfig.add_section(section) + sub_conf = conf_yaml.get(section) + for key, value in sub_conf.items(): + rconfig.set(section, key, value) + + with open(conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + @staticmethod + def configure_tempest_update_params( + tempest_conf_file, image_id=None, flavor_id=None, + compute_cnt=1, image_alt_id=None, flavor_alt_id=None, + admin_role_name='admin', cidr='192.168.120.0/24', + domain_id='default'): + # pylint: disable=too-many-branches,too-many-arguments + # pylint: disable=too-many-statements,too-many-locals + """ + Add/update needed parameters into tempest.conf file + """ + LOGGER.debug("Updating selected tempest.conf parameters...") + rconfig = configparser.RawConfigParser() + rconfig.read(tempest_conf_file) + rconfig.set( + 'compute', 'volume_device_name', env.get('VOLUME_DEVICE_NAME')) + if image_id is not None: + rconfig.set('compute', 'image_ref', image_id) + if image_alt_id is not None: + rconfig.set('compute', 'image_ref_alt', image_alt_id) + if flavor_id is not None: + rconfig.set('compute', 'flavor_ref', flavor_id) + if flavor_alt_id is not None: + rconfig.set('compute', 'flavor_ref_alt', flavor_alt_id) + if compute_cnt > 1: + # enable multinode tests + rconfig.set('compute', 'min_compute_nodes', compute_cnt) + rconfig.set('compute-feature-enabled', 'live_migration', True) + if os.environ.get('OS_REGION_NAME'): + rconfig.set('identity', 'region', os.environ.get('OS_REGION_NAME')) + rconfig.set('identity', 'admin_role', admin_role_name) + rconfig.set('identity', 'default_domain_id', domain_id) + if not rconfig.has_section('network'): + rconfig.add_section('network') + rconfig.set('network', 'default_network', cidr) + rconfig.set('network', 'project_network_cidr', cidr) + rconfig.set('network', 'project_networks_reachable', False) + rconfig.set( + 'identity', 'v3_endpoint_type', + os.environ.get('OS_INTERFACE', 'public')) + + sections = rconfig.sections() + services_list = [ + 'compute', 'volume', 'image', 'network', 'data-processing', + 'object-storage', 'orchestration'] + for service in services_list: + if service not in sections: + rconfig.add_section(service) + rconfig.set(service, 'endpoint_type', + os.environ.get('OS_INTERFACE', 'public')) + + LOGGER.debug('Add/Update required params defined in tempest_conf.yaml ' + 'into tempest.conf file') + TempestCommon.update_tempest_conf_file(tempest_conf_file, rconfig) + + @staticmethod + def configure_verifier(deployment_dir): + """ + Execute rally verify configure-verifier, which generates tempest.conf + """ + cmd = ['rally', 'verify', 'configure-verifier', '--reconfigure', + '--id', str(getattr(config.CONF, 'tempest_verifier_name'))] + output = subprocess.check_output(cmd) + LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8")) + + LOGGER.debug("Looking for tempest.conf file...") + tempest_conf_file = os.path.join(deployment_dir, "tempest.conf") + if not os.path.isfile(tempest_conf_file): + LOGGER.error("Tempest configuration file %s NOT found.", + tempest_conf_file) + return None + return tempest_conf_file + def generate_test_list(self, **kwargs): """Generate test list based on the test mode.""" LOGGER.debug("Generating test case list...") self.backup_tempest_config(self.conf_file, '/etc') if kwargs.get('mode') == 'custom': - if os.path.isfile(conf_utils.TEMPEST_CUSTOM): + if os.path.isfile(self.tempest_custom): shutil.copyfile( - conf_utils.TEMPEST_CUSTOM, self.list) + self.tempest_custom, self.list) else: - raise Exception("Tempest test list file %s NOT found." - % conf_utils.TEMPEST_CUSTOM) + raise Exception( + f"Tempest test list file {self.tempest_custom} NOT found.") else: testr_mode = kwargs.get( 'mode', r'^tempest\.(api|scenario).*\[.*\bsmoke\b.*\]$') - cmd = "(cd {0}; stestr list '{1}' >{2} 2>/dev/null)".format( - self.verifier_repo_dir, testr_mode, self.list) + cmd = (f"(cd {self.verifier_repo_dir}; " + f"stestr list '{testr_mode}' > {self.list} 2>/dev/null)") output = subprocess.check_output(cmd, shell=True) - LOGGER.info("%s\n%s", cmd, output) + LOGGER.info("%s\n%s", cmd, output.decode("utf-8")) os.remove('/etc/tempest.conf') - def apply_tempest_blacklist(self): + def apply_tempest_blacklist(self, black_list): """Exclude blacklisted test cases.""" LOGGER.debug("Applying tempest blacklist...") if os.path.exists(self.raw_list): os.remove(self.raw_list) os.rename(self.list, self.raw_list) cases_file = self.read_file(self.raw_list) - result_file = open(self.list, 'w') - black_tests = [] - try: - installer_type = env.get('INSTALLER_TYPE') - deploy_scenario = env.get('DEPLOY_SCENARIO') - if bool(installer_type) * bool(deploy_scenario): - # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the - # file - black_list_file = open(conf_utils.TEMPEST_BLACKLIST) - black_list_yaml = yaml.safe_load(black_list_file) - black_list_file.close() - for item in black_list_yaml: - scenarios = item['scenarios'] - installers = item['installers'] - if (deploy_scenario in scenarios and - installer_type in installers): - tests = item['tests'] - for test in tests: - black_tests.append(test) - break - except Exception: # pylint: disable=broad-except + with open(self.list, 'w', encoding='utf-8') as result_file: black_tests = [] - LOGGER.debug("Tempest blacklist file does not exist.") + try: + deploy_scenario = env.get('DEPLOY_SCENARIO') + if bool(deploy_scenario): + # if DEPLOY_SCENARIO is set we read the file + with open(black_list, encoding='utf-8') as black_list_file: + black_list_yaml = yaml.safe_load(black_list_file) + black_list_file.close() + for item in black_list_yaml: + scenarios = item['scenarios'] + in_it = rally.RallyBase.in_iterable_re + if in_it(deploy_scenario, scenarios): + tests = item['tests'] + black_tests.extend(tests) + except Exception: # pylint: disable=broad-except + black_tests = [] + LOGGER.debug("Tempest blacklist file does not exist.") - for cases_line in cases_file: - for black_tests_line in black_tests: - if black_tests_line in cases_line: - break - else: - result_file.write(str(cases_line) + '\n') - result_file.close() + for cases_line in cases_file: + for black_tests_line in black_tests: + if re.search(black_tests_line, cases_line): + break + else: + result_file.write(str(cases_line) + '\n') def run_verifier_tests(self, **kwargs): """Execute tempest test cases.""" @@ -201,34 +375,31 @@ class TempestCommon(singlevm.VmReady1): cmd.extend(kwargs.get('option', [])) LOGGER.info("Starting Tempest test suite: '%s'.", cmd) - f_stdout = open( - os.path.join(self.res_dir, "tempest.log"), 'w+') - f_stderr = open( - os.path.join(self.res_dir, - "tempest-error.log"), 'w+') - - proc = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=f_stderr, - bufsize=1) - - with proc.stdout: - for line in iter(proc.stdout.readline, b''): - if re.search(r"\} tempest\.", line): - LOGGER.info(line.replace('\n', '')) - elif re.search(r'(?=\(UUID=(.*)\))', line): - self.verification_id = re.search( - r'(?=\(UUID=(.*)\))', line).group(1) - LOGGER.info('Verification UUID: %s', self.verification_id) - f_stdout.write(line) - proc.wait() - - f_stdout.close() - f_stderr.close() + with open( + os.path.join(self.res_dir, "tempest.log"), 'w+', + encoding='utf-8') as f_stdout: + with subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + bufsize=1) as proc: + with proc.stdout: + for line in iter(proc.stdout.readline, b''): + if re.search(r"\} tempest\.", line.decode("utf-8")): + LOGGER.info(line.rstrip()) + elif re.search(r'(?=\(UUID=(.*)\))', + line.decode("utf-8")): + self.verification_id = re.search( + r'(?=\(UUID=(.*)\))', + line.decode("utf-8")).group(1) + f_stdout.write(line.decode("utf-8")) + proc.wait() if self.verification_id is None: raise Exception('Verification UUID not found') + LOGGER.info('Verification UUID: %s', self.verification_id) + + shutil.copy( + f"{self.deployment_dir}/tempest.log", + f"{self.res_dir}/tempest.debug.log") def parse_verifier_result(self): """Parse and save test results.""" @@ -245,8 +416,8 @@ class TempestCommon(singlevm.VmReady1): LOGGER.error("No test has been executed") return - with open(os.path.join(self.res_dir, - "tempest-error.log"), 'r') as logfile: + with open(os.path.join(self.res_dir, "rally.log"), + 'r', encoding='utf-8') as logfile: output = logfile.read() success_testcases = [] @@ -258,7 +429,7 @@ class TempestCommon(singlevm.VmReady1): output): failed_testcases.append(match) skipped_testcases = [] - for match in re.findall(r'.*\{\d{1,2}\} (.*?) \.{3} skip:', + for match in re.findall(r'.*\{\d{1,2}\} (.*?) \.{3} skip(?::| )', output): skipped_testcases.append(match) @@ -275,24 +446,14 @@ class TempestCommon(singlevm.VmReady1): LOGGER.info("Tempest %s success_rate is %s%%", self.case_name, self.result) - def generate_report(self): - """Generate verification report.""" - html_file = os.path.join(self.res_dir, - "tempest-report.html") - cmd = ["rally", "verify", "report", "--type", "html", "--uuid", - self.verification_id, "--to", html_file] - subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - def update_rally_regex(self, rally_conf='/etc/rally/rally.conf'): """Set image name as tempest img_name_regex""" rconfig = configparser.RawConfigParser() rconfig.read(rally_conf) - if not rconfig.has_section('tempest'): - rconfig.add_section('tempest') - rconfig.set('tempest', 'img_name_regex', '^{}$'.format( - self.image.name)) - with open(rally_conf, 'wb') as config_file: + if not rconfig.has_section('openstack'): + rconfig.add_section('openstack') + rconfig.set('openstack', 'img_name_regex', f'^{self.image.name}$') + with open(rally_conf, 'w', encoding='utf-8') as config_file: rconfig.write(config_file) def update_default_role(self, rally_conf='/etc/rally/rally.conf'): @@ -302,10 +463,131 @@ class TempestCommon(singlevm.VmReady1): return rconfig = configparser.RawConfigParser() rconfig.read(rally_conf) - if not rconfig.has_section('tempest'): - rconfig.add_section('tempest') - rconfig.set('tempest', 'swift_operator_role', '^{}$'.format(role.name)) - with open(rally_conf, 'wb') as config_file: + if not rconfig.has_section('openstack'): + rconfig.add_section('openstack') + rconfig.set('openstack', 'swift_operator_role', role.name) + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + @staticmethod + def clean_rally_conf(rally_conf='/etc/rally/rally.conf'): + """Clean Rally config""" + rconfig = configparser.RawConfigParser() + rconfig.read(rally_conf) + if rconfig.has_option('openstack', 'img_name_regex'): + rconfig.remove_option('openstack', 'img_name_regex') + if rconfig.has_option('openstack', 'swift_operator_role'): + rconfig.remove_option('openstack', 'swift_operator_role') + with open(rally_conf, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_auth_section(self): + """Update auth section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if not rconfig.has_section("auth"): + rconfig.add_section("auth") + if env.get("NEW_USER_ROLE").lower() != "member": + tempest_roles = [] + if rconfig.has_option("auth", "tempest_roles"): + tempest_roles = functest_utils.convert_ini_to_list( + rconfig.get("auth", "tempest_roles")) + rconfig.set( + 'auth', 'tempest_roles', + functest_utils.convert_list_to_ini( + [env.get("NEW_USER_ROLE")] + tempest_roles)) + if not json.loads(env.get("USE_DYNAMIC_CREDENTIALS").lower()): + rconfig.set('auth', 'use_dynamic_credentials', False) + account_file = os.path.join( + getattr(config.CONF, 'dir_functest_data'), 'accounts.yaml') + assert os.path.exists( + account_file), f"{account_file} doesn't exist" + rconfig.set('auth', 'test_accounts_file', account_file) + if env.get('NO_TENANT_NETWORK').lower() == 'true': + rconfig.set('auth', 'create_isolated_networks', False) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_network_section(self): + """Update network section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if self.ext_net: + if not rconfig.has_section('network'): + rconfig.add_section('network') + rconfig.set('network', 'public_network_id', self.ext_net.id) + rconfig.set('network', 'floating_network_name', self.ext_net.name) + rconfig.set('network-feature-enabled', 'floating_ips', True) + else: + if not rconfig.has_section('network-feature-enabled'): + rconfig.add_section('network-feature-enabled') + rconfig.set('network-feature-enabled', 'floating_ips', False) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_compute_section(self): + """Update compute section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if not rconfig.has_section('compute'): + rconfig.add_section('compute') + rconfig.set( + 'compute', 'fixed_network_name', + self.network.name if self.network else env.get("EXTERNAL_NETWORK")) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_validation_section(self): + """Update validation section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if not rconfig.has_section('validation'): + rconfig.add_section('validation') + rconfig.set( + 'validation', 'connect_method', + 'floating' if self.ext_net else 'fixed') + rconfig.set( + 'validation', 'network_for_ssh', + self.network.name if self.network else env.get("EXTERNAL_NETWORK")) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_scenario_section(self): + """Update scenario section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + filename = getattr( + config.CONF, f'{self.case_name}_image', self.filename) + if not rconfig.has_section('scenario'): + rconfig.add_section('scenario') + rconfig.set('scenario', 'img_file', filename) + rconfig.set('scenario', 'img_disk_format', getattr( + config.CONF, f'{self.case_name}_image_format', + self.image_format)) + extra_properties = self.extra_properties.copy() + if env.get('IMAGE_PROPERTIES'): + extra_properties.update( + functest_utils.convert_ini_to_dict( + env.get('IMAGE_PROPERTIES'))) + extra_properties.update( + getattr(config.CONF, f'{self.case_name}_extra_properties', {})) + rconfig.set( + 'scenario', 'img_properties', + functest_utils.convert_dict_to_ini(extra_properties)) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + + def update_dashboard_section(self): + """Update dashboard section in tempest.conf""" + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if env.get('DASHBOARD_URL'): + if not rconfig.has_section('dashboard'): + rconfig.add_section('dashboard') + rconfig.set('dashboard', 'dashboard_url', env.get('DASHBOARD_URL')) + else: + rconfig.set('service_available', 'horizon', False) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: rconfig.write(config_file) def configure(self, **kwargs): # pylint: disable=unused-argument @@ -315,36 +597,69 @@ class TempestCommon(singlevm.VmReady1): """ if not os.path.exists(self.res_dir): os.makedirs(self.res_dir) - compute_cnt = len(self.cloud.list_hypervisors()) + self.deployment_id = rally.RallyBase.create_rally_deployment( + environ=self.project.get_environ()) + if not self.deployment_id: + raise Exception("Deployment create failed") + self.verifier_id = self.create_verifier() + if not self.verifier_id: + raise Exception("Verifier create failed") + self.verifier_repo_dir = self.get_verifier_repo_dir( + self.verifier_id) + self.deployment_dir = self.get_verifier_deployment_dir( + self.verifier_id, self.deployment_id) - self.image_alt = self.publish_image( - '{}-img_alt_{}'.format(self.case_name, self.guid)) + compute_cnt = self.count_hypervisors() if self.count_hypervisors( + ) <= 10 else 10 + self.image_alt = self.publish_image_alt() self.flavor_alt = self.create_flavor_alt() LOGGER.debug("flavor: %s", self.flavor_alt) - self.conf_file = conf_utils.configure_verifier(self.deployment_dir) - conf_utils.configure_tempest_update_params( - self.conf_file, network_name=self.network.id, + self.conf_file = self.configure_verifier(self.deployment_dir) + if not self.conf_file: + raise Exception("Tempest verifier configuring failed") + self.configure_tempest_update_params( + self.conf_file, image_id=self.image.id, flavor_id=self.flavor.id, compute_cnt=compute_cnt, image_alt_id=self.image_alt.id, - flavor_alt_id=self.flavor_alt.id) + flavor_alt_id=self.flavor_alt.id, + admin_role_name=self.role_name, cidr=self.cidr, + domain_id=self.project.domain.id) + self.update_auth_section() + self.update_network_section() + self.update_compute_section() + self.update_validation_section() + self.update_scenario_section() + self.update_dashboard_section() self.backup_tempest_config(self.conf_file, self.res_dir) def run(self, **kwargs): self.start_time = time.time() try: - assert super(TempestCommon, self).run( + assert super().run( **kwargs) == testcase.TestCase.EX_OK + if not os.path.exists(self.res_dir): + os.makedirs(self.res_dir) self.update_rally_regex() self.update_default_role() + rally.RallyBase.update_rally_logs(self.res_dir) + shutil.copy("/etc/rally/rally.conf", self.res_dir) self.configure(**kwargs) self.generate_test_list(**kwargs) - self.apply_tempest_blacklist() + self.apply_tempest_blacklist(TempestCommon.tempest_blacklist) + if env.get('PUBLIC_ENDPOINT_ONLY').lower() == 'true': + self.apply_tempest_blacklist( + TempestCommon.tempest_public_blacklist) self.run_verifier_tests(**kwargs) self.parse_verifier_result() - self.generate_report() + rally.RallyBase.verify_report( + os.path.join(self.res_dir, "tempest-report.html"), + self.verification_id) + rally.RallyBase.verify_report( + os.path.join(self.res_dir, "tempest-report.xml"), + self.verification_id, "junit-xml") res = testcase.TestCase.EX_OK except Exception: # pylint: disable=broad-except LOGGER.exception('Error with run') @@ -357,8 +672,103 @@ class TempestCommon(singlevm.VmReady1): """ Cleanup all OpenStack objects. Should be called on completion. """ - super(TempestCommon, self).clean() + self.clean_rally_conf() + rally.RallyBase.clean_rally_logs() if self.image_alt: self.cloud.delete_image(self.image_alt) if self.flavor_alt: self.orig_cloud.delete_flavor(self.flavor_alt.id) + super().clean() + + def is_successful(self): + """The overall result of the test.""" + skips = self.details.get("skipped_number", 0) + if skips > 0 and self.deny_skipping: + return testcase.TestCase.EX_TESTCASE_FAILED + if self.tests_count and ( + self.details.get("tests_number", 0) != self.tests_count): + return testcase.TestCase.EX_TESTCASE_FAILED + return super().is_successful() + + +class TempestHeat(TempestCommon): + """Tempest Heat testcase implementation class.""" + + filename_alt = ('/home/opnfv/functest/images/' + 'Fedora-Cloud-Base-30-1.2.x86_64.qcow2') + flavor_alt_ram = 512 + flavor_alt_vcpus = 1 + flavor_alt_disk = 4 + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.user2 = self.orig_cloud.create_user( + name=f'{self.case_name}-user2_{self.project.guid}', + password=self.project.password, + domain_id=self.project.domain.id) + self.orig_cloud.grant_role( + self.role_name, user=self.user2.id, + project=self.project.project.id, domain=self.project.domain.id) + if not self.orig_cloud.get_role("heat_stack_owner"): + self.role = self.orig_cloud.create_role("heat_stack_owner") + self.orig_cloud.grant_role( + "heat_stack_owner", user=self.user2.id, + project=self.project.project.id, + domain=self.project.domain.id) + + def configure(self, **kwargs): + assert self.user2 + super().configure(**kwargs) + rconfig = configparser.RawConfigParser() + rconfig.read(self.conf_file) + if not rconfig.has_section('heat_plugin'): + rconfig.add_section('heat_plugin') + # It fails if region and domain ids are unset + rconfig.set( + 'heat_plugin', 'region', + os.environ.get('OS_REGION_NAME', 'RegionOne')) + rconfig.set('heat_plugin', 'auth_url', os.environ["OS_AUTH_URL"]) + rconfig.set('heat_plugin', 'project_domain_id', self.project.domain.id) + rconfig.set('heat_plugin', 'user_domain_id', self.project.domain.id) + rconfig.set( + 'heat_plugin', 'project_domain_name', self.project.domain.name) + rconfig.set( + 'heat_plugin', 'user_domain_name', self.project.domain.name) + rconfig.set('heat_plugin', 'username', self.user2.name) + rconfig.set('heat_plugin', 'password', self.project.password) + rconfig.set('heat_plugin', 'project_name', self.project.project.name) + rconfig.set('heat_plugin', 'admin_username', self.project.user.name) + rconfig.set('heat_plugin', 'admin_password', self.project.password) + rconfig.set( + 'heat_plugin', 'admin_project_name', self.project.project.name) + rconfig.set('heat_plugin', 'image_ref', self.image_alt.id) + rconfig.set('heat_plugin', 'instance_type', self.flavor_alt.id) + rconfig.set('heat_plugin', 'minimal_image_ref', self.image.id) + rconfig.set('heat_plugin', 'minimal_instance_type', self.flavor.id) + if self.ext_net: + rconfig.set( + 'heat_plugin', 'floating_network_name', self.ext_net.name) + if self.network: + rconfig.set('heat_plugin', 'fixed_network_name', self.network.name) + rconfig.set('heat_plugin', 'fixed_subnet_name', self.subnet.name) + rconfig.set('heat_plugin', 'network_for_ssh', self.network.name) + else: + LOGGER.warning( + 'No tenant network created. ' + 'Trying EXTERNAL_NETWORK as a fallback') + rconfig.set( + 'heat_plugin', 'fixed_network_name', + env.get("EXTERNAL_NETWORK")) + rconfig.set( + 'heat_plugin', 'network_for_ssh', env.get("EXTERNAL_NETWORK")) + with open(self.conf_file, 'w', encoding='utf-8') as config_file: + rconfig.write(config_file) + self.backup_tempest_config(self.conf_file, self.res_dir) + + def clean(self): + """ + Cleanup all OpenStack objects. Should be called on completion. + """ + super().clean() + if self.user2: + self.orig_cloud.delete_user(self.user2.id) diff --git a/functest/opnfv_tests/openstack/vmtp/vmtp.py b/functest/opnfv_tests/openstack/vmtp/vmtp.py index 326a4f383..9833cc72a 100644 --- a/functest/opnfv_tests/openstack/vmtp/vmtp.py +++ b/functest/opnfv_tests/openstack/vmtp/vmtp.py @@ -33,9 +33,10 @@ from xtesting.core import testcase from functest.core import singlevm from functest.utils import env +from functest.utils import functest_utils -class Vmtp(singlevm.VmReady1): +class Vmtp(singlevm.VmReady2): """Class to run Vmtp_ as an OPNFV Functest testcase .. _Vmtp: http://vmtp.readthedocs.io/en/latest/ @@ -49,28 +50,50 @@ class Vmtp(singlevm.VmReady1): flavor_ram = 2048 flavor_vcpus = 1 flavor_disk = 0 + create_server_timeout = 300 + ssh_retry_timeout = 240 def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = 'vmtp' - super(Vmtp, self).__init__(**kwargs) - self.config = "{}/vmtp.conf".format(self.res_dir) + super().__init__(**kwargs) + self.config = f"{self.res_dir}/vmtp.conf" (_, self.privkey_filename) = tempfile.mkstemp() (_, self.pubkey_filename) = tempfile.mkstemp() + def check_requirements(self): + if self.count_hypervisors() < 2: + self.__logger.warning("Vmtp requires at least 2 hypervisors") + self.is_skipped = True + self.project.clean() + + def create_network_resources(self): + """Create router + + It creates a router which gateway is the external network detected. + + Raises: expection on error + """ + assert self.cloud + assert self.ext_net + self.router = self.cloud.create_router( + name=f'{self.case_name}-router_{self.guid}', + ext_gateway_net_id=self.ext_net.id) + self.__logger.debug("router: %s", self.router) + def generate_keys(self): """Generate Keys Raises: Exception on error """ assert self.cloud - name = "vmtp_{}".format(self.guid) + name = f"vmtp_{self.guid}" self.__logger.info("Creating keypair with name: '%s'", name) keypair = self.cloud.create_keypair(name) self.__logger.debug("keypair: %s", keypair) - with open(self.privkey_filename, 'w') as key_file: + with open(self.privkey_filename, 'w', encoding='utf-8') as key_file: key_file.write(keypair.private_key) - with open(self.pubkey_filename, 'w') as key_file: + with open(self.pubkey_filename, 'w', encoding='utf-8') as key_file: key_file.write(keypair.public_key) self.cloud.delete_keypair(keypair.id) @@ -83,40 +106,66 @@ class Vmtp(singlevm.VmReady1): if not os.path.exists(self.res_dir): os.makedirs(self.res_dir) cmd = ['vmtp', '-sc'] - output = subprocess.check_output(cmd) + output = subprocess.check_output(cmd).decode("utf-8") self.__logger.info("%s\n%s", " ".join(cmd), output) - with open(self.config, "w+") as conf: - vmtp_conf = yaml.load(output) + with open(self.config, "w+", encoding='utf-8') as conf: + vmtp_conf = yaml.full_load(output) vmtp_conf["private_key_file"] = self.privkey_filename vmtp_conf["public_key_file"] = self.pubkey_filename vmtp_conf["image_name"] = str(self.image.name) - vmtp_conf["router_name"] = "pns_router_{}".format(self.guid) + vmtp_conf["router_name"] = str(self.router.name) vmtp_conf["flavor_type"] = str(self.flavor.name) vmtp_conf["internal_network_name"] = [ - "pns-internal-net_{}".format(self.guid), - "pns-internal-net2_{}".format(self.guid)] - vmtp_conf["vm_name_client"] = "TestClient_{}".format(self.guid) - vmtp_conf["vm_name_server"] = "TestServer_{}".format(self.guid) - vmtp_conf["security_group_name"] = "pns-security{}".format( - self.guid) + f"pns-internal-net_{self.guid}", + f"pns-internal-net2_{self.guid}"] + vmtp_conf["vm_name_client"] = f"TestClient_{self.guid}" + vmtp_conf["vm_name_server"] = f"TestServer_{self.guid}" + vmtp_conf["security_group_name"] = f"pns-security{self.guid}" vmtp_conf["dns_nameservers"] = [env.get('NAMESERVER')] + vmtp_conf["generic_retry_count"] = self.create_server_timeout // 2 + vmtp_conf["ssh_retry_count"] = self.ssh_retry_timeout // 2 conf.write(yaml.dump(vmtp_conf)) def run_vmtp(self): + # pylint: disable=unexpected-keyword-arg """Run Vmtp and generate charts Raises: Exception on error """ assert self.cloud - cmd = ['vmtp', '-d', '--json', '{}/vmtp.json'.format(self.res_dir), + new_env = dict( + os.environ, + OS_USERNAME=self.project.user.name, + OS_PROJECT_NAME=self.project.project.name, + OS_PROJECT_ID=self.project.project.id, + OS_PROJECT_DOMAIN_NAME=self.project.domain.name, + OS_USER_DOMAIN_NAME=self.project.domain.name, + OS_PASSWORD=self.project.password) + if not new_env["OS_AUTH_URL"].endswith(('v3', 'v3/')): + new_env["OS_AUTH_URL"] = f'{new_env["OS_AUTH_URL"]}/v3' + try: + del new_env['OS_TENANT_NAME'] + del new_env['OS_TENANT_ID'] + except Exception: # pylint: disable=broad-except + pass + cmd = ['vmtp', '-d', '--json', f'{self.res_dir}/vmtp.json', '-c', self.config] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + if env.get("VMTP_HYPERVISORS"): + hypervisors = functest_utils.convert_ini_to_list( + env.get("VMTP_HYPERVISORS")) + for hypervisor in hypervisors: + cmd.extend(["--hypervisor", hypervisor]) + self.__logger.debug("cmd: %s", cmd) + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT, env=new_env).decode("utf-8") self.__logger.info("%s\n%s", " ".join(cmd), output) - cmd = ['vmtp_genchart', '-c', '{}/vmtp.html'.format(self.res_dir), - '{}/vmtp.json'.format(self.res_dir)] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + cmd = ['vmtp_genchart', '-c', f'{self.res_dir}/vmtp.html', + f'{self.res_dir}/vmtp.json'] + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT).decode("utf-8") self.__logger.info("%s\n%s", " ".join(cmd), output) - with open('{}/vmtp.json'.format(self.res_dir), 'r') as res_file: + with open(f'{self.res_dir}/vmtp.json', 'r', + encoding='utf-8') as res_file: self.details = json.load(res_file) def run(self, **kwargs): @@ -124,8 +173,18 @@ class Vmtp(singlevm.VmReady1): status = testcase.TestCase.EX_RUN_ERROR try: assert self.cloud - self.image = self.publish_image() - self.flavor = self.create_flavor() + assert super().run(**kwargs) == self.EX_OK + status = testcase.TestCase.EX_RUN_ERROR + if self.orig_cloud.get_role("admin"): + role_name = "admin" + elif self.orig_cloud.get_role("Admin"): + role_name = "Admin" + else: + raise Exception("Cannot detect neither admin nor Admin") + self.orig_cloud.grant_role( + role_name, user=self.project.user.id, + project=self.project.project.id, + domain=self.project.domain.id) self.generate_keys() self.write_config() self.run_vmtp() @@ -133,7 +192,8 @@ class Vmtp(singlevm.VmReady1): status = testcase.TestCase.EX_OK except subprocess.CalledProcessError as cpe: self.__logger.error( - "Exception when calling %s\n%s", cpe.cmd, cpe.output) + "Exception when calling %s\n%s", cpe.cmd, + cpe.output.decode("utf-8")) self.result = 0 except Exception: # pylint: disable=broad-except self.__logger.exception("Cannot run vmtp") @@ -144,11 +204,10 @@ class Vmtp(singlevm.VmReady1): def clean(self): try: assert self.cloud + super().clean() os.remove(self.privkey_filename) os.remove(self.pubkey_filename) - if self.image: - self.cloud.delete_image(self.image) - self.cloud.delete_network("pns-internal-net_{}".format(self.guid)) - self.cloud.delete_network("pns-internal-net2_{}".format(self.guid)) + self.cloud.delete_network(f"pns-internal-net_{self.guid}") + self.cloud.delete_network(f"pns-internal-net2_{self.guid}") except Exception: # pylint: disable=broad-except pass diff --git a/functest/opnfv_tests/openstack/vping/vping_ssh.py b/functest/opnfv_tests/openstack/vping/vping_ssh.py index 643f4f6de..ad64348c4 100644 --- a/functest/opnfv_tests/openstack/vping/vping_ssh.py +++ b/functest/opnfv_tests/openstack/vping/vping_ssh.py @@ -29,13 +29,13 @@ class VPingSSH(singlevm.SingleVm2): """Initialize testcase.""" if "case_name" not in kwargs: kwargs["case_name"] = "vping_ssh" - super(VPingSSH, self).__init__(**kwargs) + super().__init__(**kwargs) self.vm2 = None def prepare(self): - super(VPingSSH, self).prepare() + super().prepare() self.vm2 = self.boot_vm( - '{}-vm2_{}'.format(self.case_name, self.guid), + f'{self.case_name}-vm2_{self.guid}', security_groups=[self.sec.id]) def execute(self): @@ -44,9 +44,13 @@ class VPingSSH(singlevm.SingleVm2): Returns: ping exit codes """ assert self.ssh - (_, stdout, _) = self.ssh.exec_command( - 'ping -c 1 ' + self.vm2.private_v4) - self.__logger.debug("output:\n%s", stdout.read()) + if not self.check_regex_in_console(self.vm2.name): + return 1 + ip4 = self.vm2.private_v4 or self.vm2.addresses[ + self.network.name][0].addr + (_, stdout, stderr) = self.ssh.exec_command(f'ping -c 1 {ip4}') + self.__logger.info("output:\n%s", stdout.read().decode("utf-8")) + self.__logger.info("error:\n%s", stderr.read().decode("utf-8")) return stdout.channel.recv_exit_status() def clean(self): @@ -55,4 +59,4 @@ class VPingSSH(singlevm.SingleVm2): self.cloud.delete_server( self.vm2, wait=True, timeout=getattr(config.CONF, 'vping_vm_delete_timeout')) - super(VPingSSH, self).clean() + super().clean() diff --git a/functest/opnfv_tests/openstack/vping/vping_userdata.py b/functest/opnfv_tests/openstack/vping/vping_userdata.py index 421ea6a2c..8a8f26f37 100644 --- a/functest/opnfv_tests/openstack/vping/vping_userdata.py +++ b/functest/opnfv_tests/openstack/vping/vping_userdata.py @@ -26,7 +26,7 @@ class VPingUserdata(singlevm.VmReady2): def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = "vping_userdata" - super(VPingUserdata, self).__init__(**kwargs) + super().__init__(**kwargs) self.logger = logging.getLogger(__name__) self.vm1 = None self.vm2 = None @@ -39,14 +39,13 @@ class VPingUserdata(singlevm.VmReady2): """ try: assert self.cloud - assert super(VPingUserdata, self).run( + assert super().run( **kwargs) == testcase.TestCase.EX_OK self.result = 0 self.vm1 = self.boot_vm() self.vm2 = self.boot_vm( - '{}-vm2_{}'.format(self.case_name, self.guid), + f'{self.case_name}-vm2_{self.guid}', userdata=self._get_userdata()) - self.vm2 = self.cloud.wait_for_server(self.vm2, auto_ip=False) result = self._do_vping() self.stop_time = time.time() @@ -62,7 +61,8 @@ class VPingUserdata(singlevm.VmReady2): """ Override from super """ - if not self.vm1.private_v4: + if not (self.vm1.private_v4 or self.vm1.addresses[ + self.network.name][0].addr): self.logger.error("vm1: IP addr missing") return testcase.TestCase.EX_TESTCASE_FAILED @@ -79,22 +79,23 @@ class VPingUserdata(singlevm.VmReady2): self.logger.info("vPing detected!") exit_code = testcase.TestCase.EX_OK break - elif "failed to read iid from metadata" in p_console or tries > 5: + if "failed to read iid from metadata" in p_console or tries > 5: self.logger.info("Failed to read iid from metadata") break - elif sec == getattr(config.CONF, 'vping_ping_timeout'): + if sec == getattr(config.CONF, 'vping_ping_timeout'): self.logger.info("Timeout reached.") break - elif sec % 10 == 0: + if sec % 10 == 0: if "request failed" in p_console: self.logger.debug( - "It seems userdata is not supported in nova boot. " + + "It seems userdata is not supported in nova boot. " "Waiting a bit...") tries += 1 else: self.logger.debug( "Pinging %s. Waiting for response...", - self.vm1.private_v4) + self.vm1.private_v4 or self.vm1.addresses[ + self.network.name][0].addr) sec += 1 return exit_code @@ -103,12 +104,15 @@ class VPingUserdata(singlevm.VmReady2): """ Returns the post VM creation script to be added into the VM's userdata :param test_ip: the IP value to substitute into the script - :return: the bash script contents + :return: the shell script contents """ - if self.vm1.private_v4: + ip4 = self.vm1.private_v4 or self.vm1.addresses[ + self.network.name][0].addr + if self.vm1.private_v4 or self.vm1.addresses[ + self.network.name][0].addr: return ("#!/bin/sh\n\n" "while true; do\n" - " ping -c 1 %s 2>&1 >/dev/null\n" + f" ping -c 1 {ip4} 2>&1 >/dev/null\n" " RES=$?\n" " if [ \"Z$RES\" = \"Z0\" ] ; then\n" " echo 'vPing OK'\n" @@ -117,7 +121,7 @@ class VPingUserdata(singlevm.VmReady2): " echo 'vPing KO'\n" " fi\n" " sleep 1\n" - "done\n" % str(self.vm1.private_v4)) + "done\n") return None def clean(self): @@ -130,4 +134,4 @@ class VPingUserdata(singlevm.VmReady2): self.cloud.delete_server( self.vm2, wait=True, timeout=getattr(config.CONF, 'vping_vm_delete_timeout')) - super(VPingUserdata, self).clean() + super().clean() diff --git a/functest/opnfv_tests/sdn/odl/odl.py b/functest/opnfv_tests/sdn/odl/odl.py index 43476d8bf..72c38ce2c 100644 --- a/functest/opnfv_tests/sdn/odl/odl.py +++ b/functest/opnfv_tests/sdn/odl/odl.py @@ -49,7 +49,7 @@ class ODLTests(robotframework.RobotFramework): __logger = logging.getLogger(__name__) def __init__(self, **kwargs): - super(ODLTests, self).__init__(**kwargs) + super().__init__(**kwargs) self.res_dir = os.path.join( getattr(config.CONF, 'dir_results'), 'odl') self.xml_file = os.path.join(self.res_dir, 'output.xml') @@ -66,10 +66,10 @@ class ODLTests(robotframework.RobotFramework): try: for line in fileinput.input(cls.odl_variables_file, inplace=True): - print(re.sub("@{AUTH}.*", - "@{{AUTH}} {} {}".format( - odlusername, odlpassword), - line.rstrip())) + print(re.sub( + "@{AUTH}.*", + f"@{{AUTH}} {odlusername} {odlpassword}", + line.rstrip())) return True except Exception: # pylint: disable=broad-except cls.__logger.exception("Cannot set ODL creds:") @@ -111,9 +111,8 @@ class ODLTests(robotframework.RobotFramework): odlusername = kwargs['odlusername'] odlpassword = kwargs['odlpassword'] osauthurl = kwargs['osauthurl'] - keystoneurl = "{}://{}".format( - urllib.parse.urlparse(osauthurl).scheme, - urllib.parse.urlparse(osauthurl).netloc) + keystoneurl = (f"{urllib.parse.urlparse(osauthurl).scheme}://" + f"{urllib.parse.urlparse(osauthurl).netloc}") variable = ['KEYSTONEURL:' + keystoneurl, 'NEUTRONURL:' + kwargs['neutronurl'], 'OS_AUTH_URL:"' + osauthurl + '"', @@ -135,7 +134,7 @@ class ODLTests(robotframework.RobotFramework): else: if not self.set_robotframework_vars(odlusername, odlpassword): return self.EX_RUN_ERROR - return super(ODLTests, self).run(variable=variable, suites=suites) + return super().run(variable=variable, suites=suites) def run(self, **kwargs): """Run suites in OPNFV environment @@ -169,7 +168,6 @@ class ODLTests(robotframework.RobotFramework): kwargs['odlrestconfport'] = env.get('SDN_CONTROLLER_RESTCONFPORT') kwargs['odlusername'] = env.get('SDN_CONTROLLER_USER') kwargs['odlpassword'] = env.get('SDN_CONTROLLER_PASSWORD') - installer_type = env.get('INSTALLER_TYPE') kwargs['osusername'] = os.environ['OS_USERNAME'] kwargs['osuserdomainname'] = os.environ.get( 'OS_USER_DOMAIN_NAME', 'Default') @@ -178,17 +176,6 @@ class ODLTests(robotframework.RobotFramework): 'OS_PROJECT_DOMAIN_NAME', 'Default') kwargs['osauthurl'] = os.environ['OS_AUTH_URL'] kwargs['ospassword'] = os.environ['OS_PASSWORD'] - if installer_type == 'fuel': - kwargs['odlwebport'] = '8282' - kwargs['odlrestconfport'] = '8282' - elif installer_type == 'apex' or installer_type == 'netvirt': - kwargs['odlwebport'] = '8081' - kwargs['odlrestconfport'] = '8081' - elif installer_type == 'compass': - kwargs['odlrestconfport'] = '8080' - elif installer_type == 'daisy': - kwargs['odlwebport'] = '8181' - kwargs['odlrestconfport'] = '8087' assert kwargs['odlip'] except KeyError as ex: self.__logger.error( @@ -201,7 +188,7 @@ class ODLTests(robotframework.RobotFramework): return self.run_suites(suites, **kwargs) -class ODLParser(object): # pylint: disable=too-few-public-methods +class ODLParser(): # pylint: disable=too-few-public-methods """Parser to run ODL test suites.""" def __init__(self): diff --git a/functest/opnfv_tests/vnf/epc/juju_epc.py b/functest/opnfv_tests/vnf/epc/juju_epc.py index 3f2a9ff93..1cf240b80 100644 --- a/functest/opnfv_tests/vnf/epc/juju_epc.py +++ b/functest/opnfv_tests/vnf/epc/juju_epc.py @@ -14,35 +14,16 @@ import os import time import json import re -import subprocess import sys -import uuid + from copy import deepcopy import pkg_resources -import yaml - -import six -from snaps.config.flavor import FlavorConfig -from snaps.config.image import ImageConfig -from snaps.config.network import NetworkConfig, SubnetConfig -from snaps.config.router import RouterConfig -from snaps.config.security_group import ( - Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig) -from snaps.config.user import UserConfig -from snaps.openstack.create_flavor import OpenStackFlavor -from snaps.openstack.create_image import OpenStackImage -from snaps.openstack.create_network import OpenStackNetwork -from snaps.openstack.create_router import OpenStackRouter -from snaps.openstack.create_security_group import OpenStackSecurityGroup -from snaps.openstack.create_user import OpenStackUser -from snaps.openstack.utils import keystone_utils -from snaps.openstack.utils import nova_utils -from snaps.openstack.utils import neutron_utils - -from functest.core import vnf -from functest.opnfv_tests.openstack.snaps import snaps_utils +import scp + +from functest.core import singlevm from functest.utils import config from functest.utils import env +from functest.utils import functest_utils __author__ = "Amarendra Meher <amarendra@rebaca.com>" __author__ = "Soumaya K Nayek <soumaya.nayek@rebaca.com>" @@ -61,188 +42,186 @@ CREDS_TEMPLATE2 = """credentials: default-credential: abot-epc abot-epc: auth-type: userpass - password: {pass} + password: '{pass}' project-domain-name: {project_domain_n} tenant-name: {tenant_n}""" -CREDS_TEMPLATE3 = """credentials: +CREDS_TEMPLATE = """credentials: abot-epc: default-credential: abot-epc abot-epc: auth-type: userpass - password: {pass} + password: '{pass}' project-domain-name: {project_domain_n} tenant-name: {tenant_n} user-domain-name: {user_domain_n} username: {user_n}""" -class JujuEpc(vnf.VnfOnBoarding): +class JujuEpc(singlevm.SingleVm2): # pylint:disable=too-many-instance-attributes """Abot EPC deployed with JUJU Orchestrator Case""" __logger = logging.getLogger(__name__) - juju_timeout = '3600' + cidr = '192.168.120.0/24' + + filename = ('/home/opnfv/functest/images/' + 'ubuntu-16.04-server-cloudimg-amd64-disk1.img') + filename_alt = ('/home/opnfv/functest/images/' + 'ubuntu-14.04-server-cloudimg-amd64-disk1.img') + + flavor_ram = 2048 + flavor_vcpus = 1 + flavor_disk = 10 + flavor_alt_ram = 4096 + flavor_alt_vcpus = 1 + flavor_alt_disk = 10 + username = 'ubuntu' + juju_timeout = '4800' def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = "juju_epc" - super(JujuEpc, self).__init__(**kwargs) + super().__init__(**kwargs) # Retrieve the configuration self.case_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/vnf/epc') try: self.config = getattr( - config.CONF, 'vnf_{}_config'.format(self.case_name)) - except Exception: - raise Exception("VNF config file not found") + config.CONF, f'vnf_{self.case_name}_config') + except Exception as exc: + raise Exception("VNF config file not found") from exc self.config_file = os.path.join(self.case_dir, self.config) - self.orchestrator = dict(requirements=get_config( - "orchestrator.requirements", self.config_file)) + self.orchestrator = dict( + requirements=functest_utils.get_parameter_from_yaml( + "orchestrator.requirements", self.config_file)) self.created_object = [] self.details['orchestrator'] = dict( - name=get_config("orchestrator.name", self.config_file), - version=get_config("orchestrator.version", self.config_file), + name=functest_utils.get_parameter_from_yaml( + "orchestrator.name", self.config_file), + version=functest_utils.get_parameter_from_yaml( + "orchestrator.version", self.config_file), status='ERROR', result='' ) self.vnf = dict( - descriptor=get_config("vnf.descriptor", self.config_file), - requirements=get_config("vnf.requirements", self.config_file) + descriptor=functest_utils.get_parameter_from_yaml( + "vnf.descriptor", self.config_file), + requirements=functest_utils.get_parameter_from_yaml( + "vnf.requirements", self.config_file) ) self.details['vnf'] = dict( descriptor_version=self.vnf['descriptor']['version'], - name=get_config("vnf.name", self.config_file), - version=get_config("vnf.version", self.config_file), + name=functest_utils.get_parameter_from_yaml( + "vnf.name", self.config_file), + version=functest_utils.get_parameter_from_yaml( + "vnf.version", self.config_file), ) self.__logger.debug("VNF configuration: %s", self.vnf) self.details['test_vnf'] = dict( - name=get_config("vnf_test_suite.name", self.config_file), - version=get_config("vnf_test_suite.version", self.config_file), - tag_name=get_config("vnf_test_suite.tag_name", self.config_file) + name=functest_utils.get_parameter_from_yaml( + "vnf_test_suite.name", self.config_file), + version=functest_utils.get_parameter_from_yaml( + "vnf_test_suite.version", self.config_file), + tag_name=functest_utils.get_parameter_from_yaml( + "vnf_test_suite.tag_name", self.config_file) ) - self.public_auth_url = None self.res_dir = os.path.join( getattr(config.CONF, 'dir_results'), self.case_name) - def _bypass_juju_netdiscovery_bug(self, name): - user_creator = OpenStackUser( - self.snaps_creds, - UserConfig( - name=name, - password=str(uuid.uuid4()), - project_name=self.tenant_name, - domain_name=self.snaps_creds.user_domain_name, - roles={'_member_': self.tenant_name})) - user_creator.create() - self.created_object.append(user_creator) - return user_creator + try: + self.public_auth_url = self.get_public_auth_url(self.orig_cloud) + if not self.public_auth_url.endswith(('v3', 'v3/')): + self.public_auth_url = f"{self.public_auth_url}/v3" + except Exception: # pylint: disable=broad-except + self.public_auth_url = None + self.sec = None + self.image_alt = None + self.flavor_alt = None + + def _install_juju(self): + (_, stdout, stderr) = self.ssh.exec_command( + 'sudo snap install juju --channel=2.3/stable --classic') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() + + def _install_juju_wait(self): + (_, stdout, stderr) = self.ssh.exec_command( + 'sudo apt-get update && sudo apt-get install python3-pip -y && ' + 'sudo pip3 install juju_wait===2.6.4') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() def _register_cloud(self): + assert self.public_auth_url self.__logger.info("Creating Cloud for Abot-epc .....") clouds_yaml = os.path.join(self.res_dir, "clouds.yaml") cloud_data = { 'url': self.public_auth_url, - 'region': self.snaps_creds.region_name if ( - self.snaps_creds.region_name) else 'RegionOne'} - with open(clouds_yaml, 'w') as yfile: + 'region': self.cloud.region_name if self.cloud.region_name else ( + 'RegionOne')} + with open(clouds_yaml, 'w', encoding='utf-8') as yfile: yfile.write(CLOUD_TEMPLATE.format(**cloud_data)) - cmd = ['juju', 'add-cloud', 'abot-epc', '-f', clouds_yaml, '--replace'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - - def _register_credentials_v2(self): + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put(clouds_yaml, remote_path='~/') + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju add-cloud abot-epc -f clouds.yaml --replace') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() + + def _register_credentials(self): self.__logger.info("Creating Credentials for Abot-epc .....") - user_creator = self._bypass_juju_netdiscovery_bug( - 'juju_network_discovery_bug') - snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name) - self.__logger.debug("snaps creds: %s", snaps_creds) credentials_yaml = os.path.join(self.res_dir, "credentials.yaml") creds_data = { - 'pass': snaps_creds.password, - 'tenant_n': snaps_creds.project_name, - 'user_n': snaps_creds.username} - with open(credentials_yaml, 'w') as yfile: - yfile.write(CREDS_TEMPLATE2.format(**creds_data)) - cmd = ['juju', 'add-credential', 'abot-epc', '-f', credentials_yaml, - '--replace'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - - def _register_credentials_v3(self): - self.__logger.info("Creating Credentials for Abot-epc .....") - user_creator = self._bypass_juju_netdiscovery_bug( - 'juju_network_discovery_bug') - snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name) - self.__logger.debug("snaps creds: %s", snaps_creds) - credentials_yaml = os.path.join(self.res_dir, "credentials.yaml") - creds_data = { - 'pass': snaps_creds.password, - 'tenant_n': snaps_creds.project_name, - 'user_n': snaps_creds.username, - 'project_domain_n': snaps_creds.project_domain_name, - 'user_domain_n': snaps_creds.user_domain_name} - with open(credentials_yaml, 'w') as yfile: - yfile.write(CREDS_TEMPLATE3.format(**creds_data)) - cmd = ['juju', 'add-credential', 'abot-epc', '-f', credentials_yaml, - '--replace'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - - def _add_custom_rule(self, sec_grp_name): - """ To add custom rule for SCTP Traffic """ - - security_group = OpenStackSecurityGroup( - self.snaps_creds, - SecurityGroupConfig( - name=sec_grp_name)) - - security_group.create() - - # Add custom security rule to the obtained Security Group - self.__logger.info("Adding SCTP ingress rule to SG:%s", - security_group.sec_grp_settings.name) - - try: - security_group.add_rule(SecurityGroupRuleConfig( - sec_grp_name=sec_grp_name, direction=Direction.ingress, - protocol=Protocol.sctp)) - except Exception: # pylint: disable=broad-except - self.__logger.exception( - "Some issue encountered with adding SCTP security rule ...") - - def prepare(self): - """Prepare testcase (Additional pre-configuration steps).""" - self.__logger.info("Additional pre-configuration steps") - super(JujuEpc, self).prepare() - try: - os.makedirs(self.res_dir) - except OSError as ex: - if ex.errno != errno.EEXIST: - self.__logger.exception("Cannot create %s", self.res_dir) - raise vnf.VnfPreparationException - - self.__logger.info("ENV:\n%s", env.string()) - - self.public_auth_url = keystone_utils.get_endpoint( - self.snaps_creds, 'identity') - - # it enforces a versioned public identity endpoint as juju simply - # adds /auth/tokens wich fails vs an unversioned endpoint. - if not self.public_auth_url.endswith(('v3', 'v3/', 'v2.0', 'v2.0/')): - self.public_auth_url = six.moves.urllib.parse.urljoin( - self.public_auth_url, 'v3') - self._register_cloud() - if self.snaps_creds.identity_api_version == 3: - self._register_credentials_v3() - else: - self._register_credentials_v2() + 'pass': self.project.password, + 'tenant_n': self.project.project.name, + 'user_n': self.project.user.name, + 'project_domain_n': self.cloud.auth.get( + "project_domain_name", "Default"), + 'user_domain_n': self.cloud.auth.get( + "user_domain_name", "Default")} + with open(credentials_yaml, 'w', encoding='utf-8') as yfile: + yfile.write(CREDS_TEMPLATE.format(**creds_data)) + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put(credentials_yaml, remote_path='~/') + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju add-credential abot-epc -f credentials.yaml ' + ' --replace --debug') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() + + def _publish_image(self): + region_name = self.cloud.region_name if self.cloud.region_name else ( + 'RegionOne') + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju metadata generate-image -d /home/ubuntu ' + f'-i {self.image.id} -s xenial -r {region_name} ' + f'-u {self.public_auth_url}') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() + + def publish_image_alt(self, name=None): + image_alt = super().publish_image_alt(name) + region_name = self.cloud.region_name if self.cloud.region_name else ( + 'RegionOne') + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju metadata generate-image -d /home/ubuntu ' + f'-i {image_alt.id} -s trusty -r {region_name} ' + f'-u {self.public_auth_url}') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return image_alt def deploy_orchestrator(self): # pylint: disable=too-many-locals """ @@ -250,204 +229,128 @@ class JujuEpc(vnf.VnfOnBoarding): Bootstrap juju """ - self.__logger.info("Deploying Juju Orchestrator") - private_net_name = getattr( - config.CONF, 'vnf_{}_private_net_name'.format(self.case_name)) - private_subnet_name = '{}-{}'.format( - getattr(config.CONF, - 'vnf_{}_private_subnet_name'.format(self.case_name)), - self.uuid) - private_subnet_cidr = getattr( - config.CONF, 'vnf_{}_private_subnet_cidr'.format(self.case_name)) - abot_router = '{}-{}'.format( - getattr(config.CONF, - 'vnf_{}_external_router'.format(self.case_name)), - self.uuid) - self.__logger.info("Creating full network with nameserver: %s", - env.get('NAMESERVER')) - subnet_settings = SubnetConfig( - name=private_subnet_name, - cidr=private_subnet_cidr, - dns_nameservers=[env.get('NAMESERVER')]) - network_settings = NetworkConfig( - name=private_net_name, subnet_settings=[subnet_settings]) - network_creator = OpenStackNetwork(self.snaps_creds, network_settings) - net_id = network_creator.create().id - self.created_object.append(network_creator) - - ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds) - self.__logger.info("Creating network Router ....") - router_creator = OpenStackRouter( - self.snaps_creds, RouterConfig( - name=abot_router, - external_gateway=ext_net_name, - internal_subnets=[subnet_settings.name])) - router_creator.create() - self.created_object.append(router_creator) - self.__logger.info("Creating Flavor ....") - flavor_settings = FlavorConfig( - name=self.orchestrator['requirements']['flavor']['name'], - ram=self.orchestrator['requirements']['flavor']['ram_min'], - disk=10, vcpus=1) - flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) - flavor_creator.create() - self.created_object.append(flavor_creator) - - self.__logger.info("Upload some OS images if it doesn't exist") - images = get_config("tenant_images", self.config_file) - self.__logger.info("Images needed for vEPC: %s", images) - for image_name, image_file in six.iteritems(images): - self.__logger.info("image: %s, file: %s", image_name, image_file) - if image_file and image_name: - image_creator = OpenStackImage(self.snaps_creds, ImageConfig( - name=image_name, image_user='cloud', img_format='qcow2', - image_file=image_file)) - image_id = image_creator.create().id - cmd = ['juju', 'metadata', 'generate-image', '-d', '/root', - '-i', image_id, '-s', image_name, '-r', - self.snaps_creds.region_name if ( - self.snaps_creds.region_name) else 'RegionOne', - '-u', self.public_auth_url] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - self.created_object.append(image_creator) - self.__logger.info("Network ID : %s", net_id) - + self._publish_image() + self.image_alt = self.publish_image_alt() + self.flavor_alt = self.create_flavor_alt() self.__logger.info("Starting Juju Bootstrap process...") - try: - cmd = ['timeout', '-t', JujuEpc.juju_timeout, - 'juju', 'bootstrap', 'abot-epc', 'abot-controller', - '--metadata-source', '/root', - '--constraints', 'mem=2G', - '--bootstrap-series', 'xenial', - '--config', 'network={}'.format(net_id), - '--config', 'ssl-hostname-verification=false', - '--config', 'use-floating-ip=true', - '--config', 'use-default-secgroup=true', - '--debug'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - except subprocess.CalledProcessError as cpe: - self.__logger.error( - "Exception with Juju Bootstrap: %s\n%s", - cpe.cmd, cpe.output) - return False - except Exception: # pylint: disable=broad-except - self.__logger.exception("Some issue with Juju Bootstrap ...") - return False - - return True + region_name = self.cloud.region_name if self.cloud.region_name else ( + 'RegionOne') + (_, stdout, stderr) = self.ssh.exec_command( + f'timeout {JujuEpc.juju_timeout} ' + f'/snap/bin/juju bootstrap abot-epc/{region_name} abot-controller ' + '--agent-version 2.3.9 --metadata-source /home/ubuntu ' + '--constraints mem=2G --bootstrap-series xenial ' + f'--config network={self.network.id} ' + '--config ssl-hostname-verification=false ' + f'--config external-network={self.ext_net.id} ' + '--config use-floating-ip=true ' + '--config use-default-secgroup=true ' + '--debug') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() def check_app(self, name='abot-epc-basic', status='active'): """Check application status.""" - cmd = ['juju', 'status', '--format', 'short', name] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - ret = re.search(r'(?=workload:({})\))'.format(status), output) - if ret: - self.__logger.info("%s workload is %s", name, status) - return True - self.__logger.error("%s workload differs from %s", name, status) - return False + for i in range(10): + (_, stdout, stderr) = self.ssh.exec_command( + f'/snap/bin/juju status --format short {name}') + output = stdout.read().decode("utf-8") + self.__logger.debug("stdout:\n%s", output) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + continue + ret = re.search( + rf'(?=workload:({status})\))', output) + if ret: + self.__logger.info("%s workload is %s", name, status) + break + self.__logger.info( + "loop %d: %s workload differs from %s", i + 1, name, status) + time.sleep(60) + else: + self.__logger.error("%s workload differs from %s", name, status) + return False + return True def deploy_vnf(self): """Deploy ABOT-OAI-EPC.""" self.__logger.info("Upload VNFD") - descriptor = self.vnf['descriptor'] - self.__logger.info("Get or create flavor for all Abot-EPC") - flavor_settings = FlavorConfig( - name=self.vnf['requirements']['flavor']['name'], - ram=self.vnf['requirements']['flavor']['ram_min'], - disk=10, - vcpus=1) - flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) - flavor_creator.create() - self.created_object.append(flavor_creator) - + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put( + '/src/epc-requirements/abot_charm', remote_path='~/', + recursive=True) self.__logger.info("Deploying Abot-epc bundle file ...") - cmd = ['juju', 'deploy', '{}'.format(descriptor.get('file_name'))] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - self.__logger.info("Waiting for instances .....") - try: - cmd = ['timeout', '-t', JujuEpc.juju_timeout, 'juju-wait'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - self.__logger.info("Deployed Abot-epc on Openstack") - except subprocess.CalledProcessError as cpe: - self.__logger.error( - "Exception with Juju VNF Deployment: %s\n%s", - cpe.cmd, cpe.output) - return False - except Exception: # pylint: disable=broad-except - self.__logger.exception("Some issue with the VNF Deployment ..") - return False - + (_, stdout, stderr) = self.ssh.exec_command( + 'sudo mkdir -p /src/epc-requirements && ' + 'sudo mv abot_charm /src/epc-requirements/abot_charm && ' + '/snap/bin/juju deploy ' + '/src/epc-requirements/abot_charm/functest-abot-epc-bundle/' + 'bundle.yaml') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() + (_, stdout, stderr) = self.ssh.exec_command( + 'PATH=/snap/bin/:$PATH ' + f'timeout {JujuEpc.juju_timeout} juju-wait') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() self.__logger.info("Checking status of ABot and EPC units ...") - cmd = ['juju', 'status'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.debug("%s\n%s", " ".join(cmd), output) + (_, stdout, stderr) = self.ssh.exec_command('/snap/bin/juju status') + output = stdout.read().decode("utf-8") + self.__logger.debug("stdout:\n%s", output) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() for app in ['abot-epc-basic', 'oai-epc', 'oai-hss']: if not self.check_app(app): return False - - nova_client = nova_utils.nova_client(self.snaps_creds) - instances = get_instances(nova_client) - self.__logger.info("List of Instance: %s", instances) - for items in instances: - metadata = get_instance_metadata(nova_client, items) - if 'juju-units-deployed' in metadata: - sec_group = 'juju-{}-{}'.format( - metadata['juju-controller-uuid'], - metadata['juju-model-uuid']) - self.__logger.info("Instance: %s", sec_group) - break - self.__logger.info("Adding Security group rule....") - # This will add sctp rule to a common Security Group Created - # by juju and shared to all deployed units. - self._add_custom_rule(sec_group) - - self.__logger.info("Transferring the feature files to Abot_node ...") - cmd = ['timeout', '-t', JujuEpc.juju_timeout, - 'juju', 'scp', '--', '-r', '-v', - '{}/featureFiles'.format(self.case_dir), 'abot-epc-basic/0:~/'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - - self.__logger.info("Copying the feature files within Abot_node ") - cmd = ['timeout', '-t', JujuEpc.juju_timeout, - 'juju', 'ssh', 'abot-epc-basic/0', - 'sudo', 'cp', '-vfR', '~/featureFiles/*', - '/etc/rebaca-test-suite/featureFiles'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - return True + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.put( + f'{self.case_dir}/featureFiles', remote_path='~/', + recursive=True) + (_, stdout, stderr) = self.ssh.exec_command( + f'timeout {JujuEpc.juju_timeout} /snap/bin/juju scp -- -r -v ' + '~/featureFiles abot-epc-basic/0:/etc/rebaca-test-suite/') + output = stdout.read().decode("utf-8") + self.__logger.debug("stdout:\n%s", output) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + return not stdout.channel.recv_exit_status() def test_vnf(self): """Run test on ABoT.""" start_time = time.time() - self.__logger.info("Running VNF Test cases....") - cmd = ['juju', 'run-action', 'abot-epc-basic/0', 'run', - 'tagnames={}'.format(self.details['test_vnf']['tag_name'])] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - - cmd = ['timeout', '-t', JujuEpc.juju_timeout, 'juju-wait'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - + (_, stdout, stderr) = self.ssh.exec_command( + "/snap/bin/juju run-action abot-epc-basic/0 " + f"run tagnames={self.details['test_vnf']['tag_name']}") + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() + (_, stdout, stderr) = self.ssh.exec_command( + 'PATH=/snap/bin/:$PATH ' + f'timeout {JujuEpc.juju_timeout} juju-wait') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() duration = time.time() - start_time self.__logger.info("Getting results from Abot node....") - cmd = ['timeout', '-t', JujuEpc.juju_timeout, - 'juju', 'scp', '--', '-v', - 'abot-epc-basic/0:' - '/var/lib/abot-epc-basic/artifacts/TestResults.json', - '{}/.'.format(self.res_dir)] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) + (_, stdout, stderr) = self.ssh.exec_command( + f'timeout {JujuEpc.juju_timeout} /snap/bin/juju scp ' + '-- -v abot-epc-basic/0:' + '/var/lib/abot-epc-basic/artifacts/TestResults.json .') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + if stdout.channel.recv_exit_status(): + return not stdout.channel.recv_exit_status() + scpc = scp.SCPClient(self.ssh.get_transport()) + scpc.get('TestResults.json', self.res_dir) self.__logger.info("Parsing the Test results...") - res = (process_abot_test_result('{}/TestResults.json'.format( - self.res_dir))) + res = process_abot_test_result(f'{self.res_dir}/TestResults.json') short_result = sig_test_format(res) self.__logger.info(short_result) self.details['test_vnf'].update( @@ -459,99 +362,48 @@ class JujuEpc(vnf.VnfOnBoarding): short_result['failures'], short_result['skipped']) return True - def _get_floating_ips(self): - """Get the list of floating IPs associated with the current project""" - - project_id = self.os_project.get_project().id - - neutron_client = neutron_utils.neutron_client(self.snaps_creds) - floating_ips = neutron_utils.get_floating_ips(neutron_client) - - project_floating_ip_list = list() - for floating_ip in floating_ips: - if project_id and project_id == floating_ip.project_id: - project_floating_ip_list.append(floating_ip) - - return project_floating_ip_list - - def _release_floating_ips(self, fip_list): - """ - Responsible for deleting a list of floating IPs - :param fip_list: A list of SNAPS FloatingIp objects - :return: - """ - if not fip_list: - return - - neutron_client = neutron_utils.neutron_client(self.snaps_creds) - - for floating_ip in fip_list: - neutron_utils.delete_floating_ip(neutron_client, floating_ip) - - def clean(self): - """Clean created objects/functions.""" - - # Store Floating IPs of instances created by Juju - fip_list = self._get_floating_ips() - self.__logger.info("Floating IPs assigned to project:%s", - self.os_project.get_project().name) - for floating_ip in fip_list: - self.__logger.debug("%s:%s", floating_ip.ip, - floating_ip.description) - + def execute(self): + """Prepare testcase (Additional pre-configuration steps).""" + assert self.public_auth_url + self.__logger.info("Additional pre-configuration steps") + try: + os.makedirs(self.res_dir) + except OSError as ex: + if ex.errno != errno.EEXIST: + self.__logger.exception("Cannot create %s", self.res_dir) + raise Exception from ex + self.__logger.info("ENV:\n%s", env.string()) try: - cmd = ['juju', 'debug-log', '--replay', '--no-tail'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.debug("%s\n%s", " ".join(cmd), output) - if not self.orchestrator['requirements']['preserve_setup']: - self.__logger.info("Destroying Orchestrator...") - cmd = ['timeout', '-t', JujuEpc.juju_timeout, - 'juju', 'destroy-controller', '-y', 'abot-controller', - '--destroy-all-models'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - self.__logger.info("%s\n%s", " ".join(cmd), output) - except subprocess.CalledProcessError as cpe: - self.__logger.error( - "Exception with Juju Cleanup: %s\n%s", - cpe.cmd, cpe.output) + assert self._install_juju() + assert self._install_juju_wait() + assert self._register_cloud() + assert self._register_credentials() + assert self.deploy_orchestrator() + assert self.deploy_vnf() + assert self.test_vnf() except Exception: # pylint: disable=broad-except - self.__logger.exception("General issue during the undeployment ..") - - if not self.orchestrator['requirements']['preserve_setup']: - try: - self.__logger.info('Release floating IPs assigned by Juju...') - self._release_floating_ips(fip_list) - except Exception: # pylint: disable=broad-except - self.__logger.exception( - "Exception while releasing floating IPs ...") + self.__logger.exception("juju_epc failed") + return 1 + return 0 - self.__logger.info('Remove the Abot_epc OS objects ..') - super(JujuEpc, self).clean() - - return True - - -# ---------------------------------------------------------- -# -# YAML UTILS -# -# ----------------------------------------------------------- -def get_config(parameter, file_path): - """ - 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 clean(self): + """Clean created objects/functions.""" + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju debug-log --replay --no-tail') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + (_, stdout, stderr) = self.ssh.exec_command( + '/snap/bin/juju destroy-controller -y abot-controller ' + '--destroy-all-models') + self.__logger.debug("stdout:\n%s", stdout.read().decode("utf-8")) + self.__logger.debug("stderr:\n%s", stderr.read().decode("utf-8")) + for fip in self.cloud.list_floating_ips(): + self.cloud.delete_floating_ip(fip.id) + if self.image_alt: + self.cloud.delete_image(self.image_alt) + if self.flavor_alt: + self.orig_cloud.delete_flavor(self.flavor_alt.id) + super().clean() def sig_test_format(sig_test): @@ -577,7 +429,7 @@ def sig_test_format(sig_test): def process_abot_test_result(file_path): """ Process ABoT Result """ - with open(file_path) as test_result: + with open(file_path, encoding='utf-8') as test_result: data = json.load(test_result) res = [] for tests in data: @@ -629,23 +481,3 @@ def update_data(obj): raise return obj - - -def get_instances(nova_client): - """ To get all vm info of a project """ - try: - instances = nova_client.servers.list() - return instances - except Exception as exc: # pylint: disable=broad-except - logging.error("Error [get_instances(nova_client)]: %s", exc) - return None - - -def get_instance_metadata(nova_client, instance): - """ Get instance Metadata - Instance ID """ - try: - instance = nova_client.servers.get(instance.id) - return instance.metadata - except Exception as exc: # pylint: disable=broad-except - logging.error("Error [get_instance_status(nova_client)]: %s", exc) - return None diff --git a/functest/opnfv_tests/vnf/ims/clearwater.py b/functest/opnfv_tests/vnf/ims/clearwater.py new file mode 100644 index 000000000..4c143fd70 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/clearwater.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# +# Copyright (c) 2017 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 + +"""Ease testing any Clearwater deployment""" + +import logging +import os +import re +import time + +import pkg_resources +import requests + +from functest.utils import config +import functest.utils.functest_utils as ft_utils + +__author__ = ("Valentin Boucher <valentin.boucher@orange.com>, " + "Helen Yao <helanyao@gmail.com>") + + +class ClearwaterTesting(): + """vIMS clearwater base usable by several orchestrators""" + + def __init__(self, case_name, bono_ip, ellis_ip): + self.logger = logging.getLogger(__name__) + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/ims') + self.data_dir = getattr(config.CONF, 'dir_ims_data') + self.result_dir = os.path.join( + getattr(config.CONF, 'dir_results'), case_name) + self.test_dir = getattr(config.CONF, 'dir_repo_vims_test') + + if not os.path.exists(self.data_dir): + os.makedirs(self.data_dir) + if not os.path.exists(self.result_dir): + os.makedirs(self.result_dir) + + self.ellis_ip = ellis_ip + self.bono_ip = bono_ip + + def availability_check(self, signup_code='secret', two_numbers=False): + """Create one or two numbers""" + assert self.ellis_ip + output_dict = {} + self.logger.debug('Ellis IP: %s', self.ellis_ip) + output_dict['ellis_ip'] = self.ellis_ip + account_url = f'http://{self.ellis_ip}/accounts' + params = {"password": "functest", + "full_name": "opnfv functest user", + "email": "functest@opnfv.org", + "signup_code": signup_code} + output_dict['login'] = params + + number_res = self._create_ellis_account(account_url, params) + output_dict['number'] = number_res + + session_url = f'http://{self.ellis_ip}/session' + session_data = { + 'username': params['email'], + 'password': params['password'], + 'email': params['email'] + } + cookies = self._get_ellis_session_cookies(session_url, session_data) + + number_url = ( + f"http://{self.ellis_ip}/accounts/{params['email']}/numbers") + self.logger.debug('Create 1st calling number on Ellis') + number_res = self._create_ellis_number(number_url, cookies) + + if two_numbers: + self.logger.debug('Create 2nd calling number on Ellis') + number_res = self._create_ellis_number(number_url, cookies) + output_dict['number2'] = number_res + + return output_dict + + def _create_ellis_account(self, account_url, params): + i = 80 + for iloop in range(i): + try: + req = requests.post(account_url, data=params) + if req.status_code == 201: + account_res = req.json() + self.logger.info( + 'Account %s is created on Ellis\n%s', + params.get('full_name'), account_res) + return account_res + raise Exception("Cannot create ellis account") + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot create ellis account", iloop + 1) + time.sleep(30) + raise Exception( + f"Unable to create an account {params.get('full_name')}") + + def _get_ellis_session_cookies(self, session_url, params): + i = 15 + for iloop in range(i): + try: + req = requests.post(session_url, data=params) + if req.status_code == 201: + cookies = req.cookies + self.logger.debug('cookies: %s', cookies) + return cookies + raise Exception('Failed to get cookies for Ellis') + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot get cookies for Ellis", iloop + 1) + time.sleep(10) + raise Exception('Failed to get cookies for Ellis') + + def _create_ellis_number(self, number_url, cookies): + i = 30 + for iloop in range(i): + try: + req = requests.post(number_url, cookies=cookies) + if req.status_code == 200: + number_res = req.json() + self.logger.info( + 'Calling number is created: %s', number_res) + return number_res + if req and req.json(): + reason = req.json()['reason'] + else: + reason = req + self.logger.info("cannot create a number: %s", reason) + raise Exception('Failed to create a number') + except Exception: # pylint: disable=broad-except + self.logger.info( + "try %s: cannot create a number", iloop + 1) + time.sleep(25) + raise Exception('Failed to create a number') + + def run_clearwater_live_test(self, public_domain, signup_code='secret'): + """Run the Clearwater live tests + + It first runs dnsmasq to reach clearwater services by FQDN and then the + Clearwater live tests. All results are saved in ims_test_output.txt. + + Returns: + - a dict containing the overall results + - None on error + """ + # pylint: disable=too-many-locals,too-many-arguments + self.logger.info('Run Clearwater live test') + script = (f'cd {self.test_dir};' + f'rake test[{public_domain}] SIGNUP_CODE={signup_code}') + if self.bono_ip and self.ellis_ip: + subscript = f' PROXY={self.bono_ip} ELLIS={self.ellis_ip}' + script = f'{script}{subscript}' + script = f'{script} --trace' + cmd = f"/bin/sh -c '{script}'" + self.logger.debug('Live test cmd: %s', cmd) + output_file = os.path.join(self.result_dir, "ims_test_output.txt") + ft_utils.execute_command(cmd, + error_msg='Clearwater live test failed', + output_file=output_file) + + with open(output_file, 'r', encoding='utf-8') as ofile: + result = ofile.read() + + if result != "": + self.logger.debug(result) + + vims_test_result = {} + try: + grp = re.search( + r'^(\d+) failures out of (\d+) tests run.*\n' + r'(\d+) tests skipped$', result, re.MULTILINE | re.DOTALL) + assert grp + vims_test_result["failures"] = int(grp.group(1)) + vims_test_result["total"] = int(grp.group(2)) + vims_test_result["skipped"] = int(grp.group(3)) + vims_test_result['passed'] = ( + int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1))) + if vims_test_result['total'] - vims_test_result['skipped'] > 0: + vnf_test_rate = vims_test_result['passed'] / ( + vims_test_result['total'] - vims_test_result['skipped']) + else: + vnf_test_rate = 0 + except Exception: # pylint: disable=broad-except + self.logger.exception("Cannot parse live tests results") + return None, 0 + return vims_test_result, vnf_test_rate diff --git a/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py b/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py deleted file mode 100644 index add99468b..000000000 --- a/functest/opnfv_tests/vnf/ims/clearwater_ims_base.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2017 All rights reserved -# This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# -# http://www.apache.org/licenses/LICENSE-2.0 - -"""Ease testing any Clearwater deployment""" - -import logging -import os -import re -import shlex -import shutil -import subprocess -import time - -import pkg_resources -import requests - -import functest.core.vnf as vnf -from functest.utils import config -import functest.utils.functest_utils as ft_utils - -__author__ = ("Valentin Boucher <valentin.boucher@orange.com>, " - "Helen Yao <helanyao@gmail.com>") - - -class ClearwaterOnBoardingBase(vnf.VnfOnBoarding): - """vIMS clearwater base usable by several orchestrators""" - - def __init__(self, **kwargs): - self.logger = logging.getLogger(__name__) - super(ClearwaterOnBoardingBase, self).__init__(**kwargs) - self.case_dir = pkg_resources.resource_filename( - 'functest', 'opnfv_tests/vnf/ims') - self.data_dir = getattr(config.CONF, 'dir_ims_data') - self.result_dir = os.path.join(getattr(config.CONF, 'dir_results'), - self.case_name) - self.test_dir = getattr(config.CONF, 'dir_repo_vims_test') - - if not os.path.exists(self.data_dir): - os.makedirs(self.data_dir) - if not os.path.exists(self.result_dir): - os.makedirs(self.result_dir) - - def config_ellis(self, ellis_ip, signup_code='secret', two_numbers=False): - """Create one or two numbers""" - output_dict = {} - self.logger.debug('Configure Ellis: %s', ellis_ip) - output_dict['ellis_ip'] = ellis_ip - account_url = 'http://{0}/accounts'.format(ellis_ip) - params = {"password": "functest", - "full_name": "opnfv functest user", - "email": "functest@opnfv.org", - "signup_code": signup_code} - req = requests.post(account_url, data=params) - output_dict['login'] = params - if req.status_code != 201 and req.status_code != 409: - raise Exception( - "Unable to create an account {}\n{}".format( - params, req.text)) - self.logger.debug( - 'Account %s is created on Ellis\n%s', params, req.json()) - - session_url = 'http://{0}/session'.format(ellis_ip) - session_data = { - 'username': params['email'], - 'password': params['password'], - 'email': params['email'] - } - req = requests.post(session_url, data=session_data) - if req.status_code != 201: - raise Exception('Failed to get cookie for Ellis\n{}'.format( - req.text)) - cookies = req.cookies - self.logger.debug('Cookies: %s', cookies) - - number_url = 'http://{0}/accounts/{1}/numbers'.format( - ellis_ip, params['email']) - self.logger.debug('Create 1st calling number on Ellis') - i = 30 - while req.status_code != 200 and i > 0: - try: - number_res = self._create_ellis_number(number_url, cookies) - break - except Exception: # pylint: disable=broad-except - if i == 1: - self.logger.exception("Unable to create a number") - raise Exception("Unable to create a number") - self.logger.info("Unable to create a number. Retry ..") - time.sleep(25) - i = i - 1 - output_dict['number'] = number_res - - if two_numbers: - self.logger.debug('Create 2nd calling number on Ellis') - number_res = self._create_ellis_number(number_url, cookies) - output_dict['number2'] = number_res - - return output_dict - - def _create_ellis_number(self, number_url, cookies): - req = requests.post(number_url, cookies=cookies) - - if req.status_code != 200: - if req and req.json(): - reason = req.json()['reason'] - else: - reason = req - raise Exception("Unable to create a number: %s" % reason) - number_res = req.json() - self.logger.info('Calling number is created: %s', number_res) - return number_res - - def run_clearwater_live_test(self, dns_ip, public_domain, - bono_ip=None, ellis_ip=None, - signup_code='secret'): - """Run the Clearwater live tests - - It first runs dnsmasq to reach clearwater services by FQDN and then the - Clearwater live tests. All results are saved in ims_test_output.txt. - - Returns: - - a dict containing the overall results - - None on error - """ - # pylint: disable=too-many-locals,too-many-arguments - self.logger.info('Run Clearwater live test') - dns_file = '/etc/resolv.conf' - dns_file_bak = '/etc/resolv.conf.bak' - self.logger.debug('Backup %s -> %s', dns_file, dns_file_bak) - shutil.copy(dns_file, dns_file_bak) - cmd = ("dnsmasq -d -u root --server=/clearwater.opnfv/{0} " - "-r /etc/resolv.conf.bak".format(dns_ip)) - dnsmasq_process = subprocess.Popen(shlex.split(cmd)) - script = ('echo -e "nameserver {0}" > {1};' - 'cd {2};' - 'rake test[{3}] SIGNUP_CODE={4}' - .format('127.0.0.1', - dns_file, - self.test_dir, - public_domain, - signup_code)) - if bono_ip and ellis_ip: - subscript = ' PROXY={0} ELLIS={1}'.format(bono_ip, ellis_ip) - script = '{0}{1}'.format(script, subscript) - script = ('{0}{1}'.format(script, ' --trace')) - cmd = "/bin/bash -c '{0}'".format(script) - self.logger.debug('Live test cmd: %s', cmd) - output_file = os.path.join(self.result_dir, "ims_test_output.txt") - ft_utils.execute_command(cmd, - error_msg='Clearwater live test failed', - output_file=output_file) - dnsmasq_process.kill() - with open(dns_file_bak, 'r') as bak_file: - result = bak_file.read() - with open(dns_file, 'w') as dfile: - dfile.write(result) - - with open(output_file, 'r') as ofile: - result = ofile.read() - - if result != "": - self.logger.debug(result) - - vims_test_result = {} - try: - grp = re.search( - r'(\d+) failures out of (\d+) tests run.*' - r'(\d+) tests skipped', result, re.MULTILINE | re.DOTALL) - assert grp - vims_test_result["failures"] = int(grp.group(1)) - vims_test_result["total"] = int(grp.group(2)) - vims_test_result["skipped"] = int(grp.group(3)) - vims_test_result['passed'] = ( - int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1))) - except Exception: # pylint: disable=broad-except - self.logger.exception("Cannot parse live tests results") - return None - return vims_test_result diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.py b/functest/opnfv_tests/vnf/ims/cloudify_ims.py index 786c535ed..b93af7d6d 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.py +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.py @@ -14,532 +14,249 @@ from __future__ import division import logging import os import time -import uuid -from cloudify_rest_client import CloudifyClient -from cloudify_rest_client.executions import Execution -from scp import SCPClient +import pkg_resources import six -from snaps.config.flavor import FlavorConfig -from snaps.config.image import ImageConfig -from snaps.config.keypair import KeypairConfig -from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig -from snaps.config.router import RouterConfig -from snaps.config.security_group import ( - Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig) -from snaps.config.user import UserConfig -from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig -from snaps.openstack.create_flavor import OpenStackFlavor -from snaps.openstack.create_image import OpenStackImage -from snaps.openstack.create_instance import OpenStackVmInstance -from snaps.openstack.create_keypairs import OpenStackKeypair -from snaps.openstack.create_network import OpenStackNetwork -from snaps.openstack.create_router import OpenStackRouter -from snaps.openstack.create_security_group import OpenStackSecurityGroup -from snaps.openstack.create_user import OpenStackUser -from snaps.openstack.utils import keystone_utils -from xtesting.energy import energy -import yaml - -from functest.opnfv_tests.openstack.snaps import snaps_utils -from functest.opnfv_tests.vnf.ims import clearwater_ims_base + +from functest.core import cloudify +from functest.opnfv_tests.vnf.ims import clearwater from functest.utils import config from functest.utils import env +from functest.utils import functest_utils __author__ = "Valentin Boucher <valentin.boucher@orange.com>" -class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase): +class CloudifyIms(cloudify.Cloudify): """Clearwater vIMS deployed with Cloudify Orchestrator Case.""" __logger = logging.getLogger(__name__) + filename_alt = ('/home/opnfv/functest/images/' + 'ubuntu-14.04-server-cloudimg-amd64-disk1.img') + + flavor_alt_ram = 1024 + flavor_alt_vcpus = 1 + flavor_alt_disk = 3 + + quota_security_group = 20 + quota_security_group_rule = 100 + quota_port = 50 + + cop_yaml = ("https://github.com/cloudify-cosmo/cloudify-openstack-plugin/" + "releases/download/2.14.7/plugin.yaml") + cop_wgn = ("https://github.com/cloudify-cosmo/cloudify-openstack-plugin/" + "releases/download/2.14.7/cloudify_openstack_plugin-2.14.7-py27" + "-none-linux_x86_64-centos-Core.wgn") + def __init__(self, **kwargs): """Initialize CloudifyIms testcase object.""" if "case_name" not in kwargs: kwargs["case_name"] = "cloudify_ims" - super(CloudifyIms, self).__init__(**kwargs) + super().__init__(**kwargs) # Retrieve the configuration try: self.config = getattr( - config.CONF, 'vnf_{}_config'.format(self.case_name)) - except Exception: - raise Exception("VNF config file not found") + config.CONF, f'vnf_{self.case_name}_config') + except Exception as exc: + raise Exception("VNF config file not found") from exc + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/ims') config_file = os.path.join(self.case_dir, self.config) - self.orchestrator = dict( - requirements=get_config("orchestrator.requirements", config_file), - ) + self.details['orchestrator'] = dict( - name=get_config("orchestrator.name", config_file), - version=get_config("orchestrator.version", config_file), + name=functest_utils.get_parameter_from_yaml( + "orchestrator.name", config_file), + version=functest_utils.get_parameter_from_yaml( + "orchestrator.version", config_file), status='ERROR', result='' ) - self.__logger.debug("Orchestrator configuration %s", self.orchestrator) + self.vnf = dict( - descriptor=get_config("vnf.descriptor", config_file), - inputs=get_config("vnf.inputs", config_file), - requirements=get_config("vnf.requirements", config_file) + descriptor=functest_utils.get_parameter_from_yaml( + "vnf.descriptor", config_file), + inputs=functest_utils.get_parameter_from_yaml( + "vnf.inputs", config_file) ) self.details['vnf'] = dict( descriptor_version=self.vnf['descriptor']['version'], - name=get_config("vnf.name", config_file), - version=get_config("vnf.version", config_file), + name=functest_utils.get_parameter_from_yaml( + "vnf.name", config_file), + version=functest_utils.get_parameter_from_yaml( + "vnf.version", config_file), ) self.__logger.debug("VNF configuration: %s", self.vnf) self.details['test_vnf'] = dict( - name=get_config("vnf_test_suite.name", config_file), - version=get_config("vnf_test_suite.version", config_file) + name=functest_utils.get_parameter_from_yaml( + "vnf_test_suite.name", config_file), + version=functest_utils.get_parameter_from_yaml( + "vnf_test_suite.version", config_file) ) - self.images = get_config("tenant_images", config_file) - self.__logger.info("Images needed for vIMS: %s", self.images) - - def prepare(self): - """Prepare testscase (Additional pre-configuration steps).""" - super(CloudifyIms, self).prepare() - - self.__logger.info("Additional pre-configuration steps") - - compute_quotas = self.os_project.get_compute_quotas() - network_quotas = self.os_project.get_network_quotas() - for key, value in ( - self.vnf['requirements']['compute_quotas'].items()): - setattr(compute_quotas, key, value) + self.image_alt = None + self.flavor_alt = None + self.clearwater = None - for key, value in ( - self.vnf['requirements']['network_quotas'].items()): - setattr(network_quotas, key, value) + def check_requirements(self): + if env.get('NEW_USER_ROLE').lower() == "admin": + self.__logger.warning( + "Defining NEW_USER_ROLE=admin will easily break the testcase " + "because Cloudify doesn't manage tenancy (e.g. subnet " + "overlapping)") - compute_quotas = self.os_project.update_compute_quotas(compute_quotas) - network_quotas = self.os_project.update_network_quotas(network_quotas) - - def deploy_orchestrator(self): - # pylint: disable=too-many-locals,too-many-statements + def execute(self): """ Deploy Cloudify Manager. network, security group, fip, VM creation """ + assert super().execute() == 0 start_time = time.time() - - # orchestrator VM flavor - self.__logger.info("Get or create flavor for cloudify manager vm ...") - flavor_settings = FlavorConfig( - name="{}-{}".format( - self.orchestrator['requirements']['flavor']['name'], - self.uuid), - 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) - - self.__logger.info("Creating a second user to bypass issues ...") - user_creator = OpenStackUser( - self.snaps_creds, - UserConfig( - name='cloudify_network_bug-{}'.format(self.uuid), - password=str(uuid.uuid4()), - project_name=self.tenant_name, - domain_name=self.snaps_creds.user_domain_name, - roles={'_member_': self.tenant_name})) - user_creator.create() - self.created_object.append(user_creator) - - snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name) - self.__logger.debug("snaps creds: %s", snaps_creds) - - self.__logger.info("Creating keypair ...") - kp_file = os.path.join(self.data_dir, "cloudify_ims.pem") - keypair_settings = KeypairConfig( - name='cloudify_ims_kp-{}'.format(self.uuid), - private_filepath=kp_file) - keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings) - keypair_creator.create() - self.created_object.append(keypair_creator) - - # needs some images - self.__logger.info("Upload some OS images if it doesn't exist") - for image_name, image_file in six.iteritems(self.images): - self.__logger.info("image: %s, file: %s", image_name, image_file) - if image_file and image_name: - image_creator = OpenStackImage( - snaps_creds, - ImageConfig( - name=image_name, image_user='cloud', - img_format='qcow2', image_file=image_file)) - image_creator.create() - self.created_object.append(image_creator) - - # network creation - self.__logger.info("Creating full network ...") - subnet_settings = SubnetConfig( - name='cloudify_ims_subnet-{}'.format(self.uuid), - cidr='10.67.79.0/24', - dns_nameservers=[env.get('NAMESERVER')]) - network_settings = NetworkConfig( - name='cloudify_ims_network-{}'.format(self.uuid), - subnet_settings=[subnet_settings]) - network_creator = OpenStackNetwork(snaps_creds, network_settings) - network_creator.create() - self.created_object.append(network_creator) - ext_net_name = snaps_utils.get_ext_net_name(snaps_creds) - router_creator = OpenStackRouter( - snaps_creds, - RouterConfig( - name='cloudify_ims_router-{}'.format(self.uuid), - 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( - SecurityGroupRuleConfig( - sec_grp_name="sg-cloudify-manager-{}".format(self.uuid), - direction=Direction.ingress, protocol=Protocol.tcp, - port_range_min=1, port_range_max=65535)) - sg_rules.append( - SecurityGroupRuleConfig( - sec_grp_name="sg-cloudify-manager-{}".format(self.uuid), - direction=Direction.ingress, protocol=Protocol.udp, - port_range_min=1, port_range_max=65535)) - security_group_creator = OpenStackSecurityGroup( - snaps_creds, - SecurityGroupConfig( - name="sg-cloudify-manager-{}".format(self.uuid), - rule_settings=sg_rules)) - security_group_creator.create() - self.created_object.append(security_group_creator) - - image_settings = ImageConfig( - name=self.orchestrator['requirements']['os_image'], - image_user='centos', - exists=True) - port_settings = PortConfig( - name='cloudify_manager_port-{}'.format(self.uuid), - network_name=network_settings.name) - manager_settings = VmInstanceConfig( - name='cloudify_manager-{}'.format(self.uuid), - flavor=flavor_settings.name, - port_settings=[port_settings], - security_group_names=[ - security_group_creator.sec_grp_settings.name], - floating_ip_settings=[FloatingIpConfig( - name='cloudify_manager_fip-{}'.format(self.uuid), - port_name=port_settings.name, - router_name=router_creator.router_settings.name)]) - manager_creator = OpenStackVmInstance( - 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 = keystone_utils.get_endpoint(snaps_creds, 'identity') - + self.orig_cloud.set_network_quotas( + self.project.project.name, + security_group=self.quota_security_group, + security_group_rule=self.quota_security_group_rule, + port=self.quota_port) + self.__logger.info("Put OpenStack creds in manager") cfy_creds = dict( - keystone_username=snaps_creds.username, - keystone_password=snaps_creds.password, - keystone_tenant_name=snaps_creds.project_name, - keystone_url=public_auth_url, - region=snaps_creds.region_name if snaps_creds.region_name else ( - 'RegionOne'), - user_domain_name=snaps_creds.user_domain_name, - project_domain_name=snaps_creds.project_domain_name) + keystone_username=self.project.user.name, + keystone_password=self.project.password, + keystone_tenant_name=self.project.project.name, + keystone_url=self.get_public_auth_url(self.orig_cloud), + region=os.environ.get('OS_REGION_NAME', 'RegionOne'), + user_domain_name=os.environ.get( + 'OS_USER_DOMAIN_NAME', 'Default'), + project_domain_name=os.environ.get( + 'OS_PROJECT_DOMAIN_NAME', 'Default')) self.__logger.info("Set creds for cloudify manager %s", cfy_creds) - cfy_client = CloudifyClient( - host=manager_creator.get_floating_ip().ip, - username='admin', password='admin', tenant='default_tenant', - api_version='v3') - - self.orchestrator['object'] = cfy_client - - self.__logger.info("Attemps running status of the Manager") for loop in range(10): try: - self.__logger.debug( - "status %s", cfy_client.manager.get_status()) - cfy_status = cfy_client.manager.get_status()['status'] - self.__logger.info( - "The current manager status is %s", cfy_status) - if str(cfy_status) != 'running': - raise Exception("Cloudify Manager isn't up and running") - self.__logger.info("Put OpenStack creds in manager") - secrets_list = cfy_client.secrets.list() + secrets_list = self.cfy_client.secrets.list() for k, val in six.iteritems(cfy_creds): if not any(d.get('key', None) == k for d in secrets_list): - cfy_client.secrets.create(k, val) + self.cfy_client.secrets.create(k, val) else: - cfy_client.secrets.update(k, val) + self.cfy_client.secrets.update(k, val) break except Exception: # pylint: disable=broad-except - self.logger.info( - "try %s: Cloudify Manager isn't up and running", loop + 1) + self.__logger.info( + "try %s: Cannot create secrets", loop + 1) time.sleep(30) else: - self.logger.error("Cloudify Manager isn't up and running") - return False + self.__logger.error("Cannot create secrets") + return 1 duration = time.time() - start_time - if manager_creator.vm_ssh_active(block=True): - self.__logger.info("Put private keypair in manager") - ssh = manager_creator.ssh_client() - scp = SCPClient(ssh.get_transport(), socket_timeout=15.0) - scp.put(kp_file, '~/') - cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/" - self.run_blocking_ssh_command(ssh, cmd) - cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem" - self.run_blocking_ssh_command(ssh, cmd) - # cmd2 is badly unpinned by Cloudify - cmd = "sudo yum install -y gcc python-devel python-cmd2" - self.run_blocking_ssh_command( - ssh, cmd, "Unable to install packages on manager") - self.run_blocking_ssh_command(ssh, 'cfy status') - else: - self.__logger.error("Cannot connect to manager") - return False + self.put_private_key() + self.upload_cfy_plugins(self.cop_yaml, self.cop_wgn) self.details['orchestrator'].update(status='PASS', duration=duration) self.vnf['inputs'].update(dict( - external_network_name=ext_net_name, - network_name=network_settings.name, - key_pair_name=keypair_settings.name + external_network_name=self.ext_net.name, + network_name=self.network.name, + key_pair_name=self.keypair.name )) + if self.deploy_vnf() and self.test_vnf(): + self.result = 100 + return 0 self.result = 1/3 * 100 - return True + return 1 def deploy_vnf(self): """Deploy Clearwater IMS.""" start_time = time.time() + secgroups = self.cloud.list_security_groups( + filters={'name': 'default', + 'project_id': self.project.project.id}) + if secgroups: + secgroup = secgroups[0] + else: + self.__logger.error("No 'default' security group in project %s", + self.project.project.name) + return False + + self.cloud.create_security_group_rule( + secgroup.id, port_range_min=22, port_range_max=22, + protocol='tcp', direction='ingress') + self.__logger.info("Upload VNFD") - cfy_client = self.orchestrator['object'] descriptor = self.vnf['descriptor'] - cfy_client.blueprints.upload( + self.cfy_client.blueprints.upload( descriptor.get('file_name'), descriptor.get('name')) - self.__logger.info("Get or create flavor for all clearwater vm") - flavor_settings = FlavorConfig( - name="{}-{}".format( - self.vnf['requirements']['flavor']['name'], - self.uuid), - ram=self.vnf['requirements']['flavor']['ram_min'], - disk=25, - vcpus=2) - flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) - flavor_creator.create() - self.created_object.append(flavor_creator) + self.image_alt = self.publish_image_alt() + self.flavor_alt = self.create_flavor_alt() self.vnf['inputs'].update(dict( - flavor_id=flavor_settings.name, + image_id=self.image_alt.id, + flavor_id=self.flavor_alt.id, )) self.__logger.info("Create VNF Instance") - cfy_client.deployments.create(descriptor.get('name'), - descriptor.get('name'), - self.vnf.get('inputs')) + self.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')), + cloudify.wait_for_execution( + self.cfy_client, + cloudify.get_execution_id(self.cfy_client, descriptor.get('name')), self.__logger, timeout=300) self.__logger.info("Start the VNF Instance deployment") - execution = cfy_client.executions.start(descriptor.get('name'), - 'install') + execution = self.cfy_client.executions.start( + descriptor.get('name'), 'install') # Show execution log - execution = wait_for_execution( - cfy_client, execution, self.__logger, timeout=3600) - - duration = time.time() - start_time + execution = cloudify.wait_for_execution( + self.cfy_client, execution, self.__logger, timeout=3600) self.__logger.info(execution) - if execution.status == 'terminated': - self.details['vnf'].update(status='PASS', duration=duration) - self.result += 1/3 * 100 - result = True - else: - self.details['vnf'].update(status='FAIL', duration=duration) - result = False - return result + if execution.status != 'terminated': + self.details['vnf'].update(status='FAIL', + duration=time.time() - start_time) + return False + + ellis_ip = self.cfy_client.deployments.outputs.get( + self.vnf['descriptor'].get('name'))['outputs']['ellis_ip'] + bono_ip = self.cfy_client.deployments.outputs.get( + self.vnf['descriptor'].get('name'))['outputs']['bono_ip'] + self.clearwater = clearwater.ClearwaterTesting( + self.case_name, bono_ip, ellis_ip) + self.clearwater.availability_check() + + self.details['vnf'].update(status='PASS', + duration=time.time() - start_time) + self.result += 1/3 * 100 + return True def test_vnf(self): """Run test on clearwater ims instance.""" start_time = time.time() - - cfy_client = self.orchestrator['object'] - - outputs = cfy_client.deployments.outputs.get( - self.vnf['descriptor'].get('name'))['outputs'] - dns_ip = outputs['dns_ip'] - ellis_ip = outputs['ellis_ip'] - self.config_ellis(ellis_ip) - + dns_ip = self.cfy_client.deployments.outputs.get( + self.vnf['descriptor'].get('name'))['outputs']['dns_ip'] if not dns_ip: return False - - short_result = self.run_clearwater_live_test( - dns_ip=dns_ip, + short_result, vnf_test_rate = self.clearwater.run_clearwater_live_test( public_domain=self.vnf['inputs']["public_domain"]) duration = time.time() - start_time self.__logger.info(short_result) - self.details['test_vnf'].update(result=short_result, - duration=duration) - try: - vnf_test_rate = short_result['passed'] / ( - short_result['total'] - short_result['skipped']) - # orchestrator + vnf + test_vnf - self.result += vnf_test_rate / 3 * 100 - except ZeroDivisionError: - self.__logger.error("No test has been executed") - self.details['test_vnf'].update(status='FAIL') - return False - except Exception: # pylint: disable=broad-except - self.__logger.exception("Cannot calculate results") + self.details['test_vnf'].update(result=short_result, duration=duration) + self.result += vnf_test_rate / 3 * 100 + if vnf_test_rate == 0: self.details['test_vnf'].update(status='FAIL') - return False - return True if vnf_test_rate > 0 else False + return bool(vnf_test_rate > 0) def clean(self): """Clean created objects/functions.""" - 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 Exception: # 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), - force=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 Exception: # pylint: disable=broad-except - self.__logger.exception("Some issue during the undeployment ..") - - super(CloudifyIms, self).clean() - - @staticmethod - def run_blocking_ssh_command(ssh, cmd, - error_msg="Unable to run this command"): - """Command to run ssh command with the exit status.""" - _, stdout, stderr = ssh.exec_command(cmd) - CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read()) - if stdout.channel.recv_exit_status() != 0: - CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read()) - raise Exception(error_msg) - - @energy.enable_recording - def run(self, **kwargs): - """Execute CloudifyIms test case.""" - return super(CloudifyIms, self).run(**kwargs) - - -# ---------------------------------------------------------- -# -# 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=3600, ): - """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=True, - 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)) + self.kill_existing_execution(self.vnf['descriptor'].get('name')) + if self.image_alt: + self.cloud.delete_image(self.image_alt) + if self.flavor_alt: + self.orig_cloud.delete_flavor(self.flavor_alt.id) + super().clean() diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml index 6808cf33d..869281a20 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml @@ -1,35 +1,14 @@ --- -tenant_images: - ubuntu_14.04: - /home/opnfv/functest/images/ubuntu-14.04-server-cloudimg-amd64-disk1.img - cloudify_manager_4.0: - /home/opnfv/functest/images/cloudify-manager-premium-4.0.1.qcow2 orchestrator: name: cloudify version: '4.0' - requirements: - flavor: - name: cloudify.medium - ram_min: 4096 - os_image: 'cloudify_manager_4.0' vnf: name: clearwater - version: '107' + version: '129' descriptor: - file_name: /src/vims/openstack-blueprint.yaml + file_name: /src/cloudify_vims/openstack-blueprint.yaml name: clearwater-opnfv - version: '122' - requirements: - flavor: - name: cloudify.small - ram_min: 2048 - compute_quotas: - cores: 50 - instances: 15 - network_quotas: - security_group: 20 - security_group_rule: 100 - port: 50 + version: '129' inputs: image_id: 'ubuntu_14.04' flavor_id: 'cloudify.small' @@ -46,4 +25,4 @@ vnf: homer_cluster_size: 1 vnf_test_suite: name: clearwater-live-test - version: "1.0" + version: '1.0' diff --git a/functest/opnfv_tests/vnf/ims/heat_ims.py b/functest/opnfv_tests/vnf/ims/heat_ims.py new file mode 100644 index 000000000..0d4e345a0 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/heat_ims.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python + +# Copyright (c) 2018 Kontron, Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +"""HeatIms testcase implementation.""" + +from __future__ import division + +import logging +import os +import re +import time +import tempfile + +import paramiko +import pkg_resources +from xtesting.core import testcase + +from functest.core import singlevm +from functest.opnfv_tests.vnf.ims import clearwater +from functest.utils import config +from functest.utils import env +from functest.utils import functest_utils + +__author__ = "Valentin Boucher <valentin.boucher@kontron.com>" + + +class HeatIms(singlevm.VmReady2): + # pylint: disable=too-many-instance-attributes + """Clearwater vIMS deployed with Heat Orchestrator Case.""" + + __logger = logging.getLogger(__name__) + + filename = ('/home/opnfv/functest/images/' + 'ubuntu-14.04-server-cloudimg-amd64-disk1.img') + + flavor_ram = 1024 + flavor_vcpus = 1 + flavor_disk = 3 + + quota_security_group = 20 + quota_security_group_rule = 100 + quota_port = 50 + + parameters = { + 'private_mgmt_net_cidr': '192.168.100.0/24', + 'private_mgmt_net_gateway': '192.168.100.254', + 'private_mgmt_net_pool_start': '192.168.100.1', + 'private_mgmt_net_pool_end': '192.168.100.253'} + + def __init__(self, **kwargs): + """Initialize HeatIms testcase object.""" + if "case_name" not in kwargs: + kwargs["case_name"] = "heat_ims" + super().__init__(**kwargs) + + # Retrieve the configuration + try: + self.config = getattr( + config.CONF, f'vnf_{self.case_name}_config') + except Exception as exc: + raise Exception("VNF config file not found") from exc + + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/ims') + config_file = os.path.join(self.case_dir, self.config) + + self.vnf = dict( + descriptor=functest_utils.get_parameter_from_yaml( + "vnf.descriptor", config_file), + parameters=functest_utils.get_parameter_from_yaml( + "vnf.inputs", config_file) + ) + self.details['vnf'] = dict( + descriptor_version=self.vnf['descriptor']['version'], + name=functest_utils.get_parameter_from_yaml( + "vnf.name", config_file), + version=functest_utils.get_parameter_from_yaml( + "vnf.version", config_file), + ) + self.__logger.debug("VNF configuration: %s", self.vnf) + self.keypair = None + self.stack = None + self.clearwater = None + self.role = None + (_, self.key_filename) = tempfile.mkstemp() + + def create_network_resources(self): + pass + + def execute(self): + # pylint: disable=too-many-locals,too-many-statements + """ + Prepare Tenant/User + + network, security group, fip, VM creation + """ + self.orig_cloud.set_network_quotas( + self.project.project.name, + security_group=self.quota_security_group, + security_group_rule=self.quota_security_group_rule, + port=self.quota_port) + if not self.orig_cloud.get_role("heat_stack_owner"): + self.role = self.orig_cloud.create_role("heat_stack_owner") + self.orig_cloud.grant_role( + "heat_stack_owner", user=self.project.user.id, + project=self.project.project.id, + domain=self.project.domain.id) + self.keypair = self.cloud.create_keypair( + f'{self.case_name}-kp_{self.guid}') + self.__logger.info("keypair:\n%s", self.keypair.private_key) + with open( + self.key_filename, 'w', encoding='utf-8') as private_key_file: + private_key_file.write(self.keypair.private_key) + + if self.deploy_vnf() and self.test_vnf(): + self.result = 100 + return 0 + self.result = 1/3 * 100 + return 1 + + def run(self, **kwargs): + """Deploy and test clearwater + + Here are the main actions: + - deploy clearwater stack via heat + - test the vnf instance + + Returns: + - TestCase.EX_OK + - TestCase.EX_RUN_ERROR on error + """ + status = testcase.TestCase.EX_RUN_ERROR + try: + assert self.cloud + assert super().run( + **kwargs) == testcase.TestCase.EX_OK + self.result = 0 + if not self.execute(): + self.result = 100 + status = testcase.TestCase.EX_OK + except Exception: # pylint: disable=broad-except + self.__logger.exception('Cannot run %s', self.case_name) + finally: + self.stop_time = time.time() + return status + + def _monit(self, username="ubuntu", timeout=60): + servers = self.cloud.list_servers(detailed=True) + self.__logger.debug("servers: %s", servers) + for server in servers: + if 'ns' in server.name: + break + self.__logger.info("server:\n%s", server.name) + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy()) + ssh.connect( + server.public_v4, username=username, + key_filename=self.key_filename, timeout=timeout) + (_, stdout, _) = ssh.exec_command('sudo monit summary') + self.__logger.info("output:\n%s", stdout.read().decode("utf-8")) + ssh.close() + + def deploy_vnf(self): + """Deploy Clearwater IMS.""" + start_time = time.time() + descriptor = self.vnf['descriptor'] + parameters = self.vnf['parameters'] + + parameters['public_mgmt_net_id'] = self.ext_net.id + parameters['flavor'] = self.flavor.name + parameters['image'] = self.image.name + parameters['key_name'] = self.keypair.name + parameters['external_mgmt_dns_ip'] = env.get('NAMESERVER') + parameters.update(self.parameters) + + self.__logger.info("Create Heat Stack") + self.stack = self.cloud.create_stack( + name=descriptor.get('name'), + template_file=descriptor.get('file_name'), + wait=True, **parameters) + self.__logger.debug("stack: %s", self.stack) + + self._monit() + + servers = self.cloud.list_servers(detailed=True) + self.__logger.debug("servers: %s", servers) + for server in servers: + if not self.check_regex_in_console( + server.name, regex='Cloud-init .* finished at ', loop=1): + return False + if 'ellis' in server.name: + self.__logger.debug("ellis: %s", server) + ellis_ip = server.public_v4 + elif 'bono' in server.name: + self.__logger.debug("bono: %s", server) + bono_ip = server.public_v4 + + assert ellis_ip + assert bono_ip + self.clearwater = clearwater.ClearwaterTesting( + self.case_name, bono_ip, ellis_ip) + # This call can take time and many retry because Heat is + # an infrastructure orchestrator so when Heat say "stack created" + # it means that all OpenStack ressources are created but not that + # Clearwater are up and ready (Cloud-Init script still running) + self.clearwater.availability_check() + + duration = time.time() - start_time + + self.details['vnf'].update(status='PASS', duration=duration) + self.result += 1/3 * 100 + + return True + + def test_vnf(self): + """Run test on clearwater ims instance.""" + start_time = time.time() + outputs = self.cloud.get_stack(self.stack.id).outputs + self.__logger.debug("stack outputs: %s", outputs) + dns_ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', str(outputs))[0] + if not dns_ip: + return False + short_result, vnf_test_rate = self.clearwater.run_clearwater_live_test( + public_domain=self.vnf['parameters']["zone"]) + duration = time.time() - start_time + self.__logger.info(short_result) + self.details['test_vnf'] = dict(result=short_result, duration=duration) + self.result += vnf_test_rate / 3 * 100 + if vnf_test_rate == 0: + self.details['test_vnf'].update(status='FAIL') + self._monit() + return bool(vnf_test_rate > 0) + + def clean(self): + """Clean created objects/functions.""" + assert self.cloud + try: + if self.stack: + self.cloud.delete_stack(self.stack.id, wait=True) + except TypeError: + # shade raises TypeError exceptions when checking stack status + pass + except Exception: # pylint: disable=broad-except + self.__logger.exception("Cannot clean stack ressources") + super().clean() + if self.role: + self.orig_cloud.delete_role(self.role.id) diff --git a/functest/opnfv_tests/vnf/ims/heat_ims.yaml b/functest/opnfv_tests/vnf/ims/heat_ims.yaml new file mode 100644 index 000000000..2ccdc0bf7 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/heat_ims.yaml @@ -0,0 +1,22 @@ +--- +orchestrator: + name: heat + version: '4.0' +vnf: + name: clearwater + version: '130' + descriptor: + file_name: /src/heat_vims/clearwater.yaml + name: clearwater-opnfv + version: '130' + inputs: + zone: clearwater.opnfv + dn_range_start: "6505550000" + dn_range_length: "1000" + bono_cluster_size: 1 + sprout_cluster_size: 1 + vellum_cluster_size: 1 + dime_cluster_size: 1 + homer_cluster_size: 1 + dnssec_key: + GkBraPnditvP2Em4oXV5wUTawmZaGGuO+Jt3ZnFkznGV3zFoQ+Ak13nuuOnO0JV5FqAr/KitdW6siqjXSjROXg== diff --git a/functest/opnfv_tests/vnf/router/cloudify_vrouter.py b/functest/opnfv_tests/vnf/router/cloudify_vrouter.py index e56f23cfc..32d675347 100644 --- a/functest/opnfv_tests/vnf/router/cloudify_vrouter.py +++ b/functest/opnfv_tests/vnf/router/cloudify_vrouter.py @@ -14,64 +14,54 @@ import logging import os import time -import uuid - -from cloudify_rest_client import CloudifyClient -from cloudify_rest_client.executions import Execution -from scp import SCPClient -import six -from snaps.config.flavor import FlavorConfig -from snaps.config.image import ImageConfig -from snaps.config.keypair import KeypairConfig -from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig -from snaps.config.router import RouterConfig -from snaps.config.security_group import ( - Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig) -from snaps.config.user import UserConfig -from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig -from snaps.openstack.create_flavor import OpenStackFlavor -from snaps.openstack.create_image import OpenStackImage -from snaps.openstack.create_instance import OpenStackVmInstance -from snaps.openstack.create_keypairs import OpenStackKeypair -from snaps.openstack.create_network import OpenStackNetwork -from snaps.openstack.create_security_group import OpenStackSecurityGroup -from snaps.openstack.create_router import OpenStackRouter -from snaps.openstack.create_user import OpenStackUser -import snaps.openstack.utils.glance_utils as glance_utils -from snaps.openstack.utils import keystone_utils - -from functest.opnfv_tests.openstack.snaps import snaps_utils -import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base + +import pkg_resources + +from functest.core import cloudify +from functest.opnfv_tests.vnf.router import vrouter_base from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf from functest.utils import config from functest.utils import env from functest.utils import functest_utils + __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>" -class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): +class CloudifyVrouter(cloudify.Cloudify): # pylint: disable=too-many-instance-attributes """vrouter testcase deployed with Cloudify Orchestrator.""" __logger = logging.getLogger(__name__) - name = __name__ + + filename_alt = '/home/opnfv/functest/images/vyos-1.1.8-amd64.qcow2' + + flavor_alt_ram = 1024 + flavor_alt_vcpus = 1 + flavor_alt_disk = 3 + + check_console_loop = 12 + + cop_yaml = ("https://github.com/cloudify-cosmo/cloudify-openstack-plugin/" + "releases/download/2.14.7/plugin.yaml") + cop_wgn = ("https://github.com/cloudify-cosmo/cloudify-openstack-plugin/" + "releases/download/2.14.7/cloudify_openstack_plugin-2.14.7-py27" + "-none-linux_x86_64-centos-Core.wgn") def __init__(self, **kwargs): if "case_name" not in kwargs: kwargs["case_name"] = "vyos_vrouter" - super(CloudifyVrouter, self).__init__(**kwargs) + super().__init__(**kwargs) # Retrieve the configuration try: self.config = getattr( - config.CONF, 'vnf_{}_config'.format(self.case_name)) - except Exception: - raise Exception("VNF config file not found") - - self.cfy_manager_ip = '' - self.deployment_name = '' + config.CONF, f'vnf_{self.case_name}_config') + except Exception as exc: + raise Exception("VNF config file not found") from exc + self.case_dir = pkg_resources.resource_filename( + 'functest', 'opnfv_tests/vnf/router') config_file = os.path.join(self.case_dir, self.config) self.orchestrator = dict( requirements=functest_utils.get_parameter_from_yaml( @@ -86,7 +76,7 @@ class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): result='' ) self.__logger.debug("Orchestrator configuration %s", self.orchestrator) - self.__logger.debug("name = %s", self.name) + self.__logger.debug("name = %s", __name__) self.vnf = dict( descriptor=functest_utils.get_parameter_from_yaml( "vnf.descriptor", config_file), @@ -105,6 +95,10 @@ class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): self.__logger.debug("VNF configuration: %s", self.vnf) self.util = Utilvnf() + self.util.set_credentials(self.cloud) + credentials = {"cloud": self.cloud} + self.util_info = {"credentials": credentials, + "vnf_data_dir": self.util.vnf_data_dir} self.details['test_vnf'] = dict( name=functest_utils.get_parameter_from_yaml( @@ -116,263 +110,94 @@ class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): "tenant_images", config_file) self.__logger.info("Images needed for vrouter: %s", self.images) - @staticmethod - def run_blocking_ssh_command(ssh, cmd, - error_msg="Unable to run this command"): - """Command to run ssh command with the exit status.""" - (_, stdout, stderr) = ssh.exec_command(cmd) - CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read()) - if stdout.channel.recv_exit_status() != 0: - CloudifyVrouter.__logger.error( - "SSH %s stderr: %s", cmd, stderr.read()) - raise Exception(error_msg) - - def prepare(self): - super(CloudifyVrouter, self).prepare() - self.__logger.info("Additional pre-configuration steps") - self.util.set_credentials(self.snaps_creds) - - def deploy_orchestrator(self): + self.image_alt = None + self.flavor_alt = None + + def check_requirements(self): + if env.get('NEW_USER_ROLE').lower() == "admin": + self.__logger.warning( + "Defining NEW_USER_ROLE=admin will easily break the testcase " + "because Cloudify doesn't manage tenancy (e.g. subnet " + "overlapping)") + + def execute(self): # pylint: disable=too-many-locals,too-many-statements """ Deploy Cloudify Manager. network, security group, fip, VM creation """ # network creation + super().execute() start_time = time.time() + self.put_private_key() + self.upload_cfy_plugins(self.cop_yaml, self.cop_wgn) - # orchestrator VM flavor - self.__logger.info("Get or create flavor for cloudify manager vm ...") - flavor_settings = FlavorConfig( - name="{}-{}".format( - self.orchestrator['requirements']['flavor']['name'], - self.uuid), - 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) - - user_creator = OpenStackUser( - self.snaps_creds, - UserConfig( - name='cloudify_network_bug-{}'.format(self.uuid), - password=str(uuid.uuid4()), - project_name=self.tenant_name, - domain_name=self.snaps_creds.user_domain_name, - roles={'_member_': self.tenant_name})) - user_creator.create() - self.created_object.append(user_creator) - - snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name) - self.__logger.debug("snaps creds: %s", snaps_creds) - - self.__logger.info("Creating keypair ...") - kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem") - keypair_settings = KeypairConfig( - name='cloudify_vrouter_kp-{}'.format(self.uuid), - private_filepath=kp_file) - keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings) - keypair_creator.create() - self.created_object.append(keypair_creator) - - self.__logger.info("Upload some OS images if it doesn't exist") - for image_name, image_file in six.iteritems(self.images): - self.__logger.info("image: %s, file: %s", image_name, image_file) - if image_file and image_name: - image_creator = OpenStackImage( - snaps_creds, - ImageConfig( - name=image_name, image_user='cloud', - img_format='qcow2', image_file=image_file)) - image_creator.create() - self.created_object.append(image_creator) - - self.__logger.info("Creating full network ...") - subnet_settings = SubnetConfig( - name='cloudify_vrouter_subnet-{}'.format(self.uuid), - cidr='10.67.79.0/24', - dns_nameservers=[env.get('NAMESERVER')]) - network_settings = NetworkConfig( - name='cloudify_vrouter_network-{}'.format(self.uuid), - subnet_settings=[subnet_settings]) - network_creator = OpenStackNetwork(snaps_creds, network_settings) - network_creator.create() - self.created_object.append(network_creator) - ext_net_name = snaps_utils.get_ext_net_name(snaps_creds) - router_creator = OpenStackRouter( - snaps_creds, - RouterConfig( - name='cloudify_vrouter_router-{}'.format(self.uuid), - 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( - SecurityGroupRuleConfig( - sec_grp_name="sg-cloudify-manager-{}".format(self.uuid), - direction=Direction.ingress, - protocol=Protocol.tcp, port_range_min=1, - port_range_max=65535)) - sg_rules.append( - SecurityGroupRuleConfig( - sec_grp_name="sg-cloudify-manager-{}".format(self.uuid), - direction=Direction.ingress, - protocol=Protocol.udp, port_range_min=1, - port_range_max=65535)) - security_group_creator = OpenStackSecurityGroup( - snaps_creds, - SecurityGroupConfig( - name="sg-cloudify-manager-{}".format(self.uuid), - rule_settings=sg_rules)) - security_group_creator.create() - self.created_object.append(security_group_creator) - - image_settings = ImageConfig( - name=self.orchestrator['requirements']['os_image'], - image_user='centos', exists=True) - port_settings = PortConfig( - name='cloudify_manager_port-{}'.format(self.uuid), - network_name=network_settings.name) - manager_settings = VmInstanceConfig( - name='cloudify_manager-{}'.format(self.uuid), - flavor=flavor_settings.name, - port_settings=[port_settings], - security_group_names=[ - security_group_creator.sec_grp_settings.name], - floating_ip_settings=[FloatingIpConfig( - name='cloudify_manager_fip-{}'.format(self.uuid), - port_name=port_settings.name, - router_name=router_creator.router_settings.name)]) - manager_creator = OpenStackVmInstance( - snaps_creds, manager_settings, image_settings, - keypair_settings) - - self.__logger.info("Creating cloudify manager VM") - manager_creator.create() - self.created_object.append(manager_creator) - - cfy_client = CloudifyClient( - host=manager_creator.get_floating_ip().ip, - username='admin', password='admin', tenant='default_tenant', - api_version='v3') - - self.orchestrator['object'] = cfy_client - - self.cfy_manager_ip = manager_creator.get_floating_ip().ip - - self.__logger.info("Attemps running status of the Manager") - for loop in range(10): - try: - self.__logger.debug( - "status %s", cfy_client.manager.get_status()) - cfy_status = cfy_client.manager.get_status()['status'] - self.__logger.info( - "The current manager status is %s", cfy_status) - if str(cfy_status) != 'running': - raise Exception("Cloudify Manager isn't up and running") - break - except Exception: # pylint: disable=broad-except - self.logger.info( - "try %s: Cloudify Manager isn't up and running", loop + 1) - time.sleep(30) - else: - self.logger.error("Cloudify Manager isn't up and running") - return False + self.image_alt = self.publish_image_alt() + self.flavor_alt = self.create_flavor_alt() 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/" - self.run_blocking_ssh_command(ssh, cmd) - cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem" - self.run_blocking_ssh_command(ssh, cmd) - # cmd2 is badly unpinned by Cloudify - cmd = "sudo yum install -y gcc python-devel python-cmd2" - self.run_blocking_ssh_command( - ssh, cmd, "Unable to install packages on manager") - else: - self.__logger.error("Cannot connect to manager") - return False - self.details['orchestrator'].update(status='PASS', duration=duration) - self.__logger.info("Get or create flavor for vrouter") - flavor_settings = FlavorConfig( - name="{}-{}".format( - self.vnf['requirements']['flavor']['name'], - self.uuid), - 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(snaps_creds) - image = glance_utils.get_image(glance, "vyos1.1.7") - self.vnf['inputs'].update(dict(external_network_name=ext_net_name)) - self.vnf['inputs'].update(dict(target_vnf_image_id=image.id)) - self.vnf['inputs'].update(dict(reference_vnf_image_id=image.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=snaps_creds.username)) + external_network_name=self.ext_net.name)) self.vnf['inputs'].update(dict( - keystone_password=snaps_creds.password)) + target_vnf_image_id=self.image_alt.id)) self.vnf['inputs'].update(dict( - keystone_tenant_name=snaps_creds.project_name)) + reference_vnf_image_id=self.image_alt.id)) self.vnf['inputs'].update(dict( - keystone_user_domain_name=snaps_creds.user_domain_name)) + target_vnf_flavor_id=self.flavor_alt.id)) self.vnf['inputs'].update(dict( - keystone_project_domain_name=snaps_creds.project_domain_name)) + reference_vnf_flavor_id=self.flavor_alt.id)) self.vnf['inputs'].update(dict( - region=snaps_creds.region_name if snaps_creds.region_name else ( - 'RegionOne'))) + keystone_username=self.project.user.name)) self.vnf['inputs'].update(dict( - keystone_url=keystone_utils.get_endpoint( - snaps_creds, 'identity'))) - - credentials = {"snaps_creds": snaps_creds} - self.util_info = {"credentials": credentials, - "cfy": cfy_client, - "vnf_data_dir": self.util.vnf_data_dir} + keystone_password=self.project.password)) + self.vnf['inputs'].update(dict( + keystone_tenant_name=self.project.project.name)) + self.vnf['inputs'].update(dict( + keystone_user_domain_name=os.environ.get( + 'OS_USER_DOMAIN_NAME', 'Default'))) + self.vnf['inputs'].update(dict( + keystone_project_domain_name=os.environ.get( + 'OS_PROJECT_DOMAIN_NAME', 'Default'))) + self.vnf['inputs'].update(dict( + region=os.environ.get('OS_REGION_NAME', 'RegionOne'))) + self.vnf['inputs'].update(dict( + keystone_url=self.get_public_auth_url(self.orig_cloud))) - return True + if self.deploy_vnf() and self.test_vnf(): + self.result = 100 + return 0 + self.result = 1/3 * 100 + return 1 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') + self.util_info["cfy"] = self.cfy_client + self.util_info["cfy_manager_ip"] = self.fip.floating_ip_address + self.util_info["deployment_name"] = descriptor.get('name') - cfy_client.blueprints.upload( + self.cfy_client.blueprints.upload( descriptor.get('file_name'), descriptor.get('name')) self.__logger.info("Create VNF Instance") - cfy_client.deployments.create( + self.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')), + cloudify.wait_for_execution( + self.cfy_client, cloudify.get_execution_id( + self.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') + execution = self.cfy_client.executions.start( + descriptor.get('name'), 'install') # Show execution log - execution = wait_for_execution(cfy_client, execution, self.__logger) + execution = cloudify.wait_for_execution( + self.cfy_client, execution, self.__logger) duration = time.time() - start_time @@ -387,7 +212,8 @@ class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): def test_vnf(self): start_time = time.time() - result, test_result_data = super(CloudifyVrouter, self).test_vnf() + testing = vrouter_base.VrouterOnBoardingBase(self.util, self.util_info) + result, test_result_data = testing.test_vnf() duration = time.time() - start_time if result: self.details['test_vnf'].update( @@ -400,91 +226,9 @@ class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase): 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 Exception: # 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 Exception: # pylint: disable=broad-except - self.__logger.exception("Some issue during the undeployment ..") - - super(CloudifyVrouter, self).clean() - - 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) - - -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=True, 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)) + self.kill_existing_execution(self.vnf['descriptor'].get('name')) + if self.image_alt: + self.cloud.delete_image(self.image_alt) + if self.flavor_alt: + self.orig_cloud.delete_flavor(self.flavor_alt.id) + super().clean() diff --git a/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml b/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml index 649cd6ccd..2d98dffa5 100644 --- a/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml +++ b/functest/opnfv_tests/vnf/router/cloudify_vrouter.yaml @@ -3,9 +3,6 @@ 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: 'fraser' orchestrator: name: cloudify version: '4.0' 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 index be7bee889..9eb3c5d69 100644 --- a/functest/opnfv_tests/vnf/router/test_controller/function_test_exec.py +++ b/functest/opnfv_tests/vnf/router/test_controller/function_test_exec.py @@ -12,6 +12,7 @@ """vrouter function test execution module""" import logging +import os import time import yaml @@ -20,7 +21,7 @@ from functest.opnfv_tests.vnf.router.vnf_controller.vnf_controller import ( VnfController) -class FunctionTestExec(object): +class FunctionTestExec(): """vrouter function test execution class""" logger = logging.getLogger(__name__) @@ -31,17 +32,16 @@ class FunctionTestExec(object): 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() + with open( + os.path.join( + self.util.vnf_data_dir, self.util.command_template_dir, + self.util.test_cmd_map_yaml_file), + 'r', encoding='utf-8') as test_cmd_map_file: + self.test_cmd_map_yaml = yaml.safe_load(test_cmd_map_file) - self.util.set_credentials(credentials["snaps_creds"]) + self.util.set_credentials(credentials["cloud"]) - with open(self.util.test_env_config_yaml) as file_fd: + with open(self.util.test_env_config_yaml, encoding='utf-8') as file_fd: test_env_config_yaml = yaml.safe_load(file_fd) file_fd.close() diff --git a/functest/opnfv_tests/vnf/router/utilvnf.py b/functest/opnfv_tests/vnf/router/utilvnf.py index 31e1b9196..111f20c1a 100644 --- a/functest/opnfv_tests/vnf/router/utilvnf.py +++ b/functest/opnfv_tests/vnf/router/utilvnf.py @@ -14,13 +14,9 @@ import json import logging import os -import pkg_resources import requests import yaml -from git import Repo -from snaps.openstack.utils import nova_utils - from functest.utils import config RESULT_SPRIT_INDEX = { @@ -47,22 +43,19 @@ NUMBER_OF_DIGITS_FOR_AVG_JITTER = 3 NUMBER_OF_DIGITS_FOR_AVG_PKT_LOSS = 1 -class Utilvnf(object): # pylint: disable=too-many-instance-attributes +class Utilvnf(): # pylint: disable=too-many-instance-attributes """ Utility class of vrouter testcase """ logger = logging.getLogger(__name__) def __init__(self): - self.snaps_creds = "" self.vnf_data_dir = getattr(config.CONF, 'dir_router_data') - 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/" @@ -71,29 +64,7 @@ class Utilvnf(object): # pylint: disable=too-many-instance-attributes 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 = getattr( - config.CONF, '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: + with open(self.test_env_config_yaml, encoding='utf-8') as file_fd: test_env_config_yaml = yaml.safe_load(file_fd) file_fd.close() @@ -107,71 +78,27 @@ class Utilvnf(object): # pylint: disable=too-many-instance-attributes os.remove(self.test_result_json_file) self.logger.debug("removed %s", self.test_result_json_file) - def get_nova_client(self): - nova_client = nova_utils.nova_client(self.snaps_creds) - - return nova_client + self.cloud = None - def set_credentials(self, snaps_creds): - self.snaps_creds = snaps_creds + def set_credentials(self, cloud): + self.cloud = cloud 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 - + server = self.cloud.get_server(server_name) 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 - + server = self.cloud.get_server(server_name) 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) - + url = f"http://{cfy_manager_ip}/deployments/{deployment_name}/outputs" response = requests.get( url, auth=requests.auth.HTTPBasicAuth('admin', 'admin'), @@ -200,15 +127,10 @@ class Utilvnf(object): # pylint: disable=too-many-instance-attributes 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 %s", 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 %s", vnf["vnf_name"]) - self.delete_vm(vnf["vnf_name"]) + self.cloud.delete_server(vnf["vnf_name"]) def get_vnf_info_list(self, cfy_manager_ip, topology_deploy_name, target_vnf_name): @@ -288,24 +210,29 @@ class Utilvnf(object): # pylint: disable=too-many-instance-attributes 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() + with open( + self.test_result_json_file, "w", + encoding="utf-8") as file_fd: + pass else: - file_fd = open(self.test_result_json_file, "r") - test_result = json.load(file_fd) - file_fd.close() + with open( + self.test_result_json_file, "r", + encoding="utf-8") as file_fd: + test_result = json.load(file_fd) test_result.append(result_data) - file_fd = open(self.test_result_json_file, "w") - json.dump(test_result, file_fd) - file_fd.close() + with open( + self.test_result_json_file, "w", + encoding="utf-8") as file_fd: + json.dump(test_result, file_fd) 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() + with open( + self.test_result_json_file, "r", + encoding="utf-8") as file_fd: + test_result = json.load(file_fd) output_json_data = json.dumps(test_result, sort_keys=True, indent=4) @@ -315,8 +242,6 @@ class Utilvnf(object): # pylint: disable=too-many-instance-attributes @staticmethod def get_test_scenario(file_path): - test_scenario_file = open(file_path, - 'r') - test_scenario_yaml = yaml.safe_load(test_scenario_file) - test_scenario_file.close() + with open(file_path, "r", encoding="utf-8") as test_scenario_file: + test_scenario_yaml = yaml.safe_load(test_scenario_file) return test_scenario_yaml["test_scenario_list"] diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/checker.py b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py index a7a70f6d7..d3a216ed0 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/checker.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py @@ -18,7 +18,7 @@ import re from jinja2 import Environment, FileSystemLoader -class Checker(object): +class Checker(): """vrouter test result check class""" logger = logging.getLogger(__name__) diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py index 7d9116bcc..a86a16485 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py @@ -15,7 +15,7 @@ import logging from jinja2 import Environment, FileSystemLoader -class CommandGenerator(object): +class CommandGenerator(): """command generator class for vrouter testing""" logger = logging.getLogger(__name__) diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py index c5f554cbd..269f6526b 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py @@ -24,7 +24,7 @@ DEFAULT_CONNECT_RETRY_COUNT = 10 DEFAULT_SEND_TIMEOUT = 10 -class SshClient(object): # pylint: disable=too-many-instance-attributes +class SshClient(): # pylint: disable=too-many-instance-attributes """ssh client class for vrouter testing""" logger = logging.getLogger(__name__) @@ -43,7 +43,7 @@ class SshClient(object): # pylint: disable=too-many-instance-attributes self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.util = Utilvnf() - with open(self.util.test_env_config_yaml) as file_fd: + with open(self.util.test_env_config_yaml, encoding='utf-8') as file_fd: test_env_config_yaml = yaml.safe_load(file_fd) file_fd.close() @@ -80,7 +80,7 @@ class SshClient(object): # pylint: disable=too-many-instance-attributes retrycount -= 1 if retrycount == 0: - self.logger.warn( + self.logger.warning( "Cannot establish connection to IP '%s'", self.ip_address) self.connected = False return self.connected @@ -110,7 +110,7 @@ class SshClient(object): # pylint: disable=too-many-instance-attributes cmd) break - res_buff += res + res_buff += res.decode("utf-8") self.logger.debug("Response : '%s'", res_buff) return res_buff diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py index 79acc776f..2210b3909 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py @@ -23,7 +23,7 @@ from functest.opnfv_tests.vnf.router.vnf_controller.ssh_client import ( SshClient) -class VmController(object): +class VmController(): """vm controll class""" logger = logging.getLogger(__name__) @@ -34,14 +34,12 @@ class VmController(object): credentials = util_info["credentials"] self.util = Utilvnf() - self.util.set_credentials(credentials["snaps_creds"]) + self.util.set_credentials(credentials["cloud"]) - with open(self.util.test_env_config_yaml) as file_fd: + with open(self.util.test_env_config_yaml, encoding='utf-8') 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( @@ -85,16 +83,10 @@ class VmController(object): result = ssh.connect(self.ssh_connect_timeout, self.ssh_connect_retry_count) if not result: - self.logger.warn("Reboot %s", vm_info["vnf_name"]) - 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: - self.logger.error( - "Cannot establish connection to IP '%s'. Aborting!", - ssh.ip_address) - return None + self.logger.error( + "Cannot establish connection to IP '%s'. Aborting!", + ssh.ip_address) + return None (result, _) = self.command_create_and_execute( ssh, @@ -109,10 +101,8 @@ class VmController(object): 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() + with open(prompt_file_path, 'r', encoding='utf-8') as prompt_file: + prompt = yaml.safe_load(prompt_file) config_mode_prompt = prompt["config_mode"] commands = self.command_gen_from_template(test_cmd_file_path, diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py index a5b1ad856..46584456f 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py @@ -26,7 +26,7 @@ from functest.opnfv_tests.vnf.router.vnf_controller.vm_controller import ( VmController) -class VnfController(object): +class VnfController(): """vrouter controll class""" logger = logging.getLogger(__name__) @@ -36,7 +36,7 @@ class VnfController(object): self.util = Utilvnf() self.vm_controller = VmController(util_info) - with open(self.util.test_env_config_yaml) as file_fd: + with open(self.util.test_env_config_yaml, encoding='utf-8') as file_fd: test_env_config_yaml = yaml.safe_load(file_fd) file_fd.close() @@ -49,10 +49,9 @@ class VnfController(object): def config_vnf(self, source_vnf, destination_vnf, test_cmd_file_path, parameter_file_path, prompt_file_path): # pylint: disable=too-many-arguments - parameter_file = open(parameter_file_path, - 'r') - cmd_input_param = yaml.safe_load(parameter_file) - parameter_file.close() + with open( + parameter_file_path, 'r', encoding='utf-8') as parameter_file: + cmd_input_param = yaml.safe_load(parameter_file) cmd_input_param["macaddress"] = source_vnf["data_plane_network_mac"] cmd_input_param["source_ip"] = source_vnf["data_plane_network_ip"] @@ -71,19 +70,16 @@ class VnfController(object): res_dict_data_list = [] - parameter_file = open(parameter_file_path, - 'r') - cmd_input_param = yaml.safe_load(parameter_file) - parameter_file.close() + with open( + parameter_file_path, 'r', encoding='utf-8') as parameter_file: + cmd_input_param = yaml.safe_load(parameter_file) 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() + with open(prompt_file_path, 'r', encoding='utf-8') as prompt_file: + prompt = yaml.safe_load(prompt_file) terminal_mode_prompt = prompt["terminal_mode"] ssh = SshClient(target_vnf["floating_ip"], diff --git a/functest/opnfv_tests/vnf/router/vrouter_base.py b/functest/opnfv_tests/vnf/router/vrouter_base.py index 6c4e5ce0d..932770b9c 100644 --- a/functest/opnfv_tests/vnf/router/vrouter_base.py +++ b/functest/opnfv_tests/vnf/router/vrouter_base.py @@ -19,37 +19,22 @@ import time import pkg_resources -import functest.core.vnf as vnf -from functest.utils import config 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): +class VrouterOnBoardingBase(): """vrouter testing base class""" - def __init__(self, **kwargs): + def __init__(self, util, util_info): 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 = getattr(config.CONF, 'dir_router_data') - self.result_dir = os.path.join(getattr(config.CONF, 'dir_results'), - self.case_name) - self.util = Utilvnf() - self.util_info = {} - + self.util = util + self.util_info = 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 @@ -89,10 +74,6 @@ class VrouterOnBoardingBase(vnf.VnfOnBoarding): 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) @@ -117,6 +98,7 @@ class VrouterOnBoardingBase(vnf.VnfOnBoarding): return result, test_result_data def get_vnf_info_list(self, target_vnf_name): - # pylint: disable=unused-argument,no-self-use - vnf_info_list = [] - return vnf_info_list + return self.util.get_vnf_info_list( + self.util_info["cfy_manager_ip"], + self.util_info["deployment_name"], + target_vnf_name) diff --git a/functest/tests/unit/ci/__init__.py b/functest/tests/unit/ci/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/functest/tests/unit/ci/__init__.py +++ /dev/null diff --git a/functest/tests/unit/ci/test_check_deployment.py b/functest/tests/unit/ci/test_check_deployment.py deleted file mode 100644 index aeeca5871..000000000 --- a/functest/tests/unit/ci/test_check_deployment.py +++ /dev/null @@ -1,286 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2017 Ericsson 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 - -# pylint: disable=missing-docstring - -import socket -import unittest - -import logging -import mock - -from functest.ci import check_deployment - -__author__ = "Jose Lausuch <jose.lausuch@ericsson.com>" - - -class CheckDeploymentTesting(unittest.TestCase): - """The super class which testing classes could inherit.""" - # pylint: disable=missing-docstring,too-many-public-methods - - logging.disable(logging.CRITICAL) - - def setUp(self): - self.client_test = mock.Mock() - self.deployment = check_deployment.CheckDeployment() - self.service_test = 'compute' - self.rc_file = self.deployment.rc_file - self.endpoint_test = 'http://192.168.0.6:5000/v3' - creds_attr = {'auth_url': self.endpoint_test, - 'proxy_settings': ''} - proxy_attr = {'host': '192.168.0.1', 'port': '5000'} - proxy_settings = mock.Mock() - proxy_settings.configure_mock(**proxy_attr) - self.os_creds = mock.Mock() - self.os_creds.configure_mock(**creds_attr) - self.os_creds.proxy_settings = proxy_settings - self.deployment.os_creds = self.os_creds - - @mock.patch('socket.socket.connect', side_effect=TypeError) - def test_verify_connectivity_ko1(self, *args): - self.assertFalse(check_deployment.verify_connectivity("127.0.0.1")) - args[0].assert_called_once_with((None, 80)) - - @mock.patch('socket.socket.connect', side_effect=socket.error) - def test_verify_connectivity_ko2(self, *args): - self.assertFalse( - check_deployment.verify_connectivity("http://127.0.0.1")) - args[0].assert_called_once_with(("127.0.0.1", 80)) - - @mock.patch('socket.socket.connect', side_effect=socket.error) - def test_verify_connectivity_ko3(self, *args): - self.assertFalse( - check_deployment.verify_connectivity("https://127.0.0.1")) - args[0].assert_called_once_with(("127.0.0.1", 443)) - - @mock.patch('socket.socket.connect') - def test_verify_connectivity(self, *args): - self.assertTrue( - check_deployment.verify_connectivity("https://127.0.0.1")) - args[0].assert_called_once_with(("127.0.0.1", 443)) - - @mock.patch('snaps.openstack.utils.keystone_utils.keystone_session', - return_value=mock.Mock( - get_token=mock.Mock(side_effect=Exception))) - def test_get_auth_token_ko(self, *args): - with self.assertRaises(Exception): - check_deployment.get_auth_token(self.os_creds) - args[0].assert_called_once_with(self.os_creds) - - @mock.patch('snaps.openstack.utils.keystone_utils.keystone_session', - return_value=mock.Mock( - get_token=mock.Mock(return_value="foo"))) - def test_get_auth_token(self, *args): - self.assertEqual(check_deployment.get_auth_token(self.os_creds), "foo") - args[0].assert_called_once_with(self.os_creds) - - @mock.patch('six.moves.builtins.open', - mock.mock_open(read_data='OS_AUTH_URL')) - @mock.patch('functest.ci.check_deployment.os.path.isfile', returns=True) - def test_check_rc(self, *args): - self.deployment.check_rc() - args[0].assert_called_once_with(self.rc_file) - - @mock.patch('functest.ci.check_deployment.os.path.isfile', - return_value=False) - def test_check_rc_missing_file(self, *args): - with self.assertRaises(Exception) as context: - self.deployment.check_rc() - args[0].assert_called_once_with(self.rc_file) - msg = 'RC file {} does not exist!'.format(self.rc_file) - self.assertTrue(msg in str(context.exception)) - - @mock.patch('six.moves.builtins.open', - mock.mock_open(read_data='test')) - @mock.patch('functest.ci.check_deployment.os.path.isfile', - return_value=True) - def test_check_rc_missing_os_auth(self, *args): - with self.assertRaises(Exception) as context: - self.deployment.check_rc() - args[0].assert_called_once_with(self.rc_file) - msg = 'OS_AUTH_URL not defined in {}.'.format(self.rc_file) - self.assertTrue(msg in str(context.exception)) - - @mock.patch('functest.ci.check_deployment.get_auth_token', - return_value='gAAAAABaOhXGS') - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=True) - def test_check_auth_endpoint(self, *args): - self.deployment.check_auth_endpoint() - args[0].assert_called_once_with(self.endpoint_test) - args[1].assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=False) - def test_check_auth_endpoint_ko(self, *args): - with self.assertRaises(Exception) as context: - self.deployment.check_auth_endpoint() - msg = "OS_AUTH_URL {} is not reachable.".format(self.os_creds.auth_url) - args[0].assert_called_once_with(self.os_creds.auth_url) - self.assertTrue(msg in str(context.exception)) - - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=True) - @mock.patch('functest.ci.check_deployment.keystone_utils.get_endpoint') - def test_check_public_endpoint(self, *args): - args[0].return_value = self.endpoint_test - self.deployment.check_public_endpoint() - args[0].assert_called_once_with( - mock.ANY, 'identity', interface='public') - args[1].assert_called_once_with(self.endpoint_test) - - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=False) - @mock.patch('functest.ci.check_deployment.keystone_utils.get_endpoint') - def test_check_public_endpoint_ko(self, *args): - args[0].return_value = self.endpoint_test - with self.assertRaises(Exception) as context: - self.deployment.check_public_endpoint() - args[0].assert_called_once_with( - mock.ANY, 'identity', interface='public') - args[1].assert_called_once_with(self.endpoint_test) - msg = "Public endpoint {} is not reachable.".format(self.endpoint_test) - self.assertTrue(msg in str(context.exception)) - - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=True) - @mock.patch('functest.ci.check_deployment.keystone_utils.get_endpoint') - def test_check_service_endpoint(self, *args): - self.deployment.check_service_endpoint(self.service_test) - args[0].assert_called_once_with( - mock.ANY, self.service_test, interface='public') - args[1].assert_called_once_with(args[0].return_value) - - @mock.patch('functest.ci.check_deployment.verify_connectivity', - return_value=False) - @mock.patch('functest.ci.check_deployment.keystone_utils.get_endpoint') - def test_check_service_endpoint_ko(self, *args): - args[0].return_value = self.endpoint_test - with self.assertRaises(Exception) as context: - self.deployment.check_service_endpoint(self.service_test) - msg = "{} endpoint {} is not reachable.".format( - self.service_test, self.endpoint_test) - self.assertTrue(msg in str(context.exception)) - args[0].assert_called_once_with( - mock.ANY, self.service_test, interface='public') - args[1].assert_called_once_with(args[0].return_value) - - @mock.patch('functest.ci.check_deployment.nova_utils.nova_client') - def test_check_nova(self, mock_method): - self.deployment.check_nova() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.nova_utils.nova_client', - return_value=mock.Mock( - servers=mock.Mock(list=mock.Mock(side_effect=Exception)))) - def test_check_nova_fail(self, mock_method): - with self.assertRaises(Exception): - self.deployment.check_nova() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.neutron_utils.neutron_client') - def test_check_neutron(self, mock_method): - self.deployment.check_neutron() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.neutron_utils.neutron_client', - return_value=mock.Mock( - list_networks=mock.Mock(side_effect=Exception))) - def test_check_neutron_fail(self, mock_method): - with self.assertRaises(Exception): - self.deployment.check_neutron() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.glance_utils.glance_client') - def test_check_glance(self, mock_method): - self.deployment.check_glance() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.glance_utils.glance_client', - return_value=mock.Mock( - images=mock.Mock(list=mock.Mock(side_effect=Exception)))) - def test_check_glance_fail(self, mock_method): - with self.assertRaises(Exception): - self.deployment.check_glance() - mock_method.assert_called_once_with(mock.ANY) - - @mock.patch('functest.ci.check_deployment.LOGGER.info') - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_utils.' - 'get_ext_net_name', return_value='ext-net') - def test_check_extnet(self, *args): - self.deployment.check_ext_net() - args[0].assert_called_once_with(mock.ANY) - args[1].assert_called_once_with( - "External network found: %s", "ext-net") - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_utils.' - 'get_ext_net_name', return_value='') - def test_check_extnet_none(self, mock_getext): - with self.assertRaises(Exception) as context: - self.deployment.check_ext_net() - self.assertTrue(mock_getext.called) - msg = 'ERROR: No external networks in the deployment.' - self.assertTrue(msg in str(context.exception)) - - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_rc', - side_effect=Exception) - def test_check_all_exc1(self, *args): - with self.assertRaises(Exception): - self.deployment.check_all() - args[0].assert_called_once_with() - - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials', - side_effect=Exception) - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_rc') - def test_check_all_exc2(self, *args): - with self.assertRaises(Exception): - self.deployment.check_all() - args[0].assert_called_once_with() - args[1].assert_called_once_with( - os_env_file=self.rc_file, proxy_settings_str=None, - ssh_proxy_cmd=None) - - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials', - return_value=None) - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_rc') - def test_check_all_exc3(self, *args): - with self.assertRaises(Exception): - self.deployment.check_all() - args[0].assert_called_once_with() - args[1].assert_called_once_with( - os_env_file=self.rc_file, proxy_settings_str=None, - ssh_proxy_cmd=None) - - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_ext_net') - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_glance') - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_neutron') - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_nova') - @mock.patch( - 'functest.ci.check_deployment.CheckDeployment.check_service_endpoint') - @mock.patch( - 'functest.ci.check_deployment.CheckDeployment.check_public_endpoint') - @mock.patch( - 'functest.ci.check_deployment.CheckDeployment.check_auth_endpoint') - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials') - @mock.patch('functest.ci.check_deployment.CheckDeployment.check_rc') - def test_check_all(self, *args): - self.assertEqual(self.deployment.check_all(), 0) - for i in [0, 2, 3, 5, 6, 7, 8]: - args[i].assert_called_once_with() - args[1].assert_called_once_with( - os_env_file=self.rc_file, proxy_settings_str=None, - ssh_proxy_cmd=None) - calls = [mock.call('compute'), mock.call('network'), - mock.call('image')] - args[4].assert_has_calls(calls) - - -if __name__ == "__main__": - logging.disable(logging.CRITICAL) - unittest.main(verbosity=2) diff --git a/functest/tests/unit/core/test_vnf.py b/functest/tests/unit/core/test_vnf.py deleted file mode 100644 index 81d9eef76..000000000 --- a/functest/tests/unit/core/test_vnf.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 Orange and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 - -# pylint: disable=missing-docstring - -import logging -import unittest - -import mock -from snaps.openstack.os_credentials import OSCreds -from xtesting.core import testcase - -from functest.core import vnf -from functest.utils import constants - - -class VnfBaseTesting(unittest.TestCase): - """The class testing VNF.""" - # pylint: disable=missing-docstring,too-many-public-methods - - tenant_name = 'test_tenant_name' - tenant_description = 'description' - - def setUp(self): - self.test = vnf.VnfOnBoarding(project='functest', case_name='foo') - - def test_run_deploy_orch_exc(self): - with mock.patch.object(self.test, 'prepare'), \ - mock.patch.object(self.test, 'deploy_orchestrator', - side_effect=Exception) as mock_method, \ - mock.patch.object(self.test, 'deploy_vnf', - return_value=True), \ - mock.patch.object(self.test, 'test_vnf', - return_value=True): - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - mock_method.assert_called_with() - - def test_run_deploy_vnf_exc(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=True), \ - mock.patch.object(self.test, 'deploy_vnf', - side_effect=Exception) as mock_method: - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - mock_method.assert_called_with() - - def test_run_test_vnf_exc(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=True), \ - mock.patch.object(self.test, 'deploy_vnf', return_value=True), \ - mock.patch.object(self.test, 'test_vnf', - side_effect=Exception) as mock_method: - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - mock_method.assert_called_with() - - def test_run_deploy_orch_ko(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=False), \ - mock.patch.object(self.test, 'deploy_vnf', - return_value=True), \ - mock.patch.object(self.test, 'test_vnf', - return_value=True): - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - - def test_run_vnf_deploy_ko(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=True), \ - mock.patch.object(self.test, 'deploy_vnf', - return_value=False), \ - mock.patch.object(self.test, 'test_vnf', - return_value=True): - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - - def test_run_vnf_test_ko(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=True), \ - mock.patch.object(self.test, 'deploy_vnf', - return_value=True), \ - mock.patch.object(self.test, 'test_vnf', - return_value=False): - self.assertEqual(self.test.run(), - testcase.TestCase.EX_TESTCASE_FAILED) - - def test_run_default(self): - with mock.patch.object(self.test, 'prepare'),\ - mock.patch.object(self.test, 'deploy_orchestrator', - return_value=True), \ - mock.patch.object(self.test, 'deploy_vnf', - return_value=True), \ - mock.patch.object(self.test, 'test_vnf', - return_value=True): - self.assertEqual(self.test.run(), testcase.TestCase.EX_OK) - - @mock.patch('functest.core.vnf.OpenStackUser') - @mock.patch('functest.core.vnf.OpenStackProject') - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials', - side_effect=Exception) - def test_prepare_exc1(self, *args): - with self.assertRaises(Exception): - self.test.prepare() - args[0].assert_called_with(os_env_file=constants.ENV_FILE) - args[1].assert_not_called() - args[2].assert_not_called() - - @mock.patch('functest.core.vnf.OpenStackUser') - @mock.patch('functest.core.vnf.OpenStackProject', side_effect=Exception) - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials') - def test_prepare_exc2(self, *args): - with self.assertRaises(Exception): - self.test.prepare() - args[0].assert_called_with(os_env_file=constants.ENV_FILE) - args[1].assert_called_with(mock.ANY, mock.ANY) - args[2].assert_not_called() - - @mock.patch('functest.core.vnf.OpenStackUser', side_effect=Exception) - @mock.patch('snaps.openstack.utils.keystone_utils.get_role_by_name', - return_value="admin") - @mock.patch('snaps.openstack.utils.keystone_utils.keystone_client') - @mock.patch('functest.core.vnf.OpenStackProject') - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials') - def test_prepare_exc3(self, *args): - with self.assertRaises(Exception): - self.test.prepare() - args[0].assert_called_with(os_env_file=constants.ENV_FILE) - args[1].assert_called_with(mock.ANY, mock.ANY) - args[2].assert_called_with(mock.ANY) - args[3].assert_called_with(mock.ANY, mock.ANY) - args[4].assert_called_with(mock.ANY, mock.ANY) - - @mock.patch('functest.core.vnf.OpenStackUser') - @mock.patch('snaps.openstack.utils.keystone_utils.get_role_by_name', - return_value="admin") - @mock.patch('snaps.openstack.utils.keystone_utils.keystone_client') - @mock.patch('functest.core.vnf.OpenStackProject') - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials') - def test_prepare_default(self, *args): - self.assertEqual(self.test.prepare(), testcase.TestCase.EX_OK) - args[0].assert_called_with(os_env_file=constants.ENV_FILE) - args[1].assert_called_with(mock.ANY, mock.ANY) - args[2].assert_called_with(mock.ANY) - args[3].assert_called_with(mock.ANY, mock.ANY) - args[4].assert_called_with(mock.ANY, mock.ANY) - - def test_deploy_vnf_unimplemented(self): - with self.assertRaises(vnf.VnfDeploymentException): - self.test.deploy_vnf() - - def test_test_vnf_unimplemented(self): - with self.assertRaises(vnf.VnfTestException): - self.test.test_vnf() - - def test_deploy_orch_unimplemented(self): - self.assertTrue(self.test.deploy_orchestrator()) - - @mock.patch('snaps.openstack.tests.openstack_tests.get_credentials', - return_value=OSCreds( - username='user', password='pass', - auth_url='http://foo.com:5000/v3', project_name='bar'), - side_effect=Exception) - def test_prepare_keystone_client_ko(self, *args): - with self.assertRaises(vnf.VnfPreparationException): - self.test.prepare() - args[0].assert_called_once() - - def test_vnf_clean_exc(self): - obj = mock.Mock() - obj.clean.side_effect = Exception - self.test.created_object = [obj] - self.test.clean() - obj.clean.assert_called_with() - - def test_vnf_clean(self): - obj = mock.Mock() - self.test.created_object = [obj] - self.test.clean() - obj.clean.assert_called_with() - - -if __name__ == "__main__": - logging.disable(logging.CRITICAL) - unittest.main(verbosity=2) diff --git a/functest/tests/unit/odl/test_odl.py b/functest/tests/unit/odl/test_odl.py index 58a699f0b..c675c2988 100644 --- a/functest/tests/unit/odl/test_odl.py +++ b/functest/tests/unit/odl/test_odl.py @@ -33,10 +33,10 @@ class ODLTesting(unittest.TestCase): logging.disable(logging.CRITICAL) _keystone_ip = "127.0.0.1" - _neutron_url = u"https://127.0.0.1:9696" - _neutron_id = u"dummy" + _neutron_url = "https://127.0.0.1:9696" + _neutron_id = "dummy" _sdn_controller_ip = "127.0.0.3" - _os_auth_url = "http://{}:5000/v3".format(_keystone_ip) + _os_auth_url = f"http://{_keystone_ip}:5000/v3" _os_projectname = "admin" _os_username = "admin" _os_password = "admin" @@ -49,7 +49,7 @@ class ODLTesting(unittest.TestCase): _os_interface = "public" def setUp(self): - for var in ("INSTALLER_TYPE", "SDN_CONTROLLER", "SDN_CONTROLLER_IP"): + for var in ("SDN_CONTROLLER", "SDN_CONTROLLER_IP"): if var in os.environ: del os.environ[var] os.environ["OS_AUTH_URL"] = self._os_auth_url @@ -63,8 +63,7 @@ class ODLTesting(unittest.TestCase): self.test = odl.ODLTests(case_name='odl', project_name='functest') self.defaultargs = {'odlusername': self._odl_username, 'odlpassword': self._odl_password, - 'neutronurl': "http://{}:9696".format( - self._keystone_ip), + 'neutronurl': f"http://{self._keystone_ip}:9696", 'osauthurl': self._os_auth_url, 'osusername': self._os_username, 'osuserdomainname': self._os_userdomainname, @@ -105,7 +104,7 @@ class ODLRobotTesting(ODLTesting): mock_method.assert_called_once_with( os.path.join(odl.ODLTests.odl_test_repo, 'csit/variables/Variables.robot'), inplace=True) - self.assertEqual(args[0].getvalue(), "{}\n".format(msg2)) + self.assertEqual(args[0].getvalue(), f"{msg2}\n") def test_set_vars_auth_default(self): self._test_set_vars( @@ -160,19 +159,19 @@ class ODLMainTesting(ODLTesting): args[0].assert_called_once_with(self.test.odl_variables_file) if len(args) > 1: variable = [ - 'KEYSTONEURL:{}://{}'.format( - urllib.parse.urlparse(self._os_auth_url).scheme, - urllib.parse.urlparse(self._os_auth_url).netloc), - 'NEUTRONURL:{}'.format(self._neutron_url), - 'OS_AUTH_URL:"{}"'.format(self._os_auth_url), - 'OSUSERNAME:"{}"'.format(self._os_username), - 'OSUSERDOMAINNAME:"{}"'.format(self._os_userdomainname), - 'OSTENANTNAME:"{}"'.format(self._os_projectname), - 'OSPROJECTDOMAINNAME:"{}"'.format(self._os_projectdomainname), - 'OSPASSWORD:"{}"'.format(self._os_password), - 'ODL_SYSTEM_IP:{}'.format(self._sdn_controller_ip), - 'PORT:{}'.format(self._odl_webport), - 'RESTCONFPORT:{}'.format(self._odl_restconfport)] + ('KEYSTONEURL:' + f'{urllib.parse.urlparse(self._os_auth_url).scheme}://' + f'{urllib.parse.urlparse(self._os_auth_url).netloc}'), + f'NEUTRONURL:{self._neutron_url}', + f'OS_AUTH_URL:"{self._os_auth_url}"', + f'OSUSERNAME:"{self._os_username}"', + f'OSUSERDOMAINNAME:"{self._os_userdomainname}"', + f'OSTENANTNAME:"{self._os_projectname}"', + f'OSPROJECTDOMAINNAME:"{self._os_projectdomainname}"', + f'OSPASSWORD:"{self._os_password}"', + f'ODL_SYSTEM_IP:{self._sdn_controller_ip}', + f'PORT:{self._odl_webport}', + f'RESTCONFPORT:{self._odl_restconfport}'] args[1].assert_called_once_with( odl.ODLTests.basic_suite_dir, odl.ODLTests.neutron_suite_dir, include=[], @@ -247,10 +246,34 @@ class ODLMainTesting(ODLTesting): @mock.patch('os.makedirs') @mock.patch('robot.run') @mock.patch('os.path.isfile', return_value=True) + def test_generate_report_ko(self, *args): + with mock.patch.object(self.test, 'set_robotframework_vars', + return_value=True), \ + mock.patch.object(self.test, 'parse_results'), \ + mock.patch.object(self.test, 'generate_report', + return_value=1): + self._test_run_suites(testcase.TestCase.EX_OK, *args) + + @mock.patch('os.makedirs') + @mock.patch('robot.run') + @mock.patch('os.path.isfile', return_value=True) + def test_generate_report_exc(self, *args): + with mock.patch.object(self.test, 'set_robotframework_vars', + return_value=True), \ + mock.patch.object(self.test, 'parse_results'), \ + mock.patch.object(self.test, 'generate_report', + side_effect=Exception): + self._test_run_suites(testcase.TestCase.EX_RUN_ERROR, *args) + + @mock.patch('os.makedirs') + @mock.patch('robot.run') + @mock.patch('os.path.isfile', return_value=True) def test_ok(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results'): + mock.patch.object(self.test, 'parse_results'), \ + mock.patch.object(self.test, 'generate_report', + return_value=0): self._test_run_suites(testcase.TestCase.EX_OK, *args) @mock.patch('os.makedirs') @@ -259,7 +282,9 @@ class ODLMainTesting(ODLTesting): def test_ok_no_creds(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True) as mock_method, \ - mock.patch.object(self.test, 'parse_results'): + mock.patch.object(self.test, 'parse_results'), \ + mock.patch.object(self.test, 'generate_report', + return_value=0): self._test_run_suites(testcase.TestCase.EX_OK, *args) mock_method.assert_not_called() @@ -269,7 +294,9 @@ class ODLMainTesting(ODLTesting): def test_testcases_in_failure(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results'): + mock.patch.object(self.test, 'parse_results'), \ + mock.patch.object(self.test, 'generate_report', + return_value=0): self._test_run_suites(testcase.TestCase.EX_OK, *args) @@ -505,58 +532,6 @@ class ODLRunTesting(ODLTesting): odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) - def test_fuel_no_controller_ip(self): - os.environ["INSTALLER_TYPE"] = "fuel" - self._test_missing_value() - - def test_fuel(self): - os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip - os.environ["INSTALLER_TYPE"] = "fuel" - self._test_run(testcase.TestCase.EX_OK, None, - odlip=self._sdn_controller_ip, - odlwebport='8282', - odlrestconfport='8282') - - def test_apex_no_controller_ip(self): - os.environ["INSTALLER_TYPE"] = "apex" - self._test_missing_value() - - def test_apex(self): - os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip - os.environ["INSTALLER_TYPE"] = "apex" - self._test_run(testcase.TestCase.EX_OK, None, - odlip=self._sdn_controller_ip, odlwebport='8081', - odlrestconfport='8081') - - def test_netvirt_no_controller_ip(self): - os.environ["INSTALLER_TYPE"] = "netvirt" - self._test_missing_value() - - def test_netvirt(self): - os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip - os.environ["INSTALLER_TYPE"] = "netvirt" - self._test_run(testcase.TestCase.EX_OK, None, - odlip=self._sdn_controller_ip, odlwebport='8081', - odlrestconfport='8081') - - def test_compass(self): - os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip - os.environ["INSTALLER_TYPE"] = "compass" - self._test_run(testcase.TestCase.EX_OK, None, - odlip=self._sdn_controller_ip, - odlrestconfport='8080') - - def test_daisy_no_controller_ip(self): - os.environ["INSTALLER_TYPE"] = "daisy" - self._test_missing_value() - - def test_daisy(self): - os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip - os.environ["INSTALLER_TYPE"] = "daisy" - self._test_run(testcase.TestCase.EX_OK, None, - odlip=self._sdn_controller_ip, odlwebport='8181', - odlrestconfport='8087') - class ODLArgParserTesting(ODLTesting): @@ -565,7 +540,7 @@ class ODLArgParserTesting(ODLTesting): def setUp(self): self.parser = odl.ODLParser() - super(ODLArgParserTesting, self).setUp() + super().setUp() def test_default(self): self.assertEqual(self.parser.parse_args(), self.defaultargs) @@ -575,8 +550,8 @@ class ODLArgParserTesting(ODLTesting): self.defaultargs['odlip'] = self._sdn_controller_ip self.assertEqual( self.parser.parse_args( - ["--neutronurl={}".format(self._neutron_url), - "--odlip={}".format(self._sdn_controller_ip)]), + [f"--neutronurl={self._neutron_url}", + f"--odlip={self._sdn_controller_ip}"]), self.defaultargs) @mock.patch('sys.stderr', new_callable=six.StringIO) @@ -589,7 +564,7 @@ class ODLArgParserTesting(ODLTesting): def _test_arg(self, arg, value): self.defaultargs[arg] = value self.assertEqual( - self.parser.parse_args(["--{}={}".format(arg, value)]), + self.parser.parse_args([f"--{arg}={value}"]), self.defaultargs) def test_odlusername(self): @@ -630,7 +605,7 @@ class ODLArgParserTesting(ODLTesting): def test_pushtodb(self): self.defaultargs['pushtodb'] = True - self.assertEqual(self.parser.parse_args(["--{}".format('pushtodb')]), + self.assertEqual(self.parser.parse_args(["--pushtodb"]), self.defaultargs) def test_multiple_args(self): @@ -638,8 +613,8 @@ class ODLArgParserTesting(ODLTesting): self.defaultargs['odlip'] = self._sdn_controller_ip self.assertEqual( self.parser.parse_args( - ["--neutronurl={}".format(self._neutron_url), - "--odlip={}".format(self._sdn_controller_ip)]), + [f"--neutronurl={self._neutron_url}", + f"--odlip={self._sdn_controller_ip}"]), self.defaultargs) diff --git a/functest/opnfv_tests/openstack/snaps/__init__.py b/functest/tests/unit/openstack/cinder/__init__.py index e69de29bb..e69de29bb 100644 --- a/functest/opnfv_tests/openstack/snaps/__init__.py +++ b/functest/tests/unit/openstack/cinder/__init__.py diff --git a/functest/tests/unit/openstack/cinder/test_cinder.py b/functest/tests/unit/openstack/cinder/test_cinder.py new file mode 100644 index 000000000..d3c9cabb6 --- /dev/null +++ b/functest/tests/unit/openstack/cinder/test_cinder.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python + +# Copyright (c) 2018 Enea AB 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 + +# pylint: disable=missing-docstring + + +import logging +import unittest + +import pkg_resources +import mock +import munch +import shade + +from functest.opnfv_tests.openstack.cinder import cinder_test +from functest.utils import config +from functest.utils import env + + +class CinderTesting(unittest.TestCase): + + def setUp(self): + with mock.patch('functest.core.singlevm.SingleVm2.__init__'): + self.cinder = cinder_test.CinderCheck() + self.cinder.cloud = mock.Mock() + self.cinder.case_name = 'cinder' + self.cinder.guid = '1' + + @mock.patch('functest.opnfv_tests.openstack.cinder.cinder_test.' + 'CinderCheck.connect') + @mock.patch('functest.core.singlevm.SingleVm2.prepare', + side_effect=Exception) + def test_prepare_exc1(self, *args): + self.cinder.cloud.boot_vm = mock.Mock() + with self.assertRaises(Exception): + self.cinder.prepare() + args[0].assert_called_once_with() + args[1].assert_not_called() + self.cinder.cloud.boot_vm.assert_not_called() + self.cinder.cloud.create_volume.assert_not_called() + + @mock.patch('functest.opnfv_tests.openstack.cinder.cinder_test.' + 'CinderCheck.connect') + @mock.patch('functest.opnfv_tests.openstack.cinder.cinder_test.' + 'CinderCheck.boot_vm', + side_effect=Exception) + @mock.patch('functest.core.singlevm.SingleVm2.prepare') + def test_prepare_exc2(self, *args): + self.cinder.sec = munch.Munch(id='foo') + self.cinder.keypair = munch.Munch(id='foo') + self.cinder.volume_timeout = munch.Munch(id='foo') + with self.assertRaises(Exception): + self.cinder.prepare() + args[0].assert_called_with() + args[1].assert_called_once_with( + f'{self.cinder.case_name}-vm2_{self.cinder.guid}', + security_groups=[self.cinder.sec.id], + key_name=self.cinder.keypair.id) + self.cinder.cloud.create_volume.assert_not_called() + args[2].assert_not_called() + + @mock.patch('functest.opnfv_tests.openstack.cinder.cinder_test.' + 'CinderCheck.boot_vm', return_value=munch.Munch(id='vm2')) + @mock.patch('functest.core.singlevm.SingleVm2.prepare') + def test_prepare(self, *args): + self.cinder.sec = munch.Munch(id='foo') + self.cinder.keypair = munch.Munch(id='foo') + self.cinder.ext_net = mock.Mock(id='foo') + self.cinder.ssh2 = mock.Mock() + self.cinder.fip2 = munch.Munch(id='fip2') + self.cinder.connect = mock.Mock( + return_value=(self.cinder.fip2, self.cinder.ssh2)) + self.cinder.cloud.create_volume = mock.Mock( + return_value=munch.Munch()) + self.cinder.prepare() + args[0].assert_called_once_with() + args[1].assert_called_once_with( + f'{self.cinder.case_name}-vm2_{self.cinder.guid}', + security_groups=[self.cinder.sec.id], + key_name=self.cinder.keypair.id) + self.cinder.connect.assert_called_once_with(args[1].return_value) + self.cinder.cloud.create_volume.assert_called_once_with( + name=f'{self.cinder.case_name}-volume_{self.cinder.guid}', + size='2', timeout=self.cinder.volume_timeout, wait=True) + + @mock.patch('scp.SCPClient.put') + def test_write(self, *args): + # pylint: disable=protected-access + self.cinder.ssh = mock.Mock() + self.cinder.sshvm = mock.Mock(id='foo') + self.cinder.volume = mock.Mock(id='volume') + stdout = mock.Mock() + stdout.channel.recv_exit_status.return_value = 0 + self.cinder.ssh.exec_command.return_value = (None, stdout, mock.Mock()) + self.assertEqual(self.cinder._write_data(), 0) + self.cinder.ssh.exec_command.assert_called_once_with( + f"sh ~/write_data.sh {env.get('VOLUME_DEVICE_NAME')}") + self.cinder.cloud.attach_volume.assert_called_once_with( + self.cinder.sshvm, self.cinder.volume, + timeout=self.cinder.volume_timeout) + self.cinder.cloud.detach_volume.assert_called_once_with( + self.cinder.sshvm, self.cinder.volume, + timeout=self.cinder.volume_timeout) + args[0].assert_called_once_with( + pkg_resources.resource_filename( + 'functest.opnfv_tests.openstack.cinder', 'write_data.sh'), + remote_path="~/") + + @mock.patch('scp.SCPClient.put', side_effect=Exception) + def test_write_exc1(self, *args): + # pylint: disable=protected-access + self.cinder.ssh = mock.Mock() + self.cinder.sshvm = mock.Mock(id='foo') + self.cinder.cloud.attach_volume = mock.Mock() + self.assertEqual( + self.cinder._write_data(), self.cinder.EX_RUN_ERROR) + args[0].assert_called_once_with( + pkg_resources.resource_filename( + 'functest.opnfv_tests.openstack.cinder', 'write_data.sh'), + remote_path="~/") + + @mock.patch('scp.SCPClient.put') + def test_read(self, *args): + # pylint: disable=protected-access + self.cinder.ssh2 = mock.Mock() + self.cinder.vm2 = mock.Mock(id='foo') + self.cinder.volume = mock.Mock(id='volume') + stdout = mock.Mock() + self.cinder.ssh2.exec_command.return_value = ( + None, stdout, mock.Mock()) + stdout.channel.recv_exit_status.return_value = 0 + self.assertEqual(self.cinder._read_data(), 0) + self.cinder.ssh2.exec_command.assert_called_once_with( + f"sh ~/read_data.sh {env.get('VOLUME_DEVICE_NAME')}") + self.cinder.cloud.attach_volume.assert_called_once_with( + self.cinder.vm2, self.cinder.volume, + timeout=self.cinder.volume_timeout) + self.cinder.cloud.detach_volume.assert_called_once_with( + self.cinder.vm2, self.cinder.volume, + timeout=self.cinder.volume_timeout) + args[0].assert_called_once_with( + pkg_resources.resource_filename( + 'functest.opnfv_tests.openstack.cinder', 'read_data.sh'), + remote_path="~/") + + @mock.patch('scp.SCPClient.put', side_effect=Exception) + def test_read_exc1(self, *args): + # pylint: disable=protected-access + self.cinder.ssh = mock.Mock() + self.cinder.ssh2 = mock.Mock() + self.cinder.sshvm = mock.Mock(id='foo') + self.cinder.cloud.attach_volume = mock.Mock() + self.assertEqual( + self.cinder._read_data(), self.cinder.EX_RUN_ERROR) + args[0].assert_called_once_with( + pkg_resources.resource_filename( + 'functest.opnfv_tests.openstack.cinder', 'read_data.sh'), + remote_path="~/") + + def test_execute_exc1(self): + # pylint: disable=protected-access + self.cinder._write_data = mock.Mock(side_effect=Exception) + self.cinder._read_data = mock.Mock() + with self.assertRaises(Exception): + self.cinder.execute() + self.cinder._write_data.assert_called_once_with() + self.cinder._read_data.assert_not_called() + + def test_execute_exc2(self): + # pylint: disable=protected-access + self.cinder._write_data = mock.Mock(return_value=0) + self.cinder._read_data = mock.Mock(side_effect=Exception) + with self.assertRaises(Exception): + self.cinder.execute() + self.cinder._write_data.assert_called_once_with() + self.cinder._read_data.assert_called_once_with() + + def test_execute_res1(self): + # pylint: disable=protected-access + self.cinder._write_data = mock.Mock(return_value=1) + self.cinder._read_data = mock.Mock() + self.assertEqual(self.cinder.execute(), 1) + self.cinder._write_data.assert_called_once_with() + self.cinder._read_data.assert_not_called() + + def test_execute_res2(self): + # pylint: disable=protected-access + self.cinder._write_data = mock.Mock(return_value=0) + self.cinder._read_data = mock.Mock(return_value=1) + self.assertEqual(self.cinder.execute(), 1) + self.cinder._write_data.assert_called_once_with() + self.cinder._read_data.assert_called_once_with() + + def test_execute_res3(self): + # pylint: disable=protected-access + self.cinder._write_data = mock.Mock(return_value=0) + self.cinder._read_data = mock.Mock(return_value=0) + self.assertEqual(self.cinder.execute(), 0) + self.cinder._write_data.assert_called_once_with() + self.cinder._read_data.assert_called_once_with() + + def test_clean_exc1(self): + self.cinder.cloud = None + with self.assertRaises(AssertionError): + self.cinder.clean() + + @mock.patch('functest.core.singlevm.SingleVm2.clean') + def test_clean_exc2(self, *args): + self.cinder.vm2 = munch.Munch(id='vm2') + self.cinder.cloud.delete_server = mock.Mock( + side_effect=shade.OpenStackCloudException("Foo")) + with self.assertRaises(shade.OpenStackCloudException): + self.cinder.clean() + self.cinder.cloud.delete_server.assert_called_once_with( + self.cinder.vm2, wait=True, + timeout=getattr(config.CONF, 'vping_vm_delete_timeout')) + self.cinder.cloud.delete_floating_ip.assert_not_called() + self.cinder.cloud.delete_volume.assert_not_called() + args[0].assert_not_called() + + @mock.patch('functest.core.singlevm.SingleVm2.clean', + side_effect=Exception) + def test_clean_exc3(self, mock_clean): + self.cinder.vm2 = munch.Munch(id='vm2') + self.cinder.volume = munch.Munch(id='volume') + self.cinder.fip2 = munch.Munch(id='fip2') + with self.assertRaises(Exception): + self.cinder.clean() + self.cinder.cloud.delete_server.assert_called_once_with( + self.cinder.vm2, wait=True, + timeout=getattr(config.CONF, 'vping_vm_delete_timeout')) + self.cinder.cloud.delete_floating_ip.assert_called_once_with( + self.cinder.fip2.id) + self.cinder.cloud.delete_volume.assert_called_once_with( + self.cinder.volume.id) + mock_clean.prepare() + + @mock.patch('functest.core.singlevm.SingleVm2.clean') + def test_clean(self, *args): + self.cinder.vm2 = munch.Munch(id='vm2') + self.cinder.volume = munch.Munch(id='volume') + self.cinder.fip2 = munch.Munch(id='fip2') + self.cinder.clean() + self.cinder.cloud.delete_server.assert_called_once_with( + self.cinder.vm2, wait=True, + timeout=getattr(config.CONF, 'vping_vm_delete_timeout')) + self.cinder.cloud.delete_floating_ip.assert_called_once_with( + self.cinder.fip2.id) + self.cinder.cloud.delete_volume.assert_called_once_with( + self.cinder.volume.id) + args[0].assert_called_once_with() + + @mock.patch('functest.core.singlevm.SingleVm2.clean') + def test_clean2(self, *args): + self.cinder.clean() + self.cinder.cloud.delete_server.assert_not_called() + self.cinder.cloud.delete_floating_ip.assert_not_called() + self.cinder.cloud.delete_volume.assert_not_called() + args[0].assert_called_once_with() + + +if __name__ == '__main__': + logging.disable(logging.CRITICAL) + unittest.main(verbosity=2) diff --git a/functest/tests/unit/openstack/rally/test_rally.py b/functest/tests/unit/openstack/rally/test_rally.py index 28f6055bd..f3c2e7cf6 100644 --- a/functest/tests/unit/openstack/rally/test_rally.py +++ b/functest/tests/unit/openstack/rally/test_rally.py @@ -10,6 +10,7 @@ import json import logging import os +import subprocess import unittest import mock @@ -17,35 +18,39 @@ import munch from xtesting.core import testcase from functest.opnfv_tests.openstack.rally import rally +from functest.utils import config class OSRallyTesting(unittest.TestCase): # pylint: disable=too-many-public-methods def setUp(self): with mock.patch('os_client_config.get_config') as mock_get_config, \ - mock.patch('shade.OpenStackCloud') as mock_shade: + mock.patch('shade.OpenStackCloud') as mock_shade, \ + mock.patch('functest.core.tenantnetwork.NewProject') \ + as mock_new_project: self.rally_base = rally.RallyBase() self.rally_base.image = munch.Munch(name='foo') self.rally_base.flavor = munch.Munch(name='foo') self.rally_base.flavor_alt = munch.Munch(name='bar') self.assertTrue(mock_get_config.called) self.assertTrue(mock_shade.called) + self.assertTrue(mock_new_project.called) def test_build_task_args_missing_floating_network(self): os.environ['OS_AUTH_URL'] = '' self.rally_base.ext_net = None - task_args = self.rally_base._build_task_args('test_file_name') + task_args = self.rally_base.build_task_args('test_name') self.assertEqual(task_args['floating_network'], '') def test_build_task_args_missing_net_id(self): os.environ['OS_AUTH_URL'] = '' self.rally_base.network = None - task_args = self.rally_base._build_task_args('test_file_name') + task_args = self.rally_base.build_task_args('test_name') self.assertEqual(task_args['netid'], '') @staticmethod def check_scenario_file(value): - yaml_file = 'opnfv-{}.yaml'.format('test_file_name') + yaml_file = 'opnfv-test_file_name.yaml' if yaml_file in value: return False return True @@ -59,36 +64,58 @@ class OSRallyTesting(unittest.TestCase): @staticmethod def check_temp_dir(value): - yaml_file = 'opnfv-{}.yaml'.format('test_file_name') + yaml_file = 'opnfv-test_file_name.yaml' if yaml_file in value: return True return False + @mock.patch('functest.opnfv_tests.openstack.rally.rally.' + 'RallyBase.get_verifier_deployment_id', return_value='foo') + @mock.patch('subprocess.check_output') + def test_create_rally_deployment(self, mock_exec, mock_get_id): + # pylint: disable=unused-argument + self.assertEqual(rally.RallyBase.create_rally_deployment(), 'foo') + calls = [ + mock.call(['rally', 'deployment', 'destroy', '--deployment', + str(getattr(config.CONF, 'rally_deployment_name'))]), + mock.call().decode("utf-8"), + mock.call(['rally', 'deployment', 'create', '--fromenv', '--name', + str(getattr(config.CONF, 'rally_deployment_name'))], + env=None), + mock.call().decode("utf-8"), + mock.call(['rally', 'deployment', 'check']), + mock.call().decode("utf-8")] + mock_exec.assert_has_calls(calls) + @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists') @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.makedirs') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_apply_blacklist') + 'apply_blacklist') def test_prepare_test_list_missing_temp_dir( self, mock_method, mock_os_makedirs, mock_path_exists): mock_path_exists.side_effect = self.check_temp_dir - yaml_file = 'opnfv-{}.yaml'.format('test_file_name') - ret_val = os.path.join(self.rally_base.TEMP_DIR, yaml_file) + yaml_file = 'opnfv-test_file_name.yaml' + ret_val = os.path.join(self.rally_base.temp_dir, yaml_file) self.assertEqual(self.rally_base._prepare_test_list('test_file_name'), ret_val) mock_path_exists.assert_called() mock_method.assert_called() mock_os_makedirs.assert_called() - def test_get_task_id_default(self): - cmd_raw = 'Task 1: started' - self.assertEqual(self.rally_base.get_task_id(cmd_raw), - '1') + @mock.patch('subprocess.check_output', return_value=b'1\n') + def test_get_task_id_default(self, *args): + tag = 'nova' + self.assertEqual(self.rally_base.get_task_id(tag), '1') + args[0].assert_called_with( + ['rally', 'task', 'list', '--tag', tag, '--uuids-only']) - def test_get_task_id_missing_id(self): - cmd_raw = '' - self.assertEqual(self.rally_base.get_task_id(cmd_raw), - None) + @mock.patch('subprocess.check_output', return_value=b'\n') + def test_get_task_id_missing_id(self, *args): + tag = 'nova' + self.assertEqual(self.rally_base.get_task_id(tag), '') + args[0].assert_called_with( + ['rally', 'task', 'list', '--tag', tag, '--uuids-only']) def test_task_succeed_fail(self): json_raw = json.dumps({}) @@ -104,20 +131,12 @@ class OSRallyTesting(unittest.TestCase): self.assertEqual(self.rally_base.task_succeed(json_raw), True) - def test_get_cmd_output(self): - proc = mock.Mock() - proc.stdout.__iter__ = mock.Mock(return_value=iter(['line1', 'line2'])) - self.assertEqual(self.rally_base.get_cmd_output(proc), - 'line1line2') - @mock.patch('six.moves.builtins.open', mock.mock_open()) @mock.patch('functest.opnfv_tests.openstack.rally.rally.yaml.safe_load', return_value={'scenario': [ {'scenarios': ['test_scenario'], - 'installers': ['test_installer'], 'tests': ['test']}, {'scenarios': ['other_scenario'], - 'installers': ['test_installer'], 'tests': ['other_test']}]}) def test_excl_scenario_default(self, mock_func): os.environ['INSTALLER_TYPE'] = 'test_installer' @@ -129,25 +148,18 @@ class OSRallyTesting(unittest.TestCase): @mock.patch('functest.opnfv_tests.openstack.rally.rally.yaml.safe_load', return_value={'scenario': [ {'scenarios': ['^os-[^-]+-featT-modeT$'], - 'installers': ['test_installer'], 'tests': ['test1']}, {'scenarios': ['^os-ctrlT-[^-]+-modeT$'], - 'installers': ['test_installer'], 'tests': ['test2']}, {'scenarios': ['^os-ctrlT-featT-[^-]+$'], - 'installers': ['test_installer'], 'tests': ['test3']}, {'scenarios': ['^os-'], - 'installers': ['test_installer'], 'tests': ['test4']}, {'scenarios': ['other_scenario'], - 'installers': ['test_installer'], 'tests': ['test0a']}, {'scenarios': [''], # empty scenario - 'installers': ['test_installer'], 'tests': ['test0b']}]}) def test_excl_scenario_regex(self, mock_func): - os.environ['INSTALLER_TYPE'] = 'test_installer' os.environ['DEPLOY_SCENARIO'] = 'os-ctrlT-featT-modeT' self.assertEqual(self.rally_base.excl_scenario(), ['test1', 'test2', 'test3', 'test4']) @@ -164,11 +176,13 @@ class OSRallyTesting(unittest.TestCase): {'functions': ['no_migration'], 'tests': ['test']}]}) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' '_migration_supported', return_value=False) - def test_excl_func_default(self, mock_func, mock_yaml_load): - os.environ['INSTALLER_TYPE'] = 'test_installer' + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + '_network_trunk_supported', return_value=False) + def test_excl_func_default(self, mock_trunk, mock_func, mock_yaml_load): os.environ['DEPLOY_SCENARIO'] = 'test_scenario' self.assertEqual(self.rally_base.excl_func(), ['test']) mock_func.assert_called() + mock_trunk.assert_called() mock_yaml_load.assert_called() @mock.patch('six.moves.builtins.open', side_effect=Exception) @@ -194,36 +208,30 @@ class OSRallyTesting(unittest.TestCase): return_value=False) def test_run_task_missing_task_file(self, mock_path_exists): with self.assertRaises(Exception): - self.rally_base._run_task('test_name') + self.rally_base.prepare_run() mock_path_exists.assert_called() - @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists', - return_value=True) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' '_prepare_test_list', return_value='test_file_name') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'file_is_empty', return_value=True) @mock.patch('functest.opnfv_tests.openstack.rally.rally.LOGGER.info') - def test_run_task_no_tests_for_scenario(self, mock_logger_info, - mock_file_empty, mock_prep_list, - mock_path_exists): - self.rally_base._run_task('test_name') + def test_prepare_task_no_tests_for_scenario( + self, mock_logger_info, mock_file_empty, mock_prep_list): + self.rally_base.prepare_task('test_name') mock_logger_info.assert_any_call('No tests for scenario \"%s\"', 'test_name') mock_file_empty.assert_called() mock_prep_list.assert_called() - mock_path_exists.assert_called() @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' '_prepare_test_list', return_value='test_file_name') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'file_is_empty', return_value=False) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_build_task_args', return_value={}) + 'build_task_args', return_value={}) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'get_task_id', return_value=None) - @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - 'get_cmd_output', return_value='') @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists', return_value=True) @mock.patch('functest.opnfv_tests.openstack.rally.rally.subprocess.Popen') @@ -231,8 +239,8 @@ class OSRallyTesting(unittest.TestCase): def test_run_task_taskid_missing(self, mock_logger_error, *args): # pylint: disable=unused-argument with self.assertRaises(Exception): - self.rally_base._run_task('test_name') - text = 'Failed to retrieve task_id, validating task...' + self.rally_base.run_task('test_name') + text = 'Failed to retrieve task_id' mock_logger_error.assert_any_call(text) @mock.patch('six.moves.builtins.open', mock.mock_open()) @@ -241,12 +249,10 @@ class OSRallyTesting(unittest.TestCase): @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'file_is_empty', return_value=False) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_build_task_args', return_value={}) + 'build_task_args', return_value={}) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'get_task_id', return_value='1') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - 'get_cmd_output', return_value='') - @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'task_succeed', return_value=True) @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists', return_value=True) @@ -258,17 +264,15 @@ class OSRallyTesting(unittest.TestCase): '_save_results') def test_run_task_default(self, mock_save_res, *args): # pylint: disable=unused-argument - self.rally_base._run_task('test_name') + self.rally_base.run_task('test_name') mock_save_res.assert_called() @mock.patch('six.moves.builtins.open', mock.mock_open()) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'task_succeed', return_value=True) - @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - 'get_cmd_output', return_value='') @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists', return_value=True) - @mock.patch('functest.opnfv_tests.openstack.rally.rally.subprocess.Popen') + @mock.patch('subprocess.check_output') @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.makedirs') @mock.patch('functest.opnfv_tests.openstack.rally.rally.LOGGER.info') @mock.patch('functest.opnfv_tests.openstack.rally.rally.LOGGER.debug') @@ -279,92 +283,126 @@ class OSRallyTesting(unittest.TestCase): self.rally_base._save_results('test_name', '1234') mock_summary.assert_called() - def test_prepare_env_testname_invalid(self): - self.rally_base.TESTS = ['test1', 'test2'] - self.rally_base.test_name = 'test' + def test_prepare_run_testname_invalid(self): + self.rally_base.stests = ['test1', 'test2'] with self.assertRaises(Exception): - self.rally_base._prepare_env() + self.rally_base.prepare_run(tests=['test']) - @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - 'get_external_network') - def test_prepare_env_flavor_alt_creation_failed(self, *args): + @mock.patch('functest.opnfv_tests.openstack.rally.rally.os.path.exists') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.shutil.copyfile') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.shutil.copytree') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.shutil.rmtree') + def test_prepare_run_flavor_alt_creation_failed(self, *args): # pylint: disable=unused-argument - self.rally_base.TESTS = ['test1', 'test2'] - self.rally_base.test_name = 'test1' - with mock.patch.object(self.rally_base.cloud, - 'list_hypervisors') as mock_list_hyperv, \ - mock.patch.object(self.rally_base.cloud, - 'set_flavor_specs') as mock_set_flavor_specs, \ - mock.patch.object(self.rally_base.cloud, 'create_flavor', + self.rally_base.stests = ['test1', 'test2'] + with mock.patch.object(self.rally_base, 'count_hypervisors') \ + as mock_list_hyperv, \ + mock.patch.object(self.rally_base, 'create_flavor_alt', side_effect=Exception) \ as mock_create_flavor: with self.assertRaises(Exception): - self.rally_base._prepare_env() + self.rally_base.prepare_run(tests=['test1']) mock_list_hyperv.assert_called_once() mock_create_flavor.assert_called_once() - mock_set_flavor_specs.assert_not_called() @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_run_task') - def test_run_tests_all(self, mock_run_task): - self.rally_base.TESTS = ['test1', 'test2'] - self.rally_base.test_name = 'all' - self.rally_base._run_tests() + 'prepare_task', return_value=True) + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'run_task') + def test_run_tests_all(self, mock_run_task, mock_prepare_task): + self.rally_base.tests = ['test1', 'test2'] + self.rally_base.run_tests() + mock_prepare_task.assert_any_call('test1') + mock_prepare_task.assert_any_call('test2') mock_run_task.assert_any_call('test1') mock_run_task.assert_any_call('test2') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_run_task') - def test_run_tests_default(self, mock_run_task): - self.rally_base.TESTS = ['test1', 'test2'] - self.rally_base.test_name = 'test1' - self.rally_base._run_tests() + 'prepare_task', return_value=True) + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'run_task') + def test_run_tests_default(self, mock_run_task, mock_prepare_task): + self.rally_base.tests = ['test1', 'test2'] + self.rally_base.run_tests() + mock_prepare_task.assert_any_call('test1') + mock_prepare_task.assert_any_call('test2') mock_run_task.assert_any_call('test1') + mock_run_task.assert_any_call('test2') - def test_clean_up_default(self): - with mock.patch.object(self.rally_base.cloud, + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'clean_rally_logs') + def test_clean_up_default(self, *args): + with mock.patch.object(self.rally_base.orig_cloud, 'delete_flavor') as mock_delete_flavor: self.rally_base.flavor_alt = mock.Mock() self.rally_base.clean() self.assertEqual(mock_delete_flavor.call_count, 1) + args[0].assert_called_once_with() - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'update_rally_logs') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'create_rally_deployment') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_prepare_env') + 'prepare_run') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_run_tests') + 'run_tests') @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' '_generate_report') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'export_task') def test_run_default(self, *args): self.assertEqual(self.rally_base.run(), testcase.TestCase.EX_OK) for func in args: func.assert_called() - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'update_rally_logs') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'create_rally_deployment', side_effect=Exception) - def test_run_exception_create_rally_dep(self, mock_create_rally_dep): + def test_run_exception_create_rally_dep(self, *args): self.assertEqual(self.rally_base.run(), testcase.TestCase.EX_RUN_ERROR) - mock_create_rally_dep.assert_called() + args[0].assert_called() + args[1].assert_called_once_with(self.rally_base.res_dir) - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' + 'update_rally_logs') + @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' 'create_rally_deployment', return_value=mock.Mock()) @mock.patch('functest.opnfv_tests.openstack.rally.rally.RallyBase.' - '_prepare_env', side_effect=Exception) - def test_run_exception_prepare_env(self, mock_prep_env, *args): + 'prepare_run', side_effect=Exception) + def test_run_exception_prepare_run(self, mock_prep_env, *args): # pylint: disable=unused-argument self.assertEqual(self.rally_base.run(), testcase.TestCase.EX_RUN_ERROR) mock_prep_env.assert_called() + args[1].assert_called_once_with(self.rally_base.res_dir) def test_append_summary(self): - text = '{"tasks": [{"subtasks": [{"workloads": [{"full_duration": ' \ - '1.23,"data": [{"error": []}]}]},{"workloads": ' \ - '[{"full_duration": 2.78, "data": [{"error": ["err"]}]}]}]}]}' - self.rally_base._append_summary(text, "foo_test") + json_dict = { + 'tasks': [{ + 'subtasks': [{ + 'title': 'sub_task', + 'workloads': [{ + 'full_duration': 1.23, + 'data': [{ + 'error': [] + }] + }, { + 'full_duration': 2.78, + 'data': [{ + 'error': ['err'] + }] + }] + }] + }] + } + self.rally_base._append_summary(json.dumps(json_dict), "foo_test") self.assertEqual(self.rally_base.summary[0]['test_name'], "foo_test") self.assertEqual(self.rally_base.summary[0]['overall_duration'], 4.01) self.assertEqual(self.rally_base.summary[0]['nb_tests'], 2) self.assertEqual(self.rally_base.summary[0]['nb_success'], 1) + self.assertEqual(self.rally_base.summary[0]['success'], []) + self.assertEqual(self.rally_base.summary[0]['failures'], ['sub_task']) def test_is_successful_false(self): with mock.patch('six.moves.builtins.super') as mock_super: @@ -382,6 +420,48 @@ class OSRallyTesting(unittest.TestCase): self.assertEqual(self.rally_base.is_successful(), 424) mock_super(rally.RallyBase, self).is_successful.assert_called() + @mock.patch('subprocess.check_output', + side_effect=subprocess.CalledProcessError('', '')) + def test_export_task_ko(self, *args): + file_name = (f"{self.rally_base.results_dir}/" + f"{self.rally_base.case_name}.html") + with self.assertRaises(subprocess.CalledProcessError): + self.rally_base.export_task(file_name) + cmd = ["rally", "task", "export", "--type", "html", "--deployment", + str(getattr(config.CONF, 'rally_deployment_name')), + "--to", file_name] + args[0].assert_called_with(cmd, stderr=subprocess.STDOUT) + + @mock.patch('subprocess.check_output', return_value=b'') + def test_export_task(self, *args): + file_name = (f"{self.rally_base.results_dir}/" + f"{self.rally_base.case_name}.html") + self.assertEqual(self.rally_base.export_task(file_name), None) + cmd = ["rally", "task", "export", "--type", "html", "--deployment", + str(getattr(config.CONF, 'rally_deployment_name')), + "--to", file_name] + args[0].assert_called_with(cmd, stderr=subprocess.STDOUT) + + @mock.patch('subprocess.check_output', + side_effect=subprocess.CalledProcessError('', '')) + def test_verify_report_ko(self, *args): + file_name = (f"{self.rally_base.results_dir}/" + f"{self.rally_base.case_name}.html") + with self.assertRaises(subprocess.CalledProcessError): + self.rally_base.verify_report(file_name, "1") + cmd = ["rally", "verify", "report", "--type", "html", "--uuid", "1", + "--to", file_name] + args[0].assert_called_with(cmd, stderr=subprocess.STDOUT) + + @mock.patch('subprocess.check_output', return_value=b'') + def test_verify_report(self, *args): + file_name = (f"{self.rally_base.results_dir}/" + f"{self.rally_base.case_name}.html") + self.assertEqual(self.rally_base.verify_report(file_name, "1"), None) + cmd = ["rally", "verify", "report", "--type", "html", "--uuid", "1", + "--to", file_name] + args[0].assert_called_with(cmd, stderr=subprocess.STDOUT) + if __name__ == "__main__": logging.disable(logging.CRITICAL) diff --git a/functest/tests/unit/openstack/snaps/__init__.py b/functest/tests/unit/openstack/snaps/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/functest/tests/unit/openstack/snaps/__init__.py +++ /dev/null diff --git a/functest/tests/unit/openstack/snaps/test_snaps.py b/functest/tests/unit/openstack/snaps/test_snaps.py deleted file mode 100644 index a3760445f..000000000 --- a/functest/tests/unit/openstack/snaps/test_snaps.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) 2017 Cable Television Laboratories, Inc. 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 - -# pylint: disable=missing-docstring - -import logging -import unittest - -import mock -from snaps.openstack.os_credentials import OSCreds -from xtesting.core import testcase - -from functest.opnfv_tests.openstack.snaps import api_check -from functest.opnfv_tests.openstack.snaps import health_check -from functest.opnfv_tests.openstack.snaps import smoke - - -class APICheckTesting(unittest.TestCase): - """ - Ensures the VPingUserdata class can run in Functest. This test does not - actually connect with an OpenStack pod. - """ - - def setUp(self): - self.os_creds = OSCreds( - username='user', password='pass', - auth_url='http://foo.com:5000/v3', project_name='bar') - - self.api_check = api_check.ApiCheck( - os_creds=self.os_creds, ext_net_name='foo') - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_client_tests') - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_api_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_success(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = [] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.api_check.run()) - self.assertEquals( - testcase.TestCase.EX_OK, self.api_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', image_metadata=mock.ANY, - os_creds=self.os_creds, suite=mock.ANY, use_keystone=True) - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_client_tests') - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_api_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.api_check.run()) - self.assertEquals( - testcase.TestCase.EX_TESTCASE_FAILED, - self.api_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', image_metadata=mock.ANY, - os_creds=self.os_creds, suite=mock.ANY, use_keystone=True) - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_client_tests') - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_api_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko_criteria(self, *args): - self.api_check.criteria = 90 - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.api_check.run()) - self.assertEquals( - testcase.TestCase.EX_OK, self.api_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', image_metadata=mock.ANY, - os_creds=self.os_creds, suite=mock.ANY, use_keystone=True) - - -class HealthCheckTesting(unittest.TestCase): - """ - Ensures the VPingUserdata class can run in Functest. This test does not - actually connect with an OpenStack pod. - """ - - def setUp(self): - self.os_creds = OSCreds( - username='user', password='pass', - auth_url='http://foo.com:5000/v3', project_name='bar') - - self.health_check = health_check.HealthCheck( - os_creds=self.os_creds, ext_net_name='foo') - - @mock.patch('snaps.openstack.tests.os_source_file_test.' - 'OSIntegrationTestCase.parameterize') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_success(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = [] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.health_check.run()) - self.assertEquals( - testcase.TestCase.EX_OK, self.health_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - mock.ANY, ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=None, - os_creds=self.os_creds, use_keystone=True) - - @mock.patch('snaps.openstack.tests.os_source_file_test.' - 'OSIntegrationTestCase.parameterize') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.health_check.run()) - self.assertEquals( - testcase.TestCase.EX_TESTCASE_FAILED, - self.health_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - mock.ANY, ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=None, - os_creds=self.os_creds, use_keystone=True) - - @mock.patch('snaps.openstack.tests.os_source_file_test.' - 'OSIntegrationTestCase.parameterize') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko_criteria(self, *args): - self.health_check.criteria = 90 - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.health_check.run()) - self.assertEquals( - testcase.TestCase.EX_OK, self.health_check.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - mock.ANY, ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=None, - os_creds=self.os_creds, use_keystone=True) - - -class SmokeTesting(unittest.TestCase): - """ - Ensures the VPingUserdata class can run in Functest. This test does not - actually connect with an OpenStack pod. - """ - - def setUp(self): - self.os_creds = OSCreds( - username='user', password='pass', - auth_url='http://foo.com:5000/v3', project_name='bar') - - self.smoke = smoke.SnapsSmoke( - os_creds=self.os_creds, ext_net_name='foo') - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_integration_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_success(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = [] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.smoke.run()) - self.assertEquals(testcase.TestCase.EX_OK, self.smoke.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=None, - os_creds=self.os_creds, suite=mock.ANY, use_floating_ips=True, - use_keystone=True) - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_integration_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko(self, *args): - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.smoke.run()) - self.assertEquals( - testcase.TestCase.EX_TESTCASE_FAILED, self.smoke.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=mock.ANY, - os_creds=self.os_creds, suite=mock.ANY, use_floating_ips=True, - use_keystone=True) - - @mock.patch('functest.opnfv_tests.openstack.snaps.snaps_suite_builder.' - 'add_openstack_integration_tests') - @mock.patch('unittest.TextTestRunner.run', - return_value=mock.MagicMock(name='unittest.TextTestResult')) - def test_run_1_of_100_ko_criteria(self, *args): - self.smoke.criteria = 90 - args[0].return_value.testsRun = 100 - args[0].return_value.failures = ['foo'] - args[0].return_value.errors = [] - self.assertEquals(testcase.TestCase.EX_OK, self.smoke.run()) - self.assertEquals( - testcase.TestCase.EX_OK, self.smoke.is_successful()) - args[0].assert_called_with(mock.ANY) - args[1].assert_called_with( - ext_net_name='foo', flavor_metadata=mock.ANY, - image_metadata=mock.ANY, netconf_override=None, - os_creds=self.os_creds, suite=mock.ANY, use_floating_ips=True, - use_keystone=True) - - -if __name__ == "__main__": - logging.disable(logging.CRITICAL) - unittest.main(verbosity=2) diff --git a/functest/tests/unit/openstack/tempest/test_conf_utils.py b/functest/tests/unit/openstack/tempest/test_conf_utils.py deleted file mode 100644 index 1585cac09..000000000 --- a/functest/tests/unit/openstack/tempest/test_conf_utils.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python - -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 - -# pylint: disable=missing-docstring - -import logging -import os -import unittest - -import mock - -from functest.opnfv_tests.openstack.tempest import conf_utils -from functest.utils import config - - -class OSTempestConfUtilsTesting(unittest.TestCase): - # pylint: disable=too-many-public-methods - - @mock.patch('subprocess.check_output') - def test_create_rally_deployment(self, mock_exec): - self.assertEqual(conf_utils.create_rally_deployment(), None) - calls = [ - mock.call(['rally', 'deployment', 'destroy', '--deployment', - str(getattr(config.CONF, 'rally_deployment_name'))]), - mock.call(['rally', 'deployment', 'create', '--fromenv', '--name', - str(getattr(config.CONF, 'rally_deployment_name'))]), - mock.call(['rally', 'deployment', 'check'])] - mock_exec.assert_has_calls(calls) - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils' - '.LOGGER.debug') - def test_create_verifier(self, mock_logger_debug): - mock_popen = mock.Mock() - attrs = {'poll.return_value': None, - 'stdout.readline.return_value': '0'} - mock_popen.configure_mock(**attrs) - - setattr(config.CONF, 'tempest_verifier_name', 'test_verifier_name') - with mock.patch('subprocess.Popen', side_effect=Exception), \ - self.assertRaises(Exception): - conf_utils.create_verifier() - mock_logger_debug.assert_any_call("Tempest test_verifier_name" - " does not exist") - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'create_verifier', return_value=mock.Mock()) - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'create_rally_deployment', return_value=mock.Mock()) - def test_get_verif_id_missing_verif(self, mock_rally, mock_tempest): - # pylint: disable=unused-argument - setattr(config.CONF, 'tempest_verifier_name', 'test_verifier_name') - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.subprocess.Popen') as mock_popen, \ - self.assertRaises(Exception): - mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': ''} - mock_stdout.configure_mock(**attrs) - mock_popen.return_value = mock_stdout - conf_utils.get_verifier_id() - - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'create_verifier', return_value=mock.Mock()) - @mock.patch('functest.opnfv_tests.openstack.tempest.conf_utils.' - 'create_rally_deployment', return_value=mock.Mock()) - def test_get_verifier_id_default(self, mock_rally, mock_tempest): - # pylint: disable=unused-argument - setattr(config.CONF, 'tempest_verifier_name', 'test_verifier_name') - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.subprocess.Popen') as mock_popen: - mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': 'test_deploy_id'} - mock_stdout.configure_mock(**attrs) - mock_popen.return_value = mock_stdout - - self.assertEqual(conf_utils.get_verifier_id(), - 'test_deploy_id') - - def test_get_depl_id_missing_rally(self): - setattr(config.CONF, 'tempest_verifier_name', 'test_deploy_name') - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.subprocess.Popen') as mock_popen, \ - self.assertRaises(Exception): - mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': ''} - mock_stdout.configure_mock(**attrs) - mock_popen.return_value = mock_stdout - conf_utils.get_verifier_deployment_id() - - def test_get_depl_id_default(self): - setattr(config.CONF, 'tempest_verifier_name', 'test_deploy_name') - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.subprocess.Popen') as mock_popen: - mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': 'test_deploy_id'} - mock_stdout.configure_mock(**attrs) - mock_popen.return_value = mock_stdout - - self.assertEqual(conf_utils.get_verifier_deployment_id(), - 'test_deploy_id') - - def test_get_verif_repo_dir_default(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.join', - return_value='test_verifier_repo_dir'), \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.get_verifier_id') as mock_get_id: - self.assertEqual(conf_utils.get_verifier_repo_dir(''), - 'test_verifier_repo_dir') - self.assertTrue(mock_get_id.called) - - def test_get_depl_dir_default(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.join', - return_value='test_verifier_repo_dir'), \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.get_verifier_id') as mock_get_vid, \ - mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.get_verifier_deployment_id') \ - as mock_get_did: - self.assertEqual(conf_utils.get_verifier_deployment_dir('', ''), - 'test_verifier_repo_dir') - self.assertTrue(mock_get_vid.called) - self.assertTrue(mock_get_did.called) - - def _test_missing_param(self, params, image_id, flavor_id, alt=False): - with mock.patch('six.moves.configparser.RawConfigParser.' - 'set') as mset, \ - mock.patch('six.moves.configparser.RawConfigParser.' - 'read') as mread, \ - mock.patch('six.moves.configparser.RawConfigParser.' - 'write') as mwrite, \ - mock.patch('six.moves.builtins.open', mock.mock_open()), \ - mock.patch('functest.utils.functest_utils.yaml.safe_load', - return_value={'validation': {'ssh_timeout': 300}}): - os.environ['OS_INTERFACE'] = '' - if not alt: - conf_utils.configure_tempest_update_params( - 'test_conf_file', image_id=image_id, - flavor_id=flavor_id) - mset.assert_any_call(params[0], params[1], params[2]) - else: - conf_utils.configure_tempest_update_params( - 'test_conf_file', image_alt_id=image_id, - flavor_alt_id=flavor_id) - mset.assert_any_call(params[0], params[1], params[2]) - self.assertTrue(mread.called) - self.assertTrue(mwrite.called) - - def test_upd_missing_image_id(self): - self._test_missing_param(('compute', 'image_ref', 'test_image_id'), - 'test_image_id', None) - - def test_upd_missing_image_id_alt(self): - self._test_missing_param( - ('compute', 'image_ref_alt', 'test_image_id_alt'), - 'test_image_id_alt', None, alt=True) - - def test_upd_missing_flavor_id(self): - self._test_missing_param(('compute', 'flavor_ref', 'test_flavor_id'), - None, 'test_flavor_id') - - def test_upd_missing_flavor_id_alt(self): - self._test_missing_param( - ('compute', 'flavor_ref_alt', 'test_flavor_id_alt'), - None, 'test_flavor_id_alt', alt=True) - - def test_verif_missing_conf_file(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.isfile', - return_value=False), \ - mock.patch('subprocess.check_output') as mexe, \ - self.assertRaises(Exception) as context: - conf_utils.configure_verifier('test_dep_dir') - mexe.assert_called_once_with("rally verify configure-verifier") - msg = ("Tempest configuration file 'test_dep_dir/tempest.conf'" - " NOT found.") - self.assertTrue(msg in context.exception) - - def test_configure_verifier_default(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.' - 'conf_utils.os.path.isfile', - return_value=True), \ - mock.patch('subprocess.check_output') as mexe: - self.assertEqual(conf_utils.configure_verifier('test_dep_dir'), - 'test_dep_dir/tempest.conf') - mexe.assert_called_once_with( - ['rally', 'verify', 'configure-verifier', '--reconfigure', - '--id', str(getattr(config.CONF, 'tempest_verifier_name'))]) - - -if __name__ == "__main__": - logging.disable(logging.CRITICAL) - unittest.main(verbosity=2) diff --git a/functest/tests/unit/openstack/tempest/test_tempest.py b/functest/tests/unit/openstack/tempest/test_tempest.py index 23dcd6c75..efc4393c8 100644 --- a/functest/tests/unit/openstack/tempest/test_tempest.py +++ b/functest/tests/unit/openstack/tempest/test_tempest.py @@ -14,8 +14,8 @@ import unittest import mock from xtesting.core import testcase +from functest.opnfv_tests.openstack.rally import rally from functest.opnfv_tests.openstack.tempest import tempest -from functest.opnfv_tests.openstack.tempest import conf_utils from functest.utils import config @@ -23,17 +23,24 @@ class OSTempestTesting(unittest.TestCase): # pylint: disable=too-many-public-methods def setUp(self): - with mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.get_verifier_id', - return_value='test_deploy_id'), \ + with mock.patch('os_client_config.get_config'), \ + mock.patch('shade.OpenStackCloud'), \ + mock.patch('functest.core.tenantnetwork.NewProject'), \ + mock.patch('functest.opnfv_tests.openstack.rally.rally.' + 'RallyBase.create_rally_deployment'), \ + mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' + 'TempestCommon.create_verifier'), \ mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.get_verifier_deployment_id', + 'TempestCommon.get_verifier_id', + return_value='test_deploy_id'), \ + mock.patch('functest.opnfv_tests.openstack.rally.rally.' + 'RallyBase.get_verifier_deployment_id', return_value='test_deploy_id'), \ mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.get_verifier_repo_dir', + 'TempestCommon.get_verifier_repo_dir', return_value='test_verifier_repo_dir'), \ mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'conf_utils.get_verifier_deployment_dir', + 'TempestCommon.get_verifier_deployment_dir', return_value='test_verifier_deploy_dir'), \ mock.patch('os_client_config.make_shade'): self.tempestcommon = tempest.TempestCommon() @@ -50,7 +57,7 @@ class OSTempestTesting(unittest.TestCase): msg = "Tempest test list file %s NOT found." self.tempestcommon.generate_test_list() self.assertTrue( - (msg % conf_utils.TEMPEST_CUSTOM) in context.exception) + (msg % self.tempestcommon.tempest_custom) in context.exception) @mock.patch('subprocess.check_output') @mock.patch('os.remove') @@ -75,8 +82,9 @@ class OSTempestTesting(unittest.TestCase): else: testr_mode = self.tempestcommon.mode verifier_repo_dir = 'test_verifier_repo_dir' - cmd = "(cd {0}; stestr list '{1}' >{2} 2>/dev/null)".format( - verifier_repo_dir, testr_mode, self.tempestcommon.list) + self.tempestcommon.verifier_repo_dir = verifier_repo_dir + cmd = (f"(cd {verifier_repo_dir}; stestr list '{testr_mode}' > " + f"{self.tempestcommon.list} 2>/dev/null)") self.tempestcommon.generate_test_list(mode=testr_mode) args[0].assert_called_once_with(cmd, shell=True) args[2].assert_called_once_with('/etc/tempest.conf') @@ -115,10 +123,10 @@ class OSTempestTesting(unittest.TestCase): mock.mock_open()) as mock_open, \ mock.patch.object(self.tempestcommon, 'read_file', return_value=['test1', 'test2']): - conf_utils.TEMPEST_BLACKLIST = Exception - os.environ['INSTALLER_TYPE'] = 'installer_type' + self.tempestcommon.tempest_blacklist = Exception os.environ['DEPLOY_SCENARIO'] = 'deploy_scenario' - self.tempestcommon.apply_tempest_blacklist() + self.tempestcommon.apply_tempest_blacklist( + self.tempestcommon.tempest_blacklist) obj = mock_open() obj.write.assert_any_call('test1\n') obj.write.assert_any_call('test2\n') @@ -132,7 +140,6 @@ class OSTempestTesting(unittest.TestCase): @mock.patch("os.path.exists", return_value=True) def test_apply_blacklist_default(self, *args): item_dict = {'scenarios': ['deploy_scenario'], - 'installers': ['installer_type'], 'tests': ['test2']} with mock.patch('six.moves.builtins.open', mock.mock_open()) as mock_open, \ @@ -140,9 +147,9 @@ class OSTempestTesting(unittest.TestCase): return_value=['test1', 'test2']), \ mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'yaml.safe_load', return_value=item_dict): - os.environ['INSTALLER_TYPE'] = 'installer_type' os.environ['DEPLOY_SCENARIO'] = 'deploy_scenario' - self.tempestcommon.apply_tempest_blacklist() + self.tempestcommon.apply_tempest_blacklist( + self.tempestcommon.tempest_blacklist) obj = mock_open() obj.write.assert_any_call('test1\n') self.assertFalse(obj.write.assert_any_call('test2\n')) @@ -151,35 +158,17 @@ class OSTempestTesting(unittest.TestCase): args[2].assert_called_once_with( self.tempestcommon.list, self.tempestcommon.raw_list) - @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.LOGGER.info') - def test_run_verifier_tests_default(self, mock_logger_info): - with mock.patch('six.moves.builtins.open', mock.mock_open()), \ - mock.patch('six.moves.builtins.iter', - return_value=[r'\} tempest\.']), \ - mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' - 'subprocess.Popen'): - conf_utils.TEMPEST_LIST = 'test_tempest_list' - cmd = ["rally", "verify", "start", "--load-list", - conf_utils.TEMPEST_LIST] - with self.assertRaises(Exception): - self.tempestcommon.run_verifier_tests() - mock_logger_info. \ - assert_any_call("Starting Tempest test suite: '%s'.", cmd) - @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'subprocess.Popen') - def test_generate_report(self, mock_popen): - self.tempestcommon.verification_id = "1234" - html_file = os.path.join( - os.path.join( - getattr(config.CONF, 'dir_results'), - self.tempestcommon.case_name), - "tempest-report.html") - cmd = ["rally", "verify", "report", "--type", "html", "--uuid", - "1234", "--to", html_file] - self.tempestcommon.generate_report() - mock_popen.assert_called_once_with(cmd, stdout=mock.ANY, - stderr=mock.ANY) + @mock.patch('six.moves.builtins.open', mock.mock_open()) + @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.LOGGER.info') + def test_run_verifier_tests_default(self, *args): + self.tempestcommon.tempest_list = 'test_tempest_list' + cmd = ["rally", "verify", "start", "--load-list", + self.tempestcommon.tempest_list] + with self.assertRaises(Exception): + self.tempestcommon.run_verifier_tests() + args[0].assert_any_call("Starting Tempest test suite: '%s'.", cmd) @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.' 'os.path.exists', return_value=False) @@ -224,9 +213,9 @@ class OSTempestTesting(unittest.TestCase): def test_run_apply_blacklist_ko(self): with mock.patch.object(self.tempestcommon, 'generate_test_list'), \ - mock.patch.object( - self.tempestcommon, 'apply_tempest_blacklist', - side_effect=Exception()): + mock.patch.object(self.tempestcommon, + 'apply_tempest_blacklist', + side_effect=Exception()): self._test_run(testcase.TestCase.EX_RUN_ERROR) def test_run_verifier_tests_ko(self): @@ -257,11 +246,147 @@ class OSTempestTesting(unittest.TestCase): 'apply_tempest_blacklist'), \ mock.patch.object(self.tempestcommon, 'run_verifier_tests'), \ mock.patch.object(self.tempestcommon, - 'parse_verifier_result'), \ - mock.patch.object(self.tempestcommon, 'generate_report'): + 'parse_verifier_result'): self._test_run(testcase.TestCase.EX_OK) args[0].assert_called_once_with() + @mock.patch('functest.opnfv_tests.openstack.tempest.tempest.LOGGER.debug') + def test_create_verifier(self, mock_logger_debug): + mock_popen = mock.Mock() + attrs = {'poll.return_value': None, + 'stdout.readline.return_value': '0'} + mock_popen.configure_mock(**attrs) + + setattr(config.CONF, 'tempest_verifier_name', 'test_verifier_name') + with mock.patch('subprocess.Popen', side_effect=Exception), \ + self.assertRaises(Exception): + self.tempestcommon.create_verifier() + mock_logger_debug.assert_any_call("Tempest test_verifier_name" + " does not exist") + + def test_get_verifier_id_default(self): + setattr(config.CONF, 'tempest_verifier_name', 'test_verifier_name') + + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.subprocess.Popen') as mock_popen: + attrs = {'return_value.__enter__.return_value.' + 'stdout.readline.return_value': b'test_deploy_id'} + mock_popen.configure_mock(**attrs) + + self.assertEqual(self.tempestcommon.get_verifier_id(), + 'test_deploy_id') + + def test_get_depl_id_default(self): + setattr(config.CONF, 'tempest_verifier_name', 'test_deploy_name') + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.subprocess.Popen') as mock_popen: + attrs = {'return_value.__enter__.return_value.' + 'stdout.readline.return_value': b'test_deploy_id'} + mock_popen.configure_mock(**attrs) + + self.assertEqual(rally.RallyBase.get_verifier_deployment_id(), + 'test_deploy_id') + + def test_get_verif_repo_dir_default(self): + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.os.path.join', + return_value='test_verifier_repo_dir'): + self.assertEqual(self.tempestcommon.get_verifier_repo_dir(''), + 'test_verifier_repo_dir') + + def test_get_depl_dir_default(self): + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.os.path.join', + return_value='test_verifier_repo_dir'): + self.assertEqual( + self.tempestcommon.get_verifier_deployment_dir('', ''), + 'test_verifier_repo_dir') + + def _test_missing_param(self, params, image_id, flavor_id, alt=False): + with mock.patch('six.moves.configparser.RawConfigParser.' + 'set') as mset, \ + mock.patch('six.moves.configparser.RawConfigParser.' + 'read') as mread, \ + mock.patch('six.moves.configparser.RawConfigParser.' + 'write') as mwrite, \ + mock.patch('six.moves.builtins.open', mock.mock_open()), \ + mock.patch('functest.utils.functest_utils.yaml.safe_load', + return_value={'validation': {'ssh_timeout': 300}}): + os.environ['OS_INTERFACE'] = '' + if not alt: + self.tempestcommon.configure_tempest_update_params( + 'test_conf_file', image_id=image_id, + flavor_id=flavor_id) + mset.assert_any_call(params[0], params[1], params[2]) + else: + self.tempestcommon.configure_tempest_update_params( + 'test_conf_file', image_alt_id=image_id, + flavor_alt_id=flavor_id) + mset.assert_any_call(params[0], params[1], params[2]) + self.assertTrue(mread.called) + self.assertTrue(mwrite.called) + + def test_upd_missing_image_id(self): + self._test_missing_param(('compute', 'image_ref', 'test_image_id'), + 'test_image_id', None) + + def test_upd_missing_image_id_alt(self): + self._test_missing_param( + ('compute', 'image_ref_alt', 'test_image_id_alt'), + 'test_image_id_alt', None, alt=True) + + def test_upd_missing_flavor_id(self): + self._test_missing_param(('compute', 'flavor_ref', 'test_flavor_id'), + None, 'test_flavor_id') + + def test_upd_missing_flavor_id_alt(self): + self._test_missing_param( + ('compute', 'flavor_ref_alt', 'test_flavor_id_alt'), + None, 'test_flavor_id_alt', alt=True) + + def test_verif_missing_conf_file(self): + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.os.path.isfile', + return_value=False), \ + mock.patch('subprocess.check_output') as mexe, \ + self.assertRaises(Exception) as context: + self.tempestcommon.configure_verifier('test_dep_dir') + mexe.assert_called_once_with("rally verify configure-verifier") + msg = ("Tempest configuration file 'test_dep_dir/tempest.conf'" + " NOT found.") + self.assertTrue(msg in context.exception) + + def test_configure_verifier_default(self): + with mock.patch('functest.opnfv_tests.openstack.tempest.' + 'tempest.os.path.isfile', + return_value=True), \ + mock.patch('subprocess.check_output') as mexe: + self.assertEqual( + self.tempestcommon.configure_verifier('test_dep_dir'), + 'test_dep_dir/tempest.conf') + mexe.assert_called_once_with( + ['rally', 'verify', 'configure-verifier', '--reconfigure', + '--id', str(getattr(config.CONF, 'tempest_verifier_name'))]) + + def test_is_successful_false(self): + with mock.patch('six.moves.builtins.super') as mock_super: + self.tempestcommon.deny_skipping = True + self.tempestcommon.details = {"skipped_number": 2} + self.assertEqual(self.tempestcommon.is_successful(), + testcase.TestCase.EX_TESTCASE_FAILED) + mock_super(tempest.TempestCommon, + self).is_successful.assert_not_called() + + def test_is_successful_true(self): + with mock.patch('six.moves.builtins.super') as mock_super: + self.tempestcommon.deny_skipping = False + self.tempestcommon.details = {"skipped_number": 2} + mock_super(tempest.TempestCommon, + self).is_successful.return_value = 567 + self.assertEqual(self.tempestcommon.is_successful(), 567) + mock_super(tempest.TempestCommon, + self).is_successful.assert_called() + if __name__ == "__main__": logging.disable(logging.CRITICAL) diff --git a/functest/tests/unit/openstack/vmtp/test_vmtp.py b/functest/tests/unit/openstack/vmtp/test_vmtp.py index 7f8cf40c7..850273476 100644 --- a/functest/tests/unit/openstack/vmtp/test_vmtp.py +++ b/functest/tests/unit/openstack/vmtp/test_vmtp.py @@ -66,23 +66,25 @@ class VmtpTesting(unittest.TestCase): def test_generate_keys1(self, *args): self.testcase.generate_keys() self.testcase.cloud.create_keypair.assert_called_once_with( - 'vmtp_{}'.format(self.testcase.guid)) + f'vmtp_{self.testcase.guid}') self.testcase.cloud.delete_keypair.assert_called_once_with('id') - calls = [mock.call(self.testcase.privkey_filename, 'w'), - mock.call(self.testcase.pubkey_filename, 'w')] + calls = [mock.call( + self.testcase.privkey_filename, 'w', encoding='utf-8'), + mock.call( + self.testcase.pubkey_filename, 'w', encoding='utf-8')] args[0].assert_has_calls(calls, any_order=True) @mock.patch('six.moves.builtins.open') def test_generate_keys2(self, *args): - # pylint: disable=bad-continuation with mock.patch.object( self.testcase.cloud, "create_keypair", side_effect=shade.OpenStackCloudException(None)) as mock_obj, \ self.assertRaises(shade.OpenStackCloudException): self.testcase.generate_keys() - mock_obj.assert_called_once_with('vmtp_{}'.format(self.testcase.guid)) + mock_obj.assert_called_once_with(f'vmtp_{self.testcase.guid}') args[0].assert_not_called() + if __name__ == "__main__": logging.disable(logging.CRITICAL) unittest.main(verbosity=2) diff --git a/functest/tests/unit/openstack/vping/test_vping_ssh.py b/functest/tests/unit/openstack/vping/test_vping_ssh.py index 3595638ec..a07148aab 100644 --- a/functest/tests/unit/openstack/vping/test_vping_ssh.py +++ b/functest/tests/unit/openstack/vping/test_vping_ssh.py @@ -47,7 +47,7 @@ class VpingSSHTesting(unittest.TestCase): self.vping.prepare() args[0].assert_called_once_with() args[1].assert_called_once_with( - '{}-vm2_{}'.format(self.vping.case_name, self.vping.guid), + f'{self.vping.case_name}-vm2_{self.vping.guid}', security_groups=[self.vping.sec.id]) @mock.patch('functest.opnfv_tests.openstack.vping.vping_ssh.VPingSSH.' @@ -58,27 +58,43 @@ class VpingSSHTesting(unittest.TestCase): self.vping.prepare() args[0].assert_called_once_with() args[1].assert_called_once_with( - '{}-vm2_{}'.format(self.vping.case_name, self.vping.guid), + f'{self.vping.case_name}-vm2_{self.vping.guid}', security_groups=[self.vping.sec.id]) - def test_execute_exc(self): - self.vping.vm2 = munch.Munch(private_v4='127.0.0.1') + @mock.patch('functest.opnfv_tests.openstack.vping.vping_ssh.VPingSSH.' + 'check_regex_in_console', return_value=True) + def test_execute_exc(self, *args): + self.vping.vm2 = munch.Munch(private_v4='127.0.0.1', name='foo') self.vping.ssh = mock.Mock() self.vping.ssh.exec_command.side_effect = ssh_exception.SSHException with self.assertRaises(ssh_exception.SSHException): self.vping.execute() self.vping.ssh.exec_command.assert_called_once_with( - 'ping -c 1 {}'.format(self.vping.vm2.private_v4)) + f'ping -c 1 {self.vping.vm2.private_v4}') + args[0].assert_called_once_with('foo') + + @mock.patch('functest.opnfv_tests.openstack.vping.vping_ssh.VPingSSH.' + 'check_regex_in_console', return_value=False) + def test_execute_exc2(self, *args): + self.vping.vm2 = munch.Munch(private_v4='127.0.0.1', name='foo') + self.vping.ssh = mock.Mock() + self.vping.execute() + self.vping.ssh.exec_command.assert_not_called() + args[0].assert_called_once_with('foo') def _test_execute(self, ret=0): - self.vping.vm2 = munch.Munch(private_v4='127.0.0.1') + self.vping.vm2 = munch.Munch(private_v4='127.0.0.1', name='foo') self.vping.ssh = mock.Mock() stdout = mock.Mock() stdout.channel.recv_exit_status.return_value = ret - self.vping.ssh.exec_command.return_value = (None, stdout, None) - self.assertEqual(self.vping.execute(), ret) + self.vping.ssh.exec_command.return_value = (None, stdout, mock.Mock()) + with mock.patch( + 'functest.opnfv_tests.openstack.vping.vping_ssh.VPingSSH.' + 'check_regex_in_console', return_value=True) as mock_check: + self.assertEqual(self.vping.execute(), ret) + mock_check.assert_called_once_with('foo') self.vping.ssh.exec_command.assert_called_once_with( - 'ping -c 1 {}'.format(self.vping.vm2.private_v4)) + f'ping -c 1 {self.vping.vm2.private_v4}') def test_execute1(self): self._test_execute() diff --git a/functest/tests/unit/utils/test_functest_utils.py b/functest/tests/unit/utils/test_functest_utils.py index d35ed8ced..4b642ff9d 100644 --- a/functest/tests/unit/utils/test_functest_utils.py +++ b/functest/tests/unit/utils/test_functest_utils.py @@ -15,12 +15,13 @@ import unittest import mock import pkg_resources +import six from functest.utils import functest_utils class FunctestUtilsTesting(unittest.TestCase): - # pylint: disable=too-many-instance-attributes + # pylint: disable=too-many-instance-attributes,too-many-public-methods readline = 0 test_ip = ['10.1.23.4', '10.1.14.15', '10.1.16.15'] @@ -81,7 +82,7 @@ class FunctestUtilsTesting(unittest.TestCase): def _get_environ(self, var, *args): # pylint: disable=unused-argument if var == 'INSTALLER_TYPE': return self.installer - elif var == 'DEPLOY_SCENARIO': + if var == 'DEPLOY_SCENARIO': return self.scenario return var @@ -97,27 +98,20 @@ class FunctestUtilsTesting(unittest.TestCase): as mock_subproc_open, \ mock.patch('six.moves.builtins.open', mock.mock_open()) as mopen: - - FunctestUtilsTesting.readline = 0 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - - mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 1} - mock_obj2.configure_mock(**attrs) - - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=True, - error_msg=self.error_msg, - verbose=True, - output_file=self.output_file) + stream = six.BytesIO() + stream.write(self.cmd_readline().encode("utf-8")) + attrs = { + 'return_value.__enter__.return_value.stdout': stream, + 'return_value.__enter__.return_value.wait.return_value': 1} + mock_subproc_open.configure_mock(**attrs) + resp = functest_utils.execute_command( + self.cmd, info=True, error_msg=self.error_msg, verbose=True, + output_file=self.output_file) self.assertEqual(resp, 1) - msg_exec = ("Executing command: '%s'" % self.cmd) + msg_exec = f"Executing command: '{self.cmd}'" mock_logger_info.assert_called_once_with(msg_exec) - mopen.assert_called_once_with(self.output_file, "w") + mopen.assert_called_once_with( + self.output_file, "w", encoding='utf-8') mock_logger_error.assert_called_once_with(self.error_msg) @mock.patch('functest.utils.functest_utils.LOGGER.info') @@ -126,50 +120,35 @@ class FunctestUtilsTesting(unittest.TestCase): as mock_subproc_open, \ mock.patch('six.moves.builtins.open', mock.mock_open()) as mopen: - - FunctestUtilsTesting.readline = 0 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - - mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 0} - mock_obj2.configure_mock(**attrs) - - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=True, - error_msg=self.error_msg, - verbose=True, - output_file=self.output_file) + stream = six.BytesIO() + stream.write(self.cmd_readline().encode("utf-8")) + attrs = { + 'return_value.__enter__.return_value.stdout': stream, + 'return_value.__enter__.return_value.wait.return_value': 0} + mock_subproc_open.configure_mock(**attrs) + resp = functest_utils.execute_command( + self.cmd, info=True, error_msg=self.error_msg, verbose=True, + output_file=self.output_file) self.assertEqual(resp, 0) - msg_exec = ("Executing command: '%s'" % self.cmd) + msg_exec = (f"Executing command: '{self.cmd}'") mock_logger_info.assert_called_once_with(msg_exec) - mopen.assert_called_once_with(self.output_file, "w") + mopen.assert_called_once_with( + self.output_file, "w", encoding='utf-8') @mock.patch('sys.stdout') def test_exec_cmd_args_missing_ok(self, stdout=None): # pylint: disable=unused-argument with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ as mock_subproc_open: - - FunctestUtilsTesting.readline = 2 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - - mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 0} - mock_obj2.configure_mock(**attrs) - - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=False, - error_msg="", - verbose=False, - output_file=None) + stream = six.BytesIO() + stream.write(self.cmd_readline().encode("utf-8")) + attrs = { + 'return_value.__enter__.return_value.stdout': stream, + 'return_value.__enter__.return_value.wait.return_value': 0} + mock_subproc_open.configure_mock(**attrs) + resp = functest_utils.execute_command( + self.cmd, info=False, error_msg="", verbose=False, + output_file=None) self.assertEqual(resp, 0) @mock.patch('sys.stdout') @@ -177,22 +156,16 @@ class FunctestUtilsTesting(unittest.TestCase): # pylint: disable=unused-argument with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ as mock_subproc_open: - - FunctestUtilsTesting.readline = 2 - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - - mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 1} - mock_obj2.configure_mock(**attrs) - - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=False, - error_msg="", - verbose=False, - output_file=None) + attrs = {} + stream = six.BytesIO() + stream.write(self.cmd_readline().encode("utf-8")) + attrs = { + 'return_value.__enter__.return_value.stdout': stream, + 'return_value.__enter__.return_value.wait.return_value': 1} + mock_subproc_open.configure_mock(**attrs) + resp = functest_utils.execute_command( + self.cmd, info=False, error_msg="", verbose=False, + output_file=None) self.assertEqual(resp, 1) def test_get_param_from_yaml_failed(self): @@ -204,9 +177,9 @@ class FunctestUtilsTesting(unittest.TestCase): mock_yaml.return_value = self.file_yaml functest_utils.get_parameter_from_yaml(self.parameter, self.test_file) - self.assertTrue(("The parameter %s is not" - " defined in config_functest.yaml" % - self.parameter) in excep.exception) + self.assertTrue((f"The parameter {self.parameter} is not" + " defined in config_functest.yaml" + ) in excep.exception) def test_get_param_from_yaml_def(self): with mock.patch('six.moves.builtins.open', mock.mock_open()), \ @@ -218,6 +191,218 @@ class FunctestUtilsTesting(unittest.TestCase): self.test_file), 'test_image_name') + def test_nova_version_exc1(self): + # pylint: disable=protected-access + cloud = mock.Mock() + cloud._compute_client.request.return_value = None + self.assertEqual(functest_utils.get_nova_version(cloud), None) + cloud._compute_client.request.assert_called_once_with('/', 'GET') + + def test_nova_version_exc2(self): + # pylint: disable=protected-access + cloud = mock.Mock() + cloud._compute_client.request.return_value = {"version": None} + self.assertEqual(functest_utils.get_nova_version(cloud), None) + cloud._compute_client.request.assert_called_once_with('/', 'GET') + + def test_nova_version_exc3(self): + # pylint: disable=protected-access + cloud = mock.Mock() + cloud._compute_client.request.return_value = { + "version": {"version": None}} + self.assertEqual(functest_utils.get_nova_version(cloud), None) + cloud._compute_client.request.assert_called_once_with('/', 'GET') + + def test_nova_version_exc4(self): + # pylint: disable=protected-access + cloud = mock.Mock() + cloud._compute_client.request.return_value = { + "version": {"version": "a.b"}} + self.assertEqual(functest_utils.get_nova_version(cloud), None) + cloud._compute_client.request.assert_called_once_with('/', 'GET') + + def test_nova_version(self): + # pylint: disable=protected-access + cloud = mock.Mock() + cloud._compute_client.request.return_value = { + "version": {"version": "2.1"}} + self.assertEqual(functest_utils.get_nova_version(cloud), (2, 1)) + cloud._compute_client.request.assert_called_once_with('/', 'GET') + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 61)) + def test_openstack_version1(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Rocky") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 60)) + def test_openstack_version2(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Queens") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 43)) + def test_openstack_version3(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Pike") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 39)) + def test_openstack_version4(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Ocata") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 26)) + def test_openstack_version5(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Newton") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 13)) + def test_openstack_version6(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Mitaka") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 4)) + def test_openstack_version7(self, *args): + cloud = mock.Mock() + self.assertEqual( + functest_utils.get_openstack_version(cloud), "Liberty") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 1)) + def test_openstack_version8(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version(cloud), "Kilo") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(1, 9)) + def test_openstack_version9(self, *args): + cloud = mock.Mock() + self.assertEqual( + functest_utils.get_openstack_version(cloud), "Unknown") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(3, 1)) + def test_openstack_version10(self, *args): + cloud = mock.Mock() + self.assertEqual( + functest_utils.get_openstack_version(cloud), "Master") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 66)) + def test_openstack_version11(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Stein") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 78)) + def test_openstack_version12(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Train") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 87)) + def test_openstack_version13(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Ussuri") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 88)) + def test_openstack_version14(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Wallaby") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 89)) + def test_openstack_version15(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Xena") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=(2, 92)) + def test_openstack_version16(self, *args): + cloud = mock.Mock() + self.assertEqual(functest_utils.get_openstack_version( + cloud), "Zed") + args[0].assert_called_once_with(cloud) + + @mock.patch('functest.utils.functest_utils.get_nova_version', + return_value=None) + def test_openstack_version_exc(self, *args): + cloud = mock.Mock() + self.assertEqual( + functest_utils.get_openstack_version(cloud), "Unknown") + args[0].assert_called_once_with(cloud) + + def test_convert_dict_to_ini(self): + self.assertEqual( + functest_utils.convert_dict_to_ini({}), "") + self.assertEqual( + functest_utils.convert_dict_to_ini({"a": "b"}), "a:b") + value = functest_utils.convert_dict_to_ini({"a": "b", "c": "d"}) + self.assertTrue(value in ('a:b,c:d', 'c:d,a:b')) + with self.assertRaises(AssertionError): + functest_utils.convert_list_to_ini("") + + def test_convert_list_to_ini(self): + self.assertEqual( + functest_utils.convert_list_to_ini([]), "") + self.assertEqual( + functest_utils.convert_list_to_ini(["a"]), "a") + self.assertEqual( + functest_utils.convert_list_to_ini(["a", "b"]), "a,b") + with self.assertRaises(AssertionError): + functest_utils.convert_list_to_ini("") + + def test_convert_ini_to_dict(self): + self.assertEqual( + functest_utils.convert_ini_to_dict(""), {}) + self.assertEqual( + functest_utils.convert_ini_to_dict("a:b"), {"a": "b"}) + self.assertEqual( + functest_utils.convert_ini_to_dict( + "a:b,c:d"), {"a": "b", "c": "d"}) + self.assertEqual( + functest_utils.convert_ini_to_dict( + "a:b:c,d:e:f"), {"a:b": "c", "d:e": "f"}) + with self.assertRaises(AssertionError): + functest_utils.convert_list_to_ini({}) + + def test_convert_ini_to_list(self): + self.assertEqual( + functest_utils.convert_ini_to_list(""), []) + self.assertEqual( + functest_utils.convert_ini_to_list("a"), ["a"]) + self.assertEqual( + functest_utils.convert_ini_to_list("a,b"), ["a", "b"]) + with self.assertRaises(AssertionError): + functest_utils.convert_ini_to_list([]) + if __name__ == "__main__": logging.disable(logging.CRITICAL) diff --git a/functest/tests/unit/vnf/epc/test_juju_epc.py b/functest/tests/unit/vnf/epc/test_juju_epc.py index 6b4137069..a72c61586 100644 --- a/functest/tests/unit/vnf/epc/test_juju_epc.py +++ b/functest/tests/unit/vnf/epc/test_juju_epc.py @@ -62,7 +62,6 @@ class JujuEpcTesting(unittest.TestCase): 'test_vnf': {}} @unittest.skip("It must be fixed. Please see JIRA FUNCTEST-915") - @mock.patch('snaps.openstack.create_image.OpenStackImage.create') @mock.patch('os.system') def test_prepare_default(self, *args): """ Unittest for Prepare testcase """ diff --git a/functest/tests/unit/vnf/ims/test_ims_base.py b/functest/tests/unit/vnf/ims/test_clearwater.py index 97654748a..f590a2857 100644 --- a/functest/tests/unit/vnf/ims/test_ims_base.py +++ b/functest/tests/unit/vnf/ims/test_clearwater.py @@ -12,15 +12,16 @@ import unittest import mock -from functest.opnfv_tests.vnf.ims import clearwater_ims_base as ims_base +from functest.opnfv_tests.vnf.ims import clearwater -class ClearwaterOnBoardingBaseTesting(unittest.TestCase): +class ClearwaterTesting(unittest.TestCase): def setUp(self): with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' 'os.makedirs'): - self.ims_vnf = ims_base.ClearwaterOnBoardingBase() + self.ims_vnf = clearwater.ClearwaterTesting( + "foo", "0.0.0.0", "0.0.0.0") self.mock_post = mock.Mock() attrs = {'status_code': 201, @@ -37,6 +38,7 @@ class ClearwaterOnBoardingBaseTesting(unittest.TestCase): 'cookies': ""} self.mock_post_200.configure_mock(**attrs) + if __name__ == "__main__": logging.disable(logging.CRITICAL) unittest.main(verbosity=2) diff --git a/functest/tests/unit/vnf/ims/test_cloudify_ims.py b/functest/tests/unit/vnf/ims/test_cloudify_ims.py index 79069a5de..c84adf0ff 100644 --- a/functest/tests/unit/vnf/ims/test_cloudify_ims.py +++ b/functest/tests/unit/vnf/ims/test_cloudify_ims.py @@ -10,56 +10,9 @@ import logging import unittest -import mock - -from functest.core import vnf -from functest.opnfv_tests.vnf.ims import cloudify_ims - class CloudifyImsTesting(unittest.TestCase): - - def setUp(self): - - self.tenant = 'cloudify_ims' - 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': 'clearwater', - 'descriptor': {'version': '108', - 'file_name': 'openstack-blueprint.yaml', - 'name': 'clearwater-opnfv', - 'url': 'https://foo', - 'requirements': {'flavor': - {'name': 'm1.medium', - 'ram_min': 2048}}}} - - with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' - 'os.makedirs'), \ - mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' - 'get_config', return_value={ - 'tenant_images': 'foo', - 'orchestrator': self.orchestrator, - 'vnf': self.vnf, - 'vnf_test_suite': '', - 'version': 'whatever'}): - - self.ims_vnf = cloudify_ims.CloudifyIms() - - self.images = {'image1': 'url1', - 'image2': 'url2'} - self.details = {'orchestrator': {'status': 'PASS', 'duration': 120}, - 'vnf': {}, - 'test_vnf': {}} - - def test_prepare_missing_param(self): - with self.assertRaises(vnf.VnfPreparationException): - self.ims_vnf.prepare() + pass if __name__ == "__main__": diff --git a/functest/tests/unit/vnf/router/test_cloudify_vrouter.py b/functest/tests/unit/vnf/router/test_cloudify_vrouter.py index db03d716c..b3f83e946 100644 --- a/functest/tests/unit/vnf/router/test_cloudify_vrouter.py +++ b/functest/tests/unit/vnf/router/test_cloudify_vrouter.py @@ -12,61 +12,10 @@ import logging import unittest -import mock - -from functest.core import vnf -from functest.opnfv_tests.vnf.router import cloudify_vrouter - 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}}}} - - # pylint: disable=bad-continuation - with mock.patch( - 'functest.opnfv_tests.vnf.router.cloudify_vrouter.Utilvnf'), \ - mock.patch('functest.opnfv_tests.vnf.router.' - 'cloudify_vrouter.vrouter_base.Utilvnf'), \ - mock.patch('os.makedirs'), \ - mock.patch( - 'functest.utils.functest_utils.get_parameter_from_yaml', - 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': {}} - - def test_prepare_missing_param(self): - with self.assertRaises(vnf.VnfPreparationException): - self.router_vnf.prepare() + pass if __name__ == "__main__": diff --git a/functest/tests/unit/vnf/router/test_vrouter_base.py b/functest/tests/unit/vnf/router/test_vrouter_base.py index 1851b201a..330093658 100644 --- a/functest/tests/unit/vnf/router/test_vrouter_base.py +++ b/functest/tests/unit/vnf/router/test_vrouter_base.py @@ -12,17 +12,9 @@ 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() + pass if __name__ == "__main__": diff --git a/functest/utils/config.py b/functest/utils/config.py index 61d8401c5..40414b88b 100644 --- a/functest/utils/config.py +++ b/functest/utils/config.py @@ -10,15 +10,16 @@ import six from functest.utils import env -class Config(object): +class Config(): def __init__(self): try: - # pylint: disable=bad-continuation with open(pkg_resources.resource_filename( - 'functest', 'ci/config_functest.yaml')) as yfile: + 'functest', 'ci/config_functest.yaml'), + encoding='utf-8') as yfile: self.functest_yaml = yaml.safe_load(yfile) except Exception as error: - raise Exception('Parse config failed: {}'.format(str(error))) + raise Exception( + f'Parse config failed: {str(error)}') from error @staticmethod def _merge_dicts(dict1, dict2): @@ -34,7 +35,7 @@ class Config(object): yield (k, dict2[k]) def patch_file(self, patch_file_path): - with open(patch_file_path) as yfile: + with open(patch_file_path, encoding='utf-8') as yfile: patch_file = yaml.safe_load(yfile) for key in patch_file: @@ -53,13 +54,14 @@ class Config(object): @staticmethod def _get_attr_further(attr_now, next): # pylint: disable=redefined-builtin return attr_now if next == 'general' else ( - '{}_{}'.format(attr_now, next) if attr_now else next) + f'{attr_now}_{next}' if attr_now else next) def fill(self): try: self._parse(None, self.functest_yaml) except Exception as error: - raise Exception('Parse config failed: {}'.format(str(error))) + raise Exception( + f'Parse config failed: {str(error)}') from error CONF = Config() diff --git a/functest/utils/env.py b/functest/utils/env.py index cba935ab8..2e312726c 100644 --- a/functest/utils/env.py +++ b/functest/utils/env.py @@ -17,6 +17,7 @@ from xtesting.utils import env INPUTS = { 'EXTERNAL_NETWORK': None, 'CI_LOOP': env.INPUTS['CI_LOOP'], + 'DEBUG': env.INPUTS['DEBUG'], 'DEPLOY_SCENARIO': env.INPUTS['DEPLOY_SCENARIO'], 'INSTALLER_TYPE': env.INPUTS['INSTALLER_TYPE'], 'SDN_CONTROLLER_IP': None, @@ -28,11 +29,19 @@ INPUTS = { 'NODE_NAME': env.INPUTS['NODE_NAME'], 'POD_ARCH': None, 'TEST_DB_URL': env.INPUTS['TEST_DB_URL'], - 'ENERGY_RECORDER_API_URL': env.INPUTS['ENERGY_RECORDER_API_URL'], - 'ENERGY_RECORDER_API_USER': env.INPUTS['ENERGY_RECORDER_API_USER'], - 'ENERGY_RECORDER_API_PASSWORD': env.INPUTS['ENERGY_RECORDER_API_PASSWORD'], 'VOLUME_DEVICE_NAME': 'vdb', - 'NAMESERVER': '8.8.8.8' + 'IMAGE_PROPERTIES': '', + 'FLAVOR_EXTRA_SPECS': '', + 'NAMESERVER': '8.8.8.8', + 'NEW_USER_ROLE': 'Member', + 'USE_DYNAMIC_CREDENTIALS': 'True', + 'BLOCK_MIGRATION': 'False', + 'CLEAN_ORPHAN_SECURITY_GROUPS': 'True', + 'SKIP_DOWN_HYPERVISORS': 'False', + 'PUBLIC_ENDPOINT_ONLY': 'False', + 'DASHBOARD_URL': '', + 'VMTP_HYPERVISORS': '', + 'NO_TENANT_NETWORK': 'False' } diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index b614af321..eec544489 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -11,10 +11,14 @@ from __future__ import print_function import logging +import os import subprocess import sys import yaml +from shade import _utils +import six + LOGGER = logging.getLogger(__name__) @@ -28,28 +32,26 @@ def execute_command_raise(cmd, info=False, error_msg="", def execute_command(cmd, info=False, error_msg="", verbose=True, output_file=None): if not error_msg: - error_msg = ("The command '%s' failed." % cmd) - msg_exec = ("Executing command: '%s'" % cmd) + error_msg = f"The command '{cmd}' failed." + msg_exec = f"Executing command: '{cmd}'" if verbose: if info: LOGGER.info(msg_exec) else: LOGGER.debug(msg_exec) - popen = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if output_file: - ofd = open(output_file, "w") - for line in iter(popen.stdout.readline, b''): + with subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) as popen: if output_file: - ofd.write(line) - else: - line = line.replace('\n', '') - print (line) - sys.stdout.flush() - if output_file: - ofd.close() - popen.stdout.close() - returncode = popen.wait() + with open(output_file, "w", encoding='utf-8') as ofd: + for line in iter(popen.stdout.readline, b''): + if output_file: + ofd.write(line.decode("utf-8")) + else: + line = line.decode("utf-8").replace('\n', '') + print(line) + sys.stdout.flush() + returncode = popen.wait() if returncode != 0: if verbose: LOGGER.error(error_msg) @@ -63,12 +65,161 @@ def get_parameter_from_yaml(parameter, yfile): parameter must be given in string format with dots Example: general.openstack.image_name """ - with open(yfile) as yfd: + with open(yfile, encoding='utf-8') as yfd: file_yaml = yaml.safe_load(yfd) 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" - " %s" % (parameter, yfile)) + raise ValueError(f"The parameter {parameter} is not defined in" + f" {yfile}") return value + + +def get_nova_version(cloud): + """ Get Nova API microversion + + Returns: + + - Nova API microversion + - None on operation error + """ + # pylint: disable=protected-access + try: + request = cloud._compute_client.request("/", "GET") + LOGGER.debug('cloud._compute_client.request: %s', request) + version = request["version"]["version"] + major, minor = version.split('.') + LOGGER.debug('nova version: %s', (int(major), int(minor))) + return (int(major), int(minor)) + except Exception: # pylint: disable=broad-except + LOGGER.exception("Cannot detect Nova version") + return None + + +def get_openstack_version(cloud): + """ Detect OpenStack version via Nova API microversion + + It follows `MicroversionHistory + <https://docs.openstack.org/nova/latest/reference/api-microversion-history.html>`_. + + Returns: + + - OpenStack release + - Unknown on operation error + """ + # pylint: disable=too-many-branches + version = get_nova_version(cloud) + try: + assert version + if version > (2, 93): + osversion = "Master" + elif version > (2, 90): + osversion = "Zed" + elif version > (2, 88): + osversion = "Xena" + elif version > (2, 87): + osversion = "Wallaby" + elif version > (2, 79): + osversion = "Ussuri" + elif version > (2, 72): + osversion = "Train" + elif version > (2, 65): + osversion = "Stein" + elif version > (2, 60): + osversion = "Rocky" + elif version > (2, 53): + osversion = "Queens" + elif version > (2, 42): + osversion = "Pike" + elif version > (2, 38): + osversion = "Ocata" + elif version > (2, 25): + osversion = "Newton" + elif version > (2, 12): + osversion = "Mitaka" + elif version > (2, 3): + osversion = "Liberty" + elif version >= (2, 1): + osversion = "Kilo" + else: + osversion = "Unknown" + LOGGER.info('Detect OpenStack version: %s', osversion) + return osversion + except AssertionError: + LOGGER.exception("Cannot detect OpenStack version") + return "Unknown" + + +def list_services(cloud): + # pylint: disable=protected-access + """Search Keystone services via $OS_INTERFACE. + + It mainly conforms with `Shade + <https://docs.openstack.org/shade/latest>`_ but allows testing vs + public endpoints. It's worth mentioning that it doesn't support keystone + v2. + + :returns: a list of ``munch.Munch`` containing the services description + + :raises: ``OpenStackCloudException`` if something goes wrong during the + openstack API call. + """ + url, key = '/services', 'services' + data = cloud._identity_client.get( + url, endpoint_filter={ + 'interface': os.environ.get('OS_INTERFACE', 'public')}, + error_message="Failed to list services") + services = cloud._get_and_munchify(key, data) + return _utils.normalize_keystone_services(services) + + +def search_services(cloud, name_or_id=None, filters=None): + # pylint: disable=protected-access + """Search Keystone services ia $OS_INTERFACE. + + It mainly conforms with `Shade + <https://docs.openstack.org/shade/latest>`_ but allows testing vs + public endpoints. It's worth mentioning that it doesn't support keystone + v2. + + :param name_or_id: Name or id of the desired service. + :param filters: a dict containing additional filters to use. e.g. + {'type': 'network'}. + + :returns: a list of ``munch.Munch`` containing the services description + + :raises: ``OpenStackCloudException`` if something goes wrong during the + openstack API call. + """ + services = list_services(cloud) + return _utils._filter_list(services, name_or_id, filters) + + +def convert_dict_to_ini(value): + "Convert dict to oslo.conf input" + assert isinstance(value, dict) + return ",".join(f"{key}:{val}" for (key, val) in six.iteritems(value)) + + +def convert_list_to_ini(value): + "Convert list to oslo.conf input" + assert isinstance(value, list) + return ",".join(val for val in value) + + +def convert_ini_to_dict(value): + "Convert oslo.conf input to dict" + assert isinstance(value, str) + try: + return dict((x.rsplit(':', 1) for x in value.split(','))) + except ValueError: + return {} + + +def convert_ini_to_list(value): + "Convert list to oslo.conf input" + assert isinstance(value, str) + if not value: + return [] + return list(value.split(',')) |