elif 'priority' in kwargs:
raise Exception(_("Cannot match priority on flow deletion"))
+ table = ('table' in kwargs and ",table=%s" %
+ kwargs['table'] or '')
in_port = ('in_port' in kwargs and ",in_port=%s" %
kwargs['in_port'] or '')
dl_type = ('dl_type' in kwargs and ",dl_type=%s" %
tun_id = 'tun_id' in kwargs and ",tun_id=%s" % kwargs['tun_id'] or ''
proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or ''
ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or ''
- match = (in_port + dl_type + dl_vlan + dl_src + dl_dst +
+ match = (table + in_port + dl_type + dl_vlan + dl_src + dl_dst +
(ip or proto) + nw_src + nw_dst + tun_id)
if match:
match = match[1:] # strip leading comma
flow_expr_arr.append(match)
return flow_expr_arr
- def add_flow(self, **kwargs):
+ def add_or_mod_flow_str(self, **kwargs):
if "actions" not in kwargs:
raise Exception(_("Must specify one or more actions"))
if "priority" not in kwargs:
flow_expr_arr = self._build_flow_expr_arr(**kwargs)
flow_expr_arr.append("actions=%s" % (kwargs["actions"]))
flow_str = ",".join(flow_expr_arr)
+ return flow_str
+
+ def add_flow(self, **kwargs):
+ flow_str = self.add_or_mod_flow_str(**kwargs)
self.run_ofctl("add-flow", [flow_str])
+ def mod_flow(self, **kwargs):
+ flow_str = self.add_or_mod_flow_str(**kwargs)
+ self.run_ofctl("mod-flows", [flow_str])
+
def delete_flows(self, **kwargs):
kwargs['delete'] = True
flow_expr_arr = self._build_flow_expr_arr(**kwargs)
self.int_br = self.setup_integration_br(integ_br)
self.setup_physical_bridges(bridge_mappings)
self.local_vlan_map = {}
+ self.tun_br_ofports = {constants.TYPE_GRE: set(),
+ constants.TYPE_VXLAN: set()}
self.polling_interval = polling_interval
if tunnel_ip == self.local_ip:
return
tun_name = '%s-%s' % (tunnel_type, tunnel_id)
- self.tun_br.add_tunnel_port(tun_name, tunnel_ip, self.local_ip,
- tunnel_type, self.vxlan_udp_port)
+ self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type)
def create_rpc_dispatcher(self):
'''Get the rpc dispatcher for this manager.
if network_type in constants.TUNNEL_NETWORK_TYPES:
if self.enable_tunneling:
- # outbound
- self.tun_br.add_flow(priority=4, in_port=self.patch_int_ofport,
+ # outbound broadcast/multicast
+ ofports = ','.join(self.tun_br_ofports[network_type])
+ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
dl_vlan=lvid,
- actions="set_tunnel:%s,normal" %
- segmentation_id)
- # inbound bcast/mcast
- self.tun_br.add_flow(
- priority=3,
- tun_id=segmentation_id,
- dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
- actions="mod_vlan_vid:%s,output:%s" %
- (lvid, self.patch_int_ofport))
+ actions="strip_vlan,set_tunnel:%s,"
+ "output:%s" % (segmentation_id, ofports))
+ # inbound from tunnels: set lvid in the right table
+ # and resubmit to Table LEARN_FROM_TUN for mac learning
+ self.tun_br.add_flow(table=constants.TUN_TABLE[network_type],
+ priority=1,
+ tun_id=segmentation_id,
+ actions="mod_vlan_vid:%s,resubmit(,%s)" %
+ (lvid, constants.LEARN_FROM_TUN))
else:
LOG.error(_("Cannot provision %(network_type)s network for "
"net-id=%(net_uuid)s - tunneling disabled"),
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
if self.enable_tunneling:
- self.tun_br.delete_flows(tun_id=lvm.segmentation_id)
+ self.tun_br.delete_flows(
+ table=constants.TUN_TABLE[lvm.network_type],
+ tun_id=lvm.segmentation_id)
self.tun_br.delete_flows(dl_vlan=lvm.vlan)
elif lvm.network_type == constants.TYPE_FLAT:
if lvm.physical_network in self.phys_brs:
lvm = self.local_vlan_map[net_uuid]
lvm.vif_ports[port.vif_id] = port
- if network_type in constants.TUNNEL_NETWORK_TYPES:
- if self.enable_tunneling:
- # inbound unicast
- self.tun_br.add_flow(priority=3, tun_id=segmentation_id,
- dl_dst=port.vif_mac,
- actions="mod_vlan_vid:%s,normal" %
- lvm.vlan)
-
self.int_br.set_db_attribute("Port", port.port_name, "tag",
str(lvm.vlan))
if int(port.ofport) != -1:
LOG.info(_('port_unbound() net_uuid %s not in local_vlan_map'),
net_uuid)
return
- lvm = self.local_vlan_map[net_uuid]
- vif_port = lvm.vif_ports.pop(vif_id, None)
- if vif_port:
- if self.enable_tunneling and lvm.network_type in (
- constants.TUNNEL_NETWORK_TYPES):
- # remove inbound unicast flow
- self.tun_br.delete_flows(tun_id=lvm.segmentation_id,
- dl_dst=vif_port.vif_mac)
- else:
- LOG.info(_('port_unbound: vif_id %s not in local_vlan_map'),
- vif_id)
+ lvm = self.local_vlan_map[net_uuid]
+ lvm.vif_ports.pop(vif_id, None)
if not lvm.vif_ports:
self.reclaim_local_vlan(net_uuid, lvm)
"Agent terminated!"))
exit(1)
self.tun_br.remove_all_flows()
- self.tun_br.add_flow(priority=1, actions="drop")
+
+ # Table 0 (default) will sort incoming traffic depending on in_port
+ self.tun_br.add_flow(priority=1,
+ in_port=self.patch_int_ofport,
+ actions="resubmit(,%s)" %
+ constants.PATCH_LV_TO_TUN)
+ self.tun_br.add_flow(priority=0, actions="drop")
+ # PATCH_LV_TO_TUN table will handle packets coming from patch_int
+ # unicasts go to table UCAST_TO_TUN where remote adresses are learnt
+ self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
+ 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
+ self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
+ 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 adresses will be learnt
+ for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
+ self.tun_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
+ # adresses (assumes that lvid has already been set by a previous flow)
+ learned_flow = ("table=%s,"
+ "priority=1,"
+ "hard_timeout=300,"
+ "NXM_OF_VLAN_TCI[0..11],"
+ "load:0->NXM_OF_VLAN_TCI[],"
+ "load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
+ "output:NXM_OF_IN_PORT[]" %
+ constants.UCAST_TO_TUN)
+ # Once remote mac adresses are learnt, packet is outputed to patch_int
+ self.tun_br.add_flow(table=constants.LEARN_FROM_TUN,
+ priority=1,
+ actions="learn(%s),output:%s" %
+ (learned_flow, self.patch_int_ofport))
+ # Egress unicast will be handled in table UCAST_TO_TUN, where remote
+ # mac adresses 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
+ self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
+ 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.tun_br.add_flow(table=constants.FLOOD_TO_TUN,
+ priority=0,
+ actions="drop")
def setup_physical_bridges(self, bridge_mappings):
'''Setup the physical network bridges.
else:
LOG.debug(_("No VIF port for port %s defined on agent."), port_id)
+ def setup_tunnel_port(self, port_name, remote_ip, tunnel_type):
+ ofport = self.tun_br.add_tunnel_port(port_name,
+ remote_ip,
+ self.local_ip,
+ tunnel_type,
+ self.vxlan_udp_port)
+ if ofport < 0:
+ LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
+ {'type': tunnel_type, 'ip': remote_ip})
+ else:
+ self.tun_br_ofports[tunnel_type].add(ofport)
+ # Add flow in default table to resubmit to the right
+ # tunelling table (lvid will be set in the latter)
+ self.tun_br.add_flow(priority=1,
+ in_port=ofport,
+ actions="resubmit(,%s)" %
+ constants.TUN_TABLE[tunnel_type])
+ # Update flooding flows to include the new tunnel
+ ofports = ','.join(self.tun_br_ofports[tunnel_type])
+ for network_id, vlan_mapping in self.local_vlan_map.iteritems():
+ if vlan_mapping.network_type == tunnel_type:
+ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan=vlan_mapping.vlan,
+ actions="strip_vlan,"
+ "set_tunnel:%s,output:%s" %
+ (vlan_mapping.segmentation_id,
+ ofports))
+
def treat_devices_added(self, devices):
resync = False
self.sg_agent.prepare_devices_filter(devices)
if self.local_ip != tunnel['ip_address']:
tunnel_id = tunnel.get('id', tunnel['ip_address'])
tun_name = '%s-%s' % (tunnel_type, tunnel_id)
- self.tun_br.add_tunnel_port(tun_name,
- tunnel['ip_address'],
- self.local_ip,
- tunnel_type,
- self.vxlan_udp_port)
+ self.setup_tunnel_port(tun_name,
+ tunnel['ip_address'],
+ tunnel_type)
except Exception as e:
LOG.debug(_("Unable to sync tunnel IP %(local_ip)s: %(e)s"),
{'local_ip': self.local_ip, 'e': e})
# The different types of tunnels
TUNNEL_NETWORK_TYPES = [TYPE_GRE, TYPE_VXLAN]
+
+# Various tables for tunneling flows
+PATCH_LV_TO_TUN = 1
+GRE_TUN_TO_LV = 2
+VXLAN_TUN_TO_LV = 3
+LEARN_FROM_TUN = 10
+UCAST_TO_TUN = 20
+FLOOD_TO_TUN = 21
+# Map tunnel types to tables number
+TUN_TABLE = {TYPE_GRE: GRE_TUN_TO_LV, TYPE_VXLAN: VXLAN_TUN_TO_LV}
"int_ofport")
def test_port_unbound(self):
- with contextlib.nested(
- mock.patch.object(self.agent.tun_br, "delete_flows"),
- mock.patch.object(self.agent, "reclaim_local_vlan")
- ) as (delfl_fn, reclvl_fn):
+ with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn:
self.agent.enable_tunneling = True
lvm = mock.Mock()
lvm.network_type = "gre"
lvm.vif_ports = {"vif1": mock.Mock()}
self.agent.local_vlan_map["netuid12345"] = lvm
self.agent.port_unbound("vif1", "netuid12345")
- self.assertTrue(delfl_fn.called)
self.assertTrue(reclvl_fn.called)
reclvl_fn.called = False
OFPORT_NUM = 1
VIF_PORT = ovs_lib.VifPort('port', OFPORT_NUM,
VIF_ID, VIF_MAC, 'switch')
-VIF_PORTS = {LV_ID: VIF_PORT}
+VIF_PORTS = {VIF_ID: VIF_PORT}
LVM = ovs_neutron_agent.LocalVLANMapping(LV_ID, 'gre', None, LS_ID, VIF_PORTS)
LVM_FLAT = ovs_neutron_agent.LocalVLANMapping(
LV_ID, 'flat', 'net1', LS_ID, VIF_PORTS)
LVM_VLAN = ovs_neutron_agent.LocalVLANMapping(
LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS)
+
+GRE_OFPORTS = set(['11', '12'])
+VXLAN_OFPORTS = set(['13', '14'])
+TUN_OFPORTS = {constants.TYPE_GRE: GRE_OFPORTS,
+ constants.TYPE_VXLAN: VXLAN_OFPORTS}
+
BCAST_MAC = "01:00:00:00:00:00/01:00:00:00:00:00"
+UCAST_MAC = "00:00:00:00:00:00/01:00:00:00:00:00"
class DummyPort:
'patch-tun', 'patch-int').AndReturn(self.TUN_OFPORT)
self.mock_tun_bridge.add_patch_port(
'patch-int', 'patch-tun').AndReturn(self.INT_OFPORT)
+
self.mock_tun_bridge.remove_all_flows()
- self.mock_tun_bridge.add_flow(priority=1, actions='drop')
+ self.mock_tun_bridge.add_flow(priority=1,
+ in_port=self.INT_OFPORT,
+ actions="resubmit(,%s)" %
+ constants.PATCH_LV_TO_TUN)
+ self.mock_tun_bridge.add_flow(priority=0, actions='drop')
+ self.mock_tun_bridge.add_flow(table=constants.PATCH_LV_TO_TUN,
+ dl_dst=UCAST_MAC,
+ actions="resubmit(,%s)" %
+ constants.UCAST_TO_TUN)
+ self.mock_tun_bridge.add_flow(table=constants.PATCH_LV_TO_TUN,
+ dl_dst=BCAST_MAC,
+ actions="resubmit(,%s)" %
+ constants.FLOOD_TO_TUN)
+ for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
+ self.mock_tun_bridge.add_flow(
+ table=constants.TUN_TABLE[tunnel_type],
+ priority=0,
+ actions="drop")
+ learned_flow = ("table=%s,"
+ "priority=1,"
+ "hard_timeout=300,"
+ "NXM_OF_VLAN_TCI[0..11],"
+ "load:0->NXM_OF_VLAN_TCI[],"
+ "load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
+ "output:NXM_OF_IN_PORT[]" %
+ constants.UCAST_TO_TUN)
+ self.mock_tun_bridge.add_flow(table=constants.LEARN_FROM_TUN,
+ priority=1,
+ actions="learn(%s),output:%s" %
+ (learned_flow, self.INT_OFPORT))
+ self.mock_tun_bridge.add_flow(table=constants.UCAST_TO_TUN,
+ priority=0,
+ actions="resubmit(,%s)" %
+ constants.FLOOD_TO_TUN)
+ self.mock_tun_bridge.add_flow(table=constants.FLOOD_TO_TUN,
+ priority=0,
+ actions="drop")
self.mox.StubOutWithMock(ip_lib, 'device_exists')
ip_lib.device_exists('tunnel_bridge_mapping', 'sudo').AndReturn(True)
self.mox.VerifyAll()
def testProvisionLocalVlan(self):
- action_string = 'set_tunnel:%s,normal' % LS_ID
- self.mock_tun_bridge.add_flow(priority=4, in_port=self.INT_OFPORT,
- dl_vlan=LV_ID, actions=action_string)
-
- action_string = 'mod_vlan_vid:%s,output:%s' % (LV_ID, self.INT_OFPORT)
- self.mock_tun_bridge.add_flow(priority=3, tun_id=LS_ID,
- dl_dst=BCAST_MAC, actions=action_string)
-
+ self.mock_tun_bridge.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan=LV_ID,
+ actions="strip_vlan,"
+ "set_tunnel:%s,output:%s" %
+ (LS_ID, ','.join(GRE_OFPORTS)))
+
+ self.mock_tun_bridge.add_flow(table=constants.TUN_TABLE['gre'],
+ priority=1,
+ tun_id=LS_ID,
+ actions="mod_vlan_vid:%s,resubmit(,%s)" %
+ (LV_ID, constants.LEARN_FROM_TUN))
self.mox.ReplayAll()
a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
'sudo', 2, ['gre'],
self.VETH_MTU)
a.available_local_vlans = set([LV_ID])
+ a.tun_br_ofports = TUN_OFPORTS
a.provision_local_vlan(NET_UUID, constants.TYPE_GRE, None, LS_ID)
self.mox.VerifyAll()
self.mox.VerifyAll()
def testReclaimLocalVlan(self):
- self.mock_tun_bridge.delete_flows(tun_id=LVM.segmentation_id)
-
+ self.mock_tun_bridge.delete_flows(
+ table=constants.TUN_TABLE['gre'], tun_id=LS_ID)
self.mock_tun_bridge.delete_flows(dl_vlan=LVM.vlan)
self.mox.ReplayAll()
'tag', str(LVM.vlan))
self.mock_int_bridge.delete_flows(in_port=VIF_PORT.ofport)
- action_string = 'mod_vlan_vid:%s,normal' % LV_ID
- self.mock_tun_bridge.add_flow(priority=3, tun_id=LS_ID,
- dl_dst=VIF_PORT.vif_mac,
- actions=action_string)
-
self.mox.ReplayAll()
a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
self.TUN_BRIDGE,
self.mox.VerifyAll()
def testPortUnbound(self):
- self.mock_int_bridge.set_db_attribute('Port', VIF_PORT.port_name,
- 'tag', str(LVM.vlan))
- self.mock_int_bridge.delete_flows(in_port=VIF_PORT.ofport)
+ self.mox.StubOutWithMock(
+ ovs_neutron_agent.OVSNeutronAgent, 'reclaim_local_vlan')
+ ovs_neutron_agent.OVSNeutronAgent.reclaim_local_vlan(NET_UUID, LVM)
- action_string = 'mod_vlan_vid:%s,normal' % LV_ID
- self.mock_tun_bridge.add_flow(priority=3, tun_id=LS_ID,
- dl_dst=VIF_PORT.vif_mac,
- actions=action_string)
- self.mock_tun_bridge.delete_flows(dl_dst=VIF_MAC, tun_id=LS_ID)
self.mox.ReplayAll()
a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
'sudo', 2, ['gre'],
self.VETH_MTU)
a.local_vlan_map[NET_UUID] = LVM
- a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID)
- a.available_local_vlans = set([LV_ID])
- a.local_vlan_map[NET_UUID] = LVM
a.port_unbound(VIF_ID, NET_UUID)
self.mox.VerifyAll()