This patch solves the following bugs:

* For the EAPOL trap flow, ONOS sends flow-mod including the meter id before the related meter-mod. Because of this, Voltha sends wrong flow count for the meter stats reply
* Getting the tech profile id & inner vlan id from the write metadata instruction
* ofp_flow_removed implementation is added

Change-Id: Ib74c7b80db85506f675c2d7ff25bf644c64e5398
diff --git a/ofagent/agent.py b/ofagent/agent.py
index 6117c8f..5c2da16 100644
--- a/ofagent/agent.py
+++ b/ofagent/agent.py
@@ -160,9 +160,11 @@
     def forward_change_event(self, event):
         # assert isinstance(event, ChangeEvent)
         log.info('got-change-event', change_event=event)
-        if event.HasField("port_status"):
-            if self.proto_handler is not None:
+        if self.proto_handler is not None:
+            if event.HasField("port_status"):
                 self.proto_handler.forward_port_status(event.port_status)
+            elif event.HasField("flow_removed"):
+                self.proto_handler.forward_flow_removed(event.flow_removed)
         else:
             log.error('unknown-change-event', change_event=event)
 
diff --git a/ofagent/converter.py b/ofagent/converter.py
index 7156d60..1bebc4f 100644
--- a/ofagent/converter.py
+++ b/ofagent/converter.py
@@ -66,6 +66,14 @@
         desc=ofp_port_to_loxi_port_desc(pb.desc)
     )
 
+def ofp_flow_removed_to_loxi_flow_removed(pb):
+    return of13.message.flow_removed(
+        cookie=pb.cookie,
+        priority=pb.priority,
+        reason=pb.reason,
+        table_id=pb.table_id,
+        match=make_loxi_match(pb2dict(pb.match))
+    )
 
 def ofp_port_stats_to_loxi_port_stats(pb):
     kw = pb2dict(pb)
@@ -265,6 +273,7 @@
 to_loxi_converters = {
     'ofp_port': ofp_port_to_loxi_port_desc,
     'ofp_port_status': ofp_port_status_to_loxi_port_status,
+    'ofp_flow_removed': ofp_flow_removed_to_loxi_flow_removed,
     'ofp_flow_stats': ofp_flow_stats_to_loxi_flow_stats,
     'ofp_packet_in': ofp_packet_in_to_loxi_packet_in,
     'ofp_group_stats': ofp_group_stats_to_loxi_group_stats,
diff --git a/ofagent/of_protocol_handler.py b/ofagent/of_protocol_handler.py
index 813c8a8..7deee6d 100644
--- a/ofagent/of_protocol_handler.py
+++ b/ofagent/of_protocol_handler.py
@@ -347,3 +347,6 @@
 
     def forward_port_status(self, ofp_port_status):
         self.cxn.send(to_loxi(ofp_port_status))
+
+    def forward_flow_removed(self, ofp_flow_removed):
+        self.cxn.send(to_loxi(ofp_flow_removed))
diff --git a/voltha/core/flow_decomposer.py b/voltha/core/flow_decomposer.py
index cef33c1..29c5e34 100644
--- a/voltha/core/flow_decomposer.py
+++ b/voltha/core/flow_decomposer.py
@@ -347,6 +347,10 @@
     return None
 
 
+def get_tp_id_from_metadata(write_metadata_value):
+    return (write_metadata_value >> 32) & 0xffff
+
+
 def get_inner_tag_from_write_metadata(flow):
     """
     Write metadata instruction value (metadata) is 8 bytes:
@@ -359,7 +363,7 @@
     metadata = get_metadata_from_write_metadata(flow)
     log.debug("The metadata for inner tag", metadata=metadata)
     if metadata is not None:
-        inner_tag = (metadata >> 48) & 0xffffffff
+        inner_tag = (metadata >> 48) & 0xffff
         log.debug("Found inner tag", inner_tag=inner_tag)
         return inner_tag
     return None
diff --git a/voltha/core/local_handler.py b/voltha/core/local_handler.py
index b88a7b5..39f550e 100644
--- a/voltha/core/local_handler.py
+++ b/voltha/core/local_handler.py
@@ -25,7 +25,7 @@
 from common.utils.id_generation import create_cluster_device_id
 from voltha.core.config.config_root import ConfigRoot
 from voltha.protos.openflow_13_pb2 import PacketIn, Flows, FlowGroups, \
-    Meters, ofp_port_status
+    Meters, ofp_port_status, ofp_flow_removed
 from voltha.protos.voltha_pb2_grpc import \
     add_VolthaLocalServiceServicer_to_server, VolthaLocalServiceServicer
 from voltha.protos.voltha_pb2 import \
@@ -1156,6 +1156,10 @@
         event = ChangeEvent(id=device_id, port_status=port_status)
         self.core.change_event_queue.put(event)
 
+    def send_flow_removed_event(self, device_id, flow_removed):
+        assert isinstance(flow_removed, ofp_flow_removed)
+        event = ChangeEvent(id=device_id, flow_removed=flow_removed)
+        self.core.change_event_queue.put(event)
 
     @twisted_async
     def ListAlarmFilters(self, request, context):
diff --git a/voltha/core/logical_device_agent.py b/voltha/core/logical_device_agent.py
index 16b4ff8..a40d215 100644
--- a/voltha/core/logical_device_agent.py
+++ b/voltha/core/logical_device_agent.py
@@ -86,6 +86,7 @@
             self._flows_ids_to_add = []
             self._flows_ids_to_remove = []
             self._flows_to_remove = []
+            self._flow_with_unknown_meter = dict()
 
             self.accepts_direct_logical_flows = False
             self.device_id = self.self_proxy.get('/').root_device_id
@@ -141,13 +142,22 @@
 
         self.log.debug('stopped')
 
-    def announce_flows_deleted(self, flows):
+    def announce_flows_deleted(self, flows, reason=ofp.OFPRR_DELETE):
         for f in flows:
-            self.announce_flow_deleted(f)
+            self.announce_flow_deleted(f, reason)
 
-    def announce_flow_deleted(self, flow):
+    def announce_flow_deleted(self, flow, reason=ofp.OFPRR_DELETE):
         if flow.flags & ofp.OFPFF_SEND_FLOW_REM:
-            raise NotImplementedError("announce_flow_deleted")
+            self.local_handler.send_flow_removed_event(
+                device_id=self.logical_device_id,
+                flow_removed=ofp.ofp_flow_removed(
+                    cookie=flow.cookie,
+                    priority=flow.priority,
+                    reason=reason,
+                    table_id=flow.table_id,
+                    match=flow.match
+                )
+            )
 
     def signal_flow_mod_error(self, code, flow_mod):
         pass  # TODO
@@ -224,6 +234,8 @@
             self.signal_meter_mod_error(ofp.OFPMMFC_METER_EXISTS, meter_mod)
         else:
             meter_entry = meter_entry_from_meter_mod(meter_mod)
+            meter_entry.stats.flow_count = self._flow_with_unknown_meter.get(meter_mod.meter_id, 0)
+            self._flow_with_unknown_meter.pop(meter_mod.meter_id, None)
             meters[meter_mod.meter_id] = meter_entry
             changed = True
 
@@ -352,6 +364,8 @@
                                meters=meters.values())
             except KeyError:
                 self.log.warn("meter id is not found in meters", meter_id=meter_id)
+                self._flow_with_unknown_meter[meter_id] = \
+                    self._flow_with_unknown_meter.get(meter_id, 0) + 1
 
 
     def flow_delete(self, mod):
@@ -402,6 +416,8 @@
         if changed:
             self.flows_proxy.update('/', Flows(items=flows))
             self.log.debug("flow deleted strictly", mod=mod)
+            if isinstance(mod, ofp.ofp_flow_mod):
+                self.announce_flow_deleted(flow)
 
     def flow_modify(self, mod):
         raise NotImplementedError()
@@ -552,7 +568,7 @@
         flows = to_keep
 
         # send notification to deleted ones
-        self.announce_flows_deleted(to_delete)
+        self.announce_flows_deleted(to_delete, reason=ofp.OFPRR_GROUP_DELETE)
 
         return bool(to_delete), flows
 
@@ -567,6 +583,7 @@
                 to_keep.append(f)
 
         flows = to_keep
+        # we cant use OFPRR_MELETE_DELETE for 1.3.x
         self.announce_flows_deleted(flows)
         return bool(to_delete), flows
 
diff --git a/voltha/protos/openflow_13.proto b/voltha/protos/openflow_13.proto
index 3de3829..3c7e858 100644
--- a/voltha/protos/openflow_13.proto
+++ b/voltha/protos/openflow_13.proto
@@ -2305,5 +2305,6 @@
     string id = 1; // LogicalDevice.id
     oneof event {
         ofp_port_status port_status = 2;
+        ofp_flow_removed flow_removed = 3;
     }
 }