Merge "Use microversions for new style volume attachments"
This commit is contained in:
commit
7681d3dcc0
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue