Fix formatting of the flavor properties
Do not stringify flavor properties to allow proper output formatting to json/yaml/etc Change-Id: I9f4c42acb85b726af87123134dd19de98fe95074
This commit is contained in:
parent
987af4e390
commit
ad3369ed1f
@ -5,7 +5,7 @@ asn1crypto==0.23.0
|
|||||||
bandit==1.1.0
|
bandit==1.1.0
|
||||||
cachetools==2.0.0
|
cachetools==2.0.0
|
||||||
cffi==1.14.0
|
cffi==1.14.0
|
||||||
cliff==2.8.0
|
cliff==3.4.0
|
||||||
cmd2==0.8.0
|
cmd2==0.8.0
|
||||||
contextlib2==0.4.0
|
contextlib2==0.4.0
|
||||||
coverage==4.0
|
coverage==4.0
|
||||||
@ -48,7 +48,7 @@ netifaces==0.10.4
|
|||||||
openstacksdk==0.48.0
|
openstacksdk==0.48.0
|
||||||
os-service-types==1.7.0
|
os-service-types==1.7.0
|
||||||
os-testr==1.0.0
|
os-testr==1.0.0
|
||||||
osc-lib==2.0.0
|
osc-lib==2.2.0
|
||||||
osc-placement==1.7.0
|
osc-placement==1.7.0
|
||||||
oslo.concurrency==3.26.0
|
oslo.concurrency==3.26.0
|
||||||
oslo.config==5.2.0
|
oslo.config==5.2.0
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
|
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
|
||||||
@ -30,6 +31,31 @@ from openstackclient.identity import common as identity_common
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
_formatters = {
|
||||||
|
'extra_specs': format_columns.DictColumn,
|
||||||
|
# Unless we finish switch to use SDK resources this need to be doubled this
|
||||||
|
# way
|
||||||
|
'properties': format_columns.DictColumn,
|
||||||
|
'Properties': format_columns.DictColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_flavor_columns(item):
|
||||||
|
# To maintain backwards compatibility we need to rename sdk props to
|
||||||
|
# whatever OSC was using before
|
||||||
|
column_map = {
|
||||||
|
'extra_specs': 'properties',
|
||||||
|
'ephemeral': 'OS-FLV-EXT-DATA:ephemeral',
|
||||||
|
'is_disabled': 'OS-FLV-DISABLED:disabled',
|
||||||
|
'is_public': 'os-flavor-access:is_public'
|
||||||
|
|
||||||
|
}
|
||||||
|
hidden_columns = ['links', 'location']
|
||||||
|
|
||||||
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
|
item, column_map, hidden_columns)
|
||||||
|
|
||||||
|
|
||||||
def _find_flavor(compute_client, flavor):
|
def _find_flavor(compute_client, flavor):
|
||||||
try:
|
try:
|
||||||
return compute_client.flavors.get(flavor)
|
return compute_client.flavors.get(flavor)
|
||||||
@ -191,10 +217,16 @@ class CreateFlavor(command.ShowOne):
|
|||||||
LOG.error(_("Failed to set flavor property: %s"), e)
|
LOG.error(_("Failed to set flavor property: %s"), e)
|
||||||
|
|
||||||
flavor_info = flavor._info.copy()
|
flavor_info = flavor._info.copy()
|
||||||
flavor_info.pop("links")
|
flavor_info['properties'] = flavor.get_keys()
|
||||||
flavor_info['properties'] = utils.format_dict(flavor.get_keys())
|
|
||||||
|
|
||||||
return zip(*sorted(flavor_info.items()))
|
display_columns, columns = _get_flavor_columns(flavor_info)
|
||||||
|
data = utils.get_dict_properties(
|
||||||
|
flavor_info, columns,
|
||||||
|
formatters=_formatters,
|
||||||
|
mixed_case_fields=['OS-FLV-DISABLED:disabled',
|
||||||
|
'OS-FLV-EXT-DATA:ephemeral'])
|
||||||
|
|
||||||
|
return (display_columns, data)
|
||||||
|
|
||||||
|
|
||||||
class DeleteFlavor(command.Command):
|
class DeleteFlavor(command.Command):
|
||||||
@ -309,7 +341,7 @@ class ListFlavor(command.Lister):
|
|||||||
|
|
||||||
return (column_headers,
|
return (column_headers,
|
||||||
(utils.get_item_properties(
|
(utils.get_item_properties(
|
||||||
s, columns, formatters={'Properties': utils.format_dict},
|
s, columns, formatters=_formatters,
|
||||||
) for s in data))
|
) for s in data))
|
||||||
|
|
||||||
|
|
||||||
@ -428,11 +460,8 @@ class ShowFlavor(command.ShowOne):
|
|||||||
try:
|
try:
|
||||||
flavor_access = compute_client.flavor_access.list(
|
flavor_access = compute_client.flavor_access.list(
|
||||||
flavor=resource_flavor.id)
|
flavor=resource_flavor.id)
|
||||||
projects = [utils.get_field(access, 'tenant_id')
|
access_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
|
|
||||||
# patch https://review.opendev.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")
|
||||||
@ -442,11 +471,17 @@ class ShowFlavor(command.ShowOne):
|
|||||||
flavor.update({
|
flavor.update({
|
||||||
'access_project_ids': access_projects
|
'access_project_ids': access_projects
|
||||||
})
|
})
|
||||||
flavor.pop("links", None)
|
|
||||||
|
|
||||||
flavor['properties'] = utils.format_dict(resource_flavor.get_keys())
|
flavor['properties'] = resource_flavor.get_keys()
|
||||||
|
|
||||||
return zip(*sorted(flavor.items()))
|
display_columns, columns = _get_flavor_columns(flavor)
|
||||||
|
data = utils.get_dict_properties(
|
||||||
|
flavor, columns,
|
||||||
|
formatters=_formatters,
|
||||||
|
mixed_case_fields=['OS-FLV-DISABLED:disabled',
|
||||||
|
'OS-FLV-EXT-DATA:ephemeral'])
|
||||||
|
|
||||||
|
return (display_columns, data)
|
||||||
|
|
||||||
|
|
||||||
class UnsetFlavor(command.Command):
|
class UnsetFlavor(command.Command):
|
||||||
|
@ -115,8 +115,8 @@ class FlavorTests(base.TestCase):
|
|||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
cmd_output["os-flavor-access:is_public"],
|
cmd_output["os-flavor-access:is_public"],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertDictEqual(
|
||||||
"a='b2', b='d2'",
|
{"a": "b2", "b": "d2"},
|
||||||
cmd_output["properties"],
|
cmd_output["properties"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -133,12 +133,18 @@ class FlavorTests(base.TestCase):
|
|||||||
"flavor list -f json " +
|
"flavor list -f json " +
|
||||||
"--long"
|
"--long"
|
||||||
))
|
))
|
||||||
col_name = [x["Name"] for x in cmd_output]
|
# We have list of complex json objects
|
||||||
col_properties = [x['Properties'] for x in cmd_output]
|
# Iterate through the list setting flags
|
||||||
self.assertIn(name1, col_name)
|
found_expected = False
|
||||||
self.assertIn("a='b', c='d'", col_properties)
|
for rec in cmd_output:
|
||||||
self.assertNotIn(name2, col_name)
|
if rec['Name'] == name1:
|
||||||
self.assertNotIn("b2', b='d2'", col_properties)
|
found_expected = True
|
||||||
|
self.assertEqual('b', rec['Properties']['a'])
|
||||||
|
self.assertEqual('d', rec['Properties']['c'])
|
||||||
|
elif rec['Name'] == name2:
|
||||||
|
# We should have not seen private flavor
|
||||||
|
self.assertFalse(True)
|
||||||
|
self.assertTrue(found_expected)
|
||||||
|
|
||||||
# Test list --public
|
# Test list --public
|
||||||
cmd_output = json.loads(self.openstack(
|
cmd_output = json.loads(self.openstack(
|
||||||
@ -201,8 +207,8 @@ class FlavorTests(base.TestCase):
|
|||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
cmd_output["os-flavor-access:is_public"],
|
cmd_output["os-flavor-access:is_public"],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertDictEqual(
|
||||||
"a='first', b='second'",
|
{"a": "first", "b": "second"},
|
||||||
cmd_output["properties"],
|
cmd_output["properties"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -223,9 +229,14 @@ class FlavorTests(base.TestCase):
|
|||||||
cmd_output["id"],
|
cmd_output["id"],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"a='third and 10', b='second', g='fourth'",
|
'third and 10',
|
||||||
cmd_output['properties'],
|
cmd_output['properties']['a'])
|
||||||
)
|
self.assertEqual(
|
||||||
|
'second',
|
||||||
|
cmd_output['properties']['b'])
|
||||||
|
self.assertEqual(
|
||||||
|
'fourth',
|
||||||
|
cmd_output['properties']['g'])
|
||||||
|
|
||||||
raw_output = self.openstack(
|
raw_output = self.openstack(
|
||||||
"flavor unset " +
|
"flavor unset " +
|
||||||
@ -238,7 +249,5 @@ class FlavorTests(base.TestCase):
|
|||||||
"flavor show -f json " +
|
"flavor show -f json " +
|
||||||
name1
|
name1
|
||||||
))
|
))
|
||||||
self.assertEqual(
|
|
||||||
"a='third and 10', g='fourth'",
|
self.assertNotIn('b', cmd_output['properties'])
|
||||||
cmd_output["properties"],
|
|
||||||
)
|
|
||||||
|
@ -17,8 +17,8 @@ from unittest import mock
|
|||||||
from unittest.mock import call
|
from unittest.mock import call
|
||||||
|
|
||||||
import novaclient
|
import novaclient
|
||||||
|
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
|
||||||
@ -70,7 +70,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,
|
||||||
@ -111,7 +111,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):
|
||||||
|
|
||||||
@ -165,7 +165,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):
|
||||||
|
|
||||||
@ -226,7 +226,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 = [
|
||||||
@ -300,7 +300,7 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
self.flavors_mock.create.assert_called_once_with(*args)
|
self.flavors_mock.create.assert_called_once_with(*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_with_description_api_older(self):
|
def test_flavor_create_with_description_api_older(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -429,7 +429,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):
|
||||||
@ -583,7 +583,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, tuple(data))
|
||||||
|
|
||||||
|
|
||||||
class TestFlavorSet(TestFlavor):
|
class TestFlavorSet(TestFlavor):
|
||||||
@ -817,7 +817,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,
|
||||||
@ -854,7 +854,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(
|
||||||
@ -874,13 +874,13 @@ 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,
|
[self.flavor_access.tenant_id],
|
||||||
private_flavor.description,
|
private_flavor.description,
|
||||||
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,
|
||||||
@ -894,7 +894,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):
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
Fix '-f json' output of the flavor properties to return valid json object
|
||||||
|
instead of stringying it.
|
@ -3,10 +3,10 @@
|
|||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||||
|
|
||||||
cliff!=2.9.0,>=2.8.0 # Apache-2.0
|
cliff>=3.4.0 # Apache-2.0
|
||||||
iso8601>=0.1.11 # MIT
|
iso8601>=0.1.11 # MIT
|
||||||
openstacksdk>=0.48.0 # Apache-2.0
|
openstacksdk>=0.48.0 # Apache-2.0
|
||||||
osc-lib>=2.0.0 # Apache-2.0
|
osc-lib>=2.2.0 # Apache-2.0
|
||||||
oslo.i18n>=3.15.3 # Apache-2.0
|
oslo.i18n>=3.15.3 # Apache-2.0
|
||||||
python-keystoneclient>=3.22.0 # Apache-2.0
|
python-keystoneclient>=3.22.0 # Apache-2.0
|
||||||
python-novaclient>=15.1.0 # Apache-2.0
|
python-novaclient>=15.1.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user