From 8f0b0367c5093b6d253145868cefc9cfd11b9368 Mon Sep 17 00:00:00 2001 From: "wu.chunyang" Date: Fri, 7 Mar 2025 15:01:04 +0800 Subject: [PATCH] Add cinder storage driver tests Change-Id: Iae573f2d20f94201d5e829d5b86fb475868226b1 --- trove_tempest_plugin/tests/base.py | 4 +- .../tests/scenario/base_cinder_backup.py | 80 +++++++++++++++ .../tests/scenario/test_cinder_backup.py | 98 +++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 trove_tempest_plugin/tests/scenario/base_cinder_backup.py create mode 100644 trove_tempest_plugin/tests/scenario/test_cinder_backup.py diff --git a/trove_tempest_plugin/tests/base.py b/trove_tempest_plugin/tests/base.py index 43d74f3..0217f1b 100644 --- a/trove_tempest_plugin/tests/base.py +++ b/trove_tempest_plugin/tests/base.py @@ -550,7 +550,7 @@ class BaseTroveTest(test.BaseTestCase): @classmethod def create_backup(cls, instance_id, backup_name, incremental=False, - parent_id=None, description=None): + parent_id=None, description=None, storage_driver=None): body = { "backup": { "name": backup_name, @@ -558,6 +558,8 @@ class BaseTroveTest(test.BaseTestCase): "incremental": 1 if incremental else 0, } } + if storage_driver: + body["backup"]["storage_driver"] = storage_driver if description: body['backup']['description'] = description if parent_id: diff --git a/trove_tempest_plugin/tests/scenario/base_cinder_backup.py b/trove_tempest_plugin/tests/scenario/base_cinder_backup.py new file mode 100644 index 0000000..5be1d1d --- /dev/null +++ b/trove_tempest_plugin/tests/scenario/base_cinder_backup.py @@ -0,0 +1,80 @@ +# 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 tempest import config + +from trove_tempest_plugin.tests import base as trove_base + +LOG = logging.getLogger(__name__) +CONF = config.CONF + + +class TestCinderBackupBase(trove_base.BaseTroveTest): + @classmethod + def insert_data(cls, *args, **kwargs): + pass + + @classmethod + def insert_data_inc(cls, *args, **kwargs): + pass + + def verify_data(self, *args, **kwargs): + pass + + @classmethod + def resource_setup(cls): + super(TestCinderBackupBase, cls).resource_setup() + # Insert some data to the current db instance + LOG.info(f"Inserting data on {cls.instance_ip} before creating" + f"backup") + cls.insert_data(cls.instance_ip) + + # Create a backup that is shared within this test class. + LOG.info(f"Creating backup for instance {cls.instance_id}") + name = cls.get_resource_name("backup") + backup = cls.create_backup(cls.instance_id, + name, + storage_driver="cinder") + cls.wait_for_backup_status(backup['id']) + cls.backup = cls.client.get_resource("backups", backup['id'])['backup'] + + def backup_test(self): + # Restore from backup + LOG.info(f'Creating a new instance using the backup ' + f'{self.backup["id"]}') + name = self.get_resource_name("restore") + restore_instance = self.create_instance( + name, + datastore_version=self.backup['datastore']['version'], + backup_id=self.backup['id'], + create_user=self.create_user + ) + self.wait_for_instance_status( + restore_instance['id'], + expected_op_status=["HEALTHY"], + timeout=CONF.database.database_restore_timeout) + + if self.enable_root: + self.root_password = self.get_root_pass(restore_instance['id']) + + restore_instance = self.client.get_resource( + "instances", restore_instance['id'])['instance'] + restore_instance_ip = self.get_instance_ip(restore_instance) + + LOG.info(f"Verifying data on restored instance {restore_instance_ip}") + self.verify_data(restore_instance_ip) + + # Delete the new instance explicitly to avoid too many instances + # during the test. + self.wait_for_instance_status(restore_instance['id'], + expected_status="DELETED", + need_delete=True) diff --git a/trove_tempest_plugin/tests/scenario/test_cinder_backup.py b/trove_tempest_plugin/tests/scenario/test_cinder_backup.py new file mode 100644 index 0000000..862fac9 --- /dev/null +++ b/trove_tempest_plugin/tests/scenario/test_cinder_backup.py @@ -0,0 +1,98 @@ +# 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 constants +from trove_tempest_plugin.tests.scenario import base_cinder_backup +from trove_tempest_plugin.tests import utils + + +class TestCinderBakcupMySQL(base_cinder_backup.TestCinderBackupBase): + datastore = 'mysql' + + @classmethod + def insert_data(cls, ip, username=constants.DB_USER, + password=constants.DB_PASS, database=constants.DB_NAME, + **kwargs): + db_url = f'mysql+pymysql://{username}:{password}@{ip}:3306/{database}' + with utils.SQLClient(db_url) as db_client: + cmds = [ + "CREATE TABLE Persons (ID int, String varchar(255));", + "insert into Persons VALUES (1, 'OpenStack');", + ] + db_client.mysql_execute(cmds) + + def verify_data(self, ip, username=constants.DB_USER, + password=constants.DB_PASS, database=constants.DB_NAME): + db_url = f'mysql+pymysql://{username}:{password}@{ip}:3306/{database}' + with utils.SQLClient(db_url) as db_client: + cmd = "select * from Persons;" + ret = db_client.mysql_execute(cmd) + keys = ret.keys() + rows = ret.fetchall() + self.assertEqual(1, len(rows)) + + result = dict(zip(keys, rows[0])) + expected = {'ID': 1, 'String': 'OpenStack'} + self.assertEqual(expected, result) + + @decorators.idempotent_id('2d5e2c8d-6f7c-44c1-8944-260fc684af40') + def test_backup_cinder(self): + self.backup_test() + + +class TestCinderBackupPostgreSQL(base_cinder_backup.TestCinderBackupBase): + datastore = 'postgresql' + create_user = False + enable_root = True + root_password = "" + + @classmethod + def insert_data(cls, ip): + db_url = (f'postgresql+psycopg2://root:{cls.password}@' + f'{ip}:5432/postgres') + with utils.SQLClient(db_url) as db_client: + cmd = "CREATE DATABASE testdb;" + db_client.pgsql_execute(cmd) + + db_url = (f'postgresql+psycopg2://root:{cls.password}@' + f'{ip}:5432/testdb') + with utils.SQLClient(db_url) as db_client: + cmds = [ + "CREATE TABLE persons (id INT PRIMARY KEY NOT NULL, " + "string VARCHAR(255));", + "INSERT INTO persons (id,string) VALUES (1, 'OpenStack');", + ] + db_client.pgsql_execute(cmds) + + def verify_data(self, ip): + db_url = (f'postgresql+psycopg2://root:{self.root_password}@' + f'{ip}:5432/testdb') + with utils.SQLClient(db_url) as db_client: + cmd = "select * from persons;" + ret = db_client.pgsql_execute(cmd) + keys = ret.keys() + rows = ret.fetchall() + self.assertEqual(1, len(rows)) + + result = dict(zip(keys, rows[0])) + expected = {'id': 1, 'string': 'OpenStack'} + self.assertEqual(expected, result) + + @decorators.idempotent_id('4dab0bbf-06c2-4fbd-89fb-16dece9224f2') + def test_backup_cinder(self): + self.backup_test() + + +class TestCinderBackupMariaDB(TestCinderBakcupMySQL): + datastore = 'mariadb'