diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py index 548ba260bdd..4b9306240c8 100644 --- a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py @@ -103,6 +103,17 @@ class NexusPlugin(L2DevicePluginBase): vlan_created = False vlan_trunked = False eport_id = '%s:%s' % (etype, port_id) + # Check for switch vlan bindings + try: + # This vlan has already been created on this switch + # via another operation, like SVI bindings. + nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) + vlan_created = True + auto_create = False + except cisco_exc.NexusPortBindingNotFound: + # No changes, proceed as normal + pass + try: nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id, switch_ip) @@ -282,6 +293,7 @@ class NexusPlugin(L2DevicePluginBase): etype, nexus_port = '', '' if row['port_id'] == 'router': etype, nexus_port = 'vlan', row['port_id'] + auto_untrunk = False else: etype, nexus_port = row['port_id'].split(':') diff --git a/neutron/tests/unit/cisco/test_nexus_plugin.py b/neutron/tests/unit/cisco/test_nexus_plugin.py index a8507797d8b..84e819f770b 100644 --- a/neutron/tests/unit/cisco/test_nexus_plugin.py +++ b/neutron/tests/unit/cisco/test_nexus_plugin.py @@ -141,9 +141,9 @@ class TestCiscoNexusPlugin(base.BaseTestCase): db.configure_db() # Use a mock netconf client - mock_ncclient = mock.Mock() + self.mock_ncclient = mock.Mock() self.patch_obj = mock.patch.dict('sys.modules', - {'ncclient': mock_ncclient}) + {'ncclient': self.mock_ncclient}) self.patch_obj.start() with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, @@ -261,8 +261,45 @@ class TestCiscoNexusPlugin(base.BaseTestCase): subnet_id, gateway_ip, router_id) + try: + self.assertRaises( + cisco_exc.SubnetInterfacePresent, + self._cisco_nexus_plugin.add_router_interface, + vlan_name, vlan_id, subnet_id, gateway_ip, router_id) + finally: + self._cisco_nexus_plugin.remove_router_interface(vlan_id, + router_id) - self.assertRaises( - cisco_exc.SubnetInterfacePresent, - self._cisco_nexus_plugin.add_router_interface, - vlan_name, vlan_id, subnet_id, gateway_ip, router_id) + def test_nexus_add_port_after_router_interface(self): + """Tests creating a port after a router interface. + + Test creating a port after an SVI router interface has + been created. Only a trunk call should be invoked and the + plugin should not attempt to recreate the vlan. + """ + vlan_name = self.vlan_name + vlan_id = self.vlan_id + gateway_ip = '10.0.0.1/24' + router_id = '00000R1' + subnet_id = '00001' + + self._cisco_nexus_plugin.add_router_interface(vlan_name, + vlan_id, + subnet_id, + gateway_ip, + router_id) + # Create a network on the switch + self._cisco_nexus_plugin.create_network( + self.network1, self.attachment1) + + # Grab a list of all mock calls from ncclient + last_cfgs = (self.mock_ncclient.manager.connect(). + edit_config.mock_calls) + + # The last ncclient call should be for trunking and the second + # to last call should be creating the SVI interface + last_cfg = last_cfgs[-1][2]['config'] + self.assertIn('allowed', last_cfg) + + slast_cfg = last_cfgs[-2][2]['config'] + self.assertIn('10.0.0.1/24', slast_cfg)