Merge "Add trove database instance protection plugin for karbor"
This commit is contained in:
commit
1b40dc8a80
|
@ -8,6 +8,7 @@ plugin=karbor-image-protection-plugin
|
||||||
plugin=karbor-server-protection-plugin
|
plugin=karbor-server-protection-plugin
|
||||||
plugin=karbor-share-protection-plugin
|
plugin=karbor-share-protection-plugin
|
||||||
plugin=karbor-network-protection-plugin
|
plugin=karbor-network-protection-plugin
|
||||||
|
plugin=karbor-database-protection-plugin
|
||||||
bank=karbor-swift-bank-plugin
|
bank=karbor-swift-bank-plugin
|
||||||
|
|
||||||
enabled=True
|
enabled=True
|
||||||
|
|
|
@ -0,0 +1,305 @@
|
||||||
|
# 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 functools import partial
|
||||||
|
import six
|
||||||
|
|
||||||
|
from karbor.common import constants
|
||||||
|
from karbor import exception
|
||||||
|
from karbor.services.protection.client_factory import ClientFactory
|
||||||
|
from karbor.services.protection import protection_plugin
|
||||||
|
from karbor.services.protection.protection_plugins.database \
|
||||||
|
import database_backup_plugin_schemas as database_instance_schemas
|
||||||
|
from karbor.services.protection.protection_plugins import utils
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from troveclient import exceptions as trove_exc
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
trove_backup_opts = [
|
||||||
|
cfg.IntOpt(
|
||||||
|
'poll_interval', default=15,
|
||||||
|
help='Poll interval for Trove Database Instance status.'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
DATABASE_FAILURE_STATUSES = {'BLOCKED', 'FAILED', 'REBOOT',
|
||||||
|
'SHUTDOWN', 'ERROR',
|
||||||
|
'RESTART_REQUIRED', 'EJECT', 'DETACH'}
|
||||||
|
|
||||||
|
DATABASE_IGNORE_STATUSES = {'BUILD', 'RESIZE', 'BACKUP', 'PROMOTE', 'UPGRADE'}
|
||||||
|
|
||||||
|
|
||||||
|
def get_backup_status(trove_client, backup_id):
|
||||||
|
return get_resource_status(trove_client.backups, backup_id,
|
||||||
|
'backup')
|
||||||
|
|
||||||
|
|
||||||
|
def get_database_instance_status(trove_client, instance_id):
|
||||||
|
return get_resource_status(trove_client.instances, instance_id, 'instance')
|
||||||
|
|
||||||
|
|
||||||
|
def get_resource_status(resource_manager, resource_id, resource_type):
|
||||||
|
LOG.debug('Polling %(resource_type)s (id: %(resource_id)s)',
|
||||||
|
{'resource_type': resource_type, 'resource_id': resource_id})
|
||||||
|
try:
|
||||||
|
resource = resource_manager.get(resource_id)
|
||||||
|
status = resource.status
|
||||||
|
except trove_exc.NotFound:
|
||||||
|
status = 'not-found'
|
||||||
|
LOG.debug(
|
||||||
|
'Polled %(resource_type)s (id: %(resource_id)s) status: %(status)s',
|
||||||
|
{'resource_type': resource_type, 'resource_id': resource_id,
|
||||||
|
'status': status}
|
||||||
|
)
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
class ProtectOperation(protection_plugin.Operation):
|
||||||
|
def __init__(self, poll_interval):
|
||||||
|
super(ProtectOperation, self).__init__()
|
||||||
|
self._interval = poll_interval
|
||||||
|
|
||||||
|
def _create_backup(self, trove_client, instance_id, backup_name,
|
||||||
|
description):
|
||||||
|
backup = trove_client.backups.create(
|
||||||
|
backup_name,
|
||||||
|
instance=instance_id,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
|
||||||
|
backup_id = backup.id
|
||||||
|
is_success = utils.status_poll(
|
||||||
|
partial(get_backup_status, trove_client, backup_id),
|
||||||
|
interval=self._interval,
|
||||||
|
success_statuses={'COMPLETED'},
|
||||||
|
failure_statuses={'FAILED'},
|
||||||
|
ignore_statuses={'BUILDING'},
|
||||||
|
ignore_unexpected=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_success:
|
||||||
|
try:
|
||||||
|
backup = trove_client.backups.get(backup_id)
|
||||||
|
except Exception:
|
||||||
|
reason = 'Unable to find backup.'
|
||||||
|
else:
|
||||||
|
reason = 'The status of backup is %s' % backup.status
|
||||||
|
raise exception.CreateResourceFailed(
|
||||||
|
name="Database Instance Backup",
|
||||||
|
reason=reason, resource_id=instance_id,
|
||||||
|
resource_type=constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
|
||||||
|
return backup_id
|
||||||
|
|
||||||
|
def on_main(self, checkpoint, resource, context, parameters, **kwargs):
|
||||||
|
instance_id = resource.id
|
||||||
|
bank_section = checkpoint.get_resource_bank_section(instance_id)
|
||||||
|
trove_client = ClientFactory.create_client('trove', context)
|
||||||
|
LOG.info('creating database instance backup, instance_id: %s',
|
||||||
|
instance_id)
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_PROTECTING)
|
||||||
|
instance_info = trove_client.instances.get(instance_id)
|
||||||
|
if instance_info.status != "ACTIVE":
|
||||||
|
is_success = utils.status_poll(
|
||||||
|
partial(get_database_instance_status, trove_client,
|
||||||
|
instance_id),
|
||||||
|
interval=self._interval, success_statuses={'ACTIVE'},
|
||||||
|
failure_statuses=DATABASE_FAILURE_STATUSES,
|
||||||
|
ignore_statuses=DATABASE_IGNORE_STATUSES,
|
||||||
|
)
|
||||||
|
if not is_success:
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_ERROR)
|
||||||
|
raise exception.CreateResourceFailed(
|
||||||
|
name="Database instance Backup",
|
||||||
|
reason='Database instance is in a error status.',
|
||||||
|
resource_id=instance_id,
|
||||||
|
resource_type=constants.DATABASE_RESOURCE_TYPE,
|
||||||
|
)
|
||||||
|
resource_metadata = {
|
||||||
|
'instance_id': instance_id,
|
||||||
|
'datastore': instance_info.datastore,
|
||||||
|
'flavor': instance_info.flavor,
|
||||||
|
'size': instance_info.volume['size'],
|
||||||
|
}
|
||||||
|
backup_name = parameters.get('backup_name', 'backup%s' % (
|
||||||
|
instance_id))
|
||||||
|
description = parameters.get('description', None)
|
||||||
|
try:
|
||||||
|
backup_id = self._create_backup(
|
||||||
|
trove_client, instance_id, backup_name, description)
|
||||||
|
except exception.CreateResourceFailed as e:
|
||||||
|
LOG.error('Error creating backup (instance_id: %(instance_id)s '
|
||||||
|
': %(reason)s', {'instance_id': instance_id,
|
||||||
|
'reason': e})
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_ERROR)
|
||||||
|
raise
|
||||||
|
|
||||||
|
resource_metadata['backup_id'] = backup_id
|
||||||
|
|
||||||
|
bank_section.update_object('metadata', resource_metadata)
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_AVAILABLE)
|
||||||
|
LOG.info('Backup database instance (instance_id: %(instance_id)s '
|
||||||
|
'backup_id: %(backup_id)s ) successfully',
|
||||||
|
{'instance_id': instance_id, 'backup_id': backup_id})
|
||||||
|
|
||||||
|
|
||||||
|
class RestoreOperation(protection_plugin.Operation):
|
||||||
|
def __init__(self, poll_interval):
|
||||||
|
super(RestoreOperation, self).__init__()
|
||||||
|
self._interval = poll_interval
|
||||||
|
|
||||||
|
def on_main(self, checkpoint, resource, context, parameters, **kwargs):
|
||||||
|
original_instance_id = resource.id
|
||||||
|
bank_section = checkpoint.get_resource_bank_section(
|
||||||
|
original_instance_id)
|
||||||
|
trove_client = ClientFactory.create_client('trove', context)
|
||||||
|
resource_metadata = bank_section.get_object('metadata')
|
||||||
|
restore_name = parameters.get('restore_name',
|
||||||
|
'%s@%s' % (checkpoint.id,
|
||||||
|
original_instance_id))
|
||||||
|
flavor = resource_metadata['flavor']
|
||||||
|
size = resource_metadata['size']
|
||||||
|
backup_id = resource_metadata['backup_id']
|
||||||
|
restore = kwargs.get('restore')
|
||||||
|
LOG.info("Restoring a database instance from backup, "
|
||||||
|
"original_instance_id: %s.", original_instance_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
instance_info = trove_client.instances.create(
|
||||||
|
restore_name, flavor["id"], volume={"size": size},
|
||||||
|
restorePoint={"backupRef": backup_id})
|
||||||
|
is_success = utils.status_poll(
|
||||||
|
partial(get_database_instance_status, trove_client,
|
||||||
|
instance_info.id),
|
||||||
|
interval=self._interval, success_statuses={'ACTIVE'},
|
||||||
|
failure_statuses=DATABASE_FAILURE_STATUSES,
|
||||||
|
ignore_statuses=DATABASE_IGNORE_STATUSES
|
||||||
|
)
|
||||||
|
if is_success is not True:
|
||||||
|
LOG.error('The status of database instance is '
|
||||||
|
'invalid. status:%s', instance_info.status)
|
||||||
|
restore.update_resource_status(
|
||||||
|
constants.DATABASE_RESOURCE_TYPE,
|
||||||
|
instance_info.id, instance_info.status,
|
||||||
|
"Invalid status.")
|
||||||
|
restore.save()
|
||||||
|
raise exception.RestoreResourceFailed(
|
||||||
|
name="Database instance Backup",
|
||||||
|
reason="Invalid status.",
|
||||||
|
resource_id=original_instance_id,
|
||||||
|
resource_type=constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
restore.update_resource_status(
|
||||||
|
constants.DATABASE_RESOURCE_TYPE,
|
||||||
|
instance_info.id, instance_info.status)
|
||||||
|
restore.save()
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error("Restore Database instance from backup "
|
||||||
|
"failed, instance_id: %s.", original_instance_id)
|
||||||
|
raise exception.RestoreResourceFailed(
|
||||||
|
name="Database instance Backup",
|
||||||
|
reason=e, resource_id=original_instance_id,
|
||||||
|
resource_type=constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
LOG.info("Finish restoring a Database instance from backup,"
|
||||||
|
"instance_id: %s.", original_instance_id)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteOperation(protection_plugin.Operation):
|
||||||
|
def __init__(self, poll_interval):
|
||||||
|
super(DeleteOperation, self).__init__()
|
||||||
|
self._interval = poll_interval
|
||||||
|
|
||||||
|
def on_main(self, checkpoint, resource, context, parameters, **kwargs):
|
||||||
|
resource_id = resource.id
|
||||||
|
bank_section = checkpoint.get_resource_bank_section(resource_id)
|
||||||
|
backup_id = None
|
||||||
|
try:
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_DELETING)
|
||||||
|
resource_metadata = bank_section.get_object('metadata')
|
||||||
|
backup_id = resource_metadata['backup_id']
|
||||||
|
trove_client = ClientFactory.create_client('trove', context)
|
||||||
|
try:
|
||||||
|
backup = trove_client.backups.get(backup_id)
|
||||||
|
trove_client.backups.delete(backup)
|
||||||
|
except trove_exc.NotFound:
|
||||||
|
LOG.info('Backup id: %s not found. Assuming deleted',
|
||||||
|
backup_id)
|
||||||
|
is_success = utils.status_poll(
|
||||||
|
partial(get_backup_status, trove_client, backup_id),
|
||||||
|
interval=self._interval,
|
||||||
|
success_statuses={'not-found'},
|
||||||
|
failure_statuses={'FAILED', 'DELETE_FAILED'},
|
||||||
|
ignore_statuses={'COMPLETED'},
|
||||||
|
ignore_unexpected=True
|
||||||
|
)
|
||||||
|
if not is_success:
|
||||||
|
raise exception.NotFound()
|
||||||
|
bank_section.delete_object('metadata')
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_DELETED)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error('Delete Database instance Backup failed, backup_id: %s',
|
||||||
|
backup_id)
|
||||||
|
bank_section.update_object('status',
|
||||||
|
constants.RESOURCE_STATUS_ERROR)
|
||||||
|
raise exception.DeleteResourceFailed(
|
||||||
|
name="Database instance Backup",
|
||||||
|
reason=six.text_type(e),
|
||||||
|
resource_id=resource_id,
|
||||||
|
resource_type=constants.DATABASE_RESOURCE_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseBackupProtectionPlugin(protection_plugin.ProtectionPlugin):
|
||||||
|
_SUPPORT_RESOURCE_TYPES = [constants.DATABASE_RESOURCE_TYPE]
|
||||||
|
|
||||||
|
def __init__(self, config=None):
|
||||||
|
super(DatabaseBackupProtectionPlugin, self).__init__(config)
|
||||||
|
self._config.register_opts(trove_backup_opts,
|
||||||
|
'database_backup_plugin')
|
||||||
|
self._plugin_config = self._config.database_backup_plugin
|
||||||
|
self._poll_interval = self._plugin_config.poll_interval
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_supported_resources_types(cls):
|
||||||
|
return cls._SUPPORT_RESOURCE_TYPES
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options_schema(cls, resources_type):
|
||||||
|
return database_instance_schemas.OPTIONS_SCHEMA
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_restore_schema(cls, resources_type):
|
||||||
|
return database_instance_schemas.RESTORE_SCHEMA
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_saved_info_schema(cls, resources_type):
|
||||||
|
return database_instance_schemas.SAVED_INFO_SCHEMA
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_saved_info(cls, metadata_store, resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_protect_operation(self, resource):
|
||||||
|
return ProtectOperation(self._poll_interval)
|
||||||
|
|
||||||
|
def get_restore_operation(self, resource):
|
||||||
|
return RestoreOperation(self._poll_interval)
|
||||||
|
|
||||||
|
def get_delete_operation(self, resource):
|
||||||
|
return DeleteOperation(self._poll_interval)
|
|
@ -0,0 +1,51 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
OPTIONS_SCHEMA = {
|
||||||
|
"title": "Database Instance Protection Options",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"backup_name": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Backup Name",
|
||||||
|
"description": "The name of the backup.",
|
||||||
|
"default": None
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Description",
|
||||||
|
"description": "The description of the backup."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["backup_name"]
|
||||||
|
}
|
||||||
|
|
||||||
|
RESTORE_SCHEMA = {
|
||||||
|
"title": "Database Instance Protection Restore",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"restore_name": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Restore Database Instance Name",
|
||||||
|
"description": "The name of the restore Database Instance",
|
||||||
|
"default": None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["restore_name"]
|
||||||
|
}
|
||||||
|
|
||||||
|
SAVED_INFO_SCHEMA = {
|
||||||
|
"title": "Database Instance Protection Saved Info",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import collections
|
||||||
|
from karbor.common import constants
|
||||||
|
from karbor.context import RequestContext
|
||||||
|
from karbor.resource import Resource
|
||||||
|
from karbor.services.protection.bank_plugin import Bank
|
||||||
|
from karbor.services.protection.bank_plugin import BankPlugin
|
||||||
|
from karbor.services.protection.bank_plugin import BankSection
|
||||||
|
from karbor.services.protection import client_factory
|
||||||
|
|
||||||
|
from karbor.services.protection.protection_plugins. \
|
||||||
|
database.database_backup_plugin import DatabaseBackupProtectionPlugin
|
||||||
|
from karbor.services.protection.protection_plugins.database \
|
||||||
|
import database_backup_plugin_schemas
|
||||||
|
|
||||||
|
from karbor.tests import base
|
||||||
|
import mock
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_config import fixture
|
||||||
|
|
||||||
|
|
||||||
|
class FakeBankPlugin(BankPlugin):
|
||||||
|
def update_object(self, key, value):
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_object(self, key):
|
||||||
|
return
|
||||||
|
|
||||||
|
def list_objects(self, prefix=None, limit=None, marker=None,
|
||||||
|
sort_dir=None):
|
||||||
|
return
|
||||||
|
|
||||||
|
def delete_object(self, key):
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_owner_id(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
fake_bank = Bank(FakeBankPlugin())
|
||||||
|
fake_bank_section = BankSection(bank=fake_bank, section="fake")
|
||||||
|
|
||||||
|
ResourceNode = collections.namedtuple(
|
||||||
|
"ResourceNode",
|
||||||
|
["value",
|
||||||
|
"child_nodes"]
|
||||||
|
)
|
||||||
|
|
||||||
|
Database = collections.namedtuple(
|
||||||
|
"Database",
|
||||||
|
["status"]
|
||||||
|
)
|
||||||
|
|
||||||
|
Backup = collections.namedtuple(
|
||||||
|
"Backup",
|
||||||
|
["id", "status"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def call_hooks(operation, checkpoint, resource, context, parameters, **kwargs):
|
||||||
|
def noop(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
hooks = (
|
||||||
|
'on_prepare_begin',
|
||||||
|
'on_prepare_finish',
|
||||||
|
'on_main',
|
||||||
|
'on_complete',
|
||||||
|
)
|
||||||
|
for hook_name in hooks:
|
||||||
|
hook = getattr(operation, hook_name, noop)
|
||||||
|
hook(checkpoint, resource, context, parameters, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckpointCollection(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.bank_section = fake_bank_section
|
||||||
|
|
||||||
|
def get_resource_bank_section(self, resource_id):
|
||||||
|
return self.bank_section
|
||||||
|
|
||||||
|
|
||||||
|
class TroveProtectionPluginTest(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TroveProtectionPluginTest, self).setUp()
|
||||||
|
|
||||||
|
plugin_config = cfg.ConfigOpts()
|
||||||
|
plugin_config_fixture = self.useFixture(fixture.Config(plugin_config))
|
||||||
|
plugin_config_fixture.load_raw_values(
|
||||||
|
group='database_backup_plugin',
|
||||||
|
poll_interval=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.plugin = DatabaseBackupProtectionPlugin(plugin_config)
|
||||||
|
|
||||||
|
cfg.CONF.set_default('trove_endpoint',
|
||||||
|
'http://127.0.0.1:8774/v2.1',
|
||||||
|
'trove_client')
|
||||||
|
service_catalog = [
|
||||||
|
{'type': 'database',
|
||||||
|
'endpoints': [{'publicURL': 'http://127.0.0.1:8774/v2.1/abcd'}],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.cntxt = RequestContext(user_id='demo',
|
||||||
|
project_id='abcd',
|
||||||
|
auth_token='efgh',
|
||||||
|
service_catalog=service_catalog)
|
||||||
|
self.trove_client = client_factory.ClientFactory.create_client(
|
||||||
|
"trove", self.cntxt)
|
||||||
|
self.checkpoint = CheckpointCollection()
|
||||||
|
|
||||||
|
def test_get_options_schema(self):
|
||||||
|
options_schema = self.plugin.get_options_schema(
|
||||||
|
constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
self.assertEqual(options_schema,
|
||||||
|
database_backup_plugin_schemas.OPTIONS_SCHEMA)
|
||||||
|
|
||||||
|
def test_get_restore_schema(self):
|
||||||
|
options_schema = self.plugin.get_restore_schema(
|
||||||
|
constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
self.assertEqual(options_schema,
|
||||||
|
database_backup_plugin_schemas.RESTORE_SCHEMA)
|
||||||
|
|
||||||
|
def test_get_saved_info_schema(self):
|
||||||
|
options_schema = self.plugin.get_saved_info_schema(
|
||||||
|
constants.DATABASE_RESOURCE_TYPE)
|
||||||
|
self.assertEqual(options_schema,
|
||||||
|
database_backup_plugin_schemas.SAVED_INFO_SCHEMA)
|
||||||
|
|
||||||
|
@mock.patch('karbor.services.protection.protection_plugins.database.'
|
||||||
|
'database_backup_plugin.utils.status_poll')
|
||||||
|
@mock.patch('karbor.services.protection.clients.trove.create')
|
||||||
|
def test_create_backup(self, mock_trove_create, mock_status_poll):
|
||||||
|
resource = Resource(id="123",
|
||||||
|
type=constants.DATABASE_RESOURCE_TYPE,
|
||||||
|
name='fake')
|
||||||
|
|
||||||
|
fake_bank_section.update_object = mock.MagicMock()
|
||||||
|
|
||||||
|
protect_operation = self.plugin.get_protect_operation(resource)
|
||||||
|
mock_trove_create.return_value = self.trove_client
|
||||||
|
|
||||||
|
self.trove_client.instances.get = mock.MagicMock()
|
||||||
|
self.trove_client.instances.return_value = Database(
|
||||||
|
status="ACTIVE"
|
||||||
|
)
|
||||||
|
fake_bank_section.update_object = mock.MagicMock()
|
||||||
|
self.trove_client.backups.create = mock.MagicMock()
|
||||||
|
self.trove_client.backups.create.return_value = Backup(
|
||||||
|
id="1234",
|
||||||
|
status="COMPLETED"
|
||||||
|
)
|
||||||
|
self.trove_client.backups.get = mock.MagicMock()
|
||||||
|
self.trove_client.backups.get.return_value = Backup(
|
||||||
|
id="1234",
|
||||||
|
status="COMPLETED"
|
||||||
|
)
|
||||||
|
mock_status_poll.return_value = True
|
||||||
|
call_hooks(protect_operation, self.checkpoint, resource, self.cntxt,
|
||||||
|
{})
|
||||||
|
|
||||||
|
@mock.patch('karbor.services.protection.protection_plugins.database.'
|
||||||
|
'database_backup_plugin.utils.status_poll')
|
||||||
|
@mock.patch('karbor.services.protection.clients.trove.create')
|
||||||
|
def test_delete_backup(self, mock_trove_create, mock_status_poll):
|
||||||
|
resource = Resource(id="123",
|
||||||
|
type=constants.DATABASE_RESOURCE_TYPE,
|
||||||
|
name='fake')
|
||||||
|
mock_trove_create.return_value = self.trove_client
|
||||||
|
self.trove_client.backups.get = mock.MagicMock()
|
||||||
|
self.trove_client.backups.get.return_value = Backup(
|
||||||
|
id="1234",
|
||||||
|
status="COMPLETED"
|
||||||
|
)
|
||||||
|
self.trove_client.backups.delete = mock.MagicMock()
|
||||||
|
|
||||||
|
fake_bank_section.get_object = mock.MagicMock()
|
||||||
|
fake_bank_section.get_object.return_value = {
|
||||||
|
"backup_id": "1234"}
|
||||||
|
|
||||||
|
mock_status_poll.return_value = True
|
||||||
|
delete_operation = self.plugin.get_delete_operation(resource)
|
||||||
|
call_hooks(delete_operation, self.checkpoint, resource, self.cntxt,
|
||||||
|
{})
|
||||||
|
|
||||||
|
def test_get_supported_resources_types(self):
|
||||||
|
types = self.plugin.get_supported_resources_types()
|
||||||
|
self.assertEqual(types,
|
||||||
|
[constants.DATABASE_RESOURCE_TYPE])
|
|
@ -45,6 +45,7 @@ karbor.protections =
|
||||||
karbor-share-protection-plugin = karbor.services.protection.protection_plugins.share.share_snapshot_plugin:ManilaSnapshotProtectionPlugin
|
karbor-share-protection-plugin = karbor.services.protection.protection_plugins.share.share_snapshot_plugin:ManilaSnapshotProtectionPlugin
|
||||||
karbor-noop-protection-plugin = karbor.services.protection.protection_plugins.noop_plugin:NoopProtectionPlugin
|
karbor-noop-protection-plugin = karbor.services.protection.protection_plugins.noop_plugin:NoopProtectionPlugin
|
||||||
karbor-network-protection-plugin = karbor.services.protection.protection_plugins.network.neutron_protection_plugin:NeutronProtectionPlugin
|
karbor-network-protection-plugin = karbor.services.protection.protection_plugins.network.neutron_protection_plugin:NeutronProtectionPlugin
|
||||||
|
karbor-database-protection-plugin = karbor.services.protection.protection_plugins.database.database_backup_plugin:DatabaseBackupProtectionPlugin
|
||||||
karbor.provider =
|
karbor.provider =
|
||||||
provider-registry = karbor.services.protection.provider:ProviderRegistry
|
provider-registry = karbor.services.protection.provider:ProviderRegistry
|
||||||
karbor.protectables =
|
karbor.protectables =
|
||||||
|
|
Loading…
Reference in New Issue