diff options
-rw-r--r-- | heatclient/osc/v1/stack.py | 122 | ||||
-rw-r--r-- | heatclient/tests/unit/osc/v1/test_stack.py | 53 | ||||
-rw-r--r-- | setup.cfg | 2 |
3 files changed, 119 insertions, 58 deletions
diff --git a/heatclient/osc/v1/stack.py b/heatclient/osc/v1/stack.py index cd67ace..2943ebe 100644 --- a/heatclient/osc/v1/stack.py +++ b/heatclient/osc/v1/stack.py @@ -941,7 +941,7 @@ class StackActionBase(lister.Lister): def _take_action(self, parsed_args, action, good_status, bad_status): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration - return _stack_action( + return _stacks_action( parsed_args, heat_client, action, @@ -950,34 +950,39 @@ class StackActionBase(lister.Lister): ) -def _stack_action(parsed_args, heat_client, action, good_status, bad_status): +def _stacks_action(parsed_args, heat_client, action, good_status, bad_status): rows = [] + columns = [ + 'ID', + 'Stack Name', + 'Stack Status', + 'Creation Time', + 'Updated Time' + ] for stack in parsed_args.stack: - try: - action(stack) - except heat_exc.HTTPNotFound: - msg = _('Stack not found: %s') % stack - raise exc.CommandError(msg) + data = _stack_action(stack, parsed_args, heat_client, action, + good_status, bad_status) + rows += [utils.get_dict_properties(data.to_dict(), columns)] + return (columns, rows) - if parsed_args.wait: - if not utils.wait_for_status(heat_client.stacks.get, stack, - status_field='stack_status', - success_status=good_status, - error_status=bad_status): - err = _("Error waiting for status from stack %s") % stack - raise exc.CommandError(err) - data = heat_client.stacks.get(stack) - columns = [ - 'ID', - 'Stack Name', - 'Stack Status', - 'Creation Time', - 'Updated Time' - ] - rows += [utils.get_dict_properties(data.to_dict(), columns)] +def _stack_action(stack, parsed_args, heat_client, action, + good_status, bad_status): + try: + action(stack) + except heat_exc.HTTPNotFound: + msg = _('Stack not found: %s') % stack + raise exc.CommandError(msg) - return (columns, rows) + if parsed_args.wait: + if not utils.wait_for_status(heat_client.stacks.get, stack, + status_field='stack_status', + success_status=good_status, + error_status=bad_status): + err = _("Error waiting for status from stack %s") % stack + raise exc.CommandError(err) + + return heat_client.stacks.get(stack) class SuspendStack(StackActionBase): @@ -1022,46 +1027,79 @@ class ResumeStack(StackActionBase): ) -class UpdateCancelStack(StackActionBase): - """Cancel update for a stack.""" +class CheckStack(StackActionBase): + """Check a stack.""" - log = logging.getLogger(__name__ + '.UpdateCancelStack') + log = logging.getLogger(__name__ + '.CheckStack') def get_parser(self, prog_name): return self._get_parser( prog_name, - _('Stack(s) to cancel update (name or ID)'), - _('Wait for cancel update to complete') + _('Stack(s) to check update (name or ID)'), + _('Wait for check to complete') ) def take_action(self, parsed_args): return self._take_action( parsed_args, - self.app.client_manager.orchestration.actions.cancel_update, - ['cancel_update_complete'], - ['cancel_update_failed'] + self.app.client_manager.orchestration.actions.check, + ['check_complete'], + ['check_failed'] ) -class CheckStack(StackActionBase): - """Check a stack.""" +class CancelStack(StackActionBase): + """Cancel current task for a stack. - log = logging.getLogger(__name__ + '.CheckStack') + Supported tasks for cancellation: + * update + """ + + log = logging.getLogger(__name__ + '.CancelStack') def get_parser(self, prog_name): return self._get_parser( prog_name, - _('Stack(s) to check update (name or ID)'), + _('Stack(s) to cancel (name or ID)'), _('Wait for check to complete') ) def take_action(self, parsed_args): - return self._take_action( - parsed_args, - self.app.client_manager.orchestration.actions.check, - ['check_complete'], - ['check_failed'] - ) + self.log.debug("take_action(%s)", parsed_args) + rows = [] + columns = [ + 'ID', + 'Stack Name', + 'Stack Status', + 'Creation Time', + 'Updated Time' + ] + heat_client = self.app.client_manager.orchestration + + for stack in parsed_args.stack: + try: + data = heat_client.stacks.get(stack_id=stack) + except heat_exc.HTTPNotFound: + raise exc.CommandError('Stack not found: %s' % stack) + + status = getattr(data, 'stack_status').lower() + if status == 'update_in_progress': + data = _stack_action( + stack, + parsed_args, + heat_client, + heat_client.actions.cancel_update, + ['cancel_update_complete'], + ['cancel_update_failed'] + ) + rows += [utils.get_dict_properties(data.to_dict(), columns)] + else: + err = _("Stack %(id)s with status \'%(status)s\' " + "not in cancelable state") % { + 'id': stack, 'status': status} + raise exc.CommandError(err) + + return (columns, rows) class StackHookPoll(lister.Lister): diff --git a/heatclient/tests/unit/osc/v1/test_stack.py b/heatclient/tests/unit/osc/v1/test_stack.py index 318eca3..86ec768 100644 --- a/heatclient/tests/unit/osc/v1/test_stack.py +++ b/heatclient/tests/unit/osc/v1/test_stack.py @@ -921,21 +921,24 @@ class _TestStackCheckBase(object): self.mock_client.stacks.get = mock.Mock( return_value=self.stack) - def _test_stack_action(self): + def _test_stack_action(self, get_call_count=1): arglist = ['my_stack'] parsed_args = self.check_parser(self.cmd, arglist, []) columns, rows = self.cmd.take_action(parsed_args) self.action.assert_called_once_with('my_stack') - self.mock_client.stacks.get.assert_called_once_with('my_stack') + self.mock_client.stacks.get.assert_called_with('my_stack') + self.assertEqual(get_call_count, + self.mock_client.stacks.get.call_count) self.assertEqual(self.columns, columns) self.assertEqual(1, len(rows)) - def _test_stack_action_multi(self): + def _test_stack_action_multi(self, get_call_count=2): arglist = ['my_stack1', 'my_stack2'] parsed_args = self.check_parser(self.cmd, arglist, []) columns, rows = self.cmd.take_action(parsed_args) self.assertEqual(2, self.action.call_count) - self.assertEqual(2, self.mock_client.stacks.get.call_count) + self.assertEqual(get_call_count, + self.mock_client.stacks.get.call_count) self.action.assert_called_with('my_stack2') self.mock_client.stacks.get.assert_called_with('my_stack2') self.assertEqual(self.columns, columns) @@ -948,7 +951,7 @@ class _TestStackCheckBase(object): parsed_args = self.check_parser(self.cmd, arglist, []) columns, rows = self.cmd.take_action(parsed_args) self.action.assert_called_with('my_stack') - self.mock_client.stacks.get.assert_called_once_with('my_stack') + self.mock_client.stacks.get.assert_called_with('my_stack') self.assertEqual(self.columns, columns) self.assertEqual(1, len(rows)) @@ -1026,31 +1029,51 @@ class TestStackResume(_TestStackCheckBase, TestStack): self._test_stack_action_exception() -class TestStackUpdateCancel(_TestStackCheckBase, TestStack): +class TestStackCancel(_TestStackCheckBase, TestStack): + + stack_update_in_progress = stacks.Stack(None, { + "id": '1234', + "stack_name": 'my_stack', + "creation_time": "2013-08-04T20:57:55Z", + "updated_time": "2013-08-04T20:57:55Z", + "stack_status": "UPDATE_IN_PROGRESS" + }) def setUp(self): - super(TestStackUpdateCancel, self).setUp() + super(TestStackCancel, self).setUp() self.mock_client.actions.cancel_update = mock.Mock() self._setUp( - stack.UpdateCancelStack(self.app, None), + stack.CancelStack(self.app, None), self.mock_client.actions.cancel_update ) + self.mock_client.stacks.get = mock.Mock( + return_value=self.stack_update_in_progress) - def test_stack_cancel_update(self): - self._test_stack_action() + def test_stack_cancel(self): + self._test_stack_action(2) - def test_stack_cancel_update_multi(self): - self._test_stack_action_multi() + def test_stack_cancel_multi(self): + self._test_stack_action_multi(4) - def test_stack_cancel_update_wait(self): + def test_stack_cancel_wait(self): self._test_stack_action_wait() - def test_stack_cancel_update_wait_error(self): + def test_stack_cancel_wait_error(self): self._test_stack_action_wait_error() - def test_stack_cancel_update_exception(self): + def test_stack_cancel_exception(self): self._test_stack_action_exception() + def test_stack_cancel_unsupported_state(self): + self.mock_client.stacks.get = mock.Mock( + return_value=self.stack) + error = self.assertRaises(exc.CommandError, + self._test_stack_action, + 2) + self.assertEqual('Stack my_stack with status \'create_complete\' ' + 'not in cancelable state', + str(error)) + class TestStackCheck(_TestStackCheckBase, TestStack): @@ -46,6 +46,7 @@ openstack.orchestration.v1 = software_deployment_show = heatclient.osc.v1.software_deployment:ShowDeployment stack_abandon = heatclient.osc.v1.stack:AbandonStack stack_adopt = heatclient.osc.v1.stack:AdoptStack + stack_cancel = heatclient.osc.v1.stack:CancelStack stack_check = heatclient.osc.v1.stack:CheckStack stack_create = heatclient.osc.v1.stack:CreateStack stack_delete = heatclient.osc.v1.stack:DeleteStack @@ -70,7 +71,6 @@ openstack.orchestration.v1 = stack_suspend = heatclient.osc.v1.stack:SuspendStack stack_template_show = heatclient.osc.v1.stack:TemplateShowStack stack_update = heatclient.osc.v1.stack:UpdateStack - stack_update_cancel = heatclient.osc.v1.stack:UpdateCancelStack [global] setup-hooks = |