Adding support for OLT side metadata field

Due to historic reasons, ONOS injects a metadata match field into one
of the downstream unicats flow rules, which was not yet handled by voltha.
That is fixed and tested now.

Change-Id: Ic8a47de515fa5837a70941be22da9e2d6539f614
diff --git a/cli/main.py b/cli/main.py
index fa778a1..12a157f 100755
--- a/cli/main.py
+++ b/cli/main.py
@@ -498,7 +498,8 @@
                 priority=500,
                 match_fields=[
                     in_port(nni_port_no),
-                    vlan_vid(4096 + 1000)
+                    vlan_vid(4096 + 1000),
+                    metadata(40)  # here to mimic an ONOS artifact
                 ],
                 actions=[pop_vlan()],
                 next_table_id=1
diff --git a/cli/utils.py b/cli/utils.py
index 0d677d1..7ea9c77 100644
--- a/cli/utils.py
+++ b/cli/utils.py
@@ -89,6 +89,7 @@
     'UDP_DST': lambda f: (107, 'udp_dst', str(f['udp_dst'])),
     'TCP_SRC': lambda f: (108, 'tcp_src', str(f['tcp_src'])),
     'TCP_DST': lambda f: (109, 'tcp_dst', str(f['tcp_dst'])),
+    'METADATA': lambda f: (110, 'metadata', str(f['table_metadata'])),
 }
 
 
diff --git a/ofagent/converter.py b/ofagent/converter.py
index 59330bd..2b97055 100644
--- a/ofagent/converter.py
+++ b/ofagent/converter.py
@@ -128,6 +128,7 @@
         duration_nsec=pb.stats.duration_nsec,
         bucket_stats=[to_loxi(bstat) for bstat in pb.stats.bucket_stats])
 
+
 def ofp_bucket_counter_to_loxy_bucket_counter(pb):
     return of13.bucket_counter(
         packet_count=pb.packet_count,
@@ -239,6 +240,22 @@
             ipv4_dst=lo.value))
 
 
+def loxi_oxm_udp_dst_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_UDP_DST,
+            udp_dst=lo.value))
+
+
+def loxi_oxm_metadata_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_METADATA,
+            table_metadata=lo.value))
+
+
 def loxi_apply_actions_to_ofp_instruction(lo):
     return pb2.ofp_instruction(
         type=pb2.OFPIT_APPLY_ACTIONS,
@@ -302,6 +319,8 @@
     of13.oxm.vlan_vid: loxi_oxm_vlan_vid_to_ofp_oxm,
     of13.oxm.vlan_pcp: loxi_oxm_vlan_pcp_to_ofp_oxm,
     of13.oxm.ipv4_dst: loxi_oxm_ipv4_dst_to_ofp_oxm,
+    of13.oxm.udp_dst: loxi_oxm_udp_dst_to_ofp_oxm,
+    of13.oxm.metadata: loxi_oxm_metadata_to_ofp_oxm,
 
     of13.instruction.apply_actions: loxi_apply_actions_to_ofp_instruction,
     of13.instruction.goto_table: loxi_goto_table_to_ofp_instruction,
diff --git a/ofagent/of_protocol_handler.py b/ofagent/of_protocol_handler.py
index 0508d76..87feb37 100644
--- a/ofagent/of_protocol_handler.py
+++ b/ofagent/of_protocol_handler.py
@@ -104,9 +104,13 @@
     def handle_experimenter_request(self, req):
         raise NotImplementedError()
 
-    @inlineCallbacks
     def handle_flow_mod_request(self, req):
-        yield self.rpc.update_flow_table(self.device_id, to_grpc(req))
+        try:
+            grpc_req = to_grpc(req)
+        except Exception, e:
+            log.exception('failed-to-convert', e=e)
+        else:
+            return self.rpc.update_flow_table(self.device_id, grpc_req)
 
     def handle_get_async_request(self, req):
         raise NotImplementedError()
diff --git a/ponsim/ponsim.py b/ponsim/ponsim.py
index c855e39..3086ff5 100644
--- a/ponsim/ponsim.py
+++ b/ponsim/ponsim.py
@@ -138,9 +138,12 @@
                     return False
 
             elif field.type == UDP_DST:
-                if field.udsp_dst != get_udp_dst(frame):
+                if field.udp_dst != get_udp_dst(frame):
                     return False
 
+            elif field.type == METADATA:
+                pass  # safe to ignore
+
             else:
                 raise NotImplementedError('field.type=%d' % field.type)
 
diff --git a/voltha/adapters/simulated_olt/simulated_olt.py b/voltha/adapters/simulated_olt/simulated_olt.py
index d5cd697..ec52c3a 100644
--- a/voltha/adapters/simulated_olt/simulated_olt.py
+++ b/voltha/adapters/simulated_olt/simulated_olt.py
@@ -389,6 +389,9 @@
                         _vlan_pcp = field.vlan_pcp
                         pass  # construct VLAN PCP based filter condition here
 
+                    elif field.type == METADATA:
+                        pass  # safe to ignore
+
                     # TODO
                     else:
                         raise NotImplementedError('field.type={}'.format(
diff --git a/voltha/adapters/tibit_olt/tibit_olt.py b/voltha/adapters/tibit_olt/tibit_olt.py
index f162a7d..615394b 100644
--- a/voltha/adapters/tibit_olt/tibit_olt.py
+++ b/voltha/adapters/tibit_olt/tibit_olt.py
@@ -455,6 +455,9 @@
                         log.info('#### field.type == UDP_DST ####')
                         pass  # construct UDP SDT based filter here
 
+                    elif field.type == METADATA:
+                        pass  # safe to ignore
+
                     else:
                         raise NotImplementedError('field.type={}'.format(
                             field.type))
diff --git a/voltha/core/flow_decomposer.py b/voltha/core/flow_decomposer.py
index b8221b5..6c72f07 100644
--- a/voltha/core/flow_decomposer.py
+++ b/voltha/core/flow_decomposer.py
@@ -221,6 +221,10 @@
 def arp_tha(_arp_tha):
     return ofb_field(type=ARP_THA, arp_tha=_arp_tha)
 
+def metadata(_table_metadata):
+    return ofb_field(type=METADATA, table_metadata=_table_metadata)
+
+
 # TODO finish for rest of match fields