Merge "Drop of_interface option"
This commit is contained in:
commit
23e3213a07
neutron
conf/plugins/ml2/drivers
plugins/ml2/drivers/openvswitch/agent
main.py
openflow
tests
common
fullstack
resources
test_connectivity.pytest_firewall.pytest_logging.pytest_qos.pytest_securitygroup.pyutils.pyfunctional
unit
agent
plugins/ml2/drivers/openvswitch/agent
extension_drivers
openflow
ovs_ofctl
test_br_cookie.pyservices/logapi/agent
releasenotes/notes
@ -95,10 +95,6 @@ ovs_opts = [
|
||||
"integration bridge to physical networks. "
|
||||
"Support kernel without Open vSwitch patch port "
|
||||
"support so long as it is set to True.")),
|
||||
cfg.StrOpt('of_interface', default='native',
|
||||
deprecated_for_removal=True,
|
||||
choices=['ovs-ofctl', 'native'],
|
||||
help=_("OpenFlow interface to use.")),
|
||||
cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM,
|
||||
choices=[constants.OVS_DATAPATH_SYSTEM,
|
||||
constants.OVS_DATAPATH_NETDEV],
|
||||
|
@ -18,30 +18,20 @@
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron.common import config as common_config
|
||||
from neutron.common import profiler
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native import \
|
||||
main as of_main
|
||||
|
||||
|
||||
cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
|
||||
'common.config')
|
||||
|
||||
|
||||
_main_modules = {
|
||||
'ovs-ofctl': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.'
|
||||
'ovs_ofctl.main',
|
||||
'native': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.'
|
||||
'native.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()
|
||||
of_main.init_config()
|
||||
common_config.setup_logging()
|
||||
profiler.setup("neutron-ovs-agent", cfg.CONF.host)
|
||||
mod.main()
|
||||
of_main.main()
|
||||
|
@ -17,6 +17,7 @@
|
||||
import functools
|
||||
import random
|
||||
|
||||
import debtcollector
|
||||
import eventlet
|
||||
import netaddr
|
||||
from neutron_lib import exceptions
|
||||
@ -220,9 +221,8 @@ class OpenFlowSwitchMixin(object):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = self._match(ofp, ofpp, match, **match_kwargs)
|
||||
if isinstance(instructions, six.string_types):
|
||||
# NOTE: instructions must be str for the ofctl of_interface.
|
||||
# After the ofctl driver is removed, a deprecation warning
|
||||
# could be added here.
|
||||
debtcollector.deprecate("Use of string instruction is "
|
||||
"deprecated", removal_version='U')
|
||||
jsonlist = ofctl_string.ofp_instruction_from_str(
|
||||
ofp, instructions)
|
||||
instructions = ofproto_parser.ofp_instruction_from_jsondict(
|
||||
|
@ -1,93 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from neutron_lib import constants
|
||||
|
||||
|
||||
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',
|
||||
icmp_type=constants.ICMPV6_TYPE_RA,
|
||||
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',
|
||||
icmp_type=constants.ICMPV6_TYPE_RA,
|
||||
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)
|
@ -1,203 +0,0 @@
|
||||
# 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
|
||||
"""
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_lib import constants as const
|
||||
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge
|
||||
|
||||
|
||||
class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
|
||||
"""openvswitch agent br-int specific logic."""
|
||||
|
||||
def setup_default_table(self):
|
||||
self.setup_canary_table()
|
||||
self.install_goto(dest_table_id=constants.TRANSIENT_TABLE)
|
||||
self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=3)
|
||||
self.install_drop(table_id=constants.ARP_SPOOF_TABLE)
|
||||
self.install_drop(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=constants.OPENFLOW_MAX_PRIORITY,
|
||||
dl_vlan=constants.DEAD_VLAN_TAG)
|
||||
|
||||
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,resubmit(,%d)" % (
|
||||
lvid, constants.TRANSIENT_TABLE))
|
||||
|
||||
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 == 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="mod_dl_src:%s,"
|
||||
"resubmit(,%d)" % (
|
||||
gateway_mac, constants.TRANSIENT_TABLE))
|
||||
self.add_flow(table=constants.TRANSIENT_TABLE,
|
||||
priority=4,
|
||||
dl_vlan=vlan_tag,
|
||||
dl_dst=dst_mac,
|
||||
actions="strip_vlan,output:%s" % 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)
|
||||
for table in (table_id, constants.TRANSIENT_TABLE):
|
||||
self.delete_flows(strict=True,
|
||||
priority=4,
|
||||
table=table,
|
||||
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=constants.LOCAL_SWITCHING,
|
||||
dl_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=constants.LOCAL_SWITCHING,
|
||||
in_port=port, dl_src=mac)
|
||||
|
||||
def install_icmpv6_na_spoofing_protection(self, port, ip_addresses):
|
||||
# Allow neighbor advertisements as long as they match addresses
|
||||
# that actually belong to the port.
|
||||
for ip in ip_addresses:
|
||||
self.install_goto(
|
||||
table_id=constants.ARP_SPOOF_TABLE, priority=2,
|
||||
dl_type=const.ETHERTYPE_IPV6,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
icmp_type=const.ICMPV6_TYPE_NA, nd_target=ip, in_port=port,
|
||||
dest_table_id=constants.TRANSIENT_TABLE)
|
||||
|
||||
# Now that the rules are ready, direct icmpv6 neighbor advertisement
|
||||
# traffic from the port into the anti-spoof table.
|
||||
self.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=10, dl_type=const.ETHERTYPE_IPV6,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
icmp_type=const.ICMPV6_TYPE_NA, in_port=port,
|
||||
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
|
||||
|
||||
def set_allowed_macs_for_port(self, port, mac_addresses=None,
|
||||
allow_all=False):
|
||||
if allow_all:
|
||||
self.delete_flows(table=constants.LOCAL_SWITCHING, in_port=port)
|
||||
self.delete_flows(table=constants.MAC_SPOOF_TABLE, in_port=port)
|
||||
return
|
||||
mac_addresses = mac_addresses or []
|
||||
for address in mac_addresses:
|
||||
self.install_goto(
|
||||
table_id=constants.MAC_SPOOF_TABLE, priority=2,
|
||||
eth_src=address, in_port=port,
|
||||
dest_table_id=constants.TRANSIENT_TABLE)
|
||||
# normalize so we can see if macs are the same
|
||||
mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses}
|
||||
flows = self.dump_flows_for(table=constants.MAC_SPOOF_TABLE,
|
||||
in_port=port).splitlines()
|
||||
for flow in flows:
|
||||
if 'dl_src' not in flow:
|
||||
continue
|
||||
flow_mac = flow.split('dl_src=')[1].split(' ')[0].split(',')[0]
|
||||
if netaddr.EUI(flow_mac) not in mac_addresses:
|
||||
self.delete_flows(table=constants.MAC_SPOOF_TABLE,
|
||||
in_port=port, dl_src=flow_mac)
|
||||
self.add_flow(table=constants.LOCAL_SWITCHING,
|
||||
priority=9, in_port=port,
|
||||
actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE))
|
||||
|
||||
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.add_flow(
|
||||
table=constants.ARP_SPOOF_TABLE, priority=2,
|
||||
proto='arp', arp_spa=ip, in_port=port,
|
||||
actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE))
|
||||
|
||||
# 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=constants.LOCAL_SWITCHING,
|
||||
in_port=port, proto='arp')
|
||||
self.delete_flows(table=constants.LOCAL_SWITCHING,
|
||||
in_port=port, nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
icmp_type=const.ICMPV6_TYPE_NA)
|
||||
self.delete_arp_spoofing_allow_rules(port)
|
||||
|
||||
def delete_arp_spoofing_allow_rules(self, port):
|
||||
self.delete_flows(table=constants.ARP_SPOOF_TABLE,
|
||||
in_port=port)
|
@ -1,60 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_dvr_process
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge
|
||||
|
||||
|
||||
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.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=constants.DVR_NOT_LEARN_VLAN,
|
||||
dl_src=mac)
|
@ -1,260 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.common \
|
||||
import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_dvr_process
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ovs_bridge
|
||||
|
||||
|
||||
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
|
||||
with self.deferred() as deferred_br:
|
||||
deferred_br.add_flow(priority=1,
|
||||
in_port=patch_int_ofport,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.PATCH_LV_TO_TUN)
|
||||
deferred_br.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): add arp_op=arp.ARP_REQUEST matcher?
|
||||
deferred_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
|
||||
deferred_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
|
||||
deferred_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:
|
||||
deferred_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 = ("cookie=%(cookie)s,"
|
||||
"table=%(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[]" %
|
||||
{'cookie': self.default_cookie,
|
||||
'table': constants.UCAST_TO_TUN})
|
||||
# Once remote mac addresses are learnt, output packet to patch_int
|
||||
deferred_br.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
|
||||
deferred_br.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
|
||||
deferred_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.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, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
br.add_flow(priority=1,
|
||||
in_port=port,
|
||||
actions="resubmit(,%s)" %
|
||||
constants.TUN_TABLE[network_type])
|
||||
|
||||
def cleanup_tunnel_port(self, port, deferred_br=None):
|
||||
br = deferred_br if deferred_br else self
|
||||
br.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=constants.DVR_NOT_LEARN,
|
||||
dl_src=mac)
|
||||
|
||||
def deferred(self):
|
||||
return DeferredOVSTunnelBridge(self)
|
||||
|
||||
|
||||
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',
|
||||
'setup_tunnel_port',
|
||||
'cleanup_tunnel_port',
|
||||
]
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._METHODS:
|
||||
m = getattr(self.br, name)
|
||||
return functools.partial(m, deferred_br=self)
|
||||
return super(DeferredOVSTunnelBridge, self).__getattr__(name)
|
@ -1,36 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_int
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_phys
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_tun
|
||||
from neutron.plugins.ml2.drivers.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)
|
@ -1,113 +0,0 @@
|
||||
# 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 re
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# Field name mappings (from os-ken 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 dump_flows_all_tables(self):
|
||||
return self.dump_all_flows()
|
||||
|
||||
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 install_instructions(self, instructions,
|
||||
table_id=0, priority=0, **kwargs):
|
||||
self.add_flow(table=table_id,
|
||||
priority=priority,
|
||||
actions=instructions,
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def uninstall_flows(self, **kwargs):
|
||||
# NOTE(yamamoto): super() points to ovs_lib.OVSBridge.
|
||||
# See ovs_bridge.py how this class is actually used.
|
||||
super(OpenFlowSwitchMixin, self).delete_flows(
|
||||
**self._conv_args(kwargs))
|
||||
|
||||
def _filter_flows(self, flows):
|
||||
cookie_list = self.reserved_cookies
|
||||
LOG.debug("Bridge cookies used to filter flows: %s",
|
||||
cookie_list)
|
||||
cookie_re = re.compile('cookie=(0x[A-Fa-f0-9]*)')
|
||||
table_re = re.compile('table=([0-9]*)')
|
||||
for flow in flows:
|
||||
fl_cookie = cookie_re.search(flow)
|
||||
if not fl_cookie:
|
||||
continue
|
||||
fl_cookie = fl_cookie.group(1)
|
||||
if int(fl_cookie, 16) not in cookie_list:
|
||||
fl_table = table_re.search(flow)
|
||||
if not fl_table:
|
||||
continue
|
||||
fl_table = fl_table.group(1)
|
||||
yield flow, fl_cookie, fl_table
|
||||
|
||||
def cleanup_flows(self):
|
||||
flows = self.dump_flows_all_tables()
|
||||
for flow, cookie, table in self._filter_flows(flows):
|
||||
# deleting a stale flow should be rare.
|
||||
# it might deserve some attention
|
||||
LOG.warning("Deleting flow %s", flow)
|
||||
self.delete_flows(cookie=cookie + '/-1', table=table)
|
@ -1,33 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.openflow \
|
||||
import br_cookie
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import ofswitch
|
||||
|
||||
|
||||
class OVSAgentBridge(ofswitch.OpenFlowSwitchMixin,
|
||||
br_cookie.OVSBridgeCookieMixin, ovs_lib.OVSBridge):
|
||||
"""Common code for bridges used by OVS agent"""
|
||||
|
||||
def setup_controllers(self, conf):
|
||||
self.del_controller()
|
||||
|
||||
def drop_port(self, in_port):
|
||||
self.install_drop(priority=2, in_port=in_port)
|
@ -17,13 +17,12 @@ import functools
|
||||
import fixtures
|
||||
import netaddr
|
||||
from neutron_lib import constants
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.common import utils as common_utils
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
constants as ovs_consts)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
|
||||
br_int)
|
||||
from neutron.tests.common import machine_fixtures
|
||||
from neutron.tests.common import net_helpers
|
||||
|
||||
@ -400,11 +399,17 @@ class OVSConnectionTester(OVSBaseConnectionTester):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ip_cidr, br_int_cls):
|
||||
super(OVSConnectionTester, self).__init__(ip_cidr)
|
||||
self.br_int_cls = br_int_cls
|
||||
|
||||
def _setUp(self):
|
||||
super(OVSConnectionTester, self)._setUp()
|
||||
br_name = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture()).bridge.br_name
|
||||
self.bridge = br_int.OVSIntegrationBridge(br_name)
|
||||
self.bridge = self.br_int_cls(br_name)
|
||||
self.bridge.set_secure_mode()
|
||||
self.bridge.setup_controllers(cfg.CONF)
|
||||
self.bridge.setup_default_table()
|
||||
machines = self.useFixture(
|
||||
machine_fixtures.PeerMachines(
|
||||
|
@ -197,7 +197,6 @@ class OVSConfigFixture(ConfigFixture):
|
||||
'ovs': {
|
||||
'local_ip': local_ip,
|
||||
'integration_bridge': self._generate_integration_bridge(),
|
||||
'of_interface': host_desc.of_interface,
|
||||
'bridge_mappings': '%s:%s' % (PHYSICAL_NETWORK_NAME, ext_dev)
|
||||
},
|
||||
'securitygroup': {
|
||||
@ -236,13 +235,12 @@ class OVSConfigFixture(ConfigFixture):
|
||||
})
|
||||
|
||||
def _setUp(self):
|
||||
if self.config['ovs']['of_interface'] == 'native':
|
||||
self.config['ovs'].update({
|
||||
'of_listen_port': self.useFixture(
|
||||
port.ExclusivePort(constants.PROTO_NAME_TCP,
|
||||
start=OVS_OF_PORT_LISTEN_START,
|
||||
end=OVS_OF_PORT_LISTEN_END)).port
|
||||
})
|
||||
self.config['ovs'].update({
|
||||
'of_listen_port': self.useFixture(
|
||||
port.ExclusivePort(constants.PROTO_NAME_TCP,
|
||||
start=OVS_OF_PORT_LISTEN_START,
|
||||
end=OVS_OF_PORT_LISTEN_END)).port
|
||||
})
|
||||
super(OVSConfigFixture, self)._setUp()
|
||||
|
||||
def _generate_integration_bridge(self):
|
||||
|
@ -72,14 +72,12 @@ class HostDescription(object):
|
||||
under?
|
||||
"""
|
||||
def __init__(self, l3_agent=False, dhcp_agent=False,
|
||||
of_interface='ovs-ofctl',
|
||||
l2_agent_type=constants.AGENT_TYPE_OVS,
|
||||
firewall_driver='noop', availability_zone=None,
|
||||
l3_agent_mode=None):
|
||||
self.l2_agent_type = l2_agent_type
|
||||
self.l3_agent = l3_agent
|
||||
self.dhcp_agent = dhcp_agent
|
||||
self.of_interface = of_interface
|
||||
self.firewall_driver = firewall_driver
|
||||
self.availability_zone = availability_zone
|
||||
self.l3_agent_mode = l3_agent_mode
|
||||
|
@ -25,7 +25,6 @@ from neutron.tests.fullstack import base
|
||||
from neutron.tests.fullstack.resources import config
|
||||
from neutron.tests.fullstack.resources import environment
|
||||
from neutron.tests.fullstack.resources import machine
|
||||
from neutron.tests.fullstack import utils
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
load_tests = testlib_api.module_load_tests
|
||||
@ -37,7 +36,6 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
|
||||
|
||||
of_interface = None
|
||||
arp_responder = False
|
||||
use_dhcp = True
|
||||
|
||||
@ -50,7 +48,6 @@ class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
|
||||
# agent types present on machines.
|
||||
environment.HostDescription(
|
||||
l3_agent=self.l2_pop,
|
||||
of_interface=self.of_interface,
|
||||
l2_agent_type=self.l2_agent_type,
|
||||
dhcp_agent=self.use_dhcp,
|
||||
)
|
||||
@ -104,7 +101,7 @@ class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
|
||||
class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
|
||||
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
network_scenarios = [
|
||||
scenarios = [
|
||||
('VXLAN', {'network_type': 'vxlan',
|
||||
'l2_pop': False}),
|
||||
('GRE-l2pop-arp_responder', {'network_type': 'gre',
|
||||
@ -112,8 +109,6 @@ class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
|
||||
'arp_responder': True}),
|
||||
('VLANs', {'network_type': 'vlan',
|
||||
'l2_pop': False})]
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
network_scenarios, utils.get_ovs_interface_scenarios())
|
||||
|
||||
def test_connectivity(self):
|
||||
self._test_connectivity()
|
||||
@ -125,7 +120,7 @@ class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop(
|
||||
num_hosts = 2
|
||||
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
network_scenarios = [
|
||||
scenarios = [
|
||||
('VXLAN', {'network_type': 'vxlan',
|
||||
'l2_pop': False}),
|
||||
('GRE and l2pop', {'network_type': 'gre',
|
||||
@ -133,12 +128,6 @@ class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop(
|
||||
('VLANs', {'network_type': 'vlan',
|
||||
'l2_pop': False})]
|
||||
|
||||
# Do not test for CLI ofctl interface as controller is irrelevant for CLI
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
network_scenarios,
|
||||
[(m, v) for (m, v) in utils.get_ovs_interface_scenarios()
|
||||
if v['of_interface'] != 'ovs-ofctl'])
|
||||
|
||||
def _test_controller_timeout_does_not_break_connectivity(self,
|
||||
kill_signal=None):
|
||||
# Environment preparation is effectively the same as connectivity test
|
||||
@ -203,7 +192,6 @@ class TestConnectivitySameNetworkNoDhcp(BaseConnectivitySameNetworkTest):
|
||||
use_dhcp = False
|
||||
network_type = 'vxlan'
|
||||
l2_pop = False
|
||||
of_interface = 'native'
|
||||
|
||||
def test_connectivity(self):
|
||||
self._test_connectivity()
|
||||
@ -228,8 +216,8 @@ class TestUninterruptedConnectivityOnL2AgentRestart(
|
||||
'l2_pop': False}),
|
||||
]
|
||||
scenarios = (
|
||||
testscenarios.multiply_scenarios(ovs_agent_scenario, network_scenarios,
|
||||
utils.get_ovs_interface_scenarios()) +
|
||||
testscenarios.multiply_scenarios(ovs_agent_scenario,
|
||||
network_scenarios) +
|
||||
testscenarios.multiply_scenarios(lb_agent_scenario, network_scenarios)
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,6 @@ class FirewallMigrationTestCase(base.BaseFullStackTestCase):
|
||||
host_descriptions = [
|
||||
environment.HostDescription(
|
||||
l3_agent=False,
|
||||
of_interface='native',
|
||||
l2_agent_type=constants.AGENT_TYPE_OVS,
|
||||
firewall_driver='iptables_hybrid',
|
||||
dhcp_agent=False,
|
||||
|
@ -27,13 +27,11 @@ from neutron.tests.fullstack.resources import machine
|
||||
|
||||
|
||||
class BaseLoggingTestCase(base.BaseFullStackTestCase):
|
||||
of_interface = None
|
||||
number_of_hosts = 1
|
||||
|
||||
def setUp(self):
|
||||
host_desc = [
|
||||
environment.HostDescription(
|
||||
of_interface=self.of_interface,
|
||||
l2_agent_type=constants.AGENT_TYPE_OVS,
|
||||
firewall_driver='openvswitch',
|
||||
dhcp_agent=True) for _ in range(self.number_of_hosts)]
|
||||
@ -90,8 +88,6 @@ class BaseLoggingTestCase(base.BaseFullStackTestCase):
|
||||
|
||||
|
||||
class TestLogging(BaseLoggingTestCase):
|
||||
of_interface = 'native'
|
||||
|
||||
def _create_network_log(self, resource_type,
|
||||
resource_id=None, target_id=None):
|
||||
return self.safe_client.create_network_log(
|
||||
|
@ -18,7 +18,6 @@ from neutron_lib import constants
|
||||
from neutron_lib.services.qos import constants as qos_consts
|
||||
from neutronclient.common import exceptions
|
||||
from oslo_utils import uuidutils
|
||||
import testscenarios
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import tc_lib
|
||||
@ -28,7 +27,6 @@ from neutron.tests.fullstack import base
|
||||
from neutron.tests.fullstack.resources import config as fullstack_config
|
||||
from neutron.tests.fullstack.resources import environment
|
||||
from neutron.tests.fullstack.resources import machine
|
||||
from neutron.tests.fullstack import utils as fullstack_utils
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
from neutron.conf.plugins.ml2.drivers import linuxbridge as \
|
||||
@ -48,7 +46,6 @@ DSCP_MARK = 16
|
||||
|
||||
|
||||
class BaseQoSRuleTestCase(object):
|
||||
of_interface = None
|
||||
number_of_hosts = 1
|
||||
physical_network = None
|
||||
|
||||
@ -63,7 +60,6 @@ class BaseQoSRuleTestCase(object):
|
||||
host_desc = [
|
||||
environment.HostDescription(
|
||||
l3_agent=False,
|
||||
of_interface=self.of_interface,
|
||||
l2_agent_type=self.l2_agent_type
|
||||
) for _ in range(self.number_of_hosts)]
|
||||
env_desc = environment.EnvironmentDescription(
|
||||
@ -344,12 +340,10 @@ class _TestBwLimitQoS(BaseQoSRuleTestCase):
|
||||
|
||||
class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
direction_scenarios = [
|
||||
scenarios = [
|
||||
('ingress', {'direction': constants.INGRESS_DIRECTION}),
|
||||
('egress', {'direction': constants.EGRESS_DIRECTION})
|
||||
]
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
direction_scenarios, fullstack_utils.get_ovs_interface_scenarios())
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_burst_value(limit, direction):
|
||||
@ -520,7 +514,6 @@ class _TestDscpMarkingQoS(BaseQoSRuleTestCase):
|
||||
|
||||
|
||||
class TestDscpMarkingQoSOvs(_TestDscpMarkingQoS, base.BaseFullStackTestCase):
|
||||
scenarios = fullstack_utils.get_ovs_interface_scenarios()
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
|
||||
def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark):
|
||||
@ -670,11 +663,9 @@ class _TestMinBwQoS(BaseQoSRuleTestCase):
|
||||
|
||||
class TestMinBwQoSOvs(_TestMinBwQoS, base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
direction_scenarios = [
|
||||
scenarios = [
|
||||
('egress', {'direction': constants.EGRESS_DIRECTION})
|
||||
]
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
direction_scenarios, fullstack_utils.get_ovs_interface_scenarios())
|
||||
|
||||
def _wait_for_min_bw_rule_applied(self, vm, min_bw, direction):
|
||||
if direction == constants.EGRESS_DIRECTION:
|
||||
|
@ -40,13 +40,10 @@ class OVSVersionChecker(object):
|
||||
|
||||
class BaseSecurityGroupsSameNetworkTest(base.BaseFullStackTestCase):
|
||||
|
||||
of_interface = None
|
||||
|
||||
def setUp(self):
|
||||
debug_iptables = self.firewall_driver.startswith("iptables")
|
||||
host_descriptions = [
|
||||
environment.HostDescription(
|
||||
of_interface=self.of_interface,
|
||||
l2_agent_type=self.l2_agent_type,
|
||||
firewall_driver=self.firewall_driver,
|
||||
dhcp_agent=True) for _ in range(self.num_hosts)]
|
||||
@ -92,17 +89,10 @@ class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest):
|
||||
# because of that using only one host is enough
|
||||
('ovs-hybrid', {
|
||||
'firewall_driver': 'iptables_hybrid',
|
||||
'of_interface': 'native',
|
||||
'l2_agent_type': constants.AGENT_TYPE_OVS,
|
||||
'num_hosts': 1}),
|
||||
('ovs-openflow-cli', {
|
||||
('ovs-openflow', {
|
||||
'firewall_driver': 'openvswitch',
|
||||
'of_interface': 'ovs-ofctl',
|
||||
'l2_agent_type': constants.AGENT_TYPE_OVS,
|
||||
'num_hosts': 2}),
|
||||
('ovs-openflow-native', {
|
||||
'firewall_driver': 'openvswitch',
|
||||
'of_interface': 'native',
|
||||
'l2_agent_type': constants.AGENT_TYPE_OVS,
|
||||
'num_hosts': 2}),
|
||||
('linuxbridge-iptables', {
|
||||
|
@ -1,18 +0,0 @@
|
||||
# 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.
|
||||
|
||||
|
||||
def get_ovs_interface_scenarios():
|
||||
return [
|
||||
('openflow-cli', {'of_interface': 'ovs-ofctl'}),
|
||||
('openflow-native', {'of_interface': 'native'}),
|
||||
]
|
@ -17,6 +17,7 @@
|
||||
import random
|
||||
|
||||
import eventlet
|
||||
import fixtures
|
||||
import mock
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib.utils import net
|
||||
@ -35,19 +36,77 @@ from neutron.conf.plugins.ml2.drivers import ovs_conf
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers \
|
||||
import qos_driver as ovs_qos_driver
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_int
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_phys
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
import br_tun
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import main as main_mod
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \
|
||||
as ovs_agent
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional.agent.linux import base
|
||||
|
||||
|
||||
class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
|
||||
class OVSOFControllerHelper(object):
|
||||
"""Helper class that runs os-ken openflow controller."""
|
||||
|
||||
def start_of_controller(self, conf):
|
||||
self.br_int_cls = None
|
||||
self.br_tun_cls = None
|
||||
self.br_phys_cls = None
|
||||
self.init_done = False
|
||||
self.init_done_ev = eventlet.event.Event()
|
||||
self.main_ev = eventlet.event.Event()
|
||||
self.addCleanup(self._kill_main)
|
||||
retry_count = 3
|
||||
while True:
|
||||
# Try a few different ports as a port conflict
|
||||
# causes the test to fail.
|
||||
conf.set_override('of_listen_port',
|
||||
net_helpers.get_free_namespace_port(
|
||||
n_const.PROTO_NAME_TCP),
|
||||
group='OVS')
|
||||
cfg.CONF.set_override('of_listen_port',
|
||||
conf.OVS.of_listen_port,
|
||||
group='OVS')
|
||||
main_mod.init_config()
|
||||
self._main_thread = eventlet.spawn(self._kick_main)
|
||||
|
||||
# Wait for _kick_main -> openflow main -> _agent_main
|
||||
# NOTE(yamamoto): This complexity came from how we run openflow
|
||||
# controller. 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 openflow main
|
||||
# in a separate thread instead.
|
||||
try:
|
||||
while not self.init_done:
|
||||
self.init_done_ev.wait()
|
||||
break
|
||||
except fixtures.TimeoutException:
|
||||
self._kill_main()
|
||||
retry_count -= 1
|
||||
if retry_count < 0:
|
||||
raise Exception('port allocation failed')
|
||||
|
||||
def _kick_main(self):
|
||||
with mock.patch.object(ovs_agent, 'main', self._agent_main):
|
||||
main_mod.main()
|
||||
|
||||
def _kill_main(self):
|
||||
self.main_ev.send()
|
||||
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']
|
||||
|
||||
# signal to setUp()
|
||||
self.init_done = True
|
||||
self.init_done_ev.send()
|
||||
|
||||
self.main_ev.wait()
|
||||
|
||||
|
||||
class OVSAgentTestFramework(base.BaseOVSLinuxTestCase, OVSOFControllerHelper):
|
||||
|
||||
def setUp(self):
|
||||
super(OVSAgentTestFramework, self).setUp()
|
||||
@ -68,6 +127,7 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
|
||||
self.config = self._configure_agent()
|
||||
self.driver = interface.OVSInterfaceDriver(self.config)
|
||||
self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
self.start_of_controller(self.config)
|
||||
|
||||
def _get_config_opts(self):
|
||||
config = cfg.ConfigOpts()
|
||||
@ -95,9 +155,9 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
|
||||
|
||||
def _bridge_classes(self):
|
||||
return {
|
||||
'br_int': br_int.OVSIntegrationBridge,
|
||||
'br_phys': br_phys.OVSPhysicalBridge,
|
||||
'br_tun': br_tun.OVSTunnelBridge
|
||||
'br_int': self.br_int_cls,
|
||||
'br_phys': self.br_phys_cls,
|
||||
'br_tun': self.br_tun_cls
|
||||
}
|
||||
|
||||
def create_agent(self, create_tunnels=True, ancillary_bridge=None,
|
||||
|
@ -34,6 +34,7 @@ from neutron.cmd.sanity import checks
|
||||
from neutron.conf.agent import securitygroups_rpc as security_config
|
||||
from neutron.tests.common import conn_testers
|
||||
from neutron.tests.common import helpers
|
||||
from neutron.tests.functional.agent.l2 import base as l2_base
|
||||
from neutron.tests.functional.agent.linux import base as linux_base
|
||||
from neutron.tests.functional import constants as test_constants
|
||||
|
||||
@ -131,8 +132,12 @@ class BaseFirewallTestCase(linux_base.BaseOVSLinuxTestCase):
|
||||
"OVS>=2.5). More info at "
|
||||
"https://github.com/openvswitch/ovs/blob/master/"
|
||||
"FAQ.md")
|
||||
self.of_helper = l2_base.OVSOFControllerHelper()
|
||||
self.of_helper.addCleanup = self.addCleanup
|
||||
self.of_helper.start_of_controller(cfg.CONF)
|
||||
tester = self.useFixture(
|
||||
conn_testers.OVSConnectionTester(self.ip_cidr))
|
||||
conn_testers.OVSConnectionTester(self.ip_cidr,
|
||||
self.of_helper.br_int_cls))
|
||||
firewall_drv = openvswitch_firewall.OVSFirewallDriver(tester.bridge)
|
||||
return tester, firewall_drv
|
||||
|
||||
|
@ -40,8 +40,10 @@ class TestOVSAgent(base.OVSAgentTestFramework):
|
||||
|
||||
def test_no_stale_flows_after_port_delete(self):
|
||||
def find_drop_flow(ofport, flows):
|
||||
for flow in flows.split("\n"):
|
||||
if "in_port=%d" % ofport in flow and "actions=drop" in flow:
|
||||
for flow in flows:
|
||||
# flow.instruction == [] means actions=drop
|
||||
if (not flow.instructions and
|
||||
('in_port', ofport) in flow.match.items()):
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -96,6 +98,7 @@ class TestOVSAgent(base.OVSAgentTestFramework):
|
||||
('br_int', 'br_tun', 'br_phys')):
|
||||
actual = self.ovs.db_get_val('Bridge', br_name, 'datapath_type')
|
||||
self.assertEqual(expected, actual)
|
||||
self.stop_agent(agent, self.agent_thread)
|
||||
|
||||
def test_datapath_type_change(self):
|
||||
self._check_datapath_type_netdev('system')
|
||||
|
@ -13,15 +13,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import eventlet
|
||||
import fixtures
|
||||
import mock
|
||||
import testscenarios
|
||||
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import importutils
|
||||
from testtools.content import text_content
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
@ -35,11 +29,11 @@ from neutron.plugins.ml2.drivers.openvswitch.agent \
|
||||
from neutron.tests.common import base as common_base
|
||||
from neutron.tests.common import helpers
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional.agent.l2 import base as l2_base
|
||||
from neutron.tests.functional.agent import test_ovs_lib
|
||||
from neutron.tests.functional import base
|
||||
from neutron.tests import tools
|
||||
|
||||
load_tests = testscenarios.load_tests_apply_scenarios
|
||||
|
||||
OVS_TRACE_FINAL_FLOW = 'Final flow'
|
||||
OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions'
|
||||
|
||||
@ -47,52 +41,18 @@ cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
|
||||
'common.config')
|
||||
|
||||
|
||||
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase):
|
||||
scenarios = [
|
||||
('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
||||
'agent.openflow.ovs_ofctl.main')}),
|
||||
('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
||||
'agent.openflow.native.main')})]
|
||||
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase,
|
||||
base.BaseSudoTestCase,
|
||||
l2_base.OVSOFControllerHelper):
|
||||
|
||||
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_ev = eventlet.event.Event()
|
||||
self.addCleanup(self._kill_main)
|
||||
retry_count = 3
|
||||
while True:
|
||||
cfg.CONF.set_override('of_listen_port',
|
||||
net_helpers.get_free_namespace_port(
|
||||
n_const.PROTO_NAME_TCP),
|
||||
group='OVS')
|
||||
self.of_interface_mod.init_config()
|
||||
self._main_thread = eventlet.spawn(self._kick_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.
|
||||
try:
|
||||
while not self.init_done:
|
||||
self.init_done_ev.wait()
|
||||
break
|
||||
except fixtures.TimeoutException:
|
||||
self._kill_main()
|
||||
retry_count -= 1
|
||||
if retry_count < 0:
|
||||
raise Exception('port allocation failed')
|
||||
self.start_of_controller(cfg.CONF)
|
||||
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()
|
||||
|
||||
def _run_trace(self, brname, spec):
|
||||
required_keys = [OVS_TRACE_FINAL_FLOW, OVS_TRACE_DATAPATH_ACTIONS]
|
||||
@ -112,29 +72,6 @@ class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase):
|
||||
|
||||
return trace
|
||||
|
||||
def _kick_main(self):
|
||||
with mock.patch.object(ovsagt, 'main', self._agent_main):
|
||||
self.of_interface_mod.main()
|
||||
|
||||
def _kill_main(self):
|
||||
self.main_ev.send()
|
||||
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()
|
||||
|
||||
self.main_ev.wait()
|
||||
|
||||
|
||||
class ARPSpoofTestCase(OVSAgentTestBase):
|
||||
def setUp(self):
|
||||
@ -357,9 +294,7 @@ class DeleteFlowsTestCase(OVSAgentTestBase):
|
||||
class OVSFlowTestCase(OVSAgentTestBase):
|
||||
"""Tests defined in this class use ovs-appctl ofproto/trace commands,
|
||||
which simulate processing of imaginary packets, to check desired actions
|
||||
are correctly set up by OVS flows. In this way, subtle variations in
|
||||
flows between of_interface drivers are absorbed and the same tests work
|
||||
against those drivers.
|
||||
are correctly set up by OVS flows.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
@ -466,9 +401,6 @@ class OVSFlowTestCase(OVSAgentTestBase):
|
||||
self.assertIn("pop_vlan,", trace["Datapath actions"])
|
||||
|
||||
def test_bundled_install(self):
|
||||
if 'ovs_ofctl' in self.main_module:
|
||||
self.skip("ovs-ofctl of_interface doesn't have bundled()")
|
||||
|
||||
kwargs = {'in_port': 345, 'vlan_tci': 0x1321}
|
||||
dst_p = self.useFixture(
|
||||
net_helpers.OVSPortFixture(self.br_tun, self.namespace)).port
|
||||
|
@ -27,8 +27,6 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
||||
ovs_agent_extension_api as ovs_ext_api)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
constants as ovs_consts)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
|
||||
ovs_bridge)
|
||||
from neutron.services.logapi.drivers.openvswitch import (
|
||||
ovs_firewall_log as ovs_fw_log)
|
||||
from neutron.tests.functional.agent import test_firewall
|
||||
@ -62,11 +60,12 @@ class LoggingExtensionTestFramework(test_firewall.BaseFirewallTestCase):
|
||||
self.log_driver = self.initialize_ovs_fw_log()
|
||||
|
||||
def initialize_ovs_fw_log(self):
|
||||
mock.patch('os_ken.base.app_manager.AppManager.get_instance').start()
|
||||
self.int_br = ovs_ext_api.OVSCookieBridge(
|
||||
self.of_helper.br_int_cls(self.tester.bridge.br_name))
|
||||
self.tun_br = self.of_helper.br_tun_cls('br-tun')
|
||||
agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
||||
ovs_bridge.OVSAgentBridge(self.tester.bridge.br_name),
|
||||
ovs_bridge.OVSAgentBridge('br-tun'),
|
||||
{'physnet1': ovs_bridge.OVSAgentBridge('br-physnet1')})
|
||||
self.int_br, self.tun_br,
|
||||
{'physnet1': self.of_helper.br_phys_cls('br-physnet1')})
|
||||
log_driver = ovs_fw_log.OVSFirewallLoggingDriver(agent_api)
|
||||
log_driver.initialize(self.resource_rpc)
|
||||
return log_driver
|
||||
|
@ -32,7 +32,7 @@ from neutron.objects.qos import rule
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
||||
ovs_agent_extension_api as ovs_ext_api)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native import (
|
||||
ovs_bridge)
|
||||
from neutron.tests import base
|
||||
|
||||
@ -224,10 +224,14 @@ class QosExtensionBaseTestCase(base.BaseTestCase):
|
||||
self.qos_ext = qos.QosAgentExtension()
|
||||
self.context = context.get_admin_context()
|
||||
self.connection = mock.Mock()
|
||||
os_ken_app = mock.Mock()
|
||||
self.agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
||||
ovs_bridge.OVSAgentBridge('br-int'),
|
||||
ovs_bridge.OVSAgentBridge('br-tun'),
|
||||
{'phynet1': ovs_bridge.OVSAgentBridge('br-phynet1')})
|
||||
ovs_bridge.OVSAgentBridge(
|
||||
'br-int', os_ken_app=os_ken_app),
|
||||
ovs_bridge.OVSAgentBridge(
|
||||
'br-tun', os_ken_app=os_ken_app),
|
||||
{'phynet1': ovs_bridge.OVSAgentBridge(
|
||||
'br-phynet1', os_ken_app=os_ken_app)})
|
||||
self.qos_ext.consume_api(self.agent_api)
|
||||
|
||||
# Don't rely on used driver
|
||||
|
@ -26,7 +26,7 @@ from neutron.agent.linux.openvswitch_firewall import exceptions
|
||||
from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
|
||||
as ovs_consts
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import ovs_bridge
|
||||
from neutron.tests import base
|
||||
|
||||
@ -752,7 +752,7 @@ class TestCookieContext(base.BaseTestCase):
|
||||
|
||||
self.execute = mock.patch.object(
|
||||
utils, "execute", spec=utils.execute).start()
|
||||
bridge = ovs_bridge.OVSAgentBridge('foo')
|
||||
bridge = ovs_bridge.OVSAgentBridge('foo', os_ken_app=mock.Mock())
|
||||
mock.patch.object(
|
||||
ovsfw.OVSFirewallDriver, 'initialize_bridge',
|
||||
return_value=bridge.deferred(
|
||||
|
@ -23,7 +23,7 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
||||
ovs_agent_extension_api as ovs_ext_api)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import (
|
||||
qos_driver)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native import (
|
||||
ovs_bridge)
|
||||
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import (
|
||||
ovs_test_base)
|
||||
@ -41,10 +41,14 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase):
|
||||
self.qos_driver = qos_driver.QosOVSAgentDriver()
|
||||
self.mock_clear_minimum_bandwidth_qos = mock.patch.object(
|
||||
self.qos_driver, '_minimum_bandwidth_initialize').start()
|
||||
os_ken_app = mock.Mock()
|
||||
self.agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
||||
ovs_bridge.OVSAgentBridge('br-int'),
|
||||
ovs_bridge.OVSAgentBridge('br-tun'),
|
||||
{'phys1': ovs_bridge.OVSAgentBridge('br-phys1')})
|
||||
ovs_bridge.OVSAgentBridge(
|
||||
'br-int', os_ken_app=os_ken_app),
|
||||
ovs_bridge.OVSAgentBridge(
|
||||
'br-tun', os_ken_app=os_ken_app),
|
||||
{'phys1': ovs_bridge.OVSAgentBridge(
|
||||
'br-phys1', os_ken_app=os_ken_app)})
|
||||
self.qos_driver.consume_api(self.agent_api)
|
||||
self.qos_driver.initialize()
|
||||
self.qos_driver.br_int = mock.Mock()
|
||||
|
175
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py
175
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py
@ -1,175 +0,0 @@
|
||||
# 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_lib import constants
|
||||
|
||||
from neutron.tests.unit.plugins.ml2.drivers.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)
|
||||
|
||||
def test_dump_flows_for_table(self):
|
||||
table = 23
|
||||
with mock.patch.object(self.br, 'run_ofctl') as run_ofctl:
|
||||
self.br.dump_flows(table)
|
||||
run_ofctl.assert_has_calls([mock.call("dump-flows", mock.ANY)])
|
||||
|
||||
def test_dump_all_flows(self):
|
||||
with mock.patch.object(self.br, 'run_ofctl') as run_ofctl:
|
||||
self.br.dump_flows_all_tables()
|
||||
run_ofctl.assert_has_calls([mock.call("dump-flows", [])])
|
||||
|
||||
|
||||
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,
|
||||
icmp_type=constants.ICMPV6_TYPE_RA),
|
||||
]
|
||||
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',
|
||||
icmp_type=constants.ICMPV6_TYPE_RA),
|
||||
]
|
||||
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)
|
@ -1,260 +0,0 @@
|
||||
# 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_lib import constants as const
|
||||
|
||||
from neutron.tests.unit.plugins.ml2.drivers.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.add_flow(priority=0, table=23, actions='drop'),
|
||||
call.add_flow(priority=0, table=0, actions='resubmit(,60)'),
|
||||
call.add_flow(priority=3, table=60, actions='normal'),
|
||||
call.add_flow(priority=0, table=24, actions='drop'),
|
||||
call.add_flow(actions='drop', dl_vlan=4095,
|
||||
priority=65535, table=0)
|
||||
]
|
||||
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,resubmit(,60)' % 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,resubmit(,60)' % 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='mod_dl_src:%(mac)s,resubmit(,60)' % {
|
||||
'mac': gateway_mac,
|
||||
}),
|
||||
call.add_flow(priority=4, table=60, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag,
|
||||
actions='strip_vlan,output:%(port)s' % {
|
||||
'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(
|
||||
strict=True, priority=4, table=1, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag),
|
||||
call.delete_flows(
|
||||
strict=True, priority=4, table=60, 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='mod_dl_src:%(mac)s,resubmit(,60)' % {
|
||||
'mac': gateway_mac,
|
||||
}),
|
||||
call.add_flow(priority=4, table=60, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag,
|
||||
actions='strip_vlan,output:%(port)s' % {
|
||||
'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(
|
||||
strict=True, priority=4, table=2, dl_dst=dst_mac,
|
||||
dl_vlan=vlan_tag),
|
||||
call.delete_flows(
|
||||
strict=True, priority=4, table=60, 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(dl_src=mac, table=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(dl_src=mac, table=0, in_port=port),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def test_install_icmpv6_na_spoofing_protection(self):
|
||||
port = 8888
|
||||
ip_addresses = ['2001:db8::1', 'fdf8:f53b:82e4::1/128']
|
||||
self.br.install_icmpv6_na_spoofing_protection(port, ip_addresses)
|
||||
expected = [
|
||||
call.add_flow(dl_type=const.ETHERTYPE_IPV6,
|
||||
actions='resubmit(,60)',
|
||||
icmp_type=const.ICMPV6_TYPE_NA,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
nd_target='2001:db8::1',
|
||||
priority=2, table=24, in_port=8888),
|
||||
call.add_flow(dl_type=const.ETHERTYPE_IPV6,
|
||||
actions='resubmit(,60)',
|
||||
icmp_type=const.ICMPV6_TYPE_NA,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
nd_target='fdf8:f53b:82e4::1/128',
|
||||
priority=2, table=24, in_port=8888),
|
||||
call.add_flow(dl_type=const.ETHERTYPE_IPV6,
|
||||
icmp_type=const.ICMPV6_TYPE_NA,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP,
|
||||
priority=10, table=0, in_port=8888,
|
||||
actions='resubmit(,24)')
|
||||
]
|
||||
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='resubmit(,25)',
|
||||
arp_spa='192.0.2.1',
|
||||
priority=2, table=24, in_port=8888),
|
||||
call.add_flow(proto='arp', actions='resubmit(,25)',
|
||||
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=0, in_port=8888, proto='arp'),
|
||||
call.delete_flows(table=0, in_port=8888,
|
||||
icmp_type=const.ICMPV6_TYPE_NA,
|
||||
nw_proto=const.PROTO_NUM_IPV6_ICMP),
|
||||
call.delete_flows(table=24, in_port=8888),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -1,97 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.common.constants \
|
||||
as ovs_const
|
||||
from neutron.tests.unit.plugins.ml2.drivers.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.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(dl_src=mac, table=3),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
@ -1,320 +0,0 @@
|
||||
# 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.ml2.drivers.openvswitch.agent.common.constants \
|
||||
as ovs_const
|
||||
from neutron.tests.unit.plugins.ml2.drivers.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)
|
||||
self.stamp = self.br.default_cookie
|
||||
|
||||
def test_setup_default_table(self):
|
||||
patch_int_ofport = 5555
|
||||
mock_do_action_flows = mock.patch.object(self.br,
|
||||
'do_action_flows').start()
|
||||
self.mock.attach_mock(mock_do_action_flows, 'do_action_flows')
|
||||
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
|
||||
arp_responder_enabled=False)
|
||||
flow_args = [{'priority': 1, 'in_port': patch_int_ofport,
|
||||
'actions': 'resubmit(,2)'},
|
||||
{'priority': 0, 'actions': 'drop'},
|
||||
{'priority': 0, 'table': 2,
|
||||
'dl_dst': '00:00:00:00:00:00/01:00:00:00:00:00',
|
||||
'actions': 'resubmit(,20)'},
|
||||
{'priority': 0, 'table': 2,
|
||||
'dl_dst': '01:00:00:00:00:00/01:00:00:00:00:00',
|
||||
'actions': 'resubmit(,22)'},
|
||||
{'priority': 0, 'table': 3, 'actions': 'drop'},
|
||||
{'priority': 0, 'table': 4, 'actions': 'drop'},
|
||||
{'priority': 0, 'table': 6, 'actions': 'drop'},
|
||||
{'priority': 1, 'table': 10,
|
||||
'actions': 'learn(cookie=' + str(self.stamp) +
|
||||
',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},
|
||||
{'priority': 0, 'table': 20, 'actions': 'resubmit(,22)'}
|
||||
]
|
||||
expected = [call.do_action_flows('add', flow_args, False),
|
||||
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
|
||||
mock_do_action_flows = mock.patch.object(self.br,
|
||||
'do_action_flows').start()
|
||||
self.mock.attach_mock(mock_do_action_flows, 'do_action_flows')
|
||||
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
|
||||
arp_responder_enabled=True)
|
||||
flow_args = [{'priority': 1, 'in_port': patch_int_ofport,
|
||||
'actions': 'resubmit(,2)'},
|
||||
{'priority': 0, 'actions': 'drop'},
|
||||
{'priority': 1, 'table': 2, 'dl_dst': 'ff:ff:ff:ff:ff:ff',
|
||||
'actions': 'resubmit(,21)', 'proto': 'arp'},
|
||||
{'priority': 0, 'table': 2,
|
||||
'dl_dst': '00:00:00:00:00:00/01:00:00:00:00:00',
|
||||
'actions': 'resubmit(,20)'},
|
||||
{'priority': 0, 'table': 2,
|
||||
'dl_dst': '01:00:00:00:00:00/01:00:00:00:00:00',
|
||||
'actions': 'resubmit(,22)'},
|
||||
{'priority': 0, 'table': 3, 'actions': 'drop'},
|
||||
{'priority': 0, 'table': 4, 'actions': 'drop'},
|
||||
{'priority': 0, 'table': 6, 'actions': 'drop'},
|
||||
{'priority': 1, 'table': 10,
|
||||
'actions': 'learn(cookie=' + str(self.stamp) +
|
||||
',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},
|
||||
{'priority': 0, 'table': 20, 'actions': 'resubmit(,22)'},
|
||||
{'priority': 0, 'table': 21, 'actions': 'resubmit(,22)'}
|
||||
]
|
||||
expected = [call.do_action_flows('add', flow_args, False),
|
||||
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(dl_src=mac, table=9),
|
||||
]
|
||||
self.assertEqual(expected, self.mock.mock_calls)
|
||||
|
||||
def _mock_add_tunnel_port(self, deferred_br=False):
|
||||
port_name = 'fake_port'
|
||||
remote_ip = '192.168.1.3'
|
||||
local_ip = '192.168.1.2'
|
||||
tunnel_type = 'vxlan'
|
||||
vxlan_udp_port = '4789'
|
||||
dont_fragment = True
|
||||
if deferred_br:
|
||||
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.add_port',
|
||||
return_value=9999) as add_port, \
|
||||
self.br.deferred() as deferred_br:
|
||||
ofport = deferred_br.add_tunnel_port(port_name, remote_ip,
|
||||
local_ip, tunnel_type,
|
||||
vxlan_udp_port,
|
||||
dont_fragment)
|
||||
else:
|
||||
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.add_port',
|
||||
return_value=9999) as add_port:
|
||||
ofport = self.br.add_tunnel_port(port_name, remote_ip,
|
||||
local_ip, tunnel_type,
|
||||
vxlan_udp_port,
|
||||
dont_fragment)
|
||||
self.assertEqual(9999, ofport)
|
||||
self.assertEqual(1, add_port.call_count)
|
||||
self.assertEqual(port_name, add_port.call_args[0][0])
|
||||
|
||||
def _mock_delete_port(self, deferred_br=False):
|
||||
port_name = 'fake_port'
|
||||
if deferred_br:
|
||||
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||
'delete_port') as delete_port, \
|
||||
self.br.deferred() as deferred_br:
|
||||
deferred_br.delete_port(port_name)
|
||||
else:
|
||||
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||
'delete_port') as delete_port:
|
||||
self.br.delete_port(port_name)
|
||||
self.assertEqual([call(port_name)], delete_port.mock_calls)
|
||||
|
||||
def test_add_tunnel_port(self):
|
||||
self._mock_add_tunnel_port()
|
||||
|
||||
def test_delete_port(self):
|
||||
self._mock_delete_port()
|
||||
|
||||
def test_deferred_br_add_tunnel_port(self):
|
||||
self._mock_add_tunnel_port(True)
|
||||
|
||||
def test_deferred_br_delete_port(self):
|
||||
self._mock_delete_port(True)
|
@ -16,7 +16,7 @@
|
||||
import mock
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import ovs_bridge
|
||||
from neutron.tests import base
|
||||
|
||||
@ -29,7 +29,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
|
||||
'neutron.agent.ovsdb.impl_idl._connection')
|
||||
conn_patcher.start()
|
||||
self.addCleanup(conn_patcher.stop)
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int')
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int', os_ken_app=mock.Mock())
|
||||
|
||||
def test_reserved_cookies(self):
|
||||
def_cookie = self.br.default_cookie
|
||||
@ -49,7 +49,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
|
||||
self.assertNotIn(requested_cookie, self.br.reserved_cookies)
|
||||
|
||||
def test_set_agent_uuid_stamp(self):
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int')
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int', os_ken_app=mock.Mock())
|
||||
def_cookie = self.br.default_cookie
|
||||
new_cookie = ovs_lib.generate_random_cookie()
|
||||
|
||||
@ -60,7 +60,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
|
||||
self.assertNotIn(def_cookie, self.br.reserved_cookies)
|
||||
|
||||
def test_set_agent_uuid_stamp_with_reserved_cookie(self):
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int')
|
||||
self.br = ovs_bridge.OVSAgentBridge('br-int', os_ken_app=mock.Mock())
|
||||
def_cookie = self.br.default_cookie
|
||||
new_cookie = self.br.request_cookie()
|
||||
|
||||
|
@ -38,33 +38,8 @@ class OVSAgentConfigTestBase(base.BaseTestCase):
|
||||
self.mod_dvr_agent = importutils.import_module(_DVR_AGENT_NAME)
|
||||
|
||||
|
||||
class OVSAgentTestBase(OVSAgentConfigTestBase):
|
||||
def setUp(self):
|
||||
super(OVSAgentTestBase, self).setUp()
|
||||
conn_patcher = mock.patch(
|
||||
'neutron.agent.ovsdb.impl_idl._connection')
|
||||
conn_patcher.start()
|
||||
self.addCleanup(conn_patcher.stop)
|
||||
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)
|
||||
class OVSOSKenTestBase(OVSAgentConfigTestBase):
|
||||
|
||||
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'
|
||||
|
||||
|
||||
class OVSOSKenTestBase(OVSAgentTestBase):
|
||||
_DRIVER_PACKAGE = _AGENT_PACKAGE + '.openflow.native'
|
||||
_BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge'
|
||||
_BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge'
|
||||
@ -75,6 +50,13 @@ class OVSOSKenTestBase(OVSAgentTestBase):
|
||||
self.fake_oflib_of.start()
|
||||
self.addCleanup(self.fake_oflib_of.stop)
|
||||
super(OVSOSKenTestBase, self).setUp()
|
||||
conn_patcher = mock.patch(
|
||||
'neutron.agent.ovsdb.impl_idl._connection')
|
||||
conn_patcher.start()
|
||||
self.addCleanup(conn_patcher.stop)
|
||||
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)
|
||||
os_ken_app = mock.Mock()
|
||||
self.br_int_cls = functools.partial(self.br_int_cls,
|
||||
os_ken_app=os_ken_app)
|
||||
@ -82,3 +64,10 @@ class OVSOSKenTestBase(OVSAgentTestBase):
|
||||
os_ken_app=os_ken_app)
|
||||
self.br_tun_cls = functools.partial(self.br_tun_cls,
|
||||
os_ken_app=os_ken_app)
|
||||
|
||||
def _bridge_classes(self):
|
||||
return {
|
||||
'br_int': self.br_int_cls,
|
||||
'br_phys': self.br_phys_cls,
|
||||
'br_tun': self.br_tun_cls,
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \
|
||||
import ovs_test_base
|
||||
|
||||
|
||||
class TestOVSAgentExtensionAPI(ovs_test_base.OVSOFCtlTestBase):
|
||||
class TestOVSAgentExtensionAPI(ovs_test_base.OVSOSKenTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOVSAgentExtensionAPI, self).setUp()
|
||||
@ -69,121 +69,6 @@ class TestOVSAgentExtensionAPI(ovs_test_base.OVSOFCtlTestBase):
|
||||
self._test_bridge(self.br_phys[phys_br.br_name], phys_br)
|
||||
|
||||
|
||||
class TestOVSCookieBridgeOFCtl(ovs_test_base.OVSOFCtlTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOVSCookieBridgeOFCtl, self).setUp()
|
||||
self.bridge = self.br_int_cls("br-int")
|
||||
mock.patch.object(self.bridge, "run_ofctl").start()
|
||||
|
||||
self.tested_bridge = ovs_ext_agt.OVSCookieBridge(self.bridge)
|
||||
|
||||
# mocking do_action_flows does not work, because this method is
|
||||
# later wrapped by the cookie bridge code, and six.wraps apparently
|
||||
# can't wrap a mock, so we mock deeper
|
||||
self.mock_build_flow_expr_str = mock.patch(
|
||||
'neutron.agent.common.ovs_lib._build_flow_expr_str',
|
||||
return_value="").start()
|
||||
|
||||
def test_cookie(self):
|
||||
self.assertNotEqual(self.bridge._default_cookie,
|
||||
self.tested_bridge._default_cookie)
|
||||
|
||||
def test_reserved(self):
|
||||
self.assertIn(self.tested_bridge._default_cookie,
|
||||
self.bridge.reserved_cookies)
|
||||
|
||||
def assert_mock_build_flow_expr_str_call(self, action, kwargs_list,
|
||||
strict=False):
|
||||
self.mock_build_flow_expr_str.assert_called_once_with(
|
||||
kwargs_list[0],
|
||||
action,
|
||||
strict
|
||||
)
|
||||
|
||||
def test_add_flow_without_cookie(self):
|
||||
self.tested_bridge.add_flow(in_port=1, actions="output:2")
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'add',
|
||||
[{"in_port": 1,
|
||||
"actions": "output:2",
|
||||
"cookie": self.tested_bridge._default_cookie}]
|
||||
)
|
||||
|
||||
def test_mod_flow_without_cookie(self):
|
||||
self.tested_bridge.mod_flow(in_port=1, actions="output:2")
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'mod',
|
||||
[{"in_port": 1,
|
||||
"actions": "output:2",
|
||||
"cookie": self.tested_bridge._default_cookie}]
|
||||
)
|
||||
|
||||
def test_del_flows_without_cookie(self):
|
||||
self.tested_bridge.delete_flows(in_port=1)
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'del',
|
||||
[{"in_port": 1,
|
||||
"cookie": str(self.tested_bridge._default_cookie) + '/-1'}]
|
||||
)
|
||||
|
||||
def test_add_flow_with_cookie(self):
|
||||
self.tested_bridge.add_flow(cookie=1234,
|
||||
in_port=1, actions="output:2")
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'add',
|
||||
[{"in_port": 1,
|
||||
"actions": "output:2",
|
||||
"cookie": 1234}]
|
||||
)
|
||||
|
||||
def test_mod_flow_with_cookie(self):
|
||||
self.tested_bridge.mod_flow(cookie='1234',
|
||||
in_port=1, actions="output:2")
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'mod',
|
||||
[{"in_port": 1,
|
||||
"actions": "output:2",
|
||||
"cookie": "1234"}]
|
||||
)
|
||||
|
||||
def test_del_flows_with_cookie(self):
|
||||
self.tested_bridge.delete_flows(cookie=1234, in_port=1)
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'del',
|
||||
[{"in_port": 1,
|
||||
"cookie": "1234/-1"}]
|
||||
)
|
||||
|
||||
def test_mod_flow_with_cookie_mask(self):
|
||||
self.tested_bridge.mod_flow(cookie='1234/3',
|
||||
in_port=1, actions="output:2")
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'mod',
|
||||
[{"in_port": 1,
|
||||
"actions": "output:2",
|
||||
"cookie": str(1234) + '/3'}]
|
||||
)
|
||||
|
||||
def test_del_flows_with_cookie_mask(self):
|
||||
self.tested_bridge.delete_flows(cookie='1234/7', in_port=1)
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'del',
|
||||
[{"in_port": 1,
|
||||
"cookie": str(1234) + '/7'}]
|
||||
)
|
||||
|
||||
def test_install_drop(self):
|
||||
self.tested_bridge.install_drop()
|
||||
self.assert_mock_build_flow_expr_str_call(
|
||||
'add',
|
||||
[{"table": 0,
|
||||
"priority": 0,
|
||||
"actions": "drop",
|
||||
"cookie": self.tested_bridge._default_cookie}]
|
||||
)
|
||||
|
||||
|
||||
class TestOVSCookieBridgeOSKen(native_ovs_bridge_test_base.OVSBridgeTestBase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -2422,31 +2422,8 @@ class TestOvsNeutronAgent(object):
|
||||
mock_update_segid.assert_not_called()
|
||||
|
||||
|
||||
class TestOvsNeutronAgentOFCtl(TestOvsNeutronAgent,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
def test_cleanup_stale_flows(self):
|
||||
with mock.patch.object(self.agent.int_br,
|
||||
'dump_flows_all_tables') as dump_flows,\
|
||||
mock.patch.object(self.agent.int_br,
|
||||
'delete_flows') as del_flow:
|
||||
self.agent.int_br.set_agent_uuid_stamp(1234)
|
||||
dump_flows.return_value = [
|
||||
'cookie=0x4d2, duration=50.156s, table=0,actions=drop',
|
||||
'cookie=0x4321, duration=54.143s, table=2, priority=0',
|
||||
'cookie=0x2345, duration=50.125s, table=2, priority=0',
|
||||
'cookie=0x4d2, duration=52.112s, table=3, actions=drop',
|
||||
]
|
||||
self.agent.iter_num = 3
|
||||
self.agent.cleanup_stale_flows()
|
||||
expected = [
|
||||
mock.call(cookie='0x4321/-1', table='2'),
|
||||
mock.call(cookie='0x2345/-1', table='2'),
|
||||
]
|
||||
self.assertEqual(expected, del_flow.mock_calls)
|
||||
|
||||
|
||||
class TestOvsNeutronAgentOSKen(TestOvsNeutronAgent,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
def test_cleanup_stale_flows(self):
|
||||
uint64_max = (1 << 64) - 1
|
||||
with mock.patch.object(self.agent.int_br,
|
||||
@ -2600,11 +2577,6 @@ class AncillaryBridgesTest(object):
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class AncillaryBridgesTestOFCtl(AncillaryBridgesTest,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class AncillaryBridgesTestOSKen(AncillaryBridgesTest,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
pass
|
||||
@ -3614,11 +3586,6 @@ class TestOvsDvrNeutronAgent(object):
|
||||
self._test_scan_ports_failure('scan_ancillary_ports')
|
||||
|
||||
|
||||
class TestOvsDvrNeutronAgentOFCtl(TestOvsDvrNeutronAgent,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TestOvsDvrNeutronAgentOSKen(TestOvsDvrNeutronAgent,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
pass
|
||||
|
@ -648,10 +648,6 @@ class TunnelTest(object):
|
||||
self._verify_mock_calls()
|
||||
|
||||
|
||||
class TunnelTestOFCtl(TunnelTest, ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TunnelTestOSKen(TunnelTest, ovs_test_base.OVSOSKenTestBase):
|
||||
pass
|
||||
|
||||
@ -745,11 +741,6 @@ class TunnelTestUseVethInterco(TunnelTest):
|
||||
'--timeout=10'])]
|
||||
|
||||
|
||||
class TunnelTestUseVethIntercoOFCtl(TunnelTestUseVethInterco,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TunnelTestUseVethIntercoOSKen(TunnelTestUseVethInterco,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
pass
|
||||
@ -764,11 +755,6 @@ class TunnelTestWithMTU(TunnelTestUseVethInterco):
|
||||
self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU))
|
||||
|
||||
|
||||
class TunnelTestWithMTUOFCtl(TunnelTestWithMTU,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class TunnelTestWithMTUOSKen(TunnelTestWithMTU,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
pass
|
||||
|
@ -24,7 +24,7 @@ from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
||||
ovs_agent_extension_api as ovs_ext_api)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native import (
|
||||
ovs_bridge)
|
||||
from neutron.services.logapi.agent import log_extension as log_ext
|
||||
from neutron.tests import base
|
||||
@ -54,10 +54,12 @@ class LoggingExtensionBaseTestCase(base.BaseTestCase):
|
||||
self.agent_ext = log_ext.LoggingExtension()
|
||||
self.context = context.get_admin_context()
|
||||
self.connection = mock.Mock()
|
||||
os_ken_app = mock.Mock()
|
||||
agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
||||
ovs_bridge.OVSAgentBridge('br-int'),
|
||||
ovs_bridge.OVSAgentBridge('br-tun'),
|
||||
{'physnet1': ovs_bridge.OVSAgentBridge('br-physnet1')})
|
||||
ovs_bridge.OVSAgentBridge('br-int', os_ken_app=os_ken_app),
|
||||
ovs_bridge.OVSAgentBridge('br-tun', os_ken_app=os_ken_app),
|
||||
{'physnet1': ovs_bridge.OVSAgentBridge(
|
||||
'br-physnet1', os_ken_app=os_ken_app)})
|
||||
self.agent_ext.consume_api(agent_api)
|
||||
mock.patch(
|
||||
'neutron.manager.NeutronManager.load_class_for_provider').start()
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The deprecated ``of_interface`` option is removed. Neutron will
|
||||
always use the ``native`` driver, which has been the default
|
||||
since Pike (11.0).
|
||||
If old driver ``ovs-ofctl`` was used before upgrade, automatically
|
||||
done change to ``native`` driver will cause short break of data plane
|
||||
connectivity during neutron-ovs-agent upgrade.
|
Loading…
x
Reference in New Issue
Block a user