Implement stack event list --follow

This adds a --follow option which is similar to the --wait logging
behaviour in changes like I72421c1adf9220e3440179fec672f49803a6cde2
except that --follow doesn't exit until the user does ctrl-c.

This change also includes:
- always sort events ascending for all formatters - the newest events
  should always be at the bottom of the screen as they are generally the
  most interesting
- get_event marker handling tolerates the marker not being in the event
  list. --follow was triggering this, but ignoring the marker in this
  case has not lead to events being printed more than once

Change-Id: Ie0964918fc1d05e7e18aa39ceea6d4777deeb87e
Blueprint: heat-support-python-openstackclient
This commit is contained in:
Steve Baker 2016-02-16 17:12:42 +13:00
parent 6c18458764
commit ebdf0ebc94
3 changed files with 63 additions and 3 deletions

View File

@ -75,8 +75,11 @@ def get_events(hc, stack_id, event_args, nested_depth=0,
# Slice the list if marker is specified
if marker:
marker_index = [e.id for e in events].index(marker)
events = events[marker_index:]
try:
marker_index = [e.id for e in events].index(marker)
events = events[marker_index:]
except ValueError:
pass
# Slice the list if limit is specified
if limit:

View File

@ -13,6 +13,7 @@
# Copyright 2015 IBM Corp.
import logging
import time
from cliff import lister
from cliff import show
@ -134,6 +135,11 @@ class ListEvent(lister.Lister):
'(default: asc). Specify multiple times to sort on '
'multiple keys')
)
parser.add_argument(
'--follow',
action='store_true',
help=_('Print events until process is halted')
)
return parser
def take_action(self, parsed_args):
@ -149,6 +155,7 @@ class ListEvent(lister.Lister):
'limit': parsed_args.limit,
'marker': parsed_args.marker,
'filters': heat_utils.format_parameters(parsed_args.filter),
'sort_dir': 'asc'
}
if parsed_args.resource and parsed_args.nested_depth:
@ -165,6 +172,31 @@ class ListEvent(lister.Lister):
else:
nested_depth = 0
if parsed_args.follow:
if parsed_args.formatter != 'value':
msg = _('--follow can only be specified with --format value')
raise exc.CommandError(msg)
marker = parsed_args.marker
try:
while True:
kwargs['marker'] = marker
events = event_utils.get_events(
client,
stack_id=parsed_args.stack,
event_args=kwargs,
nested_depth=nested_depth,
marker=marker)
if events:
marker = getattr(events[-1], 'id', None)
events_log = heat_utils.event_log_formatter(events)
self.app.stdout.write(events_log)
self.app.stdout.write('\n')
time.sleep(5)
# this loop never exits
except (KeyboardInterrupt, EOFError): # ctrl-c, ctrl-d
return [], []
events = event_utils.get_events(
client, stack_id=parsed_args.stack, event_args=kwargs,
nested_depth=nested_depth, marker=parsed_args.marker,
@ -175,7 +207,6 @@ class ListEvent(lister.Lister):
if parsed_args.formatter == 'value':
events = heat_utils.event_log_formatter(events).split('\n')
events.reverse()
return [], [e.split(' ') for e in events]
if len(events):

View File

@ -111,6 +111,7 @@ class TestEventList(TestEvent):
'limit': None,
'marker': None,
'filters': {},
'sort_dir': 'asc'
}
fields = ['resource_name', 'id', 'resource_status',
@ -199,6 +200,31 @@ class TestEventList(TestEvent):
self.event_client.list.assert_called_with(**self.defaults)
self.assertEqual(self.fields, columns)
@mock.patch('time.sleep')
def test_event_list_follow(self, sleep):
sleep.side_effect = [None, KeyboardInterrupt()]
arglist = ['--follow', 'my_stack']
expected = (
'2015-11-13 10:02:17 [resource1]: '
'CREATE_COMPLETE state changed\n'
'2015-11-13 10:02:17 [resource1]: '
'CREATE_COMPLETE state changed\n'
)
parsed_args = self.check_parser(self.cmd, arglist, [])
columns, data = self.cmd.take_action(parsed_args)
defaults_with_marker = dict(self.defaults)
defaults_with_marker['marker'] = '1234'
self.event_client.list.assert_has_calls([
mock.call(**self.defaults),
mock.call(**defaults_with_marker)
])
self.assertEqual([], columns)
self.assertEqual([], data)
self.assertEqual(expected, self.fake_stdout.make_string())
def test_event_list_value_format(self):
arglist = ['my_stack']
expected = ('2015-11-13 10:02:17 [resource1]: CREATE_COMPLETE '