188 lines
6.9 KiB
Python
188 lines
6.9 KiB
Python
# 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_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_service import loopingcall
|
|
from oslo_utils import uuidutils
|
|
|
|
from karbor.common import constants
|
|
from karbor.services.protection import client_factory
|
|
from karbor.services.protection import resource_flow
|
|
from karbor.services.protection import restore_heat
|
|
from taskflow import task
|
|
|
|
sync_status_opts = [
|
|
cfg.IntOpt('sync_status_interval',
|
|
default=20,
|
|
help='update protection status interval')
|
|
]
|
|
|
|
CONF = cfg.CONF
|
|
CONF.register_opts(sync_status_opts)
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class InitiateRestoreTask(task.Task):
|
|
def execute(self, restore, *args, **kwargs):
|
|
LOG.debug("Initiate restore restore_id: %s", restore.id)
|
|
restore['status'] = constants.RESTORE_STATUS_IN_PROGRESS
|
|
restore.save()
|
|
|
|
def revert(self, restore, *args, **kwargs):
|
|
LOG.debug("Failed to restore restore_id: %s", restore.id)
|
|
restore['status'] = constants.RESTORE_STATUS_FAILURE
|
|
restore.save()
|
|
|
|
|
|
class CompleteRestoreTask(task.Task):
|
|
def execute(self, restore, *args, **kwargs):
|
|
LOG.debug("Complete restore restore_id: %s", restore.id)
|
|
restore['status'] = constants.RESTORE_STATUS_SUCCESS
|
|
restore.save()
|
|
|
|
|
|
class CreateHeatTask(task.Task):
|
|
default_provides = ['heat_client', 'heat_template']
|
|
|
|
def execute(self, context, heat_conf):
|
|
LOG.info('Creating Heat template. Target: "%s"'
|
|
% heat_conf.get('auth_url', '(None)'))
|
|
heat_client = client_factory.ClientFactory.create_client(
|
|
'heat', context=context, **heat_conf)
|
|
|
|
heat_template = restore_heat.HeatTemplate()
|
|
|
|
return (heat_client, heat_template)
|
|
|
|
|
|
class CreateStackTask(task.Task):
|
|
default_provides = 'stack_id'
|
|
|
|
def execute(self, heat_client, heat_template):
|
|
stack_name = "restore_%s" % uuidutils.generate_uuid()
|
|
if heat_template.len() == 0:
|
|
LOG.info('Not creating Heat stack, no resources in template')
|
|
return None
|
|
LOG.info('Creating Heat stack, stack_name: %s', stack_name)
|
|
try:
|
|
body = heat_client.stacks.create(
|
|
stack_name=stack_name,
|
|
template=heat_template.to_dict())
|
|
LOG.debug('Created stack with id: %s', body['stack']['id'])
|
|
return body['stack']['id']
|
|
except Exception:
|
|
LOG.error("use heat to create stack failed")
|
|
raise
|
|
|
|
|
|
class SyncRestoreStatusTask(task.Task):
|
|
def execute(self, stack_id, heat_client, restore):
|
|
if stack_id is None:
|
|
LOG.info('Not syncing Heat stack status, stack is empty')
|
|
return
|
|
|
|
LOG.info('Syncing Heat stack status, stack_id: %s', stack_id)
|
|
self._restore = restore
|
|
sync_status_loop = loopingcall.FixedIntervalLoopingCall(
|
|
self._sync_status, heat_client, stack_id)
|
|
sync_status_loop.start(interval=CONF.sync_status_interval)
|
|
sync_status_loop.wait()
|
|
|
|
def _sync_status(self, heat_client, stack_id):
|
|
try:
|
|
stack = heat_client.stacks.get(stack_id)
|
|
except Exception:
|
|
LOG.debug('Heat error getting stack, stack_id: %s', stack_id)
|
|
raise
|
|
stack_status = getattr(stack, 'stack_status')
|
|
if stack_status == 'CREATE_IN_PROGRESS':
|
|
LOG.debug('Heat stack status: in progress, stack_id: %s',
|
|
stack_id)
|
|
elif stack_status == 'CREATE_COMPLETE':
|
|
LOG.info('Heat stack status: complete, stack_id: %s', stack_id)
|
|
self._update_resource_status(heat_client, stack_id)
|
|
raise loopingcall.LoopingCallDone()
|
|
else:
|
|
LOG.info('Heat stack status: failure, stack_id: %s', stack_id)
|
|
self._update_resource_status(heat_client, stack_id)
|
|
raise
|
|
|
|
def _update_resource_status(self, heat_client, stack_id):
|
|
LOG.debug('Updating resources status from heat stack (stack_id: %s)',
|
|
stack_id)
|
|
try:
|
|
resources = heat_client.resources.list(stack_id)
|
|
for resource in resources:
|
|
heat_to_karbor_map = {
|
|
'CREATE_COMPLETE': constants.RESOURCE_STATUS_AVAILABLE,
|
|
'CREATE_IN_PROGRESS': constants.RESOURCE_STATUS_RESTORING,
|
|
'CREATE_FAILED': constants.RESOURCE_STATUS_ERROR,
|
|
}
|
|
reason = resource.resource_status_reason if (
|
|
resource.resource_status == 'CREATE_FAILED'
|
|
) else None
|
|
self._restore.update_resource_status(
|
|
resource.resource_type,
|
|
resource.physical_resource_id,
|
|
heat_to_karbor_map[resource.resource_status],
|
|
reason,
|
|
)
|
|
self._restore.save()
|
|
except Exception as e:
|
|
LOG.warning('Unable to update resources status from heat stack. '
|
|
'Reason: %s', e)
|
|
|
|
|
|
def get_flow(context, workflow_engine, checkpoint, provider, restore,
|
|
restore_auth):
|
|
target = restore.get('restore_target', None)
|
|
|
|
heat_conf = {}
|
|
if target is not None:
|
|
heat_conf["auth_url"] = target
|
|
if restore_auth is not None:
|
|
auth_type = restore_auth.get("type", None)
|
|
if auth_type == "password":
|
|
heat_conf["username"] = restore_auth["username"]
|
|
heat_conf["password"] = restore_auth["password"]
|
|
|
|
resource_graph = checkpoint.resource_graph
|
|
parameters = restore.parameters
|
|
flow_name = "Restore_" + checkpoint.id
|
|
restore_flow = workflow_engine.build_flow(flow_name, 'linear')
|
|
plugins = provider.load_plugins()
|
|
resources_task_flow = resource_flow.build_resource_flow(
|
|
operation_type=constants.OPERATION_RESTORE,
|
|
context=context,
|
|
workflow_engine=workflow_engine,
|
|
resource_graph=resource_graph,
|
|
plugins=plugins,
|
|
parameters=parameters
|
|
)
|
|
|
|
workflow_engine.add_tasks(
|
|
restore_flow,
|
|
InitiateRestoreTask(),
|
|
CreateHeatTask(inject={'context': context, 'heat_conf': heat_conf}),
|
|
resources_task_flow,
|
|
CreateStackTask(),
|
|
SyncRestoreStatusTask(),
|
|
CompleteRestoreTask()
|
|
)
|
|
flow_engine = workflow_engine.get_engine(restore_flow,
|
|
store={'checkpoint': checkpoint,
|
|
'restore': restore})
|
|
return flow_engine
|