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
This commit is contained in:
parent
a5fdf2318e
commit
69f41bceae
|
@ -0,0 +1,78 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import fnmatch
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import heatclient.exc as exc
|
||||||
|
|
||||||
|
from heatclient.openstack.common._i18n import _
|
||||||
|
from heatclient.openstack.common._i18n import _LE
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_hook(hc, stack_id, resource_name, hook_type):
|
||||||
|
try:
|
||||||
|
hc.resources.signal(
|
||||||
|
stack_id=stack_id,
|
||||||
|
resource_name=resource_name,
|
||||||
|
data={'unset_hook': hook_type})
|
||||||
|
except exc.HTTPNotFound:
|
||||||
|
logger.error(
|
||||||
|
_LE("Stack %(stack)s or resource %(resource)s"
|
||||||
|
"not found for hook %(hook_type)"),
|
||||||
|
{'resource': resource_name, 'stack': stack_id,
|
||||||
|
'hook_type': hook_type})
|
||||||
|
|
||||||
|
|
||||||
|
def clear_wildcard_hooks(hc, stack_id, stack_patterns, hook_type,
|
||||||
|
resource_pattern):
|
||||||
|
if stack_patterns:
|
||||||
|
for resource in hc.resources.list(stack_id):
|
||||||
|
res_name = resource.resource_name
|
||||||
|
if fnmatch.fnmatchcase(res_name, stack_patterns[0]):
|
||||||
|
nested_stack = hc.resources.get(
|
||||||
|
stack_id=stack_id,
|
||||||
|
resource_name=res_name)
|
||||||
|
clear_wildcard_hooks(
|
||||||
|
hc,
|
||||||
|
nested_stack.physical_resource_id,
|
||||||
|
stack_patterns[1:], hook_type, resource_pattern)
|
||||||
|
else:
|
||||||
|
for resource in hc.resources.list(stack_id):
|
||||||
|
res_name = resource.resource_name
|
||||||
|
if fnmatch.fnmatchcase(res_name, resource_pattern):
|
||||||
|
clear_hook(hc, stack_id, res_name, hook_type)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hook_type_via_status(hc, stack_id):
|
||||||
|
# Figure out if the hook should be pre-create or pre-update based
|
||||||
|
# on the stack status, also sanity assertions that we're in-progress.
|
||||||
|
try:
|
||||||
|
stack = hc.stacks.get(stack_id=stack_id)
|
||||||
|
except exc.HTTPNotFound:
|
||||||
|
raise exc.CommandError(_('Stack not found: %s') % stack_id)
|
||||||
|
else:
|
||||||
|
if 'IN_PROGRESS' not in stack.stack_status:
|
||||||
|
raise exc.CommandError(_('Stack status %s not IN_PROGRESS') %
|
||||||
|
stack.stack_status)
|
||||||
|
|
||||||
|
if 'CREATE' in stack.stack_status:
|
||||||
|
hook_type = 'pre-create'
|
||||||
|
elif 'UPDATE' in stack.stack_status:
|
||||||
|
hook_type = 'pre-update'
|
||||||
|
else:
|
||||||
|
raise exc.CommandError(_('Unexpected stack status %s, '
|
||||||
|
'only create/update supported')
|
||||||
|
% stack.stack_status)
|
||||||
|
return hook_type
|
|
@ -26,7 +26,9 @@ from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
from six.moves.urllib import request
|
from six.moves.urllib import request
|
||||||
|
|
||||||
|
from heatclient.common import event_utils
|
||||||
from heatclient.common import format_utils
|
from heatclient.common import format_utils
|
||||||
|
from heatclient.common import hook_utils
|
||||||
from heatclient.common import http
|
from heatclient.common import http
|
||||||
from heatclient.common import template_utils
|
from heatclient.common import template_utils
|
||||||
from heatclient.common import utils as heat_utils
|
from heatclient.common import utils as heat_utils
|
||||||
|
@ -1060,3 +1062,134 @@ class CheckStack(StackActionBase):
|
||||||
['check_complete'],
|
['check_complete'],
|
||||||
['check_failed']
|
['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)
|
||||||
|
|
|
@ -26,6 +26,8 @@ from heatclient import exc as heat_exc
|
||||||
from heatclient.osc.v1 import stack
|
from heatclient.osc.v1 import stack
|
||||||
from heatclient.tests import inline_templates
|
from heatclient.tests import inline_templates
|
||||||
from heatclient.tests.unit.osc.v1 import fakes as orchestration_fakes
|
from heatclient.tests.unit.osc.v1 import fakes as orchestration_fakes
|
||||||
|
from heatclient.v1 import events
|
||||||
|
from heatclient.v1 import resources
|
||||||
from heatclient.v1 import stacks
|
from heatclient.v1 import stacks
|
||||||
|
|
||||||
load_tests = testscenarios.load_tests_apply_scenarios
|
load_tests = testscenarios.load_tests_apply_scenarios
|
||||||
|
@ -1074,3 +1076,139 @@ class TestStackCheck(_TestStackCheckBase, TestStack):
|
||||||
|
|
||||||
def test_stack_check_exception(self):
|
def test_stack_check_exception(self):
|
||||||
self._test_stack_action_exception()
|
self._test_stack_action_exception()
|
||||||
|
|
||||||
|
|
||||||
|
class TestStackHookPoll(TestStack):
|
||||||
|
|
||||||
|
stack = stacks.Stack(None, {
|
||||||
|
"id": '1234',
|
||||||
|
"stack_name": 'my_stack',
|
||||||
|
"creation_time": "2013-08-04T20:57:55Z",
|
||||||
|
"updated_time": "2013-08-04T20:57:55Z",
|
||||||
|
"stack_status": "CREATE_IN_PROGRESS"
|
||||||
|
})
|
||||||
|
resource = resources.Resource(None, {
|
||||||
|
'resource_name': 'resource1',
|
||||||
|
'links': [{'href': 'http://heat.example.com:8004/resource1',
|
||||||
|
'rel': 'self'},
|
||||||
|
{'href': 'http://192.168.27.100:8004/my_stack',
|
||||||
|
'rel': 'stack'}],
|
||||||
|
'logical_resource_id': 'random_group',
|
||||||
|
'creation_time': '2015-12-03T16:50:56',
|
||||||
|
'resource_status': 'INIT_COMPLETE',
|
||||||
|
'updated_time': '2015-12-03T16:50:56',
|
||||||
|
'required_by': [],
|
||||||
|
'resource_status_reason': '',
|
||||||
|
'physical_resource_id': '',
|
||||||
|
'resource_type': 'OS::Heat::ResourceGroup',
|
||||||
|
'id': '1111'
|
||||||
|
})
|
||||||
|
columns = ['ID', 'Resource Status Reason', 'Resource Status',
|
||||||
|
'Event Time']
|
||||||
|
event0 = events.Event(manager=None, info={
|
||||||
|
'resource_name': 'my_stack',
|
||||||
|
'event_time': '2015-12-02T16:50:56',
|
||||||
|
'logical_resource_id': 'my_stack',
|
||||||
|
'resource_status': 'CREATE_IN_PROGRESS',
|
||||||
|
'resource_status_reason': 'Stack CREATE started',
|
||||||
|
'id': '1234'
|
||||||
|
})
|
||||||
|
event1 = events.Event(manager=None, info={
|
||||||
|
'resource_name': 'resource1',
|
||||||
|
'event_time': '2015-12-03T19:59:58',
|
||||||
|
'logical_resource_id': 'resource1',
|
||||||
|
'resource_status': 'INIT_COMPLETE',
|
||||||
|
'resource_status_reason':
|
||||||
|
'CREATE paused until Hook pre-create is cleared',
|
||||||
|
'id': '1111'
|
||||||
|
})
|
||||||
|
row1 = ('resource1',
|
||||||
|
'1111',
|
||||||
|
'CREATE paused until Hook pre-create is cleared',
|
||||||
|
'INIT_COMPLETE',
|
||||||
|
'2015-12-03T19:59:58'
|
||||||
|
)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStackHookPoll, self).setUp()
|
||||||
|
self.cmd = stack.StackHookPoll(self.app, None)
|
||||||
|
self.mock_client.stacks.get = mock.Mock(
|
||||||
|
return_value=self.stack)
|
||||||
|
self.mock_client.events.list = mock.Mock(
|
||||||
|
return_value=[self.event0, self.event1])
|
||||||
|
self.mock_client.resources.list = mock.Mock(
|
||||||
|
return_value=[self.resource])
|
||||||
|
|
||||||
|
def test_hook_poll(self):
|
||||||
|
expected_columns = ['Resource Name'] + self.columns
|
||||||
|
expected_rows = [self.row1]
|
||||||
|
arglist = ['my_stack']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
columns, rows = self.cmd.take_action(parsed_args)
|
||||||
|
self.assertEqual(expected_rows, list(rows))
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
def test_hook_poll_nested(self):
|
||||||
|
expected_columns = ['Resource Name'] + self.columns + ['Stack Name']
|
||||||
|
expected_rows = [self.row1 + ('my_stack',)]
|
||||||
|
arglist = ['my_stack', '--nested-depth=10']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
columns, rows = self.cmd.take_action(parsed_args)
|
||||||
|
self.assertEqual(expected_rows, list(rows))
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
def test_hook_poll_nested_invalid(self):
|
||||||
|
arglist = ['my_stack', '--nested-depth=ugly']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args)
|
||||||
|
|
||||||
|
|
||||||
|
class TestStackHookClear(TestStack):
|
||||||
|
|
||||||
|
stack = stacks.Stack(None, {
|
||||||
|
"id": '1234',
|
||||||
|
"stack_name": 'my_stack',
|
||||||
|
"creation_time": "2013-08-04T20:57:55Z",
|
||||||
|
"updated_time": "2013-08-04T20:57:55Z",
|
||||||
|
"stack_status": "CREATE_IN_PROGRESS"
|
||||||
|
})
|
||||||
|
resource = resources.Resource(None, {
|
||||||
|
'stack_id': 'my_stack',
|
||||||
|
'resource_name': 'resource'
|
||||||
|
})
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStackHookClear, self).setUp()
|
||||||
|
self.cmd = stack.StackHookClear(self.app, None)
|
||||||
|
self.mock_client.stacks.get = mock.Mock(
|
||||||
|
return_value=self.stack)
|
||||||
|
self.mock_client.resources.signal = mock.Mock()
|
||||||
|
self.mock_client.resources.list = mock.Mock(
|
||||||
|
return_value=[self.resource])
|
||||||
|
|
||||||
|
def test_hook_clear(self):
|
||||||
|
arglist = ['my_stack', 'resource']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
self.cmd.take_action(parsed_args)
|
||||||
|
self.mock_client.resources.signal.assert_called_once_with(
|
||||||
|
data={'unset_hook': 'pre-create'},
|
||||||
|
resource_name='resource',
|
||||||
|
stack_id='my_stack')
|
||||||
|
|
||||||
|
def test_hook_clear_pre_create(self):
|
||||||
|
arglist = ['my_stack', 'resource', '--pre-create']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
self.cmd.take_action(parsed_args)
|
||||||
|
self.mock_client.resources.signal.assert_called_once_with(
|
||||||
|
data={'unset_hook': 'pre-create'},
|
||||||
|
resource_name='resource',
|
||||||
|
stack_id='my_stack')
|
||||||
|
|
||||||
|
def test_hook_clear_pre_update(self):
|
||||||
|
arglist = ['my_stack', 'resource', '--pre-update']
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||||
|
self.cmd.take_action(parsed_args)
|
||||||
|
self.mock_client.resources.signal.assert_called_once_with(
|
||||||
|
data={'unset_hook': 'pre-update'},
|
||||||
|
resource_name='resource',
|
||||||
|
stack_id='my_stack')
|
||||||
|
|
|
@ -16,6 +16,8 @@ import testtools
|
||||||
|
|
||||||
import heatclient.v1.shell as shell
|
import heatclient.v1.shell as shell
|
||||||
|
|
||||||
|
from heatclient.common import hook_utils
|
||||||
|
|
||||||
|
|
||||||
class TestHooks(testtools.TestCase):
|
class TestHooks(testtools.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -220,7 +222,8 @@ class TestHooks(testtools.TestCase):
|
||||||
self.assertEqual(expected_hooks, actual_hooks)
|
self.assertEqual(expected_hooks, actual_hooks)
|
||||||
|
|
||||||
def test_clear_all_hooks(self):
|
def test_clear_all_hooks(self):
|
||||||
shell._get_hook_type_via_status = mock.Mock(return_value='pre-create')
|
hook_utils.get_hook_type_via_status = mock.Mock(
|
||||||
|
return_value='pre-create')
|
||||||
type(self.args).hook = mock.PropertyMock(
|
type(self.args).hook = mock.PropertyMock(
|
||||||
return_value=['bp'])
|
return_value=['bp'])
|
||||||
type(self.args).pre_create = mock.PropertyMock(return_value=True)
|
type(self.args).pre_create = mock.PropertyMock(return_value=True)
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import fnmatch
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
@ -25,13 +24,13 @@ import yaml
|
||||||
|
|
||||||
from heatclient.common import deployment_utils
|
from heatclient.common import deployment_utils
|
||||||
from heatclient.common import event_utils
|
from heatclient.common import event_utils
|
||||||
|
from heatclient.common import hook_utils
|
||||||
from heatclient.common import http
|
from heatclient.common import http
|
||||||
from heatclient.common import template_format
|
from heatclient.common import template_format
|
||||||
from heatclient.common import template_utils
|
from heatclient.common import template_utils
|
||||||
from heatclient.common import utils
|
from heatclient.common import utils
|
||||||
|
|
||||||
from heatclient.openstack.common._i18n import _
|
from heatclient.openstack.common._i18n import _
|
||||||
from heatclient.openstack.common._i18n import _LE
|
|
||||||
from heatclient.openstack.common._i18n import _LW
|
from heatclient.openstack.common._i18n import _LW
|
||||||
|
|
||||||
import heatclient.exc as exc
|
import heatclient.exc as exc
|
||||||
|
@ -993,42 +992,15 @@ def do_hook_clear(hc, args):
|
||||||
elif args.pre_update:
|
elif args.pre_update:
|
||||||
hook_type = 'pre-update'
|
hook_type = 'pre-update'
|
||||||
else:
|
else:
|
||||||
hook_type = _get_hook_type_via_status(hc, args.id)
|
hook_type = hook_utils.get_hook_type_via_status(hc, args.id)
|
||||||
|
|
||||||
for hook_string in args.hook:
|
for hook_string in args.hook:
|
||||||
hook = [b for b in hook_string.split('/') if b]
|
hook = [b for b in hook_string.split('/') if b]
|
||||||
resource_pattern = hook[-1]
|
resource_pattern = hook[-1]
|
||||||
stack_id = args.id
|
stack_id = args.id
|
||||||
|
|
||||||
def clear_hook(stack_id, resource_name):
|
hook_utils.clear_wildcard_hooks(hc, stack_id, hook[:-1],
|
||||||
try:
|
hook_type, resource_pattern)
|
||||||
hc.resources.signal(
|
|
||||||
stack_id=stack_id,
|
|
||||||
resource_name=resource_name,
|
|
||||||
data={'unset_hook': hook_type})
|
|
||||||
except exc.HTTPNotFound:
|
|
||||||
logger.error(
|
|
||||||
_LE("Stack %(stack)s or resource %(resource)s not found"),
|
|
||||||
{'resource': resource_name, 'stack': stack_id})
|
|
||||||
|
|
||||||
def clear_wildcard_hooks(stack_id, stack_patterns):
|
|
||||||
if stack_patterns:
|
|
||||||
for resource in hc.resources.list(stack_id):
|
|
||||||
res_name = resource.resource_name
|
|
||||||
if fnmatch.fnmatchcase(res_name, stack_patterns[0]):
|
|
||||||
nested_stack = hc.resources.get(
|
|
||||||
stack_id=stack_id,
|
|
||||||
resource_name=res_name)
|
|
||||||
clear_wildcard_hooks(
|
|
||||||
nested_stack.physical_resource_id,
|
|
||||||
stack_patterns[1:])
|
|
||||||
else:
|
|
||||||
for resource in hc.resources.list(stack_id):
|
|
||||||
res_name = resource.resource_name
|
|
||||||
if fnmatch.fnmatchcase(res_name, resource_pattern):
|
|
||||||
clear_hook(stack_id, res_name)
|
|
||||||
|
|
||||||
clear_wildcard_hooks(stack_id, hook[:-1])
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('id', metavar='<NAME or ID>',
|
@utils.arg('id', metavar='<NAME or ID>',
|
||||||
|
@ -1096,29 +1068,6 @@ def do_event_list(hc, args):
|
||||||
utils.print_list(events, display_fields, sortby_index=None)
|
utils.print_list(events, display_fields, sortby_index=None)
|
||||||
|
|
||||||
|
|
||||||
def _get_hook_type_via_status(hc, stack_id):
|
|
||||||
# Figure out if the hook should be pre-create or pre-update based
|
|
||||||
# on the stack status, also sanity assertions that we're in-progress.
|
|
||||||
try:
|
|
||||||
stack = hc.stacks.get(stack_id=stack_id)
|
|
||||||
except exc.HTTPNotFound:
|
|
||||||
raise exc.CommandError(_('Stack not found: %s') % stack_id)
|
|
||||||
else:
|
|
||||||
if 'IN_PROGRESS' not in stack.stack_status:
|
|
||||||
raise exc.CommandError(_('Stack status %s not IN_PROGRESS') %
|
|
||||||
stack.stack_status)
|
|
||||||
|
|
||||||
if 'CREATE' in stack.stack_status:
|
|
||||||
hook_type = 'pre-create'
|
|
||||||
elif 'UPDATE' in stack.stack_status:
|
|
||||||
hook_type = 'pre-update'
|
|
||||||
else:
|
|
||||||
raise exc.CommandError(_('Unexpected stack status %s, '
|
|
||||||
'only create/update supported')
|
|
||||||
% stack.stack_status)
|
|
||||||
return hook_type
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('id', metavar='<NAME or ID>',
|
@utils.arg('id', metavar='<NAME or ID>',
|
||||||
help=_('Name or ID of stack to show the pending hooks for.'))
|
help=_('Name or ID of stack to show the pending hooks for.'))
|
||||||
@utils.arg('-n', '--nested-depth', metavar='<DEPTH>',
|
@utils.arg('-n', '--nested-depth', metavar='<DEPTH>',
|
||||||
|
@ -1148,7 +1097,7 @@ def do_hook_poll(hc, args):
|
||||||
else:
|
else:
|
||||||
nested_depth = 0
|
nested_depth = 0
|
||||||
|
|
||||||
hook_type = _get_hook_type_via_status(hc, args.id)
|
hook_type = hook_utils.get_hook_type_via_status(hc, args.id)
|
||||||
event_args = {'sort_dir': 'asc'}
|
event_args = {'sort_dir': 'asc'}
|
||||||
hook_events = event_utils.get_hook_events(
|
hook_events = event_utils.get_hook_events(
|
||||||
hc, stack_id=args.id, event_args=event_args,
|
hc, stack_id=args.id, event_args=event_args,
|
||||||
|
|
|
@ -49,6 +49,8 @@ openstack.orchestration.v1 =
|
||||||
stack_delete = heatclient.osc.v1.stack:DeleteStack
|
stack_delete = heatclient.osc.v1.stack:DeleteStack
|
||||||
stack_event_list = heatclient.osc.v1.event:ListEvent
|
stack_event_list = heatclient.osc.v1.event:ListEvent
|
||||||
stack_event_show = heatclient.osc.v1.event:ShowEvent
|
stack_event_show = heatclient.osc.v1.event:ShowEvent
|
||||||
|
stack_hook_clear = heatclient.osc.v1.stack:StackHookClear
|
||||||
|
stack_hook_poll = heatclient.osc.v1.stack:StackHookPoll
|
||||||
stack_list = heatclient.osc.v1.stack:ListStack
|
stack_list = heatclient.osc.v1.stack:ListStack
|
||||||
stack_output_list = heatclient.osc.v1.stack:OutputListStack
|
stack_output_list = heatclient.osc.v1.stack:OutputListStack
|
||||||
stack_output_show = heatclient.osc.v1.stack:OutputShowStack
|
stack_output_show = heatclient.osc.v1.stack:OutputShowStack
|
||||||
|
|
Loading…
Reference in New Issue