Drop cinder v2 API support

Cinder v2 API is deprecated since pike release. Along with the removal
of cinder v2 API support in cinderclient (change I335db5c1799e drops
v2 support), this commit drops cinder v2 support in horizon.

The next release of python-cinderclient drops v2 support,
so horizon needs to use v3 classes.

Includes a workaround in unit tests for two cinderclient.v3 classes
that are missing in the cinderclient releases prior to 8.0.0.  The
workaround can be removed once cinderclient change I335db5c1799edb2
is merged and released.

Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
Change-Id: Iab0f097fab6696462572dc6ea53767c91e5411b1
This commit is contained in:
Brian Rosmaita 2021-07-14 12:32:48 -04:00
parent 310a24d054
commit b58ac2894b
18 changed files with 95 additions and 120 deletions

View File

@ -28,7 +28,7 @@ from django.utils.translation import ugettext_lazy as _
from cinderclient import api_versions
from cinderclient import client as cinder_client
from cinderclient import exceptions as cinder_exception
from cinderclient.v2.contrib import list_extensions as cinder_list_extensions
from cinderclient.v3.contrib import list_extensions as cinder_list_extensions
from horizon import exceptions
from horizon.utils.memoized import memoized
@ -58,9 +58,6 @@ VERSIONS = base.APIVersionManager("volume", preferred_version='3')
try:
# pylint: disable=ungrouped-imports
from cinderclient.v2 import client as cinder_client_v2
VERSIONS.load_supported_version('2', {"client": cinder_client_v2,
"version": '2'})
from cinderclient.v3 import client as cinder_client_v3
VERSIONS.load_supported_version('3', {"client": cinder_client_v3,
"version": '3'})
@ -216,11 +213,9 @@ def _find_cinder_url(request, version=None):
version = api_version['version']
version = base.Version(version)
# We support only cinder v2 and v3.
if version.major == 3:
# We support only cinder v3.
# FIXME: 'block-storage' is also a valid service_type for cinder
candidates = ['volumev3', 'volume']
else:
candidates = ['volumev2', 'volume']
for service_name in candidates:
try:
@ -1049,8 +1044,8 @@ def message_list(request, search_opts=None):
def is_volume_service_enabled(request):
return bool(
# FIXME: 'block-storage' is also a valid service_type for cinder
base.is_service_enabled(request, 'volumev3') or
base.is_service_enabled(request, 'volumev2') or
base.is_service_enabled(request, 'volume')
)

View File

@ -77,7 +77,7 @@ class CinderServicesTab(tabs.TableTab):
slug = tables.CinderServicesTable.Meta.name
template_name = constants.INFO_DETAIL_TEMPLATE_NAME
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)

View File

@ -21,7 +21,7 @@ class Snapshots(horizon.Panel):
name = _("Snapshots")
slug = 'snapshots'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "context_is_admin"),)

View File

@ -21,7 +21,7 @@ class VolumeTypes(horizon.Panel):
name = _("Volume Types")
slug = 'volume_types'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "volume_extension:types_manage"),)

View File

@ -19,7 +19,7 @@ class Volumes(horizon.Panel):
name = _("Volumes")
slug = "volumes"
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "context_is_admin"),)

View File

@ -252,9 +252,9 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({api.cinder: ['volume_get', 'volume_unmanage']})
def test_unmanage_volume(self):
# important - need to get the v2 cinder volume which has host data
# important - need to get the v3 cinder volume which has host data
volume_list = [x for x in self.cinder_volumes.list()
if x.name == 'v2_volume']
if x.name == 'v3_volume']
volume = volume_list[0]
form_data = {'volume_name': volume.name,
'host_name': 'host@backend-name#pool',
@ -276,7 +276,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({api.cinder: ['volume_get', 'pool_list']})
def test_volume_migrate_get(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
self.mock_pool_list.return_value = self.cinder_pools.list()
self.mock_volume_get.return_value = volume
@ -293,7 +293,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({api.cinder: ['volume_get']})
def test_volume_migrate_get_volume_get_exception(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
self.mock_volume_get.side_effect = self.exceptions.cinder
url = reverse('horizon:admin:volumes:migrate',
@ -306,7 +306,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({api.cinder: ['volume_get', 'pool_list']})
def test_volume_migrate_list_pool_get_exception(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
self.mock_volume_get.return_value = volume
self.mock_pool_list.side_effect = self.exceptions.cinder
@ -323,7 +323,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({
api.cinder: ['volume_migrate', 'volume_get', 'pool_list']})
def test_volume_migrate_post(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
host = self.cinder_pools.first().name
self.mock_volume_get.return_value = volume
@ -345,7 +345,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({
api.cinder: ['volume_migrate', 'volume_get', 'pool_list']})
def test_volume_migrate_post_api_exception(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
host = self.cinder_pools.first().name
self.mock_volume_get.return_value = volume
@ -365,7 +365,7 @@ class VolumeTests(test.BaseAdminViewTests):
@test.create_mocks({api.cinder: ['volume_get']})
def test_update_volume_status_get(self):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
self.mock_volume_get.return_value = volume
url = reverse('horizon:admin:volumes:update_status',

View File

@ -23,7 +23,7 @@ class Backups(horizon.Panel):
name = _("Backups")
slug = 'backups'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "backup:get_all"),)

View File

@ -21,7 +21,7 @@ class Snapshots(horizon.Panel):
name = _("Snapshots")
slug = 'snapshots'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "volume:get_all_snapshots"),)

View File

@ -605,14 +605,9 @@
var volumeDeferred = $q.defer();
var volumeSnapshotDeferred = $q.defer();
var absoluteLimitsDeferred = $q.defer();
serviceCatalog
.ifTypeEnabled('volumev2')
.then(onVolumeServiceEnabled, onCheckVolumeV3);
function onCheckVolumeV3() {
serviceCatalog
.ifTypeEnabled('volumev3')
.then(onVolumeServiceEnabled, resolvePromises);
}
function onVolumeServiceEnabled() {
model.volumeBootable = true;
model.allowCreateVolumeFromImage = true;

View File

@ -235,8 +235,6 @@
if (theType === 'network' && neutronEnabled) {
deferred.resolve();
} else if (theType === 'volumev2' && cinderEnabled) {
deferred.resolve();
} else if (theType === 'volumev3' && cinderEnabled) {
deferred.resolve();
} else {

View File

@ -21,7 +21,7 @@ class Volumes(horizon.Panel):
name = _("Volumes")
slug = 'volumes'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2',
('openstack.services.volume',
'openstack.services.volumev3'),
)
policy_rules = (("volume", "volume:get_all"),)

View File

@ -1496,7 +1496,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
@mock.patch.object(cinder, 'tenant_absolute_limits')
@mock.patch.object(cinder, 'volume_get')
def test_get_data(self, mock_get, mock_limits, mock_quotas):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
volume._apiresource.name = ""
mock_get.return_value = volume
@ -1693,7 +1693,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
@mock.patch.object(cinder, 'volume_upload_to_image')
@mock.patch.object(cinder, 'volume_get')
def test_upload_to_image(self, mock_get, mock_upload, mock_schemas_list):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
loaded_resp = {'container_format': 'bare',
'disk_format': 'raw',
'id': '741fe2ac-aa2f-4cec-82a9-4994896b43fb',
@ -1782,7 +1782,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
@mock.patch.object(cinder, 'volume_get')
def test_retype_volume_supported_action_item(self, mock_get,
mock_limits, mock_quotas):
volume = self.cinder_volumes.get(name='v2_volume')
volume = self.cinder_volumes.get(name='v3_volume')
limits = self.cinder_limits['absolute']
mock_get.return_value = volume

View File

@ -72,8 +72,7 @@
function initAction() {
createVolumePromise = policy.ifAllowed({rules: [['volume', 'volume:create']]});
if (serviceCatalog.ifTypeEnabled('volumev2') ||
serviceCatalog.ifTypeEnabled('volumev3')) {
if (serviceCatalog.ifTypeEnabled('volumev3')) {
volumeServiceEnabledPromise = true;
} else {
volumeServiceEnabledPromise = false;

View File

@ -12,29 +12,45 @@
# License for the specific language governing permissions and limitations
# under the License.
from cinderclient.v2 import availability_zones
from cinderclient.v2.contrib import list_extensions as cinder_list_extensions
from cinderclient.v2 import pools
from cinderclient.v2 import qos_specs
from cinderclient.v2 import quotas
from cinderclient.v2 import services
from cinderclient.v2 import volume_backups as vol_backups
from cinderclient.v2 import volume_encryption_types as vol_enc_types
from cinderclient.v2 import volume_snapshots as vol_snaps
from cinderclient.v2 import volume_transfers
from cinderclient.v2 import volume_type_access
from cinderclient.v2 import volume_types
from cinderclient.v2 import volumes
from cinderclient.v3 import availability_zones
from cinderclient.v3.contrib import list_extensions as cinder_list_extensions
from cinderclient.v3 import group_snapshots
from cinderclient.v3 import group_types
from cinderclient.v3 import groups
from cinderclient.v3 import messages
from cinderclient.v3 import pools
from cinderclient.v3 import qos_specs
from cinderclient.v3 import services
from cinderclient.v3 import volume_backups as vol_backups
from cinderclient.v3 import volume_encryption_types as vol_enc_types
from cinderclient.v3 import volume_snapshots as vol_snaps
from cinderclient.v3 import volume_type_access
from cinderclient.v3 import volume_types
from cinderclient.v3 import volumes
from openstack_dashboard import api
from openstack_dashboard.api import cinder as cinder_api
from openstack_dashboard.test.test_data import utils
from openstack_dashboard.usage import quotas as usage_quotas
# FIXME: workaround for some classes being missing from cinderclient.v3
# in python-cinderclient versions < 8.0.0. These can become simple
# 'from cinderclient.v3 import xxx' above after we have
# python-cinderclient>=8.0.0 in requirements.txt
try:
# pylint: disable=ungrouped-imports
from cinderclient.v3.quotas import QuotaSet as _qs # noqa
from cinderclient.v3 import quotas # noqa
except ImportError:
from cinderclient.v2 import quotas
try:
# pylint: disable=ungrouped-imports
from cinderclient.v3.volume_transfers import VolumeTransfer as _vt # noqa
from cinderclient.v3 import volume_transfers # noqa
except ImportError:
from cinderclient.v2 import volume_transfers
def data(TEST):
TEST.cinder_messages = utils.TestDataContainer()
@ -186,12 +202,12 @@ def data(TEST):
{'volume_type_id': '1', 'project_id': '1'})
TEST.cinder_type_access.add(vol_type_access1)
# Volumes - Cinder v2
volume_v2 = volumes.Volume(
# Volumes - Cinder v3 (v2 removed in Xena)
volume_v3 = volumes.Volume(
volumes.VolumeManager(None),
{'id': "31023e92-8008-4c8b-8059-7f2293ff1234",
'name': 'v2_volume',
'description': "v2 Volume Description",
'name': 'v3_volume',
'description': "v3 Volume Description",
'status': 'available',
'size': 20,
'created_at': '2014-01-27 10:30:00',
@ -199,9 +215,9 @@ def data(TEST):
'os-vol-host-attr:host': 'host@backend-name#pool',
'bootable': 'true',
'attachments': []})
volume_v2.bootable = 'true'
volume_v3.bootable = 'true'
TEST.cinder_volumes.add(api.cinder.Volume(volume_v2))
TEST.cinder_volumes.add(api.cinder.Volume(volume_v3))
snapshot = vol_snaps.Snapshot(
vol_snaps.SnapshotManager(None),
@ -216,7 +232,7 @@ def data(TEST):
vol_snaps.SnapshotManager(None),
{'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0f',
'name': '',
'description': 'v2 volume snapshot description',
'description': 'v3 volume snapshot description',
'size': 80,
'created_at': '2014-01-27 10:30:00',
'status': 'available',
@ -225,7 +241,7 @@ def data(TEST):
vol_snaps.SnapshotManager(None),
{'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0e',
'name': '',
'description': 'v2 volume snapshot description 2',
'description': 'v3 volume snapshot description 2',
'size': 80,
'created_at': '2014-01-27 10:30:00',
'status': 'available',
@ -234,7 +250,7 @@ def data(TEST):
vol_snaps.SnapshotManager(None),
{'id': 'cd6be1eb-82ca-4587-8036-13c37c00c2b1',
'name': '',
'description': 'v2 volume snapshot with metadata description',
'description': 'v3 volume snapshot with metadata description',
'size': 80,
'created_at': '2014-01-27 10:30:00',
'status': 'available',
@ -320,7 +336,7 @@ def data(TEST):
TEST.cinder_volume_encryption.add(vol_enc_metadata1)
TEST.cinder_volume_encryption.add(vol_unenc_metadata1)
# v2 extensions
# v3 extensions
extensions = [
{'alias': 'os-services',
@ -333,11 +349,6 @@ def data(TEST):
'links': '[]',
'name': 'AdminActions',
'updated': '2012-08-25T00:00:00+00:00'},
{'alias': 'os-volume-transfer',
'description': 'Volume transfer management support.',
'links': '[]',
'name': 'VolumeTransfer',
'updated': '2013-05-29T00:00:00+00:00'},
]
extensions = [
cinder_list_extensions.ListExtResource(
@ -540,7 +551,7 @@ def data(TEST):
vol_snaps.SnapshotManager(None),
{'id': 'cd6be1eb-82ca-4587-8036-13c37c00c2b1',
'name': '',
'description': 'v2 volume snapshot with metadata description',
'description': 'v3 volume snapshot with metadata description',
'size': 80,
'status': 'available',
'volume_id': '7e4efa56-9ca1-45ff-b83c-2efb2383930d',

View File

@ -65,29 +65,6 @@ SERVICE_CATALOG = [
"interface": "public",
"url": "http://public.nova2.example.com:8774/v2"}
]},
{"type": "volumev2",
"name": "cinderv2",
"endpoints_links": [],
"endpoints": [
{"region": "RegionOne",
"interface": "admin",
"url": "http://admin.cinder.example.com:8776/v2"},
{"region": "RegionOne",
"interface": "internal",
"url": "http://int.cinder.example.com:8776/v2"},
{"region": "RegionOne",
"interface": "public",
"url": "http://public.cinder.example.com:8776/v2"},
{"region": "RegionTwo",
"interface": "admin",
"url": "http://admin.cinder2.example.com:8776/v2"},
{"region": "RegionTwo",
"interface": "internal",
"url": "http://int.cinder2.example.com:8776/v2"},
{"region": "RegionTwo",
"interface": "public",
"url": "http://public.cinder.example.com:8776/v2"}
]},
{"type": "volumev3",
"name": "cinderv3",
"endpoints_links": [],

View File

@ -711,19 +711,19 @@ class KeystoneRestTestCase(test.TestCase):
'id': '2b5bc2e59b094f898a43f5e8ce446240',
'name': 'glance'},
{'endpoints': [
{'url': 'http://cool_url/volume/v2/test',
{'url': 'http://cool_url/volume/v3/test',
'interface': 'public',
'region': 'RegionOne',
'region_id': 'RegionOne',
'id': '29a629afb80547ea9baa4266e97b4cb5'},
{'url': 'http://cool_url/volume/v2/test',
{'url': 'http://cool_url/volume/v3/test',
'interface': 'admin',
'region': 'RegionOne',
'region_id': 'RegionOne',
'id': '29a629afb80547ea9baa4266e97b4cb5'}],
'type': 'volumev2',
'type': 'volumev3',
'id': '55ef272cfa714e54b8f2046c157b027d',
'name': 'cinderv2'},
'name': 'cinderv3'},
{'endpoints': [
{'url': 'http://cool_url/compute/v2/check',
'interface': 'internal',
@ -745,14 +745,14 @@ class KeystoneRestTestCase(test.TestCase):
'id': '2b5bc2e59b094f898a43f5e8ce446240',
'name': 'glance'},
{'endpoints': [
{'url': 'http://cool_url/volume/v2/test',
{'url': 'http://cool_url/volume/v3/test',
'interface': 'public',
'region': 'RegionOne',
'region_id': 'RegionOne',
'id': '29a629afb80547ea9baa4266e97b4cb5'}],
'type': 'volumev2',
'type': 'volumev3',
'id': '55ef272cfa714e54b8f2046c157b027d',
'name': 'cinderv2'}]
'name': 'cinderv3'}]
self.assertEqual(content, jsonutils.loads(response.content))
#

View File

@ -474,44 +474,35 @@ class CinderApiVersionTests(test.TestCase):
client = api.cinder.cinderclient(self.request)
self.assertIsInstance(client, cinder_client.v3.client.Client)
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
def test_v2_setting_returns_v2_client(self):
# FIXME(e0ne): this is a temporary workaround to bypass
# @memoized_with_request decorator caching. We have to find a better
# solution instead this hack.
self.request.user.username = 'test_user_cinder_v2'
client = api.cinder.cinderclient(self.request)
self.assertIsInstance(client, cinder_client.v2.client.Client)
def test_get_v2_volume_attributes(self):
# Get a v2 volume
volume = self.cinder_volumes.get(name="v2_volume")
def test_get_v3_volume_attributes(self):
# Get a v3 volume
volume = self.cinder_volumes.get(name="v3_volume")
self.assertTrue(hasattr(volume._apiresource, 'name'))
name = "A v2 test volume name"
description = "A v2 volume description"
name = "A v3 test volume name"
description = "A v3 volume description"
setattr(volume._apiresource, 'name', name)
setattr(volume._apiresource, 'description', description)
self.assertEqual(name, volume.name)
self.assertEqual(description, volume.description)
def test_get_v2_snapshot_attributes(self):
# Get a v2 snapshot
def test_get_v3_snapshot_attributes(self):
# Get a v3 snapshot
snapshot = self.cinder_volume_snapshots.get(
description="v2 volume snapshot description")
description="v3 volume snapshot description")
self.assertFalse(hasattr(snapshot._apiresource, 'display_name'))
name = "A v2 test snapshot name"
description = "A v2 snapshot description"
name = "A v3 test snapshot name"
description = "A v3 snapshot description"
setattr(snapshot._apiresource, 'name', name)
setattr(snapshot._apiresource, 'description', description)
self.assertEqual(name, snapshot.name)
self.assertEqual(description, snapshot.description)
def test_get_v2_snapshot_metadata(self):
# Get a v2 snapshot with metadata
def test_get_v3_snapshot_metadata(self):
# Get a v3 snapshot with metadata
snapshot = self.cinder_volume_snapshots.get(
description="v2 volume snapshot with metadata description")
description="v3 volume snapshot with metadata description")
self.assertTrue(hasattr(snapshot._apiresource, 'metadata'))
self.assertFalse(hasattr(snapshot._apiresource, 'display_name'))

View File

@ -0,0 +1,9 @@
---
upgrade:
- |
With this release, Horizon uses only the Block Storage API v3.
Horizon has been using the Block Storage API v3 by default since
Queens, so this change should not impact Horizon functionality.
(The Block Storage API v2 was deprecated by the Cinder project
in the Pike release, and is scheduled to be removed in the Xena
release.)