Support backup strategy

Change-Id: Ic8c20109b287f2f9220379cb249669d18b52893d
This commit is contained in:
Lingxian Kong 2020-07-12 21:48:29 +12:00
parent a05020128d
commit 40af556264
10 changed files with 288 additions and 8 deletions

View File

@ -0,0 +1,3 @@
---
features:
- Support backup strategy CLI.

View File

@ -104,3 +104,6 @@ openstack.database.v1 =
datastore_version_list = troveclient.osc.v1.datastores:ListDatastoreVersions
datastore_version_show = troveclient.osc.v1.datastores:ShowDatastoreVersion
datastore_version_delete = troveclient.osc.v1.datastores:DeleteDatastoreVersion
database_backup_strategy_list = troveclient.osc.v1.database_backup_strategy:ListDatabaseBackupStrategies
database_backup_strategy_create = troveclient.osc.v1.database_backup_strategy:CreateDatabaseBackupStrategy
database_backup_strategy_delete = troveclient.osc.v1.database_backup_strategy:DeleteDatabaseBackupStrategy

View File

@ -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)

View File

@ -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)

View File

@ -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)))

View File

@ -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',
)

View File

@ -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):

View File

@ -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 "<BackupStrategy: %s[%s]>" % (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)

View File

@ -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):

View File

@ -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)