Refactor trove-tempest-plugin
Depends-On: https://review.opendev.org/#/c/697870/ Story: 2006554 Task: 36639 Change-Id: I6251f070f330ee886e6436d92c20d78e0401d59e
This commit is contained in:
parent
eda92dae2d
commit
605a0d3a51
52
.zuul.yaml
52
.zuul.yaml
|
@ -5,32 +5,66 @@
|
||||||
- tempest-plugin-jobs
|
- tempest-plugin-jobs
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- python-troveclient-tempest-neutron-src
|
- trove-tempest-plugin:
|
||||||
- trove-tempest-plugin
|
voting: false
|
||||||
- trove-tempest-ipv6-only
|
- trove-tempest-ipv6-only:
|
||||||
|
voting: false
|
||||||
gate:
|
gate:
|
||||||
queue: trove
|
queue: trove
|
||||||
jobs:
|
jobs:
|
||||||
- python-troveclient-tempest-neutron-src
|
- trove-tempest-plugin:
|
||||||
- trove-tempest-plugin
|
voting: false
|
||||||
- trove-tempest-ipv6-only
|
- trove-tempest-ipv6-only:
|
||||||
|
voting: false
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: trove-tempest-plugin
|
name: trove-tempest-plugin
|
||||||
parent: devstack-tempest
|
parent: devstack-tempest
|
||||||
|
timeout: 7800
|
||||||
required-projects: &base_required_projects
|
required-projects: &base_required_projects
|
||||||
- openstack/neutron
|
- openstack/python-troveclient
|
||||||
- openstack/trove
|
- openstack/trove
|
||||||
- openstack/trove-tempest-plugin
|
- openstack/trove-tempest-plugin
|
||||||
- openstack/tempest
|
- openstack/tempest
|
||||||
|
irrelevant-files:
|
||||||
|
- ^.*\.rst$
|
||||||
|
- ^doc/.*$
|
||||||
|
- ^etc/.*$
|
||||||
|
- ^releasenotes/.*$
|
||||||
vars: &base_vars
|
vars: &base_vars
|
||||||
tox_envlist: all
|
tox_envlist: all
|
||||||
|
tempest_concurrency: 2
|
||||||
devstack_localrc:
|
devstack_localrc:
|
||||||
TEMPEST_PLUGINS: /opt/stack/trove-tempest-plugin
|
TEMPEST_PLUGINS: /opt/stack/trove-tempest-plugin
|
||||||
|
USE_PYTHON3: true
|
||||||
|
devstack_local_conf:
|
||||||
|
post-config:
|
||||||
|
$TROVE_CONF:
|
||||||
|
DEFAULT:
|
||||||
|
usage_timeout: 1800
|
||||||
devstack_plugins:
|
devstack_plugins:
|
||||||
trove: https://opendev.org/openstack/trove
|
trove: https://opendev.org/openstack/trove.git
|
||||||
devstack_services:
|
devstack_services:
|
||||||
tempest: true
|
etcd3: false
|
||||||
tls-proxy: false
|
tls-proxy: false
|
||||||
|
ceilometer-acentral: false
|
||||||
|
ceilometer-acompute: false
|
||||||
|
ceilometer-alarm-evaluator: false
|
||||||
|
ceilometer-alarm-notifier: false
|
||||||
|
ceilometer-anotification: false
|
||||||
|
ceilometer-api: false
|
||||||
|
ceilometer-collector: false
|
||||||
|
cinder: true
|
||||||
|
c-sch: true
|
||||||
|
c-api: true
|
||||||
|
c-vol: true
|
||||||
|
c-bak: false
|
||||||
|
swift: true
|
||||||
|
s-account: true
|
||||||
|
s-container: true
|
||||||
|
s-object: true
|
||||||
|
s-proxy: true
|
||||||
|
tempest: true
|
||||||
tempest_test_regex: ^trove_tempest_plugin\.tests
|
tempest_test_regex: ^trove_tempest_plugin\.tests
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||||
|
sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
|
||||||
|
sphinx!=1.6.6,!=1.6.7,>=1.6.2,!=2.1.0;python_version>='3.4' # BSD
|
||||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||||
|
|
||||||
|
# releasenotes
|
||||||
reno>=2.5.0 # Apache-2.0
|
reno>=2.5.0 # Apache-2.0
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||||
six>=1.10.0 # MIT
|
|
||||||
oslo.config>=5.2.0 # Apache-2.0
|
oslo.config>=5.2.0 # Apache-2.0
|
||||||
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
|
oslo.log>=3.44.1 # Apache-2.0
|
||||||
testtools>=2.2.0 # MIT
|
oslo.serialization>=2.29.1 # Apache-2.0
|
||||||
|
oslo.service>=1.40.1 # Apache-2.0
|
||||||
|
oslotest>=3.2.0 # Apache-2.0
|
||||||
|
requests>=2.14.2 # Apache-2.0
|
||||||
|
six>=1.10.0 # MIT
|
||||||
tempest>=17.1.0 # Apache-2.0
|
tempest>=17.1.0 # Apache-2.0
|
||||||
|
tenacity>=5.1.1 # Apache-2.0
|
||||||
|
|
|
@ -2,5 +2,11 @@
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
hacking<0.13,>=0.12.0 # Apache-2.0
|
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||||
|
|
||||||
|
coverage!=4.4,>=4.0 # Apache-2.0
|
||||||
|
python-subunit>=1.0.0 # Apache-2.0/BSD
|
||||||
|
oslotest>=3.2.0 # Apache-2.0
|
||||||
|
stestr>=2.0.0 # Apache-2.0
|
||||||
|
testscenarios>=0.4 # Apache-2.0/BSD
|
||||||
|
testtools>=2.2.0 # MIT
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Copyright 2018 Samsung Electronics
|
|
||||||
# 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 tempest import config
|
|
||||||
from tempest.lib.services import clients
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class Manager(clients.ServiceClients):
|
|
||||||
"""Service clients proxy.
|
|
||||||
|
|
||||||
Enhances tests with a convenient way to access available service clients
|
|
||||||
configured for a specified set of credentials.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, credentials, service=None):
|
|
||||||
if CONF.identity.auth_version == 'v2':
|
|
||||||
identity_uri = CONF.identity.uri
|
|
||||||
else:
|
|
||||||
identity_uri = CONF.identity.uri_v3
|
|
||||||
super(Manager, self).__init__(credentials, identity_uri)
|
|
|
@ -14,27 +14,50 @@
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
service_option = cfg.BoolOpt('trove',
|
service_option = cfg.BoolOpt(
|
||||||
default=True,
|
'trove',
|
||||||
help="Whether or not Trove is expected to be "
|
default=True,
|
||||||
"available")
|
help="Whether or not Trove is expected to be available"
|
||||||
|
)
|
||||||
|
|
||||||
database_group = cfg.OptGroup(name='database',
|
database_group = cfg.OptGroup(
|
||||||
title='Database Service Options')
|
name='database',
|
||||||
|
title='Database Service Options'
|
||||||
|
)
|
||||||
|
|
||||||
DatabaseGroup = [
|
DatabaseGroup = [
|
||||||
cfg.StrOpt('catalog_type',
|
cfg.StrOpt(
|
||||||
default='database',
|
'catalog_type',
|
||||||
help="Catalog type of the Database service."),
|
default='database',
|
||||||
cfg.StrOpt('endpoint_type',
|
help="Catalog type of the Database service."
|
||||||
default='publicURL',
|
),
|
||||||
choices=['public', 'admin', 'internal',
|
cfg.StrOpt(
|
||||||
'publicURL', 'adminURL', 'internalURL'],
|
'endpoint_type',
|
||||||
help="The endpoint type to use for the Database service."),
|
default='publicURL',
|
||||||
cfg.StrOpt('db_flavor_ref',
|
choices=['public', 'admin', 'internal', 'publicURL', 'adminURL',
|
||||||
default="1",
|
'internalURL'],
|
||||||
help="Valid primary flavor to use in Database tests."),
|
help="The endpoint type to use for the Database service."
|
||||||
cfg.StrOpt('db_current_version',
|
),
|
||||||
default="v1.0",
|
cfg.IntOpt('database_build_timeout',
|
||||||
help="Current database version to use in Database tests."),
|
default=1800,
|
||||||
|
help='Timeout in seconds to wait for a database instance to '
|
||||||
|
'build.'),
|
||||||
|
cfg.StrOpt(
|
||||||
|
'flavor_id',
|
||||||
|
default="d2",
|
||||||
|
help="The Nova flavor ID used for creating database instance."
|
||||||
|
),
|
||||||
|
cfg.StrOpt(
|
||||||
|
'subnet_cidr',
|
||||||
|
default='10.1.1.0/24',
|
||||||
|
help=('The Neutron CIDR format subnet to use for database network '
|
||||||
|
'creation.')
|
||||||
|
),
|
||||||
|
cfg.StrOpt(
|
||||||
|
'volume_type',
|
||||||
|
default="lvmdriver-1",
|
||||||
|
help="The Cinder volume type used for creating database instance."
|
||||||
|
),
|
||||||
|
cfg.StrOpt('datastore_type', default="mysql"),
|
||||||
|
cfg.StrOpt('datastore_version', default="5.7"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -45,12 +45,8 @@ class TroveTempestPlugin(plugins.TempestPlugin):
|
||||||
service_params = {
|
service_params = {
|
||||||
'name': 'database',
|
'name': 'database',
|
||||||
'service_version': 'database',
|
'service_version': 'database',
|
||||||
'module_path': 'trove_tempest_plugin.services.database',
|
'module_path': 'trove_tempest_plugin.services.client',
|
||||||
'client_names': [
|
'client_names': ['TroveClient']
|
||||||
'FlavorsClient',
|
|
||||||
'LimitsClient',
|
|
||||||
'VersionsClient'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
service_params.update(service_config)
|
service_params.update(service_config)
|
||||||
return [service_params]
|
return [service_params]
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Copyright 2018 Samsung Electronics
|
||||||
|
# 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_serialization import jsonutils as json
|
||||||
|
from six.moves.urllib import parse as urlparse
|
||||||
|
|
||||||
|
from tempest.lib.common import rest_client
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class TroveClient(rest_client.RestClient):
|
||||||
|
def __init__(self, auth_provider, **kwargs):
|
||||||
|
super(TroveClient, self).__init__(auth_provider, **kwargs)
|
||||||
|
|
||||||
|
def get_resource(self, obj, id, expected_status_code=200):
|
||||||
|
url = '/%s/%s' % (obj, id)
|
||||||
|
resp, body = self.get(url)
|
||||||
|
self.expected_success(expected_status_code, resp.status)
|
||||||
|
|
||||||
|
return rest_client.ResponseBody(resp, json.loads(body))
|
||||||
|
|
||||||
|
def list_resources(self, obj, expected_status_code=200, **filters):
|
||||||
|
url = '/%s' % obj
|
||||||
|
if filters:
|
||||||
|
# Encode provided dict of fields into a series of key=value pairs
|
||||||
|
# separated by '&' characters.
|
||||||
|
#
|
||||||
|
# The field value can be a sequence. Setting option doseq to True
|
||||||
|
# enforces producing individual key-value pair for each element of
|
||||||
|
# the sequence under the same key.
|
||||||
|
#
|
||||||
|
# e.g. {'foo': 'bar', 'baz': ['test1', 'test2']}
|
||||||
|
# => foo=bar&baz=test1&baz=test2
|
||||||
|
url += '?' + urlparse.urlencode(filters, doseq=True)
|
||||||
|
|
||||||
|
resp, body = self.get(url)
|
||||||
|
self.expected_success(expected_status_code, resp.status)
|
||||||
|
|
||||||
|
return rest_client.ResponseBody(resp, json.loads(body))
|
||||||
|
|
||||||
|
def delete_resource(self, obj, id, ignore_notfound=False):
|
||||||
|
try:
|
||||||
|
resp, _ = self.delete('/{obj}/{id}'.format(obj=obj, id=id))
|
||||||
|
return resp
|
||||||
|
except exceptions.NotFound:
|
||||||
|
if ignore_notfound:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def create_resource(self, obj, req_body, extra_headers={},
|
||||||
|
expected_status_code=200):
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
headers = dict(headers, **extra_headers)
|
||||||
|
url = '/%s' % obj
|
||||||
|
|
||||||
|
resp, body = self.post(url, json.dumps(req_body), headers=headers)
|
||||||
|
self.expected_success(expected_status_code, resp.status)
|
||||||
|
|
||||||
|
return rest_client.ResponseBody(resp, json.loads(body))
|
|
@ -1,26 +0,0 @@
|
||||||
# Copyright 2018 Samsung Electronics
|
|
||||||
# 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 trove_tempest_plugin.services.database.flavors_client import FlavorsClient
|
|
||||||
from trove_tempest_plugin.services.database.limits_client import LimitsClient
|
|
||||||
from trove_tempest_plugin.services.database.versions_client import (
|
|
||||||
VersionsClient)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'FlavorsClient',
|
|
||||||
'LimitsClient',
|
|
||||||
'VersionsClient'
|
|
||||||
]
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Copyright 2018 Samsung Electronics
|
|
||||||
# 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_serialization import jsonutils as json
|
|
||||||
from six.moves.urllib import parse as urllib
|
|
||||||
|
|
||||||
from tempest.lib.common import rest_client
|
|
||||||
|
|
||||||
|
|
||||||
class BaseClient(rest_client.RestClient):
|
|
||||||
|
|
||||||
def show_resource(self, uri, expected_status_code=200, **fields):
|
|
||||||
if fields:
|
|
||||||
# Encode provided dict of fields into a series of key=value pairs
|
|
||||||
# separated by '&' characters.
|
|
||||||
#
|
|
||||||
# The field value can be a sequence. Setting option doseq to True
|
|
||||||
# enforces producing individual key-value pair for each element of
|
|
||||||
# the sequence under the same key.
|
|
||||||
#
|
|
||||||
# e.g. {'foo': 'bar', 'baz': ['test1', 'test2']}
|
|
||||||
# => foo=bar&baz=test1&baz=test2
|
|
||||||
uri += '?' + urllib.urlencode(fields, doseq=True)
|
|
||||||
resp, body = self.get(uri)
|
|
||||||
self.expected_success(expected_status_code, resp.status)
|
|
||||||
body = json.loads(body)
|
|
||||||
return rest_client.ResponseBody(resp, body)
|
|
||||||
|
|
||||||
def list_resources(self, uri, expected_status_code=200, **filters):
|
|
||||||
if filters:
|
|
||||||
uri += '?' + urllib.urlencode(filters, doseq=True)
|
|
||||||
resp, body = self.get(uri)
|
|
||||||
self.expected_success(expected_status_code, resp.status)
|
|
||||||
body = json.loads(body)
|
|
||||||
return rest_client.ResponseBody(resp, body)
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from trove_tempest_plugin.services.database import base_client
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsClient(base_client.BaseClient):
|
|
||||||
|
|
||||||
uri = '/flavors'
|
|
||||||
|
|
||||||
def list_flavors(self):
|
|
||||||
return self.list_resources(self.uri)
|
|
||||||
|
|
||||||
def show_flavor(self, flavor_id):
|
|
||||||
uri = '%s/%s' % (self.uri, flavor_id)
|
|
||||||
return self.show_resource(uri)
|
|
|
@ -1,25 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from trove_tempest_plugin.services.database import base_client
|
|
||||||
|
|
||||||
|
|
||||||
class LimitsClient(base_client.BaseClient):
|
|
||||||
|
|
||||||
uri = '/limits'
|
|
||||||
|
|
||||||
def list_limits(self):
|
|
||||||
"""List all limits."""
|
|
||||||
return self.list_resources(self.uri)
|
|
|
@ -1,30 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from trove_tempest_plugin.services.database import base_client
|
|
||||||
|
|
||||||
|
|
||||||
class VersionsClient(base_client.BaseClient):
|
|
||||||
|
|
||||||
uri = ''
|
|
||||||
|
|
||||||
def __init__(self, auth_provider, service, region, **kwargs):
|
|
||||||
super(VersionsClient, self).__init__(
|
|
||||||
auth_provider, service, region, **kwargs)
|
|
||||||
self.skip_path()
|
|
||||||
|
|
||||||
def list_versions(self):
|
|
||||||
"""List all versions."""
|
|
||||||
return self.list_resources(self.uri)
|
|
|
@ -1,88 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from tempest.common import utils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from testtools import testcase as testtools
|
|
||||||
|
|
||||||
from trove_tempest_plugin.tests import base_test
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseFlavorsTest(base_test.BaseDatabaseTest):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
super(DatabaseFlavorsTest, cls).setup_clients()
|
|
||||||
cls.client = cls.database_flavors_client
|
|
||||||
|
|
||||||
@testtools.attr('smoke')
|
|
||||||
@decorators.idempotent_id('c94b825e-0132-4686-8049-8a4a2bc09525')
|
|
||||||
def test_get_db_flavor(self):
|
|
||||||
# The expected flavor details should be returned
|
|
||||||
flavor = (self.client.show_flavor(self.db_flavor_ref)
|
|
||||||
['flavor'])
|
|
||||||
self.assertEqual(self.db_flavor_ref, str(flavor['id']))
|
|
||||||
self.assertIn('ram', flavor)
|
|
||||||
self.assertIn('links', flavor)
|
|
||||||
self.assertIn('name', flavor)
|
|
||||||
|
|
||||||
@testtools.attr('smoke')
|
|
||||||
@decorators.idempotent_id('685025d6-0cec-4673-8a8d-995cb8e0d3bb')
|
|
||||||
def test_list_db_flavors(self):
|
|
||||||
flavor = (self.client.show_flavor(self.db_flavor_ref)
|
|
||||||
['flavor'])
|
|
||||||
# List of all flavors should contain the expected flavor
|
|
||||||
flavors = self.client.list_flavors()['flavors']
|
|
||||||
self.assertIn(flavor, flavors)
|
|
||||||
|
|
||||||
def _check_values(self, names, db_flavor, os_flavor, in_db=True):
|
|
||||||
for name in names:
|
|
||||||
self.assertIn(name, os_flavor)
|
|
||||||
if in_db:
|
|
||||||
self.assertIn(name, db_flavor)
|
|
||||||
self.assertEqual(str(db_flavor[name]), str(os_flavor[name]),
|
|
||||||
"DB flavor differs from OS on '%s' value"
|
|
||||||
% name)
|
|
||||||
else:
|
|
||||||
self.assertNotIn(name, db_flavor)
|
|
||||||
|
|
||||||
@testtools.attr('smoke')
|
|
||||||
@decorators.idempotent_id('afb2667f-4ec2-4925-bcb7-313fdcffb80d')
|
|
||||||
@utils.services('compute')
|
|
||||||
def test_compare_db_flavors_with_os(self):
|
|
||||||
db_flavors = self.client.list_flavors()['flavors']
|
|
||||||
os_flavors = (self.os_flavors_client.list_flavors(detail=True)
|
|
||||||
['flavors'])
|
|
||||||
self.assertEqual(len(os_flavors), len(db_flavors),
|
|
||||||
"OS flavors %s do not match DB flavors %s" %
|
|
||||||
(os_flavors, db_flavors))
|
|
||||||
for os_flavor in os_flavors:
|
|
||||||
db_flavor =\
|
|
||||||
self.client.show_flavor(os_flavor['id'])['flavor']
|
|
||||||
if db_flavor['id']:
|
|
||||||
self.assertIn('id', db_flavor)
|
|
||||||
self.assertEqual(str(db_flavor['id']), str(os_flavor['id']),
|
|
||||||
"DB flavor id differs from OS flavor id value"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.assertIn('str_id', db_flavor)
|
|
||||||
self.assertEqual(db_flavor['str_id'], str(os_flavor['id']),
|
|
||||||
"DB flavor id differs from OS flavor id value"
|
|
||||||
)
|
|
||||||
|
|
||||||
self._check_values(['name', 'ram', 'vcpus',
|
|
||||||
'disk'], db_flavor, os_flavor)
|
|
||||||
self._check_values(['swap'], db_flavor, os_flavor,
|
|
||||||
in_db=False)
|
|
|
@ -1,36 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions as lib_exc
|
|
||||||
from testtools import testcase as testtools
|
|
||||||
|
|
||||||
from trove_tempest_plugin.tests import base_test
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseFlavorsNegativeTest(base_test.BaseDatabaseTest):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
super(DatabaseFlavorsNegativeTest, cls).setup_clients()
|
|
||||||
cls.client = cls.database_flavors_client
|
|
||||||
|
|
||||||
@testtools.attr('negative')
|
|
||||||
@decorators.idempotent_id('f8e7b721-373f-4a64-8e9c-5327e975af3e')
|
|
||||||
def test_get_non_existent_db_flavor(self):
|
|
||||||
# flavor details are not returned for non-existent flavors
|
|
||||||
self.assertRaises(lib_exc.NotFound,
|
|
||||||
self.client.show_flavor, -1)
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from testtools import testcase as testtools
|
|
||||||
|
|
||||||
from trove_tempest_plugin.tests import base_test
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseLimitsTest(base_test.BaseDatabaseTest):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(DatabaseLimitsTest, cls).resource_setup()
|
|
||||||
cls.client = cls.database_limits_client
|
|
||||||
|
|
||||||
@testtools.attr('smoke')
|
|
||||||
@decorators.idempotent_id('73024538-f316-4829-b3e9-b459290e137a')
|
|
||||||
def test_absolute_limits(self):
|
|
||||||
# Test to verify if all absolute limit parameters are
|
|
||||||
# present when verb is ABSOLUTE
|
|
||||||
limits = self.client.list_limits()['limits']
|
|
||||||
expected_abs_limits = ['max_backups', 'max_volumes',
|
|
||||||
'max_instances', 'verb']
|
|
||||||
absolute_limit = [l for l in limits
|
|
||||||
if l['verb'] == 'ABSOLUTE']
|
|
||||||
self.assertEqual(1, len(absolute_limit), "One ABSOLUTE limit "
|
|
||||||
"verb is allowed. Fetched %s"
|
|
||||||
% len(absolute_limit))
|
|
||||||
actual_abs_limits = absolute_limit[0].keys()
|
|
||||||
missing_abs_limit = set(expected_abs_limits) - set(actual_abs_limits)
|
|
||||||
self.assertEmpty(missing_abs_limit,
|
|
||||||
"Failed to find the following absolute limit(s)"
|
|
||||||
" in a fetched list: %s" %
|
|
||||||
', '.join(str(a) for a in missing_abs_limit))
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from testtools import testcase as testtools
|
|
||||||
|
|
||||||
from trove_tempest_plugin.tests import base_test
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseVersionsTest(base_test.BaseDatabaseTest):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
super(DatabaseVersionsTest, cls).setup_clients()
|
|
||||||
cls.client = cls.database_versions_client
|
|
||||||
|
|
||||||
@testtools.attr('smoke')
|
|
||||||
@decorators.idempotent_id('6952cd77-90cd-4dca-bb60-8e2c797940cf')
|
|
||||||
def test_list_db_versions(self):
|
|
||||||
versions = self.client.list_versions()['versions']
|
|
||||||
self.assertTrue(len(versions) > 0, "No database versions found")
|
|
||||||
# List of all versions should contain the current version, and there
|
|
||||||
# should only be one 'current' version
|
|
||||||
current_versions = list()
|
|
||||||
for version in versions:
|
|
||||||
if 'CURRENT' == version['status']:
|
|
||||||
current_versions.append(version['id'])
|
|
||||||
self.assertEqual(1, len(current_versions))
|
|
||||||
self.assertIn(self.db_current_version, current_versions)
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
# Copyright 2014 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.
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_service import loopingcall
|
||||||
|
import tenacity
|
||||||
|
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib.common.utils import test_utils
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
from tempest import test
|
||||||
|
|
||||||
|
from trove_tempest_plugin.tests import utils
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTroveTest(test.BaseTestCase):
|
||||||
|
credentials = ('admin', 'primary')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_resource_name(cls, resource_type):
|
||||||
|
prefix = "trove-tempest-%s" % cls.__name__
|
||||||
|
return data_utils.rand_name(resource_type, prefix=prefix)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def skip_checks(cls):
|
||||||
|
super(BaseTroveTest, cls).skip_checks()
|
||||||
|
|
||||||
|
if not CONF.service_available.trove:
|
||||||
|
raise cls.skipException("Database service is not available.")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_clients(cls):
|
||||||
|
super(BaseTroveTest, cls).setup_clients()
|
||||||
|
|
||||||
|
cls.client = cls.os_primary.database.TroveClient()
|
||||||
|
cls.admin_client = cls.os_admin.database.TroveClient()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_credentials(cls):
|
||||||
|
# Do not create network resources automatically.
|
||||||
|
cls.set_network_resources()
|
||||||
|
super(BaseTroveTest, cls).setup_credentials()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@tenacity.retry(
|
||||||
|
retry=tenacity.retry_if_exception_type(exceptions.Conflict),
|
||||||
|
wait=tenacity.wait_incrementing(1, 1, 5),
|
||||||
|
stop=tenacity.stop_after_attempt(15)
|
||||||
|
)
|
||||||
|
def _delete_network(cls, net_id):
|
||||||
|
"""Make sure the network is deleted.
|
||||||
|
|
||||||
|
Neutron can be slow to clean up ports from the subnets/networks.
|
||||||
|
Retry this delete a few times if we get a "Conflict" error to give
|
||||||
|
neutron time to fully cleanup the ports.
|
||||||
|
"""
|
||||||
|
networks_client = cls.os_primary.networks_client
|
||||||
|
try:
|
||||||
|
networks_client.delete_network(net_id)
|
||||||
|
except Exception:
|
||||||
|
LOG.error('Unable to delete network %s', net_id)
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@tenacity.retry(
|
||||||
|
retry=tenacity.retry_if_exception_type(exceptions.Conflict),
|
||||||
|
wait=tenacity.wait_incrementing(1, 1, 5),
|
||||||
|
stop=tenacity.stop_after_attempt(15)
|
||||||
|
)
|
||||||
|
def _delete_subnet(cls, subnet_id):
|
||||||
|
"""Make sure the subnet is deleted.
|
||||||
|
|
||||||
|
Neutron can be slow to clean up ports from the subnets/networks.
|
||||||
|
Retry this delete a few times if we get a "Conflict" error to give
|
||||||
|
neutron time to fully cleanup the ports.
|
||||||
|
"""
|
||||||
|
subnets_client = cls.os_primary.subnets_client
|
||||||
|
try:
|
||||||
|
subnets_client.delete_subnet(subnet_id)
|
||||||
|
except Exception:
|
||||||
|
LOG.error('Unable to delete subnet %s', subnet_id)
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _create_network(cls):
|
||||||
|
"""Create database instance network."""
|
||||||
|
networks_client = cls.os_primary.networks_client
|
||||||
|
subnets_client = cls.os_primary.subnets_client
|
||||||
|
routers_client = cls.os_primary.routers_client
|
||||||
|
|
||||||
|
network_kwargs = {"name": cls.get_resource_name("network")}
|
||||||
|
result = networks_client.create_network(**network_kwargs)
|
||||||
|
LOG.info('Private network created: %s', result['network'])
|
||||||
|
cls.private_network = result['network']["id"]
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
utils.wait_for_removal,
|
||||||
|
cls._delete_network,
|
||||||
|
networks_client.show_network,
|
||||||
|
cls.private_network
|
||||||
|
)
|
||||||
|
|
||||||
|
subnet_kwargs = {
|
||||||
|
'name': cls.get_resource_name("subnet"),
|
||||||
|
'network_id': cls.private_network,
|
||||||
|
'cidr': CONF.database.subnet_cidr,
|
||||||
|
'ip_version': 4
|
||||||
|
}
|
||||||
|
result = subnets_client.create_subnet(**subnet_kwargs)
|
||||||
|
subnet_id = result['subnet']['id']
|
||||||
|
LOG.info('Private subnet created: %s', result['subnet'])
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
utils.wait_for_removal,
|
||||||
|
cls._delete_subnet,
|
||||||
|
subnets_client.show_subnet,
|
||||||
|
subnet_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# In dev node, Trove instance needs to connect with control host
|
||||||
|
router_params = {
|
||||||
|
'name': cls.get_resource_name("router"),
|
||||||
|
'external_gateway_info': {
|
||||||
|
"network_id": CONF.network.public_network_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = routers_client.create_router(**router_params)
|
||||||
|
router_id = result['router']['id']
|
||||||
|
LOG.info('Private router created: %s', result['router'])
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
utils.wait_for_removal,
|
||||||
|
routers_client.delete_router,
|
||||||
|
routers_client.show_router,
|
||||||
|
router_id
|
||||||
|
)
|
||||||
|
|
||||||
|
routers_client.add_router_interface(router_id, subnet_id=subnet_id)
|
||||||
|
LOG.info('Subnet %s added to the router %s', subnet_id, router_id)
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
routers_client.remove_router_interface,
|
||||||
|
router_id,
|
||||||
|
subnet_id=subnet_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
super(BaseTroveTest, cls).resource_setup()
|
||||||
|
|
||||||
|
# Create network for database instance, use cls.private_network as the
|
||||||
|
# network ID.
|
||||||
|
cls._create_network()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_instance(cls, database="test_db", username="test_user",
|
||||||
|
password="password"):
|
||||||
|
"""Create database instance.
|
||||||
|
|
||||||
|
Creating database instance is time-consuming, so we define this method
|
||||||
|
as a class method, which means the instance is shared in a single
|
||||||
|
TestCase. According to
|
||||||
|
https://docs.openstack.org/tempest/latest/write_tests.html#adding-a-new-testcase,
|
||||||
|
all test methods within a TestCase are assumed to be executed serially.
|
||||||
|
"""
|
||||||
|
name = cls.get_resource_name("instance")
|
||||||
|
body = {
|
||||||
|
"instance": {
|
||||||
|
"name": name,
|
||||||
|
"flavorRef": CONF.database.flavor_id,
|
||||||
|
"volume": {
|
||||||
|
"size": 1,
|
||||||
|
"type": CONF.database.volume_type
|
||||||
|
},
|
||||||
|
"databases": [
|
||||||
|
{
|
||||||
|
"name": database
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"name": username,
|
||||||
|
"password": password,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datastore": {
|
||||||
|
"type": CONF.database.datastore_type,
|
||||||
|
"version": CONF.database.datastore_version
|
||||||
|
},
|
||||||
|
"nics": [
|
||||||
|
{
|
||||||
|
"net-id": cls.private_network
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = cls.client.create_resource("instances", body)
|
||||||
|
instance_id = res["instance"]["id"]
|
||||||
|
cls.addClassResourceCleanup(cls.wait_for_instance_status, instance_id,
|
||||||
|
need_delete=True, status="DELETED")
|
||||||
|
|
||||||
|
return instance_id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wait_for_instance_status(cls, id, status="ACTIVE", need_delete=False):
|
||||||
|
def _wait():
|
||||||
|
try:
|
||||||
|
res = cls.client.get_resource("instances", id)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
if need_delete or status == "DELETED":
|
||||||
|
raise loopingcall.LoopingCallDone()
|
||||||
|
return
|
||||||
|
|
||||||
|
if res["instance"]["status"] == status:
|
||||||
|
raise loopingcall.LoopingCallDone()
|
||||||
|
elif status != "ERROR" and res["instance"]["status"] == "ERROR":
|
||||||
|
# If instance status goes to ERROR but is not expected, stop
|
||||||
|
# waiting
|
||||||
|
message = "Instance status is ERROR."
|
||||||
|
caller = test_utils.find_test_caller()
|
||||||
|
if caller:
|
||||||
|
message = '({caller}) {message}'.format(caller=caller,
|
||||||
|
message=message)
|
||||||
|
raise exceptions.UnexpectedResponseCode(message)
|
||||||
|
|
||||||
|
if need_delete:
|
||||||
|
cls.client.delete_resource("instances", id, ignore_notfound=True)
|
||||||
|
|
||||||
|
timer = loopingcall.FixedIntervalWithTimeoutLoopingCall(_wait)
|
||||||
|
try:
|
||||||
|
timer.start(interval=10,
|
||||||
|
timeout=CONF.database.database_build_timeout).wait()
|
||||||
|
except loopingcall.LoopingCallTimeOut:
|
||||||
|
message = ("Instance %s is not in the expected status: %s" %
|
||||||
|
(id, status))
|
||||||
|
caller = test_utils.find_test_caller()
|
||||||
|
if caller:
|
||||||
|
message = '({caller}) {message}'.format(caller=caller,
|
||||||
|
message=message)
|
||||||
|
raise exceptions.TimeoutException(message)
|
|
@ -1,86 +0,0 @@
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from tempest import config
|
|
||||||
from tempest import test
|
|
||||||
|
|
||||||
from trove_tempest_plugin import clients
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class BaseDatabaseTest(test.BaseTestCase):
|
|
||||||
"""Base test case class.
|
|
||||||
|
|
||||||
Includes parts common to API and scenario tests:
|
|
||||||
* test case callbacks,
|
|
||||||
* service clients initialization.
|
|
||||||
"""
|
|
||||||
|
|
||||||
credentials = ['primary']
|
|
||||||
client_manager = clients.Manager
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(BaseDatabaseTest, cls).skip_checks()
|
|
||||||
if not CONF.service_available.trove:
|
|
||||||
skip_msg = ("%s skipped as trove is not available" % cls.__name__)
|
|
||||||
raise cls.skipException(skip_msg)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
"""Setups service clients.
|
|
||||||
|
|
||||||
Tempest provides a convenient fabrication interface, which can be used
|
|
||||||
to produce instances of clients configured with the required parameters
|
|
||||||
and a selected set of credentials. Thanks to this interface, the
|
|
||||||
complexity of client initialization is hidden from the developer. All
|
|
||||||
parameters such as "catalog_type", "auth_provider", "build_timeout"
|
|
||||||
etc. are read from Tempest configuration and then automatically
|
|
||||||
installed in the clients.
|
|
||||||
|
|
||||||
The fabrication interface is enabled through the client manager, which
|
|
||||||
is hooked to the class by the "client_manager" property.
|
|
||||||
|
|
||||||
To initialize a new client, one need to specify the set of credentials
|
|
||||||
(primary, admin) to be used and the category of client (eg compute,
|
|
||||||
image, database, etc.). Together, they constitute a proxy for the
|
|
||||||
fabricators of specific client classes from a given category.
|
|
||||||
|
|
||||||
For example, initializing a new flavors client from the database
|
|
||||||
category with primary privileges boils down to the following call:
|
|
||||||
|
|
||||||
flavors_client = cls.os_primary.database.FlavorsClient()
|
|
||||||
|
|
||||||
In order to initialize a new networks client from the compute category
|
|
||||||
with administrator privilages:
|
|
||||||
|
|
||||||
networks_client = cls.os_admin.compute.NetworksClient()
|
|
||||||
|
|
||||||
Note, that selected set of credentials must be declared in the
|
|
||||||
"credentials" property of this class.
|
|
||||||
"""
|
|
||||||
super(BaseDatabaseTest, cls).setup_clients()
|
|
||||||
cls.database_flavors_client = cls.os_primary.database.FlavorsClient()
|
|
||||||
cls.os_flavors_client = cls.os_primary.compute.FlavorsClient()
|
|
||||||
cls.database_limits_client = cls.os_primary.database.LimitsClient()
|
|
||||||
cls.database_versions_client = cls.os_primary.database.VersionsClient()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(BaseDatabaseTest, cls).resource_setup()
|
|
||||||
cls.catalog_type = CONF.database.catalog_type
|
|
||||||
cls.db_flavor_ref = CONF.database.db_flavor_ref
|
|
||||||
cls.db_current_version = CONF.database.db_current_version
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Copyright 2019 Catalyst Cloud Ltd.
|
||||||
|
#
|
||||||
|
# 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 tempest.lib import decorators
|
||||||
|
|
||||||
|
from trove_tempest_plugin.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestInstanceBasic(base.BaseTroveTest):
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
super(TestInstanceBasic, cls).resource_setup()
|
||||||
|
|
||||||
|
cls.instance_id = cls.create_instance()
|
||||||
|
cls.wait_for_instance_status(cls.instance_id)
|
||||||
|
|
||||||
|
@decorators.idempotent_id("40cf38ce-cfbf-11e9-8760-1458d058cfb2")
|
||||||
|
def test_database_access(self):
|
||||||
|
pass
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright 2019 Catalyst Cloud Ltd.
|
||||||
|
#
|
||||||
|
# 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 tempest.lib import exceptions
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_removal(delete_func, show_func, *args, **kwargs):
|
||||||
|
"""Call the delete function, then wait for it to be 'NotFound'
|
||||||
|
|
||||||
|
:param delete_func: The delete function to call.
|
||||||
|
:param show_func: The show function to call looking for 'NotFound'.
|
||||||
|
:param ID: The ID of the object to delete/show.
|
||||||
|
:raises TimeoutException: The object did not achieve the status or ERROR in
|
||||||
|
the check_timeout period.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
check_timeout = 15
|
||||||
|
try:
|
||||||
|
delete_func(*args, **kwargs)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return
|
||||||
|
|
||||||
|
start = int(time.time())
|
||||||
|
LOG.info('Waiting for object to be NotFound')
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
show_func(*args, **kwargs)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return
|
||||||
|
|
||||||
|
if int(time.time()) - start >= check_timeout:
|
||||||
|
message = ('%s did not raise NotFound in %s seconds.' %
|
||||||
|
(show_func.__name__, check_timeout))
|
||||||
|
raise exceptions.TimeoutException(message)
|
||||||
|
time.sleep(3)
|
Loading…
Reference in New Issue