Improve rebuild test

Change-Id: I8addc9eb5a6109116ff0d088c0b69d6865856970
This commit is contained in:
Lingxian Kong 2021-07-11 21:53:50 +12:00
parent 23ac0ee82b
commit 4922ec75db
6 changed files with 136 additions and 41 deletions

View File

@ -13,8 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
from urllib import parse as urlparse
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urlparse
import tenacity
from tempest.lib.common import rest_client
from tempest.lib import exceptions
@ -50,6 +52,12 @@ class TroveClient(rest_client.RestClient):
return rest_client.ResponseBody(resp, json.loads(body))
@tenacity.retry(
wait=tenacity.wait_fixed(5),
stop=tenacity.stop_after_attempt(2),
retry=tenacity.retry_if_exception_type(),
reraise=True
)
def delete_resource(self, obj, id, ignore_notfound=False):
try:
resp, _ = self.delete('/{obj}/{id}'.format(obj=obj, id=id))

View File

@ -521,3 +521,49 @@ class BaseTroveTest(test.BaseTestCase):
def get_root_pass(cls, instance_id):
resp = cls.client.create_resource(f"instances/{instance_id}/root", {})
return resp['user']['password']
@classmethod
def rebuild_instance(cls, instance_id, image_id):
rebuild_req = {
"rebuild": {
"image_id": image_id
}
}
cls.admin_client.create_resource(
f"mgmt/instances/{instance_id}/action",
rebuild_req, expected_status_code=202,
need_response=False)
cls.wait_for_instance_status(instance_id)
@classmethod
def create_config(cls, name, values, datastore, datastore_version):
create_config = {
"configuration": {
"datastore": {
"type": datastore,
"version": datastore_version
},
"values": values,
"name": name
}
}
config = cls.client.create_resource('configurations', create_config)
return config
@classmethod
def attach_config(cls, instance_id, config_id):
attach_config = {
"instance": {
"configuration": config_id
}
}
cls.client.put_resource(f'instances/{instance_id}', attach_config)
@classmethod
def detach_config(cls, instance_id):
detach_config = {
"instance": {
"configuration": None
}
}
cls.client.put_resource(f'instances/{instance_id}', detach_config)

View File

@ -40,6 +40,9 @@ class TestInstanceActionsBase(trove_base.BaseTroveTest):
def get_db_version(self):
pass
def get_config_value(self, ip, option, **kwargs):
pass
@classmethod
def resource_setup(cls):
super(TestInstanceActionsBase, cls).resource_setup()
@ -143,22 +146,43 @@ class TestInstanceActionsBase(trove_base.BaseTroveTest):
ret = self.client.get_resource('instances', self.instance_id)
self.assertEqual(2, ret['instance']['volume']['size'])
def rebuild_test(self):
def rebuild_test(self, config_values, config_need_restart=False):
LOG.info(f"Inserting data on {self.instance_ip} before rebuilding")
self.insert_data_before_rebuild(self.instance_ip)
# Create configuration before rebuild
config_name = self.get_resource_name('config')
LOG.info(f"Creating new configuration {config_name} for rebuild")
config = self.create_config(
config_name, config_values, self.datastore,
self.instance['datastore']['version'])
config_id = config['configuration']['id']
self.addCleanup(self.client.delete_resource, 'configurations',
config_id, ignore_notfound=True)
# Attach the configuration
LOG.info(f"Attaching config {config_id} to instance "
f"{self.instance_id}")
self.attach_config(self.instance_id, config_id)
self.addCleanup(self.detach_config, self.instance_id)
if config_need_restart:
LOG.info(f"Restarting instance {self.instance_id}")
self.restart_instance(self.instance_id)
# Verify the config before rebuild
key = list(config_values.keys())[0]
value = list(config_values.values())[0]
LOG.info(f"Getting config value for {key} on {self.instance_ip}")
cur_value = self.get_config_value(self.instance_ip, key)
self.assertEqual(value, cur_value)
LOG.info(f"Rebuilding instance {self.instance_id} with image "
f"{CONF.database.rebuild_image_id}")
rebuild_req = {
"rebuild": {
"image_id": CONF.database.rebuild_image_id
}
}
self.admin_client.create_resource(
f"mgmt/instances/{self.instance_id}/action",
rebuild_req, expected_status_code=202,
need_response=False)
self.wait_for_instance_status(self.instance_id)
self.rebuild_instance(self.instance_id, CONF.database.rebuild_image_id)
LOG.info(f"Verifying data on {self.instance_ip} after rebuilding")
self.verify_data_after_rebuild(self.instance_ip)
# Verify configuration before rebuild
LOG.info(f"Verifying config {key} on {self.instance_ip} after "
f"rebuilding")
cur_value = self.get_config_value(self.instance_ip, key)
self.assertEqual(value, cur_value)

View File

@ -36,21 +36,14 @@ class TestInstanceBasicBase(trove_base.BaseTroveTest):
value should be in type int.
"""
# Create new configuration
config_name = 'test_config'
config_name = self.get_resource_name('config')
key = list(create_values.keys())[0]
value = list(create_values.values())[0]
create_config = {
"configuration": {
"datastore": {
"type": self.datastore,
"version": self.instance['datastore']['version']
},
"values": create_values,
"name": config_name
}
}
LOG.info(f"Creating new configuration {config_name}")
config = self.client.create_resource('configurations', create_config)
config = self.create_config(
config_name, create_values, self.datastore,
self.instance['datastore']['version'])
config_id = config['configuration']['id']
self.addCleanup(self.client.delete_resource, 'configurations',
config_id, ignore_notfound=True)
@ -61,15 +54,9 @@ class TestInstanceBasicBase(trove_base.BaseTroveTest):
self.assertEqual(0, len(ret['instances']))
# Attach the configuration to the existing instance
attach_config = {
"instance": {
"configuration": config_id
}
}
LOG.info(f"Attaching config {config_id} to instance "
f"{self.instance_id}")
self.client.put_resource(f'instances/{self.instance_id}',
attach_config)
self.attach_config(self.instance_id, config_id)
if need_restart:
LOG.info(f"Restarting instance {self.instance_id}")
@ -107,13 +94,7 @@ class TestInstanceBasicBase(trove_base.BaseTroveTest):
# Detach the configuration from the instance
LOG.info(f"Detaching from instance {self.instance_id}")
detach_config = {
"instance": {
"configuration": None
}
}
self.client.put_resource(f'instances/{self.instance_id}',
detach_config)
self.detach_config(self.instance_id)
if need_restart:
LOG.info(f"Restarting instance {self.instance_id}")

View File

@ -209,3 +209,15 @@ class TestReplicationBase(trove_base.BaseTroveTest):
ret = self.client.get_resource('instances', self.instance_id)
self.assertIsNone(ret['instance'].get('replicas'))
self.assertIsNone(ret['instance'].get('replica_of'))
# Rebuild test for replication cluster, now replica1 is the primary
if CONF.database.rebuild_image_id:
LOG.info(f"Rebuilding primary {replica1_id}")
self.rebuild_instance(replica1_id, CONF.database.rebuild_image_id)
LOG.info(f"Verifying data after rebuild {replica1_id}")
self.verify_data_after_promote(replica1_ip)
LOG.info(f"Rebuilding replica {replica2_id}")
self.rebuild_instance(replica2_id, CONF.database.rebuild_image_id)
LOG.info(f"Verifying data after rebuild {replica2_id}")
self.verify_data_after_promote(replica2_ip)

View File

@ -98,6 +98,16 @@ class InstanceActionsMySQLBase(base_actions.TestInstanceActionsBase):
ret = db_client.mysql_execute(cmd)
return ret.first()[0]
def get_config_value(self, ip, option, username=constants.DB_USER,
password=constants.DB_PASS):
db_url = f'mysql+pymysql://{username}:{password}@{ip}:3306'
with utils.SQLClient(db_url) as db_client:
cmd = f"show variables where Variable_name in ('{option}');"
ret = db_client.mysql_execute(cmd)
rows = ret.fetchall()
self.assertEqual(1, len(rows))
return int(rows[0][1])
class TestInstanceActionsMySQL(InstanceActionsMySQLBase):
datastore = 'mysql'
@ -117,7 +127,8 @@ class TestInstanceActionsMySQL(InstanceActionsMySQLBase):
@testtools.skipUnless(CONF.database.rebuild_image_id,
'Image for rebuild not configured.')
def test_rebuild(self):
self.rebuild_test()
config_values = {"max_connections": 555}
self.rebuild_test(config_values)
class TestInstanceActionsMariaDB(InstanceActionsMySQLBase):
@ -138,7 +149,8 @@ class TestInstanceActionsMariaDB(InstanceActionsMySQLBase):
@testtools.skipUnless(CONF.database.rebuild_image_id,
'Image for rebuild not configured.')
def test_rebuild(self):
self.rebuild_test()
config_values = {"max_connections": 555}
self.rebuild_test(config_values)
class TestInstanceActionsPostgreSQL(base_actions.TestInstanceActionsBase):
@ -223,6 +235,17 @@ class TestInstanceActionsPostgreSQL(base_actions.TestInstanceActionsBase):
return version.split(' ')[0]
def get_config_value(self, ip, option):
db_url = (f'postgresql+psycopg2://root:{self.password}@'
f'{ip}:5432/postgres')
with utils.SQLClient(db_url) as db_client:
cmd = f"SELECT setting FROM pg_settings WHERE name='{option}';"
ret = db_client.pgsql_execute(cmd)
rows = ret.fetchall()
self.assertEqual(1, len(rows))
return int(rows[0][0])
@decorators.idempotent_id("97f1e7ca-f415-11ea-a950-00224d6b7bc1")
@testtools.skipUnless(
'postgresql' in CONF.database.pre_upgrade_datastore_versions,
@ -238,4 +261,5 @@ class TestInstanceActionsPostgreSQL(base_actions.TestInstanceActionsBase):
@testtools.skipUnless(CONF.database.rebuild_image_id,
'Image for rebuild not configured.')
def test_rebuild(self):
self.rebuild_test()
config_values = {"max_connections": 101}
self.rebuild_test(config_values, config_need_restart=True)