Drop of_interface option
Default value for "of_interface" config option was switched to "native" in Pike release. In the same release this option was deprecated to removal. Now it's time to remove it and force use of "native" driver to manage openflows. Change-Id: Ic900209868acfbe3bbb56fabbbf5c4472857e412 Co-Authored-By: Ihar Hrachyshka <ihrachys@redhat.com> Co-Authored-By: Slawek Kaplonski <skaplons@redhat.com>
This commit is contained in:
parent
0c87350a94
commit
4aeec20001
@ -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()
|
||||
|
@ -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…
Reference in New Issue
Block a user