summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Vanderwiel <vanderwl@us.ibm.com>2016-02-12 13:55:21 -0600
committerMark Vanderwiel <vanderwl@us.ibm.com>2016-02-24 18:47:16 -0600
commit9608df957fc8cf4e18ec35846c891aafbe52ac92 (patch)
treeabb8d7e428d13fe708dc688c7d9d0f335814a997
parentf9a2aee026081af9e3d1408c5d1f47aaddb6a75c (diff)
downloadpython-heatclient-9608df957fc8cf4e18ec35846c891aafbe52ac92.tar.gz
Fix collision with update and update cancel
"openstack update" collides/hides with "openstack update cancel" I discussed a few possible solutions in the bug report. Will change to have a new "openstack stack cancel" command and it will cancel the pending action, for now just supporting update. Blueprint: heat-support-python-openstackclient Change-Id: I7e054ecffea5b03a815f69515651e9c377ff68c2 Closes-Bug: #1545131
-rw-r--r--heatclient/osc/v1/stack.py122
-rw-r--r--heatclient/tests/unit/osc/v1/test_stack.py53
-rw-r--r--setup.cfg2
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):
diff --git a/setup.cfg b/setup.cfg
index c1d58bd..57fd8a0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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 =