diff options
Diffstat (limited to 'functest')
32 files changed, 1397 insertions, 1088 deletions
diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index c75afdaf..11ff7fdb 100755 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -1,206 +1,211 @@ -general:
- directories:
- # Relative to the path where the repo is cloned:
- dir_vping: functest/opnfv_tests/openstack/vping
- dir_odl: functest/opnfv_tests/sdn/odl
- dir_rally: functest/opnfv_tests/openstack/rally
- dir_tempest_cases: functest/opnfv_tests/openstack/tempest/custom_tests
- dir_vIMS: functest/opnfv_tests/vnf/ims
- dir_onos: functest/opnfv_tests/sdn/onos/teston
- dir_onos_sfc: functest/opnfv_tests/sdn/onos/sfc
-
- # Absolute path
- dir_home: /home/opnfv
- dir_repos: /home/opnfv/repos
- dir_repo_functest: /home/opnfv/repos/functest
- dir_repo_rally: /home/opnfv/repos/rally
- dir_repo_tempest: /home/opnfv/repos/tempest
- dir_repo_releng: /home/opnfv/repos/releng
- dir_repo_vims_test: /home/opnfv/repos/vims-test
- dir_repo_sdnvpn: /home/opnfv/repos/sdnvpn
- dir_repo_sfc: /home/opnfv/repos/sfc
- dir_repo_onos: /home/opnfv/repos/onos
- dir_repo_promise: /home/opnfv/repos/promise
- dir_repo_doctor: /home/opnfv/repos/doctor
- dir_repo_copper: /home/opnfv/repos/copper
- dir_repo_ovno: /home/opnfv/repos/ovno
- dir_repo_parser: /home/opnfv/repos/parser
- dir_repo_domino: /home/opnfv/repos/domino
- dir_functest: /home/opnfv/functest
- dir_functest_test: /home/opnfv/repos/functest/functest/opnfv_tests
- dir_results: /home/opnfv/functest/results
- dir_functest_conf: /home/opnfv/functest/conf
- dir_functest_data: /home/opnfv/functest/data
- dir_vIMS_data: /home/opnfv/functest/data/vIMS/
- dir_rally_inst: /home/opnfv/.rally
-
- openstack:
- creds: /home/opnfv/functest/conf/openstack.creds
- snapshot_file: /home/opnfv/functest/conf/openstack_snapshot.yaml
-
- image_name: Cirros-0.3.4
- image_file_name: cirros-0.3.4-x86_64-disk.img
- image_disk_format: qcow2
-
- flavor_name: opnfv_flavor
- flavor_ram: 512
- flavor_disk: 1
- flavor_vcpus: 1
-
- # Private network for functest. Will be created by config_functest.py
- neutron_private_net_name: functest-net
- neutron_private_subnet_name: functest-subnet
- neutron_private_subnet_cidr: 192.168.120.0/24
- neutron_private_subnet_start: 192.168.120.2
- neutron_private_subnet_end: 192.168.120.254
- neutron_private_subnet_gateway: 192.168.120.254
- neutron_router_name: functest-router
-
- functest:
- testcases_yaml: /home/opnfv/repos/functest/functest/ci/testcases.yaml
-
-healthcheck:
- disk_image: /home/opnfv/functest/data/cirros-0.3.4-x86_64-disk.img
- disk_format: qcow2
- wait_time: 60
-
-vping:
- ping_timeout: 200
- vm_flavor: m1.tiny # adapt to your environment
- vm_name_1: opnfv-vping-1
- vm_name_2: opnfv-vping-2
- image_name: functest-vping
- vping_private_net_name: vping-net
- vping_private_subnet_name: vping-subnet
- vping_private_subnet_cidr: 192.168.130.0/24
- vping_router_name: vping-router
- vping_sg_name: vPing-sg
- vping_sg_descr: Security group for vPing test case
-
-onos_sfc:
- image_base_url: http://artifacts.opnfv.org/sfc/demo
- image_name: TestSfcVm
- image_file_name: firewall_block_image.img
-
-tempest:
- identity:
- tenant_name: tempest
- tenant_description: Tenant for Tempest test suite
- user_name: tempest
- user_password: tempest
- validation:
- ssh_timeout: 130
- private_net_name: tempest-net
- private_subnet_name: tempest-subnet
- private_subnet_cidr: 192.168.150.0/24
- router_name: tempest-router
- use_custom_images: False
- use_custom_flavors: False
-
-rally:
- deployment_name: opnfv-rally
- network_name: rally-net
- subnet_name: rally-subnet
- subnet_cidr: 192.168.140.0/24
- router_name: rally-router
-
-vIMS:
- general:
- tenant_name: vIMS
- tenant_description: vIMS Functionality Testing
- images:
- ubuntu:
- image_url: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
- image_name: ubuntu_14.04
- centos:
- image_url: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2
- image_name: centos_7
- cloudify:
- blueprint:
- url: https://github.com/boucherv-orange/cloudify-manager-blueprints.git
- branch: "3.3.1-build"
- requierments:
- ram_min: 3000
- os_image: centos_7
- inputs:
- keystone_username: ""
- keystone_password: ""
- keystone_tenant_name: ""
- keystone_url: ""
- manager_public_key_name: 'manager-kp'
- agent_public_key_name: 'agent-kp'
- image_id: ""
- flavor_id: "3"
- external_network_name: ""
- ssh_user: centos
- agents_user: ubuntu
- clearwater:
- blueprint:
- file_name: 'openstack-blueprint.yaml'
- name: "clearwater-opnfv"
- destination_folder: "opnfv-cloudify-clearwater"
- url: https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git
- branch: "stable"
- deployment-name: 'clearwater-opnfv'
- requierments:
- ram_min: 1700
- os_image: ubuntu_14.04
- inputs:
- image_id: ''
- flavor_id: ''
- agent_user: 'ubuntu'
- external_network_name: ''
- public_domain: clearwater.opnfv
-ONOS:
- general:
- onosbench_username: 'root'
- onosbench_password: 'root'
- onoscli_username: 'root'
- onoscli_password: 'root'
- runtimeout: 300
- environment:
- OCT: '10.20.0.1'
- OC1: '10.20.0.7'
- OC2: '10.20.0.7'
- OC3: '10.20.0.7'
- OCN: '10.20.0.4'
- OCN2: '10.20.0.5'
- installer_master: '10.20.0.2'
- installer_master_username: 'root'
- installer_master_password: 'r00tme'
-multisite:
- fuel_environment:
- installer_username: 'root'
- installer_password: 'r00tme'
- compass_environment:
- installer_username: 'root'
- installer_password: 'root'
- multisite_controller_ip: '10.1.0.50'
-promise:
- tenant_name: promise
- tenant_description: promise Functionality Testing
- user_name: promiser
- user_pwd: test
- image_name: promise-img
- flavor_name: promise-flavor
- flavor_vcpus: 1
- flavor_ram: 128
- flavor_disk: 0
- network_name: promise-net
- subnet_name: promise-subnet
- subnet_cidr: 192.168.121.0/24
- router_name: promise-router
-
-example:
- example_vm_name: example-vm
- example_flavor: m1.small
- example_image_name: functest-example-vm
- example_private_net_name: example-net
- example_private_subnet_name: example-subnet
- example_private_subnet_cidr: 192.168.170.0/24
- example_router_name: example-router
- example_sg_name: example-sg
- example_sg_descr: Example Security group
-
-results:
- test_db_url: http://testresults.opnfv.org/test/api/v1
+general: + directories: + # Relative to the path where the repo is cloned: + dir_vping: functest/opnfv_tests/openstack/vping + dir_odl: functest/opnfv_tests/sdn/odl + dir_rally: functest/opnfv_tests/openstack/rally + dir_tempest_cases: functest/opnfv_tests/openstack/tempest/custom_tests + dir_vIMS: functest/opnfv_tests/vnf/ims + dir_onos: functest/opnfv_tests/sdn/onos/teston + dir_onos_sfc: functest/opnfv_tests/sdn/onos/sfc + + # Absolute path + dir_home: /home/opnfv + dir_repos: /home/opnfv/repos + dir_repo_functest: /home/opnfv/repos/functest + dir_repo_rally: /home/opnfv/repos/rally + dir_repo_tempest: /home/opnfv/repos/tempest + dir_repo_releng: /home/opnfv/repos/releng + dir_repo_vims_test: /home/opnfv/repos/vims-test + dir_repo_sdnvpn: /home/opnfv/repos/sdnvpn + dir_repo_sfc: /home/opnfv/repos/sfc + dir_repo_onos: /home/opnfv/repos/onos + dir_repo_promise: /home/opnfv/repos/promise + dir_repo_doctor: /home/opnfv/repos/doctor + dir_repo_copper: /home/opnfv/repos/copper + dir_repo_ovno: /home/opnfv/repos/ovno + dir_repo_parser: /home/opnfv/repos/parser + dir_repo_domino: /home/opnfv/repos/domino + dir_repo_snaps: /home/opnfv/repos/snaps + dir_functest: /home/opnfv/functest + dir_functest_test: /home/opnfv/repos/functest/functest/opnfv_tests + dir_results: /home/opnfv/functest/results + dir_functest_conf: /home/opnfv/functest/conf + dir_functest_data: /home/opnfv/functest/data + dir_vIMS_data: /home/opnfv/functest/data/vIMS/ + dir_rally_inst: /home/opnfv/.rally + + openstack: + creds: /home/opnfv/functest/conf/openstack.creds + snapshot_file: /home/opnfv/functest/conf/openstack_snapshot.yaml + + image_name: Cirros-0.3.4 + image_file_name: cirros-0.3.4-x86_64-disk.img + image_disk_format: qcow2 + + flavor_name: opnfv_flavor + flavor_ram: 512 + flavor_disk: 1 + flavor_vcpus: 1 + + # Private network for functest. Will be created by config_functest.py + neutron_private_net_name: functest-net + neutron_private_subnet_name: functest-subnet + neutron_private_subnet_cidr: 192.168.120.0/24 + neutron_private_subnet_start: 192.168.120.2 + neutron_private_subnet_end: 192.168.120.254 + neutron_private_subnet_gateway: 192.168.120.254 + neutron_router_name: functest-router + + functest: + testcases_yaml: /home/opnfv/repos/functest/functest/ci/testcases.yaml + +healthcheck: + disk_image: /home/opnfv/functest/data/cirros-0.3.4-x86_64-disk.img + disk_format: qcow2 + wait_time: 60 + +snaps: + use_keystone: True + use_floating_ips: False + +vping: + ping_timeout: 200 + vm_flavor: m1.tiny # adapt to your environment + vm_name_1: opnfv-vping-1 + vm_name_2: opnfv-vping-2 + image_name: functest-vping + vping_private_net_name: vping-net + vping_private_subnet_name: vping-subnet + vping_private_subnet_cidr: 192.168.130.0/24 + vping_router_name: vping-router + vping_sg_name: vPing-sg + vping_sg_descr: Security group for vPing test case + +onos_sfc: + image_base_url: http://artifacts.opnfv.org/sfc/demo + image_name: TestSfcVm + image_file_name: firewall_block_image.img + +tempest: + identity: + tenant_name: tempest + tenant_description: Tenant for Tempest test suite + user_name: tempest + user_password: tempest + validation: + ssh_timeout: 130 + private_net_name: tempest-net + private_subnet_name: tempest-subnet + private_subnet_cidr: 192.168.150.0/24 + router_name: tempest-router + use_custom_images: False + use_custom_flavors: False + +rally: + deployment_name: opnfv-rally + network_name: rally-net + subnet_name: rally-subnet + subnet_cidr: 192.168.140.0/24 + router_name: rally-router + +vIMS: + general: + tenant_name: vIMS + tenant_description: vIMS Functionality Testing + images: + ubuntu: + image_url: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img + image_name: ubuntu_14.04 + centos: + image_url: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2 + image_name: centos_7 + cloudify: + blueprint: + url: https://github.com/boucherv-orange/cloudify-manager-blueprints.git + branch: "3.3.1-build" + requierments: + ram_min: 3000 + os_image: centos_7 + inputs: + keystone_username: "" + keystone_password: "" + keystone_tenant_name: "" + keystone_url: "" + manager_public_key_name: 'manager-kp' + agent_public_key_name: 'agent-kp' + image_id: "" + flavor_id: "3" + external_network_name: "" + ssh_user: centos + agents_user: ubuntu + clearwater: + blueprint: + file_name: 'openstack-blueprint.yaml' + name: "clearwater-opnfv" + destination_folder: "opnfv-cloudify-clearwater" + url: https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git + branch: "stable" + deployment-name: 'clearwater-opnfv' + requierments: + ram_min: 1700 + os_image: ubuntu_14.04 + inputs: + image_id: '' + flavor_id: '' + agent_user: 'ubuntu' + external_network_name: '' + public_domain: clearwater.opnfv +ONOS: + general: + onosbench_username: 'root' + onosbench_password: 'root' + onoscli_username: 'root' + onoscli_password: 'root' + runtimeout: 300 + environment: + OCT: '10.20.0.1' + OC1: '10.20.0.7' + OC2: '10.20.0.7' + OC3: '10.20.0.7' + OCN: '10.20.0.4' + OCN2: '10.20.0.5' + installer_master: '10.20.0.2' + installer_master_username: 'root' + installer_master_password: 'r00tme' +multisite: + fuel_environment: + installer_username: 'root' + installer_password: 'r00tme' + compass_environment: + installer_username: 'root' + installer_password: 'root' + multisite_controller_ip: '10.1.0.50' +promise: + tenant_name: promise + tenant_description: promise Functionality Testing + user_name: promiser + user_pwd: test + image_name: promise-img + flavor_name: promise-flavor + flavor_vcpus: 1 + flavor_ram: 128 + flavor_disk: 0 + network_name: promise-net + subnet_name: promise-subnet + subnet_cidr: 192.168.121.0/24 + router_name: promise-router + +example: + example_vm_name: example-vm + example_flavor: m1.small + example_image_name: functest-example-vm + example_private_net_name: example-net + example_private_subnet_name: example-subnet + example_private_subnet_cidr: 192.168.170.0/24 + example_router_name: example-router + example_sg_name: example-sg + example_sg_descr: Example Security group + +results: + test_db_url: http://testresults.opnfv.org/test/api/v1 diff --git a/functest/ci/exec_test.sh b/functest/ci/exec_test.sh index 913ce08e..2b4cd8b4 100755 --- a/functest/ci/exec_test.sh +++ b/functest/ci/exec_test.sh @@ -61,17 +61,7 @@ function odl_tests(){ fi } -function sfc_prepare(){ - ids=($(neutron security-group-list|grep default|awk '{print $2}')) - for id in ${ids[@]}; do - if ! neutron security-group-show $id|grep "22/tcp" &>/dev/null; then - neutron security-group-rule-create --protocol tcp \ - --port-range-min 22 --port-range-max 22 --direction ingress $id - neutron security-group-rule-create --protocol tcp \ - --port-range-min 22 --port-range-max 22 --direction egress $id - fi - done -} + function run_test(){ test_name=$1 @@ -93,14 +83,6 @@ function run_test(){ --ospassword ${OS_PASSWORD} \ --odlip $odl_ip --odlwebport $odl_port ${args} ;; - "tempest_smoke_serial") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $clean_flag -s -m smoke $report - ;; - "tempest_full_parallel") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $serial_flag $clean_flag -m full $report - ;; "vims") python ${FUNCTEST_TEST_DIR}/vnf/ims/vims.py $clean_flag $report ;; @@ -140,19 +122,6 @@ function run_test(){ "moon") python ${REPOS_DIR}/moon/tests/run_tests.py $report ;; - "multisite") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/gen_tempest_conf.py - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $clean_flag -s -m feature_multisite $report \ - -c ${FUNCTEST_TEST_DIR}/openstack/tempest/tempest_multisite.conf - ;; - "odl-sfc") - ODL_SFC_DIR=${REPOS_DIR}/sfc/tests/functest/odl-sfc - # pass FUNCTEST_REPO_DIR inside prepare_odl_sfc.bash - FUNCTEST_REPO_DIR=${FUNCTEST_REPO_DIR} python ${ODL_SFC_DIR}/prepare_odl_sfc.py || exit $? - source ${ODL_SFC_DIR}/tackerc - python ${ODL_SFC_DIR}/sfc.py $report - ;; *) echo "The test case '${test_name}' does not exist." exit 1 @@ -197,10 +166,6 @@ done echo "Sourcing Credentials ${creds} to run the test.." source ${creds} -# ODL Boron workaround to create additional flow rules to allow port 22 TCP -if [[ $DEPLOY_SCENARIO == *"odl_l2-sfc"* ]]; then - sfc_prepare -fi # Run test run_test $TEST diff --git a/functest/ci/run_tests.py b/functest/ci/run_tests.py index d2a64aea..557ba08d 100644..100755 --- a/functest/ci/run_tests.py +++ b/functest/ci/run_tests.py @@ -124,6 +124,7 @@ def run_test(test, tier_name): logger.info("Running test case '%s'..." % test_name) print_separator("=") logger.debug("\n%s" % test) + source_rc_file() if GlobalVariables.CLEAN_FLAG: generate_os_snapshot() @@ -140,9 +141,10 @@ def run_test(test, tier_name): cls = getattr(module, run_dict['class']) test_case = cls() result = test_case.run() - if (result == testcase_base.TestcaseBase.EX_OK and - GlobalVariables.REPORT_FLAG): - result = test_case.push_to_db() + if result == testcase_base.TestcaseBase.EX_OK: + if GlobalVariables.REPORT_FLAG: + test_case.push_to_db() + result = test_case.check_criteria() except ImportError: logger.exception("Cannot import module {}".format( run_dict['module'])) diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index d483e589..6f57c703 100755 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -67,7 +67,9 @@ tiers: dependencies: installer: '' scenario: '' - + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestSmokeSerial' - name: rally_sanity criteria: 'success_rate == 100%' @@ -106,6 +108,62 @@ tiers: installer: '' scenario: 'onos' + - + name: connection_check + criteria: 'status == "PASS"' + 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: + installer: '' + scenario: '' + run: + module: 'functest.opnfv_tests.openstack.snaps.connection_check' + class: 'ConnectionCheck' + + - + name: api_check + criteria: 'status == "PASS"' + 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: + installer: '' + scenario: '' + run: + module: 'functest.opnfv_tests.openstack.snaps.api_check' + class: 'ApiCheck' + + - + name: snaps_smoke + criteria: 'status == "PASS"' + 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: + installer: '' + scenario: '' + run: + module: 'functest.opnfv_tests.openstack.snaps.smoke' + class: 'SnapsSmoke' + - name: features order: 2 @@ -166,6 +224,10 @@ tiers: dependencies: installer: '(apex)|(joid)' scenario: '^((?!fdio|lxd).)*$' + run: + module: 'functest.opnfv_tests.features.copper' + class: 'Copper' + - name: moon criteria: 'status == "PASS"' @@ -184,6 +246,9 @@ tiers: dependencies: installer: '(fuel)|(compass)' scenario: 'multisite' + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestMultisite' - name: odl-sfc criteria: 'status == "PASS"' @@ -193,6 +258,9 @@ tiers: dependencies: installer: '(apex)|(fuel)' scenario: 'odl_l2-sfc' + run: + module: 'functest.opnfv_tests.features.odl_sfc' + class: 'OpenDaylightSFC' - name: onos_sfc criteria: 'status == "PASS"' @@ -215,7 +283,7 @@ tiers: module: 'functest.opnfv_tests.vnf.rnc.parser' class: 'Parser' - - name: openstack + name: components order: 3 ci_loop: 'weekly' description : >- @@ -232,6 +300,9 @@ tiers: dependencies: installer: '' scenario: '' + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestFullParallel' - name: rally_full diff --git a/functest/cli/commands/cli_testcase.py b/functest/cli/commands/cli_testcase.py index efe177d5..70a77a14 100644 --- a/functest/cli/commands/cli_testcase.py +++ b/functest/cli/commands/cli_testcase.py @@ -50,10 +50,12 @@ class CliTestcase: click.echo("Functest environment is not ready. " "Run first 'functest env prepare'") else: - if noclean: - cmd = ("python %s/functest/ci/run_tests.py " - "-n -t %s" % (ft_constants.FUNCTEST_REPO_DIR, testname)) - else: - cmd = ("python %s/functest/ci/run_tests.py " - "-t %s" % (ft_constants.FUNCTEST_REPO_DIR, testname)) - ft_utils.execute_command(cmd) + tests = testname.split(",") + for test in tests: + if noclean: + cmd = ("python %s/functest/ci/run_tests.py " + "-n -t %s" % (ft_constants.FUNCTEST_REPO_DIR, test)) + else: + cmd = ("python %s/functest/ci/run_tests.py " + "-t %s" % (ft_constants.FUNCTEST_REPO_DIR, test)) + ft_utils.execute_command(cmd) diff --git a/functest/core/feature_base.py b/functest/core/feature_base.py new file mode 100644 index 00000000..01a27f30 --- /dev/null +++ b/functest/core/feature_base.py @@ -0,0 +1,58 @@ +import time + +import testcase_base as base +import functest.utils.functest_utils as ft_utils +import functest.utils.functest_logger as ft_logger + + +class FeatureBase(base.TestcaseBase): + def __init__(self, project='functest', case='', repo='', cmd=''): + super(FeatureBase, self).__init__() + self.project_name = project + self.case_name = case + self.cmd = cmd + self.repo = self.get_conf('general.directories.{}'.format(repo)) + self.result_file = self.get_result_file() + self.logger = ft_logger.Logger(project).getLogger() + + def run(self, **kwargs): + self.prepare() + self.start_time = time.time() + ret = ft_utils.execute_command(self.cmd, output_file=self.result_file) + self.stop_time = time.time() + self.post() + self.parse_results(ret) + self.log_results() + return base.TestcaseBase.EX_OK + + def prepare(self, **kwargs): + pass + + def post(self, **kwargs): + pass + + def parse_results(self, ret): + exit_code = base.TestcaseBase.EX_OK + if ret == 0: + self.logger.info("{} OK".format(self.project_name)) + self.criteria = 'PASS' + else: + self.logger.info("{} FAILED".format(self.project_name)) + exit_code = base.TestcaseBase.EX_RUN_ERROR + self.criteria = "FAIL" + + return exit_code + + def get_result_file(self): + dir = self.get_conf('general.directories.dir_results') + return "{}/{}.log".format(dir, self.project_name) + + def log_results(self): + ft_utils.logger_test_results(self.project_name, + self.case_name, + self.criteria, + self.details) + + @staticmethod + def get_conf(parameter): + return ft_utils.get_functest_config(parameter) diff --git a/functest/core/pytest_suite_runner.py b/functest/core/pytest_suite_runner.py new file mode 100644 index 00000000..2d5b2667 --- /dev/null +++ b/functest/core/pytest_suite_runner.py @@ -0,0 +1,55 @@ +#!/usr/bin/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 + +import testcase_base as base +import unittest +import time + + +class PyTestSuiteRunner(base.TestcaseBase): + """ + This superclass is designed to execute pre-configured unittest.TestSuite() + objects + """ + def __init__(self): + super(PyTestSuiteRunner, self).__init__() + self.suite = None + + def run(self, **kwargs): + """ + Starts test execution from the functest framework + """ + self.start_time = time.time() + result = unittest.TextTestRunner(verbosity=2).run(self.suite) + self.stop_time = time.time() + + if result.errors: + self.logger.error('Number of errors in test suite - ' + + str(len(result.errors))) + for test, message in result.errors: + self.logger.error(str(test) + " ERROR with " + message) + + if result.failures: + self.logger.error('Number of failures in test suite - ' + + str(len(result.failures))) + for test, message in result.failures: + self.logger.error(str(test) + " FAILED with " + message) + + if (result.errors and len(result.errors) > 0) \ + or (result.failures and len(result.failures) > 0): + self.logger.info("%s FAILED" % self.case_name) + self.criteria = 'FAIL' + exit_code = base.TestcaseBase.EX_RUN_ERROR + else: + self.logger.info("%s OK" % self.case_name) + exit_code = base.TestcaseBase.EX_OK + self.criteria = 'PASS' + + self.details = {} + return exit_code diff --git a/functest/core/testcase_base.py b/functest/core/testcase_base.py index e869803d..838b6398 100644 --- a/functest/core/testcase_base.py +++ b/functest/core/testcase_base.py @@ -18,6 +18,7 @@ class TestcaseBase(object): EX_OK = os.EX_OK EX_RUN_ERROR = os.EX_SOFTWARE EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1 + EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2 logger = ft_logger.Logger(__name__).getLogger() @@ -29,6 +30,15 @@ class TestcaseBase(object): self.start_time = "" self.stop_time = "" + def check_criteria(self): + try: + assert self.criteria + if self.criteria == 'PASS': + return TestcaseBase.EX_OK + except: + self.logger.error("Please run test before checking the results") + return TestcaseBase.EX_TESTCASE_FAILED + def run(self, **kwargs): self.logger.error("Run must be implemented") return TestcaseBase.EX_RUN_ERROR diff --git a/functest/opnfv_tests/features/copper.py b/functest/opnfv_tests/features/copper.py index d003779e..8d5393c9 100755 --- a/functest/opnfv_tests/features/copper.py +++ b/functest/opnfv_tests/features/copper.py @@ -14,70 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import argparse -import sys -import time +import functest.core.feature_base as base -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as functest_utils -import functest.utils.functest_constants as ft_constants -parser = argparse.ArgumentParser() -parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") -args = parser.parse_args() - -COPPER_REPO_DIR = ft_constants.COPPER_REPO_DIR -RESULTS_DIR = ft_constants.FUNCTEST_RESULTS_DIR - -logger = ft_logger.Logger("copper").getLogger() - - -def main(): - cmd = "%s/tests/run.sh %s/tests" % (COPPER_REPO_DIR, COPPER_REPO_DIR) - - start_time = time.time() - - log_file = RESULTS_DIR + "/copper.log" - ret_val = functest_utils.execute_command(cmd, - output_file=log_file) - - stop_time = time.time() - duration = round(stop_time - start_time, 1) - if ret_val == 0: - logger.info("COPPER PASSED") - test_status = 'PASS' - else: - logger.info("COPPER FAILED") - test_status = 'FAIL' - - details = { - 'timestart': start_time, - 'duration': duration, - 'status': test_status, - } - functest_utils.logger_test_results("Copper", - "copper-notification", - details['status'], details) - try: - if args.report: - functest_utils.push_results_to_db("copper", - "copper-notification", - start_time, - stop_time, - details['status'], - details) - logger.info("COPPER results pushed to DB") - except: - logger.error("Error pushing results into Database '%s'" - % sys.exc_info()[0]) - - if ret_val != 0: - sys.exit(-1) - - sys.exit(0) - - -if __name__ == '__main__': - main() +class Copper(base.FeatureBase): + def __init__(self): + super(Copper, self).__init__(project='copper', + case='copper-notification', + repo='dir_repo_copper') + self.cmd = "%s/tests/run.sh %s/tests" % (self.repo, self.repo) diff --git a/functest/opnfv_tests/features/odl_sfc.py b/functest/opnfv_tests/features/odl_sfc.py new file mode 100644 index 00000000..b194b284 --- /dev/null +++ b/functest/opnfv_tests/features/odl_sfc.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# +# Copyright (c) 2016 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +import functest.core.feature_base as base + + +class OpenDaylightSFC(base.FeatureBase): + + def __init__(self): + super(OpenDaylightSFC, self).__init__(project='sfc', + case='functest-odl-sfc"', + repo='dir_repo_sfc') + self.cmd = 'cd %s/tests/functest && python ./run_tests.py' % self.repo diff --git a/functest/opnfv_tests/features/sdnvpn.py b/functest/opnfv_tests/features/sdnvpn.py index 451299eb..451299eb 100644..100755 --- a/functest/opnfv_tests/features/sdnvpn.py +++ b/functest/opnfv_tests/features/sdnvpn.py diff --git a/functest/opnfv_tests/openstack/snaps/__init__.py b/functest/opnfv_tests/openstack/snaps/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/__init__.py diff --git a/functest/opnfv_tests/openstack/snaps/api_check.py b/functest/opnfv_tests/openstack/snaps/api_check.py new file mode 100644 index 00000000..27889209 --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/api_check.py @@ -0,0 +1,32 @@ +# 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 + +import functest.utils.functest_utils as ft_utils +from functest.core.pytest_suite_runner import PyTestSuiteRunner +from functest.opnfv_tests.openstack.snaps import snaps_utils +from snaps import test_suite_builder +import unittest + + +class ApiCheck(PyTestSuiteRunner): + """ + 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): + super(ApiCheck, self).__init__() + + self.suite = unittest.TestSuite() + self.case_name = "api_check" + creds_file = ft_utils.get_functest_config('general.openstack.creds') + use_key = ft_utils.get_functest_config('snaps.use_keystone') + ext_net_name = snaps_utils.get_ext_net_name() + + test_suite_builder.add_openstack_api_tests(self.suite, creds_file, + ext_net_name, + use_keystone=use_key) diff --git a/functest/opnfv_tests/openstack/snaps/connection_check.py b/functest/opnfv_tests/openstack/snaps/connection_check.py new file mode 100644 index 00000000..c2f5b102 --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/connection_check.py @@ -0,0 +1,32 @@ +# 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 + +import functest.utils.functest_utils as ft_utils +from functest.core.pytest_suite_runner import PyTestSuiteRunner +from functest.opnfv_tests.openstack.snaps import snaps_utils +from snaps import test_suite_builder +import unittest + + +class ConnectionCheck(PyTestSuiteRunner): + """ + This test executes the Python Tests included with the SNAPS libraries + that simply obtain the different OpenStack clients and may perform + simple queries + """ + def __init__(self): + super(ConnectionCheck, self).__init__() + + self.suite = unittest.TestSuite() + self.case_name = "connection_check" + creds_file = ft_utils.get_functest_config('general.openstack.creds') + use_key = ft_utils.get_functest_config('snaps.use_keystone') + ext_net_name = snaps_utils.get_ext_net_name() + + test_suite_builder.add_openstack_client_tests(self.suite, creds_file, + ext_net_name, + use_keystone=use_key) diff --git a/functest/opnfv_tests/openstack/snaps/smoke.py b/functest/opnfv_tests/openstack/snaps/smoke.py new file mode 100644 index 00000000..f66c17ff --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/smoke.py @@ -0,0 +1,41 @@ +# 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 + +import functest.utils.functest_utils as ft_utils +from functest.core.pytest_suite_runner import PyTestSuiteRunner +from functest.opnfv_tests.openstack.snaps import snaps_utils +from snaps import test_suite_builder +import unittest +import os + + +class SnapsSmoke(PyTestSuiteRunner): + """ + 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): + super(SnapsSmoke, self).__init__() + + self.suite = unittest.TestSuite() + self.case_name = "snaps_smoke" + creds_file = ft_utils.get_functest_config('general.openstack.creds') + use_key = ft_utils.get_functest_config('snaps.use_keystone') + use_fip = ft_utils.get_functest_config('snaps.use_floating_ips') + ext_net_name = snaps_utils.get_ext_net_name() + + # Tests requiring floating IPs leverage files contained within the + # SNAPS repository and are found relative to that path + if use_fip: + snaps_dir = ft_utils.get_functest_config( + 'general.directories.dir_repo_snaps') + '/snaps' + os.chdir(snaps_dir) + + test_suite_builder.add_openstack_integration_tests( + self.suite, creds_file, ext_net_name, use_keystone=use_key, + use_floating_ips=use_fip) diff --git a/functest/opnfv_tests/openstack/snaps/snaps_utils.py b/functest/opnfv_tests/openstack/snaps/snaps_utils.py new file mode 100644 index 00000000..a25ad3e0 --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/snaps_utils.py @@ -0,0 +1,22 @@ +# 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 + +import functest.utils.functest_utils as ft_utils +from snaps.openstack.tests import openstack_tests +from snaps.openstack.utils import neutron_utils + + +def get_ext_net_name(): + """ + Returns the first external network name + :return: + """ + os_env_file = ft_utils.get_functest_config('general.openstack.creds') + os_creds = openstack_tests.get_credentials(os_env_file=os_env_file) + neutron = neutron_utils.neutron_client(os_creds) + ext_nets = neutron_utils.get_external_networks(neutron) + return ext_nets[0]['network']['name'] diff --git a/functest/opnfv_tests/openstack/tempest/__init__.py b/functest/opnfv_tests/openstack/tempest/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/__init__.py diff --git a/functest/opnfv_tests/openstack/tempest/gen_tempest_conf.py b/functest/opnfv_tests/openstack/tempest/conf_utils.py index 1216a671..38b97e74 100755..100644 --- a/functest/opnfv_tests/openstack/tempest/gen_tempest_conf.py +++ b/functest/opnfv_tests/openstack/tempest/conf_utils.py @@ -7,38 +7,117 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Execute Multisite Tempest test cases -## - import ConfigParser import os import re import shutil -import functest.utils.functest_utils as ft_utils -import functest.utils.functest_logger as ft_logger -from run_tempest import configure_tempest -from run_tempest import TEMPEST_RESULTS_DIR -import functest.utils.functest_constants as ft_constants -logger = ft_logger.Logger("gen_tempest_conf").getLogger() +import functest.utils.functest_constants as ft_constants +import functest.utils.functest_utils as ft_utils +import opnfv.utils.constants as releng_constants + + +IMAGE_ID_ALT = None +FLAVOR_ID_ALT = None +REPO_PATH = ft_constants.FUNCTEST_REPO_DIR +GLANCE_IMAGE_PATH = os.path.join(ft_constants.FUNCTEST_DATA_DIR, + ft_constants.GLANCE_IMAGE_FILENAME) +TEMPEST_TEST_LIST_DIR = ft_constants.TEMPEST_TEST_LIST_DIR +TEMPEST_RESULTS_DIR = os.path.join(ft_constants.FUNCTEST_RESULTS_DIR, + 'tempest') +TEMPEST_CUSTOM = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'test_list.txt') +TEMPEST_BLACKLIST = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'blacklist.txt') +TEMPEST_DEFCORE = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'defcore_req.txt') +TEMPEST_RAW_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_raw_list.txt') +TEMPEST_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_list.txt') CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE CI_INSTALLER_IP = ft_constants.CI_INSTALLER_IP -def configure_tempest_multisite(deployment_dir): +def configure_tempest(logger, deployment_dir, IMAGE_ID=None, FLAVOR_ID=None): + """ + Add/update needed parameters into tempest.conf file generated by Rally + """ + tempest_conf_file = deployment_dir + "/tempest.conf" + if os.path.isfile(tempest_conf_file): + logger.debug("Deleting old tempest.conf file...") + os.remove(tempest_conf_file) + + logger.debug("Generating new tempest.conf file...") + cmd = "rally verify genconfig" + ft_utils.execute_command(cmd) + + logger.debug("Finding tempest.conf file...") + if not os.path.isfile(tempest_conf_file): + logger.error("Tempest configuration file %s NOT found." + % tempest_conf_file) + return releng_constants.EXIT_RUN_ERROR + + logger.debug("Updating selected tempest.conf parameters...") + config = ConfigParser.RawConfigParser() + config.read(tempest_conf_file) + config.set( + 'compute', + 'fixed_network_name', + ft_constants.TEMPEST_PRIVATE_NET_NAME) + if ft_constants.TEMPEST_USE_CUSTOM_IMAGES: + if IMAGE_ID is not None: + config.set('compute', 'image_ref', IMAGE_ID) + if IMAGE_ID_ALT is not None: + config.set('compute', 'image_ref_alt', IMAGE_ID_ALT) + if ft_constants.TEMPEST_USE_CUSTOM_FLAVORS: + if FLAVOR_ID is not None: + config.set('compute', 'flavor_ref', FLAVOR_ID) + if FLAVOR_ID_ALT is not None: + config.set('compute', 'flavor_ref_alt', FLAVOR_ID_ALT) + config.set('identity', 'tenant_name', ft_constants.TEMPEST_TENANT_NAME) + config.set('identity', 'username', ft_constants.TEMPEST_USER_NAME) + config.set('identity', 'password', ft_constants.TEMPEST_USER_PASSWORD) + config.set( + 'validation', 'ssh_timeout', ft_constants.TEMPEST_SSH_TIMEOUT) + + if ft_constants.OS_ENDPOINT_TYPE is not None: + services_list = ['compute', + 'volume', + 'image', + 'network', + 'data-processing', + 'object-storage', + 'orchestration'] + sections = config.sections() + for service in services_list: + if service not in sections: + config.add_section(service) + config.set(service, 'endpoint_type', + ft_constants.OS_ENDPOINT_TYPE) + + with open(tempest_conf_file, 'wb') as config_file: + config.write(config_file) + + # Copy tempest.conf to /home/opnfv/functest/results/tempest/ + shutil.copyfile( + tempest_conf_file, TEMPEST_RESULTS_DIR + '/tempest.conf') + + return releng_constants.EXIT_OK + + +def configure_tempest_multisite(logger, deployment_dir): """ Add/update needed parameters into tempest.conf file generated by Rally """ logger.debug("configure the tempest") - configure_tempest(deployment_dir) + configure_tempest(logger, deployment_dir) logger.debug("Finding tempest.conf file...") tempest_conf_old = os.path.join(deployment_dir, '/tempest.conf') if not os.path.isfile(tempest_conf_old): logger.error("Tempest configuration file %s NOT found." % tempest_conf_old) - exit(-1) + return releng_constants.EXIT_RUN_ERROR # Copy tempest.conf to /home/opnfv/functest/results/tempest/ cur_path = os.path.split(os.path.realpath(__file__))[0] @@ -110,17 +189,4 @@ def configure_tempest_multisite(deployment_dir): with open(tempest_conf_file, 'wb') as config_file: config.write(config_file) - return True - - -def main(): - - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - - deployment_dir = ft_utils.get_deployment_dir() - configure_tempest_multisite(deployment_dir) - - -if __name__ == '__main__': - main() + return releng_constants.EXIT_OK diff --git a/functest/opnfv_tests/openstack/tempest/run_tempest.py b/functest/opnfv_tests/openstack/tempest/run_tempest.py deleted file mode 100755 index 6406cd19..00000000 --- a/functest/opnfv_tests/openstack/tempest/run_tempest.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/env python -# -# Description: -# Runs tempest and pushes the results to the DB -# -# Authors: -# morgan.richomme@orange.com -# jose.lausuch@ericsson.com -# viktor.tikkanen@nokia.com -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -# -import ConfigParser -import os -import re -import shutil -import subprocess -import sys -import time - -import argparse -import yaml - -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils -import functest.utils.openstack_utils as os_utils -import functest.utils.functest_constants as ft_constants - -modes = ['full', 'smoke', 'baremetal', 'compute', 'data_processing', - 'identity', 'image', 'network', 'object_storage', 'orchestration', - 'telemetry', 'volume', 'custom', 'defcore', 'feature_multisite'] - -""" tests configuration """ -parser = argparse.ArgumentParser() -parser.add_argument("-d", "--debug", - help="Debug mode", - action="store_true") -parser.add_argument("-s", "--serial", - help="Run tests in one thread", - action="store_true") -parser.add_argument("-m", "--mode", - help="Tempest test mode [smoke, all]", - default="smoke") -parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") -parser.add_argument("-n", "--noclean", - help="Don't clean the created resources for this test.", - action="store_true") -parser.add_argument("-c", "--conf", - help="User-specified Tempest config file location", - default="") - -args = parser.parse_args() - -""" logging configuration """ -logger = ft_logger.Logger("run_tempest").getLogger() - -GLANCE_IMAGE_NAME = ft_constants.GLANCE_IMAGE_NAME -GLANCE_IMAGE_FILENAME = ft_constants.GLANCE_IMAGE_FILENAME -GLANCE_IMAGE_FORMAT = ft_constants.GLANCE_IMAGE_FORMAT -GLANCE_IMAGE_PATH = os.path.join(ft_constants.FUNCTEST_DATA_DIR, - GLANCE_IMAGE_FILENAME) - -IMAGE_ID_ALT = None - -FLAVOR_NAME = ft_constants.FLAVOR_NAME -FLAVOR_RAM = ft_constants.FLAVOR_RAM -FLAVOR_DISK = ft_constants.FLAVOR_DISK -FLAVOR_VCPUS = ft_constants.FLAVOR_VCPUS -FLAVOR_ID_ALT = None - -TEMPEST_PRIVATE_NET_NAME = ft_constants.TEMPEST_PRIVATE_NET_NAME -TEMPEST_PRIVATE_SUBNET_NAME = ft_constants.TEMPEST_PRIVATE_SUBNET_NAME -TEMPEST_PRIVATE_SUBNET_CIDR = ft_constants.TEMPEST_PRIVATE_SUBNET_CIDR -TEMPEST_ROUTER_NAME = ft_constants.TEMPEST_ROUTER_NAME -TEMPEST_TENANT_NAME = ft_constants.TEMPEST_TENANT_NAME -TEMPEST_TENANT_DESCRIPTION = ft_constants.TEMPEST_TENANT_DESCRIPTION -TEMPEST_USER_NAME = ft_constants.TEMPEST_USER_NAME -TEMPEST_USER_PASSWORD = ft_constants.TEMPEST_USER_PASSWORD -TEMPEST_SSH_TIMEOUT = ft_constants.TEMPEST_SSH_TIMEOUT -TEMPEST_USE_CUSTOM_IMAGES = ft_constants.TEMPEST_USE_CUSTOM_IMAGES -TEMPEST_USE_CUSTOM_FLAVORS = ft_constants.TEMPEST_USE_CUSTOM_FLAVORS - -RESULTS_DIR = ft_constants.FUNCTEST_RESULTS_DIR -TEMPEST_RESULTS_DIR = os.path.join(RESULTS_DIR, 'tempest') - -REPO_PATH = ft_constants.FUNCTEST_REPO_DIR -TEMPEST_TEST_LIST_DIR = ft_constants.TEMPEST_TEST_LIST_DIR -TEMPEST_CUSTOM = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'test_list.txt') -TEMPEST_BLACKLIST = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'blacklist.txt') -TEMPEST_DEFCORE = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'defcore_req.txt') -TEMPEST_RAW_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_raw_list.txt') -TEMPEST_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_list.txt') - - -class GlobalVariables: - IMAGE_ID = None - FLAVOR_ID = None - MODE = "smoke" - - -def get_info(file_result): - test_run = "" - duration = "" - test_failed = "" - - p = subprocess.Popen('cat tempest.log', - shell=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - for line in p.stdout.readlines(): - # print line, - if (len(test_run) < 1): - test_run = re.findall("[0-9]*\.[0-9]*s", line) - if (len(duration) < 1): - duration = re.findall("[0-9]*\ tests", line) - regexp = r"(failures=[0-9]+)" - if (len(test_failed) < 1): - test_failed = re.findall(regexp, line) - - logger.debug("test_run:" + test_run) - logger.debug("duration:" + duration) - - -def create_tempest_resources(): - keystone_client = os_utils.get_keystone_client() - - logger.debug("Creating tenant and user for Tempest suite") - tenant_id = os_utils.create_tenant(keystone_client, - TEMPEST_TENANT_NAME, - TEMPEST_TENANT_DESCRIPTION) - if not tenant_id: - logger.error("Error : Failed to create %s tenant" - % TEMPEST_TENANT_NAME) - - user_id = os_utils.create_user(keystone_client, TEMPEST_USER_NAME, - TEMPEST_USER_PASSWORD, - None, tenant_id) - if not user_id: - logger.error("Error : Failed to create %s user" % TEMPEST_USER_NAME) - - logger.debug("Creating private network for Tempest suite") - network_dic = \ - os_utils.create_shared_network_full(TEMPEST_PRIVATE_NET_NAME, - TEMPEST_PRIVATE_SUBNET_NAME, - TEMPEST_ROUTER_NAME, - TEMPEST_PRIVATE_SUBNET_CIDR) - if not network_dic: - exit(1) - - if TEMPEST_USE_CUSTOM_IMAGES: - # adding alternative image should be trivial should we need it - logger.debug("Creating image for Tempest suite") - _, GlobalVariables.IMAGE_ID = os_utils.get_or_create_image( - GLANCE_IMAGE_NAME, GLANCE_IMAGE_PATH, GLANCE_IMAGE_FORMAT) - if not GlobalVariables.IMAGE_ID: - exit(-1) - - if TEMPEST_USE_CUSTOM_FLAVORS: - # adding alternative flavor should be trivial should we need it - logger.debug("Creating flavor for Tempest suite") - _, GlobalVariables.FLAVOR_ID = os_utils.get_or_create_flavor( - FLAVOR_NAME, FLAVOR_RAM, FLAVOR_DISK, FLAVOR_VCPUS) - if not GlobalVariables.FLAVOR_ID: - exit(-1) - - -def configure_tempest(deployment_dir): - """ - Add/update needed parameters into tempest.conf file generated by Rally - """ - - tempest_conf_file = deployment_dir + "/tempest.conf" - if os.path.isfile(tempest_conf_file): - logger.debug("Deleting old tempest.conf file...") - os.remove(tempest_conf_file) - - logger.debug("Generating new tempest.conf file...") - cmd = "rally verify genconfig" - ft_utils.execute_command(cmd) - - logger.debug("Finding tempest.conf file...") - if not os.path.isfile(tempest_conf_file): - logger.error("Tempest configuration file %s NOT found." - % tempest_conf_file) - exit(-1) - - logger.debug("Updating selected tempest.conf parameters...") - config = ConfigParser.RawConfigParser() - config.read(tempest_conf_file) - config.set('compute', 'fixed_network_name', TEMPEST_PRIVATE_NET_NAME) - if TEMPEST_USE_CUSTOM_IMAGES: - if GlobalVariables.IMAGE_ID is not None: - config.set('compute', 'image_ref', GlobalVariables.IMAGE_ID) - if IMAGE_ID_ALT is not None: - config.set('compute', 'image_ref_alt', IMAGE_ID_ALT) - if TEMPEST_USE_CUSTOM_FLAVORS: - if GlobalVariables.FLAVOR_ID is not None: - config.set('compute', 'flavor_ref', GlobalVariables.FLAVOR_ID) - if FLAVOR_ID_ALT is not None: - config.set('compute', 'flavor_ref_alt', FLAVOR_ID_ALT) - config.set('identity', 'tenant_name', TEMPEST_TENANT_NAME) - config.set('identity', 'username', TEMPEST_USER_NAME) - config.set('identity', 'password', TEMPEST_USER_PASSWORD) - config.set('validation', 'ssh_timeout', TEMPEST_SSH_TIMEOUT) - - if ft_constants.OS_ENDPOINT_TYPE is not None: - services_list = ['compute', 'volume', 'image', 'network', - 'data-processing', 'object-storage', 'orchestration'] - sections = config.sections() - for service in services_list: - if service not in sections: - config.add_section(service) - config.set(service, 'endpoint_type', - ft_constants.OS_ENDPOINT_TYPE) - - with open(tempest_conf_file, 'wb') as config_file: - config.write(config_file) - - # Copy tempest.conf to /home/opnfv/functest/results/tempest/ - shutil.copyfile(tempest_conf_file, TEMPEST_RESULTS_DIR + '/tempest.conf') - return True - - -def read_file(filename): - with open(filename) as src: - return [line.strip() for line in src.readlines()] - - -def generate_test_list(deployment_dir, mode): - logger.debug("Generating test case list...") - if mode == 'defcore': - shutil.copyfile(TEMPEST_DEFCORE, TEMPEST_RAW_LIST) - elif mode == 'custom': - if os.path.isfile(TEMPEST_CUSTOM): - shutil.copyfile(TEMPEST_CUSTOM, TEMPEST_RAW_LIST) - else: - logger.error("Tempest test list file %s NOT found." - % TEMPEST_CUSTOM) - exit(-1) - else: - if mode == 'smoke': - testr_mode = "smoke" - elif mode == 'feature_multisite': - testr_mode = " | grep -i kingbird " - elif mode == 'full': - testr_mode = "" - else: - testr_mode = 'tempest.api.' + mode - cmd = ("cd " + deployment_dir + ";" + "testr list-tests " + - testr_mode + ">" + TEMPEST_RAW_LIST + ";cd") - ft_utils.execute_command(cmd) - - -def apply_tempest_blacklist(): - logger.debug("Applying tempest blacklist...") - cases_file = read_file(TEMPEST_RAW_LIST) - result_file = open(TEMPEST_LIST, 'w') - black_tests = [] - try: - installer_type = ft_constants.CI_INSTALLER_TYPE - deploy_scenario = ft_constants.CI_SCENARIO - if (bool(installer_type) * bool(deploy_scenario)): - # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the file - black_list_file = open(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: - 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() - - -def run_tempest(OPTION): - # - # the "main" function of the script which launches Rally to run Tempest - # :param option: tempest option (smoke, ..) - # :return: void - # - logger.info("Starting Tempest test suite: '%s'." % OPTION) - start_time = time.time() - stop_time = start_time - cmd_line = "rally verify start " + OPTION + " --system-wide" - - header = ("Tempest environment:\n" - " Installer: %s\n Scenario: %s\n Node: %s\n Date: %s\n" % - (ft_constants.CI_INSTALLER_TYPE, - ft_constants.CI_SCENARIO, - ft_constants.CI_NODE, - time.strftime("%a %b %d %H:%M:%S %Z %Y"))) - - f_stdout = open(TEMPEST_RESULTS_DIR + "/tempest.log", 'w+') - f_stderr = open(TEMPEST_RESULTS_DIR + "/tempest-error.log", 'w+') - f_env = open(TEMPEST_RESULTS_DIR + "/environment.log", 'w+') - f_env.write(header) - - # subprocess.call(cmd_line, shell=True, stdout=f_stdout, stderr=f_stderr) - p = subprocess.Popen( - cmd_line, shell=True, - stdout=subprocess.PIPE, - stderr=f_stderr, - bufsize=1) - - with p.stdout: - for line in iter(p.stdout.readline, b''): - if re.search("\} tempest\.", line): - logger.info(line.replace('\n', '')) - f_stdout.write(line) - p.wait() - - f_stdout.close() - f_stderr.close() - f_env.close() - - cmd_line = "rally verify show" - output = "" - p = subprocess.Popen( - cmd_line, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in p.stdout: - if re.search("Tests\:", line): - break - output += line - logger.info(output) - - cmd_line = "rally verify list" - cmd = os.popen(cmd_line) - output = (((cmd.read()).splitlines()[-2]).replace(" ", "")).split("|") - # Format: - # | UUID | Deployment UUID | smoke | tests | failures | Created at | - # Duration | Status | - num_tests = output[4] - num_failures = output[5] - time_start = output[6] - duration = output[7] - # Compute duration (lets assume it does not take more than 60 min) - dur_min = int(duration.split(':')[1]) - dur_sec_float = float(duration.split(':')[2]) - dur_sec_int = int(round(dur_sec_float, 0)) - dur_sec_int = dur_sec_int + 60 * dur_min - stop_time = time.time() - - try: - diff = (int(num_tests) - int(num_failures)) - success_rate = 100 * diff / int(num_tests) - except: - success_rate = 0 - - if 'smoke' in args.mode: - case_name = 'tempest_smoke_serial' - elif 'feature' in args.mode: - case_name = args.mode.replace("feature_", "") - else: - case_name = 'tempest_full_parallel' - - status = ft_utils.check_success_rate(case_name, success_rate) - logger.info("Tempest %s success_rate is %s%%, is marked as %s" - % (case_name, success_rate, status)) - - # Push results in payload of testcase - if args.report: - # add the test in error in the details sections - # should be possible to do it during the test - logger.debug("Pushing tempest results into DB...") - with open(TEMPEST_RESULTS_DIR + "/tempest.log", 'r') as myfile: - output = myfile.read() - error_logs = "" - - for match in re.findall('(.*?)[. ]*FAILED', output): - error_logs += match - - # Generate json results for DB - json_results = {"timestart": time_start, "duration": dur_sec_int, - "tests": int(num_tests), "failures": int(num_failures), - "errors": error_logs} - logger.info("Results: " + str(json_results)) - # split Tempest smoke and full - - try: - ft_utils.push_results_to_db("functest", - case_name, - start_time, - stop_time, - status, - json_results) - except: - logger.error("Error pushing results into Database '%s'" - % sys.exc_info()[0]) - - if status == "PASS": - return 0 - else: - return -1 - - -def main(): - - if not (args.mode in modes): - logger.error("Tempest mode not valid. " - "Possible values are:\n" + str(modes)) - exit(-1) - - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - - deployment_dir = ft_utils.get_deployment_dir() - create_tempest_resources() - - if "" == args.conf: - GlobalVariables.MODE = "" - configure_tempest(deployment_dir) - else: - GlobalVariables.MODE = " --tempest-config " + args.conf - - generate_test_list(deployment_dir, args.mode) - apply_tempest_blacklist() - - GlobalVariables.MODE += " --tests-file " + TEMPEST_LIST - if args.serial: - GlobalVariables.MODE += " --concur 1" - - ret_val = run_tempest(GlobalVariables.MODE) - if ret_val != 0: - sys.exit(-1) - - sys.exit(0) - - -if __name__ == '__main__': - main() diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py new file mode 100644 index 00000000..ec0ca766 --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -0,0 +1,316 @@ +#!/usr/bin/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 +# + +import os +import re +import shutil +import subprocess +import time + +import yaml + +import conf_utils +import functest.core.testcase_base as testcase_base +import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils +import functest.utils.openstack_utils as os_utils +import functest.utils.functest_constants as ft_constants +import opnfv.utils.constants as releng_constants + +""" logging configuration """ +logger = ft_logger.Logger("Tempest").getLogger() + + +class TempestCommon(testcase_base.TestcaseBase): + + def __init__(self): + self.case_name = "" + self.MODE = "" + self.OPTION = "" + self.FLAVOR_ID = None + self.IMAGE_ID = None + self.DEPLOYMENT_DIR = ft_utils.get_deployment_dir() + + def read_file(self, filename): + with open(filename) as src: + return [line.strip() for line in src.readlines()] + + def create_tempest_resources(self): + keystone_client = os_utils.get_keystone_client() + + logger.debug("Creating tenant and user for Tempest suite") + tenant_id = os_utils.create_tenant( + keystone_client, + ft_constants.TEMPEST_TENANT_NAME, + ft_constants.TEMPEST_TENANT_DESCRIPTION) + if not tenant_id: + logger.error("Error : Failed to create %s tenant" + % ft_constants.TEMPEST_TENANT_NAME) + + user_id = os_utils.create_user(keystone_client, + ft_constants.TEMPEST_USER_NAME, + ft_constants.TEMPEST_USER_PASSWORD, + None, tenant_id) + if not user_id: + logger.error("Error : Failed to create %s user" % + ft_constants.TEMPEST_USER_NAME) + + logger.debug("Creating private network for Tempest suite") + network_dic = \ + os_utils.create_shared_network_full( + ft_constants.TEMPEST_PRIVATE_NET_NAME, + ft_constants.TEMPEST_PRIVATE_SUBNET_NAME, + ft_constants.TEMPEST_ROUTER_NAME, + ft_constants.TEMPEST_PRIVATE_SUBNET_CIDR) + if not network_dic: + return releng_constants.EXIT_RUN_ERROR + + if ft_constants.TEMPEST_USE_CUSTOM_IMAGES: + # adding alternative image should be trivial should we need it + logger.debug("Creating image for Tempest suite") + _, self.IMAGE_ID = os_utils.get_or_create_image( + ft_constants.GLANCE_IMAGE_NAME, conf_utils.GLANCE_IMAGE_PATH, + ft_constants.GLANCE_IMAGE_FORMAT) + if not self.IMAGE_ID: + return releng_constants.EXIT_RUN_ERROR + + if ft_constants.TEMPEST_USE_CUSTOM_FLAVORS: + # adding alternative flavor should be trivial should we need it + logger.debug("Creating flavor for Tempest suite") + _, self.FLAVOR_ID = os_utils.get_or_create_flavor( + ft_constants.FLAVOR_NAME, + ft_constants.FLAVOR_RAM, + ft_constants.FLAVOR_DISK, + ft_constants.FLAVOR_VCPUS) + if not self.FLAVOR_ID: + return releng_constants.EXIT_RUN_ERROR + + return releng_constants.EXIT_OK + + def generate_test_list(self, DEPLOYMENT_DIR): + logger.debug("Generating test case list...") + if self.MODE == 'defcore': + shutil.copyfile( + conf_utils.TEMPEST_DEFCORE, conf_utils.TEMPEST_RAW_LIST) + elif self.MODE == 'custom': + if os.path.isfile(conf_utils.TEMPEST_CUSTOM): + shutil.copyfile( + conf_utils.TEMPEST_CUSTOM, conf_utils.TEMPEST_RAW_LIST) + else: + logger.error("Tempest test list file %s NOT found." + % conf_utils.TEMPEST_CUSTOM) + return releng_constants.EXIT_RUN_ERROR + else: + if self.MODE == 'smoke': + testr_mode = "smoke" + elif self.MODE == 'feature_multisite': + testr_mode = " | grep -i kingbird " + elif self.MODE == 'full': + testr_mode = "" + else: + testr_mode = 'tempest.api.' + self.MODE + cmd = ("cd " + DEPLOYMENT_DIR + ";" + "testr list-tests " + + testr_mode + ">" + conf_utils.TEMPEST_RAW_LIST + ";cd") + ft_utils.execute_command(cmd) + + return releng_constants.EXIT_OK + + def apply_tempest_blacklist(self): + logger.debug("Applying tempest blacklist...") + cases_file = self.read_file(conf_utils.TEMPEST_RAW_LIST) + result_file = open(conf_utils.TEMPEST_LIST, 'w') + black_tests = [] + try: + installer_type = ft_constants.CI_INSTALLER_TYPE + deploy_scenario = ft_constants.CI_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: + 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() + return releng_constants.EXIT_OK + + def run(self): + if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR): + os.makedirs(conf_utils.TEMPEST_RESULTS_DIR) + + # Pre-configuration + res = self.create_tempest_resources() + if res != releng_constants.EXIT_OK: + return res + + res = conf_utils.configure_tempest(logger, + self.DEPLOYMENT_DIR, + self.IMAGE_ID, + self.FLAVOR_ID) + if res != releng_constants.EXIT_OK: + return res + + res = self.generate_test_list(self.DEPLOYMENT_DIR) + if res != releng_constants.EXIT_OK: + return res + + res = self.apply_tempest_blacklist() + if res != releng_constants.EXIT_OK: + return res + + self.OPTION += (" --tests-file %s " % conf_utils.TEMPEST_LIST) + + cmd_line = "rally verify start " + self.OPTION + " --system-wide" + logger.info("Starting Tempest test suite: '%s'." % cmd_line) + + header = ("Tempest environment:\n" + " Installer: %s\n Scenario: %s\n Node: %s\n Date: %s\n" % + (ft_constants.CI_INSTALLER_TYPE, + ft_constants.CI_SCENARIO, + ft_constants.CI_NODE, + time.strftime("%a %b %d %H:%M:%S %Z %Y"))) + + f_stdout = open(conf_utils.TEMPEST_RESULTS_DIR + "/tempest.log", 'w+') + f_stderr = open( + conf_utils.TEMPEST_RESULTS_DIR + "/tempest-error.log", 'w+') + f_env = open(conf_utils.TEMPEST_RESULTS_DIR + "/environment.log", 'w+') + f_env.write(header) + + # subprocess.call(cmd_line, shell=True, + # stdout=f_stdout, stderr=f_stderr) + p = subprocess.Popen( + cmd_line, shell=True, + stdout=subprocess.PIPE, + stderr=f_stderr, + bufsize=1) + + with p.stdout: + for line in iter(p.stdout.readline, b''): + if re.search("\} tempest\.", line): + logger.info(line.replace('\n', '')) + f_stdout.write(line) + p.wait() + + f_stdout.close() + f_stderr.close() + f_env.close() + + cmd_line = "rally verify show" + output = "" + p = subprocess.Popen(cmd_line, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in p.stdout: + if re.search("Tests\:", line): + break + output += line + logger.info(output) + + cmd_line = "rally verify list" + cmd = os.popen(cmd_line) + output = (((cmd.read()).splitlines()[-2]).replace(" ", "")).split("|") + # Format: + # | UUID | Deployment UUID | smoke | tests | failures | Created at | + # Duration | Status | + num_tests = output[4] + num_failures = output[5] + duration = output[7] + # Compute duration (lets assume it does not take more than 60 min) + dur_min = int(duration.split(':')[1]) + dur_sec_float = float(duration.split(':')[2]) + dur_sec_int = int(round(dur_sec_float, 0)) + dur_sec_int = dur_sec_int + 60 * dur_min + + try: + diff = (int(num_tests) - int(num_failures)) + success_rate = 100 * diff / int(num_tests) + except: + success_rate = 0 + + if 'smoke' in self.MODE: + self.CASE_NAME = 'tempest_smoke_serial' + elif 'feature' in self.MODE: + self.CASE_NAME = self.MODE.replace( + "feature_", "") + else: + self.CASE_NAME = 'tempest_full_parallel' + + status = ft_utils.check_success_rate( + self.CASE_NAME, success_rate) + logger.info("Tempest %s success_rate is %s%%, is marked as %s" + % (self.CASE_NAME, success_rate, status)) + + if status == "PASS": + return releng_constants.EXIT_OK + else: + return releng_constants.EXIT_RUN_ERROR + + +class TempestSmokeSerial(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_smoke_serial" + self.MODE = "smoke" + self.OPTION = "--concur 1" + + +class TempestSmokeParallel(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_smoke_parallel" + self.MODE = "smoke" + self.OPTION = "" + + +class TempestFullParallel(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_full_parallel" + self.MODE = "full" + + +class TempestMultisite(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "multisite" + self.MODE = "feature_multisite" + self.OPTION = "--concur 1" + conf_utils.configure_tempest_multisite(logger, self.DEPLOYMENT_DIR) + + +class TempestCustom(TempestCommon): + + def __init__(self, mode, option): + TempestCommon.__init__(self) + self.case_name = "tempest_custom" + self.MODE = mode + self.OPTION = option diff --git a/functest/opnfv_tests/openstack/vping/vping_ssh.py b/functest/opnfv_tests/openstack/vping/vping_ssh.py index 8ae590ed..8ae590ed 100644..100755 --- a/functest/opnfv_tests/openstack/vping/vping_ssh.py +++ b/functest/opnfv_tests/openstack/vping/vping_ssh.py diff --git a/functest/opnfv_tests/openstack/vping/vping_userdata.py b/functest/opnfv_tests/openstack/vping/vping_userdata.py index fa91c12a..fa91c12a 100644..100755 --- a/functest/opnfv_tests/openstack/vping/vping_userdata.py +++ b/functest/opnfv_tests/openstack/vping/vping_userdata.py diff --git a/functest/opnfv_tests/sdn/odl/odl.py b/functest/opnfv_tests/sdn/odl/odl.py index 95440746..0905e55c 100755 --- a/functest/opnfv_tests/sdn/odl/odl.py +++ b/functest/opnfv_tests/sdn/odl/odl.py @@ -20,10 +20,9 @@ from robot.errors import RobotError import robot.run from robot.utils.robottime import timestamp_to_secs -import functest.core.testcase_base as testcase_base +from functest.core import testcase_base import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as op_utils -import functest.utils.functest_constants as ft_constants class ODLResultVisitor(ResultVisitor): @@ -36,7 +35,7 @@ class ODLResultVisitor(ResultVisitor): output['name'] = test.name output['parent'] = test.parent.name output['status'] = test.status - output['startime'] = test.starttime + output['starttime'] = test.starttime output['endtime'] = test.endtime output['critical'] = test.critical output['text'] = test.message @@ -49,17 +48,17 @@ class ODLResultVisitor(ResultVisitor): class ODLTests(testcase_base.TestcaseBase): - repos = ft_constants.REPOS_DIR + repos = "/home/opnfv/repos/" odl_test_repo = os.path.join(repos, "odl_test") neutron_suite_dir = os.path.join(odl_test_repo, "csit/suites/openstack/neutron") basic_suite_dir = os.path.join(odl_test_repo, "csit/suites/integration/basic") - res_dir = os.path.join(ft_constants.FUNCTEST_RESULTS_DIR, "odl") - + res_dir = '/home/opnfv/functest/results/odl/' logger = ft_logger.Logger("opendaylight").getLogger() def __init__(self): + testcase_base.TestcaseBase.__init__(self) self.case_name = "odl" @classmethod @@ -79,8 +78,8 @@ class ODLTests(testcase_base.TestcaseBase): return False def parse_results(self): - output_dir = os.path.join(self.res_dir, 'output.xml') - result = ExecutionResult(output_dir) + xml_file = os.path.join(self.res_dir, 'output.xml') + result = ExecutionResult(xml_file) visitor = ODLResultVisitor() result.visit(visitor) self.criteria = result.suite.status @@ -89,7 +88,6 @@ class ODLTests(testcase_base.TestcaseBase): self.details = {} self.details['description'] = result.suite.name self.details['tests'] = visitor.get_data() - return self.criteria def main(self, **kwargs): dirs = [self.basic_suite_dir, self.neutron_suite_dir] @@ -128,10 +126,8 @@ class ODLTests(testcase_base.TestcaseBase): self.logger.info("\n" + stdout.read()) self.logger.info("ODL results were successfully generated") try: - test_res = self.parse_results() + self.parse_results() self.logger.info("ODL results were successfully parsed") - if test_res is not "PASS": - return self.EX_RUN_ERROR except RobotError as e: self.logger.error("Run tests before publishing: %s" % e.message) @@ -146,11 +142,8 @@ class ODLTests(testcase_base.TestcaseBase): def run(self): try: - kclient = op_utils.get_keystone_client() - keystone_url = kclient.service_catalog.url_for( - service_type='identity', endpoint_type='publicURL') - neutron_url = kclient.service_catalog.url_for( - service_type='network', endpoint_type='publicURL') + keystone_url = op_utils.get_endpoint(service_type='identity') + neutron_url = op_utils.get_endpoint(service_type='network') kwargs = {'keystoneip': urlparse.urlparse(keystone_url).hostname} kwargs['neutronip'] = urlparse.urlparse(neutron_url).hostname kwargs['odlip'] = kwargs['neutronip'] @@ -158,29 +151,23 @@ class ODLTests(testcase_base.TestcaseBase): kwargs['odlrestconfport'] = '8181' kwargs['odlusername'] = 'admin' kwargs['odlpassword'] = 'admin' - - installer_type = ft_constants.CI_INSTALLER_TYPE - kwargs['osusername'] = ft_constants.OS_USERNAME - kwargs['ostenantname'] = ft_constants.OS_TENANT_NAME - kwargs['ospassword'] = ft_constants.OS_PASSWORD - + installer_type = None + if 'INSTALLER_TYPE' in os.environ: + installer_type = os.environ['INSTALLER_TYPE'] + kwargs['osusername'] = os.environ['OS_USERNAME'] + kwargs['ostenantname'] = os.environ['OS_TENANT_NAME'] + kwargs['ospassword'] = os.environ['OS_PASSWORD'] if installer_type == 'fuel': kwargs['odlwebport'] = '8282' elif installer_type == 'apex': - if ft_constants.SDN_CONTROLLER_IP is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER_IP + kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP'] kwargs['odlwebport'] = '8181' elif installer_type == 'joid': - if ft_constants.SDN_CONTROLLER is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER + kwargs['odlip'] = os.environ['SDN_CONTROLLER'] elif installer_type == 'compass': kwargs['odlwebport'] = '8181' else: - if ft_constants.SDN_CONTROLLER_IP is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER_IP + kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP'] except KeyError as e: self.logger.error("Cannot run ODL testcases. " "Please check env var: " diff --git a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py index 8ca32e9b..349b42a8 100644 --- a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py +++ b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py @@ -1,4 +1,4 @@ -import os +import os import re import time import json diff --git a/functest/opnfv_tests/vnf/rnc/parser.py b/functest/opnfv_tests/vnf/rnc/parser.py index a50d4f1e..1cff7220 100644 --- a/functest/opnfv_tests/vnf/rnc/parser.py +++ b/functest/opnfv_tests/vnf/rnc/parser.py @@ -14,66 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import os -import sys -import time -import argparse +import functest.core.feature_base as base -import functest.core.testcase_base as testcase_base -import functest.utils.functest_constants as ft_constants -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils - - -class Parser(testcase_base.TestcaseBase): +class Parser(base.FeatureBase): def __init__(self): - super(Parser, self).__init__() - self.project_name = "parser" - self.case_name = "parser-basics" - self.logger = ft_logger.Logger("parser").getLogger() - self.log_file = os.path.join( - ft_constants.FUNCTEST_RESULTS_DIR, "parser.log") - - def run(self, **kwargs): - cmd = 'cd %s/tests && ./functest_run.sh' % ft_constants.PARSER_REPO_DIR - - self.start_time = time.time() - ret = ft_utils.execute_command(cmd, - info=True, - output_file=self.log_file) - self.stop_time = time.time() - - self.criteria, details = ft_utils.check_test_result(self.project_name, - ret, - self.start_time, - self.stop_time) - - ft_utils.logger_test_results(self.project_name, - self.case_name, - self.criteria, - details) - - return ret - - @staticmethod - def get_conf(parameter): - return ft_utils.get_functest_config(parameter) - - -if __name__ == '__main__': - args_parser = argparse.ArgumentParser() - args_parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") - args = vars(args_parser.parse_args()) - parser = Parser() - try: - result = parser.run(**args) - if result != testcase_base.TestcaseBase.EX_OK: - sys.exit(result) - if args['report']: - sys.exit(parser.push_to_db()) - except Exception: - sys.exit(testcase_base.TestcaseBase.EX_RUN_ERROR) + super(Parser, self).__init__(project='parser', + case='parser-basics', + repo='dir_repo_parser') + self.cmd = 'cd %s/tests && ./functest_run.sh' % self.repo diff --git a/functest/tests/unit/core/test_testcase_base.py b/functest/tests/unit/core/test_testcase_base.py index fe7b0d05..b7c81d87 100644 --- a/functest/tests/unit/core/test_testcase_base.py +++ b/functest/tests/unit/core/test_testcase_base.py @@ -11,7 +11,7 @@ import logging import mock import unittest -import functest.core.testcase_base as testcase_base +from functest.core import testcase_base class TestcaseBaseTesting(unittest.TestCase): @@ -24,7 +24,7 @@ class TestcaseBaseTesting(unittest.TestCase): self.test.case_name = "base" self.test.start_time = "1" self.test.stop_time = "2" - self.test.criteria = "100" + self.test.criteria = "PASS" self.test.details = {"Hello": "World"} def test_run_unimplemented(self): @@ -82,6 +82,21 @@ class TestcaseBaseTesting(unittest.TestCase): self.test.project, self.test.case_name, self.test.start_time, self.test.stop_time, self.test.criteria, self.test.details) + def test_check_criteria_missing(self): + self.test.criteria = None + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_TESTCASE_FAILED) + + def test_check_criteria_failed(self): + self.test.criteria = 'FAILED' + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_TESTCASE_FAILED) + + def test_check_criteria_pass(self): + self.test.criteria = 'PASS' + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_OK) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/functest/tests/unit/odl/test_odl.py b/functest/tests/unit/odl/test_odl.py index ef18016b..d8c7f84e 100644 --- a/functest/tests/unit/odl/test_odl.py +++ b/functest/tests/unit/odl/test_odl.py @@ -13,11 +13,12 @@ import mock import os import unittest +from keystoneauth1.exceptions import auth_plugins from robot.errors import RobotError +from robot.result import testcase -import functest.core.testcase_base as testcase_base +from functest.core import testcase_base from functest.opnfv_tests.sdn.odl import odl -from functest.utils import functest_constants as ft_constants class ODLTesting(unittest.TestCase): @@ -36,11 +37,41 @@ class ODLTesting(unittest.TestCase): _odl_password = "admin" def setUp(self): - ft_constants.OS_USERNAME = self._os_username - ft_constants.OS_PASSWORD = self._os_password - ft_constants.OS_TENANT_NAME = self._os_tenantname + for var in ("INSTALLER_TYPE", "SDN_CONTROLLER", "SDN_CONTROLLER_IP"): + if var in os.environ: + del os.environ[var] + os.environ["OS_USERNAME"] = self._os_username + os.environ["OS_PASSWORD"] = self._os_password + os.environ["OS_TENANT_NAME"] = self._os_tenantname self.test = odl.ODLTests() + def test_empty_visitor(self): + visitor = odl.ODLResultVisitor() + self.assertFalse(visitor.get_data()) + + def test_visitor(self): + visitor = odl.ODLResultVisitor() + data = {'name': 'foo', + 'parent': 'bar', + 'status': 'PASS', + 'starttime': "20161216 16:00:00.000", + 'endtime': "20161216 16:00:01.000", + 'elapsedtime': 1000, + 'text': 'Hello, World!', + 'critical': True} + test = testcase.TestCase(name=data['name'], + status=data['status'], + message=data['text'], + starttime=data['starttime'], + endtime=data['endtime']) + test.parent = mock.Mock() + config = {'name': data['parent'], + 'criticality.test_is_critical.return_value': data[ + 'critical']} + test.parent.configure_mock(**config) + visitor.visit_test(test) + self.assertEqual(visitor.get_data(), [data]) + @mock.patch('fileinput.input', side_effect=Exception()) def test_set_robotframework_vars_failed(self, *args): self.assertFalse(self.test.set_robotframework_vars()) @@ -59,14 +90,6 @@ class ODLTesting(unittest.TestCase): else: return None - @classmethod - def _get_fake_keystone_client(cls): - kclient = mock.Mock() - kclient.service_catalog = mock.Mock() - kclient.service_catalog.url_for = mock.Mock( - side_effect=cls._fake_url_for) - return kclient - def _get_main_kwargs(self, key=None): kwargs = {'odlusername': self._odl_username, 'odlpassword': self._odl_password, @@ -85,9 +108,9 @@ class ODLTesting(unittest.TestCase): def _test_main(self, status, *args): kwargs = self._get_main_kwargs() self.assertEqual(self.test.main(**kwargs), status) - odl_res_dir = odl.ODLTests.res_dir if len(args) > 0: - args[0].assert_called_once_with(odl_res_dir) + args[0].assert_called_once_with( + odl.ODLTests.res_dir) if len(args) > 1: variable = ['KEYSTONE:{}'.format(self._keystone_ip), 'NEUTRON:{}'.format(self._neutron_ip), @@ -97,18 +120,17 @@ class ODLTesting(unittest.TestCase): 'ODL_SYSTEM_IP:{}'.format(self._sdn_controller_ip), 'PORT:{}'.format(self._odl_webport), 'RESTCONFPORT:{}'.format(self._odl_restconfport)] - output_file = os.path.join(odl_res_dir, 'output.xml') args[1].assert_called_once_with( odl.ODLTests.basic_suite_dir, odl.ODLTests.neutron_suite_dir, log='NONE', - output=output_file, + output=os.path.join(odl.ODLTests.res_dir, 'output.xml'), report='NONE', stdout=mock.ANY, variable=variable) if len(args) > 2: - stdout_file = os.path.join(odl_res_dir, 'stdout.txt') - args[2].assert_called_with(stdout_file) + args[2].assert_called_with( + os.path.join(odl.ODLTests.res_dir, 'stdout.txt')) def _test_main_missing_keyword(self, key): kwargs = self._get_main_kwargs(key) @@ -200,8 +222,7 @@ class ODLTesting(unittest.TestCase): def test_main(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove') @@ -210,8 +231,7 @@ class ODLTesting(unittest.TestCase): def test_main_makedirs_oserror17(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove') @@ -220,8 +240,7 @@ class ODLTesting(unittest.TestCase): def test_main_testcases_in_failure(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove', side_effect=OSError) @@ -230,25 +249,20 @@ class ODLTesting(unittest.TestCase): def test_main_remove_oserror(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) def _test_run_missing_env_var(self, var): - if var == 'OS_USERNAME': - ft_constants.OS_USERNAME = None - elif var == 'OS_PASSWORD': - ft_constants.OS_PASSWORD = None - elif var == 'OS_TENANT_NAME': - ft_constants.OS_TENANT_NAME = None - - self.assertEqual(self.test.run(), - testcase_base.TestcaseBase.EX_RUN_ERROR) + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + del os.environ[var] + self.assertEqual(self.test.run(), + testcase_base.TestcaseBase.EX_RUN_ERROR) def _test_run(self, status=testcase_base.TestcaseBase.EX_OK, exception=None, odlip="127.0.0.3", odlwebport="8080"): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): if exception: self.test.main = mock.Mock(side_effect=exception) else: @@ -262,6 +276,12 @@ class ODLTesting(unittest.TestCase): ospassword=self._os_password, ostenantname=self._os_tenantname, osusername=self._os_username) + def test_run_exception(self): + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=auth_plugins.MissingAuthPlugin()): + self.assertEqual(self.test.run(), + testcase_base.TestcaseBase.EX_RUN_ERROR) + def test_run_missing_os_username(self): self._test_run_missing_env_var("OS_USERNAME") @@ -272,72 +292,64 @@ class ODLTesting(unittest.TestCase): self._test_run_missing_env_var("OS_TENANT_NAME") def test_run_main_false(self): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(testcase_base.TestcaseBase.EX_RUN_ERROR, odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_main_exception(self): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip with self.assertRaises(Exception): + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(status=testcase_base.TestcaseBase.EX_RUN_ERROR, exception=Exception(), odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_missing_sdn_controller_ip(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_without_installer_type(self): - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = None + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_fuel(self): - ft_constants.CI_INSTALLER_TYPE = "fuel" + os.environ["INSTALLER_TYPE"] = "fuel" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._neutron_ip, odlwebport='8282') def test_run_apex_missing_sdn_controller_ip(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = "apex" - ft_constants.SDN_CONTROLLER_IP = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + os.environ["INSTALLER_TYPE"] = "apex" self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_apex(self): - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = "apex" + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip + os.environ["INSTALLER_TYPE"] = "apex" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._sdn_controller_ip, odlwebport='8181') def test_run_joid_missing_sdn_controller(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = "joid" - ft_constants.SDN_CONTROLLER = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + os.environ["INSTALLER_TYPE"] = "joid" self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_joid(self): - ft_constants.SDN_CONTROLLER = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = "joid" + os.environ["SDN_CONTROLLER"] = self._sdn_controller_ip + os.environ["INSTALLER_TYPE"] = "joid" self._test_run(testcase_base.TestcaseBase.EX_OK, - odlip=self._sdn_controller_ip, - odlwebport=self._odl_webport) + odlip=self._sdn_controller_ip, odlwebport='8080') def test_run_compass(self, *args): - ft_constants.CI_INSTALLER_TYPE = "compass" + os.environ["INSTALLER_TYPE"] = "compass" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._neutron_ip, odlwebport='8181') diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index a25967b6..b1e4d3cd 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -21,6 +21,9 @@ import requests import yaml from git import Repo +import time +import functools + import functest.utils.functest_logger as ft_logger logger = ft_logger.Logger("functest_utils").getLogger() @@ -418,25 +421,8 @@ def merge_dicts(dict1, dict2): yield (k, dict2[k]) -def check_test_result(test_name, ret, start_time, stop_time): - def get_criteria_value(): - return get_criteria_by_test(test_name).split('==')[1].strip() - - status = 'FAIL' - if str(ret) == get_criteria_value(): - status = 'PASS' - - details = { - 'timestart': start_time, - 'duration': round(stop_time - start_time, 1), - 'status': status, - } - - return status, details - - def get_testcases_file_dir(): - return "/home/opnfv/repos/functest/functest/ci/testcases.yaml" + return get_functest_config('general.functest.testcases_yaml') def get_functest_yaml(): @@ -448,3 +434,17 @@ def get_functest_yaml(): def print_separator(): logger.info("==============================================") + + +def timethis(func): + """Measure the time it takes for a function to complete""" + @functools.wraps(func) + def timed(*args, **kwargs): + ts = time.time() + result = func(*args, **kwargs) + te = time.time() + elapsed = '{0}'.format(te - ts) + logger.info('{f}(*{a}, **{kw}) took: {t} sec'.format( + f=func.__name__, a=args, kw=kwargs, t=elapsed)) + return result, elapsed + return timed diff --git a/functest/utils/openstack_clean.py b/functest/utils/openstack_clean.py index 949eee90..c08568bd 100755 --- a/functest/utils/openstack_clean.py +++ b/functest/utils/openstack_clean.py @@ -9,6 +9,8 @@ # - Neutron networks, subnets and ports # - Routers # - Users and tenants +# - Tacker VNFDs and VNFs +# - Tacker SFCs and SFC classifiers # # Author: # jose.lausuch@ericsson.com @@ -105,7 +107,7 @@ def remove_volumes(cinder_client, default_volumes): for volume in volumes: volume_id = getattr(volume, 'id') - volume_name = getattr(volume, 'display_name') + volume_name = getattr(volume, 'name') logger.debug("'%s', ID=%s " % (volume_name, volume_id)) if (volume_id not in default_volumes and volume_name not in default_volumes.values()): @@ -393,7 +395,7 @@ def main(): default_security_groups = snapshot_yaml.get('secgroups') default_floatingips = snapshot_yaml.get('floatingips') default_users = snapshot_yaml.get('users') - default_tenants = snapshot_yaml.get('tenants') + # default_tenants = snapshot_yaml.get('tenants') if not os_utils.check_credentials(): logger.error("Please source the openrc credentials and run " @@ -414,8 +416,10 @@ def main(): separator() remove_users(keystone_client, default_users) separator() - remove_tenants(keystone_client, default_tenants) - separator() + # TODO (Helen) tenant does not exist in V3 + # need to figure our anohter general verification point + # remove_tenants(keystone_client, default_tenants) + # separator() if __name__ == '__main__': diff --git a/functest/utils/openstack_snapshot.py b/functest/utils/openstack_snapshot.py index 4be1af44..5b50ffa5 100755 --- a/functest/utils/openstack_snapshot.py +++ b/functest/utils/openstack_snapshot.py @@ -62,7 +62,7 @@ def get_volumes(cinder_client): volumes = os_utils.get_volumes(cinder_client) if volumes is not None: for volume in volumes: - dic_volumes.update({volume.id: volume.display_name}) + dic_volumes.update({volume.id: volume.name}) return {'volumes': dic_volumes} @@ -149,7 +149,7 @@ def main(): snapshot.update(get_security_groups(neutron_client)) snapshot.update(get_floatinips(nova_client)) snapshot.update(get_users(keystone_client)) - snapshot.update(get_tenants(keystone_client)) + # snapshot.update(get_tenants(keystone_client)) with open(OS_SNAPSHOT_FILE, 'w+') as yaml_file: yaml_file.write(yaml.safe_dump(snapshot, default_flow_style=False)) diff --git a/functest/utils/openstack_tacker.py b/functest/utils/openstack_tacker.py index 6ab05668..f17b421e 100644..100755 --- a/functest/utils/openstack_tacker.py +++ b/functest/utils/openstack_tacker.py @@ -21,7 +21,7 @@ logger = ft_logger.Logger("tacker_utils").getLogger() def get_tacker_client(): - creds_tacker = os_utils.get_credentials('tacker') + creds_tacker = os_utils.get_credentials() return tackerclient.Client(**creds_tacker) diff --git a/functest/utils/openstack_utils.py b/functest/utils/openstack_utils.py index df6fb5d1..ec784121 100755 --- a/functest/utils/openstack_utils.py +++ b/functest/utils/openstack_utils.py @@ -14,16 +14,21 @@ import subprocess import sys import time +from keystoneauth1 import loading +from keystoneauth1 import session from cinderclient import client as cinderclient -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils from glanceclient import client as glanceclient -from keystoneclient.v2_0 import client as keystoneclient -from neutronclient.v2_0 import client as neutronclient from novaclient import client as novaclient +from keystoneclient import client as keystoneclient +from neutronclient.neutron import client as neutronclient + +import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils logger = ft_logger.Logger("openstack_utils").getLogger() +DEFAULT_API_VERSION = '2' + # ********************************************* # CREDENTIALS @@ -37,68 +42,63 @@ class MissingEnvVar(Exception): return str.format("Please set the mandatory env var: {}", self.var) +def is_keystone_v3(): + keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') + if (keystone_api_version is None or + keystone_api_version == '2'): + return False + else: + return True + + +def get_rc_env_vars(): + keystone_v3 = is_keystone_v3() + env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD'] + if keystone_v3 is False: + env_vars.extend(['OS_TENANT_NAME']) + else: + env_vars.extend(['OS_PROJECT_NAME', + 'OS_USER_DOMAIN_NAME', + 'OS_PROJECT_DOMAIN_NAME']) + return env_vars + + def check_credentials(): """ Check if the OpenStack credentials (openrc) are sourced """ - env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_NAME'] + env_vars = get_rc_env_vars() return all(map(lambda v: v in os.environ and os.environ[v], env_vars)) -def get_credentials(service): - """Returns a creds dictionary filled with the following keys: - * username - * password/api_key (depending on the service) - * tenant_name/project_id (depending on the service) - * auth_url - :param service: a string indicating the name of the service - requesting the credentials. +def get_env_cred_dict(): + env_cred_dict = { + 'OS_USERNAME': 'username', + 'OS_PASSWORD': 'password', + 'OS_AUTH_URL': 'auth_url', + 'OS_TENANT_NAME': 'tenant_name', + 'OS_USER_DOMAIN_NAME': 'user_domain_name', + 'OS_PROJECT_DOMAIN_NAME': 'project_domain_name', + 'OS_PROJECT_NAME': 'project_name', + 'OS_ENDPOINT_TYPE': 'endpoint_type', + 'OS_REGION_NAME': 'region_name' + } + return env_cred_dict + + +def get_credentials(): + """Returns a creds dictionary filled with parsed from env """ creds = {} + env_vars = get_rc_env_vars() + env_cred_dict = get_env_cred_dict() - # Check that the env vars exists: - envvars = ('OS_USERNAME', 'OS_PASSWORD', 'OS_AUTH_URL', 'OS_TENANT_NAME') - for envvar in envvars: + for envvar in env_vars: if os.getenv(envvar) is None: raise MissingEnvVar(envvar) - - # Unfortunately, each of the OpenStack client will request slightly - # different entries in their credentials dict. - if service.lower() in ("nova", "cinder"): - password = "api_key" - tenant = "project_id" - else: - password = "password" - tenant = "tenant_name" - - # The most common way to pass these info to the script is to do it through - # environment variables. - creds.update({ - "username": os.environ.get("OS_USERNAME"), - password: os.environ.get("OS_PASSWORD"), - "auth_url": os.environ.get("OS_AUTH_URL"), - tenant: os.environ.get("OS_TENANT_NAME") - }) - if os.getenv('OS_ENDPOINT_TYPE') is not None: - creds.update({ - "endpoint_type": os.environ.get("OS_ENDPOINT_TYPE") - }) - if os.getenv('OS_REGION_NAME') is not None: - creds.update({ - "region_name": os.environ.get("OS_REGION_NAME") - }) - cacert = os.environ.get("OS_CACERT") - if cacert is not None: - # each openstack client uses differnt kwargs for this - creds.update({"cacert": cacert, - "ca_cert": cacert, - "https_ca_cert": cacert, - "https_cacert": cacert, - "ca_file": cacert}) - creds.update({"insecure": "True", "https_insecure": "True"}) - if not os.path.isfile(cacert): - logger.info("WARNING: The 'OS_CACERT' environment variable is " - "set to %s but the file does not exist." % cacert) + else: + creds_key = env_cred_dict.get(envvar) + creds.update({creds_key: os.getenv(envvar)}) return creds @@ -112,59 +112,121 @@ def source_credentials(rc_file): def get_credentials_for_rally(): - creds = get_credentials("keystone") - admin_keys = ['username', 'tenant_name', 'password'] - endpoint_types = [('internalURL', 'internal'), - ('publicURL', 'public'), ('adminURL', 'admin')] - if 'endpoint_type' in creds.keys(): - for k, v in endpoint_types: - if creds['endpoint_type'] == k: - creds['endpoint_type'] = v + creds = get_credentials() + env_cred_dict = get_env_cred_dict() rally_conf = {"type": "ExistingCloud", "admin": {}} for key in creds: - if key in admin_keys: - rally_conf['admin'][key] = creds[key] - else: + if key == 'auth_url': rally_conf[key] = creds[key] + else: + rally_conf['admin'][key] = creds[key] + + endpoint_types = [('internalURL', 'internal'), + ('publicURL', 'public'), ('adminURL', 'admin')] + + endpoint_type = os.getenv('OS_ENDPOINT_TYPE') + if endpoint_type is not None: + cred_key = env_cred_dict.get('OS_ENDPOINT_TYPE') + for k, v in endpoint_types: + if endpoint_type == k: + rally_conf[cred_key] = v + + region_name = os.getenv('OS_REGION_NAME') + if region_name is not None: + cred_key = env_cred_dict.get('OS_REGION_NAME') + rally_conf[cred_key] = region_name return rally_conf +def get_session_auth(): + loader = loading.get_plugin_loader('password') + creds = get_credentials() + auth = loader.load_from_options(**creds) + return auth + + +def get_endpoint(service_type, endpoint_type='publicURL'): + auth = get_session_auth() + return get_session().get_endpoint(auth=auth, + service_type=service_type, + endpoint_type=endpoint_type) + + +def get_session(): + auth = get_session_auth() + return session.Session(auth=auth) + + # ********************************************* # CLIENTS # ********************************************* +def get_keystone_client_version(): + api_version = os.getenv('OS_IDENTITY_API_VERSION') + if api_version is not None: + logger.info("OS_IDENTITY_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION + + def get_keystone_client(): - creds_keystone = get_credentials("keystone") - return keystoneclient.Client(**creds_keystone) + sess = get_session() + return keystoneclient.Client(get_keystone_client_version(), session=sess) + + +def get_nova_client_version(): + api_version = os.getenv('OS_COMPUTE_API_VERSION') + if api_version is not None: + logger.info("OS_COMPUTE_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION def get_nova_client(): - creds_nova = get_credentials("nova") - return novaclient.Client('2', **creds_nova) + sess = get_session() + return novaclient.Client(get_nova_client_version(), session=sess) + + +def get_cinder_client_version(): + api_version = os.getenv('OS_VOLUME_API_VERSION') + if api_version is not None: + logger.info("OS_VOLUME_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION def get_cinder_client(): - creds_cinder = get_credentials("cinder") - creds_cinder.update({ - "service_type": "volume" - }) - return cinderclient.Client('2', **creds_cinder) + sess = get_session() + return cinderclient.Client(get_cinder_client_version(), session=sess) + + +def get_neutron_client_version(): + api_version = os.getenv('OS_NETWORK_API_VERSION') + if api_version is not None: + logger.info("OS_NETWORK_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION def get_neutron_client(): - creds_neutron = get_credentials("neutron") - return neutronclient.Client(**creds_neutron) + sess = get_session() + return neutronclient.Client(get_neutron_client_version(), session=sess) + + +def get_glance_client_version(): + api_version = os.getenv('OS_IMAGE_API_VERSION') + if api_version is not None: + logger.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version) + return api_version + return DEFAULT_API_VERSION def get_glance_client(): - keystone_client = get_keystone_client() - glance_endpoint_type = 'publicURL' - os_endpoint_type = os.getenv('OS_ENDPOINT_TYPE') - if os_endpoint_type is not None: - glance_endpoint_type = os_endpoint_type - glance_endpoint = keystone_client.service_catalog.url_for( - service_type='image', endpoint_type=glance_endpoint_type) - return glanceclient.Client(1, glance_endpoint, - token=keystone_client.auth_token) + sess = get_session() + return glanceclient.Client(get_glance_client_version(), session=sess) # ********************************************* @@ -218,6 +280,45 @@ def get_flavor_id_by_ram_range(nova_client, min_ram, max_ram): return id +def get_aggregates(nova_client): + try: + aggregates = nova_client.aggregates.list() + return aggregates + except Exception, e: + logger.error("Error [get_aggregates(nova_client)]: %s" % e) + return None + + +def get_aggregate_id(nova_client, aggregate_name): + try: + aggregates = get_aggregates(nova_client) + _id = [ag.id for ag in aggregates if ag.name == aggregate_name][0] + return _id + except Exception, e: + logger.error("Error [get_aggregate_id(nova_client, %s)]:" + " %s" % (aggregate_name, e)) + return None + + +def get_availability_zones(nova_client): + try: + availability_zones = nova_client.availability_zones.list() + return availability_zones + except Exception, e: + logger.error("Error [get_availability_zones(nova_client)]: %s" % e) + return None + + +def get_availability_zone_names(nova_client): + try: + az_names = [az.zoneName for az in get_availability_zones(nova_client)] + return az_names + except Exception, e: + logger.error("Error [get_availability_zone_names(nova_client)]:" + " %s" % e) + return None + + def create_flavor(nova_client, flavor_name, ram, disk, vcpus, public=True): try: flavor = nova_client.flavors.create( @@ -281,6 +382,40 @@ def get_hypervisors(nova_client): return None +def create_aggregate(nova_client, aggregate_name, av_zone): + try: + nova_client.aggregates.create(aggregate_name, av_zone) + return True + except Exception, e: + logger.error("Error [create_aggregate(nova_client, %s, %s)]: %s" + % (aggregate_name, av_zone, e)) + return None + + +def add_host_to_aggregate(nova_client, aggregate_name, compute_host): + try: + aggregate_id = get_aggregate_id(nova_client, aggregate_name) + nova_client.aggregates.add_host(aggregate_id, compute_host) + return True + except Exception, e: + logger.error("Error [add_host_to_aggregate(nova_client, %s, %s)]: %s" + % (aggregate_name, compute_host, e)) + return None + + +def create_aggregate_with_host( + nova_client, aggregate_name, av_zone, compute_host): + try: + create_aggregate(nova_client, aggregate_name, av_zone) + add_host_to_aggregate(nova_client, aggregate_name, compute_host) + return True + except Exception, e: + logger.error("Error [create_aggregate_with_host(" + "nova_client, %s, %s, %s)]: %s" + % (aggregate_name, av_zone, compute_host, e)) + return None + + def create_instance(flavor_name, image_id, network_id, @@ -373,13 +508,13 @@ def create_floating_ip(neutron_client): return {'fip_addr': fip_addr, 'fip_id': fip_id} -def add_floating_ip(nova_client, server_id, floatingip_id): +def add_floating_ip(nova_client, server_id, floatingip_addr): try: - nova_client.servers.add_floating_ip(server_id, floatingip_id) + nova_client.servers.add_floating_ip(server_id, floatingip_addr) return True except Exception, e: logger.error("Error [add_floating_ip(nova_client, '%s', '%s')]: %s" - % (server_id, floatingip_id, e)) + % (server_id, floatingip_addr, e)) return False @@ -403,6 +538,36 @@ def delete_floating_ip(nova_client, floatingip_id): return False +def remove_host_from_aggregate(nova_client, aggregate_name, compute_host): + try: + aggregate_id = get_aggregate_id(nova_client, aggregate_name) + nova_client.aggregates.remove_host(aggregate_id, compute_host) + return True + except Exception, e: + logger.error("Error [remove_host_from_aggregate(nova_client, %s, %s)]:" + " %s" % (aggregate_name, compute_host, e)) + return False + + +def remove_hosts_from_aggregate(nova_client, aggregate_name): + aggregate_id = get_aggregate_id(nova_client, aggregate_name) + hosts = nova_client.aggregates.get(aggregate_id).hosts + assert( + all(remove_host_from_aggregate(nova_client, aggregate_name, host) + for host in hosts)) + + +def delete_aggregate(nova_client, aggregate_name): + try: + remove_hosts_from_aggregate(nova_client, aggregate_name) + nova_client.aggregates.delete(aggregate_name) + return True + except Exception, e: + logger.error("Error [delete_aggregate(nova_client, %s)]: %s" + % (aggregate_name, e)) + return False + + # ********************************************* # NEUTRON # ********************************************* @@ -940,38 +1105,29 @@ def get_image_id(glance_client, image_name): def create_glance_image(glance_client, image_name, file_path, disk="qcow2", - container="bare", public=True): + container="bare", public="public"): if not os.path.isfile(file_path): logger.error("Error: file %s does not exist." % file_path) return None try: image_id = get_image_id(glance_client, image_name) if image_id != '': - if logger: - logger.info("Image %s already exists." % image_name) + logger.info("Image %s already exists." % image_name) else: - if logger: - logger.info("Creating image '%s' from '%s'..." % (image_name, - file_path)) - try: - properties = ft_utils.get_functest_config( - 'general.image_properties') - except ValueError: - # image properties are not configured - # therefore don't add any properties - properties = {} - with open(file_path) as fimage: - image = glance_client.images.create(name=image_name, - is_public=public, - disk_format=disk, - container_format=container, - properties=properties, - data=fimage) + logger.info("Creating image '%s' from '%s'..." % (image_name, + file_path)) + + image = glance_client.images.create(name=image_name, + visibility=public, + disk_format=disk, + container_format=container) image_id = image.id + with open(file_path) as image_data: + glance_client.images.upload(image_id, image_data) return image_id except Exception, e: logger.error("Error [create_glance_image(glance_client, '%s', '%s', " - "'%s')]: %s" % (image_name, file_path, str(public), e)) + "'%s')]: %s" % (image_name, file_path, public, e)) return None |