Use ovs_port_updated event to update ofport and mac
Currently, df apps use ovs idl to query the mac of ovs Interface.
ovs_port_updated event was introduced in [1]. And there is already an
ovs Interface object in the event. So, df apps can just get mac address
from that event. This can save a query from ovs db, and also stop abuse
of ovs idl.
[1] 19232467d6
Change-Id: I31d86ce224d43d61cb8861907608248647f10015
Related-Bug: #1622879
This commit is contained in:
parent
585c38f22b
commit
6c328c7614
|
@ -16,10 +16,8 @@
|
|||
import collections
|
||||
|
||||
import netaddr
|
||||
from neutron.agent.ovsdb.native import idlutils
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_config import cfg
|
||||
from oslo_service import loopingcall
|
||||
from ryu.ofproto import ether
|
||||
import six
|
||||
|
||||
|
@ -39,8 +37,6 @@ DF_DNAT_APP_OPTS = [
|
|||
cfg.StrOpt('ex_peer_patch_port', default='patch-int',
|
||||
help=_("Peer patch port in external bridge for integration "
|
||||
"bridge.")),
|
||||
cfg.IntOpt('send_arp_interval', default=5,
|
||||
help=_("Polling interval for arp request in seconds"))
|
||||
]
|
||||
|
||||
FIP_GW_RESOLVING_STATUS = 'resolving'
|
||||
|
@ -55,50 +51,31 @@ class DNATApp(df_base_app.DFlowApp):
|
|||
cfg.CONF.register_opts(DF_DNAT_APP_OPTS, group='df_dnat_app')
|
||||
self.external_network_bridge = \
|
||||
cfg.CONF.df_dnat_app.external_network_bridge
|
||||
self.external_bridge_mac = ""
|
||||
self.integration_bridge = cfg.CONF.df.integration_bridge
|
||||
self.int_peer_patch_port = cfg.CONF.df_dnat_app.int_peer_patch_port
|
||||
self.ex_peer_patch_port = cfg.CONF.df_dnat_app.ex_peer_patch_port
|
||||
self.send_arp_interval = cfg.CONF.df_dnat_app.send_arp_interval
|
||||
self.external_networks = collections.defaultdict(int)
|
||||
self.local_floatingips = collections.defaultdict(str)
|
||||
|
||||
def switch_features_handler(self, ev):
|
||||
self._init_external_bridge()
|
||||
self._init_external_network_bridge_check()
|
||||
self._install_output_to_physical_patch(self.external_ofport)
|
||||
|
||||
def _check_for_external_network_bridge_mac(self):
|
||||
idl = self.vswitch_api.idl
|
||||
if not idl:
|
||||
def ovs_port_updated(self, ovs_port):
|
||||
if ovs_port.get_name() != self.external_network_bridge:
|
||||
return
|
||||
interface = idlutils.row_by_value(
|
||||
idl,
|
||||
'Interface',
|
||||
'name',
|
||||
self.external_network_bridge,
|
||||
None,
|
||||
)
|
||||
if not interface:
|
||||
return
|
||||
if not interface.mac_in_use[0]:
|
||||
return
|
||||
if interface.mac_in_use[0] == '00:00:00:00:00:00':
|
||||
return
|
||||
return interface.mac_in_use[0]
|
||||
|
||||
def _wait_for_external_network_bridge_mac(self):
|
||||
mac = self._check_for_external_network_bridge_mac()
|
||||
if not mac:
|
||||
mac = ovs_port.get_mac_in_use()
|
||||
if (self.external_bridge_mac == mac
|
||||
or not mac
|
||||
or mac == '00:00:00:00:00:00'):
|
||||
return
|
||||
|
||||
for key, floatingip in six.iteritems(self.local_floatingips):
|
||||
self._install_dnat_egress_rules(floatingip, mac)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
def _init_external_network_bridge_check(self):
|
||||
"""Spawn a thread to check that br-ex is ready."""
|
||||
periodic = loopingcall.FixedIntervalLoopingCall(
|
||||
self._wait_for_external_network_bridge_mac)
|
||||
periodic.start(interval=self.send_arp_interval)
|
||||
self.external_bridge_mac = mac
|
||||
|
||||
def _init_external_bridge(self):
|
||||
self.external_ofport = self.vswitch_api.create_patch_port(
|
||||
|
@ -272,9 +249,9 @@ class DNATApp(df_base_app.DFlowApp):
|
|||
const.PRIORITY_MEDIUM,
|
||||
const.EGRESS_NAT_TABLE,
|
||||
match=match)
|
||||
mac = self._check_for_external_network_bridge_mac()
|
||||
if mac:
|
||||
self._install_dnat_egress_rules(floatingip, mac)
|
||||
if self.external_bridge_mac:
|
||||
self._install_dnat_egress_rules(floatingip,
|
||||
self.external_bridge_mac)
|
||||
|
||||
def _remove_egress_nat_rules(self, floatingip):
|
||||
net = netaddr.IPNetwork(floatingip.get_external_cidr())
|
||||
|
@ -366,16 +343,6 @@ class DNATApp(df_base_app.DFlowApp):
|
|||
self._install_ingress_nat_rules(floatingip)
|
||||
self._install_egress_nat_rules(floatingip)
|
||||
|
||||
def update_bridge_port(self, lport):
|
||||
port_name = lport.get_name()
|
||||
if port_name != self.external_network_bridge:
|
||||
return
|
||||
mac = self._check_for_external_network_bridge_mac()
|
||||
if not mac:
|
||||
return
|
||||
for key, floatingip in six.iteritems(self.local_floatingips):
|
||||
self._install_dnat_egress_rules(floatingip, mac)
|
||||
|
||||
def delete_floatingip(self, floatingip):
|
||||
self._remove_ingress_nat_rules(floatingip)
|
||||
self._remove_egress_nat_rules(floatingip)
|
||||
|
|
|
@ -25,8 +25,6 @@ from oslo_config import cfg
|
|||
from oslo_log import log
|
||||
from oslo_utils import encodeutils
|
||||
|
||||
from neutron.agent.ovsdb.native import idlutils
|
||||
|
||||
from dragonflow._i18n import _, _LW, _LE
|
||||
from dragonflow.common import exceptions
|
||||
from dragonflow.common import utils as df_utils
|
||||
|
@ -76,6 +74,7 @@ class MetadataServiceApp(df_base_app.DFlowApp):
|
|||
)
|
||||
self._arp_responder = None
|
||||
self._ofport = None
|
||||
self._interface_mac = ""
|
||||
cfg.CONF.register_opts(options, group='df_metadata')
|
||||
self._ip = cfg.CONF.df_metadata.ip
|
||||
self._port = cfg.CONF.df_metadata.port
|
||||
|
@ -85,7 +84,20 @@ class MetadataServiceApp(df_base_app.DFlowApp):
|
|||
if ovs_port.get_name() != cfg.CONF.df.metadata_interface:
|
||||
return
|
||||
|
||||
self._add_metadata_interface_flows()
|
||||
ofport = ovs_port.get_ofport()
|
||||
mac = ovs_port.get_mac_in_use()
|
||||
if not ofport or not mac:
|
||||
return
|
||||
|
||||
if ofport <= 0:
|
||||
return
|
||||
|
||||
if ofport == self._ofport and mac == self._interface_mac:
|
||||
return
|
||||
|
||||
self._add_tap_metadata_port(ofport, mac)
|
||||
self._ofport = ofport
|
||||
self._interface_mac = mac
|
||||
|
||||
def ovs_port_deleted(self, ovs_port):
|
||||
if ovs_port.get_name() != cfg.CONF.df.metadata_interface:
|
||||
|
@ -110,28 +122,7 @@ class MetadataServiceApp(df_base_app.DFlowApp):
|
|||
match=parser.OFPMatch(in_port=self._ofport))
|
||||
|
||||
self._ofport = None
|
||||
|
||||
def _add_metadata_interface_flows(self):
|
||||
idl = self.vswitch_api.idl
|
||||
if not idl:
|
||||
return
|
||||
interface = idlutils.row_by_value(
|
||||
idl,
|
||||
'Interface',
|
||||
'name',
|
||||
self._interface,
|
||||
None,
|
||||
)
|
||||
if not interface:
|
||||
return
|
||||
ofport = interface.ofport
|
||||
if not ofport:
|
||||
return
|
||||
if isinstance(ofport, list):
|
||||
ofport = ofport[0]
|
||||
if ofport <= 0:
|
||||
return
|
||||
self._add_tap_metadata_port(ofport, interface.mac_in_use[0])
|
||||
self._interface_mac = ""
|
||||
|
||||
def _add_tap_metadata_port(self, ofport, mac):
|
||||
"""
|
||||
|
|
|
@ -1155,6 +1155,9 @@ class OvsPort(DbStoreObject):
|
|||
def get_attached_mac(self):
|
||||
return self.ovs_port.get_attached_mac()
|
||||
|
||||
def get_mac_in_use(self):
|
||||
return self.ovs_port.get_mac_in_use()
|
||||
|
||||
def get_remote_ip(self):
|
||||
return self.ovs_port.get_remote_ip()
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ class LocalInterface(object):
|
|||
self.iface_id = ""
|
||||
self.peer = ""
|
||||
self.attached_mac = ""
|
||||
self.mac_in_use = ""
|
||||
self.remote_ip = ""
|
||||
self.tunnel_type = ""
|
||||
|
||||
|
@ -103,6 +104,8 @@ class LocalInterface(object):
|
|||
result.uuid = row.uuid
|
||||
if row.ofport:
|
||||
result.ofport = int(row.ofport[0])
|
||||
if row.mac_in_use:
|
||||
result.mac_in_use = row.mac_in_use[0]
|
||||
result.name = row.name
|
||||
if row.admin_state:
|
||||
result.admin_state = row.admin_state[0]
|
||||
|
@ -141,6 +144,9 @@ class LocalInterface(object):
|
|||
def get_attached_mac(self):
|
||||
return self.attached_mac
|
||||
|
||||
def get_mac_in_use(self):
|
||||
return self.mac_in_use
|
||||
|
||||
def get_remote_ip(self):
|
||||
return self.remote_ip
|
||||
|
||||
|
@ -150,11 +156,11 @@ class LocalInterface(object):
|
|||
def __str__(self):
|
||||
if self.ofport is None:
|
||||
self.ofport = -1
|
||||
return "uuid:%s, ofport:%d, name:%s, " \
|
||||
"admin_state:%s, type:%s, " \
|
||||
"iface_id:%s, peer:%s, " \
|
||||
"attached_mac:%s, remote_ip:%s, " \
|
||||
"tunnel_type:%s" % (self.uuid,
|
||||
return ("uuid:%s, ofport:%d, name:%s, "
|
||||
"admin_state:%s, type:%s, "
|
||||
"iface_id:%s, peer:%s, "
|
||||
"attached_mac:%s, mac_in_use:%s, remote_ip:%s, "
|
||||
"tunnel_type:%s" % (self.uuid,
|
||||
self.ofport,
|
||||
self.name,
|
||||
self.admin_state,
|
||||
|
@ -162,5 +168,6 @@ class LocalInterface(object):
|
|||
self.iface_id,
|
||||
self.peer,
|
||||
self.attached_mac,
|
||||
self.mac_in_use,
|
||||
self.remote_ip,
|
||||
self.tunnel_type)
|
||||
self.tunnel_type))
|
||||
|
|
|
@ -18,6 +18,7 @@ from oslo_config import cfg
|
|||
|
||||
from dragonflow.controller import df_local_controller
|
||||
from dragonflow.controller import ryu_base_app
|
||||
from dragonflow.controller import topology
|
||||
from dragonflow.db import api_nb
|
||||
from dragonflow.tests import base as tests_base
|
||||
|
||||
|
@ -28,6 +29,7 @@ class DFAppTestBase(tests_base.BaseTestCase):
|
|||
def setUp(self):
|
||||
cfg.CONF.set_override('apps_list', self.apps_list, group='df')
|
||||
super(DFAppTestBase, self).setUp()
|
||||
mock.patch('ryu.base.app_manager.AppManager.get_instance').start()
|
||||
self.controller = df_local_controller.DfLocalController('fake_host')
|
||||
self.nb_api = self.controller.nb_api = mock.MagicMock()
|
||||
self.vswitch_api = self.controller.vswitch_api = mock.MagicMock()
|
||||
|
@ -40,6 +42,7 @@ class DFAppTestBase(tests_base.BaseTestCase):
|
|||
self.open_flow_app = self.controller.open_flow_app
|
||||
self.datapath = self.open_flow_app._datapath = mock.Mock()
|
||||
self.open_flow_app.load(self.controller.open_flow_app, **kwargs)
|
||||
self.controller.topology = topology.Topology(self.controller, False)
|
||||
|
||||
self.vswitch_api.get_local_ports_to_ofport_mapping.return_value = (
|
||||
{}, {fake_local_port1.get_id(): 2})
|
||||
|
|
|
@ -53,3 +53,40 @@ class TestDNATApp(test_app_base.DFAppTestBase):
|
|||
self.dnat_app.mod_flow.assert_called_once_with(
|
||||
self.datapath, inst=mock.ANY, table_id=constants.INGRESS_NAT_TABLE,
|
||||
priority=constants.PRIORITY_MEDIUM, match=mock.ANY)
|
||||
|
||||
def test_external_bridge_online(self):
|
||||
self.dnat_app.local_floatingips[
|
||||
test_app_base.fake_floatingip1.get_id()] = (
|
||||
test_app_base.fake_floatingip1)
|
||||
|
||||
with mock.patch.object(self.dnat_app,
|
||||
'_install_dnat_egress_rules') as mock_func:
|
||||
fake_ovs_port = mock.Mock()
|
||||
fake_ovs_port.get_ofport = mock.Mock(return_value=-1)
|
||||
fake_ovs_port.get_name = mock.Mock(
|
||||
return_value=self.dnat_app.external_network_bridge)
|
||||
# Device without mac will not trigger update flow
|
||||
fake_ovs_port.get_mac_in_use = mock.Mock(return_value="")
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Other device update will not trigger update flow
|
||||
fake_ovs_port.get_mac_in_use = mock.Mock(
|
||||
return_value="aa:bb:cc:dd:ee:ff")
|
||||
fake_ovs_port.get_name = mock.Mock(return_value="no-bridge")
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Device with mac will trigger update flow
|
||||
fake_ovs_port.get_name = mock.Mock(
|
||||
return_value=self.dnat_app.external_network_bridge)
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_called_once_with(test_app_base.fake_floatingip1,
|
||||
"aa:bb:cc:dd:ee:ff")
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Duplicated updated will not trigger update flow
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright (c) 2016 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from dragonflow.tests.unit import test_app_base
|
||||
|
||||
|
||||
class TestMetadataServiceApp(test_app_base.DFAppTestBase):
|
||||
apps_list = "metadata_service_app.MetadataServiceApp"
|
||||
|
||||
def setUp(self):
|
||||
super(TestMetadataServiceApp, self).setUp()
|
||||
self.meta_app = self.open_flow_app.dispatcher.apps[0]
|
||||
|
||||
def test_metadata_interface_online(self):
|
||||
with mock.patch.object(self.meta_app,
|
||||
'_add_tap_metadata_port') as mock_func:
|
||||
fake_ovs_port = mock.Mock()
|
||||
fake_ovs_port.get_ofport = mock.Mock(return_value=1)
|
||||
fake_ovs_port.get_name = mock.Mock(
|
||||
return_value=self.meta_app._interface)
|
||||
# Device without mac will not trigger update flow
|
||||
fake_ovs_port.get_mac_in_use = mock.Mock(return_value="")
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Other device update will not trigger update flow
|
||||
fake_ovs_port.get_mac_in_use = mock.Mock(
|
||||
return_value="aa:bb:cc:dd:ee:ff")
|
||||
fake_ovs_port.get_name = mock.Mock(return_value="no-interface")
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Device with mac will trigger update flow
|
||||
fake_ovs_port.get_name = mock.Mock(
|
||||
return_value=self.meta_app._interface)
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_called_once_with(1,
|
||||
"aa:bb:cc:dd:ee:ff")
|
||||
mock_func.reset_mock()
|
||||
|
||||
# Duplicated updated will not trigger update flow
|
||||
self.controller.ovs_port_updated(fake_ovs_port)
|
||||
mock_func.assert_not_called()
|
Loading…
Reference in New Issue