Ensure FC ZoneManager is called

This patch ensures that the FC ZoneManager
is called during for all cases in Cinder
that does a volume attach/detach for FC
enabled drivers.
The problem was that we had code in the volume
manager that manually called the ZoneManager,
after initialize_connection and terminate_connection,
but other places in Cinder were not calling the
ZoneManager.
This patch creates 2 new decorators that can
be used for any driver's initialize_connection
and terminate_connection call.   The decorator
checks to make sure that the return value is for
a fibre_channel attachment and then calls the
ZoneManager's add_connection or delete_connection.
Change-Id: Ie3ae70785f500a140003ad3a8495e0ddc3516ea8
Closes-Bug: 1321798
This commit is contained in:
Walter A. Boring IV 2014-06-19 10:35:29 -07:00
parent 776c94a8a3
commit 824de4a01d
14 changed files with 290 additions and 253 deletions

View File

@ -16,6 +16,7 @@ from cinder.openstack.common import log as logging
from cinder.tests.brick.fake_lvm import FakeBrickLVM
from cinder.volume import driver
from cinder.volume.drivers import lvm
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -77,6 +78,42 @@ class FakeISERDriver(FakeISCSIDriver):
return (None, None)
class FakeFibreChannelDriver(driver.FibreChannelDriver):
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
return {
'driver_volume_type': 'fibre_channel',
'data': {
'initiator_target_map': {'fake_wwn': ['fake_wwn2']},
}}
@fczm_utils.AddFCZone
def no_zone_initialize_connection(self, volume, connector):
"""This shouldn't call the ZM."""
return {
'driver_volume_type': 'bogus',
'data': {
'initiator_target_map': {'fake_wwn': ['fake_wwn2']},
}}
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
return {
'driver_volume_type': 'fibre_channel',
'data': {
'initiator_target_map': {'fake_wwn': ['fake_wwn2']},
}}
@fczm_utils.RemoveFCZone
def no_zone_terminate_connection(self, volume, connector, **kwargs):
return {
'driver_volume_type': 'bogus',
'data': {
'initiator_target_map': {'fake_wwn': ['fake_wwn2']},
}}
class LoggingVolumeDriver(driver.VolumeDriver):
"""Logs and records calls, for unit tests."""

View File

@ -3530,14 +3530,9 @@ class FibreChannelTestCase(DriverTestCase):
"""Test Case for FibreChannelDriver."""
driver_name = "cinder.volume.driver.FibreChannelDriver"
def setUp(self):
super(FibreChannelTestCase, self).setUp()
self.driver = driver.FibreChannelDriver()
self.driver.do_setup(None)
def test_initialize_connection(self):
self.assertRaises(NotImplementedError,
self.driver.initialize_connection, {}, {})
self.volume.driver.initialize_connection, {}, {})
def test_validate_connector(self):
"""validate_connector() successful use case.
@ -3547,33 +3542,33 @@ class FibreChannelTestCase(DriverTestCase):
"""
connector = {'wwpns': ["not empty"],
'wwnns': ["not empty"]}
self.driver.validate_connector(connector)
self.volume.driver.validate_connector(connector)
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.driver.validate_connector, connector)
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.driver.validate_connector, connector)
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.driver.validate_connector, connector)
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.driver.validate_connector, connector)
self.volume.driver.validate_connector, connector)
class VolumePolicyTestCase(test.TestCase):

View File

@ -25,7 +25,7 @@ from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.zonemanager.drivers.fc_zone_driver import FCZoneDriver
from cinder.zonemanager.fc_zone_manager import ZoneManager
from cinder.zonemanager import fc_zone_manager
from mock import Mock
fabric_name = 'BRCD_FAB_3'
@ -34,45 +34,52 @@ fabric_map = {'BRCD_FAB_3': ['20240002ac000a50']}
target_list = ['20240002ac000a50']
class TestFCZoneManager(ZoneManager, test.TestCase):
class TestFCZoneManager(test.TestCase):
def setUp(self):
super(TestFCZoneManager, self).setUp()
self.configuration = conf.Configuration(None)
self.configuration.set_default('fc_fabric_names', fabric_name)
self.driver = Mock(FCZoneDriver)
config = conf.Configuration(None)
config.fc_fabric_names = fabric_name
def fake_build_driver(self):
self.driver = Mock(FCZoneDriver)
self.stubs.Set(fc_zone_manager.ZoneManager, '_build_driver',
fake_build_driver)
self.zm = fc_zone_manager.ZoneManager(configuration=config)
def __init__(self, *args, **kwargs):
test.TestCase.__init__(self, *args, **kwargs)
def test_add_connection(self):
with mock.patch.object(self.driver, 'add_connection')\
with mock.patch.object(self.zm.driver, 'add_connection')\
as add_connection_mock:
self.driver.get_san_context.return_value = fabric_map
self.add_connection(init_target_map)
self.driver.get_san_context.assert_called_once(target_list)
self.zm.driver.get_san_context.return_value = fabric_map
self.zm.add_connection(init_target_map)
self.zm.driver.get_san_context.assert_called_once(target_list)
add_connection_mock.assert_called_once_with(fabric_name,
init_target_map)
def test_add_connection_error(self):
with mock.patch.object(self.driver, 'add_connection')\
with mock.patch.object(self.zm.driver, 'add_connection')\
as add_connection_mock:
add_connection_mock.side_effect = exception.FCZoneDriverException
self.assertRaises(exception.ZoneManagerException,
self.add_connection, init_target_map)
self.zm.add_connection, init_target_map)
def test_delete_connection(self):
with mock.patch.object(self.driver, 'delete_connection')\
with mock.patch.object(self.zm.driver, 'delete_connection')\
as delete_connection_mock:
self.driver.get_san_context.return_value = fabric_map
self.delete_connection(init_target_map)
self.driver.get_san_context.assert_called_once_with(target_list)
self.zm.driver.get_san_context.return_value = fabric_map
self.zm.delete_connection(init_target_map)
self.zm.driver.get_san_context.assert_called_once_with(target_list)
delete_connection_mock.assert_called_once_with(fabric_name,
init_target_map)
def test_delete_connection_error(self):
with mock.patch.object(self.driver, 'delete_connection')\
with mock.patch.object(self.zm.driver, 'delete_connection')\
as del_connection_mock:
del_connection_mock.side_effect = exception.FCZoneDriverException
self.assertRaises(exception.ZoneManagerException,
self.delete_connection, init_target_map)
self.zm.delete_connection, init_target_map)

View File

@ -0,0 +1,90 @@
# (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Unit tests for Volume Manager."""
import mock
from cinder import test
from cinder.tests import fake_driver
from cinder import utils
from cinder.volume import configuration as conf
from cinder.zonemanager.drivers.brocade import brcd_fc_zone_driver
from cinder.zonemanager import fc_zone_manager
class TestVolumeDriver(test.TestCase):
def setUp(self):
super(TestVolumeDriver, self).setUp()
self.driver = fake_driver.FakeFibreChannelDriver()
brcd_fc_zone_driver.BrcdFCZoneDriver = mock.Mock()
self.addCleanup(self._cleanup)
def _cleanup(self):
self.driver = None
def __init__(self, *args, **kwargs):
test.TestCase.__init__(self, *args, **kwargs)
@mock.patch.object(utils, 'require_driver_initialized')
def test_initialize_connection_with_decorator(self, utils_mock):
utils_mock.return_value = True
with mock.patch.object(fc_zone_manager.ZoneManager, 'add_connection')\
as add_zone_mock:
with mock.patch.object(conf.Configuration, 'safe_get')\
as mock_safe_get:
mock_safe_get.return_value = 'fabric'
conn_info = self.driver.initialize_connection(None, None)
init_target_map = conn_info['data']['initiator_target_map']
add_zone_mock.assert_called_once_with(init_target_map)
@mock.patch.object(utils, 'require_driver_initialized')
def test_initialize_connection_no_decorator(self, utils_mock):
utils_mock.return_value = True
with mock.patch.object(fc_zone_manager.ZoneManager, 'add_connection')\
as add_zone_mock:
with mock.patch.object(conf.Configuration, 'safe_get')\
as mock_safe_get:
mock_safe_get.return_value = 'fabric'
self.driver.no_zone_initialize_connection(None, None)
assert not add_zone_mock.called
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_with_decorator(self, utils_mock):
utils_mock.return_value = True
with mock.patch.object(fc_zone_manager.ZoneManager,
'delete_connection') as remove_zone_mock:
with mock.patch.object(conf.Configuration, 'safe_get')\
as mock_safe_get:
mock_safe_get.return_value = 'fabric'
conn_info = self.driver.terminate_connection(None, None)
init_target_map = conn_info['data']['initiator_target_map']
remove_zone_mock.assert_called_once_with(init_target_map)
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_no_decorator(self, utils_mock):
utils_mock.return_value = True
with mock.patch.object(fc_zone_manager.ZoneManager,
'delete_connection') as remove_zone_mock:
with mock.patch.object(conf.Configuration, 'safe_get')\
as mock_safe_get:
mock_safe_get.return_value = 'fabric'
self.driver.no_zone_terminate_connection(None, None)
assert not remove_zone_mock.called

View File

@ -1,164 +0,0 @@
# (c) Copyright 2014 Brocade Communications Systems Inc.
# All Rights Reserved.
#
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Unit tests for Volume Manager."""
import mock
from cinder import exception
from cinder import test
from cinder import utils
from cinder.volume import configuration as conf
from cinder.volume import driver
from cinder.volume import manager
from cinder.zonemanager import fc_zone_manager
init_target_map = {'10008c7cff523b01': ['20240002ac000a50']}
conn_info = {
'driver_volume_type': 'fibre_channel',
'data': {
'target_discovered': True,
'target_lun': 1,
'target_wwn': '20240002ac000a50',
'initiator_target_map': {
'10008c7cff523b01': ['20240002ac000a50']
}
}
}
conn_info_no_init_target_map = {
'driver_volume_type': 'fibre_channel',
'data': {
'target_discovered': True,
'target_lun': 1,
'target_wwn': '20240002ac000a50',
}
}
class TestVolumeManager(manager.VolumeManager, test.TestCase):
def setUp(self):
super(TestVolumeManager, self).setUp()
self.configuration = conf.Configuration(None)
self.configuration.set_default('fc_fabric_names', 'BRCD_FAB_4',
'fc-zone-manager')
self.configuration.zoning_mode = 'fabric'
self.driver = mock.Mock(driver.VolumeDriver)
self.driver.initialize_connection.return_value = conn_info
self.driver.terminate_connection.return_value = conn_info
self.driver.create_export.return_value = None
self.db = mock.Mock()
self.db.volume_get.return_value = {'volume_type_id': None}
self.db.volume_admin_metadata_get.return_value = {}
self.context_mock = mock.Mock()
self.context_mock.elevated.return_value = None
self.zonemanager = fc_zone_manager.ZoneManager(
configuration=self.configuration)
def tearDown(self):
super(TestVolumeManager, self).tearDown()
self.configuration = None
self.db = None
self.driver = None
self.zonemanager = None
def __init__(self, *args, **kwargs):
test.TestCase.__init__(self, *args, **kwargs)
@mock.patch.object(utils, 'require_driver_initialized')
def test_initialize_connection_voltype_fc_mode_fabric(self,
utils_mock):
utils_mock.return_value = True
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
self.initialize_connection(self.context_mock, None, None)
add_del_conn_mock.assert_called_once_with(conn_info, 1)
@mock.patch.object(utils, 'require_driver_initialized')
def test_initialize_connection_voltype_fc_mode_none(self,
utils_mock):
utils_mock.return_value = True
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
self.configuration.zoning_mode = 'none'
self.zonemanager = None
self.initialize_connection(self.context_mock, None, None)
assert not add_del_conn_mock.called
def test_terminate_connection_exception(self):
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
add_del_conn_mock.side_effect = exception.ZoneManagerException
self.assertRaises(exception.VolumeBackendAPIException,
self.terminate_connection, None, None, None,
False)
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_voltype_fc_mode_fabric(self,
utils_mock):
utils_mock.return_value = True
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
self.terminate_connection(self.context_mock, None, None, False)
add_del_conn_mock.assert_called_once_with(conn_info, 0)
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_mode_none(self,
utils_mock):
utils_mock.return_value = True
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
self.configuration.zoning_mode = 'none'
self.zonemanager = None
self.terminate_connection(self.context_mock, None, None, False)
assert not add_del_conn_mock.called
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_conn_info_none(self,
utils_mock):
utils_mock.return_value = True
self.driver.terminate_connection.return_value = None
with mock.patch.object(manager.VolumeManager,
'_add_or_delete_fc_connection')\
as add_del_conn_mock:
self.terminate_connection(self.context_mock, None, None, False)
assert not add_del_conn_mock.called
@mock.patch.object(fc_zone_manager.ZoneManager, 'add_connection')
def test__add_or_delete_connection_add(self,
add_connection_mock):
self._add_or_delete_fc_connection(conn_info, 1)
add_connection_mock.assert_called_once_with(init_target_map)
@mock.patch.object(fc_zone_manager.ZoneManager, 'delete_connection')
def test__add_or_delete_connection_delete(self,
delete_connection_mock):
self._add_or_delete_fc_connection(conn_info, 0)
delete_connection_mock.assert_called_once_with(init_target_map)
@mock.patch.object(fc_zone_manager.ZoneManager, 'delete_connection')
def test__add_or_delete_connection_no_init_target_map(self,
del_conn_mock):
self._add_or_delete_fc_connection(conn_info_no_init_target_map, 0)
assert not del_conn_mock.called

View File

@ -21,6 +21,7 @@ from cinder import context
from cinder.openstack.common import log as logging
from cinder.volume import driver
from cinder.volume.drivers.emc import emc_smis_common
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -118,6 +119,7 @@ class EMCSMISFCDriver(driver.FibreChannelDriver):
"""Make sure volume is exported."""
pass
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info.
@ -168,6 +170,7 @@ class EMCSMISFCDriver(driver.FibreChannelDriver):
return data
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
self.common.terminate_connection(volume, connector)

View File

@ -19,6 +19,7 @@ Volume Drivers for Huawei OceanStor HVS storage arrays.
from cinder.volume import driver
from cinder.volume.drivers.huawei.rest_common import HVSCommon
from cinder.zonemanager import utils as fczm_utils
class HuaweiHVSISCSIDriver(driver.ISCSIDriver):
@ -150,10 +151,12 @@ class HuaweiHVSFCDriver(driver.FibreChannelDriver):
data['driver_version'] = self.VERSION
return data
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
"""Map a volume to a host."""
return self.common.initialize_connection_fc(volume, connector)
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
self.common.terminate_connection(volume, connector, **kwargs)

View File

@ -25,6 +25,7 @@ from cinder.openstack.common import log as logging
from cinder.volume import driver
from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import ssh_common
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -438,6 +439,7 @@ class HuaweiTFCDriver(driver.FibreChannelDriver):
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
"""Create FC connection between a volume and a host."""
LOG.debug('initialize_connection: volume name: %(vol)s, '
@ -547,6 +549,7 @@ class HuaweiTFCDriver(driver.FibreChannelDriver):
def _get_fc_port_ctr(self, port_details):
return port_details['ControllerID']
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
LOG.debug('terminate_connection: volume: %(vol)s, host: %(host)s, '

View File

@ -49,6 +49,7 @@ from cinder import utils
from cinder.volume.drivers.ibm.storwize_svc import helpers as storwize_helpers
from cinder.volume.drivers.san import san
from cinder.volume import volume_types
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -294,6 +295,7 @@ class StorwizeSVCDriver(san.SanDriver):
return self._helpers.get_vdisk_params(self.configuration, self._state,
type_id, volume_type=volume_type)
@fczm_utils.AddFCZone
@utils.synchronized('storwize-host', external=True)
def initialize_connection(self, volume, connector):
"""Perform the necessary work so that an iSCSI/FC connection can
@ -464,6 +466,7 @@ class StorwizeSVCDriver(san.SanDriver):
return i_t_map
@fczm_utils.RemoveFCZone
@utils.synchronized('storwize-host', external=True)
def terminate_connection(self, volume, connector, **kwargs):
"""Cleanup after an iSCSI connection has been terminated.

View File

@ -39,6 +39,7 @@ from cinder import utils
import cinder.volume.driver
from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon
from cinder.volume.drivers.san import san
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -163,6 +164,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
finally:
self.common.client_logout()
@fczm_utils.AddFCZone
@utils.synchronized('3par', external=True)
def initialize_connection(self, volume, connector):
"""Assigns the volume to a server.
@ -221,6 +223,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
finally:
self.common.client_logout()
@fczm_utils.RemoveFCZone
@utils.synchronized('3par', external=True)
def terminate_connection(self, volume, connector, **kwargs):
"""Driver entry point to unattach a volume from an instance."""

View File

@ -18,6 +18,7 @@ from cinder import utils
import cinder.volume.driver
from cinder.volume.drivers.san.hp import hp_msa_common as hpcommon
from cinder.volume.drivers.san import san
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -80,6 +81,7 @@ class HPMSAFCDriver(cinder.volume.driver.FibreChannelDriver):
finally:
self.common.client_logout()
@fczm_utils.AddFCZone
@utils.synchronized('msa', external=True)
def initialize_connection(self, volume, connector):
self.common.client_login()
@ -97,6 +99,7 @@ class HPMSAFCDriver(cinder.volume.driver.FibreChannelDriver):
finally:
self.common.client_logout()
@fczm_utils.RemoveFCZone
@utils.synchronized('msa', external=True)
def terminate_connection(self, volume, connector, **kwargs):
self.common.client_login()

View File

@ -62,7 +62,6 @@ from cinder.volume.flows.manager import manage_existing
from cinder.volume import rpcapi as volume_rpcapi
from cinder.volume import utils as volume_utils
from cinder.volume import volume_types
from cinder.zonemanager.fc_zone_manager import ZoneManager
from eventlet.greenpool import GreenPool
@ -180,7 +179,6 @@ class VolumeManager(manager.SchedulerDependentManager):
db=self.db,
host=self.host)
self.zonemanager = None
try:
self.extra_capabilities = jsonutils.loads(
self.driver.configuration.extra_capabilities)
@ -200,14 +198,6 @@ class VolumeManager(manager.SchedulerDependentManager):
"""
ctxt = context.get_admin_context()
if self.configuration.safe_get('zoning_mode') == 'fabric':
self.zonemanager = ZoneManager(configuration=self.configuration)
LOG.info(_("Starting FC Zone Manager %(zm_version)s,"
" Driver %(drv_name)s %(drv_version)s") %
{'zm_version': self.zonemanager.get_version(),
'drv_name': self.zonemanager.driver.__class__.__name__,
'drv_version': self.zonemanager.driver.get_version()})
LOG.info(_("Starting volume driver %(driver_name)s (%(version)s)") %
{'driver_name': self.driver.__class__.__name__,
'version': self.driver.get_version()})
@ -848,13 +838,7 @@ class VolumeManager(manager.SchedulerDependentManager):
if volume_metadata.get('readonly') == 'True'
else 'rw')
conn_info['data']['access_mode'] = access_mode
# NOTE(skolathur): If volume_type is fibre_channel, invoke
# FCZoneManager to add access control via FC zoning.
vol_type = conn_info.get('driver_volume_type', None)
mode = self.configuration.zoning_mode
LOG.debug("Zoning Mode: %s", mode)
if vol_type == 'fibre_channel' and self.zonemanager:
self._add_or_delete_fc_connection(conn_info, 1)
return conn_info
def terminate_connection(self, context, volume_id, connector, force=False):
@ -869,17 +853,8 @@ class VolumeManager(manager.SchedulerDependentManager):
volume_ref = self.db.volume_get(context, volume_id)
try:
conn_info = self.driver.terminate_connection(volume_ref,
connector,
force=force)
# NOTE(skolathur): If volume_type is fibre_channel, invoke
# FCZoneManager to remove access control via FC zoning.
if conn_info:
vol_type = conn_info.get('driver_volume_type', None)
mode = self.configuration.zoning_mode
LOG.debug("Zoning Mode: %s", mode)
if vol_type == 'fibre_channel' and self.zonemanager:
self._add_or_delete_fc_connection(conn_info, 0)
self.driver.terminate_connection(volume_ref, connector,
force=force)
except Exception as err:
err_msg = (_('Unable to terminate volume connection: %(err)s')
% {'err': err})
@ -1336,34 +1311,3 @@ class VolumeManager(manager.SchedulerDependentManager):
# Update volume stats
self.stats['allocated_capacity_gb'] += volume_ref['size']
return volume_ref['id']
def _add_or_delete_fc_connection(self, conn_info, zone_op):
"""Add or delete connection control to fibre channel network.
In case of fibre channel, when zoning mode is set as fabric
ZoneManager is invoked to apply FC zoning configuration to the network
using initiator and target WWNs used for attach/detach.
params conn_info: connector passed by volume driver after
initialize_connection or terminate_connection.
params zone_op: Indicates if it is a zone add or delete operation
zone_op=0 for delete connection and 1 for add connection
"""
_initiator_target_map = None
if 'initiator_target_map' in conn_info['data']:
_initiator_target_map = conn_info['data']['initiator_target_map']
LOG.debug("Initiator Target map:%s", _initiator_target_map)
# NOTE(skolathur): Invoke Zonemanager to handle automated FC zone
# management when vol_type is fibre_channel and zoning_mode is fabric
# Initiator_target map associating each initiator WWN to one or more
# target WWN is passed to ZoneManager to add or update zone config.
LOG.debug("Zoning op: %s", zone_op)
if _initiator_target_map is not None:
try:
if zone_op == 1:
self.zonemanager.add_connection(_initiator_target_map)
elif zone_op == 0:
self.zonemanager.delete_connection(_initiator_target_map)
except exception.ZoneManagerException as e:
with excutils.save_and_reraise_exception():
LOG.error(e)

View File

@ -65,12 +65,23 @@ CONF.register_opts(zone_manager_opts, 'fc-zone-manager')
class ZoneManager(fc_common.FCCommon):
"""Manages Connection control during attach/detach."""
"""Manages Connection control during attach/detach.
VERSION = "1.0"
Version History:
1.0 - Initial version
1.0.1 - Added __new__ for singleton
"""
VERSION = "1.0.1"
driver = None
fabric_names = []
def __new__(class_, *args, **kwargs):
if not hasattr(class_, "_instance"):
class_._instance = object.__new__(class_, *args, **kwargs)
return class_._instance
def __init__(self, **kwargs):
"""Load the driver from the one specified in args, or from flags."""
super(ZoneManager, self).__init__(**kwargs)
@ -79,6 +90,9 @@ class ZoneManager(fc_common.FCCommon):
if self.configuration:
self.configuration.append_config_values(zone_manager_opts)
self._build_driver()
def _build_driver(self):
zone_driver = self.configuration.zone_driver
LOG.debug("Zone Driver from config: {%s}", zone_driver)

View File

@ -0,0 +1,96 @@
# (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""
Utility functions related to the Zone Manager.
"""
import logging
from cinder.openstack.common import log
from cinder.volume.configuration import Configuration
from cinder.volume import manager
from cinder.zonemanager import fc_zone_manager
LOG = log.getLogger(__name__)
LOG.logger.setLevel(logging.DEBUG)
def create_zone_manager():
"""If zoning is enabled, build the Zone Manager."""
config = Configuration(manager.volume_manager_opts)
LOG.debug("zoning mode %s" % config.safe_get('zoning_mode'))
if config.safe_get('zoning_mode') == 'fabric':
LOG.debug("FC Zone Manager enabled.")
zm = fc_zone_manager.ZoneManager(configuration=config)
LOG.info(_("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 not enabled in cinder.conf.")
return None
def AddFCZone(initialize_connection):
"""Decorator to add a FC Zone."""
def decorator(self, *args, **kwargs):
conn_info = initialize_connection(self, *args, **kwargs)
if not conn_info:
LOG.warn(_("Driver didn't return connection info, "
"can't add zone."))
return None
vol_type = conn_info.get('driver_volume_type', None)
if vol_type == 'fibre_channel':
if 'initiator_target_map' in conn_info['data']:
init_target_map = conn_info['data']['initiator_target_map']
zm = create_zone_manager()
if zm:
LOG.debug("Add FC Zone for mapping '%s'." %
init_target_map)
zm.add_connection(init_target_map)
return conn_info
return decorator
def RemoveFCZone(terminate_connection):
"""Decorator for FC drivers to remove zone."""
def decorator(self, *args, **kwargs):
conn_info = terminate_connection(self, *args, **kwargs)
if not conn_info:
LOG.warn(_("Driver didn't return connection info from "
"terminate_connection call."))
return None
vol_type = conn_info.get('driver_volume_type', None)
if vol_type == 'fibre_channel':
if 'initiator_target_map' in conn_info['data']:
init_target_map = conn_info['data']['initiator_target_map']
zm = create_zone_manager()
if zm:
LOG.debug("Remove FC Zone for mapping '%s'." %
init_target_map)
zm.delete_connection(init_target_map)
return conn_info
return decorator