Skip to content

Commit

Permalink
Remove dependency on novaclient list_extensions API
Browse files Browse the repository at this point in the history
The novaclient list_extensions API binding was removed in the
16.0.0 release [1]. The ability to enable/disable extensions
in nova has been deprecated since Liberty [2] and was removed
in Newton [3].

For horizon this only matters for the OPENSTACK_NOVA_EXTENSIONS_BLACKLIST
config setting and some javascript code used to compile panels based on
enabled extensions. In order to work with novaclient 16.0.0+, this
change removes the list_extensions usage since all extensions
are enabled in nova and thus for horizon a nova extension is only
not supported if it's in the configured blacklist. To continue supporting
the javascript code which uses the getExtensions function, the extension
names are hard-coded. Note that the method meant to test that code,
_test_extension_list, was wrong but never ran because of the underscore
prefix on the method name. That is fixed here.

[1] https://review.opendev.org/686516/
[2] https://review.opendev.org/214592/
[3] https://review.opendev.org/351362/

Change-Id: Iebb1e78c718b931d632445e4de6d7a29ccb92be2
Closes-Bug: #1847959
  • Loading branch information
mriedem committed Oct 24, 2019
1 parent 85a1ddd commit b148c92
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 26 deletions.
120 changes: 110 additions & 10 deletions openstack_dashboard/api/nova.py
Expand Up @@ -30,7 +30,6 @@
from novaclient import api_versions
from novaclient import exceptions as nova_exceptions
from novaclient.v2 import instance_action as nova_instance_action
from novaclient.v2 import list_extensions as nova_list_extensions
from novaclient.v2 import servers as nova_servers

from horizon import exceptions as horizon_exceptions
Expand All @@ -49,6 +48,113 @@
VOLUME_STATE_AVAILABLE = "available"
DEFAULT_QUOTA_NAME = 'default'

# python-novaclient 16.0.0 removed the list_extensions module and the
# GET /extensions API is deprecated since Newton. Furthermore, the ability
# to enable/disable compute API extensions was also removed in Newton.
# Therefore we hard-code the list of extensions here until the
# OPENSTACK_NOVA_EXTENSIONS_BLACKLIST setting is no longer used.
EXTENSIONS = (
'AccessIPs',
'AdminActions',
'AdminPassword',
'Agents',
'Aggregates',
'AssistedVolumeSnapshots',
'AttachInterfaces',
'AvailabilityZone',
'BareMetalExtStatus',
'BareMetalNodes',
'BlockDeviceMapping',
'BlockDeviceMappingV2Boot',
'CellCapacities',
'Cells',
'Certificates',
'Cloudpipe',
'CloudpipeUpdate',
'ConfigDrive',
'ConsoleAuthTokens',
'ConsoleOutput',
'Consoles',
'CreateBackup',
'Createserverext',
'DeferredDelete',
'DiskConfig',
'Evacuate',
'ExtendedAvailabilityZone',
'ExtendedEvacuateFindHost',
'ExtendedFloatingIps',
'ExtendedHypervisors',
'ExtendedIps',
'ExtendedIpsMac',
'ExtendedNetworks',
'ExtendedQuotas',
'ExtendedRescueWithImage',
'ExtendedServerAttributes',
'ExtendedServices',
'ExtendedServicesDelete',
'ExtendedStatus',
'ExtendedStatus',
'ExtendedVolumes',
'FixedIPs',
'FlavorAccess',
'FlavorDisabled',
'FlavorExtraData',
'FlavorExtraSpecs',
'FlavorManage',
'FlavorRxtx',
'FlavorSwap',
'FloatingIpDns',
'FloatingIpPools',
'FloatingIps',
'FloatingIpsBulk',
'Fping',
'HideServerAddresses',
'Hosts',
'HypervisorStatus',
'Hypervisors',
'ImageSize',
'InstanceActions',
'Keypairs',
'LockServer',
'MigrateServer',
'Migrations',
'Multinic',
'MultipleCreate',
'NetworkAssociationSupport',
'Networks',
'OSInstanceUsageAuditLog',
'OSTenantNetworks',
'PauseServer',
'Personality',
'PreserveEphemeralOnRebuild',
'QuotaClasses',
'Quotas',
'Rescue',
'SchedulerHints',
'SecurityGroupDefaultRules',
'SecurityGroups',
'ServerDiagnostics',
'ServerExternalEvents',
'ServerGroupQuotas',
'ServerGroups',
'ServerListMultiStatus',
'ServerPassword',
'ServerSortKeys',
'ServerStartStop',
'ServerUsage',
'Services',
'Shelve',
'SimpleTenantUsage',
'SuspendServer',
'UsedLimits',
'UsedLimitsForAdmin',
'UserData',
'UserQuotas',
'VirtualInterfaces',
'VolumeAttachmentUpdate',
'Volumes'
)


get_microversion = _nova.get_microversion
server_get = _nova.server_get
Expand Down Expand Up @@ -1010,13 +1116,10 @@ def interface_detach(request, server, port_id):
@profiler.trace
@memoized.memoized
def list_extensions(request):
"""List all nova extensions, except the ones in the blacklist."""
"""List all nova extension names, except the ones in the blacklist."""
blacklist = set(settings.OPENSTACK_NOVA_EXTENSIONS_BLACKLIST)
nova_api = _nova.novaclient(request)
return tuple(
extension for extension in
nova_list_extensions.ListExtManager(nova_api).show_all()
if extension.name not in blacklist
extension for extension in EXTENSIONS if extension not in blacklist
)


Expand All @@ -1028,10 +1131,7 @@ def extension_supported(extension_name, request):
Example values for the extension_name include AdminActions, ConsoleOutput,
etc.
"""
for ext in list_extensions(request):
if ext.name == extension_name:
return True
return False
return extension_name in list_extensions(request)


@profiler.trace
Expand Down
4 changes: 2 additions & 2 deletions openstack_dashboard/api/rest/nova.py
Expand Up @@ -512,13 +512,13 @@ def get(self, request):
"""Get a list of extensions.
The listing result is an object with property "items". Each item is
an image.
an object with property "name".
Example GET:
http://localhost/api/nova/extensions
"""
result = api.nova.list_extensions(request)
return {'items': [e.to_dict() for e in result]}
return {'items': [{'name': e} for e in result]}


@urls.register
Expand Down
Expand Up @@ -548,12 +548,7 @@
* {
* "items": [
* {
* "alias": "NMN",
* "description": "Multiple network support.",
* "links": [],
* "name": "Multinic",
* "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1",
* "updated": "2011-06-09T00:00:00Z"
* "name": "Multinic"
* }
* ]
* }
Expand Down
10 changes: 2 additions & 8 deletions openstack_dashboard/test/unit/api/rest/test_nova.py
Expand Up @@ -487,15 +487,9 @@ def test_server_edit_metadata(self):
# Extensions
#
@test.create_mocks({api.nova: ['list_extensions']})
@mock.patch.object(settings,
'OPENSTACK_NOVA_EXTENSIONS_BLACKLIST', ['baz'])
def _test_extension_list(self):
def test_extension_list(self):
request = self.mock_rest_request()
self.mock_list_extensions.return_value = [
mock.Mock(**{'to_dict.return_value': {'name': 'foo'}}),
mock.Mock(**{'to_dict.return_value': {'name': 'bar'}}),
mock.Mock(**{'to_dict.return_value': {'name': 'baz'}}),
]
self.mock_list_extensions.return_value = ['foo', 'bar']
response = nova.Extensions().get(request)
self.assertStatusCode(response, 200)
self.assertEqual({"items": [{"name": "foo"}, {"name": "bar"}]},
Expand Down
9 changes: 9 additions & 0 deletions openstack_dashboard/test/unit/api/test_nova.py
Expand Up @@ -72,6 +72,15 @@ def _mock_current_version(self, mock_novaclient, version,
# To handle upgrade_api
mock_novaclient.api_version = api_versions.APIVersion(version)

@override_settings(OPENSTACK_NOVA_EXTENSIONS_BLACKLIST=['ConsoleOutput'])
def test_extension_supported(self):
self.assertTrue(api.nova.extension_supported(
'Evacuate', mock.sentinel.request))
self.assertFalse(api.nova.extension_supported(
'ConsoleOutput', mock.sentinel.request))
self.assertFalse(api.nova.extension_supported(
'DoesNotExist', mock.sentinel.request))

@mock.patch.object(api._nova, 'novaclient')
def test_server_reboot(self, mock_novaclient):
server = self.servers.first()
Expand Down

0 comments on commit b148c92

Please sign in to comment.