Add json and yaml formatting to manila-manage

Add the possibility to specify how the data will be displayed
while issuing the manila-manage command. This can be helpful to
automations,

Change-Id: I0af426254be886e31553275feb23b9ec90ec2cc8
This commit is contained in:
silvacarloss 2023-10-25 15:33:34 -03:00 committed by Carlos Eduardo
parent 4bf505404a
commit 861a67193b
3 changed files with 149 additions and 25 deletions

View File

@ -55,9 +55,11 @@
import os import os
import sys import sys
import yaml
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_serialization import jsonutils
from manila.common import config # Need to register global_opts # noqa from manila.common import config # Need to register global_opts # noqa
from manila import context from manila import context
@ -69,6 +71,7 @@ from manila import version
CONF = cfg.CONF CONF = cfg.CONF
ALLOWED_OUTPUT_FORMATS = ['table', 'json', 'yaml']
HOST_UPDATE_HELP_MSG = ("A fully qualified host string is of the format " HOST_UPDATE_HELP_MSG = ("A fully qualified host string is of the format "
"'HostA@BackendB#PoolC'. Provide only the host name " "'HostA@BackendB#PoolC'. Provide only the host name "
"(ex: 'HostA') to update the hostname part of " "(ex: 'HostA') to update the hostname part of "
@ -78,6 +81,8 @@ HOST_UPDATE_HELP_MSG = ("A fully qualified host string is of the format "
HOST_UPDATE_CURRENT_HOST_HELP = ("Current share host name. %s" % HOST_UPDATE_CURRENT_HOST_HELP = ("Current share host name. %s" %
HOST_UPDATE_HELP_MSG) HOST_UPDATE_HELP_MSG)
HOST_UPDATE_NEW_HOST_HELP = "New share host name. %s" % HOST_UPDATE_HELP_MSG HOST_UPDATE_NEW_HOST_HELP = "New share host name. %s" % HOST_UPDATE_HELP_MSG
LIST_OUTPUT_FORMAT_HELP = ("Format to be used to print the output (table, "
"json, yaml). Defaults to 'table'")
SHARE_SERVERS_UPDATE_HELP = ("List of share servers to be updated, separated " SHARE_SERVERS_UPDATE_HELP = ("List of share servers to be updated, separated "
"by commas.") "by commas.")
SHARE_SERVERS_UPDATE_CAPABILITIES_HELP = ( SHARE_SERVERS_UPDATE_CAPABILITIES_HELP = (
@ -93,6 +98,37 @@ def args(*args, **kwargs):
return _decorator return _decorator
class ListCommand(object):
def list_json(self, resource_name, resource_list):
resource_list = {resource_name: resource_list}
object_list = jsonutils.dumps(resource_list, indent=4)
print(object_list)
def list_yaml(self, resource_name, resource_list):
resource_list = {resource_name: resource_list}
data_yaml = yaml.dump(resource_list)
print(data_yaml)
def list_table(self, resource_name, resource_list):
print_format = "{0:<16} {1:<36} {2:<16} {3:<10} {4:<5} {5:<10}"
print(print_format.format(
*[k.capitalize().replace(
'_', ' ') for k in resource_list[0].keys()]))
for resource in resource_list:
# Print is not transforming into a string, so let's ensure it
# happens
resource['updated_at'] = str(resource['updated_at'])
print(print_format.format(*resource.values()))
def _check_format_output(self, format_output):
if format_output not in ALLOWED_OUTPUT_FORMATS:
print('Invalid output format specified. Defaulting to table.')
return 'table'
else:
return format_output
class ShellCommands(object): class ShellCommands(object):
def bpython(self): def bpython(self):
"""Runs a bpython shell. """Runs a bpython shell.
@ -319,35 +355,35 @@ class GetLogCommands(object):
print("No manila entries in syslog!") print("No manila entries in syslog!")
class ServiceCommands(object): class ServiceCommands(ListCommand):
"""Methods for managing services.""" """Methods for managing services."""
def list(self):
@args('--format_output', required=False, default='table',
help=LIST_OUTPUT_FORMAT_HELP)
def list(self, format_output):
"""Show a list of all manila services.""" """Show a list of all manila services."""
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
services = db.service_get_all(ctxt) services = db.service_get_all(ctxt)
print_format = "%-16s %-36s %-16s %-10s %-5s %-10s" format_output = self._check_format_output(format_output)
print(print_format % (
_('Binary'), services_list = []
_('Host'), for service in services:
_('Zone'), alive = utils.service_is_up(service)
_('Status'), state = ":-)" if alive else "XXX"
_('State'),
_('Updated At'))
)
for svc in services:
alive = utils.service_is_up(svc)
art = ":-)" if alive else "XXX"
status = 'enabled' status = 'enabled'
if svc['disabled']: if service['disabled']:
status = 'disabled' status = 'disabled'
print(print_format % ( services_list.append({
svc['binary'], 'binary': service['binary'],
svc['host'].partition('.')[0], 'host': service['host'].partition('.')[0],
svc['availability_zone']['name'], 'zone': service['availability_zone']['name'],
status, 'status': status,
art, 'state': state,
svc['updated_at'], 'updated_at': str(service['updated_at']),
)) })
method_list_name = f'list_{format_output}'
getattr(self, method_list_name)('services', services_list)
def cleanup(self): def cleanup(self):
"""Remove manila services reporting as 'down'.""" """Remove manila services reporting as 'down'."""

View File

@ -18,9 +18,11 @@ import io
import readline import readline
import sys import sys
from unittest import mock from unittest import mock
import yaml
import ddt import ddt
from oslo_config import cfg from oslo_config import cfg
from oslo_serialization import jsonutils
from manila.cmd import manage as manila_manage from manila.cmd import manage as manila_manage
from manila import context from manila import context
@ -48,6 +50,7 @@ class ManilaCmdManageTestCase(test.TestCase):
self.service_cmds = manila_manage.ServiceCommands() self.service_cmds = manila_manage.ServiceCommands()
self.share_cmds = manila_manage.ShareCommands() self.share_cmds = manila_manage.ShareCommands()
self.server_cmds = manila_manage.ShareServerCommands() self.server_cmds = manila_manage.ShareServerCommands()
self.list_commands = manila_manage.ListCommand()
@mock.patch.object(manila_manage.ShellCommands, 'run', mock.Mock()) @mock.patch.object(manila_manage.ShellCommands, 'run', mock.Mock())
def test_shell_commands_bpython(self): def test_shell_commands_bpython(self):
@ -291,7 +294,7 @@ class ManilaCmdManageTestCase(test.TestCase):
'Zone', 'Zone',
'Status', 'Status',
'State', 'State',
'Updated At') 'Updated at')
service_format = format % (service['binary'], service_format = format % (service['binary'],
service['host'].partition('.')[0], service['host'].partition('.')[0],
service['availability_zone']['name'], service['availability_zone']['name'],
@ -299,12 +302,47 @@ class ManilaCmdManageTestCase(test.TestCase):
':-)', ':-)',
service['updated_at']) service['updated_at'])
expected_out = print_format + '\n' + service_format + '\n' expected_out = print_format + '\n' + service_format + '\n'
self.service_cmds.list() self.service_cmds.list(format_output='table')
self.assertEqual(expected_out, fake_out.getvalue()) self.assertEqual(expected_out, fake_out.getvalue())
get_admin_context.assert_called_with() get_admin_context.assert_called_with()
service_get_all.assert_called_with(ctxt) service_get_all.assert_called_with(ctxt)
service_is_up.assert_called_with(service) service_is_up.assert_called_with(service)
@ddt.data('json', 'yaml')
def test_service_commands_list_format(self, format_output):
ctxt = context.RequestContext('fake-user', 'fake-project')
format_method_name = f'list_{format_output}'
mock_list_method = self.mock_object(
self.service_cmds, format_method_name)
get_admin_context = self.mock_object(context, 'get_admin_context')
service_get_all = self.mock_object(db, 'service_get_all')
service_is_up = self.mock_object(utils, 'service_is_up')
get_admin_context.return_value = ctxt
service = {'binary': 'manila-binary',
'host': 'fake-host.fake-domain',
'availability_zone': {'name': 'fake-zone'},
'updated_at': '2014-06-30 11:22:33',
'disabled': False}
services = [service]
service_get_all.return_value = services
service_is_up.return_value = True
with mock.patch('sys.stdout', new=io.StringIO()):
self.service_cmds.list(format_output=format_output)
get_admin_context.assert_called_with()
service_get_all.assert_called_with(ctxt)
service_is_up.assert_called_with(service)
service_format = {
'binary': service['binary'],
'host': service['host'].partition('.')[0],
'zone': service['availability_zone']['name'],
'status': 'enabled',
'state': ':-)',
'updated_at': service['updated_at'],
}
mock_list_method.assert_called_once_with(
'services', [service_format])
@ddt.data(True, False) @ddt.data(True, False)
def test_service_commands_cleanup(self, service_is_up): def test_service_commands_cleanup(self, service_is_up):
ctxt = context.RequestContext('fake-user', 'fake-project') ctxt = context.RequestContext('fake-user', 'fake-project')
@ -508,3 +546,47 @@ class ManilaCmdManageTestCase(test.TestCase):
True) True)
self.assertEqual(1, exit.code) self.assertEqual(1, exit.code)
@mock.patch('builtins.print')
def test_list_commands_json(self, mock_print):
resource_name = 'service'
service_format = [{
'binary': 'manila-binary',
'host': 'fake-host',
'availability_zone': 'fakeaz',
'status': 'enabled',
'state': ':-)',
'updated_at': '13 04:57:49 PM -03 2023'
}]
mock_json_dumps = self.mock_object(
jsonutils, 'dumps', mock.Mock(return_value=service_format[0]))
services = {resource_name: service_format}
self.list_commands.list_json('service', service_format)
mock_json_dumps.assert_called_once_with(
services, indent=4)
mock_print.assert_called_once_with(service_format[0])
@mock.patch('builtins.print')
def test_list_commands_yaml(self, mock_print):
resource_name = 'service'
service_format = [{
'binary': 'manila-binary',
'host': 'fake-host',
'availability_zone': 'fakeaz',
'status': 'enabled',
'state': ':-)',
'updated_at': '13 04:57:49 PM -03 2023'
}]
mock_yaml_dump = self.mock_object(
yaml, 'dump', mock.Mock(return_value=service_format[0]))
services = {resource_name: service_format}
self.list_commands.list_yaml('service', service_format)
mock_yaml_dump.assert_called_once_with(
services)
mock_print.assert_called_once_with(service_format[0])

View File

@ -0,0 +1,6 @@
---
features:
- |
The possibility to specify the desired output format while issuing the
`manila-manage service list` command was added. It is now possible to
display the output also in yaml and json formats.