Improve the basic test case for mysql
Change-Id: I9202d1eaae517afc181973344b14903879fac24e
This commit is contained in:
parent
605a0d3a51
commit
18e399b9e0
@ -12,3 +12,5 @@ requests>=2.14.2 # Apache-2.0
|
||||
six>=1.10.0 # MIT
|
||||
tempest>=17.1.0 # Apache-2.0
|
||||
tenacity>=5.1.1 # Apache-2.0
|
||||
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
|
||||
PyMySQL>=0.7.6 # MIT License
|
@ -38,6 +38,10 @@ DatabaseGroup = [
|
||||
'internalURL'],
|
||||
help="The endpoint type to use for the Database service."
|
||||
),
|
||||
cfg.ListOpt(
|
||||
'enabled_datastores',
|
||||
default=['mysql']
|
||||
),
|
||||
cfg.IntOpt('database_build_timeout',
|
||||
default=1800,
|
||||
help='Timeout in seconds to wait for a database instance to '
|
||||
@ -47,6 +51,12 @@ DatabaseGroup = [
|
||||
default="d2",
|
||||
help="The Nova flavor ID used for creating database instance."
|
||||
),
|
||||
cfg.StrOpt(
|
||||
'shared_network',
|
||||
default="private",
|
||||
help=('Pre-defined network name or ID used for creating database '
|
||||
'instance.')
|
||||
),
|
||||
cfg.StrOpt(
|
||||
'subnet_cidr',
|
||||
default='10.1.1.0/24',
|
||||
@ -58,6 +68,4 @@ DatabaseGroup = [
|
||||
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"),
|
||||
]
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
from oslo_utils import uuidutils
|
||||
import tenacity
|
||||
|
||||
from tempest import config
|
||||
@ -30,6 +31,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class BaseTroveTest(test.BaseTestCase):
|
||||
credentials = ('admin', 'primary')
|
||||
datastore = None
|
||||
|
||||
@classmethod
|
||||
def get_resource_name(cls, resource_type):
|
||||
@ -43,6 +45,11 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
if not CONF.service_available.trove:
|
||||
raise cls.skipException("Database service is not available.")
|
||||
|
||||
if cls.datastore not in CONF.database.enabled_datastores:
|
||||
raise cls.skipException(
|
||||
"Datastore %s is not enabled." % cls.datastore
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseTroveTest, cls).setup_clients()
|
||||
@ -103,6 +110,22 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
subnets_client = cls.os_primary.subnets_client
|
||||
routers_client = cls.os_primary.routers_client
|
||||
|
||||
if CONF.database.shared_network:
|
||||
private_network = CONF.database.shared_network
|
||||
if not uuidutils.is_uuid_like(private_network):
|
||||
networks = networks_client.list_networks()['networks']
|
||||
for net in networks:
|
||||
if net['name'] == private_network:
|
||||
private_network = net['id']
|
||||
break
|
||||
else:
|
||||
raise exceptions.NotFound(
|
||||
'Shared network %s not found' % private_network
|
||||
)
|
||||
|
||||
cls.private_network = private_network
|
||||
return
|
||||
|
||||
network_kwargs = {"name": cls.get_resource_name("network")}
|
||||
result = networks_client.create_network(**network_kwargs)
|
||||
LOG.info('Private network created: %s', result['network'])
|
||||
@ -163,6 +186,9 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
# network ID.
|
||||
cls._create_network()
|
||||
|
||||
cls.instance_id = cls.create_instance()
|
||||
cls.wait_for_instance_status(cls.instance_id)
|
||||
|
||||
@classmethod
|
||||
def create_instance(cls, database="test_db", username="test_user",
|
||||
password="password"):
|
||||
@ -175,6 +201,17 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
all test methods within a TestCase are assumed to be executed serially.
|
||||
"""
|
||||
name = cls.get_resource_name("instance")
|
||||
|
||||
# Get datastore version
|
||||
res = cls.client.list_resources("datastores")
|
||||
for d in res['datastores']:
|
||||
if d['name'] == cls.datastore:
|
||||
if d.get('default_version'):
|
||||
datastore_version = d['default_version']
|
||||
else:
|
||||
datastore_version = d['versions'][0]['name']
|
||||
break
|
||||
|
||||
body = {
|
||||
"instance": {
|
||||
"name": name,
|
||||
@ -195,8 +232,8 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
}
|
||||
],
|
||||
"datastore": {
|
||||
"type": CONF.database.datastore_type,
|
||||
"version": CONF.database.datastore_version
|
||||
"type": cls.datastore,
|
||||
"version": datastore_version
|
||||
},
|
||||
"nics": [
|
||||
{
|
||||
@ -220,10 +257,12 @@ class BaseTroveTest(test.BaseTestCase):
|
||||
res = cls.client.get_resource("instances", id)
|
||||
except exceptions.NotFound:
|
||||
if need_delete or status == "DELETED":
|
||||
LOG.info('Instance %s is deleted', id)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
return
|
||||
|
||||
if res["instance"]["status"] == status:
|
||||
LOG.info('Instance %s becomes %s', id, status)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
elif status != "ERROR" and res["instance"]["status"] == "ERROR":
|
||||
# If instance status goes to ERROR but is not expected, stop
|
||||
|
@ -0,0 +1,53 @@
|
||||
# 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 oslo_utils import netutils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from trove_tempest_plugin.tests import base
|
||||
from trove_tempest_plugin.tests import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestMySQLInstanceBasic(base.BaseTroveTest):
|
||||
datastore = 'mysql'
|
||||
|
||||
def _access_db(self, ip, username='test_user', password='password'):
|
||||
db_engine = utils.LocalSqlClient.init_engine(ip, username, password)
|
||||
db_client = utils.LocalSqlClient(db_engine)
|
||||
|
||||
LOG.info('Trying to access the database %s', ip)
|
||||
|
||||
with db_client:
|
||||
cmd = "SELECT 1;"
|
||||
db_client.execute(cmd)
|
||||
|
||||
@decorators.idempotent_id("40cf38ce-cfbf-11e9-8760-1458d058cfb2")
|
||||
def test_database_access(self):
|
||||
res = self.client.get_resource("instances", self.instance_id)
|
||||
ips = res["instance"].get('ip', [])
|
||||
|
||||
# TODO(lxkong): IPv6 needs to be supported.
|
||||
v4_ip = None
|
||||
for ip in ips:
|
||||
if netutils.is_valid_ipv4(ip):
|
||||
v4_ip = ip
|
||||
break
|
||||
|
||||
self.assertIsNotNone(v4_ip)
|
||||
time.sleep(5)
|
||||
self._access_db(v4_ip)
|
@ -1,29 +0,0 @@
|
||||
# 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
|
@ -14,7 +14,7 @@
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
import sqlalchemy
|
||||
from tempest.lib import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -49,3 +49,41 @@ def wait_for_removal(delete_func, show_func, *args, **kwargs):
|
||||
(show_func.__name__, check_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
class LocalSqlClient(object):
|
||||
"""A sqlalchemy wrapper to manage transactions."""
|
||||
|
||||
def __init__(self, engine):
|
||||
self.engine = engine
|
||||
|
||||
def __enter__(self):
|
||||
self.conn = self.engine.connect()
|
||||
self.trans = self.conn.begin()
|
||||
return self.conn
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if self.trans:
|
||||
if type is not None:
|
||||
self.trans.rollback()
|
||||
else:
|
||||
self.trans.commit()
|
||||
self.conn.close()
|
||||
|
||||
def execute(self, t, **kwargs):
|
||||
try:
|
||||
return self.conn.execute(t, kwargs)
|
||||
except Exception as e:
|
||||
self.trans.rollback()
|
||||
self.trans = None
|
||||
raise exceptions.TempestException(
|
||||
'Failed to execute database command %s, error: %s' %
|
||||
(t, str(e))
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def init_engine(host, user, password):
|
||||
return sqlalchemy.create_engine(
|
||||
"mysql+pymysql://%s:%s@%s:3306" % (user, password, host),
|
||||
pool_recycle=1800
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user