From 6541304d5e4103237135f7783c82458199d61bcb Mon Sep 17 00:00:00 2001 From: hujin Date: Thu, 11 Oct 2018 13:28:11 +0800 Subject: [PATCH] filter "updated_at" and "revision_number" in _gateway_ports_equal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the HA attribute of the router changes, the code determines whether the gateway in memory is consistent with the gateway in the database to decide whether it needs to be reconfigured. But there are problems with the judging conditions. After the HA attribute of the router changes, the relevant parameters of gateway port will be updated by ML2 agent, including "binding:host_id"、"updated_at" and "revison_number". Method "_gateway_ports_equal" removes only the "binding:host_id" property of the port, resulting in unequal results for each decision Change-Id: I19e024ff360611d191da2bd3bff1b86abe1a8ea1 Closes-Bug: 1797298 --- neutron/agent/l3/ha_router.py | 5 +++- neutron/tests/unit/agent/l3/test_ha_router.py | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index 651784dec6c..a7a964206b1 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -28,6 +28,8 @@ from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import keepalived from neutron.common import utils as common_utils +from neutron.extensions import revisions +from neutron.extensions import timestamp LOG = logging.getLogger(__name__) HA_DEV_PREFIX = 'ha-' @@ -408,7 +410,8 @@ class HaRouter(router.RouterInfo): def _get_filtered_dict(d, ignore): return {k: v for k, v in d.items() if k not in ignore} - keys_to_ignore = set([portbindings.HOST_ID]) + keys_to_ignore = set([portbindings.HOST_ID, timestamp.UPDATED, + revisions.REVISION]) port1_filtered = _get_filtered_dict(port1, keys_to_ignore) port2_filtered = _get_filtered_dict(port2, keys_to_ignore) return port1_filtered == port2_filtered diff --git a/neutron/tests/unit/agent/l3/test_ha_router.py b/neutron/tests/unit/agent/l3/test_ha_router.py index 33ec7bc902c..accbe4cc9d6 100644 --- a/neutron/tests/unit/agent/l3/test_ha_router.py +++ b/neutron/tests/unit/agent/l3/test_ha_router.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import signal import mock @@ -20,12 +21,18 @@ from oslo_utils import uuidutils from neutron.agent.l3 import ha_router from neutron.agent.l3 import router_info from neutron.tests import base +from neutron.tests.common import l3_test_common from neutron.tests import tools _uuid = uuidutils.generate_uuid class TestBasicRouterOperations(base.BaseTestCase): + def setUp(self): + super(TestBasicRouterOperations, self).setUp() + self.device_exists_p = mock.patch( + 'neutron.agent.linux.ip_lib.device_exists') + self.device_exists = self.device_exists_p.start() def _create_router(self, router=None, **kwargs): if not router: @@ -141,3 +148,25 @@ class TestBasicRouterOperations(base.BaseTestCase): 'ha_state') self.mock_open = IOError self.assertEqual('unknown', ri.ha_state) + + def test_gateway_ports_equal(self): + ri = self._create_router(mock.MagicMock()) + ri.driver = mock.MagicMock() + subnet_id, qos_policy_id = _uuid(), _uuid() + _, old_gw_port = l3_test_common.prepare_ext_gw_test( + self, ri, True) + old_gw_port['qos_policy_id'] = qos_policy_id + new_gw_port = copy.deepcopy(old_gw_port) + new_gw_port.update({'binding:host_id': 'node02', + 'updated_at': '2018-11-02T14:07:00', + 'revision_number': 101, + 'qos_policy_id': qos_policy_id}) + self.assertTrue(ri._gateway_ports_equal(old_gw_port, new_gw_port)) + + fixed_ip = {'ip_address': '10.10.10.3', 'subnet_id': subnet_id} + new_gw_port['fixed_ips'].append(fixed_ip) + self.assertFalse(ri._gateway_ports_equal(old_gw_port, new_gw_port)) + + new_gw_port['fixed_ips'].remove(fixed_ip) + new_gw_port['qos_policy_id'] = _uuid() + self.assertFalse(ri._gateway_ports_equal(old_gw_port, new_gw_port))