Merge "Create subcloud-backup restore command"
This commit is contained in:
commit
638dfd63c3
@ -17,8 +17,6 @@ from pecan import expose
|
|||||||
from pecan import request as pecan_request
|
from pecan import request as pecan_request
|
||||||
from pecan import response
|
from pecan import response
|
||||||
|
|
||||||
from dccommon import consts as dccommon_consts
|
|
||||||
|
|
||||||
from dcmanager.api.controllers import restcomm
|
from dcmanager.api.controllers import restcomm
|
||||||
from dcmanager.api.policies import subcloud_backup as subcloud_backup_policy
|
from dcmanager.api.policies import subcloud_backup as subcloud_backup_policy
|
||||||
from dcmanager.api import policy
|
from dcmanager.api import policy
|
||||||
@ -49,30 +47,40 @@ class SubcloudBackupController(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_backup_payload(request):
|
def _get_payload(request, verb):
|
||||||
return SubcloudBackupController._get_payload(request, {
|
expected_params = dict()
|
||||||
"subcloud": "text",
|
if verb == 'create':
|
||||||
"group": "text",
|
expected_params = {
|
||||||
"local_only": "text",
|
"subcloud": "text",
|
||||||
"registry_images": "text",
|
"group": "text",
|
||||||
"backup_values": "yaml",
|
"local_only": "text",
|
||||||
"sysadmin_password": "text"
|
"registry_images": "text",
|
||||||
})
|
"backup_values": "yaml",
|
||||||
|
"sysadmin_password": "text"
|
||||||
|
}
|
||||||
|
elif verb == 'delete':
|
||||||
|
expected_params = {
|
||||||
|
"release": "text",
|
||||||
|
"subcloud": "text",
|
||||||
|
"group": "text",
|
||||||
|
"local_only": "text",
|
||||||
|
"sysadmin_password": "text"
|
||||||
|
}
|
||||||
|
elif verb == 'restore':
|
||||||
|
expected_params = {
|
||||||
|
"with_install": "text",
|
||||||
|
"local_only": "text",
|
||||||
|
"registry_images": "text",
|
||||||
|
"sysadmin_password": "text",
|
||||||
|
"restore_values": "text",
|
||||||
|
"subcloud": "text",
|
||||||
|
"group": "text"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
pecan.abort(400, _("Unexpected verb received"))
|
||||||
|
|
||||||
@staticmethod
|
return SubcloudBackupController._get_json_payload(request,
|
||||||
def _get_backup_delete_payload(request):
|
expected_params)
|
||||||
return SubcloudBackupController._get_payload(request, {
|
|
||||||
"release": "text",
|
|
||||||
"subcloud": "text",
|
|
||||||
"group": "text",
|
|
||||||
"local_only": "text",
|
|
||||||
"sysadmin_password": "text"
|
|
||||||
})
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_payload(request, expected_params):
|
|
||||||
return SubcloudBackupController._get_json_payload(
|
|
||||||
request, expected_params)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_json_payload(request, expected_params):
|
def _get_json_payload(request, expected_params):
|
||||||
@ -107,36 +115,47 @@ class SubcloudBackupController(object):
|
|||||||
pecan.abort(400, msg)
|
pecan.abort(400, msg)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_param_to_bool(payload, param_name, default):
|
def _convert_param_to_bool(payload, param_names, default=False):
|
||||||
param = payload.get(param_name)
|
for param_name in param_names:
|
||||||
if param:
|
param = payload.get(param_name)
|
||||||
if param.lower() == 'true':
|
if param:
|
||||||
payload[param_name] = True
|
if param.lower() == 'true':
|
||||||
elif param.lower() == 'false':
|
payload[param_name] = True
|
||||||
payload[param_name] = False
|
elif param.lower() == 'false':
|
||||||
|
payload[param_name] = False
|
||||||
|
else:
|
||||||
|
pecan.abort(400, _('Invalid %s value, should be boolean'
|
||||||
|
% param_name))
|
||||||
else:
|
else:
|
||||||
pecan.abort(400, _('Invalid %s value, should be boolean'
|
payload[param_name] = default
|
||||||
% param_name))
|
|
||||||
else:
|
|
||||||
payload[param_name] = default
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_subcloud(subcloud):
|
def _validate_subclouds(subclouds, operation):
|
||||||
if not subcloud:
|
"""Validate the subcloud according to the operation
|
||||||
pecan.abort(404, _('Subcloud not found'))
|
|
||||||
|
|
||||||
if subcloud.availability_status != dccommon_consts.AVAILABILITY_ONLINE:
|
Create/Delete: The subcloud is managed, online and in complete state.
|
||||||
pecan.abort(400, _('Subcloud must be online for this operation'))
|
Restore: The subcloud is unmanaged, and not in the process of
|
||||||
|
installation, boostrap, deployment or rehoming.
|
||||||
|
|
||||||
if subcloud.management_state != dccommon_consts.MANAGEMENT_MANAGED:
|
If none of the subclouds are valid, the operation will be aborted.
|
||||||
pecan.abort(400, _('Operation not allowed while subcloud is unmanaged. '
|
|
||||||
'Please manage the subcloud and try again.'))
|
|
||||||
|
|
||||||
elif subcloud.deploy_status != consts.DEPLOY_STATE_DONE:
|
Args:
|
||||||
pecan.abort(400, _("The current subcloud deploy state is %s. "
|
subclouds (list): List of subclouds to be validated
|
||||||
"This operation is only allowed while subcloud "
|
operation (string): Subcloud backup operation
|
||||||
"deploy state is 'complete'."
|
"""
|
||||||
% subcloud.deploy_status))
|
|
||||||
|
if operation == 'create' or operation == 'delete':
|
||||||
|
valid_subclouds = [subcloud for subcloud in subclouds if
|
||||||
|
utils.is_valid_for_backup(subcloud)]
|
||||||
|
elif operation == 'restore':
|
||||||
|
valid_subclouds = [subcloud for subcloud in subclouds if
|
||||||
|
utils.is_valid_for_restore(subcloud)]
|
||||||
|
else:
|
||||||
|
pecan.abort(400, _('Operation %s is not valid' % operation))
|
||||||
|
|
||||||
|
if not valid_subclouds:
|
||||||
|
pecan.abort(400, _('Subcloud backup %s is not allowed because the '
|
||||||
|
'subcloud(s) are in invalid states.') % operation)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_subclouds_from_group(group, context):
|
def _get_subclouds_from_group(group, context):
|
||||||
@ -145,37 +164,7 @@ class SubcloudBackupController(object):
|
|||||||
|
|
||||||
return db_api.subcloud_get_for_group(context, group.id)
|
return db_api.subcloud_get_for_group(context, group.id)
|
||||||
|
|
||||||
@staticmethod
|
def _read_entity_from_request_params(self, context, payload):
|
||||||
def _validate_group_subclouds(group_subclouds):
|
|
||||||
if not group_subclouds:
|
|
||||||
pecan.abort(400, _('No subclouds present in group'))
|
|
||||||
|
|
||||||
online_subclouds = [subcloud for subcloud in group_subclouds
|
|
||||||
if subcloud.availability_status ==
|
|
||||||
dccommon_consts.AVAILABILITY_ONLINE]
|
|
||||||
|
|
||||||
if not online_subclouds:
|
|
||||||
pecan.abort(400, _('No online subclouds present in group'))
|
|
||||||
|
|
||||||
managed_subclouds = [subcloud for subcloud in group_subclouds
|
|
||||||
if subcloud.management_state ==
|
|
||||||
dccommon_consts.MANAGEMENT_MANAGED]
|
|
||||||
|
|
||||||
if not managed_subclouds:
|
|
||||||
pecan.abort(400, _('No online and managed subclouds present in group. '
|
|
||||||
'Please manage subclouds and try again.'))
|
|
||||||
|
|
||||||
invalid_states = consts.INVALID_DEPLOY_STATES_FOR_BACKUP
|
|
||||||
valid_state_subclouds = [subcloud for subcloud in managed_subclouds
|
|
||||||
if subcloud.deploy_status not in invalid_states]
|
|
||||||
|
|
||||||
if not valid_state_subclouds:
|
|
||||||
pecan.abort(400, _('This operation is not allowed while subcloud '
|
|
||||||
'install, bootstrap or deploy is in progress. '
|
|
||||||
'No online and managed subclouds in a valid '
|
|
||||||
'deploy state present for this group.'))
|
|
||||||
|
|
||||||
def _read_entity_from_request_params(self, context, payload, validate_subclouds):
|
|
||||||
subcloud_ref = payload.get('subcloud')
|
subcloud_ref = payload.get('subcloud')
|
||||||
group_ref = payload.get('group')
|
group_ref = payload.get('group')
|
||||||
|
|
||||||
@ -184,14 +173,14 @@ class SubcloudBackupController(object):
|
|||||||
pecan.abort(400, _("'subcloud' and 'group' parameters "
|
pecan.abort(400, _("'subcloud' and 'group' parameters "
|
||||||
"should not be given at the same time"))
|
"should not be given at the same time"))
|
||||||
subcloud = utils.subcloud_get_by_ref(context, subcloud_ref)
|
subcloud = utils.subcloud_get_by_ref(context, subcloud_ref)
|
||||||
if validate_subclouds:
|
if not subcloud:
|
||||||
self._validate_subcloud(subcloud)
|
pecan.abort(400, _('Subcloud not found'))
|
||||||
return RequestEntity('subcloud', subcloud.id, [subcloud])
|
return RequestEntity('subcloud', subcloud.id, [subcloud])
|
||||||
elif group_ref:
|
elif group_ref:
|
||||||
group = utils.subcloud_group_get_by_ref(context, group_ref)
|
group = utils.subcloud_group_get_by_ref(context, group_ref)
|
||||||
group_subclouds = self._get_subclouds_from_group(group, context)
|
group_subclouds = self._get_subclouds_from_group(group, context)
|
||||||
if validate_subclouds:
|
if not group_subclouds:
|
||||||
self._validate_group_subclouds(group_subclouds)
|
pecan.abort(400, _('No subclouds present in group'))
|
||||||
return RequestEntity('group', group.id, group_subclouds)
|
return RequestEntity('group', group.id, group_subclouds)
|
||||||
else:
|
else:
|
||||||
pecan.abort(400, _("'subcloud' or 'group' parameter is required"))
|
pecan.abort(400, _("'subcloud' or 'group' parameter is required"))
|
||||||
@ -207,8 +196,7 @@ class SubcloudBackupController(object):
|
|||||||
Subcloud.backup_status.name: consts.BACKUP_STATE_INITIAL
|
Subcloud.backup_status.name: consts.BACKUP_STATE_INITIAL
|
||||||
}
|
}
|
||||||
|
|
||||||
db_api.subcloud_bulk_update_by_ids(context, subcloud_ids,
|
db_api.subcloud_bulk_update_by_ids(context, subcloud_ids, update_form)
|
||||||
update_form)
|
|
||||||
|
|
||||||
@utils.synchronized(LOCK_NAME)
|
@utils.synchronized(LOCK_NAME)
|
||||||
@index.when(method='POST', template='json')
|
@index.when(method='POST', template='json')
|
||||||
@ -216,20 +204,19 @@ class SubcloudBackupController(object):
|
|||||||
"""Create a new subcloud backup."""
|
"""Create a new subcloud backup."""
|
||||||
|
|
||||||
context = restcomm.extract_context_from_environ()
|
context = restcomm.extract_context_from_environ()
|
||||||
payload = self._get_backup_payload(pecan_request)
|
payload = self._get_payload(pecan_request, 'create')
|
||||||
|
|
||||||
policy.authorize(subcloud_backup_policy.POLICY_ROOT % "create", {},
|
policy.authorize(subcloud_backup_policy.POLICY_ROOT % "create", {},
|
||||||
restcomm.extract_credentials_for_policy())
|
restcomm.extract_credentials_for_policy())
|
||||||
|
|
||||||
request_entity = self._read_entity_from_request_params(
|
request_entity = self._read_entity_from_request_params(context, payload)
|
||||||
context, payload, validate_subclouds=True)
|
self._validate_subclouds(request_entity.subclouds, 'create')
|
||||||
|
|
||||||
# Set subcloud/group ID as reference instead of name to ease processing
|
# Set subcloud/group ID as reference instead of name to ease processing
|
||||||
payload[request_entity.type] = request_entity.id
|
payload[request_entity.type] = request_entity.id
|
||||||
subclouds = request_entity.subclouds
|
subclouds = request_entity.subclouds
|
||||||
|
|
||||||
self._convert_param_to_bool(payload, 'local_only', False)
|
self._convert_param_to_bool(payload, ['local_only', 'registry_images'])
|
||||||
self._convert_param_to_bool(payload, 'registry_images', False)
|
|
||||||
|
|
||||||
if not payload.get('local_only') and payload.get('registry_images'):
|
if not payload.get('local_only') and payload.get('registry_images'):
|
||||||
pecan.abort(400, _('Option registry_images can not be used without '
|
pecan.abort(400, _('Option registry_images can not be used without '
|
||||||
@ -257,9 +244,8 @@ class SubcloudBackupController(object):
|
|||||||
|
|
||||||
:param release_version: Backup release version to be deleted
|
:param release_version: Backup release version to be deleted
|
||||||
"""
|
"""
|
||||||
|
|
||||||
context = restcomm.extract_context_from_environ()
|
context = restcomm.extract_context_from_environ()
|
||||||
payload = self._get_backup_delete_payload(pecan_request)
|
payload = self._get_payload(pecan_request, verb)
|
||||||
|
|
||||||
if verb == 'delete':
|
if verb == 'delete':
|
||||||
policy.authorize(subcloud_backup_policy.POLICY_ROOT % "delete", {},
|
policy.authorize(subcloud_backup_policy.POLICY_ROOT % "delete", {},
|
||||||
@ -268,15 +254,16 @@ class SubcloudBackupController(object):
|
|||||||
if not release_version:
|
if not release_version:
|
||||||
pecan.abort(400, _('Release version required'))
|
pecan.abort(400, _('Release version required'))
|
||||||
|
|
||||||
self._convert_param_to_bool(payload, 'local_only', False)
|
self._convert_param_to_bool(payload, ['local_only'])
|
||||||
self._validate_and_decode_sysadmin_password(payload, 'sysadmin_password')
|
self._validate_and_decode_sysadmin_password(payload, 'sysadmin_password')
|
||||||
|
|
||||||
local_delete = payload.get('local_only')
|
request_entity = self._read_entity_from_request_params(context, payload)
|
||||||
|
|
||||||
# Validate subcloud state when deleting locally
|
# Validate subcloud state when deleting locally
|
||||||
# Not needed for centralized storage, since connection is not required
|
# Not needed for centralized storage, since connection is not required
|
||||||
request_entity = self._read_entity_from_request_params(
|
local_only = payload.get('local_only')
|
||||||
context, payload, validate_subclouds=local_delete)
|
if local_only:
|
||||||
|
self._validate_subclouds(request_entity.subclouds, verb)
|
||||||
|
|
||||||
# Set subcloud/group ID as reference instead of name to ease processing
|
# Set subcloud/group ID as reference instead of name to ease processing
|
||||||
payload[request_entity.type] = request_entity.id
|
payload[request_entity.type] = request_entity.id
|
||||||
@ -295,5 +282,58 @@ class SubcloudBackupController(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Unable to delete subcloud backups")
|
LOG.exception("Unable to delete subcloud backups")
|
||||||
pecan.abort(500, _('Unable to delete subcloud backups'))
|
pecan.abort(500, _('Unable to delete subcloud backups'))
|
||||||
|
elif verb == 'restore':
|
||||||
|
policy.authorize(subcloud_backup_policy.POLICY_ROOT % "restore", {},
|
||||||
|
restcomm.extract_credentials_for_policy())
|
||||||
|
|
||||||
|
if not payload:
|
||||||
|
pecan.abort(400, _('Body required'))
|
||||||
|
|
||||||
|
self._validate_and_decode_sysadmin_password(payload, 'sysadmin_password')
|
||||||
|
|
||||||
|
self._convert_param_to_bool(payload, ['local_only', 'with_install',
|
||||||
|
'registry_images'])
|
||||||
|
|
||||||
|
if not payload['local_only'] and payload['registry_images']:
|
||||||
|
pecan.abort(400, _('Option registry_images cannot be used '
|
||||||
|
'without local_only option.'))
|
||||||
|
|
||||||
|
request_entity = self._read_entity_from_request_params(context, payload)
|
||||||
|
if len(request_entity.subclouds) == 0:
|
||||||
|
msg = "No subclouds exist under %s %s" % (request_entity.type,
|
||||||
|
request_entity.id)
|
||||||
|
pecan.abort(400, _(msg))
|
||||||
|
|
||||||
|
self._validate_subclouds(request_entity.subclouds, verb)
|
||||||
|
|
||||||
|
payload[request_entity.type] = request_entity.id
|
||||||
|
|
||||||
|
valid_subclouds = [subcloud for subcloud in
|
||||||
|
request_entity.subclouds if
|
||||||
|
subcloud.data_install]
|
||||||
|
|
||||||
|
if not valid_subclouds:
|
||||||
|
pecan.abort(400, _('Cannot proceed with the restore operation '
|
||||||
|
'since the subcloud(s) do not contain '
|
||||||
|
'install data.'))
|
||||||
|
|
||||||
|
if payload.get('with_install'):
|
||||||
|
# Confirm the active system controller load is still in dc-vault
|
||||||
|
matching_iso, err_msg = utils.get_matching_iso()
|
||||||
|
if err_msg:
|
||||||
|
LOG.exception(err_msg)
|
||||||
|
pecan.abort(400, _(err_msg))
|
||||||
|
LOG.info("Restore operation will use image %s in subcloud "
|
||||||
|
"installation" % matching_iso)
|
||||||
|
|
||||||
|
try:
|
||||||
|
message = self.dcmanager_rpc_client.restore_subcloud_backups(
|
||||||
|
context, payload)
|
||||||
|
return utils.subcloud_db_list_to_dict(request_entity.subclouds)
|
||||||
|
except RemoteError as e:
|
||||||
|
pecan.abort(422, e.value)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception("Unable to restore subcloud")
|
||||||
|
pecan.abort(500, _('Unable to restore subcloud'))
|
||||||
else:
|
else:
|
||||||
pecan.abort(400, _('Invalid request'))
|
pecan.abort(400, _('Invalid request'))
|
||||||
|
@ -313,7 +313,8 @@ class SubcloudsController(object):
|
|||||||
)
|
)
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
def _get_subcloud_db_install_values(self, subcloud):
|
@staticmethod
|
||||||
|
def _get_subcloud_db_install_values(subcloud):
|
||||||
if not subcloud.data_install:
|
if not subcloud.data_install:
|
||||||
msg = _("Failed to read data install from db")
|
msg = _("Failed to read data install from db")
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
@ -599,8 +600,10 @@ class SubcloudsController(object):
|
|||||||
if k == 'image':
|
if k == 'image':
|
||||||
if software_version == tsc.SW_VERSION:
|
if software_version == tsc.SW_VERSION:
|
||||||
# check for the image at load vault load location
|
# check for the image at load vault load location
|
||||||
matching_iso, matching_sig = \
|
matching_iso, err_msg = utils.get_matching_iso()
|
||||||
SubcloudsController.verify_active_load_in_vault()
|
if err_msg:
|
||||||
|
LOG.exception(err_msg)
|
||||||
|
pecan.abort(400, _(err_msg))
|
||||||
LOG.info("image was not in install_values: will reference %s" %
|
LOG.info("image was not in install_values: will reference %s" %
|
||||||
matching_iso)
|
matching_iso)
|
||||||
else:
|
else:
|
||||||
@ -685,7 +688,6 @@ class SubcloudsController(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_restore_values(payload):
|
def _validate_restore_values(payload):
|
||||||
"""Validate the restore values to ensure parameters for remote restore are present"""
|
"""Validate the restore values to ensure parameters for remote restore are present"""
|
||||||
|
|
||||||
restore_values = payload.get(RESTORE_VALUES)
|
restore_values = payload.get(RESTORE_VALUES)
|
||||||
for p in MANDATORY_RESTORE_VALUES:
|
for p in MANDATORY_RESTORE_VALUES:
|
||||||
if p not in restore_values:
|
if p not in restore_values:
|
||||||
@ -805,22 +807,6 @@ class SubcloudsController(object):
|
|||||||
data_install=data_install)
|
data_install=data_install)
|
||||||
return subcloud
|
return subcloud
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def verify_active_load_in_vault():
|
|
||||||
try:
|
|
||||||
matching_iso, matching_sig = utils.get_vault_load_files(tsc.SW_VERSION)
|
|
||||||
if not matching_iso:
|
|
||||||
msg = _('Failed to get active load image. Provide '
|
|
||||||
'active load image via '
|
|
||||||
'"system --os-region-name SystemController '
|
|
||||||
'load-import --active"')
|
|
||||||
LOG.exception(msg)
|
|
||||||
pecan.abort(400, msg)
|
|
||||||
return matching_iso, matching_sig
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(str(e))
|
|
||||||
pecan.abort(400, str(e))
|
|
||||||
|
|
||||||
@index.when(method='GET', template='json')
|
@index.when(method='GET', template='json')
|
||||||
def get(self, subcloud_ref=None, detail=None):
|
def get(self, subcloud_ref=None, detail=None):
|
||||||
"""Get details about subcloud.
|
"""Get details about subcloud.
|
||||||
@ -1313,8 +1299,10 @@ class SubcloudsController(object):
|
|||||||
# image not in install values, add the matching image into the
|
# image not in install values, add the matching image into the
|
||||||
# install values.
|
# install values.
|
||||||
if 'image' not in install_values:
|
if 'image' not in install_values:
|
||||||
matching_iso, matching_sig = \
|
matching_iso, err_msg = utils.get_matching_iso()
|
||||||
SubcloudsController.verify_active_load_in_vault()
|
if err_msg:
|
||||||
|
LOG.exception(err_msg)
|
||||||
|
pecan.abort(400, _(err_msg))
|
||||||
LOG.info("image was not in install_values: will reference %s" %
|
LOG.info("image was not in install_values: will reference %s" %
|
||||||
matching_iso)
|
matching_iso)
|
||||||
install_values['image'] = matching_iso
|
install_values['image'] = matching_iso
|
||||||
@ -1370,8 +1358,8 @@ class SubcloudsController(object):
|
|||||||
consts.DEPLOY_STATE_DEPLOYING]:
|
consts.DEPLOY_STATE_DEPLOYING]:
|
||||||
pecan.abort(400, _('This operation is not allowed while subcloud install, '
|
pecan.abort(400, _('This operation is not allowed while subcloud install, '
|
||||||
'bootstrap or deploy is in progress.'))
|
'bootstrap or deploy is in progress.'))
|
||||||
sysadmin_password = \
|
|
||||||
payload.get('sysadmin_password')
|
sysadmin_password = payload.get('sysadmin_password')
|
||||||
if not sysadmin_password:
|
if not sysadmin_password:
|
||||||
pecan.abort(400, _('subcloud sysadmin_password required'))
|
pecan.abort(400, _('subcloud sysadmin_password required'))
|
||||||
|
|
||||||
@ -1405,8 +1393,11 @@ class SubcloudsController(object):
|
|||||||
'install_values': install_values,
|
'install_values': install_values,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Confirm the active system controller load is still in dc-vault
|
# Get the active system controller load is still in dc-vault
|
||||||
SubcloudsController.verify_active_load_in_vault()
|
matching_iso, err_msg = utils.get_matching_iso()
|
||||||
|
if err_msg:
|
||||||
|
LOG.exception(err_msg)
|
||||||
|
pecan.abort(400, _(err_msg))
|
||||||
else:
|
else:
|
||||||
# Not Redfish capable subcloud. The subcloud has been reinstalled
|
# Not Redfish capable subcloud. The subcloud has been reinstalled
|
||||||
# and required patches have been applied.
|
# and required patches have been applied.
|
||||||
|
@ -32,6 +32,17 @@ subcloud_backup_rules = [
|
|||||||
'path': '/v1.0/subcloud-backup/delete/{release_version}'
|
'path': '/v1.0/subcloud-backup/delete/{release_version}'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name=POLICY_ROOT % 'restore',
|
||||||
|
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||||
|
description="Restore a subcloud backup.",
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'method': 'PATCH',
|
||||||
|
'path': '/v1.0/subcloud-backup/restore'
|
||||||
|
}
|
||||||
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -186,10 +186,7 @@ ERROR_DESC_EMPTY = 'No errors present'
|
|||||||
# error_description max length
|
# error_description max length
|
||||||
ERROR_DESCRIPTION_LENGTH = 2048
|
ERROR_DESCRIPTION_LENGTH = 2048
|
||||||
|
|
||||||
# States to discard while backing up subclouds
|
# States to discard while restoring subclouds
|
||||||
INVALID_DEPLOY_STATES_FOR_BACKUP = [DEPLOY_STATE_INSTALLING,
|
|
||||||
DEPLOY_STATE_BOOTSTRAPPING,
|
|
||||||
DEPLOY_STATE_DEPLOYING]
|
|
||||||
INVALID_DEPLOY_STATES_FOR_RESTORE = [DEPLOY_STATE_INSTALLING,
|
INVALID_DEPLOY_STATES_FOR_RESTORE = [DEPLOY_STATE_INSTALLING,
|
||||||
DEPLOY_STATE_BOOTSTRAPPING,
|
DEPLOY_STATE_BOOTSTRAPPING,
|
||||||
DEPLOY_STATE_DEPLOYING,
|
DEPLOY_STATE_DEPLOYING,
|
||||||
|
@ -32,15 +32,13 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from dccommon import consts as dccommon_consts
|
from dccommon import consts as dccommon_consts
|
||||||
|
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
|
||||||
|
from dccommon.drivers.openstack import vim
|
||||||
from dccommon import exceptions as dccommon_exceptions
|
from dccommon import exceptions as dccommon_exceptions
|
||||||
from dcmanager.common import consts
|
from dcmanager.common import consts
|
||||||
from dcmanager.common import exceptions
|
from dcmanager.common import exceptions
|
||||||
from dcmanager.db import api as db_api
|
from dcmanager.db import api as db_api
|
||||||
|
|
||||||
from dccommon.drivers.openstack import vim
|
|
||||||
|
|
||||||
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
DC_MANAGER_USERNAME = "root"
|
DC_MANAGER_USERNAME = "root"
|
||||||
@ -696,3 +694,19 @@ def is_valid_for_restore(subcloud):
|
|||||||
and subcloud.deploy_status not in
|
and subcloud.deploy_status not in
|
||||||
consts.INVALID_DEPLOY_STATES_FOR_RESTORE
|
consts.INVALID_DEPLOY_STATES_FOR_RESTORE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_matching_iso():
|
||||||
|
try:
|
||||||
|
matching_iso, _ = get_vault_load_files(tsc.SW_VERSION)
|
||||||
|
if not matching_iso:
|
||||||
|
error_msg = ('Failed to get active load image. Provide '
|
||||||
|
'active load image via '
|
||||||
|
'"system --os-region-name SystemController '
|
||||||
|
'load-import --active"')
|
||||||
|
LOG.exception(error_msg)
|
||||||
|
return None, error_msg
|
||||||
|
return matching_iso, None
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception("Could not load vault files.")
|
||||||
|
return None, str(e)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user