Use poll_for_events for "openstack stack delete"
Change-Id: Ie918e095e7d67c94991f1a7e4b0ede9127134936 Blueprint: heat-support-python-openstackclient
This commit is contained in:
@@ -17,7 +17,6 @@ import base64
|
||||
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.
|
||||
|
||||
|
@@ -631,25 +631,39 @@ class DeleteStack(command.Command):
|
||||
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.') %
|
||||
|
@@ -581,38 +581,35 @@ class TestStackDelete(TestStack):
|
||||
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):
|
||||
|
@@ -188,17 +188,6 @@ class ShellTest(testtools.TestCase):
|
||||
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):
|
||||
|
||||
|
Reference in New Issue
Block a user