From: Peter Feiner Date: Tue, 18 Jun 2013 20:48:30 +0000 (+0000) Subject: faster quantum-openvswitch-agent periodic reports X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=364e97b44158976dca923bcf9ff5dff926da5dc4;p=openstack-build%2Fneutron-build.git faster quantum-openvswitch-agent periodic reports One of two patches that fixes bug #1194438. Instead of spawning ovs-vsctl for every port on br-int, we just dump the required columns from the Interfaces table and grab the rows that we need. This is a big win because the time it takes for ovs-vsctl to connect to the openvswitch controller is orders of magnitude greater than the time it takes to parse the rows. In practice, instead of taking roughly 1s per interface, the agent's periodic task only takes 1s in total. Change-Id: Idbf32c38e0c4a9c9634c1e4f0e79bd556b720493 --- diff --git a/quantum/agent/linux/ovs_lib.py b/quantum/agent/linux/ovs_lib.py index b13bbc9e9..66441e192 100644 --- a/quantum/agent/linux/ovs_lib.py +++ b/quantum/agent/linux/ovs_lib.py @@ -22,6 +22,7 @@ import re from quantum.agent.linux import ip_lib from quantum.agent.linux import utils +from quantum.openstack.common import jsonutils from quantum.openstack.common import log as logging from quantum.plugins.openvswitch.common import constants @@ -252,10 +253,18 @@ class OVSBridge: return edge_ports def get_vif_port_set(self): - edge_ports = set() port_names = self.get_port_name_list() - for name in port_names: - external_ids = self.db_get_map("Interface", name, "external_ids") + edge_ports = set() + args = ['--format=json', '--', '--columns=name,external_ids', + 'list', 'Interface'] + result = self.run_vsctl(args) + if not result: + return edge_ports + for row in jsonutils.loads(result)['data']: + name = row[0] + if name not in port_names: + continue + external_ids = dict(row[1][1]) if "iface-id" in external_ids and "attached-mac" in external_ids: edge_ports.add(external_ids['iface-id']) elif ("xs-vif-uuid" in external_ids and diff --git a/quantum/tests/unit/openvswitch/test_ovs_lib.py b/quantum/tests/unit/openvswitch/test_ovs_lib.py index 1995c8987..7deb0a25d 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_lib.py +++ b/quantum/tests/unit/openvswitch/test_ovs_lib.py @@ -19,7 +19,9 @@ import mock import mox import testtools -from quantum.agent.linux import ovs_lib, utils +from quantum.agent.linux import ovs_lib +from quantum.agent.linux import utils +from quantum.openstack.common import jsonutils from quantum.openstack.common import uuidutils from quantum.tests import base @@ -264,12 +266,92 @@ class OVS_Lib_Test(base.BaseTestCase): self.assertEqual(ports[0].switch.br_name, self.BR_NAME) self.mox.VerifyAll() + def _encode_ovs_json(self, headings, data): + # See man ovs-vsctl(8) for the encoding details. + r = {"data": [], + "headings": headings} + for row in data: + ovs_row = [] + r["data"].append(ovs_row) + for cell in row: + if isinstance(cell, str): + ovs_row.append(cell) + elif isinstance(cell, dict): + ovs_row.append(["map", cell.items()]) + else: + raise TypeError('%r not str or dict' % type(cell)) + return jsonutils.dumps(r) + + def _test_get_vif_port_set(self, is_xen): + utils.execute(["ovs-vsctl", self.TO, "list-ports", self.BR_NAME], + root_helper=self.root_helper).AndReturn('tap99\ntun22') + + if is_xen: + id_key = 'xs-vif-uuid' + else: + id_key = 'iface-id' + + headings = ['name', 'external_ids'] + data = [ + # A vif port on this bridge: + ['tap99', {id_key: 'tap99id', 'attached-mac': 'tap99mac'}], + # A vif port on another bridge: + ['tap88', {id_key: 'tap88id', 'attached-mac': 'tap88id'}], + # Non-vif port on this bridge: + ['tun22', {}], + ] + + utils.execute(["ovs-vsctl", self.TO, "--format=json", + "--", "--columns=name,external_ids", + "list", "Interface"], + root_helper=self.root_helper).AndReturn( + self._encode_ovs_json(headings, data)) + + if is_xen: + self.mox.StubOutWithMock(self.br, 'get_xapi_iface_id') + self.br.get_xapi_iface_id('tap99id').AndReturn('tap99id') + + self.mox.ReplayAll() + + port_set = self.br.get_vif_port_set() + self.assertEqual(set(['tap99id']), port_set) + self.mox.VerifyAll() + def test_get_vif_ports_nonxen(self): self._test_get_vif_ports(False) def test_get_vif_ports_xen(self): self._test_get_vif_ports(True) + def test_get_vif_port_set_nonxen(self): + self._test_get_vif_port_set(False) + + def test_get_vif_port_set_xen(self): + self._test_get_vif_port_set(True) + + def test_get_vif_port_set_list_ports_error(self): + utils.execute(["ovs-vsctl", self.TO, "list-ports", self.BR_NAME], + root_helper=self.root_helper).AndRaise(RuntimeError()) + utils.execute(["ovs-vsctl", self.TO, "--format=json", + "--", "--columns=name,external_ids", + "list", "Interface"], + root_helper=self.root_helper).AndReturn( + self._encode_ovs_json(['name', 'external_ids'], [])) + self.mox.ReplayAll() + self.assertEqual(set(), self.br.get_vif_port_set()) + self.mox.VerifyAll() + + def test_get_vif_port_set_list_interface_error(self): + utils.execute(["ovs-vsctl", self.TO, "list-ports", self.BR_NAME], + root_helper=self.root_helper).AndRaise('tap99\n') + utils.execute(["ovs-vsctl", self.TO, "--format=json", + "--", "--columns=name,external_ids", + "list", "Interface"], + root_helper=self.root_helper).AndRaise(RuntimeError()) + self.mox.ReplayAll() + self.assertEqual(set(), self.br.get_vif_port_set()) + self.mox.VerifyAll() + def test_clear_db_attribute(self): pname = "tap77" utils.execute(["ovs-vsctl", self.TO, "clear", "Port",