VOL-506: Multiple PON Multicast support. Original correction by Niren Chidrawar.
Change-Id: Ia41dbd8b88d007db96e7773bfc95b78481d9fa13
diff --git a/tests/utests/voltha/core/flow_helpers.py b/tests/utests/voltha/core/flow_helpers.py
index 856f986..2141f6f 100644
--- a/tests/utests/voltha/core/flow_helpers.py
+++ b/tests/utests/voltha/core/flow_helpers.py
@@ -37,3 +37,10 @@
diff = make_patch(msg1_dict, msg2_dict)
return dumps(diff.patch, indent=2)
+ def assertFlowNotInFlows(self, flow, flows):
+ if flow in flows.items:
+ self.fail('flow id %d is in flows' % flow.id)
+
+ def assertFlowInFlows(self, flow, flows):
+ if flow not in flows.items:
+ self.fail('flow id %d is not in flows' % flow.id)
diff --git a/tests/utests/voltha/core/test_logical_device_agent.py b/tests/utests/voltha/core/test_logical_device_agent.py
index bfa6c5b..d9d7652 100644
--- a/tests/utests/voltha/core/test_logical_device_agent.py
+++ b/tests/utests/voltha/core/test_logical_device_agent.py
@@ -640,7 +640,7 @@
actions=[group(2)]
))
self.lda._flow_table_updated(self.flows)
- self.assertEqual(len(self.device_flows['olt'].items), 1)
+ self.assertEqual(len(self.device_flows['olt'].items), 0)
self.assertEqual(len(self.device_flows['onu1'].items), 3)
self.assertEqual(len(self.device_flows['onu2'].items), 3)
self.assertEqual(len(self.device_flows['onu2'].items), 3)
@@ -648,7 +648,7 @@
self.assertEqual(len(self.device_groups['onu1'].items), 0)
self.assertEqual(len(self.device_groups['onu2'].items), 0)
- self.assertFlowsEqual(self.device_flows['olt'].items[0], mk_flow_stat(
+ self.assertFlowNotInFlows(mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe60a0a0a)],
@@ -656,7 +656,7 @@
pop_vlan(),
output(1)
]
- ))
+ ), self.device_flows['olt'])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLEX TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -769,7 +769,7 @@
self.lda._flow_table_updated(self.flows)
# now check device level flows
- self.assertEqual(len(self.device_flows['olt'].items), 16)
+ self.assertEqual(len(self.device_flows['olt'].items), 15)
self.assertEqual(len(self.device_flows['onu1'].items), 5)
self.assertEqual(len(self.device_flows['onu2'].items), 5)
self.assertEqual(len(self.device_groups['olt'].items), 0)
@@ -835,37 +835,37 @@
actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 4000)),
output(0)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[10], mk_flow_stat(
+ self.assertFlowNotInFlows(mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe4010101)],
actions=[pop_vlan(), output(1)]
- ))
- self.assertFlowsEqual(self.device_flows['olt'].items[11], mk_flow_stat(
+ ), self.device_flows['olt'])
+ self.assertFlowsEqual(self.device_flows['olt'].items[10], mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe4010102)],
actions=[pop_vlan(), output(1)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[12], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[11], mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe4010103)],
actions=[pop_vlan(), output(1)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[13], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[12], mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe4010104)],
actions=[pop_vlan(), output(1)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[14], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[13], mk_flow_stat(
priority=500,
match_fields=[in_port(1), vlan_vid(4096 + 101)],
actions=[
push_vlan(0x8100), set_field(vlan_vid(4096 + 1000)), output(0)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[15], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[14], mk_flow_stat(
priority=500,
match_fields=[in_port(1), vlan_vid(4096 + 102)],
actions=[
diff --git a/tests/utests/voltha/core/test_multipon_lda.py b/tests/utests/voltha/core/test_multipon_lda.py
index cb99cf2..31a8d9d 100644
--- a/tests/utests/voltha/core/test_multipon_lda.py
+++ b/tests/utests/voltha/core/test_multipon_lda.py
@@ -334,7 +334,7 @@
ipv4_dst(0xe60a0a0a)],
actions=[
pop_vlan(),
- output(2)
+ output(1)
]
))
self.assertFlowsEqual(self.device_flows['onu2'].items[3], mk_flow_stat(
@@ -361,7 +361,7 @@
actions=[group(2)]
))
self.lda._flow_table_updated(self.flows)
- self.assertEqual(len(self.device_flows['olt'].items), 1)
+ self.assertEqual(len(self.device_flows['olt'].items), 0)
self.assertEqual(len(self.device_flows['onu1'].items), 3)
self.assertEqual(len(self.device_flows['onu2'].items), 3)
self.assertEqual(len(self.device_flows['onu2'].items), 3)
@@ -369,7 +369,7 @@
self.assertEqual(len(self.device_groups['onu1'].items), 0)
self.assertEqual(len(self.device_groups['onu2'].items), 0)
- self.assertFlowsEqual(self.device_flows['olt'].items[0], mk_flow_stat(
+ self.assertFlowNotInFlows(mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe60a0a0a)],
@@ -377,7 +377,7 @@
pop_vlan(),
output(2)
]
- ))
+ ), self.device_flows['olt'])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLEX TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_complex_flow_table_decomposition(self):
@@ -489,7 +489,7 @@
self.lda._flow_table_updated(self.flows)
# now check device level flows
- self.assertEqual(len(self.device_flows['olt'].items), 18)
+ self.assertEqual(len(self.device_flows['olt'].items), 17)
self.assertEqual(len(self.device_flows['onu1'].items), 5)
self.assertEqual(len(self.device_flows['onu2'].items), 5)
self.assertEqual(len(self.device_groups['olt'].items), 0)
@@ -555,47 +555,47 @@
actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 4000)),
output(0)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[10], mk_flow_stat(
+ self.assertFlowNotInFlows(mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
ipv4_dst(0xe4010101)],
actions=[pop_vlan(), output(2)]
+ ), self.device_flows['olt'])
+ self.assertFlowsEqual(self.device_flows['olt'].items[10], mk_flow_stat(
+ priority=1000,
+ match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
+ ipv4_dst(0xe4010102)],
+ actions=[pop_vlan(), output(1)]
))
self.assertFlowsEqual(self.device_flows['olt'].items[11], mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
- ipv4_dst(0xe4010102)],
- actions=[pop_vlan(), output(2)]
+ ipv4_dst(0xe4010103)],
+ actions=[pop_vlan(), output(1)]
))
self.assertFlowsEqual(self.device_flows['olt'].items[12], mk_flow_stat(
priority=1000,
match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
- ipv4_dst(0xe4010103)],
- actions=[pop_vlan(), output(2)]
+ ipv4_dst(0xe4010104)],
+ actions=[pop_vlan(), output(1)]
))
self.assertFlowsEqual(self.device_flows['olt'].items[13], mk_flow_stat(
- priority=1000,
- match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
- ipv4_dst(0xe4010104)],
- actions=[pop_vlan(), output(2)]
- ))
- self.assertFlowsEqual(self.device_flows['olt'].items[14], mk_flow_stat(
priority=500,
match_fields=[in_port(0), metadata(101), vlan_vid(4096 + 1000)],
actions=[pop_vlan(), output(1)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[15], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[14], mk_flow_stat(
priority=500,
match_fields=[in_port(1), vlan_vid(4096 + 101)],
actions=[
push_vlan(0x8100), set_field(vlan_vid(4096 + 1000)), output(0)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[16], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[15], mk_flow_stat(
priority=500,
match_fields=[in_port(0), metadata(201), vlan_vid(4096 + 1000)],
actions=[pop_vlan(), output(2)]
))
- self.assertFlowsEqual(self.device_flows['olt'].items[17], mk_flow_stat(
+ self.assertFlowsEqual(self.device_flows['olt'].items[16], mk_flow_stat(
priority=500,
match_fields=[in_port(2), vlan_vid(4096 + 201)],
actions=[
diff --git a/voltha/core/flow_decomposer.py b/voltha/core/flow_decomposer.py
index fad56dc..d5bb69f 100644
--- a/voltha/core/flow_decomposer.py
+++ b/voltha/core/flow_decomposer.py
@@ -454,14 +454,19 @@
def hash_flow_stats(flow):
"""
Return unique 64-bit integer hash for flow covering the following
- attributes: 'table_id', 'priority', 'flags', 'cookie', 'match'
+ attributes: 'table_id', 'priority', 'flags', 'cookie', 'match', '_instruction_string'
"""
- hex = md5('{},{},{},{},{}'.format(
+ _instruction_string = ""
+ for _instruction in flow.instructions:
+ _instruction_string += _instruction.SerializeToString()
+
+ hex = md5('{},{},{},{},{},{}'.format(
flow.table_id,
flow.priority,
flow.flags,
flow.cookie,
- flow.match.SerializeToString()
+ flow.match.SerializeToString(),
+ _instruction_string
)).hexdigest()
return int(hex[:16], 16)
@@ -522,7 +527,9 @@
self._egress_port == other._egress_port)
def __ne__(self, other):
return not self.__eq__(other)
-
+ def __str__(self):
+ return 'RouteHop device_id {}, ingress_port {}, egress_port {}'.format(
+ self._device.id, self._ingress_port, self._egress_port)
class FlowDecomposer(object):
@@ -781,73 +788,85 @@
))
- else: # multicast case
+ else:
grp_id = get_group(flow)
- assert grp_id is not None
+
+ if grp_id is not None: # multicast case
- fl_lst, _ = device_rules.setdefault(
- ingress_hop.device.id, ([], []))
- fl_lst.append(mk_flow_stat(
- priority=flow.priority,
- cookie=flow.cookie,
- match_fields=[
- in_port(ingress_hop.ingress_port.port_no)
- ] + [
- field for field in get_ofb_fields(flow)
- if field.type not in (IN_PORT,)
- ],
- actions=[
- action for action in get_actions(flow)
- if action.type not in (GROUP,)
- ] + [
- pop_vlan(),
- output(ingress_hop.egress_port.port_no)
- ]
- ))
+ fl_lst_olt, _ = device_rules.setdefault(
+ ingress_hop.device.id, ([], []))
- # having no group yet is the same as having a group with
- # no buckets
- group = group_map.get(grp_id, ofp.ofp_group_entry())
+ # having no group yet is the same as having a group with
+ # no buckets
+ group = group_map.get(grp_id, ofp.ofp_group_entry())
- for bucket in group.desc.buckets:
- found_pop_vlan = False
- other_actions = []
- for action in bucket.actions:
- if action.type == POP_VLAN:
- found_pop_vlan = True
- elif action.type == OUTPUT:
- out_port_no = action.output.port
- else:
- other_actions.append(action)
- # re-run route request to determine egress device and
- # ports
- route2 = self.get_route(in_port_no, out_port_no)
- if route2 is None:
- log.error('mc-no-route', in_port_no=in_port_no,
- out_port_no=out_port_no, route2=route2,
- comment='deleting flow')
- self.flow_delete(flow)
- continue
+ for bucket in group.desc.buckets:
+ found_pop_vlan = False
+ other_actions = []
+ for action in bucket.actions:
+ if action.type == POP_VLAN:
+ found_pop_vlan = True
+ elif action.type == OUTPUT:
+ out_port_no = action.output.port
+ else:
+ other_actions.append(action)
+ # re-run route request to determine egress device and
+ # ports
+ route2 = self.get_route(in_port_no, out_port_no)
+ if not route2 or len(route2) != 2:
+ log.error('mc-no-route', in_port_no=in_port_no,
+ out_port_no=out_port_no, route2=route2,
+ comment='deleting flow')
+ self.flow_delete(flow)
+ continue
- assert len(route2) == 2
- ingress_hop2, egress_hop = route2
- assert ingress_hop.ingress_port == ingress_hop2.ingress_port
+ ingress_hop2, egress_hop = route2
- fl_lst, _ = device_rules.setdefault(
- egress_hop.device.id, ([], []))
- fl_lst.append(mk_flow_stat(
- priority=flow.priority,
- cookie=flow.cookie,
- match_fields=[
- in_port(egress_hop.ingress_port.port_no)
- ] + [
- field for field in get_ofb_fields(flow)
- if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP)
- ],
- actions=other_actions + [
- output(egress_hop.egress_port.port_no)
- ]
- ))
+ if ingress_hop.ingress_port != ingress_hop2.ingress_port:
+ log.error('mc-ingress-hop-hop2-mismatch',
+ ingress_hop=ingress_hop,
+ ingress_hop2=ingress_hop2,
+ in_port_no=in_port_no,
+ out_port_no=out_port_no,
+ comment='ignoring flow')
+ continue
+
+ fl_lst_olt.append(mk_flow_stat(
+ priority=flow.priority,
+ cookie=flow.cookie,
+ match_fields=[
+ in_port(ingress_hop.ingress_port.port_no)
+ ] + [
+ field for field in get_ofb_fields(flow)
+ if field.type not in (IN_PORT,)
+ ],
+ actions=[
+ action for action in get_actions(flow)
+ if action.type not in (GROUP,)
+ ] + [
+ pop_vlan(),
+ output(egress_hop.ingress_port.port_no)
+ ]
+ ))
+
+ fl_lst_onu, _ = device_rules.setdefault(
+ egress_hop.device.id, ([], []))
+ fl_lst_onu.append(mk_flow_stat(
+ priority=flow.priority,
+ cookie=flow.cookie,
+ match_fields=[
+ in_port(egress_hop.ingress_port.port_no)
+ ] + [
+ field for field in get_ofb_fields(flow)
+ if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP)
+ ],
+ actions=other_actions + [
+ output(egress_hop.egress_port.port_no)
+ ]
+ ))
+ else:
+ raise NotImplementedError('undefined downstream case for flows')
+
return device_rules