diff --git a/heat/cmd/manage.py b/heat/cmd/manage.py index 2e5d38c881..468132d80d 100644 --- a/heat/cmd/manage.py +++ b/heat/cmd/manage.py @@ -122,10 +122,8 @@ def do_migrate(): except exception.NotFound: raise Exception(_("Stack with id %s can not be found.") % CONF.command.stack_id) - except exception.ActionInProgress: - raise Exception(_("The stack or some of its nested stacks are " - "in progress. Note, that all the stacks should be " - "in COMPLETE state in order to be migrated.")) + except (exception.NotSupported, exception.ActionNotComplete) as ex: + raise Exception(ex.message) def purge_deleted(): diff --git a/heat/common/exception.py b/heat/common/exception.py index cf6acb5ce2..02d817b08d 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -487,6 +487,11 @@ class ActionInProgress(HeatException): "in progress.") +class ActionNotComplete(HeatException): + msg_fmt = _("Stack %(stack_name)s has an action (%(action)s) " + "in progress or failed state.") + + class StopActionFailed(HeatException): msg_fmt = _("Failed to stop stack (%(stack_name)s) on other engine " "(%(engine_id)s)") diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index 77bca37bb7..5ad21185d5 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -607,7 +607,8 @@ def stack_get_status(context, stack_id): def stack_get_all_by_owner_id(context, owner_id): results = soft_delete_aware_query( - context, models.Stack).filter_by(owner_id=owner_id).all() + context, models.Stack).filter_by(owner_id=owner_id, + backup=False).all() return results diff --git a/heat/engine/service.py b/heat/engine/service.py index d1248697d4..44bc61e33f 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -2207,14 +2207,30 @@ class EngineService(service.ServiceBase): parent_stack = parser.Stack.load(ctxt, stack_id=stack_id, show_deleted=False) + + if parent_stack.owner_id is not None: + msg = _("Migration of nested stack %s") % stack_id + raise exception.NotSupported(feature=msg) + + if parent_stack.status != parent_stack.COMPLETE: + raise exception.ActionNotComplete(stack_name=parent_stack.name, + action=parent_stack.action) + if parent_stack.convergence: LOG.info("Convergence was already enabled for stack %s", stack_id) return + db_stacks = stack_object.Stack.get_all_by_root_owner_id( ctxt, parent_stack.id) stacks = [parser.Stack.load(ctxt, stack_id=st.id, stack=st) for st in db_stacks] + + # check if any of the nested stacks is in IN_PROGRESS/FAILED state + for stack in stacks: + if stack.status != stack.COMPLETE: + raise exception.ActionNotComplete(stack_name=stack.name, + action=stack.action) stacks.append(parent_stack) locks = [] try: diff --git a/heat/tests/db/test_sqlalchemy_api.py b/heat/tests/db/test_sqlalchemy_api.py index ac48aa6323..84ec251ebd 100644 --- a/heat/tests/db/test_sqlalchemy_api.py +++ b/heat/tests/db/test_sqlalchemy_api.py @@ -1383,6 +1383,7 @@ def create_stack(ctx, template, user_creds, **kwargs): 'parameters': {}, 'user_creds_id': user_creds['id'], 'owner_id': None, + 'backup': False, 'timeout': '60', 'disable_rollback': 0, 'current_traversal': 'dummy-uuid',