]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Adds support for configuring zoning in a virtual fabric
authorAngela Smith <aallen@brocade.com>
Tue, 2 Feb 2016 21:00:54 +0000 (13:00 -0800)
committerAngela Smith <aallen@brocade.com>
Mon, 8 Feb 2016 21:49:04 +0000 (13:49 -0800)
Through use of HTTP connector, driver is able to set the VFID
context to support any virtual fabric configured in the chassis.

Implements: blueprint brocade-zone-driver-virtualfabrics-support

DocImpact

Change-Id: I52dd2eced18024c8b04107ef6cd797b3a5e19fb3

cinder/tests/unit/zonemanager/test_brcd_fc_zone_driver.py
cinder/tests/unit/zonemanager/test_brcd_http_fc_zone_client.py
cinder/zonemanager/drivers/brocade/brcd_fabric_opts.py
cinder/zonemanager/drivers/brocade/brcd_fc_zone_connector_factory.py
cinder/zonemanager/drivers/brocade/brcd_fc_zone_driver.py
cinder/zonemanager/drivers/brocade/brcd_http_fc_zone_client.py
releasenotes/notes/brocade_virtual_fabrics_support-d2d0b95b19457c1d.yaml [new file with mode: 0644]

index e42543dc049ecb47cc91ac267a9ca098d9f9dfe1..385e823b04c30765bd9cec918e70e88fdf2242c6 100644 (file)
@@ -121,6 +121,7 @@ class TestBrcdFcZoneDriver(BrcdFcZoneDriverBaseTest, test.TestCase):
             password="password",
             key="/home/stack/.ssh/id_rsa",
             port=22,
+            vfid="2",
             protocol=protocol
         )
         return client
@@ -217,7 +218,7 @@ class FakeClient(object):
 
 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.")
@@ -226,7 +227,7 @@ class FakeBrcdFCZoneClientCLI(FakeClient):
 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")
index 739e377b5a710c83c04ef7622452036378ae70db..627534d8ed058d6e72249679fcf36b83efe6b2ca 100644 (file)
@@ -17,6 +17,7 @@
 """Unit tests for brcd fc zone client http(s)."""
 import time
 
+import mock
 from mock import patch
 
 from cinder import exception
@@ -47,6 +48,7 @@ session = None
 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>
@@ -253,6 +255,157 @@ didOffset=96
 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">
@@ -367,6 +520,70 @@ class TestBrcdHttpFCZoneClient(client.BrcdHTTPFCZoneClient, test.TestCase):
                           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
@@ -499,6 +716,11 @@ class TestBrcdHttpFCZoneClient(client.BrcdHTTPFCZoneClient, test.TestCase):
         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'}
index 7cb11f73b54f2c32424b91f887e5f7ebecdbd8f1..b5b26343621ebf639a23fc9a15fdec5acad4ae9b 100644 (file)
@@ -48,6 +48,9 @@ brcd_zone_opts = [
     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,
index 0a4234d89f542552ae3524756c3d72214b82fe4b..a6e7a8a66794db57c40b89b308101a5b0865555b 100644 (file)
@@ -47,6 +47,7 @@ class BrcdFCZoneFactory(object):
             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"
@@ -55,7 +56,8 @@ class BrcdFCZoneFactory(object):
                       {'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):
@@ -66,6 +68,7 @@ class BrcdFCZoneFactory(object):
                     username=fabric_user,
                     password=fabric_pwd,
                     port=fabric_port,
+                    vfid=fc_vfid,
                     protocol=sb_connector
                 )
             else:
index 21cfbe962a279a3f56343d626f388bb817bf4a31..b927a6bb71926de3d90ecfe5b0641e8728e7828a 100644 (file)
@@ -68,9 +68,10 @@ class BrcdFCZoneDriver(fc_zone_driver.FCZoneDriver):
         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)
index 7c4398c04b8e49a803807e4c86370047d1759b20..bf3afc4114b8a630417ded5128c88c014878e87d 100644 (file)
@@ -24,7 +24,7 @@ import six
 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
 
 
@@ -34,22 +34,24 @@ LOG = logging.getLogger(__name__)
 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 = {}
@@ -67,6 +69,7 @@ class BrcdHTTPFCZoneClient(object):
         # 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.
@@ -259,6 +262,71 @@ class BrcdHTTPFCZoneClient(object):
             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."""
 
@@ -568,6 +636,23 @@ class BrcdHTTPFCZoneClient(object):
             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.
 
@@ -729,6 +814,26 @@ class BrcdHTTPFCZoneClient(object):
                                               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()
diff --git a/releasenotes/notes/brocade_virtual_fabrics_support-d2d0b95b19457c1d.yaml b/releasenotes/notes/brocade_virtual_fabrics_support-d2d0b95b19457c1d.yaml
new file mode 100644 (file)
index 0000000..bab9bc7
--- /dev/null
@@ -0,0 +1,7 @@
+---
+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.