[Compute]Make column content readable for both human and machine

Currently, we use utils.format_dict(), utils.format_list(),
utils.format_list_of_dicts to make column value can be easy to read by
human, but osc support to format the CLI output into several format,
like: json, shell, csv, yaml, most of these should be understand by
program and code, so keeping the column content as the original value
make sense, like {u'name': u'RuiChen'} than name='RuiChen'

The patch include all compute commands.

Change-Id: I313a52f94895625e6045df870320840fee157759
Implements: blueprint osc-formattable-columns
Partial-Bug: #1538015
Partial-Bug: #1538006
This commit is contained in:
Rui Chen
2017-03-10 17:32:44 +08:00
parent 5309bf5f87
commit ff9bd34b3c
15 changed files with 335 additions and 175 deletions

@ -18,6 +18,7 @@
import logging import logging
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
@ -324,7 +325,9 @@ class ShowAggregate(command.ShowOne):
# 'metadata' --> 'properties' # 'metadata' --> 'properties'
data._info.update( data._info.update(
{ {
'properties': utils.format_dict(data._info.pop('metadata')), 'properties': format_columns.DictColumn(
data._info.pop('metadata')
),
}, },
) )

@ -17,6 +17,7 @@
import logging import logging
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
@ -180,7 +181,14 @@ class CreateFlavor(command.ShowOne):
flavor_info = flavor._info.copy() flavor_info = flavor._info.copy()
flavor_info.pop("links") flavor_info.pop("links")
flavor_info['properties'] = utils.format_dict(flavor.get_keys()) flavor_info['properties'] = format_columns.DictColumn(
# NOTE(RuiChen): novaclient flavor.get_keys() return a mixin class
# DictWithMeta, that can't be represented properly
# in yaml format(-f yaml), wrapping it in base type
# dict is a workaround, please do not remove the
# conversion.
dict(flavor.get_keys())
)
return zip(*sorted(six.iteritems(flavor_info))) return zip(*sorted(six.iteritems(flavor_info)))
@ -291,13 +299,19 @@ class ListFlavor(command.Lister):
"Properties", "Properties",
) )
for f in data: for f in data:
f.properties = f.get_keys() # NOTE(RuiChen): novaclient flavor.get_keys() return a mixin
# class DictWithMeta, that can't be represented
# properly in yaml format(-f yaml), wrapping it
# in base type dict is a workaround, please do
# not remove the conversion.
f.properties = dict(f.get_keys())
column_headers = columns column_headers = columns
return (column_headers, return (column_headers,
(utils.get_item_properties( (utils.get_item_properties(
s, columns, formatters={'Properties': utils.format_dict}, s, columns,
formatters={'Properties': format_columns.DictColumn},
) for s in data)) ) for s in data))
@ -405,9 +419,7 @@ class ShowFlavor(command.ShowOne):
flavor=resource_flavor.id) flavor=resource_flavor.id)
projects = [utils.get_field(access, 'tenant_id') projects = [utils.get_field(access, 'tenant_id')
for access in flavor_access] for access in flavor_access]
# TODO(Huanxuan Ao): This format case can be removed after access_projects = format_columns.ListColumn(projects)
# patch https://review.openstack.org/#/c/330223/ merged.
access_projects = utils.format_list(projects)
except Exception as e: except Exception as e:
msg = _("Failed to get access projects list " msg = _("Failed to get access projects list "
"for flavor '%(flavor)s': %(e)s") "for flavor '%(flavor)s': %(e)s")
@ -419,7 +431,14 @@ class ShowFlavor(command.ShowOne):
}) })
flavor.pop("links", None) flavor.pop("links", None)
flavor['properties'] = utils.format_dict(resource_flavor.get_keys()) flavor['properties'] = format_columns.DictColumn(
# NOTE(RuiChen): novaclient flavor.get_keys() return a mixin class
# DictWithMeta, that can't be represented properly
# in yaml format(-f yaml), wrapping it in base type
# dict is a workaround, please do not remove the
# conversion.
dict(resource_flavor.get_keys())
)
return zip(*sorted(six.iteritems(flavor))) return zip(*sorted(six.iteritems(flavor)))

@ -23,6 +23,7 @@ import os
import sys import sys
from novaclient.v2 import servers from novaclient.v2 import servers
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
@ -37,22 +38,6 @@ from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _format_servers_list_networks(networks):
"""Return a formatted string of a server's networks
:param networks: a Server.networks field
:rtype: a string of formatted network addresses
"""
output = []
for (network, addresses) in networks.items():
if not addresses:
continue
addresses_csv = ', '.join(addresses)
group = "%s=%s" % (network, addresses_csv)
output.append(group)
return '; '.join(output)
def _format_servers_list_power_state(state): def _format_servers_list_power_state(state):
"""Return a formatted string of a server's power state """Return a formatted string of a server's power state
@ -154,24 +139,26 @@ def _prep_server_detail(compute_client, image_client, server):
if 'os-extended-volumes:volumes_attached' in info: if 'os-extended-volumes:volumes_attached' in info:
info.update( info.update(
{ {
'volumes_attached': utils.format_list_of_dicts( 'volumes_attached': format_columns.ListDictColumn(
info.pop('os-extended-volumes:volumes_attached')) info.pop('os-extended-volumes:volumes_attached')
)
} }
) )
if 'security_groups' in info: if 'security_groups' in info:
info.update( info.update(
{ {
'security_groups': utils.format_list_of_dicts( 'security_groups': format_columns.ListDictColumn(
info.pop('security_groups')) info.pop('security_groups')
)
} }
) )
# NOTE(dtroyer): novaclient splits these into separate entries... # NOTE(dtroyer): novaclient splits these into separate entries...
# Format addresses in a useful way # Format addresses in a useful way
info['addresses'] = _format_servers_list_networks(server.networks) info['addresses'] = format_columns.DictListColumn(server.networks)
# Map 'metadata' field to 'properties' # Map 'metadata' field to 'properties'
info.update( info.update(
{'properties': utils.format_dict(info.pop('metadata'))} {'properties': format_columns.DictColumn(info.pop('metadata'))}
) )
# Migrate tenant_id to project_id naming # Migrate tenant_id to project_id naming
@ -1161,8 +1148,8 @@ class ListServer(command.Lister):
formatters={ formatters={
'OS-EXT-STS:power_state': 'OS-EXT-STS:power_state':
_format_servers_list_power_state, _format_servers_list_power_state,
'Networks': _format_servers_list_networks, 'Networks': format_columns.DictListColumn,
'Metadata': utils.format_dict, 'Metadata': format_columns.DictColumn,
}, },
) for s in data)) ) for s in data))
return table return table

@ -17,6 +17,7 @@
import sys import sys
from osc_lib.cli import format_columns
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@ -122,7 +123,9 @@ class CreateServerBackup(command.ShowOne):
if self.app.client_manager._api_version['image'] == '1': if self.app.client_manager._api_version['image'] == '1':
info = {} info = {}
info.update(image._info) info.update(image._info)
info['properties'] = utils.format_dict(info.get('properties', {})) info['properties'] = format_columns.DictColumn(
info.get('properties', {})
)
else: else:
# Get the right image module to format the output # Get the right image module to format the output
image_module = importutils.import_module( image_module = importutils.import_module(

@ -17,6 +17,7 @@
import logging import logging
from osc_lib.cli import format_columns
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@ -28,8 +29,8 @@ LOG = logging.getLogger(__name__)
_formatters = { _formatters = {
'policies': utils.format_list, 'policies': format_columns.ListColumn,
'members': utils.format_list, 'members': format_columns.ListColumn,
} }
@ -155,8 +156,8 @@ class ListServerGroup(command.Lister):
(utils.get_item_properties( (utils.get_item_properties(
s, columns, s, columns,
formatters={ formatters={
'Policies': utils.format_list, 'Policies': format_columns.ListColumn,
'Members': utils.format_list, 'Members': format_columns.ListColumn,
} }
) for s in data)) ) for s in data))

@ -18,6 +18,7 @@
import logging import logging
import sys import sys
from osc_lib.cli import format_columns
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@ -101,7 +102,9 @@ class CreateServerImage(command.ShowOne):
if self.app.client_manager._api_version['image'] == '1': if self.app.client_manager._api_version['image'] == '1':
info = {} info = {}
info.update(image._info) info.update(image._info)
info['properties'] = utils.format_dict(info.get('properties', {})) info['properties'] = format_columns.DictColumn(
info.get('properties', {})
)
else: else:
# Get the right image module to format the output # Get the right image module to format the output
image_module = importutils.import_module( image_module = importutils.import_module(

@ -136,14 +136,7 @@ class AggregateTests(base.TestCase):
'internal', 'internal',
cmd_output['availability_zone'] cmd_output['availability_zone']
) )
self.assertIn( self.assertEqual({'c': 'd'}, cmd_output['properties'])
"c='d'",
cmd_output['properties']
)
self.assertNotIn(
"a='b'",
cmd_output['properties']
)
# Test unset # Test unset
raw_output = self.openstack( raw_output = self.openstack(
@ -157,10 +150,7 @@ class AggregateTests(base.TestCase):
'aggregate show -f json ' + 'aggregate show -f json ' +
name2 name2
)) ))
self.assertNotIn( self.assertEqual({}, cmd_output['properties'])
"c='d'",
cmd_output['properties']
)
def test_aggregate_add_and_remove_host(self): def test_aggregate_add_and_remove_host(self):
"""Test aggregate add and remove host""" """Test aggregate add and remove host"""

@ -91,7 +91,7 @@ class FlavorTests(base.TestCase):
"--ram 123 " + "--ram 123 " +
"--private " + "--private " +
"--property a=b2 " + "--property a=b2 " +
"--property b=d2 " + "--property c=d2 " +
name2 name2
)) ))
self.addCleanup(self.openstack, "flavor delete " + name2) self.addCleanup(self.openstack, "flavor delete " + name2)
@ -116,10 +116,7 @@ class FlavorTests(base.TestCase):
False, False,
cmd_output["os-flavor-access:is_public"], cmd_output["os-flavor-access:is_public"],
) )
self.assertEqual( self.assertEqual({'a': 'b2', 'c': 'd2'}, cmd_output["properties"])
"a='b2', b='d2'",
cmd_output["properties"],
)
# Test list # Test list
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
@ -135,11 +132,11 @@ class FlavorTests(base.TestCase):
"--long" "--long"
)) ))
col_name = [x["Name"] for x in cmd_output] col_name = [x["Name"] for x in cmd_output]
col_properties = [x['Properties'] for x in cmd_output]
self.assertIn(name1, col_name) self.assertIn(name1, col_name)
self.assertIn("a='b', c='d'", col_properties)
self.assertNotIn(name2, col_name) self.assertNotIn(name2, col_name)
self.assertNotIn("b2', b='d2'", col_properties)
props = [x['Properties'] for x in cmd_output]
self.assertIn({'a': 'b', 'c': 'd'}, props)
# Test list --public # Test list --public
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
@ -203,10 +200,8 @@ class FlavorTests(base.TestCase):
False, False,
cmd_output["os-flavor-access:is_public"], cmd_output["os-flavor-access:is_public"],
) )
self.assertEqual( self.assertEqual({'a': 'first', 'b': 'second'},
"a='first', b='second'", cmd_output['properties'])
cmd_output["properties"],
)
raw_output = self.openstack( raw_output = self.openstack(
"flavor set " + "flavor set " +
@ -224,10 +219,8 @@ class FlavorTests(base.TestCase):
"qaz", "qaz",
cmd_output["id"], cmd_output["id"],
) )
self.assertEqual( self.assertEqual({'a': 'third and 10', 'b': 'second', 'g': 'fourth'},
"a='third and 10', b='second', g='fourth'", cmd_output['properties'])
cmd_output['properties'],
)
raw_output = self.openstack( raw_output = self.openstack(
"flavor unset " + "flavor unset " +
@ -240,7 +233,5 @@ class FlavorTests(base.TestCase):
"flavor show -f json " + "flavor show -f json " +
name1 name1
)) ))
self.assertEqual( self.assertEqual({'a': 'third and 10', 'g': 'fourth'},
"a='third and 10', g='fourth'", cmd_output['properties'])
cmd_output["properties"],
)

@ -107,25 +107,19 @@ class ServerTests(common.ComputeTestCase):
'server show -f json ' + 'server show -f json ' +
name name
)) ))
# Really, shouldn't this be a list? self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
self.assertEqual(
"a='b', c='d'",
cmd_output['properties'],
)
raw_output = self.openstack( raw_output = self.openstack(
'server unset ' + 'server unset ' +
'--property a ' + '--property a ' +
name name
) )
self.assertOutput('', raw_output)
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
'server show -f json ' + 'server show -f json ' +
name name
)) ))
self.assertEqual( self.assertEqual({'c': 'd'}, cmd_output['properties'])
"c='d'",
cmd_output['properties'],
)
# Test set --name # Test set --name
new_name = uuid.uuid4().hex new_name = uuid.uuid4().hex
@ -248,10 +242,8 @@ class ServerTests(common.ComputeTestCase):
'server show -f json ' + 'server show -f json ' +
name name
)) ))
self.assertIn( self.assertIsInstance(cmd_output['addresses'], dict)
floating_ip, self.assertIn(floating_ip, cmd_output['addresses']['private'])
cmd_output['addresses'],
)
# detach ip # detach ip
raw_output = self.openstack( raw_output = self.openstack(
@ -265,10 +257,8 @@ class ServerTests(common.ComputeTestCase):
'server show -f json ' + 'server show -f json ' +
name name
)) ))
self.assertNotIn( self.assertIsInstance(cmd_output['addresses'], dict)
floating_ip, self.assertNotIn(floating_ip, cmd_output['addresses']['private'])
cmd_output['addresses'],
)
def test_server_reboot(self): def test_server_reboot(self):
"""Test server reboot""" """Test server reboot"""
@ -456,8 +446,10 @@ class ServerTests(common.ComputeTestCase):
server_name server_name
)) ))
volumes_attached = cmd_output['volumes_attached'] volumes_attached = cmd_output['volumes_attached']
self.assertTrue(volumes_attached.startswith('id=')) self.assertIsInstance(volumes_attached, list)
attached_volume_id = volumes_attached.replace('id=', '') self.assertEqual(1, len(volumes_attached))
self.assertIn('id', volumes_attached[0])
attached_volume_id = volumes_attached[0]['id']
# check the volume that attached on server # check the volume that attached on server
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
@ -514,7 +506,7 @@ class ServerTests(common.ComputeTestCase):
'server show -f json ' + server_name 'server show -f json ' + server_name
)) ))
self.assertIsNotNone(server['addresses']) self.assertIsNotNone(server['addresses'])
self.assertEqual('', server['addresses']) self.assertEqual({}, server['addresses'])
def test_server_create_with_security_group(self): def test_server_create_with_security_group(self):
"""Test server create with security group ID and name""" """Test server create with security group ID and name"""
@ -553,14 +545,27 @@ class ServerTests(common.ComputeTestCase):
self.assertIsNotNone(server['id']) self.assertIsNotNone(server['id'])
self.assertEqual(server_name, server['name']) self.assertEqual(server_name, server['name'])
self.assertIn(str(security_group1['id']), server['security_groups']) self.assertIsInstance(server['security_groups'], list)
self.assertIn(str(security_group2['id']), server['security_groups']) self.assertEqual(2, len(server['security_groups']))
# NOTE(RuiChen): Nova return security group id in response of server
# create API, but return security group name in server
# show API for the same server, so we assert id and name
# for different create and show commands in the
# following code.
sg_ids = [each_sg['name'] for each_sg in server['security_groups']]
# Security group id is integer in nova-network, convert to string
self.assertIn(str(security_group1['id']), sg_ids)
self.assertIn(str(security_group2['id']), sg_ids)
self.wait_for_status(server_name, 'ACTIVE') self.wait_for_status(server_name, 'ACTIVE')
server = json.loads(self.openstack( server = json.loads(self.openstack(
'server show -f json ' + server_name 'server show -f json ' + server_name
)) ))
self.assertIn(sg_name1, server['security_groups']) self.assertIsInstance(server['security_groups'], list)
self.assertIn(sg_name2, server['security_groups']) self.assertEqual(2, len(server['security_groups']))
sg_names = [each_sg['name'] for each_sg in server['security_groups']]
self.assertIn(sg_name1, sg_names)
self.assertIn(sg_name2, sg_names)
def test_server_create_with_empty_network_option_latest(self): def test_server_create_with_empty_network_option_latest(self):
"""Test server create with empty network option in nova 2.latest.""" """Test server create with empty network option in nova 2.latest."""

@ -32,10 +32,7 @@ class ServerGroupTests(base.TestCase):
name1, name1,
cmd_output['name'] cmd_output['name']
) )
self.assertEqual( self.assertEqual(['affinity'], cmd_output['policies'])
'affinity',
cmd_output['policies']
)
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
'server group create -f json ' + 'server group create -f json ' +
@ -46,10 +43,7 @@ class ServerGroupTests(base.TestCase):
name2, name2,
cmd_output['name'] cmd_output['name']
) )
self.assertEqual( self.assertEqual(['anti-affinity'], cmd_output['policies'])
'anti-affinity',
cmd_output['policies']
)
del_output = self.openstack( del_output = self.openstack(
'server group delete ' + name1 + ' ' + name2) 'server group delete ' + name1 + ' ' + name2)
@ -73,10 +67,7 @@ class ServerGroupTests(base.TestCase):
name1, name1,
cmd_output['name'] cmd_output['name']
) )
self.assertEqual( self.assertEqual(['affinity'], cmd_output['policies'])
'affinity',
cmd_output['policies']
)
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
'server group create -f json ' + 'server group create -f json ' +
@ -90,10 +81,7 @@ class ServerGroupTests(base.TestCase):
name2, name2,
cmd_output['name'] cmd_output['name']
) )
self.assertEqual( self.assertEqual(['anti-affinity'], cmd_output['policies'])
'anti-affinity',
cmd_output['policies']
)
# test server group list # test server group list
cmd_output = json.loads(self.openstack( cmd_output = json.loads(self.openstack(
@ -102,5 +90,5 @@ class ServerGroupTests(base.TestCase):
self.assertIn(name1, names) self.assertIn(name1, names)
self.assertIn(name2, names) self.assertIn(name2, names)
policies = [x["Policies"] for x in cmd_output] policies = [x["Policies"] for x in cmd_output]
self.assertIn('affinity', policies) self.assertIn(['affinity'], policies)
self.assertIn('anti-affinity', policies) self.assertIn(['anti-affinity'], policies)

@ -16,6 +16,7 @@
import mock import mock
from mock import call from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@ -443,7 +444,7 @@ class TestAggregateShow(TestAggregate):
TestAggregate.fake_ag.hosts, TestAggregate.fake_ag.hosts,
TestAggregate.fake_ag.id, TestAggregate.fake_ag.id,
TestAggregate.fake_ag.name, TestAggregate.fake_ag.name,
utils.format_dict( format_columns.DictColumn(
{key: value {key: value
for key, value in TestAggregate.fake_ag.metadata.items() for key, value in TestAggregate.fake_ag.metadata.items()
if key != 'availability_zone'}), if key != 'availability_zone'}),
@ -467,7 +468,7 @@ class TestAggregateShow(TestAggregate):
self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
class TestAggregateUnset(TestAggregate): class TestAggregateUnset(TestAggregate):

@ -16,8 +16,8 @@
import mock import mock
from mock import call from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils
from openstackclient.compute.v2 import flavor from openstackclient.compute.v2 import flavor
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@ -67,7 +67,7 @@ class TestFlavorCreate(TestFlavor):
flavor.id, flavor.id,
flavor.name, flavor.name,
flavor.is_public, flavor.is_public,
utils.format_dict(flavor.properties), format_columns.DictColumn(flavor.properties),
flavor.ram, flavor.ram,
flavor.rxtx_factor, flavor.rxtx_factor,
flavor.swap, flavor.swap,
@ -107,7 +107,7 @@ class TestFlavorCreate(TestFlavor):
self.flavors_mock.create.assert_called_once_with(*default_args) self.flavors_mock.create.assert_called_once_with(*default_args)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
def test_flavor_create_all_options(self): def test_flavor_create_all_options(self):
@ -154,7 +154,7 @@ class TestFlavorCreate(TestFlavor):
self.flavor.get_keys.assert_called_once_with() self.flavor.get_keys.assert_called_once_with()
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
def test_flavor_create_other_options(self): def test_flavor_create_other_options(self):
@ -208,7 +208,7 @@ class TestFlavorCreate(TestFlavor):
{'key1': 'value1', 'key2': 'value2'}) {'key1': 'value1', 'key2': 'value2'})
self.flavor.get_keys.assert_called_with() self.flavor.get_keys.assert_called_with()
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
def test_public_flavor_create_with_project(self): def test_public_flavor_create_with_project(self):
arglist = [ arglist = [
@ -338,7 +338,7 @@ class TestFlavorList(TestFlavor):
data_long = (data[0] + ( data_long = (data[0] + (
flavors[0].swap, flavors[0].swap,
flavors[0].rxtx_factor, flavors[0].rxtx_factor,
u'property=\'value\'' format_columns.DictColumn(flavors[0].properties),
), ) ), )
def setUp(self): def setUp(self):
@ -376,7 +376,7 @@ class TestFlavorList(TestFlavor):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_flavor_list_all_flavors(self): def test_flavor_list_all_flavors(self):
arglist = [ arglist = [
@ -405,7 +405,7 @@ class TestFlavorList(TestFlavor):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_flavor_list_private_flavors(self): def test_flavor_list_private_flavors(self):
arglist = [ arglist = [
@ -434,7 +434,7 @@ class TestFlavorList(TestFlavor):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_flavor_list_public_flavors(self): def test_flavor_list_public_flavors(self):
arglist = [ arglist = [
@ -463,7 +463,7 @@ class TestFlavorList(TestFlavor):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_flavor_list_long(self): def test_flavor_list_long(self):
arglist = [ arglist = [
@ -492,7 +492,7 @@ class TestFlavorList(TestFlavor):
) )
self.assertEqual(self.columns_long, columns) self.assertEqual(self.columns_long, columns)
self.assertEqual(tuple(self.data_long), tuple(data)) self.assertListItemEqual(self.data_long, list(data))
class TestFlavorSet(TestFlavor): class TestFlavorSet(TestFlavor):
@ -652,7 +652,7 @@ class TestFlavorShow(TestFlavor):
flavor.id, flavor.id,
flavor.name, flavor.name,
flavor.is_public, flavor.is_public,
utils.format_dict(flavor.get_keys()), format_columns.DictColumn(flavor.get_keys()),
flavor.ram, flavor.ram,
flavor.rxtx_factor, flavor.rxtx_factor,
flavor.swap, flavor.swap,
@ -689,7 +689,7 @@ class TestFlavorShow(TestFlavor):
columns, data = self.cmd.take_action(parsed_args) columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
def test_private_flavor_show(self): def test_private_flavor_show(self):
private_flavor = compute_fakes.FakeFlavor.create_one_flavor( private_flavor = compute_fakes.FakeFlavor.create_one_flavor(
@ -709,12 +709,12 @@ class TestFlavorShow(TestFlavor):
data_with_project = ( data_with_project = (
private_flavor.disabled, private_flavor.disabled,
private_flavor.ephemeral, private_flavor.ephemeral,
self.flavor_access.tenant_id, format_columns.ListColumn([self.flavor_access.tenant_id]),
private_flavor.disk, private_flavor.disk,
private_flavor.id, private_flavor.id,
private_flavor.name, private_flavor.name,
private_flavor.is_public, private_flavor.is_public,
utils.format_dict(private_flavor.get_keys()), format_columns.DictColumn(private_flavor.get_keys()),
private_flavor.ram, private_flavor.ram,
private_flavor.rxtx_factor, private_flavor.rxtx_factor,
private_flavor.swap, private_flavor.swap,
@ -728,7 +728,7 @@ class TestFlavorShow(TestFlavor):
self.flavor_access_mock.list.assert_called_with( self.flavor_access_mock.list.assert_called_with(
flavor=private_flavor.id) flavor=private_flavor.id)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(data_with_project, data) self.assertItemEqual(data_with_project, data)
class TestFlavorUnset(TestFlavor): class TestFlavorUnset(TestFlavor):

@ -18,6 +18,7 @@ import getpass
import mock import mock
from mock import call from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils as common_utils from osc_lib import utils as common_utils
from oslo_utils import timeutils from oslo_utils import timeutils
@ -298,14 +299,15 @@ class TestServerCreate(TestServer):
def datalist(self): def datalist(self):
datalist = ( datalist = (
server._format_servers_list_power_state( server._format_servers_list_power_state(
getattr(self.new_server, 'OS-EXT-STS:power_state')), getattr(self.new_server, 'OS-EXT-STS:power_state')
'', ),
format_columns.DictListColumn(self.new_server.networks),
self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')', self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')',
self.new_server.id, self.new_server.id,
self.image.name + ' (' + self.new_server.image.get('id') + ')', self.image.name + ' (' + self.new_server.image.get('id') + ')',
self.new_server.name, self.new_server.name,
self.new_server.networks, self.new_server.networks,
'', format_columns.DictColumn(''),
) )
return datalist return datalist
@ -313,7 +315,10 @@ class TestServerCreate(TestServer):
super(TestServerCreate, self).setUp() super(TestServerCreate, self).setUp()
attrs = { attrs = {
'networks': {}, 'networks': {
'private': ['fdb4:4f0f:960b:0:f816:3eff:fed9:af5e',
'10.0.0.8']
},
} }
self.new_server = compute_fakes.FakeServer.create_one_server( self.new_server = compute_fakes.FakeServer.create_one_server(
attrs=attrs) attrs=attrs)
@ -394,7 +399,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
self.assertFalse(self.images_mock.called) self.assertFalse(self.images_mock.called)
self.assertFalse(self.flavors_mock.called) self.assertFalse(self.flavors_mock.called)
@ -459,7 +464,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_not_exist_security_group(self): def test_server_create_with_not_exist_security_group(self):
arglist = [ arglist = [
@ -545,7 +550,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_network(self): def test_server_create_with_network(self):
arglist = [ arglist = [
@ -650,7 +655,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_auto_network(self): def test_server_create_with_auto_network(self):
arglist = [ arglist = [
@ -695,7 +700,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_none_network(self): def test_server_create_with_none_network(self):
arglist = [ arglist = [
@ -740,7 +745,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_conflict_network_options(self): def test_server_create_with_conflict_network_options(self):
arglist = [ arglist = [
@ -903,7 +908,7 @@ class TestServerCreate(TestServer):
**kwargs **kwargs
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False) @mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_create_with_wait_fails(self, mock_wait_for_status): def test_server_create_with_wait_fails(self, mock_wait_for_status):
@ -1010,7 +1015,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping(self): def test_server_create_with_block_device_mapping(self):
arglist = [ arglist = [
@ -1062,7 +1067,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_min_input(self): def test_server_create_with_block_device_mapping_min_input(self):
arglist = [ arglist = [
@ -1113,7 +1118,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_default_input(self): def test_server_create_with_block_device_mapping_default_input(self):
arglist = [ arglist = [
@ -1164,7 +1169,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_full_input(self): def test_server_create_with_block_device_mapping_full_input(self):
arglist = [ arglist = [
@ -1219,7 +1224,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_snapshot(self): def test_server_create_with_block_device_mapping_snapshot(self):
arglist = [ arglist = [
@ -1274,7 +1279,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_multiple(self): def test_server_create_with_block_device_mapping_multiple(self):
arglist = [ arglist = [
@ -1337,7 +1342,7 @@ class TestServerCreate(TestServer):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data) self.assertItemEqual(self.datalist(), data)
def test_server_create_with_block_device_mapping_invalid_format(self): def test_server_create_with_block_device_mapping_invalid_format(self):
# 1. block device mapping don't contain equal sign "=" # 1. block device mapping don't contain equal sign "="
@ -1597,7 +1602,7 @@ class TestServerList(TestServer):
s.id, s.id,
s.name, s.name,
s.status, s.status,
server._format_servers_list_networks(s.networks), format_columns.DictListColumn(s.networks),
self.image.name, self.image.name,
self.flavor.name, self.flavor.name,
)) ))
@ -1609,20 +1614,20 @@ class TestServerList(TestServer):
server._format_servers_list_power_state( server._format_servers_list_power_state(
getattr(s, 'OS-EXT-STS:power_state') getattr(s, 'OS-EXT-STS:power_state')
), ),
server._format_servers_list_networks(s.networks), format_columns.DictListColumn(s.networks),
self.image.name, self.image.name,
s.image['id'], s.image['id'],
self.flavor.name, self.flavor.name,
s.flavor['id'], s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'), getattr(s, 'OS-EXT-AZ:availability_zone'),
getattr(s, 'OS-EXT-SRV-ATTR:host'), getattr(s, 'OS-EXT-SRV-ATTR:host'),
s.Metadata, format_columns.DictColumn(s.Metadata),
)) ))
self.data_no_name_lookup.append(( self.data_no_name_lookup.append((
s.id, s.id,
s.name, s.name,
s.status, s.status,
server._format_servers_list_networks(s.networks), format_columns.DictListColumn(s.networks),
s.image['id'], s.image['id'],
s.flavor['id'] s.flavor['id']
)) ))
@ -1640,7 +1645,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_server_list_long_option(self): def test_server_list_long_option(self):
arglist = [ arglist = [
@ -1656,7 +1661,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns_long, columns) self.assertEqual(self.columns_long, columns)
self.assertEqual(tuple(self.data_long), tuple(data)) self.assertListItemEqual(self.data_long, list(data))
def test_server_list_no_name_lookup_option(self): def test_server_list_no_name_lookup_option(self):
arglist = [ arglist = [
@ -1672,7 +1677,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data_no_name_lookup), tuple(data)) self.assertListItemEqual(self.data_no_name_lookup, list(data))
def test_server_list_n_option(self): def test_server_list_n_option(self):
arglist = [ arglist = [
@ -1688,7 +1693,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data_no_name_lookup), tuple(data)) self.assertListItemEqual(self.data_no_name_lookup, list(data))
def test_server_list_with_image(self): def test_server_list_with_image(self):
@ -1708,7 +1713,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_server_list_with_flavor(self): def test_server_list_with_flavor(self):
@ -1728,7 +1733,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
def test_server_list_with_changes_since(self): def test_server_list_with_changes_since(self):
@ -1749,7 +1754,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertListItemEqual(self.data, list(data))
@mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError) @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): def test_server_list_with_invalid_changes_since(self, mock_parse_isotime):
@ -2700,14 +2705,16 @@ class TestServerShow(TestServer):
self.data = ( self.data = (
'Running', 'Running',
'public=10.20.30.40, 2001:db8::f', format_columns.DictListColumn(
{'public': ['10.20.30.40', '2001:db8::f']}
),
self.flavor.name + " (" + self.flavor.id + ")", self.flavor.name + " (" + self.flavor.id + ")",
self.server.id, self.server.id,
self.image.name + " (" + self.image.id + ")", self.image.name + " (" + self.image.id + ")",
self.server.name, self.server.name,
{'public': ['10.20.30.40', '2001:db8::f']}, {'public': ['10.20.30.40', '2001:db8::f']},
'tenant-id-xxx', 'tenant-id-xxx',
'', format_columns.DictColumn(''),
) )
def test_show_no_options(self): def test_show_no_options(self):
@ -2730,7 +2737,7 @@ class TestServerShow(TestServer):
columns, data = self.cmd.take_action(parsed_args) columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
def test_show_diagnostics(self): def test_show_diagnostics(self):
arglist = [ arglist = [
@ -2989,13 +2996,13 @@ class TestServerGeneral(TestServer):
# Prepare expected data. # Prepare expected data.
# Since networks is a dict, whose items are in random order, there # Since networks is a dict, whose items are in random order, there
# could be two results after formatted. # could be two results after formatted.
data_1 = (u'private=2001:db8::f, 10.20.30.40; ' data_1 = (u'private=10.20.30.40, 2001:db8::f; '
u'public=10.20.30.40, 2001:db8::f') u'public=10.20.30.40, 2001:db8::f')
data_2 = (u'public=10.20.30.40, 2001:db8::f; ' data_2 = (u'public=10.20.30.40, 2001:db8::f; '
u'private=2001:db8::f, 10.20.30.40') u'private=10.20.30.40, 2001:db8::f')
# Call _format_servers_list_networks(). format_col = format_columns.DictListColumn(networks)
networks_format = server._format_servers_list_networks(networks) networks_format = format_col.human_readable()
msg = ('Network string is not formatted correctly.\n' msg = ('Network string is not formatted correctly.\n'
'reference = %s or %s\n' 'reference = %s or %s\n'
@ -3044,5 +3051,17 @@ class TestServerGeneral(TestServer):
# 'networks' is used to create _server. Remove it. # 'networks' is used to create _server. Remove it.
server_detail.pop('networks') server_detail.pop('networks')
# Special handle for 'properties', it's DictColumn type
prop = server_detail.pop('properties')
expected_prop = info.pop('properties')
self.assertIsInstance(prop, format_columns.DictColumn)
self.assertEqual(expected_prop, prop.human_readable())
# Special handle for 'addresses', it's DictListColumn type
prop = server_detail.pop('addresses')
expected_prop = info.pop('addresses')
self.assertIsInstance(prop, format_columns.DictListColumn)
self.assertEqual(expected_prop, prop.human_readable())
# Check the results. # Check the results.
self.assertEqual(info, server_detail) self.assertEqual(info, server_detail)

@ -15,6 +15,7 @@
import mock import mock
from osc_lib.cli import format_columns
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@ -38,9 +39,9 @@ class TestServerGroup(compute_fakes.TestComputev2):
data = ( data = (
fake_server_group.id, fake_server_group.id,
utils.format_list(fake_server_group.members), format_columns.ListColumn(fake_server_group.members),
fake_server_group.name, fake_server_group.name,
utils.format_list(fake_server_group.policies), format_columns.ListColumn(fake_server_group.policies),
fake_server_group.project_id, fake_server_group.project_id,
fake_server_group.user_id, fake_server_group.user_id,
) )
@ -78,7 +79,7 @@ class TestServerGroupCreate(TestServerGroup):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)
class TestServerGroupDelete(TestServerGroup): class TestServerGroupDelete(TestServerGroup):
@ -182,14 +183,14 @@ class TestServerGroupList(TestServerGroup):
list_data = (( list_data = ((
TestServerGroup.fake_server_group.id, TestServerGroup.fake_server_group.id,
TestServerGroup.fake_server_group.name, TestServerGroup.fake_server_group.name,
utils.format_list(TestServerGroup.fake_server_group.policies), format_columns.ListColumn(TestServerGroup.fake_server_group.policies),
),) ),)
list_data_long = (( list_data_long = ((
TestServerGroup.fake_server_group.id, TestServerGroup.fake_server_group.id,
TestServerGroup.fake_server_group.name, TestServerGroup.fake_server_group.name,
utils.format_list(TestServerGroup.fake_server_group.policies), format_columns.ListColumn(TestServerGroup.fake_server_group.policies),
utils.format_list(TestServerGroup.fake_server_group.members), format_columns.ListColumn(TestServerGroup.fake_server_group.members),
TestServerGroup.fake_server_group.project_id, TestServerGroup.fake_server_group.project_id,
TestServerGroup.fake_server_group.user_id, TestServerGroup.fake_server_group.user_id,
),) ),)
@ -211,7 +212,7 @@ class TestServerGroupList(TestServerGroup):
self.server_groups_mock.list.assert_called_once_with(False) self.server_groups_mock.list.assert_called_once_with(False)
self.assertEqual(self.list_columns, columns) self.assertEqual(self.list_columns, columns)
self.assertEqual(self.list_data, tuple(data)) self.assertListItemEqual(self.list_data, list(data))
def test_server_group_list_with_all_projects_and_long(self): def test_server_group_list_with_all_projects_and_long(self):
arglist = [ arglist = [
@ -227,7 +228,7 @@ class TestServerGroupList(TestServerGroup):
self.server_groups_mock.list.assert_called_once_with(True) self.server_groups_mock.list.assert_called_once_with(True)
self.assertEqual(self.list_columns_long, columns) self.assertEqual(self.list_columns_long, columns)
self.assertEqual(self.list_data_long, tuple(data)) self.assertListItemEqual(self.list_data_long, list(data))
class TestServerGroupShow(TestServerGroup): class TestServerGroupShow(TestServerGroup):
@ -250,4 +251,4 @@ class TestServerGroupShow(TestServerGroup):
columns, data = self.cmd.take_action(parsed_args) columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertItemEqual(self.data, data)

@ -0,0 +1,149 @@
---
features:
- |
Make console output machine readable. Openstackclient support to format the
console output into several formats, like: json, shell, csv, yaml, table.
Most of these should be understood by program and code, so we should keep
column content as actually complex data structures rather than string, if
users specify the output format as machine readable type, for example:
``json``, ``yaml``, ``cvs`` and so on, that make sense, like:
{'key': 'value'} vs "key='value'"
[Blueprint :oscbp:`osc-formattable-columns`]
The following output format are changed when machine readable format is
specified:
- Compute v2:
- ``properties`` in **aggregate show**
- ``properties`` in **flavor create**
- ``Properties`` in **flavor list**
- ``properties``, ``access_project_ids`` in **flavor show**
- ``volumes_attached``, ``security_groups``, ``addresses``,
``properties`` in **server create**
- ``volumes_attached``, ``security_groups``, ``addresses``,
``properties`` in **server rebuild**
- ``volumes_attached``, ``security_groups``, ``addresses``,
``properties`` in **server show**
- ``Networks``, ``Metadata`` in **server list**
- ``properties`` in **server backup create**
- ``policies``, ``members`` in **server group create**
- ``policies``, ``members`` in **server group show**
- ``Policies``, ``Members`` in **server group list**
- ``properties`` in **server image create**
- Object store v1:
- ``properties`` in **account show**
- ``properties`` in **container show**
- ``properties`` in **object show**
- Volume v1:
- ``Volume ID`` in **volume backup list**
- ``properties`` in **volume qos create**
- ``Specs``, ``Associations`` in **volume qos list**
- ``associations``, ``properties`` in **volume qos show**
- ``properties`` in **snapshot create**
- ``Metadata``, ``Volume ID`` in **snapshot list**
- ``properties`` in **snapshot show**
- ``properties`` in **volume create**
- ``Metadata``, ``Attachments`` in **volume list**
- ``properties`` in **volume show**
- ``properties`` in **volume snapshot create**
- ``Metadata``, ``Volume ID`` in **volume snapshot list**
- ``properties`` in **volume snapshot show**
- ``properties``, ``encryption`` in **volume type create**
- ``Extra Specs``, ``id`` in **volume type list**
- ``properties``, ``encryption`` in **volume type show**
- Volume v2:
- ``Volume ID`` in **volume backup list**
- ``Volume Types`` in **consistency group list**
- ``properties`` in **volume qos create**
- ``Specs``, ``Associations`` in **volume qos list**
- ``associations``, ``properties`` in **volume qos show**
- ``properties`` in **snapshot create**
- ``Metadata``, ``Volume ID`` in **snapshot list**
- ``properties`` in **snapshot show**
- ``properties`` in **volume create**
- ``Metadata``, ``Attachments`` in **volume list**
- ``properties`` in **volume show**
- ``properties`` in **volume snapshot create**
- ``Metadata``, ``Volume ID`` in **volume snapshot list**
- ``properties`` in **volume snapshot show**
- ``properties``, ``encryption`` in **volume type create**
- ``Extra Specs``, ``id`` in **volume type list**
- ``properties``, ``access_project_ids``, ``encryption``
in **volume type show**
- Identity v2.0:
- ``Endpoints`` in **catalog list**
- ``endpoints`` in **catalog show**
- ``properties`` in **project show**
- ``tenantId`` in **user list**
- Identity v3:
- ``Endpoints`` in **catalog list**
- ``endpoints`` in **catalog show**
- ``remote_ids`` in **identity provider create**
- ``remote_ids`` in **identity provider show**
- Image v1:
- ``properties`` in **image create**
- ``Visibility``, ``Properties`` in **image list**
- ``properties`` in **image show**
- Image v2:
- ``tags``, ``properties`` in **image create**
- ``Tags`` in **image list**
- ``tags``, ``properties`` in **image show**
- Network v2:
- ``subnet_ip_availability`` in **ip availability show**
- ``subnets``, ``subnet_ids``, ``admin_state_up``,
``is_admin_state_up``, ``router:external``, ``is_router_external``,
``availability_zones``, ``availability_zone_hints``, ``tags`` in
**network create**
- ``Subnets``, ``State``, ``Router Type``, ``Availability Zones``,
``Tags`` in **network list**
- ``subnets``, ``subnet_ids``, ``admin_state_up``,
``is_admin_state_up``, ``router:external``, ``is_router_external``,
``availability_zones``, ``availability_zone_hints``, ``tags`` in
**network show**
- ``Alive``, ``State`` in **network agent list**
- ``alive``, ``admin_state_up``, ``configurations`` in
**network agent show**
- ``admin_state_up``, ``allowed_address_pairs``, ``binding_profile``,
``binding_vif_details``, ``dns_assignment``, ``extra_dhcp_opts``,
``fixed_ips``, ``security_group_ids``, ``tags`` in **port create**
- ``Fixed IP Addresses``, ``Status``, ``Security Groups``, ``Tags``
in **port list**
- ``admin_state_up``, ``allowed_address_pairs``, ``binding_profile``,
``binding_vif_details``, ``dns_assignment``, ``extra_dhcp_opts``,
``fixed_ips``, ``security_group_ids``, ``tags`` in **port show**
- ``admin_state_up``, ``external_gateway_info``,
``availability_zones``, ``availability_zone_hints``, ``routes``,
``tags`` in **router create**
- ``State``, ``Routes``, ``External gateway info``, ``Tags``,
``Availability zones`` in **router list**
- ``admin_state_up``, ``external_gateway_info``,
``availability_zones``, ``availability_zone_hints``, ``routes``,
``tags`` in **router show**
- ``security_group_rules`` in **security group create**
- ``security_group_rules`` in **security group show**
- ``allocation_pools``, ``dns_nameservers``, ``host_routes``, ``tags``,
``service_types`` in **subnet create**
- ``Name Servers``, ``Allocation Pools``, ``Host Routes``, ``Tags``,
``Service Types`` in **subnet list**
- ``allocation_pools``, ``dns_nameservers``, ``host_routes``, ``tags``,
``service_types`` in **subnet show**
- ``prefixes``, ``tags`` in **subnet pool create**
- ``Prefixes``, ``Tags`` in **subnet pool list**
- ``prefixes``, ``tags`` in **subnet pool show**