Improve readability of vnf package show command
Display show command data in human_readable format for better readability for complex attributes of vnf package like 'softwareImages', 'checksum', '_links' and 'useDefinedData'. Please checkout below link to see the difference in vnf package show response data before and after resolving the issue. http://paste.openstack.org/show/792305/ Change-Id: I4827437461fd42b4da8a90d7f8ef703625c78cf5
This commit is contained in:
@@ -17,6 +17,7 @@ from functools import reduce
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from cliff import columns as cliff_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 utils
|
from osc_lib import utils
|
||||||
@@ -29,6 +30,18 @@ from tackerclient.osc import utils as tacker_osc_utils
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FormatComplexDataColumn(cliff_columns.FormattableColumn):
|
||||||
|
|
||||||
|
def human_readable(self):
|
||||||
|
return tacker_osc_utils.format_dict_with_indention(self._value)
|
||||||
|
|
||||||
|
|
||||||
|
formatters = {'softwareImages': FormatComplexDataColumn,
|
||||||
|
'checksum': FormatComplexDataColumn,
|
||||||
|
'_links': FormatComplexDataColumn,
|
||||||
|
'userDefinedData': FormatComplexDataColumn}
|
||||||
|
|
||||||
|
|
||||||
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
|
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
|
||||||
'vnfProductName', 'softwareImages', 'userDefinedData',
|
'vnfProductName', 'softwareImages', 'userDefinedData',
|
||||||
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
|
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
|
||||||
@@ -87,7 +100,8 @@ class CreateVnfPackage(command.ShowOne):
|
|||||||
display_columns, columns = _get_columns(vnf_package)
|
display_columns, columns = _get_columns(vnf_package)
|
||||||
data = utils.get_item_properties(
|
data = utils.get_item_properties(
|
||||||
sdk_utils.DictModel(vnf_package),
|
sdk_utils.DictModel(vnf_package),
|
||||||
columns, mixed_case_fields=_mixed_case_fields)
|
columns, formatters=formatters,
|
||||||
|
mixed_case_fields=_mixed_case_fields)
|
||||||
return (display_columns, data)
|
return (display_columns, data)
|
||||||
|
|
||||||
|
|
||||||
@@ -192,7 +206,8 @@ class ListVnfPackage(command.Lister):
|
|||||||
long_listing=True)
|
long_listing=True)
|
||||||
return (headers,
|
return (headers,
|
||||||
(utils.get_dict_properties(
|
(utils.get_dict_properties(
|
||||||
s, columns, mixed_case_fields=_mixed_case_fields,
|
s, columns, formatters=formatters,
|
||||||
|
mixed_case_fields=_mixed_case_fields,
|
||||||
) for s in data['vnf_packages']))
|
) for s in data['vnf_packages']))
|
||||||
|
|
||||||
|
|
||||||
@@ -215,7 +230,8 @@ class ShowVnfPackage(command.ShowOne):
|
|||||||
display_columns, columns = _get_columns(vnf_package)
|
display_columns, columns = _get_columns(vnf_package)
|
||||||
data = utils.get_item_properties(
|
data = utils.get_item_properties(
|
||||||
sdk_utils.DictModel(vnf_package),
|
sdk_utils.DictModel(vnf_package),
|
||||||
columns, mixed_case_fields=_mixed_case_fields)
|
columns, formatters=formatters,
|
||||||
|
mixed_case_fields=_mixed_case_fields)
|
||||||
return (display_columns, data)
|
return (display_columns, data)
|
||||||
|
|
||||||
|
|
||||||
@@ -452,5 +468,6 @@ class UpdateVnfPackage(command.ShowOne):
|
|||||||
display_columns, columns = self.get_columns(updated_values)
|
display_columns, columns = self.get_columns(updated_values)
|
||||||
data = utils.get_item_properties(
|
data = utils.get_item_properties(
|
||||||
sdk_utils.DictModel(updated_values),
|
sdk_utils.DictModel(updated_values),
|
||||||
columns, mixed_case_fields=_mixed_case_fields)
|
columns, formatters=formatters,
|
||||||
|
mixed_case_fields=_mixed_case_fields)
|
||||||
return (display_columns, data)
|
return (display_columns, data)
|
||||||
|
@@ -17,6 +17,8 @@ import mock
|
|||||||
from requests_mock.contrib import fixture as requests_mock_fixture
|
from requests_mock.contrib import fixture as requests_mock_fixture
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
from cliff import columns as cliff_columns
|
||||||
|
|
||||||
|
|
||||||
class FixturedTestCase(testtools.TestCase):
|
class FixturedTestCase(testtools.TestCase):
|
||||||
client_fixture_class = None
|
client_fixture_class = None
|
||||||
@@ -51,6 +53,20 @@ class FixturedTestCase(testtools.TestCase):
|
|||||||
msg = 'method %s should not have been called' % m
|
msg = 'method %s should not have been called' % m
|
||||||
self.fail(msg)
|
self.fail(msg)
|
||||||
|
|
||||||
|
def assertListItemsEqual(self, expected, actual):
|
||||||
|
"""Assertion based on human_readable values of list items"""
|
||||||
|
|
||||||
|
self.assertEqual(len(expected), len(actual))
|
||||||
|
for col_expected, col_actual in zip(expected, actual):
|
||||||
|
if isinstance(col_actual, tuple):
|
||||||
|
self.assertListItemsEqual(col_expected, col_actual)
|
||||||
|
elif isinstance(col_expected, cliff_columns.FormattableColumn):
|
||||||
|
self.assertIsInstance(col_actual, col_expected.__class__)
|
||||||
|
self.assertEqual(col_expected.human_readable(),
|
||||||
|
col_actual.human_readable())
|
||||||
|
else:
|
||||||
|
self.assertEqual(col_expected, col_actual)
|
||||||
|
|
||||||
|
|
||||||
class ParserException(Exception):
|
class ParserException(Exception):
|
||||||
pass
|
pass
|
||||||
|
@@ -104,8 +104,9 @@ class TestCreateVnfPackage(TestVnfPackage):
|
|||||||
|
|
||||||
columns, data = (self.create_vnf_package.take_action(parsed_args))
|
columns, data = (self.create_vnf_package.take_action(parsed_args))
|
||||||
self.assertCountEqual(_get_columns_vnf_package(), columns)
|
self.assertCountEqual(_get_columns_vnf_package(), columns)
|
||||||
self.assertCountEqual(vnf_package_fakes.get_vnf_package_data(json),
|
headers, attributes = vnf_package._get_columns(json)
|
||||||
data)
|
self.assertListItemsEqual(vnf_package_fakes.get_vnf_package_data(
|
||||||
|
json, columns=attributes), data)
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
@@ -186,7 +187,7 @@ class TestListVnfPackage(TestVnfPackage):
|
|||||||
vnf_package_obj, columns=columns, list_action=True, **kwargs))
|
vnf_package_obj, columns=columns, list_action=True, **kwargs))
|
||||||
|
|
||||||
self.assertCountEqual(self.get_list_columns(**kwargs), actual_columns)
|
self.assertCountEqual(self.get_list_columns(**kwargs), actual_columns)
|
||||||
self.assertCountEqual(expected_data, list(data))
|
self.assertListItemsEqual(expected_data, list(data))
|
||||||
|
|
||||||
def test_take_action_with_exclude_fields(self):
|
def test_take_action_with_exclude_fields(self):
|
||||||
parsed_args = self.check_parser(
|
parsed_args = self.check_parser(
|
||||||
@@ -265,7 +266,7 @@ class TestListVnfPackage(TestVnfPackage):
|
|||||||
self.assertCountEqual(self.get_list_columns(
|
self.assertCountEqual(self.get_list_columns(
|
||||||
extra_fields=['Software Images', 'Checksum']),
|
extra_fields=['Software Images', 'Checksum']),
|
||||||
actual_columns)
|
actual_columns)
|
||||||
self.assertCountEqual(expected_data, list(data))
|
self.assertListItemsEqual(expected_data, list(data))
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
@@ -288,12 +289,13 @@ class TestShowVnfPackage(TestVnfPackage):
|
|||||||
self.requests_mock.register_uri('GET', url, json=vnf_package_obj,
|
self.requests_mock.register_uri('GET', url, json=vnf_package_obj,
|
||||||
headers=self.header)
|
headers=self.header)
|
||||||
columns, data = (self.show_vnf_package.take_action(parsed_args))
|
columns, data = (self.show_vnf_package.take_action(parsed_args))
|
||||||
self.assertEqual(sorted(_get_columns_vnf_package(
|
|
||||||
vnf_package_obj=vnf_package_obj, action='show')), sorted(columns))
|
|
||||||
self.assertCountEqual(_get_columns_vnf_package(
|
self.assertCountEqual(_get_columns_vnf_package(
|
||||||
vnf_package_obj=vnf_package_obj, action='show'), columns)
|
vnf_package_obj=vnf_package_obj, action='show'), columns)
|
||||||
self.assertCountEqual(
|
|
||||||
vnf_package_fakes.get_vnf_package_data(vnf_package_obj), data)
|
headers, attributes = vnf_package._get_columns(vnf_package_obj)
|
||||||
|
self.assertListItemsEqual(
|
||||||
|
vnf_package_fakes.get_vnf_package_data(vnf_package_obj,
|
||||||
|
columns=attributes), data)
|
||||||
|
|
||||||
def test_show_no_options(self):
|
def test_show_no_options(self):
|
||||||
self.assertRaises(base.ParserException, self.check_parser,
|
self.assertRaises(base.ParserException, self.check_parser,
|
||||||
@@ -536,7 +538,7 @@ class TestUpdateVnfPackage(TestVnfPackage):
|
|||||||
columns, data = self.update_vnf_package.take_action(parsed_args)
|
columns, data = self.update_vnf_package.take_action(parsed_args)
|
||||||
self.assertItemsEqual(_get_columns_vnf_package(
|
self.assertItemsEqual(_get_columns_vnf_package(
|
||||||
vnf_package_obj=fake_response, action='update'), columns)
|
vnf_package_obj=fake_response, action='update'), columns)
|
||||||
self.assertItemsEqual(
|
self.assertListItemsEqual(
|
||||||
vnf_package_fakes.get_vnf_package_data(fake_response), data)
|
vnf_package_fakes.get_vnf_package_data(fake_response), data)
|
||||||
|
|
||||||
def test_update_no_options(self):
|
def test_update_no_options(self):
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from tackerclient.osc.v1.vnfpkgm import vnf_package
|
||||||
|
|
||||||
|
|
||||||
def vnf_package_obj(attrs=None, onboarded_state=False):
|
def vnf_package_obj(attrs=None, onboarded_state=False):
|
||||||
"""Create a fake vnf package.
|
"""Create a fake vnf package.
|
||||||
@@ -34,7 +36,7 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
|
|||||||
"onboardingState": "CREATED",
|
"onboardingState": "CREATED",
|
||||||
"operationalState": "DISABLED",
|
"operationalState": "DISABLED",
|
||||||
"usageState": "NOT_IN_USE",
|
"usageState": "NOT_IN_USE",
|
||||||
"userDefinedData": None}
|
"userDefinedData": {'key': 'value'}}
|
||||||
|
|
||||||
if onboarded_state:
|
if onboarded_state:
|
||||||
fake_vnf_package = {"id": "60a6ac16-b50d-4e92-964b-b3cf98c7cf5c",
|
fake_vnf_package = {"id": "60a6ac16-b50d-4e92-964b-b3cf98c7cf5c",
|
||||||
@@ -70,7 +72,7 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
|
|||||||
"onboardingState": "ONBOARDED",
|
"onboardingState": "ONBOARDED",
|
||||||
"operationalState": "ENABLED",
|
"operationalState": "ENABLED",
|
||||||
"usageState": "IN_USE",
|
"usageState": "IN_USE",
|
||||||
"userDefinedData": None,
|
"userDefinedData": {'key': 'value'},
|
||||||
"_links": {
|
"_links": {
|
||||||
"self": {
|
"self": {
|
||||||
"href": "string"
|
"href": "string"
|
||||||
@@ -88,10 +90,10 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
|
|||||||
return fake_vnf_package
|
return fake_vnf_package
|
||||||
|
|
||||||
|
|
||||||
def get_vnf_package_data(vnf_package, **kwargs):
|
def get_vnf_package_data(vnf_package_obj, **kwargs):
|
||||||
"""Get the vnf package data from a FakeVnfPackage dict object.
|
"""Get the vnf package data from a FakeVnfPackage dict object.
|
||||||
|
|
||||||
:param vnf_package:
|
:param vnf_package_obj:
|
||||||
A FakeVnfPackage dict object
|
A FakeVnfPackage dict object
|
||||||
:return:
|
:return:
|
||||||
A list which may include the following values:
|
A list which may include the following values:
|
||||||
@@ -99,19 +101,27 @@ def get_vnf_package_data(vnf_package, **kwargs):
|
|||||||
'vnfd': {'href': 'string'}}, '60a6ac16-b50d-4e92-964b-b3cf98c7cf5c',
|
'vnfd': {'href': 'string'}}, '60a6ac16-b50d-4e92-964b-b3cf98c7cf5c',
|
||||||
'CREATED', 'DISABLED', 'NOT_IN_USE', {'Test_key': 'Test_value'}]
|
'CREATED', 'DISABLED', 'NOT_IN_USE', {'Test_key': 'Test_value'}]
|
||||||
"""
|
"""
|
||||||
|
complex_attributes = ['softwareImages', 'checksum', '_links',
|
||||||
|
'userDefinedData']
|
||||||
|
for attribute in complex_attributes:
|
||||||
|
if vnf_package_obj.get(attribute):
|
||||||
|
vnf_package_obj.update(
|
||||||
|
{attribute: vnf_package.FormatComplexDataColumn(
|
||||||
|
vnf_package_obj[attribute])})
|
||||||
|
|
||||||
if kwargs.get('list_action'):
|
if kwargs.get('list_action'):
|
||||||
# In case of List VNF packages we get empty string as data for
|
# In case of List VNF packages we get empty string as data for
|
||||||
# 'vnfProductName' if onboardingState is CREATED. Hence to match
|
# 'vnfProductName' if onboardingState is CREATED. Hence to match
|
||||||
# up with actual data we are adding here empty string.
|
# up with actual data we are adding here empty string.
|
||||||
if not vnf_package.get('vnfProductName'):
|
if not vnf_package_obj.get('vnfProductName'):
|
||||||
vnf_package['vnfProductName'] = ''
|
vnf_package_obj['vnfProductName'] = ''
|
||||||
|
|
||||||
# return the list of data as per column order
|
|
||||||
if kwargs.get('columns'):
|
if kwargs.get('columns'):
|
||||||
return tuple([vnf_package[key] for key in kwargs.get('columns')])
|
# return the list of data as per column order
|
||||||
|
return tuple([vnf_package_obj[key] for key in kwargs.get('columns')])
|
||||||
|
|
||||||
return tuple([vnf_package[key] for key in sorted(vnf_package.keys())])
|
return tuple([vnf_package_obj[key] for key in sorted(
|
||||||
|
vnf_package_obj.keys())])
|
||||||
|
|
||||||
|
|
||||||
def create_vnf_packages(count=2, onboarded_vnf_package=False):
|
def create_vnf_packages(count=2, onboarded_vnf_package=False):
|
||||||
|
Reference in New Issue
Block a user