mirror of
https://opendev.org/openstack/python-heatclient.git
synced 2026-01-16 23:00:35 +00:00
Merge "OpenstackClient plugin for software deployment output show"
This commit is contained in:
commit
3afe92b71d
5 changed files with 259 additions and 7 deletions
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue