From: Dane LeBlanc Date: Fri, 17 May 2013 15:42:59 +0000 (-0400) Subject: Add rollback for Cisco plugin update_port failure X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=87b2548e8845fa9710a1b89a642d06fd28148572;p=openstack-build%2Fneutron-build.git Add rollback for Cisco plugin update_port failure Fixes bug 1174433 This fix adds a rollback of the OVS plugin update_port operation following a failure to update the port in the Cisco Nexus sub-plugin. Also, this fix removes an unneccessary call to the Nexus sub-plugin's update_network method within the Cisco plugin model layer's update_network method (as well as the associated argument setup). The Nexus sub-plugin's update_network is a no-op, so there's no need to call it. Change-Id: I10cba0d7f1d632c7f77eb6c4a94ea82b958bc6a2 --- diff --git a/quantum/plugins/cisco/models/virt_phy_sw_v2.py b/quantum/plugins/cisco/models/virt_phy_sw_v2.py index 835d72961..fe0bd9178 100644 --- a/quantum/plugins/cisco/models/virt_phy_sw_v2.py +++ b/quantum/plugins/cisco/models/virt_phy_sw_v2.py @@ -21,6 +21,7 @@ import inspect import logging +import sys from novaclient.v1_1 import client as nova_client from oslo.config import cfg @@ -213,31 +214,20 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): Perform this operation in the context of the configured device plugins. + + Note that the Nexus sub-plugin does not need to be notified + (and the Nexus switch does not need to be [re]configured) + for an update network operation because the Nexus sub-plugin + is agnostic of all network-level attributes except the + segmentation ID. Furthermore, updating of the segmentation ID + is not supported by the OVS plugin since it is considered a + provider attribute, so it is not supported by this method. """ LOG.debug(_("update_network() called")) args = [context, id, network] ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, self._func_name(), args) - try: - vlan_id = self._get_segmentation_id(ovs_output[0]['id']) - if not self._validate_vlan_id(vlan_id): - return ovs_output[0] - vlan_ids = self._get_all_segmentation_ids() - args = [ovs_output[0]['tenant_id'], id, {'vlan_id': vlan_id}, - {'net_admin_state': ovs_output[0]['admin_state_up']}, - {'vlan_ids': vlan_ids}] - self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - args) - except Exception: - # TODO(dane): The call to the nexus plugin update network - # failed, so the OVS plugin should be rolled back, that is, - # "re-updated" back to the original network config. - LOG.exception(_("Unable to update network '%s' on Nexus switch"), - network['network']['name']) - raise - return ovs_output[0] def delete_network(self, context, id): @@ -304,9 +294,10 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): self._invoke_nexus_for_net_create( context, tenant_id, net_id, instance_id) - except Exception as e: + except Exception: # Create network on the Nexus plugin has failed, so we need # to rollback the port creation on the VSwitch plugin. + exc_info = sys.exc_info() try: id = ovs_output[0]['id'] args = [context, id] @@ -316,7 +307,7 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): args) finally: # Re-raise the original exception - raise e + raise exc_info[0], exc_info[1], exc_info[2] return ovs_output[0] def get_port(self, context, id, fields=None): @@ -354,12 +345,19 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): return ovs_output[0] except Exception: - # TODO(dane): The call to the nexus plugin create network - # failed, so the OVS plugin should be rolled back, that is, - # "re-updated" back to the original port config. - LOG.exception(_("Unable to update port '%s' on Nexus switch"), - port['port']['name']) - raise + exc_info = sys.exc_info() + LOG.error(_("Unable to update port '%s' on Nexus switch"), + old_port['name'], exc_info=exc_info) + try: + # Roll back vSwitch plugin to original port attributes. + args = [context, id, {'port': old_port}] + ovs_output = self._invoke_plugin_per_device( + const.VSWITCH_PLUGIN, + self._func_name(), + args) + finally: + # Re-raise the original exception + raise exc_info[0], exc_info[1], exc_info[2] def delete_port(self, context, id): """Delete port. @@ -379,7 +377,8 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, self._func_name(), args) - except Exception as e: + except Exception: + exc_info = sys.exc_info() # Roll back the delete port on the Nexus plugin try: tenant_id = port['tenant_id'] @@ -389,7 +388,7 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): net_id, instance_id) finally: # Raise the original exception. - raise e + raise exc_info[0], exc_info[1], exc_info[2] return ovs_output[0] diff --git a/quantum/tests/unit/cisco/test_network_plugin.py b/quantum/tests/unit/cisco/test_network_plugin.py index d1b8de8f8..bca3e83cc 100644 --- a/quantum/tests/unit/cisco/test_network_plugin.py +++ b/quantum/tests/unit/cisco/test_network_plugin.py @@ -23,6 +23,7 @@ import webob.exc as wexc from quantum.api.v2 import base from quantum.common import exceptions as q_exc from quantum import context +from quantum.db import db_base_plugin_v2 as base_plugin from quantum.db import l3_db from quantum.manager import QuantumManager from quantum.plugins.cisco.common import cisco_constants as const @@ -407,6 +408,41 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, ) self._assertExpectedHTTP(res.status_int, KeyError) + def test_model_update_port_rollback(self): + """Test for proper rollback for Cisco model layer update port failure. + + Test that the vSwitch plugin port configuration is rolled back + (restored) by the Cisco plugin model layer when there is a + failure in the Nexus sub-plugin for an update port operation. + + """ + with self.port(fmt=self.fmt) as orig_port: + + inserted_exc = ValueError + with mock.patch.object( + virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, + '_invoke_nexus_for_net_create', + side_effect=inserted_exc): + + # Send an update port request with a new device ID + device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" + if orig_port['port']['device_id'] == device_id: + device_id = "600df00d-e4a8-4a3a-8906-feed600df00d" + data = {'port': {'device_id': device_id}} + port_id = orig_port['port']['id'] + req = self.new_update_request('ports', data, port_id) + res = req.get_response(self.api) + + # Sanity check failure result code + self._assertExpectedHTTP(res.status_int, inserted_exc) + + # Check that the port still has the original device ID + plugin = base_plugin.QuantumDbPluginV2() + ctx = context.get_admin_context() + db_port = plugin._get_port(ctx, port_id) + self.assertEqual(db_port['device_id'], + orig_port['port']['device_id']) + def test_model_delete_port_rollback(self): """Test for proper rollback for OVS plugin delete port failure.