Compare commits

...

7 Commits

Author SHA1 Message Date
Yasufumi Ogawa
82e1a837bc Drop test for lower constraints
As we agreed, drop lower constraints test from stable branches to avoid
difficulty of maintaining package depencencies for the recent pip
dependency resolver.

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: Ifc3055086c2d0b14b5227105a0866a181f9446f0
2021-09-22 16:37:56 +09:00
Zuul
7bda108ddf Merge "Improve readability of vnflcm show command" into stable/ussuri 2020-05-25 10:09:31 +00:00
Shubham
ee2e460333 Improve readability of vnflcm show command
Display show command data in human_readable format for better
readability for complex attributes of vnf instance like
'vimConnectionInfo', 'instantiatedVnfInfo', '_links'.

Please checkout below link to see the difference in vnflcm show
response data before and after resolving the issue.
http://paste.openstack.org/show/792866

Change-Id: I52684618f7042f5566ea3bed7255d17d2fd61df2
2020-05-18 10:40:04 +05:30
Shubham
548453627f Display correct fields in output for list command
If user list vnf packages with --exclude_default option, it should
display all attributes except default set of complex.

$ openstack vnf package list --exclude_default
Expected fields:
  'id', 'vnfProductName', 'onboardingState', 'usageState',
  'operationalState', '_links', 'vnfdVersion', 'vnfProvider',
  'vnfSoftwareVersion', 'vnfdId'

Actual fields displayed are:
   'id', 'vnfProductName', 'onboardingState', 'usageState',
   'operationalState'

Fixed this issue and now it will display fields as mentioned above in
expected fields.

Closes-Bug: 1875330

Change-Id: I61da40d2e6e7e42999b7ab2d2441cddde64118d9
2020-05-14 12:55:17 +05:30
Shubham
86fefdb119 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
(cherry picked from commit e030c9019a)
2020-05-14 12:30:06 +05:30
0d6e331c93 Update TOX/UPPER_CONSTRAINTS_FILE for stable/ussuri
Update the URL to the upper-constraints file to point to the redirect
rule on releases.openstack.org so that anyone working on this branch
will switch to the correct upper-constraints list automatically when
the requirements repository branches.

Until the requirements repository has as stable/ussuri branch, tests will
continue to use the upper-constraints list on master.

Change-Id: I6408964243c70fd2ca48cdb46041a8c2c15cf52e
2020-04-10 12:25:54 +00:00
70d79b533a Update .gitreview for stable/ussuri
Change-Id: I8ebaa10d72136b2defe1f9c05923281efc4237f5
2020-04-10 12:25:52 +00:00
12 changed files with 121 additions and 117 deletions

View File

@@ -2,3 +2,4 @@
host=review.opendev.org
port=29418
project=openstack/python-tackerclient.git
defaultbranch=stable/ussuri

View File

@@ -1,7 +1,6 @@
- project:
templates:
- check-requirements
- openstack-lower-constraints-jobs
- openstack-python3-ussuri-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3

View File

@@ -1,63 +0,0 @@
appdirs==1.3.0
Babel==2.3.4
cliff==2.8.0
cmd2==0.8.0
coverage==4.0
ddt==1.0.1
debtcollector==1.2.0
decorator==3.4.0
deprecation==1.0
dogpile.cache==0.6.2
extras==1.0.0
fixtures==3.0.0
flake8==2.5.5
hacking==0.12.0
iso8601==0.1.11
jmespath==0.9.0
jsonpatch==1.16
jsonpointer==1.13
keystoneauth1==3.4.0
linecache2==1.0.0
mccabe==0.2.1
mock==2.0.0
monotonic==0.6
msgpack-python==0.4.0
munch==2.1.0
netaddr==0.7.18
netifaces==0.10.4
openstacksdk==0.11.2
os-client-config==1.28.0
os-service-types==1.2.0
osc-lib==1.8.0
oslo.config==5.2.0
oslo.context==2.19.2
oslo.i18n==3.15.3
oslo.log==3.36.0
oslo.serialization==2.18.0
oslo.utils==3.40.0
pbr==2.0.0
pep8==1.5.7
positional==1.2.1
prettytable==0.7.2
pyflakes==0.8.1
pyinotify==0.9.6
pyparsing==2.1.0
pyperclip==1.5.27
python-dateutil==2.5.3
python-keystoneclient==3.8.0
python-mimeparse==1.6.0
python-subunit==1.0.0
pytz==2013.6
PyYAML==3.12
requests==2.14.2
requests-mock==1.2.0
requestsexceptions==1.2.0
rfc3986==0.3.1
simplejson==3.5.1
six==1.10.0
stestr==2.0.0
stevedore==1.20.0
testtools==2.2.0
traceback2==1.4.0
unittest2==1.1.0
wrapt==1.7.0

View File

@@ -22,6 +22,7 @@ to this module. They should go to tackerclient.osc.v1.utils.
import operator
from cliff import columns as cliff_columns
from keystoneclient import exceptions as identity_exc
from keystoneclient.v3 import domains
from keystoneclient.v3 import projects
@@ -205,3 +206,9 @@ def _find_identity_resource(identity_client_manager, name_or_id,
# The above are borrowed from openstackclient.identity.common.
class FormatComplexDataColumn(cliff_columns.FormattableColumn):
def human_readable(self):
return format_dict_with_indention(self._value)

View File

@@ -18,7 +18,6 @@ import logging
import os
import time
from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -52,6 +51,10 @@ EXTRA_WAITING_TIME = 10
SLEEP_TIME = 1
formatters = {'vimConnectionInfo': tacker_osc_utils.FormatComplexDataColumn,
'instantiatedVnfInfo': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn}
def _get_columns(vnflcm_obj, action=None):
column_map = {
@@ -132,9 +135,9 @@ class CreateVnfLcm(command.ShowOne):
print((_('VNF Instance %(id)s is created and instantiation'
' request has been accepted.') % {'id': vnf['id']}))
display_columns, columns = _get_columns(vnf)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf),
columns, mixed_case_fields=_mixed_case_fields)
data = utils.get_item_properties(sdk_utils.DictModel(vnf),
columns, formatters=formatters,
mixed_case_fields=_mixed_case_fields)
return (display_columns, data)
@@ -156,7 +159,7 @@ class ShowVnfLcm(command.ShowOne):
data = utils.get_item_properties(
sdk_utils.DictModel(obj),
columns, mixed_case_fields=_mixed_case_fields,
formatters={'instantiatedVnfInfo': format_columns.DictColumn})
formatters=formatters)
return (display_columns, data)

View File

@@ -29,6 +29,12 @@ from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
formatters = {'softwareImages': tacker_osc_utils.FormatComplexDataColumn,
'checksum': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn,
'userDefinedData': tacker_osc_utils.FormatComplexDataColumn}
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
'vnfProductName', 'softwareImages', 'userDefinedData',
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
@@ -87,7 +93,8 @@ class CreateVnfPackage(command.ShowOne):
display_columns, columns = _get_columns(vnf_package)
data = utils.get_item_properties(
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)
@@ -136,12 +143,12 @@ class ListVnfPackage(command.Lister):
lambda x, y: x + (' ' if y.isupper() else '') + y, field).title()
def get_attributes(self, extra_fields=None, all_fields=False,
exclude_fields=None):
exclude_fields=None, exclude_default=False):
fields = ['id', 'vnfProductName', 'onboardingState',
'usageState', 'operationalState']
'usageState', 'operationalState', '_links']
complex_fields = ['checksum', 'softwareImages', 'userDefinedData']
simple_fields = ['vnfdVersion', 'vnfProvider', 'vnfSoftwareVersion',
'vnfdId', '_links']
'vnfdId']
if extra_fields:
fields.extend(extra_fields)
@@ -153,6 +160,9 @@ class ListVnfPackage(command.Lister):
fields.extend(complex_fields)
fields.extend(simple_fields)
if exclude_default:
fields.extend(simple_fields)
attrs = []
for field in fields:
if field == '_links':
@@ -168,6 +178,7 @@ class ListVnfPackage(command.Lister):
extra_fields = []
exclude_fields = []
all_fields = False
exclude_default = False
if parsed_args.filter:
_params['filter'] = parsed_args.filter
if parsed_args.fields:
@@ -181,6 +192,7 @@ class ListVnfPackage(command.Lister):
exclude_fields.extend(fields)
if parsed_args.exclude_default:
_params['exclude_default'] = None
exclude_default = True
if parsed_args.all_fields:
_params['all_fields'] = None
all_fields = True
@@ -188,11 +200,12 @@ class ListVnfPackage(command.Lister):
client = self.app.client_manager.tackerclient
data = client.list_vnf_packages(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
self.get_attributes(extra_fields, all_fields, exclude_fields),
long_listing=True)
self.get_attributes(extra_fields, all_fields, exclude_fields,
exclude_default), long_listing=True)
return (headers,
(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']))
@@ -215,7 +228,8 @@ class ShowVnfPackage(command.ShowOne):
display_columns, columns = _get_columns(vnf_package)
data = utils.get_item_properties(
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)
@@ -452,5 +466,6 @@ class UpdateVnfPackage(command.ShowOne):
display_columns, columns = self.get_columns(updated_values)
data = utils.get_item_properties(
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)

View File

@@ -17,6 +17,8 @@ import mock
from requests_mock.contrib import fixture as requests_mock_fixture
import testtools
from cliff import columns as cliff_columns
class FixturedTestCase(testtools.TestCase):
client_fixture_class = None
@@ -51,6 +53,20 @@ class FixturedTestCase(testtools.TestCase):
msg = 'method %s should not have been called' % m
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):
pass

View File

@@ -104,8 +104,9 @@ class TestCreateVnfPackage(TestVnfPackage):
columns, data = (self.create_vnf_package.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnf_package(), columns)
self.assertCountEqual(vnf_package_fakes.get_vnf_package_data(json),
data)
headers, attributes = vnf_package._get_columns(json)
self.assertListItemsEqual(vnf_package_fakes.get_vnf_package_data(
json, columns=attributes), data)
@ddt.ddt
@@ -122,12 +123,12 @@ class TestListVnfPackage(TestVnfPackage):
count=3, onboarded_vnf_package=onboarded_vnf_package)
def get_list_columns(self, all_fields=False, exclude_fields=None,
extra_fields=None):
extra_fields=None, exclude_default=False):
columns = ['Id', 'Vnf Product Name', 'Onboarding State', 'Usage State',
'Operational State']
'Operational State', 'Links']
complex_columns = ['Checksum', 'Software Images', 'User Defined Data']
simple_columns = ['Vnfd Version', 'Vnf Provider', 'Vnfd Id', 'Links',
simple_columns = ['Vnfd Version', 'Vnf Provider', 'Vnfd Id',
'Vnf Software Version']
if extra_fields:
@@ -140,6 +141,9 @@ class TestListVnfPackage(TestVnfPackage):
columns.extend(complex_columns)
columns.extend(simple_columns)
if exclude_default:
columns.extend(simple_columns)
return columns
def _get_mock_response_for_list_vnf_packages(
@@ -163,7 +167,7 @@ class TestListVnfPackage(TestVnfPackage):
expected_data.append(vnf_package_fakes.get_vnf_package_data(
vnf_package_obj, columns=columns, list_action=True))
self.assertCountEqual(self.get_list_columns(), actual_columns)
self.assertCountEqual(expected_data, list(data))
self.assertListItemsEqual(expected_data, list(data))
@ddt.data('all_fields', 'exclude_default')
def test_take_action(self, arg):
@@ -177,7 +181,7 @@ class TestListVnfPackage(TestVnfPackage):
actual_columns, data = self.list_vnf_package.take_action(parsed_args)
expected_data = []
kwargs = {arg: True} if arg == 'all_fields' else {}
kwargs = {arg: True}
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_package.get_attributes(**kwargs), long_listing=True)
@@ -186,7 +190,7 @@ class TestListVnfPackage(TestVnfPackage):
vnf_package_obj, columns=columns, list_action=True, **kwargs))
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):
parsed_args = self.check_parser(
@@ -222,7 +226,7 @@ class TestListVnfPackage(TestVnfPackage):
exclude_fields=['Software Images', 'Checksum',
'User Defined Data'])
self.assertCountEqual(expected_columns, actual_columns)
self.assertCountEqual(expected_data, list(data))
self.assertListItemsEqual(expected_data, list(data))
@ddt.data((['--all_fields', '--fields', 'softwareImages'],
[('all_fields', True), ('fields', 'softwareImages')]),
@@ -254,7 +258,8 @@ class TestListVnfPackage(TestVnfPackage):
expected_data = []
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_package.get_attributes(
extra_fields=['softwareImages', 'checksum']),
extra_fields=['softwareImages', 'checksum'],
exclude_default=True),
long_listing=True)
for vnf_package_obj in updated_vnf_packages['vnf_packages']:
@@ -263,9 +268,10 @@ class TestListVnfPackage(TestVnfPackage):
exclude_default=True))
self.assertCountEqual(self.get_list_columns(
extra_fields=['Software Images', 'Checksum']),
extra_fields=['Software Images', 'Checksum'],
exclude_default=True),
actual_columns)
self.assertCountEqual(expected_data, list(data))
self.assertListItemsEqual(expected_data, list(data))
@ddt.ddt
@@ -288,12 +294,13 @@ class TestShowVnfPackage(TestVnfPackage):
self.requests_mock.register_uri('GET', url, json=vnf_package_obj,
headers=self.header)
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(
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):
self.assertRaises(base.ParserException, self.check_parser,
@@ -536,7 +543,7 @@ class TestUpdateVnfPackage(TestVnfPackage):
columns, data = self.update_vnf_package.take_action(parsed_args)
self.assertItemsEqual(_get_columns_vnf_package(
vnf_package_obj=fake_response, action='update'), columns)
self.assertItemsEqual(
self.assertListItemsEqual(
vnf_package_fakes.get_vnf_package_data(fake_response), data)
def test_update_no_options(self):

View File

@@ -105,7 +105,9 @@ class TestCreateVnfLcm(TestVnfLcm):
json={}, headers=self.header)
sys.stdout = buffer = StringIO()
columns, data = (self.create_vnf_lcm.take_action(parsed_args))
actual_columns, data = (self.create_vnf_lcm.take_action(parsed_args))
headers, attributes = vnflcm._get_columns(json)
expected_message = (
'VNF Instance ' + json['id'] + ' is created and instantiation '
@@ -114,9 +116,9 @@ class TestCreateVnfLcm(TestVnfLcm):
self.assertEqual(expected_message, buffer.getvalue().strip())
self.assertItemsEqual(_get_columns_vnflcm(),
columns)
self.assertItemsEqual(vnflcm_fakes.get_vnflcm_data(json),
data)
actual_columns)
self.assertListItemsEqual(vnflcm_fakes.get_vnflcm_data(
json, columns=attributes), data)
class TestShowVnfLcm(TestVnfLcm):
@@ -145,6 +147,10 @@ class TestShowVnfLcm(TestVnfLcm):
columns, data = (self.show_vnf_lcm.take_action(parsed_args))
self.assertItemsEqual(_get_columns_vnflcm(action='show'),
columns)
headers, attributes = vnflcm._get_columns(vnf_instance, action='show')
self.assertListItemsEqual(
vnflcm_fakes.get_vnflcm_data(vnf_instance, columns=attributes),
data)
class TestListVnfLcm(TestVnfLcm):

View File

@@ -15,6 +15,8 @@
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def vnf_package_obj(attrs=None, onboarded_state=False):
"""Create a fake vnf package.
@@ -34,7 +36,7 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
"onboardingState": "CREATED",
"operationalState": "DISABLED",
"usageState": "NOT_IN_USE",
"userDefinedData": None}
"userDefinedData": {'key': 'value'}}
if onboarded_state:
fake_vnf_package = {"id": "60a6ac16-b50d-4e92-964b-b3cf98c7cf5c",
@@ -70,7 +72,7 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
"onboardingState": "ONBOARDED",
"operationalState": "ENABLED",
"usageState": "IN_USE",
"userDefinedData": None,
"userDefinedData": {'key': 'value'},
"_links": {
"self": {
"href": "string"
@@ -88,10 +90,10 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
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.
:param vnf_package:
:param vnf_package_obj:
A FakeVnfPackage dict object
:return:
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',
'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: tacker_osc_utils.FormatComplexDataColumn(
vnf_package_obj[attribute])})
if kwargs.get('list_action'):
# In case of List VNF packages we get empty string as data for
# 'vnfProductName' if onboardingState is CREATED. Hence to match
# up with actual data we are adding here empty string.
if not vnf_package.get('vnfProductName'):
vnf_package['vnfProductName'] = ''
if not vnf_package_obj.get('vnfProductName'):
vnf_package_obj['vnfProductName'] = ''
# return the list of data as per column order
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):

View File

@@ -16,6 +16,8 @@
from oslo_utils.fixture import uuidsentinel
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def vnf_instance_response(attrs=None, instantiation_state='NOT_INSTANTIATED'):
"""Create a fake vnf instance.
@@ -117,6 +119,12 @@ def get_vnflcm_data(vnf_instance, list_action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FFalse%2C%2520columns%3DNone)%3A%253C%2Fcode">
:return:
A tuple object sorted based on the name of the columns.
"""
complex_attributes = ['vimConnectionInfo', 'instantiatedVnfInfo', '_links']
for attribute in complex_attributes:
if vnf_instance.get(attribute):
vnf_instance.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
vnf_instance[attribute])})
if list_action:
for item in ['vnfInstanceDescription', 'vnfdVersion']:
@@ -126,7 +134,8 @@ def get_vnflcm_data(vnf_instance, list_action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FFalse%2C%2520columns%3DNone)%3A%253C%2Fcode">
if columns:
return tuple([vnf_instance[key] for key in columns])
return tuple([vnf_instance[key] for key in sorted(vnf_instance.keys())])
return tuple([vnf_instance[key] for key in sorted(
vnf_instance.keys())])
def create_vnf_instances(count=2):

10
tox.ini
View File

@@ -13,7 +13,7 @@ setenv = VIRTUAL_ENV={envdir}
usedevelop = True
install_command = pip install {opts} {packages}
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/ussuri}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
@@ -31,7 +31,7 @@ commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/ussuri}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -53,9 +53,3 @@ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools
# F821 undefined name 'unicode'
# if isinstance(config, str) or isinstance(config, unicode):
builtins = unicode
[testenv:lower-constraints]
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt