VOL-2513: Push UNI port statistics as KPI events to kafka
          - Create PM ME's for GEM and UNI ports
          - Collect and push GEM and UNI statistics to Kafka
          - Bump version to 2.3.19

Change-Id: I1ee16b15f215011acdd3a2dec496dd067b3aacfa
diff --git a/VERSION b/VERSION
index ad37abb..d245ee8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.3.18
+2.3.19
diff --git a/pyvoltha/adapters/extensions/events/kpi/onu/onu_pm_interval_metrics.py b/pyvoltha/adapters/extensions/events/kpi/onu/onu_pm_interval_metrics.py
index fc5b417..425d93e 100644
--- a/pyvoltha/adapters/extensions/events/kpi/onu/onu_pm_interval_metrics.py
+++ b/pyvoltha/adapters/extensions/events/kpi/onu/onu_pm_interval_metrics.py
@@ -54,7 +54,7 @@
     ETHERNET_BRIDGE_HISTORY_ENABLED = True
     ETHERNET_UNI_HISTORY_ENABLED = True
     FEC_HISTORY_ENABLED = True
-    GEM_PORT_HISTORY_ENABLED = False
+    GEM_PORT_HISTORY_ENABLED = True
     TRANS_CONV_HISTORY_ENABLED = False
     XGPON_DOWNSTREAM_HISTORY = False
     XGPON_UPSTREAM_HISTORY = False
diff --git a/pyvoltha/adapters/extensions/omci/onu_configuration.py b/pyvoltha/adapters/extensions/omci/onu_configuration.py
index 52d5b6a..657729e 100644
--- a/pyvoltha/adapters/extensions/omci/onu_configuration.py
+++ b/pyvoltha/adapters/extensions/omci/onu_configuration.py
@@ -128,7 +128,8 @@
             '_circuit_pack': None,
             '_software': None,
             '_pptp': None,
-            '_veip': None
+            '_veip': None,
+            '_gem_ctp' : None
         }
 
     @property
@@ -448,6 +449,25 @@
         return results if len(results) else None
 
     @property
+    def gem_ctp_entities(self):
+        """
+        This managed entity organizes data associated with each access network
+        interface supported by a G-PON ONU. The ONU automatically creates one
+        instance of this managed entity for each PON physical port.
+        """
+        gem_ctp = self._get_capability('_gem_ctp', GemPortNetworkCtp.class_id)
+        results = dict()
+
+        if gem_ctp is not None:
+            for inst, inst_data in gem_ctp.items():
+                if isinstance(inst, int):
+                    results[inst] = {
+                        'entity-id':                 inst,
+                        'port_id':                   inst_data[ATTRIBUTES_KEY].get('port_id', 0)
+                    }
+        return results if len(results) else None
+
+    @property
     def uni_g_entities(self):
         """
         This managed entity organizes data associated with user network interfaces
diff --git a/pyvoltha/adapters/extensions/omci/state_machines/performance_intervals.py b/pyvoltha/adapters/extensions/omci/state_machines/performance_intervals.py
index 356af3b..4269d64 100644
--- a/pyvoltha/adapters/extensions/omci/state_machines/performance_intervals.py
+++ b/pyvoltha/adapters/extensions/omci/state_machines/performance_intervals.py
@@ -20,6 +20,8 @@
 from datetime import datetime, timedelta
 from random import uniform, shuffle
 from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
+from pyvoltha.common.utils.asleep import asleep
 from pyvoltha.common.utils.indexpool import IndexPool
 from voltha_protos.omci_mib_db_pb2 import OpenOmciEventType
 from pyvoltha.adapters.extensions.omci.omci_defs import EntityOperations, ReasonCodes
@@ -30,6 +32,7 @@
 from pyvoltha.adapters.extensions.omci.omci_entities import MacBridgePortConfigurationData
 from pyvoltha.adapters.extensions.omci.omci_entities import EthernetPMMonitoringHistoryData, \
     FecPerformanceMonitoringHistoryData, \
+    GemPortNetworkCtpMonitoringHistoryData, \
     XgPonTcPerformanceMonitoringHistoryData, \
     XgPonDownstreamPerformanceMonitoringHistoryData, \
     XgPonUpstreamPerformanceMonitoringHistoryData, \
@@ -350,6 +353,7 @@
         config = self._device.configuration
         anis = config.ani_g_entities
         unis = config.uni_g_entities
+        gem_ctps = config.gem_ctp_entities
 
         if anis is not None:
             for entity_id in six.iterkeys(anis):
@@ -359,15 +363,36 @@
                 self.delete_pm_me(XgPonUpstreamPerformanceMonitoringHistoryData.class_id, entity_id)
 
         if unis is not None:
-            for entity_id in six.iterkeys(config.uni_g_entities):
+            for entity_id in six.iterkeys(unis):
                 self.delete_pm_me(EthernetPMMonitoringHistoryData.class_id, entity_id)
+                self.delete_pm_me(EthernetFrameUpstreamPerformanceMonitoringHistoryData.class_id, entity_id)
+                self.delete_pm_me(EthernetFrameDownstreamPerformanceMonitoringHistoryData.class_id, entity_id)
 
+        if gem_ctps is not None:
+            for entity_id in six.iterkeys(gem_ctps):
+                self.delete_pm_me(GemPortNetworkCtpMonitoringHistoryData.class_id, entity_id)
+
+    @inlineCallbacks
+    def add_gem_ctp_pm_me(self):
+        config = self._device.configuration
+        gem_ctps = config.gem_ctp_entities
+        self.log.debug('gem-ctp-entities', gem_ctps=gem_ctps)
+
+        if gem_ctps is not None:
+            for entity_id in six.iterkeys(gem_ctps):
+                self.add_pm_me(GemPortNetworkCtpMonitoringHistoryData.class_id, entity_id)
+        else:
+            yield asleep(0.3)
+            self.add_gem_ctp_pm_me()
+
+    @inlineCallbacks
     def on_enter_starting(self):
         """ Add the PON/ANI and UNI PM intervals"""
         self.advertise(OpenOmciEventType.state_change, self.state)
 
         self._device = self._agent.get_device(self._device_id)
         self._cancel_deferred()
+        class_id = None
 
         # Set up OMCI ME Response subscriptions
         try:
@@ -386,6 +411,7 @@
             config = self._device.configuration
             anis = config.ani_g_entities
             unis = config.uni_g_entities
+            self.log.debug('ANI-UNI-entities', anis=anis, unis=unis)
 
             if anis is not None:
                 for entity_id in six.iterkeys(anis):
@@ -401,6 +427,10 @@
             if unis is not None:
                 for entity_id in six.iterkeys(config.uni_g_entities):
                     self.add_pm_me(EthernetPMMonitoringHistoryData.class_id, entity_id)
+                    self.add_pm_me(EthernetFrameUpstreamPerformanceMonitoringHistoryData.class_id, entity_id)
+                    self.add_pm_me(EthernetFrameDownstreamPerformanceMonitoringHistoryData.class_id, entity_id)
+
+            yield self.add_gem_ctp_pm_me()
 
             # Look for existing instances of dynamically created ME's that have PM
             # associated with them and add them now
diff --git a/pyvoltha/adapters/extensions/omci/tasks/omci_create_pm_task.py b/pyvoltha/adapters/extensions/omci/tasks/omci_create_pm_task.py
index e0243e3..ef19fdd 100644
--- a/pyvoltha/adapters/extensions/omci/tasks/omci_create_pm_task.py
+++ b/pyvoltha/adapters/extensions/omci/tasks/omci_create_pm_task.py
@@ -76,6 +76,7 @@
         """ Perform the create requests """
 
         try:
+            self.log.debug('create-pm-mes', mes=self._me_dict)
             for pm, me in self._me_dict.items():
                 pm_class_id = pm[0]
                 pm_entity_id = pm[1]