Migrate 'extension list' to SDK

Migrate the block storage aspects of this command to SDK. This means the
'extension list' command, like the 'availability zone list' command
previously, is now using SDK entirely.

While we're here, we also make some fixes to the unit tests for the
network and compute aspects of the command. While we migrated the
network and compute extension commands from neutronclient and novaclient
respectively some time back, we never fully updated the tests. Do this
now.

Change-Id: I631a6a09dfd9d614b1dd7b322dcee8490a52cc43
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/885132
This commit is contained in:
Stephen Finucane 2023-06-02 13:17:29 +01:00
parent 44cf963d8e
commit b87b57551b
7 changed files with 124 additions and 148 deletions

View File

@ -22,15 +22,24 @@ from osc_lib import utils
from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
def _get_extension_columns(item):
column_map = {
'updated': 'updated_at',
}
hidden_columns = ['id', 'links', 'location']
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, hidden_columns
)
class ListExtension(command.Lister):
_description = _("List API extensions")
def get_parser(self, prog_name):
parser = super(ListExtension, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'--compute',
action='store_true',
@ -70,7 +79,7 @@ class ListExtension(command.Lister):
'Alias',
'Description',
'Namespace',
'Updated',
'Updated At',
'Links',
)
else:
@ -105,12 +114,12 @@ class ListExtension(command.Lister):
LOG.warning(message)
if parsed_args.volume or show_all:
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
try:
data += volume_client.list_extensions.show_all()
data += volume_client.extensions()
except Exception:
message = _(
"Extensions list not supported by " "Block Storage API"
"Extensions list not supported by Block Storage API"
)
LOG.warning(message)
@ -120,7 +129,7 @@ class ListExtension(command.Lister):
data += network_client.extensions()
except Exception:
message = _(
"Failed to retrieve extensions list " "from Network API"
"Failed to retrieve extensions list from Network API"
)
LOG.warning(message)
@ -153,7 +162,12 @@ class ShowExtension(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
ext = str(parsed_args.extension)
obj = client.find_extension(ext, ignore_missing=False).to_dict()
return zip(*sorted(obj.items()))
extension = client.find_extension(
parsed_args.extension,
ignore_missing=False,
)
display_columns, columns = _get_extension_columns(extension)
data = utils.get_dict_properties(extension, columns)
return display_columns, data

View File

@ -20,7 +20,7 @@ from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils
from openstackclient.tests.unit import utils as tests_utils
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
class TestExtension(utils.TestCommand):
@ -37,26 +37,16 @@ class TestExtension(utils.TestCommand):
sdk_connection = mock.Mock()
self.app.client_manager.sdk_connection = sdk_connection
self.compute_extensions_mock = sdk_connection.compute.extensions
self.compute_extensions_mock.reset_mock()
volume_client = volume_fakes.FakeVolumeClient(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
self.app.client_manager.volume = volume_client
volume_client.list_extensions = mock.Mock()
self.volume_extensions_mock = volume_client.list_extensions
self.volume_extensions_mock = sdk_connection.volume.extensions
self.volume_extensions_mock.reset_mock()
network_client = network_fakes.FakeNetworkV2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
self.app.client_manager.network = network_client
network_client.extensions = mock.Mock()
self.network_extensions_mock = network_client.extensions
self.network_extensions_mock.reset_mock()
self.app.client_manager.network = mock.Mock()
self.network_client = self.app.client_manager.network
self.network_client.extensions = mock.Mock()
class TestExtensionList(TestExtension):
@ -66,14 +56,14 @@ class TestExtensionList(TestExtension):
'Alias',
'Description',
'Namespace',
'Updated',
'Updated At',
'Links',
)
volume_extension = volume_fakes.create_one_extension()
identity_extension = identity_fakes.FakeExtension.create_one_extension()
compute_extension = compute_fakes.create_one_extension()
network_extension = network_fakes.FakeExtension.create_one_extension()
network_extension = network_fakes.create_one_extension()
def setUp(self):
super().setUp()
@ -82,10 +72,8 @@ class TestExtensionList(TestExtension):
self.identity_extension
]
self.compute_extensions_mock.return_value = [self.compute_extension]
self.volume_extensions_mock.show_all.return_value = [
self.volume_extension
]
self.network_extensions_mock.return_value = [self.network_extension]
self.volume_extensions_mock.return_value = [self.volume_extension]
self.network_client.extensions.return_value = [self.network_extension]
# Get the command object to test
self.cmd = extension.ListExtension(self.app, None)
@ -134,8 +122,8 @@ class TestExtensionList(TestExtension):
self._test_extension_list_helper(arglist, verifylist, datalist)
self.identity_extensions_mock.list.assert_called_with()
self.compute_extensions_mock.assert_called_with()
self.volume_extensions_mock.show_all.assert_called_with()
self.network_extensions_mock.assert_called_with()
self.volume_extensions_mock.assert_called_with()
self.network_client.extensions.assert_called_with()
def test_extension_list_long(self):
arglist = [
@ -150,7 +138,7 @@ class TestExtensionList(TestExtension):
self.identity_extension.alias,
self.identity_extension.description,
self.identity_extension.namespace,
self.identity_extension.updated,
'',
self.identity_extension.links,
),
(
@ -158,31 +146,31 @@ class TestExtensionList(TestExtension):
self.compute_extension.alias,
self.compute_extension.description,
self.compute_extension.namespace,
self.compute_extension.updated,
self.compute_extension.updated_at,
self.compute_extension.links,
),
(
self.volume_extension.name,
self.volume_extension.alias,
self.volume_extension.description,
self.volume_extension.namespace,
self.volume_extension.updated,
'',
self.volume_extension.updated_at,
self.volume_extension.links,
),
(
self.network_extension.name,
self.network_extension.alias,
self.network_extension.description,
self.network_extension.namespace,
self.network_extension.updated,
'',
self.network_extension.updated_at,
self.network_extension.links,
),
)
self._test_extension_list_helper(arglist, verifylist, datalist, True)
self.identity_extensions_mock.list.assert_called_with()
self.compute_extensions_mock.assert_called_with()
self.volume_extensions_mock.show_all.assert_called_with()
self.network_extensions_mock.assert_called_with()
self.volume_extensions_mock.assert_called_with()
self.network_client.extensions.assert_called_with()
def test_extension_list_identity(self):
arglist = [
@ -216,7 +204,7 @@ class TestExtensionList(TestExtension):
),
)
self._test_extension_list_helper(arglist, verifylist, datalist)
self.network_extensions_mock.assert_called_with()
self.network_client.extensions.assert_called_with()
def test_extension_list_network_with_long(self):
arglist = [
@ -232,15 +220,15 @@ class TestExtensionList(TestExtension):
self.network_extension.name,
self.network_extension.alias,
self.network_extension.description,
self.network_extension.namespace,
self.network_extension.updated,
'',
self.network_extension.updated_at,
self.network_extension.links,
),
)
self._test_extension_list_helper(
arglist, verifylist, datalist, long=True
)
self.network_extensions_mock.assert_called_with()
self.network_client.extensions.assert_called_with()
def test_extension_list_compute(self):
arglist = [
@ -282,7 +270,7 @@ class TestExtensionList(TestExtension):
)
self._test_extension_list_helper(arglist, verifylist, datalist)
self.compute_extensions_mock.assert_called_with()
self.network_extensions_mock.assert_called_with()
self.network_client.extensions.assert_called_with()
def test_extension_list_volume(self):
arglist = [
@ -299,28 +287,24 @@ class TestExtensionList(TestExtension):
),
)
self._test_extension_list_helper(arglist, verifylist, datalist)
self.volume_extensions_mock.show_all.assert_called_with()
self.volume_extensions_mock.assert_called_with()
class TestExtensionShow(TestExtension):
extension_details = network_fakes.FakeExtension.create_one_extension()
extension_details = network_fakes.create_one_extension()
columns = (
'alias',
'description',
'links',
'name',
'namespace',
'updated',
'updated_at',
)
data = (
extension_details.alias,
extension_details.description,
extension_details.links,
extension_details.name,
extension_details.namespace,
extension_details.updated,
extension_details.updated_at,
)
def setUp(self):

View File

@ -21,6 +21,7 @@ import uuid
from novaclient import api_versions
from openstack.compute.v2 import aggregate as _aggregate
from openstack.compute.v2 import availability_zone as _availability_zone
from openstack.compute.v2 import extension as _extension
from openstack.compute.v2 import flavor as _flavor
from openstack.compute.v2 import hypervisor as _hypervisor
from openstack.compute.v2 import keypair as _keypair
@ -288,35 +289,33 @@ def create_agents(attrs=None, count=2):
def create_one_extension(attrs=None):
"""Create a fake extension.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with name, namespace, etc.
:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.extension.Extension object
"""
attrs = attrs or {}
# Set default attributes.
extension_info = {
'alias': 'NMN',
'description': 'description-' + uuid.uuid4().hex,
'links': [
{
"href": "https://github.com/openstack/compute-api",
"type": "text/html",
"rel": "describedby",
}
],
'name': 'name-' + uuid.uuid4().hex,
'namespace': (
'http://docs.openstack.org/compute/ext/multinic/api/v1.1'
),
'description': 'description-' + uuid.uuid4().hex,
'updated': '2014-01-07T12:00:0-00:00',
'alias': 'NMN',
'links': (
'[{"href":'
'"https://github.com/openstack/compute-api", "type":'
' "text/html", "rel": "describedby"}]'
),
'updated_at': '2014-01-07T12:00:0-00:00',
}
# Overwrite default attributes.
extension_info.update(attrs)
extension = fakes.FakeResource(
info=copy.deepcopy(extension_info), loaded=True
)
extension = _extension.Extension(**extension_info)
return extension

View File

@ -23,6 +23,7 @@ from openstack.network.v2 import address_scope as _address_scope
from openstack.network.v2 import agent as network_agent
from openstack.network.v2 import auto_allocated_topology as allocated_topology
from openstack.network.v2 import availability_zone as _availability_zone
from openstack.network.v2 import extension as _extension
from openstack.network.v2 import flavor as _flavor
from openstack.network.v2 import local_ip as _local_ip
from openstack.network.v2 import local_ip_association as _local_ip_association
@ -101,21 +102,12 @@ class FakeNetworkV2Client(object):
class TestNetworkV2(utils.TestCommand):
def setUp(self):
super(TestNetworkV2, self).setUp()
super().setUp()
self.namespace = argparse.Namespace()
self.app.client_manager.session = mock.Mock()
self.app.client_manager.network = FakeNetworkV2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
self.app.client_manager.sdk_connection = mock.Mock()
self.app.client_manager.sdk_connection.network = (
self.app.client_manager.network
)
self.app.client_manager.network = mock.Mock()
self.app.client_manager.identity = (
identity_fakes_v3.FakeIdentityv3Client(
@ -125,37 +117,30 @@ class TestNetworkV2(utils.TestCommand):
)
class FakeExtension(object):
"""Fake one or more extension."""
def create_one_extension(attrs=None):
"""Create a fake extension.
@staticmethod
def create_one_extension(attrs=None):
"""Create a fake extension.
:param Dictionary attrs:
A dictionary with all attributes
:return:
An Extension object with name, namespace, etc.
"""
attrs = attrs or {}
:param Dictionary attrs:
A dictionary with all attributes
:return:
A FakeResource object with name, namespace, etc.
"""
attrs = attrs or {}
# Set default attributes.
extension_info = {
'name': 'name-' + uuid.uuid4().hex,
'description': 'description-' + uuid.uuid4().hex,
'alias': 'Dystopian',
'links': [],
'updated_at': '2013-07-09T12:00:0-00:00',
}
# Set default attributes.
extension_info = {
'name': 'name-' + uuid.uuid4().hex,
'namespace': 'http://docs.openstack.org/network/',
'description': 'description-' + uuid.uuid4().hex,
'updated': '2013-07-09T12:00:0-00:00',
'alias': 'Dystopian',
'links': '[{"href":' '"https://github.com/os/network", "type"}]',
}
# Overwrite default attributes.
extension_info.update(attrs)
# Overwrite default attributes.
extension_info.update(attrs)
extension = fakes.FakeResource(
info=copy.deepcopy(extension_info), loaded=True
)
return extension
extension = _extension.Extension(**extension_info)
return extension
class FakeNetworkQosPolicy(object):

View File

@ -485,7 +485,7 @@ class TestDeleteRouter(TestRouter):
class TestListRouter(TestRouter):
# The routers going to be listed up.
routers = network_fakes.FakeRouter.create_routers(count=3)
_extensions = network_fakes.FakeExtension.create_one_extension()
extensions = network_fakes.create_one_extension()
columns = (
'ID',
@ -572,7 +572,7 @@ class TestListRouter(TestRouter):
return_value=self.routers
)
self.network.routers = mock.Mock(return_value=self.routers)
self.network.find_extension = mock.Mock(return_value=self._extensions)
self.network.find_extension = mock.Mock(return_value=self.extensions)
self.network.find_router = mock.Mock(return_value=self.routers[0])
self._testagent = network_fakes.create_one_network_agent()
self.network.get_agent = mock.Mock(return_value=self._testagent)

View File

@ -56,8 +56,6 @@ class FakeVolumeClient:
self.cgsnapshots.resource_class = fakes.FakeResource(None, {})
self.consistencygroups = mock.Mock()
self.consistencygroups.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
self.limits = mock.Mock()
self.limits.resource_class = fakes.FakeResource(None, {})
self.pools = mock.Mock()
@ -727,42 +725,6 @@ def get_consistency_group_snapshots(snapshots=None, count=2):
return mock.Mock(side_effect=snapshots)
def create_one_extension(attrs=None):
"""Create a fake extension.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with name, namespace, etc.
"""
attrs = attrs or {}
# Set default attributes.
extension_info = {
'name': 'name-' + uuid.uuid4().hex,
'namespace': (
'http://docs.openstack.org/'
'block-service/ext/scheduler-hints/api/v2'
),
'description': 'description-' + uuid.uuid4().hex,
'updated': '2013-04-18T00:00:00+00:00',
'alias': 'OS-SCH-HNT',
'links': (
'[{"href":'
'"https://github.com/openstack/block-api", "type":'
' "text/html", "rel": "describedby"}]'
),
}
# Overwrite default attributes.
extension_info.update(attrs)
extension = fakes.FakeResource(
info=copy.deepcopy(extension_info), loaded=True
)
return extension
def create_one_qos(attrs=None):
"""Create a fake Qos specification.

View File

@ -17,6 +17,7 @@ import uuid
from cinderclient import api_versions
from openstack.block_storage.v3 import availability_zone as _availability_zone
from openstack.block_storage.v3 import block_storage_summary as _summary
from openstack.block_storage.v3 import extension as _extension
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit import fakes
@ -116,6 +117,37 @@ def create_availability_zones(attrs=None, count=2):
return availability_zones
def create_one_extension(attrs=None):
"""Create a fake extension.
:param dict attrs: A dictionary with all attributes
:return: A fake
openstack.block_storage.v3.extension.Extension object
"""
attrs = attrs or {}
# Set default attributes.
extension_info = {
'alias': 'OS-SCH-HNT',
'description': 'description-' + uuid.uuid4().hex,
'links': [
{
"href": "https://github.com/openstack/block-api",
"type": "text/html",
"rel": "describedby",
}
],
'name': 'name-' + uuid.uuid4().hex,
'updated_at': '2013-04-18T00:00:00+00:00',
}
# Overwrite default attributes.
extension_info.update(attrs)
extension = _extension.Extension(**extension_info)
return extension
def create_one_cluster(attrs=None):
"""Create a fake service cluster.