VOL-1237: Fix for OLT Pipeline not providing inner Q-in-Q tag to OLT

Change-Id: Ib1f61f0388a4ae253c140fca3e3170f65b188778
diff --git a/tests/utests/voltha/core/test_flow_decomposer.py b/tests/utests/voltha/core/test_flow_decomposer.py
index 598f1f0..5e77621 100644
--- a/tests/utests/voltha/core/test_flow_decomposer.py
+++ b/tests/utests/voltha/core/test_flow_decomposer.py
@@ -650,7 +650,7 @@
         flow1 = mk_flow_stat(
             match_fields=[
                 in_port(0),
-                vlan_vid(ofp.OFPVID_PRESENT | 1000),
+                metadata((1000 << 32) | 1),
                 vlan_pcp(0)
             ],
             actions=[
@@ -682,7 +682,7 @@
             priority=500,
             match_fields=[
                 in_port(2),
-                vlan_vid(ofp.OFPVID_PRESENT | 1000),
+                metadata(1000),
                 vlan_pcp(0)
             ],
             actions=[
diff --git a/tests/utests/voltha/core/test_multipon_lda.py b/tests/utests/voltha/core/test_multipon_lda.py
index 74fd11f..cb99cf2 100644
--- a/tests/utests/voltha/core/test_multipon_lda.py
+++ b/tests/utests/voltha/core/test_multipon_lda.py
@@ -443,7 +443,7 @@
                 match_fields=[
                     in_port(0),
                     vlan_vid(4096 + 1000),
-                    metadata(c_vid)
+                    metadata((c_vid << 32) | port)
                 ],
                 actions=[pop_vlan()],
                 next_table_id=1
@@ -581,7 +581,7 @@
         ))
         self.assertFlowsEqual(self.device_flows['olt'].items[14], mk_flow_stat(
             priority=500,
-            match_fields=[in_port(0), vlan_vid(4096 + 1000), metadata(101)],
+            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(
@@ -592,7 +592,7 @@
         ))
         self.assertFlowsEqual(self.device_flows['olt'].items[16], mk_flow_stat(
             priority=500,
-            match_fields=[in_port(0), vlan_vid(4096 + 1000), metadata(201)],
+            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(
diff --git a/voltha/adapters/openolt/openolt_flow_mgr.py b/voltha/adapters/openolt/openolt_flow_mgr.py
index d098065..edda513 100644
--- a/voltha/adapters/openolt/openolt_flow_mgr.py
+++ b/voltha/adapters/openolt/openolt_flow_mgr.py
@@ -644,9 +644,14 @@
     def find_next_flow(self, flow):
         table_id = fd.get_goto_table_id(flow)
         metadata = 0
+        # Prior to ONOS 1.13.5, Metadata contained the UNI output port number. In
+        # 1.13.5 and later, the lower 32-bits is the output port number and the
+        # upper 32-bits is the inner-vid we are looking for. Use just the lower 32
+        # bits.  Allows this code to work with pre- and post-1.13.5 ONOS OltPipeline
+
         for field in fd.get_ofb_fields(flow):
             if field.type == fd.METADATA:
-                metadata = field.table_metadata
+                metadata = field.table_metadata & 0xFFFFFFFF
         if table_id is None:
             return None
         flows = self.logical_flows_proxy.get('/').items
diff --git a/voltha/core/flow_decomposer.py b/voltha/core/flow_decomposer.py
index 35f3f1d..fad56dc 100644
--- a/voltha/core/flow_decomposer.py
+++ b/voltha/core/flow_decomposer.py
@@ -316,13 +316,62 @@
     return None
 
 def get_metadata(flow):
+    ''' legacy get method (only want lower 32 bits '''
+    for field in get_ofb_fields(flow):
+        if field.type == METADATA:
+            return field.table_metadata & 0xffffffff
+    return None
+
+def get_metadata_64_bit(flow):
     for field in get_ofb_fields(flow):
         if field.type == METADATA:
             return field.table_metadata
     return None
 
-# test and extract next table and group information
 
+def get_port_number_from_metadata(flow):
+    """
+    The port number (UNI on ONU) is in the lower 32-bits of metadata and
+    the inner_tag is in the upper 32-bits
+
+    This is set in the ONOS OltPipeline as a metadata field
+    """
+    md = get_metadata_64_bit(flow)
+
+    if md is None:
+        return None
+
+    if md <= 0xffffffff:
+        log.warn('onos-upgrade-suggested',
+                 netadata=md,
+                 message='Legacy MetaData detected form OltPipeline')
+        return md
+
+    return md & 0xffffffff
+
+
+def get_inner_tag_from_metadata(flow):
+    """
+    The port number (UNI on ONU) is in the lower 32-bits of metadata and
+    the inner_tag is in the upper 32-bits
+
+    This is set in the ONOS OltPipeline as a metadata field
+    """
+    md = get_metadata_64_bit(flow)
+
+    if md is None:
+        return None
+
+    if md <= 0xffffffff:
+        log.warn('onos-upgrade-suggested',
+                 netadata=md,
+                 message='Legacy MetaData detected form OltPipeline')
+        return md
+
+    return (md >> 32) & 0xffffffff
+
+
+# test and extract next table and group information
 def has_next_table(flow):
     return get_goto_table_id(flow) is not None
 
@@ -670,30 +719,40 @@
                 if has_next_table(flow):
                     assert out_port_no is None
 
-                    # For downstream flows with dual-tags, recalculate route with
-                    # inner-tag as logical output port. Otherwise PON-0 is always
-                    # selected.
-                    inner_tag = get_metadata(flow)
-                    if inner_tag is not None:
-                        route = self.get_route(in_port_no, inner_tag)
+                    # For downstream flows with dual-tags, recalculate route.
+                    port_number = get_port_number_from_metadata(flow)
+
+                    if port_number is not None:
+                        route = self.get_route(in_port_no, port_number)
                         if route is None:
                             log.error('no-route-double-tag', in_port_no=in_port_no,
-                                      out_port_no=inner_tag, comment='deleting flow')
+                                      out_port_no=port_number, comment='deleting flow',
+                                      metadata=get_metadata_64_bit(flow))
                             self.flow_delete(flow)
                             return device_rules
                         assert len(route) == 2
                         ingress_hop, egress_hop = route
 
+                    inner_tag = get_inner_tag_from_metadata(flow)
+
+                    if inner_tag is None:
+                        log.error('no-inner-tag-double-tag', in_port_no=in_port_no,
+                                  out_port_no=port_number, comment='deleting flow',
+                                  metadata=get_metadata_64_bit(flow))
+                        self.flow_delete(flow)
+                        return device_rules
+
                     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)
+                            in_port(ingress_hop.ingress_port.port_no),
+                            metadata(inner_tag)
                         ] + [
                             field for field in get_ofb_fields(flow)
-                            if field.type not in (IN_PORT,)
+                            if field.type not in (IN_PORT, METADATA)
                         ],
                         actions=[
                             action for action in get_actions(flow)