Merge "Use microversions for new style volume attachments"

This commit is contained in:
Jenkins 2017-06-08 17:57:47 +00:00 committed by Gerrit Code Review
commit 7681d3dcc0
3 changed files with 123 additions and 3 deletions

View File

@ -149,6 +149,13 @@ class UnsupportedCinderAPIVersion(NovaException):
msg_fmt = _('Nova does not support Cinder API version %(version)s')
class CinderAPIVersionNotAvailable(NovaException):
"""Used to indicate that a requested Cinder API version, generally a
microversion, is not available.
"""
msg_fmt = _('Cinder API version %(version)s is not available.')
class Forbidden(NovaException):
msg_fmt = _("Forbidden")
code = 403

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from cinderclient import api_versions as cinder_api_versions
from cinderclient import exceptions as cinder_exception
from keystoneclient import exceptions as keystone_exception
import mock
@ -717,3 +718,79 @@ class CinderApiTestCase(test.NoDBTestCase):
my_func.side_effect = raised_exc
self.assertRaises(expected_exc, wrapper(my_func), 'foo', 'bar', 'baz')
class CinderClientTestCase(test.NoDBTestCase):
"""Used to test constructing a cinder client object at various versions."""
def setUp(self):
super(CinderClientTestCase, self).setUp()
cinder.reset_globals()
self.ctxt = context.RequestContext('fake-user', 'fake-project')
# Mock out the keystoneauth stuff.
self.mock_session = mock.Mock(
autospec='keystoneauth1.loading.session.Session')
load_session = mock.patch('keystoneauth1.loading.'
'load_session_from_conf_options',
return_value=self.mock_session).start()
self.addCleanup(load_session.stop)
@mock.patch('cinderclient.client.get_volume_api_from_url',
return_value='3')
def test_create_v3_client_no_microversion(self, get_volume_api):
"""Tests that creating a v3 client, which is the default, and without
specifying a microversion will default to 3.0 as the version to use.
"""
client = cinder.cinderclient(self.ctxt)
self.assertEqual(cinder_api_versions.APIVersion('3.0'),
client.api_version)
get_volume_api.assert_called_once_with(
self.mock_session.get_endpoint.return_value)
@mock.patch('cinderclient.client.get_volume_api_from_url',
return_value='2')
def test_create_v2_client_with_microversion_fails(self, get_volume_api):
"""Tests that requesting a microversion against a v2 client will raise
an exception.
"""
self.assertRaises(exception.CinderAPIVersionNotAvailable,
cinder.cinderclient, self.ctxt, microversion='3.27')
get_volume_api.assert_called_once_with(
self.mock_session.get_endpoint.return_value)
@mock.patch('cinderclient.client.get_volume_api_from_url',
return_value='3')
@mock.patch('cinderclient.client.get_highest_client_server_version',
return_value=2.0) # Fake the case that cinder is really old.
def test_create_v3_client_with_microversion_too_new(self,
get_highest_version,
get_volume_api):
"""Tests that creating a v3 client and requesting a microversion that
is either too new for the server (or client) to support raises an
exception.
"""
self.assertRaises(exception.CinderAPIVersionNotAvailable,
cinder.cinderclient, self.ctxt, microversion='3.27')
get_volume_api.assert_called_once_with(
self.mock_session.get_endpoint.return_value)
get_highest_version.assert_called_once_with(
self.mock_session.get_endpoint.return_value)
@mock.patch('cinderclient.client.get_highest_client_server_version',
return_value=float(cinder_api_versions.MAX_VERSION))
@mock.patch('cinderclient.client.get_volume_api_from_url',
return_value='3')
def test_create_v3_client_with_microversion_available(self,
get_volume_api,
get_highest_version):
"""Tests that creating a v3 client and requesting a microversion that
is available in the server and supported by the client will result in
creating a Client object with the requested microversion.
"""
client = cinder.cinderclient(self.ctxt, microversion='3.27')
self.assertEqual(cinder_api_versions.APIVersion('3.27'),
client.api_version)
get_volume_api.assert_called_once_with(
self.mock_session.get_endpoint.return_value)
get_highest_version.assert_called_once_with(
self.mock_session.get_endpoint.return_value)

View File

@ -23,6 +23,7 @@ import copy
import functools
import sys
from cinderclient import api_versions as cinder_api_versions
from cinderclient import client as cinder_client
from cinderclient import exceptions as cinder_exception
from keystoneauth1 import exceptions as keystone_exception
@ -56,7 +57,37 @@ def reset_globals():
_SESSION = None
def cinderclient(context):
def _check_microversion(url, microversion):
"""Checks to see if the requested microversion is supported by the current
version of python-cinderclient and the volume API endpoint.
:param url: Cinder API endpoint URL.
:param microversion: Requested microversion. If not available at the given
API endpoint URL, a CinderAPIVersionNotAvailable exception is raised.
:returns: The microversion if it is available. This can be used to
construct the cinder v3 client object.
:raises: CinderAPIVersionNotAvailable if the microversion is not available.
"""
max_api_version = cinder_client.get_highest_client_server_version(url)
# get_highest_client_server_version returns a float which we need to cast
# to a str and create an APIVersion object to do our version comparison.
max_api_version = cinder_api_versions.APIVersion(str(max_api_version))
# Check if the max_api_version matches the requested minimum microversion.
if max_api_version.matches(microversion):
# The requested microversion is supported by the client and the server.
return microversion
raise exception.CinderAPIVersionNotAvailable(version=microversion)
def cinderclient(context, microversion=None):
"""Constructs a cinder client object for making API requests.
:param context: The nova request context for auth.
:param microversion: Optional microversion to check against the client.
This implies that Cinder v3 is required for any calls that require a
microversion. If the microversion is not available, this method will
raise an CinderAPIVersionNotAvailable exception.
"""
global _SESSION
if not _SESSION:
@ -89,13 +120,18 @@ def cinderclient(context):
raise exception.UnsupportedCinderAPIVersion(version=version)
if version == '2':
if microversion is not None:
# The Cinder v2 API does not support microversions.
raise exception.CinderAPIVersionNotAvailable(version=microversion)
LOG.warning("The support for the Cinder API v2 is deprecated, please "
"upgrade to Cinder API v3.")
if version == '3':
# TODO(ildikov): Add microversion support for picking up the new
# attach/detach API that was added in 3.27.
version = '3.0'
# Check to see a specific microversion is requested and if so, can it
# be handled by the backing server.
if microversion is not None:
version = _check_microversion(url, microversion)
return cinder_client.Client(version,
session=_SESSION,