]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
ML2 Cisco Nexus MD: Support portchannel interfaces
authorRich Curran <rcurran@cisco.com>
Thu, 27 Mar 2014 19:34:08 +0000 (15:34 -0400)
committerRich Curran <rcurran@cisco.com>
Fri, 28 Mar 2014 14:14:00 +0000 (10:14 -0400)
Port of port-channel interface support that was implemented for the cisco
core plugin - https://review.openstack.org/#/c/42037

Note that additional port-channel UT already existed. (Created during
initial cisco core nexus subplugin -> ml2 cisco nexus md port.)
See test_cisco_nexus.py module, NEXUS_PORTCHANNELS references.

Closes-Bug: 1294900
Change-Id: Ifc64f605e5783ee1e85d66c87f422f64b47fa996

etc/neutron/plugins/ml2/ml2_conf_cisco.ini
neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py
neutron/plugins/ml2/drivers/cisco/nexus/nexus_db_v2.py
neutron/plugins/ml2/drivers/cisco/nexus/nexus_network_driver.py
neutron/plugins/ml2/drivers/cisco/nexus/nexus_snippets.py
neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py
neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py
neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus_db.py

index 6b6f5a76de96f3428dc07b40503f491329e2c09e..927c6f5bea7d1fe3331a3534627ade130eda47ec 100644 (file)
 #
 # Cisco Nexus Switch Format.
 # [ml2_mech_cisco_nexus:<IP address of switch>]
-# <hostname>=<port>                 (1)
+# <hostname>=<intf_type:port>       (1)
 # ssh_port=<ssh port>               (2)
 # username=<credential username>    (3)
 # password=<credential password>    (4)
 #
 # (1) For each host connected to a port on the switch, specify the hostname
 #     and the Nexus physical port (interface) it is connected to.
+#     Valid intf_type's are 'ethernet' and 'port-channel'.
+#     The default setting for <intf_type:> is 'ethernet' and need not be
+#     added to this setting.
 # (2) The TCP port for connecting via SSH to manage the switch. This is
 #     port number 22 unless the switch has been configured otherwise.
 # (3) The username for logging into the switch to manage it.
@@ -38,7 +41,8 @@
 # Example:
 # [ml2_mech_cisco_nexus:1.1.1.1]
 # compute1=1/1
-# compute2=1/2
+# compute2=ethernet:1/2
+# compute3=port-channel:1
 # ssh_port=22
 # username=admin
 # password=mySecretPassword
index 8f98f9fcae0cc95000f474f956bce205df1acf60..3514017cceb220614bd5169805e3b1c7359ab9dd 100644 (file)
@@ -68,10 +68,18 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
         return port['status'] == n_const.PORT_STATUS_ACTIVE
 
     def _get_switch_info(self, host_id):
+        host_connections = []
         for switch_ip, attr in self._nexus_switches:
             if str(attr) == str(host_id):
                 port_id = self._nexus_switches[switch_ip, attr]
-                return port_id, switch_ip
+                if ':' in port_id:
+                    intf_type, port = port_id.split(':')
+                else:
+                    intf_type, port = 'ethernet', port_id
+                host_connections.append((switch_ip, intf_type, port))
+
+        if host_connections:
+            return host_connections
         else:
             raise excep.NexusComputeHostNotConfigured(host=host_id)
 
@@ -80,9 +88,11 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
 
         Called during update precommit port event.
         """
-        port_id, switch_ip = self._get_switch_info(host_id)
-        nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
-                                      device_id)
+        host_connections = self._get_switch_info(host_id)
+        for switch_ip, intf_type, nexus_port in host_connections:
+            port_id = '%s:%s' % (intf_type, nexus_port)
+            nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
+                                          device_id)
 
     def _configure_switch_entry(self, vlan_id, device_id, host_id):
         """Create a nexus switch entry.
@@ -92,19 +102,21 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
 
         Called during update postcommit port event.
         """
-        port_id, switch_ip = self._get_switch_info(host_id)
         vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id)
-
-        # Check to see if this is the first binding to use this vlan on the
-        # switch/port. Configure switch accordingly.
-        bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
-        if len(bindings) == 1:
-            LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
-            self.driver.create_and_trunk_vlan(switch_ip, vlan_id, vlan_name,
-                                              port_id)
-        else:
-            LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
-            self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id, port_id)
+        host_connections = self._get_switch_info(host_id)
+
+        for switch_ip, intf_type, nexus_port in host_connections:
+            # Check to see if this is the first binding to use this vlan on the
+            # switch/port. Configure switch accordingly.
+            bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+            if len(bindings) == 1:
+                LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
+                self.driver.create_and_trunk_vlan(
+                    switch_ip, vlan_id, vlan_name, intf_type, nexus_port)
+            else:
+                LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
+                self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id,
+                                                     intf_type, nexus_port)
 
     def _delete_nxos_db(self, vlan_id, device_id, host_id):
         """Delete the nexus database entry.
@@ -112,9 +124,10 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
         Called during delete precommit port event.
         """
         try:
-            row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
-            nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
-                                             row.switch_ip, row.instance_id)
+            rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
+            for row in rows:
+                nxos_db.remove_nexusport_binding(
+                    row.port_id, row.vlan_id, row.switch_ip, row.instance_id)
         except excep.NexusPortBindingNotFound:
             return
 
@@ -126,21 +139,24 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
 
         Called during update postcommit port event.
         """
-        port_id, switch_ip = self._get_switch_info(host_id)
-
-        # if there are no remaining db entries using this vlan on this nexus
-        # switch port then remove vlan from the switchport trunk.
-        try:
-            nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip)
-        except excep.NexusPortBindingNotFound:
-            self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id, port_id)
-
+        host_connections = self._get_switch_info(host_id)
+        for switch_ip, intf_type, nexus_port in host_connections:
             # if there are no remaining db entries using this vlan on this
-            # nexus switch then remove the vlan.
+            # nexus switch port then remove vlan from the switchport trunk.
+            port_id = '%s:%s' % (intf_type, nexus_port)
             try:
-                nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+                nxos_db.get_port_vlan_switch_binding(port_id, vlan_id,
+                                                     switch_ip)
             except excep.NexusPortBindingNotFound:
-                self.driver.delete_vlan(switch_ip, vlan_id)
+                self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id,
+                                                      intf_type, nexus_port)
+
+                # if there are no remaining db entries using this vlan on this
+                # nexus switch then remove the vlan.
+                try:
+                    nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+                except excep.NexusPortBindingNotFound:
+                    self.driver.delete_vlan(switch_ip, vlan_id)
 
     def _is_vm_migration(self, context):
         if not context.bound_segment and context.original_bound_segment:
index a9194379d0f5bc980af5eea47cedc07dc10c4d93..141040e58512b7d72b39a7a43524780ffc9acc56 100644 (file)
@@ -82,11 +82,11 @@ def update_nexusport_binding(port_id, new_vlan_id):
     return binding
 
 
-def get_nexusvm_binding(vlan_id, instance_id):
+def get_nexusvm_bindings(vlan_id, instance_id):
     """Lists nexusvm bindings."""
-    LOG.debug(_("get_nexusvm_binding() called"))
-    return _lookup_first_nexus_binding(instance_id=instance_id,
-                                       vlan_id=vlan_id)
+    LOG.debug(_("get_nexusvm_bindings() called"))
+    return _lookup_all_nexus_bindings(instance_id=instance_id,
+                                      vlan_id=vlan_id)
 
 
 def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
index 4be4ab88fb7e00247b96764dd7cfc0e9c6fbd874..8670c553487f7dc4bba1e34134efeadcef98dd26 100644 (file)
@@ -154,54 +154,46 @@ class CiscoNexusDriver(object):
         confstr = self.create_xml_snippet(confstr)
         self._edit_config(nexus_host, target='running', config=confstr)
 
-    def enable_port_trunk(self, nexus_host, interface):
-        """Enable trunk mode an interface on Nexus Switch."""
-        confstr = snipp.CMD_PORT_TRUNK % (interface)
-        confstr = self.create_xml_snippet(confstr)
-        LOG.debug(_("NexusDriver: %s"), confstr)
-        self._edit_config(nexus_host, target='running', config=confstr)
-
-    def disable_switch_port(self, nexus_host, interface):
-        """Disable trunk mode an interface on Nexus Switch."""
-        confstr = snipp.CMD_NO_SWITCHPORT % (interface)
-        confstr = self.create_xml_snippet(confstr)
-        LOG.debug(_("NexusDriver: %s"), confstr)
-        self._edit_config(nexus_host, target='running', config=confstr)
-
-    def enable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
+    def enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
+                                 interface):
         """Enable a VLAN on a trunk interface."""
         # If more than one VLAN is configured on this interface then
         # include the 'add' keyword.
-        if len(nexus_db_v2.get_port_switch_bindings(interface,
-                                                    nexus_host)) == 1:
+        if len(nexus_db_v2.get_port_switch_bindings(
+                '%s:%s' % (intf_type, interface), nexus_host)) == 1:
             snippet = snipp.CMD_INT_VLAN_SNIPPET
         else:
             snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
-        confstr = snippet % (interface, vlanid)
+        confstr = snippet % (intf_type, interface, vlanid, intf_type)
         confstr = self.create_xml_snippet(confstr)
         LOG.debug(_("NexusDriver: %s"), confstr)
         self._edit_config(nexus_host, target='running', config=confstr)
 
-    def disable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
+    def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
+                                  interface):
         """Disable a VLAN on a trunk interface."""
-        confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
+        confstr = (snipp.CMD_NO_VLAN_INT_SNIPPET %
+                   (intf_type, interface, vlanid, intf_type))
         confstr = self.create_xml_snippet(confstr)
         LOG.debug(_("NexusDriver: %s"), confstr)
         self._edit_config(nexus_host, target='running', config=confstr)
 
     def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name,
-                              nexus_port):
+                              intf_type, nexus_port):
         """Create VLAN and trunk it on the specified ports."""
         self.create_vlan(nexus_host, vlan_id, vlan_name)
         LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id)
         if nexus_port:
-            self.enable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
+            self.enable_vlan_on_trunk_int(nexus_host, vlan_id, intf_type,
+                                          nexus_port)
 
-    def delete_and_untrunk_vlan(self, nexus_host, vlan_id, nexus_port):
+    def delete_and_untrunk_vlan(self, nexus_host, vlan_id, intf_type,
+                                nexus_port):
         """Delete VLAN and untrunk it from the specified ports."""
         self.delete_vlan(nexus_host, vlan_id)
         if nexus_port:
-            self.disable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
+            self.disable_vlan_on_trunk_int(nexus_host, vlan_id, intf_type,
+                                           nexus_port)
 
     def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
         confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
index b30c7e63878e5cd7340407e8bf09c28e4561922f..fb38e4199fa5c11bd7b4673020dcd52367233dae 100644 (file)
@@ -85,7 +85,7 @@ CMD_NO_VLAN_CONF_SNIPPET = """
 
 CMD_INT_VLAN_HEADER = """
           <interface>
-            <ethernet>
+            <%s>
               <interface>%s</interface>
               <__XML__MODE_if-ethernet-switch>
                 <switchport>
@@ -106,7 +106,7 @@ CMD_INT_VLAN_TRAILER = """
                   </trunk>
                 </switchport>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
@@ -120,7 +120,7 @@ CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
 
 CMD_PORT_TRUNK = """
           <interface>
-            <ethernet>
+            <%s>
               <interface>%s</interface>
               <__XML__MODE_if-ethernet-switch>
                 <switchport></switchport>
@@ -131,13 +131,13 @@ CMD_PORT_TRUNK = """
                   </mode>
                 </switchport>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
 CMD_NO_SWITCHPORT = """
           <interface>
-            <ethernet>
+            <%s>
               <interface>%s</interface>
               <__XML__MODE_if-ethernet-switch>
                 <no>
@@ -145,13 +145,13 @@ CMD_NO_SWITCHPORT = """
                   </switchport>
                 </no>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
 CMD_NO_VLAN_INT_SNIPPET = """
           <interface>
-            <ethernet>
+            <%s>
               <interface>%s</interface>
               <__XML__MODE_if-ethernet-switch>
                 <switchport></switchport>
@@ -167,7 +167,7 @@ CMD_NO_VLAN_INT_SNIPPET = """
                   </trunk>
                 </switchport>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
index 51a4f1d323eb078e652100a361d6947d0f3e53a4..c43315f5a5ff73b073ee1a96ecdaa4db588e204a 100644 (file)
@@ -418,8 +418,10 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
         with self._create_resources() as result:
             # Verify initial database entry.
             # Use port_id to verify that 1st host name was used.
-            binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1)
-            self.assertEqual(binding.port_id, NEXUS_INTERFACE)
+            binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
+                                                       DEVICE_ID_1)[0]
+            intf_type, nexus_port = binding.port_id.split(':')
+            self.assertEqual(nexus_port, NEXUS_INTERFACE)
 
             port = self.deserialize(self.fmt, result)
             port_id = port['port']['id']
@@ -434,7 +436,7 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
 
             # Verify that port entry has been deleted.
             self.assertRaises(c_exc.NexusPortBindingNotFound,
-                              nexus_db_v2.get_nexusvm_binding,
+                              nexus_db_v2.get_nexusvm_bindings,
                               VLAN_START, DEVICE_ID_1)
 
             # Trigger update event to bind segment with new host.
@@ -445,8 +447,10 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
 
             # Verify that port entry has been added using new host name.
             # Use port_id to verify that 2nd host name was used.
-            binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1)
-            self.assertEqual(binding.port_id, NEXUS_INTERFACE_2)
+            binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
+                                                       DEVICE_ID_1)[0]
+            intf_type, nexus_port = binding.port_id.split(':')
+            self.assertEqual(nexus_port, NEXUS_INTERFACE_2)
 
     def test_nexus_config_fail(self):
         """Test a Nexus switch configuration failure.
index dfa74c07f7f4fc4039ade1ba0aaf1b77833e3231..390477e1d3c234aff8ca831b4033da8a532c4660 100644 (file)
@@ -38,8 +38,8 @@ HOST_NAME_PC = 'testpchost'
 INSTANCE_1 = 'testvm1'
 INSTANCE_2 = 'testvm2'
 INSTANCE_PC = 'testpcvm'
-NEXUS_PORT_1 = '1/10'
-NEXUS_PORT_2 = '1/20'
+NEXUS_PORT_1 = 'ethernet:1/10'
+NEXUS_PORT_2 = 'ethernet:1/20'
 NEXUS_PORTCHANNELS = 'portchannel:2'
 VLAN_ID_1 = 267
 VLAN_ID_2 = 265
index b66514c541a7aa8cf5aa1938124be15ba0ccddf4..72d36ca493b6c148211f5391a271b076aac58b32 100644 (file)
@@ -76,8 +76,8 @@ class CiscoNexusDbTest(base.BaseTestCase):
         return nexus_db_v2.get_nexusvlan_binding(npb.vlan, npb.switch)
 
     def _get_nexusvm_binding(self, npb):
-        """Gets port bindings based on vlan and instance."""
-        return nexus_db_v2.get_nexusvm_binding(npb.vlan, npb.instance)
+        """Gets port binding based on vlan and instance."""
+        return nexus_db_v2.get_nexusvm_bindings(npb.vlan, npb.instance)[0]
 
     def _get_port_vlan_switch_binding(self, npb):
         """Gets port bindings based on port, vlan, and switch."""
@@ -149,7 +149,7 @@ class CiscoNexusDbTest(base.BaseTestCase):
         self._assert_bindings_match(npb, npb22)
 
         with testtools.ExpectedException(exceptions.NexusPortBindingNotFound):
-            nexus_db_v2.get_nexusvm_binding(npb21.vlan, "dummyInstance")
+            nexus_db_v2.get_nexusvm_bindings(npb21.vlan, "dummyInstance")[0]
 
     def test_nexusportvlanswitchbinding_get(self):
         """Tests get of port bindings based on port, vlan, and switch."""