Reinstate removed api tests
Story: 2008365 Task: 41273 Change-Id: Ib08bf8951352c978b88a4746aa7b44e0b8cec177
This commit is contained in:
parent
2581bc079a
commit
0716623b79
33
.zuul.yaml
33
.zuul.yaml
|
@ -25,14 +25,13 @@
|
|||
This job is for stable branch prior to Ussuri for testing
|
||||
on py2.
|
||||
required-projects:
|
||||
- openstack/neutron
|
||||
- openstack/trove
|
||||
- openstack/trove-tempest-plugin
|
||||
- openstack/tempest
|
||||
- opendev.org/openstack/neutron
|
||||
- opendev.org/openstack/trove
|
||||
- opendev.org/openstack/trove-tempest-plugin
|
||||
- opendev.org/openstack/tempest
|
||||
vars:
|
||||
tox_envlist: all
|
||||
devstack_localrc:
|
||||
TEMPEST_PLUGINS: /opt/stack/trove-tempest-plugin
|
||||
USE_PYTHON3: False
|
||||
devstack_plugins:
|
||||
trove: https://opendev.org/openstack/trove
|
||||
|
@ -51,14 +50,15 @@
|
|||
- job:
|
||||
name: trove-tempest-plugin
|
||||
parent: devstack-tempest
|
||||
nodeset: trove-ubuntu-bionic
|
||||
timeout: 7800
|
||||
description: |
|
||||
This job is for testing on py3 which is Ussuri onwards.
|
||||
required-projects: &base_required_projects
|
||||
- openstack/python-troveclient
|
||||
- openstack/trove
|
||||
- openstack/trove-tempest-plugin
|
||||
- openstack/tempest
|
||||
- opendev.org/openstack/python-troveclient
|
||||
- opendev.org/openstack/trove
|
||||
- opendev.org/openstack/trove-tempest-plugin
|
||||
- opendev.org/openstack/tempest
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/.*$
|
||||
|
@ -67,26 +67,16 @@
|
|||
vars: &base_vars
|
||||
tox_envlist: all
|
||||
tempest_concurrency: 1
|
||||
devstack_localrc:
|
||||
TEMPEST_PLUGINS: /opt/stack/trove-tempest-plugin
|
||||
USE_PYTHON3: true
|
||||
devstack_local_conf:
|
||||
post-config:
|
||||
$TROVE_CONF:
|
||||
DEFAULT:
|
||||
usage_timeout: 1800
|
||||
usage_timeout: 600
|
||||
devstack_plugins:
|
||||
trove: https://opendev.org/openstack/trove.git
|
||||
devstack_services:
|
||||
etcd3: 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
|
||||
|
@ -98,11 +88,12 @@
|
|||
s-object: true
|
||||
s-proxy: true
|
||||
tempest: true
|
||||
tempest_test_regex: ^trove_tempest_plugin\.tests.scenario\.test_instance_basic\.TestInstanceBasicMySQL\.test_database_access
|
||||
tempest_test_regex: ^trove_tempest_plugin
|
||||
|
||||
- job:
|
||||
name: trove-tempest-ipv6-only
|
||||
parent: devstack-tempest-ipv6
|
||||
nodeset: trove-ubuntu-bionic
|
||||
description: |
|
||||
Trove devstack tempest tests job for IPv6-only deployment
|
||||
required-projects: *base_required_projects
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# 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.common.utils import data_utils
|
||||
|
||||
|
||||
def rand_name():
|
||||
"""Return a safe name to use for a user or a DB name
|
||||
|
||||
Some datastores have limits on size and characters
|
||||
"""
|
||||
return data_utils.rand_name().replace('-', '')[:16]
|
|
@ -0,0 +1,112 @@
|
|||
# Copyright 2019 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 re
|
||||
import time
|
||||
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
def wait_for_db_instance_status(client, instance_id, status,
|
||||
failure_pattern='ERROR'):
|
||||
"""Wait for a db instance to reach a given status"""
|
||||
start = int(time.time())
|
||||
fail_regexp = re.compile(failure_pattern)
|
||||
if status == 'ACTIVE':
|
||||
status = ['ACTIVE', 'HEALTHY']
|
||||
else:
|
||||
status = [status]
|
||||
while True:
|
||||
try:
|
||||
body = client.show_db_instance(instance_id)['instance']
|
||||
except lib_exc.NotFound:
|
||||
if status == ['DELETE_COMPLETE']:
|
||||
return
|
||||
instance_name = body['name']
|
||||
instance_status = body['status']
|
||||
if instance_status in status:
|
||||
return body
|
||||
if fail_regexp.search(instance_status):
|
||||
raise KeyError("Instance in ERROR state")
|
||||
if int(time.time()) - start >= client.build_timeout:
|
||||
message = ('DB Instance %s failed to reach %s status'
|
||||
'(current: %s) within the required time (%s s).' %
|
||||
(instance_name, status, instance_status,
|
||||
client.build_timeout))
|
||||
raise lib_exc.TimeoutException(message)
|
||||
|
||||
time.sleep(client.build_interval)
|
||||
|
||||
|
||||
def wait_for_db_instance_decommission(client, instance_id):
|
||||
"""Wait for a db instance decomission"""
|
||||
start = int(time.time())
|
||||
while True:
|
||||
try:
|
||||
client.show_db_instance(instance_id)['instance']
|
||||
except lib_exc.NotFound:
|
||||
return
|
||||
if int(time.time()) - start >= client.build_timeout:
|
||||
message = ('DB Instance %s deletion failed '
|
||||
'within the required time (%s s).' %
|
||||
(instance_id, client.build_timeout))
|
||||
raise lib_exc.TimeoutException(message)
|
||||
|
||||
time.sleep(client.build_interval)
|
||||
|
||||
|
||||
def wait_for_backup_status(client, backup_id, status,
|
||||
failure_pattern='FAILED'):
|
||||
"""Wait for a backup to reach a given status"""
|
||||
start = int(time.time())
|
||||
fail_regexp = re.compile(failure_pattern)
|
||||
|
||||
while True:
|
||||
try:
|
||||
body = client.show_backup(backup_id)['backup']
|
||||
except lib_exc.NotFound:
|
||||
if status == 'DELETE_COMPLETE':
|
||||
return
|
||||
backup_name = body['name']
|
||||
backup_status = body['status']
|
||||
if backup_status == status:
|
||||
return body
|
||||
if fail_regexp.search(backup_status):
|
||||
raise KeyError("Backup in FAILED state")
|
||||
if int(time.time()) - start >= client.build_timeout:
|
||||
message = ('Backup %s failed to reach %s status'
|
||||
'(current: %s) within the required time (%s s).' %
|
||||
(backup_name, status, backup_status,
|
||||
client.build_timeout))
|
||||
raise lib_exc.TimeoutException(message)
|
||||
|
||||
time.sleep(client.build_interval)
|
||||
|
||||
|
||||
def wait_for_backup_delete(client, backup_id):
|
||||
"""Wait for a backup to be deleted"""
|
||||
start = int(time.time())
|
||||
while True:
|
||||
try:
|
||||
client.show_backup(backup_id)['backup']
|
||||
except lib_exc.NotFound:
|
||||
return
|
||||
if int(time.time()) - start >= client.build_timeout:
|
||||
message = ('Backup %s deletion failed '
|
||||
'within the required time (%s s).' %
|
||||
(backup_id, client.build_timeout))
|
||||
raise lib_exc.TimeoutException(message)
|
||||
|
||||
time.sleep(client.build_interval)
|
|
@ -38,10 +38,28 @@ DatabaseGroup = [
|
|||
'internalURL'],
|
||||
help="The endpoint type to use for the Database service."
|
||||
),
|
||||
cfg.StrOpt('db_current_version',
|
||||
default="v1.0",
|
||||
help="Current database version to use in database tests."),
|
||||
cfg.ListOpt(
|
||||
'enabled_datastores',
|
||||
default=['mysql']
|
||||
),
|
||||
cfg.StrOpt('datastore_type',
|
||||
default="MySQL",
|
||||
help="Type of the Database"),
|
||||
cfg.StrOpt('datastore_version',
|
||||
default=None,
|
||||
help="Specific datastore version to use (optional)"),
|
||||
cfg.StrOpt('availability_zone',
|
||||
default='nova',
|
||||
help="Availability zone of the db instance to use"),
|
||||
cfg.IntOpt('volume_size',
|
||||
default=1,
|
||||
help="Volume size for the db instances"),
|
||||
cfg.StrOpt('dns_name_server',
|
||||
default=None,
|
||||
help="The DNS server used to query trove instance domain name"),
|
||||
cfg.DictOpt(
|
||||
'default_datastore_versions',
|
||||
default={'mysql': '5.7.29'},
|
||||
|
@ -54,29 +72,33 @@ DatabaseGroup = [
|
|||
'upgrade.',
|
||||
),
|
||||
cfg.IntOpt('database_build_timeout',
|
||||
default=1800,
|
||||
default=300,
|
||||
help='Timeout in seconds to wait for a database instance to '
|
||||
'build.'),
|
||||
cfg.IntOpt(
|
||||
'database_restore_timeout',
|
||||
default=3600,
|
||||
default=120,
|
||||
help='Timeout in seconds to wait for a database instance to '
|
||||
'be restored.'
|
||||
),
|
||||
cfg.IntOpt(
|
||||
'backup_wait_timeout',
|
||||
default=600,
|
||||
default=120,
|
||||
help='Timeout in seconds to wait for a backup to be completed.'
|
||||
),
|
||||
cfg.StrOpt(
|
||||
'flavor_id',
|
||||
default="d2",
|
||||
help="The Nova flavor ID used for creating database instance."
|
||||
help="The Nova flavor ID used for creating database instance.",
|
||||
deprecated_group='database',
|
||||
deprecated_name='db_flavor_ref',
|
||||
),
|
||||
cfg.StrOpt(
|
||||
'resize_flavor_id',
|
||||
default="d3",
|
||||
help="The Nova flavor ID used for resizing database instance."
|
||||
help="The Nova flavor ID used for resizing database instance.",
|
||||
deprecated_group='database',
|
||||
deprecated_name='db_flavor_ref_alt',
|
||||
),
|
||||
cfg.StrOpt(
|
||||
'shared_network',
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# 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_serialization import jsonutils as json
|
||||
from six.moves.urllib import parse as urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseBackupsClient(rest_client.RestClient):
|
||||
|
||||
def list_backups(self, params=None):
|
||||
"""List all available backups."""
|
||||
url = 'backups'
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_backup(self, instance_id, name, description=None, parent=None,
|
||||
incremental=False):
|
||||
"""Create a backup."""
|
||||
url = 'backups'
|
||||
data = {'instance': instance_id,
|
||||
'name': name,
|
||||
'incremental': int(incremental)}
|
||||
post_body = json.dumps({"backup": data})
|
||||
resp, body = self.post(url, body=post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_backup(self, backup_id):
|
||||
"""Delete the backup"""
|
||||
url = 'backups/%s' % backup_id
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(202, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_backup(self, backup_id):
|
||||
"""Show backups."""
|
||||
url = 'backups/%s' % backup_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,31 @@
|
|||
# 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_serialization import jsonutils as json
|
||||
from six.moves.urllib import parse as urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseDatastoresClient(rest_client.RestClient):
|
||||
|
||||
def list_db_datastores(self, params=None):
|
||||
"""List all available datastores."""
|
||||
url = 'datastores'
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,37 @@
|
|||
# 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_serialization import jsonutils as json
|
||||
from six.moves import urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseFlavorsClient(rest_client.RestClient):
|
||||
|
||||
def list_db_flavors(self, params=None):
|
||||
url = 'flavors'
|
||||
if params:
|
||||
url += '?%s' % urllib.parse.urlencode(params)
|
||||
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_db_flavor(self, db_flavor_id):
|
||||
resp, body = self.get("flavors/%s" % db_flavor_id)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,234 @@
|
|||
# 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.
|
||||
|
||||
import time
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
from six.moves.urllib import parse as urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseInstancesClient(rest_client.RestClient):
|
||||
|
||||
def list_db_instances(self, params=None):
|
||||
"""List all available instances."""
|
||||
url = 'instances'
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_db_instance(self, params):
|
||||
"""Create an instance."""
|
||||
url = 'instances'
|
||||
headers = self.get_headers()
|
||||
resp, body = self.post(url, headers=headers, body=params)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_db_instance(self, instance_id):
|
||||
"""Show the db instance"""
|
||||
url = 'instances/%s' % instance_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_db_instance(self, instance_id, **kwargs):
|
||||
"""Updates the db instance"""
|
||||
url = 'instances/%s' % instance_id
|
||||
post_body = json.dumps({'instance': kwargs})
|
||||
resp, body = self.patch(url, post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def _action(self, instance_id, action_name, **kwargs):
|
||||
post_body = json.dumps({action_name: kwargs})
|
||||
resp, body = self.post('instances/%s/action' % instance_id,
|
||||
post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def restart_db_instance(self, instance_id):
|
||||
"""Restart the db instance"""
|
||||
return self._action(instance_id, "restart")
|
||||
|
||||
def upgrade_db_instance(self, instance_id, datastore_version):
|
||||
body = json.dumps({"instance": {
|
||||
"datastore_version": datastore_version}})
|
||||
resp, body = self.patch('instances/%s' % instance_id,
|
||||
body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def resize_db_instance(self, instance_id, flavor_id):
|
||||
return self._action(instance_id, "resize", flavorRef=flavor_id)
|
||||
|
||||
def resize_db_instance_volume(self, instance_id, new_size):
|
||||
return self._action(instance_id, "resize", volume={'size': new_size})
|
||||
|
||||
def delete_db_instance(self, instance_id):
|
||||
"""Delete the db instance"""
|
||||
url = 'instances/%s' % instance_id
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(202, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_databases(self, instance_id):
|
||||
"""List all databases on an instance."""
|
||||
resp, body = self.get('instances/%s/databases' % instance_id)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_database(self, instance_id, name):
|
||||
"""Creates a database on an instance"""
|
||||
post_body = json.dumps({"databases": [{"name": name}]})
|
||||
resp, body = self.post('instances/%s/databases' % instance_id,
|
||||
post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
time.sleep(2)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_database(self, instance_id, name):
|
||||
"""Deletes a database on an instance"""
|
||||
resp, body = self.delete('instances/%s/databases/%s' % (instance_id,
|
||||
name))
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
time.sleep(2)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def root_show(self, instance_id):
|
||||
"""Shows if root has ever been enabled on an instance"""
|
||||
url = 'instances/%s/root' % instance_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def root_enable(self, instance_id):
|
||||
"""Enables root on an instance"""
|
||||
url = 'instances/%s/root' % instance_id
|
||||
resp, body = self.post(url, body=json.dumps({}))
|
||||
self.expected_success(200, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def root_disable(self, instance_id):
|
||||
"""Disables root on an instance"""
|
||||
url = 'instances/%s/root' % instance_id
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(204, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_users(self, instance_id):
|
||||
"""List all users on an instance."""
|
||||
resp, body = self.get('instances/%s/users' % instance_id)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_user(self, instance_id, name):
|
||||
"""Get a user on an instance."""
|
||||
resp, body = self.get('instances/%s/users/%s' % (instance_id, name))
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_user(self, instance_id, name, password, databases=[]):
|
||||
"""Creates a user on an instance"""
|
||||
post_body = json.dumps({"users": [{"name": name,
|
||||
"password": password,
|
||||
"databases": databases}]})
|
||||
resp, body = self.post('instances/%s/users' % instance_id,
|
||||
post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
time.sleep(2)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_user(self, instance_id, name, **kwargs):
|
||||
"""Updates the user"""
|
||||
url = 'instances/%s/users/%s' % (instance_id, name)
|
||||
post_body = json.dumps({'user': kwargs})
|
||||
resp, body = self.put(url, post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
time.sleep(2)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_user(self, instance_id, name):
|
||||
"""Updates the user"""
|
||||
url = 'instances/%s/users/%s' % (instance_id, name)
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
time.sleep(2)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def grant_user_access(self, instance_id, name, databases):
|
||||
"""Grants a user access to a database"""
|
||||
url = 'instances/%s/users/%s/databases' % (instance_id, name)
|
||||
databases = [{'name': x} for x in databases]
|
||||
post_body = json.dumps({'databases': databases})
|
||||
resp, body = self.put(url, post_body)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def revoke_user_access(self, instance_id, name, database):
|
||||
"""Revokes a user access to a database"""
|
||||
url = 'instances/%s/users/%s/databases/%s' % (instance_id, name,
|
||||
database)
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(202, resp.status)
|
||||
if body:
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_user_access(self, instance_id, name):
|
||||
"""Shows access details of a user of an instanceqq"""
|
||||
url = 'instances/%s/users/%s/databases' % (instance_id, name)
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_backups(self, instance_id):
|
||||
"""List all backups on an instance."""
|
||||
resp, body = self.get('instances/%s/backups' % instance_id)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,31 @@
|
|||
# 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_serialization import jsonutils as json
|
||||
from six.moves.urllib import parse as urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseLimitsClient(rest_client.RestClient):
|
||||
|
||||
def list_db_limits(self, params=None):
|
||||
"""List all limits."""
|
||||
url = 'limits'
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,37 @@
|
|||
# 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_serialization import jsonutils as json
|
||||
from six.moves.urllib import parse as urllib
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class DatabaseVersionsClient(rest_client.RestClient):
|
||||
|
||||
def __init__(self, auth_provider, service, region, **kwargs):
|
||||
super(DatabaseVersionsClient, self).__init__(
|
||||
auth_provider, service, region, **kwargs)
|
||||
self.skip_path()
|
||||
|
||||
def list_db_versions(self, params=None):
|
||||
"""List all versions."""
|
||||
url = ''
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
|
@ -0,0 +1,158 @@
|
|||
# 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.
|
||||
|
||||
import json
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib.common.utils import test_utils
|
||||
import tempest.test
|
||||
|
||||
from trove_tempest_plugin.common import waiters
|
||||
from trove_tempest_plugin.services.database.json import backups_client
|
||||
from trove_tempest_plugin.services.database.json import datastores_client
|
||||
from trove_tempest_plugin.services.database.json import flavors_client
|
||||
from trove_tempest_plugin.services.database.json import instances_client
|
||||
from trove_tempest_plugin.services.database.json import limits_client
|
||||
from trove_tempest_plugin.services.database.json import versions_client
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseDatabaseTest(tempest.test.BaseTestCase):
|
||||
"""Base test case class for all Database API tests."""
|
||||
|
||||
credentials = ['primary']
|
||||
|
||||
@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):
|
||||
super(BaseDatabaseTest, cls).setup_clients()
|
||||
default_params = config.service_client_config()
|
||||
|
||||
# NOTE: Tempest uses timeout values of compute API if project specific
|
||||
# timeout values don't exist.
|
||||
default_params_with_timeout_values = {
|
||||
'build_interval': CONF.compute.build_interval,
|
||||
'build_timeout': CONF.database.database_build_timeout
|
||||
}
|
||||
default_params_with_timeout_values.update(default_params)
|
||||
cls.database_flavors_client = flavors_client.DatabaseFlavorsClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
cls.os_flavors_client = cls.os_primary.flavors_client
|
||||
cls.database_limits_client = limits_client.DatabaseLimitsClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
cls.database_versions_client = versions_client.DatabaseVersionsClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
cls.database_datastores_client =\
|
||||
datastores_client.DatabaseDatastoresClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
cls.database_instances_client =\
|
||||
instances_client.DatabaseInstancesClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
cls.database_backups_client =\
|
||||
backups_client.DatabaseBackupsClient(
|
||||
cls.os_primary.auth_provider,
|
||||
CONF.database.catalog_type,
|
||||
CONF.identity.region,
|
||||
**default_params_with_timeout_values)
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseDatabaseTest, cls).resource_setup()
|
||||
|
||||
cls.catalog_type = CONF.database.catalog_type
|
||||
cls.flavor_id = CONF.database.flavor_id
|
||||
cls.resize_flavor_id = CONF.database.resize_flavor_id
|
||||
cls.db_current_version = CONF.database.db_current_version
|
||||
cls.datastore_type = CONF.database.datastore_type
|
||||
cls.datastore_version = CONF.database.datastore_version
|
||||
cls.availability_zone = CONF.database.availability_zone
|
||||
cls.volume_size = CONF.database.volume_size
|
||||
cls.dns_name_server = CONF.database.dns_name_server
|
||||
|
||||
@classmethod
|
||||
def create_test_instance(cls, backup_id=None, datastore_version=None):
|
||||
"""Wrapper utility that returns a test serinstancever.
|
||||
|
||||
This wrapper utility calls the common create test instance and
|
||||
returns a test instance. The purpose of this wrapper is to minimize
|
||||
the impact on the code of the tests already using this
|
||||
function.
|
||||
|
||||
:param validatable: Whether the server will connectable via db protocol
|
||||
:param validation_resources: Dictionary of validation resources as
|
||||
returned by `get_class_validation_resources`.
|
||||
:param kwargs: Extra arguments are passed down to the
|
||||
`create_test_instance` call.
|
||||
"""
|
||||
name = data_utils.rand_name(cls.__name__ + "-instance")
|
||||
tenant_network = cls.get_tenant_network()
|
||||
|
||||
instance_dict = {
|
||||
"users": [],
|
||||
"availability_zone": CONF.database.availability_zone,
|
||||
"flavorRef": CONF.database.flavor_id,
|
||||
"volume": {"size": CONF.database.volume_size},
|
||||
"databases": [],
|
||||
"datastore": {"type": CONF.database.datastore_type},
|
||||
"name": name,
|
||||
"nics": [{"network_id": tenant_network['id']}],
|
||||
}
|
||||
if CONF.database.datastore_version and not datastore_version:
|
||||
datastore_version = CONF.database.datastore_version
|
||||
if datastore_version:
|
||||
instance_dict["datastore"]["version"] = datastore_version
|
||||
if backup_id:
|
||||
instance_dict["restorePoint"] = {"backupRef": backup_id}
|
||||
|
||||
post_body = json.dumps({'instance': instance_dict})
|
||||
instance = cls.client.create_db_instance(post_body)['instance']
|
||||
|
||||
# For each instance schedule wait and delete, so we first delete all
|
||||
# and then wait for all
|
||||
cls.addClassResourceCleanup(
|
||||
waiters.wait_for_db_instance_decommission,
|
||||
cls.database_instances_client, instance['id'])
|
||||
|
||||
cls.addClassResourceCleanup(
|
||||
test_utils.call_and_ignore_notfound_exc,
|
||||
cls.database_instances_client.delete_db_instance, instance['id'])
|
||||
|
||||
waiters.wait_for_db_instance_status(cls.client, instance['id'],
|
||||
'ACTIVE')
|
||||
|
||||
return instance
|
|
@ -0,0 +1,33 @@
|
|||
# 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.api.database import base
|
||||
|
||||
|
||||
class DatabaseDatastoresTest(base.BaseDatabaseTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(DatabaseDatastoresTest, cls).resource_setup()
|
||||
cls.client = cls.database_datastores_client
|
||||
|
||||
@testtools.attr('smoke')
|
||||
@decorators.idempotent_id('e4cdcadf-51bc-41ec-8cc6-530a3da08d10')
|
||||
def test_datastores(self):
|
||||
datastores = self.client.list_db_datastores()['datastores']
|
||||
self.assertTrue(len(datastores) > 0, "No available datastores found")
|
|
@ -0,0 +1,58 @@
|
|||
# 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.api.database import base
|
||||
|
||||
|
||||
class DatabaseFlavorsTest(base.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_db_flavor(self.flavor_id)
|
||||
['flavor'])
|
||||
self.assertEqual(self.flavor_id, str(flavor['str_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_db_flavor(self.flavor_id)
|
||||
['flavor'])
|
||||
# List of all flavors should contain the expected flavor
|
||||
flavors = self.client.list_db_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)
|
|
@ -0,0 +1,36 @@
|
|||
# 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.api.database import base
|
||||
|
||||
|
||||
class DatabaseFlavorsNegativeTest(base.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_db_flavor, -1)
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2019 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 exceptions as lib_exc
|
||||
|
||||
from trove_tempest_plugin.common import waiters
|
||||
from trove_tempest_plugin.tests.api.database import base
|
||||
|
||||
|
||||
class WithInstanceBaseTest(base.BaseDatabaseTest):
|
||||
|
||||
def setUp(self):
|
||||
# Normally we use the same server with all test cases,
|
||||
# but if it has an issue, we build a new one
|
||||
super(WithInstanceBaseTest, self).setUp()
|
||||
# Check if the server is in a clean state after test
|
||||
try:
|
||||
waiters.wait_for_db_instance_status(self.client, self.instance_id,
|
||||
'ACTIVE')
|
||||
except lib_exc.NotFound:
|
||||
instance = self.create_test_instance()
|
||||
self.__class__.instance_id = instance['id']
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(WithInstanceBaseTest, cls).setup_clients()
|
||||
cls.client = cls.database_instances_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls, datastore_version=None):
|
||||
super(WithInstanceBaseTest, cls).resource_setup()
|
||||
instance = cls.create_test_instance(
|
||||
datastore_version=datastore_version)
|
||||
cls.instance_id = instance['id']
|
|
@ -0,0 +1,119 @@
|
|||
# Copyright 2019 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 trove_tempest_plugin.common import utils
|
||||
from trove_tempest_plugin.common import waiters
|
||||
from trove_tempest_plugin.tests.api.database.instances import base
|
||||
|
||||
|
||||
class InstanceActionsTest(base.WithInstanceBaseTest):
|
||||
|
||||
@decorators.idempotent_id('ace549b3-eee0-4502-bf20-7594d4bf4856')
|
||||
def test_restart_server(self):
|
||||
self.client.restart_db_instance(self.instance_id)
|
||||
waiters.wait_for_db_instance_status(self.client, self.instance_id,
|
||||
'ACTIVE')
|
||||
|
||||
@decorators.idempotent_id('39eff887-008b-4a93-bead-6ddc969117c3')
|
||||
def test_resize_server(self):
|
||||
self.client.resize_db_instance(self.instance_id,
|
||||
self.resize_flavor_id)
|
||||
waiters.wait_for_db_instance_status(self.client, self.instance_id,
|
||||
'ACTIVE')
|
||||
instance = self.client.show_db_instance(self.instance_id)['instance']
|
||||
self.assertEqual(self.resize_flavor_id, instance['flavor']['id'])
|
||||
|
||||
@decorators.idempotent_id('bba1be08-409b-4916-b9a8-6ec03425cd6e')
|
||||
def test_resize_volume(self):
|
||||
new_size = 2
|
||||
self.client.resize_db_instance_volume(self.instance_id, new_size)
|
||||
waiters.wait_for_db_instance_status(self.client, self.instance_id,
|
||||
'ACTIVE')
|
||||
instance = self.client.show_db_instance(self.instance_id)['instance']
|
||||
self.assertEqual(new_size, instance['volume']['size'])
|
||||
|
||||
@decorators.idempotent_id('215fbcbe-40d3-4a8f-9fe1-55ea9e8fb814')
|
||||
def test_update_name(self):
|
||||
new_name = 'new-name'
|
||||
self.client.update_db_instance(self.instance_id, name=new_name)
|
||||
waiters.wait_for_db_instance_status(self.client, self.instance_id,
|
||||
'ACTIVE')
|
||||
|
||||
# Verify the name of the instance has changed
|
||||
instance = self.client.show_db_instance(self.instance_id)['instance']
|
||||
self.assertEqual(new_name, instance['name'])
|
||||
|
||||
@decorators.idempotent_id('38b5462a-308e-4cb8-9530-cc6741c95501')
|
||||
def test_list_create_delete_database(self):
|
||||
name = utils.rand_name()
|
||||
self.client.create_database(self.instance_id, name=name)
|
||||
databases = self.client.list_databases(self.instance_id)['databases']
|
||||
databases = [x['name'] for x in databases]
|
||||
self.assertIn(name, databases)
|
||||
self.client.delete_database(self.instance_id, name=name)
|
||||
databases = self.client.list_databases(self.instance_id)['databases']
|
||||
databases = [x['name'] for x in databases]
|
||||
self.assertNotIn(name, databases)
|
||||
|
||||
@decorators.idempotent_id('bf4840fe-8cf8-46a1-8371-13021e87c690')
|
||||
def test_enable_disable_root(self):
|
||||
root_show = self.client.root_show(self.instance_id)
|
||||
self.assertFalse(root_show['rootEnabled'])
|
||||
root_enable = self.client.root_enable(self.instance_id)
|
||||
self.assertIn('password', list(root_enable['user'].keys()))
|
||||
# TODO(sorrison) Test connection with root user/password
|
||||
root_show = self.client.root_show(self.instance_id)
|
||||
self.assertTrue(root_show['rootEnabled'])
|
||||
self.client.root_disable(self.instance_id)
|
||||
root_show = self.client.root_show(self.instance_id)
|
||||
# Show root show's if root as ever been enabled so disabling should
|
||||
# have no impact
|
||||
self.assertTrue(root_show['rootEnabled'])
|
||||
|
||||
@decorators.idempotent_id('9f11d15b-9640-4c33-a7db-c78224763014')
|
||||
def test_list_create_delete_user(self):
|
||||
name = utils.rand_name()
|
||||
self.client.create_user(self.instance_id, name=name, password='secret')
|
||||
users = self.client.list_users(self.instance_id)['users']
|
||||
users = [x['name'] for x in users]
|
||||
self.assertIn(name, users)
|
||||
self.client.delete_user(self.instance_id, name=name)
|
||||
users = self.client.list_users(self.instance_id)['users']
|
||||
users = [x['name'] for x in users]
|
||||
self.assertNotIn(name, users)
|
||||
|
||||
@decorators.idempotent_id('6f8b8350-f2a9-47b2-a108-8e3653cb9b57')
|
||||
def test_grant_revoke_list_access(self):
|
||||
user = utils.rand_name()
|
||||
db = utils.rand_name()
|
||||
self.client.create_user(self.instance_id, name=user, password='secret')
|
||||
self.client.create_database(self.instance_id, name=db)
|
||||
access = self.client.show_user_access(self.instance_id, user)
|
||||
self.assertEqual([], access['databases'])
|
||||
self.client.grant_user_access(self.instance_id, user, [db])
|
||||
access = self.client.show_user_access(self.instance_id, user)
|
||||
access = [x['name'] for x in access['databases']]
|
||||
self.assertIn(db, access)
|
||||
self.client.revoke_user_access(self.instance_id, user, db)
|
||||
access = self.client.show_user_access(self.instance_id, user)
|
||||
access = [x['name'] for x in access['databases']]
|
||||
self.assertNotIn(db, access)
|
||||
|
||||
@decorators.idempotent_id('b13ff6fb-6214-416b-8aea-23dc3c24d00e')
|
||||
def test_list_backups(self):
|
||||
backups = self.client.list_backups(self.instance_id)
|
||||
self.assertEqual([], backups['backups'])
|
|
@ -0,0 +1,74 @@
|
|||
# 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.common.utils import test_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from trove_tempest_plugin.common import utils
|
||||
from trove_tempest_plugin.common import waiters
|
||||
from trove_tempest_plugin.tests.api.database.instances import base
|
||||
|
||||
|
||||
class InstanceBackupsTest(base.WithInstanceBaseTest):
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(InstanceBackupsTest, cls).setup_clients()
|
||||
cls.backup_client = cls.database_backups_client
|
||||
|
||||
def _add_cleanup(self, backup_id):
|
||||
self.addClassResourceCleanup(
|
||||
waiters.wait_for_backup_delete,
|
||||
self.backup_client, backup_id)
|
||||
|
||||
self.addClassResourceCleanup(
|
||||
test_utils.call_and_ignore_notfound_exc,
|
||||
self.backup_client.delete_backup, backup_id)
|
||||
|
||||
@decorators.idempotent_id('e4c2bf6d-e619-4d9b-a79b-67f5463a8705')
|
||||
def test_list_create_delete_backup(self):
|
||||
name = utils.rand_name()
|
||||
backup = self.backup_client.create_backup(self.instance_id, name)
|
||||
backup_id = backup['backup']['id']
|
||||
waiters.wait_for_backup_status(self.backup_client, backup_id,
|
||||
'COMPLETED')
|
||||
backups = self.backup_client.list_backups()
|
||||
backup_ids = [x['id'] for x in backups['backups']]
|
||||
self.assertIn(backup_id, backup_ids)
|
||||
|
||||
self.backup_client.delete_backup(backup_id)
|
||||
waiters.wait_for_backup_delete(self.backup_client, backup_id)
|
||||
|
||||
backups = self.backup_client.list_backups()
|
||||
backup_ids = [x['id'] for x in backups['backups']]
|
||||
self.assertNotIn(backup_id, backup_ids)
|
||||
|
||||
@decorators.idempotent_id('2ddab860-b487-481f-b89e-9ad06d5f6286')
|
||||
def test_backup_incremental(self):
|
||||
name = utils.rand_name()
|
||||
backup = self.backup_client.create_backup(self.instance_id, name)
|
||||
parent_id = backup['backup']['id']
|
||||
self._add_cleanup(parent_id)
|
||||
waiters.wait_for_backup_status(self.backup_client, parent_id,
|
||||
'COMPLETED')
|
||||
backup = self.backup_client.create_backup(self.instance_id, name,
|
||||
incremental=True,
|
||||
parent=parent_id)
|
||||
backup_id = backup['backup']['id']
|
||||
self._add_cleanup(backup_id)
|
||||
waiters.wait_for_backup_status(self.backup_client, backup_id,
|
||||
'COMPLETED')
|
||||
backup = self.backup_client.show_backup(backup_id)
|
||||
self.assertEqual(parent_id, backup['backup']['parent_id'])
|
|
@ -0,0 +1,47 @@
|
|||
# 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.api.database import base
|
||||
|
||||
|
||||
class DatabaseLimitsTest(base.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_db_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))
|
|
@ -0,0 +1,41 @@
|
|||
# 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.api.database import base
|
||||
|
||||
|
||||
class DatabaseVersionsTest(base.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_db_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)
|
Loading…
Reference in New Issue