]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Adding VPC support to the Cisco plugin
authorArvind Somya <asomya@cisco.com>
Tue, 28 May 2013 19:47:09 +0000 (12:47 -0700)
committerArvind Somya <asomya@cisco.com>
Wed, 21 Aug 2013 14:26:38 +0000 (10:26 -0400)
Adding VPC(Virtual Port Channel) support to the Cisco plugin.

Change-Id: I898e4355d05f6f43593deb2d977dfc1e55fb2fc8
Implements: Blueprint cisco-plugin-vpc-support

neutron/plugins/cisco/db/nexus_db_v2.py
neutron/plugins/cisco/models/virt_phy_sw_v2.py
neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py
neutron/plugins/cisco/nexus/cisco_nexus_snippets.py
neutron/plugins/cisco/test/nexus/fake_nexus_driver.py
neutron/tests/unit/cisco/test_network_plugin.py
neutron/tests/unit/cisco/test_nexus_db.py
neutron/tests/unit/cisco/test_nexus_plugin.py

index 6ee7ca91119847557c7670c72557f8c3600e15c5..a11a8a04187ceea08262d058208d45f99d19c615 100644 (file)
@@ -86,11 +86,12 @@ 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)
+
+    return _lookup_all_nexus_bindings(vlan_id=vlan_id,
+                                      instance_id=instance_id)
 
 
 def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
index f3c49eec1275daaed910e354c3df98f9673a064e..4b42a095f5f362aa58628e6c849ff9c3fa5ec320 100644 (file)
@@ -406,7 +406,8 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         """
         LOG.debug(_("delete_port() called"))
         port = self.get_port(context, id)
-        if self.config_nexus:
+        exclude_list = ('', 'compute:none', 'network:dhcp')
+        if self.config_nexus and port['device_owner'] not in exclude_list:
             vlan_id = self._get_segmentation_id(port['network_id'])
             n_args = [port['device_id'], vlan_id]
             self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
index 645d914008a4144db06a8cb829f35943c2723408..bb0f8e6deca77df71e24639dbd35b631cbff6794 100644 (file)
@@ -151,53 +151,57 @@ class CiscoNEXUSDriver():
         confstr = self.create_xml_snippet(confstr)
         self._edit_config(nexus_host, target='running', config=confstr)
 
-    def enable_port_trunk(self, nexus_host, interface):
+    def enable_port_trunk(self, nexus_host, etype, interface):
         """Enable trunk mode an interface on Nexus Switch."""
-        confstr = snipp.CMD_PORT_TRUNK % (interface)
+        confstr = snipp.CMD_PORT_TRUNK % (etype, interface, etype)
         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):
+    def disable_switch_port(self, nexus_host, etype, interface):
         """Disable trunk mode an interface on Nexus Switch."""
-        confstr = snipp.CMD_NO_SWITCHPORT % (interface)
+        confstr = snipp.CMD_NO_SWITCHPORT % (etype, interface, etype)
         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, etype, interface):
         """Enable a VLAN on a trunk interface."""
         # If one or more VLANs are already configured on this interface,
         # include the 'add' keyword.
-        if nexus_db_v2.get_port_switch_bindings(interface, nexus_host):
+        if nexus_db_v2.get_port_switch_bindings('%s:%s' % (etype, interface),
+                                                nexus_host):
             snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
         else:
             snippet = snipp.CMD_INT_VLAN_SNIPPET
-        confstr = snippet % (interface, vlanid)
+        confstr = snippet % (etype, interface, vlanid, etype)
         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, etype, interface):
         """Disable a VLAN on a trunk interface."""
-        confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
+        confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (etype, interface,
+                                                   vlanid, etype)
         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):
+                              etype, 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,
+                                          etype, nexus_port)
 
-    def delete_and_untrunk_vlan(self, nexus_host, vlan_id, nexus_port):
+    def delete_and_untrunk_vlan(self, nexus_host, vlan_id, etype, 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,
+                                           etype, nexus_port)
 
     def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
         confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
index 68b8bbf122faebf9d4969a584d9ca6b13a3ea1ef..4f700b46c89eb3d0d7f3ece71fbbe9c95d8e930d 100644 (file)
@@ -48,6 +48,7 @@ class NexusPlugin(L2DevicePluginBase):
         """Extract configuration parameters from the configuration file."""
         self._client = importutils.import_object(conf.CISCO.nexus_driver)
         LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver)
+        self._nexus_switches = conf.get_device_dictionary()
 
     def get_all_networks(self, tenant_id):
         """Get all networks.
@@ -71,10 +72,20 @@ class NexusPlugin(L2DevicePluginBase):
         appropriate interfaces for this VLAN.
         """
         LOG.debug(_("NexusPlugin:create_network() called"))
-        # Grab the switch IP and port for this host
-        host = str(attachment[const.HOST_NAME])
-        switch_ip, port_id = self._client.get_switch_and_port_id(host)
-        if not switch_ip and not port_id:
+        # Grab the switch IPs and ports for this host
+        host_connections = []
+        host = attachment['host_name']
+        for switch_type, switch_ip, attr in self._nexus_switches:
+            if str(attr) == str(host):
+                port = self._nexus_switches[switch_type, switch_ip, attr]
+                # Get ether type for port, assume an ethernet type
+                # if none specified.
+                if ':' in port:
+                    etype, port_id = port.split(':')
+                else:
+                    etype, port_id = 'ethernet', port
+                host_connections.append((switch_ip, etype, port_id))
+        if not host_connections:
             raise cisco_exc.NexusComputeHostNotConfigured(host=host)
 
         vlan_id = network[const.NET_VLAN_ID]
@@ -88,21 +99,19 @@ class NexusPlugin(L2DevicePluginBase):
             auto_trunk = conf.CISCO.provider_vlan_auto_trunk
 
         # Check if this network is already in the DB
-        vlan_created = False
-        vlan_trunked = False
-
-        try:
-            nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip)
-        except cisco_exc.NexusPortBindingNotFound:
-            # Check for vlan/switch binding
+        for switch_ip, etype, port_id in host_connections:
+            vlan_created = False
+            vlan_trunked = False
+            eport_id = '%s:%s' % (etype, port_id)
             try:
-                nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+                nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id,
+                                                     switch_ip)
             except cisco_exc.NexusPortBindingNotFound:
                 if auto_create and auto_trunk:
                     # Create vlan and trunk vlan on the port
                     LOG.debug("Nexus: create & trunk vlan %s" % vlan_name)
                     self._client.create_and_trunk_vlan(
-                        switch_ip, vlan_id, vlan_name, port_id)
+                        switch_ip, vlan_id, vlan_name, etype, port_id)
                     vlan_created = True
                     vlan_trunked = True
                 elif auto_create:
@@ -110,32 +119,35 @@ class NexusPlugin(L2DevicePluginBase):
                     LOG.debug("Nexus: create vlan %s" % vlan_name)
                     self._client.create_vlan(switch_ip, vlan_id, vlan_name)
                     vlan_created = True
-            else:
-                if auto_trunk:
+                elif auto_trunk:
                     # Only trunk vlan on the port
                     LOG.debug("Nexus: trunk vlan %s" % vlan_name)
                     self._client.enable_vlan_on_trunk_int(
-                        switch_ip, vlan_id, port_id)
+                        switch_ip, vlan_id, etype, port_id)
                     vlan_trunked = True
 
-        try:
-            instance = attachment[const.INSTANCE_ID]
-            nxos_db.add_nexusport_binding(port_id, str(vlan_id),
-                                          switch_ip, instance)
-        except Exception:
-            with excutils.save_and_reraise_exception():
-                # Add binding failed, roll back any vlan creation/enabling
-                if vlan_created and vlan_trunked:
-                    LOG.debug("Nexus: delete & untrunk vlan %s" % vlan_name)
-                    self._client.delete_and_untrunk_vlan(switch_ip, vlan_id,
-                                                         port_id)
-                elif vlan_created:
-                    LOG.debug("Nexus: delete vlan %s" % vlan_name)
-                    self._client.delete_vlan(switch_ip, vlan_id)
-                elif vlan_trunked:
-                    LOG.debug("Nexus: untrunk vlan %s" % vlan_name)
-                    self._client.disable_vlan_on_trunk_int(switch_ip, vlan_id,
-                                                           port_id)
+            try:
+                instance = attachment[const.INSTANCE_ID]
+                nxos_db.add_nexusport_binding(eport_id, str(vlan_id),
+                                              switch_ip, instance)
+            except Exception:
+                with excutils.save_and_reraise_exception():
+                    # Add binding failed, roll back any vlan creation/enabling
+                    if vlan_created and vlan_trunked:
+                        LOG.debug("Nexus: delete & untrunk vlan %s" %
+                                  vlan_name)
+                        self._client.delete_and_untrunk_vlan(switch_ip,
+                                                             vlan_id,
+                                                             etype, port_id)
+                    elif vlan_created:
+                        LOG.debug("Nexus: delete vlan %s" % vlan_name)
+                        self._client.delete_vlan(switch_ip, vlan_id)
+                    elif vlan_trunked:
+                        LOG.debug("Nexus: untrunk vlan %s" % vlan_name)
+                        self._client.disable_vlan_on_trunk_int(switch_ip,
+                                                               vlan_id,
+                                                               etype,
+                                                               port_id)
 
         net_id = network[const.NET_ID]
         new_net_dict = {const.NET_ID: net_id,
@@ -160,10 +172,10 @@ class NexusPlugin(L2DevicePluginBase):
         except cisco_exc.NexusPortBindingNotFound:
             # Create vlan and trunk vlan on the port
             self._client.create_and_trunk_vlan(
-                switch_ip, vlan_id, vlan_name, nexus_port=None)
+                switch_ip, vlan_id, vlan_name, etype=None, nexus_port=None)
         # Check if a router interface has already been created
         try:
-            nxos_db.get_nexusvm_binding(vlan_id, router_id)
+            nxos_db.get_nexusvm_bindings(vlan_id, router_id)
             raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id,
                                                    router_id=router_id)
         except cisco_exc.NexusPortBindingNotFound:
@@ -176,8 +188,8 @@ class NexusPlugin(L2DevicePluginBase):
     def remove_router_interface(self, vlan_id, router_id):
         """Remove VLAN SVI from the Nexus Switch."""
         # Grab switch_ip from database
-        switch_ip = nxos_db.get_nexusvm_binding(vlan_id,
-                                                router_id).switch_ip
+        switch_ip = nxos_db.get_nexusvm_bindings(vlan_id,
+                                                 router_id)[0].switch_ip
 
         # Delete the SVI interface from the switch
         self._client.delete_vlan_svi(switch_ip, vlan_id)
@@ -250,9 +262,9 @@ class NexusPlugin(L2DevicePluginBase):
         is still required on the interfaces trunked.
         """
         LOG.debug(_("NexusPlugin:delete_port() called"))
-        # Delete DB row for this port
+        # Delete DB row(s) for this port
         try:
-            row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
+            rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
         except cisco_exc.NexusPortBindingNotFound:
             return
 
@@ -263,37 +275,42 @@ class NexusPlugin(L2DevicePluginBase):
             auto_untrunk = conf.CISCO.provider_vlan_auto_trunk
             LOG.debug("delete_network(): provider vlan %s" % vlan_id)
 
-        switch_ip = row.switch_ip
-        nexus_port = None
-        if row.port_id != 'router':
-            nexus_port = row.port_id
+        instance_id = False
+        for row in rows:
+            instance_id = row['instance_id']
+            switch_ip = row.switch_ip
+            etype, nexus_port = '', ''
+            if row['port_id'] == 'router':
+                etype, nexus_port = 'vlan', row['port_id']
+            else:
+                etype, nexus_port = row['port_id'].split(':')
 
-        nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
-                                         row.switch_ip,
-                                         row.instance_id)
-        # Check for any other bindings with the same vlan_id and switch_ip
-        try:
-            nxos_db.get_nexusvlan_binding(row.vlan_id, row.switch_ip)
-        except cisco_exc.NexusPortBindingNotFound:
+            nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
+                                             row.switch_ip,
+                                             row.instance_id)
+            # Check for any other bindings with the same vlan_id and switch_ip
             try:
-                # Delete this vlan from this switch
-                if nexus_port and auto_untrunk:
-                    self._client.disable_vlan_on_trunk_int(
-                        switch_ip, row.vlan_id, nexus_port)
-                if auto_delete:
-                    self._client.delete_vlan(switch_ip, row.vlan_id)
-            except Exception:
-                # The delete vlan operation on the Nexus failed,
-                # so this delete_port request has failed. For
-                # consistency, roll back the Nexus database to what
-                # it was before this request.
-                with excutils.save_and_reraise_exception():
-                    nxos_db.add_nexusport_binding(row.port_id,
-                                                  row.vlan_id,
-                                                  row.switch_ip,
-                                                  row.instance_id)
-
-        return row.instance_id
+                nxos_db.get_nexusvlan_binding(row.vlan_id, row.switch_ip)
+            except cisco_exc.NexusPortBindingNotFound:
+                try:
+                    # Delete this vlan from this switch
+                    if nexus_port and auto_untrunk:
+                        self._client.disable_vlan_on_trunk_int(
+                            switch_ip, row.vlan_id, etype, nexus_port)
+                    if auto_delete:
+                        self._client.delete_vlan(switch_ip, row.vlan_id)
+                except Exception:
+                    # The delete vlan operation on the Nexus failed,
+                    # so this delete_port request has failed. For
+                    # consistency, roll back the Nexus database to what
+                    # it was before this request.
+                    with excutils.save_and_reraise_exception():
+                        nxos_db.add_nexusport_binding(row.port_id,
+                                                      row.vlan_id,
+                                                      row.switch_ip,
+                                                      row.instance_id)
+
+        return instance_id
 
     def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
         """Update port.
index cf7f6332fdca25fb76d96e79b5147b7fcf37d02f..831462154f970cf82bfda8e2aed9e63a89c4f34c 100644 (file)
@@ -15,6 +15,7 @@
 #    under the License.
 #
 # @author: Edgar Magana, Cisco Systems, Inc.
+# @author: Arvind Somya (asomya@cisco.com) Cisco Systems, Inc.
 
 """
 Nexus-OS XML-based configuration snippets
@@ -88,7 +89,7 @@ CMD_NO_VLAN_CONF_SNIPPET = """
 
 CMD_INT_VLAN_HEADER = """
           <interface>
-            <ethernet>
+            <%s>
               <interface>%s</interface>
               <__XML__MODE_if-ethernet-switch>
                 <switchport>
@@ -109,7 +110,7 @@ CMD_INT_VLAN_TRAILER = """
                   </trunk>
                 </switchport>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
@@ -123,7 +124,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>
@@ -134,13 +135,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>
@@ -148,14 +149,14 @@ 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>
@@ -171,7 +172,7 @@ CMD_NO_VLAN_INT_SNIPPET = """
                   </trunk>
                 </switchport>
               </__XML__MODE_if-ethernet-switch>
-            </ethernet>
+            </%s>
           </interface>
 """
 
index 0841d363e086b588b8400d9651335cbf97e3064f..e56b4866ec005da1c560e9afd98dd35c954f2073 100644 (file)
@@ -53,7 +53,7 @@ class CiscoNEXUSFakeDriver():
         """Disable trunk mode an interface on Nexus Switch."""
         pass
 
-    def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+    def enable_vlan_on_trunk_int(self, mgr, etype, interface, vlanid):
         """Enable vlan on trunk interface.
 
         Enable trunk mode vlan access an interface on Nexus Switch given
index dcb9826647fada09b67130113cc60fc0b709b6ca..656f0086126e37423a0fa29ca0b70aecbe3ac342 100644 (file)
@@ -184,7 +184,9 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
         with self.network(name=name) as network:
             with self.subnet(network=network, cidr=cidr) as subnet:
                 net_id = subnet['subnet']['network_id']
-                res = self._create_port(self.fmt, net_id)
+                res = self._create_port(self.fmt, net_id,
+                                        device_id='testdev',
+                                        device_owner='testowner')
                 port = self.deserialize(self.fmt, res)
                 try:
                     yield res
index 712dffd40edf7fd40d222c992c35c93f2725f0aa..023bcc02938aa2a6b78dc439ea3d656aaa8af505 100644 (file)
@@ -115,13 +115,13 @@ class CiscoNexusDbTest(base.BaseTestCase):
         npb22 = self._npb_test_obj(20, 200)
         self._add_to_db([npb11, npb21, npb22])
 
-        npb = nxdb.get_nexusvm_binding(npb21.vlan, npb21.instance)
+        npb = nxdb.get_nexusvm_bindings(npb21.vlan, npb21.instance)[0]
         self._assert_equal(npb, npb21)
-        npb = nxdb.get_nexusvm_binding(npb22.vlan, npb22.instance)
+        npb = nxdb.get_nexusvm_bindings(npb22.vlan, npb22.instance)[0]
         self._assert_equal(npb, npb22)
 
         with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
-            nxdb.get_nexusvm_binding(npb21.vlan, "dummyInstance")
+            nxdb.get_nexusvm_bindings(npb21.vlan, "dummyInstance")
 
     def test_nexusportvlanswitchbinding_get(self):
         npb11 = self._npb_test_obj(10, 100)
index cf0c70fe7cb6d967c64c284b09bc1732a5fdc094..a8507797d8b68e953aae7205ba348b1c3af84190 100644 (file)
@@ -30,10 +30,15 @@ from neutron.tests import base
 NEXUS_IP_ADDRESS = '1.1.1.1'
 HOSTNAME1 = 'testhost1'
 HOSTNAME2 = 'testhost2'
+HOSTNAME3 = 'testhost3'
 INSTANCE1 = 'testvm1'
 INSTANCE2 = 'testvm2'
+INSTANCE3 = 'testvm3'
 NEXUS_PORT1 = '1/10'
 NEXUS_PORT2 = '1/20'
+NEXUS_PC_IP_ADDRESS = '2.2.2.2'
+NEXUS_PORTCHANNELS = 'portchannel:2'
+PC_HOSTNAME = 'testpchost'
 NEXUS_SSH_PORT = '22'
 NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.'
                 'cisco_nexus_network_driver_v2.CiscoNEXUSDriver')
@@ -58,6 +63,8 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
         self.second_net_id = 5
         self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
         self.second_vlan_id = 265
+        self._pchostname = PC_HOSTNAME
+
         self.attachment1 = {
             const.TENANT_ID: self.tenant_id,
             const.INSTANCE_ID: INSTANCE1,
@@ -68,6 +75,11 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
             const.INSTANCE_ID: INSTANCE2,
             const.HOST_NAME: HOSTNAME2,
         }
+        self.attachment3 = {
+            const.TENANT_ID: self.second_tenant_id,
+            const.INSTANCE_ID: INSTANCE3,
+            const.HOST_NAME: HOSTNAME3,
+        }
         self.network1 = {
             const.NET_ID: self.net_id,
             const.NET_NAME: self.net_name,
@@ -80,6 +92,12 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
             const.NET_VLAN_NAME: self.second_vlan_name,
             const.NET_VLAN_ID: self.second_vlan_id,
         }
+        self.network3 = {
+            const.NET_ID: 8,
+            const.NET_NAME: 'vpc_net',
+            const.NET_VLAN_NAME: 'q-268',
+            const.NET_VLAN_ID: '268',
+        }
         self.providernet = {
             const.NET_ID: 9,
             const.NET_NAME: 'pnet1',
@@ -97,12 +115,28 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
                 (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
                 (NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
                 (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
+                (NEXUS_PC_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
+            }
+            self._nexus_switches = {
+                ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1,
+                ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
+                ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, HOSTNAME3):
+                NEXUS_PORTCHANNELS,
+                ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, 'ssh_port'):
+                NEXUS_SSH_PORT,
+                ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME3):
+                NEXUS_PORTCHANNELS,
+                ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
             }
             self._client.credentials = {
                 NEXUS_IP_ADDRESS: {
                     'username': 'admin',
                     'password': 'pass1234'
                 },
+                NEXUS_PC_IP_ADDRESS: {
+                    'username': 'admin',
+                    'password': 'password'
+                },
             }
             db.configure_db()
 
@@ -118,18 +152,28 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
 
         self.addCleanup(self.patch_obj.stop)
 
-    def test_create_networks(self):
+    def test_create_delete_networks(self):
         """Tests creation of two new Virtual Networks."""
         new_net_dict = self._cisco_nexus_plugin.create_network(
             self.network1, self.attachment1)
         for attr in NET_ATTRS:
             self.assertEqual(new_net_dict[attr], self.network1[attr])
 
+        expected_instance_id = self._cisco_nexus_plugin.delete_port(
+            INSTANCE1, self.vlan_id)
+
+        self.assertEqual(expected_instance_id, INSTANCE1)
+
         new_net_dict = self._cisco_nexus_plugin.create_network(
             self.network2, self.attachment1)
         for attr in NET_ATTRS:
             self.assertEqual(new_net_dict[attr], self.network2[attr])
 
+        expected_instance_id = self._cisco_nexus_plugin.delete_port(
+            INSTANCE1, self.second_vlan_id)
+
+        self.assertEqual(expected_instance_id, INSTANCE1)
+
     def test_create_providernet(self):
         with mock.patch.object(cdb, 'is_provider_vlan',
                                return_value=True) as mock_db:
@@ -169,15 +213,22 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
             for attr in NET_ATTRS:
                 self.assertEqual(new_net_dict[attr], self.providernet[attr])
 
-    def test_nexus_delete_port(self):
-        """Test deletion of a vlan."""
-        self._cisco_nexus_plugin.create_network(
-            self.network1, self.attachment1)
-
-        expected_instance_id = self._cisco_nexus_plugin.delete_port(
-            INSTANCE1, self.vlan_id)
+    def test_create_delete_network_portchannel(self):
+        """Tests creation of a network over a portchannel."""
+        new_net_dict = self._cisco_nexus_plugin.create_network(
+            self.network3, self.attachment3)
+        self.assertEqual(new_net_dict[const.NET_ID],
+                         self.network3[const.NET_ID])
+        self.assertEqual(new_net_dict[const.NET_NAME],
+                         self.network3[const.NET_NAME])
+        self.assertEqual(new_net_dict[const.NET_VLAN_NAME],
+                         self.network3[const.NET_VLAN_NAME])
+        self.assertEqual(new_net_dict[const.NET_VLAN_ID],
+                         self.network3[const.NET_VLAN_ID])
 
-        self.assertEqual(expected_instance_id, INSTANCE1)
+        self._cisco_nexus_plugin.delete_port(
+            INSTANCE3, self.network3[const.NET_VLAN_ID]
+        )
 
     def test_nexus_add_remove_router_interface(self):
         """Tests addition of a router interface."""