251 lines
8.3 KiB
Python
251 lines
8.3 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
# Copyright 2015 IBM Corp.
|
|
|
|
import logging
|
|
import time
|
|
|
|
from cliff.formatters import base
|
|
from osc_lib.command import command
|
|
from osc_lib import utils
|
|
|
|
from heatclient._i18n import _
|
|
from heatclient.common import event_utils
|
|
from heatclient.common import utils as heat_utils
|
|
from heatclient import exc
|
|
|
|
|
|
class ShowEvent(command.ShowOne):
|
|
"""Show event details."""
|
|
|
|
log = logging.getLogger(__name__ + '.ShowEvent')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ShowEvent, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'stack',
|
|
metavar='<stack>',
|
|
help=_('Name or ID of stack to show events for')
|
|
)
|
|
parser.add_argument(
|
|
'resource',
|
|
metavar='<resource>',
|
|
help=_('Name of the resource event belongs to')
|
|
)
|
|
parser.add_argument(
|
|
'event',
|
|
metavar='<event>',
|
|
help=_('ID of event to display details for')
|
|
)
|
|
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug('take_action(%s)', parsed_args)
|
|
|
|
client = self.app.client_manager.orchestration
|
|
|
|
fields = {
|
|
'stack_id': parsed_args.stack,
|
|
'resource_name': parsed_args.resource,
|
|
'event_id': parsed_args.event
|
|
}
|
|
|
|
try:
|
|
client.stacks.get(parsed_args.stack)
|
|
client.resources.get(parsed_args.stack, parsed_args.resource)
|
|
event = client.events.get(**fields)
|
|
except exc.HTTPNotFound as ex:
|
|
raise exc.CommandError(str(ex))
|
|
|
|
formatters = {
|
|
'links': heat_utils.link_formatter,
|
|
'resource_properties': heat_utils.json_formatter
|
|
}
|
|
|
|
columns = []
|
|
for key in event.to_dict():
|
|
columns.append(key)
|
|
|
|
return columns, utils.get_item_properties(event, columns,
|
|
formatters=formatters)
|
|
|
|
|
|
class ListEvent(command.Lister):
|
|
"""List events."""
|
|
|
|
log = logging.getLogger(__name__ + '.ListEvent')
|
|
|
|
@property
|
|
def formatter_default(self):
|
|
return 'log'
|
|
|
|
@property
|
|
def formatter_namespace(self):
|
|
return 'heatclient.event.formatter.list'
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ListEvent, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'stack',
|
|
metavar='<stack>',
|
|
help=_('Name or ID of stack to show events for')
|
|
)
|
|
parser.add_argument(
|
|
'--resource',
|
|
metavar='<resource>',
|
|
help=_('Name of resource to show events for. Note: this cannot '
|
|
'be specified with --nested-depth')
|
|
)
|
|
parser.add_argument(
|
|
'--filter',
|
|
metavar='<key=value>',
|
|
action='append',
|
|
help=_('Filter parameters to apply on returned events')
|
|
)
|
|
parser.add_argument(
|
|
'--limit',
|
|
metavar='<limit>',
|
|
type=int,
|
|
help=_('Limit the number of events returned')
|
|
)
|
|
parser.add_argument(
|
|
'--marker',
|
|
metavar='<id>',
|
|
help=_('Only return events that appear after the given ID')
|
|
)
|
|
parser.add_argument(
|
|
'--nested-depth',
|
|
metavar='<depth>',
|
|
type=int,
|
|
help=_('Depth of nested stacks from which to display events. '
|
|
'Note: this cannot be specified with --resource')
|
|
)
|
|
parser.add_argument(
|
|
'--sort',
|
|
metavar='<key>[:<direction>]',
|
|
action='append',
|
|
help=_('Sort output by selected keys and directions (asc or desc) '
|
|
'(default: asc). Specify multiple times to sort on '
|
|
'multiple keys. Sort key can be: '
|
|
'"event_time" (default), "resource_name", "links", '
|
|
'"logical_resource_id", "resource_status", '
|
|
'"resource_status_reason", "physical_resource_id", or '
|
|
'"id". You can leave the key empty and specify ":desc" '
|
|
'for sorting by reverse time.')
|
|
)
|
|
parser.add_argument(
|
|
'--follow',
|
|
action='store_true',
|
|
help=_('Print events until process is halted')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug('take_action(%s)', parsed_args)
|
|
|
|
client = self.app.client_manager.orchestration
|
|
|
|
columns = ['id', 'resource_status', 'resource_status_reason',
|
|
'event_time', 'physical_resource_id']
|
|
|
|
kwargs = {
|
|
'resource_name': parsed_args.resource,
|
|
'filters': heat_utils.format_parameters(parsed_args.filter),
|
|
'sort_dir': 'asc'
|
|
}
|
|
|
|
if parsed_args.resource and parsed_args.nested_depth:
|
|
msg = _('--nested-depth cannot be specified with --resource')
|
|
raise exc.CommandError(msg)
|
|
|
|
if parsed_args.nested_depth:
|
|
columns.append('stack_name')
|
|
nested_depth = parsed_args.nested_depth
|
|
else:
|
|
nested_depth = 0
|
|
|
|
if parsed_args.sort:
|
|
sorts = []
|
|
sort_keys = []
|
|
for sort in parsed_args.sort:
|
|
if sort.startswith(":"):
|
|
sorts.append(":".join(["event_time", sort.lstrip(":")]))
|
|
else:
|
|
sorts.append(sort)
|
|
sort_keys.append(sort.split(":")[0])
|
|
kwargs['sort_keys'] = sort_keys
|
|
|
|
if ":" in parsed_args.sort[0]:
|
|
kwargs['sort_dir'] = parsed_args.sort[0].split(":")[1]
|
|
|
|
if parsed_args.follow:
|
|
if parsed_args.formatter != 'log':
|
|
msg = _('--follow can only be specified with --format log')
|
|
raise exc.CommandError(msg)
|
|
|
|
marker = parsed_args.marker
|
|
try:
|
|
event_log_context = heat_utils.EventLogContext()
|
|
while True:
|
|
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, event_log_context)
|
|
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,
|
|
limit=parsed_args.limit)
|
|
|
|
if parsed_args.sort:
|
|
events = utils.sort_items(events, ','.join(sorts))
|
|
|
|
if parsed_args.formatter == 'log':
|
|
return [], events
|
|
|
|
if len(events):
|
|
if hasattr(events[0], 'resource_name'):
|
|
columns.insert(0, 'resource_name')
|
|
columns.append('logical_resource_id')
|
|
else:
|
|
columns.insert(0, 'logical_resource_id')
|
|
|
|
return (
|
|
columns,
|
|
(utils.get_item_properties(s, columns) for s in events)
|
|
)
|
|
|
|
|
|
class LogFormatter(base.ListFormatter):
|
|
"""A formatter which prints event objects in a log style"""
|
|
|
|
def add_argument_group(self, parser):
|
|
pass
|
|
|
|
def emit_list(self, column_names, data, stdout, parsed_args):
|
|
stdout.write(heat_utils.event_log_formatter(data))
|
|
stdout.write('\n')
|