From: Dane LeBlanc Date: Thu, 21 Nov 2013 18:13:02 +0000 (-0500) Subject: Improve unit test coverage for Cisco plugin nexus code X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=26e9fad61f86bedd16179ede9736b56c4a9a5752;p=openstack-build%2Fneutron-build.git Improve unit test coverage for Cisco plugin nexus code Closes-Bug: #1190622 This fix improves the unit test coverage for the Cisco Nexus plugin's cisco_nexus_network_driver_v2.py and cisco_nexus_plugin_v2.py source files from: cisco_nexus_network_driver_v2: 72% cisco_nexus_plugin_v2: 79% To: cisco_nexus_network_driver_v2: 99% cisco_nexus_plugin_v2: 94% Much of what the remaining "uncovered" code (coverage tool reports as partially covered) can be attributed to the coverage tool not being aware that execution does not continue at the end of a save_and_reraise_exception() context block (i.e. the exception will be reraised, but the coverage tool isn't aware of this). This fix and coverage results are dependent on the fix for #1246080. Change-Id: Ie0e11fc0a12502739fb39bb4b30deb04dd31b7b0 --- diff --git a/neutron/plugins/cisco/l2device_plugin_base.py b/neutron/plugins/cisco/l2device_plugin_base.py index cbac97585..1b64c89e3 100644 --- a/neutron/plugins/cisco/l2device_plugin_base.py +++ b/neutron/plugins/cisco/l2device_plugin_base.py @@ -30,15 +30,6 @@ class L2DevicePluginBase(object): __metaclass__ = ABCMeta - @abstractmethod - def get_all_networks(self, tenant_id, **kwargs): - """Get newtorks. - - :returns: - :raises: - """ - pass - @abstractmethod def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, **kwargs): @@ -67,15 +58,6 @@ class L2DevicePluginBase(object): """ pass - @abstractmethod - def get_all_ports(self, tenant_id, net_id, **kwargs): - """Get ports. - - :returns: - :raises: - """ - pass - @abstractmethod def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): """Create port. @@ -103,15 +85,6 @@ class L2DevicePluginBase(object): """ pass - @abstractmethod - def get_port_details(self, tenant_id, net_id, port_id, **kwargs): - """Get port details. - - :returns: - :raises: - """ - pass - @abstractmethod def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, **kwargs): diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py index dd09e92f9..bef145f03 100644 --- a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py +++ b/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py @@ -84,12 +84,6 @@ class CiscoNEXUSDriver(): } return self.credentials[nexus_ip] - def get_switch_and_port_id(self, host_name): - for switch_ip, attr in self.nexus_switches: - if str(attr) == host_name: - return switch_ip, self.nexus_switches[switch_ip, attr] - return None, None - def nxos_connect(self, nexus_host): """Make SSH connection to the Nexus Switch.""" if getattr(self.connections.get(nexus_host), 'connected', None): @@ -151,20 +145,6 @@ class CiscoNEXUSDriver(): confstr = self.create_xml_snippet(confstr) self._edit_config(nexus_host, target='running', config=confstr) - def enable_port_trunk(self, nexus_host, etype, interface): - """Enable trunk mode an interface on Nexus Switch.""" - 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, etype, interface): - """Disable trunk mode an interface on Nexus Switch.""" - 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, etype, interface): """Enable a VLAN on a trunk interface.""" # If one or more VLANs are already configured on this interface, diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py index 4d31644f5..927345a51 100644 --- a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py @@ -26,7 +26,6 @@ PlugIn for Nexus OS driver import logging -from neutron.common import exceptions as exc from neutron.openstack.common import excutils from neutron.openstack.common import importutils from neutron.plugins.cisco.common import cisco_constants as const @@ -50,15 +49,6 @@ class NexusPlugin(L2DevicePluginBase): 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. - - Returns a dictionary containing all for - the specified tenant. - """ - LOG.debug(_("NexusPlugin:get_all_networks() called")) - return self._networks.values() - def create_network(self, network, attachment): """Create or update a network when an attachment is changed. @@ -240,30 +230,26 @@ class NexusPlugin(L2DevicePluginBase): def delete_network(self, tenant_id, net_id, **kwargs): """Delete network. - Deletes the VLAN in all switches, and removes the VLAN configuration - from the relevant interfaces. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:delete_network() called")) + LOG.debug(_("NexusPlugin:delete_network() called")) # pragma no cover def update_network(self, tenant_id, net_id, **kwargs): - """Update the properties of a particular Virtual Network.""" - LOG.debug(_("NexusPlugin:update_network() called")) - - def get_all_ports(self, tenant_id, net_id, **kwargs): - """Get all ports. + """Update the properties of a particular Virtual Network. - This is probably not applicable to the Nexus plugin. - Delete if not required. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:get_all_ports() called")) + LOG.debug(_("NexusPlugin:update_network() called")) # pragma no cover def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): """Create port. - This is probably not applicable to the Nexus plugin. - Delete if not required. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:create_port() called")) + LOG.debug(_("NexusPlugin:create_port() called")) # pragma no cover def delete_port(self, device_id, vlan_id): """Delete port. @@ -337,40 +323,25 @@ class NexusPlugin(L2DevicePluginBase): def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): """Update port. - This is probably not applicable to the Nexus plugin. - Delete if not required. - """ - LOG.debug(_("NexusPlugin:update_port() called")) - - def get_port_details(self, tenant_id, net_id, port_id, **kwargs): - """Get port details. - - This is probably not applicable to the Nexus plugin. - Delete if not required. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:get_port_details() called")) + LOG.debug(_("NexusPlugin:update_port() called")) # pragma no cover def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, **kwargs): """Plug interfaces. - This is probably not applicable to the Nexus plugin. - Delete if not required. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:plug_interface() called")) + LOG.debug(_("NexusPlugin:plug_interface() called")) # pragma no cover def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): """Unplug interface. - This is probably not applicable to the Nexus plugin. - Delete if not required. + Not applicable to Nexus plugin. Defined here to satisfy abstract + method requirements. """ - LOG.debug(_("NexusPlugin:unplug_interface() called")) - - def _get_network(self, tenant_id, network_id, context, base_plugin_ref): - """Get the Network ID.""" - network = base_plugin_ref._get_network(context, network_id) - if not network: - raise exc.NetworkNotFound(net_id=network_id) - return {const.NET_ID: network_id, const.NET_NAME: network.name, - const.NET_PORTS: network.ports} + LOG.debug(_("NexusPlugin:unplug_interface() called") + ) # pragma no cover diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py b/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py index 831462154..90d265443 100644 --- a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py +++ b/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py @@ -37,7 +37,6 @@ EXEC_CONF_SNIPPET = """ """ - CMD_VLAN_CONF_SNIPPET = """ @@ -122,38 +121,6 @@ CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER + CMD_VLAN_ADD_ID + CMD_INT_VLAN_TRAILER) -CMD_PORT_TRUNK = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - - - - -""" - -CMD_NO_SWITCHPORT = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - -""" - - CMD_NO_VLAN_INT_SNIPPET = """ <%s> @@ -176,7 +143,6 @@ CMD_NO_VLAN_INT_SNIPPET = """ """ - FILTER_SHOW_VLAN_BRIEF_SNIPPET = """ @@ -185,7 +151,6 @@ FILTER_SHOW_VLAN_BRIEF_SNIPPET = """ """ - CMD_VLAN_SVI_SNIPPET = """ diff --git a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py b/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py index e56b4866e..b40cbef14 100644 --- a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py +++ b/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py @@ -45,10 +45,6 @@ class CiscoNEXUSFakeDriver(): """Delete a VLAN on Nexus Switch given the VLAN ID.""" pass - def enable_port_trunk(self, mgr, interface): - """Enable trunk mode an interface on Nexus Switch.""" - pass - def disable_switch_port(self, mgr, interface): """Disable trunk mode an interface on Nexus Switch.""" pass diff --git a/neutron/tests/unit/cisco/test_network_plugin.py b/neutron/tests/unit/cisco/test_network_plugin.py index c19f139bd..1a5566904 100644 --- a/neutron/tests/unit/cisco/test_network_plugin.py +++ b/neutron/tests/unit/cisco/test_network_plugin.py @@ -33,6 +33,7 @@ from neutron.manager import NeutronManager from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.common import config as cisco_config +from neutron.plugins.cisco.db import network_db_v2 from neutron.plugins.cisco.db import nexus_db_v2 from neutron.plugins.cisco.models import virt_phy_sw_v2 from neutron.plugins.openvswitch.common import config as ovs_config @@ -163,6 +164,29 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): config = {attr: None} self.mock_ncclient.configure_mock(**config) + @staticmethod + def _config_dependent_side_effect(match_config, exc): + """Generates a config-dependent side effect for ncclient edit_config. + + This method generates a mock side-effect function which can be + configured on the mock ncclient module for the edit_config method. + This side effect will cause a given exception to be raised whenever + the XML config string that is passed to edit_config contains all + words in a given match config string. + + :param match_config: String containing keywords to be matched + :param exc: Exception to be raised when match is found + :return: Side effect function for the mock ncclient module's + edit_config method. + + """ + keywords = match_config.split() + + def _side_effect_function(target, config): + if all(word in config for word in keywords): + raise exc + return _side_effect_function + def _is_in_nexus_cfg(self, words): """Check if any config sent to Nexus contains all words in a list.""" for call in (self.mock_ncclient.manager.connect.return_value. @@ -186,11 +210,13 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): vlan_created == vlan_creation_expected and add_appears == add_keyword_expected) - def _is_vlan_unconfigured(self, vlan_deletion_expected=True): - vlan_deleted = self._is_in_last_nexus_cfg( + def _is_vlan_unconfigured(self, vlan_deletion_expected=True, + vlan_untrunk_expected=True): + vlan_deleted = self._is_in_nexus_cfg( ['no', 'vlan', 'vlan-id-create-delete']) - return (self._is_in_nexus_cfg(['allowed', 'vlan', 'remove']) and - vlan_deleted == vlan_deletion_expected) + vlan_untrunked = self._is_in_nexus_cfg(['allowed', 'vlan', 'remove']) + return (vlan_deleted == vlan_deletion_expected and + vlan_untrunked == vlan_untrunk_expected) class TestCiscoBasicGet(CiscoNetworkPluginV2TestCase, @@ -419,25 +445,17 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, are ignored by the Nexus plugin. """ - def mock_edit_config_a(target, config): - if all(word in config for word in ['state', 'active']): - raise Exception("Can't modify state for extended") - - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - mock_edit_config_a): - with self._create_port_res() as res: - self.assertEqual(res.status_int, wexc.HTTPCreated.code) - - def mock_edit_config_b(target, config): - if all(word in config for word in ['no', 'shutdown']): - raise Exception("Command is only allowed on VLAN") - - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - mock_edit_config_b): - with self._create_port_res() as res: - self.assertEqual(res.status_int, wexc.HTTPCreated.code) + config_err_strings = { + "state active": "Can't modify state for extended", + "no shutdown": "Command is only allowed on VLAN", + } + for config, err_string in config_err_strings.items(): + with self._patch_ncclient( + 'manager.connect.return_value.edit_config.side_effect', + self._config_dependent_side_effect(config, + Exception(err_string))): + with self._create_port_res() as res: + self.assertEqual(res.status_int, wexc.HTTPCreated.code) def test_nexus_vlan_config_rollback(self): """Test rollback following Nexus VLAN state config failure. @@ -448,20 +466,19 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, for the extended VLAN range). """ - def mock_edit_config(target, config): - if all(word in config for word in ['state', 'active']): - raise ValueError - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - mock_edit_config): - with self._create_port_res(do_delete=False) as res: - # Confirm that the last configuration sent to the Nexus - # switch was deletion of the VLAN. - self.assertTrue( - self._is_in_last_nexus_cfg(['', '']) - ) - self._assertExpectedHTTP(res.status_int, - c_exc.NexusConfigFailed) + vlan_state_configs = ['state active', 'no shutdown'] + for config in vlan_state_configs: + with self._patch_ncclient( + 'manager.connect.return_value.edit_config.side_effect', + self._config_dependent_side_effect(config, ValueError)): + with self._create_port_res(do_delete=False) as res: + # Confirm that the last configuration sent to the Nexus + # switch was deletion of the VLAN. + self.assertTrue( + self._is_in_last_nexus_cfg(['', '']) + ) + self._assertExpectedHTTP(res.status_int, + c_exc.NexusConfigFailed) def test_get_seg_id_fail(self): """Test handling of a NetworkSegmentIDNotFound exception. @@ -501,7 +518,9 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, self._assertExpectedHTTP(res.status_int, c_exc.NexusComputeHostNotConfigured) - def test_nexus_bind_fail_rollback(self): + def _check_rollback_on_bind_failure(self, + vlan_deletion_expected, + vlan_untrunk_expected): """Test for proper rollback following add Nexus DB binding failure. Test that the Cisco Nexus plugin correctly rolls back the vlan @@ -509,15 +528,47 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, within the plugin's create_port() method. """ + inserted_exc = KeyError with mock.patch.object(nexus_db_v2, 'add_nexusport_binding', - side_effect=KeyError): + side_effect=inserted_exc): with self._create_port_res(do_delete=False) as res: - # Confirm that the last configuration sent to the Nexus - # switch was a removal of vlan from the test interface. - self.assertTrue( - self._is_in_last_nexus_cfg(['', '']) - ) - self._assertExpectedHTTP(res.status_int, KeyError) + # Confirm that the configuration sent to the Nexus + # switch includes deletion of the vlan (if expected) + # and untrunking of the vlan from the ethernet interface + # (if expected). + self.assertTrue(self._is_vlan_unconfigured( + vlan_deletion_expected=vlan_deletion_expected, + vlan_untrunk_expected=vlan_untrunk_expected)) + self._assertExpectedHTTP(res.status_int, inserted_exc) + + def test_nexus_rollback_on_bind_failure_non_provider_vlan(self): + """Test rollback upon DB binding failure for non-provider vlan.""" + self._check_rollback_on_bind_failure(vlan_deletion_expected=True, + vlan_untrunk_expected=True) + + def test_nexus_rollback_on_bind_failure_prov_vlan_no_auto_create(self): + """Test rollback on bind fail for prov vlan w auto-create disabled.""" + with mock.patch.object(network_db_v2, 'is_provider_vlan', + return_value=True): + # Disable auto-create. This config change will be cleared based + # on cleanup scheduled in the CiscoNetworkPluginV2TestCase + # class' setUp() method. + cisco_config.CONF.set_override('provider_vlan_auto_create', + False, 'CISCO') + self._check_rollback_on_bind_failure(vlan_deletion_expected=False, + vlan_untrunk_expected=True) + + def test_nexus_rollback_on_bind_failure_prov_vlan_no_auto_trunk(self): + """Test rollback on bind fail for prov vlan w auto-trunk disabled.""" + with mock.patch.object(network_db_v2, 'is_provider_vlan', + return_value=True): + # Disable auto-trunk. This config change will be cleared + # based on post-test cleanup scheduled in the + # CiscoNetworkPluginV2TestCase class' setUp() method. + cisco_config.CONF.set_override('provider_vlan_auto_trunk', + False, 'CISCO') + self._check_rollback_on_bind_failure(vlan_deletion_expected=True, + vlan_untrunk_expected=False) def test_model_update_port_rollback(self): """Test for proper rollback for Cisco model layer update port failure. diff --git a/neutron/tests/unit/cisco/test_nexus_plugin.py b/neutron/tests/unit/cisco/test_nexus_plugin.py index 74309d92d..6a959d609 100644 --- a/neutron/tests/unit/cisco/test_nexus_plugin.py +++ b/neutron/tests/unit/cisco/test_nexus_plugin.py @@ -99,6 +99,10 @@ class TestCiscoNexusPlugin(base.BaseTestCase): const.NET_VLAN_NAME: 'q-268', const.NET_VLAN_ID: '268', } + self.delete_port_args_1 = [ + self.attachment1[const.INSTANCE_ID], + self.network1[const.NET_VLAN_ID], + ] self.providernet = { const.NET_ID: 9, const.NET_NAME: 'pnet1', @@ -181,44 +185,38 @@ class TestCiscoNexusPlugin(base.BaseTestCase): self.assertEqual(expected_instance_id, INSTANCE1) - def test_create_providernet(self): + def _create_delete_providernet(self, auto_create, auto_trunk): + cfg.CONF.set_override( + 'provider_vlan_auto_create', auto_create, 'CISCO') + cfg.CONF.set_override( + 'provider_vlan_auto_trunk', auto_trunk, 'CISCO') + self.addCleanup(cfg.CONF.reset) with mock.patch.object(cdb, 'is_provider_vlan', return_value=True) as mock_db: + # Create a provider network new_net_dict = self._cisco_nexus_plugin.create_network( self.providernet, self.attachment1) mock_db.assert_called_once() for attr in NET_ATTRS: self.assertEqual(new_net_dict[attr], self.providernet[attr]) + # Delete the provider network + instance_id = self._cisco_nexus_plugin.delete_port( + self.attachment1[const.INSTANCE_ID], + self.providernet[const.NET_VLAN_ID]) + self.assertEqual(instance_id, + self.attachment1[const.INSTANCE_ID]) - def test_create_provider_vlan_network_cfg_auto_man(self): - cfg.CONF.set_override('provider_vlan_auto_create', True, 'CISCO') - cfg.CONF.set_override('provider_vlan_auto_trunk', False, 'CISCO') - self.addCleanup(cfg.CONF.reset) - with mock.patch.object(cdb, 'is_provider_vlan', return_value=True): - new_net_dict = self._cisco_nexus_plugin.create_network( - self.providernet, self.attachment1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.providernet[attr]) + def test_create_delete_providernet(self): + self._create_delete_providernet(auto_create=True, auto_trunk=True) - def test_create_provider_vlan_network_cfg_man_auto(self): - cfg.CONF.set_override('provider_vlan_auto_create', False, 'CISCO') - cfg.CONF.set_override('provider_vlan_auto_trunk', True, 'CISCO') - self.addCleanup(cfg.CONF.reset) - with mock.patch.object(cdb, 'is_provider_vlan', return_value=True): - new_net_dict = self._cisco_nexus_plugin.create_network( - self.providernet, self.attachment1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.providernet[attr]) + def test_create_delete_provider_vlan_network_cfg_auto_man(self): + self._create_delete_providernet(auto_create=True, auto_trunk=False) - def test_create_provider_vlan_network_cfg_man_man(self): - cfg.CONF.set_override('provider_vlan_auto_create', False, 'CISCO') - cfg.CONF.set_override('provider_vlan_auto_trunk', False, 'CISCO') - self.addCleanup(cfg.CONF.reset) - with mock.patch.object(cdb, 'is_provider_vlan', return_value=True): - new_net_dict = self._cisco_nexus_plugin.create_network( - self.providernet, self.attachment1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.providernet[attr]) + def test_create_delete_provider_vlan_network_cfg_man_auto(self): + self._create_delete_providernet(auto_create=False, auto_trunk=True) + + def test_create_delete_provider_vlan_network_cfg_man_man(self): + self._create_delete_providernet(auto_create=False, auto_trunk=False) def test_create_delete_network_portchannel(self): """Tests creation of a network over a portchannel.""" @@ -237,45 +235,48 @@ class TestCiscoNexusPlugin(base.BaseTestCase): INSTANCE3, self.network3[const.NET_VLAN_ID] ) - def test_nexus_add_remove_router_interface(self): - """Tests addition of a router interface.""" + def _add_router_interface(self): + """Add a router interface using fixed (canned) parameters.""" vlan_name = self.vlan_name vlan_id = self.vlan_id gateway_ip = '10.0.0.1/24' router_id = '00000R1' subnet_id = '00001' + return self._cisco_nexus_plugin.add_router_interface( + vlan_name, vlan_id, subnet_id, gateway_ip, router_id) - result = self._cisco_nexus_plugin.add_router_interface(vlan_name, - vlan_id, - subnet_id, - gateway_ip, - router_id) - self.assertTrue(result) - result = self._cisco_nexus_plugin.remove_router_interface(vlan_id, - router_id) - self.assertEqual(result, router_id) - - def test_nexus_add_router_interface_fail(self): - """Tests deletion of a router interface.""" - vlan_name = self.vlan_name + def _remove_router_interface(self): + """Remove a router interface created with _add_router_interface.""" vlan_id = self.vlan_id - gateway_ip = '10.0.0.1/24' router_id = '00000R1' - subnet_id = '00001' + return self._cisco_nexus_plugin.remove_router_interface(vlan_id, + router_id) - self._cisco_nexus_plugin.add_router_interface(vlan_name, - vlan_id, - subnet_id, - gateway_ip, - router_id) + def test_nexus_add_remove_router_interface(self): + """Tests addition of a router interface.""" + self.assertTrue(self._add_router_interface()) + self.assertEqual(self._remove_router_interface(), '00000R1') + + def test_nexus_dup_add_router_interface(self): + """Tests a duplicate add of a router interface.""" + self._add_router_interface() try: self.assertRaises( cisco_exc.SubnetInterfacePresent, - self._cisco_nexus_plugin.add_router_interface, - vlan_name, vlan_id, subnet_id, gateway_ip, router_id) + self._add_router_interface) finally: - self._cisco_nexus_plugin.remove_router_interface(vlan_id, - router_id) + self._remove_router_interface() + + def test_nexus_no_svi_switch_exception(self): + """Tests failure to find a Nexus switch for SVI placement.""" + # Clear the Nexus switches dictionary. + with mock.patch.dict(self._cisco_nexus_plugin._client.nexus_switches, + {}, clear=True): + # Clear the first Nexus IP address discovered in config + with mock.patch.object(cisco_config, 'first_device_ip', + new=None): + self.assertRaises(cisco_exc.NoNexusSviSwitch, + self._add_router_interface) def test_nexus_add_port_after_router_interface(self): """Tests creating a port after a router interface. @@ -284,23 +285,13 @@ class TestCiscoNexusPlugin(base.BaseTestCase): 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) + self._add_router_interface() # 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(). + last_cfgs = (self.mock_ncclient.manager.connect.return_value. edit_config.mock_calls) # The last ncclient call should be for trunking and the second