password="password",
key="/home/stack/.ssh/id_rsa",
port=22,
+ vfid="2",
protocol=protocol
)
return client
class FakeBrcdFCZoneClientCLI(FakeClient):
def __init__(self, ipaddress, username,
- password, port, key, protocol):
+ password, port, key, vfid, protocol):
self.firmware_supported = True
if not GlobalVars._is_normal_test:
raise paramiko.SSHException("Unable to connect to fabric.")
class FakeBrcdHttpFCZoneClient(FakeClient):
def __init__(self, ipaddress, username,
- password, port, key, protocol):
+ password, port, key, vfid, protocol):
self.firmware_supported = True
if not GlobalVars._is_normal_test:
raise requests.exception.HTTPError("Unable to connect to fabric")
"""Unit tests for brcd fc zone client http(s)."""
import time
+import mock
from mock import patch
from cinder import exception
active_cfg = 'openstack_cfg'
activate = True
no_activate = False
+vf_enable = True
ns_info = ['10:00:00:05:1e:7c:64:96']
nameserver_info = """
<HTML>
swFWVersion=v7.3.0b_rc1_bld06
swDomain=2
"""
+parsed_session_info_vf = """
+sessionId=524461483
+user=admin
+userRole=admin
+isAdminRole=Yes
+authSource=0
+sessionIp=172.26.1.146
+valid=yes
+adName=
+adId=128
+adCapable=1
+currentAD=AD0
+currentADId=0
+homeAD=AD0
+trueADEnvironment=0
+adList=
+adIdList=
+pfAdmin=0
+switchIsMember=0
+definedADList=AD0,Physical Fabric
+definedADIdList=0,255,
+effectiveADList=AD0,Physical Fabric
+rc=0
+err=
+contextType=1
+vfEnabled=true
+vfSupported=true
+HomeVF=128
+sessionLFId=2
+isContextManageable=1
+manageableLFList=2,128,
+activeLFList=128,2,
+"""
+session_info_vf = """
+<BODY>
+<PRE>
+--BEGIN SESSION
+sessionId=524461483
+user=admin
+userRole=admin
+isAdminRole=Yes
+authSource=0
+sessionIp=172.26.1.146
+valid=yes
+adName=
+adId=128
+adCapable=1
+currentAD=AD0
+currentADId=0
+homeAD=AD0
+trueADEnvironment=0
+adList=
+adIdList=
+pfAdmin=0
+switchIsMember=0
+definedADList=AD0,Physical Fabric
+definedADIdList=0,255,
+effectiveADList=AD0,Physical Fabric
+rc=0
+err=
+contextType=1
+vfEnabled=true
+vfSupported=true
+HomeVF=128
+sessionLFId=2
+isContextManageable=1
+manageableLFList=2,128,
+activeLFList=128,2,
+--END SESSION
+</PRE>
+</BODY>
+"""
+session_info_vf_not_changed = """
+<BODY>
+<PRE>
+--BEGIN SESSION
+sessionId=524461483
+user=admin
+userRole=admin
+isAdminRole=Yes
+authSource=0
+sessionIp=172.26.1.146
+User-Agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML,
+valid=yes
+adName=
+adId=128
+adCapable=1
+currentAD=AD0
+currentADId=0
+homeAD=AD0
+trueADEnvironment=0
+adList=
+adIdList=
+pfAdmin=0
+switchIsMember=0
+definedADList=AD0,Physical Fabric
+definedADIdList=0,255,
+effectiveADList=AD0,Physical Fabric
+rc=0
+err=
+contextType=1
+vfEnabled=true
+vfSupported=true
+HomeVF=128
+sessionLFId=128
+isContextManageable=1
+manageableLFList=2,128,
+activeLFList=128,2,
+--END SESSION
+</PRE>
+</BODY>
+"""
+session_info_AD = """<HTML>
+<HEAD>
+<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
+<META HTTP-EQUIV="Expires" CONTENT="-1">
+<TITLE>Webtools Session Info</TITLE>
+</HEAD>
+<BODY>
+<PRE>
+--BEGIN SESSION
+sessionId=-2096740776
+user=
+userRole=root
+isAdminRole=No
+authSource=0
+sessionIp=
+User-Agent=
+valid=no
+adName=
+adId=0
+adCapable=1
+currentAD=AD0
+currentADId=0
+homeAD=AD0
+trueADEnvironment=0
+adList=
+adIdList=
+pfAdmin=0
+switchIsMember=1
+definedADList=AD0,Physical Fabric
+definedADIdList=0,255,
+effectiveADList=AD0,Physical Fabric
+rc=-2
+err=Could not obtain session data from store
+contextType=0
+--END SESSION
+</PRE>
+</BODY>
+</HTML>
+"""
zone_info = """<HTML>
<HEAD>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
parsed_value,
invalid_keyname)
+ def test_get_managable_vf_list(self):
+ manageable_list = ['2', '128']
+ self.assertEqual(
+ manageable_list, self.get_managable_vf_list(session_info_vf))
+ self.assertRaises(exception.BrocadeZoningHttpException,
+ self.get_managable_vf_list, session_info_AD)
+
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'is_vf_enabled')
+ def test_check_change_vf_context_vf_enabled(self, is_vf_enabled_mock):
+ is_vf_enabled_mock.return_value = (True, session_info_vf)
+ self.vfid = None
+ self.assertRaises(
+ exception.BrocadeZoningHttpException,
+ self.check_change_vf_context)
+ self.vfid = "2"
+ with mock.patch.object(self, 'change_vf_context') \
+ as change_vf_context_mock:
+ self.check_change_vf_context()
+ change_vf_context_mock.assert_called_once_with(
+ self.vfid, session_info_vf)
+
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'is_vf_enabled')
+ def test_check_change_vf_context_vf_disabled(self, is_vf_enabled_mock):
+ is_vf_enabled_mock.return_value = (False, session_info_AD)
+ self.vfid = "128"
+ self.assertRaises(
+ exception.BrocadeZoningHttpException,
+ self.check_change_vf_context)
+
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'get_managable_vf_list')
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'connect')
+ def test_change_vf_context_valid(self, connect_mock,
+ get_managable_vf_list_mock):
+ get_managable_vf_list_mock.return_value = ['2', '128']
+ connect_mock.return_value = session_info_vf
+ self.assertIsNone(self.change_vf_context("2", session_info_vf))
+ data = zone_constant.CHANGE_VF.format(vfid="2")
+ headers = {zone_constant.AUTH_HEADER: self.auth_header}
+ connect_mock.assert_called_once_with(
+ zone_constant.POST_METHOD, zone_constant.SESSION_PAGE,
+ data, headers)
+
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'get_managable_vf_list')
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'connect')
+ def test_change_vf_context_vf_not_changed(self,
+ connect_mock,
+ get_managable_vf_list_mock):
+ get_managable_vf_list_mock.return_value = ['2', '128']
+ connect_mock.return_value = session_info_vf_not_changed
+ self.assertRaises(exception.BrocadeZoningHttpException,
+ self.change_vf_context, "2", session_info_vf)
+ data = zone_constant.CHANGE_VF.format(vfid="2")
+ headers = {zone_constant.AUTH_HEADER: self.auth_header}
+ connect_mock.assert_called_once_with(
+ zone_constant.POST_METHOD, zone_constant.SESSION_PAGE,
+ data, headers)
+
+ @mock.patch.object(client.BrcdHTTPFCZoneClient, 'get_managable_vf_list')
+ def test_change_vf_context_vfid_not_managaed(self,
+ get_managable_vf_list_mock):
+ get_managable_vf_list_mock.return_value = ['2', '128']
+ self.assertRaises(exception.BrocadeZoningHttpException,
+ self.change_vf_context, "12", session_info_vf)
+
@patch.object(client.BrcdHTTPFCZoneClient, 'connect')
def test_is_supported_firmware(self, connect_mock):
connect_mock.return_value = switch_page_resp
connect_mock.return_value = nameserver_info
self.assertEqual(ns_info, self.get_nameserver_info())
+ @patch.object(client.BrcdHTTPFCZoneClient, 'get_session_info')
+ def test_is_vf_enabled(self, get_session_info_mock):
+ get_session_info_mock.return_value = session_info_vf
+ self.assertEqual((True, parsed_session_info_vf), self.is_vf_enabled())
+
def test_delete_update_zones_cfgs(self):
cfgs = {'openstack_cfg': 'zone1;zone2'}
cfg.StrOpt('zone_name_prefix',
default='openstack',
help='Overridden zone name prefix.'),
+ cfg.StrOpt('fc_virtual_fabric_id',
+ default=None,
+ help='Virtual Fabric ID.'),
cfg.StrOpt('principal_switch_wwn',
default=None,
deprecated_for_removal=True,
fabric_user = fabric.safe_get('fc_fabric_user')
fabric_pwd = fabric.safe_get('fc_fabric_password')
fabric_port = fabric.safe_get('fc_fabric_port')
+ fc_vfid = fabric.safe_get('fc_virtual_fabric_id')
fabric_ssh_cert_path = fabric.safe_get('fc_fabric_ssh_cert_path')
LOG.debug("Client not found. Creating connection client for"
{'ip': fabric_ip,
'connector': sb_connector,
'user': fabric_user,
- 'port': fabric_port})
+ 'port': fabric_port,
+ 'vf_id': fc_vfid})
if sb_connector.lower() in (fc_zone_constants.HTTP,
fc_zone_constants.HTTPS):
username=fabric_user,
password=fabric_pwd,
port=fabric_port,
+ vfid=fc_vfid,
protocol=sb_connector
)
else:
1.1 - Implements performance enhancements
1.2 - Added support for friendly zone name
1.3 - Added HTTP connector support
+ 1.4 - Adds support to zone in Virtual Fabrics
"""
- VERSION = "1.3"
+ VERSION = "1.4"
def __init__(self, **kwargs):
super(BrcdFCZoneDriver, self).__init__(**kwargs)
import time
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _, _LI
import cinder.zonemanager.drivers.brocade.fc_zone_constants as zone_constant
class BrcdHTTPFCZoneClient(object):
def __init__(self, ipaddress, username,
- password, port, protocol):
+ password, port, vfid, protocol):
"""Initializing the client with the parameters passed.
Creates authentication token and authenticate with switch
- to ensure the credentials are correct.
+ to ensure the credentials are correct and change the VF context.
:param ipaddress: IP Address of the device.
:param username: User id to login.
:param password: User password.
:param port: Device Communication port
+ :param vfid: Virtual Fabric ID.
:param protocol: Communication Protocol.
"""
self.switch_ip = ipaddress
self.switch_user = username
self.switch_pwd = password
self.protocol = protocol
+ self.vfid = vfid
self.cfgs = {}
self.zones = {}
self.alias = {}
# If authenticated successfully, save the auth status and
# create auth header for future communication with the device.
self.is_auth, self.auth_header = self.authenticate()
+ self.check_change_vf_context()
def connect(self, requestType, requestURL, payload='', header=None):
"""Connect to the switch using HTTP/HTTPS protocol.
LOG.error(msg)
raise exception.BrocadeZoningHttpException(reason=msg)
+ def get_managable_vf_list(self, session_info):
+ """List of VFIDs that can be managed.
+
+ :param session_info: Session information from the switch
+ :returns: manageable VF list
+ :raises: BrocadeZoningHttpException
+ """
+ try:
+ # Check the value of manageableLFList NVP,
+ # throw exception as not supported if the nvp not available
+ vf_list = self.get_nvp_value(session_info,
+ zone_constant.MANAGEABLE_VF)
+ if vf_list:
+ vf_list = vf_list.split(",") # convert the string to list
+ except exception.BrocadeZoningHttpException as e:
+ msg = (_("Error while checking whether "
+ "VF is available for management %s.") % six.text_type(e))
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+ return vf_list[:-1]
+
+ def change_vf_context(self, vfid, session_data):
+ """Change the VF context in the session.
+
+ :param vfid: VFID to which context should be changed.
+ :param session_data: Session information from the switch
+ :raises: BrocadeZoningHttpException
+ """
+ try:
+ managable_vf_list = self.get_managable_vf_list(session_data)
+ LOG.debug("Manageable VF IDs are %(vflist)s.",
+ {'vflist': managable_vf_list})
+ # proceed changing the VF context
+ # if VF id can be managed if not throw exception
+ if vfid in managable_vf_list:
+ headers = {zone_constant.AUTH_HEADER: self.auth_header}
+ data = zone_constant.CHANGE_VF.format(vfid=vfid)
+ response = self.connect(zone_constant.POST_METHOD,
+ zone_constant.SESSION_PAGE,
+ data,
+ headers)
+ parsed_info = self.get_parsed_data(response,
+ zone_constant.SESSION_BEGIN,
+ zone_constant.SESSION_END)
+ session_LF_Id = self.get_nvp_value(parsed_info,
+ zone_constant.SESSION_LF_ID)
+ if session_LF_Id == vfid:
+ LOG.info(_LI("VF context is changed in the session."))
+ else:
+ msg = _("Cannot change VF context in the session.")
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+
+ else:
+ msg = (_("Cannot change VF context, "
+ "specified VF is not available "
+ "in the manageable VF list %(vf_list)s.")
+ % {'vf_list': managable_vf_list})
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+ except exception.BrocadeZoningHttpException as e:
+ msg = (_("Error while changing VF context %s.") % six.text_type(e))
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+
def get_zone_info(self):
"""Parse all the zone information and store it in the dictionary."""
raise exception.BrocadeZoningHttpException(reason=msg)
return zones, cfgs, active_cfg
+ def is_vf_enabled(self):
+ """To check whether VF is enabled or not.
+
+ :returns: boolean to indicate VF enabled and session information
+ """
+ session_info = self.get_session_info()
+ parsed_data = self.get_parsed_data(session_info,
+ zone_constant.SESSION_BEGIN,
+ zone_constant.SESSION_END)
+ try:
+ is_vf_enabled = bool(self.get_nvp_value(
+ parsed_data, zone_constant.VF_ENABLED))
+ except exception.BrocadeZoningHttpException:
+ is_vf_enabled = False
+ parsed_data = None
+ return is_vf_enabled, parsed_data
+
def get_nameserver_info(self):
"""Get name server data from fabric.
zone_constant.ZONE_ERROR_MSG)
return errorCode, errorMessage
+ def check_change_vf_context(self):
+ """Check whether VF related configurations is valid and proceed."""
+ vf_enabled, session_data = self.is_vf_enabled()
+ # VF enabled will be false if vf is disable or not supported
+ LOG.debug("VF enabled on switch: %(vfenabled)s.",
+ {'vfenabled': vf_enabled})
+ # Change the VF context in the session
+ if vf_enabled:
+ if self.vfid is None:
+ msg = _("No VF ID is defined in the configuration file.")
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+ elif self.vfid != 128:
+ self.change_vf_context(self.vfid, session_data)
+ else:
+ if self.vfid is not None:
+ msg = _("VF is not enabled.")
+ LOG.error(msg)
+ raise exception.BrocadeZoningHttpException(reason=msg)
+
def cleanup(self):
"""Close session."""
self.session.close()
--- /dev/null
+---
+features:
+ - Support for configuring Fibre Channel zoning on
+ Brocade switches through Cinder Fibre Channel Zone
+ Manager and Brocade Fibre Channel zone plugin.
+ To zone in a Virtual Fabric, set the configuration
+ option 'fc_virtual_fabric_id' for the fabric.