From a3c32990a81b202ebac7408a9da08dcf03c8306c Mon Sep 17 00:00:00 2001 From: Mark Vanderwiel Date: Mon, 29 Feb 2016 14:18:04 -0600 Subject: [PATCH] Use poll_for_events for "openstack stack delete" Change-Id: Ie918e095e7d67c94991f1a7e4b0ede9127134936 Blueprint: heat-support-python-openstackclient --- heatclient/common/utils.py | 25 --------------- heatclient/osc/v1/stack.py | 36 +++++++++++++++------- heatclient/tests/unit/osc/v1/test_stack.py | 33 +++++++++----------- heatclient/tests/unit/test_utils.py | 11 ------- 4 files changed, 40 insertions(+), 65 deletions(-) diff --git a/heatclient/common/utils.py b/heatclient/common/utils.py index b646e599..a36ab38c 100644 --- a/heatclient/common/utils.py +++ b/heatclient/common/utils.py @@ -17,7 +17,6 @@ import logging import os import textwrap -import time import uuid from oslo_serialization import jsonutils @@ -116,30 +115,6 @@ def event_log_formatter(events): return "\n".join(event_log) -def wait_for_delete(status_f, - res_id, - status_field='status', - sleep_time=5, - timeout=300): - """Wait for resource deletion.""" - - total_time = 0 - while total_time < timeout: - try: - res = status_f(res_id) - except exc.HTTPNotFound: - return True - - status = res.get(status_field, '').lower() - if 'failed' in status: - return False - - time.sleep(sleep_time) - total_time += sleep_time - - return False - - def print_update_list(lst, fields, formatters=None): """Print the stack-update --dry-run output as a table. diff --git a/heatclient/osc/v1/stack.py b/heatclient/osc/v1/stack.py index 8a51217b..c256bb10 100644 --- a/heatclient/osc/v1/stack.py +++ b/heatclient/osc/v1/stack.py @@ -631,25 +631,39 @@ def take_action(self, parsed_args): failure_count = 0 stacks_waiting = [] for sid in parsed_args.stack: + marker = None + if parsed_args.wait: + try: + # find the last event to use as the marker + events = event_utils.get_events(heat_client, + stack_id=sid, + event_args={ + 'sort_dir': 'desc', + 'limit': 1}) + if events: + marker = events[0].id + except heat_exc.CommandError as ex: + failure_count += 1 + print(ex) + continue + try: heat_client.stacks.delete(sid) - stacks_waiting.append(sid) + stacks_waiting.append((sid, marker)) except heat_exc.HTTPNotFound: failure_count += 1 print(_('Stack not found: %s') % sid) if parsed_args.wait: - for sid in stacks_waiting: - def status_f(id): - return heat_client.stacks.get(id).to_dict() - - # TODO(jonesbr): switch to use openstack client wait_for_delete - # when version 2.1.0 is adopted. - if not heat_utils.wait_for_delete(status_f, - sid, - status_field='stack_status'): + for sid, marker in stacks_waiting: + try: + stack_status, msg = event_utils.poll_for_events( + heat_client, sid, action='DELETE', marker=marker) + except heat_exc.CommandError: + continue + if stack_status == 'DELETE_FAILED': failure_count += 1 - print(_('Stack failed to delete: %s') % sid) + print(msg) if failure_count: msg = (_('Unable to delete %(count)d of the %(total)d stacks.') % diff --git a/heatclient/tests/unit/osc/v1/test_stack.py b/heatclient/tests/unit/osc/v1/test_stack.py index ecfacf61..4ab4818d 100644 --- a/heatclient/tests/unit/osc/v1/test_stack.py +++ b/heatclient/tests/unit/osc/v1/test_stack.py @@ -581,38 +581,35 @@ def test_stack_delete_one_found_one_not_found(self): self.stack_client.delete.assert_any_call('stack2') self.assertEqual('Unable to delete 1 of the 2 stacks.', str(error)) - def test_stack_delete_wait(self): + @mock.patch('heatclient.common.event_utils.poll_for_events', + return_value=('DELETE_COMPLETE', + 'Stack my_stack DELETE_COMPLETE')) + @mock.patch('heatclient.common.event_utils.get_events', return_value=[]) + def test_stack_delete_wait(self, mock_get_event, mock_poll, ): arglist = ['stack1', 'stack2', 'stack3', '--wait'] parsed_args = self.check_parser(self.cmd, arglist, []) - self.cmd.take_action(parsed_args) - self.stack_client.delete.assert_any_call('stack1') - self.stack_client.get.assert_any_call('stack1') self.stack_client.delete.assert_any_call('stack2') - self.stack_client.get.assert_any_call('stack2') self.stack_client.delete.assert_any_call('stack3') - self.stack_client.get.assert_any_call('stack3') - def test_stack_delete_wait_one_pass_one_fail(self): + @mock.patch('heatclient.common.event_utils.poll_for_events') + @mock.patch('heatclient.common.event_utils.get_events', return_value=[]) + def test_stack_delete_wait_fail(self, mock_get_event, mock_poll): + mock_poll.side_effect = [['DELETE_COMPLETE', + 'Stack my_stack DELETE_COMPLETE'], + ['DELETE_FAILED', + 'Stack my_stack DELETE_FAILED'], + ['DELETE_COMPLETE', + 'Stack my_stack DELETE_COMPLETE']] arglist = ['stack1', 'stack2', 'stack3', '--wait'] - self.stack_client.get.side_effect = [ - stacks.Stack(None, {'stack_status': 'DELETE_FAILED'}), - heat_exc.HTTPNotFound, - stacks.Stack(None, {'stack_status': 'DELETE_FAILED'}), - ] parsed_args = self.check_parser(self.cmd, arglist, []) - error = self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args) - self.stack_client.delete.assert_any_call('stack1') - self.stack_client.get.assert_any_call('stack1') self.stack_client.delete.assert_any_call('stack2') - self.stack_client.get.assert_any_call('stack2') self.stack_client.delete.assert_any_call('stack3') - self.stack_client.get.assert_any_call('stack3') - self.assertEqual('Unable to delete 2 of the 3 stacks.', str(error)) + self.assertEqual('Unable to delete 1 of the 3 stacks.', str(error)) @mock.patch('sys.stdin', spec=six.StringIO) def test_stack_delete_prompt(self, mock_stdin): diff --git a/heatclient/tests/unit/test_utils.py b/heatclient/tests/unit/test_utils.py index c58f7888..8237b4fc 100644 --- a/heatclient/tests/unit/test_utils.py +++ b/heatclient/tests/unit/test_utils.py @@ -188,17 +188,6 @@ def test_event_log_formatter(self): self.assertEqual(expected, utils.event_log_formatter(events_list)) self.assertEqual('', utils.event_log_formatter([])) - def test_wait_for_delete(self): - def status_f(id): - raise exc.HTTPNotFound - - def bad_status_f(id): - return {'status': 'failed'} - - self.assertTrue(utils.wait_for_delete(status_f, 123)) - self.assertFalse(utils.wait_for_delete(status_f, 123, timeout=0)) - self.assertFalse(utils.wait_for_delete(bad_status_f, 123)) - class ShellTestParameterFiles(testtools.TestCase):