Flow decomposer support for Tech Profile Reference in metadata instead of using Flow Table ID as reference in Table 1.

Also in this patch:
 - Update flow count field of meter stats
 - ONU root field was True and because of this flow decomposer was not working properly - it is fixed (now onu root field is false) - related to VOL-1354
 - Meter & write metadata support is added to vcli
 - No-action (drop) flow support is added.
 - Removed broken wildcard inport support and in-band control support
 - Meter functions (meter stats reply, meter modify etc.) are fixed
 - Metadata information  passed on to the OLT and ONU Adapters.
 - Meter Reference in all Flow Tables passed on to the OLT and ONU Adapters.
 - Fixed unit tests and added more unit tests
 - Fixed ponsim (partially) to deal with changes to flow decomposer

Change-Id: Id4b16fc05a6740a3f521b2cc4f6fdbff88da4fa5
diff --git a/ofagent/converter.py b/ofagent/converter.py
index fb7aae5..7156d60 100644
--- a/ofagent/converter.py
+++ b/ofagent/converter.py
@@ -31,6 +31,7 @@
     FieldDescriptor.TYPE_STRING: str
 })
 
+
 def pb2dict(pb):
     """
     Convert protobuf to a dict of values good for instantiating
@@ -41,33 +42,53 @@
     """
     return protobuf_to_dict(pb, type_callable_map)
 
+
 def to_loxi(grpc_object):
     cls = grpc_object.__class__
     converter = to_loxi_converters[cls.__name__]
     return converter(grpc_object)
 
+
 def to_grpc(loxi_object):
     cls = loxi_object.__class__
     converter = to_grpc_converters[cls]
     return converter(loxi_object)
 
+
 def ofp_port_to_loxi_port_desc(pb):
     kw = pb2dict(pb)
     return of13.common.port_desc(**kw)
 
+
 def ofp_port_status_to_loxi_port_status(pb):
     return of13.message.port_status(
         reason=pb.reason,
         desc=ofp_port_to_loxi_port_desc(pb.desc)
     )
 
+
 def ofp_port_stats_to_loxi_port_stats(pb):
     kw = pb2dict(pb)
     return of13.port_stats_entry(**kw)
 
+
 def ofp_meter_stats_to_loxi_meter_stats(pb):
-    kw = pb2dict(pb)
-    return of13.meter_stats(**kw)
+    return of13.meter_stats(
+        meter_id=pb.meter_id,
+        flow_count=pb.flow_count,
+        packet_in_count=pb.packet_in_count,
+        byte_in_count=pb.byte_in_count,
+        duration_sec=pb.duration_sec,
+        duration_nsec=pb.duration_nsec,
+        band_stats=[to_loxi(band_stat) for band_stat in pb.band_stats])
+
+
+def ofp_meter_band_stats_to_loxi_meter_stats(pb):
+    return of13.meter_band_stats(
+        packet_band_count=pb.packet_band_count,
+        byte_band_count=pb.byte_band_count
+    )
+
 
 def make_loxi_field(oxm_field):
     assert oxm_field['oxm_class'] == pb2.OFPXMC_OPENFLOW_BASIC
@@ -87,6 +108,9 @@
             of13.oxm.ip_proto(value=ofb_field['ip_proto']))
 
     elif field_type == pb2.OFPXMT_OFB_VLAN_VID:
+        if ofb_field.get('has_mask', 0):
+            return of13.oxm.vlan_vid_masked(value=ofb_field['vlan_vid'],
+                                            value_mask=ofb_field['vlan_vid_mask'])
         return (
             of13.oxm.vlan_vid(value=ofb_field['vlan_vid']))
 
@@ -118,6 +142,7 @@
         raise NotImplementedError(
             'OXM match field for type %s' % field_type)
 
+
 def make_loxi_match(match):
     assert match.get('type', pb2.OFPMT_STANDARD) == pb2.OFPMT_OXM
     loxi_match_fields = []
@@ -174,12 +199,20 @@
             return of13.instruction.write_actions(
                 actions=[make_loxi_action(a)
                          for a in inst['actions']['actions']])
+        elif type == pb2.OFPIT_WRITE_METADATA:
+            return of13.instruction.write_metadata(
+                metadata=inst['write_metadata']['metadata'])
+        elif type == pb2.OFPIT_METER:
+            return of13.instruction.meter(
+                meter_id=inst['meter']['meter_id'])
 
         else:
             raise NotImplementedError('Instruction type %d' % type)
 
     kw['match'] = make_loxi_match(kw['match'])
-    kw['instructions'] = [make_loxi_instruction(i) for i in kw['instructions']]
+    # if the flow action is drop, then the instruction is not found in the dict
+    if 'instructions' in kw:
+        kw['instructions'] = [make_loxi_instruction(i) for i in kw['instructions']]
     del kw['id']
     return of13.flow_stats_entry(**kw)
 
@@ -195,6 +228,7 @@
     )
     return packet_in
 
+
 def ofp_group_desc_to_loxi_group_desc(pb):
     return of13.group_desc_stats_entry(
         group_type=pb.type,
@@ -239,7 +273,8 @@
     'ofp_bucket': ofp_bucket_to_loxi_bucket,
     'ofp_action': make_loxi_action,
     'ofp_port_stats': ofp_port_stats_to_loxi_port_stats,
-    'ofp_meter_stats': ofp_meter_stats_to_loxi_meter_stats
+    'ofp_meter_stats': ofp_meter_stats_to_loxi_meter_stats,
+    'ofp_meter_band_stats': ofp_meter_band_stats_to_loxi_meter_stats
 }
 
 
@@ -259,6 +294,7 @@
         match=to_grpc(lo.match),
         instructions=[to_grpc(i) for i in lo.instructions])
 
+
 def loxi_meter_mod_to_ofp_meter_mod(lo):
     return pb2.ofp_meter_mod(
         command=lo.command,
@@ -368,6 +404,16 @@
             vlan_vid=lo.value))
 
 
+def loxi_oxm_vlan_vid_masked_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_VLAN_VID,
+            has_mask=True,
+            vlan_vid=lo.value,
+            vlan_vid_mask=lo.value_mask))
+
+
 def loxi_oxm_vlan_pcp_to_ofp_oxm(lo):
     return pb2.ofp_oxm_field(
         oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
@@ -425,6 +471,20 @@
         goto_table=pb2.ofp_instruction_goto_table(table_id=lo.table_id))
 
 
+def loxi_write_metadata_to_ofp_instruction(lo):
+    return pb2.ofp_instruction(
+        type=pb2.OFPIT_WRITE_METADATA,
+        write_metadata=pb2.ofp_instruction_write_metadata(
+            metadata=lo.metadata,
+            metadata_mask=lo.metadata_mask))
+
+
+def loxi_meter_to_ofp_instruction(lo):
+    return pb2.ofp_instruction(
+        type=pb2.OFPIT_METER,
+        meter=pb2.ofp_instruction_meter(meter_id=lo.meter_id))
+
+
 def loxi_output_action_to_ofp_action(lo):
     return pb2.ofp_action(
         type=pb2.OFPAT_OUTPUT,
@@ -486,6 +546,7 @@
     of13.oxm.in_port: loxi_oxm_in_port_to_ofp_oxm,
     of13.oxm.ip_proto: loxi_oxm_ip_proto_to_ofp_oxm,
     of13.oxm.vlan_vid: loxi_oxm_vlan_vid_to_ofp_oxm,
+    of13.oxm.vlan_vid_masked: loxi_oxm_vlan_vid_masked_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_src: loxi_oxm_udp_src_to_ofp_oxm,
@@ -496,6 +557,8 @@
     of13.instruction.clear_actions: loxi_clear_actions_to_ofp_instruction,
     of13.instruction.write_actions: loxi_write_actions_to_ofp_instruction,
     of13.instruction.goto_table: loxi_goto_table_to_ofp_instruction,
+    of13.instruction.write_metadata: loxi_write_metadata_to_ofp_instruction,
+    of13.instruction.meter: loxi_meter_to_ofp_instruction,
 
     of13.action.output: loxi_output_action_to_ofp_action,
     of13.action.group: loxi_group_action_to_ofp_action,
diff --git a/ofagent/grpc_client.py b/ofagent/grpc_client.py
index 16be6f0..155f18d 100644
--- a/ofagent/grpc_client.py
+++ b/ofagent/grpc_client.py
@@ -210,7 +210,7 @@
             meter_mod=meter_mod
         )
         res = yield threads.deferToThread(
-            self.local_stub.UpdateLogicalDeviceMeterTable, req)
+            self.local_stub.UpdateLogicalDeviceMeterTable, req, timeout=self.grpc_timeout)
         returnValue(res)
 
     @inlineCallbacks
@@ -238,6 +238,13 @@
         returnValue(res.items)
 
     @inlineCallbacks
+    def list_meters(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDeviceMeters, req, timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
     def list_ports(self, device_id):
         req = ID(id=device_id)
         res = yield threads.deferToThread(
@@ -262,10 +269,3 @@
         res = yield threads.deferToThread(
             self.local_stub.Subscribe, subscriber, timeout=self.grpc_timeout)
         returnValue(res)
-
-    @inlineCallbacks
-    def get_meter_stats(self, device_id):
-        req = ID(id=device_id)
-        res = yield threads.deferToThread(
-            self.local_stub.GetMeterStatsOfLogicalDevice, req)
-        returnValue(res.items)
diff --git a/ofagent/of_protocol_handler.py b/ofagent/of_protocol_handler.py
index 01f896b..813c8a8 100644
--- a/ofagent/of_protocol_handler.py
+++ b/ofagent/of_protocol_handler.py
@@ -132,7 +132,6 @@
         elif self.role == ofp.OFPCR_ROLE_SLAVE:
            self.cxn.send(ofp.message.bad_request_error_msg(code=ofp.OFPBRC_IS_SLAVE))
 
-
     def handle_meter_mod_request(self, req):
         if self.role == ofp.OFPCR_ROLE_MASTER or self.role == ofp.OFPCR_ROLE_EQUAL:
             try:
@@ -148,10 +147,9 @@
     @inlineCallbacks
     def handle_meter_stats_request(self, req):
         try:
-            meter_stats = yield self.rpc.get_meter_stats(self.device_id)
-            meter_stats = [to_loxi(m) for m in meter_stats]
-            of_message = ofp.message.meter_stats_reply(xid=req.xid, entries=meter_stats)
-            self.cxn.send(of_message)
+            meters = yield self.rpc.list_meters(self.device_id)
+            self.cxn.send(ofp.message.meter_stats_reply(
+                xid=req.xid, entries=[to_loxi(m.stats) for m in meters]))
         except Exception, e:
             log.exception("failed-meter-stats-request", req=req, e=e)