OVS-agent: Separate ovs-ofctl using code as a driver
This is a preparation to introduce another Ryu-based implementation. The aim is to replace this with the new Ryu-based implementation eventually. Add a config option for OVS-agent which selects the implementation. Currently, the only available choice is 'ovs-ofctl'. Also, this commit simplifies DVR logics by reducing duplications and makes some of DVR UTs actually check the flows rather than just "add_flow is called". Partially-Implements: blueprint ovs-ofctl-to-python Change-Id: Ie1224f8a1c17268cd7d1c474ed82fdfb8852eaa8
This commit is contained in:
parent
6ba78cec1a
commit
eab71473c3
etc/neutron/plugins/openvswitch
neutron
cmd/eventlet/plugins
plugins/openvswitch
tests
functional/agent
unit/plugins/openvswitch
@ -53,6 +53,10 @@
|
||||
# ovs-vsctl set-manager ptcp:6640:127.0.0.1
|
||||
# ovsdb_connection = tcp:127.0.0.1:6640
|
||||
|
||||
# (StrOpt) OpenFlow interface to use.
|
||||
# 'ovs-ofctl' is currently the only available choice.
|
||||
# of_interface = ovs-ofctl
|
||||
|
||||
[agent]
|
||||
# Agent's polling interval in seconds
|
||||
# polling_interval = 2
|
||||
|
@ -13,8 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
|
||||
import neutron.plugins.openvswitch.agent.main as agent_main
|
||||
|
||||
|
||||
def main():
|
||||
ovs_neutron_agent.main()
|
||||
agent_main.main()
|
||||
|
45
neutron/plugins/openvswitch/agent/main.py
Normal file
45
neutron/plugins/openvswitch/agent/main.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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 sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron.common import config as common_config
|
||||
from neutron.common import utils as n_utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
cfg.CONF.import_group('OVS', 'neutron.plugins.openvswitch.common.config')
|
||||
|
||||
|
||||
_main_modules = {
|
||||
'ovs-ofctl': 'neutron.plugins.openvswitch.agent.openflow.ovs_ofctl.main',
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
common_config.init(sys.argv[1:])
|
||||
driver_name = cfg.CONF.OVS.of_interface
|
||||
mod_name = _main_modules[driver_name]
|
||||
mod = importutils.import_module(mod_name)
|
||||
mod.init_config()
|
||||
common_config.setup_logging()
|
||||
n_utils.log_opt_values(LOG)
|
||||
mod.main()
|
0
neutron/plugins/openvswitch/agent/openflow/__init__.py
Normal file
0
neutron/plugins/openvswitch/agent/openflow/__init__.py
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
# Copyright 2011 VMware, Inc.
|
||||
# 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.
|
||||
|
||||
|
||||
class OVSDVRProcessMixin(object):
|
||||
"""Common logic for br-tun and br-phys' DVR_PROCESS tables.
|
||||
|
||||
Inheriters should provide self.dvr_process_table_id and
|
||||
self.dvr_process_next_table_id.
|
||||
"""
|
||||
|
||||
def install_dvr_process_ipv4(self, vlan_tag, gateway_ip):
|
||||
# block ARP
|
||||
self.add_flow(table=self.dvr_process_table_id,
|
||||
priority=3,
|
||||
dl_vlan=vlan_tag,
|
||||
proto='arp',
|
||||
nw_dst=gateway_ip,
|
||||
actions='drop')
|
||||
|
||||
def delete_dvr_process_ipv4(self, vlan_tag, gateway_ip):
|
||||
self.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag,
|
||||
proto='arp',
|
||||
nw_dst=gateway_ip)
|
||||
|
||||
def install_dvr_process_ipv6(self, vlan_tag, gateway_mac):
|
||||
# block RA
|
||||
self.add_flow(table=self.dvr_process_table_id,
|
||||
priority=3,
|
||||
dl_vlan=vlan_tag,
|
||||
proto='icmp6',
|
||||
dl_src=gateway_mac,
|
||||
actions='drop')
|
||||
|
||||
def delete_dvr_process_ipv6(self, vlan_tag, gateway_mac):
|
||||
self.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag,
|
||||
proto='icmp6',
|
||||
dl_src=gateway_mac)
|
||||
|
||||
def install_dvr_process(self, vlan_tag, vif_mac, dvr_mac_address):
|
||||
self.add_flow(table=self.dvr_process_table_id,
|
||||
priority=2,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_dst=vif_mac,
|
||||
actions="drop")
|
||||
self.add_flow(table=self.dvr_process_table_id,
|
||||
priority=1,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_src=vif_mac,
|
||||
actions="mod_dl_src:%s,resubmit(,%s)" %
|
||||
(dvr_mac_address, self.dvr_process_next_table_id))
|
||||
|
||||
def delete_dvr_process(self, vlan_tag, vif_mac):
|
||||
self.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_dst=vif_mac)
|
||||
self.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_src=vif_mac)
|
133
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/br_int.py
Normal file
133
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/br_int.py
Normal file
@ -0,0 +1,133 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
"""
|
||||
* references
|
||||
** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic
|
||||
"""
|
||||
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import ovs_bridge
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
|
||||
class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
|
||||
"""openvswitch agent br-int specific logic."""
|
||||
|
||||
def setup_default_table(self):
|
||||
self.delete_flows()
|
||||
self.install_normal()
|
||||
self.setup_canary_table()
|
||||
self.install_drop(table_id=constants.ARP_SPOOF_TABLE)
|
||||
|
||||
def setup_canary_table(self):
|
||||
self.install_drop(constants.CANARY_TABLE)
|
||||
|
||||
def check_canary_table(self):
|
||||
canary_flows = self.dump_flows(constants.CANARY_TABLE)
|
||||
if canary_flows == '':
|
||||
return constants.OVS_RESTARTED
|
||||
elif canary_flows is None:
|
||||
return constants.OVS_DEAD
|
||||
else:
|
||||
return constants.OVS_NORMAL
|
||||
|
||||
def provision_local_vlan(self, port, lvid, segmentation_id):
|
||||
if segmentation_id is None:
|
||||
dl_vlan = 0xffff
|
||||
else:
|
||||
dl_vlan = segmentation_id
|
||||
self.add_flow(priority=3,
|
||||
in_port=port,
|
||||
dl_vlan=dl_vlan,
|
||||
actions="mod_vlan_vid:%s,normal" % lvid)
|
||||
|
||||
def reclaim_local_vlan(self, port, segmentation_id):
|
||||
if segmentation_id is None:
|
||||
dl_vlan = 0xffff
|
||||
else:
|
||||
dl_vlan = segmentation_id
|
||||
self.delete_flows(in_port=port, dl_vlan=dl_vlan)
|
||||
|
||||
@staticmethod
|
||||
def _dvr_to_src_mac_table_id(network_type):
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
return constants.DVR_TO_SRC_MAC_VLAN
|
||||
else:
|
||||
return constants.DVR_TO_SRC_MAC
|
||||
|
||||
def install_dvr_to_src_mac(self, network_type,
|
||||
vlan_tag, gateway_mac, dst_mac, dst_port):
|
||||
table_id = self._dvr_to_src_mac_table_id(network_type)
|
||||
self.add_flow(table=table_id,
|
||||
priority=4,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_dst=dst_mac,
|
||||
actions="strip_vlan,mod_dl_src:%s,"
|
||||
"output:%s" % (gateway_mac, dst_port))
|
||||
|
||||
def delete_dvr_to_src_mac(self, network_type, vlan_tag, dst_mac):
|
||||
table_id = self._dvr_to_src_mac_table_id(network_type)
|
||||
self.delete_flows(table=table_id,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_dst=dst_mac)
|
||||
|
||||
def add_dvr_mac_vlan(self, mac, port):
|
||||
self.install_goto(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=4,
|
||||
in_port=port,
|
||||
eth_src=mac,
|
||||
dest_table_id=constants.DVR_TO_SRC_MAC_VLAN)
|
||||
|
||||
def remove_dvr_mac_vlan(self, mac):
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
self.delete_flows(table_id=constants.LOCAL_SWITCHING,
|
||||
eth_src=mac)
|
||||
|
||||
def add_dvr_mac_tun(self, mac, port):
|
||||
# Table LOCAL_SWITCHING will now sort DVR traffic from other
|
||||
# traffic depending on in_port
|
||||
self.install_goto(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=2,
|
||||
in_port=port,
|
||||
eth_src=mac,
|
||||
dest_table_id=constants.DVR_TO_SRC_MAC)
|
||||
|
||||
def remove_dvr_mac_tun(self, mac, port):
|
||||
self.delete_flows(table_id=constants.LOCAL_SWITCHING,
|
||||
in_port=port, eth_src=mac)
|
||||
|
||||
def install_arp_spoofing_protection(self, port, ip_addresses):
|
||||
# allow ARPs as long as they match addresses that actually
|
||||
# belong to the port.
|
||||
for ip in ip_addresses:
|
||||
self.install_normal(
|
||||
table_id=constants.ARP_SPOOF_TABLE, priority=2,
|
||||
proto='arp', arp_spa=ip, in_port=port)
|
||||
|
||||
# Now that the rules are ready, direct ARP traffic from the port into
|
||||
# the anti-spoof table.
|
||||
# This strategy fails gracefully because OVS versions that can't match
|
||||
# on ARP headers will just process traffic normally.
|
||||
self.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=10, proto='arp', in_port=port,
|
||||
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
|
||||
|
||||
def delete_arp_spoofing_protection(self, port):
|
||||
self.delete_flows(table_id=constants.LOCAL_SWITCHING,
|
||||
in_port=port, proto='arp')
|
||||
self.delete_flows(table_id=constants.ARP_SPOOF_TABLE,
|
||||
in_port=port)
|
@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import br_dvr_process
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import ovs_bridge
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
|
||||
class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge,
|
||||
br_dvr_process.OVSDVRProcessMixin):
|
||||
"""openvswitch agent physical bridge specific logic."""
|
||||
|
||||
# Used by OVSDVRProcessMixin
|
||||
dvr_process_table_id = constants.DVR_PROCESS_VLAN
|
||||
dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION
|
||||
|
||||
def setup_default_table(self):
|
||||
self.delete_flows()
|
||||
self.install_normal()
|
||||
|
||||
def provision_local_vlan(self, port, lvid, segmentation_id, distributed):
|
||||
table_id = constants.LOCAL_VLAN_TRANSLATION if distributed else 0
|
||||
if segmentation_id is None:
|
||||
self.add_flow(table=table_id,
|
||||
priority=4,
|
||||
in_port=port,
|
||||
dl_vlan=lvid,
|
||||
actions="strip_vlan,normal")
|
||||
else:
|
||||
self.add_flow(table=table_id,
|
||||
priority=4,
|
||||
in_port=port,
|
||||
dl_vlan=lvid,
|
||||
actions="mod_vlan_vid:%s,normal" % segmentation_id)
|
||||
|
||||
def reclaim_local_vlan(self, port, lvid):
|
||||
self.delete_flows(in_port=port, dl_vlan=lvid)
|
||||
|
||||
def add_dvr_mac_vlan(self, mac, port):
|
||||
self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN,
|
||||
priority=2, eth_src=mac, port=port)
|
||||
|
||||
def remove_dvr_mac_vlan(self, mac):
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
self.delete_flows(table_id=constants.DVR_NOT_LEARN_VLAN,
|
||||
eth_src=mac)
|
246
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/br_tun.py
Normal file
246
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/br_tun.py
Normal file
@ -0,0 +1,246 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
# Copyright 2011 VMware, Inc.
|
||||
# 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 functools
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import br_dvr_process
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import ovs_bridge
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
|
||||
class OVSTunnelBridge(ovs_bridge.OVSAgentBridge,
|
||||
br_dvr_process.OVSDVRProcessMixin):
|
||||
"""openvswitch agent tunnel bridge specific logic."""
|
||||
|
||||
# Used by OVSDVRProcessMixin
|
||||
dvr_process_table_id = constants.DVR_PROCESS
|
||||
dvr_process_next_table_id = constants.PATCH_LV_TO_TUN
|
||||
|
||||
def setup_default_table(self, patch_int_ofport, arp_responder_enabled):
|
||||
# Table 0 (default) will sort incoming traffic depending on in_port
|
||||
self.add_flow(priority=1,
|
||||
in_port=patch_int_ofport,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN)
|
||||
self.add_flow(priority=0, actions="drop")
|
||||
|
||||
if arp_responder_enabled:
|
||||
# ARP broadcast-ed request go to the local ARP_RESPONDER table to
|
||||
# be locally resolved
|
||||
# REVISIT(yamamoto): arp_op=arp.ARP_REQUEST
|
||||
self.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=1,
|
||||
proto='arp',
|
||||
dl_dst="ff:ff:ff:ff:ff:ff",
|
||||
actions=("resubmit(,%s)" %
|
||||
constants.ARP_RESPONDER))
|
||||
|
||||
# PATCH_LV_TO_TUN table will handle packets coming from patch_int
|
||||
# unicasts go to table UCAST_TO_TUN where remote addresses are learnt
|
||||
self.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
dl_dst="00:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions="resubmit(,%s)" % constants.UCAST_TO_TUN)
|
||||
|
||||
# Broadcasts/multicasts go to table FLOOD_TO_TUN that handles flooding
|
||||
self.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions="resubmit(,%s)" % constants.FLOOD_TO_TUN)
|
||||
|
||||
# Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id
|
||||
# for each tunnel type, and resubmit to table LEARN_FROM_TUN where
|
||||
# remote mac addresses will be learnt
|
||||
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
self.add_flow(table=constants.TUN_TABLE[tunnel_type],
|
||||
priority=0,
|
||||
actions="drop")
|
||||
|
||||
# LEARN_FROM_TUN table will have a single flow using a learn action to
|
||||
# dynamically set-up flows in UCAST_TO_TUN corresponding to remote mac
|
||||
# addresses (assumes that lvid has already been set by a previous flow)
|
||||
learned_flow = ("table=%s,"
|
||||
"priority=1,"
|
||||
"hard_timeout=300,"
|
||||
"NXM_OF_VLAN_TCI[0..11],"
|
||||
"NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
|
||||
"load:0->NXM_OF_VLAN_TCI[],"
|
||||
"load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
|
||||
"output:NXM_OF_IN_PORT[]" %
|
||||
constants.UCAST_TO_TUN)
|
||||
# Once remote mac addresses are learnt, output packet to patch_int
|
||||
self.add_flow(table=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions="learn(%s),output:%s" %
|
||||
(learned_flow, patch_int_ofport))
|
||||
|
||||
# Egress unicast will be handled in table UCAST_TO_TUN, where remote
|
||||
# mac addresses will be learned. For now, just add a default flow that
|
||||
# will resubmit unknown unicasts to table FLOOD_TO_TUN to treat them
|
||||
# as broadcasts/multicasts
|
||||
self.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN)
|
||||
|
||||
if arp_responder_enabled:
|
||||
# If none of the ARP entries correspond to the requested IP, the
|
||||
# broadcast-ed packet is resubmitted to the flooding table
|
||||
self.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN)
|
||||
|
||||
# FLOOD_TO_TUN will handle flooding in tunnels based on lvid,
|
||||
# for now, add a default drop action
|
||||
self.install_drop(table_id=constants.FLOOD_TO_TUN)
|
||||
|
||||
def provision_local_vlan(self, network_type, lvid, segmentation_id,
|
||||
distributed=False):
|
||||
if distributed:
|
||||
table_id = constants.DVR_NOT_LEARN
|
||||
else:
|
||||
table_id = constants.LEARN_FROM_TUN
|
||||
self.add_flow(table=constants.TUN_TABLE[network_type],
|
||||
priority=1,
|
||||
tun_id=segmentation_id,
|
||||
actions="mod_vlan_vid:%s,"
|
||||
"resubmit(,%s)" %
|
||||
(lvid, table_id))
|
||||
|
||||
def reclaim_local_vlan(self, network_type, segmentation_id):
|
||||
self.delete_flows(table=constants.TUN_TABLE[network_type],
|
||||
tun_id=segmentation_id)
|
||||
|
||||
@staticmethod
|
||||
def _ofport_set_to_str(ports_set):
|
||||
return ",".join(map(str, ports_set))
|
||||
|
||||
def install_flood_to_tun(self, vlan, tun_id, ports, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
br.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=vlan,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(tun_id, self._ofport_set_to_str(ports)))
|
||||
|
||||
def delete_flood_to_tun(self, vlan, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
br.delete_flows(table=constants.FLOOD_TO_TUN, dl_vlan=vlan)
|
||||
|
||||
def install_unicast_to_tun(self, vlan, tun_id, port, mac,
|
||||
deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
br.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=2,
|
||||
dl_vlan=vlan,
|
||||
dl_dst=mac,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(tun_id, port))
|
||||
|
||||
def delete_unicast_to_tun(self, vlan, mac, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
if mac is None:
|
||||
br.delete_flows(table=constants.UCAST_TO_TUN,
|
||||
dl_vlan=vlan)
|
||||
else:
|
||||
br.delete_flows(table=constants.UCAST_TO_TUN,
|
||||
dl_vlan=vlan,
|
||||
dl_dst=mac)
|
||||
|
||||
def install_arp_responder(self, vlan, ip, mac, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
actions = constants.ARP_RESPONDER_ACTIONS % {
|
||||
'mac': netaddr.EUI(mac, dialect=netaddr.mac_unix),
|
||||
'ip': netaddr.IPAddress(ip),
|
||||
}
|
||||
br.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=1,
|
||||
proto='arp',
|
||||
dl_vlan=vlan,
|
||||
nw_dst='%s' % ip,
|
||||
actions=actions)
|
||||
|
||||
def delete_arp_responder(self, vlan, ip, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
if ip is None:
|
||||
br.delete_flows(table=constants.ARP_RESPONDER,
|
||||
proto='arp',
|
||||
dl_vlan=vlan)
|
||||
else:
|
||||
br.delete_flows(table=constants.ARP_RESPONDER,
|
||||
proto='arp',
|
||||
dl_vlan=vlan,
|
||||
nw_dst='%s' % ip)
|
||||
|
||||
def setup_tunnel_port(self, network_type, port):
|
||||
self.add_flow(priority=1,
|
||||
in_port=port,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.TUN_TABLE[network_type])
|
||||
|
||||
def cleanup_tunnel_port(self, port):
|
||||
self.delete_flows(in_port=port)
|
||||
|
||||
def add_dvr_mac_tun(self, mac, port):
|
||||
# Table DVR_NOT_LEARN ensures unique dvr macs in the cloud
|
||||
# are not learnt, as they may result in flow explosions
|
||||
self.install_output(table_id=constants.DVR_NOT_LEARN,
|
||||
priority=1,
|
||||
eth_src=mac,
|
||||
port=port)
|
||||
|
||||
def remove_dvr_mac_tun(self, mac):
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
self.delete_flows(table_id=constants.DVR_NOT_LEARN,
|
||||
eth_src=mac)
|
||||
|
||||
def deferred(self, **kwargs):
|
||||
return DeferredOVSTunnelBridge(self, **kwargs)
|
||||
|
||||
|
||||
class DeferredOVSTunnelBridge(ovs_lib.DeferredOVSBridge):
|
||||
_METHODS = [
|
||||
'install_unicast_to_tun',
|
||||
'delete_unicast_to_tun',
|
||||
'install_flood_to_tun',
|
||||
'delete_flood_to_tun',
|
||||
'install_arp_responder',
|
||||
'delete_arp_responder',
|
||||
]
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._METHODS:
|
||||
m = getattr(self.br, name)
|
||||
return functools.partial(m, deferred_br=self)
|
||||
raise AttributeError(name)
|
33
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/main.py
Normal file
33
neutron/plugins/openvswitch/agent/openflow/ovs_ofctl/main.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import br_int
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import br_phys
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import br_tun
|
||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
|
||||
|
||||
|
||||
def init_config():
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
bridge_classes = {
|
||||
'br_int': br_int.OVSIntegrationBridge,
|
||||
'br_phys': br_phys.OVSPhysicalBridge,
|
||||
'br_tun': br_tun.OVSTunnelBridge,
|
||||
}
|
||||
ovs_neutron_agent.main(bridge_classes)
|
@ -0,0 +1,74 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
# Field name mappings (from Ryu to ovs-ofctl)
|
||||
_keywords = {
|
||||
'eth_src': 'dl_src',
|
||||
'eth_dst': 'dl_dst',
|
||||
'ipv4_src': 'nw_src',
|
||||
'ipv4_dst': 'nw_dst',
|
||||
'table_id': 'table',
|
||||
}
|
||||
|
||||
|
||||
class OpenFlowSwitchMixin(object):
|
||||
"""Mixin to provide common convenient routines for an openflow switch."""
|
||||
|
||||
@staticmethod
|
||||
def _conv_args(kwargs):
|
||||
for our_name, ovs_ofctl_name in _keywords.items():
|
||||
if our_name in kwargs:
|
||||
kwargs[ovs_ofctl_name] = kwargs.pop(our_name)
|
||||
return kwargs
|
||||
|
||||
def dump_flows(self, table_id):
|
||||
return self.dump_flows_for_table(table_id)
|
||||
|
||||
def install_goto_next(self, table_id):
|
||||
self.install_goto(table_id=table_id, dest_table_id=table_id + 1)
|
||||
|
||||
def install_output(self, port, table_id=0, priority=0, **kwargs):
|
||||
self.add_flow(table=table_id,
|
||||
priority=priority,
|
||||
actions="output:%s" % port,
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def install_normal(self, table_id=0, priority=0, **kwargs):
|
||||
self.add_flow(table=table_id,
|
||||
priority=priority,
|
||||
actions="normal",
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def install_goto(self, dest_table_id, table_id=0, priority=0, **kwargs):
|
||||
self.add_flow(table=table_id,
|
||||
priority=priority,
|
||||
actions="resubmit(,%s)" % dest_table_id,
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def install_drop(self, table_id=0, priority=0, **kwargs):
|
||||
self.add_flow(table=table_id,
|
||||
priority=priority,
|
||||
actions="drop",
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def delete_flows(self, **kwargs):
|
||||
# NOTE(yamamoto): super() points to ovs_lib.OVSBridge.
|
||||
# See ovs_bridge.py how this class is actually used.
|
||||
if kwargs:
|
||||
super(OpenFlowSwitchMixin, self).delete_flows(
|
||||
**self._conv_args(kwargs))
|
||||
else:
|
||||
super(OpenFlowSwitchMixin, self).remove_all_flows()
|
@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.plugins.openvswitch.agent.openflow.ovs_ofctl import ofswitch
|
||||
|
||||
|
||||
class OVSAgentBridge(ofswitch.OpenFlowSwitchMixin, ovs_lib.OVSBridge):
|
||||
"""Common code for bridges used by OVS agent"""
|
||||
|
||||
def setup_controllers(self, conf):
|
||||
self.set_protocols("[OpenFlow10]")
|
||||
self.del_controller()
|
||||
|
||||
def drop_port(self, in_port):
|
||||
self.install_drop(priority=2, in_port=in_port)
|
@ -171,8 +171,9 @@ class OVSDVRNeutronAgent(object):
|
||||
|
||||
if not self.in_distributed_mode():
|
||||
# switch all traffic using L2 learning
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=1, actions="normal")
|
||||
# REVISIT(yamamoto): why to install the same flow as
|
||||
# setup_integration_br?
|
||||
self.int_br.install_normal()
|
||||
|
||||
def get_dvr_mac_address_with_retry(self):
|
||||
# Get the local DVR MAC Address from the Neutron Server.
|
||||
@ -204,52 +205,42 @@ class OVSDVRNeutronAgent(object):
|
||||
LOG.info(_LI("L2 Agent operating in DVR Mode with MAC %s"),
|
||||
self.dvr_mac_address)
|
||||
# Remove existing flows in integration bridge
|
||||
self.int_br.remove_all_flows()
|
||||
self.int_br.delete_flows()
|
||||
|
||||
# Add a canary flow to int_br to track OVS restarts
|
||||
self.int_br.add_flow(table=constants.CANARY_TABLE, priority=0,
|
||||
actions="drop")
|
||||
self.int_br.setup_canary_table()
|
||||
|
||||
# Insert 'drop' action as the default for Table DVR_TO_SRC_MAC
|
||||
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
||||
priority=1,
|
||||
actions="drop")
|
||||
self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1)
|
||||
|
||||
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC_VLAN,
|
||||
priority=1,
|
||||
actions="drop")
|
||||
self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN,
|
||||
priority=1)
|
||||
|
||||
# Insert 'normal' action as the default for Table LOCAL_SWITCHING
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=1,
|
||||
actions="normal")
|
||||
self.int_br.install_normal(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=1)
|
||||
|
||||
for physical_network in self.bridge_mappings:
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=2,
|
||||
in_port=self.int_ofports[physical_network],
|
||||
actions="drop")
|
||||
self.int_br.install_drop(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=2,
|
||||
in_port=self.int_ofports[
|
||||
physical_network])
|
||||
|
||||
def setup_dvr_flows_on_tun_br(self):
|
||||
'''Setup up initial dvr flows into br-tun'''
|
||||
if not self.enable_tunneling or not self.in_distributed_mode():
|
||||
return
|
||||
|
||||
self.tun_br.add_flow(priority=1,
|
||||
in_port=self.patch_int_ofport,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_PROCESS)
|
||||
self.tun_br.install_goto(dest_table_id=constants.DVR_PROCESS,
|
||||
priority=1,
|
||||
in_port=self.patch_int_ofport)
|
||||
|
||||
# table-miss should be sent to learning table
|
||||
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.LEARN_FROM_TUN)
|
||||
self.tun_br.install_goto(table_id=constants.DVR_NOT_LEARN,
|
||||
dest_table_id=constants.LEARN_FROM_TUN)
|
||||
|
||||
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN)
|
||||
self.tun_br.install_goto(table_id=constants.DVR_PROCESS,
|
||||
dest_table_id=constants.PATCH_LV_TO_TUN)
|
||||
|
||||
def setup_dvr_flows_on_phys_br(self):
|
||||
'''Setup up initial dvr flows into br-phys'''
|
||||
@ -257,27 +248,63 @@ class OVSDVRNeutronAgent(object):
|
||||
return
|
||||
|
||||
for physical_network in self.bridge_mappings:
|
||||
self.phys_brs[physical_network].add_flow(priority=2,
|
||||
self.phys_brs[physical_network].install_goto(
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_PROCESS_VLAN)
|
||||
self.phys_brs[physical_network].add_flow(priority=1,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_NOT_LEARN_VLAN)
|
||||
self.phys_brs[physical_network].add_flow(
|
||||
table=constants.DVR_PROCESS_VLAN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.LOCAL_VLAN_TRANSLATION)
|
||||
self.phys_brs[physical_network].add_flow(
|
||||
table=constants.LOCAL_VLAN_TRANSLATION,
|
||||
priority=2,
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
actions="drop")
|
||||
self.phys_brs[physical_network].add_flow(
|
||||
table=constants.DVR_NOT_LEARN_VLAN,
|
||||
dest_table_id=constants.DVR_PROCESS_VLAN)
|
||||
self.phys_brs[physical_network].install_goto(
|
||||
priority=1,
|
||||
actions="NORMAL")
|
||||
dest_table_id=constants.DVR_NOT_LEARN_VLAN)
|
||||
self.phys_brs[physical_network].install_goto(
|
||||
table_id=constants.DVR_PROCESS_VLAN,
|
||||
priority=0,
|
||||
dest_table_id=constants.LOCAL_VLAN_TRANSLATION)
|
||||
self.phys_brs[physical_network].install_drop(
|
||||
table_id=constants.LOCAL_VLAN_TRANSLATION,
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
priority=2)
|
||||
self.phys_brs[physical_network].install_normal(
|
||||
table_id=constants.DVR_NOT_LEARN_VLAN,
|
||||
priority=1)
|
||||
|
||||
def _add_dvr_mac_for_phys_br(self, physical_network, mac):
|
||||
self.int_br.add_dvr_mac_vlan(mac=mac,
|
||||
port=self.int_ofports[physical_network])
|
||||
phys_br = self.phys_brs[physical_network]
|
||||
phys_br.add_dvr_mac_vlan(mac=mac,
|
||||
port=self.phys_ofports[physical_network])
|
||||
|
||||
def _remove_dvr_mac_for_phys_br(self, physical_network, mac):
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
self.int_br.remove_dvr_mac_vlan(mac=mac)
|
||||
phys_br = self.phys_brs[physical_network]
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
phys_br.remove_dvr_mac_vlan(mac=mac)
|
||||
|
||||
def _add_dvr_mac_for_tun_br(self, mac):
|
||||
self.int_br.add_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport)
|
||||
self.tun_br.add_dvr_mac_tun(mac=mac, port=self.patch_int_ofport)
|
||||
|
||||
def _remove_dvr_mac_for_tun_br(self, mac):
|
||||
self.int_br.remove_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport)
|
||||
# REVISIT(yamamoto): match in_port as well?
|
||||
self.tun_br.remove_dvr_mac_tun(mac=mac)
|
||||
|
||||
def _add_dvr_mac(self, mac):
|
||||
for physical_network in self.bridge_mappings:
|
||||
self._add_dvr_mac_for_phys_br(physical_network, mac)
|
||||
if self.enable_tunneling:
|
||||
self._add_dvr_mac_for_tun_br(mac)
|
||||
LOG.debug("Added DVR MAC flow for %s", mac)
|
||||
self.registered_dvr_macs.add(mac)
|
||||
|
||||
def _remove_dvr_mac(self, mac):
|
||||
for physical_network in self.bridge_mappings:
|
||||
self._remove_dvr_mac_for_phys_br(physical_network, mac)
|
||||
if self.enable_tunneling:
|
||||
self._remove_dvr_mac_for_tun_br(mac)
|
||||
LOG.debug("Removed DVR MAC flow for %s", mac)
|
||||
self.registered_dvr_macs.remove(mac)
|
||||
|
||||
def setup_dvr_mac_flows_on_all_brs(self):
|
||||
if not self.in_distributed_mode():
|
||||
@ -289,38 +316,7 @@ class OVSDVRNeutronAgent(object):
|
||||
for mac in dvr_macs:
|
||||
if mac['mac_address'] == self.dvr_mac_address:
|
||||
continue
|
||||
for physical_network in self.bridge_mappings:
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=4,
|
||||
in_port=self.int_ofports[physical_network],
|
||||
dl_src=mac['mac_address'],
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_TO_SRC_MAC_VLAN)
|
||||
self.phys_brs[physical_network].add_flow(
|
||||
table=constants.DVR_NOT_LEARN_VLAN,
|
||||
priority=2,
|
||||
dl_src=mac['mac_address'],
|
||||
actions="output:%s" %
|
||||
self.phys_ofports[physical_network])
|
||||
|
||||
if self.enable_tunneling:
|
||||
# Table 0 (default) will now sort DVR traffic from other
|
||||
# traffic depending on in_port
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=2,
|
||||
in_port=self.patch_tun_ofport,
|
||||
dl_src=mac['mac_address'],
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_TO_SRC_MAC)
|
||||
# Table DVR_NOT_LEARN ensures unique dvr macs in the cloud
|
||||
# are not learnt, as they may
|
||||
# result in flow explosions
|
||||
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
||||
priority=1,
|
||||
dl_src=mac['mac_address'],
|
||||
actions="output:%s" %
|
||||
self.patch_int_ofport)
|
||||
self.registered_dvr_macs.add(mac['mac_address'])
|
||||
self._add_dvr_mac(mac['mac_address'])
|
||||
|
||||
def dvr_mac_address_update(self, dvr_macs):
|
||||
if not self.dvr_mac_address:
|
||||
@ -342,50 +338,10 @@ class OVSDVRNeutronAgent(object):
|
||||
dvr_macs_removed = self.registered_dvr_macs - dvr_host_macs
|
||||
|
||||
for oldmac in dvr_macs_removed:
|
||||
for physical_network in self.bridge_mappings:
|
||||
self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
|
||||
in_port=self.int_ofports[physical_network],
|
||||
dl_src=oldmac)
|
||||
self.phys_brs[physical_network].delete_flows(
|
||||
table=constants.DVR_NOT_LEARN_VLAN,
|
||||
dl_src=oldmac)
|
||||
if self.enable_tunneling:
|
||||
self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
|
||||
in_port=self.patch_tun_ofport,
|
||||
dl_src=oldmac)
|
||||
self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN,
|
||||
dl_src=oldmac)
|
||||
LOG.debug("Removed DVR MAC flow for %s", oldmac)
|
||||
self.registered_dvr_macs.remove(oldmac)
|
||||
self._remove_dvr_mac(oldmac)
|
||||
|
||||
for newmac in dvr_macs_added:
|
||||
for physical_network in self.bridge_mappings:
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=4,
|
||||
in_port=self.int_ofports[physical_network],
|
||||
dl_src=newmac,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_TO_SRC_MAC_VLAN)
|
||||
self.phys_brs[physical_network].add_flow(
|
||||
table=constants.DVR_NOT_LEARN_VLAN,
|
||||
priority=2,
|
||||
dl_src=newmac,
|
||||
actions="output:%s" %
|
||||
self.phys_ofports[physical_network])
|
||||
if self.enable_tunneling:
|
||||
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=2,
|
||||
in_port=self.patch_tun_ofport,
|
||||
dl_src=newmac,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.DVR_TO_SRC_MAC)
|
||||
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
||||
priority=1,
|
||||
dl_src=newmac,
|
||||
actions="output:%s" %
|
||||
self.patch_int_ofport)
|
||||
LOG.debug("Added DVR MAC flow for %s", newmac)
|
||||
self.registered_dvr_macs.add(newmac)
|
||||
self._add_dvr_mac(newmac)
|
||||
|
||||
def in_distributed_mode(self):
|
||||
return self.dvr_mac_address is not None
|
||||
@ -394,16 +350,11 @@ class OVSDVRNeutronAgent(object):
|
||||
return device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE
|
||||
|
||||
def process_tunneled_network(self, network_type, lvid, segmentation_id):
|
||||
if self.in_distributed_mode():
|
||||
table_id = constants.DVR_NOT_LEARN
|
||||
else:
|
||||
table_id = constants.LEARN_FROM_TUN
|
||||
self.tun_br.add_flow(table=constants.TUN_TABLE[network_type],
|
||||
priority=1,
|
||||
tun_id=segmentation_id,
|
||||
actions="mod_vlan_vid:%s,"
|
||||
"resubmit(,%s)" %
|
||||
(lvid, table_id))
|
||||
self.tun_br.provision_local_vlan(
|
||||
network_type=network_type,
|
||||
lvid=lvid,
|
||||
segmentation_id=segmentation_id,
|
||||
distributed=self.in_distributed_mode())
|
||||
|
||||
def _bind_distributed_router_interface_port(self, port, lvm,
|
||||
fixed_ips, device_owner):
|
||||
@ -436,10 +387,8 @@ class OVSDVRNeutronAgent(object):
|
||||
# DVR takes over
|
||||
ldm.set_dvr_owned(True)
|
||||
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
|
||||
subnet_info = ldm.get_subnet_info()
|
||||
@ -469,73 +418,31 @@ class OVSDVRNeutronAgent(object):
|
||||
comp_ovsport.add_subnet(subnet_uuid)
|
||||
self.local_ports[vif.vif_id] = comp_ovsport
|
||||
# create rule for just this vm port
|
||||
self.int_br.add_flow(table=table_id,
|
||||
priority=4,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=comp_ovsport.get_mac(),
|
||||
actions="strip_vlan,mod_dl_src:%s,"
|
||||
"output:%s" %
|
||||
(subnet_info['gateway_mac'],
|
||||
comp_ovsport.get_ofport()))
|
||||
self.int_br.install_dvr_to_src_mac(
|
||||
network_type=lvm.network_type,
|
||||
vlan_tag=vlan_to_use,
|
||||
gateway_mac=subnet_info['gateway_mac'],
|
||||
dst_mac=comp_ovsport.get_mac(),
|
||||
dst_port=comp_ovsport.get_ofport())
|
||||
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
args = {'table': constants.DVR_PROCESS_VLAN,
|
||||
'priority': 3,
|
||||
'dl_vlan': lvm.vlan,
|
||||
'actions': "drop"}
|
||||
if ip_version == 4:
|
||||
args['proto'] = 'arp'
|
||||
args['nw_dst'] = subnet_info['gateway_ip']
|
||||
else:
|
||||
args['proto'] = 'icmp6'
|
||||
args['icmp_type'] = n_const.ICMPV6_TYPE_RA
|
||||
args['dl_src'] = subnet_info['gateway_mac']
|
||||
# TODO(vivek) remove the IPv6 related add_flow once SNAT is not
|
||||
# TODO(vivek) remove the IPv6 related flows once SNAT is not
|
||||
# used for IPv6 DVR.
|
||||
self.phys_brs[lvm.physical_network].add_flow(**args)
|
||||
self.phys_brs[lvm.physical_network].add_flow(
|
||||
table=constants.DVR_PROCESS_VLAN,
|
||||
priority=2,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port.vif_mac,
|
||||
actions="drop")
|
||||
|
||||
self.phys_brs[lvm.physical_network].add_flow(
|
||||
table=constants.DVR_PROCESS_VLAN,
|
||||
priority=1,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_src=port.vif_mac,
|
||||
actions="mod_dl_src:%s,resubmit(,%s)" %
|
||||
(self.dvr_mac_address, constants.LOCAL_VLAN_TRANSLATION))
|
||||
|
||||
br = self.phys_brs[lvm.physical_network]
|
||||
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
args = {'table': constants.DVR_PROCESS,
|
||||
'priority': 3,
|
||||
'dl_vlan': lvm.vlan,
|
||||
'actions': "drop"}
|
||||
if ip_version == 4:
|
||||
args['proto'] = 'arp'
|
||||
args['nw_dst'] = subnet_info['gateway_ip']
|
||||
else:
|
||||
args['proto'] = 'icmp6'
|
||||
args['icmp_type'] = n_const.ICMPV6_TYPE_RA
|
||||
args['dl_src'] = subnet_info['gateway_mac']
|
||||
# TODO(vivek) remove the IPv6 related add_flow once SNAT is not
|
||||
# used for IPv6 DVR.
|
||||
self.tun_br.add_flow(**args)
|
||||
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
||||
priority=2,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port.vif_mac,
|
||||
actions="drop")
|
||||
br = self.tun_br
|
||||
# TODO(vivek) remove the IPv6 related flows once SNAT is not
|
||||
# used for IPv6 DVR.
|
||||
if ip_version == 4:
|
||||
br.install_dvr_process_ipv4(
|
||||
vlan_tag=lvm.vlan, gateway_ip=subnet_info['gateway_ip'])
|
||||
else:
|
||||
br.install_dvr_process_ipv6(
|
||||
vlan_tag=lvm.vlan, gateway_mac=subnet_info['gateway_mac'])
|
||||
br.install_dvr_process(
|
||||
vlan_tag=lvm.vlan, vif_mac=port.vif_mac,
|
||||
dvr_mac_address=self.dvr_mac_address)
|
||||
|
||||
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
||||
priority=1,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_src=port.vif_mac,
|
||||
actions="mod_dl_src:%s,resubmit(,%s)" %
|
||||
(self.dvr_mac_address,
|
||||
constants.PATCH_LV_TO_TUN))
|
||||
# the dvr router interface is itself a port, so capture it
|
||||
# queue this subnet to that port. A subnet appears only once as
|
||||
# a router interface on any given router
|
||||
@ -578,20 +485,16 @@ class OVSDVRNeutronAgent(object):
|
||||
port.vif_mac, device_owner)
|
||||
ovsport.add_subnet(subnet_uuid)
|
||||
self.local_ports[port.vif_id] = ovsport
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
# create a rule for this vm port
|
||||
self.int_br.add_flow(table=table_id,
|
||||
priority=4,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=ovsport.get_mac(),
|
||||
actions="strip_vlan,mod_dl_src:%s,"
|
||||
"output:%s" %
|
||||
(subnet_info['gateway_mac'],
|
||||
ovsport.get_ofport()))
|
||||
self.int_br.install_dvr_to_src_mac(
|
||||
network_type=lvm.network_type,
|
||||
vlan_tag=vlan_to_use,
|
||||
gateway_mac=subnet_info['gateway_mac'],
|
||||
dst_mac=ovsport.get_mac(),
|
||||
dst_port=ovsport.get_ofport())
|
||||
|
||||
def _bind_centralized_snat_port_on_dvr_subnet(self, port, lvm,
|
||||
fixed_ips, device_owner):
|
||||
@ -631,19 +534,15 @@ class OVSDVRNeutronAgent(object):
|
||||
port.vif_mac, device_owner)
|
||||
ovsport.add_subnet(subnet_uuid)
|
||||
self.local_ports[port.vif_id] = ovsport
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
self.int_br.add_flow(table=table_id,
|
||||
priority=4,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=ovsport.get_mac(),
|
||||
actions="strip_vlan,mod_dl_src:%s,"
|
||||
" output:%s" %
|
||||
(subnet_info['gateway_mac'],
|
||||
ovsport.get_ofport()))
|
||||
self.int_br.install_dvr_to_src_mac(
|
||||
network_type=lvm.network_type,
|
||||
vlan_tag=vlan_to_use,
|
||||
gateway_mac=subnet_info['gateway_mac'],
|
||||
dst_mac=ovsport.get_mac(),
|
||||
dst_port=ovsport.get_ofport())
|
||||
|
||||
def bind_port_to_dvr(self, port, local_vlan_map,
|
||||
fixed_ips, device_owner):
|
||||
@ -681,10 +580,8 @@ class OVSDVRNeutronAgent(object):
|
||||
subnet_set = set(subnet_ids)
|
||||
network_type = lvm.network_type
|
||||
physical_network = lvm.physical_network
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
# ensure we process for all the subnets laid on this removed port
|
||||
for sub_uuid in subnet_set:
|
||||
@ -700,9 +597,9 @@ class OVSDVRNeutronAgent(object):
|
||||
compute_ports = ldm.get_compute_ofports()
|
||||
for vif_id in compute_ports:
|
||||
comp_port = self.local_ports[vif_id]
|
||||
self.int_br.delete_flows(table=table_id,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=comp_port.get_mac())
|
||||
self.int_br.delete_dvr_to_src_mac(
|
||||
network_type=network_type,
|
||||
vlan_tag=vlan_to_use, dst_mac=comp_port.get_mac())
|
||||
ldm.remove_all_compute_ofports()
|
||||
|
||||
if ldm.get_csnat_ofport() == constants.OFPORT_INVALID:
|
||||
@ -711,47 +608,23 @@ class OVSDVRNeutronAgent(object):
|
||||
# ports available on this agent anymore
|
||||
self.local_dvr_map.pop(sub_uuid, None)
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
args = {'table': constants.DVR_PROCESS_VLAN,
|
||||
'dl_vlan': lvm.vlan}
|
||||
if ip_version == 4:
|
||||
args['proto'] = 'arp'
|
||||
args['nw_dst'] = subnet_info['gateway_ip']
|
||||
else:
|
||||
args['proto'] = 'icmp6'
|
||||
args['icmp_type'] = n_const.ICMPV6_TYPE_RA
|
||||
args['dl_src'] = subnet_info['gateway_mac']
|
||||
self.phys_br[physical_network].delete_flows(**args)
|
||||
|
||||
br = self.phys_br[physical_network]
|
||||
if network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
args = {'table': constants.DVR_PROCESS,
|
||||
'dl_vlan': lvm.vlan}
|
||||
if ip_version == 4:
|
||||
args['proto'] = 'arp'
|
||||
args['nw_dst'] = subnet_info['gateway_ip']
|
||||
else:
|
||||
args['proto'] = 'icmp6'
|
||||
args['icmp_type'] = n_const.ICMPV6_TYPE_RA
|
||||
args['dl_src'] = subnet_info['gateway_mac']
|
||||
self.tun_br.delete_flows(**args)
|
||||
br = self.tun_br
|
||||
if ip_version == 4:
|
||||
br.delete_dvr_process_ipv4(
|
||||
vlan_tag=lvm.vlan, gateway_ip=subnet_info['gateway_ip'])
|
||||
else:
|
||||
br.delete_dvr_process_ipv6(
|
||||
vlan_tag=lvm.vlan, gateway_mac=subnet_info['gateway_mac'])
|
||||
ovsport.remove_subnet(sub_uuid)
|
||||
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
self.phys_br[physical_network].delete_flows(
|
||||
table=constants.DVR_PROCESS_VLAN,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port.vif_mac)
|
||||
self.phys_br[physical_network].delete_flows(
|
||||
table=constants.DVR_PROCESS_VLAN,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_src=port.vif_mac)
|
||||
|
||||
br = self.phys_br[physical_network]
|
||||
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
self.tun_br.delete_flows(table=constants.DVR_PROCESS,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port.vif_mac)
|
||||
self.tun_br.delete_flows(table=constants.DVR_PROCESS,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_src=port.vif_mac)
|
||||
br = self.tun_br
|
||||
br.delete_dvr_process(vlan_tag=lvm.vlan, vif_mac=port.vif_mac)
|
||||
|
||||
# release port state
|
||||
self.local_ports.pop(port.vif_id, None)
|
||||
|
||||
@ -767,15 +640,13 @@ class OVSDVRNeutronAgent(object):
|
||||
continue
|
||||
ldm = self.local_dvr_map[sub_uuid]
|
||||
ldm.remove_compute_ofport(port.vif_id)
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
# first remove this vm port rule
|
||||
self.int_br.delete_flows(table=table_id,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=ovsport.get_mac())
|
||||
self.int_br.delete_dvr_to_src_mac(
|
||||
network_type=lvm.network_type,
|
||||
vlan_tag=vlan_to_use, dst_mac=ovsport.get_mac())
|
||||
# release port state
|
||||
self.local_ports.pop(port.vif_id, None)
|
||||
|
||||
@ -790,15 +661,13 @@ class OVSDVRNeutronAgent(object):
|
||||
return
|
||||
ldm = self.local_dvr_map[sub_uuid]
|
||||
ldm.set_csnat_ofport(constants.OFPORT_INVALID)
|
||||
table_id = constants.DVR_TO_SRC_MAC
|
||||
vlan_to_use = lvm.vlan
|
||||
if lvm.network_type == p_const.TYPE_VLAN:
|
||||
table_id = constants.DVR_TO_SRC_MAC_VLAN
|
||||
vlan_to_use = lvm.segmentation_id
|
||||
# then remove csnat port rule
|
||||
self.int_br.delete_flows(table=table_id,
|
||||
dl_vlan=vlan_to_use,
|
||||
dl_dst=ovsport.get_mac())
|
||||
self.int_br.delete_dvr_to_src_mac(
|
||||
network_type=lvm.network_type,
|
||||
vlan_tag=vlan_to_use, dst_mac=ovsport.get_mac())
|
||||
if not ldm.is_dvr_owned():
|
||||
# if not owned by DVR (only used for csnat), remove this
|
||||
# subnet state altogether
|
||||
|
@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2011 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -25,7 +24,6 @@ from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
from six import moves
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.common import polling
|
||||
from neutron.agent.common import utils
|
||||
@ -34,7 +32,6 @@ from neutron.agent.linux import ip_lib
|
||||
from neutron.agent import rpc as agent_rpc
|
||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
||||
from neutron.api.rpc.handlers import dvr_rpc
|
||||
from neutron.common import config as common_config
|
||||
from neutron.common import constants as q_const
|
||||
from neutron.common import exceptions
|
||||
from neutron.common import topics
|
||||
@ -49,11 +46,16 @@ from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
cfg.CONF.import_group('AGENT', 'neutron.plugins.openvswitch.common.config')
|
||||
cfg.CONF.import_group('OVS', 'neutron.plugins.openvswitch.common.config')
|
||||
|
||||
# A placeholder for dead vlans.
|
||||
DEAD_VLAN_TAG = p_const.MAX_VLAN_TAG + 1
|
||||
|
||||
|
||||
class _mac_mydialect(netaddr.mac_unix):
|
||||
word_fmt = '%.2x'
|
||||
|
||||
|
||||
class DeviceListRetrievalError(exceptions.NeutronException):
|
||||
message = _("Unable to retrieve port details for devices: %(devices)s "
|
||||
"because of error: %(error)s")
|
||||
@ -119,7 +121,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
# 1.2 Support DVR (Distributed Virtual Router) RPC
|
||||
target = oslo_messaging.Target(version='1.2')
|
||||
|
||||
def __init__(self, integ_br, tun_br, local_ip,
|
||||
def __init__(self, bridge_classes, integ_br, tun_br, local_ip,
|
||||
bridge_mappings, polling_interval, tunnel_types=None,
|
||||
veth_mtu=None, l2_population=False,
|
||||
enable_distributed_routing=False,
|
||||
@ -132,6 +134,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
quitting_rpc_timeout=None):
|
||||
'''Constructor.
|
||||
|
||||
:param bridge_classes: a dict for bridge classes.
|
||||
:param integ_br: name of the integration bridge.
|
||||
:param tun_br: name of the tunnel bridge.
|
||||
:param local_ip: local IP address of this hypervisor.
|
||||
@ -159,6 +162,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
SIGTERM is received
|
||||
'''
|
||||
super(OVSNeutronAgent, self).__init__()
|
||||
self.br_int_cls = bridge_classes['br_int']
|
||||
self.br_phys_cls = bridge_classes['br_phys']
|
||||
self.br_tun_cls = bridge_classes['br_tun']
|
||||
self.use_veth_interconnection = use_veth_interconnection
|
||||
self.veth_mtu = veth_mtu
|
||||
self.available_local_vlans = set(moves.range(p_const.MIN_VLAN_TAG,
|
||||
@ -197,7 +203,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
# Keep track of int_br's device count for use by _report_state()
|
||||
self.int_br_device_count = 0
|
||||
|
||||
self.int_br = ovs_lib.OVSBridge(integ_br)
|
||||
self.int_br = self.br_int_cls(integ_br)
|
||||
self.setup_integration_br()
|
||||
# Stores port update notifications for processing in main rpc loop
|
||||
self.updated_ports = set()
|
||||
@ -440,21 +446,16 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
||||
if port_info == q_const.FLOODING_ENTRY:
|
||||
lvm.tun_ofports.add(ofport)
|
||||
ofports = _ofport_set_to_str(lvm.tun_ofports)
|
||||
br.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=lvm.vlan,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(lvm.segmentation_id, ofports))
|
||||
br.install_flood_to_tun(lvm.vlan, lvm.segmentation_id,
|
||||
lvm.tun_ofports)
|
||||
else:
|
||||
self.setup_entry_for_arp_reply(br, 'add', lvm.vlan,
|
||||
port_info.mac_address,
|
||||
port_info.ip_address)
|
||||
br.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=2,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port_info.mac_address,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(lvm.segmentation_id, ofport))
|
||||
br.install_unicast_to_tun(lvm.vlan,
|
||||
lvm.segmentation_id,
|
||||
ofport,
|
||||
port_info.mac_address)
|
||||
|
||||
def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
||||
if port_info == q_const.FLOODING_ENTRY:
|
||||
@ -463,21 +464,16 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
return
|
||||
lvm.tun_ofports.remove(ofport)
|
||||
if len(lvm.tun_ofports) > 0:
|
||||
ofports = _ofport_set_to_str(lvm.tun_ofports)
|
||||
br.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=lvm.vlan,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(lvm.segmentation_id, ofports))
|
||||
br.install_flood_to_tun(lvm.vlan, lvm.segmentation_id,
|
||||
lvm.tun_ofports)
|
||||
else:
|
||||
# This local vlan doesn't require any more tunnelling
|
||||
br.delete_flows(table=constants.FLOOD_TO_TUN, dl_vlan=lvm.vlan)
|
||||
br.delete_flood_to_tun(lvm.vlan)
|
||||
else:
|
||||
self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan,
|
||||
port_info.mac_address,
|
||||
port_info.ip_address)
|
||||
br.delete_flows(table=constants.UCAST_TO_TUN,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port_info.mac_address)
|
||||
br.delete_unicast_to_tun(lvm.vlan, port_info.mac_address)
|
||||
|
||||
def _fdb_chg_ip(self, context, fdb_entries):
|
||||
LOG.debug("update chg_ip received")
|
||||
@ -496,25 +492,39 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
if not self.arp_responder_enabled:
|
||||
return
|
||||
|
||||
mac = netaddr.EUI(mac_address, dialect=netaddr.mac_unix)
|
||||
ip = netaddr.IPAddress(ip_address)
|
||||
mac = str(netaddr.EUI(mac_address, dialect=_mac_mydialect))
|
||||
ip = str(netaddr.IPAddress(ip_address))
|
||||
|
||||
if action == 'add':
|
||||
actions = constants.ARP_RESPONDER_ACTIONS % {'mac': mac, 'ip': ip}
|
||||
br.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=1,
|
||||
proto='arp',
|
||||
dl_vlan=local_vid,
|
||||
nw_dst='%s' % ip,
|
||||
actions=actions)
|
||||
br.install_arp_responder(local_vid, ip, mac)
|
||||
elif action == 'remove':
|
||||
br.delete_flows(table=constants.ARP_RESPONDER,
|
||||
proto='arp',
|
||||
dl_vlan=local_vid,
|
||||
nw_dst='%s' % ip)
|
||||
br.delete_arp_responder(local_vid, ip)
|
||||
else:
|
||||
LOG.warning(_LW('Action %s not supported'), action)
|
||||
|
||||
def _local_vlan_for_flat(self, lvid, physical_network):
|
||||
phys_br = self.phys_brs[physical_network]
|
||||
phys_port = self.phys_ofports[physical_network]
|
||||
int_br = self.int_br
|
||||
int_port = self.int_ofports[physical_network]
|
||||
phys_br.provision_local_vlan(port=phys_port, lvid=lvid,
|
||||
segmentation_id=None,
|
||||
distributed=False)
|
||||
int_br.provision_local_vlan(port=int_port, lvid=lvid,
|
||||
segmentation_id=None)
|
||||
|
||||
def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id):
|
||||
distributed = self.enable_distributed_routing
|
||||
phys_br = self.phys_brs[physical_network]
|
||||
phys_port = self.phys_ofports[physical_network]
|
||||
int_br = self.int_br
|
||||
int_port = self.int_ofports[physical_network]
|
||||
phys_br.provision_local_vlan(port=phys_port, lvid=lvid,
|
||||
segmentation_id=segmentation_id,
|
||||
distributed=distributed)
|
||||
int_br.provision_local_vlan(port=int_port, lvid=lvid,
|
||||
segmentation_id=segmentation_id)
|
||||
|
||||
def provision_local_vlan(self, net_uuid, network_type, physical_network,
|
||||
segmentation_id, local_vlan=None):
|
||||
'''Provisions a local VLAN.
|
||||
@ -554,28 +564,20 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
if network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
if self.enable_tunneling:
|
||||
# outbound broadcast/multicast
|
||||
ofports = _ofport_set_to_str(
|
||||
self.tun_br_ofports[network_type].values())
|
||||
ofports = self.tun_br_ofports[network_type].values()
|
||||
if ofports:
|
||||
self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=lvid,
|
||||
actions="strip_vlan,"
|
||||
"set_tunnel:%s,output:%s" %
|
||||
(segmentation_id, ofports))
|
||||
self.tun_br.install_flood_to_tun(lvid,
|
||||
segmentation_id,
|
||||
ofports)
|
||||
# inbound from tunnels: set lvid in the right table
|
||||
# and resubmit to Table LEARN_FROM_TUN for mac learning
|
||||
if self.enable_distributed_routing:
|
||||
self.dvr_agent.process_tunneled_network(
|
||||
network_type, lvid, segmentation_id)
|
||||
else:
|
||||
self.tun_br.add_flow(
|
||||
table=constants.TUN_TABLE[network_type],
|
||||
priority=1,
|
||||
tun_id=segmentation_id,
|
||||
actions="mod_vlan_vid:%s,"
|
||||
"resubmit(,%s)" %
|
||||
(lvid, constants.LEARN_FROM_TUN))
|
||||
|
||||
self.tun_br.provision_local_vlan(
|
||||
network_type=network_type, lvid=lvid,
|
||||
segmentation_id=segmentation_id)
|
||||
else:
|
||||
LOG.error(_LE("Cannot provision %(network_type)s network for "
|
||||
"net-id=%(net_uuid)s - tunneling disabled"),
|
||||
@ -583,18 +585,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
'net_uuid': net_uuid})
|
||||
elif network_type == p_const.TYPE_FLAT:
|
||||
if physical_network in self.phys_brs:
|
||||
# outbound
|
||||
br = self.phys_brs[physical_network]
|
||||
br.add_flow(priority=4,
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
dl_vlan=lvid,
|
||||
actions="strip_vlan,normal")
|
||||
# inbound
|
||||
self.int_br.add_flow(
|
||||
priority=3,
|
||||
in_port=self.int_ofports[physical_network],
|
||||
dl_vlan=0xffff,
|
||||
actions="mod_vlan_vid:%s,normal" % lvid)
|
||||
self._local_vlan_for_flat(lvid, physical_network)
|
||||
else:
|
||||
LOG.error(_LE("Cannot provision flat network for "
|
||||
"net-id=%(net_uuid)s - no bridge for "
|
||||
@ -603,26 +594,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
'physical_network': physical_network})
|
||||
elif network_type == p_const.TYPE_VLAN:
|
||||
if physical_network in self.phys_brs:
|
||||
# outbound
|
||||
br = self.phys_brs[physical_network]
|
||||
if self.enable_distributed_routing:
|
||||
br.add_flow(table=constants.LOCAL_VLAN_TRANSLATION,
|
||||
priority=4,
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
dl_vlan=lvid,
|
||||
actions="mod_vlan_vid:%s,normal" % segmentation_id)
|
||||
else:
|
||||
br.add_flow(priority=4,
|
||||
in_port=self.phys_ofports[physical_network],
|
||||
dl_vlan=lvid,
|
||||
actions="mod_vlan_vid:%s,normal" % segmentation_id)
|
||||
|
||||
# inbound
|
||||
self.int_br.add_flow(priority=3,
|
||||
in_port=self.
|
||||
int_ofports[physical_network],
|
||||
dl_vlan=segmentation_id,
|
||||
actions="mod_vlan_vid:%s,normal" % lvid)
|
||||
self._local_vlan_for_vlan(lvid, physical_network,
|
||||
segmentation_id)
|
||||
else:
|
||||
LOG.error(_LE("Cannot provision VLAN network for "
|
||||
"net-id=%(net_uuid)s - no bridge for "
|
||||
@ -654,10 +627,12 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
|
||||
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
if self.enable_tunneling:
|
||||
self.tun_br.delete_flows(
|
||||
table=constants.TUN_TABLE[lvm.network_type],
|
||||
tun_id=lvm.segmentation_id)
|
||||
self.tun_br.delete_flows(dl_vlan=lvm.vlan)
|
||||
self.tun_br.reclaim_local_vlan(
|
||||
network_type=lvm.network_type,
|
||||
segmentation_id=lvm.segmentation_id)
|
||||
self.tun_br.delete_flood_to_tun(lvm.vlan)
|
||||
self.tun_br.delete_unicast_to_tun(lvm.vlan, None)
|
||||
self.tun_br.delete_arp_responder(lvm.vlan, None)
|
||||
if self.l2_pop:
|
||||
# Try to remove tunnel ports if not used by other networks
|
||||
for ofport in lvm.tun_ofports:
|
||||
@ -667,24 +642,26 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
if lvm.physical_network in self.phys_brs:
|
||||
# outbound
|
||||
br = self.phys_brs[lvm.physical_network]
|
||||
br.delete_flows(in_port=self.phys_ofports[lvm.
|
||||
physical_network],
|
||||
dl_vlan=lvm.vlan)
|
||||
br.reclaim_local_vlan(
|
||||
port=self.phys_ofports[lvm.physical_network],
|
||||
lvid=lvm.vlan)
|
||||
# inbound
|
||||
br = self.int_br
|
||||
br.delete_flows(in_port=self.int_ofports[lvm.physical_network],
|
||||
dl_vlan=0xffff)
|
||||
br.reclaim_local_vlan(
|
||||
port=self.int_ofports[lvm.physical_network],
|
||||
segmentation_id=None)
|
||||
elif lvm.network_type == p_const.TYPE_VLAN:
|
||||
if lvm.physical_network in self.phys_brs:
|
||||
# outbound
|
||||
br = self.phys_brs[lvm.physical_network]
|
||||
br.delete_flows(in_port=self.phys_ofports[lvm.
|
||||
physical_network],
|
||||
dl_vlan=lvm.vlan)
|
||||
br.reclaim_local_vlan(
|
||||
port=self.phys_ofports[lvm.physical_network],
|
||||
lvid=lvm.vlan)
|
||||
# inbound
|
||||
br = self.int_br
|
||||
br.delete_flows(in_port=self.int_ofports[lvm.physical_network],
|
||||
dl_vlan=lvm.segmentation_id)
|
||||
br.reclaim_local_vlan(
|
||||
port=self.int_ofports[lvm.physical_network],
|
||||
segmentation_id=lvm.segmentation_id)
|
||||
elif lvm.network_type == p_const.TYPE_LOCAL:
|
||||
# no flows needed for local networks
|
||||
pass
|
||||
@ -746,6 +723,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
self.int_br.set_db_attribute(
|
||||
"Port", port.port_name, "tag", lvm.vlan)
|
||||
if port.ofport != -1:
|
||||
# NOTE(yamamoto): Remove possible drop_port flow
|
||||
# installed by port_dead.
|
||||
self.int_br.delete_flows(in_port=port.ofport)
|
||||
|
||||
# update plugin about port status
|
||||
@ -766,43 +745,22 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
@staticmethod
|
||||
def setup_arp_spoofing_protection(bridge, vif, port_details):
|
||||
# clear any previous flows related to this port in our ARP table
|
||||
bridge.delete_flows(table=constants.LOCAL_SWITCHING,
|
||||
in_port=vif.ofport, proto='arp')
|
||||
bridge.delete_flows(table=constants.ARP_SPOOF_TABLE,
|
||||
in_port=vif.ofport)
|
||||
bridge.delete_arp_spoofing_protection(port=vif.ofport)
|
||||
if not port_details.get('port_security_enabled', True):
|
||||
LOG.info(_LI("Skipping ARP spoofing rules for port '%s' because "
|
||||
"it has port security disabled"), vif.port_name)
|
||||
return
|
||||
# all of the rules here are based on 'in_port' match criteria
|
||||
# so their cleanup will be handled by 'update_stale_ofport_rules'
|
||||
|
||||
# collect all of the addresses and cidrs that belong to the port
|
||||
addresses = [f['ip_address'] for f in port_details['fixed_ips']]
|
||||
addresses = {f['ip_address'] for f in port_details['fixed_ips']}
|
||||
if port_details.get('allowed_address_pairs'):
|
||||
addresses += [p['ip_address']
|
||||
for p in port_details['allowed_address_pairs']]
|
||||
addresses |= {p['ip_address']
|
||||
for p in port_details['allowed_address_pairs']}
|
||||
|
||||
# allow ARPs as long as they match addresses that actually
|
||||
# belong to the port.
|
||||
for ip in addresses:
|
||||
if netaddr.IPNetwork(ip).version != 4:
|
||||
continue
|
||||
bridge.add_flow(table=constants.ARP_SPOOF_TABLE, priority=2,
|
||||
proto='arp', arp_spa=ip, in_port=vif.ofport,
|
||||
actions="NORMAL")
|
||||
addresses = {ip for ip in addresses
|
||||
if netaddr.IPNetwork(ip).version == 4}
|
||||
|
||||
# drop any ARPs in this table that aren't explicitly allowed
|
||||
bridge.add_flow(table=constants.ARP_SPOOF_TABLE, priority=1,
|
||||
proto='arp', actions="DROP")
|
||||
|
||||
# Now that the rules are ready, direct ARP traffic from the port into
|
||||
# the anti-spoof table.
|
||||
# This strategy fails gracefully because OVS versions that can't match
|
||||
# on ARP headers will just process traffic normally.
|
||||
bridge.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=10, proto='arp', in_port=vif.ofport,
|
||||
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
|
||||
bridge.install_arp_spoofing_protection(port=vif.ofport,
|
||||
ip_addresses=addresses)
|
||||
|
||||
def port_unbound(self, vif_id, net_uuid=None):
|
||||
'''Unbind port.
|
||||
@ -841,8 +799,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
if cur_tag != DEAD_VLAN_TAG:
|
||||
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
||||
DEAD_VLAN_TAG)
|
||||
self.int_br.add_flow(priority=2, in_port=port.ofport,
|
||||
actions="drop")
|
||||
self.int_br.drop_port(in_port=port.ofport)
|
||||
|
||||
def setup_integration_br(self):
|
||||
'''Setup the integration bridge.
|
||||
@ -855,14 +812,11 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
# which does nothing if bridge already exists.
|
||||
self.int_br.create()
|
||||
self.int_br.set_secure_mode()
|
||||
self.int_br.setup_controllers(cfg.CONF)
|
||||
|
||||
self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port)
|
||||
self.int_br.remove_all_flows()
|
||||
# switch all traffic using L2 learning
|
||||
self.int_br.add_flow(priority=1, actions="normal")
|
||||
# Add a canary flow to int_br to track OVS restarts
|
||||
self.int_br.add_flow(table=constants.CANARY_TABLE, priority=0,
|
||||
actions="drop")
|
||||
|
||||
self.int_br.setup_default_table()
|
||||
|
||||
def setup_ancillary_bridges(self, integ_br, tun_br):
|
||||
'''Setup ancillary bridges - for example br-ex.'''
|
||||
@ -899,9 +853,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
:param tun_br_name: the name of the tunnel bridge.
|
||||
'''
|
||||
if not self.tun_br:
|
||||
self.tun_br = ovs_lib.OVSBridge(tun_br_name)
|
||||
self.tun_br = self.br_tun_cls(tun_br_name)
|
||||
|
||||
self.tun_br.reset_bridge(secure_mode=True)
|
||||
self.tun_br.setup_controllers(cfg.CONF)
|
||||
self.patch_tun_ofport = self.int_br.add_patch_port(
|
||||
cfg.CONF.OVS.int_peer_patch_port, cfg.CONF.OVS.tun_peer_patch_port)
|
||||
self.patch_int_ofport = self.tun_br.add_patch_port(
|
||||
@ -913,83 +868,15 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
"version of OVS does not support tunnels or patch "
|
||||
"ports. Agent terminated!"))
|
||||
exit(1)
|
||||
self.tun_br.remove_all_flows()
|
||||
self.tun_br.delete_flows()
|
||||
|
||||
def setup_tunnel_br(self):
|
||||
'''Setup the tunnel bridge.
|
||||
|
||||
Add all flows to the tunnel bridge.
|
||||
'''
|
||||
# Table 0 (default) will sort incoming traffic depending on in_port
|
||||
self.tun_br.add_flow(priority=1,
|
||||
in_port=self.patch_int_ofport,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN)
|
||||
self.tun_br.add_flow(priority=0, actions="drop")
|
||||
if self.arp_responder_enabled:
|
||||
# ARP broadcast-ed request go to the local ARP_RESPONDER table to
|
||||
# be locally resolved
|
||||
self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=1,
|
||||
proto='arp',
|
||||
dl_dst="ff:ff:ff:ff:ff:ff",
|
||||
actions=("resubmit(,%s)" %
|
||||
constants.ARP_RESPONDER))
|
||||
# PATCH_LV_TO_TUN table will handle packets coming from patch_int
|
||||
# unicasts go to table UCAST_TO_TUN where remote addresses are learnt
|
||||
self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
dl_dst="00:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions="resubmit(,%s)" % constants.UCAST_TO_TUN)
|
||||
# Broadcasts/multicasts go to table FLOOD_TO_TUN that handles flooding
|
||||
self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions="resubmit(,%s)" % constants.FLOOD_TO_TUN)
|
||||
# Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id
|
||||
# for each tunnel type, and resubmit to table LEARN_FROM_TUN where
|
||||
# remote mac addresses will be learnt
|
||||
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
self.tun_br.add_flow(table=constants.TUN_TABLE[tunnel_type],
|
||||
priority=0,
|
||||
actions="drop")
|
||||
# LEARN_FROM_TUN table will have a single flow using a learn action to
|
||||
# dynamically set-up flows in UCAST_TO_TUN corresponding to remote mac
|
||||
# addresses (assumes that lvid has already been set by a previous flow)
|
||||
learned_flow = ("table=%s,"
|
||||
"priority=1,"
|
||||
"hard_timeout=300,"
|
||||
"NXM_OF_VLAN_TCI[0..11],"
|
||||
"NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
|
||||
"load:0->NXM_OF_VLAN_TCI[],"
|
||||
"load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
|
||||
"output:NXM_OF_IN_PORT[]" %
|
||||
constants.UCAST_TO_TUN)
|
||||
# Once remote mac addresses are learnt, output packet to patch_int
|
||||
self.tun_br.add_flow(table=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions="learn(%s),output:%s" %
|
||||
(learned_flow, self.patch_int_ofport))
|
||||
# Egress unicast will be handled in table UCAST_TO_TUN, where remote
|
||||
# mac addresses will be learned. For now, just add a default flow that
|
||||
# will resubmit unknown unicasts to table FLOOD_TO_TUN to treat them
|
||||
# as broadcasts/multicasts
|
||||
self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN)
|
||||
if self.arp_responder_enabled:
|
||||
# If none of the ARP entries correspond to the requested IP, the
|
||||
# broadcast-ed packet is resubmitted to the flooding table
|
||||
self.tun_br.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN)
|
||||
# FLOOD_TO_TUN will handle flooding in tunnels based on lvid,
|
||||
# for now, add a default drop action
|
||||
self.tun_br.add_flow(table=constants.FLOOD_TO_TUN,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
self.tun_br.setup_default_table(self.patch_int_ofport,
|
||||
self.arp_responder_enabled)
|
||||
|
||||
def get_peer_name(self, prefix, name):
|
||||
"""Construct a peer name based on the prefix and name.
|
||||
@ -1041,9 +928,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
{'physical_network': physical_network,
|
||||
'bridge': bridge})
|
||||
sys.exit(1)
|
||||
br = ovs_lib.OVSBridge(bridge)
|
||||
br.remove_all_flows()
|
||||
br.add_flow(priority=1, actions="normal")
|
||||
br = self.br_phys_cls(bridge)
|
||||
br.setup_controllers(cfg.CONF)
|
||||
br.setup_default_table()
|
||||
self.phys_brs[physical_network] = br
|
||||
|
||||
# interconnect physical and integration bridges using veth/patchs
|
||||
@ -1076,9 +963,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
self.phys_ofports[physical_network] = phys_ofport
|
||||
|
||||
# block all untranslated traffic between bridges
|
||||
self.int_br.add_flow(priority=2, in_port=int_ofport,
|
||||
actions="drop")
|
||||
br.add_flow(priority=2, in_port=phys_ofport, actions="drop")
|
||||
self.int_br.drop_port(in_port=int_ofport)
|
||||
br.drop_port(in_port=phys_ofport)
|
||||
|
||||
if self.use_veth_interconnection:
|
||||
# enable veth to pass traffic
|
||||
@ -1114,7 +1000,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
# delete any stale rules based on removed ofports
|
||||
ofports_deleted = set(previous.values()) - set(current.values())
|
||||
for ofport in ofports_deleted:
|
||||
self.int_br.delete_flows(in_port=ofport)
|
||||
self.int_br.delete_arp_spoofing_protection(port=ofport)
|
||||
|
||||
# store map for next iteration
|
||||
self.vifname_to_ofport_map = current
|
||||
@ -1237,20 +1123,16 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
self.tun_br_ofports[tunnel_type][remote_ip] = ofport
|
||||
# Add flow in default table to resubmit to the right
|
||||
# tunnelling table (lvid will be set in the latter)
|
||||
br.add_flow(priority=1,
|
||||
in_port=ofport,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.TUN_TABLE[tunnel_type])
|
||||
br.setup_tunnel_port(tunnel_type, ofport)
|
||||
|
||||
ofports = _ofport_set_to_str(self.tun_br_ofports[tunnel_type].values())
|
||||
ofports = self.tun_br_ofports[tunnel_type].values()
|
||||
if ofports and not self.l2_pop:
|
||||
# Update flooding flows to include the new tunnel
|
||||
for vlan_mapping in list(self.local_vlan_map.values()):
|
||||
if vlan_mapping.network_type == tunnel_type:
|
||||
br.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=vlan_mapping.vlan,
|
||||
actions="strip_vlan,set_tunnel:%s,output:%s" %
|
||||
(vlan_mapping.segmentation_id, ofports))
|
||||
br.install_flood_to_tun(vlan_mapping.vlan,
|
||||
vlan_mapping.segmentation_id,
|
||||
ofports)
|
||||
return ofport
|
||||
|
||||
def setup_tunnel_port(self, br, remote_ip, network_type):
|
||||
@ -1276,7 +1158,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
port_name = '%s-%s' % (tunnel_type,
|
||||
self.get_ip_in_hex(remote_ip))
|
||||
br.delete_port(port_name)
|
||||
br.delete_flows(in_port=ofport)
|
||||
br.cleanup_tunnel_port(ofport)
|
||||
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
|
||||
|
||||
def treat_devices_added_or_updated(self, devices, ovs_restarted):
|
||||
@ -1521,18 +1403,14 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
|
||||
def check_ovs_status(self):
|
||||
# Check for the canary flow
|
||||
canary_flow = self.int_br.dump_flows_for_table(constants.CANARY_TABLE)
|
||||
if canary_flow == '':
|
||||
status = self.int_br.check_canary_table()
|
||||
if status == constants.OVS_RESTARTED:
|
||||
LOG.warn(_LW("OVS is restarted. OVSNeutronAgent will reset "
|
||||
"bridges and recover ports."))
|
||||
return constants.OVS_RESTARTED
|
||||
elif canary_flow is None:
|
||||
elif status == constants.OVS_DEAD:
|
||||
LOG.warn(_LW("OVS is dead. OVSNeutronAgent will keep running "
|
||||
"and checking OVS status periodically."))
|
||||
return constants.OVS_DEAD
|
||||
else:
|
||||
# OVS is in normal status
|
||||
return constants.OVS_NORMAL
|
||||
return status
|
||||
|
||||
def loop_count_and_wait(self, start_time, port_stats):
|
||||
# sleep till end of polling interval
|
||||
@ -1688,6 +1566,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
self.loop_count_and_wait(start, port_stats)
|
||||
|
||||
def daemon_loop(self):
|
||||
# Start everything.
|
||||
LOG.info(_LI("Agent initialized successfully, now running... "))
|
||||
signal.signal(signal.SIGTERM, self._handle_sigterm)
|
||||
with polling.get_polling_manager(
|
||||
self.minimize_polling,
|
||||
self.ovsdb_monitor_respawn_interval) as pm:
|
||||
@ -1713,10 +1594,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
"Agent and Server side."))
|
||||
|
||||
|
||||
def _ofport_set_to_str(ofport_set):
|
||||
return ",".join(map(str, ofport_set))
|
||||
|
||||
|
||||
def create_agent_config_map(config):
|
||||
"""Create a map of agent config parameters.
|
||||
|
||||
@ -1757,36 +1634,25 @@ def create_agent_config_map(config):
|
||||
return kwargs
|
||||
|
||||
|
||||
def main():
|
||||
cfg.CONF.register_opts(ip_lib.OPTS)
|
||||
config.register_root_helper(cfg.CONF)
|
||||
common_config.init(sys.argv[1:])
|
||||
common_config.setup_logging()
|
||||
q_utils.log_opt_values(LOG)
|
||||
|
||||
try:
|
||||
agent_config = create_agent_config_map(cfg.CONF)
|
||||
except ValueError as e:
|
||||
LOG.error(_LE('%s Agent terminated!'), e)
|
||||
sys.exit(1)
|
||||
|
||||
def prepare_xen_compute():
|
||||
is_xen_compute_host = 'rootwrap-xen-dom0' in cfg.CONF.AGENT.root_helper
|
||||
if is_xen_compute_host:
|
||||
# Force ip_lib to always use the root helper to ensure that ip
|
||||
# commands target xen dom0 rather than domU.
|
||||
cfg.CONF.register_opts(ip_lib.OPTS)
|
||||
cfg.CONF.set_default('ip_lib_force_root', True)
|
||||
|
||||
|
||||
def main(bridge_classes):
|
||||
try:
|
||||
agent = OVSNeutronAgent(**agent_config)
|
||||
agent_config = create_agent_config_map(cfg.CONF)
|
||||
except ValueError:
|
||||
LOG.exception(_LE("Agent failed to create agent config map"))
|
||||
raise SystemExit(1)
|
||||
prepare_xen_compute()
|
||||
try:
|
||||
agent = OVSNeutronAgent(bridge_classes, **agent_config)
|
||||
except RuntimeError as e:
|
||||
LOG.error(_LE("%s Agent terminated!"), e)
|
||||
sys.exit(1)
|
||||
signal.signal(signal.SIGTERM, agent._handle_sigterm)
|
||||
|
||||
# Start everything.
|
||||
LOG.info(_LI("Agent initialized successfully, now running... "))
|
||||
agent.daemon_loop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -44,6 +44,8 @@ ovs_opts = [
|
||||
cfg.BoolOpt('use_veth_interconnection', default=False,
|
||||
help=_("Use veths instead of patch ports to interconnect the "
|
||||
"integration bridge to physical bridges.")),
|
||||
cfg.StrOpt('of_interface', default='ovs-ofctl', choices=['ovs-ofctl'],
|
||||
help=_("OpenFlow interface to use.")),
|
||||
]
|
||||
|
||||
agent_opts = [
|
||||
|
@ -13,9 +13,16 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import eventlet
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.cmd.sanity import checks
|
||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent as ovsagt
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
from neutron.tests.common import machine_fixtures
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional.agent import test_ovs_lib
|
||||
@ -23,16 +30,68 @@ from neutron.tests.functional import base
|
||||
from neutron.tests import tools
|
||||
|
||||
|
||||
class ARPSpoofTestCase(test_ovs_lib.OVSBridgeTestBase,
|
||||
base.BaseSudoTestCase):
|
||||
cfg.CONF.import_group('OVS', 'neutron.plugins.openvswitch.common.config')
|
||||
|
||||
|
||||
class _OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase,
|
||||
base.BaseSudoTestCase):
|
||||
def setUp(self):
|
||||
super(_OVSAgentTestBase, self).setUp()
|
||||
self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
|
||||
self.of_interface_mod = importutils.import_module(self._MAIN_MODULE)
|
||||
self.br_int_cls = None
|
||||
self.br_tun_cls = None
|
||||
self.br_phys_cls = None
|
||||
self.br_int = None
|
||||
self.init_done = False
|
||||
self.init_done_ev = eventlet.event.Event()
|
||||
self._main_thread = eventlet.spawn(self._kick_main)
|
||||
self.addCleanup(self._kill_main)
|
||||
|
||||
# Wait for _kick_main -> of_interface main -> _agent_main
|
||||
# NOTE(yamamoto): This complexity came from how "native" of_interface
|
||||
# runs its openflow controller. "native" of_interface's main routine
|
||||
# blocks while running the embedded openflow controller. In that case,
|
||||
# the agent rpc_loop runs in another thread. However, for FT we need
|
||||
# to run setUp() and test_xxx() in the same thread. So I made this
|
||||
# run of_interface's main in a separate thread instead.
|
||||
while not self.init_done:
|
||||
self.init_done_ev.wait()
|
||||
|
||||
def _kick_main(self):
|
||||
with mock.patch.object(ovsagt, 'main', self._agent_main):
|
||||
self.of_interface_mod.main()
|
||||
|
||||
def _kill_main(self):
|
||||
self._main_thread.kill()
|
||||
self._main_thread.wait()
|
||||
|
||||
def _agent_main(self, bridge_classes):
|
||||
self.br_int_cls = bridge_classes['br_int']
|
||||
self.br_phys_cls = bridge_classes['br_phys']
|
||||
self.br_tun_cls = bridge_classes['br_tun']
|
||||
self.br_int = self.br_int_cls(self.br.br_name)
|
||||
self.br_int.set_secure_mode()
|
||||
self.br_int.setup_controllers(cfg.CONF)
|
||||
self.br_int.setup_default_table()
|
||||
|
||||
# signal to setUp()
|
||||
self.init_done = True
|
||||
self.init_done_ev.send()
|
||||
|
||||
|
||||
class _OVSAgentOFCtlTestBase(_OVSAgentTestBase):
|
||||
_MAIN_MODULE = 'neutron.plugins.openvswitch.agent.openflow.ovs_ofctl.main'
|
||||
|
||||
|
||||
class _ARPSpoofTestCase(object):
|
||||
def setUp(self):
|
||||
if not checks.arp_header_match_supported():
|
||||
self.skipTest("ARP header matching not supported")
|
||||
# NOTE(kevinbenton): it would be way cooler to use scapy for
|
||||
# these but scapy requires the python process to be running as
|
||||
# root to bind to the ports.
|
||||
super(ARPSpoofTestCase, self).setUp()
|
||||
super(_ARPSpoofTestCase, self).setUp()
|
||||
self.src_addr = '192.168.0.1'
|
||||
self.dst_addr = '192.168.0.2'
|
||||
self.src_namespace = self.useFixture(
|
||||
@ -120,4 +179,22 @@ class ARPSpoofTestCase(test_ovs_lib.OVSBridgeTestBase,
|
||||
'allowed_address_pairs': [
|
||||
dict(ip_address=ip) for ip in addrs]}
|
||||
ovsagt.OVSNeutronAgent.setup_arp_spoofing_protection(
|
||||
self.br, VifPort(), details)
|
||||
self.br_int, VifPort(), details)
|
||||
|
||||
|
||||
class ARPSpoofOFCtlTestCase(_ARPSpoofTestCase, _OVSAgentOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class _CanaryTableTestCase(object):
|
||||
def test_canary_table(self):
|
||||
self.br_int.delete_flows()
|
||||
self.assertEqual(constants.OVS_RESTARTED,
|
||||
self.br_int.check_canary_table())
|
||||
self.br_int.setup_canary_table()
|
||||
self.assertEqual(constants.OVS_NORMAL,
|
||||
self.br_int.check_canary_table())
|
||||
|
||||
|
||||
class CanaryTableOFCtlTestCase(_CanaryTableTestCase, _OVSAgentOFCtlTestBase):
|
||||
pass
|
||||
|
160
neutron/tests/unit/plugins/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py
Normal file
160
neutron/tests/unit/plugins/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py
Normal file
@ -0,0 +1,160 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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 neutron.tests.unit.plugins.openvswitch.agent import ovs_test_base
|
||||
|
||||
|
||||
call = mock.call # short hand
|
||||
|
||||
|
||||
class OVSBridgeTestBase(ovs_test_base.OVSOFCtlTestBase):
|
||||
def setup_bridge_mock(self, name, cls):
|
||||
self.br = cls(name)
|
||||
mock_add_flow = mock.patch.object(self.br, 'add_flow').start()
|
||||
mock_mod_flow = mock.patch.object(self.br, 'mod_flow').start()
|
||||
mock_delete_flows = mock.patch.object(self.br, 'delete_flows').start()
|
||||
self.mock = mock.Mock()
|
||||
self.mock.attach_mock(mock_add_flow, 'add_flow')
|
||||
self.mock.attach_mock(mock_mod_flow, 'mod_flow')
|
||||
self.mock.attach_mock(mock_delete_flows, 'delete_flows')
|
||||
|
||||
def test_drop_port(self):
|
||||
in_port = 2345
|
||||
self.br.drop_port(in_port=in_port)
|
||||
expected = [
|
||||
call.add_flow(priority=2, table=0, actions='drop',
|
||||
in_port=in_port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_goto(self):
|
||||
dest_table_id = 123
|
||||
priority = 99
|
||||
in_port = 666
|
||||
self.br.install_goto(dest_table_id=dest_table_id,
|
||||
priority=priority, in_port=in_port)
|
||||
expected = [
|
||||
call.add_flow(priority=priority, table=0,
|
||||
actions='resubmit(,%s)' % dest_table_id,
|
||||
in_port=in_port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_drop(self):
|
||||
priority = 99
|
||||
in_port = 666
|
||||
self.br.install_drop(priority=priority, in_port=in_port)
|
||||
expected = [
|
||||
call.add_flow(priority=priority, table=0,
|
||||
actions='drop',
|
||||
in_port=in_port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_normal(self):
|
||||
priority = 99
|
||||
in_port = 666
|
||||
self.br.install_normal(priority=priority, in_port=in_port)
|
||||
expected = [
|
||||
call.add_flow(priority=priority, table=0,
|
||||
actions='normal',
|
||||
in_port=in_port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
|
||||
class OVSDVRProcessTestMixin(object):
|
||||
def test_install_dvr_process_ipv4(self):
|
||||
vlan_tag = 999
|
||||
gateway_ip = '192.0.2.1'
|
||||
self.br.install_dvr_process_ipv4(vlan_tag=vlan_tag,
|
||||
gateway_ip=gateway_ip)
|
||||
expected = [
|
||||
call.add_flow(table=self.dvr_process_table_id,
|
||||
proto='arp', nw_dst=gateway_ip, actions='drop',
|
||||
priority=3, dl_vlan=vlan_tag),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_dvr_process_ipv4(self):
|
||||
vlan_tag = 999
|
||||
gateway_ip = '192.0.2.1'
|
||||
self.br.delete_dvr_process_ipv4(vlan_tag=vlan_tag,
|
||||
gateway_ip=gateway_ip)
|
||||
expected = [
|
||||
call.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag, proto='arp',
|
||||
nw_dst=gateway_ip),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_dvr_process_ipv6(self):
|
||||
vlan_tag = 999
|
||||
gateway_mac = '08:60:6e:7f:74:e7'
|
||||
self.br.install_dvr_process_ipv6(vlan_tag=vlan_tag,
|
||||
gateway_mac=gateway_mac)
|
||||
expected = [
|
||||
call.add_flow(table=self.dvr_process_table_id,
|
||||
proto='icmp6', dl_src=gateway_mac, actions='drop',
|
||||
priority=3, dl_vlan=vlan_tag),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_dvr_process_ipv6(self):
|
||||
vlan_tag = 999
|
||||
gateway_mac = '08:60:6e:7f:74:e7'
|
||||
self.br.delete_dvr_process_ipv6(vlan_tag=vlan_tag,
|
||||
gateway_mac=gateway_mac)
|
||||
expected = [
|
||||
call.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag, dl_src=gateway_mac,
|
||||
proto='icmp6'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_dvr_process(self):
|
||||
vlan_tag = 999
|
||||
vif_mac = '00:0e:0c:5e:95:d0'
|
||||
dvr_mac_address = 'f2:0b:a4:5b:b2:ab'
|
||||
self.br.install_dvr_process(vlan_tag=vlan_tag,
|
||||
vif_mac=vif_mac,
|
||||
dvr_mac_address=dvr_mac_address)
|
||||
expected = [
|
||||
call.add_flow(priority=2, table=self.dvr_process_table_id,
|
||||
dl_dst=vif_mac, dl_vlan=vlan_tag, actions='drop'),
|
||||
call.add_flow(priority=1, table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag, dl_src=vif_mac,
|
||||
actions='mod_dl_src:%(mac)s,resubmit(,%(next)s)' % {
|
||||
'mac': dvr_mac_address,
|
||||
'next': self.dvr_process_next_table_id,
|
||||
}),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_dvr_process(self):
|
||||
vlan_tag = 999
|
||||
vif_mac = '00:0e:0c:5e:95:d0'
|
||||
self.br.delete_dvr_process(vlan_tag=vlan_tag,
|
||||
vif_mac=vif_mac)
|
||||
expected = [
|
||||
call.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_dst=vif_mac, dl_vlan=vlan_tag),
|
||||
call.delete_flows(table=self.dvr_process_table_id,
|
||||
dl_vlan=vlan_tag, dl_src=vif_mac),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -0,0 +1,213 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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 neutron.tests.unit.plugins.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge_test_base
|
||||
|
||||
|
||||
call = mock.call # short hand
|
||||
|
||||
|
||||
class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
|
||||
def setUp(self):
|
||||
super(OVSIntegrationBridgeTest, self).setUp()
|
||||
self.setup_bridge_mock('br-int', self.br_int_cls)
|
||||
|
||||
def test_setup_default_table(self):
|
||||
self.br.setup_default_table()
|
||||
expected = [
|
||||
call.delete_flows(),
|
||||
call.add_flow(priority=0, table=0, actions='normal'),
|
||||
call.add_flow(priority=0, table=23, actions='drop'),
|
||||
call.add_flow(priority=0, table=24, actions='drop'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_provision_local_vlan(self):
|
||||
port = 999
|
||||
lvid = 888
|
||||
segmentation_id = 777
|
||||
self.br.provision_local_vlan(port=port, lvid=lvid,
|
||||
segmentation_id=segmentation_id)
|
||||
expected = [
|
||||
call.add_flow(priority=3, dl_vlan=segmentation_id,
|
||||
in_port=port,
|
||||
actions='mod_vlan_vid:%s,normal' % lvid),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_provision_local_vlan_novlan(self):
|
||||
port = 999
|
||||
lvid = 888
|
||||
segmentation_id = None
|
||||
self.br.provision_local_vlan(port=port, lvid=lvid,
|
||||
segmentation_id=segmentation_id)
|
||||
expected = [
|
||||
call.add_flow(priority=3, dl_vlan=0xffff,
|
||||
in_port=port,
|
||||
actions='mod_vlan_vid:%s,normal' % lvid),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_reclaim_local_vlan(self):
|
||||
port = 999
|
||||
segmentation_id = 777
|
||||
self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id)
|
||||
expected = [
|
||||
call.delete_flows(dl_vlan=segmentation_id, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_reclaim_local_vlan_novlan(self):
|
||||
port = 999
|
||||
segmentation_id = None
|
||||
self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id)
|
||||
expected = [
|
||||
call.delete_flows(dl_vlan=0xffff, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_dvr_to_src_mac(self):
|
||||
network_type = 'vxlan'
|
||||
vlan_tag = 1111
|
||||
gateway_mac = '08:60:6e:7f:74:e7'
|
||||
dst_mac = '00:02:b3:13:fe:3d'
|
||||
dst_port = 6666
|
||||
self.br.install_dvr_to_src_mac(network_type=network_type,
|
||||
vlan_tag=vlan_tag,
|
||||
gateway_mac=gateway_mac,
|
||||
dst_mac=dst_mac,
|
||||
dst_port=dst_port)
|
||||
expected = [
|
||||
call.add_flow(priority=4, table=1, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag,
|
||||
actions='strip_vlan,mod_dl_src:%(mac)s,'
|
||||
'output:%(port)s' % {
|
||||
'mac': gateway_mac,
|
||||
'port': dst_port,
|
||||
}),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_dvr_to_src_mac(self):
|
||||
network_type = 'vxlan'
|
||||
vlan_tag = 1111
|
||||
dst_mac = '00:02:b3:13:fe:3d'
|
||||
self.br.delete_dvr_to_src_mac(network_type=network_type,
|
||||
vlan_tag=vlan_tag,
|
||||
dst_mac=dst_mac)
|
||||
expected = [
|
||||
call.delete_flows(table=1, dl_dst=dst_mac, dl_vlan=vlan_tag),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_dvr_to_src_mac_vlan(self):
|
||||
network_type = 'vlan'
|
||||
vlan_tag = 1111
|
||||
gateway_mac = '08:60:6e:7f:74:e7'
|
||||
dst_mac = '00:02:b3:13:fe:3d'
|
||||
dst_port = 6666
|
||||
self.br.install_dvr_to_src_mac(network_type=network_type,
|
||||
vlan_tag=vlan_tag,
|
||||
gateway_mac=gateway_mac,
|
||||
dst_mac=dst_mac,
|
||||
dst_port=dst_port)
|
||||
expected = [
|
||||
call.add_flow(priority=4, table=2, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag,
|
||||
actions='strip_vlan,mod_dl_src:%(mac)s,'
|
||||
'output:%(port)s' % {
|
||||
'mac': gateway_mac,
|
||||
'port': dst_port,
|
||||
}),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_dvr_to_src_mac_vlan(self):
|
||||
network_type = 'vlan'
|
||||
vlan_tag = 1111
|
||||
dst_mac = '00:02:b3:13:fe:3d'
|
||||
self.br.delete_dvr_to_src_mac(network_type=network_type,
|
||||
vlan_tag=vlan_tag,
|
||||
dst_mac=dst_mac)
|
||||
expected = [
|
||||
call.delete_flows(table=2, dl_dst=dst_mac, dl_vlan=vlan_tag),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_add_dvr_mac_vlan(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
port = 8888
|
||||
self.br.add_dvr_mac_vlan(mac=mac, port=port)
|
||||
expected = [
|
||||
call.add_flow(priority=4, table=0, actions='resubmit(,2)',
|
||||
dl_src=mac, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_remove_dvr_mac_vlan(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
self.br.remove_dvr_mac_vlan(mac=mac)
|
||||
expected = [
|
||||
call.delete_flows(eth_src=mac, table_id=0),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_add_dvr_mac_tun(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
port = 8888
|
||||
self.br.add_dvr_mac_tun(mac=mac, port=port)
|
||||
expected = [
|
||||
call.add_flow(priority=2, table=0, actions='resubmit(,1)',
|
||||
dl_src=mac, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_remove_dvr_mac_tun(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
port = 8888
|
||||
self.br.remove_dvr_mac_tun(mac=mac, port=port)
|
||||
expected = [
|
||||
call.delete_flows(eth_src=mac, table_id=0, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_arp_spoofing_protection(self):
|
||||
port = 8888
|
||||
ip_addresses = ['192.0.2.1', '192.0.2.2/32']
|
||||
self.br.install_arp_spoofing_protection(port, ip_addresses)
|
||||
expected = [
|
||||
call.add_flow(proto='arp', actions='normal',
|
||||
arp_spa='192.0.2.1',
|
||||
priority=2, table=24, in_port=8888),
|
||||
call.add_flow(proto='arp', actions='normal',
|
||||
arp_spa='192.0.2.2/32',
|
||||
priority=2, table=24, in_port=8888),
|
||||
call.add_flow(priority=10, table=0, in_port=8888,
|
||||
actions='resubmit(,24)', proto='arp')
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_arp_spoofing_protection(self):
|
||||
port = 8888
|
||||
self.br.delete_arp_spoofing_protection(port)
|
||||
expected = [
|
||||
call.delete_flows(table_id=0, in_port=8888, proto='arp'),
|
||||
call.delete_flows(table_id=24, in_port=8888),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -0,0 +1,97 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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
|
||||
|
||||
import neutron.plugins.openvswitch.common.constants as ovs_const
|
||||
from neutron.tests.unit.plugins.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge_test_base
|
||||
|
||||
|
||||
call = mock.call # short hand
|
||||
|
||||
|
||||
class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
|
||||
ovs_bridge_test_base.OVSDVRProcessTestMixin):
|
||||
dvr_process_table_id = ovs_const.DVR_PROCESS_VLAN
|
||||
dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION
|
||||
|
||||
def setUp(self):
|
||||
super(OVSPhysicalBridgeTest, self).setUp()
|
||||
self.setup_bridge_mock('br-phys', self.br_phys_cls)
|
||||
|
||||
def test_setup_default_table(self):
|
||||
self.br.setup_default_table()
|
||||
expected = [
|
||||
call.delete_flows(),
|
||||
call.add_flow(priority=0, table=0, actions='normal'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_provision_local_vlan(self):
|
||||
port = 999
|
||||
lvid = 888
|
||||
segmentation_id = 777
|
||||
distributed = False
|
||||
self.br.provision_local_vlan(port=port, lvid=lvid,
|
||||
segmentation_id=segmentation_id,
|
||||
distributed=distributed)
|
||||
expected = [
|
||||
call.add_flow(priority=4, table=0, dl_vlan=lvid, in_port=port,
|
||||
actions='mod_vlan_vid:%s,normal' % segmentation_id),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_provision_local_vlan_novlan(self):
|
||||
port = 999
|
||||
lvid = 888
|
||||
segmentation_id = None
|
||||
distributed = False
|
||||
self.br.provision_local_vlan(port=port, lvid=lvid,
|
||||
segmentation_id=segmentation_id,
|
||||
distributed=distributed)
|
||||
expected = [
|
||||
call.add_flow(priority=4, table=0, dl_vlan=lvid, in_port=port,
|
||||
actions='strip_vlan,normal')
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_reclaim_local_vlan(self):
|
||||
port = 999
|
||||
lvid = 888
|
||||
self.br.reclaim_local_vlan(port=port, lvid=lvid)
|
||||
expected = [
|
||||
call.delete_flows(dl_vlan=lvid, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_add_dvr_mac_vlan(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
port = 8888
|
||||
self.br.add_dvr_mac_vlan(mac=mac, port=port)
|
||||
expected = [
|
||||
call.add_flow(priority=2, table=3, dl_src=mac,
|
||||
actions='output:%s' % port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_remove_dvr_mac_vlan(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
self.br.remove_dvr_mac_vlan(mac=mac)
|
||||
expected = [
|
||||
call.delete_flows(eth_src=mac, table_id=3),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -0,0 +1,259 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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
|
||||
import netaddr
|
||||
|
||||
import neutron.plugins.openvswitch.common.constants as ovs_const
|
||||
from neutron.tests.unit.plugins.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge_test_base
|
||||
|
||||
|
||||
call = mock.call # short hand
|
||||
|
||||
|
||||
class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
|
||||
ovs_bridge_test_base.OVSDVRProcessTestMixin):
|
||||
dvr_process_table_id = ovs_const.DVR_PROCESS
|
||||
dvr_process_next_table_id = ovs_const.PATCH_LV_TO_TUN
|
||||
|
||||
def setUp(self):
|
||||
super(OVSTunnelBridgeTest, self).setUp()
|
||||
self.setup_bridge_mock('br-tun', self.br_tun_cls)
|
||||
|
||||
def test_setup_default_table(self):
|
||||
patch_int_ofport = 5555
|
||||
arp_responder_enabled = False
|
||||
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
|
||||
arp_responder_enabled=arp_responder_enabled)
|
||||
expected = [
|
||||
call.add_flow(priority=1, in_port=patch_int_ofport,
|
||||
actions='resubmit(,2)'),
|
||||
call.add_flow(priority=0, actions='drop'),
|
||||
call.add_flow(priority=0, table=2,
|
||||
dl_dst='00:00:00:00:00:00/01:00:00:00:00:00',
|
||||
actions='resubmit(,20)'),
|
||||
call.add_flow(priority=0, table=2,
|
||||
dl_dst='01:00:00:00:00:00/01:00:00:00:00:00',
|
||||
actions='resubmit(,22)'),
|
||||
call.add_flow(priority=0, table=3, actions='drop'),
|
||||
call.add_flow(priority=0, table=4, actions='drop'),
|
||||
call.add_flow(priority=1, table=10,
|
||||
actions='learn(table=20,priority=1,'
|
||||
'hard_timeout=300,NXM_OF_VLAN_TCI[0..11],'
|
||||
'NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],'
|
||||
'load:0->NXM_OF_VLAN_TCI[],'
|
||||
'load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],'
|
||||
'output:NXM_OF_IN_PORT[]),'
|
||||
'output:%s' % patch_int_ofport),
|
||||
call.add_flow(priority=0, table=20, actions='resubmit(,22)'),
|
||||
call.add_flow(priority=0, table=22, actions='drop'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_setup_default_table_arp_responder_enabled(self):
|
||||
patch_int_ofport = 5555
|
||||
arp_responder_enabled = True
|
||||
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
|
||||
arp_responder_enabled=arp_responder_enabled)
|
||||
expected = [
|
||||
call.add_flow(priority=1, in_port=patch_int_ofport,
|
||||
actions='resubmit(,2)'),
|
||||
call.add_flow(priority=0, actions='drop'),
|
||||
call.add_flow(priority=1, table=2, dl_dst='ff:ff:ff:ff:ff:ff',
|
||||
actions='resubmit(,21)', proto='arp'),
|
||||
call.add_flow(priority=0, table=2,
|
||||
dl_dst='00:00:00:00:00:00/01:00:00:00:00:00',
|
||||
actions='resubmit(,20)'),
|
||||
call.add_flow(priority=0, table=2,
|
||||
dl_dst='01:00:00:00:00:00/01:00:00:00:00:00',
|
||||
actions='resubmit(,22)'),
|
||||
call.add_flow(priority=0, table=3, actions='drop'),
|
||||
call.add_flow(priority=0, table=4, actions='drop'),
|
||||
call.add_flow(priority=1, table=10,
|
||||
actions='learn(table=20,priority=1,'
|
||||
'hard_timeout=300,NXM_OF_VLAN_TCI[0..11],'
|
||||
'NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],'
|
||||
'load:0->NXM_OF_VLAN_TCI[],'
|
||||
'load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],'
|
||||
'output:NXM_OF_IN_PORT[]),'
|
||||
'output:%s' % patch_int_ofport),
|
||||
call.add_flow(priority=0, table=20, actions='resubmit(,22)'),
|
||||
call.add_flow(priority=0, table=21, actions='resubmit(,22)'),
|
||||
call.add_flow(priority=0, table=22, actions='drop'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_provision_local_vlan(self):
|
||||
network_type = 'vxlan'
|
||||
lvid = 888
|
||||
segmentation_id = 777
|
||||
distributed = False
|
||||
self.br.provision_local_vlan(network_type=network_type, lvid=lvid,
|
||||
segmentation_id=segmentation_id,
|
||||
distributed=distributed)
|
||||
expected = [
|
||||
call.add_flow(priority=1, tun_id=segmentation_id,
|
||||
actions='mod_vlan_vid:%s,resubmit(,10)' % lvid,
|
||||
table=4),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_reclaim_local_vlan(self):
|
||||
network_type = 'vxlan'
|
||||
segmentation_id = 777
|
||||
self.br.reclaim_local_vlan(network_type=network_type,
|
||||
segmentation_id=segmentation_id)
|
||||
expected = [
|
||||
call.delete_flows(tun_id=segmentation_id, table=4),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_flood_to_tun(self):
|
||||
vlan = 3333
|
||||
tun_id = 2222
|
||||
ports = [11, 44, 22, 33]
|
||||
self.br.install_flood_to_tun(vlan=vlan,
|
||||
tun_id=tun_id,
|
||||
ports=ports)
|
||||
expected = [
|
||||
call.mod_flow(table=22, dl_vlan=vlan,
|
||||
actions='strip_vlan,set_tunnel:%(tun)s,'
|
||||
'output:%(ports)s' % {
|
||||
'tun': tun_id,
|
||||
'ports': ','.join(map(str, ports)),
|
||||
}),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_flood_to_tun(self):
|
||||
vlan = 3333
|
||||
self.br.delete_flood_to_tun(vlan=vlan)
|
||||
expected = [
|
||||
call.delete_flows(table=22, dl_vlan=vlan),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_unicast_to_tun(self):
|
||||
vlan = 3333
|
||||
port = 55
|
||||
mac = '08:60:6e:7f:74:e7'
|
||||
tun_id = 2222
|
||||
self.br.install_unicast_to_tun(vlan=vlan,
|
||||
tun_id=tun_id,
|
||||
port=port,
|
||||
mac=mac)
|
||||
expected = [
|
||||
call.add_flow(priority=2, table=20, dl_dst=mac, dl_vlan=vlan,
|
||||
actions='strip_vlan,set_tunnel:%(tun)s,'
|
||||
'output:%(port)s' % {
|
||||
'tun': tun_id,
|
||||
'port': port,
|
||||
}),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_unicast_to_tun(self):
|
||||
vlan = 3333
|
||||
mac = '08:60:6e:7f:74:e7'
|
||||
self.br.delete_unicast_to_tun(vlan=vlan, mac=mac)
|
||||
expected = [
|
||||
call.delete_flows(table=20, dl_dst=mac, dl_vlan=vlan),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_unicast_to_tun_without_mac(self):
|
||||
vlan = 3333
|
||||
mac = None
|
||||
self.br.delete_unicast_to_tun(vlan=vlan, mac=mac)
|
||||
expected = [
|
||||
call.delete_flows(table=20, dl_vlan=vlan),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_arp_responder(self):
|
||||
vlan = 3333
|
||||
ip = '192.0.2.1'
|
||||
mac = '08:60:6e:7f:74:e7'
|
||||
self.br.install_arp_responder(vlan=vlan, ip=ip, mac=mac)
|
||||
expected = [
|
||||
call.add_flow(proto='arp', nw_dst=ip,
|
||||
actions='move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'
|
||||
'mod_dl_src:%(mac)s,load:0x2->NXM_OF_ARP_OP[],'
|
||||
'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
|
||||
'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],'
|
||||
'load:%(mac)#x->NXM_NX_ARP_SHA[],'
|
||||
'load:%(ip)#x->NXM_OF_ARP_SPA[],in_port' % {
|
||||
'mac': netaddr.EUI(mac,
|
||||
dialect=netaddr.mac_unix),
|
||||
'ip': netaddr.IPAddress(ip),
|
||||
},
|
||||
priority=1, table=21, dl_vlan=vlan),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_arp_responder(self):
|
||||
vlan = 3333
|
||||
ip = '192.0.2.1'
|
||||
self.br.delete_arp_responder(vlan=vlan, ip=ip)
|
||||
expected = [
|
||||
call.delete_flows(table=21, dl_vlan=vlan, proto='arp', nw_dst=ip),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_delete_arp_responder_without_ip(self):
|
||||
vlan = 3333
|
||||
ip = None
|
||||
self.br.delete_arp_responder(vlan=vlan, ip=ip)
|
||||
expected = [
|
||||
call.delete_flows(table=21, dl_vlan=vlan, proto='arp'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_setup_tunnel_port(self):
|
||||
network_type = 'vxlan'
|
||||
port = 11111
|
||||
self.br.setup_tunnel_port(network_type=network_type, port=port)
|
||||
expected = [
|
||||
call.add_flow(priority=1, in_port=port, actions='resubmit(,4)'),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_cleanup_tunnel_port(self):
|
||||
port = 11111
|
||||
self.br.cleanup_tunnel_port(port=port)
|
||||
expected = [
|
||||
call.delete_flows(in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_add_dvr_mac_tun(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
port = 8888
|
||||
self.br.add_dvr_mac_tun(mac=mac, port=port)
|
||||
expected = [
|
||||
call.add_flow(priority=1, table=9, dl_src=mac,
|
||||
actions='output:%s' % port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_remove_dvr_mac_tun(self):
|
||||
mac = '00:02:b3:13:fe:3d'
|
||||
self.br.remove_dvr_mac_tun(mac=mac)
|
||||
expected = [
|
||||
call.delete_flows(eth_src=mac, table_id=9),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
|
||||
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# 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.
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
_AGENT_PACKAGE = 'neutron.plugins.openvswitch.agent'
|
||||
_AGENT_NAME = _AGENT_PACKAGE + '.ovs_neutron_agent'
|
||||
_DVR_AGENT_NAME = 'neutron.plugins.openvswitch.agent.ovs_dvr_neutron_agent'
|
||||
|
||||
|
||||
class OVSAgentConfigTestBase(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(OVSAgentConfigTestBase, self).setUp()
|
||||
self.mod_agent = importutils.import_module(_AGENT_NAME)
|
||||
self.mod_dvr_agent = importutils.import_module(_DVR_AGENT_NAME)
|
||||
|
||||
|
||||
class OVSAgentTestBase(OVSAgentConfigTestBase):
|
||||
def setUp(self):
|
||||
super(OVSAgentTestBase, self).setUp()
|
||||
self.br_int_cls = importutils.import_class(self._BR_INT_CLASS)
|
||||
self.br_phys_cls = importutils.import_class(self._BR_PHYS_CLASS)
|
||||
self.br_tun_cls = importutils.import_class(self._BR_TUN_CLASS)
|
||||
|
||||
def _bridge_classes(self):
|
||||
return {
|
||||
'br_int': self.br_int_cls,
|
||||
'br_phys': self.br_phys_cls,
|
||||
'br_tun': self.br_tun_cls,
|
||||
}
|
||||
|
||||
|
||||
class OVSOFCtlTestBase(OVSAgentTestBase):
|
||||
_DRIVER_PACKAGE = _AGENT_PACKAGE + '.openflow.ovs_ofctl'
|
||||
_BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge'
|
||||
_BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge'
|
||||
_BR_PHYS_CLASS = _DRIVER_PACKAGE + '.br_phys.OVSPhysicalBridge'
|
File diff suppressed because it is too large
Load Diff
@ -24,9 +24,8 @@ from oslo_log import log
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit.plugins.openvswitch.agent import ovs_test_base
|
||||
|
||||
|
||||
# Useful global dummy variables.
|
||||
@ -40,11 +39,6 @@ OFPORT_NUM = 1
|
||||
VIF_PORT = ovs_lib.VifPort('port', OFPORT_NUM,
|
||||
VIF_ID, VIF_MAC, 'switch')
|
||||
VIF_PORTS = {VIF_ID: VIF_PORT}
|
||||
LVM = ovs_neutron_agent.LocalVLANMapping(LV_ID, 'gre', None, LS_ID, VIF_PORTS)
|
||||
LVM_FLAT = ovs_neutron_agent.LocalVLANMapping(
|
||||
LV_ID, 'flat', 'net1', LS_ID, VIF_PORTS)
|
||||
LVM_VLAN = ovs_neutron_agent.LocalVLANMapping(
|
||||
LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS)
|
||||
FIXED_IPS = [{'subnet_id': 'my-subnet-uuid',
|
||||
'ip_address': '1.1.1.1'}]
|
||||
VM_DEVICE_OWNER = "compute:None"
|
||||
@ -66,7 +60,7 @@ class DummyVlanBinding(object):
|
||||
self.vlan_id = vlan_id
|
||||
|
||||
|
||||
class TunnelTest(base.BaseTestCase):
|
||||
class TunnelTest(object):
|
||||
USE_VETH_INTERCONNECTION = False
|
||||
VETH_MTU = None
|
||||
|
||||
@ -86,21 +80,41 @@ class TunnelTest(base.BaseTestCase):
|
||||
self.MAP_TUN_INT_OFPORT = 33333
|
||||
self.MAP_TUN_PHY_OFPORT = 44444
|
||||
|
||||
self.LVM = self.mod_agent.LocalVLANMapping(
|
||||
LV_ID, 'gre', None, LS_ID, VIF_PORTS)
|
||||
self.LVM_FLAT = self.mod_agent.LocalVLANMapping(
|
||||
LV_ID, 'flat', 'net1', LS_ID, VIF_PORTS)
|
||||
self.LVM_VLAN = self.mod_agent.LocalVLANMapping(
|
||||
LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS)
|
||||
|
||||
self.inta = mock.Mock()
|
||||
self.intb = mock.Mock()
|
||||
|
||||
self.ovs_bridges = {self.INT_BRIDGE: mock.Mock(),
|
||||
self.TUN_BRIDGE: mock.Mock(),
|
||||
self.MAP_TUN_BRIDGE: mock.Mock(),
|
||||
}
|
||||
self.ovs_bridges = {
|
||||
self.INT_BRIDGE: mock.create_autospec(
|
||||
self.br_int_cls('br-int')),
|
||||
self.TUN_BRIDGE: mock.create_autospec(
|
||||
self.br_tun_cls('br-tun')),
|
||||
self.MAP_TUN_BRIDGE: mock.create_autospec(
|
||||
self.br_phys_cls('br-phys')),
|
||||
}
|
||||
self.ovs_int_ofports = {
|
||||
'patch-tun': self.TUN_OFPORT,
|
||||
'int-%s' % self.MAP_TUN_BRIDGE: self.MAP_TUN_INT_OFPORT
|
||||
}
|
||||
|
||||
self.mock_bridge = mock.patch.object(ovs_lib, 'OVSBridge').start()
|
||||
self.mock_bridge.side_effect = (lambda br_name:
|
||||
self.ovs_bridges[br_name])
|
||||
def lookup_br(br_name, *args, **kwargs):
|
||||
return self.ovs_bridges[br_name]
|
||||
|
||||
self.mock_int_bridge_cls = mock.patch(self._BR_INT_CLASS,
|
||||
autospec=True).start()
|
||||
self.mock_int_bridge_cls.side_effect = lookup_br
|
||||
self.mock_phys_bridge_cls = mock.patch(self._BR_PHYS_CLASS,
|
||||
autospec=True).start()
|
||||
self.mock_phys_bridge_cls.side_effect = lookup_br
|
||||
self.mock_tun_bridge_cls = mock.patch(self._BR_TUN_CLASS,
|
||||
autospec=True).start()
|
||||
self.mock_tun_bridge_cls.side_effect = lookup_br
|
||||
|
||||
self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE]
|
||||
self.mock_int_bridge.add_port.return_value = self.MAP_TUN_INT_OFPORT
|
||||
@ -139,10 +153,14 @@ class TunnelTest(base.BaseTestCase):
|
||||
|
||||
self._define_expected_calls()
|
||||
|
||||
def _define_expected_calls(self):
|
||||
self.mock_bridge_expected = [
|
||||
def _define_expected_calls(self, arp_responder=False):
|
||||
self.mock_int_bridge_cls_expected = [
|
||||
mock.call(self.INT_BRIDGE),
|
||||
]
|
||||
self.mock_phys_bridge_cls_expected = [
|
||||
mock.call(self.MAP_TUN_BRIDGE),
|
||||
]
|
||||
self.mock_tun_bridge_cls_expected = [
|
||||
mock.call(self.TUN_BRIDGE),
|
||||
]
|
||||
|
||||
@ -150,20 +168,17 @@ class TunnelTest(base.BaseTestCase):
|
||||
self.mock_int_bridge_expected = [
|
||||
mock.call.create(),
|
||||
mock.call.set_secure_mode(),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.delete_port('patch-tun'),
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1, actions='normal'),
|
||||
mock.call.add_flow(priority=0, table=constants.CANARY_TABLE,
|
||||
actions='drop'),
|
||||
mock.call.setup_default_table(),
|
||||
]
|
||||
|
||||
self.mock_map_tun_bridge_expected = [
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1, actions='normal'),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.setup_default_table(),
|
||||
mock.call.delete_port('phy-%s' % self.MAP_TUN_BRIDGE),
|
||||
mock.call.add_patch_port('phy-%s' % self.MAP_TUN_BRIDGE,
|
||||
constants.NONEXISTENT_PEER),
|
||||
]
|
||||
constants.NONEXISTENT_PEER), ]
|
||||
self.mock_int_bridge_expected += [
|
||||
mock.call.delete_port('int-%s' % self.MAP_TUN_BRIDGE),
|
||||
mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE,
|
||||
@ -171,17 +186,13 @@ class TunnelTest(base.BaseTestCase):
|
||||
]
|
||||
|
||||
self.mock_int_bridge_expected += [
|
||||
mock.call.add_flow(priority=2,
|
||||
in_port=self.MAP_TUN_INT_OFPORT,
|
||||
actions='drop'),
|
||||
mock.call.drop_port(in_port=self.MAP_TUN_INT_OFPORT),
|
||||
mock.call.set_db_attribute(
|
||||
'Interface', 'int-%s' % self.MAP_TUN_BRIDGE,
|
||||
'options:peer', 'phy-%s' % self.MAP_TUN_BRIDGE),
|
||||
]
|
||||
self.mock_map_tun_bridge_expected += [
|
||||
mock.call.add_flow(priority=2,
|
||||
in_port=self.MAP_TUN_PHY_OFPORT,
|
||||
actions='drop'),
|
||||
mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT),
|
||||
mock.call.set_db_attribute(
|
||||
'Interface', 'phy-%s' % self.MAP_TUN_BRIDGE,
|
||||
'options:peer', 'int-%s' % self.MAP_TUN_BRIDGE),
|
||||
@ -189,6 +200,7 @@ class TunnelTest(base.BaseTestCase):
|
||||
|
||||
self.mock_tun_bridge_expected = [
|
||||
mock.call.reset_bridge(secure_mode=True),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.add_patch_port('patch-int', 'patch-tun'),
|
||||
]
|
||||
self.mock_int_bridge_expected += [
|
||||
@ -199,48 +211,8 @@ class TunnelTest(base.BaseTestCase):
|
||||
]
|
||||
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN,
|
||||
in_port=self.INT_OFPORT),
|
||||
mock.call.add_flow(priority=0, actions="drop"),
|
||||
mock.call.add_flow(priority=0, table=constants.PATCH_LV_TO_TUN,
|
||||
dl_dst=UCAST_MAC,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.UCAST_TO_TUN),
|
||||
mock.call.add_flow(priority=0, table=constants.PATCH_LV_TO_TUN,
|
||||
dl_dst=BCAST_MAC,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN),
|
||||
]
|
||||
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
self.mock_tun_bridge_expected.append(
|
||||
mock.call.add_flow(
|
||||
table=constants.TUN_TABLE[tunnel_type],
|
||||
priority=0,
|
||||
actions="drop"))
|
||||
learned_flow = ("table=%s,"
|
||||
"priority=1,"
|
||||
"hard_timeout=300,"
|
||||
"NXM_OF_VLAN_TCI[0..11],"
|
||||
"NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
|
||||
"load:0->NXM_OF_VLAN_TCI[],"
|
||||
"load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
|
||||
"output:NXM_OF_IN_PORT[]" %
|
||||
constants.UCAST_TO_TUN)
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.add_flow(table=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions="learn(%s),output:%s" %
|
||||
(learned_flow, self.INT_OFPORT)),
|
||||
mock.call.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN),
|
||||
mock.call.add_flow(table=constants.FLOOD_TO_TUN,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
mock.call.delete_flows(),
|
||||
mock.call.setup_default_table(self.INT_OFPORT, arp_responder),
|
||||
]
|
||||
|
||||
self.device_exists_expected = []
|
||||
@ -255,6 +227,12 @@ class TunnelTest(base.BaseTestCase):
|
||||
self.execute_expected = []
|
||||
|
||||
def _build_agent(self, **kwargs):
|
||||
bridge_classes = {
|
||||
'br_int': self.mock_int_bridge_cls,
|
||||
'br_phys': self.mock_phys_bridge_cls,
|
||||
'br_tun': self.mock_tun_bridge_cls,
|
||||
}
|
||||
kwargs.setdefault('bridge_classes', bridge_classes)
|
||||
kwargs.setdefault('integ_br', self.INT_BRIDGE)
|
||||
kwargs.setdefault('tun_br', self.TUN_BRIDGE)
|
||||
kwargs.setdefault('local_ip', '10.0.0.1')
|
||||
@ -264,14 +242,19 @@ class TunnelTest(base.BaseTestCase):
|
||||
kwargs.setdefault('veth_mtu', self.VETH_MTU)
|
||||
kwargs.setdefault('use_veth_interconnection',
|
||||
self.USE_VETH_INTERCONNECTION)
|
||||
return ovs_neutron_agent.OVSNeutronAgent(**kwargs)
|
||||
return self.mod_agent.OVSNeutronAgent(**kwargs)
|
||||
|
||||
def _verify_mock_call(self, mock_obj, expected):
|
||||
mock_obj.assert_has_calls(expected)
|
||||
self.assertEqual(len(mock_obj.mock_calls), len(expected))
|
||||
|
||||
def _verify_mock_calls(self):
|
||||
self._verify_mock_call(self.mock_bridge, self.mock_bridge_expected)
|
||||
self._verify_mock_call(self.mock_int_bridge_cls,
|
||||
self.mock_int_bridge_cls_expected)
|
||||
self._verify_mock_call(self.mock_tun_bridge_cls,
|
||||
self.mock_tun_bridge_cls_expected)
|
||||
self._verify_mock_call(self.mock_phys_bridge_cls,
|
||||
self.mock_phys_bridge_cls_expected)
|
||||
self._verify_mock_call(self.mock_int_bridge,
|
||||
self.mock_int_bridge_expected)
|
||||
self._verify_mock_call(self.mock_map_tun_bridge,
|
||||
@ -296,20 +279,7 @@ class TunnelTest(base.BaseTestCase):
|
||||
# The next two tests use l2_pop flag to test ARP responder
|
||||
def test_construct_with_arp_responder(self):
|
||||
self._build_agent(l2_population=True, arp_responder=True)
|
||||
self.mock_tun_bridge_expected.insert(
|
||||
5, mock.call.add_flow(table=constants.PATCH_LV_TO_TUN,
|
||||
priority=1,
|
||||
proto="arp",
|
||||
dl_dst="ff:ff:ff:ff:ff:ff",
|
||||
actions="resubmit(,%s)" %
|
||||
constants.ARP_RESPONDER)
|
||||
)
|
||||
self.mock_tun_bridge_expected.insert(
|
||||
12, mock.call.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN)
|
||||
)
|
||||
self._define_expected_calls(True)
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_construct_without_arp_responder(self):
|
||||
@ -321,18 +291,13 @@ class TunnelTest(base.BaseTestCase):
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_provision_local_vlan(self):
|
||||
ofports = ','.join(TUN_OFPORTS[p_const.TYPE_GRE].values())
|
||||
ofports = TUN_OFPORTS[p_const.TYPE_GRE].values()
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.mod_flow(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=LV_ID,
|
||||
actions="strip_vlan,"
|
||||
"set_tunnel:%s,output:%s" %
|
||||
(LS_ID, ofports)),
|
||||
mock.call.add_flow(table=constants.TUN_TABLE['gre'],
|
||||
priority=1,
|
||||
tun_id=LS_ID,
|
||||
actions="mod_vlan_vid:%s,resubmit(,%s)" %
|
||||
(LV_ID, constants.LEARN_FROM_TUN)),
|
||||
mock.call.install_flood_to_tun(LV_ID, LS_ID, ofports),
|
||||
mock.call.provision_local_vlan(
|
||||
network_type=p_const.TYPE_GRE,
|
||||
lvid=LV_ID,
|
||||
segmentation_id=LS_ID),
|
||||
]
|
||||
|
||||
a = self._build_agent()
|
||||
@ -342,15 +307,17 @@ class TunnelTest(base.BaseTestCase):
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_provision_local_vlan_flat(self):
|
||||
action_string = 'strip_vlan,normal'
|
||||
self.mock_map_tun_bridge_expected.append(
|
||||
mock.call.add_flow(priority=4, in_port=self.MAP_TUN_PHY_OFPORT,
|
||||
dl_vlan=LV_ID, actions=action_string))
|
||||
|
||||
action_string = 'mod_vlan_vid:%s,normal' % LV_ID
|
||||
mock.call.provision_local_vlan(
|
||||
port=self.MAP_TUN_PHY_OFPORT,
|
||||
lvid=LV_ID,
|
||||
segmentation_id=None,
|
||||
distributed=False))
|
||||
self.mock_int_bridge_expected.append(
|
||||
mock.call.add_flow(priority=3, in_port=self.INT_OFPORT,
|
||||
dl_vlan=65535, actions=action_string))
|
||||
mock.call.provision_local_vlan(
|
||||
port=self.INT_OFPORT,
|
||||
lvid=LV_ID,
|
||||
segmentation_id=None))
|
||||
|
||||
a = self._build_agent()
|
||||
a.available_local_vlans = set([LV_ID])
|
||||
@ -366,16 +333,17 @@ class TunnelTest(base.BaseTestCase):
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_provision_local_vlan_vlan(self):
|
||||
action_string = 'mod_vlan_vid:%s,normal' % LS_ID
|
||||
self.mock_map_tun_bridge_expected.append(
|
||||
mock.call.add_flow(priority=4, in_port=self.MAP_TUN_PHY_OFPORT,
|
||||
dl_vlan=LV_ID, actions=action_string))
|
||||
|
||||
action_string = 'mod_vlan_vid:%s,normal' % LV_ID
|
||||
mock.call.provision_local_vlan(
|
||||
port=self.MAP_TUN_PHY_OFPORT,
|
||||
lvid=LV_ID,
|
||||
segmentation_id=LS_ID,
|
||||
distributed=False))
|
||||
self.mock_int_bridge_expected.append(
|
||||
mock.call.add_flow(priority=3, in_port=self.INT_OFPORT,
|
||||
dl_vlan=LS_ID, actions=action_string))
|
||||
|
||||
mock.call.provision_local_vlan(
|
||||
port=self.INT_OFPORT,
|
||||
lvid=LV_ID,
|
||||
segmentation_id=LS_ID))
|
||||
a = self._build_agent()
|
||||
a.available_local_vlans = set([LV_ID])
|
||||
a.phys_brs['net1'] = self.mock_map_tun_bridge
|
||||
@ -391,54 +359,58 @@ class TunnelTest(base.BaseTestCase):
|
||||
|
||||
def test_reclaim_local_vlan(self):
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.delete_flows(
|
||||
table=constants.TUN_TABLE['gre'], tun_id=LS_ID),
|
||||
mock.call.delete_flows(dl_vlan=LVM.vlan)
|
||||
mock.call.reclaim_local_vlan(network_type='gre',
|
||||
segmentation_id=LS_ID),
|
||||
mock.call.delete_flood_to_tun(LV_ID),
|
||||
mock.call.delete_unicast_to_tun(LV_ID, None),
|
||||
mock.call.delete_arp_responder(LV_ID, None),
|
||||
]
|
||||
|
||||
a = self._build_agent()
|
||||
a.available_local_vlans = set()
|
||||
a.local_vlan_map[NET_UUID] = LVM
|
||||
a.local_vlan_map[NET_UUID] = self.LVM
|
||||
a.reclaim_local_vlan(NET_UUID)
|
||||
self.assertIn(LVM.vlan, a.available_local_vlans)
|
||||
self.assertIn(self.LVM.vlan, a.available_local_vlans)
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_reclaim_local_vlan_flat(self):
|
||||
self.mock_map_tun_bridge_expected.append(
|
||||
mock.call.delete_flows(
|
||||
in_port=self.MAP_TUN_PHY_OFPORT, dl_vlan=LVM_FLAT.vlan))
|
||||
mock.call.reclaim_local_vlan(
|
||||
port=self.MAP_TUN_PHY_OFPORT,
|
||||
lvid=self.LVM_FLAT.vlan))
|
||||
self.mock_int_bridge_expected.append(
|
||||
mock.call.delete_flows(
|
||||
dl_vlan=65535, in_port=self.INT_OFPORT))
|
||||
|
||||
mock.call.reclaim_local_vlan(
|
||||
port=self.INT_OFPORT,
|
||||
segmentation_id=None))
|
||||
a = self._build_agent()
|
||||
a.phys_brs['net1'] = self.mock_map_tun_bridge
|
||||
a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
|
||||
a.int_ofports['net1'] = self.INT_OFPORT
|
||||
|
||||
a.available_local_vlans = set()
|
||||
a.local_vlan_map[NET_UUID] = LVM_FLAT
|
||||
a.local_vlan_map[NET_UUID] = self.LVM_FLAT
|
||||
a.reclaim_local_vlan(NET_UUID)
|
||||
self.assertIn(LVM_FLAT.vlan, a.available_local_vlans)
|
||||
self.assertIn(self.LVM_FLAT.vlan, a.available_local_vlans)
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_reclaim_local_vlan_vlan(self):
|
||||
self.mock_map_tun_bridge_expected.append(
|
||||
mock.call.delete_flows(
|
||||
in_port=self.MAP_TUN_PHY_OFPORT, dl_vlan=LVM_VLAN.vlan))
|
||||
mock.call.reclaim_local_vlan(
|
||||
port=self.MAP_TUN_PHY_OFPORT,
|
||||
lvid=self.LVM_VLAN.vlan))
|
||||
self.mock_int_bridge_expected.append(
|
||||
mock.call.delete_flows(
|
||||
dl_vlan=LS_ID, in_port=self.INT_OFPORT))
|
||||
|
||||
mock.call.reclaim_local_vlan(
|
||||
port=self.INT_OFPORT,
|
||||
segmentation_id=LS_ID))
|
||||
a = self._build_agent()
|
||||
a.phys_brs['net1'] = self.mock_map_tun_bridge
|
||||
a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
|
||||
a.int_ofports['net1'] = self.INT_OFPORT
|
||||
|
||||
a.available_local_vlans = set()
|
||||
a.local_vlan_map[NET_UUID] = LVM_VLAN
|
||||
a.local_vlan_map[NET_UUID] = self.LVM_VLAN
|
||||
a.reclaim_local_vlan(NET_UUID)
|
||||
self.assertIn(LVM_VLAN.vlan, a.available_local_vlans)
|
||||
self.assertIn(self.LVM_VLAN.vlan, a.available_local_vlans)
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_port_bound(self):
|
||||
@ -453,17 +425,18 @@ class TunnelTest(base.BaseTestCase):
|
||||
vlan_mapping)]
|
||||
|
||||
a = self._build_agent()
|
||||
a.local_vlan_map[NET_UUID] = LVM
|
||||
a.local_vlan_map[NET_UUID] = self.LVM
|
||||
a.local_dvr_map = {}
|
||||
self.ovs_bridges[self.INT_BRIDGE].db_get_val.return_value = {}
|
||||
a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID,
|
||||
FIXED_IPS, VM_DEVICE_OWNER, False)
|
||||
self._verify_mock_calls()
|
||||
|
||||
def test_port_unbound(self):
|
||||
with mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
|
||||
with mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'reclaim_local_vlan') as reclaim_local_vlan:
|
||||
a = self._build_agent()
|
||||
a.local_vlan_map[NET_UUID] = LVM
|
||||
a.local_vlan_map[NET_UUID] = self.LVM
|
||||
a.port_unbound(VIF_ID, NET_UUID)
|
||||
|
||||
reclaim_local_vlan.assert_called_once_with(NET_UUID)
|
||||
@ -474,14 +447,14 @@ class TunnelTest(base.BaseTestCase):
|
||||
mock.call.db_get_val('Port', VIF_PORT.port_name, 'tag'),
|
||||
mock.call.set_db_attribute(
|
||||
'Port', VIF_PORT.port_name,
|
||||
'tag', ovs_neutron_agent.DEAD_VLAN_TAG),
|
||||
mock.call.add_flow(priority=2, in_port=VIF_PORT.ofport,
|
||||
actions='drop')
|
||||
'tag', self.mod_agent.DEAD_VLAN_TAG),
|
||||
mock.call.drop_port(in_port=VIF_PORT.ofport),
|
||||
]
|
||||
|
||||
a = self._build_agent()
|
||||
a.available_local_vlans = set([LV_ID])
|
||||
a.local_vlan_map[NET_UUID] = LVM
|
||||
a.local_vlan_map[NET_UUID] = self.LVM
|
||||
self.ovs_bridges[self.INT_BRIDGE].db_get_val.return_value = mock.Mock()
|
||||
a.port_dead(VIF_PORT)
|
||||
self._verify_mock_calls()
|
||||
|
||||
@ -491,8 +464,7 @@ class TunnelTest(base.BaseTestCase):
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.add_tunnel_port('gre-0a000a01', '10.0.10.1', '10.0.0.1',
|
||||
'gre', 4789, True),
|
||||
mock.call.add_flow(priority=1, in_port=tunnel_port,
|
||||
actions='resubmit(,3)')
|
||||
mock.call.setup_tunnel_port('gre', tunnel_port),
|
||||
]
|
||||
|
||||
a = self._build_agent()
|
||||
@ -517,20 +489,22 @@ class TunnelTest(base.BaseTestCase):
|
||||
'removed': set(['tap0'])}
|
||||
|
||||
self.mock_int_bridge_expected += [
|
||||
mock.call.dump_flows_for_table(constants.CANARY_TABLE),
|
||||
mock.call.dump_flows_for_table(constants.CANARY_TABLE)
|
||||
mock.call.check_canary_table(),
|
||||
mock.call.check_canary_table()
|
||||
]
|
||||
|
||||
self.ovs_bridges[self.INT_BRIDGE].check_canary_table.return_value = \
|
||||
constants.OVS_NORMAL
|
||||
with contextlib.nested(
|
||||
mock.patch.object(log.KeywordArgumentAdapter, 'exception'),
|
||||
mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
|
||||
mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'scan_ports'),
|
||||
mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
|
||||
mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'process_network_ports'),
|
||||
mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
|
||||
mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'tunnel_sync'),
|
||||
mock.patch.object(time, 'sleep'),
|
||||
mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
|
||||
mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'update_stale_ofport_rules')
|
||||
) as (log_exception, scan_ports, process_network_ports,
|
||||
ts, time_sleep, update_stale):
|
||||
@ -569,29 +543,35 @@ class TunnelTest(base.BaseTestCase):
|
||||
self._verify_mock_calls()
|
||||
|
||||
|
||||
class TunnelTestOFCtl(TunnelTest, ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TunnelTestUseVethInterco(TunnelTest):
|
||||
USE_VETH_INTERCONNECTION = True
|
||||
|
||||
def _define_expected_calls(self):
|
||||
self.mock_bridge_expected = [
|
||||
def _define_expected_calls(self, arp_responder=False):
|
||||
self.mock_int_bridge_cls_expected = [
|
||||
mock.call(self.INT_BRIDGE),
|
||||
]
|
||||
self.mock_phys_bridge_cls_expected = [
|
||||
mock.call(self.MAP_TUN_BRIDGE),
|
||||
]
|
||||
self.mock_tun_bridge_cls_expected = [
|
||||
mock.call(self.TUN_BRIDGE),
|
||||
]
|
||||
|
||||
self.mock_int_bridge_expected = [
|
||||
mock.call.create(),
|
||||
mock.call.set_secure_mode(),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.delete_port('patch-tun'),
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1, actions='normal'),
|
||||
mock.call.add_flow(table=constants.CANARY_TABLE, priority=0,
|
||||
actions="drop")
|
||||
mock.call.setup_default_table(),
|
||||
]
|
||||
|
||||
self.mock_map_tun_bridge_expected = [
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1, actions='normal'),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.setup_default_table(),
|
||||
mock.call.delete_port('phy-%s' % self.MAP_TUN_BRIDGE),
|
||||
mock.call.add_port(self.intb),
|
||||
]
|
||||
@ -601,18 +581,15 @@ class TunnelTestUseVethInterco(TunnelTest):
|
||||
]
|
||||
|
||||
self.mock_int_bridge_expected += [
|
||||
mock.call.add_flow(priority=2,
|
||||
in_port=self.MAP_TUN_INT_OFPORT,
|
||||
actions='drop')
|
||||
mock.call.drop_port(in_port=self.MAP_TUN_INT_OFPORT),
|
||||
]
|
||||
self.mock_map_tun_bridge_expected += [
|
||||
mock.call.add_flow(priority=2,
|
||||
in_port=self.MAP_TUN_PHY_OFPORT,
|
||||
actions='drop')
|
||||
mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT),
|
||||
]
|
||||
|
||||
self.mock_tun_bridge_expected = [
|
||||
mock.call.reset_bridge(secure_mode=True),
|
||||
mock.call.setup_controllers(mock.ANY),
|
||||
mock.call.add_patch_port('patch-int', 'patch-tun'),
|
||||
]
|
||||
self.mock_int_bridge_expected += [
|
||||
@ -622,50 +599,8 @@ class TunnelTestUseVethInterco(TunnelTest):
|
||||
mock.call.get_vif_ports(),
|
||||
]
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.remove_all_flows(),
|
||||
mock.call.add_flow(priority=1,
|
||||
in_port=self.INT_OFPORT,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN),
|
||||
mock.call.add_flow(priority=0, actions='drop'),
|
||||
mock.call.add_flow(priority=0,
|
||||
table=constants.PATCH_LV_TO_TUN,
|
||||
dl_dst=UCAST_MAC,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.UCAST_TO_TUN),
|
||||
mock.call.add_flow(priority=0,
|
||||
table=constants.PATCH_LV_TO_TUN,
|
||||
dl_dst=BCAST_MAC,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN),
|
||||
]
|
||||
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
self.mock_tun_bridge_expected.append(
|
||||
mock.call.add_flow(
|
||||
table=constants.TUN_TABLE[tunnel_type],
|
||||
priority=0,
|
||||
actions="drop"))
|
||||
learned_flow = ("table=%s,"
|
||||
"priority=1,"
|
||||
"hard_timeout=300,"
|
||||
"NXM_OF_VLAN_TCI[0..11],"
|
||||
"NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
|
||||
"load:0->NXM_OF_VLAN_TCI[],"
|
||||
"load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
|
||||
"output:NXM_OF_IN_PORT[]" %
|
||||
constants.UCAST_TO_TUN)
|
||||
self.mock_tun_bridge_expected += [
|
||||
mock.call.add_flow(table=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions="learn(%s),output:%s" %
|
||||
(learned_flow, self.INT_OFPORT)),
|
||||
mock.call.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.FLOOD_TO_TUN),
|
||||
mock.call.add_flow(table=constants.FLOOD_TO_TUN,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
mock.call.delete_flows(),
|
||||
mock.call.setup_default_table(self.INT_OFPORT, arp_responder),
|
||||
]
|
||||
|
||||
self.device_exists_expected = [
|
||||
@ -690,10 +625,20 @@ class TunnelTestUseVethInterco(TunnelTest):
|
||||
'--timeout=10'])]
|
||||
|
||||
|
||||
class TunnelTestUseVethIntercoOFCtl(TunnelTestUseVethInterco,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TunnelTestWithMTU(TunnelTestUseVethInterco):
|
||||
VETH_MTU = 1500
|
||||
|
||||
def _define_expected_calls(self):
|
||||
super(TunnelTestWithMTU, self)._define_expected_calls()
|
||||
def _define_expected_calls(self, arp_responder=False):
|
||||
super(TunnelTestWithMTU, self)._define_expected_calls(arp_responder)
|
||||
self.inta_expected.append(mock.call.link.set_mtu(self.VETH_MTU))
|
||||
self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU))
|
||||
|
||||
|
||||
class TunnelTestWithMTUOFCtl(TunnelTestWithMTU,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user