diff --git a/nova/conf/cinder.py b/nova/conf/cinder.py index ae3f0f432b0c..27346a9f0783 100644 --- a/nova/conf/cinder.py +++ b/nova/conf/cinder.py @@ -33,6 +33,9 @@ Possible values: * Format is separated values of the form: :: +Note: Nova does not support the Cinder v1 API since the Nova 15.0.0 Ocata +release. + Related options: * endpoint_template - Setting this option will override catalog_info @@ -45,7 +48,10 @@ this template for cinder endpoint Possible values: * URL for cinder endpoint API - e.g. http://localhost:8776/v1/%(project_id)s + e.g. http://localhost:8776/v2/%(project_id)s + +Note: Nova does not support the Cinder v1 API since the Nova 15.0.0 Ocata +release. Related options: diff --git a/nova/exception.py b/nova/exception.py index 83ec87913175..c01b5c540fac 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -141,6 +141,10 @@ class CinderConnectionFailed(NovaException): msg_fmt = _("Connection to cinder host failed: %(reason)s") +class UnsupportedCinderAPIVersion(NovaException): + msg_fmt = _('Nova does not support Cinder API version %(version)s') + + class Forbidden(NovaException): msg_fmt = _("Forbidden") code = 403 diff --git a/nova/tests/unit/test_cinder.py b/nova/tests/unit/test_cinder.py index b35e7a6e9286..c6a5655c6729 100644 --- a/nova/tests/unit/test_cinder.py +++ b/nova/tests/unit/test_cinder.py @@ -14,10 +14,9 @@ import collections -from cinderclient.v1 import client as cinder_client_v1 from cinderclient.v2 import client as cinder_client_v2 +import mock from requests_mock.contrib import fixture -from testtools import matchers import nova.conf from nova import context @@ -115,63 +114,25 @@ class BaseCinderTestCase(object): self.assertEqual(cacert, self.create_client().client.session.verify) -class CinderTestCase(BaseCinderTestCase, test.NoDBTestCase): - """Test case for cinder volume v1 api.""" +# NOTE(mriedem): This does not extend BaseCinderTestCase because Cinder v1 is +# no longer supported, this is just to test that trying to use v1 fails. +class CinderV1TestCase(test.NoDBTestCase): - URL = "http://localhost:8776/v1/project_id" - - CATALOG = [{ - "type": "volumev2", - "name": "cinderv2", - "endpoints": [{"publicURL": URL}] - }] - - def create_client(self): - c = super(CinderTestCase, self).create_client() - self.assertIsInstance(c, cinder_client_v1.Client) - return c - - def stub_volume(self, **kwargs): - volume = { - 'display_name': None, - 'display_description': None, - "attachments": [], - "availability_zone": "cinder", - "created_at": "2012-09-10T00:00:00.000000", - "id": _volume_id, - "metadata": {}, - "size": 1, - "snapshot_id": None, - "status": "available", - "volume_type": "None", - "bootable": "true", - "multiattach": "true" - } - volume.update(kwargs) - return volume - - def test_cinder_endpoint_template(self): - endpoint = 'http://other_host:8776/v1/%(project_id)s' - self.flags(endpoint_template=endpoint, group='cinder') - self.assertEqual('http://other_host:8776/v1/project_id', - self.create_client().client.endpoint_override) - - def test_get_non_existing_volume(self): - self.requests.get(self.URL + '/volumes/nonexisting', - status_code=404) - - self.assertRaises(exception.VolumeNotFound, self.api.get, self.context, - 'nonexisting') - - def test_volume_with_image_metadata(self): - v = self.stub_volume(id='1234', volume_image_metadata=_image_metadata) - m = self.requests.get(self.URL + '/volumes/5678', json={'volume': v}) - - volume = self.api.get(self.context, '5678') - self.assertThat(m.last_request.path, - matchers.EndsWith('/volumes/5678')) - self.assertIn('volume_image_metadata', volume) - self.assertEqual(_image_metadata, volume['volume_image_metadata']) + @mock.patch.object(cinder.ks_loading, 'load_session_from_conf_options') + @mock.patch.object(cinder.cinder_client, 'get_volume_api_from_url', + return_value='1') + def test_cinderclient_unsupported_v1(self, get_api_version, load_session): + """Tests that we fail if trying to use Cinder v1.""" + self.flags(catalog_info='volume:cinder:publicURL', group='cinder') + # setup mocks + get_endpoint = mock.Mock( + return_value='http://localhost:8776/v1/%(project_id)s') + fake_session = mock.Mock(get_endpoint=get_endpoint) + load_session.return_value = fake_session + ctxt = context.get_admin_context() + self.assertRaises(exception.UnsupportedCinderAPIVersion, + cinder.cinderclient, ctxt) + get_api_version.assert_called_once_with(get_endpoint.return_value) class CinderV2TestCase(BaseCinderTestCase, test.NoDBTestCase): diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py index 6be828a63bad..addb43726fde 100644 --- a/nova/volume/cinder.py +++ b/nova/volume/cinder.py @@ -25,7 +25,6 @@ import sys from cinderclient import client as cinder_client from cinderclient import exceptions as cinder_exception -from cinderclient.v1 import client as v1_client from keystoneauth1 import exceptions as keystone_exception from keystoneauth1 import loading as ks_loading from oslo_log import log as logging @@ -47,7 +46,6 @@ CONF = nova.conf.CONF LOG = logging.getLogger(__name__) _SESSION = None -_V1_ERROR_RAISED = False def reset_globals(): @@ -59,7 +57,6 @@ def reset_globals(): def cinderclient(context): global _SESSION - global _V1_ERROR_RAISED if not _SESSION: _SESSION = ks_loading.load_session_from_conf_options( @@ -87,13 +84,8 @@ def cinderclient(context): # values. version = cinder_client.get_volume_api_from_url(url) - if version == '1' and not _V1_ERROR_RAISED: - msg = _LW('Cinder V1 API is deprecated as of the Juno ' - 'release, and Nova is still configured to use it. ' - 'Enable the V2 API in Cinder and set ' - 'cinder.catalog_info in nova.conf to use it.') - LOG.warning(msg) - _V1_ERROR_RAISED = True + if version == '1': + raise exception.UnsupportedCinderAPIVersion(version=version) return cinder_client.Client(version, session=_SESSION, @@ -132,14 +124,8 @@ def _untranslate_volume_summary_view(context, vol): d['attach_status'] = 'attached' else: d['attach_status'] = 'detached' - # NOTE(dzyu) volume(cinder) v2 API uses 'name' instead of 'display_name', - # and use 'description' instead of 'display_description' for volume. - if hasattr(vol, 'display_name'): - d['display_name'] = vol.display_name - d['display_description'] = vol.display_description - else: - d['display_name'] = vol.name - d['display_description'] = vol.description + d['display_name'] = vol.name + d['display_description'] = vol.description # TODO(jdg): Information may be lost in this translation d['volume_type_id'] = vol.volume_type d['snapshot_id'] = vol.snapshot_id @@ -163,15 +149,8 @@ def _untranslate_snapshot_summary_view(context, snapshot): d['progress'] = snapshot.progress d['size'] = snapshot.size d['created_at'] = snapshot.created_at - - # NOTE(dzyu) volume(cinder) v2 API uses 'name' instead of 'display_name', - # 'description' instead of 'display_description' for snapshot. - if hasattr(snapshot, 'display_name'): - d['display_name'] = snapshot.display_name - d['display_description'] = snapshot.display_description - else: - d['display_name'] = snapshot.name - d['display_description'] = snapshot.description + d['display_name'] = snapshot.name + d['display_description'] = snapshot.description d['volume_id'] = snapshot.volume_id d['project_id'] = snapshot.project_id @@ -438,14 +417,9 @@ class API(object): project_id=context.project_id, availability_zone=availability_zone, metadata=metadata, - imageRef=image_id) - - if isinstance(client, v1_client.Client): - kwargs['display_name'] = name - kwargs['display_description'] = description - else: - kwargs['name'] = name - kwargs['description'] = description + imageRef=image_id, + name=name, + description=description) item = client.volumes.create(size, **kwargs) return _untranslate_volume_summary_view(context, item) diff --git a/releasenotes/notes/ocata-drop-cinder-v1-support-e383bc3623dbdb21.yaml b/releasenotes/notes/ocata-drop-cinder-v1-support-e383bc3623dbdb21.yaml new file mode 100644 index 000000000000..9b7ce86b645a --- /dev/null +++ b/releasenotes/notes/ocata-drop-cinder-v1-support-e383bc3623dbdb21.yaml @@ -0,0 +1,3 @@ +--- +upgrade: + - Nova no longer supports the deprecated Cinder v1 API.