Browse Source

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>
changes/96/599496/12
Ihar Hrachyshka 4 years ago
committed by Slawek Kaplonski
parent
commit
4aeec20001
  1. 4
      neutron/conf/plugins/ml2/drivers/ovs_conf.py
  2. 18
      neutron/plugins/ml2/drivers/openvswitch/agent/main.py
  3. 6
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ofswitch.py
  4. 0
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py
  5. 93
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_dvr_process.py
  6. 203
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py
  7. 60
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py
  8. 260
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_tun.py
  9. 36
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/main.py
  10. 113
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ofswitch.py
  11. 33
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge.py
  12. 11
      neutron/tests/common/conn_testers.py
  13. 14
      neutron/tests/fullstack/resources/config.py
  14. 2
      neutron/tests/fullstack/resources/environment.py
  15. 20
      neutron/tests/fullstack/test_connectivity.py
  16. 1
      neutron/tests/fullstack/test_firewall.py
  17. 4
      neutron/tests/fullstack/test_logging.py
  18. 13
      neutron/tests/fullstack/test_qos.py
  19. 12
      neutron/tests/fullstack/test_securitygroup.py
  20. 18
      neutron/tests/fullstack/utils.py
  21. 80
      neutron/tests/functional/agent/l2/base.py
  22. 7
      neutron/tests/functional/agent/test_firewall.py
  23. 7
      neutron/tests/functional/agent/test_l2_ovs_agent.py
  24. 90
      neutron/tests/functional/agent/test_ovs_flows.py
  25. 11
      neutron/tests/functional/services/logapi/test_logging.py
  26. 12
      neutron/tests/unit/agent/l2/extensions/test_qos.py
  27. 4
      neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py
  28. 12
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py
  29. 0
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py
  30. 175
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py
  31. 260
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py
  32. 97
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py
  33. 320
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_tun.py
  34. 8
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/test_br_cookie.py
  35. 41
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/ovs_test_base.py
  36. 117
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_agent_extension_api.py
  37. 35
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py
  38. 14
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py
  39. 10
      neutron/tests/unit/services/logapi/agent/test_log_extension.py
  40. 9
      releasenotes/notes/remove-of_interface-option-531ac8a1c767603a.yaml

4
neutron/conf/plugins/ml2/drivers/ovs_conf.py

@ -95,10 +95,6 @@ ovs_opts = [
"integration bridge to physical networks. "
"Support kernel without Open vSwitch patch port "
"support so long as it is set to True.")),
cfg.StrOpt('of_interface', default='native',
deprecated_for_removal=True,
choices=['ovs-ofctl', 'native'],
help=_("OpenFlow interface to use.")),
cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM,
choices=[constants.OVS_DATAPATH_SYSTEM,
constants.OVS_DATAPATH_NETDEV],

18
neutron/plugins/ml2/drivers/openvswitch/agent/main.py

@ -18,30 +18,20 @@
import sys
from oslo_config import cfg
from oslo_utils import importutils
from neutron.common import config as common_config
from neutron.common import profiler
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native import \
main as of_main
cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
'common.config')
_main_modules = {
'ovs-ofctl': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.'
'ovs_ofctl.main',
'native': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.'
'native.main',
}
def main():
common_config.init(sys.argv[1:])
driver_name = cfg.CONF.OVS.of_interface
mod_name = _main_modules[driver_name]
mod = importutils.import_module(mod_name)
mod.init_config()
of_main.init_config()
common_config.setup_logging()
profiler.setup("neutron-ovs-agent", cfg.CONF.host)
mod.main()
of_main.main()

6
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ofswitch.py

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

0
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py

93
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_dvr_process.py

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

203
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py

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

60
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py

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

260
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_tun.py

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

36
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/main.py

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

113
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ofswitch.py

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

33
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge.py

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

11
neutron/tests/common/conn_testers.py

@ -17,13 +17,12 @@ import functools
import fixtures
import netaddr
from neutron_lib import constants
from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.common import utils as common_utils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
constants as ovs_consts)
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
br_int)
from neutron.tests.common import machine_fixtures
from neutron.tests.common import net_helpers
@ -400,11 +399,17 @@ class OVSConnectionTester(OVSBaseConnectionTester):
"""
def __init__(self, ip_cidr, br_int_cls):
super(OVSConnectionTester, self).__init__(ip_cidr)
self.br_int_cls = br_int_cls
def _setUp(self):
super(OVSConnectionTester, self)._setUp()
br_name = self.useFixture(
net_helpers.OVSBridgeFixture()).bridge.br_name
self.bridge = br_int.OVSIntegrationBridge(br_name)
self.bridge = self.br_int_cls(br_name)
self.bridge.set_secure_mode()
self.bridge.setup_controllers(cfg.CONF)
self.bridge.setup_default_table()
machines = self.useFixture(
machine_fixtures.PeerMachines(

14
neutron/tests/fullstack/resources/config.py

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

2
neutron/tests/fullstack/resources/environment.py

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

20
neutron/tests/fullstack/test_connectivity.py

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

1
neutron/tests/fullstack/test_firewall.py

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

4
neutron/tests/fullstack/test_logging.py

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

13
neutron/tests/fullstack/test_qos.py

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

12
neutron/tests/fullstack/test_securitygroup.py

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

18
neutron/tests/fullstack/utils.py

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

80
neutron/tests/functional/agent/l2/base.py

@ -17,6 +17,7 @@
import random
import eventlet
import fixtures
import mock
from neutron_lib import constants as n_const
from neutron_lib.utils import net
@ -35,19 +36,77 @@ from neutron.conf.plugins.ml2.drivers import ovs_conf
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers \
import qos_driver as ovs_qos_driver
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
import br_int
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
import br_phys
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
import br_tun
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
import main as main_mod
from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \
as ovs_agent
from neutron.tests.common import net_helpers
from neutron.tests.functional.agent.linux import base
class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
class OVSOFControllerHelper(object):
"""Helper class that runs os-ken openflow controller."""
def start_of_controller(self, conf):
self.br_int_cls = None
self.br_tun_cls = None
self.br_phys_cls = None
self.init_done = False
self.init_done_ev = eventlet.event.Event()
self.main_ev = eventlet.event.Event()
self.addCleanup(self._kill_main)
retry_count = 3
while True:
# Try a few different ports as a port conflict
# causes the test to fail.
conf.set_override('of_listen_port',
net_helpers.get_free_namespace_port(
n_const.PROTO_NAME_TCP),
group='OVS')
cfg.CONF.set_override('of_listen_port',
conf.OVS.of_listen_port,
group='OVS')
main_mod.init_config()
self._main_thread = eventlet.spawn(self._kick_main)
# Wait for _kick_main -> openflow main -> _agent_main
# NOTE(yamamoto): This complexity came from how we run openflow
# controller. Main routine blocks while running the embedded
# openflow controller. In that case, the agent rpc_loop runs in
# another thread. However, for FT we need to run setUp() and
# test_xxx() in the same thread. So I made this run openflow main
# in a separate thread instead.
try:
while not self.init_done:
self.init_done_ev.wait()
break
except fixtures.TimeoutException:
self._kill_main()
retry_count -= 1
if retry_count < 0:
raise Exception('port allocation failed')
def _kick_main(self):
with mock.patch.object(ovs_agent, 'main', self._agent_main):
main_mod.main()
def _kill_main(self):
self.main_ev.send()
self._main_thread.wait()
def _agent_main(self, bridge_classes):
self.br_int_cls = bridge_classes['br_int']
self.br_phys_cls = bridge_classes['br_phys']
self.br_tun_cls = bridge_classes['br_tun']
# signal to setUp()
self.init_done = True
self.init_done_ev.send()
self.main_ev.wait()
class OVSAgentTestFramework(base.BaseOVSLinuxTestCase, OVSOFControllerHelper):
def setUp(self):
super(OVSAgentTestFramework, self).setUp()
@ -68,6 +127,7 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
self.config = self._configure_agent()
self.driver = interface.OVSInterfaceDriver(self.config)
self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name
self.start_of_controller(self.config)
def _get_config_opts(self):
config = cfg.ConfigOpts()
@ -95,9 +155,9 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
def _bridge_classes(self):
return {
'br_int': br_int.OVSIntegrationBridge,
'br_phys': br_phys.OVSPhysicalBridge,
'br_tun': br_tun.OVSTunnelBridge
'br_int': self.br_int_cls,
'br_phys': self.br_phys_cls,
'br_tun': self.br_tun_cls
}
def create_agent(self, create_tunnels=True, ancillary_bridge=None,

7
neutron/tests/functional/agent/test_firewall.py

@ -34,6 +34,7 @@ from neutron.cmd.sanity import checks
from neutron.conf.agent import securitygroups_rpc as security_config
from neutron.tests.common import conn_testers
from neutron.tests.common import helpers
from neutron.tests.functional.agent.l2 import base as l2_base
from neutron.tests.functional.agent.linux import base as linux_base
from neutron.tests.functional import constants as test_constants
@ -131,8 +132,12 @@ class BaseFirewallTestCase(linux_base.BaseOVSLinuxTestCase):
"OVS>=2.5). More info at "
"https://github.com/openvswitch/ovs/blob/master/"
"FAQ.md")
self.of_helper = l2_base.OVSOFControllerHelper()
self.of_helper.addCleanup = self.addCleanup
self.of_helper.start_of_controller(cfg.CONF)
tester = self.useFixture(
conn_testers.OVSConnectionTester(self.ip_cidr))
conn_testers.OVSConnectionTester(self.ip_cidr,
self.of_helper.br_int_cls))
firewall_drv = openvswitch_firewall.OVSFirewallDriver(tester.bridge)
return tester, firewall_drv

7
neutron/tests/functional/agent/test_l2_ovs_agent.py

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

90
neutron/tests/functional/agent/test_ovs_flows.py

@ -13,15 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import eventlet
import fixtures
import mock
import testscenarios
from neutron_lib import constants as n_const
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_utils import importutils
from testtools.content import text_content
from neutron.agent.common import ovs_lib
@ -35,11 +29,11 @@ from neutron.plugins.ml2.drivers.openvswitch.agent \
from neutron.tests.common import base as common_base
from neutron.tests.common import helpers
from neutron.tests.common import net_helpers
from neutron.tests.functional.agent.l2 import base as l2_base
from neutron.tests.functional.agent import test_ovs_lib
from neutron.tests.functional import base
from neutron.tests import tools
load_tests = testscenarios.load_tests_apply_scenarios
OVS_TRACE_FINAL_FLOW = 'Final flow'
OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions'
@ -47,52 +41,18 @@ cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
'common.config')
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase):
scenarios = [
('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
'agent.openflow.ovs_ofctl.main')}),
('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
'agent.openflow.native.main')})]
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase,
base.BaseSudoTestCase,
l2_base.OVSOFControllerHelper):
def setUp(self):
super(OVSAgentTestBase, self).setUp()
self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
self.of_interface_mod = importutils.import_module(self.main_module)
self.br_int_cls = None
self.br_tun_cls = None
self.br_phys_cls = None
self.br_int = None
self.init_done = False
self.init_done_ev = eventlet.event.Event()
self.main_ev = eventlet.event.Event()
self.addCleanup(self._kill_main)
retry_count = 3