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:
parent
776c94a8a3
commit
824de4a01d
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, '
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue