summaryrefslogtreecommitdiff
path: root/heatclient/osc
diff options
context:
space:
mode:
authorMark Vanderwiel <vanderwl@us.ibm.com>2015-12-02 12:07:57 -0600
committerMark Vanderwiel <vanderwl@us.ibm.com>2016-02-18 09:27:19 -0600
commit69f41bceae632253d52cc38c3d4d0adff7ea55e5 (patch)
tree15fc51403256bd1ce5ca11d0b188a9eb2b25076a /heatclient/osc
parenta5fdf2318e350e89ad5b74d1bdad81450800a355 (diff)
downloadpython-heatclient-69f41bceae632253d52cc38c3d4d0adff7ea55e5.tar.gz
Add stack hook poll and clear to openstack client
Refactor existing hook helper fuctions into utilites. based upon heat clis: heat hook-poll hest hook-clear Change-Id: Ib46634cc62369fb5932dcd0967ae492446c79a88 Blueprint: heat-support-python-openstackclient
Diffstat (limited to 'heatclient/osc')
-rw-r--r--heatclient/osc/v1/stack.py133
1 files changed, 133 insertions, 0 deletions
diff --git a/heatclient/osc/v1/stack.py b/heatclient/osc/v1/stack.py
index 98f417e..cd67ace 100644
--- a/heatclient/osc/v1/stack.py
+++ b/heatclient/osc/v1/stack.py
@@ -26,7 +26,9 @@ from oslo_serialization import jsonutils
import six
from six.moves.urllib import request
+from heatclient.common import event_utils
from heatclient.common import format_utils
+from heatclient.common import hook_utils
from heatclient.common import http
from heatclient.common import template_utils
from heatclient.common import utils as heat_utils
@@ -1060,3 +1062,134 @@ class CheckStack(StackActionBase):
['check_complete'],
['check_failed']
)
+
+
+class StackHookPoll(lister.Lister):
+ '''List resources with pending hook for a stack.'''
+
+ log = logging.getLogger(__name__ + '.StackHookPoll')
+
+ def get_parser(self, prog_name):
+ parser = super(StackHookPoll, self).get_parser(prog_name)
+ parser.add_argument(
+ 'stack',
+ metavar='<stack>',
+ help=_('Stack to display (name or ID)')
+ )
+ parser.add_argument(
+ '--nested-depth',
+ metavar='<nested-depth>',
+ help=_('Depth of nested stacks from which to display hooks')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+ heat_client = self.app.client_manager.orchestration
+ return _hook_poll(
+ parsed_args,
+ heat_client
+ )
+
+
+def _hook_poll(args, heat_client):
+ """List resources with pending hook for a stack."""
+
+ # There are a few steps to determining if a stack has pending hooks
+ # 1. The stack is IN_PROGRESS status (otherwise, by definition no hooks
+ # can be pending
+ # 2. There is an event for a resource associated with hitting a hook
+ # 3. There is not an event associated with clearing the hook in step(2)
+ #
+ # So, essentially, this ends up being a specially filtered type of event
+ # listing, because all hook status is exposed via events. In future
+ # we might consider exposing some more efficient interface via the API
+ # to reduce the expense of this brute-force polling approach
+ columns = ['ID', 'Resource Status Reason', 'Resource Status', 'Event Time']
+
+ if args.nested_depth:
+ try:
+ nested_depth = int(args.nested_depth)
+ except ValueError:
+ msg = _("--nested-depth invalid value %s") % args.nested_depth
+ raise exc.CommandError(msg)
+ columns.append('Stack Name')
+ else:
+ nested_depth = 0
+
+ hook_type = hook_utils.get_hook_type_via_status(heat_client, args.stack)
+ event_args = {'sort_dir': 'asc'}
+ hook_events = event_utils.get_hook_events(
+ heat_client, stack_id=args.stack, event_args=event_args,
+ nested_depth=nested_depth, hook_type=hook_type)
+
+ if len(hook_events) >= 1:
+ if hasattr(hook_events[0], 'resource_name'):
+ columns.insert(0, 'Resource Name')
+ else:
+ columns.insert(0, 'Logical Resource ID')
+
+ rows = (utils.get_item_properties(h, columns) for h in hook_events)
+ return (columns, rows)
+
+
+class StackHookClear(command.Command):
+ """Clear resource hooks on a given stack."""
+
+ log = logging.getLogger(__name__ + '.StackHookClear')
+
+ def get_parser(self, prog_name):
+ parser = super(StackHookClear, self).get_parser(prog_name)
+ parser.add_argument(
+ 'stack',
+ metavar='<stack>',
+ help=_('Stack to display (name or ID)')
+ )
+ parser.add_argument(
+ '--pre-create',
+ action='store_true',
+ help=_('Clear the pre-create hooks')
+ )
+ parser.add_argument(
+ '--pre-update',
+ action='store_true',
+ help=_('Clear the pre-update hooks')
+ )
+ parser.add_argument(
+ 'hook',
+ metavar='<resource>',
+ nargs='+',
+ help=_('Resource names with hooks to clear. Resources '
+ 'in nested stacks can be set using slash as a separator: '
+ 'nested_stack/another/my_resource. You can use wildcards '
+ 'to match multiple stacks or resources: '
+ 'nested_stack/an*/*_resource')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+ heat_client = self.app.client_manager.orchestration
+ return _hook_clear(
+ parsed_args,
+ heat_client
+ )
+
+
+def _hook_clear(args, heat_client):
+ """Clear resource hooks on a given stack."""
+ if args.pre_create:
+ hook_type = 'pre-create'
+ elif args.pre_update:
+ hook_type = 'pre-update'
+ else:
+ hook_type = hook_utils.get_hook_type_via_status(heat_client,
+ args.stack)
+
+ for hook_string in args.hook:
+ hook = [b for b in hook_string.split('/') if b]
+ resource_pattern = hook[-1]
+ stack_id = args.stack
+
+ hook_utils.clear_wildcard_hooks(heat_client, stack_id, hook[:-1],
+ hook_type, resource_pattern)