Merge "OpenstackClient plugin for software deployment output show"

This commit is contained in:
Jenkins 2016-02-24 23:57:11 +00:00 committed by Gerrit Code Review
commit 3afe92b71d
5 changed files with 259 additions and 7 deletions

View file

@ -13,6 +13,8 @@
# Copyright 2015 IBM Corp.
from cliff import show
import six
import sys
class RawFormat(show.ShowOne):
@ -51,3 +53,41 @@ class ValueFormat(RawFormat):
@property
def formatter_default(self):
return 'value'
def indent_and_truncate(txt, spaces=0, truncate=False, truncate_limit=10,
truncate_prefix=None, truncate_postfix=None):
"""Indents supplied multiline text by the specified number of spaces
"""
if txt is None:
return
lines = six.text_type(txt).splitlines()
if truncate and len(lines) > truncate_limit:
lines = lines[-truncate_limit:]
if truncate_prefix is not None:
lines.insert(0, truncate_prefix)
if truncate_postfix is not None:
lines.append(truncate_postfix)
if spaces > 0:
lines = [" " * spaces + line for line in lines]
return '\n'.join(lines)
def print_software_deployment_output(data, name, out=sys.stdout, long=False):
"""Prints details of the software deployment for user consumption
The format attempts to be valid yaml, but is primarily aimed at showing
useful information to the user in a helpful layout.
"""
if name in ('deploy_stdout', 'deploy_stderr'):
output = indent_and_truncate(
data.get(name),
spaces=4,
truncate=not long,
truncate_prefix='...',
truncate_postfix='(truncated, view all with --long)')
out.write(' %s: |\n%s\n' % (name, output))
else:
out.write(' %s: %s\n' % (name, data.get(name)))

View file

@ -140,8 +140,8 @@ class DeleteDeployment(command.Command):
def get_parser(self, prog_name):
parser = super(DeleteDeployment, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar='<ID>',
'deployment',
metavar='<deployment>',
nargs='+',
help=_('ID of the deployment(s) to delete.')
)
@ -152,7 +152,7 @@ class DeleteDeployment(command.Command):
hc = self.app.client_manager.orchestration
failure_count = 0
for deploy_id in parsed_args.id:
for deploy_id in parsed_args.deployment:
try:
sd = hc.software_deployments.get(deployment_id=deploy_id)
hc.software_deployments.delete(
@ -178,7 +178,7 @@ class DeleteDeployment(command.Command):
raise exc.CommandError(_('Unable to delete %(count)s of the '
'%(total)s deployments.') %
{'count': failure_count,
'total': len(parsed_args.id)})
'total': len(parsed_args.deployment)})
class ListDeployment(lister.Lister):
@ -229,8 +229,8 @@ class ShowDeployment(show.ShowOne):
def get_parser(self, prog_name):
parser = super(ShowDeployment, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar='<id>',
'deployment',
metavar='<deployment>',
help=_('ID of the deployment')
)
parser.add_argument(
@ -246,7 +246,7 @@ class ShowDeployment(show.ShowOne):
heat_client = self.app.client_manager.orchestration
try:
data = heat_client.software_deployments.get(
deployment_id=parsed_args.id)
deployment_id=parsed_args.deployment)
except heat_exc.HTTPNotFound:
raise exc.CommandError(_('Software Deployment not found: %s') % id)
else:
@ -286,3 +286,71 @@ class ShowMetadataDeployment(command.Command):
md = heat_client.software_deployments.metadata(
server_id=parsed_args.server)
print(jsonutils.dumps(md, indent=2))
class ShowOutputDeployment(command.Command):
"""Show a specific deployment output."""
log = logging.getLogger(__name__ + '.ShowOutputDeployment')
def get_parser(self, prog_name):
parser = super(ShowOutputDeployment, self).get_parser(prog_name)
parser.add_argument(
'deployment',
metavar='<deployment>',
help=_('ID of deployment to show the output for')
)
parser.add_argument(
'output',
metavar='<output-name>',
nargs='?',
default=None,
help=_('Name of an output to display')
)
parser.add_argument(
'--all',
default=False,
action='store_true',
help=_('Display all deployment outputs')
)
parser.add_argument(
'--long',
action='store_true',
default=False,
help='Show full deployment logs in output',
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
heat_client = self.app.client_manager.orchestration
if (not parsed_args.all and parsed_args.output is None or
parsed_args.all and parsed_args.output is not None):
raise exc.CommandError(
_('Error: either %(output)s or %(all)s argument is needed.')
% {'output': '<output-name>', 'all': '--all'})
try:
sd = heat_client.software_deployments.get(
deployment_id=parsed_args.deployment)
except heat_exc.HTTPNotFound:
raise exc.CommandError(_('Deployment not found: %s')
% parsed_args.deployment)
outputs = sd.output_values
if outputs:
if parsed_args.all:
print('output_values:\n')
for k in outputs.keys():
format_utils.print_software_deployment_output(
data=outputs, name=k, long=parsed_args.long)
else:
if parsed_args.output not in outputs:
msg = (_('Output %(output)s does not exist in deployment'
' %(deployment)s')
% {'output': parsed_args.output,
'deployment': parsed_args.deployment})
raise exc.CommandError(msg)
else:
print('output_value:\n')
format_utils.print_software_deployment_output(
data=outputs, name=parsed_args.output)

View file

@ -398,3 +398,55 @@ class TestDeploymentMetadataShow(TestDeployment):
self.cmd.take_action(parsed_args)
self.sd_client.metadata.assert_called_with(
server_id='ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5')
class TestDeploymentOutputShow(TestDeployment):
get_response = {
"status": "IN_PROGRESS",
"server_id": "ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5",
"config_id": "3d5ec2a8-7004-43b6-a7f6-542bdbe9d434",
"output_values": None,
"input_values": None,
"action": "CREATE",
"status_reason": "Deploy data available",
"id": "06e87bcc-33a2-4bce-aebd-533e698282d3",
"creation_time": "2015-01-31T15:12:36Z",
"updated_time": "2015-01-31T15:18:21Z"
}
def setUp(self):
super(TestDeploymentOutputShow, self).setUp()
self.cmd = software_deployment.ShowOutputDeployment(self.app, None)
def test_deployment_output_show(self):
arglist = ['85c3a507-351b-4b28-a7d8-531c8d53f4e6', '--all', '--long']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.sd_client.get = mock.Mock(
return_value=software_deployments.SoftwareDeployment(
None, self.get_response))
self.cmd.take_action(parsed_args)
self.sd_client.get.assert_called_with(**{
'deployment_id': '85c3a507-351b-4b28-a7d8-531c8d53f4e6'
})
def test_deployment_output_show_invalid(self):
arglist = ['85c3a507-351b-4b28-a7d8-531c8d53f4e6']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.sd_client.get = mock.Mock()
error = self.assertRaises(
exc.CommandError,
self.cmd.take_action,
parsed_args)
self.assertIn('either <output-name> or --all argument is needed',
str(error))
def test_deployment_output_show_not_found(self):
arglist = ['85c3a507-351b-4b28-a7d8-531c8d53f4e6', '--all']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.sd_client.get = mock.Mock()
self.sd_client.get.side_effect = heat_exc.HTTPNotFound()
self.assertRaises(
exc.CommandError,
self.cmd.take_action,
parsed_args)

View file

@ -13,6 +13,7 @@
# Copyright 2015 IBM Corp.
import json
import six
import yaml
from heatclient.common import format_utils
@ -89,3 +90,93 @@ abcde
self.cmd.run(parsed_args)
self.assertEqual(expected, self.app.stdout.make_string())
def test_indent_and_truncate(self):
self.assertEqual(
None,
format_utils.indent_and_truncate(None))
self.assertEqual(
None,
format_utils.indent_and_truncate(None, truncate=True))
self.assertEqual(
'',
format_utils.indent_and_truncate(''))
self.assertEqual(
'one',
format_utils.indent_and_truncate('one'))
self.assertEqual(
None,
format_utils.indent_and_truncate(None, spaces=2))
self.assertEqual(
'',
format_utils.indent_and_truncate('', spaces=2))
self.assertEqual(
' one',
format_utils.indent_and_truncate('one', spaces=2))
self.assertEqual(
'one\ntwo\nthree\nfour\nfive',
format_utils.indent_and_truncate('one\ntwo\nthree\nfour\nfive'))
self.assertEqual(
'three\nfour\nfive',
format_utils.indent_and_truncate(
'one\ntwo\nthree\nfour\nfive',
truncate=True,
truncate_limit=3))
self.assertEqual(
' and so on\n three\n four\n five\n truncated',
format_utils.indent_and_truncate(
'one\ntwo\nthree\nfour\nfive',
spaces=2,
truncate=True,
truncate_limit=3,
truncate_prefix='and so on',
truncate_postfix='truncated'))
def test_print_software_deployment_output(self):
out = six.StringIO()
format_utils.print_software_deployment_output(
{'deploy_stdout': ''}, out=out, name='deploy_stdout')
self.assertEqual(
' deploy_stdout: |\n\n',
out.getvalue())
ov = {'deploy_stdout': '', 'deploy_stderr': '1\n2\n3\n4\n5\n6\n7\n8\n9'
'\n10\n11',
'deploy_status_code': 0}
out = six.StringIO()
format_utils.print_software_deployment_output(ov, out=out,
name='deploy_stderr')
self.assertEqual(
u'''\
deploy_stderr: |
...
2
3
4
5
6
7
8
9
10
11
(truncated, view all with --long)
''', out.getvalue())
out = six.StringIO()
format_utils.print_software_deployment_output(ov, out=out,
name='deploy_stderr',
long=True)
self.assertEqual(
u'''\
deploy_stderr: |
1
2
3
4
5
6
7
8
9
10
11
''', out.getvalue())

View file

@ -42,6 +42,7 @@ openstack.orchestration.v1 =
software_deployment_delete = heatclient.osc.v1.software_deployment:DeleteDeployment
software_deployment_list = heatclient.osc.v1.software_deployment:ListDeployment
software_deployment_metadata_show = heatclient.osc.v1.software_deployment:ShowMetadataDeployment
software_deployment_output_show = heatclient.osc.v1.software_deployment:ShowOutputDeployment
software_deployment_show = heatclient.osc.v1.software_deployment:ShowDeployment
stack_abandon = heatclient.osc.v1.stack:AbandonStack
stack_adopt = heatclient.osc.v1.stack:AdoptStack