Initial Ranger-Tempest-Plugin Commit

First commit with zuul tests and
migration of existing Ranger-Tempest-
Plugin codebase.

Change-Id: I45d6946a397cc148ec3789af1c1ad4374a99396a
This commit is contained in:
jh629g 2019-07-15 10:24:49 -05:00
parent 0fd09aa2fe
commit e7c69f048c
51 changed files with 5538 additions and 0 deletions

4
.zuul.yaml Normal file
View File

@ -0,0 +1,4 @@
- project:
name: x/ranger-tempest-plugin
templates:
- openstack-python-jobs

100
Dockerfile Normal file
View File

@ -0,0 +1,100 @@
FROM ubuntu:16.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 \
libmysqlclient-dev \
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 setuptools && \
pip install wheel && \
pip install --upgrade six && \
pip install pbr==2.0.0
##########################################################################
### aic-orm-tempest-plugin setup
##########################################################################
WORKDIR /
COPY . ranger-tempest-plugin/
WORKDIR /ranger-tempest-plugin/
RUN python 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
### now run 'pip install -r requirements'
RUN pip install -r /tempest/requirements.txt && \
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 \
&& mkdir -p /var/log/tempest \
&& rm -rf /tempest/.stestr \
&& rm -rf /tempest/.stestr.conf
# copy tempest test setup files
COPY tempest_setup/.stestr.conf /tempest/
COPY tempest_setup/create_tenant.sh /tempest/etc
COPY tempest_setup/accounts.yaml /tempest/etc
COPY tempest_setup/tempest.conf /tempest/etc
##########################################################################
### END OF openstack tempest setup steps
##########################################################################
##########################################################################
### RUN tempest tests on test_regions
##########################################################################
### create egg-info for tempest
WORKDIR /tempest/
RUN python /tempest/setup.py develop
#ENTRYPOINT ostestr run ranger_tempest_plugin.tests.api.test_regions \
# && /bin/bash
#ENTRYPOINT ostestr run ranger_tempest_plugin.tests.api.test_flavors/ \
# && /bin/bash
#ENTRYPOINT ostestr run ranger_tempest_plugin.tests.api.test_customers/ \
# && /bin/bash
#ENTRYPOINT ostestr run ranger_tempest_plugin.tests.api.test_images/ \
# && /bin/bash

46
changed_python_files.sh Executable file
View File

@ -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

18
flake8rc Normal file
View File

@ -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

378
pylintrc Normal file
View File

@ -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*(# )?<?https?://\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

View File

@ -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:
https://docs.openstack.org/tempest/latest/#using-plugins
See the tempest docs for information on writing new tests etc:
https://docs.openstack.org/tempest/latest/
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 <path_of_the_plugin>
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

View File

View File

@ -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, credential=None):
super(OrmClientManager, self).__init__(credential)
self.cms_client = CmsClient(base_client.RangerAuthProvider(credential),
CONF.identity.catalog_type,
CONF.identity.region,
CONF.ranger.RANGER_CMS_BASE_URL)
self.fms_client = FmsClient(base_client.RangerAuthProvider(credential),
CONF.identity.catalog_type,
CONF.identity.region,
CONF.ranger.RANGER_FMS_BASE_URL)
self.rms_client = RmsClient(base_client.RangerAuthProvider(credential),
CONF.identity.catalog_type,
CONF.identity.region,
CONF.ranger.RANGER_RMS_BASE_URL)
self.ims_client = ImsClient(base_client.RangerAuthProvider(credential),
CONF.identity.catalog_type,
CONF.identity.region,
CONF.ranger.RANGER_IMS_BASE_URL)

75
ranger_tempest_plugin/config.py Executable file
View File

@ -0,0 +1,75 @@
# 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("ranger", default=False,
help="Whether or not ranger is expected to be available")
]
orm_group = cfg.OptGroup(
name="ranger",
title="Ranger 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"),
cfg.StrOpt("RANGER_CMS_BASE_URL",
help="Ranger Project Service URL"),
cfg.StrOpt("RANGER_FMS_BASE_URL",
help="Ranger Flavor Service URL"),
cfg.StrOpt("RANGER_IMS_BASE_URL",
help="Ranger Image Service URL"),
cfg.StrOpt("RANGER_RMS_BASE_URL",
help="Ranger Region Service URL"),
cfg.BoolOpt('verify',
default=False,
help='Flag for SSL verfiy Enabled/Disabled.'),
cfg.BoolOpt('auth_enabled',
default=False,
help='Token Authentication enabled/disabled'),
cfg.ListOpt("flavor_series",
default=['xx'],
help="Supported flavor series"),
cfg.StrOpt("domain",
default='Default',
help="Domain used for Ranger tempest testing")
]

View File

@ -0,0 +1,89 @@
# 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
identity_url = CONF.identity.uri_v3.strip('/v3')
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': identity_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

46
ranger_tempest_plugin/plugin.py Executable file
View File

@ -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 RangerPlugin(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)

View File

@ -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.*)

View File

@ -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

View File

@ -0,0 +1,251 @@
# 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"},
"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"},
"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]
}

View File

@ -0,0 +1,210 @@
# 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
_delete = {
'status_code': [204]
}
_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']
}
_group = {
'type': 'object',
'properties': {
'id': {'type': 'string'},
'links': _links,
'created': {'type': 'string', 'format': 'date-time'}
},
'required': ['id', 'links', 'created']
}
_users = {
'type': 'object',
'properties': {
'id': {
'type': 'array',
'items': {'type': 'string'}
},
'domain': {'type': 'string'},
},
'required': ['id', 'domain']
}
create_group = {
'status_code': [201],
'response_body': {
'type': 'object',
'properties': {
'group': _group,
'transaction_id': {'type': 'string'}
},
'required': ['group', 'transaction_id']
}
}
get_group = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'status': _status,
'uuid': {'type': 'string'},
'enabled': {'type': 'boolean'},
'domain': {'type': 'string'},
'name': {'type': 'string'},
'regions': {'type': 'array'},
'description': {'type': 'string'},
'users': {'type': 'array'}
},
'required': ['status', 'uuid', 'enabled', 'domain', 'name',
'regions', 'description']
}
}
list_groups = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'groups': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'status': _status,
'description': {'type': 'string'},
'enabled': {'type': 'boolean'},
'domain': {'type': 'string'},
'regions': {
'type': 'array',
'items': {'type': 'string'}
},
'id': {'type': 'string'},
'name': {'type': 'string'}
},
'required': ['status', 'description', 'enabled',
'domain', 'regions', 'id', 'name']
}
}
},
'required': ['groups']
}
}
delete_group = _delete
delete_groups_region = _delete
_roles = {
'type': 'object',
'properties': {
'roles': {
'type': 'array',
'items': {'type': 'string'}
},
'customer': {'type': 'string'},
'domain': {'type': 'string'}
},
'required': ['roles']
}
assign_group_roles = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'transaction_id': {'type': 'string'},
'roles': {
'type': 'array',
'items': _roles
},
'links': _links,
'created': {'type': 'string', 'format': 'date-time'}
},
'required': ['transaction_id', 'roles', 'links', 'created']
}
}
unassign_group_role = _delete
list_group_roles = {
'status_code': [200],
'response_body': {
'type': 'array',
'properties': {
'roles': {
'type': 'array',
'items': {'type': 'string'}
},
'customer': {'type': 'string'},
'domain': {'type': 'string'}
},
'required': ['roles']
}
}
add_groups_region = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'regions': {
'type': 'array',
'items': _region
},
'transaction_id': {'type': 'string'}
},
'required': ['regions', 'transaction_id']
}
}
add_groups_users = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'transaction_id': {'type': 'string'},
'users': {
'type': 'array',
'items': _users
},
'links': _links,
'created': {'type': 'string', 'format': 'date-time'}
},
'required': ['transaction_id', 'users', 'links', 'created']
}
}
delete_groups_user = _delete

View File

@ -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

View File

@ -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]
}

View File

View File

@ -0,0 +1,169 @@
# 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
import requests
from tempest import config
from tempest.lib import auth
from tempest.lib.common import rest_client
CONF = config.CONF
class ResponseError(Exception):
pass
class ConnectionError(Exception):
pass
class RangerClientBase(rest_client.RestClient):
rms_url = CONF.ranger.RANGER_RMS_BASE_URL
auth_region = CONF.identity.region
timeout = 10
# def get_keystone_ep(rms_url, region_name):
def get_keystone_ep(self):
"""Get the Keystone EP from tempest conf.
"""
return CONF.identity.uri_v3.strip('/v3')
def get_token(self, timeout, host):
headers = {
'Content-Type': 'application/json',
}
url = '%s/v3/auth/tokens'
data = '''
{
"auth":{
"identity":{
"methods":[
"password"
],
"password":{
"user":{
"domain":{
"name":"%s"
},
"name":"%s",
"password":"%s"
}
}
},
"scope":{
"project":{
"name":"%s",
"domain":{
"name":"%s"
}
}
}
}
}'''
if not CONF.ranger.auth_enabled:
return None
region = self.auth_region
keystone_ep = self.get_keystone_ep()
if keystone_ep is None:
raise ConnectionError(
'Failed in get_token, host: {}, region: {}'.format(host,
region))
url = url % (keystone_ep,)
data = data % (CONF.auth.admin_domain_name,
CONF.auth.admin_username,
CONF.auth.admin_password,
CONF.auth.admin_project_name,
CONF.auth.admin_domain_name,)
try:
resp = requests.post(url,
timeout=timeout,
data=data,
headers=headers)
if resp.status_code != 201:
raise ResponseError(
'Failed to get token (Reason: {})'.format(
resp.status_code))
return resp.headers['x-subject-token']
except Exception as e:
raise ConnectionError(e.message)
def get_headers(self):
headers = {'X-Auth-Region': CONF.identity.region,
'X-Auth-Token': self.get_token(self.timeout, self.rms_url),
'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 RangerAuthProvider(auth.KeystoneV3AuthProvider):
def __init__(self, credentials, auth_url=CONF.identity.uri_v3):
super(RangerAuthProvider, self).__init__(credentials, auth_url)
def auth_request(self, method, url, headers=None, body=None, filters=None):
filters = {'service': 'identity'}
auth_headers = super(RangerAuthProvider,
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']

View File

@ -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.RangerClientBase):
cms_url = CONF.ranger.RANGER_CMS_BASE_URL
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)

View File

@ -0,0 +1,149 @@
# 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.RangerClientBase):
fms_url = CONF.ranger.RANGER_FMS_BASE_URL
version = "v1"
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)
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)
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)

View File

@ -0,0 +1,116 @@
# 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.
#
# Group processes currently are non-functional, but this code is being
# kept for the time being until we are certain it is no longer needed.
import json
import urllib
from ranger_tempest_plugin.schemas import group_schema as schema
from ranger_tempest_plugin.services import base_client
from tempest import config
CONF = config.CONF
class GrpClient(base_client.RangerClientBase):
cms_url = CONF.ranger.RANGER_CMS_BASE_URL
version = 'v1'
def create_group(self, **kwargs):
uri = '%s/%s/orm/groups' % (self.cms_url, self.version)
post_body = json.dumps(kwargs)
return self.post_request(uri, post_body, schema.create_group)
def get_group(self, identifier):
uri = '%s/%s/orm/groups/%s' \
% (self.cms_url, self.version, identifier)
return self.get_request(uri, schema.get_group)
def list_groups(self, filter=None):
uri = '%s/%s/orm/groups' % (self.cms_url, self.version)
if filter is not None:
uri += '?' + urllib.urlencode(filter)
return self.get_request(uri, schema.list_groups)
def add_groups_region(self, group_id, *args):
uri = '%s/%s/orm/groups/%s/regions' % (
self.cms_url, self.version, group_id)
post_body = json.dumps(args)
return self.post_request(uri, post_body, schema.add_groups_region)
def delete_groups_region(self, group_id, region_id):
uri = '%s/%s/orm/groups/%s/regions/%s' % (
self.cms_url, self.version, group_id, region_id)
return self.delete_request(uri, schema.delete_groups_region)
def delete_group(self, group_id):
uri = '%s/%s/orm/groups/%s' \
% (self.cms_url, self.version, group_id)
return self.delete_request(uri, schema.delete_group)
def assign_group_roles(self, group_id, *args):
uri = '%s/%s/orm/groups/%s/roles' % (
self.cms_url, self.version, group_id)
post_body = json.dumps(args)
return self.post_request(uri, post_body, schema.assign_group_roles)
def assign_group_region_roles(self, group_id, region_id, *args):
uri = '%s/%s/orm/groups/%s/regions/%s/roles' % (
self.cms_url, self.version, group_id, region_id)
post_body = json.dumps(args)
return self.post_request(uri, post_body, schema.assign_group_roles)
def unassign_group_role(
self, group_id, role, assignmenet_type, assignment_value):
uri = '%s/%s/orm/groups/%s/roles/%s/%s/%s' % (self.cms_url,
self.version,
group_id,
role,
assignmenet_type,
assignment_value)
return self.delete_request(uri, schema.unassign_group_role)
def list_group_roles(self, group_id, params):
uri = '%s/%s/orm/groups/%s/roles/%s' % (
self.cms_url, self.version, group_id, params)
return self.get_request(uri, schema.list_group_roles)
def add_group_default_user(self, group_id, *args):
uri = '%s/%s/orm/groups/%s/users' \
% (self.cms_url, self.version, group_id)
post_body = json.dumps(args)
return self.post_request(uri, post_body, schema.add_groups_users)
def delete_group_default_user(self, group_id, user_id, user_domain):
uri = '%s/%s/orm/groups/%s/users/%s/%s' % (
self.cms_url, self.version, group_id, user_id, user_domain)
return self.delete_request(uri, schema.delete_groups_region)
def add_group_region_user(self, group_id, region_id, *args):
uri = '%s/%s/orm/groups/%s/regions/%s/users' \
% (self.cms_url, self.version, group_id, region_id)
post_body = json.dumps(args)
return self.post_request(uri, post_body, schema.add_groups_users)
def delete_groups_region_user(self, group_id, region_id,
user_id, user_domain):
uri = '%s/%s/orm/groups/%s/regions/%s/users/%s/%s' % (
self.cms_url, self.version, group_id, region_id,
user_id, user_domain)
return self.delete_request(uri, schema.delete_groups_region)

View File

@ -0,0 +1,102 @@
# 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.RangerClientBase):
ims_url = CONF.ranger.RANGER_IMS_BASE_URL
version = "v1"
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)

View File

@ -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.RangerClientBase):
rms_url = CONF.ranger.RANGER_RMS_BASE_URL
identity_url = CONF.identity.uri_v3.strip('/v3')
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': self.identity_url,
'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)

View File

View File

View File

@ -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 oslo_log import log as logging
from ranger_tempest_plugin import clients
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.projects_client
@classmethod
def skip_checks(cls):
super(BaseOrmTest, cls).skip_checks()
if not CONF.service_available.ranger:
skip_msg = ("%s skipped as ranger 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)

View File

@ -0,0 +1,324 @@
# 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 oslo_log import log as logging
from ranger_tempest_plugin.tests.api import base
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_projects()["projects"]
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

View File

@ -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 oslo_log import log as logging
from ranger_tempest_plugin.tests.api import base
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_project_id(
cls.os_primary.credentials.project_name)
cls.alt_tenant_id = cls._get_project_id(
cls.os_alt.credentials.project_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, 40)
disk = random.randint(2, 102)
post_body["description"] = \
"orm-plugin-BaseORMTest-flavor"
post_body["series"] = random.choice(CONF.ranger.flavor_series)
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"
post_body['tag'] = {'a': 'b', 'c': 'd'}
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' %
(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) 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_project_id(cls, project_name):
body = cls.identity_client.list_projects()
for project in body["projects"]:
if(project["name"] == project_name):
return project["id"]
message = ('project %s not found on projects list' % project_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

View File

@ -0,0 +1,244 @@
# 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 oslo_log import log as logging
from ranger_tempest_plugin.tests.api import base
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_project_id(
cls.os_primary.credentials.project_name)
cls.alt_tenant_id = cls._get_project_id(
cls.os_alt.credentials.project_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")
post_body["url"] = CONF.ranger.image_url
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"
# set image tags
post_body["tags"] = ["tag1", "tag2"]
# 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_project_id(cls, project_name):
body = cls.identity_client.list_projects()
for project in body["projects"]:
if(project["name"] == project_name):
return project["id"]
message = ('project %s not found on project list' % project_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)

View File

@ -0,0 +1,30 @@
# 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 oslo_log import log as logging
from ranger_tempest_plugin.tests.api import base
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()

View File

@ -0,0 +1,344 @@
# 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)

View File

@ -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()

View File

@ -0,0 +1,361 @@
# 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 import decorators
from tempest.lib import exceptions
from tempest.lib.common.utils import data_utils
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']
cls.tags = cls.flavor['tag']
# add custom extra specs
cls.custom_es = {'os_extra_specs': {'g': 'guava', 'h': 'honeydew'}}
cls.client.add_extra_specs(cls.flavor_id, cls.custom_es)
cls._wait_for_flavor_status_on_dcp(cls.flavor_id, 'Success')
super(TestTempestFms, cls).resource_setup()
def _get_flavor_details(self, flavor_id):
_, body = self.client.get_flavor(flavor_id)
return body["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')
def _restore_default_tags(self, flavor_id):
tag_body = {"tags": self.tags}
_, body = self.client.update_tags(flavor_id, tag_body)
self._wait_for_flavor_status_on_dcp(flavor_id, 'Success')
_, tag_body = self.client.get_tags(flavor_id)
self.assertDictEqual(self.tags, body.get("tags"))
def _restore_custom_es(self, flavor_id):
_, body = self.client.update_extra_specs(flavor_id, self.custom_es)
self._wait_for_flavor_status_on_dcp(flavor_id, 'Success')
_, es = self.client.get_extra_specs(flavor_id)
es_expected = self.custom_es.get("os_extra_specs")
es_expected.update(self.dflt_ex_specs)
self.assertDictEqual(es_expected, es.get("os_extra_specs"))
@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)
test_flvr_id = body["flavor"]['id']
self._wait_for_flavor_status_on_dcp(test_flvr_id, 'Success')
# do not forget to add this account to addCleanUp
self.addCleanup(self._del_flv_and_validate_deletion_on_dcp_and_lcp,
test_flvr_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):
# test add_tags command with two sets of key:values
add_tag_body = {"tags": {"aa": "bb", "cc": "dd"}}
self._exec_tags_function(self.flavor_id, add_tag_body, 'add', None)
_, tag_body = self.client.get_tags(self.flavor_id)
subset = {k: v for k, v in tag_body.get("tags").items()
if k in add_tag_body.get("tags")}
self.assertDictEqual(add_tag_body.get("tags"), subset)
@decorators.idempotent_id('db8e5c0f-0041-45d4-9939-e079296123d8')
def test_replace_flvr_tags(self):
# test replace_tags command
replace_tag_body = {"tags": {"ee": "ff", "gg": "hh"}}
self._exec_tags_function(self.flavor_id, replace_tag_body,
'update', None)
self.addCleanup(self._restore_default_tags, self.flavor_id)
_, tag_body = self.client.get_tags(self.flavor_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):
# test delete_tag command - delete a tag from tags body
delete_tag_key = "a"
self._exec_tags_function(self.flavor_id, None, 'delete',
delete_tag_key)
# do get_tag and tries to retrieve the deleted tag
_, tag_body = self.client.get_tags(self.flavor_id)
tag_present = tag_body.get("tags").get(delete_tag_key, None)
self.assertIsNone(tag_present)
@decorators.idempotent_id('9c511020-53ed-4139-8c53-451cb0ea8c75')
def test_delete_all_flvr_tags(self):
# ensure there is at least a tag
_, tag_body = self.client.get_tags(self.flavor_id)
self.assertTrue(True if tag_body.get("tags") else False)
# test delete_all_tags command - run get_tag again and confirm
# that the tag dict is now empty
self._exec_tags_function(self.flavor_id, None, 'delete', None)
self.addCleanup(self._restore_default_tags, self.flavor_id)
_, tag_body = self.client.get_tags(self.flavor_id)
# assert that tag_body contains nothing
self.assertFalse(tag_body["tags"])
@decorators.idempotent_id('ec74d68f-b42a-41a8-9685-ff5eca25ea0c')
def test_add_delete_flvr_region(self):
# setup data to add region
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"])
# remove added region and 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):
# get extra specs before the add
_, es_body = self.client.get_extra_specs(self.flavor_id)
es_expected = es_body.get("os_extra_specs")
# add custom extra specs
add_es_body = {"os_extra_specs": {"a": "apple", "b": "banana"}}
self._exec_ex_spec_function(self.flavor_id, add_es_body, 'add', None)
_, es = self.client.get_extra_specs(self.flavor_id)
# assert extra specs add results match expected
es_expected.update(add_es_body["os_extra_specs"])
self.assertDictEqual(es_expected, es.get("os_extra_specs"))
@decorators.idempotent_id('043948fd-125b-4d96-bf40-42464066a7e1')
def test_update_flvr_extra_specs(self):
# add custom extra spec using update_extra_spec
replace_es_body = {"os_extra_specs": {"z": "zebra", "x": "xray"}}
self._exec_ex_spec_function(self.flavor_id, replace_es_body, 'update',
None)
self.addCleanup(self._restore_custom_es, self.flavor_id)
# assert extra specs update results match expected
_, flvr_ex_specs = self.client.get_extra_specs(self.flavor_id)
es_expected = replace_es_body.get("os_extra_specs")
es_expected.update(self.dflt_ex_specs)
self.assertDictEqual(es_expected, flvr_ex_specs.get("os_extra_specs"))
@decorators.idempotent_id('df83e2cd-d202-4b2f-8459-391a73067ec5')
def test_delete_flvr_extra_spec(self):
# get extra specs before the delete
_, es_body = self.client.get_extra_specs(self.flavor_id)
# now delete one of the custom extra specs
delete_es_key_h = "h"
self._exec_ex_spec_function(self.flavor_id, None, 'delete',
delete_es_key_h)
# assert extra specs update results match expected
es_body["os_extra_specs"].pop(delete_es_key_h)
_, flvr_ex_specs = self.client.get_extra_specs(self.flavor_id)
self.assertDictEqual(es_body["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):
# run delete ALL extra specs - note that this will only
# delete custom extra specs, NOT the default extra specs
self._exec_ex_spec_function(self.flavor_id, None, 'delete', None)
self.addCleanup(self._restore_custom_es, self.flavor_id)
_, flvr_ex_specs = self.client.get_extra_specs(self.flavor_id)
# assert that flavor extra specs now contains only
# the default extra specs
self.assertDictEqual(self.dflt_ex_specs,
flvr_ex_specs.get("os_extra_specs"))
@decorators.idempotent_id('187195b5-adfb-4c73-a2f5-42117021f5f2')
def test_add_delete_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)
self.assertEqual(flavor["tenants"][0], self.tenant_id)
# 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)
self.assertIn(self.alt_tenant_id, flavor["tenants"])
# 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)
self.assertEqual(flavor["tenants"][0], self.tenant_id)
@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)

View File

@ -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()

View File

@ -0,0 +1,260 @@
# 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):
# setup public image for tempest testing
cls.image_params = \
cls._get_image_params(set_private=False)
cls.public_image = \
cls._create_img_and_validate_creation_on_dcp_and_lcp(
**cls.image_params)
# setup private image for tempest testing
cls.image_params = cls._get_image_params(set_enabled=False)
cls.private_image = \
cls._create_img_and_validate_creation_on_dcp_and_lcp(
**cls.image_params)
super(TestTempestIms, cls).resource_setup()
@classmethod
def resource_cleanup(cls):
cls._del_img_validate_deletion_on_dcp_and_lcp(cls.public_image['id'])
cls._del_img_validate_deletion_on_dcp_and_lcp(cls.private_image['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.public_image['id'], self.public_image['name']]:
_, body = self.client.get_image(identifier)
self.assertIn(self.public_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
- filter by visibility (public/private)
"""
# 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
public_filter = "?visibility=public"
private_filter = "?visibility=private"
# list public images
_, body = self.client.list_images(public_filter)
image_ids = [img['id'] for img in body['images']]
self.assertIn(self.public_image['id'], image_ids)
# list private images
_, body = self.client.list_images(private_filter)
image_ids = [img['id'] for img in body['images']]
self.assertIn(self.private_image['id'], image_ids)
# execute list_customers with the rest of the 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.private_image['id'], images)
@decorators.idempotent_id('4435fef4-49a9-435b-8463-cf8a1e0b7cd8')
def test_disable_image(self):
# disable self.public_image and check if request is successful
self.client.enabled_image(self.public_image['id'], False)
self._wait_for_image_status_on_dcp(self.public_image['id'], 'Success')
_, body = self.client.get_image(self.public_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):
# enable self.private_image and check if request is successful
self.client.enabled_image(self.private_image['id'], True)
self._wait_for_image_status_on_dcp(self.private_image['id'], 'Success')
_, body = self.client.get_image(self.private_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_delete_image_region(self):
# 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 region is successfully added
_, body = self.client.get_image(test_image_id)
image = body["image"]
self.assertEqual(image["regions"][0]["name"], self.region_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)
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_delete_image_tenant(self):
# add alt tenant to self.private_image & check if status = "Success"
self.client.add_customer_to_image(self.private_image['id'],
self.alt_tenant_id)
self._wait_for_image_status_on_dcp(self.private_image['id'],
'Success')
# check that alt tenant successfully added to image tenants array
_, body = self.client.get_image(self.private_image['id'])
image = body["image"]
self.assertEqual(len(image["customers"]), 2)
self.assertIn(self.alt_tenant_id, image['customers'])
# now delete alt_tenant_id and ensure operation is successful
_, body = self.client.delete_customer_from_image(
self.private_image['id'],
self.alt_tenant_id)
self._wait_for_image_status_on_dcp(self.private_image['id'], 'Success')
# image region array should no longer contain alt tenant
_, body = self.client.get_image(self.private_image['id'])
image = body["image"]
self.assertNotIn(self.alt_tenant_id, image['customers'])
@decorators.idempotent_id('bac99348-6b13-4b30-958b-3c039b27eda3')
def test_update_image_tenant(self):
# replace current tenant in self.private_image with alt tenant
self.client.update_customer(self.private_image['id'],
self.alt_tenant_id)
self._wait_for_image_status_on_dcp(self.private_image['id'], 'Success')
# check that image tenants array contains only alt tenant
_, body = self.client.get_image(self.private_image['id'])
image = body["image"]
self.assertEqual(len(image["customers"]), 1)
self.assertIn(self.alt_tenant_id, 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)
image = self._data_setup(post_body)
test_image_id = image['id']
# setup region and change 'enabled', 'customers' 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"] = False
post_body["customers"] = [self.alt_tenant_id]
# empty tags list
post_body["tags"] = []
_, 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, image['customers'])
self.assertFalse(image['enabled'])
self.assertFalse(image['tags'])
@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)

View File

@ -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()

View File

@ -0,0 +1,87 @@
# 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 import decorators
from tempest.lib.common.utils import data_utils
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'])

View File

@ -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()

View File

@ -0,0 +1,177 @@
# 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 import decorators
from tempest.lib import exceptions
from tempest.lib.common.utils import data_utils
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('829c7da0-2332-4f80-ad35-24306b67ed0e')
def test_create_and_delete_region(self):
# create new region for API test
region = orm_data_utils.rand_region()
_, region_body = self.client.create_region(region['id'], **region)
test_region_name = region_body['name']
test_region_id = region_body['id']
_, body = self.client.get_region(test_region_name)
self.assertEqual(test_region_id, body['id'])
# now delete the region
self.client.delete_region(test_region_id)
self.assertRaises(exceptions.NotFound, self.client.get_region,
test_region_id)
@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 = {'ranger_agent_version': 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]))

View File

@ -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()

34
setup.cfg Executable file
View File

@ -0,0 +1,34 @@
[metadata]
name = ranger_tempest_plugin
version = 0.0.1
summary = Basic Tempest plugin for Ranger 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
[files]
packages = ranger_tempest_plugin
[entry_points]
tempest.test_plugins =
ranger-tempest-plugin = ranger_tempest_plugin.plugin:RangerPlugin
[pbr]
warnerrors = true

29
setup.py Executable file
View File

@ -0,0 +1,29 @@
# Copyright (c) 2012 OpenStack Foundation
# 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 setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
pbr=True)

View File

@ -0,0 +1,6 @@
[DEFAULT]
test_path=../ranger/ranger-tempest-plugin/ranger_tempest_plugin/tests/
top_dir=../ranger/ranger-tempest-plugin/
test_command=${PYTHON:-python} -m subunit.run discover orm $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -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'

View File

@ -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

104
tempest_setup/ranger-tempest.sh Executable file
View File

@ -0,0 +1,104 @@
#!/bin/bash
# Shell script to move to tempest, copy tempest.conf, and
# initialize tempest tests using stestr
# It should be noted that this script can not be ran out of
# the box and does require configuration before running.
function execute_tests {
# move to Tempest directory
cd ${TEMPEST_DIRECTORY}
# check for necessary folders, make them if not found
if [[ ! -d tempest_lock ]]; then
mkdir tempest_lock
fi
if [[ ! -d images ]]; then
mkdir images
fi
if [[ ! -d .stestr ]]; then
stestr init
fi
# sets RANGER_DIRECTORY to relevant subdirectory and copies files for tests
RANGER_DIRECTORY=${RANGER_DIRECTORY}/ranger-tempest-plugin/tempest_setup
# check for necessary files, copy them from ranger if not found
if [[ ! -e ./.stestr.conf ]]; then
cp ${RANGER_DIRECTORY}/.stestr.conf ./
fi
if [[ ! -e etc/tempest.conf ]]; then
cp ${RANGER_DIRECTORY}/tempest.conf etc/
fi
if [[ ! -e etc/create_tenant.sh ]]; then
cp ${RANGER_DIRECTORY}/create_tenant.sh etc/
fi
if [[ ! -e etc/accounts.yaml ]]; then
cp ${RANGER_DIRECTORY}/accounts.yaml etc/
fi
# runs tests using stestr and regex, ex: ranger_tempest_plugin.tests.api.test_regions
stestr run --concurrency ${CONCURRENCY} --log-file /var/log/tempest/tempest_run.log ${TEST_REGEX}
}
usage()
{
cat << EOF
usage: ./ranger-tempest.sh -t TEMPEST_DIRECTORY -c CONCURRENCY -r RANGER_DIRECTORY -f TEST_REGEX
This script automates a few steps necessary to run Tempest against Ranger
OPTIONS:
-h Show this message
-t The Tempest Folder fully-formed path
-c Concurrency
-r The location of your Ranger folder
-f The regex representing the tests that will be ran
EOF
}
TEMPEST_DIRECTORY=
CONCURRENCY=1
TEST_REGEX=
RANGER_DIRECTORY=
while getopts "ht:c:f:r:" OPTION
do
case $OPTION in
h)
usage
exit 1
;;
t)
TEMPEST_DIRECTORY=$OPTARG
;;
c)
CONCURRENCY=$OPTARG
;;
f)
TEST_REGEX=$OPTARG
;;
r)
RANGER_DIRECTORY=$OPTARG
;;
?)
usage
exit
;;
esac
done
if [[ -z $TEMPEST_DIRECTORY ]]; then
echo "The script requires the location of the Tempest folder"
usage
exit 1
#elif [[ -z $TEST_REGEX ]]; then
# echo "The script expects a regex of tests to run"
# usage
# exit 1
elif [[ -z $RANGER_DIRECTORY ]]; then
echo "This script requires the location of the Ranger folder"
usage
exit 1
else
execute_tests
fi

118
tempest_setup/tempest.conf Normal file
View File

@ -0,0 +1,118 @@
[DEFAULT]
debug = true
log_file = tempest.log
log_dir = /var/log/tempest
[auth]
test_accounts_file = /opt/stack/tempest/etc/accounts.yaml
use_dynamic_credentials = true
tempest_roles = admin
admin_username = admin
admin_project_name = admin
admin_password = nomoresecret
admin_domain_name = Default
[compute]
# uncomment image_ref and image_ref_alt and set their values accordingly
# image_ref =
# image_ref_alt =
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.130:5000/v2.0
uri_v3 = http://192.168.56.130:5000/v3
auth_version = v3
region = local
admin_role = admin
[identity-feature-enabled]
api_v2 = false
api_v2_admin = false
api_v3 = true
api_extensions = all
[image-feature-enabled]
deactivate_image = true
[network]
# uncomment public_network_id and set it to 'openstack network list | grep public' ID value
public_network_id = 5a9ae5bf-0570-4a44-b8fa-5ba2fb60d850
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_lock
[scenario]
img_dir = 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
nova_ec2 = true
ranger = true
contrail = true
mistral = true
[validation]
run_validation = true
connect_method = floating
image_ssh_user = cirros
# set image_ssh_password as appropriate
image_ssh_password = password
[volume]
backend_names = SOLIDFIRE
storage_protocol = iSCSI
[volume-feature-enabled]
multi_backend = false
[ranger]
RANGER_RMS_BASE_URL='http://192.168.56.127:8080'
RANGER_CMS_BASE_URL='http://192.168.56.127:7080'
RANGER_FMS_BASE_URL='http://192.168.56.127:8082'
RANGER_IMS_BASE_URL='http://192.168.56.127:8084'
image_url=http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-i386/current/images/netboot/mini.iso
# verify=True
auth_enabled=True
catalog_type = ranger
flavor_series = p1

71
tox.ini Normal file
View File

@ -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