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:
Mark Vanderwiel 2015-12-11 13:47:28 -06:00
parent ff20a76b94
commit 0306a41fa0
3 changed files with 152 additions and 3 deletions

View File

@ -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})

View File

@ -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'
})

View File

@ -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