Add supported driver checks to Zone Manager

This patch adds the supported driver checks to the
Fibre Channel Zone Manager.  This is the same mechanism
that is in Cinder volume drivers.

When a zone manager driver is marked as unsupported, the zone manager
won't allow it to work unless the enable_unsupported_driver=True is
added to the fc-zone-manager section in cinder.conf

Change-Id: If707b9005f60dc28eb99f46929fb700a2de0f755
Implements: blueprint zonemanager-supported-driver
This commit is contained in:
Walter A. Boring IV 2016-08-31 10:04:32 -07:00
parent b0c54b672b
commit a93314b679
5 changed files with 150 additions and 12 deletions

View File

@ -1014,6 +1014,10 @@ class FCSanLookupServiceException(CinderException):
message = _("Fibre Channel SAN Lookup failure: %(reason)s")
class ZoneManagerNotInitialized(CinderException):
message = _("Fibre Channel Zone Manager not initialized")
class BrocadeZoningCliException(CinderException):
message = _("Brocade Fibre Channel Zoning CLI error: %(reason)s")

View File

@ -49,11 +49,17 @@ class TestFCZoneManager(test.TestCase):
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def setUp(self, opt_mock):
super(TestFCZoneManager, self).setUp()
def __init__(self, *args, **kwargs):
super(TestFCZoneManager, self).__init__(*args, **kwargs)
def setup_fake_driver(self):
config = conf.Configuration(None)
config.fc_fabric_names = fabric_name
def fake_build_driver(self):
self.driver = mock.Mock(fc_zone_driver.FCZoneDriver)
self.set_initialized(True)
self.mock_object(fc_zone_manager.ZoneManager, '_build_driver',
fake_build_driver)
@ -61,13 +67,48 @@ class TestFCZoneManager(test.TestCase):
self.zm = fc_zone_manager.ZoneManager(configuration=config)
self.configuration = conf.Configuration(None)
self.configuration.fc_fabric_names = fabric_name
self.driver = mock.Mock(fc_zone_driver.FCZoneDriver)
def __init__(self, *args, **kwargs):
super(TestFCZoneManager, self).__init__(*args, **kwargs)
def test_unsupported_driver_disabled(self):
config = conf.Configuration(fc_zone_manager.zone_manager_opts,
'fc-zone-manager')
config.fc_fabric_names = fabric_name
config.enable_unsupported_driver = False
def fake_import(self, *args, **kwargs):
fake_driver = mock.Mock(fc_zone_driver.FCZoneDriver)
fake_driver.supported = False
return fake_driver
self.patch('oslo_utils.importutils.import_object',
fake_import)
zm = fc_zone_manager.ZoneManager(configuration=config)
self.assertFalse(zm.driver.supported)
self.assertFalse(zm.initialized)
def test_unsupported_driver_enabled(self):
config = conf.Configuration(None)
config.fc_fabric_names = fabric_name
def fake_import(self, *args, **kwargs):
fake_driver = mock.Mock(fc_zone_driver.FCZoneDriver)
fake_driver.supported = False
return fake_driver
self.patch('oslo_utils.importutils.import_object',
fake_import)
with mock.patch(
'cinder.volume.configuration.Configuration') as mock_config:
mock_config.return_value.zone_driver = 'test'
mock_config.return_value.enable_unsupported_driver = True
zm = fc_zone_manager.ZoneManager(configuration=config)
self.assertFalse(zm.driver.supported)
self.assertTrue(zm.initialized)
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_add_connection(self, opt_mock):
self.setup_fake_driver()
with mock.patch.object(self.zm.driver, 'add_connection')\
as add_connection_mock:
self.zm.driver.get_san_context.return_value = fabric_map
@ -80,6 +121,7 @@ class TestFCZoneManager(test.TestCase):
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_add_connection_error(self, opt_mock):
self.setup_fake_driver()
with mock.patch.object(self.zm.driver, 'add_connection')\
as add_connection_mock:
add_connection_mock.side_effect = exception.FCZoneDriverException
@ -88,6 +130,7 @@ class TestFCZoneManager(test.TestCase):
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_delete_connection(self, opt_mock):
self.setup_fake_driver()
with mock.patch.object(self.zm.driver, 'delete_connection')\
as delete_connection_mock:
self.zm.driver.get_san_context.return_value = fabric_map
@ -100,6 +143,7 @@ class TestFCZoneManager(test.TestCase):
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_delete_connection_error(self, opt_mock):
self.setup_fake_driver()
with mock.patch.object(self.zm.driver, 'delete_connection')\
as del_connection_mock:
del_connection_mock.side_effect = exception.FCZoneDriverException

View File

@ -44,3 +44,13 @@ class FCZoneDriver(
def __init__(self, **kwargs):
super(FCZoneDriver, self).__init__(**kwargs)
LOG.debug("Initializing FCZoneDriver")
# If a driver hasn't maintained their CI system, this will get set
# to False, which prevents the driver from starting.
# Add enable_unsupported_driver = True in cinder.conf to get the
# unsupported driver started.
self._supported = True
@property
def supported(self):
return self._supported

View File

@ -37,7 +37,7 @@ from oslo_utils import importutils
import six
from cinder import exception
from cinder.i18n import _, _LI
from cinder.i18n import _, _LE, _LI, _LW
from cinder.volume import configuration as config
from cinder.zonemanager import fc_common
import cinder.zonemanager.fczm_constants as zone_constant
@ -61,7 +61,16 @@ zone_manager_opts = [
cfg.StrOpt('fc_san_lookup_service',
default='cinder.zonemanager.drivers.brocade'
'.brcd_fc_san_lookup_service.BrcdFCSanLookupService',
help='FC SAN Lookup Service')
help='FC SAN Lookup Service'),
cfg.BoolOpt('enable_unsupported_driver',
default=False,
help="Set this to True when you want to allow an unsupported "
"zone manager driver to start. Drivers that haven't "
"maintained a working CI system and testing are marked "
"as unsupported until CI is working again. This also "
"marks a driver as deprecated and may be removed in the "
"next release."),
]
CONF = cfg.CONF
@ -81,6 +90,7 @@ class ZoneManager(fc_common.FCCommon):
VERSION = "1.0.2"
driver = None
_initialized = False
fabric_names = []
def __new__(class_, *args, **kwargs):
@ -94,6 +104,7 @@ class ZoneManager(fc_common.FCCommon):
self.configuration = config.Configuration(zone_manager_opts,
'fc-zone-manager')
self.set_initialized(False)
self._build_driver()
def _build_driver(self):
@ -102,11 +113,52 @@ class ZoneManager(fc_common.FCCommon):
{'driver': zone_driver})
zm_config = config.Configuration(zone_manager_opts, 'fc-zone-manager')
# Initialize vendor specific implementation of FCZoneDriver
# Initialize vendor specific implementation of FCZoneDriver
self.driver = importutils.import_object(
zone_driver,
configuration=zm_config)
if not self.driver.supported:
self._log_unsupported_driver_warning()
if not self.configuration.enable_unsupported_driver:
LOG.error(_LE("Unsupported drivers are disabled."
" You can re-enable by adding "
"enable_unsupported_driver=True to the "
"fc-zone-manager section in cinder.conf"),
resource={'type': 'zone_manager',
'id': self.__class__.__name__})
return
self.set_initialized(True)
@property
def initialized(self):
return self._initialized
def set_initialized(self, value=True):
self._initialized = value
def _require_initialized(self):
"""Verifies that the zone manager has been properly initialized."""
if not self.initialized:
LOG.error(_LE("Fibre Channel Zone Manager is not initialized."""))
raise exception.ZoneManagerNotInitialized()
else:
self._log_unsupported_driver_warning()
def _log_unsupported_driver_warning(self):
"""Annoy the log about unsupported fczm drivers."""
if not self.driver.supported:
LOG.warning(_LW("Zone Manager driver (%(driver_name)s %(version)s)"
" is currently unsupported and may be removed in "
"the next release of OpenStack. Use at your own "
"risk."),
{'driver_name': self.driver.__class__.__name__,
'version': self.driver.get_version()},
resource={'type': 'zone_manager',
'id': self.driver.__class__.__name__})
def get_zoning_state_ref_count(self, initiator_wwn, target_wwn):
"""Zone management state check.
@ -136,6 +188,17 @@ class ZoneManager(fc_common.FCCommon):
host_name = None
storage_system = None
try:
# Make sure the driver is loaded and we are initialized
self._log_unsupported_driver_warning()
self._require_initialized()
except exception.ZoneManagerNotInitialized:
LOG.error(_LE("Cannot add Fibre Channel Zone because the "
"Zone Manager is not initialized properly."),
resource={'type': 'zone_manager',
'id': self.__class__.__name__})
return
try:
initiator_target_map = (
conn_info[zone_constant.DATA][zone_constant.IT_MAP])
@ -202,6 +265,17 @@ class ZoneManager(fc_common.FCCommon):
host_name = None
storage_system = None
try:
# Make sure the driver is loaded and we are initialized
self._log_unsupported_driver_warning()
self._require_initialized()
except exception.ZoneManagerNotInitialized:
LOG.error(_LE("Cannot delete fibre channel zone because the "
"Zone Manager is not initialized properly."),
resource={'type': 'zone_manager',
'id': self.__class__.__name__})
return
try:
initiator_target_map = (
conn_info[zone_constant.DATA][zone_constant.IT_MAP])

View File

@ -35,12 +35,18 @@ def create_zone_manager():
if config.safe_get('zoning_mode') == 'fabric':
LOG.debug("FC Zone Manager enabled.")
zm = fc_zone_manager.ZoneManager()
LOG.info(_LI("Using FC Zone Manager %(zm_version)s,"
" Driver %(drv_name)s %(drv_version)s."),
{'zm_version': zm.get_version(),
'drv_name': zm.driver.__class__.__name__,
'drv_version': zm.driver.get_version()})
return zm
if zm.initialized:
LOG.info(_LI("Using FC Zone Manager %(zm_version)s,"
" Driver %(drv_name)s %(drv_version)s."),
{'zm_version': zm.get_version(),
'drv_name': zm.driver.__class__.__name__,
'drv_version': zm.driver.get_version()})
return zm
else:
LOG.debug("FC Zone Manager %(zm_version)s disabled",
{"zm_version": zm.get_version()})
return None
else:
LOG.debug("FC Zone Manager not enabled in cinder.conf.")
return None