# Copyright 2015 Red Hat Inc. # # 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. from stacktaskclient.common import utils import stacktaskclient.exc as exc from stacktaskclient.openstack.common._i18n import _ def get_hook_events(hc, stack_id, event_args, nested_depth=0, hook_type='pre-create'): if hook_type == 'pre-create': stack_action_reason = 'Stack CREATE started' hook_event_reason = 'CREATE paused until Hook pre-create is cleared' hook_clear_event_reason = 'Hook pre-create is cleared' elif hook_type == 'pre-update': stack_action_reason = 'Stack UPDATE started' hook_event_reason = 'UPDATE paused until Hook pre-update is cleared' hook_clear_event_reason = 'Hook pre-update is cleared' else: raise exc.CommandError(_('Unexpected hook type %s') % hook_type) events = get_events(hc, stack_id=stack_id, event_args=event_args, nested_depth=nested_depth) # Get the most recent event associated with this action, which gives us the # event when we moved into IN_PROGRESS for the hooks we're interested in. stack_name = stack_id.split("/")[0] action_start_event = [e for e in enumerate(events) if e[1].resource_status_reason == stack_action_reason and e[1].stack_name == stack_name][-1] # Slice the events with the index from the enumerate action_start_index = action_start_event[0] events = events[action_start_index:] # Get hook events still pending by some list filtering/comparison # We build a map hook events per-resource, and remove any event # for which there is a corresponding hook-clear event. resource_event_map = {} for e in events: stack_resource = (e.stack_name, e.resource_name) if e.resource_status_reason == hook_event_reason: resource_event_map[(e.stack_name, e.resource_name)] = e elif e.resource_status_reason == hook_clear_event_reason: if resource_event_map.get(stack_resource): del(resource_event_map[(e.stack_name, e.resource_name)]) return list(resource_event_map.values()) def get_events(hc, stack_id, event_args, nested_depth=0, marker=None, limit=None): events = _get_stack_events(hc, stack_id, event_args) if nested_depth > 0: events.extend(_get_nested_events(hc, nested_depth, stack_id, event_args)) # Because there have been multiple stacks events mangled into # one list, we need to sort before passing to print_list # Note we can't use the prettytable sortby_index here, because # the "start" option doesn't allow post-sort slicing, which # will be needed to make "--marker" work for nested_depth lists events.sort(key=lambda x: x.event_time) # Slice the list if marker is specified if marker: marker_index = [e.id for e in events].index(marker) events = events[marker_index:] # Slice the list if limit is specified if limit: limit_index = min(int(limit), len(events)) events = events[:limit_index] return events def _get_nested_ids(hc, stack_id): nested_ids = [] try: resources = hc.resources.list(stack_id=stack_id) except exc.HTTPNotFound: raise exc.CommandError(_('Stack not found: %s') % stack_id) for r in resources: nested_id = utils.resource_nested_identifier(r) if nested_id: nested_ids.append(nested_id) return nested_ids def _get_nested_events(hc, nested_depth, stack_id, event_args): # FIXME(shardy): this is very inefficient, we should add nested_depth to # the event_list API in a future stacktask version, but this will be required # until kilo stacktask is EOL. nested_ids = _get_nested_ids(hc, stack_id) nested_events = [] for n_id in nested_ids: stack_events = _get_stack_events(hc, n_id, event_args) if stack_events: nested_events.extend(stack_events) if nested_depth > 1: next_depth = nested_depth - 1 nested_events.extend(_get_nested_events( hc, next_depth, n_id, event_args)) return nested_events def _get_stack_events(hc, stack_id, event_args): event_args['stack_id'] = stack_id try: events = hc.events.list(**event_args) except exc.HTTPNotFound as ex: # it could be the stack or resource that is not found # just use the message that the server sent us. raise exc.CommandError(str(ex)) else: # Show which stack the event comes from (for nested events) for e in events: e.stack_name = stack_id.split("/")[0] return events