From 5109d16bc52abb20636c94c0208fb498d1c626eb Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Sat, 14 May 2016 23:44:23 -0700 Subject: [PATCH] Make deepcopy of update body in API layer This adjusts the update handler in the API layer to pass a deepcopy of the original update body to ensure that the original is used on any generated retry requests. Without this update handlers that modified the body as they processed it (e.g. the external gateway update for routers) would be missing data on a retry request. This adds a test case for the bug that was caused by this. Closes-Bug: #1584920 Change-Id: I88fb9d45eb82ef48e0d865decdcc7fad1c5fb361 --- neutron/api/v2/base.py | 3 ++- .../tests/unit/extensions/test_l3_ext_gw_mode.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/neutron/api/v2/base.py b/neutron/api/v2/base.py index 30c4c1c56db..84f9cb05ed7 100644 --- a/neutron/api/v2/base.py +++ b/neutron/api/v2/base.py @@ -593,7 +593,8 @@ class Controller(object): @db_api.retry_db_errors def _update(self, request, id, body, **kwargs): - body = Controller.prepare_request_body(request.context, body, False, + body = Controller.prepare_request_body(request.context, + copy.deepcopy(body), False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] diff --git a/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py b/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py index 3413667aa46..a6423c4da90 100644 --- a/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py +++ b/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py @@ -17,6 +17,7 @@ import mock from neutron_lib import constants from oslo_config import cfg +from oslo_db import exception as db_exc from oslo_serialization import jsonutils from oslo_utils import uuidutils import testscenarios @@ -389,6 +390,19 @@ class ExtGwModeIntTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, expected_code=expected_code, neutron_context=neutron_context) + def test_router_gateway_set_retry(self): + with self.router() as r, self.subnet() as s: + ext_net_id = s['subnet']['network_id'] + self._set_net_external(ext_net_id) + with mock.patch.object( + l3_db.L3_NAT_dbonly_mixin, '_validate_gw_info', + side_effect=[db_exc.RetryRequest(None), ext_net_id]): + self._set_router_external_gateway(r['router']['id'], + ext_net_id) + res = self._show('routers', r['router']['id'])['router'] + self.assertEqual(ext_net_id, + res['external_gateway_info']['network_id']) + def test_router_create_with_gwinfo_invalid_ext_ip(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id'])