Remove dependency on novaclient list_extensions API
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
This commit is contained in:
parent
85a1dddf12
commit
b148c92075
@ -30,7 +30,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from novaclient import exceptions as nova_exceptions
|
from novaclient import exceptions as nova_exceptions
|
||||||
from novaclient.v2 import instance_action as nova_instance_action
|
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 novaclient.v2 import servers as nova_servers
|
||||||
|
|
||||||
from horizon import exceptions as horizon_exceptions
|
from horizon import exceptions as horizon_exceptions
|
||||||
@ -49,6 +48,113 @@ INSTANCE_ACTIVE_STATE = 'ACTIVE'
|
|||||||
VOLUME_STATE_AVAILABLE = "available"
|
VOLUME_STATE_AVAILABLE = "available"
|
||||||
DEFAULT_QUOTA_NAME = 'default'
|
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
|
get_microversion = _nova.get_microversion
|
||||||
server_get = _nova.server_get
|
server_get = _nova.server_get
|
||||||
@ -1010,13 +1116,10 @@ def interface_detach(request, server, port_id):
|
|||||||
@profiler.trace
|
@profiler.trace
|
||||||
@memoized.memoized
|
@memoized.memoized
|
||||||
def list_extensions(request):
|
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)
|
blacklist = set(settings.OPENSTACK_NOVA_EXTENSIONS_BLACKLIST)
|
||||||
nova_api = _nova.novaclient(request)
|
|
||||||
return tuple(
|
return tuple(
|
||||||
extension for extension in
|
extension for extension in EXTENSIONS if extension not in blacklist
|
||||||
nova_list_extensions.ListExtManager(nova_api).show_all()
|
|
||||||
if extension.name not in blacklist
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1028,10 +1131,7 @@ def extension_supported(extension_name, request):
|
|||||||
Example values for the extension_name include AdminActions, ConsoleOutput,
|
Example values for the extension_name include AdminActions, ConsoleOutput,
|
||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
for ext in list_extensions(request):
|
return extension_name in list_extensions(request)
|
||||||
if ext.name == extension_name:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@profiler.trace
|
@profiler.trace
|
||||||
|
@ -512,13 +512,13 @@ class Extensions(generic.View):
|
|||||||
"""Get a list of extensions.
|
"""Get a list of extensions.
|
||||||
|
|
||||||
The listing result is an object with property "items". Each item is
|
The listing result is an object with property "items". Each item is
|
||||||
an image.
|
an object with property "name".
|
||||||
|
|
||||||
Example GET:
|
Example GET:
|
||||||
http://localhost/api/nova/extensions
|
http://localhost/api/nova/extensions
|
||||||
"""
|
"""
|
||||||
result = api.nova.list_extensions(request)
|
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
|
@urls.register
|
||||||
|
@ -548,12 +548,7 @@
|
|||||||
* {
|
* {
|
||||||
* "items": [
|
* "items": [
|
||||||
* {
|
* {
|
||||||
* "alias": "NMN",
|
* "name": "Multinic"
|
||||||
* "description": "Multiple network support.",
|
|
||||||
* "links": [],
|
|
||||||
* "name": "Multinic",
|
|
||||||
* "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1",
|
|
||||||
* "updated": "2011-06-09T00:00:00Z"
|
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
|
@ -487,15 +487,9 @@ class NovaRestTestCase(test.TestCase):
|
|||||||
# Extensions
|
# Extensions
|
||||||
#
|
#
|
||||||
@test.create_mocks({api.nova: ['list_extensions']})
|
@test.create_mocks({api.nova: ['list_extensions']})
|
||||||
@mock.patch.object(settings,
|
def test_extension_list(self):
|
||||||
'OPENSTACK_NOVA_EXTENSIONS_BLACKLIST', ['baz'])
|
|
||||||
def _test_extension_list(self):
|
|
||||||
request = self.mock_rest_request()
|
request = self.mock_rest_request()
|
||||||
self.mock_list_extensions.return_value = [
|
self.mock_list_extensions.return_value = ['foo', 'bar']
|
||||||
mock.Mock(**{'to_dict.return_value': {'name': 'foo'}}),
|
|
||||||
mock.Mock(**{'to_dict.return_value': {'name': 'bar'}}),
|
|
||||||
mock.Mock(**{'to_dict.return_value': {'name': 'baz'}}),
|
|
||||||
]
|
|
||||||
response = nova.Extensions().get(request)
|
response = nova.Extensions().get(request)
|
||||||
self.assertStatusCode(response, 200)
|
self.assertStatusCode(response, 200)
|
||||||
self.assertEqual({"items": [{"name": "foo"}, {"name": "bar"}]},
|
self.assertEqual({"items": [{"name": "foo"}, {"name": "bar"}]},
|
||||||
|
@ -72,6 +72,15 @@ class ComputeApiTests(test.APIMockTestCase):
|
|||||||
# To handle upgrade_api
|
# To handle upgrade_api
|
||||||
mock_novaclient.api_version = api_versions.APIVersion(version)
|
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')
|
@mock.patch.object(api._nova, 'novaclient')
|
||||||
def test_server_reboot(self, mock_novaclient):
|
def test_server_reboot(self, mock_novaclient):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
|
Loading…
Reference in New Issue
Block a user