From 338f5820601ea53c24c7663e756102c1f157b02d Mon Sep 17 00:00:00 2001 From: stewie925 Date: Mon, 21 May 2018 20:19:08 -0500 Subject: [PATCH] Add Ranger Tempest Tests Change-Id: Ie96bad932c6041c0b2781e4fecdc1098cdb5f2fc --- ranger_tempest_tests/.gitignore | 11 + ranger_tempest_tests/Dockerfile | 96 +++++ .../aic-orm-tempest-plugin/.gitignore | 11 + .../aic_orm_tempest_plugin/README.rst | 85 ++++ .../aic_orm_tempest_plugin/__init__.py | 0 .../aic_orm_tempest_plugin-blacklist.txt | 6 + .../aic_orm_tempest_plugin/clients.py | 47 ++ .../aic_orm_tempest_plugin/config.py | 55 +++ .../aic_orm_tempest_plugin/data_utils.py | 88 ++++ .../aic_orm_tempest_plugin/plugin.py | 46 ++ .../schemas/__init__.py | 0 .../schemas/customers_schema.py | 190 ++++++++ .../schemas/flavors_schema.py | 257 +++++++++++ .../schemas/images_schema.py | 201 +++++++++ .../schemas/regions_schema.py | 206 +++++++++ .../services/__init__.py | 0 .../services/base_client.py | 83 ++++ .../services/cms_client.py | 129 ++++++ .../services/fms_client.py | 169 ++++++++ .../services/ims_client.py | 112 +++++ .../services/rms_client.py | 159 +++++++ .../aic_orm_tempest_plugin/tests/__init__.py | 0 .../tests/api/__init__.py | 0 .../aic_orm_tempest_plugin/tests/api/base.py | 52 +++ .../tests/api/cms_base.py | 326 ++++++++++++++ .../tests/api/fms_base.py | 261 +++++++++++ .../tests/api/ims_base.py | 248 +++++++++++ .../tests/api/rms_base.py | 31 ++ .../tests/api/test_customers.py | 342 +++++++++++++++ .../tests/api/test_customers_negative.py | 42 ++ .../tests/api/test_flavors.py | 408 ++++++++++++++++++ .../tests/api/test_flavors_negative.py | 44 ++ .../tests/api/test_images.py | 299 +++++++++++++ .../tests/api/test_images_negative.py | 47 ++ .../tests/api/test_region_groups.py | 85 ++++ .../tests/api/test_regiongroups_negative.py | 42 ++ .../tests/api/test_regions.py | 158 +++++++ .../tests/api/test_regions_negative.py | 42 ++ .../tests/scenario/__init__.py | 0 .../changed_python_files.sh | 46 ++ .../aic-orm-tempest-plugin/flake8rc | 18 + .../aic-orm-tempest-plugin/pylintrc | 378 ++++++++++++++++ .../aic-orm-tempest-plugin/requirements.txt | 2 + .../aic-orm-tempest-plugin/setup.cfg | 31 ++ .../aic-orm-tempest-plugin/setup.py | 6 + .../aic-orm-tempest-plugin/tox.ini | 71 +++ ranger_tempest_tests/changed_python_files.sh | 46 ++ ranger_tempest_tests/flake8rc | 18 + ranger_tempest_tests/pylintrc | 378 ++++++++++++++++ .../ranger_tempest_plugin/README.rst | 85 ++++ .../ranger_tempest_plugin/__init__.py | 0 .../ranger_tempest_plugin/clients.py | 47 ++ .../ranger_tempest_plugin/config.py | 55 +++ .../ranger_tempest_plugin/data_utils.py | 88 ++++ .../ranger_tempest_plugin/plugin.py | 46 ++ .../ranger_tempest_plugin-blacklist.txt | 6 + .../ranger_tempest_plugin/schemas/__init__.py | 0 .../schemas/customers_schema.py | 190 ++++++++ .../schemas/flavors_schema.py | 257 +++++++++++ .../schemas/images_schema.py | 201 +++++++++ .../schemas/regions_schema.py | 207 +++++++++ .../services/__init__.py | 0 .../services/base_client.py | 83 ++++ .../services/cms_client.py | 129 ++++++ .../services/fms_client.py | 169 ++++++++ .../services/ims_client.py | 112 +++++ .../services/rms_client.py | 159 +++++++ .../ranger_tempest_plugin/tests/__init__.py | 0 .../tests/api/__init__.py | 0 .../ranger_tempest_plugin/tests/api/base.py | 52 +++ .../tests/api/cms_base.py | 326 ++++++++++++++ .../tests/api/fms_base.py | 261 +++++++++++ .../tests/api/ims_base.py | 248 +++++++++++ .../tests/api/rms_base.py | 31 ++ .../tests/api/test_customers.py | 342 +++++++++++++++ .../tests/api/test_customers_negative.py | 42 ++ .../tests/api/test_flavors.py | 408 ++++++++++++++++++ .../tests/api/test_flavors_negative.py | 44 ++ .../tests/api/test_images.py | 300 +++++++++++++ .../tests/api/test_images_negative.py | 47 ++ .../tests/api/test_region_groups.py | 85 ++++ .../tests/api/test_regiongroups_negative.py | 42 ++ .../tests/api/test_regions.py | 158 +++++++ .../tests/api/test_regions_negative.py | 42 ++ .../tests/scenario/__init__.py | 0 ranger_tempest_tests/setup.cfg | 31 ++ ranger_tempest_tests/setup.py | 6 + .../tempest_setup/.testr.conf | 10 + .../tempest_setup/accounts.yaml | 19 + .../tempest_setup/create_tenant.sh | 23 + .../tempest_setup/tempest.conf | 110 +++++ ranger_tempest_tests/tox.ini | 71 +++ 92 files changed, 9904 insertions(+) create mode 100644 ranger_tempest_tests/.gitignore create mode 100644 ranger_tempest_tests/Dockerfile create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/.gitignore create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/README.rst create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/__init__.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/aic_orm_tempest_plugin-blacklist.txt create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/clients.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/config.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/data_utils.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/plugin.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/__init__.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/customers_schema.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/flavors_schema.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/images_schema.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/regions_schema.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/__init__.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/base_client.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/cms_client.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/fms_client.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/ims_client.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/rms_client.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/__init__.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/__init__.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/base.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/cms_base.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/fms_base.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/ims_base.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/rms_base.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers_negative.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors_negative.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images_negative.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_region_groups.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regiongroups_negative.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions_negative.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/scenario/__init__.py create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/changed_python_files.sh create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/flake8rc create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/pylintrc create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/requirements.txt create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/setup.cfg create mode 100755 ranger_tempest_tests/aic-orm-tempest-plugin/setup.py create mode 100644 ranger_tempest_tests/aic-orm-tempest-plugin/tox.ini create mode 100755 ranger_tempest_tests/changed_python_files.sh create mode 100644 ranger_tempest_tests/flake8rc create mode 100644 ranger_tempest_tests/pylintrc create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/README.rst create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/__init__.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/clients.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/config.py create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/data_utils.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/plugin.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/ranger_tempest_plugin-blacklist.txt create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/schemas/__init__.py create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/schemas/customers_schema.py create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/schemas/flavors_schema.py create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/schemas/images_schema.py create mode 100644 ranger_tempest_tests/ranger_tempest_plugin/schemas/regions_schema.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/__init__.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/base_client.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/cms_client.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/fms_client.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/ims_client.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/services/rms_client.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/__init__.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/__init__.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/base.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/cms_base.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/fms_base.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/ims_base.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/rms_base.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers_negative.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors_negative.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images_negative.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_region_groups.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regiongroups_negative.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions_negative.py create mode 100755 ranger_tempest_tests/ranger_tempest_plugin/tests/scenario/__init__.py create mode 100755 ranger_tempest_tests/setup.cfg create mode 100755 ranger_tempest_tests/setup.py create mode 100644 ranger_tempest_tests/tempest_setup/.testr.conf create mode 100644 ranger_tempest_tests/tempest_setup/accounts.yaml create mode 100644 ranger_tempest_tests/tempest_setup/create_tenant.sh create mode 100644 ranger_tempest_tests/tempest_setup/tempest.conf create mode 100644 ranger_tempest_tests/tox.ini diff --git a/ranger_tempest_tests/.gitignore b/ranger_tempest_tests/.gitignore new file mode 100644 index 00000000..dfbcd745 --- /dev/null +++ b/ranger_tempest_tests/.gitignore @@ -0,0 +1,11 @@ +*.pyc +*.log +.venv +.testrepository +.project +.pydevproject +build +dist +ranger_plugin.egg-info +.settings/ +.tox/ diff --git a/ranger_tempest_tests/Dockerfile b/ranger_tempest_tests/Dockerfile new file mode 100644 index 00000000..9bb3a987 --- /dev/null +++ b/ranger_tempest_tests/Dockerfile @@ -0,0 +1,96 @@ +FROM ubuntu:14.04 + +ENV DEBIAN_FRONTEND noninteractive +ENV container docker +ENV LC_ALL C.UTF-8 +ENV LANG C.UTF-8 + +RUN apt -qq update && \ +apt -y install git \ +netcat \ +netbase \ +openssh-server \ +python-minimal \ +python-setuptools \ +python-pip \ +python-dev \ +python-dateutil \ +ca-certificates \ +openstack-pkg-tools \ +apache2 \ +gcc \ +g++ \ +libffi-dev \ +libssl-dev --no-install-recommends \ +&& apt-get clean \ +&& rm -rf \ + /var/lib/apt/lists/* \ + /tmp/* \ + /var/tmp/* \ + /usr/share/man \ + /usr/share/doc \ + /usr/share/doc-base +RUN pip install -U pip setuptools --user +RUN pip install wheel --user +# RUN pip install virtualenv + + +########################################################################## +### aic-orm-tempest-plugin setup +########################################################################## +WORKDIR / +COPY aic-orm-tempest-plugin/. ranger-tempest-plugin/ +### create egg-info for ranger-tempest-plugin (uncomment two lines below when ready) +WORKDIR /ranger-tempest-plugin/ +RUN python /ranger-tempest-plugin/setup.py develop + +########################################################################## +### END OF ranger-tempest-plugin setup +########################################################################## + +########################################################################## +### openstack tempest setup steps +########################################################################## +## +### reset workdir to root before executing tempest steps +WORKDIR / +## +### git clone tempest +RUN git clone https://git.openstack.org/openstack/tempest + +### change requirements params - customized for ranger tempest +RUN sed -i 's/paramiko>=.*/paramiko<2.1.3,>=2.0/g' \ + /tempest/requirements.txt +# if os-testr exists, replace os-testr line else insert after PrettyTable line +RUN grep -q 'os-testr.*' /tempest/tempest/requirements.txt \ + && sed -i 's/os-testr.*/os-testr==0.8.0/' /tempest/requirements.txt \ + || sed -i '/PrettyTable.*/a os-testr==0.8.0' /tempest/requirements.txt + +### now run 'pip install -r requirements' +RUN pip install -r /tempest/requirements.txt +RUN pip install -r /tempest/test-requirements.txt + +### create required tempest directories - and remove .stestr folder +RUN mkdir -p /tempest/logs \ + && mkdir -p /tempest/tempest_lock \ + && mkdir -p /tempest/images \ + && rm -rf /tempest/.stestr + +# copy tempest test setup files +COPY tempest_setup/.testr.conf /tempest/ +COPY tempest_setup/create_tenant.sh /tempest/ +COPY tempest_setup/accounts.yaml /tempest/etc +COPY tempest_setup/tempest.conf /tempest/etc + +########################################################################## +### END OF openstack tempest setup teps +########################################################################## + +########################################################################## +### RUN tempest tests on test_regions +########################################################################## +### create egg-info for tempest +WORKDIR /tempest/ +RUN python /tempest/setup.py develop +ENTRYPOINT ostestr run aic_orm_tempest_plugin.tests.api.test_regions \ + && /bin/bash \ No newline at end of file diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/.gitignore b/ranger_tempest_tests/aic-orm-tempest-plugin/.gitignore new file mode 100644 index 00000000..9a44f2ad --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/.gitignore @@ -0,0 +1,11 @@ +*.pyc +*.log +.venv +.testrepository +.project +.pydevproject +build +dist +aic_orm_plugin.egg-info +.settings/ +.tox/ diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/README.rst b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/README.rst new file mode 100755 index 00000000..77468c1d --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/README.rst @@ -0,0 +1,85 @@ +================================= +Tempest Integration of ORM +================================= + +This directory contains Tempest tests to cover the ORM project, as well +as a plugin to automatically load these tests into tempest. + +See the tempest plugin docs for information on using it: +http://docs.openstack.org/developer/tempest/plugin.html#using-plugins + +See the tempest docs for information on writing new tests etc: +http://docs.openstack.org/developer/tempest/ + +Quickstart +---------- + +#. You first need to install Tempest in a venv. If virtual environment is not + installed install it using "sudo apt-get install python-virtualenv":: + + $ virtualenv .venv + $ source .venv/bin/activate + $ cd tempest + $ pip install tox + $ pip install tempest + +#. Clone/install the plugin:: + + $ pip install -e + For example: + pip install -e /opt/stack/aic_orm + + + +tempest.conf file +-------------------- + +This file should be present in tempest/etc + + +Following content must be added in tempest.conf file: + + +[auth] +# Use predefined credentials instead of creating users on the fly. +use_dynamic_credentials=false +admin_username = username +admin_password = password +admin_project_name= tenant/project/customer name +test_accounts_file=/opt/stack/tempest/etc/accounts.yaml -- Provide the accurate path of the accounts.yaml file + +[oslo_concurrency] +lock_path = Provide respective path for oslo_concurrency + +[orm] +uri = Provide orm url. For exmple: http://orm.***.***.***.att.com +catalog_type = orm + +accounts.yaml file +------------------ + +accounts.yaml file must be added in the path tempest/etc + +Following content must be present with the given format in accounts.yaml file: + +- username: 'username1' + tenant_name: 'tenant_name1' + password: 'password1' +- username: 'username2' + tenant_name: 'tenant_name2' + password: 'password2' + + + +Running the tests +----------------- + +To run all tests from this plugin, run from the tempest repo:: + + $ tox -e all-plugin -- aic_orm + +To run all tempest tests including this plugin, run:: + + $ tox -e all-plugin + + diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/aic_orm_tempest_plugin-blacklist.txt b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/aic_orm_tempest_plugin-blacklist.txt new file mode 100755 index 00000000..f10624fa --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/aic_orm_tempest_plugin-blacklist.txt @@ -0,0 +1,6 @@ +############################################################################################### +# Blacklist ORM Tests since the tests are having issues. +# IST is looking into the same +# This will be removed once IST resolves the same [no ETA has been provided] +############################################################################################### +(?:aic_orm_tempest_plugin.*) \ No newline at end of file diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/clients.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/clients.py new file mode 100755 index 00000000..5f73a5c3 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/clients.py @@ -0,0 +1,47 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.services import base_client +from aic_orm_tempest_plugin.services.cms_client import CmsClient +from aic_orm_tempest_plugin.services.fms_client import FmsClient +from aic_orm_tempest_plugin.services.ims_client import ImsClient +from aic_orm_tempest_plugin.services.rms_client import RmsClient + +from tempest import clients +from tempest import config + +CONF = config.CONF + + +class OrmClientManager(clients.Manager): + + def __init__(self, credentials=None): + super(OrmClientManager, self).__init__(credentials) + self.cms_client = CmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.fms_client = FmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.rms_client = RmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.ims_client = ImsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/config.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/config.py new file mode 100755 index 00000000..a32e2c6d --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/config.py @@ -0,0 +1,55 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg + + +service_available_group = cfg.OptGroup( + name="service_available", + title="Available OpenStack Services" +) + +ServiceAvailableGroup = [ + cfg.BoolOpt("orm", default=False, + help="Whether or not orm is expected to be available") +] + +orm_group = cfg.OptGroup( + name="orm", + title="Orm Service option" +) + +OrmGroup = [ + cfg.StrOpt("uri", + default="orm", + help="Uri of the orm service."), + cfg.StrOpt("cms_port", + default='7080', + help="cms port of the orm url."), + cfg.StrOpt("fms_port", + default='8082', + help="fms port of the orm url."), + cfg.StrOpt("region_port", + default='8080', + help="region port of the orm url."), + cfg.BoolOpt("alt_region_available", + default=None, + help="alt_region_available of the orm alternate region."), + cfg.StrOpt("ims_port", + default='8084', + help="ims port of the orm url."), + cfg.StrOpt("image_url", + help="swift container url where image is located") +] diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/data_utils.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/data_utils.py new file mode 100644 index 00000000..6734bd69 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/data_utils.py @@ -0,0 +1,88 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +from oslo_log import log as logging +from tempest import config +from tempest.lib.common.utils import data_utils + +LOG = logging.getLogger(__name__) +CONF = config.CONF + + +def rand_region_status(exclude=[]): + statuses = {'functional', 'maintenance', 'down', 'building'}.difference( + exclude) + return random.choice(list(statuses)) + + +def rand_region_metadata(): + metadata = {} + for i in range(random.randint(2, 10)): + metadata[data_utils.rand_name()] = [data_utils.arbitrary_string()] + return metadata + + +def rand_region(id=None): + if id is None: + id = data_utils.rand_name() + region_dict = { + 'status': rand_region_status(), + 'id': id, + 'name': id, + 'designType': data_utils.arbitrary_string(), + 'locationType': data_utils.arbitrary_string(), + 'vlcpName': data_utils.arbitrary_string(), + 'description': data_utils.arbitrary_string(), + 'aicVersion': data_utils.arbitrary_string(), + 'OSVersion': data_utils.arbitrary_string(), + 'CLLI': data_utils.arbitrary_string(), + 'address': { + 'country': data_utils.arbitrary_string(), + 'state': data_utils.arbitrary_string(), + 'city': data_utils.arbitrary_string(), + 'street': data_utils.arbitrary_string(), + 'zip': str(data_utils.rand_int_id(start=10000, end=99999)) + }, + 'metadata': { + data_utils.rand_name(): [data_utils.arbitrary_string()], + data_utils.rand_name(): [data_utils.arbitrary_string()] + }, + 'endpoints': [{ + 'publicURL': data_utils.rand_url(), + 'type': 'dashboard' + }, { + 'publicURL': data_utils.rand_url(), + 'type': 'identity' + }, { + 'publicURL': data_utils.rand_url(), + 'type': 'ord' + }] + } + return region_dict + + +def rand_region_group(region_ids, id=None): + if id is None: + id = data_utils.rand_name() + group_dict = { + 'name': id, + 'id': id, + 'description': data_utils.arbitrary_string(), + 'regions': region_ids + } + + return group_dict diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/plugin.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/plugin.py new file mode 100755 index 00000000..38db7fbb --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/plugin.py @@ -0,0 +1,46 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import os + +from aic_orm_tempest_plugin import config as project_config + +from tempest import config +from tempest.test_discover import plugins + + +class OrmPlugin(plugins.TempestPlugin): + def get_opt_lists(self): + return [( + project_config.orm_group.name, + project_config.OrmGroup)] + + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(__file__)))[0] + test_dir = "aic_orm_tempest_plugin/tests" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + config.register_opt_group( + conf, + project_config.service_available_group, + project_config.ServiceAvailableGroup) + config.register_opt_group( + conf, + project_config.orm_group, + project_config.OrmGroup) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/customers_schema.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/customers_schema.py new file mode 100644 index 00000000..925ac991 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/customers_schema.py @@ -0,0 +1,190 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_status = { + 'type': 'string', + 'enum': ['Success', 'no regions', 'Error', 'Pending', 'Submitted'] +} + +_links = { + 'type': 'object', + 'properties': { + 'self': {'type': 'string'} + }, + 'required': ['self'] +} + +_region = { + 'type': 'object', + 'properties': { + 'added': {'type': 'string'}, + 'id': {'type': 'string'}, + 'links': _links + }, + 'required': ['added', 'id', 'links'] +} + +_user = { + 'type': 'object', + 'properties': { + 'added': {'type': 'string', 'format': 'date-time'}, + 'id': {'type': 'string'}, + 'links': _links + }, + 'required': ['added', 'id', 'links'] +} + +_customer = { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': _links, + 'created': {'type': 'string', 'format': 'date-time'} + }, + 'required': ['id', 'links', 'created'] +} + +create_customer = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +update_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +add_regions = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': { + 'type': 'array', + 'items': _region + }, + 'transaction_id': {'type': 'string'} + }, + 'required': ['regions', 'transaction_id'] + } +} + +add_users = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'users': { + 'type': 'array', + 'items': _user + }, + 'transaction_id': {'type': 'string'} + }, + 'required': ['users', 'transaction_id'] + } +} + +replace_users = add_users + +add_metadata = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +replace_metadata = add_metadata + +enable_customer = add_metadata + +get_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'status': _status, + 'uuid': {'type': 'string'}, + 'users': {'type': 'array'}, + 'description': {'type': 'string'}, + 'enabled': {'type': 'boolean'}, + 'defaultQuotas': {'type': 'array'}, + 'name': {'type': 'string'}, + 'regions': {'type': 'array'}, + 'custId': {'type': 'string'}, + 'metadata': {'type': 'object'} + }, + 'required': ['status', 'uuid', 'users', 'description', 'enabled', + 'defaultQuotas', 'name', 'regions', 'custId', 'metadata'] + } +} + +list_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customers': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'status': _status, + 'description': {'type': 'string'}, + 'enabled': {'type': 'boolean'}, + 'num_regions': {'type': 'integer'}, + 'regions': { + 'type': 'array', + 'items': {'type': 'string'} + }, + 'id': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'required': ['status', 'description', 'enabled', + 'num_regions', 'regions', 'id', 'name'] + } + } + }, + 'required': ['customers'] + } +} + +delete_customer = { + 'status_code': [204] +} + +delete_region_from_customer = delete_customer + +delete_default_user = delete_customer + +delete_user_from_region = delete_customer diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/flavors_schema.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/flavors_schema.py new file mode 100644 index 00000000..e8336de0 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/flavors_schema.py @@ -0,0 +1,257 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_regions = { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "type": {"type": "string"}, + "name": {"type": "string"}, + "error_message": {"type": "string"} + } + }, + "required": ["status", "type", "name"] +} + +_extra_specs = { + "type": "object", + "additionalProperties": { + "type": "string" + } +} + +_tags = { + "type": "object", + "items": { + "type": "array", + "items": {"type": "string"} + } +} + +_tenants = { + "type": "array", + "items": { + "type": "string" + } +} + +_flavor = { + "type": "object", + "properties": { + "flavor": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "alias": {"type": "string"}, + "description": {"type": "string"}, + "tags": _tags, + "series": { + "type": "string", + "enum": ["gv", "nd", "ns", "nv", "ss"] + }, + "extra-specs": _extra_specs, + "ram": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "ephemeral": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "visibility": { + "type": "string", + "enum": ["public", "private"] + }, + "options": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "regions": _regions, + "vcpus": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "swap": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "disk": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "tenants": _tenants, + "id": {"type": "string"}, + "name": {"type": "string"} + }, + "required": ["status", "series", "extra-specs", + "ram", "ephemeral", "visibility", "vcpus", + "regions", "swap", "disk", "tenants", + "id", "name"] + } + }, + "required": ["flavor"] +} + +create_flavor = { + "status_code": [201], + "response_body": _flavor +} + +get_flavor = { + "status_code": [200], + "response_body": _flavor +} + +delete_flavor = { + "status_code": [204] +} + +list_flavors = { + "status_code": [200], + "response_body": { + "type": "object", + "properties": { + "flavors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "description": {"type": "string"}, + "tags": _tags, + "series": { + "type": "string", + "enum": ["gv", "nd", "ns", "nv", "ss"] + }, + "extra-specs": _extra_specs, + "ram": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "ephemeral": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "visibiity": { + "type": "string", + "enum": ["public", "private"] + }, + "options": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "regions": _regions, + "vcpus": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "swap": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "disk": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "tenants": _tenants, + "id": {"type": "string"}, + "name": {"type": "string"} + }, + "required": ["status", "description", "series", + "extra-specs", "ram", "ephemeral", + "visibility", "vcpus", "regions", "swap", + "disk", "tenants", "id", "name"] + } + } + }, + "required": ["flavors"] + } +} + +get_extra_specs = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'os_extra_specs': _extra_specs + } + } +} + +add_extra_specs = { + "status_code": [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'os_extra_specs': _extra_specs + } + } +} + +update_extra_specs = get_extra_specs + +delete_extra_specs = { + "status_code": [204] +} + +add_tags = { + 'status_code': [201], + 'response_body': _tags +} + +delete_tags = { + "status_code": [204] +} + +get_tags = { + 'status_code': [200], + 'response_body': _tags +} + +update_tags = get_tags + +add_region = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': _regions + } + } +} + +delete_region = { + "status_code": [204] +} + +add_tenant = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'tenants': _tenants + } + } +} + +delete_tenant = { + "status_code": [204] +} diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/images_schema.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/images_schema.py new file mode 100644 index 00000000..600cde8a --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/images_schema.py @@ -0,0 +1,201 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_region_status = { + "type": "string", + "enum": ["Submitted", "Pending", "Success", "Error"] +} + +_regions = { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": _region_status, + "name": {"type": "string"}, + "checksum": {"type": "string"}, + "size": {"type": "string"}, + "virtual_size": {"type": "string"}, + "type": {"type": "string"}, + "error_message": {"type": "string"} + } + } +} + +_tags = { + "type": "array", + "items": { + "type": "string" + } +} + +_status = { + "type": "string", + "enum": ["Success", "no regions", "Error", "Pending"] +} + +_links = { + "type": "object", + "properties": { + "self": {"type": "string"} + }, + "required": ["self"] +} + +_image = { + "type": "object", + "properties": { + "image": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "links": { + "type": "object", + "items": { + "self": {"type": "string"} + } + }, + "locations": { + "type": "array", + "items": { + "type": "string" + } + }, + "file": {"type": "string"}, + "owner": {"type": "string"}, + "id": {"type": "string"}, + "self": {"type": "string"}, + "created-at": {"type": "string"}, + "updated-at": {"type": "string"}, + "regions": _regions, + "disk-format": {"type": "string"}, + "min-ram": { + "type": "number" + }, + "schema": {"type": "string"}, + "status": _status, + "customers": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": _tags, + "visibility": { + "type": "string", + "enum": ["public", "private"] + }, + "min-disk": { + "type": "number" + }, + "properties": { + "type": "object", + "items": { + "type": "array", + "items": {"type": "string"} + } + }, + "name": {"type": "string"}, + "url": {"type": "string"}, + "protected": { + "type": "boolean" + }, + "container-format": {"type": "string"} + }, + "required": ["links", "enabled", "locations", "file", + "status", "owner", "id", "self", "disk-format", + "min-ram", "properties", "visibility", + "regions", "schema", "min-disk", "customers", + "protected", "url", "name", "container-format"] + } + }, + "required": ["image"] +} + +create_image = { + "status_code": [201], + "response_body": _image +} + +add_tenant_to_image = { + "status_code": [201], + "response_body": _image +} + +get_image = { + "status_code": [200], + "response_body": _image +} + +update_tenant = get_image + +update_image = get_image + +delete_image = { + "status_code": [204] +} + +delete_tenant_from_image = { + "status_code": [204] +} + +add_region = { + "status_code": [200, 201], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': _regions + } + } +} + +delete_region = delete_image + +_region_names_array = { + "type": "array", + "items": { + "type": "string" + } +} + +list_images = { + "status_code": [200], + "response_body": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": _status, + "regions": _region_names_array, + "name": {"type": "string"}, + "visibiity": { + "type": "string", + "enum": ["public", "private"] + }, + "id": {"type": "string"} + }, + "required": ["status", "regions", "name", + "visibility", "id"] + } + } + }, + "required": ["images"] + } +} + +enable_image_resp = get_image diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/regions_schema.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/regions_schema.py new file mode 100644 index 00000000..ca011891 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/schemas/regions_schema.py @@ -0,0 +1,206 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_metadata = { + 'type': 'object', + 'items': { + 'type': 'array', + 'items': {'type': 'string'} + } +} + +_region = { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'endpoints': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'publicURL': {'type': 'string'}, + 'type': {'type': 'string'} + }, + 'required': ['publicURL', 'type'] + } + }, + 'CLLI': {'type': 'string'}, + 'name': {'type': 'string'}, + 'description': {'type': 'string'}, + 'designType': {'type': 'string'}, + 'locationType': {'type': 'string'}, + 'vlcpName': {'type': 'string'}, + 'address': { + 'type': 'object', + 'properties': { + 'country': {'type': 'string'}, + 'state': {'type': 'string'}, + 'street': {'type': 'string'}, + 'zip': {'type': 'string'}, + 'city': {'type': 'string'}, + }, + 'required': ['country', 'state', 'street', 'zip', 'city'] + }, + 'aicVersion': {'type': 'string'}, + 'OSVersion': {'type': 'string'}, + 'id': {'type': 'string'}, + 'metadata': _metadata, + 'created': {'type': 'string', 'format': 'date-time'}, + 'modified': {'type': 'string', 'format': 'date-time'} + }, + 'required': ['status', 'endpoints', 'aicVersion', 'OSVersion', 'CLLI', + 'created', 'modified', 'metadata', 'address', 'locationType', + 'designType', 'description', 'name', 'id', 'vlcpName'] +} + +get_region = { + 'status_code': [200], + 'response_body': _region +} + +get_region_metadata = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': {'metadata': _metadata}, + 'required': ['metadata'] + } +} + +create_region = { + 'status_code': [201], + 'response_body': _region +} + +update_region = create_region + +update_metadata = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': {'metadata': _metadata}, + 'required': ['metadata'] + } +} + +update_status = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'links': { + 'type': 'object', + 'properties': { + 'self': {'type': 'string'} + } + } + }, + 'required': ['status', 'links'] + } +} + +delete_region = { + 'status_code': [204] +} + +list_region = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': { + 'type': 'array', + 'items': _region + } + }, + 'required': ['regions'] + } +} + +list_region_v1 = { + 'status_code': [200], + 'response_body': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'vLCP_name': {'type': 'string'}, + 'ORD_EP': {'type': 'string'}, + 'horizon_EP': {'type': 'string'}, + 'design_type': {'type': 'string'}, + 'AIC_version': {'type': 'string'}, + 'id': {'type': 'string'}, + 'OS_version': {'type': 'string'}, + 'keystone_EP': {'type': 'string'}, + 'zone_name': {'type': 'string'}, + 'location_type': {'type': 'string'} + } + } + } +} + +_region_group = { + 'type': 'object', + 'properties': { + 'description': {'type': 'string'}, + 'links': { + 'type': 'object', + 'properties': {'self': {'type': 'string'}} + }, + 'created': {'type': 'string', 'format': 'date-time'}, + 'modified': {'type': 'string', 'format': 'date-time'}, + 'id': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'required': ['description', 'created', 'modified', 'id', 'name'] +} + +create_region_group = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'group': _region_group + }, + 'required': ['group'] + } +} + +update_region_group = create_region_group + +get_region_group = { + 'status_code': [200], + 'response_body': _region_group +} + +list_region_groups = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'groups': { + 'type': 'array', + 'items': _region_group + } + }, + 'required': ['groups'] + } +} + +delete_region_group = { + 'status_code': [204] +} diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/base_client.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/base_client.py new file mode 100755 index 00000000..bc2832ff --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/base_client.py @@ -0,0 +1,83 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from tempest import config +from tempest.lib import auth +from tempest.lib.common import rest_client + +CONF = config.CONF + + +class OrmClientBase(rest_client.RestClient): + + def get_headers(self): + headers = {'X-Auth-Region': CONF.identity.region, + 'X-AIC-ORM-Tracking-Id': 'test', + 'X-AIC-ORM-Requester': CONF.auth.admin_username, + 'X-AIC-ORM-Client': 'cli', + 'Content-Type': 'application/json' + } + return headers + + def get_request(self, uri, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.get(uri, extra_headers=ex_headers) + self.expected_success(200, resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def put_request(self, uri, put_body, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.put(uri, body=put_body, extra_headers=ex_headers) + self.expected_success([200, 201], resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def delete_request(self, uri, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.delete(uri, extra_headers=ex_headers) + self.expected_success(204, resp.status) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def post_request(self, uri, post_body, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.post(uri, body=post_body, + extra_headers=ex_headers) + self.expected_success([200, 201], resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + +class OrmAuthProvider(auth.KeystoneV2AuthProvider): + + def __init__(self, credentials, auth_url=CONF.identity.uri): + super(OrmAuthProvider, self).__init__(credentials, auth_url) + + def auth_request(self, method, url, headers=None, body=None, filters=None): + filters = {'service': 'identity'} + auth_headers = super(OrmAuthProvider, + self).auth_request(method, + url, + filters=filters) + base_headers = auth_headers[1] + base_headers.update(headers) + auth_req = dict(url=url, headers=base_headers, body=body) + return auth_req['url'], auth_req['headers'], auth_req['body'] diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/cms_client.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/cms_client.py new file mode 100755 index 00000000..d3dd3322 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/cms_client.py @@ -0,0 +1,129 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import urllib + +from aic_orm_tempest_plugin.schemas import customers_schema as schema +from aic_orm_tempest_plugin.services import base_client + +from tempest import config + +CONF = config.CONF + + +class CmsClient(base_client.OrmClientBase): + + cms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.cms_port) + version = 'v1' + + # POST + + def create_customer(self, **kwargs): + uri = '%s/%s/orm/customers' % (self.cms_url, self.version) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.create_customer) + + def add_default_user(self, customer_id, *args): + uri = '%s/%s/orm/customers/%s/users' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(args) + return self.post_request(uri, post_body, schema.add_users) + + def add_regions(self, customer_id, regions): + uri = '%s/%s/orm/customers/%s/regions' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(regions) + return self.post_request(uri, post_body, schema.add_regions) + + def add_region_user(self, customer_id, region_id, *args): + uri = '%s/%s/orm/customers/%s/regions/%s/users' \ + % (self.cms_url, self.version, customer_id, region_id) + post_body = json.dumps(args) + return self.post_request(uri, post_body, schema.add_users) + + def add_metadata(self, customer_id, metadata): + uri = '%s/%s/orm/customers/%s/metadata' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(metadata) + return self.post_request(uri, post_body, schema.add_metadata) + + # PUT + + def update_customer(self, customer_id, customer): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(customer) + return self.put_request(uri, put_body, schema.update_customer) + + def enable_customer(self, customer_id, value): + uri = '%s/%s/orm/customers/%s/enabled' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps({'enabled': value}) + return self.put_request(uri, put_body, schema.enable_customer) + + def replace_default_user(self, customer_id, *args): + uri = '%s/%s/orm/customers/%s/users' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(args) + return self.put_request(uri, put_body, schema.replace_users) + + def replace_region_user(self, customer_id, region_id, *args): + uri = '%s/%s/orm/customers/%s/regions/%s/users' \ + % (self.cms_url, self.version, customer_id, region_id) + + put_body = json.dumps(args) + return self.put_request(uri, put_body, schema.replace_users) + + def replace_metadata(self, customer_id, metadata): + uri = '%s/%s/orm/customers/%s/metadata' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(metadata) + return self.put_request(uri, put_body, schema.replace_metadata) + + # GET + + def get_customer(self, identifier): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, identifier) + return self.get_request(uri, schema.get_customer) + + def list_customers(self, filter=None): + uri = '%s/%s/orm/customers' % (self.cms_url, self.version) + if filter is not None: + uri += '?' + urllib.urlencode(filter) + return self.get_request(uri, schema.list_customer) + + # DELETE + + def delete_region_from_customer(self, customer_id, region_id): + uri = '%s/%s/orm/customers/%s/regions/%s' % ( + self.cms_url, self.version, customer_id, region_id) + return self.delete_request(uri, schema.delete_region_from_customer) + + def delete_customer(self, customer_id): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, customer_id) + return self.delete_request(uri, schema.delete_customer) + + def delete_default_user(self, customer_id, user_id): + uri = '%s/%s/orm/customers/%s/users/%s' \ + % (self.cms_url, self.version, customer_id, user_id) + return self.delete_request(uri, schema.delete_default_user) + + def delete_region_user(self, customer_id, region_id, user_id): + uri = '%s/%s/orm/customers/%s/regions/%s/users/%s' \ + % (self.cms_url, self.version, customer_id, region_id, user_id) + return self.delete_request(uri, schema.delete_user_from_region) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/fms_client.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/fms_client.py new file mode 100755 index 00000000..9c569898 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/fms_client.py @@ -0,0 +1,169 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from aic_orm_tempest_plugin.schemas import flavors_schema as schema +from aic_orm_tempest_plugin.services import base_client + +from tempest import config +from tempest.lib.common import rest_client + +CONF = config.CONF + + +class FmsClient(base_client.OrmClientBase): + + fms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.fms_port) + version = "v1" + + def get_extra_headers(self): + headers = {'X-Auth-Region': CONF.identity.region, + 'X-AIC-ORM-Tracking-Id': 'test', + 'X-AIC-ORM-Requester': CONF.auth.admin_username, + 'X-AIC-ORM-Client': 'cli' + } + return headers + + def create_flavor(self, **kwargs): + uri = '%s/%s/orm/flavors' % (self.fms_url, self.version) + post_body = {"flavor": kwargs} + post_body = json.dumps(post_body) + # ex_headers = self.get_headers() + # resp, body = self.post(uri, body=post_body, + # extra_headers=ex_headers) + # body = json.loads(body) + # self.validate_response(schema.create_flavor, resp, body) + # return rest_client.ResponseBody(resp, body["flavor"]) + return self.post_request(uri, post_body, schema.create_flavor) + + def get_flavor(self, identifier, para=None): + if para is None: + uri = '%s/%s/orm/flavors/%s' % (self.fms_url, self.version, + identifier) + else: + uri = '%s/%s/orm/flavors/%s/%s' % (self.fms_url, self.version, + identifier, para) + return self.get_request(uri, schema.get_flavor) + + def list_flavors(self, para=None): + if para is None: + uri = '%s/%s/orm/flavors' % (self.fms_url, self.version) + else: + uri = '%s/%s/orm/flavors/%s' % (self.fms_url, self.version, para) + # ex_headers = self.get_headers() + # resp, body = self.get(url, extra_headers=ex_headers) + # self.expected_success(200, resp.status) + # body = json.loads(body) + # self.validate_response(schema.list_flavors, resp, body) + # return rest_client.ResponseBody(resp, body) + return self.get_request(uri, schema.list_flavors) + + def delete_region_from_flavor(self, flavor_id, region_id): + uri = '%s/%s/orm/flavors/%s/regions/%s' % (self.fms_url, + self.version, flavor_id, + region_id) + ex_headers = self.get_headers() + resp, body = self.delete(uri, extra_headers=ex_headers) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) + + def delete_flavor(self, flavor_id): + uri = '%s/%s/orm/flavors/%s' %\ + (self.fms_url, self.version, flavor_id) + return self.delete_request(uri, schema.delete_flavor) + + def delete_tags(self, flavor_id, para): + if para is None: + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + else: + uri = '%s/%s/orm/flavors/%s/tags/%s' % (self.fms_url, self.version, + flavor_id, para) + return self.delete_request(uri, schema.delete_tags) + + def get_tags(self, flavor_id): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + return self.get_request(uri, schema.get_tags) + + def add_tags(self, flavor_id, tag_body): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + post_body = json.dumps(tag_body) + return self.post_request(uri, post_body, schema.add_tags) + + def update_tags(self, flavor_id, tag_body): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + put_body = json.dumps(tag_body) + return self.put_request(uri, put_body, schema.update_tags) + + def get_extra_specs(self, flavor_id): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + return self.get_request(uri, schema.get_extra_specs) + + def add_flvr_tenants(self, flavor_id, tenant_body): + uri = '%s/%s/orm/flavors/%s/tenants/' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(tenant_body) + return self.post_request(uri, post_body, schema.add_tenant) + + def add_flvr_regions(self, flavor_id, region_body): + uri = '%s/%s/orm/flavors/%s/regions' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(region_body) + return self.post_request(uri, post_body, schema.add_region) + + def delete_flvr_region(self, flavor_id, region_id): + uri = '%s/%s/orm/flavors/%s/regions/%s' % (self.fms_url, + self.version, + flavor_id, region_id) + return self.delete_request(uri, schema.delete_region) + + def add_extra_specs(self, flavor_id, extra_specs_body): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(extra_specs_body) + return self.post_request(uri, post_body, schema.add_extra_specs) + + def update_extra_specs(self, flavor_id, extra_specs_body): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + put_body = json.dumps(extra_specs_body) + return self.put_request(uri, put_body, schema.update_extra_specs) + + def delete_extra_specs(self, flavor_id, para): + if para is None: + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + else: + uri = '%s/%s/orm/flavors/%s/os_extra_specs/%s' % (self.fms_url, + self.version, + flavor_id, para) + return self.delete_request(uri, schema.delete_extra_specs) + + def delete_flvr_tenant(self, flavor_id, tenant): + uri = '%s/%s/orm/flavors/%s/tenants/%s' % (self.fms_url, + self.version, + flavor_id, tenant) + return self.delete_request(uri, schema.delete_tenant) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/ims_client.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/ims_client.py new file mode 100755 index 00000000..1bfb561b --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/ims_client.py @@ -0,0 +1,112 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from aic_orm_tempest_plugin.schemas import images_schema as schema +from aic_orm_tempest_plugin.services import base_client +from tempest import config + +CONF = config.CONF + + +class ImsClient(base_client.OrmClientBase): + + ims_url = '%s:%s' % (CONF.orm.uri, CONF.orm.ims_port) + version = "v1" + + def get_headers(self): + headers = {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Region': CONF.identity.region, + 'X-AIC-ORM-Tracking-Id': 'test', + 'X-AIC-ORM-Requester': CONF.auth.admin_username, + 'X-AIC-ORM-Client': 'cli' + } + return headers + + def create_image(self, **kwargs): + uri = '%s/%s/orm/images' % (self.ims_url, self.version) + post_body = {"image": kwargs} + post_body = json.dumps(post_body) + return self.post_request(uri, post_body, schema.create_image) + + def update_image(self, image_id, para=None, **kwargs): + if para is None: + uri = '%s/%s/orm/images/%s' % ( + self.ims_url, self.version, image_id) + else: + uri = '%s/%s/orm/images/%s/%s' % ( + self.ims_url, self.version, image_id, para) + put_body = {"image": kwargs} + put_body = json.dumps(put_body) + return self.put_request(uri, put_body, schema.update_image) + + def get_image(self, identifier, para=None): + if para is None: + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, + identifier) + else: + uri = '%s/%s/orm/images/%s/%s' % (self.ims_url, self.version, + identifier, para) + return self.get_request(uri, schema.get_image) + + def list_images(self, para=None): + if para is None: + uri = '%s/%s/orm/images' % (self.ims_url, self.version) + else: + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, para) + return self.get_request(uri, schema.list_images) + + def enabled_image(self, image_id, bool): + uri = '%s/%s/orm/images/%s/enabled' \ + % (self.ims_url, self.version, image_id) + put_body = json.dumps({'enabled': bool}) + return self.put_request(uri, put_body, schema.enable_image_resp) + + def add_region_to_image(self, image_id, region_id): + uri = '%s/%s/orm/images/%s/regions/' % (self.ims_url, + self.version, image_id) + post_body = json.dumps({"regions": [{"name": region_id}]}) + return self.post_request(uri, post_body, schema.add_region) + + def delete_region_from_image(self, image_id, region_id): + uri = '%s/%s/orm/images/%s/regions/%s' % (self.ims_url, + self.version, image_id, + region_id) + return self.delete_request(uri, schema.delete_region) + + def delete_image(self, image_id): + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, image_id) + return self.delete_request(uri, schema.delete_image) + + def add_customer_to_image(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers' % ( + self.ims_url, self.version, image_id) + post_body = json.dumps({"customers": [tenant_id]}) + return self.post_request(uri, post_body, schema.add_tenant_to_image) + + def update_customer(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers' % (self.ims_url, self.version, + image_id) + put_body = json.dumps({"customers": [tenant_id]}) + return self.put_request(uri, put_body, schema.update_tenant) + + def delete_customer_from_image(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers/%s' % (self.ims_url, + self.version, + image_id, + tenant_id) + return self.delete_request(uri, schema.delete_tenant_from_image) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/rms_client.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/rms_client.py new file mode 100755 index 00000000..567ae45d --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/services/rms_client.py @@ -0,0 +1,159 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import urllib + +from aic_orm_tempest_plugin.schemas import regions_schema as schema +from aic_orm_tempest_plugin.services import base_client + +from tempest import config + + +CONF = config.CONF + + +class RmsClient(base_client.OrmClientBase): + + rms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.region_port) + version = "v2" + + def create_region(self, region_id, **kwargs): + uri = '%s/%s/orm/regions' % (self.rms_url, self.version) + post_body = { + 'status': 'functional', + 'name': region_id, + 'id': region_id, + 'description': region_id, + 'designType': 'compact', + 'locationType': 'testlocation', + 'vlcpName': 'testvlcp', + 'address': { + 'country': 'usa', + 'state': 'tx', + 'city': 'austin', + 'street': '12 main', + 'zip': '12345' + }, + 'metadata': { + 'key': ["value"] + }, + 'endpoints': [ + { + 'publicURL': + 'https://dashboard-aic.%s.cci.att.com' % region_id, + 'type': 'dashboard' + }, + { + 'publicURL': + 'https://identity-aic.%s.cci.att.com:5000' % region_id, + 'type': 'identity' + }, + { + 'publicURL': + 'https://ord-aic.%s.cci.att.com:9010' % region_id, + 'type': 'ord' + }, + + ], + 'aicVersion': '3.6', + 'OSVersion': 'kilo', + 'CLLI': 'testclli' + } + if kwargs is not None: + for key in kwargs: + post_body[key] = kwargs[key] + + post_body = json.dumps(post_body) + return self.post_request(uri, post_body, schema.create_region) + + def update_region(self, region_id, **kwargs): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, region_id) + put_body = json.dumps(kwargs) + return self.put_request(uri, put_body, schema.update_region) + + def update_region_status(self, region_id, status): + uri = '%s/%s/orm/regions/%s/status' \ + % (self.rms_url, self.version, region_id) + put_body = json.dumps(status) + return self.put_request(uri, put_body, schema.update_status) + + def update_region_metadata(self, region_id, metadata): + uri = '%s/%s/orm/regions/%s/metadata' \ + % (self.rms_url, self.version, region_id) + put_body = json.dumps(metadata) + return self.put_request(uri, put_body, schema.update_metadata) + + def get_region(self, identifier): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region) + + def get_region_metadata(self, identifier): + uri = '%s/%s/orm/regions/%s/metadata'\ + % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region_metadata) + + def list_regions_v1(self): + uri = self.rms_url + '/lcp' + return self.get_request(uri, schema.list_region_v1) + + def list_regions(self, filter=None): + uri = '%s/%s/orm/regions' % (self.rms_url, self.version) + if filter is not None: + uri += '?' + urllib.urlencode(filter) + return self.get_request(uri, schema.list_region) + + def delete_region(self, region_id): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, region_id) + return self.delete_request(uri, schema.delete_region) + + def add_region_metadata(self, region_id, **kwargs): + uri = '%s/%s/orm/regions/%s/metadata'\ + % (self.rms_url, self.version, region_id) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.update_metadata) +# +# def delete_region_metadata(self, region_id, key): +# uri = '%s/%s/orm/regions/%s/metadata/%s' % ( +# self.rms_url, self.version, region_id, key) +# ex_headers = self.get_headers() +# resp, body = self.delete(uri, extra_headers=ex_headers) +# self.expected_success(200, resp.status) +# body = json.loads(body) +# return rest_client.ResponseBody(resp, body) + + def create_region_group(self, **kwargs): + uri = '%s/%s/orm/groups' % (self.rms_url, self.version) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.create_region_group) + + def update_region_group(self, group_id, **kwargs): + uri = '%s/%s/orm/groups/%s' % (self.rms_url, self.version, group_id) + put_body = json.dumps(kwargs) + return self.put_request(uri, put_body, schema.update_region_group) + + def get_region_group(self, identifier): + uri = '%s/%s/orm/groups/%s'\ + % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region_group) + + def list_region_groups(self): + uri = '%s/%s/orm/groups' % (self.rms_url, self.version) + return self.get_request(uri, schema.list_region_groups) + + def delete_region_group(self, region_group_id): + uri = '%s/%s/orm/groups/%s' % (self.rms_url, self.version, + region_group_id) + return self.delete_request(uri, schema.delete_region_group) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/base.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/base.py new file mode 100755 index 00000000..2ca5736e --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/base.py @@ -0,0 +1,52 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin import clients + +from oslo_log import log as logging +import six +from tempest import config +from tempest import test + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class BaseOrmTest(test.BaseTestCase): + + credentials = ['admin', 'primary', 'alt'] + + client_manager = clients.OrmClientManager + + build_timeout = 120 + build_interval = 10 + + @classmethod + def setup_clients(cls): + super(BaseOrmTest, cls).setup_clients() + cls.identity_client = cls.os_admin.tenants_client + + @classmethod + def skip_checks(cls): + super(BaseOrmTest, cls).skip_checks() + if not CONF.service_available.orm: + skip_msg = ("%s skipped as orm is not available" % cls.__name__) + raise cls.skipException(skip_msg) + + def assertExpected(self, expected, actual, excluded_keys): + for key, value in six.iteritems(expected): + if key not in excluded_keys: + self.assertIn(key, actual) + self.assertEqual(value, actual[key], key) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/cms_base.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/cms_base.py new file mode 100755 index 00000000..aa48144e --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/cms_base.py @@ -0,0 +1,326 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +import time + +from aic_orm_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.common.utils import data_utils + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class CmsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + @classmethod + def setup_clients(cls): + super(CmsBaseOrmTest, cls).setup_clients() + cls.client = cls.os_primary.cms_client + + @classmethod + def _get_quota(cls): + compute, storage, network, quota = {}, {}, {}, {} + compute["instances"] = "10" + compute["injected-files"] = "10" + compute["key-pairs"] = "10" + compute["ram"] = "10" + compute["vcpus"] = "51" + compute["metadata-items"] = "34" + compute["injected-file-content-bytes"] = "25" + storage["gigabytes"] = "10" + storage["snapshots"] = "10" + storage["volumes"] = "10" + network["floating-ips"] = "10" + network["networks"] = "10" + network["ports"] = "10" + network["routers"] = "10" + network["subnets"] = "10" + network["security-group-rules"] = "51" + network["security-groups"] = "50" + quota['compute'] = [compute] + quota['storage'] = [storage] + quota['network'] = [network] + return quota + + @classmethod + def _get_additional_quota_for_cust(cls): + quota = cls._get_quota() + quota["compute"][0]["floating-ips"] = "10" + quota["compute"][0]["fixed-ips"] = "10" + quota["compute"][0]["injected-file-path-bytes"] = "34" + quota["compute"][0]["server-groups"] = "10" + quota["compute"][0]["server-group-members"] = "34" + quota["network"][0]["health-monitor"] = "10" + quota["network"][0]["member"] = "10" + quota["network"][0]["nat-instance"] = "10" + quota["network"][0]["pool"] = "10" + quota["network"][0]["route-table"] = "10" + quota["network"][0]["vip"] = "10" + return quota + + @classmethod + def _get_customer_params(cls, quota=None, enabled=True, region_users=True, + default_users=True): + region, user, metadata, payload = {}, {}, {}, {} + cust_name = data_utils.rand_name('ormTempestCms') + if not quota: + quota = cls._get_quota() + region['name'] = CONF.identity.region + region['type'] = 'single' + region['quotas'] = [quota] + user['id'] = cls.os_primary.credentials.username + user['role'] = ["admin"] + region["users"] = [user] if region_users else [] + regions = [region] + metadata['my_server_name'] = cust_name + metadata['ocx_cust'] = random.randint(0, 999999999) + payload["description"] = cust_name + payload["enabled"] = True if enabled else False + payload["name"] = cust_name + payload['metadata'] = metadata + payload["regions"] = regions + payload["defaultQuotas"] = [quota] + payload['users'] = [user] if default_users else [] + return payload + + @classmethod + def _get_bare_customer_params(cls): + customer = {} + customer['description'] = '' + customer['enabled'] = True + customer['name'] = data_utils.rand_name('ormTempestCms') + customer['regions'] = [] + customer['defaultQuotas'] = [] + customer['users'] = [] + return customer + + @classmethod + def _get_user_params(cls, alt=False): + users = [] + if not alt: + users.append({'id': cls.os_primary.credentials.username, + 'role': ['admin']}) + else: + users.append({'id': cls.os_alt.credentials.username, + 'role': ['admin_viewer', 'admin_support']}) + return users + + @classmethod + def _get_region_params(cls): + quota = cls._get_quota() + region = {} + region['name'] = CONF.identity.region + region['type'] = 'single' + region['quotas'] = [quota] + return [region] + + @classmethod + def _create_cust_validate_creation_on_dcp_and_lcp(self, **kwargs): + """ Creates a customer record: kwargs contains field data + needed for customer POST body: + - name + - description + - enabled + - metadata + - regions + - defaultQuotas + - ephemeral + - regions + - visibility + - tenants + """ + _, body = self.client.create_customer(**kwargs) + customer_id = body["customer"]["id"] + _, customer = self.client.get_customer(customer_id) + if customer["name"] == kwargs["name"]: + if customer["regions"] == []: + customer_status = "no regions" + else: + customer_status = "Success" + + self._wait_for_status(customer_id, customer_status) + return customer_id + else: + message = "customer %s not created successfully" % kwargs["name"] + exceptions.TempestException(message) + + @classmethod + def _wait_for_status(cls, customer_id, status): + customer_status = cls.client.get_customer(customer_id)[1]["status"] + start = int(time.time()) + while customer_status != status: + time.sleep(cls.build_interval) + customer_status = cls.client.get_customer(customer_id)[1]["status"] + if customer_status == 'Error': + message = ('customer %s failed to reach %s status' + ' and is in ERROR status on orm' % + (customer_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('customer %s failed to reach %s' + 'status within the required time (%s s)' + 'on orm and is in %s status.' + % (customer_id, status, + cls.build_timeout, + customer_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_cust_quota_on_lcp(cls, quota, cust_id): + expected_quota_count = len(quota["compute"][0]) +\ + len(quota["storage"][0]) +\ + len(quota["network"][0]) + actual_quota_count = 0 + body = cls.nova_quotas_client.show_quota_set(cust_id) + for param in quota["compute"][0]: + if param in body["quota_set"]: + if (quota["compute"][0][param] == + str(body["quota_set"][param])): + actual_quota_count += 1 + body = cls.volume_quotas_client.show_quota_set(cust_id) + for param in quota["storage"][0]: + if param in body["quota_set"]: + if str(body["quota_set"][param]) == quota["compute"][0][param]: + actual_quota_count += 1 + body = cls.networks_quotas_client.show_quotas(cust_id) + for param in quota["network"][0]: + if param in body["quota_set"]: + if (quota["compute"][0][param] == + str(body["quota_set"][param])): + actual_quota_count += 1 + if expected_quota_count == actual_quota_count: + return True + else: + return False + + @classmethod + def _validate_users_on_cust_on_dcp_and_lcp(cls, post_body, cust_id): + default_users_req, region_users_req, \ + default_users_dcp, region_users_dcp, \ + users_lcp = [], [], [], [], [] + for user in post_body["regions"][0]["users"]: + region_users_req.append(user["id"]) + for user in post_body["users"]: + default_users_req.append(user["id"]) + expected_users_count = len(region_users_req) + len(default_users_req) + actual_users_count = 0 + lcp_body = cls.identity_client.list_tenant_users(cust_id) + for user in lcp_body["users"]: + users_lcp.append(user["id"]) + dcp_body = cls.client.get_customer(cust_id) + for user in dcp_body["regions"][0]["users"]: + region_users_dcp.append(user["id"]) + for user in dcp_body["users"]: + default_users_dcp.append(user["id"]) + for user in default_users_req: + if (user in users_lcp) and (user in default_users_dcp): + actual_users_count += 1 + for user in region_users_req: + if (user in users_lcp) and (user in region_users_dcp): + actual_users_count += 1 + if expected_users_count == actual_users_count: + return True + else: + return False + + @classmethod + def _del_cust_validate_deletion_on_dcp_and_lcp(cls, customer_id): + _, customer = cls.client.get_customer(customer_id) + regions_on_customer = [region for region in customer["regions"]] + if regions_on_customer: + region_name_on_customer = regions_on_customer[0]["name"] + cls._delete_region_from_customer_and_validate_deletion( + customer_id, region_name_on_customer) + cls.client.delete_customer(customer_id) + cls._wait_for_customer_deletion_on_dcp(customer_id) + cls._validate_customer_deletion_on_lcp(customer_id) + + @classmethod + def _delete_region_from_customer_and_validate_deletion( + cls, customer_id, rname): + _, region = cls.os_admin.rms_client.get_region(rname) + region_id = region["id"] + cls.client.delete_region_from_customer(customer_id, region_id) + # cls._wait_for_cust_status_on_dcp(customer_id, "no regions") + cls._wait_for_status(customer_id, "no regions") + _, body = cls.client.get_customer(customer_id) + regions_on_customer = [rgn for rgn in body["regions"]] + if regions_on_customer: + message = "Region %s failed to get deleted from customer %s " % ( + rname, customer_id) + raise exceptions.TempestException(message) + cls._validate_customer_deletion_on_lcp(customer_id) + + @classmethod + def _wait_for_customer_deletion_on_dcp(cls, customer_id): + _, body = cls.client.list_customers() + customer_list = body["customers"] + customer_ids = [customer["id"] + for customer in customer_list + if customer["id"] == customer_id] + start = int(time.time()) + while customer_ids: + time.sleep(cls.build_interval) + _, body = cls.client.list_customers()["customers"] + customer_list = body["customers"] + customer_ids = [customer["id"] + for customer in customer_list + if customer["id"] == customer_id] + if customer_ids: + customer_status = customer_ids[0]["status"] + if customer_status == 'Error': + message = "customer %s failed to get deleted and is in\ + error status" % customer_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ( + 'customer %s failed to get deleted within ' + 'the required time (%s s) and is in %s status.' + % (customer_id, cls.build_timeout, + customer_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_customer_deletion_on_lcp(cls, customer_id): + body = cls.identity_client.list_tenants()["tenants"] + customer_ids = [customer["id"] + for customer in body + if customer["id"] == customer_id] + if customer_ids: + message = "customer %s failed to get deleted on lcp" \ + % customer_id + raise exceptions.TempestException(message) + + @classmethod + def _update_cust_and_validate_status_on_dcp_and_lcp( + cls, customer_id, para, **kwargs): + body = cls.client.update_customer(customer_id, para, **kwargs) + if body["id"] == customer_id: + cls._wait_for_cust_status_on_dcp(customer_id, "Success") + cls._validate_cust_creation_on_lcp(customer_id) + else: + message = "customer %s not updated successfully" % customer_id + raise exceptions.TempestException(message) + return body diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/fms_base.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/fms_base.py new file mode 100755 index 00000000..3fb458ff --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/fms_base.py @@ -0,0 +1,261 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +import time + +from aic_orm_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class FmsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + # added setup_clients function by stew + @classmethod + def setup_clients(cls): + super(FmsBaseOrmTest, cls).setup_clients() + cls.client = cls.os_primary.fms_client + cls.flavors_client = cls.os_admin.flavors_client + cls.tenant_id = cls._get_tenant_id( + cls.os_primary.credentials.tenant_name) + cls.alt_tenant_id = cls._get_tenant_id( + cls.os_alt.credentials.tenant_name) + + @classmethod + def _get_flavor_params(cls, set_region=True, single_tenant=True): + post_body, region = {}, {} + region["name"] = CONF.identity.region + ram = random.randint(1, 4) * 1024 + swap = random.randint(1, 40) * 1024 + vcpus = random.randint(2, 46) + disk = random.randint(2, 102) + post_body["description"] = \ + "orm-plugin-BaseORMTest-flavor" + post_body["series"] = random.choice(["ns", "nd", "gv", "nv"]) + post_body["alias"] = "flavor_alias" + post_body["ram"] = str(ram) + post_body["vcpus"] = str(vcpus) + post_body["disk"] = str(disk) + post_body["swap"] = str(swap) + post_body["ephemeral"] = "1024" + post_body["regions"] = [region] if set_region else [] + post_body["visibility"] = "private" + + if single_tenant: + post_body["tenants"] = [cls.tenant_id] + else: + post_body["tenants"] = [cls.tenant_id, cls.alt_tenant_id] + return post_body + + @classmethod + def _create_flv_and_validate_creation_on_dcp_and_lcp(cls, **kwargs): + """ kwargs contain all field data needed in a flavor POST body: + - name + - description + - alias + - ram + - vcpus + - disk + - swap + - ephemeral + - regions + - visibility + - tenants + """ + _, body = cls.client.create_flavor(**kwargs) + flavor = body["flavor"] + flavor_id = flavor["id"] + _, body = cls.client.get_flavor(flavor_id) + flavor_detail = body["flavor"] + if flavor_detail["vcpus"] == kwargs["vcpus"]: + if flavor_detail["regions"] == []: + flavor_status = "no regions" + else: + flavor_status = "Success" + flavor_id = flavor_detail["id"] + cls._wait_for_flavor_status_on_dcp(flavor_id, flavor_status) + cls._validate_flavor_creation_on_lcp(flavor_id) + return flavor + else: + message = "flavor %s not created successfully" % flavor_id + raise exceptions.TempestException(message) + + @classmethod + def _wait_for_flavor_status_on_dcp(cls, flavor_id, status): + _, body = cls.client.get_flavor(flavor_id) + flavor = body["flavor"] + flavor_status = flavor["status"] + start = int(time.time()) + while flavor_status != status: + time.sleep(cls.build_interval) + _, body = cls.client.get_flavor(flavor_id) + flavor = body["flavor"] + flavor_status = flavor["status"] + if flavor_status == 'Error': + message = ('flavor %s failed to reach %s status' + ' and is in ERROR status on orm' % + (flavor_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message =\ + 'flavor %s failed to reach %s status within' + ' the required time (%s s) on orm and is in' + '%s status.' % (flavor_id, status, + cls.build_timeout, flavor_status) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_flavor_creation_on_lcp(cls, flavor_id): + _, body = cls.client.list_flavors() + flavor = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + if not flavor: + message = "flavor %s not in nova flavor list" % flavor_id + raise exceptions.TempestException(message) + + @classmethod + def _validate_flv_extraspecs_on_dcp_and_lcp(cls, flavor_id, + expected_specs): + expected_specs_count = len(expected_specs) + _, body = cls.client.get_flavor(flavor_id) + flavor_orm = body["flavor"] + flavor_lcp = cls.flavors_client.show_flavor(flavor_id)["flavor"] + + def _validate_extra_specs(flv): + actual_specs_count = 0 + actual_specs = {} + for spec in flv["extra-specs"]: + actual_specs[spec] = flv["extra-specs"][spec] + for spec in expected_specs: + if spec in actual_specs: + if expected_specs[spec] == actual_specs[spec]: + actual_specs_count += 1 + if expected_specs_count == actual_specs_count: + return True + else: + return False + if _validate_extra_specs(flavor_orm) and\ + _validate_extra_specs(flavor_lcp): + return True + else: + return False + + @classmethod + def _del_flv_and_validate_deletion_on_dcp_and_lcp(cls, flavor_id): + _, body = cls.client.get_flavor(flavor_id) + regions_on_flavor = [region for region in body["flavor"]["regions"]] + if regions_on_flavor: + region_name_on_flavor = regions_on_flavor[0]["name"] + cls._delete_region_from_flavor_and_validate_deletion( + flavor_id, region_name_on_flavor) + cls.client.delete_flavor(flavor_id) + cls._wait_for_flavor_deletion_on_dcp(flavor_id) + cls._validate_flavor_deletion_on_lcp(flavor_id) + + @classmethod + def _delete_region_from_flavor_and_validate_deletion( + cls, flavor_id, rname): + _, body = cls.rms_client.get_region(rname) + region_id = body["id"] + cls.client.delete_region_from_flavor(flavor_id, region_id) + cls._wait_for_flavor_status_on_dcp(flavor_id, "no regions") + _, body = cls.client.get_flavor(flavor_id) + regions_on_flavor = body["flavor"]["regions"] + + if regions_on_flavor: + message = \ + "Region %s failed to get deleted from flavor %s " % ( + rname, flavor_id) + raise exceptions.TempestException(message) + cls._validate_flavor_deletion_on_lcp(flavor_id) + + @classmethod + def _wait_for_flavor_deletion_on_dcp(cls, flavor_id): + _, body = cls.client.list_flavors() + flavor_ids = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + start = int(time.time()) + while flavor_ids: + time.sleep(cls.build_interval) + _, body = cls.client.list_flavors() + flavor_ids = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + if flavor_ids: + flavor_status = flavor_ids[0]["status"] + if flavor_status == 'Error': + message = \ + 'Flavor %s failed to get deleted' + 'and is in error status' % \ + flavor_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ( + 'flavor %s failed to get deleted within ' + 'the required time (%s s) and is in %s status.' + % (flavor_id, cls.build_timeout, flavor_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_flavor_deletion_on_lcp(cls, flavor_id): + body = cls.flavors_client.list_flavors()["flavors"] + flavor_ids = [flavor["id"] for flavor in body + if flavor["id"] == flavor_id] + if flavor_ids: + flavor_status = cls.flavors_client.show_flavor( + flavor_id)["flavor"]["status"] + message = "flavor %s failed to get deleted and is in %s status" \ + % (flavor_id, flavor_status) + raise exceptions.TempestException(message) + + @classmethod + def _get_tenant_id(cls, tenant_name): + body = cls.identity_client.list_tenants() + for tenant in body["tenants"]: + if(tenant["name"] == tenant_name): + return tenant["id"] + message = ('tenant %s not found on tenant list' % cls.tenant_name) + raise exceptions.TempestException(message) + + @classmethod + def _get_expected_flavor_name(cls, post_body): + name = post_body["series"] + "." + "c" + \ + post_body["vcpus"] + "r" + \ + str(int(post_body["ram"]) / 1024) \ + + "d" + post_body["disk"] + "s" + \ + str(int(post_body["swap"]) / 1024) \ + + "e" + str(int(post_body["ephemeral"]) / 1024) + return name + + @classmethod + def _validate_flv_geometry_on_lcp(cls, flavor_id, post_body): + flv = cls.flavors_client.show_flavor(flavor_id)["flavor"] + if flv["vcpus"] == int(post_body["vcpus"]) and \ + flv["ram"] == post_body["ram"] and \ + flv["swap"] == int(post_body["swap"]) and \ + flv["disk"] == int(post_body["disk"]) and \ + flv["ephemeral"] == post_body["ephemeral"]: + return True + else: + return False diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/ims_base.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/ims_base.py new file mode 100755 index 00000000..f163acf1 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/ims_base.py @@ -0,0 +1,248 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +from aic_orm_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.common.utils import data_utils + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class ImsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + @classmethod + def setup_clients(cls): + super(ImsBaseOrmTest, cls).setup_clients() + # service clients + cls.client = cls.os_primary.ims_client + cls.rms_client = cls.os_primary.rms_client + cls.cms_client = cls.os_primary.cms_client + + # setup variables + cls.region_id = CONF.identity.region + cls.tenant_id = cls._get_tenant_id( + cls.os_primary.credentials.tenant_name) + cls.alt_tenant_id = cls._get_tenant_id( + cls.os_alt.credentials.tenant_name) + + @classmethod + def _get_image_params(cls, set_region=True, single_tenant=True, + set_private=True, set_enabled=True): + region, post_body = {}, {} + post_body["name"] = data_utils.rand_name( + "orm-plugin-TestTempestIms-image") + + # use ubuntu website for the image location URL + ubuntu_url = "http://archive.ubuntu.com/" + ubuntu_dir = "ubuntu/dists/xenial/main/installer-i386/current/images/" + ubuntu_iso = "netboot/mini.iso" + post_body["url"] = ubuntu_url + ubuntu_dir + ubuntu_iso + + post_body["disk-format"] = "qcow2" + post_body["container-format"] = "bare" + + region["name"] = cls.region_id + region["type"] = "single" + region["checksum"] = "7297321c2fa6424417a548c85edd6e98" + region["virtual_size"] = "None" + region["size"] = "38797312" + + # set enabled status to True or False based on set_enabled value + post_body["enabled"] = True if set_enabled else False + # add region for the image as needed + post_body["regions"] = [region] if set_region else [] + # create image with visibililty = "public" or "private" + post_body["visibility"] = "private" if set_private else "public" + # add tenant for the image only if set_private + if set_private: + if single_tenant: + post_body["customers"] = [cls.tenant_id] + else: + post_body["customers"] = [cls.tenant_id, cls.alt_tenant_id] + else: + post_body["customers"] = [] + + return post_body + + @classmethod + def _get_tenant_id(cls, tenant_name): + body = cls.identity_client.list_tenants() + for tenant in body["tenants"]: + if(tenant["name"] == tenant_name): + return tenant["id"] + message = ('tenant %s not found on tenant list' % cls.tenant_name) + raise exceptions.TempestException(message) + + @classmethod + def _create_img_and_validate_creation_on_dcp_and_lcp(cls, **kwargs): + _, body = cls.client.create_image(**kwargs) + image = body["image"] + image_id = image["id"] + + _, body = cls.client.get_image(image_id) + image_detail = body["image"] + if image_detail["name"] == kwargs["name"]: + if image_detail["regions"] == []: + image_status = "no regions" + else: + image_status = "Success" + + cls._wait_for_image_status_on_dcp(image_id, image_status) + return image + else: + message = ('image %s not created successfully' % kwargs["name"]) + raise exceptions.TempestException(message) + + @classmethod + def _wait_for_image_status_on_dcp(cls, image_id, status): + _, body = cls.client.get_image(image_id) + image_status = body["image"]["status"] + start = int(time.time()) + while image_status != status: + time.sleep(cls.build_interval) + _, body = cls.client.get_image(image_id) + image_status = body["image"]["status"] + if image_status == 'Error': + message = ('Image %s failed to reach %s status' + ' and is in ERROR status on orm' % + (image_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('Image %s failed to reach %s' + ' status within ''the required time (%s s)' + ' on orm and is in %s status.' + % (image_id, status, + cls.build_timeout, + image_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_image_status_on_lcp(cls, image_id, status): + _, body = cls.client.list_images()["images"] + image_ids = [image["id"] for image in body] + if image_id not in image_ids: + message = ('Image %s not in image list on LCP' % image_id) + raise exceptions.TempestException(message) + else: + image_status = cls.client.show_image()["image"]["status"] + if image_status != status: + message = ('Image %s is in %s status instead of %s on LCP.' + % (image_id, image_status, status)) + raise exceptions.TempestException(message) + + @classmethod + def _update_img_validate_status_on_dcp_and_lcp(cls, image_id, para=None, + **kwargs): + if para: + cls.client.update_image(image_id, para, **kwargs) + else: + cls.client.update_image(image_id, **kwargs) + cls._wait_for_image_status_on_dcp(image_id, "Success") + cls._validate_image_status_on_lcp(image_id, "active") + + @classmethod + def _del_img_validate_deletion_on_dcp_and_lcp(cls, image_id): + _, body = cls.client.get_image(image_id) + image = body["image"] + + regions_on_image = [region for region in image["regions"]] + if regions_on_image: + region_id = regions_on_image[0]["name"] + cls._delete_region_from_image_and_validate_deletion( + image_id, region_id) + + cls.client.delete_image(image_id) + cls._validate_image_deletion_on_lcp(image_id) + + @classmethod + def _delete_region_from_image_and_validate_deletion(cls, image_id, + region_id): + cls.client.delete_region_from_image(image_id, region_id) + cls._wait_for_image_status_on_dcp(image_id, "no regions") + + @classmethod + def _wait_for_image_deletion_on_dcp(cls, image_id): + _, body = cls.client.list_images() + image_ids = [image["id"] for image in body["images"]] + if image_id in image_ids: + start = int(time.time()) + while image_id in image_ids: + time.sleep(cls.build_interval) + _, image_list = cls.client.list_images()["images"] + image_ids = [image["id"] + for image in image_list if image["id"] == + image_id] + if image_ids: + image_status = image_list[0]["status"] + _, body = cls.client.list_images()["images"] + image_ids = [image["id"] for image in body] + if image_id in image_ids: + image_status = image_ids[0]["status"] + if image_status == 'Error': + message = \ + 'Image %s failed to get deleted ' + 'and is in error status' % \ + image_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('Image %s failed to get deleted within ' + 'the required time (%s s) on orm ' + 'and is in %s status.' + % (image_id, cls.build_timeout, image_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_image_deletion_on_lcp(cls, image_id): + _, body = cls.client.list_images() + image_ids = [image["id"] for image in body["images"] + if image["id"] == image_id] + if image_id in image_ids: + image_status = body["status"] + message = "image %s failed to get deleted and is in %s status" \ + % (image_id, image_status) + raise exceptions.TempestException(message) + + @classmethod + def _validate_custs_on_img_on_dcp_and_lcp(cls, image_id, + expected_customers): + expected_customers = sorted(expected_customers) + + _, actual_customers_orm = sorted( + cls.client.get_image(image_id)["customers"]) + members = cls.client.member_list(image_id)["members"] + actual_customers_lcp =\ + sorted([member["member_id"] for member in members]) + if actual_customers_orm != expected_customers: + message = ( + 'Incorrect customers on image on orm.' + 'expected customers = %s, actual customers = %s' + % (expected_customers, actual_customers_orm)) + raise exceptions.TempestException(message) + if actual_customers_lcp != expected_customers: + message = ( + 'Incorrect customers on image on orm.' + 'expected customers = %s, actual customers = %s' + % (expected_customers, actual_customers_lcp)) + raise exceptions.TempestException(message) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/rms_base.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/rms_base.py new file mode 100755 index 00000000..23c13dab --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/rms_base.py @@ -0,0 +1,31 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class RmsBaseOrmTest(base.BaseOrmTest): + + @classmethod + def setup_clients(cls): + cls.client = cls.os_primary.rms_client + super(RmsBaseOrmTest, cls).setup_clients() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers.py new file mode 100755 index 00000000..3fda2ff0 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers.py @@ -0,0 +1,342 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +from aic_orm_tempest_plugin.tests.api import cms_base +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestCms(cms_base.CmsBaseOrmTest): + + @classmethod + def resource_setup(cls): + cls.setup_customer = cls._get_customer_params() + cls.setup_customer_id = \ + cls._create_cust_validate_creation_on_dcp_and_lcp( + **cls.setup_customer) + + cls.bare_customer = cls._get_bare_customer_params() + cls.bare_customer_id = \ + cls._create_cust_validate_creation_on_dcp_and_lcp( + **cls.bare_customer) + + super(TestTempestCms, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls._del_cust_validate_deletion_on_dcp_and_lcp(cls.setup_customer_id) + cls._del_cust_validate_deletion_on_dcp_and_lcp(cls.bare_customer_id) + super(TestTempestCms, cls).resource_cleanup() + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d89') + def test_get_customer(self): + ''' Execute 'get_customer' using the following options: + - get customer by id (using cust_id parameter) + - get customer by name (using cust_name parameter) + ''' + + # execute get_customer using cust_id and cust_name + for identifier in [self.setup_customer_id, + self.setup_customer['name']]: + _, body = self.client.get_customer(identifier) + self.assertIn(self.setup_customer_id, body['uuid']) + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d90') + def test_list_customers_with_filters(self): + ''' this function executes 'list customer' with all available filters: + - no filter (i.e. list all customers) + - filter by metadata key + - filter by region + - filter by default user id + - customer name contains a substring + - customer name starting with a string + ''' + + # format filter parameter values + region_name = [ + region['name'] for region in self.setup_customer['regions']] + userid = [user['id'] for user in self.setup_customer['users']] + cust_name = self.setup_customer['name'] + substr_name = random.randint(0, len(cust_name)) + + # get the first key from metadata as the metadata key filter + metadata_key = list(self.setup_customer['metadata'].keys())[0] + + # define the list customer filters to be used for this test + no_filter = None + metadata_filter = {'metadata': metadata_key} + region_filter = {'region': region_name[0]} + user_filter = {'user': userid[0]} + contains_filter = {'contains': cust_name[substr_name:]} + startswith_filter = {'starts_with': cust_name[:substr_name]} + + # execute list_customers with the available filters + for list_filter in [no_filter, metadata_filter, region_filter, + user_filter, contains_filter, startswith_filter]: + _, body = self.client.list_customers(list_filter) + customers = [cust['id'] for cust in body['customers']] + self.assertIn(self.setup_customer_id, customers) + + @decorators.idempotent_id('ac132678-fdb6-4037-a310-813313044055') + def test_enable_customer(self): + # setup data for test case + post_body = self._get_customer_params(enabled=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + + # update enabled status from 'False' to 'True' and validate update OK + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['enabled']) + self.client.enable_customer(test_customer_id, True) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertTrue(body['enabled']) + + @decorators.idempotent_id('7dfd5f7e-7031-4ee1-b355-cd5cdeb21bd1') + def test_add_default_user(self): + # setup data for "add_default_user" test case; leave default + # and region users blank at the initial data creation + post_body = self._get_customer_params(default_users=False, + region_users=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['users']) + self.assertFalse(body["regions"][0]['users']) + + # now add a default user, then validate that new user is added to + # BOTH default user AND region user successfully + post_default_user = self._get_user_params() + new_user_id = post_default_user[0]["id"] + _, body = self.client.add_default_user(test_customer_id, + *post_default_user) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertIn(new_user_id, [x['id'] for x in body['users']]) + self.assertIn(new_user_id, [x['id'] + for x in body['regions'][0]['users']]) + + @decorators.idempotent_id('699e8487-035e-4ae0-97b4-ca51b9a08aea') + def test_delete_default_user(self): + # setup data for delete_default_user test case + post_body = self._get_customer_params() + default_user_id = post_body["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertEqual(default_user_id, body['users'][0]['id']) + self.assertIn(default_user_id, + [x['id'] for x in body['regions'][0]['users']]) + + # delete default user and validate operation success by confirming + # user id no longer in both default users and region users list + _, body = self.client.delete_default_user(test_customer_id, + default_user_id) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['users']) + self.assertNotIn(default_user_id, + [x['id'] for x in body['regions'][0]['users']]) + + @decorators.idempotent_id('48ffd49f-2b36-40b4-b1b4-0c805b7ba7c2') + def test_replace_default_user(self): + # setup data for "replace_default_user" test case; no need to + # set region user as default user will also be assigned to it + post_body = self._get_customer_params(region_users=False) + default_user_id = post_body["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertIn(default_user_id, [x['id'] for x in body['users']]) + self.assertEqual(body['users'], body['regions'][0]['users']) + + # replace default user + put_default_user = self._get_user_params(alt=True) + updated_user_id = put_default_user[0]["id"] + _, body = self.client.replace_default_user(test_customer_id, + *put_default_user) + self._wait_for_status(test_customer_id, 'Success') + + # validate that BOTH the customer default user and region user + # are replaced with the new default_user successfully + _, body = self.client.get_customer(test_customer_id) + self.assertEqual(len(body['users']), + len(body['regions'][0]['users'])) + self.assertIn(updated_user_id, [x['id'] for x in body['users']]) + self.assertEqual(body['users'], body['regions'][0]['users']) + + @decorators.idempotent_id('07a631f9-3aa5-4797-9ead-4531ced89e2a') + def test_add_region_user(self): + # We are leaving both default and region users as blank to ensure + # region user is empty on initial data creation for this test case + post_body = self._get_customer_params(region_users=False, + default_users=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + # confirm that the region users body is empty after data creation + self.assertFalse(body["regions"][0]["users"]) + + # now we shall add user to regions[0] + post_region_user = self._get_user_params() + _, body = self.client.add_region_user(test_customer_id, + CONF.identity.region, + *post_region_user) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + # validate that the region user is no longer empty after the add + self.assertTrue(customer["regions"][0]["users"]) + + @decorators.idempotent_id('9b2b3af8-2444-4143-a9e6-78c33b36c823') + def test_delete_region_user(self): + # To test delete_region_users scenario, leave default user blank + # for the test customer data, or else default user info will be + # added as region user as well + post_body = self._get_customer_params(default_users=False) + region_user_id = post_body["regions"][0]["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertTrue(body["regions"][0]["users"]) + + # delete the user from the region, then validate operation success + _, body = self.client.delete_region_user(test_customer_id, + CONF.identity.region, + region_user_id) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + # validate that the region user is now empty + self.assertFalse(customer["regions"][0]["users"]) + + @decorators.idempotent_id('0ca59977-ef29-46b9-be92-14980a12c573') + def test_replace_region_user(self): + post_body = self._get_customer_params() + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + + # update region user then confirm that update is successful + put_region_user = self._get_user_params(alt=True) + new_region_user_id = put_region_user[0]["id"] + + _, body = self.client.replace_region_user(test_customer_id, + CONF.identity.region, + *put_region_user) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + self.assertIn(new_region_user_id, [x['id'] + for x in customer["regions"][0]['users']]) + + @decorators.idempotent_id('f1444965-c711-438d-ab86-a2412acbe8e0') + def test_replace_metadata(self): + metadata = {'metadata': {'replace_key': 'replace_value'}} + _, body = self.client.replace_metadata(self.setup_customer_id, + metadata) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'metadata': 'replace_key'}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('80713a87-8e95-481f-a198-6b4515d48362') + def test_add_metadata(self): + metadata = {'metadata': {'add_key': 'add_value'}} + _, body = self.client.add_metadata(self.setup_customer_id, + metadata) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'metadata': 'add_key'}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('19a6bbe2-92b9-46be-95b7-aa0d9f247d88') + def test_add_regions(self): + region = self._get_region_params() + _, body = self.client.add_regions(self.bare_customer_id, + region) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'region': region[0]['name']}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('09a43bc1-439e-4497-a026-f2c3de451d29') + def test_delete_regions(self): + # setup data for delete_region + post_body = self._get_customer_params() + region_name = post_body["regions"][0]["name"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, customer = self.client.get_customer(test_customer_id) + self.assertTrue(customer["regions"]) + _, body = self.client.delete_region_from_customer(test_customer_id, + region_name) + self._wait_for_status(test_customer_id, 'no regions') + _, customer = self.client.get_customer(test_customer_id) + self.assertFalse(customer["regions"]) + + @decorators.idempotent_id('c7a24667-2a99-41ac-a42d-6c1163ef48af') + def test_create_customer(self): + post_body = self._get_customer_params(quota=False) + test_cust_name = post_body['name'] + _, body = self.client.create_customer(**post_body) + test_customer_id = body['customer']['id'] + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_cust_name) + self.assertIn(test_customer_id, body['uuid']) + + @decorators.idempotent_id('43785f87-27d5-408d-997f-de602caeb698') + def test_replace_customer(self): + customer = self._get_bare_customer_params() + customer['name'] = self.setup_customer['name'] + customer['regions'] = [{'name': CONF.identity.region}] + _, body = self.client.update_customer(self.setup_customer_id, + customer) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.get_customer(self.setup_customer_id) + self.assertExpected(customer, body, ['name', 'regions']) + for region in customer['regions']: + self.assertIn(region['name'], [x['name'] for x in body['regions']]) + + @decorators.idempotent_id('e8b9077a-d45c-4e24-a433-e7dfa07486b9') + def test_delete_customer(self): + # setup data for test case + post_body = self._get_customer_params() + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + + # delete the data and do get_customer to ensure 404-NotFound response + self._del_cust_validate_deletion_on_dcp_and_lcp(test_customer_id) + self.assertRaises(exceptions.NotFound, self.client.get_customer, + test_customer_id) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers_negative.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers_negative.py new file mode 100755 index 00000000..a3a43d2e --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_customers_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestCmsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestCmsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestCmsNegative, cls).setup_clients() + cls.client = cls.cmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestCmsNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestCmsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors.py new file mode 100755 index 00000000..bf09d5e6 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors.py @@ -0,0 +1,408 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from aic_orm_tempest_plugin.tests.api import fms_base +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestFms(fms_base.FmsBaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestFms, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestFms, cls).setup_clients() + cls.rms_client = cls.os_primary.rms_client + + @classmethod + def resource_setup(cls): + + # create flavor then save off flavor_id for use in test cases + body = cls._get_flavor_params() + cls.flavor = cls._create_flv_and_validate_creation_on_dcp_and_lcp( + **body) + + # these variables will be used to test list filters + cls.flavor_id = cls.flavor['id'] + cls.flavor_name = cls.flavor['name'] + cls.visibility = cls.flavor['visibility'] + cls.series = cls.flavor['series'] + cls.tenant_id = cls.tenant_id + cls.region_id = CONF.identity.region + cls.alias = cls.flavor['alias'] + cls.dflt_ex_specs = cls.flavor['extra-specs'] + super(TestTempestFms, cls).resource_setup() + + def _get_flavor_details(self, flavor_id): + _, body = self.client.get_flavor(flavor_id) + flavor = body["flavor"] + return flavor + + def _data_setup(self, post_body): + flavor = self._create_flv_and_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_flv_and_validate_deletion_on_dcp_and_lcp, + flavor["id"]) + return flavor + + def _exec_tags_function(self, flavor_id, req_json, action, para): + + if action == 'add': + _, body = self.client.add_tags(flavor_id, req_json) + + elif action == 'update': + _, body = self.client.update_tags(flavor_id, req_json) + + elif action == 'delete': + _, body = self.client.delete_tags(flavor_id, para) + + self._wait_for_flavor_status_on_dcp(flavor_id, 'Success') + + def _exec_ex_spec_function(self, flavor_id, es_body, action, para): + if action == 'add': + _, body = self.client.add_extra_specs(flavor_id, es_body) + + elif action == 'update': + _, body = self.client.update_extra_specs(flavor_id, es_body) + + elif action == 'delete': + _, body = self.client.delete_extra_specs(flavor_id, para) + + self._wait_for_flavor_status_on_dcp(flavor_id, 'Success') + + @classmethod + def resource_cleanup(cls): + # cls.delete_role_to_admin() + flavor_id = cls.flavor['id'] + cls._del_flv_and_validate_deletion_on_dcp_and_lcp(flavor_id) + super(TestTempestFms, cls).resource_cleanup() + + @decorators.idempotent_id('2a4481cd-acce-4a5d-af7c-940222a6238b') + def test_get_flavor(self): + """ execute get_flavor using flavor_id / flavor_name + """ + for para in [self.flavor_id, self.flavor_name]: + _, body = self.client.get_flavor(para) + self.assertIn(self.flavor_id, body["flavor"]["id"]) + + @decorators.idempotent_id('c46a503a-951c-4d00-afaa-46076b54db16') + def test_list_flavor_with_filters(self): + """ this function executes 'list flavors' with the following filters: + - None (no filters, i.e. list all flavors) + - alias filter + - region filter + - visibility filter + - 'contains' filter + - 'starts_with' filter + - 'series' filter + - "tenant" filter + """ + # for use by the 'constains and 'starts_with' filter + str_index1 = data_utils.rand_int_id(0, len(self.flavor_name) - 1) + str_index2 = data_utils.rand_int_id(str_index1 + 1, + len(self.flavor_name)) + + # define the list flavors filters to be used for this test + alias_filter = "?alias=%s" % self.alias + region_filter = "?region=%s" % self.region_id + visibility_filter = "?visibility=%s" % self.visibility + contains_filter = '?contains=%s' \ + % self.flavor_name[str_index1:str_index2] + startswith_filter = "?starts_with=%s" % self.flavor_name[:str_index2] + series_filter = "?series=%s" % self.series + tenant_filter = "?tenant=%s" % self.tenant_id + + for list_filter in [None, region_filter, visibility_filter, + alias_filter, contains_filter, startswith_filter, + series_filter, tenant_filter]: + _, body = self.client.list_flavors(list_filter) + flavor_ids = [flvr["id"] for flvr in body["flavors"]] + self.assertIn(self.flavor_id, flavor_ids) + + @decorators.idempotent_id('7b9d1f91-a8a4-458d-aaad-a98b5bf033b4') + def test_create_flavor(self): + post_body = self._get_flavor_params() + # call client create_flavor and wait till status equals 'Success' + _, body = self.client.create_flavor(**post_body) + flavor = body["flavor"] + test_flvr_id = flavor['id'] + self._wait_for_flavor_status_on_dcp(flavor["id"], 'Success') + + # do not forget to add this account to addCleanUp + self.addCleanup(self._del_flv_and_validate_deletion_on_dcp_and_lcp, + flavor["id"]) + + # verify flavor record created successfully + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(flavor["visibility"], "private") + self.assertEqual(flavor["regions"][0]["name"], CONF.identity.region) + self.assertEqual(flavor["status"], "Success") + + @decorators.idempotent_id('4cad10ce-67d2-4633-b347-2c16783a31b9') + def test_add_flvr_tags(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test add_tags command with two sets of key:values + add_tag_body = {"tags": {"a": "b", "c": "d"}} + self._exec_tags_function(test_flvr_id, add_tag_body, 'add', None) + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(add_tag_body.get("tags"), tag_body.get("tags")) + + @decorators.idempotent_id('db8e5c0f-0041-45d4-9939-e079296123d8') + def test_replace_flvr_tags(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params() + tags = {} + tags["a"] = "b" + tags["c"] = "d" + post_body["tag"] = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test replace_tags command + replace_tag_body = {"tags": {"e": "f", "g": "h"}} + self._exec_tags_function(test_flvr_id, replace_tag_body, + 'update', None) + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(replace_tag_body.get("tags"), + tag_body.get("tags")) + + @decorators.idempotent_id('e0a0eca6-e120-45ab-a1a4-f5b95fdf97f1') + def test_delete_flvr_tag(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params() + tags = {} + tags["e"] = "f" + tags["g"] = "h" + post_body["tag"] = test_tag_body = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test delete_tag command - delete the first key in tags body + delete_tag_key_e = "e" + self._exec_tags_function(test_flvr_id, None, 'delete', + delete_tag_key_e) + # do get_tag and confirm that resp body["tag"] now contains only + # second keypair + _, tag_body = self.client.get_tags(test_flvr_id) + test_tag_body.pop(delete_tag_key_e) + self.assertDictEqual(test_tag_body, tag_body.get("tags")) + + @decorators.idempotent_id('9c511020-53ed-4139-8c53-451cb0ea8c75') + def test_delete_all_flvr_tags(self): + # setup data for test case + post_body = self._get_flavor_params() + tags = {} + tags["i"] = "j" + tags["k"] = "l" + post_body["tag"] = test_tag_body = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # before execute test, check that "tag" body is populated correctly + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(test_tag_body, tag_body.get("tags")) + # test delete_all_tags command - run get_tag again and confirm + # that the tag dict is now empty + self._exec_tags_function(test_flvr_id, None, 'delete', None) + _, tag_body = self.client.get_tags(test_flvr_id) + # assert that tag_body contains nothing + self.assertFalse(tag_body["tags"]) + + @decorators.idempotent_id('ec74d68f-b42a-41a8-9685-ff5eca25ea0c') + def test_add_flvr_region(self): + # setup data for test case + post_body = self._get_flavor_params(set_region=False) + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + post_region_body = '{"regions": [{"name": "%s"}]}' % (self.region_id) + post_region_body = json.loads(post_region_body) + _, body = self.client.add_flvr_regions(test_flvr_id, + post_region_body) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + _, body = self.client.get_flavor(test_flvr_id) + self.assertEqual(body["flavor"]["regions"][0]["name"], + post_region_body["regions"][0]["name"]) + + @decorators.idempotent_id('5c7e6a94-89d2-4851-bf57-26371da7f47a') + def test_delete_flvr_region(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # delete the region then check to confirm flavor status + _, body = self.client.delete_flvr_region(test_flvr_id, + self.region_id) + # flavor status must show 'no regions' when it has no region assigned + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'no regions') + # flavor region is now empty after the lone region was removed + _, body = self.client.get_flavor(test_flvr_id) + self.assertTrue(len(body["flavor"]["regions"]) == 0) + + @decorators.idempotent_id('71404409-5d95-472c-8dac-b49a1c0c4b37') + def test_add_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # add a custom extra spec + add_es_body = {"os_extra_specs": {"a": "apple"}} + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', + None) + # assert extra specs add results match expected + test_dflt_ex_specs.update(add_es_body["os_extra_specs"]) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('043948fd-125b-4d96-bf40-42464066a7e1') + def test_update_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # run the test + add_es_body = {"os_extra_specs": {"a": "apple"}} + replace_es_body = {"os_extra_specs": {"a": "apple", "b": "banana"}} + + # add one custom extra spec , then replace with additional extra spec + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + # then add another custom extra spec using update_extra_spec + self._exec_ex_spec_function(test_flvr_id, replace_es_body, 'update', + None) + # assert extra specs update results match expected + test_dflt_ex_specs.update(replace_es_body["os_extra_specs"]) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('df83e2cd-d202-4b2f-8459-391a73067ec5') + def test_delete_flvr_extra_spec(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # run the test + add_es_body = {"os_extra_specs": {"g": "guava", "h": "honeydew"}} + delete_es_key_h = "h" + # add two custom extra specs then do get_extra_specs to save off + # the extra_spec add results + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + _, test_ex_specs = self.client.get_extra_specs(test_flvr_id) + # now delete one of the custom extra specs + self._exec_ex_spec_function(test_flvr_id, None, 'delete', + delete_es_key_h) + # assert extra specs update results match expected + test_ex_specs["os_extra_specs"].pop(delete_es_key_h) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_ex_specs["os_extra_specs"], + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('e3fc7ce3-c8fe-4805-8ad3-7be2c94fe7ad') + def test_delete_all_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # run the test + # add custom extra specs, then validate extra-spec add result + add_es_body = {"os_extra_specs": {"c": "carrots"}} + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + _, ex_specs_add_results = self.client.get_extra_specs(test_flvr_id) + self.assertIn("c", ex_specs_add_results["os_extra_specs"]) + self.assertEqual("carrots", + ex_specs_add_results["os_extra_specs"]["c"]) + # run delete ALL extra specs - note that this will only + # delete custom extra specs, NOT the default extra specs + self._exec_ex_spec_function(test_flvr_id, None, 'delete', None) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + # assert that flavor extra specs now contains only + # the default extra specs + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('187195b5-adfb-4c73-a2f5-42117021f5f2') + def test_add_flvr_tenant(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # check that flavor contains one tenant before testing add_flvr_tenant + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 1) + + # test add_flvr_tenant by adding one more tenant + post_tenant_body = '{"tenants": ["%s"]}' % (self.alt_tenant_id) + post_tenant_body = json.loads(post_tenant_body) + _, body = self.client.add_flvr_tenants(test_flvr_id, post_tenant_body) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + + # get flavor on same flavor id and confirm we have two tenants now + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 2) + + @decorators.idempotent_id('a7c976cd-6064-4279-ab64-2575d091cdae') + def test_delete_flvr_tenant(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params(single_tenant=False) + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # checking that flavor created contains two tenants + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 2) + + # delete one tenant, then wait until status = 'Success' + _, body = self.client.delete_flvr_tenant(test_flvr_id, + self.alt_tenant_id) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + # get flavor to confirm flavor["tenants"] now shows one tenant only + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 1) + + @decorators.idempotent_id('36958bba-673e-4397-85a9-fddb01e9ca0d') + def test_delete_flavor(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._create_flv_and_validate_creation_on_dcp_and_lcp( + **post_body) + test_flvr_id = flavor['id'] + + # delete the data and do get_flavor to ensure 404-NotFound response + self._del_flv_and_validate_deletion_on_dcp_and_lcp(test_flvr_id) + self.assertRaises(exceptions.NotFound, self.client.get_flavor, + test_flvr_id) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors_negative.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors_negative.py new file mode 100755 index 00000000..6fd4bbae --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_flavors_negative.py @@ -0,0 +1,44 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestFmsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestFmsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestFmsNegative, cls).setup_clients() + cls.client = cls.fmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestFmsNegative, cls).resource_setup() + cls.env_name = cls.env_name + cls.tenant_id = cls.tenant_id + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestFmsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images.py new file mode 100755 index 00000000..aa6d6348 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images.py @@ -0,0 +1,299 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import ims_base +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestIms(ims_base.ImsBaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestIms, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestIms, cls).setup_clients() + + @classmethod + def resource_setup(cls): + # create an image for tempest testing + cls.image_params = cls._get_image_params() + cls.image = cls._create_img_and_validate_creation_on_dcp_and_lcp( + **cls.image_params) + + # save off specific data needed for our tempest tests + cls.image_id = cls.image['id'] + cls.image_name = cls.image['name'] + # cls.visibility = cls.image['visibility'] + # cls.tenant_id = cls.image["customers"][0] + super(TestTempestIms, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls._del_img_validate_deletion_on_dcp_and_lcp(cls.image_id) + # cls.region_id) + super(TestTempestIms, cls).resource_cleanup() + + def _data_setup(self, post_body): + image = self._create_img_and_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_img_validate_deletion_on_dcp_and_lcp, + image["id"]) + # only check for Success image status if "regions" is not empty + if image["regions"]: + self._wait_for_image_status_on_dcp(image["id"], 'Success') + + return image + + @decorators.idempotent_id('2b1bb28b-4151-4e75-ae1b-d21089c3418c') + def test_get_image(self): + ''' Execute 'get_image' using the following options: + - get image by id (using cust_id parameter) + - get image by name (using cust_name parameter) + ''' + # execute get_image using image ID and iamge_name + for identifier in [self.image_id, self.image_name]: + _, body = self.client.get_image(identifier) + self.assertIn(self.image_id, body['image']['id']) + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d90') + def test_list_images_with_filters(self): + ''' this function executes 'list customer' with all available filters: + - no filter (i.e. list all images) + - filter by region + - filter by customer + ''' + + # define the list customer filters to be used for this test + no_filter = None + customer_filter = "?customer=%s" % self.tenant_id + region_filter = "?region=%s" % self.region_id + + # execute list_customers with the available filters + for list_filter in [no_filter, region_filter, + customer_filter]: + _, body = self.client.list_images(list_filter) + images = [image['id'] for image in body['images']] + self.assertIn(self.image_id, images) + + @decorators.idempotent_id('eae7ca20-5383-4579-9f73-0138b8b3ec85') + def test_list_public_images(self): + ''' list images with visibility = 'public' + ''' + # set_private = False to create image with visibility = 'public' + post_body = self._get_image_params(set_private=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + # confirm image visibility is set to "public" after image is created + self.assertEqual(image["visibility"], "public") + filter_public_images = "?visibility=%s" % image["visibility"] + + # list all public images and check if test_image_id is in the list + _, body = self.client.list_images(filter_public_images) + image_ids = [img['id'] for img in body['images']] + self.assertIn(test_image_id, image_ids) + + @decorators.idempotent_id('dc321d60-f3bd-477c-b7bf-1594626f0a12') + def test_list_private_images(self): + ''' list images with visibility = 'private' + ''' + # image data created with visibility = private set by default + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + # confirm image visibility is set to "private" after image is created + self.assertEqual(image["visibility"], "private") + filter_private_images = "?visibility=%s" % image["visibility"] + + # list all public images and check if test_image_id is in the list + _, body = self.client.list_images(filter_private_images) + image_ids = [img['id'] for img in body['images']] + self.assertIn(test_image_id, image_ids) + + @decorators.idempotent_id('4435fef4-49a9-435b-8463-cf8a1e0b7cd8') + def test_disable_image(self): + # setup data for test case - "enabled" is set to "true" by default + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + # send False to IMS client enable_image function to disable customer + self.client.enabled_image(test_image_id, False) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + _, body = self.client.get_image(test_image_id) + image = body["image"] + # assert that the image["enabled"] value is 'False' + self.assertTrue(not image['enabled']) + + @decorators.idempotent_id('f32a13e3-6f38-423b-a616-09c8d4e1c277') + def test_enable_image(self): + # setup data for test case - set_enabled is set to "False" + post_body = self._get_image_params(set_enabled=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + # send True to IMS client enable_image function to enable customer + self.client.enabled_image(test_image_id, True) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + _, body = self.client.get_image(test_image_id) + image = body["image"] + # assert that the image["enabled"] value is 'True' + self.assertTrue(image['enabled']) + + @decorators.idempotent_id('cb9e3022-00d7-4a21-bdb2-67d3cd15a4f8') + def test_add_image_region(self): + # set_region = False to skip region assignment in data setup + post_body = self._get_image_params(set_region=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + + # add region to image then check to confirm image status = "Success" + self.client.add_region_to_image(test_image_id, self.region_id) + # image status must show 'Success' when assigned to a region + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image regions array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], self.region_id) + + @decorators.idempotent_id('1be2d6fd-57b0-4acf-b895-1996f857739b') + def test_delete_image_region(self): + # setup data for test case + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + + # delete the region then check to confirm image status = "no regions" + _, body = self.client.delete_region_from_image(test_image_id, + self.region_id) + # image status must show 'no regions' when it has no region assigned + self._wait_for_image_status_on_dcp(test_image_id, 'no regions') + # image region array should be empty after the region was removed + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertFalse(image["regions"]) + + @decorators.idempotent_id('0ee68189-66a8-4213-ad68-bc12991c174a') + def test_add_image_tenant(self): + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + # add another tenant to image then check if image status = "Success" + self.client.add_customer_to_image(test_image_id, self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image tenants array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(len(image["customers"]), 2) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('bac99348-6b13-4b30-958b-3c039b27eda3') + def test_update_image_tenant(self): + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + # add another tenant to image then check if image status = "Success" + self.client.update_customer(test_image_id, self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image tenants array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(len(image["customers"]), 1) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('0506f23d-2d30-4214-9a4a-003ace86aa7d') + def test_delete_image_tenant(self): + # assign two tenants to image + post_body = self._get_image_params(single_tenant=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + # delete one tenant then check if image status = "Success" + _, body = self.client.delete_customer_from_image(test_image_id, + self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # image region array should be empty after the region was removed + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('0331e02a-ab52-4341-b676-a02462244277') + def test_create_image(self): + post_body = self._get_image_params() + # call client create_IMAGE and wait till status equals 'Success' + _, body = self.client.create_image(**post_body) + image = body["image"] + test_image_id = image["id"] + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + + # do not forget to add this account to addCleanUp + self.addCleanup(self._del_img_validate_deletion_on_dcp_and_lcp, + test_image_id) + # verify image record created successfully + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], CONF.identity.region) + + @decorators.idempotent_id('01160918-e217-401d-a6a0-e7992ab76e41') + def test_update_image(self): + region = {} + post_body = self._get_image_params(set_region=False, + set_enabled=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + + # setup region and change 'enabled' and 'customer' properties + region["name"] = self.region_id + region["type"] = "single" + region["checksum"] = "7297321c2fa6424417a548c85edd6e98" + region["virtual_size"] = "None" + region["size"] = "38797312" + post_body["regions"] = [region] + + post_body["enabled"] = True + post_body["customers"] = [self.alt_tenant_id] + _, body = self.client.update_image(test_image_id, para=None, + **post_body) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # verify image record updated successfully + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], CONF.identity.region) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + self.assertTrue(image['enabled']) + + @decorators.idempotent_id('23e2e7e2-5b19-4c66-b35c-7c686a986627') + def test_delete_image(self): + # setup data for test case + post_body = self._get_image_params() + image = self._create_img_and_validate_creation_on_dcp_and_lcp( + **post_body) + test_image_id = image['id'] + + # delete the data and do get_image to ensure 404-NotFound response + self._del_img_validate_deletion_on_dcp_and_lcp(test_image_id) + self.assertRaises(exceptions.NotFound, self.client.get_image, + test_image_id) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images_negative.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images_negative.py new file mode 100755 index 00000000..d855f7b3 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_images_negative.py @@ -0,0 +1,47 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestImsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestImsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestImsNegative, cls).setup_clients() + cls.client = cls.imsclient + cls.servers_client = cls.servers_client + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestImsNegative, cls).resource_setup() + cls.env_name = cls.env_name + cls.tenant_id = cls.tenant_id + cls.flavor_ref = CONF.compute.flavor_ref + cls.network_ref = CONF.network.public_network_id + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestImsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_region_groups.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_region_groups.py new file mode 100755 index 00000000..b7038224 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_region_groups.py @@ -0,0 +1,85 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin import data_utils as orm_data_utils +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +CONF = config.CONF + + +class TestTempestRegionGroup(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegionGroup, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegionGroup, cls).setup_clients() + cls.client = cls.os_primary.rms_client + + @classmethod + def resource_setup(cls): + cls.setup_ids = [] + cls.group_ids = [] + # create standard region + _, cls.region_1 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_1['id']) + # create region sharing region_1 properties + _, cls.region_2 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_2['id']) + + _, cls.group_1 = cls.client.create_region_group( + **orm_data_utils.rand_region_group([cls.setup_ids[0]]) + ) + cls.group_ids.append(cls.group_1['group']['id']) + _, cls.group_2 = cls.client.create_region_group( + **orm_data_utils.rand_region_group(cls.setup_ids) + ) + cls.group_ids.append(cls.group_2['group']['id']) + + super(TestTempestRegionGroup, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + for region_id in cls.setup_ids: + cls.client.delete_region(region_id) + for group_id in cls.group_ids: + cls.client.delete_region_group(group_id) + super(TestTempestRegionGroup, cls).resource_cleanup() + + @decorators.idempotent_id('0d377eb2-754d-49c1-9a4f-c7019dfe80ca') + def test_update_group(self): + id = self.group_ids[-1] + group = orm_data_utils.rand_region_group(self.setup_ids, id) + _, body = self.client.update_region_group(id, **group) + self.assertExpected(group, body['group'], ['regions']) + + @decorators.idempotent_id('b946c6c4-d601-42b9-befd-ba40992a3c53') + def test_list_groups(self): + _, body = self.client.list_region_groups() + groups = [x['id'] for x in body['groups']] + self.assertIn(self.group_1['group']['id'], groups) + self.assertIn(self.group_2['group']['id'], groups) + + @decorators.idempotent_id('9a37d966-4416-4ff3-8f3b-6847810662d7') + def test_get_group(self): + id = self.group_1['group']['id'] + _, body = self.client.get_region_group(id) + self.assertExpected(self.group_1['group'], body, ['links']) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regiongroups_negative.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regiongroups_negative.py new file mode 100755 index 00000000..e41a4a21 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regiongroups_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestRegGroupNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegGroupNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegGroupNegative, cls).setup_clients() + cls.client = cls.rmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestRegGroupNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestRegGroupNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions.py new file mode 100755 index 00000000..b5520806 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions.py @@ -0,0 +1,158 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin import data_utils as orm_data_utils +from aic_orm_tempest_plugin.tests.api import rms_base + +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +CONF = config.CONF + + +class TestTempestRegion(rms_base.RmsBaseOrmTest): + + @classmethod + def resource_setup(cls): + cls.setup_ids = [] + # create standard region + _, cls.region_1 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_1['id']) + # create region sharing region_1 properties + _, cls.region_2 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_2['id']) + # create region with differing properties + _, cls.region_3 = cls.client.create_region( + data_utils.rand_name(), + **{'status': 'down', + 'aicVersion': '3.0', + 'OSVersion': 'mitaka', + 'CLLI': '123450', + 'address': {'country': 'Mexico', 'state': 'Sonora', + 'city': 'Nogales', 'street': '12 main', + 'zip': '84000'}, + 'metadata': {'meta1': ['val1']}, + 'designType': 'large'}) + cls.setup_ids.append(cls.region_3['id']) + + super(TestTempestRegion, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + for region_id in cls.setup_ids: + cls.client.delete_region(region_id) + super(TestTempestRegion, cls).resource_cleanup() + + @decorators.idempotent_id('eecedcb0-9c96-453d-bd72-71dba26fa1c5') + def test_list_region(self): + _, body = self.client.list_regions() + regions = [x['id'] for x in body['regions']] + self.assertIn(self.region_1['id'], regions) + + @decorators.idempotent_id('0164e040-7775-4837-9ac2-aaa0f71cdfca') + def test_list_region_v1(self): + _, body = self.client.list_regions_v1() + regions = [x['id'] for x in body] + self.assertIn(self.region_1['id'], regions) + + @decorators.idempotent_id('e6c6fdfe-5fa2-45f5-8bd8-fb9cf79e99fc') + def test_list_region_with_name(self): + filter = {'regionname': self.region_1['name']} + self._list_regions_with_filter(filter, 'name') + + @decorators.idempotent_id('b3310e32-0c31-4d37-9789-cc3c4639dabe') + def test_list_region_with_osversion(self): + filter = {'osversion': self.region_1['OSVersion']} + self._list_regions_with_filter(filter, 'OSVersion') + + @decorators.idempotent_id('0b2d3e79-c14a-4527-94b0-04eeae053a80') + def test_list_region_with_status(self): + filter = {'status': self.region_1['status']} + self._list_regions_with_filter(filter, 'status') + + @decorators.idempotent_id('871be582-ecaa-4a46-a403-4d6b5e59d7de') + def test_list_region_with_aicversion(self): + filter = {'aicversion': self.region_1['aicVersion']} + self._list_regions_with_filter(filter, 'aicVersion') + + @decorators.idempotent_id('ac18be48-c787-4a65-913f-a0b0a80fbd1d') + def test_list_region_with_clli(self): + filter = {'clli': self.region_1['CLLI']} + self._list_regions_with_filter(filter, 'CLLI') + + @decorators.idempotent_id('f2b2361d-ce71-43a8-9f01-acb529835880') + def test_list_region_with_metadata(self): + filter = {'metadata': self.region_1['metadata'].keys()[0]} + self._list_regions_with_filter(filter, 'metadata') + + @decorators.idempotent_id('4533b31a-115d-466d-bf75-8ac24338c1a5') + def test_list_region_with_address(self): + filter = { + 'country': self.region_1['address']['country'], + 'city': self.region_1['address']['city'], + 'street': self.region_1['address']['street'], + 'zip': self.region_1['address']['zip'] + } + self._list_regions_with_filter(filter, 'address') + + @decorators.idempotent_id('726b8215-af10-4385-83c7-32b51502dff1') + def test_list_region_with_type(self): + filter = {'type': self.region_1['designType']} + self._list_regions_with_filter(filter, 'designType') + + @decorators.idempotent_id('4875ea70-a5a1-4b46-b752-246221670d26') + def test_list_region_with_vlcp(self): + filter = {'vlcp_name': self.region_1['vlcpName']} + self._list_regions_with_filter(filter, 'vlcpName') + + @decorators.idempotent_id('358f3cbc-4ae5-4b43-be36-6df55eae8fd9') + def test_get_region(self): + _, body = self.client.get_region(self.region_1['id']) + self.assertExpected(self.region_1, body, []) + + @decorators.idempotent_id('cefb952f-7777-4878-87d2-d78ac345f0d2') + def test_get_region_metadata(self): + _, body = self.client.get_region_metadata(self.region_1['id']) + self.assertExpected(self.region_1['metadata'], body['metadata'], []) + + @decorators.idempotent_id('b2c3baf5-22af-4bf9-bcad-b6a1a74e82d9') + def test_update_region(self): + id = self.setup_ids[-1] + region = orm_data_utils.rand_region(id) + _, body = self.client.update_region(id, **region) + self.assertExpected(region, body, []) + + @decorators.idempotent_id('0d5644d8-92bc-497c-8fc5-b57417d86e6d') + def test_update_region_status(self): + status = {} + status['status'] = orm_data_utils.rand_region_status( + [self.region_1['status']]) + _, body = self.client.update_region_status(self.region_1['id'], status) + self.assertExpected(status, body, ['links']) + + @decorators.idempotent_id('5c1a2624-6abe-49e7-82c8-30e8df1377d0') + def test_update_region_metadata(self): + metadata = {} + metadata['metadata'] = orm_data_utils.rand_region_metadata() + _, body = self.client.update_region_metadata(self.region_1['id'], + metadata) + self.assertExpected(metadata, body, []) + + def _list_regions_with_filter(self, filter, key): + _, body = self.client.list_regions(filter) + regions = [x for x in body['regions']] + self.assertTrue( + all([region[key] == self.region_1[key] for region in regions])) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions_negative.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions_negative.py new file mode 100755 index 00000000..615d3362 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/api/test_regions_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aic_orm_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestRegionsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegionsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegionsNegative, cls).setup_clients() + cls.client = cls.rmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestRegionsNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestRegionsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/scenario/__init__.py b/ranger_tempest_tests/aic-orm-tempest-plugin/aic_orm_tempest_plugin/tests/scenario/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/changed_python_files.sh b/ranger_tempest_tests/aic-orm-tempest-plugin/changed_python_files.sh new file mode 100755 index 00000000..a3aa2167 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/changed_python_files.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e +export TERM=xterm-color + +# ========================================================================= +# This script will find all the changed python files and run +# 1. flake8 +# 3. pylint +# for each of the changed file and provide the relevant output +# ========================================================================= + +TOXINIDIR=$1 +CHECKTYPE=$2 +POSTARGS=$3 +PYLINTRCFILE=${TOXINIDIR}/pylintrc +FLAKERC=${TOXINIDIR}/flake8rc + +# Find all the changed python files that needs to be checked / linted for +# ==================================================================== +# If dev is passed as {postargs} then it is indicative that the +# check will be performed in a local dev environment +# Else it is indicative of a pipeline for container build process +# ==================================================================== + +if [[ "${POSTARGS}" = "dev" ]]; then + CHANGEDFILES=($(git status -s | grep -v 'D' | egrep '\.py' | awk -F ' ' '{print $2}' || true)) + LENGTHARR=${#CHANGEDFILES[@]} +else + CHANGEDFILES=($(git diff-tree --no-commit-id --name-only --diff-filter AM -r HEAD | egrep '\.py' || true)) + LENGTHARR=${#CHANGEDFILES[@]} +fi + + +for ((i=0;i<$LENGTHARR; i++));do + echo "$(tput setaf 4)============================================================$(tput setaf 9)" + echo "$(tput setaf 5) Performing check on ${CHANGEDFILES[i]}$(tput setaf 9)" + echo "$(tput setaf 4)============================================================$(tput setaf 9)" + echo ${CHANGEDFILES[i]} + if [[ "${CHECKTYPE}" = "flake" ]]; then + flake8 --config=$FLAKERC ${CHANGEDFILES[i]} + elif [[ "${CHECKTYPE}" = "pylint" ]]; then + pylint -rn --rcfile=$PYLINTRCFILE ${CHANGEDFILES[i]} + fi +done + diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/flake8rc b/ranger_tempest_tests/aic-orm-tempest-plugin/flake8rc new file mode 100644 index 00000000..09ba5f96 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/flake8rc @@ -0,0 +1,18 @@ +[flake8] +ignore = E125,E123,E129 +show-source = False +exclude = + .git, + .venv, + .tox, + dist, + doc, + *egg, + *.pyc, + *.egg-info, + .cache, + .eggs +enable-extensions = H106,H203,H904 +import-order-style = google +filename = *.py + diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/pylintrc b/ranger_tempest_tests/aic-orm-tempest-plugin/pylintrc new file mode 100644 index 00000000..bbe5e557 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/pylintrc @@ -0,0 +1,378 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" + +disable=protected-access,fixme,too-many-branches, + attribute-defined-outside-init,too-many-locals, + too-many-arguments,too-many-statements, + too-many-return-statements,too-few-public-methods, + import-error,too-many-lines,too-many-instance-attributes, + too-many-public-methods,duplicate-code,broad-except, + redefined-builtin,missing-docstring,no-member,bad-continuation, + bad-indentation,superfluous-parens,literal-comparison,unused-import, + no-self-use,unused-variable,anomalous-backslash-in-string, + line-too-long,len-as-condition,unnecessary-pass, + # Crashes, see #743. + redefined-variable-type, + # bug in 1.7.2 https://github.com/PyCQA/pylint/issues/1493 + not-callable + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=parseable + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,75}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=__.*__ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# List of decorators that define properties, such as abc.abstractproperty. +property-classes=abc.abstractproperty + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + +# List of decorators that create context managers from functions, such as +# contextlib.contextmanager. +contextmanager-decorators=contextlib.contextmanager + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception + diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/requirements.txt b/ranger_tempest_tests/aic-orm-tempest-plugin/requirements.txt new file mode 100644 index 00000000..07141d43 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/requirements.txt @@ -0,0 +1,2 @@ +# An empty requirements file indicates the requirements are satisfied by +# openstack/tempest project's master branch on gerrit.mtn5.cci.att.com diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/setup.cfg b/ranger_tempest_tests/aic-orm-tempest-plugin/setup.cfg new file mode 100755 index 00000000..073c051c --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/setup.cfg @@ -0,0 +1,31 @@ +[metadata] +name = aic_orm_plugin +version = 0.0.1 +summary = Basic Tempest plugin for Orm with a mos tempest smoke test case +description-file = +author = +author-email = +home-page = +classifier = + Development Status :: 4 - Beta + Environment :: Console + Environment :: OpenStack + Intended Audience :: OpenStack Development Team + Intended Audience :: OpenStack QA Team + Intended Audience :: Information Technology + Operating System :: Linux + Natural Language :: English + Topic :: Software Development :: Quality Assurance + Topic :: Software Development :: Testing + Programming Language :: Python +keywords = + Plugin + Tempest + OpenStack + +[entry_points] +tempest.test_plugins = + aic_orm = aic_orm_tempest_plugin.plugin:OrmPlugin + +[pbr] +warnerrors = true diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/setup.py b/ranger_tempest_tests/aic-orm-tempest-plugin/setup.py new file mode 100755 index 00000000..b54a7ed5 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True +) diff --git a/ranger_tempest_tests/aic-orm-tempest-plugin/tox.ini b/ranger_tempest_tests/aic-orm-tempest-plugin/tox.ini new file mode 100644 index 00000000..c6da58f8 --- /dev/null +++ b/ranger_tempest_tests/aic-orm-tempest-plugin/tox.ini @@ -0,0 +1,71 @@ +[tox] +minversion = 1.6 +envlist = py27,pep8,pylint +skipdist = True + + +[testenv] +usedevelop = True +install_command = pip install {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +commands = + find . -type f -name "*.pyc" -delete +whitelist_externals = + find + + +[testenv:venv] +commands = {posargs} + + +#Linters +[testenv:pep8] +sitepackages = True +basepython = python +deps = + flake8>=2.6.2, <3.4.0 + flake8-import-order>=0.9, <0.13 +commands = + bash changed_python_files.sh {toxinidir} "flake" {posargs} +whitelist_externals = + bash + +# For use in development environment +[testenv:pep8dev] +sitepackages = True +basepython = python +skip_install = true +deps = + {[testenv:pep8]deps} +commands = + {[testenv:pep8]commands} dev +whitelist_externals = + bash + + +# At this time we are only checking for C0103(invalid-name) which in the near +# future will be expanded upon +[testenv:pylint] +sitepackages = True +basepython = python +deps = + pylint==1.7.2 +commands = + bash changed_python_files.sh {toxinidir} "pylint" {posargs} +whitelist_externals = + bash + + +# For use in development environment +[testenv:pylintdev] +sitepackages = True +basepython = python +skip_install = true +deps = + {[testenv:pylint]deps} +commands = + {[testenv:pylint]commands} dev +whitelist_externals = + bash + diff --git a/ranger_tempest_tests/changed_python_files.sh b/ranger_tempest_tests/changed_python_files.sh new file mode 100755 index 00000000..a3aa2167 --- /dev/null +++ b/ranger_tempest_tests/changed_python_files.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e +export TERM=xterm-color + +# ========================================================================= +# This script will find all the changed python files and run +# 1. flake8 +# 3. pylint +# for each of the changed file and provide the relevant output +# ========================================================================= + +TOXINIDIR=$1 +CHECKTYPE=$2 +POSTARGS=$3 +PYLINTRCFILE=${TOXINIDIR}/pylintrc +FLAKERC=${TOXINIDIR}/flake8rc + +# Find all the changed python files that needs to be checked / linted for +# ==================================================================== +# If dev is passed as {postargs} then it is indicative that the +# check will be performed in a local dev environment +# Else it is indicative of a pipeline for container build process +# ==================================================================== + +if [[ "${POSTARGS}" = "dev" ]]; then + CHANGEDFILES=($(git status -s | grep -v 'D' | egrep '\.py' | awk -F ' ' '{print $2}' || true)) + LENGTHARR=${#CHANGEDFILES[@]} +else + CHANGEDFILES=($(git diff-tree --no-commit-id --name-only --diff-filter AM -r HEAD | egrep '\.py' || true)) + LENGTHARR=${#CHANGEDFILES[@]} +fi + + +for ((i=0;i<$LENGTHARR; i++));do + echo "$(tput setaf 4)============================================================$(tput setaf 9)" + echo "$(tput setaf 5) Performing check on ${CHANGEDFILES[i]}$(tput setaf 9)" + echo "$(tput setaf 4)============================================================$(tput setaf 9)" + echo ${CHANGEDFILES[i]} + if [[ "${CHECKTYPE}" = "flake" ]]; then + flake8 --config=$FLAKERC ${CHANGEDFILES[i]} + elif [[ "${CHECKTYPE}" = "pylint" ]]; then + pylint -rn --rcfile=$PYLINTRCFILE ${CHANGEDFILES[i]} + fi +done + diff --git a/ranger_tempest_tests/flake8rc b/ranger_tempest_tests/flake8rc new file mode 100644 index 00000000..09ba5f96 --- /dev/null +++ b/ranger_tempest_tests/flake8rc @@ -0,0 +1,18 @@ +[flake8] +ignore = E125,E123,E129 +show-source = False +exclude = + .git, + .venv, + .tox, + dist, + doc, + *egg, + *.pyc, + *.egg-info, + .cache, + .eggs +enable-extensions = H106,H203,H904 +import-order-style = google +filename = *.py + diff --git a/ranger_tempest_tests/pylintrc b/ranger_tempest_tests/pylintrc new file mode 100644 index 00000000..bbe5e557 --- /dev/null +++ b/ranger_tempest_tests/pylintrc @@ -0,0 +1,378 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" + +disable=protected-access,fixme,too-many-branches, + attribute-defined-outside-init,too-many-locals, + too-many-arguments,too-many-statements, + too-many-return-statements,too-few-public-methods, + import-error,too-many-lines,too-many-instance-attributes, + too-many-public-methods,duplicate-code,broad-except, + redefined-builtin,missing-docstring,no-member,bad-continuation, + bad-indentation,superfluous-parens,literal-comparison,unused-import, + no-self-use,unused-variable,anomalous-backslash-in-string, + line-too-long,len-as-condition,unnecessary-pass, + # Crashes, see #743. + redefined-variable-type, + # bug in 1.7.2 https://github.com/PyCQA/pylint/issues/1493 + not-callable + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=parseable + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,75}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=__.*__ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# List of decorators that define properties, such as abc.abstractproperty. +property-classes=abc.abstractproperty + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + +# List of decorators that create context managers from functions, such as +# contextlib.contextmanager. +contextmanager-decorators=contextlib.contextmanager + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception + diff --git a/ranger_tempest_tests/ranger_tempest_plugin/README.rst b/ranger_tempest_tests/ranger_tempest_plugin/README.rst new file mode 100755 index 00000000..748a090e --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/README.rst @@ -0,0 +1,85 @@ +================================= +Tempest Integration of ORM +================================= + +This directory contains Tempest tests to cover the ORM project, as well +as a plugin to automatically load these tests into tempest. + +See the tempest plugin docs for information on using it: +http://docs.openstack.org/developer/tempest/plugin.html#using-plugins + +See the tempest docs for information on writing new tests etc: +http://docs.openstack.org/developer/tempest/ + +Quickstart +---------- + +#. You first need to install Tempest in a venv. If virtual environment is not + installed install it using "sudo apt-get install python-virtualenv":: + + $ virtualenv .venv + $ source .venv/bin/activate + $ cd tempest + $ pip install tox + $ pip install tempest + +#. Clone/install the plugin:: + + $ pip install -e + For example: + pip install -e /opt/stack/ranger + + + +tempest.conf file +-------------------- + +This file should be present in tempest/etc + + +Following content must be added in tempest.conf file: + + +[auth] +# Use predefined credentials instead of creating users on the fly. +use_dynamic_credentials=false +admin_username = username +admin_password = password +admin_project_name= tenant/project/customer name +test_accounts_file=/opt/stack/tempest/etc/accounts.yaml -- Provide the accurate path of the accounts.yaml file + +[oslo_concurrency] +lock_path = Provide respective path for oslo_concurrency + +[orm] +uri = Provide orm url. For exmple: http://orm.***.***.***.com +catalog_type = orm + +accounts.yaml file +------------------ + +accounts.yaml file must be added in the path tempest/etc + +Following content must be present with the given format in accounts.yaml file: + +- username: 'username1' + tenant_name: 'tenant_name1' + password: 'password1' +- username: 'username2' + tenant_name: 'tenant_name2' + password: 'password2' + + + +Running the tests +----------------- + +To run all tests from this plugin, run from the tempest repo:: + + $ tox -e all-plugin -- ranger + +To run all tempest tests including this plugin, run:: + + $ tox -e all-plugin + + diff --git a/ranger_tempest_tests/ranger_tempest_plugin/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/ranger_tempest_plugin/clients.py b/ranger_tempest_tests/ranger_tempest_plugin/clients.py new file mode 100755 index 00000000..b3147ea5 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/clients.py @@ -0,0 +1,47 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.services import base_client +from ranger_tempest_plugin.services.cms_client import CmsClient +from ranger_tempest_plugin.services.fms_client import FmsClient +from ranger_tempest_plugin.services.ims_client import ImsClient +from ranger_tempest_plugin.services.rms_client import RmsClient + +from tempest import clients +from tempest import config + +CONF = config.CONF + + +class OrmClientManager(clients.Manager): + + def __init__(self, credentials=None): + super(OrmClientManager, self).__init__(credentials) + self.cms_client = CmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.fms_client = FmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.rms_client = RmsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) + self.ims_client = ImsClient(base_client.OrmAuthProvider(credentials), + CONF.identity.catalog_type, + CONF.identity.region, + CONF.orm.uri) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/config.py b/ranger_tempest_tests/ranger_tempest_plugin/config.py new file mode 100755 index 00000000..a32e2c6d --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/config.py @@ -0,0 +1,55 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg + + +service_available_group = cfg.OptGroup( + name="service_available", + title="Available OpenStack Services" +) + +ServiceAvailableGroup = [ + cfg.BoolOpt("orm", default=False, + help="Whether or not orm is expected to be available") +] + +orm_group = cfg.OptGroup( + name="orm", + title="Orm Service option" +) + +OrmGroup = [ + cfg.StrOpt("uri", + default="orm", + help="Uri of the orm service."), + cfg.StrOpt("cms_port", + default='7080', + help="cms port of the orm url."), + cfg.StrOpt("fms_port", + default='8082', + help="fms port of the orm url."), + cfg.StrOpt("region_port", + default='8080', + help="region port of the orm url."), + cfg.BoolOpt("alt_region_available", + default=None, + help="alt_region_available of the orm alternate region."), + cfg.StrOpt("ims_port", + default='8084', + help="ims port of the orm url."), + cfg.StrOpt("image_url", + help="swift container url where image is located") +] diff --git a/ranger_tempest_tests/ranger_tempest_plugin/data_utils.py b/ranger_tempest_tests/ranger_tempest_plugin/data_utils.py new file mode 100644 index 00000000..a3916133 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/data_utils.py @@ -0,0 +1,88 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +from oslo_log import log as logging +from tempest import config +from tempest.lib.common.utils import data_utils + +LOG = logging.getLogger(__name__) +CONF = config.CONF + + +def rand_region_status(exclude=[]): + statuses = {'functional', 'maintenance', 'down', 'building'}.difference( + exclude) + return random.choice(list(statuses)) + + +def rand_region_metadata(): + metadata = {} + for i in range(random.randint(2, 10)): + metadata[data_utils.rand_name()] = [data_utils.arbitrary_string()] + return metadata + + +def rand_region(id=None): + if id is None: + id = data_utils.rand_name() + region_dict = { + 'status': rand_region_status(), + 'id': id, + 'name': id, + 'designType': data_utils.arbitrary_string(), + 'locationType': data_utils.arbitrary_string(), + 'vlcpName': data_utils.arbitrary_string(), + 'description': data_utils.arbitrary_string(), + 'rangerAgentVersion': data_utils.arbitrary_string(), + 'OSVersion': data_utils.arbitrary_string(), + 'CLLI': data_utils.arbitrary_string(), + 'address': { + 'country': data_utils.arbitrary_string(), + 'state': data_utils.arbitrary_string(), + 'city': data_utils.arbitrary_string(), + 'street': data_utils.arbitrary_string(), + 'zip': str(data_utils.rand_int_id(start=10000, end=99999)) + }, + 'metadata': { + data_utils.rand_name(): [data_utils.arbitrary_string()], + data_utils.rand_name(): [data_utils.arbitrary_string()] + }, + 'endpoints': [{ + 'publicURL': data_utils.rand_url(), + 'type': 'dashboard' + }, { + 'publicURL': data_utils.rand_url(), + 'type': 'identity' + }, { + 'publicURL': data_utils.rand_url(), + 'type': 'ord' + }] + } + return region_dict + + +def rand_region_group(region_ids, id=None): + if id is None: + id = data_utils.rand_name() + group_dict = { + 'name': id, + 'id': id, + 'description': data_utils.arbitrary_string(), + 'regions': region_ids + } + + return group_dict diff --git a/ranger_tempest_tests/ranger_tempest_plugin/plugin.py b/ranger_tempest_tests/ranger_tempest_plugin/plugin.py new file mode 100755 index 00000000..d6b3189b --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/plugin.py @@ -0,0 +1,46 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import os + +from ranger_tempest_plugin import config as project_config + +from tempest import config +from tempest.test_discover import plugins + + +class OrmPlugin(plugins.TempestPlugin): + def get_opt_lists(self): + return [( + project_config.orm_group.name, + project_config.OrmGroup)] + + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(__file__)))[0] + test_dir = "ranger_tempest_plugin/tests" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + config.register_opt_group( + conf, + project_config.service_available_group, + project_config.ServiceAvailableGroup) + config.register_opt_group( + conf, + project_config.orm_group, + project_config.OrmGroup) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/ranger_tempest_plugin-blacklist.txt b/ranger_tempest_tests/ranger_tempest_plugin/ranger_tempest_plugin-blacklist.txt new file mode 100755 index 00000000..f10624fa --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/ranger_tempest_plugin-blacklist.txt @@ -0,0 +1,6 @@ +############################################################################################### +# Blacklist ORM Tests since the tests are having issues. +# IST is looking into the same +# This will be removed once IST resolves the same [no ETA has been provided] +############################################################################################### +(?:aic_orm_tempest_plugin.*) \ No newline at end of file diff --git a/ranger_tempest_tests/ranger_tempest_plugin/schemas/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/schemas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ranger_tempest_tests/ranger_tempest_plugin/schemas/customers_schema.py b/ranger_tempest_tests/ranger_tempest_plugin/schemas/customers_schema.py new file mode 100644 index 00000000..925ac991 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/schemas/customers_schema.py @@ -0,0 +1,190 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_status = { + 'type': 'string', + 'enum': ['Success', 'no regions', 'Error', 'Pending', 'Submitted'] +} + +_links = { + 'type': 'object', + 'properties': { + 'self': {'type': 'string'} + }, + 'required': ['self'] +} + +_region = { + 'type': 'object', + 'properties': { + 'added': {'type': 'string'}, + 'id': {'type': 'string'}, + 'links': _links + }, + 'required': ['added', 'id', 'links'] +} + +_user = { + 'type': 'object', + 'properties': { + 'added': {'type': 'string', 'format': 'date-time'}, + 'id': {'type': 'string'}, + 'links': _links + }, + 'required': ['added', 'id', 'links'] +} + +_customer = { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': _links, + 'created': {'type': 'string', 'format': 'date-time'} + }, + 'required': ['id', 'links', 'created'] +} + +create_customer = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +update_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +add_regions = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': { + 'type': 'array', + 'items': _region + }, + 'transaction_id': {'type': 'string'} + }, + 'required': ['regions', 'transaction_id'] + } +} + +add_users = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'users': { + 'type': 'array', + 'items': _user + }, + 'transaction_id': {'type': 'string'} + }, + 'required': ['users', 'transaction_id'] + } +} + +replace_users = add_users + +add_metadata = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customer': _customer, + 'transaction_id': {'type': 'string'} + }, + 'required': ['customer', 'transaction_id'] + } +} + +replace_metadata = add_metadata + +enable_customer = add_metadata + +get_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'status': _status, + 'uuid': {'type': 'string'}, + 'users': {'type': 'array'}, + 'description': {'type': 'string'}, + 'enabled': {'type': 'boolean'}, + 'defaultQuotas': {'type': 'array'}, + 'name': {'type': 'string'}, + 'regions': {'type': 'array'}, + 'custId': {'type': 'string'}, + 'metadata': {'type': 'object'} + }, + 'required': ['status', 'uuid', 'users', 'description', 'enabled', + 'defaultQuotas', 'name', 'regions', 'custId', 'metadata'] + } +} + +list_customer = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'customers': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'status': _status, + 'description': {'type': 'string'}, + 'enabled': {'type': 'boolean'}, + 'num_regions': {'type': 'integer'}, + 'regions': { + 'type': 'array', + 'items': {'type': 'string'} + }, + 'id': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'required': ['status', 'description', 'enabled', + 'num_regions', 'regions', 'id', 'name'] + } + } + }, + 'required': ['customers'] + } +} + +delete_customer = { + 'status_code': [204] +} + +delete_region_from_customer = delete_customer + +delete_default_user = delete_customer + +delete_user_from_region = delete_customer diff --git a/ranger_tempest_tests/ranger_tempest_plugin/schemas/flavors_schema.py b/ranger_tempest_tests/ranger_tempest_plugin/schemas/flavors_schema.py new file mode 100644 index 00000000..e8336de0 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/schemas/flavors_schema.py @@ -0,0 +1,257 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_regions = { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "type": {"type": "string"}, + "name": {"type": "string"}, + "error_message": {"type": "string"} + } + }, + "required": ["status", "type", "name"] +} + +_extra_specs = { + "type": "object", + "additionalProperties": { + "type": "string" + } +} + +_tags = { + "type": "object", + "items": { + "type": "array", + "items": {"type": "string"} + } +} + +_tenants = { + "type": "array", + "items": { + "type": "string" + } +} + +_flavor = { + "type": "object", + "properties": { + "flavor": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "alias": {"type": "string"}, + "description": {"type": "string"}, + "tags": _tags, + "series": { + "type": "string", + "enum": ["gv", "nd", "ns", "nv", "ss"] + }, + "extra-specs": _extra_specs, + "ram": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "ephemeral": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "visibility": { + "type": "string", + "enum": ["public", "private"] + }, + "options": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "regions": _regions, + "vcpus": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "swap": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "disk": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "tenants": _tenants, + "id": {"type": "string"}, + "name": {"type": "string"} + }, + "required": ["status", "series", "extra-specs", + "ram", "ephemeral", "visibility", "vcpus", + "regions", "swap", "disk", "tenants", + "id", "name"] + } + }, + "required": ["flavor"] +} + +create_flavor = { + "status_code": [201], + "response_body": _flavor +} + +get_flavor = { + "status_code": [200], + "response_body": _flavor +} + +delete_flavor = { + "status_code": [204] +} + +list_flavors = { + "status_code": [200], + "response_body": { + "type": "object", + "properties": { + "flavors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": {"type": "string"}, + "description": {"type": "string"}, + "tags": _tags, + "series": { + "type": "string", + "enum": ["gv", "nd", "ns", "nv", "ss"] + }, + "extra-specs": _extra_specs, + "ram": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "ephemeral": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "visibiity": { + "type": "string", + "enum": ["public", "private"] + }, + "options": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "regions": _regions, + "vcpus": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "swap": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "disk": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "tenants": _tenants, + "id": {"type": "string"}, + "name": {"type": "string"} + }, + "required": ["status", "description", "series", + "extra-specs", "ram", "ephemeral", + "visibility", "vcpus", "regions", "swap", + "disk", "tenants", "id", "name"] + } + } + }, + "required": ["flavors"] + } +} + +get_extra_specs = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'os_extra_specs': _extra_specs + } + } +} + +add_extra_specs = { + "status_code": [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'os_extra_specs': _extra_specs + } + } +} + +update_extra_specs = get_extra_specs + +delete_extra_specs = { + "status_code": [204] +} + +add_tags = { + 'status_code': [201], + 'response_body': _tags +} + +delete_tags = { + "status_code": [204] +} + +get_tags = { + 'status_code': [200], + 'response_body': _tags +} + +update_tags = get_tags + +add_region = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': _regions + } + } +} + +delete_region = { + "status_code": [204] +} + +add_tenant = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'tenants': _tenants + } + } +} + +delete_tenant = { + "status_code": [204] +} diff --git a/ranger_tempest_tests/ranger_tempest_plugin/schemas/images_schema.py b/ranger_tempest_tests/ranger_tempest_plugin/schemas/images_schema.py new file mode 100644 index 00000000..600cde8a --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/schemas/images_schema.py @@ -0,0 +1,201 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_region_status = { + "type": "string", + "enum": ["Submitted", "Pending", "Success", "Error"] +} + +_regions = { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": _region_status, + "name": {"type": "string"}, + "checksum": {"type": "string"}, + "size": {"type": "string"}, + "virtual_size": {"type": "string"}, + "type": {"type": "string"}, + "error_message": {"type": "string"} + } + } +} + +_tags = { + "type": "array", + "items": { + "type": "string" + } +} + +_status = { + "type": "string", + "enum": ["Success", "no regions", "Error", "Pending"] +} + +_links = { + "type": "object", + "properties": { + "self": {"type": "string"} + }, + "required": ["self"] +} + +_image = { + "type": "object", + "properties": { + "image": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "links": { + "type": "object", + "items": { + "self": {"type": "string"} + } + }, + "locations": { + "type": "array", + "items": { + "type": "string" + } + }, + "file": {"type": "string"}, + "owner": {"type": "string"}, + "id": {"type": "string"}, + "self": {"type": "string"}, + "created-at": {"type": "string"}, + "updated-at": {"type": "string"}, + "regions": _regions, + "disk-format": {"type": "string"}, + "min-ram": { + "type": "number" + }, + "schema": {"type": "string"}, + "status": _status, + "customers": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": _tags, + "visibility": { + "type": "string", + "enum": ["public", "private"] + }, + "min-disk": { + "type": "number" + }, + "properties": { + "type": "object", + "items": { + "type": "array", + "items": {"type": "string"} + } + }, + "name": {"type": "string"}, + "url": {"type": "string"}, + "protected": { + "type": "boolean" + }, + "container-format": {"type": "string"} + }, + "required": ["links", "enabled", "locations", "file", + "status", "owner", "id", "self", "disk-format", + "min-ram", "properties", "visibility", + "regions", "schema", "min-disk", "customers", + "protected", "url", "name", "container-format"] + } + }, + "required": ["image"] +} + +create_image = { + "status_code": [201], + "response_body": _image +} + +add_tenant_to_image = { + "status_code": [201], + "response_body": _image +} + +get_image = { + "status_code": [200], + "response_body": _image +} + +update_tenant = get_image + +update_image = get_image + +delete_image = { + "status_code": [204] +} + +delete_tenant_from_image = { + "status_code": [204] +} + +add_region = { + "status_code": [200, 201], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': _regions + } + } +} + +delete_region = delete_image + +_region_names_array = { + "type": "array", + "items": { + "type": "string" + } +} + +list_images = { + "status_code": [200], + "response_body": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": _status, + "regions": _region_names_array, + "name": {"type": "string"}, + "visibiity": { + "type": "string", + "enum": ["public", "private"] + }, + "id": {"type": "string"} + }, + "required": ["status", "regions", "name", + "visibility", "id"] + } + } + }, + "required": ["images"] + } +} + +enable_image_resp = get_image diff --git a/ranger_tempest_tests/ranger_tempest_plugin/schemas/regions_schema.py b/ranger_tempest_tests/ranger_tempest_plugin/schemas/regions_schema.py new file mode 100644 index 00000000..3bcf5e47 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/schemas/regions_schema.py @@ -0,0 +1,207 @@ +# Copyright 2017 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +_metadata = { + 'type': 'object', + 'items': { + 'type': 'array', + 'items': {'type': 'string'} + } +} + +_region = { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'endpoints': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'publicURL': {'type': 'string'}, + 'type': {'type': 'string'} + }, + 'required': ['publicURL', 'type'] + } + }, + 'CLLI': {'type': 'string'}, + 'name': {'type': 'string'}, + 'description': {'type': 'string'}, + 'designType': {'type': 'string'}, + 'locationType': {'type': 'string'}, + 'vlcpName': {'type': 'string'}, + 'address': { + 'type': 'object', + 'properties': { + 'country': {'type': 'string'}, + 'state': {'type': 'string'}, + 'street': {'type': 'string'}, + 'zip': {'type': 'string'}, + 'city': {'type': 'string'}, + }, + 'required': ['country', 'state', 'street', 'zip', 'city'] + }, + 'rangerAgentVersion': {'type': 'string'}, + 'OSVersion': {'type': 'string'}, + 'id': {'type': 'string'}, + 'metadata': _metadata, + 'created': {'type': 'string', 'format': 'date-time'}, + 'modified': {'type': 'string', 'format': 'date-time'} + }, + 'required': ['status', 'endpoints', 'rangerAgentVersion', 'OSVersion', + 'CLLI', 'created', 'modified', 'metadata', 'address', + 'locationType', 'designType', 'description', 'name', 'id', + 'vlcpName'] +} + +get_region = { + 'status_code': [200], + 'response_body': _region +} + +get_region_metadata = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': {'metadata': _metadata}, + 'required': ['metadata'] + } +} + +create_region = { + 'status_code': [201], + 'response_body': _region +} + +update_region = create_region + +update_metadata = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': {'metadata': _metadata}, + 'required': ['metadata'] + } +} + +update_status = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'links': { + 'type': 'object', + 'properties': { + 'self': {'type': 'string'} + } + } + }, + 'required': ['status', 'links'] + } +} + +delete_region = { + 'status_code': [204] +} + +list_region = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'regions': { + 'type': 'array', + 'items': _region + } + }, + 'required': ['regions'] + } +} + +list_region_v1 = { + 'status_code': [200], + 'response_body': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'vLCP_name': {'type': 'string'}, + 'ORD_EP': {'type': 'string'}, + 'horizon_EP': {'type': 'string'}, + 'design_type': {'type': 'string'}, + 'rangerAgentVersion': {'type': 'string'}, + 'id': {'type': 'string'}, + 'OS_version': {'type': 'string'}, + 'keystone_EP': {'type': 'string'}, + 'zone_name': {'type': 'string'}, + 'location_type': {'type': 'string'} + } + } + } +} + +_region_group = { + 'type': 'object', + 'properties': { + 'description': {'type': 'string'}, + 'links': { + 'type': 'object', + 'properties': {'self': {'type': 'string'}} + }, + 'created': {'type': 'string', 'format': 'date-time'}, + 'modified': {'type': 'string', 'format': 'date-time'}, + 'id': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'required': ['description', 'created', 'modified', 'id', 'name'] +} + +create_region_group = { + 'status_code': [201], + 'response_body': { + 'type': 'object', + 'properties': { + 'group': _region_group + }, + 'required': ['group'] + } +} + +update_region_group = create_region_group + +get_region_group = { + 'status_code': [200], + 'response_body': _region_group +} + +list_region_groups = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'groups': { + 'type': 'array', + 'items': _region_group + } + }, + 'required': ['groups'] + } +} + +delete_region_group = { + 'status_code': [204] +} diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/services/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/base_client.py b/ranger_tempest_tests/ranger_tempest_plugin/services/base_client.py new file mode 100755 index 00000000..f6832a49 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/services/base_client.py @@ -0,0 +1,83 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from tempest import config +from tempest.lib import auth +from tempest.lib.common import rest_client + +CONF = config.CONF + + +class OrmClientBase(rest_client.RestClient): + + def get_headers(self): + headers = {'X-Auth-Region': CONF.identity.region, + 'X-RANGER-Tracking-Id': 'test', + 'X-RANGER-Requester': CONF.auth.admin_username, + 'X-RANGER-Client': 'cli', + 'Content-Type': 'application/json' + } + return headers + + def get_request(self, uri, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.get(uri, extra_headers=ex_headers) + self.expected_success(200, resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def put_request(self, uri, put_body, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.put(uri, body=put_body, extra_headers=ex_headers) + self.expected_success([200, 201], resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def delete_request(self, uri, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.delete(uri, extra_headers=ex_headers) + self.expected_success(204, resp.status) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + def post_request(self, uri, post_body, expected_body_schema): + ex_headers = self.get_headers() + resp, body = self.post(uri, body=post_body, + extra_headers=ex_headers) + self.expected_success([200, 201], resp.status) + body = json.loads(body) + self.validate_response(expected_body_schema, resp, body) + return resp, body + + +class OrmAuthProvider(auth.KeystoneV2AuthProvider): + + def __init__(self, credentials, auth_url=CONF.identity.uri): + super(OrmAuthProvider, self).__init__(credentials, auth_url) + + def auth_request(self, method, url, headers=None, body=None, filters=None): + filters = {'service': 'identity'} + auth_headers = super(OrmAuthProvider, + self).auth_request(method, + url, + filters=filters) + base_headers = auth_headers[1] + base_headers.update(headers) + auth_req = dict(url=url, headers=base_headers, body=body) + return auth_req['url'], auth_req['headers'], auth_req['body'] diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/cms_client.py b/ranger_tempest_tests/ranger_tempest_plugin/services/cms_client.py new file mode 100755 index 00000000..a2bb62d1 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/services/cms_client.py @@ -0,0 +1,129 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import urllib + +from ranger_tempest_plugin.schemas import customers_schema as schema +from ranger_tempest_plugin.services import base_client + +from tempest import config + +CONF = config.CONF + + +class CmsClient(base_client.OrmClientBase): + + cms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.cms_port) + version = 'v1' + + # POST + + def create_customer(self, **kwargs): + uri = '%s/%s/orm/customers' % (self.cms_url, self.version) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.create_customer) + + def add_default_user(self, customer_id, *args): + uri = '%s/%s/orm/customers/%s/users' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(args) + return self.post_request(uri, post_body, schema.add_users) + + def add_regions(self, customer_id, regions): + uri = '%s/%s/orm/customers/%s/regions' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(regions) + return self.post_request(uri, post_body, schema.add_regions) + + def add_region_user(self, customer_id, region_id, *args): + uri = '%s/%s/orm/customers/%s/regions/%s/users' \ + % (self.cms_url, self.version, customer_id, region_id) + post_body = json.dumps(args) + return self.post_request(uri, post_body, schema.add_users) + + def add_metadata(self, customer_id, metadata): + uri = '%s/%s/orm/customers/%s/metadata' \ + % (self.cms_url, self.version, customer_id) + post_body = json.dumps(metadata) + return self.post_request(uri, post_body, schema.add_metadata) + + # PUT + + def update_customer(self, customer_id, customer): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(customer) + return self.put_request(uri, put_body, schema.update_customer) + + def enable_customer(self, customer_id, value): + uri = '%s/%s/orm/customers/%s/enabled' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps({'enabled': value}) + return self.put_request(uri, put_body, schema.enable_customer) + + def replace_default_user(self, customer_id, *args): + uri = '%s/%s/orm/customers/%s/users' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(args) + return self.put_request(uri, put_body, schema.replace_users) + + def replace_region_user(self, customer_id, region_id, *args): + uri = '%s/%s/orm/customers/%s/regions/%s/users' \ + % (self.cms_url, self.version, customer_id, region_id) + + put_body = json.dumps(args) + return self.put_request(uri, put_body, schema.replace_users) + + def replace_metadata(self, customer_id, metadata): + uri = '%s/%s/orm/customers/%s/metadata' \ + % (self.cms_url, self.version, customer_id) + put_body = json.dumps(metadata) + return self.put_request(uri, put_body, schema.replace_metadata) + + # GET + + def get_customer(self, identifier): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, identifier) + return self.get_request(uri, schema.get_customer) + + def list_customers(self, filter=None): + uri = '%s/%s/orm/customers' % (self.cms_url, self.version) + if filter is not None: + uri += '?' + urllib.urlencode(filter) + return self.get_request(uri, schema.list_customer) + + # DELETE + + def delete_region_from_customer(self, customer_id, region_id): + uri = '%s/%s/orm/customers/%s/regions/%s' % ( + self.cms_url, self.version, customer_id, region_id) + return self.delete_request(uri, schema.delete_region_from_customer) + + def delete_customer(self, customer_id): + uri = '%s/%s/orm/customers/%s' \ + % (self.cms_url, self.version, customer_id) + return self.delete_request(uri, schema.delete_customer) + + def delete_default_user(self, customer_id, user_id): + uri = '%s/%s/orm/customers/%s/users/%s' \ + % (self.cms_url, self.version, customer_id, user_id) + return self.delete_request(uri, schema.delete_default_user) + + def delete_region_user(self, customer_id, region_id, user_id): + uri = '%s/%s/orm/customers/%s/regions/%s/users/%s' \ + % (self.cms_url, self.version, customer_id, region_id, user_id) + return self.delete_request(uri, schema.delete_user_from_region) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/fms_client.py b/ranger_tempest_tests/ranger_tempest_plugin/services/fms_client.py new file mode 100755 index 00000000..26779ef2 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/services/fms_client.py @@ -0,0 +1,169 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from ranger_tempest_plugin.schemas import flavors_schema as schema +from ranger_tempest_plugin.services import base_client + +from tempest import config +from tempest.lib.common import rest_client + +CONF = config.CONF + + +class FmsClient(base_client.OrmClientBase): + + fms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.fms_port) + version = "v1" + + def get_extra_headers(self): + headers = {'X-Auth-Region': CONF.identity.region, + 'X-RANGER-Tracking-Id': 'test', + 'X-RANGER-Requester': CONF.auth.admin_username, + 'X-RANGER-Client': 'cli' + } + return headers + + def create_flavor(self, **kwargs): + uri = '%s/%s/orm/flavors' % (self.fms_url, self.version) + post_body = {"flavor": kwargs} + post_body = json.dumps(post_body) + # ex_headers = self.get_headers() + # resp, body = self.post(uri, body=post_body, + # extra_headers=ex_headers) + # body = json.loads(body) + # self.validate_response(schema.create_flavor, resp, body) + # return rest_client.ResponseBody(resp, body["flavor"]) + return self.post_request(uri, post_body, schema.create_flavor) + + def get_flavor(self, identifier, para=None): + if para is None: + uri = '%s/%s/orm/flavors/%s' % (self.fms_url, self.version, + identifier) + else: + uri = '%s/%s/orm/flavors/%s/%s' % (self.fms_url, self.version, + identifier, para) + return self.get_request(uri, schema.get_flavor) + + def list_flavors(self, para=None): + if para is None: + uri = '%s/%s/orm/flavors' % (self.fms_url, self.version) + else: + uri = '%s/%s/orm/flavors/%s' % (self.fms_url, self.version, para) + # ex_headers = self.get_headers() + # resp, body = self.get(url, extra_headers=ex_headers) + # self.expected_success(200, resp.status) + # body = json.loads(body) + # self.validate_response(schema.list_flavors, resp, body) + # return rest_client.ResponseBody(resp, body) + return self.get_request(uri, schema.list_flavors) + + def delete_region_from_flavor(self, flavor_id, region_id): + uri = '%s/%s/orm/flavors/%s/regions/%s' % (self.fms_url, + self.version, flavor_id, + region_id) + ex_headers = self.get_headers() + resp, body = self.delete(uri, extra_headers=ex_headers) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) + + def delete_flavor(self, flavor_id): + uri = '%s/%s/orm/flavors/%s' %\ + (self.fms_url, self.version, flavor_id) + return self.delete_request(uri, schema.delete_flavor) + + def delete_tags(self, flavor_id, para): + if para is None: + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + else: + uri = '%s/%s/orm/flavors/%s/tags/%s' % (self.fms_url, self.version, + flavor_id, para) + return self.delete_request(uri, schema.delete_tags) + + def get_tags(self, flavor_id): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + return self.get_request(uri, schema.get_tags) + + def add_tags(self, flavor_id, tag_body): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + post_body = json.dumps(tag_body) + return self.post_request(uri, post_body, schema.add_tags) + + def update_tags(self, flavor_id, tag_body): + uri = '%s/%s/orm/flavors/%s/tags' % (self.fms_url, self.version, + flavor_id) + put_body = json.dumps(tag_body) + return self.put_request(uri, put_body, schema.update_tags) + + def get_extra_specs(self, flavor_id): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + return self.get_request(uri, schema.get_extra_specs) + + def add_flvr_tenants(self, flavor_id, tenant_body): + uri = '%s/%s/orm/flavors/%s/tenants/' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(tenant_body) + return self.post_request(uri, post_body, schema.add_tenant) + + def add_flvr_regions(self, flavor_id, region_body): + uri = '%s/%s/orm/flavors/%s/regions' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(region_body) + return self.post_request(uri, post_body, schema.add_region) + + def delete_flvr_region(self, flavor_id, region_id): + uri = '%s/%s/orm/flavors/%s/regions/%s' % (self.fms_url, + self.version, + flavor_id, region_id) + return self.delete_request(uri, schema.delete_region) + + def add_extra_specs(self, flavor_id, extra_specs_body): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + post_body = json.dumps(extra_specs_body) + return self.post_request(uri, post_body, schema.add_extra_specs) + + def update_extra_specs(self, flavor_id, extra_specs_body): + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + put_body = json.dumps(extra_specs_body) + return self.put_request(uri, put_body, schema.update_extra_specs) + + def delete_extra_specs(self, flavor_id, para): + if para is None: + uri = '%s/%s/orm/flavors/%s/os_extra_specs' % (self.fms_url, + self.version, + flavor_id) + else: + uri = '%s/%s/orm/flavors/%s/os_extra_specs/%s' % (self.fms_url, + self.version, + flavor_id, para) + return self.delete_request(uri, schema.delete_extra_specs) + + def delete_flvr_tenant(self, flavor_id, tenant): + uri = '%s/%s/orm/flavors/%s/tenants/%s' % (self.fms_url, + self.version, + flavor_id, tenant) + return self.delete_request(uri, schema.delete_tenant) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/ims_client.py b/ranger_tempest_tests/ranger_tempest_plugin/services/ims_client.py new file mode 100755 index 00000000..75537629 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/services/ims_client.py @@ -0,0 +1,112 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from ranger_tempest_plugin.schemas import images_schema as schema +from ranger_tempest_plugin.services import base_client +from tempest import config + +CONF = config.CONF + + +class ImsClient(base_client.OrmClientBase): + + ims_url = '%s:%s' % (CONF.orm.uri, CONF.orm.ims_port) + version = "v1" + + def get_headers(self): + headers = {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Region': CONF.identity.region, + 'X-RANGER-Tracking-Id': 'test', + 'X-RANGER-Requester': CONF.auth.admin_username, + 'X-RANGER-Client': 'cli' + } + return headers + + def create_image(self, **kwargs): + uri = '%s/%s/orm/images' % (self.ims_url, self.version) + post_body = {"image": kwargs} + post_body = json.dumps(post_body) + return self.post_request(uri, post_body, schema.create_image) + + def update_image(self, image_id, para=None, **kwargs): + if para is None: + uri = '%s/%s/orm/images/%s' % ( + self.ims_url, self.version, image_id) + else: + uri = '%s/%s/orm/images/%s/%s' % ( + self.ims_url, self.version, image_id, para) + put_body = {"image": kwargs} + put_body = json.dumps(put_body) + return self.put_request(uri, put_body, schema.update_image) + + def get_image(self, identifier, para=None): + if para is None: + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, + identifier) + else: + uri = '%s/%s/orm/images/%s/%s' % (self.ims_url, self.version, + identifier, para) + return self.get_request(uri, schema.get_image) + + def list_images(self, para=None): + if para is None: + uri = '%s/%s/orm/images' % (self.ims_url, self.version) + else: + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, para) + return self.get_request(uri, schema.list_images) + + def enabled_image(self, image_id, bool): + uri = '%s/%s/orm/images/%s/enabled' \ + % (self.ims_url, self.version, image_id) + put_body = json.dumps({'enabled': bool}) + return self.put_request(uri, put_body, schema.enable_image_resp) + + def add_region_to_image(self, image_id, region_id): + uri = '%s/%s/orm/images/%s/regions/' % (self.ims_url, + self.version, image_id) + post_body = json.dumps({"regions": [{"name": region_id}]}) + return self.post_request(uri, post_body, schema.add_region) + + def delete_region_from_image(self, image_id, region_id): + uri = '%s/%s/orm/images/%s/regions/%s' % (self.ims_url, + self.version, image_id, + region_id) + return self.delete_request(uri, schema.delete_region) + + def delete_image(self, image_id): + uri = '%s/%s/orm/images/%s' % (self.ims_url, self.version, image_id) + return self.delete_request(uri, schema.delete_image) + + def add_customer_to_image(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers' % ( + self.ims_url, self.version, image_id) + post_body = json.dumps({"customers": [tenant_id]}) + return self.post_request(uri, post_body, schema.add_tenant_to_image) + + def update_customer(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers' % (self.ims_url, self.version, + image_id) + put_body = json.dumps({"customers": [tenant_id]}) + return self.put_request(uri, put_body, schema.update_tenant) + + def delete_customer_from_image(self, image_id, tenant_id): + uri = '%s/%s/orm/images/%s/customers/%s' % (self.ims_url, + self.version, + image_id, + tenant_id) + return self.delete_request(uri, schema.delete_tenant_from_image) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/services/rms_client.py b/ranger_tempest_tests/ranger_tempest_plugin/services/rms_client.py new file mode 100755 index 00000000..55096fba --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/services/rms_client.py @@ -0,0 +1,159 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import urllib + +from ranger_tempest_plugin.schemas import regions_schema as schema +from ranger_tempest_plugin.services import base_client + +from tempest import config + + +CONF = config.CONF + + +class RmsClient(base_client.OrmClientBase): + + rms_url = '%s:%s' % (CONF.orm.uri, CONF.orm.region_port) + version = "v2" + + def create_region(self, region_id, **kwargs): + uri = '%s/%s/orm/regions' % (self.rms_url, self.version) + post_body = { + 'status': 'functional', + 'name': region_id, + 'id': region_id, + 'description': region_id, + 'designType': 'compact', + 'locationType': 'testlocation', + 'vlcpName': 'testvlcp', + 'address': { + 'country': 'usa', + 'state': 'tx', + 'city': 'austin', + 'street': '12 main', + 'zip': '12345' + }, + 'metadata': { + 'key': ["value"] + }, + 'endpoints': [ + { + 'publicURL': + 'https://dashboard-ranger.%s.com' % region_id, + 'type': 'dashboard' + }, + { + 'publicURL': + 'https://identity-ranger.%s.com:5000' % region_id, + 'type': 'identity' + }, + { + 'publicURL': + 'https://ranger-agent.%s.com:9010' % region_id, + 'type': 'ord' + }, + + ], + 'rangerAgentVersion': '3.6', + 'OSVersion': 'kilo', + 'CLLI': 'testclli' + } + if kwargs is not None: + for key in kwargs: + post_body[key] = kwargs[key] + + post_body = json.dumps(post_body) + return self.post_request(uri, post_body, schema.create_region) + + def update_region(self, region_id, **kwargs): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, region_id) + put_body = json.dumps(kwargs) + return self.put_request(uri, put_body, schema.update_region) + + def update_region_status(self, region_id, status): + uri = '%s/%s/orm/regions/%s/status' \ + % (self.rms_url, self.version, region_id) + put_body = json.dumps(status) + return self.put_request(uri, put_body, schema.update_status) + + def update_region_metadata(self, region_id, metadata): + uri = '%s/%s/orm/regions/%s/metadata' \ + % (self.rms_url, self.version, region_id) + put_body = json.dumps(metadata) + return self.put_request(uri, put_body, schema.update_metadata) + + def get_region(self, identifier): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region) + + def get_region_metadata(self, identifier): + uri = '%s/%s/orm/regions/%s/metadata'\ + % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region_metadata) + + def list_regions_v1(self): + uri = self.rms_url + '/lcp' + return self.get_request(uri, schema.list_region_v1) + + def list_regions(self, filter=None): + uri = '%s/%s/orm/regions' % (self.rms_url, self.version) + if filter is not None: + uri += '?' + urllib.urlencode(filter) + return self.get_request(uri, schema.list_region) + + def delete_region(self, region_id): + uri = '%s/%s/orm/regions/%s' % (self.rms_url, self.version, region_id) + return self.delete_request(uri, schema.delete_region) + + def add_region_metadata(self, region_id, **kwargs): + uri = '%s/%s/orm/regions/%s/metadata'\ + % (self.rms_url, self.version, region_id) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.update_metadata) +# +# def delete_region_metadata(self, region_id, key): +# uri = '%s/%s/orm/regions/%s/metadata/%s' % ( +# self.rms_url, self.version, region_id, key) +# ex_headers = self.get_headers() +# resp, body = self.delete(uri, extra_headers=ex_headers) +# self.expected_success(200, resp.status) +# body = json.loads(body) +# return rest_client.ResponseBody(resp, body) + + def create_region_group(self, **kwargs): + uri = '%s/%s/orm/groups' % (self.rms_url, self.version) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.create_region_group) + + def update_region_group(self, group_id, **kwargs): + uri = '%s/%s/orm/groups/%s' % (self.rms_url, self.version, group_id) + put_body = json.dumps(kwargs) + return self.put_request(uri, put_body, schema.update_region_group) + + def get_region_group(self, identifier): + uri = '%s/%s/orm/groups/%s'\ + % (self.rms_url, self.version, identifier) + return self.get_request(uri, schema.get_region_group) + + def list_region_groups(self): + uri = '%s/%s/orm/groups' % (self.rms_url, self.version) + return self.get_request(uri, schema.list_region_groups) + + def delete_region_group(self, region_group_id): + uri = '%s/%s/orm/groups/%s' % (self.rms_url, self.version, + region_group_id) + return self.delete_request(uri, schema.delete_region_group) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/base.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/base.py new file mode 100755 index 00000000..9427fbf5 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/base.py @@ -0,0 +1,52 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin import clients + +from oslo_log import log as logging +import six +from tempest import config +from tempest import test + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class BaseOrmTest(test.BaseTestCase): + + credentials = ['admin', 'primary', 'alt'] + + client_manager = clients.OrmClientManager + + build_timeout = 120 + build_interval = 10 + + @classmethod + def setup_clients(cls): + super(BaseOrmTest, cls).setup_clients() + cls.identity_client = cls.os_admin.tenants_client + + @classmethod + def skip_checks(cls): + super(BaseOrmTest, cls).skip_checks() + if not CONF.service_available.orm: + skip_msg = ("%s skipped as orm is not available" % cls.__name__) + raise cls.skipException(skip_msg) + + def assertExpected(self, expected, actual, excluded_keys): + for key, value in six.iteritems(expected): + if key not in excluded_keys: + self.assertIn(key, actual) + self.assertEqual(value, actual[key], key) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/cms_base.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/cms_base.py new file mode 100755 index 00000000..1b4c8fe1 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/cms_base.py @@ -0,0 +1,326 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +import time + +from ranger_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.common.utils import data_utils + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class CmsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + @classmethod + def setup_clients(cls): + super(CmsBaseOrmTest, cls).setup_clients() + cls.client = cls.os_primary.cms_client + + @classmethod + def _get_quota(cls): + compute, storage, network, quota = {}, {}, {}, {} + compute["instances"] = "10" + compute["injected-files"] = "10" + compute["key-pairs"] = "10" + compute["ram"] = "10" + compute["vcpus"] = "51" + compute["metadata-items"] = "34" + compute["injected-file-content-bytes"] = "25" + storage["gigabytes"] = "10" + storage["snapshots"] = "10" + storage["volumes"] = "10" + network["floating-ips"] = "10" + network["networks"] = "10" + network["ports"] = "10" + network["routers"] = "10" + network["subnets"] = "10" + network["security-group-rules"] = "51" + network["security-groups"] = "50" + quota['compute'] = [compute] + quota['storage'] = [storage] + quota['network'] = [network] + return quota + + @classmethod + def _get_additional_quota_for_cust(cls): + quota = cls._get_quota() + quota["compute"][0]["floating-ips"] = "10" + quota["compute"][0]["fixed-ips"] = "10" + quota["compute"][0]["injected-file-path-bytes"] = "34" + quota["compute"][0]["server-groups"] = "10" + quota["compute"][0]["server-group-members"] = "34" + quota["network"][0]["health-monitor"] = "10" + quota["network"][0]["member"] = "10" + quota["network"][0]["nat-instance"] = "10" + quota["network"][0]["pool"] = "10" + quota["network"][0]["route-table"] = "10" + quota["network"][0]["vip"] = "10" + return quota + + @classmethod + def _get_customer_params(cls, quota=None, enabled=True, region_users=True, + default_users=True): + region, user, metadata, payload = {}, {}, {}, {} + cust_name = data_utils.rand_name('ormTempestCms') + if not quota: + quota = cls._get_quota() + region['name'] = CONF.identity.region + region['type'] = 'single' + region['quotas'] = [quota] + user['id'] = cls.os_primary.credentials.username + user['role'] = ["admin"] + region["users"] = [user] if region_users else [] + regions = [region] + metadata['my_server_name'] = cust_name + metadata['ocx_cust'] = random.randint(0, 999999999) + payload["description"] = cust_name + payload["enabled"] = True if enabled else False + payload["name"] = cust_name + payload['metadata'] = metadata + payload["regions"] = regions + payload["defaultQuotas"] = [quota] + payload['users'] = [user] if default_users else [] + return payload + + @classmethod + def _get_bare_customer_params(cls): + customer = {} + customer['description'] = '' + customer['enabled'] = True + customer['name'] = data_utils.rand_name('ormTempestCms') + customer['regions'] = [] + customer['defaultQuotas'] = [] + customer['users'] = [] + return customer + + @classmethod + def _get_user_params(cls, alt=False): + users = [] + if not alt: + users.append({'id': cls.os_primary.credentials.username, + 'role': ['admin']}) + else: + users.append({'id': cls.os_alt.credentials.username, + 'role': ['admin_viewer', 'admin_support']}) + return users + + @classmethod + def _get_region_params(cls): + quota = cls._get_quota() + region = {} + region['name'] = CONF.identity.region + region['type'] = 'single' + region['quotas'] = [quota] + return [region] + + @classmethod + def _create_cust_validate_creation_on_dcp_and_lcp(self, **kwargs): + """ Creates a customer record: kwargs contains field data needed for + customer POST body: + - name + - description + - enabled + - metadata + - regions + - defaultQuotas + - ephemeral + - regions + - visibility + - tenants + """ + _, body = self.client.create_customer(**kwargs) + customer_id = body["customer"]["id"] + _, customer = self.client.get_customer(customer_id) + if customer["name"] == kwargs["name"]: + if customer["regions"] == []: + customer_status = "no regions" + else: + customer_status = "Success" + + self._wait_for_status(customer_id, customer_status) + return customer_id + else: + message = "customer %s not created successfully" % kwargs["name"] + exceptions.TempestException(message) + + @classmethod + def _wait_for_status(cls, customer_id, status): + customer_status = cls.client.get_customer(customer_id)[1]["status"] + start = int(time.time()) + while customer_status != status: + time.sleep(cls.build_interval) + customer_status = cls.client.get_customer(customer_id)[1]["status"] + if customer_status == 'Error': + message = ('customer %s failed to reach %s status' + ' and is in ERROR status on orm' % + (customer_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('customer %s failed to reach %s' + 'status within the required time (%s s)' + 'on orm and is in %s status.' + % (customer_id, status, + cls.build_timeout, + customer_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_cust_quota_on_lcp(cls, quota, cust_id): + expected_quota_count = len(quota["compute"][0]) +\ + len(quota["storage"][0]) +\ + len(quota["network"][0]) + actual_quota_count = 0 + body = cls.nova_quotas_client.show_quota_set(cust_id) + for param in quota["compute"][0]: + if param in body["quota_set"]: + if (quota["compute"][0][param] == + str(body["quota_set"][param])): + actual_quota_count += 1 + body = cls.volume_quotas_client.show_quota_set(cust_id) + for param in quota["storage"][0]: + if param in body["quota_set"]: + if str(body["quota_set"][param]) == quota["compute"][0][param]: + actual_quota_count += 1 + body = cls.networks_quotas_client.show_quotas(cust_id) + for param in quota["network"][0]: + if param in body["quota_set"]: + if (quota["compute"][0][param] == + str(body["quota_set"][param])): + actual_quota_count += 1 + if expected_quota_count == actual_quota_count: + return True + else: + return False + + @classmethod + def _validate_users_on_cust_on_dcp_and_lcp(cls, post_body, cust_id): + default_users_req, region_users_req, \ + default_users_dcp, region_users_dcp, \ + users_lcp = [], [], [], [], [] + for user in post_body["regions"][0]["users"]: + region_users_req.append(user["id"]) + for user in post_body["users"]: + default_users_req.append(user["id"]) + expected_users_count = len(region_users_req) + len(default_users_req) + actual_users_count = 0 + lcp_body = cls.identity_client.list_tenant_users(cust_id) + for user in lcp_body["users"]: + users_lcp.append(user["id"]) + dcp_body = cls.client.get_customer(cust_id) + for user in dcp_body["regions"][0]["users"]: + region_users_dcp.append(user["id"]) + for user in dcp_body["users"]: + default_users_dcp.append(user["id"]) + for user in default_users_req: + if (user in users_lcp) and (user in default_users_dcp): + actual_users_count += 1 + for user in region_users_req: + if (user in users_lcp) and (user in region_users_dcp): + actual_users_count += 1 + if expected_users_count == actual_users_count: + return True + else: + return False + + @classmethod + def _del_cust_validate_deletion_on_dcp_and_lcp(cls, customer_id): + _, customer = cls.client.get_customer(customer_id) + regions_on_customer = [region for region in customer["regions"]] + if regions_on_customer: + region_name_on_customer = regions_on_customer[0]["name"] + cls._delete_region_from_customer_and_validate_deletion( + customer_id, region_name_on_customer) + cls.client.delete_customer(customer_id) + cls._wait_for_customer_deletion_on_dcp(customer_id) + cls._validate_customer_deletion_on_lcp(customer_id) + + @classmethod + def _delete_region_from_customer_and_validate_deletion( + cls, customer_id, rname): + _, region = cls.os_admin.rms_client.get_region(rname) + region_id = region["id"] + cls.client.delete_region_from_customer(customer_id, region_id) + # cls._wait_for_cust_status_on_dcp(customer_id, "no regions") + cls._wait_for_status(customer_id, "no regions") + _, body = cls.client.get_customer(customer_id) + regions_on_customer = [rgn for rgn in body["regions"]] + if regions_on_customer: + message = "Region %s failed to get deleted from customer %s " % ( + rname, customer_id) + raise exceptions.TempestException(message) + cls._validate_customer_deletion_on_lcp(customer_id) + + @classmethod + def _wait_for_customer_deletion_on_dcp(cls, customer_id): + _, body = cls.client.list_customers() + customer_list = body["customers"] + customer_ids = [customer["id"] + for customer in customer_list + if customer["id"] == customer_id] + start = int(time.time()) + while customer_ids: + time.sleep(cls.build_interval) + _, body = cls.client.list_customers()["customers"] + customer_list = body["customers"] + customer_ids = [customer["id"] + for customer in customer_list + if customer["id"] == customer_id] + if customer_ids: + customer_status = customer_ids[0]["status"] + if customer_status == 'Error': + message = "customer %s failed to get deleted and is in\ + error status" % customer_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ( + 'customer %s failed to get deleted within ' + 'the required time (%s s) and is in %s status.' + % (customer_id, cls.build_timeout, + customer_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_customer_deletion_on_lcp(cls, customer_id): + body = cls.identity_client.list_tenants()["tenants"] + customer_ids = [customer["id"] + for customer in body + if customer["id"] == customer_id] + if customer_ids: + message = "customer %s failed to get deleted on lcp" \ + % customer_id + raise exceptions.TempestException(message) + + @classmethod + def _update_cust_and_validate_status_on_dcp_and_lcp( + cls, customer_id, para, **kwargs): + body = cls.client.update_customer(customer_id, para, **kwargs) + if body["id"] == customer_id: + cls._wait_for_cust_status_on_dcp(customer_id, "Success") + cls._validate_cust_creation_on_lcp(customer_id) + else: + message = "customer %s not updated successfully" % customer_id + raise exceptions.TempestException(message) + return body diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/fms_base.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/fms_base.py new file mode 100755 index 00000000..b627f801 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/fms_base.py @@ -0,0 +1,261 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +import time + +from ranger_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class FmsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + # added setup_clients function by stew + @classmethod + def setup_clients(cls): + super(FmsBaseOrmTest, cls).setup_clients() + cls.client = cls.os_primary.fms_client + cls.flavors_client = cls.os_admin.flavors_client + cls.tenant_id = cls._get_tenant_id( + cls.os_primary.credentials.tenant_name) + cls.alt_tenant_id = cls._get_tenant_id( + cls.os_alt.credentials.tenant_name) + + @classmethod + def _get_flavor_params(cls, set_region=True, single_tenant=True): + post_body, region = {}, {} + region["name"] = CONF.identity.region + ram = random.randint(1, 4) * 1024 + swap = random.randint(1, 40) * 1024 + vcpus = random.randint(2, 46) + disk = random.randint(2, 102) + post_body["description"] = \ + "orm-plugin-BaseORMTest-flavor" + post_body["series"] = random.choice(["ns", "nd", "gv", "nv"]) + post_body["alias"] = "flavor_alias" + post_body["ram"] = str(ram) + post_body["vcpus"] = str(vcpus) + post_body["disk"] = str(disk) + post_body["swap"] = str(swap) + post_body["ephemeral"] = "1024" + post_body["regions"] = [region] if set_region else [] + post_body["visibility"] = "private" + + if single_tenant: + post_body["tenants"] = [cls.tenant_id] + else: + post_body["tenants"] = [cls.tenant_id, cls.alt_tenant_id] + return post_body + + @classmethod + def _create_flv_and_validate_creation_on_dcp_and_lcp(cls, **kwargs): + """ kwargs contain all field data needed in a flavor POST body: + - name + - description + - alias + - ram + - vcpus + - disk + - swap + - ephemeral + - regions + - visibility + - tenants + """ + _, body = cls.client.create_flavor(**kwargs) + flavor = body["flavor"] + flavor_id = flavor["id"] + _, body = cls.client.get_flavor(flavor_id) + flavor_detail = body["flavor"] + if flavor_detail["vcpus"] == kwargs["vcpus"]: + if flavor_detail["regions"] == []: + flavor_status = "no regions" + else: + flavor_status = "Success" + flavor_id = flavor_detail["id"] + cls._wait_for_flavor_status_on_dcp(flavor_id, flavor_status) + cls._validate_flavor_creation_on_lcp(flavor_id) + return flavor + else: + message = "flavor %s not created successfully" % flavor_id + raise exceptions.TempestException(message) + + @classmethod + def _wait_for_flavor_status_on_dcp(cls, flavor_id, status): + _, body = cls.client.get_flavor(flavor_id) + flavor = body["flavor"] + flavor_status = flavor["status"] + start = int(time.time()) + while flavor_status != status: + time.sleep(cls.build_interval) + _, body = cls.client.get_flavor(flavor_id) + flavor = body["flavor"] + flavor_status = flavor["status"] + if flavor_status == 'Error': + message = ('flavor %s failed to reach %s status' + ' and is in ERROR status on orm' % + (flavor_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message =\ + 'flavor %s failed to reach %s status within' + ' the required time (%s s) on orm and is in' + '%s status.' % (flavor_id, status, + cls.build_timeout, flavor_status) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_flavor_creation_on_lcp(cls, flavor_id): + _, body = cls.client.list_flavors() + flavor = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + if not flavor: + message = "flavor %s not in nova flavor list" % flavor_id + raise exceptions.TempestException(message) + + @classmethod + def _validate_flv_extraspecs_on_dcp_and_lcp(cls, flavor_id, + expected_specs): + expected_specs_count = len(expected_specs) + _, body = cls.client.get_flavor(flavor_id) + flavor_orm = body["flavor"] + flavor_lcp = cls.flavors_client.show_flavor(flavor_id)["flavor"] + + def _validate_extra_specs(flv): + actual_specs_count = 0 + actual_specs = {} + for spec in flv["extra-specs"]: + actual_specs[spec] = flv["extra-specs"][spec] + for spec in expected_specs: + if spec in actual_specs: + if expected_specs[spec] == actual_specs[spec]: + actual_specs_count += 1 + if expected_specs_count == actual_specs_count: + return True + else: + return False + if _validate_extra_specs(flavor_orm) and\ + _validate_extra_specs(flavor_lcp): + return True + else: + return False + + @classmethod + def _del_flv_and_validate_deletion_on_dcp_and_lcp(cls, flavor_id): + _, body = cls.client.get_flavor(flavor_id) + regions_on_flavor = [region for region in body["flavor"]["regions"]] + if regions_on_flavor: + region_name_on_flavor = regions_on_flavor[0]["name"] + cls._delete_region_from_flavor_and_validate_deletion( + flavor_id, region_name_on_flavor) + cls.client.delete_flavor(flavor_id) + cls._wait_for_flavor_deletion_on_dcp(flavor_id) + cls._validate_flavor_deletion_on_lcp(flavor_id) + + @classmethod + def _delete_region_from_flavor_and_validate_deletion( + cls, flavor_id, rname): + _, body = cls.rms_client.get_region(rname) + region_id = body["id"] + cls.client.delete_region_from_flavor(flavor_id, region_id) + cls._wait_for_flavor_status_on_dcp(flavor_id, "no regions") + _, body = cls.client.get_flavor(flavor_id) + regions_on_flavor = body["flavor"]["regions"] + + if regions_on_flavor: + message = \ + "Region %s failed to get deleted from flavor %s " % ( + rname, flavor_id) + raise exceptions.TempestException(message) + cls._validate_flavor_deletion_on_lcp(flavor_id) + + @classmethod + def _wait_for_flavor_deletion_on_dcp(cls, flavor_id): + _, body = cls.client.list_flavors() + flavor_ids = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + start = int(time.time()) + while flavor_ids: + time.sleep(cls.build_interval) + _, body = cls.client.list_flavors() + flavor_ids = [flavor["id"] for flavor in body["flavors"] + if flavor["id"] == flavor_id] + if flavor_ids: + flavor_status = flavor_ids[0]["status"] + if flavor_status == 'Error': + message = \ + 'Flavor %s failed to get deleted' + 'and is in error status' % \ + flavor_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ( + 'flavor %s failed to get deleted within ' + 'the required time (%s s) and is in %s status.' + % (flavor_id, cls.build_timeout, flavor_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_flavor_deletion_on_lcp(cls, flavor_id): + body = cls.flavors_client.list_flavors()["flavors"] + flavor_ids = [flavor["id"] for flavor in body + if flavor["id"] == flavor_id] + if flavor_ids: + flavor_status = cls.flavors_client.show_flavor( + flavor_id)["flavor"]["status"] + message = "flavor %s failed to get deleted and is in %s status" \ + % (flavor_id, flavor_status) + raise exceptions.TempestException(message) + + @classmethod + def _get_tenant_id(cls, tenant_name): + body = cls.identity_client.list_tenants() + for tenant in body["tenants"]: + if(tenant["name"] == tenant_name): + return tenant["id"] + message = ('tenant %s not found on tenant list' % cls.tenant_name) + raise exceptions.TempestException(message) + + @classmethod + def _get_expected_flavor_name(cls, post_body): + name = post_body["series"] + "." + "c" + \ + post_body["vcpus"] + "r" + \ + str(int(post_body["ram"]) / 1024) \ + + "d" + post_body["disk"] + "s" + \ + str(int(post_body["swap"]) / 1024) \ + + "e" + str(int(post_body["ephemeral"]) / 1024) + return name + + @classmethod + def _validate_flv_geometry_on_lcp(cls, flavor_id, post_body): + flv = cls.flavors_client.show_flavor(flavor_id)["flavor"] + if flv["vcpus"] == int(post_body["vcpus"]) and \ + flv["ram"] == post_body["ram"] and \ + flv["swap"] == int(post_body["swap"]) and \ + flv["disk"] == int(post_body["disk"]) and \ + flv["ephemeral"] == post_body["ephemeral"]: + return True + else: + return False diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/ims_base.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/ims_base.py new file mode 100755 index 00000000..8fc3e549 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/ims_base.py @@ -0,0 +1,248 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +from ranger_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +from tempest.common.utils import data_utils + +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class ImsBaseOrmTest(base.BaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + @classmethod + def setup_clients(cls): + super(ImsBaseOrmTest, cls).setup_clients() + # service clients + cls.client = cls.os_primary.ims_client + cls.rms_client = cls.os_primary.rms_client + cls.cms_client = cls.os_primary.cms_client + + # setup variables + cls.region_id = CONF.identity.region + cls.tenant_id = cls._get_tenant_id( + cls.os_primary.credentials.tenant_name) + cls.alt_tenant_id = cls._get_tenant_id( + cls.os_alt.credentials.tenant_name) + + @classmethod + def _get_image_params(cls, set_region=True, single_tenant=True, + set_private=True, set_enabled=True): + region, post_body = {}, {} + post_body["name"] = data_utils.rand_name( + "orm-plugin-TestTempestIms-image") + + # use ubuntu website for the image location URL + ubuntu_url = "http://archive.ubuntu.com/" + ubuntu_dir = "ubuntu/dists/xenial/main/installer-i386/current/images/" + ubuntu_iso = "netboot/mini.iso" + post_body["url"] = ubuntu_url + ubuntu_dir + ubuntu_iso + + post_body["disk-format"] = "qcow2" + post_body["container-format"] = "bare" + + region["name"] = cls.region_id + region["type"] = "single" + region["checksum"] = "7297321c2fa6424417a548c85edd6e98" + region["virtual_size"] = "None" + region["size"] = "38797312" + + # set enabled status to True or False based on set_enabled value + post_body["enabled"] = True if set_enabled else False + # add region for the image as needed + post_body["regions"] = [region] if set_region else [] + # create image with visibililty = "public" or "private" + post_body["visibility"] = "private" if set_private else "public" + # add tenant for the image only if set_private + if set_private: + if single_tenant: + post_body["customers"] = [cls.tenant_id] + else: + post_body["customers"] = [cls.tenant_id, cls.alt_tenant_id] + else: + post_body["customers"] = [] + + return post_body + + @classmethod + def _get_tenant_id(cls, tenant_name): + body = cls.identity_client.list_tenants() + for tenant in body["tenants"]: + if(tenant["name"] == tenant_name): + return tenant["id"] + message = ('tenant %s not found on tenant list' % cls.tenant_name) + raise exceptions.TempestException(message) + + @classmethod + def _create_img_and_validate_creation_on_dcp_and_lcp(cls, **kwargs): + _, body = cls.client.create_image(**kwargs) + image = body["image"] + image_id = image["id"] + + _, body = cls.client.get_image(image_id) + image_detail = body["image"] + if image_detail["name"] == kwargs["name"]: + if image_detail["regions"] == []: + image_status = "no regions" + else: + image_status = "Success" + + cls._wait_for_image_status_on_dcp(image_id, image_status) + return image + else: + message = ('image %s not created successfully' % kwargs["name"]) + raise exceptions.TempestException(message) + + @classmethod + def _wait_for_image_status_on_dcp(cls, image_id, status): + _, body = cls.client.get_image(image_id) + image_status = body["image"]["status"] + start = int(time.time()) + while image_status != status: + time.sleep(cls.build_interval) + _, body = cls.client.get_image(image_id) + image_status = body["image"]["status"] + if image_status == 'Error': + message = ('Image %s failed to reach %s status' + ' and is in ERROR status on orm' % + (image_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('Image %s failed to reach %s' + ' status within ''the required time (%s s)' + ' on orm and is in %s status.' + % (image_id, status, + cls.build_timeout, + image_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_image_status_on_lcp(cls, image_id, status): + _, body = cls.client.list_images()["images"] + image_ids = [image["id"] for image in body] + if image_id not in image_ids: + message = ('Image %s not in image list on LCP' % image_id) + raise exceptions.TempestException(message) + else: + image_status = cls.client.show_image()["image"]["status"] + if image_status != status: + message = ('Image %s is in %s status instead of %s on LCP.' + % (image_id, image_status, status)) + raise exceptions.TempestException(message) + + @classmethod + def _update_img_validate_status_on_dcp_and_lcp(cls, image_id, para=None, + **kwargs): + if para: + cls.client.update_image(image_id, para, **kwargs) + else: + cls.client.update_image(image_id, **kwargs) + cls._wait_for_image_status_on_dcp(image_id, "Success") + cls._validate_image_status_on_lcp(image_id, "active") + + @classmethod + def _del_img_validate_deletion_on_dcp_and_lcp(cls, image_id): + _, body = cls.client.get_image(image_id) + image = body["image"] + + regions_on_image = [region for region in image["regions"]] + if regions_on_image: + region_id = regions_on_image[0]["name"] + cls._delete_region_from_image_and_validate_deletion( + image_id, region_id) + + cls.client.delete_image(image_id) + cls._validate_image_deletion_on_lcp(image_id) + + @classmethod + def _delete_region_from_image_and_validate_deletion(cls, image_id, + region_id): + cls.client.delete_region_from_image(image_id, region_id) + cls._wait_for_image_status_on_dcp(image_id, "no regions") + + @classmethod + def _wait_for_image_deletion_on_dcp(cls, image_id): + _, body = cls.client.list_images() + image_ids = [image["id"] for image in body["images"]] + if image_id in image_ids: + start = int(time.time()) + while image_id in image_ids: + time.sleep(cls.build_interval) + _, image_list = cls.client.list_images()["images"] + image_ids = [image["id"] + for image in image_list if image["id"] == + image_id] + if image_ids: + image_status = image_list[0]["status"] + _, body = cls.client.list_images()["images"] + image_ids = [image["id"] for image in body] + if image_id in image_ids: + image_status = image_ids[0]["status"] + if image_status == 'Error': + message = \ + 'Image %s failed to get deleted ' + 'and is in error status' % \ + image_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('Image %s failed to get deleted within ' + 'the required time (%s s) on orm ' + 'and is in %s status.' + % (image_id, cls.build_timeout, image_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_image_deletion_on_lcp(cls, image_id): + _, body = cls.client.list_images() + image_ids = [image["id"] for image in body["images"] + if image["id"] == image_id] + if image_id in image_ids: + image_status = body["status"] + message = "image %s failed to get deleted and is in %s status" \ + % (image_id, image_status) + raise exceptions.TempestException(message) + + @classmethod + def _validate_custs_on_img_on_dcp_and_lcp(cls, image_id, + expected_customers): + expected_customers = sorted(expected_customers) + + _, actual_customers_orm = sorted( + cls.client.get_image(image_id)["customers"]) + members = cls.client.member_list(image_id)["members"] + actual_customers_lcp =\ + sorted([member["member_id"] for member in members]) + if actual_customers_orm != expected_customers: + message = ( + 'Incorrect customers on image on orm.' + 'expected customers = %s, actual customers = %s' + % (expected_customers, actual_customers_orm)) + raise exceptions.TempestException(message) + if actual_customers_lcp != expected_customers: + message = ( + 'Incorrect customers on image on orm.' + 'expected customers = %s, actual customers = %s' + % (expected_customers, actual_customers_lcp)) + raise exceptions.TempestException(message) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/rms_base.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/rms_base.py new file mode 100755 index 00000000..41c0b3ab --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/rms_base.py @@ -0,0 +1,31 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from oslo_log import log as logging + +from tempest import config + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class RmsBaseOrmTest(base.BaseOrmTest): + + @classmethod + def setup_clients(cls): + cls.client = cls.os_primary.rms_client + super(RmsBaseOrmTest, cls).setup_clients() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers.py new file mode 100755 index 00000000..29786415 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers.py @@ -0,0 +1,342 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import random + +from ranger_tempest_plugin.tests.api import cms_base +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestCms(cms_base.CmsBaseOrmTest): + + @classmethod + def resource_setup(cls): + cls.setup_customer = cls._get_customer_params() + cls.setup_customer_id = \ + cls._create_cust_validate_creation_on_dcp_and_lcp( + **cls.setup_customer) + + cls.bare_customer = cls._get_bare_customer_params() + cls.bare_customer_id = \ + cls._create_cust_validate_creation_on_dcp_and_lcp( + **cls.bare_customer) + + super(TestTempestCms, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls._del_cust_validate_deletion_on_dcp_and_lcp(cls.setup_customer_id) + cls._del_cust_validate_deletion_on_dcp_and_lcp(cls.bare_customer_id) + super(TestTempestCms, cls).resource_cleanup() + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d89') + def test_get_customer(self): + """ Execute 'get_customer' using the following options: + - get customer by id (using cust_id parameter) + - get customer by name (using cust_name parameter) + """ + + # execute get_customer using cust_id and cust_name + for identifier in [self.setup_customer_id, + self.setup_customer['name']]: + _, body = self.client.get_customer(identifier) + self.assertIn(self.setup_customer_id, body['uuid']) + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d90') + def test_list_customers_with_filters(self): + """ This function executes 'list customer' with all available filters: + - no filter (i.e. list all customers) + - filter by metadata key + - filter by region + - filter by default user id + - customer name contains a substring + - customer name starting with a string + """ + + # format filter parameter values + region_name = [ + region['name'] for region in self.setup_customer['regions']] + userid = [user['id'] for user in self.setup_customer['users']] + cust_name = self.setup_customer['name'] + substr_name = random.randint(0, len(cust_name)) + + # get the first key from metadata as the metadata key filter + metadata_key = list(self.setup_customer['metadata'].keys())[0] + + # define the list customer filters to be used for this test + no_filter = None + metadata_filter = {'metadata': metadata_key} + region_filter = {'region': region_name[0]} + user_filter = {'user': userid[0]} + contains_filter = {'contains': cust_name[substr_name:]} + startswith_filter = {'starts_with': cust_name[:substr_name]} + + # execute list_customers with the available filters + for list_filter in [no_filter, metadata_filter, region_filter, + user_filter, contains_filter, startswith_filter]: + _, body = self.client.list_customers(list_filter) + customers = [cust['id'] for cust in body['customers']] + self.assertIn(self.setup_customer_id, customers) + + @decorators.idempotent_id('ac132678-fdb6-4037-a310-813313044055') + def test_enable_customer(self): + # setup data for test case + post_body = self._get_customer_params(enabled=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + + # update enabled status from 'False' to 'True' and validate update OK + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['enabled']) + self.client.enable_customer(test_customer_id, True) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertTrue(body['enabled']) + + @decorators.idempotent_id('7dfd5f7e-7031-4ee1-b355-cd5cdeb21bd1') + def test_add_default_user(self): + # setup data for "add_default_user" test case; leave default + # and region users blank at the initial data creation + post_body = self._get_customer_params(default_users=False, + region_users=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['users']) + self.assertFalse(body["regions"][0]['users']) + + # now add a default user, then validate that new user is added to + # BOTH default user AND region user successfully + post_default_user = self._get_user_params() + new_user_id = post_default_user[0]["id"] + _, body = self.client.add_default_user(test_customer_id, + *post_default_user) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertIn(new_user_id, [x['id'] for x in body['users']]) + self.assertIn(new_user_id, [x['id'] + for x in body['regions'][0]['users']]) + + @decorators.idempotent_id('699e8487-035e-4ae0-97b4-ca51b9a08aea') + def test_delete_default_user(self): + # setup data for delete_default_user test case + post_body = self._get_customer_params() + default_user_id = post_body["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertEqual(default_user_id, body['users'][0]['id']) + self.assertIn(default_user_id, + [x['id'] for x in body['regions'][0]['users']]) + + # delete default user and validate operation success by confirming + # user id no longer in both default users and region users list + _, body = self.client.delete_default_user(test_customer_id, + default_user_id) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_customer_id) + self.assertFalse(body['users']) + self.assertNotIn(default_user_id, + [x['id'] for x in body['regions'][0]['users']]) + + @decorators.idempotent_id('48ffd49f-2b36-40b4-b1b4-0c805b7ba7c2') + def test_replace_default_user(self): + # setup data for "replace_default_user" test case; no need to + # set region user as default user will also be assigned to it + post_body = self._get_customer_params(region_users=False) + default_user_id = post_body["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertIn(default_user_id, [x['id'] for x in body['users']]) + self.assertEqual(body['users'], body['regions'][0]['users']) + + # replace default user + put_default_user = self._get_user_params(alt=True) + updated_user_id = put_default_user[0]["id"] + _, body = self.client.replace_default_user(test_customer_id, + *put_default_user) + self._wait_for_status(test_customer_id, 'Success') + + # validate that BOTH the customer default user and region user + # are replaced with the new default_user successfully + _, body = self.client.get_customer(test_customer_id) + self.assertEqual(len(body['users']), + len(body['regions'][0]['users'])) + self.assertIn(updated_user_id, [x['id'] for x in body['users']]) + self.assertEqual(body['users'], body['regions'][0]['users']) + + @decorators.idempotent_id('07a631f9-3aa5-4797-9ead-4531ced89e2a') + def test_add_region_user(self): + # We are leaving both default and region users as blank to ensure + # region user is empty on initial data creation for this test case + post_body = self._get_customer_params(region_users=False, + default_users=False) + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + # confirm that the region users body is empty after data creation + self.assertFalse(body["regions"][0]["users"]) + + # now we shall add user to regions[0] + post_region_user = self._get_user_params() + _, body = self.client.add_region_user(test_customer_id, + CONF.identity.region, + *post_region_user) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + # validate that the region user is no longer empty after the add + self.assertTrue(customer["regions"][0]["users"]) + + @decorators.idempotent_id('9b2b3af8-2444-4143-a9e6-78c33b36c823') + def test_delete_region_user(self): + # To test delete_region_users scenario, leave default user blank + # for the test customer data, or else default user info will be + # added as region user as well + post_body = self._get_customer_params(default_users=False) + region_user_id = post_body["regions"][0]["users"][0]["id"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, body = self.client.get_customer(test_customer_id) + self.assertTrue(body["regions"][0]["users"]) + + # delete the user from the region, then validate operation success + _, body = self.client.delete_region_user(test_customer_id, + CONF.identity.region, + region_user_id) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + # validate that the region user is now empty + self.assertFalse(customer["regions"][0]["users"]) + + @decorators.idempotent_id('0ca59977-ef29-46b9-be92-14980a12c573') + def test_replace_region_user(self): + post_body = self._get_customer_params() + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + + # update region user then confirm that update is successful + put_region_user = self._get_user_params(alt=True) + new_region_user_id = put_region_user[0]["id"] + + _, body = self.client.replace_region_user(test_customer_id, + CONF.identity.region, + *put_region_user) + self._wait_for_status(test_customer_id, 'Success') + _, customer = self.client.get_customer(test_customer_id) + self.assertIn(new_region_user_id, [x['id'] + for x in customer["regions"][0]['users']]) + + @decorators.idempotent_id('f1444965-c711-438d-ab86-a2412acbe8e0') + def test_replace_metadata(self): + metadata = {'metadata': {'replace_key': 'replace_value'}} + _, body = self.client.replace_metadata(self.setup_customer_id, + metadata) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'metadata': 'replace_key'}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('80713a87-8e95-481f-a198-6b4515d48362') + def test_add_metadata(self): + metadata = {'metadata': {'add_key': 'add_value'}} + _, body = self.client.add_metadata(self.setup_customer_id, + metadata) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'metadata': 'add_key'}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('19a6bbe2-92b9-46be-95b7-aa0d9f247d88') + def test_add_regions(self): + region = self._get_region_params() + _, body = self.client.add_regions(self.bare_customer_id, + region) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.list_customers({'region': region[0]['name']}) + self.assertIn(self.setup_customer_id, + [x['id'] for x in body['customers']]) + + @decorators.idempotent_id('09a43bc1-439e-4497-a026-f2c3de451d29') + def test_delete_regions(self): + # setup data for delete_region + post_body = self._get_customer_params() + region_name = post_body["regions"][0]["name"] + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + _, customer = self.client.get_customer(test_customer_id) + self.assertTrue(customer["regions"]) + _, body = self.client.delete_region_from_customer(test_customer_id, + region_name) + self._wait_for_status(test_customer_id, 'no regions') + _, customer = self.client.get_customer(test_customer_id) + self.assertFalse(customer["regions"]) + + @decorators.idempotent_id('c7a24667-2a99-41ac-a42d-6c1163ef48af') + def test_create_customer(self): + post_body = self._get_customer_params(quota=False) + test_cust_name = post_body['name'] + _, body = self.client.create_customer(**post_body) + test_customer_id = body['customer']['id'] + self.addCleanup(self._del_cust_validate_deletion_on_dcp_and_lcp, + test_customer_id) + self._wait_for_status(test_customer_id, 'Success') + _, body = self.client.get_customer(test_cust_name) + self.assertIn(test_customer_id, body['uuid']) + + @decorators.idempotent_id('43785f87-27d5-408d-997f-de602caeb698') + def test_replace_customer(self): + customer = self._get_bare_customer_params() + customer['name'] = self.setup_customer['name'] + customer['regions'] = [{'name': CONF.identity.region}] + _, body = self.client.update_customer(self.setup_customer_id, + customer) + self._wait_for_status(self.setup_customer_id, 'Success') + _, body = self.client.get_customer(self.setup_customer_id) + self.assertExpected(customer, body, ['name', 'regions']) + for region in customer['regions']: + self.assertIn(region['name'], [x['name'] for x in body['regions']]) + + @decorators.idempotent_id('e8b9077a-d45c-4e24-a433-e7dfa07486b9') + def test_delete_customer(self): + # setup data for test case + post_body = self._get_customer_params() + test_customer_id = self._create_cust_validate_creation_on_dcp_and_lcp( + **post_body) + + # delete the data and do get_customer to ensure 404-NotFound response + self._del_cust_validate_deletion_on_dcp_and_lcp(test_customer_id) + self.assertRaises(exceptions.NotFound, self.client.get_customer, + test_customer_id) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers_negative.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers_negative.py new file mode 100755 index 00000000..ac530fbd --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_customers_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestCmsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestCmsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestCmsNegative, cls).setup_clients() + cls.client = cls.cmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestCmsNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestCmsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors.py new file mode 100755 index 00000000..73866884 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors.py @@ -0,0 +1,408 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from ranger_tempest_plugin.tests.api import fms_base +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestFms(fms_base.FmsBaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestFms, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestFms, cls).setup_clients() + cls.rms_client = cls.os_primary.rms_client + + @classmethod + def resource_setup(cls): + + # create flavor then save off flavor_id for use in test cases + body = cls._get_flavor_params() + cls.flavor = cls._create_flv_and_validate_creation_on_dcp_and_lcp( + **body) + + # these variables will be used to test list filters + cls.flavor_id = cls.flavor['id'] + cls.flavor_name = cls.flavor['name'] + cls.visibility = cls.flavor['visibility'] + cls.series = cls.flavor['series'] + cls.tenant_id = cls.tenant_id + cls.region_id = CONF.identity.region + cls.alias = cls.flavor['alias'] + cls.dflt_ex_specs = cls.flavor['extra-specs'] + super(TestTempestFms, cls).resource_setup() + + def _get_flavor_details(self, flavor_id): + _, body = self.client.get_flavor(flavor_id) + flavor = body["flavor"] + return flavor + + def _data_setup(self, post_body): + flavor = self._create_flv_and_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_flv_and_validate_deletion_on_dcp_and_lcp, + flavor["id"]) + return flavor + + def _exec_tags_function(self, flavor_id, req_json, action, para): + + if action == 'add': + _, body = self.client.add_tags(flavor_id, req_json) + + elif action == 'update': + _, body = self.client.update_tags(flavor_id, req_json) + + elif action == 'delete': + _, body = self.client.delete_tags(flavor_id, para) + + self._wait_for_flavor_status_on_dcp(flavor_id, 'Success') + + def _exec_ex_spec_function(self, flavor_id, es_body, action, para): + if action == 'add': + _, body = self.client.add_extra_specs(flavor_id, es_body) + + elif action == 'update': + _, body = self.client.update_extra_specs(flavor_id, es_body) + + elif action == 'delete': + _, body = self.client.delete_extra_specs(flavor_id, para) + + self._wait_for_flavor_status_on_dcp(flavor_id, 'Success') + + @classmethod + def resource_cleanup(cls): + # cls.delete_role_to_admin() + flavor_id = cls.flavor['id'] + cls._del_flv_and_validate_deletion_on_dcp_and_lcp(flavor_id) + super(TestTempestFms, cls).resource_cleanup() + + @decorators.idempotent_id('2a4481cd-acce-4a5d-af7c-940222a6238b') + def test_get_flavor(self): + """ Execute get_flavor using flavor_id / flavor_name + """ + for para in [self.flavor_id, self.flavor_name]: + _, body = self.client.get_flavor(para) + self.assertIn(self.flavor_id, body["flavor"]["id"]) + + @decorators.idempotent_id('c46a503a-951c-4d00-afaa-46076b54db16') + def test_list_flavor_with_filters(self): + """ This function executes 'list flavors' with the following filters: + - None (no filters, i.e. list all flavors) + - alias filter + - region filter + - visibility filter + - 'contains' filter + - 'starts_with' filter + - 'series' filter + - "tenant" filter + """ + # for use by the 'constains and 'starts_with' filter + str_index1 = data_utils.rand_int_id(0, len(self.flavor_name) - 1) + str_index2 = data_utils.rand_int_id(str_index1 + 1, + len(self.flavor_name)) + + # define the list flavors filters to be used for this test + alias_filter = "?alias=%s" % self.alias + region_filter = "?region=%s" % self.region_id + visibility_filter = "?visibility=%s" % self.visibility + contains_filter = '?contains=%s' \ + % self.flavor_name[str_index1:str_index2] + startswith_filter = "?starts_with=%s" % self.flavor_name[:str_index2] + series_filter = "?series=%s" % self.series + tenant_filter = "?tenant=%s" % self.tenant_id + + for list_filter in [None, region_filter, visibility_filter, + alias_filter, contains_filter, startswith_filter, + series_filter, tenant_filter]: + _, body = self.client.list_flavors(list_filter) + flavor_ids = [flvr["id"] for flvr in body["flavors"]] + self.assertIn(self.flavor_id, flavor_ids) + + @decorators.idempotent_id('7b9d1f91-a8a4-458d-aaad-a98b5bf033b4') + def test_create_flavor(self): + post_body = self._get_flavor_params() + # call client create_flavor and wait till status equals 'Success' + _, body = self.client.create_flavor(**post_body) + flavor = body["flavor"] + test_flvr_id = flavor['id'] + self._wait_for_flavor_status_on_dcp(flavor["id"], 'Success') + + # do not forget to add this account to addCleanUp + self.addCleanup(self._del_flv_and_validate_deletion_on_dcp_and_lcp, + flavor["id"]) + + # verify flavor record created successfully + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(flavor["visibility"], "private") + self.assertEqual(flavor["regions"][0]["name"], CONF.identity.region) + self.assertEqual(flavor["status"], "Success") + + @decorators.idempotent_id('4cad10ce-67d2-4633-b347-2c16783a31b9') + def test_add_flvr_tags(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test add_tags command with two sets of key:values + add_tag_body = {"tags": {"a": "b", "c": "d"}} + self._exec_tags_function(test_flvr_id, add_tag_body, 'add', None) + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(add_tag_body.get("tags"), tag_body.get("tags")) + + @decorators.idempotent_id('db8e5c0f-0041-45d4-9939-e079296123d8') + def test_replace_flvr_tags(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params() + tags = {} + tags["a"] = "b" + tags["c"] = "d" + post_body["tag"] = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test replace_tags command + replace_tag_body = {"tags": {"e": "f", "g": "h"}} + self._exec_tags_function(test_flvr_id, replace_tag_body, + 'update', None) + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(replace_tag_body.get("tags"), + tag_body.get("tags")) + + @decorators.idempotent_id('e0a0eca6-e120-45ab-a1a4-f5b95fdf97f1') + def test_delete_flvr_tag(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params() + tags = {} + tags["e"] = "f" + tags["g"] = "h" + post_body["tag"] = test_tag_body = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # test delete_tag command - delete the first key in tags body + delete_tag_key_e = "e" + self._exec_tags_function(test_flvr_id, None, 'delete', + delete_tag_key_e) + # do get_tag and confirm that resp body["tag"] now contains only + # second keypair + _, tag_body = self.client.get_tags(test_flvr_id) + test_tag_body.pop(delete_tag_key_e) + self.assertDictEqual(test_tag_body, tag_body.get("tags")) + + @decorators.idempotent_id('9c511020-53ed-4139-8c53-451cb0ea8c75') + def test_delete_all_flvr_tags(self): + # setup data for test case + post_body = self._get_flavor_params() + tags = {} + tags["i"] = "j" + tags["k"] = "l" + post_body["tag"] = test_tag_body = tags + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # before execute test, check that "tag" body is populated correctly + _, tag_body = self.client.get_tags(test_flvr_id) + self.assertDictEqual(test_tag_body, tag_body.get("tags")) + # test delete_all_tags command - run get_tag again and confirm + # that the tag dict is now empty + self._exec_tags_function(test_flvr_id, None, 'delete', None) + _, tag_body = self.client.get_tags(test_flvr_id) + # assert that tag_body contains nothing + self.assertFalse(tag_body["tags"]) + + @decorators.idempotent_id('ec74d68f-b42a-41a8-9685-ff5eca25ea0c') + def test_add_flvr_region(self): + # setup data for test case + post_body = self._get_flavor_params(set_region=False) + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + post_region_body = '{"regions": [{"name": "%s"}]}' % (self.region_id) + post_region_body = json.loads(post_region_body) + _, body = self.client.add_flvr_regions(test_flvr_id, + post_region_body) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + _, body = self.client.get_flavor(test_flvr_id) + self.assertEqual(body["flavor"]["regions"][0]["name"], + post_region_body["regions"][0]["name"]) + + @decorators.idempotent_id('5c7e6a94-89d2-4851-bf57-26371da7f47a') + def test_delete_flvr_region(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # delete the region then check to confirm flavor status + _, body = self.client.delete_flvr_region(test_flvr_id, + self.region_id) + # flavor status must show 'no regions' when it has no region assigned + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'no regions') + # flavor region is now empty after the lone region was removed + _, body = self.client.get_flavor(test_flvr_id) + self.assertTrue(len(body["flavor"]["regions"]) == 0) + + @decorators.idempotent_id('71404409-5d95-472c-8dac-b49a1c0c4b37') + def test_add_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # add a custom extra spec + add_es_body = {"os_extra_specs": {"a": "apple"}} + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', + None) + # assert extra specs add results match expected + test_dflt_ex_specs.update(add_es_body["os_extra_specs"]) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('043948fd-125b-4d96-bf40-42464066a7e1') + def test_update_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # run the test + add_es_body = {"os_extra_specs": {"a": "apple"}} + replace_es_body = {"os_extra_specs": {"a": "apple", "b": "banana"}} + + # add one custom extra spec , then replace with additional extra spec + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + # then add another custom extra spec using update_extra_spec + self._exec_ex_spec_function(test_flvr_id, replace_es_body, 'update', + None) + # assert extra specs update results match expected + test_dflt_ex_specs.update(replace_es_body["os_extra_specs"]) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('df83e2cd-d202-4b2f-8459-391a73067ec5') + def test_delete_flvr_extra_spec(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # run the test + add_es_body = {"os_extra_specs": {"g": "guava", "h": "honeydew"}} + delete_es_key_h = "h" + # add two custom extra specs then do get_extra_specs to save off + # the extra_spec add results + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + _, test_ex_specs = self.client.get_extra_specs(test_flvr_id) + # now delete one of the custom extra specs + self._exec_ex_spec_function(test_flvr_id, None, 'delete', + delete_es_key_h) + # assert extra specs update results match expected + test_ex_specs["os_extra_specs"].pop(delete_es_key_h) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + self.assertDictEqual(test_ex_specs["os_extra_specs"], + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('e3fc7ce3-c8fe-4805-8ad3-7be2c94fe7ad') + def test_delete_all_flvr_extra_specs(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + test_dflt_ex_specs = flavor['extra-specs'] + + # run the test + # add custom extra specs, then validate extra-spec add result + add_es_body = {"os_extra_specs": {"c": "carrots"}} + self._exec_ex_spec_function(test_flvr_id, add_es_body, 'add', None) + _, ex_specs_add_results = self.client.get_extra_specs(test_flvr_id) + self.assertIn("c", ex_specs_add_results["os_extra_specs"]) + self.assertEqual("carrots", + ex_specs_add_results["os_extra_specs"]["c"]) + # run delete ALL extra specs - note that this will only + # delete custom extra specs, NOT the default extra specs + self._exec_ex_spec_function(test_flvr_id, None, 'delete', None) + _, flvr_ex_specs = self.client.get_extra_specs(test_flvr_id) + # assert that flavor extra specs now contains only + # the default extra specs + self.assertDictEqual(test_dflt_ex_specs, + flvr_ex_specs.get("os_extra_specs")) + + @decorators.idempotent_id('187195b5-adfb-4c73-a2f5-42117021f5f2') + def test_add_flvr_tenant(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # check that flavor contains one tenant before testing add_flvr_tenant + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 1) + + # test add_flvr_tenant by adding one more tenant + post_tenant_body = '{"tenants": ["%s"]}' % (self.alt_tenant_id) + post_tenant_body = json.loads(post_tenant_body) + _, body = self.client.add_flvr_tenants(test_flvr_id, post_tenant_body) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + + # get flavor on same flavor id and confirm we have two tenants now + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 2) + + @decorators.idempotent_id('a7c976cd-6064-4279-ab64-2575d091cdae') + def test_delete_flvr_tenant(self): + # setup data for test case and assign two tags + post_body = self._get_flavor_params(single_tenant=False) + flavor = self._data_setup(post_body) + test_flvr_id = flavor['id'] + + # checking that flavor created contains two tenants + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 2) + + # delete one tenant, then wait until status = 'Success' + _, body = self.client.delete_flvr_tenant(test_flvr_id, + self.alt_tenant_id) + self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success') + # get flavor to confirm flavor["tenants"] now shows one tenant only + flavor = self._get_flavor_details(test_flvr_id) + self.assertEqual(len(flavor["tenants"]), 1) + + @decorators.idempotent_id('36958bba-673e-4397-85a9-fddb01e9ca0d') + def test_delete_flavor(self): + # setup data for test case + post_body = self._get_flavor_params() + flavor = self._create_flv_and_validate_creation_on_dcp_and_lcp( + **post_body) + test_flvr_id = flavor['id'] + + # delete the data and do get_flavor to ensure 404-NotFound response + self._del_flv_and_validate_deletion_on_dcp_and_lcp(test_flvr_id) + self.assertRaises(exceptions.NotFound, self.client.get_flavor, + test_flvr_id) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors_negative.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors_negative.py new file mode 100755 index 00000000..da1e72ad --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_flavors_negative.py @@ -0,0 +1,44 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestFmsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestFmsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestFmsNegative, cls).setup_clients() + cls.client = cls.fmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestFmsNegative, cls).resource_setup() + cls.env_name = cls.env_name + cls.tenant_id = cls.tenant_id + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestFmsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images.py new file mode 100755 index 00000000..18348294 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images.py @@ -0,0 +1,300 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import ims_base +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestIms(ims_base.ImsBaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestIms, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestIms, cls).setup_clients() + + @classmethod + def resource_setup(cls): + # create an image for tempest testing + cls.image_params = cls._get_image_params() + cls.image = cls._create_img_and_validate_creation_on_dcp_and_lcp( + **cls.image_params) + + # save off specific data needed for our tempest tests + cls.image_id = cls.image['id'] + cls.image_name = cls.image['name'] + # cls.visibility = cls.image['visibility'] + # cls.tenant_id = cls.image["customers"][0] + super(TestTempestIms, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls._del_img_validate_deletion_on_dcp_and_lcp(cls.image_id) + # cls.region_id) + super(TestTempestIms, cls).resource_cleanup() + + def _data_setup(self, post_body): + image = self._create_img_and_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_img_validate_deletion_on_dcp_and_lcp, + image["id"]) + # only check for Success image status if "regions" is not empty + if image["regions"]: + self._wait_for_image_status_on_dcp(image["id"], 'Success') + + return image + + @decorators.idempotent_id('2b1bb28b-4151-4e75-ae1b-d21089c3418c') + def test_get_image(self): + """ Execute 'get_image' using the following options: + - get image by id (using cust_id parameter) + - get image by name (using cust_name parameter) + """ + + # execute get_image using image ID and iamge_name + for identifier in [self.image_id, self.image_name]: + _, body = self.client.get_image(identifier) + self.assertIn(self.image_id, body['image']['id']) + + @decorators.idempotent_id('6072c438-1e45-4c0b-97a6-e5127bd33d90') + def test_list_images_with_filters(self): + """ This function executes 'list customer' with all available filters: + - no filter (i.e. list all images) + - filter by region + - filter by customer + """ + + # define the list customer filters to be used for this test + no_filter = None + customer_filter = "?customer=%s" % self.tenant_id + region_filter = "?region=%s" % self.region_id + + # execute list_customers with the available filters + for list_filter in [no_filter, region_filter, + customer_filter]: + _, body = self.client.list_images(list_filter) + images = [image['id'] for image in body['images']] + self.assertIn(self.image_id, images) + + @decorators.idempotent_id('eae7ca20-5383-4579-9f73-0138b8b3ec85') + def test_list_public_images(self): + """ List images with visibility = 'public' + """ + # set_private = False to create image with visibility = 'public' + post_body = self._get_image_params(set_private=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + # confirm image visibility is set to "public" after image is created + self.assertEqual(image["visibility"], "public") + filter_public_images = "?visibility=%s" % image["visibility"] + + # list all public images and check if test_image_id is in the list + _, body = self.client.list_images(filter_public_images) + image_ids = [img['id'] for img in body['images']] + self.assertIn(test_image_id, image_ids) + + @decorators.idempotent_id('dc321d60-f3bd-477c-b7bf-1594626f0a12') + def test_list_private_images(self): + """ List images with visibility = 'private' + """ + # image data created with visibility = private set by default + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + # confirm image visibility is set to "private" after image is created + self.assertEqual(image["visibility"], "private") + filter_private_images = "?visibility=%s" % image["visibility"] + + # list all public images and check if test_image_id is in the list + _, body = self.client.list_images(filter_private_images) + image_ids = [img['id'] for img in body['images']] + self.assertIn(test_image_id, image_ids) + + @decorators.idempotent_id('4435fef4-49a9-435b-8463-cf8a1e0b7cd8') + def test_disable_image(self): + # setup data for test case - "enabled" is set to "true" by default + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + # send False to IMS client enable_image function to disable customer + self.client.enabled_image(test_image_id, False) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + _, body = self.client.get_image(test_image_id) + image = body["image"] + # assert that the image["enabled"] value is 'False' + self.assertTrue(not image['enabled']) + + @decorators.idempotent_id('f32a13e3-6f38-423b-a616-09c8d4e1c277') + def test_enable_image(self): + # setup data for test case - set_enabled is set to "False" + post_body = self._get_image_params(set_enabled=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + # send True to IMS client enable_image function to enable customer + self.client.enabled_image(test_image_id, True) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + _, body = self.client.get_image(test_image_id) + image = body["image"] + # assert that the image["enabled"] value is 'True' + self.assertTrue(image['enabled']) + + @decorators.idempotent_id('cb9e3022-00d7-4a21-bdb2-67d3cd15a4f8') + def test_add_image_region(self): + # set_region = False to skip region assignment in data setup + post_body = self._get_image_params(set_region=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + + # add region to image then check to confirm image status = "Success" + self.client.add_region_to_image(test_image_id, self.region_id) + # image status must show 'Success' when assigned to a region + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image regions array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], self.region_id) + + @decorators.idempotent_id('1be2d6fd-57b0-4acf-b895-1996f857739b') + def test_delete_image_region(self): + # setup data for test case + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + + # delete the region then check to confirm image status = "no regions" + _, body = self.client.delete_region_from_image(test_image_id, + self.region_id) + # image status must show 'no regions' when it has no region assigned + self._wait_for_image_status_on_dcp(test_image_id, 'no regions') + # image region array should be empty after the region was removed + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertFalse(image["regions"]) + + @decorators.idempotent_id('0ee68189-66a8-4213-ad68-bc12991c174a') + def test_add_image_tenant(self): + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + # add another tenant to image then check if image status = "Success" + self.client.add_customer_to_image(test_image_id, self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image tenants array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(len(image["customers"]), 2) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('bac99348-6b13-4b30-958b-3c039b27eda3') + def test_update_image_tenant(self): + post_body = self._get_image_params() + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + # add another tenant to image then check if image status = "Success" + self.client.update_customer(test_image_id, self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # check that image tenants array is populated correctly + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(len(image["customers"]), 1) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('0506f23d-2d30-4214-9a4a-003ace86aa7d') + def test_delete_image_tenant(self): + # assign two tenants to image + post_body = self._get_image_params(single_tenant=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + _, body = self.client.get_image(test_image_id) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + + # delete one tenant then check if image status = "Success" + _, body = self.client.delete_customer_from_image(test_image_id, + self.alt_tenant_id) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # image region array should be empty after the region was removed + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertNotIn(self.alt_tenant_id, body['image']['customers']) + + @decorators.idempotent_id('0331e02a-ab52-4341-b676-a02462244277') + def test_create_image(self): + post_body = self._get_image_params() + # call client create_IMAGE and wait till status equals 'Success' + _, body = self.client.create_image(**post_body) + image = body["image"] + test_image_id = image["id"] + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + + # do not forget to add this account to addCleanUp + self.addCleanup(self._del_img_validate_deletion_on_dcp_and_lcp, + test_image_id) + # verify image record created successfully + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], CONF.identity.region) + + @decorators.idempotent_id('01160918-e217-401d-a6a0-e7992ab76e41') + def test_update_image(self): + region = {} + post_body = self._get_image_params(set_region=False, + set_enabled=False) + image = self._data_setup(post_body) + test_image_id = image['id'] + + # setup region and change 'enabled' and 'customer' properties + region["name"] = self.region_id + region["type"] = "single" + region["checksum"] = "7297321c2fa6424417a548c85edd6e98" + region["virtual_size"] = "None" + region["size"] = "38797312" + post_body["regions"] = [region] + + post_body["enabled"] = True + post_body["customers"] = [self.alt_tenant_id] + _, body = self.client.update_image(test_image_id, para=None, + **post_body) + self._wait_for_image_status_on_dcp(test_image_id, 'Success') + # verify image record updated successfully + _, body = self.client.get_image(test_image_id) + image = body["image"] + self.assertEqual(image["regions"][0]["name"], CONF.identity.region) + self.assertIn(self.alt_tenant_id, body['image']['customers']) + self.assertTrue(image['enabled']) + + @decorators.idempotent_id('23e2e7e2-5b19-4c66-b35c-7c686a986627') + def test_delete_image(self): + # setup data for test case + post_body = self._get_image_params() + image = self._create_img_and_validate_creation_on_dcp_and_lcp( + **post_body) + test_image_id = image['id'] + + # delete the data and do get_image to ensure 404-NotFound response + self._del_img_validate_deletion_on_dcp_and_lcp(test_image_id) + self.assertRaises(exceptions.NotFound, self.client.get_image, + test_image_id) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images_negative.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images_negative.py new file mode 100755 index 00000000..b06f40ce --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_images_negative.py @@ -0,0 +1,47 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestImsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestImsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestImsNegative, cls).setup_clients() + cls.client = cls.imsclient + cls.servers_client = cls.servers_client + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestImsNegative, cls).resource_setup() + cls.env_name = cls.env_name + cls.tenant_id = cls.tenant_id + cls.flavor_ref = CONF.compute.flavor_ref + cls.network_ref = CONF.network.public_network_id + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestImsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_region_groups.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_region_groups.py new file mode 100755 index 00000000..2e06aff2 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_region_groups.py @@ -0,0 +1,85 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin import data_utils as orm_data_utils +from ranger_tempest_plugin.tests.api import base + +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +CONF = config.CONF + + +class TestTempestRegionGroup(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegionGroup, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegionGroup, cls).setup_clients() + cls.client = cls.os_primary.rms_client + + @classmethod + def resource_setup(cls): + cls.setup_ids = [] + cls.group_ids = [] + # create standard region + _, cls.region_1 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_1['id']) + # create region sharing region_1 properties + _, cls.region_2 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_2['id']) + + _, cls.group_1 = cls.client.create_region_group( + **orm_data_utils.rand_region_group([cls.setup_ids[0]]) + ) + cls.group_ids.append(cls.group_1['group']['id']) + _, cls.group_2 = cls.client.create_region_group( + **orm_data_utils.rand_region_group(cls.setup_ids) + ) + cls.group_ids.append(cls.group_2['group']['id']) + + super(TestTempestRegionGroup, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + for region_id in cls.setup_ids: + cls.client.delete_region(region_id) + for group_id in cls.group_ids: + cls.client.delete_region_group(group_id) + super(TestTempestRegionGroup, cls).resource_cleanup() + + @decorators.idempotent_id('0d377eb2-754d-49c1-9a4f-c7019dfe80ca') + def test_update_group(self): + id = self.group_ids[-1] + group = orm_data_utils.rand_region_group(self.setup_ids, id) + _, body = self.client.update_region_group(id, **group) + self.assertExpected(group, body['group'], ['regions']) + + @decorators.idempotent_id('b946c6c4-d601-42b9-befd-ba40992a3c53') + def test_list_groups(self): + _, body = self.client.list_region_groups() + groups = [x['id'] for x in body['groups']] + self.assertIn(self.group_1['group']['id'], groups) + self.assertIn(self.group_2['group']['id'], groups) + + @decorators.idempotent_id('9a37d966-4416-4ff3-8f3b-6847810662d7') + def test_get_group(self): + id = self.group_1['group']['id'] + _, body = self.client.get_region_group(id) + self.assertExpected(self.group_1['group'], body, ['links']) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regiongroups_negative.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regiongroups_negative.py new file mode 100755 index 00000000..f5779ce0 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regiongroups_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestRegGroupNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegGroupNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegGroupNegative, cls).setup_clients() + cls.client = cls.rmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestRegGroupNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestRegGroupNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions.py new file mode 100755 index 00000000..f1e857c2 --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions.py @@ -0,0 +1,158 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin import data_utils as orm_data_utils +from ranger_tempest_plugin.tests.api import rms_base + +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +CONF = config.CONF + + +class TestTempestRegion(rms_base.RmsBaseOrmTest): + + @classmethod + def resource_setup(cls): + cls.setup_ids = [] + # create standard region + _, cls.region_1 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_1['id']) + # create region sharing region_1 properties + _, cls.region_2 = cls.client.create_region(data_utils.rand_name()) + cls.setup_ids.append(cls.region_2['id']) + # create region with differing properties + _, cls.region_3 = cls.client.create_region( + data_utils.rand_name(), + **{'status': 'down', + 'rangerAgentVersion': '3.0', + 'OSVersion': 'mitaka', + 'CLLI': '123450', + 'address': {'country': 'Mexico', 'state': 'Sonora', + 'city': 'Nogales', 'street': '12 main', + 'zip': '84000'}, + 'metadata': {'meta1': ['val1']}, + 'designType': 'large'}) + cls.setup_ids.append(cls.region_3['id']) + + super(TestTempestRegion, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + for region_id in cls.setup_ids: + cls.client.delete_region(region_id) + super(TestTempestRegion, cls).resource_cleanup() + + @decorators.idempotent_id('eecedcb0-9c96-453d-bd72-71dba26fa1c5') + def test_list_region(self): + _, body = self.client.list_regions() + regions = [x['id'] for x in body['regions']] + self.assertIn(self.region_1['id'], regions) + + @decorators.idempotent_id('0164e040-7775-4837-9ac2-aaa0f71cdfca') + def test_list_region_v1(self): + _, body = self.client.list_regions_v1() + regions = [x['id'] for x in body] + self.assertIn(self.region_1['id'], regions) + + @decorators.idempotent_id('e6c6fdfe-5fa2-45f5-8bd8-fb9cf79e99fc') + def test_list_region_with_name(self): + filter = {'regionname': self.region_1['name']} + self._list_regions_with_filter(filter, 'name') + + @decorators.idempotent_id('b3310e32-0c31-4d37-9789-cc3c4639dabe') + def test_list_region_with_osversion(self): + filter = {'osversion': self.region_1['OSVersion']} + self._list_regions_with_filter(filter, 'OSVersion') + + @decorators.idempotent_id('0b2d3e79-c14a-4527-94b0-04eeae053a80') + def test_list_region_with_status(self): + filter = {'status': self.region_1['status']} + self._list_regions_with_filter(filter, 'status') + + @decorators.idempotent_id('871be582-ecaa-4a46-a403-4d6b5e59d7de') + def test_list_region_with_ranger_version(self): + filter = {'rangerAgentVersion': self.region_1['rangerAgentVersion']} + self._list_regions_with_filter(filter, 'rangerAgentVersion') + + @decorators.idempotent_id('ac18be48-c787-4a65-913f-a0b0a80fbd1d') + def test_list_region_with_clli(self): + filter = {'clli': self.region_1['CLLI']} + self._list_regions_with_filter(filter, 'CLLI') + + @decorators.idempotent_id('f2b2361d-ce71-43a8-9f01-acb529835880') + def test_list_region_with_metadata(self): + filter = {'metadata': self.region_1['metadata'].keys()[0]} + self._list_regions_with_filter(filter, 'metadata') + + @decorators.idempotent_id('4533b31a-115d-466d-bf75-8ac24338c1a5') + def test_list_region_with_address(self): + filter = { + 'country': self.region_1['address']['country'], + 'city': self.region_1['address']['city'], + 'street': self.region_1['address']['street'], + 'zip': self.region_1['address']['zip'] + } + self._list_regions_with_filter(filter, 'address') + + @decorators.idempotent_id('726b8215-af10-4385-83c7-32b51502dff1') + def test_list_region_with_type(self): + filter = {'type': self.region_1['designType']} + self._list_regions_with_filter(filter, 'designType') + + @decorators.idempotent_id('4875ea70-a5a1-4b46-b752-246221670d26') + def test_list_region_with_vlcp(self): + filter = {'vlcp_name': self.region_1['vlcpName']} + self._list_regions_with_filter(filter, 'vlcpName') + + @decorators.idempotent_id('358f3cbc-4ae5-4b43-be36-6df55eae8fd9') + def test_get_region(self): + _, body = self.client.get_region(self.region_1['id']) + self.assertExpected(self.region_1, body, []) + + @decorators.idempotent_id('cefb952f-7777-4878-87d2-d78ac345f0d2') + def test_get_region_metadata(self): + _, body = self.client.get_region_metadata(self.region_1['id']) + self.assertExpected(self.region_1['metadata'], body['metadata'], []) + + @decorators.idempotent_id('b2c3baf5-22af-4bf9-bcad-b6a1a74e82d9') + def test_update_region(self): + id = self.setup_ids[-1] + region = orm_data_utils.rand_region(id) + _, body = self.client.update_region(id, **region) + self.assertExpected(region, body, []) + + @decorators.idempotent_id('0d5644d8-92bc-497c-8fc5-b57417d86e6d') + def test_update_region_status(self): + status = {} + status['status'] = orm_data_utils.rand_region_status( + [self.region_1['status']]) + _, body = self.client.update_region_status(self.region_1['id'], status) + self.assertExpected(status, body, ['links']) + + @decorators.idempotent_id('5c1a2624-6abe-49e7-82c8-30e8df1377d0') + def test_update_region_metadata(self): + metadata = {} + metadata['metadata'] = orm_data_utils.rand_region_metadata() + _, body = self.client.update_region_metadata(self.region_1['id'], + metadata) + self.assertExpected(metadata, body, []) + + def _list_regions_with_filter(self, filter, key): + _, body = self.client.list_regions(filter) + regions = [x for x in body['regions']] + self.assertTrue( + all([region[key] == self.region_1[key] for region in regions])) diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions_negative.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions_negative.py new file mode 100755 index 00000000..ee3ce2ed --- /dev/null +++ b/ranger_tempest_tests/ranger_tempest_plugin/tests/api/test_regions_negative.py @@ -0,0 +1,42 @@ +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ranger_tempest_plugin.tests.api import base + +from tempest import config + +CONF = config.CONF + + +class TestTempestRegionsNegative(base.BaseOrmTest): + + @classmethod + def setup_credentials(cls): + super(TestTempestRegionsNegative, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(TestTempestRegionsNegative, cls).setup_clients() + cls.client = cls.rmsclient + + @classmethod + def resource_setup(cls): + cls.set_role_to_admin() + super(TestTempestRegionsNegative, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls.delete_role_to_admin() + super(TestTempestRegionsNegative, cls).resource_cleanup() diff --git a/ranger_tempest_tests/ranger_tempest_plugin/tests/scenario/__init__.py b/ranger_tempest_tests/ranger_tempest_plugin/tests/scenario/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/ranger_tempest_tests/setup.cfg b/ranger_tempest_tests/setup.cfg new file mode 100755 index 00000000..963cd31b --- /dev/null +++ b/ranger_tempest_tests/setup.cfg @@ -0,0 +1,31 @@ +[metadata] +name = ranger_plugin +version = 0.0.1 +summary = Basic Tempest plugin for Orm with a mos tempest smoke test case +description-file = +author = +author-email = +home-page = +classifier = + Development Status :: 4 - Beta + Environment :: Console + Environment :: OpenStack + Intended Audience :: OpenStack Development Team + Intended Audience :: OpenStack QA Team + Intended Audience :: Information Technology + Operating System :: Linux + Natural Language :: English + Topic :: Software Development :: Quality Assurance + Topic :: Software Development :: Testing + Programming Language :: Python +keywords = + Plugin + Tempest + OpenStack + +[entry_points] +tempest.test_plugins = + ranger = ranger_tempest_plugin.plugin:OrmPlugin + +[pbr] +warnerrors = true diff --git a/ranger_tempest_tests/setup.py b/ranger_tempest_tests/setup.py new file mode 100755 index 00000000..b54a7ed5 --- /dev/null +++ b/ranger_tempest_tests/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True +) diff --git a/ranger_tempest_tests/tempest_setup/.testr.conf b/ranger_tempest_tests/tempest_setup/.testr.conf new file mode 100644 index 00000000..5f87ce81 --- /dev/null +++ b/ranger_tempest_tests/tempest_setup/.testr.conf @@ -0,0 +1,10 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ + OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ + OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \ + OS_TEST_LOCK_PATH=${OS_TEST_LOCK_PATH:-${TMPDIR:-'/tmp'}} \ + ${PYTHON:-python} -m subunit.run discover -t ${OS_TOP_LEVEL:-./} ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list +group_regex=([^\.]*\.)* + diff --git a/ranger_tempest_tests/tempest_setup/accounts.yaml b/ranger_tempest_tests/tempest_setup/accounts.yaml new file mode 100644 index 00000000..74a7b98c --- /dev/null +++ b/ranger_tempest_tests/tempest_setup/accounts.yaml @@ -0,0 +1,19 @@ +- username: 'm07057' + tenant_name: 'tempest_m07057' + password: 'devstack' + roles: + - 'admin' + +- username: 'm01690' + tenant_name: 'tempest_m01690' + password: 'devstack' + roles: + - 'ResellerAdmin' + - 'Member' + +- username: 'm01691' + tenant_name: 'tempest_m01691' + password: 'devstack' + roles: + - 'ResellerAdmin' + - 'Member' diff --git a/ranger_tempest_tests/tempest_setup/create_tenant.sh b/ranger_tempest_tests/tempest_setup/create_tenant.sh new file mode 100644 index 00000000..45619b94 --- /dev/null +++ b/ranger_tempest_tests/tempest_setup/create_tenant.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +source devstack/openrc admin admin + +openstack project create 'tempest_m07057' +openstack project create 'tempest_m01690' +openstack project create 'tempest_m01691' + +openstack user create 'm07057' --project 'tempest_m07057' --password devstack +openstack user create 'm01690' --project 'tempest_m01690' --password devstack +openstack user create 'm01691' --project 'tempest_m01691' --password devstack + +openstack role add --project 'tempest_m07057' --user m07057 admin + +openstack role add --project 'tempest_m01690' --user m01690 ResellerAdmin +openstack role add --project 'tempest_m01690' --user m01690 Member + +openstack role add --project 'tempest_m01691' --user m01691 ResellerAdmin +openstack role add --project 'tempest_m01691' --user m01691 Member + +openstack role add --project 'tempest_m07057' --user admin admin +openstack role add --project 'tempest_m01690' --user admin admin +openstack role add --project 'tempest_m01691' --user admin admin \ No newline at end of file diff --git a/ranger_tempest_tests/tempest_setup/tempest.conf b/ranger_tempest_tests/tempest_setup/tempest.conf new file mode 100644 index 00000000..d7298669 --- /dev/null +++ b/ranger_tempest_tests/tempest_setup/tempest.conf @@ -0,0 +1,110 @@ +[DEFAULT] +debug = true +log_file = tempest.log +log_dir = /tempest/logs + +[auth] +test_accounts_file = /tempest/etc/accounts.yaml +use_dynamic_credentials = true +tempest_roles = admin +admin_username = admin +admin_project_name = admin +admin_password = nomoresecrete +admin_domain_name = Default + +[compute] +image_ref = 199668fa-0cd4-4b84-92cb-f5c87a16628a +image_ref_alt = 199668fa-0cd4-4b84-92cb-f5c87a16628a +flavor_ref = 1 +flavor_ref_alt = 84 +fixed_network_name = public + +[compute-feature-enabled] +change_password = false +console_output = true +resize = true +vnc_console = true +enable_instance_password = true +attach_encrypted_volume = false +scheduler_available_filters = all + +[identity] +disable_ssl_certificate_validation = false +ca_certificates_file = /etc/ssl/certs/ca-certificates.crt +uri = http://192.168.56.107:5000/v2.0 +uri_v3 = http://192.168.56.107:5000/v3.0 +auth_version = v2 +# region = RegionOne +region = local +admin_role = admin + +[identity-feature-enabled] +api_v2 = true +api_v2_admin = true +api_v3 = true +api_extensions = all + +[image-feature-enabled] +deactivate_image = true + +[network] +public_network_id = 6953ce50-6ea3-4091-8f68-fd7b0c5fdcd3 +floating_network_name = public + +[network-feature-enabled] +ipv6 = true +api_extensions = contrail,security-group,ipam,port-security,binding,provider,agent,quotas,route-table,extra_lbaas_opts,external-net,policy,router,allowed-address-pairs,extra_dhcp_opt,service-interface,lbaas +ipv6_subnet_attributes = true + +[object-storage] +operator_role = Member +reseller_admin_role = ResellerAdmin + +[orchestration] +instance_type = 1 + +[oslo_concurrency] +lock_path = /tempest/tempest_lock + +[scenario] +img_dir = /tempest/images +img_file = cirros-0.3.4-x86_64-disk.img + +[service_available] +heat-cfn = true +cinder = true +swift = true +cinderv2 = true +heat = true +novav3 = true +trove = true +designate = true +ceilometer = true +murano = true +keystone = true +s3 = true +glance = true +neutron = true +nova = true +ord = true +nova_ec2 = true +orm = true +contrail = true +mistral = true + +[validation] +run_validation = true +connect_method = floating +image_ssh_user = cirros +image_ssh_password = cubswin:) + +[volume] +backend_names = SOLIDFIRE +storage_protocol = iSCSI + +[volume-feature-enabled] +multi_backend = false + +[orm] +uri = http://192.168.56.101 +catalog_type = orm diff --git a/ranger_tempest_tests/tox.ini b/ranger_tempest_tests/tox.ini new file mode 100644 index 00000000..c6da58f8 --- /dev/null +++ b/ranger_tempest_tests/tox.ini @@ -0,0 +1,71 @@ +[tox] +minversion = 1.6 +envlist = py27,pep8,pylint +skipdist = True + + +[testenv] +usedevelop = True +install_command = pip install {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +commands = + find . -type f -name "*.pyc" -delete +whitelist_externals = + find + + +[testenv:venv] +commands = {posargs} + + +#Linters +[testenv:pep8] +sitepackages = True +basepython = python +deps = + flake8>=2.6.2, <3.4.0 + flake8-import-order>=0.9, <0.13 +commands = + bash changed_python_files.sh {toxinidir} "flake" {posargs} +whitelist_externals = + bash + +# For use in development environment +[testenv:pep8dev] +sitepackages = True +basepython = python +skip_install = true +deps = + {[testenv:pep8]deps} +commands = + {[testenv:pep8]commands} dev +whitelist_externals = + bash + + +# At this time we are only checking for C0103(invalid-name) which in the near +# future will be expanded upon +[testenv:pylint] +sitepackages = True +basepython = python +deps = + pylint==1.7.2 +commands = + bash changed_python_files.sh {toxinidir} "pylint" {posargs} +whitelist_externals = + bash + + +# For use in development environment +[testenv:pylintdev] +sitepackages = True +basepython = python +skip_install = true +deps = + {[testenv:pylint]deps} +commands = + {[testenv:pylint]commands} dev +whitelist_externals = + bash +