Raise correct exception when validate_connector failed

Cinder volume manager uses validate_connector() method to verify if required
information is in connector when handling initialize_connection() request.
validate_connector() is actually a pure input validation method, basically
checking if 'initiator' or 'wwpns' is in connector if storage protocol is
iSCSI or FC.  However, when required information is missing, currently drivers
raises either VolumeBackendAPIException or VolumeDriverException, which would
then bubble up to API and then to user (Nova) as InternalServerError.

This change adds a new exception - InvalidConnectorException, that drivers
should raise when connector is found not valid.  With that, Cinder API would
raise BadRequest instead to user, suggesting things are missing in request.

Change-Id: I4f04f5d0c558404836a2bd270f7f22f3f2d4f314
Closes-bug: #1409580
This commit is contained in:
Zhiteng Huang
2015-01-12 13:27:15 +08:00
parent 5f17a953ea
commit c7d97c4ad7
14 changed files with 51 additions and 39 deletions

View File

@@ -195,6 +195,9 @@ class VolumeActionsController(wsgi.Controller):
info = self.volume_api.initialize_connection(context,
volume,
connector)
except exception.InvalidInput as err:
raise webob.exc.HTTPBadRequest(
explanation=err)
except exception.VolumeBackendAPIException as error:
msg = _("Unable to fetch connection information from backend.")
raise webob.exc.HTTPInternalServerError(explanation=msg)

View File

@@ -492,6 +492,10 @@ class FailedCmdWithDump(VolumeDriverException):
message = _("Operation failed with status=%(status)s. Full dump: %(data)s")
class InvalidConnectorException(VolumeDriverException):
message = _("Connector doesn't have required information: %(missing)s")
class GlanceMetadataExists(Invalid):
message = _("Glance metadata cannot be updated, key %(key)s"
" exists for volume id %(volume_id)s")

View File

@@ -563,7 +563,7 @@ class AdminActionsTest(test.TestCase):
connector = {}
# start service to handle rpc messages for attach requests
svc = self.start_service('volume', host='test')
self.assertRaises(exception.VolumeBackendAPIException,
self.assertRaises(exception.InvalidInput,
self.volume_api.initialize_connection,
ctx, volume, connector)
# cleanup

View File

@@ -129,7 +129,7 @@ class TestBaseISCSITargetDriver(test.TestCase):
def test_validate_connector(self):
bad_connector = {'no_initiator': 'nada'}
self.assertRaises(exception.VolumeBackendAPIException,
self.assertRaises(exception.InvalidConnectorException,
self.target.validate_connector,
bad_connector)

View File

@@ -1485,7 +1485,7 @@ class HuaweiTFCDriverTestCase(test.TestCase):
def test_validate_connector_failed(self):
invalid_connector = {'host': 'testhost'}
self.assertRaises(exception.VolumeBackendAPIException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector,
invalid_connector)

View File

@@ -843,9 +843,9 @@ class FlashSystemDriverTestCase(test.TestCase):
self.driver._protocol = 'FC'
self.driver.validate_connector(conn_fc)
self.driver.validate_connector(conn_both)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_iscsi)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_neither)
# clear environment

View File

@@ -2042,24 +2042,24 @@ class StorwizeSVCDriverTestCase(test.TestCase):
self.driver._state['enabled_protocols'] = set(['iSCSI'])
self.driver.validate_connector(conn_iscsi)
self.driver.validate_connector(conn_both)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_fc)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_neither)
self.driver._state['enabled_protocols'] = set(['FC'])
self.driver.validate_connector(conn_fc)
self.driver.validate_connector(conn_both)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_iscsi)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_neither)
self.driver._state['enabled_protocols'] = set(['iSCSI', 'FC'])
self.driver.validate_connector(conn_iscsi)
self.driver.validate_connector(conn_fc)
self.driver.validate_connector(conn_both)
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.driver.validate_connector, conn_neither)
def test_storwize_svc_host_maps(self):

View File

@@ -4127,7 +4127,7 @@ class ISCSITestCase(DriverTestCase):
# Validate a connector without the initiator
connector = {'ip': '10.0.0.2', 'host': 'fakehost'}
self.assertRaises(exception.VolumeBackendAPIException,
self.assertRaises(exception.InvalidConnectorException,
iscsi_driver.validate_connector, connector)
@@ -4213,27 +4213,27 @@ class FibreChannelTestCase(DriverTestCase):
def test_validate_connector_no_wwpns(self):
"""validate_connector() throws exception when it has no wwpns."""
connector = {'wwnns': ["not empty"]}
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.volume.driver.validate_connector, connector)
def test_validate_connector_empty_wwpns(self):
"""validate_connector() throws exception when it has empty wwpns."""
connector = {'wwpns': [],
'wwnns': ["not empty"]}
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.volume.driver.validate_connector, connector)
def test_validate_connector_no_wwnns(self):
"""validate_connector() throws exception when it has no wwnns."""
connector = {'wwpns': ["not empty"]}
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.volume.driver.validate_connector, connector)
def test_validate_connector_empty_wwnns(self):
"""validate_connector() throws exception when it has empty wwnns."""
connector = {'wwnns': [],
'wwpns': ["not empty"]}
self.assertRaises(exception.VolumeDriverException,
self.assertRaises(exception.InvalidConnectorException,
self.volume.driver.validate_connector, connector)

View File

@@ -1079,11 +1079,12 @@ class ISCSIDriver(VolumeDriver):
def validate_connector(self, connector):
# iSCSI drivers require the initiator information
if 'initiator' not in connector:
err_msg = (_('The volume driver requires the iSCSI initiator '
'name in the connector.'))
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
required = 'initiator'
if required not in connector:
err_msg = (_LE('The volume driver requires %(data)s '
'in the connector.'), {'data': required})
LOG.error(*err_msg)
raise exception.InvalidConnectorException(missing=required)
def terminate_connection(self, volume, connector, **kwargs):
pass
@@ -1361,8 +1362,9 @@ class FibreChannelDriver(VolumeDriver):
def validate_connector_has_setting(connector, setting):
"""Test for non-empty setting in connector."""
if setting not in connector or not connector[setting]:
msg = (_(
msg = (_LE(
"FibreChannelDriver validate_connector failed. "
"No '%s'. Make sure HBA state is Online.") % setting)
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
"No '%(setting)s'. Make sure HBA state is Online."),
{'setting': setting})
LOG.error(*msg)
raise exception.InvalidConnectorException(missing=setting)

View File

@@ -21,7 +21,7 @@ import re
import time
from cinder import exception
from cinder.i18n import _, _LW
from cinder.i18n import _, _LE, _LW
from cinder.openstack.common import log as logging
from cinder.volume import driver
from cinder.volume.drivers.huawei import huawei_utils
@@ -435,10 +435,10 @@ class HuaweiTFCDriver(driver.FibreChannelDriver):
def validate_connector(self, connector):
"""Check for wwpns in connector."""
if 'wwpns' not in connector:
err_msg = (_('validate_connector: The FC driver requires the'
err_msg = (_LE('validate_connector: The FC driver requires the'
' wwpns in the connector.'))
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
raise exception.InvalidConnectorException(missing='wwpns')
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):

View File

@@ -1137,11 +1137,11 @@ class FlashSystemDriver(san.SanDriver):
def validate_connector(self, connector):
"""Check connector."""
if 'FC' != self._protocol or 'wwpns' not in connector:
msg = (_('The connector does not contain the '
'required information.'))
if 'FC' == self._protocol and 'wwpns' not in connector:
msg = (_LE('The connector does not contain the '
'required information: wwpns is missing'))
LOG.error(msg)
raise exception.VolumeDriverException(data=msg)
raise exception.InvalidConnectorException(missing='wwpns')
def create_volume(self, volume):
"""Create volume."""

View File

@@ -308,10 +308,11 @@ class StorwizeSVCDriver(san.SanDriver):
if 'FC' in self._state['enabled_protocols'] and 'wwpns' in connector:
valid = True
if not valid:
msg = (_('The connector does not contain the required '
msg = (_LE('The connector does not contain the required '
'information.'))
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
raise exception.InvalidConnectorException(
missing='initiator or wwpns')
def _get_vdisk_params(self, type_id, volume_type=None,
volume_metadata=None):

View File

@@ -886,8 +886,10 @@ class VolumeManager(manager.SchedulerDependentManager):
utils.require_driver_initialized(self.driver)
try:
self.driver.validate_connector(connector)
except exception.InvalidConnectorException as err:
raise exception.InvalidInput(reason=err)
except Exception as err:
err_msg = (_('Unable to fetch connection information from '
err_msg = (_('Unable to validate connector information in '
'backend: %(err)s') % {'err': err})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)

View File

@@ -184,8 +184,8 @@ class ISCSITarget(driver.Target):
def validate_connector(self, connector):
# NOTE(jdg): api passes in connector which is initiator info
if 'initiator' not in connector:
err_msg = (_('The volume driver requires the iSCSI initiator '
err_msg = (_LE('The volume driver requires the iSCSI initiator '
'name in the connector.'))
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
raise exception.InvalidConnectorException(missing='initiator')
return True