VOL-1398: Adtran-ONU - Initial containerization commit

Change-Id: I7afcc1ad65b9ef80da994b0b0ddf74860911bb46
diff --git a/adapters/adtran_onu/omci/adtn_mib_reconcile_task.py b/adapters/adtran_onu/omci/adtn_mib_reconcile_task.py
new file mode 100644
index 0000000..b0892ad
--- /dev/null
+++ b/adapters/adtran_onu/omci/adtn_mib_reconcile_task.py
@@ -0,0 +1,184 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from twisted.internet.defer import returnValue
+from pyvoltha.adapters.extensions.omci.omci_defs import *
+from voltha.extensions.omci.omci_entities import Ieee8021pMapperServiceProfile
+from voltha.extensions.omci.tasks.mib_reconcile_task import MibReconcileTask
+from voltha.extensions.omci.database.mib_db_api import ATTRIBUTES_KEY
+from twisted.internet.defer import  inlineCallbacks
+from voltha.extensions.omci.omci_defs import ReasonCodes, EntityOperations
+from voltha.extensions.omci.omci_me import MEFrame
+
+OP = EntityOperations
+RC = ReasonCodes
+AA = AttributeAccess
+
+
+class AdtnMibReconcileTask(MibReconcileTask):
+    """
+    Adtran ONU OpenOMCI MIB Reconcile Task
+
+    For some crazy reason, the ADTRAN ONU does not report the IEEE802.1p Mapper ME
+    in the ONU upload even though it does exists.  This results in an 'instance
+    exists' error when trying to create it on the ONU
+    """
+    name = "Adtran MIB Reconcile Task"
+
+    def __init__(self, omci_agent, device_id, diffs):
+        super(AdtnMibReconcileTask, self).__init__(omci_agent, device_id, diffs)
+
+        self.name = AdtnMibReconcileTask.name
+        self._me_130_okay = False   # Set true once bug is fixed (auto detect)
+        self._omci_managed = False  # Set once ONU Data tracking of MIB-Data-Sync supported
+
+    @inlineCallbacks
+    def fix_olt_only(self, olt, onu_db, olt_db):
+        """
+        Fix ME's that were only found on the OLT. For OLT only MEs there are
+        the following things that will be checked.
+
+            o ME's that do not have an OpenOMCI class decoder. These are stored
+              as binary blobs in the MIB database. Since the OLT will never
+              create these (all are learned from ONU), it is assumed the ONU
+              has removed them for some purpose. So delete them from the OLT
+              database.
+
+            o For ME's that are created by the ONU (no create/delete access), the
+              MEs 'may' not be on the ONU because of a reboot or an OLT created
+              ME was deleted and the ONU gratuitously removes it.  So delete them
+              from the OLT database.
+
+            o For ME's that are created by the OLT/OpenOMCI, delete them from the
+              ONU
+
+        :param olt: (list(int,int)) List of tuples where (class_id, inst_id)
+        :param onu_db: (dict) ONU Database snapshot at time of audit
+        :param olt_db: (dict) OLT Database snapshot at time of audit
+
+        :return: (int, int) successes, failures
+        """
+        # Has IEEE 802.1p reporting Bug fixed?
+
+        if self._me_130_okay or Ieee8021pMapperServiceProfile.class_id in onu_db:
+            self._me_130_okay = True
+            returnValue(super(AdtnMibReconcileTask, self).fix_olt_only(olt, onu_db, olt_db))
+
+        ############################
+        # Base class handles all but ME 130
+        local_mes = {Ieee8021pMapperServiceProfile.class_id}
+        not_manual = [(cid, eid) for cid, eid in olt if cid not in local_mes]
+
+        results = yield super(AdtnMibReconcileTask, self).fix_olt_only(not_manual,
+                                                                       onu_db,
+                                                                       olt_db)
+        successes = results[0]
+        failures = results[1]
+
+        # If IEEE 802.1p mapper needs to be checked, do it manually as the IBONT 602
+        # manipulates it during MEF EVC/EVC-Map creation
+        for cid in local_mes:
+            class_entry = olt_db.get(cid, None)
+
+            if class_entry is not None:
+                entries = {k: v for k, v in class_entry.items() if isinstance(k, int)}
+                for eid, instance in entries.items():
+                    try:
+                        self.strobe_watchdog()
+                        results = yield self.manual_verification(cid, eid, instance[ATTRIBUTES_KEY])
+                        successes += results[0]
+                        failures += results[1]
+
+                    except Exception as _e:
+                        failures += 1
+
+        returnValue((successes, failures))
+
+    @inlineCallbacks
+    def update_mib_data_sync(self):
+        """ IBONT version does not support MDS"""
+        if self._omci_managed:
+            results = yield super(AdtnMibReconcileTask, self).update_mib_data_sync()
+            returnValue(results)
+
+        returnValue((1, 0))
+
+    @inlineCallbacks
+    def manual_verification(self, cid, eid, attributes):
+        # Trim off read-only attributes from ones passed in
+
+        me_map = self._device.me_map
+        ro_set = {AA.R}
+        ro_attrs = {attr.field.name for attr in me_map[cid].attributes
+                    if attr.access == ro_set}
+        attributes = {k: v for k, v in attributes.items() if k not in ro_attrs}
+        attributes_to_fix = dict()
+
+        try:
+            while len(attributes):
+                frame = MEFrame(me_map[cid], eid, attributes).get()
+                self.strobe_watchdog()
+                results = yield self._device.omci_cc.send(frame)
+                omci_message = results.fields['omci_message'].fields
+                status = omci_message['success_code']
+
+                if status == RC.UnknownEntity.value:
+                    self.strobe_watchdog()
+                    results = yield self.create_instance(me_map[cid], eid, attributes)
+                    returnValue((results[0], results[1]))
+
+                if status != RC.Success.value:
+                    self.log.error('manual-check-get-failed', cid=cid, eid=eid,
+                                   attributes=attributes, status=status)
+                    returnValue((1, 0))
+
+                onu_attr = {k: v for k, v in omci_message['data'].items()}
+                attributes_to_fix.update({k: v for k, v in onu_attr.items()
+                                         if k in attributes and v != attributes[k]})
+                attributes = {k: v for k, v in attributes if k not in onu_attr.keys()}
+
+            if len(attributes_to_fix) > 0:
+                try:
+                    frame = MEFrame(me_map[cid], eid, attributes_to_fix).set()
+                    self.strobe_watchdog()
+                    yield self._device.omci_cc.send(frame)
+                    returnValue((1, 0))
+
+                except Exception as _e:
+                    returnValue((0, 1))
+
+        except Exception as e:
+            self.log.exception('manual-check-failed', e=e, cid=cid, eid=eid)
+            raise
+
+    @inlineCallbacks
+    def create_instance(self, cid, eid, attributes):
+        try:
+            me_map = self._device.me_map
+            frame = MEFrame(me_map[cid], eid, attributes).create()
+
+            self.strobe_watchdog()
+            results = yield self._device.omci_cc.send(frame)
+            status = results.fields['omci_message'].fields['success_code']
+            if status == RC.Success.value or status == RC.InstanceExists.value:
+                returnValue((1, 0))
+
+            self.log.error('manual-check-create-failed', cid=cid, eid=eid,
+                           attributes=attributes, status=status)
+            returnValue((0, 1))
+
+        except Exception as e:
+            self.log.exception('manual-check-failed', e=e, cid=cid, eid=eid)
+            raise