Add openstack client stack resource signal
Add the openstack stack resource signal command. Based from the existing heat commands: heat resource-signal Change-Id: I3b3628d86b71d448feea197f6c92d3d3d19726b5 Blueprint: heat-support-python-openstackclient
This commit is contained in:
parent
ff20a76b94
commit
0306a41fa0
|
@ -13,16 +13,20 @@
|
|||
|
||||
"""Orchestration v1 Stack action implementations"""
|
||||
|
||||
from cliff import command
|
||||
import logging
|
||||
import six
|
||||
from six.moves.urllib import request
|
||||
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import utils
|
||||
from openstackclient.i18n import _
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from heatclient.common import format_utils
|
||||
from heatclient.common import utils as heat_utils
|
||||
from heatclient import exc as heat_exc
|
||||
|
||||
|
||||
|
@ -161,10 +165,78 @@ def _resource_metadata(heat_client, args):
|
|||
try:
|
||||
metadata = heat_client.resources.metadata(**fields)
|
||||
except heat_exc.HTTPNotFound:
|
||||
raise exc.CommandError(_('Stack or resource not found: '
|
||||
'%(stack)s %(resource)s') %
|
||||
raise exc.CommandError(_('Stack %(stack)s or resource %(resource)s '
|
||||
'not found.') %
|
||||
{'stack': args.stack,
|
||||
'resource': args.resource})
|
||||
|
||||
data = list(six.itervalues(metadata))
|
||||
columns = list(six.iterkeys(metadata))
|
||||
return columns, data
|
||||
|
||||
|
||||
class ResourceSignal(command.Command):
|
||||
"""Signal a resource with optional data."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ResourceSignal")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ResourceSignal, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'stack',
|
||||
metavar='<STACK>',
|
||||
help=_('Name or ID of stack the resource belongs to'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource',
|
||||
metavar='<RESOURCE>',
|
||||
help=_('Name of the resoure to signal'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--data',
|
||||
metavar='<DATA>',
|
||||
help=_('JSON Data to send to the signal handler')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--data-file',
|
||||
metavar='<DATA_FILE>',
|
||||
help=_('File containing JSON data to send to the signal handler')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
heat_client = self.app.client_manager.orchestration
|
||||
return _resource_signal(heat_client, parsed_args)
|
||||
|
||||
|
||||
def _resource_signal(heat_client, args):
|
||||
fields = {'stack_id': args.stack,
|
||||
'resource_name': args.resource}
|
||||
data = args.data
|
||||
data_file = args.data_file
|
||||
if data and data_file:
|
||||
raise exc.CommandError(_('Should only specify one of data or '
|
||||
'data-file'))
|
||||
|
||||
if data_file:
|
||||
data_url = heat_utils.normalise_file_path_to_url(data_file)
|
||||
data = request.urlopen(data_url).read()
|
||||
|
||||
if data:
|
||||
try:
|
||||
data = jsonutils.loads(data)
|
||||
except ValueError as ex:
|
||||
raise exc.CommandError(_('Data should be in JSON format: %s') % ex)
|
||||
if not isinstance(data, dict):
|
||||
raise exc.CommandError(_('Data should be a JSON dict'))
|
||||
|
||||
fields['data'] = data
|
||||
try:
|
||||
heat_client.resources.signal(**fields)
|
||||
except heat_exc.HTTPNotFound:
|
||||
raise exc.CommandError(_('Stack %(stack)s or resource %(resource)s '
|
||||
'not found.') %
|
||||
{'stack': args.stack,
|
||||
'resource': args.resource})
|
||||
|
|
|
@ -219,4 +219,80 @@ class TestResourceMetadata(TestResource):
|
|||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.resource_client.metadata = mock.Mock(
|
||||
side_effect=heat_exc.HTTPNotFound)
|
||||
self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args)
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertEqual('Stack my_stack or resource my_resource not found.',
|
||||
str(error))
|
||||
|
||||
|
||||
class TestResourceSignal(TestResource):
|
||||
|
||||
def setUp(self):
|
||||
super(TestResourceSignal, self).setUp()
|
||||
self.cmd = resources.ResourceSignal(self.app, None)
|
||||
self.resource_client.signal = mock.Mock()
|
||||
|
||||
def test_resource_signal(self):
|
||||
arglist = ['my_stack', 'my_resource']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.resource_client.signal.assert_called_with(**{
|
||||
'stack_id': 'my_stack',
|
||||
'resource_name': 'my_resource'
|
||||
})
|
||||
|
||||
def test_resource_signal_error(self):
|
||||
arglist = ['my_stack', 'my_resource']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.resource_client.signal = mock.Mock(
|
||||
side_effect=heat_exc.HTTPNotFound)
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertEqual('Stack my_stack or resource my_resource not found.',
|
||||
str(error))
|
||||
|
||||
def test_resource_signal_data(self):
|
||||
arglist = ['my_stack', 'my_resource',
|
||||
'--data', '{"message":"Content"}']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.resource_client.signal.assert_called_with(**{
|
||||
'data': {u'message': u'Content'},
|
||||
'stack_id': 'my_stack',
|
||||
'resource_name': 'my_resource'
|
||||
})
|
||||
|
||||
def test_resource_signal_data_not_json(self):
|
||||
arglist = ['my_stack', 'my_resource', '--data', '{']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertIn('Data should be in JSON format', str(error))
|
||||
|
||||
def test_resource_signal_data_and_file_error(self):
|
||||
arglist = ['my_stack', 'my_resource',
|
||||
'--data', '{}', '--data-file', 'file']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = self.assertRaises(exc.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
self.assertEqual('Should only specify one of data or data-file',
|
||||
str(error))
|
||||
|
||||
@mock.patch('six.moves.urllib.request.urlopen')
|
||||
def test_resource_signal_file(self, urlopen):
|
||||
data = mock.Mock()
|
||||
data.read.side_effect = ['{"message":"Content"}']
|
||||
urlopen.return_value = data
|
||||
|
||||
arglist = ['my_stack', 'my_resource', '--data-file', 'test_file']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.resource_client.signal.assert_called_with(**{
|
||||
'data': {u'message': u'Content'},
|
||||
'stack_id': 'my_stack',
|
||||
'resource_name': 'my_resource'
|
||||
})
|
||||
|
|
|
@ -53,6 +53,7 @@ openstack.orchestration.v1 =
|
|||
stack_resource_list = heatclient.osc.v1.resources:ResourceList
|
||||
stack_resource_metadata = heatclient.osc.v1.resources:ResourceMetadata
|
||||
stack_resource_show = heatclient.osc.v1.resources:ResourceShow
|
||||
stack_resource_signal = heatclient.osc.v1.resources:ResourceSignal
|
||||
stack_resume = heatclient.osc.v1.stack:ResumeStack
|
||||
stack_show = heatclient.osc.v1.stack:ShowStack
|
||||
stack_snapshot_list = heatclient.osc.v1.snapshot:ListSnapshot
|
||||
|
|
Loading…
Reference in New Issue