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
This commit is contained in:
Mark Vanderwiel
2016-02-12 13:55:21 -06:00
parent f9a2aee026
commit 9608df957f
3 changed files with 123 additions and 62 deletions

View File

@@ -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,9 +950,24 @@ 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:
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)
def _stack_action(stack, parsed_args, heat_client, action,
good_status, bad_status):
try:
action(stack)
except heat_exc.HTTPNotFound:
@@ -967,17 +982,7 @@ def _stack_action(parsed_args, heat_client, action, good_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)]
return (columns, rows)
return heat_client.stacks.get(stack)
class SuspendStack(StackActionBase):
@@ -1022,27 +1027,6 @@ class ResumeStack(StackActionBase):
)
class UpdateCancelStack(StackActionBase):
"""Cancel update for a stack."""
log = logging.getLogger(__name__ + '.UpdateCancelStack')
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')
)
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']
)
class CheckStack(StackActionBase):
"""Check a stack."""
@@ -1064,6 +1048,60 @@ class CheckStack(StackActionBase):
)
class CancelStack(StackActionBase):
"""Cancel current task for a stack.
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 cancel (name or ID)'),
_('Wait for check to complete')
)
def take_action(self, parsed_args):
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):
'''List resources with pending hook for a stack.'''

View File

@@ -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):

View File

@@ -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 =