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:
Ihar Hrachyshka 2017-10-20 12:14:21 -07:00 committed by Slawek Kaplonski
parent 0c87350a94
commit 4aeec20001
40 changed files with 179 additions and 2051 deletions

View File

@ -95,10 +95,6 @@ ovs_opts = [
"integration bridge to physical networks. " "integration bridge to physical networks. "
"Support kernel without Open vSwitch patch port " "Support kernel without Open vSwitch patch port "
"support so long as it is set to True.")), "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, cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM,
choices=[constants.OVS_DATAPATH_SYSTEM, choices=[constants.OVS_DATAPATH_SYSTEM,
constants.OVS_DATAPATH_NETDEV], constants.OVS_DATAPATH_NETDEV],

View File

@ -18,30 +18,20 @@
import sys import sys
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import importutils
from neutron.common import config as common_config from neutron.common import config as common_config
from neutron.common import profiler 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.' cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
'common.config') '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(): def main():
common_config.init(sys.argv[1:]) common_config.init(sys.argv[1:])
driver_name = cfg.CONF.OVS.of_interface of_main.init_config()
mod_name = _main_modules[driver_name]
mod = importutils.import_module(mod_name)
mod.init_config()
common_config.setup_logging() common_config.setup_logging()
profiler.setup("neutron-ovs-agent", cfg.CONF.host) profiler.setup("neutron-ovs-agent", cfg.CONF.host)
mod.main() of_main.main()

View File

@ -17,6 +17,7 @@
import functools import functools
import random import random
import debtcollector
import eventlet import eventlet
import netaddr import netaddr
from neutron_lib import exceptions from neutron_lib import exceptions
@ -220,9 +221,8 @@ class OpenFlowSwitchMixin(object):
(dp, ofp, ofpp) = self._get_dp() (dp, ofp, ofpp) = self._get_dp()
match = self._match(ofp, ofpp, match, **match_kwargs) match = self._match(ofp, ofpp, match, **match_kwargs)
if isinstance(instructions, six.string_types): if isinstance(instructions, six.string_types):
# NOTE: instructions must be str for the ofctl of_interface. debtcollector.deprecate("Use of string instruction is "
# After the ofctl driver is removed, a deprecation warning "deprecated", removal_version='U')
# could be added here.
jsonlist = ofctl_string.ofp_instruction_from_str( jsonlist = ofctl_string.ofp_instruction_from_str(
ofp, instructions) ofp, instructions)
instructions = ofproto_parser.ofp_instruction_from_jsondict( instructions = ofproto_parser.ofp_instruction_from_jsondict(

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -17,13 +17,12 @@ import functools
import fixtures import fixtures
import netaddr import netaddr
from neutron_lib import constants from neutron_lib import constants
from oslo_config import cfg
from oslo_utils import uuidutils from oslo_utils import uuidutils
from neutron.common import utils as common_utils from neutron.common import utils as common_utils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
constants as ovs_consts) 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 machine_fixtures
from neutron.tests.common import net_helpers 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): def _setUp(self):
super(OVSConnectionTester, self)._setUp() super(OVSConnectionTester, self)._setUp()
br_name = self.useFixture( br_name = self.useFixture(
net_helpers.OVSBridgeFixture()).bridge.br_name 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() self.bridge.setup_default_table()
machines = self.useFixture( machines = self.useFixture(
machine_fixtures.PeerMachines( machine_fixtures.PeerMachines(

View File

@ -197,7 +197,6 @@ class OVSConfigFixture(ConfigFixture):
'ovs': { 'ovs': {
'local_ip': local_ip, 'local_ip': local_ip,
'integration_bridge': self._generate_integration_bridge(), 'integration_bridge': self._generate_integration_bridge(),
'of_interface': host_desc.of_interface,
'bridge_mappings': '%s:%s' % (PHYSICAL_NETWORK_NAME, ext_dev) 'bridge_mappings': '%s:%s' % (PHYSICAL_NETWORK_NAME, ext_dev)
}, },
'securitygroup': { 'securitygroup': {
@ -236,13 +235,12 @@ class OVSConfigFixture(ConfigFixture):
}) })
def _setUp(self): def _setUp(self):
if self.config['ovs']['of_interface'] == 'native': self.config['ovs'].update({
self.config['ovs'].update({ 'of_listen_port': self.useFixture(
'of_listen_port': self.useFixture( port.ExclusivePort(constants.PROTO_NAME_TCP,
port.ExclusivePort(constants.PROTO_NAME_TCP, start=OVS_OF_PORT_LISTEN_START,
start=OVS_OF_PORT_LISTEN_START, end=OVS_OF_PORT_LISTEN_END)).port
end=OVS_OF_PORT_LISTEN_END)).port })
})
super(OVSConfigFixture, self)._setUp() super(OVSConfigFixture, self)._setUp()
def _generate_integration_bridge(self): def _generate_integration_bridge(self):

View File

@ -72,14 +72,12 @@ class HostDescription(object):
under? under?
""" """
def __init__(self, l3_agent=False, dhcp_agent=False, def __init__(self, l3_agent=False, dhcp_agent=False,
of_interface='ovs-ofctl',
l2_agent_type=constants.AGENT_TYPE_OVS, l2_agent_type=constants.AGENT_TYPE_OVS,
firewall_driver='noop', availability_zone=None, firewall_driver='noop', availability_zone=None,
l3_agent_mode=None): l3_agent_mode=None):
self.l2_agent_type = l2_agent_type self.l2_agent_type = l2_agent_type
self.l3_agent = l3_agent self.l3_agent = l3_agent
self.dhcp_agent = dhcp_agent self.dhcp_agent = dhcp_agent
self.of_interface = of_interface
self.firewall_driver = firewall_driver self.firewall_driver = firewall_driver
self.availability_zone = availability_zone self.availability_zone = availability_zone
self.l3_agent_mode = l3_agent_mode self.l3_agent_mode = l3_agent_mode

View File

@ -25,7 +25,6 @@ from neutron.tests.fullstack import base
from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import config
from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import environment
from neutron.tests.fullstack.resources import machine from neutron.tests.fullstack.resources import machine
from neutron.tests.fullstack import utils
from neutron.tests.unit import testlib_api from neutron.tests.unit import testlib_api
load_tests = testlib_api.module_load_tests load_tests = testlib_api.module_load_tests
@ -37,7 +36,6 @@ LOG = logging.getLogger(__name__)
class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase): class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
of_interface = None
arp_responder = False arp_responder = False
use_dhcp = True use_dhcp = True
@ -50,7 +48,6 @@ class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
# agent types present on machines. # agent types present on machines.
environment.HostDescription( environment.HostDescription(
l3_agent=self.l2_pop, l3_agent=self.l2_pop,
of_interface=self.of_interface,
l2_agent_type=self.l2_agent_type, l2_agent_type=self.l2_agent_type,
dhcp_agent=self.use_dhcp, dhcp_agent=self.use_dhcp,
) )
@ -104,7 +101,7 @@ class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest): class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
l2_agent_type = constants.AGENT_TYPE_OVS l2_agent_type = constants.AGENT_TYPE_OVS
network_scenarios = [ scenarios = [
('VXLAN', {'network_type': 'vxlan', ('VXLAN', {'network_type': 'vxlan',
'l2_pop': False}), 'l2_pop': False}),
('GRE-l2pop-arp_responder', {'network_type': 'gre', ('GRE-l2pop-arp_responder', {'network_type': 'gre',
@ -112,8 +109,6 @@ class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
'arp_responder': True}), 'arp_responder': True}),
('VLANs', {'network_type': 'vlan', ('VLANs', {'network_type': 'vlan',
'l2_pop': False})] 'l2_pop': False})]
scenarios = testscenarios.multiply_scenarios(
network_scenarios, utils.get_ovs_interface_scenarios())
def test_connectivity(self): def test_connectivity(self):
self._test_connectivity() self._test_connectivity()
@ -125,7 +120,7 @@ class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop(
num_hosts = 2 num_hosts = 2
l2_agent_type = constants.AGENT_TYPE_OVS l2_agent_type = constants.AGENT_TYPE_OVS
network_scenarios = [ scenarios = [
('VXLAN', {'network_type': 'vxlan', ('VXLAN', {'network_type': 'vxlan',
'l2_pop': False}), 'l2_pop': False}),
('GRE and l2pop', {'network_type': 'gre', ('GRE and l2pop', {'network_type': 'gre',
@ -133,12 +128,6 @@ class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop(
('VLANs', {'network_type': 'vlan', ('VLANs', {'network_type': 'vlan',
'l2_pop': False})] '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, def _test_controller_timeout_does_not_break_connectivity(self,
kill_signal=None): kill_signal=None):
# Environment preparation is effectively the same as connectivity test # Environment preparation is effectively the same as connectivity test
@ -203,7 +192,6 @@ class TestConnectivitySameNetworkNoDhcp(BaseConnectivitySameNetworkTest):
use_dhcp = False use_dhcp = False
network_type = 'vxlan' network_type = 'vxlan'
l2_pop = False l2_pop = False
of_interface = 'native'
def test_connectivity(self): def test_connectivity(self):
self._test_connectivity() self._test_connectivity()
@ -228,8 +216,8 @@ class TestUninterruptedConnectivityOnL2AgentRestart(
'l2_pop': False}), 'l2_pop': False}),
] ]
scenarios = ( scenarios = (
testscenarios.multiply_scenarios(ovs_agent_scenario, network_scenarios, testscenarios.multiply_scenarios(ovs_agent_scenario,
utils.get_ovs_interface_scenarios()) + network_scenarios) +
testscenarios.multiply_scenarios(lb_agent_scenario, network_scenarios) testscenarios.multiply_scenarios(lb_agent_scenario, network_scenarios)
) )

View File

@ -44,7 +44,6 @@ class FirewallMigrationTestCase(base.BaseFullStackTestCase):
host_descriptions = [ host_descriptions = [
environment.HostDescription( environment.HostDescription(
l3_agent=False, l3_agent=False,
of_interface='native',
l2_agent_type=constants.AGENT_TYPE_OVS, l2_agent_type=constants.AGENT_TYPE_OVS,
firewall_driver='iptables_hybrid', firewall_driver='iptables_hybrid',
dhcp_agent=False, dhcp_agent=False,

View File

@ -27,13 +27,11 @@ from neutron.tests.fullstack.resources import machine
class BaseLoggingTestCase(base.BaseFullStackTestCase): class BaseLoggingTestCase(base.BaseFullStackTestCase):
of_interface = None
number_of_hosts = 1 number_of_hosts = 1
def setUp(self): def setUp(self):
host_desc = [ host_desc = [
environment.HostDescription( environment.HostDescription(
of_interface=self.of_interface,
l2_agent_type=constants.AGENT_TYPE_OVS, l2_agent_type=constants.AGENT_TYPE_OVS,
firewall_driver='openvswitch', firewall_driver='openvswitch',
dhcp_agent=True) for _ in range(self.number_of_hosts)] dhcp_agent=True) for _ in range(self.number_of_hosts)]
@ -90,8 +88,6 @@ class BaseLoggingTestCase(base.BaseFullStackTestCase):
class TestLogging(BaseLoggingTestCase): class TestLogging(BaseLoggingTestCase):
of_interface = 'native'
def _create_network_log(self, resource_type, def _create_network_log(self, resource_type,
resource_id=None, target_id=None): resource_id=None, target_id=None):
return self.safe_client.create_network_log( return self.safe_client.create_network_log(

View File

@ -18,7 +18,6 @@ from neutron_lib import constants
from neutron_lib.services.qos import constants as qos_consts from neutron_lib.services.qos import constants as qos_consts
from neutronclient.common import exceptions from neutronclient.common import exceptions
from oslo_utils import uuidutils from oslo_utils import uuidutils
import testscenarios
from neutron.agent.common import ovs_lib from neutron.agent.common import ovs_lib
from neutron.agent.linux import tc_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 config as fullstack_config
from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import environment
from neutron.tests.fullstack.resources import machine 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.tests.unit import testlib_api
from neutron.conf.plugins.ml2.drivers import linuxbridge as \ from neutron.conf.plugins.ml2.drivers import linuxbridge as \
@ -48,7 +46,6 @@ DSCP_MARK = 16
class BaseQoSRuleTestCase(object): class BaseQoSRuleTestCase(object):
of_interface = None
number_of_hosts = 1 number_of_hosts = 1
physical_network = None physical_network = None
@ -63,7 +60,6 @@ class BaseQoSRuleTestCase(object):
host_desc = [ host_desc = [
environment.HostDescription( environment.HostDescription(
l3_agent=False, l3_agent=False,
of_interface=self.of_interface,
l2_agent_type=self.l2_agent_type l2_agent_type=self.l2_agent_type
) for _ in range(self.number_of_hosts)] ) for _ in range(self.number_of_hosts)]
env_desc = environment.EnvironmentDescription( env_desc = environment.EnvironmentDescription(
@ -344,12 +340,10 @@ class _TestBwLimitQoS(BaseQoSRuleTestCase):
class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase): class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase):
l2_agent_type = constants.AGENT_TYPE_OVS l2_agent_type = constants.AGENT_TYPE_OVS
direction_scenarios = [ scenarios = [
('ingress', {'direction': constants.INGRESS_DIRECTION}), ('ingress', {'direction': constants.INGRESS_DIRECTION}),
('egress', {'direction': constants.EGRESS_DIRECTION}) ('egress', {'direction': constants.EGRESS_DIRECTION})
] ]
scenarios = testscenarios.multiply_scenarios(
direction_scenarios, fullstack_utils.get_ovs_interface_scenarios())
@staticmethod @staticmethod
def _get_expected_burst_value(limit, direction): def _get_expected_burst_value(limit, direction):
@ -520,7 +514,6 @@ class _TestDscpMarkingQoS(BaseQoSRuleTestCase):
class TestDscpMarkingQoSOvs(_TestDscpMarkingQoS, base.BaseFullStackTestCase): class TestDscpMarkingQoSOvs(_TestDscpMarkingQoS, base.BaseFullStackTestCase):
scenarios = fullstack_utils.get_ovs_interface_scenarios()
l2_agent_type = constants.AGENT_TYPE_OVS l2_agent_type = constants.AGENT_TYPE_OVS
def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark): def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark):
@ -670,11 +663,9 @@ class _TestMinBwQoS(BaseQoSRuleTestCase):
class TestMinBwQoSOvs(_TestMinBwQoS, base.BaseFullStackTestCase): class TestMinBwQoSOvs(_TestMinBwQoS, base.BaseFullStackTestCase):
l2_agent_type = constants.AGENT_TYPE_OVS l2_agent_type = constants.AGENT_TYPE_OVS
direction_scenarios = [ scenarios = [
('egress', {'direction': constants.EGRESS_DIRECTION}) ('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): def _wait_for_min_bw_rule_applied(self, vm, min_bw, direction):
if direction == constants.EGRESS_DIRECTION: if direction == constants.EGRESS_DIRECTION:

View File

@ -40,13 +40,10 @@ class OVSVersionChecker(object):
class BaseSecurityGroupsSameNetworkTest(base.BaseFullStackTestCase): class BaseSecurityGroupsSameNetworkTest(base.BaseFullStackTestCase):
of_interface = None
def setUp(self): def setUp(self):
debug_iptables = self.firewall_driver.startswith("iptables") debug_iptables = self.firewall_driver.startswith("iptables")
host_descriptions = [ host_descriptions = [
environment.HostDescription( environment.HostDescription(
of_interface=self.of_interface,
l2_agent_type=self.l2_agent_type, l2_agent_type=self.l2_agent_type,
firewall_driver=self.firewall_driver, firewall_driver=self.firewall_driver,
dhcp_agent=True) for _ in range(self.num_hosts)] 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 # because of that using only one host is enough
('ovs-hybrid', { ('ovs-hybrid', {
'firewall_driver': 'iptables_hybrid', 'firewall_driver': 'iptables_hybrid',
'of_interface': 'native',
'l2_agent_type': constants.AGENT_TYPE_OVS, 'l2_agent_type': constants.AGENT_TYPE_OVS,
'num_hosts': 1}), 'num_hosts': 1}),
('ovs-openflow-cli', { ('ovs-openflow', {
'firewall_driver': 'openvswitch', '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, 'l2_agent_type': constants.AGENT_TYPE_OVS,
'num_hosts': 2}), 'num_hosts': 2}),
('linuxbridge-iptables', { ('linuxbridge-iptables', {

View File

@ -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'}),
]

View File

@ -17,6 +17,7 @@
import random import random
import eventlet import eventlet
import fixtures
import mock import mock
from neutron_lib import constants as n_const from neutron_lib import constants as n_const
from neutron_lib.utils import net 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.common import constants
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers \ from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers \
import qos_driver as ovs_qos_driver import qos_driver as ovs_qos_driver
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
import br_int import main as main_mod
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 \ from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \
as ovs_agent as ovs_agent
from neutron.tests.common import net_helpers from neutron.tests.common import net_helpers
from neutron.tests.functional.agent.linux import base 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): def setUp(self):
super(OVSAgentTestFramework, self).setUp() super(OVSAgentTestFramework, self).setUp()
@ -68,6 +127,7 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
self.config = self._configure_agent() self.config = self._configure_agent()
self.driver = interface.OVSInterfaceDriver(self.config) self.driver = interface.OVSInterfaceDriver(self.config)
self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name
self.start_of_controller(self.config)
def _get_config_opts(self): def _get_config_opts(self):
config = cfg.ConfigOpts() config = cfg.ConfigOpts()
@ -95,9 +155,9 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
def _bridge_classes(self): def _bridge_classes(self):
return { return {
'br_int': br_int.OVSIntegrationBridge, 'br_int': self.br_int_cls,
'br_phys': br_phys.OVSPhysicalBridge, 'br_phys': self.br_phys_cls,
'br_tun': br_tun.OVSTunnelBridge 'br_tun': self.br_tun_cls
} }
def create_agent(self, create_tunnels=True, ancillary_bridge=None, def create_agent(self, create_tunnels=True, ancillary_bridge=None,

View File

@ -34,6 +34,7 @@ from neutron.cmd.sanity import checks
from neutron.conf.agent import securitygroups_rpc as security_config from neutron.conf.agent import securitygroups_rpc as security_config
from neutron.tests.common import conn_testers from neutron.tests.common import conn_testers
from neutron.tests.common import helpers 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.agent.linux import base as linux_base
from neutron.tests.functional import constants as test_constants from neutron.tests.functional import constants as test_constants
@ -131,8 +132,12 @@ class BaseFirewallTestCase(linux_base.BaseOVSLinuxTestCase):
"OVS>=2.5). More info at " "OVS>=2.5). More info at "
"https://github.com/openvswitch/ovs/blob/master/" "https://github.com/openvswitch/ovs/blob/master/"
"FAQ.md") "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( 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) firewall_drv = openvswitch_firewall.OVSFirewallDriver(tester.bridge)
return tester, firewall_drv return tester, firewall_drv

View File

@ -40,8 +40,10 @@ class TestOVSAgent(base.OVSAgentTestFramework):
def test_no_stale_flows_after_port_delete(self): def test_no_stale_flows_after_port_delete(self):
def find_drop_flow(ofport, flows): def find_drop_flow(ofport, flows):
for flow in flows.split("\n"): for flow in flows:
if "in_port=%d" % ofport in flow and "actions=drop" in flow: # flow.instruction == [] means actions=drop
if (not flow.instructions and
('in_port', ofport) in flow.match.items()):
return True return True
return False return False
@ -96,6 +98,7 @@ class TestOVSAgent(base.OVSAgentTestFramework):
('br_int', 'br_tun', 'br_phys')): ('br_int', 'br_tun', 'br_phys')):
actual = self.ovs.db_get_val('Bridge', br_name, 'datapath_type') actual = self.ovs.db_get_val('Bridge', br_name, 'datapath_type')
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
self.stop_agent(agent, self.agent_thread)
def test_datapath_type_change(self): def test_datapath_type_change(self):
self._check_datapath_type_netdev('system') self._check_datapath_type_netdev('system')

View File

@ -13,15 +13,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import eventlet
import fixtures
import mock
import testscenarios
from neutron_lib import constants as n_const from neutron_lib import constants as n_const
from oslo_config import cfg from oslo_config import cfg
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import importutils
from testtools.content import text_content from testtools.content import text_content
from neutron.agent.common import ovs_lib 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 base as common_base
from neutron.tests.common import helpers from neutron.tests.common import helpers
from neutron.tests.common import net_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.agent import test_ovs_lib
from neutron.tests.functional import base
from neutron.tests import tools from neutron.tests import tools
load_tests = testscenarios.load_tests_apply_scenarios
OVS_TRACE_FINAL_FLOW = 'Final flow' OVS_TRACE_FINAL_FLOW = 'Final flow'
OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions' OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions'
@ -47,52 +41,18 @@ cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
'common.config') 'common.config')
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase): class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase,
scenarios = [ base.BaseSudoTestCase,
('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.' l2_base.OVSOFControllerHelper):
'agent.openflow.ovs_ofctl.main')}),
('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
'agent.openflow.native.main')})]
def setUp(self): def setUp(self):
super(OVSAgentTestBase, self).setUp() super(OVSAgentTestBase, self).setUp()
self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
self.of_interface_mod = importutils.import_module(self.main_module) self.start_of_controller(cfg.CONF)
self.br_int_cls = None self.br_int = self.br_int_cls(self.br.br_name)
self.br_tun_cls = None self.br_int.set_secure_mode()
self.br_phys_cls = None self.br_int.setup_controllers(cfg.CONF)
self.br_int = None self.br_int.setup_default_table()
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')
def _run_trace(self, brname, spec): def _run_trace(self, brname, spec):
required_keys = [OVS_TRACE_FINAL_FLOW, OVS_TRACE_DATAPATH_ACTIONS] required_keys = [OVS_TRACE_FINAL_FLOW, OVS_TRACE_DATAPATH_ACTIONS]
@ -112,29 +72,6 @@ class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase):
return trace 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): class ARPSpoofTestCase(OVSAgentTestBase):
def setUp(self): def setUp(self):
@ -357,9 +294,7 @@ class DeleteFlowsTestCase(OVSAgentTestBase):
class OVSFlowTestCase(OVSAgentTestBase): class OVSFlowTestCase(OVSAgentTestBase):
"""Tests defined in this class use ovs-appctl ofproto/trace commands, """Tests defined in this class use ovs-appctl ofproto/trace commands,
which simulate processing of imaginary packets, to check desired actions which simulate processing of imaginary packets, to check desired actions
are correctly set up by OVS flows. In this way, subtle variations in are correctly set up by OVS flows.
flows between of_interface drivers are absorbed and the same tests work
against those drivers.
""" """
def setUp(self): def setUp(self):
@ -466,9 +401,6 @@ class OVSFlowTestCase(OVSAgentTestBase):
self.assertIn("pop_vlan,", trace["Datapath actions"]) self.assertIn("pop_vlan,", trace["Datapath actions"])
def test_bundled_install(self): 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} kwargs = {'in_port': 345, 'vlan_tci': 0x1321}
dst_p = self.useFixture( dst_p = self.useFixture(
net_helpers.OVSPortFixture(self.br_tun, self.namespace)).port net_helpers.OVSPortFixture(self.br_tun, self.namespace)).port

View File

@ -27,8 +27,6 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import (
ovs_agent_extension_api as ovs_ext_api) ovs_agent_extension_api as ovs_ext_api)
from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
constants as ovs_consts) constants as ovs_consts)
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
ovs_bridge)
from neutron.services.logapi.drivers.openvswitch import ( from neutron.services.logapi.drivers.openvswitch import (
ovs_firewall_log as ovs_fw_log) ovs_firewall_log as ovs_fw_log)
from neutron.tests.functional.agent import test_firewall 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() self.log_driver = self.initialize_ovs_fw_log()
def initialize_ovs_fw_log(self): 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( agent_api = ovs_ext_api.OVSAgentExtensionAPI(
ovs_bridge.OVSAgentBridge(self.tester.bridge.br_name), self.int_br, self.tun_br,
ovs_bridge.OVSAgentBridge('br-tun'), {'physnet1': self.of_helper.br_phys_cls('br-physnet1')})
{'physnet1': ovs_bridge.OVSAgentBridge('br-physnet1')})
log_driver = ovs_fw_log.OVSFirewallLoggingDriver(agent_api) log_driver = ovs_fw_log.OVSFirewallLoggingDriver(agent_api)
log_driver.initialize(self.resource_rpc) log_driver.initialize(self.resource_rpc)
return log_driver return log_driver

View File

@ -32,7 +32,7 @@ from neutron.objects.qos import rule
from neutron.plugins.ml2.drivers.openvswitch.agent import ( from neutron.plugins.ml2.drivers.openvswitch.agent import (
ovs_agent_extension_api as ovs_ext_api) 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.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) ovs_bridge)
from neutron.tests import base from neutron.tests import base
@ -224,10 +224,14 @@ class QosExtensionBaseTestCase(base.BaseTestCase):
self.qos_ext = qos.QosAgentExtension() self.qos_ext = qos.QosAgentExtension()
self.context = context.get_admin_context() self.context = context.get_admin_context()
self.connection = mock.Mock() self.connection = mock.Mock()
os_ken_app = mock.Mock()
self.agent_api = ovs_ext_api.OVSAgentExtensionAPI( self.agent_api = ovs_ext_api.OVSAgentExtensionAPI(
ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge(
ovs_bridge.OVSAgentBridge('br-tun'), 'br-int', os_ken_app=os_ken_app),
{'phynet1': ovs_bridge.OVSAgentBridge('br-phynet1')}) 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) self.qos_ext.consume_api(self.agent_api)
# Don't rely on used driver # Don't rely on used driver

View File

@ -26,7 +26,7 @@ from neutron.agent.linux.openvswitch_firewall import exceptions
from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
as ovs_consts 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 import ovs_bridge
from neutron.tests import base from neutron.tests import base
@ -752,7 +752,7 @@ class TestCookieContext(base.BaseTestCase):
self.execute = mock.patch.object( self.execute = mock.patch.object(
utils, "execute", spec=utils.execute).start() utils, "execute", spec=utils.execute).start()
bridge = ovs_bridge.OVSAgentBridge('foo') bridge = ovs_bridge.OVSAgentBridge('foo', os_ken_app=mock.Mock())
mock.patch.object( mock.patch.object(
ovsfw.OVSFirewallDriver, 'initialize_bridge', ovsfw.OVSFirewallDriver, 'initialize_bridge',
return_value=bridge.deferred( return_value=bridge.deferred(

View File

@ -23,7 +23,7 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import (
ovs_agent_extension_api as ovs_ext_api) ovs_agent_extension_api as ovs_ext_api)
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import ( from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import (
qos_driver) 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) ovs_bridge)
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import ( from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import (
ovs_test_base) ovs_test_base)
@ -41,10 +41,14 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase):
self.qos_driver = qos_driver.QosOVSAgentDriver() self.qos_driver = qos_driver.QosOVSAgentDriver()
self.mock_clear_minimum_bandwidth_qos = mock.patch.object( self.mock_clear_minimum_bandwidth_qos = mock.patch.object(
self.qos_driver, '_minimum_bandwidth_initialize').start() self.qos_driver, '_minimum_bandwidth_initialize').start()
os_ken_app = mock.Mock()
self.agent_api = ovs_ext_api.OVSAgentExtensionAPI( self.agent_api = ovs_ext_api.OVSAgentExtensionAPI(
ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge(
ovs_bridge.OVSAgentBridge('br-tun'), 'br-int', os_ken_app=os_ken_app),
{'phys1': ovs_bridge.OVSAgentBridge('br-phys1')}) 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.consume_api(self.agent_api)
self.qos_driver.initialize() self.qos_driver.initialize()
self.qos_driver.br_int = mock.Mock() self.qos_driver.br_int = mock.Mock()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -16,7 +16,7 @@
import mock import mock
from neutron.agent.common import ovs_lib 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 import ovs_bridge
from neutron.tests import base from neutron.tests import base
@ -29,7 +29,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
'neutron.agent.ovsdb.impl_idl._connection') 'neutron.agent.ovsdb.impl_idl._connection')
conn_patcher.start() conn_patcher.start()
self.addCleanup(conn_patcher.stop) 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 test_reserved_cookies(self):
def_cookie = self.br.default_cookie def_cookie = self.br.default_cookie
@ -49,7 +49,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
self.assertNotIn(requested_cookie, self.br.reserved_cookies) self.assertNotIn(requested_cookie, self.br.reserved_cookies)
def test_set_agent_uuid_stamp(self): 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 def_cookie = self.br.default_cookie
new_cookie = ovs_lib.generate_random_cookie() new_cookie = ovs_lib.generate_random_cookie()
@ -60,7 +60,7 @@ class TestBRCookieOpenflow(base.BaseTestCase):
self.assertNotIn(def_cookie, self.br.reserved_cookies) self.assertNotIn(def_cookie, self.br.reserved_cookies)
def test_set_agent_uuid_stamp_with_reserved_cookie(self): 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 def_cookie = self.br.default_cookie
new_cookie = self.br.request_cookie() new_cookie = self.br.request_cookie()

View File

@ -38,33 +38,8 @@ class OVSAgentConfigTestBase(base.BaseTestCase):
self.mod_dvr_agent = importutils.import_module(_DVR_AGENT_NAME) self.mod_dvr_agent = importutils.import_module(_DVR_AGENT_NAME)
class OVSAgentTestBase(OVSAgentConfigTestBase): class OVSOSKenTestBase(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)
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' _DRIVER_PACKAGE = _AGENT_PACKAGE + '.openflow.native'
_BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge' _BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge'
_BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge' _BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge'
@ -75,6 +50,13 @@ class OVSOSKenTestBase(OVSAgentTestBase):
self.fake_oflib_of.start() self.fake_oflib_of.start()
self.addCleanup(self.fake_oflib_of.stop) self.addCleanup(self.fake_oflib_of.stop)
super(OVSOSKenTestBase, self).setUp() 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() os_ken_app = mock.Mock()
self.br_int_cls = functools.partial(self.br_int_cls, self.br_int_cls = functools.partial(self.br_int_cls,
os_ken_app=os_ken_app) os_ken_app=os_ken_app)
@ -82,3 +64,10 @@ class OVSOSKenTestBase(OVSAgentTestBase):
os_ken_app=os_ken_app) os_ken_app=os_ken_app)
self.br_tun_cls = functools.partial(self.br_tun_cls, self.br_tun_cls = functools.partial(self.br_tun_cls,
os_ken_app=os_ken_app) 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,
}

View File

@ -26,7 +26,7 @@ from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \
import ovs_test_base import ovs_test_base
class TestOVSAgentExtensionAPI(ovs_test_base.OVSOFCtlTestBase): class TestOVSAgentExtensionAPI(ovs_test_base.OVSOSKenTestBase):
def setUp(self): def setUp(self):
super(TestOVSAgentExtensionAPI, self).setUp() 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) 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): class TestOVSCookieBridgeOSKen(native_ovs_bridge_test_base.OVSBridgeTestBase):
def setUp(self): def setUp(self):

View File

@ -2422,31 +2422,8 @@ class TestOvsNeutronAgent(object):
mock_update_segid.assert_not_called() 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, class TestOvsNeutronAgentOSKen(TestOvsNeutronAgent,
ovs_test_base.OVSOSKenTestBase): ovs_test_base.OVSOSKenTestBase):
def test_cleanup_stale_flows(self): def test_cleanup_stale_flows(self):
uint64_max = (1 << 64) - 1 uint64_max = (1 << 64) - 1
with mock.patch.object(self.agent.int_br, with mock.patch.object(self.agent.int_br,
@ -2600,11 +2577,6 @@ class AncillaryBridgesTest(object):
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
class AncillaryBridgesTestOFCtl(AncillaryBridgesTest,
ovs_test_base.OVSOFCtlTestBase):
pass
class AncillaryBridgesTestOSKen(AncillaryBridgesTest, class AncillaryBridgesTestOSKen(AncillaryBridgesTest,
ovs_test_base.OVSOSKenTestBase): ovs_test_base.OVSOSKenTestBase):
pass pass
@ -3614,11 +3586,6 @@ class TestOvsDvrNeutronAgent(object):
self._test_scan_ports_failure('scan_ancillary_ports') self._test_scan_ports_failure('scan_ancillary_ports')
class TestOvsDvrNeutronAgentOFCtl(TestOvsDvrNeutronAgent,
ovs_test_base.OVSOFCtlTestBase):
pass
class TestOvsDvrNeutronAgentOSKen(TestOvsDvrNeutronAgent, class TestOvsDvrNeutronAgentOSKen(TestOvsDvrNeutronAgent,
ovs_test_base.OVSOSKenTestBase): ovs_test_base.OVSOSKenTestBase):
pass pass

View File

@ -648,10 +648,6 @@ class TunnelTest(object):
self._verify_mock_calls() self._verify_mock_calls()
class TunnelTestOFCtl(TunnelTest, ovs_test_base.OVSOFCtlTestBase):
pass
class TunnelTestOSKen(TunnelTest, ovs_test_base.OVSOSKenTestBase): class TunnelTestOSKen(TunnelTest, ovs_test_base.OVSOSKenTestBase):
pass pass
@ -745,11 +741,6 @@ class TunnelTestUseVethInterco(TunnelTest):
'--timeout=10'])] '--timeout=10'])]
class TunnelTestUseVethIntercoOFCtl(TunnelTestUseVethInterco,
ovs_test_base.OVSOFCtlTestBase):
pass
class TunnelTestUseVethIntercoOSKen(TunnelTestUseVethInterco, class TunnelTestUseVethIntercoOSKen(TunnelTestUseVethInterco,
ovs_test_base.OVSOSKenTestBase): ovs_test_base.OVSOSKenTestBase):
pass pass
@ -764,11 +755,6 @@ class TunnelTestWithMTU(TunnelTestUseVethInterco):
self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU)) self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU))
class TunnelTestWithMTUOFCtl(TunnelTestWithMTU,
ovs_test_base.OVSOFCtlTestBase):
pass
class TunnelTestWithMTUOSKen(TunnelTestWithMTU, class TunnelTestWithMTUOSKen(TunnelTestWithMTU,
ovs_test_base.OVSOSKenTestBase): ovs_test_base.OVSOSKenTestBase):
pass pass

View File

@ -24,7 +24,7 @@ from neutron.api.rpc.handlers import resources_rpc
from neutron.plugins.ml2.drivers.openvswitch.agent import ( from neutron.plugins.ml2.drivers.openvswitch.agent import (
ovs_agent_extension_api as ovs_ext_api) 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.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) ovs_bridge)
from neutron.services.logapi.agent import log_extension as log_ext from neutron.services.logapi.agent import log_extension as log_ext
from neutron.tests import base from neutron.tests import base
@ -54,10 +54,12 @@ class LoggingExtensionBaseTestCase(base.BaseTestCase):
self.agent_ext = log_ext.LoggingExtension() self.agent_ext = log_ext.LoggingExtension()
self.context = context.get_admin_context() self.context = context.get_admin_context()
self.connection = mock.Mock() self.connection = mock.Mock()
os_ken_app = mock.Mock()
agent_api = ovs_ext_api.OVSAgentExtensionAPI( agent_api = ovs_ext_api.OVSAgentExtensionAPI(
ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge('br-int', os_ken_app=os_ken_app),
ovs_bridge.OVSAgentBridge('br-tun'), ovs_bridge.OVSAgentBridge('br-tun', os_ken_app=os_ken_app),
{'physnet1': ovs_bridge.OVSAgentBridge('br-physnet1')}) {'physnet1': ovs_bridge.OVSAgentBridge(
'br-physnet1', os_ken_app=os_ken_app)})
self.agent_ext.consume_api(agent_api) self.agent_ext.consume_api(agent_api)
mock.patch( mock.patch(
'neutron.manager.NeutronManager.load_class_for_provider').start() 'neutron.manager.NeutronManager.load_class_for_provider').start()

View File

@ -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.