]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Added support for NOS version 4.1.0, 5.0.0 and greater
authorShiv Haris <sharis@brocade.com>
Fri, 20 Jun 2014 22:05:10 +0000 (15:05 -0700)
committerShiv Haris <sharis@brocade.com>
Wed, 9 Jul 2014 20:25:01 +0000 (13:25 -0700)
NETCONF temaplates for NOS version greater than 4.1.0 are slightly
different (argh). An init time check of the NOS version is done
to enable selection of the correct NETCONF templates.

Change-Id: I01e82ad402fbbb25d92a99a3325ca2608dd514cb
Closes-bug: #1332719

etc/neutron/plugins/ml2/ml2_conf_brocade.ini
neutron/plugins/ml2/drivers/brocade/mechanism_brocade.py
neutron/plugins/ml2/drivers/brocade/nos/nctemplates.py
neutron/plugins/ml2/drivers/brocade/nos/nosdriver.py
neutron/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py

index 66987e9910de7b3062b3f2de484b0d803c680bbe..67574110b63bab3f6620c6ab95483199669eaa79 100644 (file)
@@ -3,6 +3,7 @@
 # password = <mgmt admin password>
 # address  = <switch mgmt ip address>
 # ostype   = NOS
+# osversion = autodetect | n.n.n
 # physical_networks = physnet1,physnet2
 #
 # Example:
@@ -10,4 +11,5 @@
 # password = password
 # address  = 10.24.84.38
 # ostype   = NOS
+# osversion   = 4.1.1
 # physical_networks = physnet1,physnet2
index 015921df5231f13a9905738e935311e70dc69378..92e0b2954f3e7e67127ae54f4cc1021f28a0e141 100644 (file)
@@ -39,7 +39,9 @@ ML2_BROCADE = [cfg.StrOpt('address', default='',
                cfg.StrOpt('physical_networks', default='',
                           help=_('Allowed physical networks')),
                cfg.StrOpt('ostype', default='NOS',
-                          help=_('Unused'))
+                          help=_('OS Type of the switch')),
+               cfg.StrOpt('osversion', default='4.0.0',
+                          help=_('OS Version number'))
                ]
 
 cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade")
@@ -66,12 +68,52 @@ class BrocadeMechanism(driver_api.MechanismDriver):
     def brocade_init(self):
         """Brocade specific initialization for this class."""
 
-        self._switch = {'address': cfg.CONF.ml2_brocade.address,
-                        'username': cfg.CONF.ml2_brocade.username,
-                        'password': cfg.CONF.ml2_brocade.password
-                        }
+        osversion = None
+        self._switch = {
+            'address': cfg.CONF.ml2_brocade.address,
+            'username': cfg.CONF.ml2_brocade.username,
+            'password': cfg.CONF.ml2_brocade.password,
+            'ostype': cfg.CONF.ml2_brocade.ostype,
+            'osversion': cfg.CONF.ml2_brocade.osversion}
+
         self._driver = importutils.import_object(NOS_DRIVER)
 
+        # Detect version of NOS on the switch
+        osversion = self._switch['osversion']
+        if osversion == "autodetect":
+            osversion = self._driver.get_nos_version(
+                self._switch['address'],
+                self._switch['username'],
+                self._switch['password'])
+
+        virtual_fabric_enabled = self._driver.is_virtual_fabric_enabled(
+                self._switch['address'],
+                self._switch['username'],
+                self._switch['password'])
+
+        if virtual_fabric_enabled:
+            LOG.debug(_("Virtual Fabric: enabled"))
+        else:
+            LOG.debug(_("Virtual Fabric: not enabled"))
+
+        self.set_features_enabled(osversion, virtual_fabric_enabled)
+
+    def set_features_enabled(self, nos_version, virtual_fabric_enabled):
+        self._virtual_fabric_enabled = virtual_fabric_enabled
+        version = nos_version.split(".", 2)
+
+        # Starting 4.1.0 port profile domains are supported
+        if int(version[0]) >= 5 or (int(version[0]) >= 4
+                                    and int(version[1]) >= 1):
+            self._pp_domains_supported = True
+        else:
+            self._pp_domains_supported = False
+        self._driver.set_features_enabled(self._pp_domains_supported,
+                                          self._virtual_fabric_enabled)
+
+    def get_features_enabled(self):
+        return self._pp_domains_supported, self._virtual_fabric_enabled
+
     def create_network_precommit(self, mech_context):
         """Create Network in the mechanism specific database table."""
 
index dbf7575deba6258cad0c2dc32a682fff8ac71712..f885ff82095583cff76aa29f229b388b070b6e1b 100644 (file)
 Interface Configuration Commands
 """
 
+# Get NOS Version
+SHOW_FIRMWARE_VERSION = (
+    "show-firmware-version xmlns:nc="
+    "'urn:brocade.com:mgmt:brocade-firmware-ext'"
+)
+GET_VCS_DETAILS = (
+    'get-vcs-details xmlns:nc="urn:brocade.com:mgmt:brocade-vcs"'
+)
+SHOW_VIRTUAL_FABRIC = (
+    'show-virtual-fabric xmlns:nc="urn:brocade.com:mgmt:brocade-vcs"'
+)
+GET_VIRTUAL_FABRIC_INFO = (
+    'interface xmlns:nc="urn:brocade.com:mgmt:brocade-firmware-ext"'
+)
+
+NOS_VERSION = "./*/{urn:brocade.com:mgmt:brocade-firmware-ext}os-version"
+VFAB_ENABLE = "./*/*/*/{urn:brocade.com:mgmt:brocade-vcs}vfab-enable"
+
 # Create VLAN (vlan_id)
 CREATE_VLAN_INTERFACE = """
     <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
@@ -72,6 +90,20 @@ CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
     </config>
 """
 
+# Configure L2 mode for VLAN sub-profile (port_profile_name)
+CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN = """
+    <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+            <name>{name}</name>
+            <vlan-profile>
+                <switchport-basic>
+                   <basic/>
+                </switchport-basic>
+            </vlan-profile>
+        </port-profile>
+    </config>
+"""
+
 # Configure L2 mode for VLAN sub-profile (port_profile_name)
 CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """
     <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
@@ -185,6 +217,29 @@ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
     </config>
 """
 
+#port-profile domain management commands
+REMOVE_PORTPROFILE_FROM_DOMAIN = """
+    <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <port-profile-domain xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+            <port-profile-domain-name>{domain_name}</port-profile-domain-name>
+                <profile  operation="delete">
+                    <profile-name>{name}</profile-name>
+                </profile>
+            </port-profile-domain>
+    </config>
+"""
+#put port profile in default domain
+CONFIGURE_PORTPROFILE_IN_DOMAIN = """
+    <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <port-profile-domain xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+            <port-profile-domain-name>{domain_name}</port-profile-domain-name>
+                <profile>
+                    <profile-name>{name}</profile-name>
+                </profile>
+            </port-profile-domain>
+    </config>
+"""
+
 #
 # Constants
 #
index f647370aee6d9954a562026d48ef22b92c764916..47d6330951c8e30f4af802586fdedb40f9c05b3c 100644 (file)
@@ -23,6 +23,7 @@ Neutron network life-cycle management.
 """
 
 from ncclient import manager
+from xml.etree import ElementTree
 
 from neutron.openstack.common import excutils
 from neutron.openstack.common import log as logging
@@ -51,6 +52,18 @@ class NOSdriver():
 
     def __init__(self):
         self.mgr = None
+        self._virtual_fabric_enabled = False
+        self._pp_domains_supported = False
+
+    def set_features_enabled(self, pp_domains_supported,
+                             virtual_fabric_enabled):
+        """Set features in the driver based on what was detected by the MD."""
+        self._pp_domains_supported = pp_domains_supported
+        self._virtual_fabric_enabled = virtual_fabric_enabled
+
+    def get_features_enabled(self):
+        """Respond to status of features enabled."""
+        return self._pp_domains_supported, self._virtual_fabric_enabled
 
     def connect(self, host, username, password):
         """Connect via SSH and initialize the NETCONF session."""
@@ -69,6 +82,7 @@ class NOSdriver():
             self.mgr = manager.connect(host=host, port=SSH_PORT,
                                        username=username, password=password,
                                        unknown_host_cb=nos_unknown_host_cb)
+
         except Exception:
             with excutils.save_and_reraise_exception():
                 LOG.exception(_("Connect failed to switch"))
@@ -83,16 +97,46 @@ class NOSdriver():
             self.mgr.close_session()
             self.mgr = None
 
+    def get_nos_version(self, host, username, password):
+        """Show version of NOS."""
+        try:
+            mgr = self.connect(host, username, password)
+            return self.nos_version_request(mgr)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("NETCONF error"))
+                self.close_session()
+
+    def is_virtual_fabric_enabled(self, host, username, password):
+        """Show version of NOS."""
+        try:
+            mgr = self.connect(host, username, password)
+            return (self.virtual_fabric_info(mgr) == "enabled")
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("NETCONF error"))
+                self.close_session()
+
     def create_network(self, host, username, password, net_id):
         """Creates a new virtual network."""
 
+        domain_name = "default"
         name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
         try:
             mgr = self.connect(host, username, password)
             self.create_vlan_interface(mgr, net_id)
             self.create_port_profile(mgr, name)
+
+            if self._pp_domains_supported and self._virtual_fabric_enabled:
+                self.configure_port_profile_in_domain(mgr, domain_name, name)
+
             self.create_vlan_profile_for_port_profile(mgr, name)
-            self.configure_l2_mode_for_vlan_profile(mgr, name)
+
+            if self._pp_domains_supported:
+                self.configure_l2_mode_for_vlan_profile_with_domains(mgr, name)
+            else:
+                self.configure_l2_mode_for_vlan_profile(mgr, name)
+
             self.configure_trunk_mode_for_vlan_profile(mgr, name)
             self.configure_allowed_vlans_for_vlan_profile(mgr, name, net_id)
             self.activate_port_profile(mgr, name)
@@ -104,9 +148,12 @@ class NOSdriver():
     def delete_network(self, host, username, password, net_id):
         """Deletes a virtual network."""
 
+        domain_name = "default"
         name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
         try:
             mgr = self.connect(host, username, password)
+            if self._pp_domains_supported and self._virtual_fabric_enabled:
+                self.remove_port_profile_from_domain(mgr, domain_name, name)
             self.deactivate_port_profile(mgr, name)
             self.delete_port_profile(mgr, name)
             self.delete_vlan_interface(mgr, net_id)
@@ -234,3 +281,37 @@ class NOSdriver():
         confstr = template.CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE.format(
             name=name, vlan_id=vlan_id)
         mgr.edit_config(target='running', config=confstr)
+
+    def remove_port_profile_from_domain(self, mgr, domain_name, name):
+        """Remove port-profile from default domain."""
+        confstr = template.REMOVE_PORTPROFILE_FROM_DOMAIN.format(
+            domain_name=domain_name, name=name)
+        mgr.edit_config(target='running', config=confstr)
+
+    def configure_port_profile_in_domain(self, mgr, domain_name, name):
+        """put port-profile in default domain."""
+        confstr = template.CONFIGURE_PORTPROFILE_IN_DOMAIN.format(
+            domain_name=domain_name, name=name)
+        mgr.edit_config(target='running', config=confstr)
+
+    def configure_l2_mode_for_vlan_profile_with_domains(self, mgr, name):
+        """Configures L2 mode for VLAN sub-profile."""
+        confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN.format(
+            name=name)
+        mgr.edit_config(target='running', config=confstr)
+
+    def nos_version_request(self, mgr):
+        """Get firmware information using NETCONF rpc."""
+        reply = mgr.dispatch(template.SHOW_FIRMWARE_VERSION, None, None)
+        et = ElementTree.fromstring(str(reply))
+        return et.find(template.NOS_VERSION).text
+
+    def virtual_fabric_info(self, mgr):
+        """Get virtual fabric info using NETCONF get-config."""
+        response = mgr.get_config('running',
+                                  filter=("xpath", "/vcs/virtual-fabric"))
+        et = ElementTree.fromstring(str(response))
+        vfab_enable = et.find(template.VFAB_ENABLE)
+        if vfab_enable is not None:
+            return "enabled"
+        return "disabled"
index 721822dfb614ba88c4b04236946a1ea5fdf2acfb..4f20f2ccc82853a16c06c1387f388a145649d419 100644 (file)
@@ -66,3 +66,51 @@ class TestBrocadeMechDriverPortsV2(test_db_plugin.TestPortsV2,
 class TestBrocadeMechDriverSubnetsV2(test_db_plugin.TestSubnetsV2,
                                      TestBrocadeMechDriverV2):
     pass
+
+
+class TestBrocadeMechDriverFeaturesEnabledTestCase(TestBrocadeMechDriverV2):
+
+    def setUp(self):
+        super(TestBrocadeMechDriverFeaturesEnabledTestCase, self).setUp()
+
+    def test_version_features(self):
+
+        vf = True
+        # Test for NOS version 4.0.3
+        self.mechanism_driver.set_features_enabled("4.0.3", vf)
+        # Verify
+        pp_domain_support, virtual_fabric_enabled = (
+            self.mechanism_driver.get_features_enabled()
+        )
+        self.assertFalse(pp_domain_support)
+        self.assertTrue(virtual_fabric_enabled)
+
+        # Test for NOS version 4.1.0
+        vf = True
+        self.mechanism_driver.set_features_enabled("4.1.0", vf)
+        # Verify
+        pp_domain_support, virtual_fabric_enabled = (
+            self.mechanism_driver.get_features_enabled()
+        )
+        self.assertTrue(pp_domain_support)
+        self.assertTrue(virtual_fabric_enabled)
+
+        # Test for NOS version 4.1.3
+        vf = False
+        self.mechanism_driver.set_features_enabled("4.1.3", vf)
+        # Verify
+        pp_domain_support, virtual_fabric_enabled = (
+            self.mechanism_driver.get_features_enabled()
+        )
+        self.assertTrue(pp_domain_support)
+        self.assertFalse(virtual_fabric_enabled)
+
+        # Test for NOS version 5.0.0
+        vf = True
+        self.mechanism_driver.set_features_enabled("5.0.0", vf)
+        # Verify
+        pp_domain_support, virtual_fabric_enabled = (
+            self.mechanism_driver.get_features_enabled()
+        )
+        self.assertTrue(pp_domain_support)
+        self.assertTrue(virtual_fabric_enabled)