--- /dev/null
+# (c) Copyright 2015 Brocade Communications Systems Inc.
+# 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.
+#
+
+
+"""Unit tests for friendly zone name."""
+import ddt
+import string
+
+from cinder import test
+from cinder.zonemanager.drivers import driver_utils
+
+TEST_CHAR_SET = string.ascii_letters + string.digits
+
+
+@ddt.ddt
+class TestDriverUtils(test.TestCase):
+
+ @ddt.data('OSHost10010008c7cff523b01AMCEArray20240002ac000a50')
+ def test_get_friendly_zone_name_valid_hostname_storagesystem(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50", "OS_Host100", 'AMCE'
+ '_Array', "openstack", TEST_CHAR_SET))
+
+ @ddt.data('openstack10008c7cff523b0120240002ac000a50')
+ def test_get_friendly_zone_name_hostname_storagesystem_none(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50", None, None,
+ "openstack", TEST_CHAR_SET))
+
+ @ddt.data('openstack10008c7cff523b0120240002ac000a50')
+ def test_get_friendly_zone_name_storagesystem_none(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50", "OS_Host100", None,
+ "openstack", TEST_CHAR_SET))
+
+ @ddt.data('openstack10008c7cff523b0120240002ac000a50')
+ def test_get_friendly_zone_name_hostname_none(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50", None, "AMCE_Array",
+ "openstack", TEST_CHAR_SET))
+
+ @ddt.data('OSHost10010008c7cff523b01')
+ def test_get_friendly_zone_name_initiator_mode(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator', "10:00:8c:7c:ff:52:3b:01", None,
+ "OS_Host100", None, "openstack", TEST_CHAR_SET))
+
+ @ddt.data('openstack10008c7cff523b01')
+ def test_get_friendly_zone_name_initiator_mode_hostname_none(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator', "10:00:8c:7c:ff:52:3b:01", None,
+ None, None, "openstack", TEST_CHAR_SET))
+
+ @ddt.data('OSHost100XXXX10008c7cff523b01AMCEArrayYYYY20240002ac000a50')
+ def test_get_friendly_zone_name_storagename_length_too_long(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50",
+ "OS_Host100XXXXXXXXXX",
+ "AMCE_ArrayYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
+ "YYYY", "openstack", TEST_CHAR_SET))
+
+ @ddt.data('OSHost100XXXX10008c7cff523b01AMCEArrayYYYY20240002ac000a50')
+ def test_get_friendly_zone_name_max_length(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50",
+ "OS_Host100XXXXXXXXXX",
+ "AMCE_ArrayYYYYYYYYYY",
+ "openstack", TEST_CHAR_SET))
+
+ @ddt.data('OSHost100XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX10008c7cff523b01')
+ def test_get_friendly_zone_name_initiator_mode_hostname_max_length(self,
+ value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator', "10:00:8c:7c:ff:52:3b:01", None,
+ 'OS_Host100XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+ 'XXXXX',
+ None, "openstack", TEST_CHAR_SET))
+
+ @ddt.data('openstack110008c7cff523b0120240002ac000a50')
+ def test_get_friendly_zone_name_invalid_characters(self, value):
+ self.assertEqual(value,
+ driver_utils.get_friendly_zone_name(
+ 'initiator-target', "10:00:8c:7c:ff:52:3b:01",
+ "20:24:00:02:ac:00:0a:50", None, "AMCE_Array",
+ "open-stack*1_", TEST_CHAR_SET))
fabric_name = 'BRCD_FAB_3'
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']
+ }
+ }
+}
fabric_map = {'BRCD_FAB_3': ['20240002ac000a50']}
target_list = ['20240002ac000a50']
with mock.patch.object(self.zm.driver, 'add_connection')\
as add_connection_mock:
self.zm.driver.get_san_context.return_value = fabric_map
- self.zm.add_connection(init_target_map)
+ self.zm.add_connection(conn_info)
self.zm.driver.get_san_context.assert_called_once_with(target_list)
add_connection_mock.assert_called_once_with(fabric_name,
- init_target_map)
+ init_target_map,
+ None,
+ None)
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_add_connection_error(self, opt_mock):
as add_connection_mock:
add_connection_mock.side_effect = exception.FCZoneDriverException
self.assertRaises(exception.ZoneManagerException,
- self.zm.add_connection, init_target_map)
+ self.zm.add_connection, conn_info)
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_delete_connection(self, opt_mock):
with mock.patch.object(self.zm.driver, 'delete_connection')\
as delete_connection_mock:
self.zm.driver.get_san_context.return_value = fabric_map
- self.zm.delete_connection(init_target_map)
+ self.zm.delete_connection(conn_info)
self.zm.driver.get_san_context.assert_called_once_with(target_list)
delete_connection_mock.assert_called_once_with(fabric_name,
- init_target_map)
+ init_target_map,
+ None,
+ None)
@mock.patch('oslo_config.cfg._is_opt_registered', return_value=False)
def test_delete_connection_error(self, opt_mock):
as del_connection_mock:
del_connection_mock.side_effect = exception.FCZoneDriverException
self.assertRaises(exception.ZoneManagerException,
- self.zm.delete_connection, init_target_map)
+ self.zm.delete_connection, conn_info)
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)
+ add_zone_mock.assert_called_once_with(conn_info)
@mock.patch.object(utils, 'require_driver_initialized')
def test_initialize_connection_no_decorator(self, utils_mock):
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)
+ remove_zone_mock.assert_called_once_with(conn_info)
@mock.patch.object(utils, 'require_driver_initialized')
def test_terminate_connection_no_decorator(self, utils_mock):
default=True,
help='overridden zoning activation state'),
cfg.StrOpt('zone_name_prefix',
+ default='openstack',
help='overridden zone name prefix'),
cfg.StrOpt('principal_switch_wwn',
help='Principal switch WWN of the fabric'),
fabric_configs = {}
for fabric_name in fabric_names:
config = configuration.Configuration(brcd_zone_opts, fabric_name)
- LOG.debug("Loaded FC fabric config %s", fabric_name)
+ LOG.debug("Loaded FC fabric config %(fabricname)s",
+ {'fabricname': fabric_name})
fabric_configs[fabric_name] = config
return fabric_configs
from oslo_utils import excutils
from oslo_utils import importutils
import six
+import string
from cinder import exception
-from cinder.i18n import _, _LE, _LI
+from cinder.i18n import _, _LE, _LI, _LW
from cinder.zonemanager.drivers.brocade import brcd_fabric_opts as fabric_opts
+from cinder.zonemanager.drivers import driver_utils
from cinder.zonemanager.drivers import fc_zone_driver
LOG = logging.getLogger(__name__)
+SUPPORTED_CHARS = string.ascii_letters + string.digits + '_'
brcd_opts = [
cfg.StrOpt('brcd_sb_connector',
default='cinder.zonemanager.drivers.brocade'
Version history:
1.0 - Initial Brocade FC zone driver
1.1 - Implements performance enhancements
+ 1.2 - Added support for friendly zone name
"""
- VERSION = "1.1"
+ VERSION = "1.2"
def __init__(self, **kwargs):
super(BrcdFCZoneDriver, self).__init__(**kwargs)
self.configuration = kwargs.get('configuration', None)
if self.configuration:
self.configuration.append_config_values(brcd_opts)
- # Adding a hack to hendle parameters from super classes
- # in case configured with multi backend.
+ # Adding a hack to handle parameters from super classes
+ # in case configured with multiple back ends.
fabric_names = self.configuration.safe_get('fc_fabric_names')
base_san_opts = []
if not fabric_names:
[wwn_str[i:i + 2] for i in range(0, len(wwn_str), 2)])
@lockutils.synchronized('brcd', 'fcfabric-', True)
- def add_connection(self, fabric, initiator_target_map):
+ def add_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Concrete implementation of add_connection.
Based on zoning policy and state of each I-T pair, list of zone
:param fabric: Fabric name from cinder.conf file
:param initiator_target_map: Mapping of initiator to list of targets
"""
- LOG.debug("Add connection for Fabric: %s", fabric)
- LOG.info(_LI("BrcdFCZoneDriver - Add connection "
- "for I-T map: %s"), initiator_target_map)
+ LOG.info(_LI("BrcdFCZoneDriver - Add connection for fabric "
+ "%(fabric)s for I-T map: %(i_t_map)s"),
+ {'fabric': fabric,
+ 'i_t_map': initiator_target_map})
zoning_policy = self.configuration.zoning_policy
zoning_policy_fab = self.fabric_configs[fabric].safe_get(
'zoning_policy')
- if zoning_policy_fab:
- zoning_policy = zoning_policy_fab
zone_name_prefix = self.fabric_configs[fabric].safe_get(
'zone_name_prefix')
- if not zone_name_prefix:
- zone_name_prefix = 'openstack'
zone_activate = self.fabric_configs[fabric].safe_get(
'zone_activate')
+ if zoning_policy_fab:
+ zoning_policy = zoning_policy_fab
+ LOG.info(_LI("Zoning policy for Fabric %(policy)s"),
+ {'policy': zoning_policy})
+ if (zoning_policy != 'initiator'
+ and zoning_policy != 'initiator-target'):
+ LOG.info(_LI("Zoning policy is not valid, "
+ "no zoning will be performed."))
+ return
- LOG.info(_LI("Zoning policy for Fabric %s"), zoning_policy)
cli_client = self._get_cli_client(fabric)
cfgmap_from_fabric = self._get_active_zone_set(cli_client)
target = t.lower()
zone_members = [self.get_formatted_wwn(initiator),
self.get_formatted_wwn(target)]
- zone_name = (zone_name_prefix
- + initiator.replace(':', '')
- + target.replace(':', ''))
+ zone_name = driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ zone_name_prefix,
+ SUPPORTED_CHARS)
if (
len(cfgmap_from_fabric) == 0 or (
zone_name not in zone_names)):
zone_map[zone_name] = zone_members
else:
# This is I-T zoning, skip if zone already exists.
- LOG.info(_LI("Zone exists in I-T mode. "
- "Skipping zone creation %s"), zone_name)
+ LOG.info(_LI("Zone exists in I-T mode. Skipping "
+ "zone creation for %(zonename)s"),
+ {'zonename': zone_name})
elif zoning_policy == 'initiator':
zone_members = [self.get_formatted_wwn(initiator)]
for t in t_list:
target = t.lower()
zone_members.append(self.get_formatted_wwn(target))
- zone_name = zone_name_prefix + initiator.replace(':', '')
+ zone_name = driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ zone_name_prefix,
+ SUPPORTED_CHARS)
if len(zone_names) > 0 and (zone_name in zone_names):
zone_members = zone_members + filter(
cfgmap_from_fabric['zones'][zone_name])
zone_map[zone_name] = zone_members
- else:
- msg = _("Zoning Policy: %s, not "
- "recognized") % zoning_policy
- LOG.error(msg)
- raise exception.FCZoneDriverException(msg)
- LOG.info(_LI("Zone map to add: %s"), zone_map)
+ LOG.info(_LI("Zone map to add: %(zonemap)s"),
+ {'zonemap': zone_map})
if len(zone_map) > 0:
try:
msg = _("Failed to add zoning configuration.")
LOG.exception(msg)
raise exception.FCZoneDriverException(msg)
- LOG.debug("Zones added successfully: %s", zone_map)
+ LOG.debug("Zones added successfully: %(zonemap)s",
+ {'zonemap': zone_map})
@lockutils.synchronized('brcd', 'fcfabric-', True)
- def delete_connection(self, fabric, initiator_target_map):
+ def delete_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Concrete implementation of delete_connection.
Based on zoning policy and state of each I-T pair, list of zones
:param fabric: Fabric name from cinder.conf file
:param initiator_target_map: Mapping of initiator to list of targets
"""
- LOG.debug("Delete connection for fabric: %s", fabric)
- LOG.info(_LI("BrcdFCZoneDriver - Delete connection for I-T map: %s"),
- initiator_target_map)
+ LOG.info(_LI("BrcdFCZoneDriver - Delete connection for fabric "
+ "%(fabric)s for I-T map: %(i_t_map)s"),
+ {'fabric': fabric,
+ 'i_t_map': initiator_target_map})
zoning_policy = self.configuration.zoning_policy
zoning_policy_fab = self.fabric_configs[fabric].safe_get(
'zoning_policy')
- if zoning_policy_fab:
- zoning_policy = zoning_policy_fab
zone_name_prefix = self.fabric_configs[fabric].safe_get(
'zone_name_prefix')
- if not zone_name_prefix:
- zone_name_prefix = 'openstack'
zone_activate = self.fabric_configs[fabric].safe_get(
'zone_activate')
-
- LOG.info(_LI("Zoning policy for fabric %s"), zoning_policy)
+ if zoning_policy_fab:
+ zoning_policy = zoning_policy_fab
+ LOG.info(_LI("Zoning policy for fabric %(policy)s"),
+ {'policy': zoning_policy})
conn = self._get_cli_client(fabric)
cfgmap_from_fabric = self._get_active_zone_set(conn)
# Based on zoning policy, get zone member list and push changes to
# fabric. This operation could result in an update for zone config
# with new member list or deleting zones from active cfg.
- LOG.debug("zone config from Fabric: %s", cfgmap_from_fabric)
+ LOG.debug("zone config from Fabric: %(cfgmap)s",
+ {'cfgmap': cfgmap_from_fabric})
for initiator_key in initiator_target_map.keys():
initiator = initiator_key.lower()
formatted_initiator = self.get_formatted_wwn(initiator)
# In this case, zone needs to be deleted.
for t in t_list:
target = t.lower()
- zone_name = (
- zone_name_prefix
- + initiator.replace(':', '')
- + target.replace(':', ''))
- LOG.debug("Zone name to del: %s", zone_name)
+ zone_name = driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ zone_name_prefix,
+ SUPPORTED_CHARS)
+ LOG.debug("Zone name to delete: %(zonename)s",
+ {'zonename': zone_name})
if len(zone_names) > 0 and (zone_name in zone_names):
# delete zone.
- LOG.debug("Added zone to delete to "
- "list: %s", zone_name)
+ LOG.debug("Added zone to delete to list: %(zonename)s",
+ {'zonename': zone_name})
zones_to_delete.append(zone_name)
elif zoning_policy == 'initiator':
target = t.lower()
zone_members.append(self.get_formatted_wwn(target))
- zone_name = zone_name_prefix + initiator.replace(':', '')
+ zone_name = driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ zone_name_prefix,
+ SUPPORTED_CHARS)
if (zone_names and (zone_name in zone_names)):
filtered_members = filter(
# filtered list and if it is non-empty, add initiator
# to it and update zone if filtered list is empty, we
# remove that zone.
- LOG.debug("Zone delete - I mode: "
- "filtered targets: %s", filtered_members)
+ LOG.debug("Zone delete - initiator mode: "
+ "filtered targets: %(targets)s",
+ {'targets': filtered_members})
if filtered_members:
filtered_members.append(formatted_initiator)
- LOG.debug("Filtered zone members to "
- "update: %s", filtered_members)
+ LOG.debug("Filtered zone members to update: "
+ "%(members)s", {'members': filtered_members})
zone_map[zone_name] = filtered_members
- LOG.debug("Filtered zone Map to "
- "update: %s", zone_map)
+ LOG.debug("Filtered zone map to update: %(zonemap)s",
+ {'zonemap': zone_map})
else:
zones_to_delete.append(zone_name)
else:
- LOG.info(_LI("Zoning Policy: %s, not "
- "recognized"), zoning_policy)
- LOG.debug("Final Zone map to update: %s", zone_map)
- LOG.debug("Final Zone list to delete: %s", zones_to_delete)
+ LOG.warning(_LW("Zoning policy not recognized: %(policy)s"),
+ {'policy': zoning_policy})
+ LOG.debug("Final zone map to update: %(zonemap)s",
+ {'zonemap': zone_map})
+ LOG.debug("Final zone list to delete: %(zones)s",
+ {'zones': zones_to_delete})
try:
# Update zone membership.
if zone_map:
fabric_map = {}
fc_fabric_names = self.configuration.fc_fabric_names
fabrics = [x.strip() for x in fc_fabric_names.split(',')]
- LOG.debug("Fabric List: %s", fabrics)
- LOG.debug("Target wwn List: %s", target_wwn_list)
+ LOG.debug("Fabric List: %(fabrics)s", {'fabrics': fabrics})
+ LOG.debug("Target WWN list: %(targetwwns)s",
+ {'targetwwns': target_wwn_list})
if len(fabrics) > 0:
for t in target_wwn_list:
formatted_target_list.append(self.get_formatted_wwn(t.lower()))
- LOG.debug("Formatted Target wwn List:"
- " %s", formatted_target_list)
+ LOG.debug("Formatted target WWN list: %(targetlist)s",
+ {'targetlist': formatted_target_list})
for fabric_name in fabrics:
conn = self._get_cli_client(fabric_name)
nsinfo = None
try:
nsinfo = conn.get_nameserver_info()
- LOG.debug("name server info from fabric: %s", nsinfo)
+ LOG.debug("Name server info from fabric: %(nsinfo)s",
+ {'nsinfo': nsinfo})
conn.cleanup()
except exception.BrocadeZoningCliException:
if not conn.is_supported_firmware():
nsinfo)
if visible_targets:
- LOG.info(_LI("Filtered targets for SAN is: %s"),
- {fabric_name: visible_targets})
+ LOG.info(_LI("Filtered targets for SAN is: %(targets)s"),
+ {'targets': visible_targets})
# getting rid of the ':' before returning
for idx, elem in enumerate(visible_targets):
visible_targets[idx] = str(
visible_targets[idx]).replace(':', '')
fabric_map[fabric_name] = visible_targets
else:
- LOG.debug("No targets are in the nameserver for SAN %s",
- fabric_name)
- LOG.debug("Return SAN context output: %s", fabric_map)
+ LOG.debug("No targets found in the nameserver "
+ "for fabric: %(fabric)s",
+ {'fabric': fabric_name})
+ LOG.debug("Return SAN context output: %(fabricmap)s",
+ {'fabricmap': fabric_map})
return fabric_map
def _get_active_zone_set(self, conn):
msg = (_("Failed to retrieve active zoning configuration %s")
% six.text_type(e))
raise exception.FCZoneDriverException(msg)
- LOG.debug("Active zone set from fabric: %s", cfgmap)
+ LOG.debug("Active zone set from fabric: %(cfgmap)s",
+ {'cfgmap': cfgmap})
return cfgmap
def _get_cli_client(self, fabric):
try:
cli_client = self.sb_conn_map.get(fabric_ip)
if not cli_client:
- LOG.debug("CLI client not found, creating for %s", fabric_ip)
+ LOG.debug("CLI client not found, creating for %(ip)s",
+ {'ip': fabric_ip})
cli_client = importutils.import_object(
self.configuration.brcd_sb_connector,
ipaddress=fabric_ip,
# under the License.
#
-
"""
Common constants used by Brocade FC Zone Driver.
"""
from oslo_utils import excutils
from oslo_utils import importutils
import six
+import string
from cinder import exception
from cinder.i18n import _, _LE, _LI
from cinder.zonemanager.drivers.cisco import cisco_fabric_opts as fabric_opts
+from cinder.zonemanager.drivers import driver_utils
from cinder.zonemanager.drivers import fc_zone_driver
from cinder.zonemanager import utils as zm_utils
LOG = logging.getLogger(__name__)
+SUPPORTED_CHARS = string.ascii_letters + string.digits + '$' + '-' + '^' + '_'
cisco_opts = [
cfg.StrOpt('cisco_sb_connector',
default='cinder.zonemanager.drivers.cisco'
Version history:
1.0 - Initial Cisco FC zone driver
+ 1.1 - Added friendly zone name support
"""
- VERSION = "1.0.0"
+ VERSION = "1.1.0"
def __init__(self, **kwargs):
super(CiscoFCZoneDriver, self).__init__(**kwargs)
fabric_names)
@lockutils.synchronized('cisco', 'fcfabric-', True)
- def add_connection(self, fabric, initiator_target_map):
+ def add_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Concrete implementation of add_connection.
Based on zoning policy and state of each I-T pair, list of zone
zone_members = [
zm_utils.get_formatted_wwn(initiator),
zm_utils.get_formatted_wwn(target)]
- zone_name = (self.
- configuration.cisco_zone_name_prefix
- + initiator.replace(':', '')
- + target.replace(':', ''))
+ zone_name = (
+ driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ self.configuration.cisco_zone_name_prefix,
+ SUPPORTED_CHARS))
if (len(cfgmap_from_fabric) == 0 or (
zone_name not in zone_names)):
zone_map[zone_name] = zone_members
zone_members.append(
zm_utils.get_formatted_wwn(target))
- zone_name = self.configuration.cisco_zone_name_prefix \
- + initiator.replace(':', '')
+ zone_name = (
+ driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ self.configuration.cisco_zone_name_prefix,
+ SUPPORTED_CHARS))
if len(zone_names) > 0 and (zone_name in zone_names):
zone_members = zone_members + filter(
LOG.debug("Zoning session exists VSAN: %s", zoning_vsan)
@lockutils.synchronized('cisco', 'fcfabric-', True)
- def delete_connection(self, fabric, initiator_target_map):
+ def delete_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Concrete implementation of delete_connection.
Based on zoning policy and state of each I-T pair, list of zones
for t in t_list:
target = t.lower()
zone_name = (
- self.configuration.cisco_zone_name_prefix
- + initiator.replace(':', '')
- + target.replace(':', ''))
+ driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ self.configuration.cisco_zone_name_prefix,
+ SUPPORTED_CHARS))
LOG.debug("Zone name to del: %s", zone_name)
if (len(zone_names) > 0 and (zone_name in zone_names)):
# delete zone.
zone_members.append(
zm_utils.get_formatted_wwn(target))
- zone_name = self.configuration.cisco_zone_name_prefix \
- + initiator.replace(':', '')
+ zone_name = driver_utils.get_friendly_zone_name(
+ zoning_policy,
+ initiator,
+ target,
+ host_name,
+ storage_system,
+ self.configuration.cisco_zone_name_prefix,
+ SUPPORTED_CHARS)
if (zone_names and (zone_name in zone_names)):
filtered_members = filter(
--- /dev/null
+# (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.
+#
+
+import re
+
+from oslo_log import log
+
+from cinder.i18n import _LI
+
+LOG = log.getLogger(__name__)
+
+
+def get_friendly_zone_name(zoning_policy, initiator, target,
+ host_name, storage_system, zone_name_prefix,
+ supported_chars):
+ """Utility function implementation of _get_friendly_zone_name.
+
+ Get friendly zone name is used to form the zone name
+ based on the details provided by the caller
+
+ :param zoning_policy - determines the zoning policy is either
+ initiator-target or initiator
+ :param initiator - initiator WWN
+ :param target - target WWN
+ :param host_name - Host name returned from Volume Driver
+ :param storage_system - Storage name returned from Volume Driver
+ :param zone_name_prefix - user defined zone prefix configured
+ in cinder.conf
+ :param supported_chars - Supported character set of FC switch vendor.
+ Example: 'abc123_-$'. These are defined in the FC zone drivers.
+ """
+ if host_name is None:
+ host_name = ''
+ if storage_system is None:
+ storage_system = ''
+ if zoning_policy == 'initiator-target':
+ host_name = host_name[:14]
+ storage_system = storage_system[:14]
+ if len(host_name) > 0 and len(storage_system) > 0:
+ zone_name = (host_name + "_"
+ + initiator.replace(':', '') + "_"
+ + storage_system + "_"
+ + target.replace(':', ''))
+ else:
+ zone_name = (zone_name_prefix
+ + initiator.replace(':', '')
+ + target.replace(':', ''))
+ LOG.info(_LI("Zone name created using prefix because either "
+ "host name or storage system is none."))
+ else:
+ host_name = host_name[:47]
+ if len(host_name) > 0:
+ zone_name = (host_name + "_"
+ + initiator.replace(':', ''))
+ else:
+ zone_name = (zone_name_prefix
+ + initiator.replace(':', ''))
+ LOG.info(_LI("Zone name created using prefix because host "
+ "name is none."))
+
+ LOG.info(_LI("Friendly zone name after forming: %(zonename)s"),
+ {'zonename': zone_name})
+ zone_name = re.sub('[^%s]' % supported_chars, '', zone_name)
+ return zone_name
super(FCZoneDriver, self).__init__(**kwargs)
LOG.debug("Initializing FCZoneDriver")
- def add_connection(self, fabric, initiator_target_map):
+ def add_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Add connection control.
Abstract method to add connection control.
"""
raise NotImplementedError()
- def delete_connection(self, fabric, initiator_target_map):
+ def delete_connection(self, fabric, initiator_target_map, host_name=None,
+ storage_system=None):
"""Delete connection control.
Abstract method to remove connection control.
from cinder.i18n import _, _LI
from cinder.volume import configuration as config
from cinder.zonemanager import fc_common
+import cinder.zonemanager.fczm_constants as zone_constant
+
LOG = logging.getLogger(__name__)
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')
]
CONF = cfg.CONF
class ZoneManager(fc_common.FCCommon):
+
"""Manages Connection control during attach/detach.
Version History:
1.0 - Initial version
1.0.1 - Added __new__ for singleton
+ 1.0.2 - Added friendly zone name
"""
- VERSION = "1.0.1"
+ VERSION = "1.0.2"
driver = None
fabric_names = []
self.configuration = config.Configuration(zone_manager_opts,
'fc-zone-manager')
-
self._build_driver()
def _build_driver(self):
zone_driver = self.configuration.zone_driver
- LOG.debug("Zone Driver from config: {%s}", zone_driver)
+ LOG.debug("Zone driver from config: %(driver)s",
+ {'driver': zone_driver})
+ zm_config = config.Configuration(zone_manager_opts, 'fc-zone-manager')
# Initialize vendor specific implementation of FCZoneDriver
self.driver = importutils.import_object(
zone_driver,
- configuration=self.configuration)
+ configuration=zm_config)
def get_zoning_state_ref_count(self, initiator_wwn, target_wwn):
"""Zone management state check.
# check the state for I-T pair
return count
- def add_connection(self, initiator_target_map):
+ def add_connection(self, conn_info):
"""Add connection control.
Adds connection control for the given initiator target map.
}
"""
connected_fabric = None
+ host_name = None
+ storage_system = None
+
try:
+ initiator_target_map = (
+ conn_info[zone_constant.DATA][zone_constant.IT_MAP])
+
+ if zone_constant.HOST in conn_info[zone_constant.DATA]:
+ host_name = conn_info[
+ zone_constant.DATA][
+ zone_constant.HOST].replace(" ", "_")
+
+ if zone_constant.STORAGE in conn_info[zone_constant.DATA]:
+ storage_system = (
+ conn_info[
+ zone_constant.DATA][
+ zone_constant.STORAGE].replace(" ", "_"))
+
for initiator in initiator_target_map.keys():
target_list = initiator_target_map[initiator]
- LOG.debug("Target List: %s", target_list)
+ LOG.debug("Target list : %(targets)s",
+ {'targets': target_list})
# get SAN context for the target list
fabric_map = self.get_san_context(target_list)
- LOG.debug("Fabric Map after context lookup: %s", fabric_map)
+ LOG.debug("Fabric map after context lookup: %(fabricmap)s",
+ {'fabricmap': fabric_map})
# iterate over each SAN and apply connection control
for fabric in fabric_map.keys():
connected_fabric = fabric
i_t_map = {initiator: t_list}
valid_i_t_map = self.get_valid_initiator_target_map(
i_t_map, True)
- LOG.info(_LI("Final filtered map for fabric: %s"),
- valid_i_t_map)
+ LOG.info(_LI("Final filtered map for fabric: %(i_t_map)s"),
+ {'i_t_map': valid_i_t_map})
# Call driver to add connection control
- self.driver.add_connection(fabric, valid_i_t_map)
+ self.driver.add_connection(fabric, valid_i_t_map,
+ host_name, storage_system)
- LOG.info(_LI("Add Connection: Finished iterating "
+ LOG.info(_LI("Add connection: finished iterating "
"over all target list"))
except Exception as e:
msg = _("Failed adding connection for fabric=%(fabric)s: "
LOG.error(msg)
raise exception.ZoneManagerException(reason=msg)
- def delete_connection(self, initiator_target_map):
+ def delete_connection(self, conn_info):
"""Delete connection.
Updates/deletes connection control for the given initiator target map.
}
"""
connected_fabric = None
+ host_name = None
+ storage_system = None
+
try:
+ initiator_target_map = (
+ conn_info[zone_constant.DATA][zone_constant.IT_MAP])
+
+ if zone_constant.HOST in conn_info[zone_constant.DATA]:
+ host_name = conn_info[zone_constant.DATA][zone_constant.HOST]
+
+ if zone_constant.STORAGE in conn_info[zone_constant.DATA]:
+ storage_system = (
+ conn_info[
+ zone_constant.DATA][
+ zone_constant.STORAGE].replace(" ", "_"))
+
for initiator in initiator_target_map.keys():
target_list = initiator_target_map[initiator]
- LOG.info(_LI("Delete connection Target List: %s"),
- target_list)
+ LOG.info(_LI("Delete connection target list: %(targets)s"),
+ {'targets': target_list})
# get SAN context for the target list
fabric_map = self.get_san_context(target_list)
- LOG.debug("Delete connection Fabric Map from SAN "
- "context: %s", fabric_map)
+ LOG.debug("Delete connection fabric map from SAN "
+ "context: %(fabricmap)s", {'fabricmap': fabric_map})
# iterate over each SAN and apply connection control
for fabric in fabric_map.keys():
i_t_map = {initiator: t_list}
valid_i_t_map = self.get_valid_initiator_target_map(
i_t_map, False)
- LOG.info(_LI("Final filtered map for delete "
- "connection: %s"), valid_i_t_map)
+ LOG.info(_LI("Final filtered map for delete connection: "
+ "%(i_t_map)s"), {'i_t_map': valid_i_t_map})
# Call driver to delete connection control
if len(valid_i_t_map) > 0:
- self.driver.delete_connection(fabric, valid_i_t_map)
+ self.driver.delete_connection(fabric,
+ valid_i_t_map,
+ host_name,
+ storage_system)
- LOG.debug("Delete Connection - Finished iterating over all"
+ LOG.debug("Delete connection - finished iterating over all"
" target list")
except Exception as e:
msg = _("Failed removing connection for fabric=%(fabric)s: "
to list of target WWNs visible to the fabric.
"""
fabric_map = self.driver.get_san_context(target_wwn_list)
- LOG.debug("Got SAN context: %s", fabric_map)
+ LOG.debug("Got SAN context: %(fabricmap)s", {'fabricmap': fabric_map})
return fabric_map
def get_valid_initiator_target_map(self, initiator_target_map,
filtered_i_t_map[initiator] = t_list
else:
LOG.info(_LI("No targets to add or remove connection for "
- "I: %s"), initiator)
+ "initiator: %(init_wwn)s"),
+ {'init_wwn': initiator})
return filtered_i_t_map
--- /dev/null
+#
+# 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.
+#
+
+"""
+Common constants used by FC Zone Manager.
+"""
+IT_MAP = 'initiator_target_map'
+DATA = 'data'
+HOST = 'host_name'
+STORAGE = 'storage_system'
+SYSTEM = 'system'
def create_zone_manager():
"""If zoning is enabled, build the Zone Manager."""
config = configuration.Configuration(manager.volume_manager_opts)
- LOG.debug("Zoning mode: %s", config.safe_get('zoning_mode'))
+ 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()
def create_lookup_service():
config = configuration.Configuration(manager.volume_manager_opts)
- LOG.debug("Zoning mode: %s", config.safe_get('zoning_mode'))
+ LOG.debug("Zoning mode: %s.", config.safe_get('zoning_mode'))
if config.safe_get('zoning_mode') == 'fabric':
LOG.debug("FC Lookup Service enabled.")
lookup = fc_san_lookup_service.FCSanLookupService()
- LOG.info(_LI("Using FC lookup service %s"), lookup.lookup_service)
+ LOG.info(_LI("Using FC lookup service %s."), lookup.lookup_service)
return lookup
else:
LOG.debug("FC Lookup Service not enabled in cinder.conf.")
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:
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)
+ LOG.debug("AddFCZone connection info: %(conninfo)s.",
+ {'conninfo': conn_info})
+ zm.add_connection(conn_info)
return conn_info
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:
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)
+ LOG.debug("RemoveFCZone connection info: %(conninfo)s.",
+ {'conninfo': conn_info})
+ zm.delete_connection(conn_info)
return conn_info
--- /dev/null
+---
+features:
+ - Cinder FC Zone Manager Friendly Zone Names
+ This feature adds support for Fibre Channel user
+ friendly zone names if implemented by the volume driver.
+ If the volume driver passes the host name and
+ storage system to the Fibre Channel Zone Manager
+ in the conn_info structure, the zone manager
+ will use these names in structuring the zone
+ name to provide a user friendly zone name.