Show resource name path in event log formatter

The resource name path was introduced in the 'openstack stack failures'
command and is a user-friendly way of referring to a deeply nested
resource.

This change modifies the event log formatter to build and display the
resource name path for each event based only off data in previous
events.

For building the full resource path, and omitting ugly stack names
from the resource path, the following heat change is needed
Ib8feb7752bd5736785d142216312bb35629b3601. However the output is still
improved slightly when running against an older heat which does not
have this change.

Change-Id: I86b687fc516da215a6baeef8b365a520d12a8ab1
Closes-Bug: #1619415
This commit is contained in:
Steve Baker 2016-09-02 05:13:29 +00:00
parent ed287fb913
commit b28d8c6101
4 changed files with 217 additions and 4 deletions

View File

@ -179,6 +179,7 @@ def poll_for_events(hc, stack_name, action=None, poll_period=5, marker=None,
msg_template = _("\n Stack %(name)s %(status)s \n")
if not out:
out = sys.stdout
event_log_context = utils.EventLogContext()
while True:
events = get_events(hc, stack_id=stack_name, nested_depth=nested_depth,
event_args={'sort_dir': 'asc',
@ -190,7 +191,7 @@ def poll_for_events(hc, stack_name, action=None, poll_period=5, marker=None,
no_event_polls = 0
# set marker to last event that was received.
marker = getattr(events[-1], 'id', None)
events_log = utils.event_log_formatter(events)
events_log = utils.event_log_formatter(events, event_log_context)
out.write(events_log)
out.write('\n')

View File

@ -97,16 +97,84 @@ def print_dict(d, formatters=None):
print(pt.get_string(sortby='Property'))
def event_log_formatter(events):
class EventLogContext(object):
def __init__(self):
# key is a stack id or the name of the nested stack, value is a tuple
# of the parent stack id, and the name of the resource in the parent
# stack
self.id_to_res_info = {}
def prepend_paths(self, resource_path, stack_id):
if stack_id not in self.id_to_res_info:
return
stack_id, res_name = self.id_to_res_info.get(stack_id)
if res_name in self.id_to_res_info:
# do a double lookup to skip the ugly stack name that doesn't
# correspond to an actual resource name
n_stack_id, res_name = self.id_to_res_info.get(res_name)
resource_path.insert(0, res_name)
self.prepend_paths(resource_path, n_stack_id)
elif res_name:
resource_path.insert(0, res_name)
def build_resource_name(self, event):
res_name = getattr(event, 'resource_name')
# Contribute this event to self.id_to_res_info to assist with
# future calls to build_resource_name
def get_stack_id():
for l in getattr(event, 'links', []):
if l.get('rel') == 'stack':
if 'href' not in l:
return None
stack_link = l['href']
return stack_link.split('/')[-1]
stack_id = get_stack_id()
if not stack_id:
return res_name
phys_id = getattr(event, 'physical_resource_id')
status = getattr(event, 'resource_status')
is_stack_event = stack_id == phys_id
if is_stack_event:
# this is an event for a stack
self.id_to_res_info[stack_id] = (stack_id, res_name)
elif phys_id and status == 'CREATE_IN_PROGRESS':
# this might be an event for a resource which creates a stack
self.id_to_res_info[phys_id] = (stack_id, res_name)
# Now build this resource path based on previous calls to
# build_resource_name
resource_path = []
if res_name and not is_stack_event:
resource_path.append(res_name)
self.prepend_paths(resource_path, stack_id)
return '.'.join(resource_path)
def event_log_formatter(events, event_log_context=None):
"""Return the events in log format."""
event_log = []
log_format = ("%(event_time)s "
"[%(rsrc_name)s]: %(rsrc_status)s %(rsrc_status_reason)s")
# It is preferable for a context to be passed in, but there might be enough
# events in this call to build a better resource name, so create a context
# anyway
if event_log_context is None:
event_log_context = EventLogContext()
for event in events:
rsrc_name = event_log_context.build_resource_name(event)
event_time = getattr(event, 'event_time', '')
log = log_format % {
'event_time': event_time.replace('T', ' '),
'rsrc_name': getattr(event, 'resource_name', ''),
'rsrc_name': rsrc_name,
'rsrc_status': getattr(event, 'resource_status', ''),
'rsrc_status_reason': getattr(event, 'resource_status_reason', '')
}

View File

@ -177,6 +177,7 @@ class ListEvent(command.Lister):
marker = parsed_args.marker
try:
event_log_context = heat_utils.EventLogContext()
while True:
events = event_utils.get_events(
client,
@ -186,7 +187,8 @@ class ListEvent(command.Lister):
marker=marker)
if events:
marker = getattr(events[-1], 'id', None)
events_log = heat_utils.event_log_formatter(events)
events_log = heat_utils.event_log_formatter(
events, event_log_context)
self.app.stdout.write(events_log)
self.app.stdout.write('\n')
time.sleep(5)

View File

@ -191,6 +191,148 @@ class ShellTest(testtools.TestCase):
self.assertEqual(expected, utils.event_log_formatter(events_list))
self.assertEqual('', utils.event_log_formatter([]))
def test_event_log_formatter_resource_path_old_heat(self):
events = [{
'resource_name': 'nested',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested/1bed5d4d-41d6-4451-b274-c073ebee375d',
'rel': 'stack'
}],
'logical_resource_id': 'nested',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'Stack CREATE started',
'physical_resource_id': '1bed5d4d-41d6-4451-b274-c073ebee375d',
}, {
'resource_name': 'rg1',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested/1bed5d4d-41d6-4451-b274-c073ebee375d',
'rel': 'stack'
}],
'logical_resource_id': 'rg1',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'state changed',
'physical_resource_id': None, # note the None from old heat
'id': '375c49ae-cefb-4fb3-8f4d-1d5f1b9e3e5d'
}, {
'resource_name': 'nested-rg1-m4zxcs4pra6t',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested-rg1-m4zxcs4pra6t/'
'3400bbad-a825-4226-ac23-c607846420db',
'rel': 'stack'
}],
'logical_resource_id': 'nested-rg1-m4zxcs4pra6t',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'Stack CREATE started',
'physical_resource_id': '3400bbad-a825-4226-ac23-c607846420db',
'id': '7e521c84-cd35-4f4c-b0de-962bd3cc40a8'
}, {
'resource_name': '1',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested-rg1-m4zxcs4pra6t/'
'3400bbad-a825-4226-ac23-c607846420db',
'rel': 'stack'
}],
'logical_resource_id': '1',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'state changed',
'physical_resource_id': None, # note the None from old heat
'id': 'c6186c16-94ef-4214-a11a-7e3cc8a17f82'
}]
events_list = [hc_res.Resource(manager=None, info=event)
for event in events]
expected = '''\
2016-09-05 04:10:24Z [nested]: \
CREATE_IN_PROGRESS Stack CREATE started
2016-09-05 04:10:24Z [nested.rg1]: \
CREATE_IN_PROGRESS state changed
2016-09-05 04:10:24Z [nested-rg1-m4zxcs4pra6t]: \
CREATE_IN_PROGRESS Stack CREATE started
2016-09-05 04:10:24Z [nested-rg1-m4zxcs4pra6t.1]: \
CREATE_IN_PROGRESS state changed'''
self.assertEqual(expected, utils.event_log_formatter(events_list))
def test_event_log_formatter_resource_path(self):
events = [{
'resource_name': 'nested',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested/1bed5d4d-41d6-4451-b274-c073ebee375d',
'rel': 'stack'
}],
'logical_resource_id': 'nested',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'Stack CREATE started',
'physical_resource_id': '1bed5d4d-41d6-4451-b274-c073ebee375d',
}, {
'resource_name': 'rg1',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested/1bed5d4d-41d6-4451-b274-c073ebee375d',
'rel': 'stack'
}],
'logical_resource_id': 'rg1',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'state changed',
'physical_resource_id': 'nested-rg1-m4zxcs4pra6t',
'id': '375c49ae-cefb-4fb3-8f4d-1d5f1b9e3e5d'
}, {
'resource_name': 'nested-rg1-m4zxcs4pra6t',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested-rg1-m4zxcs4pra6t/'
'3400bbad-a825-4226-ac23-c607846420db',
'rel': 'stack'
}],
'logical_resource_id': 'nested-rg1-m4zxcs4pra6t',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'Stack CREATE started',
'physical_resource_id': '3400bbad-a825-4226-ac23-c607846420db',
'id': '7e521c84-cd35-4f4c-b0de-962bd3cc40a8'
}, {
'resource_name': '1',
'event_time': '2016-09-05T04:10:24Z',
'links': [{
'href': 'http://192.0.2.1:8004/v1/t/stacks/'
'nested-rg1-m4zxcs4pra6t/'
'3400bbad-a825-4226-ac23-c607846420db',
'rel': 'stack'
}],
'logical_resource_id': '1',
'resource_status': 'CREATE_IN_PROGRESS',
'resource_status_reason': 'state changed',
'physical_resource_id': 'nested-rg1-m4zxcs4pra6t-1-z6sgpq54n6e7',
'id': 'c6186c16-94ef-4214-a11a-7e3cc8a17f82'
}]
events_list = [hc_res.Resource(manager=None, info=event)
for event in events]
expected = '''\
2016-09-05 04:10:24Z [nested]: \
CREATE_IN_PROGRESS Stack CREATE started
2016-09-05 04:10:24Z [nested.rg1]: \
CREATE_IN_PROGRESS state changed
2016-09-05 04:10:24Z [nested.rg1]: \
CREATE_IN_PROGRESS Stack CREATE started
2016-09-05 04:10:24Z [nested.rg1.1]: \
CREATE_IN_PROGRESS state changed'''
self.assertEqual(expected, utils.event_log_formatter(events_list))
class ShellTestParameterFiles(testtools.TestCase):