Merge "Implement L2 Communication for vlan network."

This commit is contained in:
Jenkins 2016-10-05 14:19:22 +00:00 committed by Gerrit Code Review
commit 15f2751804
2 changed files with 284 additions and 4 deletions

View File

@ -18,6 +18,7 @@ from neutron_lib import constants as common_const
from oslo_config import cfg
from oslo_log import log
from ryu.lib.mac import haddr_to_bin
from ryu.ofproto import ether
from dragonflow._i18n import _, _LI
from dragonflow.controller.common import arp_responder
@ -225,8 +226,14 @@ class L2App(df_base_app.DFlowApp):
del local_ports[lport_id]
if len(local_ports) == 0:
del self.local_networks[local_network_id]
self._del_multicast_broadcast_flows_for_local(local_network_id)
# delete local_networks
remote_ports = network.get('remote')
if not remote_ports:
del self.local_networks[local_network_id]
else:
self._update_multicast_broadcast_flows_for_local(local_ports,
topic,
@ -478,7 +485,10 @@ class L2App(df_base_app.DFlowApp):
if network and network.get('local'):
return
self._del_network_flows_for_tunnel(segmentation_id)
if network_type == 'vlan':
self._del_network_flows_for_vlan(segmentation_id)
else:
self._del_network_flows_for_tunnel(segmentation_id)
def _del_network_flows_for_tunnel(self, segmentation_id):
LOG.info(_LI("Delete network flows for tunnel."))
@ -594,8 +604,13 @@ class L2App(df_base_app.DFlowApp):
del remote_ports[lport_id]
if len(remote_ports) == 0:
del self.local_networks[network_id]
self._del_multicast_broadcast_flows_for_remote(network_id)
# delete local_networks
local_ports = network.get('local')
if not local_ports:
del self.local_networks[network_id]
else:
self._update_multicast_broadcast_flows_for_remote(network_id,
segmentation_id,
@ -735,6 +750,7 @@ class L2App(df_base_app.DFlowApp):
lport_id = lport.get_id()
mac = lport.get_mac()
network_id = lport.get_external_value('local_network_id')
network_type = lport.get_external_value('network_type')
segmentation_id = lport.get_external_value('segmentation_id')
ofport = lport.get_external_value('ofport')
port_key = lport.get_tunnel_key()
@ -769,6 +785,9 @@ class L2App(df_base_app.DFlowApp):
self._add_arp_responder(lport)
if network_type == 'vlan':
return
match = parser.OFPMatch(reg7=port_key)
actions = [parser.OFPActionSetField(tunnel_id_nxm=segmentation_id),
parser.OFPActionOutput(port=ofport)]
@ -797,7 +816,12 @@ class L2App(df_base_app.DFlowApp):
local_ports = network.get('local')
if local_ports:
return
self._install_network_flows_for_tunnel(segmentation_id,
if network_type == 'vlan':
self._install_network_flows_for_vlan(segmentation_id,
local_network_id)
else:
self._install_network_flows_for_tunnel(segmentation_id,
local_network_id)
"""
@ -833,6 +857,93 @@ class L2App(df_base_app.DFlowApp):
priority=const.PRIORITY_MEDIUM,
match=match)
"""
Install network flows for vlan
"""
def _install_network_flows_for_vlan(self, segmentation_id,
local_network_id):
LOG.info(_LI("Install network flows on first vlan up"))
# L2_LOOKUP for Remote ports
datapath = self.get_datapath()
parser = datapath.ofproto_parser
ofproto = datapath.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(
datapath=datapath,
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(
datapath=datapath,
inst=inst,
table_id=const.EGRESS_TABLE,
priority=const.PRIORITY_LOW,
match=match)
# 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(
datapath=datapath,
inst=inst,
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
priority=const.PRIORITY_LOW,
match=match)
def _del_network_flows_for_vlan(self, segmentation_id):
LOG.info(_LI("Delete network flows for vlan"))
if segmentation_id is None:
return
datapath = self.get_datapath()
parser = datapath.ofproto_parser
ofproto = datapath.ofproto
match = parser.OFPMatch()
match.set_vlan_vid(segmentation_id)
self.mod_flow(
datapath=datapath,
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
command=ofproto.OFPFC_DELETE,
priority=const.PRIORITY_LOW,
out_port=ofproto.OFPP_ANY,
out_group=ofproto.OFPG_ANY,
match=match)
def _get_multicast_broadcast_match(self, network_id):
match = self.get_datapath().\
ofproto_parser.OFPMatch(eth_dst='01:00:00:00:00:00')

View File

@ -14,6 +14,7 @@ import re
from oslo_config import cfg
import ConfigParser
from dragonflow.controller.common import constants as const
from dragonflow.tests.common import utils
from dragonflow.tests.fullstack import test_base
@ -21,6 +22,7 @@ from dragonflow.tests.fullstack import test_objects as objects
ML2_CONF_INI = '/etc/neutron/plugins/ml2/ml2_conf.ini'
L2_ML2_APP_NAME = 'l2_ml2_app.L2App'
VLAN_MIN_DEFAULT = 2
class TestL2FLows(test_base.DFTestBase):
@ -86,6 +88,64 @@ class TestL2FLows(test_base.DFTestBase):
vm.close()
network.close()
def test_vlan_network_flows(self):
if self._check_l2_ml2_app_enable() is False:
return
physical_network, vlan_min = self._parse_network_vlan_ranges()
if physical_network is None or vlan_min is None:
self.assertIsNotNone(None)
return
# Create network
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
network_params = {"name": "vlan_1",
"provider:network_type": "vlan",
"provider:physical_network": physical_network,
"provider:segmentation_id": vlan_min}
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',
'ip_version': 4,
'name': 'private',
'enable_dhcp': True}
subnet = self.neutron.create_subnet({'subnet': subnet_params})
self.assertIsNotNone(subnet)
# Create VM
ovs = utils.OvsFlowsParser()
vm = self.store(objects.VMTestObj(self, self.neutron))
vm.create(network=network)
ip = vm.get_first_ipv4()
self.assertIsNotNone(ip)
mac = vm.get_first_mac()
self.assertIsNotNone(mac)
metadataid = utils.wait_until_is_and_return(
lambda: self._get_metadata_id(ovs.dump(self.integration_bridge),
ip, mac),
exception=Exception('Metadata id was not found in OpenFlow rules')
)
port = utils.wait_until_is_and_return(
lambda: self._get_vm_port(ip, mac),
exception=Exception('No port assigned to VM')
)
port_key = port.get_tunnel_key()
port_key_hex = hex(port_key)
r = self._check_vlan_flows(ovs.dump(self.integration_bridge),
metadataid,
vlan_min,
port_key_hex,
mac)
self.assertIsNotNone(r)
vm.server.stop()
vm.close()
network.close()
def _check_tunnel_flows(self, flows, metadtata, segmentation_id,
port_key_hex, mac):
l2_lookup_unicast_match = 'metadata=0x' + metadtata + \
@ -132,8 +192,117 @@ class TestL2FLows(test_base.DFTestBase):
return True
def _check_vlan_flows(self, flows, metadtata, segmentation_id,
port_key_hex, mac):
l2_lookup_unicast_match = 'metadata=0x' + metadtata + \
',dl_dst=' + mac
l2_lookup_unicast_action = 'goto_table:' + \
str(const.EGRESS_TABLE)
l2_lookup_unknown_match = 'metadata=0x' + metadtata + \
',dl_dst=00:00:00:00:00:00/01:00:00:00:00:00'
l2_lookup_unkown_action = 'goto_table:' + \
str(const.EGRESS_TABLE)
l2_lookup_multicast_match = 'metadata=0x' + metadtata + ',dl_dst=' + \
'01:00:00:00:00:00/01:00:00:00:00:00'
l2_lookup_multicast_action = 'set_field:' + port_key_hex + \
'->reg7,resubmit(,' + \
str(const.EGRESS_TABLE) + ')' + \
',set_field:0' + \
'->reg7,resubmit(,' + \
str(const.EGRESS_TABLE) + ')'
egress_match = 'metadata=0x' + metadtata
egress_action = 'push_vlan:0x8100,set_field:' + \
str(int(segmentation_id) + 4096) + \
"->vlan_vid,goto_table:" + \
str(const.EGRESS_EXTERNAL_TABLE)
ingress_match = 'dl_vlan=' + str(segmentation_id)
ingress_action = 'set_field:0x' + metadtata + '->metadata,' \
'pop_vlan,goto_table:' + \
str(const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE)
l2_lookup_unicast_check = None
l2_lookup_multicast_check = None
l2_lookup_unkown_check = None
egress_check = None
ingress_check = None
for flow in flows:
if flow['table'] == str(const.L2_LOOKUP_TABLE):
if (l2_lookup_multicast_match in flow['match']):
if l2_lookup_multicast_action in flow['actions']:
l2_lookup_multicast_check = True
if (l2_lookup_unicast_match in flow['match']):
if l2_lookup_unicast_action in flow['actions']:
l2_lookup_unicast_check = True
if (l2_lookup_unknown_match in flow['match']):
if l2_lookup_unkown_action in flow['actions']:
l2_lookup_unkown_check = True
if flow['table'] == str(const.EGRESS_TABLE):
if (egress_match in flow['match']):
if egress_action in flow['actions']:
egress_check = True
if flow['table'] == str(
const.INGRESS_CLASSIFICATION_DISPATCH_TABLE):
if (ingress_match in flow['match']):
if ingress_action in flow['actions']:
ingress_check = True
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
def _get_vlan_ranges(self):
readhandle = None
vlan_ranges = None
try:
config = ConfigParser.ConfigParser()
readhandle = open(ML2_CONF_INI, 'r')
config.readfp(readhandle)
vlan_ranges = config.get("ml2_type_vlan", 'network_vlan_ranges')
except Exception:
vlan_ranges = None
if readhandle is not None:
try:
readhandle.close()
except Exception:
return vlan_ranges
return vlan_ranges
def _check_l2_ml2_app_enable(self):
apps_list = cfg.CONF.df.apps_list
if L2_ML2_APP_NAME in apps_list:
return True
return False
def _parse_network_vlan_ranges(self):
network_vlan_ranges = self._get_vlan_ranges()
if network_vlan_ranges is None:
return None
network_vlan_range_list = network_vlan_ranges.split(',')
if not network_vlan_range_list:
return None
network_vlan_range = network_vlan_range_list[0]
if ':' in network_vlan_range:
try:
physical_network, vlan_min, vlan_max = \
network_vlan_range.split(':')
except ValueError:
return None
else:
physical_network = network_vlan_range
vlan_min = VLAN_MIN_DEFAULT
return physical_network, vlan_min