provider_networks_app

patch include:
L2 app modifcation as a result of having provider_networks app
Unit test for provider-networks app and the provider_networks
application.

Change-Id: I390402cde57106c7076ab3d6cc932744d3c40bca
Partially-implements: bp Provider-networks-app
This commit is contained in:
itamaro 2017-01-17 17:16:29 +02:00 committed by Itamar Ofek
parent ccdcbdc109
commit 46d53e1fa6
9 changed files with 460 additions and 313 deletions

View File

@ -16,7 +16,8 @@ OVS_BRANCH=${OVS_BRANCH:-branch-2.6}
DEFAULT_TUNNEL_TYPE="geneve"
DEFAULT_APPS_LIST="l2_app.L2App,l3_proactive_app.L3ProactiveApp,"\
"dhcp_app.DHCPApp,dnat_app.DNATApp,sg_app.SGApp,portsec_app.PortSecApp,"\
"portqos_app.PortQosApp,classifier_app.ClassifierApp,tunneling_app.TunnelingApp"
"portqos_app.PortQosApp,classifier_app.ClassifierApp,tunneling_app.TunnelingApp,"\
"provider_networks_app.ProviderNetworksApp"
if is_service_enabled df-metadata ; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,metadata_service_app.MetadataServiceApp"

View File

@ -43,6 +43,10 @@ def get_vhu_sockpath(sock_dir, port_id):
(n_const.VHOST_USER_DEVICE_PREFIX + port_id)[:14])
def get_bitmask(width):
return ~((-1 >> width) << width)
def is_valid_version(old_obj, new_obj):
if not old_obj:
return True

View File

@ -20,6 +20,7 @@ from dragonflow.conf import df_dnat
from dragonflow.conf import df_l2
from dragonflow.conf import df_l3
from dragonflow.conf import df_metadata_service
from dragonflow.conf import df_provider_networks
from dragonflow.conf import df_ryu
@ -35,3 +36,4 @@ df_l2.register_opts()
df_l3.register_opts()
df_dnat.register_opts()
df_ryu.register_opts()
df_provider_networks.register_opts()

View File

@ -22,18 +22,7 @@ df_l2_app_opts = [
cfg.BoolOpt(
'l2_responder',
default=True,
help=_('Install OVS flows to respond to ARP and ND requests.')),
cfg.ListOpt('bridge_mappings',
default=[],
help=_("Comma-separated list of <physical_network>:<bridge> "
"tuples mapping physical network names to the "
"dragonflow's node-specific Open vSwitch bridge names "
"to be used for flat and VLAN networks. Each bridge "
"must exist, and should have a physical network "
"interface configured as a port. All physical "
"networks configured on the server should have "
"mappings to appropriate bridges on each dragonflow "
"node.")),
help=_('Install OVS flows to respond to ARP and ND requests.'))
]

View File

@ -0,0 +1,42 @@
# Copyright (c) 2017 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from dragonflow._i18n import _
df_provider_networks_opts = [
cfg.ListOpt('bridge_mappings',
default=['public:br-ex'],
help=_("Comma-separated list of <physical_network>:<bridge> "
"tuples mapping physical network names to the "
"dragonflow's node-specific Open vSwitch bridge names "
"to be used for flat and VLAN networks. Each bridge "
"must exist, and should have a physical network "
"interface configured as a port. All physical "
"networks configured on the server should have "
"mappings to appropriate bridges on each dragonflow "
"node.")),
]
def register_opts():
cfg.CONF.register_opts(df_provider_networks_opts,
group='df_provider_networks')
def list_opts():
return {'df_provider_networks_app': df_provider_networks_opts}

View File

@ -17,13 +17,11 @@ import collections
import netaddr
from neutron_lib import constants as common_const
from neutron_lib.utils import helpers
from oslo_log import log
from ryu.lib.mac import haddr_to_bin
from ryu.lib.packet import in_proto
from ryu.ofproto import ether
from dragonflow._i18n import _, _LI, _LE
from dragonflow import conf as cfg
from dragonflow.controller.common import arp_responder
from dragonflow.controller.common import constants as const
@ -56,42 +54,8 @@ class L2App(df_base_app.DFlowApp):
self.local_networks = collections.defaultdict(_LocalNetwork)
self.integration_bridge = cfg.CONF.df.integration_bridge
self.is_install_l2_responder = cfg.CONF.df_l2_app.l2_responder
self.bridge_mappings = self._parse_bridge_mappings(
cfg.CONF.df_l2_app.bridge_mappings)
self.int_ofports = {}
def _parse_bridge_mappings(self, bridge_mappings):
try:
return helpers.parse_mappings(bridge_mappings)
except ValueError as e:
raise ValueError(_("Parsing bridge_mappings failed: %s.") % e)
def setup_physical_bridges(self, bridge_mappings):
'''Setup the physical network bridges.
Creates physical network bridges and links them to the
integration bridge using veths or patch ports.
:param bridge_mappings: map physical network names to bridge names.
'''
for physical_network, bridge in bridge_mappings.items():
LOG.info(_LI("Mapping physical network %(physical_network)s to "
"bridge %(bridge)s"),
{'physical_network': physical_network,
'bridge': bridge})
int_ofport = self.vswitch_api.create_patch_port(
self.integration_bridge,
'int-' + bridge,
'phy-' + bridge)
self.vswitch_api.create_patch_port(
bridge,
'phy-' + bridge,
'int-' + bridge)
self.int_ofports[physical_network] = int_ofport
def switch_features_handler(self, ev):
self.setup_physical_bridges(self.bridge_mappings)
self.add_flow_go_to_table(const.SERVICES_CLASSIFICATION_TABLE,
const.PRIORITY_DEFAULT,
const.L2_LOOKUP_TABLE)
@ -218,9 +182,6 @@ class L2App(df_base_app.DFlowApp):
self._del_multicast_broadcast_handling_for_local(lport_id,
topic,
local_network_id)
self._del_network_flows_on_last_port_down(local_network_id,
segmentation_id,
network_type)
def _del_multicast_broadcast_handling_for_local(self,
lport_id,
@ -363,9 +324,6 @@ class L2App(df_base_app.DFlowApp):
ofport = lport.get_external_value('ofport')
port_key = lport.get_unique_key()
network_id = lport.get_external_value('local_network_id')
network_type = lport.get_external_value('network_type')
physical_network = lport.get_external_value('physical_network')
segmentation_id = lport.get_external_value('segmentation_id')
if ofport is None or network_id is None:
return
@ -404,33 +362,12 @@ class L2App(df_base_app.DFlowApp):
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_MEDIUM,
match=match)
self._install_network_flows_on_first_port_up(segmentation_id,
physical_network,
network_type,
network_id)
self._add_multicast_broadcast_handling_for_local_port(lport_id,
port_key,
network_id,
topic)
self._add_l2_responders(lport)
def _del_network_flows_on_last_port_down(self,
local_network_id,
segmentation_id,
network_type):
network = self.local_networks.get(local_network_id)
if network and network.local_ports:
return
LOG.info(_LI("Remove network flows on last port down. Network type "
"is %(type)s, and segmentation ID is %(s_id)s."),
{'type': network_type, 's_id': segmentation_id})
if network_type == 'vlan':
self._del_network_flows_for_vlan(segmentation_id, local_network_id)
elif network_type == 'flat':
self._del_network_flows_for_flat(local_network_id)
def _add_multicast_broadcast_handling_for_local_port(self,
lport_id,
port_key,
@ -523,203 +460,9 @@ class L2App(df_base_app.DFlowApp):
self._add_l2_responders(lport)
def _install_network_flows_on_first_port_up(self,
segmentation_id,
physical_network,
network_type,
local_network_id):
network = self.local_networks.get(local_network_id)
if network and network.local_ports:
return
LOG.info(_LI("Install network flows on first port up. Network type "
"is %(type)s, and segmentation ID is %(s_id)s."),
{'type': network_type, 's_id': segmentation_id})
if network_type == 'vlan':
self._install_network_flows_for_vlan(segmentation_id,
physical_network,
local_network_id)
elif network_type == 'flat':
self._install_network_flows_for_flat(physical_network,
local_network_id)
"""
Install network flows for vlan
"""
def _install_network_flows_for_vlan(self, segmentation_id,
physical_network, local_network_id):
# L2_LOOKUP for Remote ports
parser = self.parser
ofproto = self.ofproto
match = parser.OFPMatch()
addint = haddr_to_bin('00:00:00:00:00:00')
add_mask_int = haddr_to_bin('01:00:00:00:00:00')
match.set_dl_dst_masked(addint, add_mask_int)
match.set_metadata(local_network_id)
inst = [parser.OFPInstructionGotoTable(const.EGRESS_TABLE)]
self.mod_flow(
inst=inst,
table_id=const.L2_LOOKUP_TABLE,
priority=const.PRIORITY_MEDIUM,
match=match)
# EGRESS for Remote ports
# Table=Egress
# Match: metadata=network_id
# Actions: mod_vlan, output:patch
match = parser.OFPMatch(metadata=local_network_id)
actions = [parser.OFPActionPushVlan(ether.ETH_TYPE_8021Q),
parser.OFPActionSetField(
vlan_vid=(segmentation_id & 0x1fff) | 0x1000)]
action_inst = parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)
goto_inst = parser.OFPInstructionGotoTable(const.EGRESS_EXTERNAL_TABLE)
inst = [action_inst, goto_inst]
self.mod_flow(
inst=inst,
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_LOW,
match=match)
# Add EGRESS port according to physical_network
self._install_output_to_physical_patch(physical_network,
local_network_id)
# Ingress
# Match: dl_vlan=vlan_id,
# Actions: metadata=network_id,
# goto 'Destination Port Classification'
match = parser.OFPMatch()
match.set_vlan_vid(segmentation_id)
actions = [parser.OFPActionSetField(metadata=local_network_id),
parser.OFPActionPopVlan()]
action_inst = parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)
goto_inst = parser.OFPInstructionGotoTable(
const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE)
inst = [action_inst, goto_inst]
self.mod_flow(
inst=inst,
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def _install_network_flows_for_flat(self, physical_network,
local_network_id):
parser = self.parser
ofproto = self.ofproto
match = parser.OFPMatch()
addint = haddr_to_bin('00:00:00:00:00:00')
add_mask_int = haddr_to_bin('01:00:00:00:00:00')
match.set_dl_dst_masked(addint, add_mask_int)
match.set_metadata(local_network_id)
inst = [parser.OFPInstructionGotoTable(const.EGRESS_TABLE)]
self.mod_flow(
inst=inst,
table_id=const.L2_LOOKUP_TABLE,
priority=const.PRIORITY_MEDIUM,
match=match)
# EGRESS for Remote ports
# Table=Egress
match = parser.OFPMatch(metadata=local_network_id)
goto_inst = parser.OFPInstructionGotoTable(const.EGRESS_EXTERNAL_TABLE)
inst = [goto_inst]
self.mod_flow(
inst=inst,
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_LOW,
match=match)
# Add EGRESS port according to physical_network
self._install_output_to_physical_patch(physical_network,
local_network_id)
# Ingress
match = parser.OFPMatch(vlan_vid=0)
actions = [parser.OFPActionSetField(metadata=local_network_id)]
action_inst = parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)
goto_inst = parser.OFPInstructionGotoTable(
const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE)
inst = [action_inst, goto_inst]
self.mod_flow(
inst=inst,
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def _del_network_flows_for_vlan(self, segmentation_id, local_network_id):
if segmentation_id is None:
return
parser = self.parser
ofproto = self.ofproto
match = parser.OFPMatch()
match.set_vlan_vid(segmentation_id)
self.mod_flow(
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
command=ofproto.OFPFC_DELETE,
priority=const.PRIORITY_LOW,
match=match)
match = parser.OFPMatch(metadata=local_network_id)
self.mod_flow(
table_id=const.EGRESS_EXTERNAL_TABLE,
command=ofproto.OFPFC_DELETE,
priority=const.PRIORITY_HIGH,
match=match)
def _get_multicast_broadcast_match(self, network_id):
match = self.parser.OFPMatch(eth_dst='01:00:00:00:00:00')
addint = haddr_to_bin('01:00:00:00:00:00')
match.set_dl_dst_masked(addint, addint)
match.set_metadata(network_id)
return match
def _del_network_flows_for_flat(self, local_network_id):
parser = self.parser
ofproto = self.ofproto
match = parser.OFPMatch(vlan_vid=0)
self.mod_flow(
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
command=ofproto.OFPFC_DELETE,
priority=const.PRIORITY_LOW,
match=match)
match = parser.OFPMatch(metadata=local_network_id)
self.mod_flow(
table_id=const.EGRESS_EXTERNAL_TABLE,
command=ofproto.OFPFC_DELETE,
priority=const.PRIORITY_HIGH,
match=match)
def _install_output_to_physical_patch(self, physical_network,
local_network_id):
if physical_network not in self.int_ofports:
LOG.error(_LE("Physical network %s unknown for dragonflow"),
physical_network)
return
parser = self.parser
ofproto = self.ofproto
match = parser.OFPMatch(metadata=local_network_id)
ofport = self.int_ofports[physical_network]
actions = [parser.OFPActionOutput(ofport,
ofproto.OFPCML_NO_BUFFER)]
actions_inst = parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)
inst = [actions_inst]
self.mod_flow(inst=inst,
table_id=const.EGRESS_EXTERNAL_TABLE,
priority=const.PRIORITY_HIGH, match=match)

View File

@ -0,0 +1,263 @@
# Copyright (c) 2017 OpenStack Foundation.
# 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 dragonflow._i18n import _LI, _LE
from dragonflow.common import utils
from dragonflow import conf as cfg
from dragonflow.controller.common import constants as const
from dragonflow.controller.common import logical_networks
from dragonflow.controller import df_base_app
from neutron_lib.utils import helpers
from oslo_log import log
from ryu.lib import mac as mac_api
NET_VLAN = 'vlan'
NET_FLAT = 'flat'
NETWORK_TYPES = (NET_VLAN, NET_FLAT)
VLAN_TAG_BITS = 12
VLAN_MASK = utils.get_bitmask(VLAN_TAG_BITS)
LOG = log.getLogger(__name__)
class ProviderNetworksApp(df_base_app.DFlowApp):
def __init__(self, *args, **kwargs):
super(ProviderNetworksApp, self).__init__(*args, **kwargs)
self.integration_bridge = cfg.CONF.df.integration_bridge
self.logical_networks = logical_networks.LogicalNetworks()
self.bridge_mappings = self._parse_bridge_mappings(
cfg.CONF.df_provider_networks.bridge_mappings)
self.int_ofports = {}
def _parse_bridge_mappings(self, bridge_mappings):
try:
return helpers.parse_mappings(bridge_mappings)
except ValueError:
LOG.exception(_LE("Failed to parse bridge mapping"))
raise
def _setup_physical_bridges(self, bridge_mappings):
'''Setup the physical network bridges.
Creates physical network bridges and links them to the
integration bridge using veths or patch ports.
:param bridge_mappings: map physical network names to bridge names.
'''
for physical_network, bridge in bridge_mappings.items():
LOG.info(_LI("Mapping physical network %(physical_network)s to "
"bridge %(bridge)s"),
{'physical_network': physical_network,
'bridge': bridge})
int_ofport = self.vswitch_api.create_patch_port(
self.integration_bridge,
'int-' + bridge,
'phy-' + bridge)
self.vswitch_api.create_patch_port(
bridge,
'phy-' + bridge,
'int-' + bridge)
self.int_ofports[physical_network] = int_ofport
def switch_features_handler(self, ev):
self._setup_physical_bridges(self.bridge_mappings)
def add_local_port(self, lport):
network_type = lport.get_external_value('network_type')
if network_type not in NETWORK_TYPES:
return
network_id = lport.get_external_value('local_network_id')
port_count = self.logical_networks.get_local_port_count(
network_id=network_id,
network_type=network_type)
LOG.info(_LI("adding %(net_type)s local port %(lport)s"),
{'net_type': network_type,
'lport': lport})
if port_count == 0:
self._new_network_flow(lport,
network_id,
network_type)
self.logical_networks.add_local_port(port_id=lport.get_id(),
network_id=network_id,
network_type=network_type)
def _match_actions_by_network_type(self, lport, network_id, network_type):
actions = [
self.parser.OFPActionSetField(metadata=network_id)]
match = None
if network_type == NET_VLAN:
segmentation_id = lport.get_external_value('segmentation_id')
match = self.parser.OFPMatch()
match.set_vlan_vid(segmentation_id)
actions.append(self.parser.OFPActionPopVlan())
elif network_type == NET_FLAT:
match = self.parser.OFPMatch(vlan_vid=0)
return match, actions
def _new_network_flow(self, lport, network_id, network_type):
LOG.debug('new %(net_type)s network: %(net_id)s',
{'net_type': network_type,
'net_id': network_id})
self._network_classification_flow(lport, network_id, network_type)
self._l2_lookup_flow(network_id)
self._egress_flow(lport, network_id, network_type)
self._egress_external_flow(lport, network_id)
def _l2_lookup_flow(self, network_id):
LOG.debug('l2 lookup flow for network %(net_id)s',
{'net_id': network_id})
match = self._make_bum_match(metadata=network_id)
inst = [self.parser.OFPInstructionGotoTable(const.EGRESS_TABLE)]
self.mod_flow(
inst=inst,
table_id=const.L2_LOOKUP_TABLE,
priority=const.PRIORITY_MEDIUM,
match=match)
def _egress_flow(self, lport, network_id, network_type):
LOG.debug('egrees flow for network %(net_id)s',
{'net_id': network_id})
match = self.parser.OFPMatch(metadata=network_id)
inst = [self.parser.OFPInstructionGotoTable(
const.EGRESS_EXTERNAL_TABLE)]
if network_type == NET_VLAN:
segmentation_id = lport.get_external_value('segmentation_id')
vlan_tag = (segmentation_id & VLAN_MASK)
# from open flow documentation:
# https://www.opennetworking.org/images/stories/downloads/\
# sdn-resources/onf-specifications/openflow/\
# openflow-spec-v1.3.3.pdf
# "... in particular the OFPVID_PRESENT bit must be set in
# OXM_OF_VLAN_VID set-field actions."
vlan_tag |= self.ofproto.OFPVID_PRESENT
actions = [
self.parser.OFPActionPushVlan(),
self.parser.OFPActionSetField(vlan_vid=vlan_tag)]
action_inst = self.parser.OFPInstructionActions(
self.ofproto.OFPIT_APPLY_ACTIONS,
actions)
inst.insert(0, action_inst)
self.mod_flow(
inst=inst,
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def _egress_external_flow(self, lport, network_id):
LOG.debug('egrees external flow for network %(net_id)s',
{'net_id': network_id})
physical_network = lport.get_external_value('physical_network')
match = self.parser.OFPMatch(metadata=network_id)
ofport = self.int_ofports[physical_network]
actions = [
self.parser.OFPActionOutput(ofport,
self.ofproto.OFPCML_NO_BUFFER)]
actions_inst = self.parser.OFPInstructionActions(
self.ofproto.OFPIT_APPLY_ACTIONS, actions)
inst = [actions_inst]
self.mod_flow(
inst=inst,
table_id=const.EGRESS_EXTERNAL_TABLE,
priority=const.PRIORITY_HIGH,
match=match)
def _network_classification_flow(self, lport, network_id, network_type):
LOG.debug('network classification flow for network_id: %(net_id)s',
{'net_id': network_id})
match, actions = self._match_actions_by_network_type(lport,
network_id,
network_type)
action_inst = self.parser.OFPInstructionActions(
self.ofproto.OFPIT_APPLY_ACTIONS, actions)
goto_inst = self.parser.OFPInstructionGotoTable(const.L2_LOOKUP_TABLE)
inst = [action_inst, goto_inst]
self.mod_flow(
inst=inst,
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def remove_local_port(self, lport):
network_type = lport.get_external_value('network_type')
if network_type not in NETWORK_TYPES:
return
network_id = lport.get_external_value('local_network_id')
self.logical_networks.remove_local_port(port_id=lport.get_id(),
network_id=network_id,
network_type=network_type)
port_count = self.logical_networks.get_local_port_count(
network_id=network_id,
network_type=network_type)
if port_count == 0:
self._remove_network_flow(lport, network_id, network_type)
def _remove_network_flow(self, lport, network_id, network_type):
self._remove_network_classification_flow(lport,
network_id,
network_type)
self._remove_l2_lookup_flow(network_id)
self._remove_egress_flow(lport, network_id)
self._remove_egress_external_flow(lport, network_id)
def _remove_network_classification_flow(self,
lport,
network_id,
network_type):
match, actions = self._match_actions_by_network_type(lport,
network_id,
network_type)
self.mod_flow(
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
command=self.ofproto.OFPFC_DELETE,
priority=const.PRIORITY_LOW,
match=match)
def _remove_l2_lookup_flow(self, network_id):
match = self._make_bum_match(metadata=network_id)
self.mod_flow(
table_id=const.L2_LOOKUP_TABLE,
command=self.ofproto.OFPFC_DELETE,
priority=const.PRIORITY_MEDIUM,
match=match)
def _remove_egress_flow(self, lport, network_id):
match = self.parser.OFPMatch(metadata=network_id)
self.mod_flow(
command=self.ofproto.OFPFC_DELETE,
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def _remove_egress_external_flow(self, lport, network_id):
match = self.parser.OFPMatch(metadata=network_id)
self.mod_flow(
command=self.ofproto.OFPFC_DELETE,
table_id=const.EGRESS_EXTERNAL_TABLE,
priority=const.PRIORITY_HIGH,
match=match)
def _make_bum_match(self, metadata):
match = self.parser.OFPMatch()
match.set_metadata(metadata)
encoded_mac = mac_api.haddr_to_bin(mac_api.DONTCARE_STR)
encoded_mask = mac_api.haddr_to_bin(mac_api.UNICAST)
match.set_dl_dst_masked(encoded_mac, encoded_mask)
return match

View File

@ -15,14 +15,19 @@ import re
from oslo_config import cfg
import ConfigParser
from dragonflow.common import utils as df_utils
from dragonflow.controller.common import constants as const
from dragonflow.tests.common import utils
from dragonflow.tests.fullstack import test_base
from dragonflow.tests.fullstack import test_objects as objects
ML2_CONF_INI = '/etc/neutron/plugins/ml2/ml2_conf.ini'
L2_ML2_APP_NAME = 'l2_app.L2App'
PROVIDER_NET_APP_NAME = 'provider_networks_app.ProviderNetworksApp'
TUNNEL_NET_APP_NAME = 'tunneling_app.TunnelingApp'
VLAN_MIN_DEFAULT = 2
VLAN_TAG_BITS = 12
VLAN_MASK = df_utils.get_bitmask(VLAN_TAG_BITS)
OFPVID_PRESENT = 0x1000
class TestL2FLows(test_base.DFTestBase):
@ -44,7 +49,7 @@ class TestL2FLows(test_base.DFTestBase):
return None
def test_tunnel_network_flows(self):
if self._check_l2_ml2_app_enable() is False:
if self._check_tunneling_app_enable() is False:
return
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
@ -87,12 +92,13 @@ class TestL2FLows(test_base.DFTestBase):
hex(segmentation_id),
tunnel_key_hex,
mac, ofport)
self.assertIsNotNone(r)
for key, value in r.items():
self.assertIsNotNone(value, key)
vm.close()
network.close()
def test_vlan_network_flows(self):
if self._check_l2_ml2_app_enable() is False:
if not self._check_providers_net_app_enable():
return
physical_network, vlan_min = self._parse_network_vlan_ranges()
@ -110,8 +116,8 @@ class TestL2FLows(test_base.DFTestBase):
# Create subnet
subnet_params = {'network_id': network_id,
'cidr': '100.64.0.0/24',
'gateway_ip': '10.64.0.1',
'cidr': '100.64.2.0/24',
'gateway_ip': '10.64.2.1',
'ip_version': 4,
'name': 'private',
'enable_dhcp': True}
@ -144,7 +150,8 @@ class TestL2FLows(test_base.DFTestBase):
vlan_min,
port_key_hex,
mac)
self.assertIsNotNone(r)
for key, value in r.items():
self.assertIsNotNone(value, key)
vm.server.stop()
vm.close()
network.close()
@ -189,12 +196,9 @@ class TestL2FLows(test_base.DFTestBase):
if ingress_action in flow['actions']:
ingress_check = True
if (l2_lookup_multicast_check is None or
l2_lookup_unicast_check is None or
ingress_check is None):
return None
return True
return {'l2_lookup_multicast_check': l2_lookup_multicast_check,
'l2_lookup_unicast_check': l2_lookup_unicast_check,
'ingress_check': ingress_check}
def _check_vlan_flows(self, flows, metadtata, segmentation_id,
port_key_hex, mac):
@ -217,14 +221,14 @@ class TestL2FLows(test_base.DFTestBase):
egress_match = 'metadata=0x' + metadtata
egress_action = 'push_vlan:0x8100,set_field:' + \
str(int(segmentation_id) + 4096) + \
str((segmentation_id & VLAN_MASK) | OFPVID_PRESENT) + \
"->vlan_vid,goto_table:" + \
str(const.EGRESS_EXTERNAL_TABLE)
ingress_match = 'dl_vlan=' + str(segmentation_id)
ingress_match = 'dl_vlan=%s' % segmentation_id
ingress_action = 'set_field:0x' + metadtata + '->metadata,' \
'pop_vlan,goto_table:' + \
str(const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE)
str(const.L2_LOOKUP_TABLE)
l2_lookup_unicast_check = None
l2_lookup_multicast_check = None
@ -237,53 +241,55 @@ class TestL2FLows(test_base.DFTestBase):
if (l2_lookup_multicast_match in flow['match']):
if l2_lookup_multicast_action in flow['actions']:
l2_lookup_multicast_check = True
continue
if (l2_lookup_unicast_match in flow['match']):
if l2_lookup_unicast_action in flow['actions']:
l2_lookup_unicast_check = True
continue
if (l2_lookup_unknown_match in flow['match']):
if l2_lookup_unkown_action in flow['actions']:
l2_lookup_unkown_check = True
continue
if flow['table'] == str(const.EGRESS_TABLE):
if (egress_match in flow['match']):
if egress_action in flow['actions']:
egress_check = True
continue
if flow['table'] == str(
const.INGRESS_CLASSIFICATION_DISPATCH_TABLE):
if (ingress_match in flow['match']):
if ingress_action in flow['actions']:
ingress_check = True
continue
if (l2_lookup_multicast_check is None or
l2_lookup_unicast_check is None or
l2_lookup_unkown_check is None or
egress_check is None or
ingress_check is None):
return None
return True
return {'l2_lookup_multicast_check': l2_lookup_multicast_check,
'l2_lookup_unicast_check': l2_lookup_unicast_check,
'l2_lookup_unkown_check': l2_lookup_unkown_check,
'egress_vlan_tag': egress_check,
'ingress_check': ingress_check}
def test_flat_network_flows(self):
if self._check_l2_ml2_app_enable() is False:
if not self._check_providers_net_app_enable():
return
physical_network = self._parse_flat_network()
if not physical_network:
self.assertIsNotNone(None)
return
# Create network
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
network_params = {"name": "vlan_1",
network_params = {"name": "flat_1",
"provider:network_type": "flat",
"provider:physical_network": physical_network}
network_id = network.create(network=network_params)
# Create subnet
subnet_params = {'network_id': network_id,
'cidr': '100.64.0.0/24',
'gateway_ip': '10.64.0.1',
'cidr': '100.64.1.0/24',
'gateway_ip': '10.64.1.1',
'ip_version': 4,
'name': 'private',
'enable_dhcp': True}
@ -314,7 +320,9 @@ class TestL2FLows(test_base.DFTestBase):
port_key_hex = hex(port_key)
r = self._check_flat_flows(ovs.dump(self.integration_bridge),
metadataid, port_key_hex, mac)
self.assertIsNotNone(r)
for key, value in r.items():
self.assertIsNotNone(value, key)
vm.server.stop()
vm.close()
network.close()
@ -345,7 +353,8 @@ class TestL2FLows(test_base.DFTestBase):
ingress_match = 'vlan_tci=0x0000/0x1fff'
ingress_action = 'set_field:0x' + metadtata + \
'->metadata,goto_table:' + \
str(const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE)
str(const.L2_LOOKUP_TABLE)
l2_lookup_unicast_check = None
l2_lookup_multicast_check = None
l2_lookup_unkown_check = None
@ -357,30 +366,32 @@ class TestL2FLows(test_base.DFTestBase):
if (l2_lookup_multicast_match in flow['match']):
if l2_lookup_multicast_action in flow['actions']:
l2_lookup_multicast_check = True
continue
if (l2_lookup_unicast_match in flow['match']):
if l2_lookup_unicast_action in flow['actions']:
l2_lookup_unicast_check = True
continue
if (l2_lookup_unkown_match in flow['match']):
if l2_lookup_unkown_action in flow['actions']:
l2_lookup_unkown_check = True
continue
if flow['table'] == str(const.EGRESS_TABLE):
if (egress_match in flow['match']):
if egress_action in flow['actions']:
egress_check = True
continue
if flow['table'] == str(
const.INGRESS_CLASSIFICATION_DISPATCH_TABLE):
if (ingress_match in flow['match']):
if ingress_action in flow['actions']:
ingress_check = True
continue
if (l2_lookup_multicast_check is None or
l2_lookup_unicast_check is None or
l2_lookup_unkown_check is None or
egress_check is None or
ingress_check is None):
return None
return True
return {'l2_lookup_multicast_check': l2_lookup_multicast_check,
'l2_lookup_unicast_check': l2_lookup_unicast_check,
'l2_lookup_unkown_check': l2_lookup_unkown_check,
'egress_check': egress_check,
'ingress_check': ingress_check}
def _get_config_values(self, section, key):
readhandle = None
@ -400,9 +411,14 @@ class TestL2FLows(test_base.DFTestBase):
return value
return value
def _check_l2_ml2_app_enable(self):
apps_list = cfg.CONF.df.apps_list
if L2_ML2_APP_NAME in apps_list:
def _check_tunneling_app_enable(self):
return self._check_if_app_enabled(TUNNEL_NET_APP_NAME)
def _check_providers_net_app_enable(self):
return self._check_if_app_enabled(PROVIDER_NET_APP_NAME)
def _check_if_app_enabled(self, app_name):
if app_name in cfg.CONF.df.apps_list:
return True
return False

View File

@ -0,0 +1,87 @@
# Copyright (c) 2017 OpenStack Foundation.
# 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 dragonflow.controller.common import constants as const
from dragonflow.tests.unit import test_app_base
make_fake_local_port = test_app_base.make_fake_local_port
make_fake_logic_switch = test_app_base.make_fake_logic_switch
make_fake_remote_port = test_app_base.make_fake_remote_port
class TestProviderNetsApp(test_app_base.DFAppTestBase):
apps_list = "provider_networks_app.ProviderNetworksApp"
def setUp(self):
super(TestProviderNetsApp, self).setUp()
fake_vlan_switch1 = make_fake_logic_switch(
subnets=test_app_base.fake_lswitch_default_subnets,
network_type='vlan',
id='fake_vlan_switch1',
mtu=1454,
physical_network='phynet',
router_external=False,
unique_key=6,
topic='fake_tenant1',
segmentation_id=10,
name='private')
self.controller.update_lswitch(fake_vlan_switch1)
self.app = self.open_flow_app.dispatcher.apps[0]
self.app.ofproto.OFPVID_PRESENT = 0x1000
def test_provider_vlan_port(self):
fake_local_vlan_port1 = make_fake_local_port(
network_type='vlan',
lswitch='fake_vlan_switch1',
local_network_id=21,
segmentation_id=5)
self.app.int_ofports['phynet'] = 1
self.controller.update_lport(fake_local_vlan_port1)
match = self.app.parser.OFPMatch(metadata=21)
actions = [
self.app.parser.OFPActionOutput(
self.app.int_ofports['phynet'],
self.app.ofproto.OFPCML_NO_BUFFER)]
inst = [self.app.parser.OFPInstructionActions(
self.app.ofproto.OFPIT_APPLY_ACTIONS, actions)]
self.app.mod_flow.assert_called_with(
inst=inst,
match=match,
priority=const.PRIORITY_HIGH,
table_id=const.EGRESS_EXTERNAL_TABLE)
self.app.mod_flow.reset_mock()
fake_local_vlan_port2 = make_fake_local_port(
network_type='vlan',
lswitch='fake_vlan_switch1',
macs=['1a:0b:0c:0d:0f:0f'],
ips=['10.0.0.112'],
ofport=12)
self.controller.update_lport(fake_local_vlan_port2)
self.app.mod_flow.assert_not_called()
self.app.mod_flow.reset_mock()
self.controller.delete_lport(fake_local_vlan_port1.get_id())
self.app.mod_flow.assert_not_called()
self.app.mod_flow.reset_mock()
self.controller.delete_lport(fake_local_vlan_port2.get_id())
self.app.mod_flow.assert_called_with(
command=self.app.ofproto.OFPFC_DELETE,
table_id=const.EGRESS_EXTERNAL_TABLE,
priority=const.PRIORITY_HIGH,
match=match)
self.app.mod_flow.reset_mock()