From 40af5562643856efad8717698511e613b4b142d4 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Sun, 12 Jul 2020 21:48:29 +1200 Subject: Support backup strategy Change-Id: Ic8c20109b287f2f9220379cb249669d18b52893d --- troveclient/base.py | 1 + troveclient/osc/v1/database_backup_strategy.py | 105 +++++++++++++++++++++ troveclient/osc/v1/database_backups.py | 22 ++++- .../tests/osc/v1/test_database_backup_strategy.py | 89 +++++++++++++++++ troveclient/tests/osc/v1/test_database_backups.py | 6 +- troveclient/v1/backup_strategy.py | 60 ++++++++++++ troveclient/v1/backups.py | 5 +- troveclient/v1/client.py | 2 + 8 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 troveclient/osc/v1/database_backup_strategy.py create mode 100644 troveclient/tests/osc/v1/test_database_backup_strategy.py create mode 100644 troveclient/v1/backup_strategy.py (limited to 'troveclient') diff --git a/troveclient/base.py b/troveclient/base.py index 7af8668..950ab2e 100644 --- a/troveclient/base.py +++ b/troveclient/base.py @@ -187,6 +187,7 @@ class Manager(utils.HookableMixin): def _delete(self, url): resp, body = self.api.client.delete(url) + return resp, body def _update(self, url, body, **kwargs): self.run_hooks('modify_body_for_update', body, **kwargs) diff --git a/troveclient/osc/v1/database_backup_strategy.py b/troveclient/osc/v1/database_backup_strategy.py new file mode 100644 index 0000000..db36709 --- /dev/null +++ b/troveclient/osc/v1/database_backup_strategy.py @@ -0,0 +1,105 @@ +# Copyright 2020 Catalyst Cloud +# +# 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 osc_lib.command import command +from osc_lib import utils as osc_utils + +from troveclient.i18n import _ + + +class ListDatabaseBackupStrategies(command.Lister): + _description = _("List backup strategies") + columns = ['Project ID', 'Instance ID', 'Swift Container'] + + def get_parser(self, prog_name): + parser = super(ListDatabaseBackupStrategies, self).get_parser( + prog_name) + + parser.add_argument( + '--instance-id', + help=_('Filter results by database instance ID.') + ) + parser.add_argument( + '--project-id', + help=_('Project ID in Keystone. Only admin user is allowed to ' + 'list backup strategy for other projects.') + ) + + return parser + + def take_action(self, parsed_args): + manager = self.app.client_manager.database.backup_strategies + result = manager.list(instance_id=parsed_args.instance_id, + project_id=parsed_args.project_id) + backup_strategies = [osc_utils.get_item_properties(item, self.columns) + for item in result] + + return self.columns, backup_strategies + + +class CreateDatabaseBackupStrategy(command.ShowOne): + _description = _("Creates backup strategy for the project or a particular " + "instance.") + + def get_parser(self, prog_name): + parser = super(CreateDatabaseBackupStrategy, self).get_parser( + prog_name) + + parser.add_argument( + '--project-id', + help=_('Project ID in Keystone. Only admin user is allowed to ' + 'create backup strategy for other projects.') + ) + parser.add_argument( + '--instance-id', + help=_('Database instance ID.') + ) + parser.add_argument( + '--swift-container', + help=_('The container name for storing the backup data when Swift ' + 'is used as backup storage backend.') + ) + + return parser + + def take_action(self, parsed_args): + manager = self.app.client_manager.database.backup_strategies + result = manager.create( + instance_id=parsed_args.instance_id, + swift_container=parsed_args.swift_container + ) + return zip(*sorted(result.to_dict().items())) + + +class DeleteDatabaseBackupStrategy(command.Command): + _description = _("Deletes backup strategy.") + + def get_parser(self, prog_name): + parser = super(DeleteDatabaseBackupStrategy, self).get_parser( + prog_name) + parser.add_argument( + '--project-id', + help=_('Project ID in Keystone. Only admin user is allowed to ' + 'delete backup strategy for other projects.') + ) + parser.add_argument( + '--instance-id', + help=_('Database instance ID.') + ) + return parser + + def take_action(self, parsed_args): + manager = self.app.client_manager.database.backup_strategies + manager.delete(instance_id=parsed_args.instance_id, + project_id=parsed_args.project_id) diff --git a/troveclient/osc/v1/database_backups.py b/troveclient/osc/v1/database_backups.py index 53e19fc..f6e5e31 100644 --- a/troveclient/osc/v1/database_backups.py +++ b/troveclient/osc/v1/database_backups.py @@ -225,6 +225,15 @@ class CreateDatabaseBackup(command.ShowOne): ' full or incremental backup. It will create a' ' full backup if no existing backup found.') ) + parser.add_argument( + '--swift-container', + help=_('The container name for storing the backup data when Swift ' + 'is used as backup storage backend. If not specified, will ' + 'use the container name configured in the backup strategy, ' + 'otherwise, the default value configured by the cloud ' + 'operator. Non-existent container is created ' + 'automatically.') + ) return parser def take_action(self, parsed_args): @@ -232,11 +241,14 @@ class CreateDatabaseBackup(command.ShowOne): database_backups = manager.backups instance = osc_utils.find_resource(manager.instances, parsed_args.instance) - backup = database_backups.create(parsed_args.name, - instance, - description=parsed_args.description, - parent_id=parsed_args.parent, - incremental=parsed_args.incremental) + backup = database_backups.create( + parsed_args.name, + instance, + description=parsed_args.description, + parent_id=parsed_args.parent, + incremental=parsed_args.incremental, + swift_container=parsed_args.swift_container + ) backup = set_attributes_for_print_detail(backup) return zip(*sorted(six.iteritems(backup))) diff --git a/troveclient/tests/osc/v1/test_database_backup_strategy.py b/troveclient/tests/osc/v1/test_database_backup_strategy.py new file mode 100644 index 0000000..fe5c951 --- /dev/null +++ b/troveclient/tests/osc/v1/test_database_backup_strategy.py @@ -0,0 +1,89 @@ +# Copyright 2020 Catalyst Cloud +# +# 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 troveclient.osc.v1 import database_backup_strategy +from troveclient.tests.osc.v1 import fakes +from troveclient.v1 import backup_strategy + + +class TestBackupStrategy(fakes.TestDatabasev1): + def setUp(self): + super(TestBackupStrategy, self).setUp() + self.manager = self.app.client_manager.database.backup_strategies + + +class TestBackupStrategyList(TestBackupStrategy): + def setUp(self): + super(TestBackupStrategyList, self).setUp() + self.cmd = database_backup_strategy.ListDatabaseBackupStrategies( + self.app, None) + + def test_list(self): + item = backup_strategy.BackupStrategy( + None, + { + 'project_id': 'fake_project_id', + 'instance_id': 'fake_instance_id', + 'swift_container': 'fake_container' + } + ) + self.manager.list.return_value = [item] + + parsed_args = self.check_parser(self.cmd, [], []) + columns, data = self.cmd.take_action(parsed_args) + + self.manager.list.assert_called_once_with(instance_id=None, + project_id=None) + self.assertEqual( + database_backup_strategy.ListDatabaseBackupStrategies.columns, + columns) + self.assertEqual( + [('fake_project_id', 'fake_instance_id', 'fake_container')], + data) + + +class TestBackupStrategyCreate(TestBackupStrategy): + def setUp(self): + super(TestBackupStrategyCreate, self).setUp() + self.cmd = database_backup_strategy.CreateDatabaseBackupStrategy( + self.app, None) + + def test_create(self): + args = ['--instance-id', 'fake_instance_id', '--swift-container', + 'fake_container'] + parsed_args = self.check_parser(self.cmd, args, []) + self.cmd.take_action(parsed_args) + + self.manager.create.assert_called_once_with( + instance_id='fake_instance_id', + swift_container='fake_container' + ) + + +class TestBackupStrategyDelete(TestBackupStrategy): + def setUp(self): + super(TestBackupStrategyDelete, self).setUp() + self.cmd = database_backup_strategy.DeleteDatabaseBackupStrategy( + self.app, None) + + def test_delete(self): + args = ['--instance-id', 'fake_instance_id', '--project-id', + 'fake_project'] + parsed_args = self.check_parser(self.cmd, args, []) + self.cmd.take_action(parsed_args) + + self.manager.delete.assert_called_once_with( + project_id='fake_project', + instance_id='fake_instance_id', + ) diff --git a/troveclient/tests/osc/v1/test_database_backups.py b/troveclient/tests/osc/v1/test_database_backups.py index a8c1966..0010a46 100644 --- a/troveclient/tests/osc/v1/test_database_backups.py +++ b/troveclient/tests/osc/v1/test_database_backups.py @@ -224,7 +224,8 @@ class TestBackupCreate(TestBackups): '1234', description=None, parent_id=None, - incremental=False) + incremental=False, + swift_container=None) @mock.patch.object(utils, 'find_resource') def test_incremental_backup_create(self, mock_find): @@ -237,7 +238,8 @@ class TestBackupCreate(TestBackups): '1234', description='backup 1234', parent_id='1234-1', - incremental=True) + incremental=True, + swift_container=None) class TestDatabaseBackupExecutionDelete(TestBackups): diff --git a/troveclient/v1/backup_strategy.py b/troveclient/v1/backup_strategy.py new file mode 100644 index 0000000..61c124f --- /dev/null +++ b/troveclient/v1/backup_strategy.py @@ -0,0 +1,60 @@ +# Copyright 2020 Catalyst Cloud +# +# 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 troveclient import base +from troveclient import common + + +class BackupStrategy(base.Resource): + def __repr__(self): + return "" % (self.project_id, self.instance_id) + + +class BackupStrategiesManager(base.ManagerWithFind): + resource_class = BackupStrategy + + def list(self, instance_id=None, project_id=None): + query_strings = {} + if instance_id: + query_strings["instance_id"] = instance_id + if project_id: + query_strings["project_id"] = project_id + + url = common.append_query_strings('/backup_strategies', + **query_strings) + + return self._list(url, "backup_strategies") + + def create(self, instance_id=None, swift_container=None): + backup_strategy = {} + if instance_id: + backup_strategy['instance_id'] = instance_id + if swift_container: + backup_strategy['swift_container'] = swift_container + body = {"backup_strategy": backup_strategy} + + return self._create("/backup_strategies", body, "backup_strategy") + + def delete(self, instance_id=None, project_id=None): + url = "/backup_strategies" + query_strings = {} + if instance_id: + query_strings["instance_id"] = instance_id + if project_id: + query_strings["project_id"] = project_id + + url = common.append_query_strings('/backup_strategies', + **query_strings) + + resp, body = self._delete(url) + common.check_for_exceptions(resp, body, url) diff --git a/troveclient/v1/backups.py b/troveclient/v1/backups.py index 3e93ddc..cce6560 100644 --- a/troveclient/v1/backups.py +++ b/troveclient/v1/backups.py @@ -74,7 +74,7 @@ class Backups(base.ManagerWithFind): query_strings) def create(self, name, instance, description=None, - parent_id=None, incremental=False): + parent_id=None, incremental=False, swift_container=None): """Create a new backup from the given instance. :param name: name for backup. @@ -83,6 +83,7 @@ class Backups(base.ManagerWithFind): :param parent_id: base for incremental backup (optional). :param incremental: flag to indicate incremental backup based on last backup + :param swift_container: Swift container name. :returns: :class:`Backups` """ body = { @@ -98,6 +99,8 @@ class Backups(base.ManagerWithFind): body['backup']['description'] = description if parent_id: body['backup']['parent_id'] = parent_id + if swift_container: + body['backup']['swift_container'] = swift_container return self._create("/backups", body, "backup") def delete(self, backup): diff --git a/troveclient/v1/client.py b/troveclient/v1/client.py index 97af857..88a7257 100644 --- a/troveclient/v1/client.py +++ b/troveclient/v1/client.py @@ -15,6 +15,7 @@ # under the License. from troveclient import client as trove_client +from troveclient.v1 import backup_strategy from troveclient.v1 import backups from troveclient.v1 import clusters from troveclient.v1 import configurations @@ -67,6 +68,7 @@ class Client(object): self.users = users.Users(self) self.databases = databases.Databases(self) self.backups = backups.Backups(self) + self.backup_strategies = backup_strategy.BackupStrategiesManager(self) self.clusters = clusters.Clusters(self) self.instances = instances.Instances(self) self.limits = limits.Limits(self) -- cgit v1.2.1