Support backup strategy
Change-Id: Ic8c20109b287f2f9220379cb249669d18b52893d
This commit is contained in:
parent
a05020128d
commit
40af556264
3
releasenotes/notes/victoria-backup-strategy.yaml
Normal file
3
releasenotes/notes/victoria-backup-strategy.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Support backup strategy CLI.
|
@ -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
|
||||
|
@ -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)
|
||||
|
105
troveclient/osc/v1/database_backup_strategy.py
Normal file
105
troveclient/osc/v1/database_backup_strategy.py
Normal 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)
|
@ -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)))
|
||||
|
||||
|
89
troveclient/tests/osc/v1/test_database_backup_strategy.py
Normal file
89
troveclient/tests/osc/v1/test_database_backup_strategy.py
Normal 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',
|
||||
)
|
@ -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):
|
||||
|
60
troveclient/v1/backup_strategy.py
Normal file
60
troveclient/v1/backup_strategy.py
Normal 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)
|
@ -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):
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user