VOL-805: Add support for partially decoding unknowm Managed Entities (MEs)

Change-Id: I28f70d46ee5f0ed97adb8309e32c6331dcacdfc5
diff --git a/tests/utests/voltha/extensions/omci/mock/mock_onu_handler.py b/tests/utests/voltha/extensions/omci/mock/mock_onu_handler.py
index 243bb6e..1897354 100644
--- a/tests/utests/voltha/extensions/omci/mock/mock_onu_handler.py
+++ b/tests/utests/voltha/extensions/omci/mock/mock_onu_handler.py
@@ -16,6 +16,7 @@
 
 from mock_adapter_agent import MockProxyAddress, MockDevice
 from voltha.extensions.omci.omci_cc import *
+from voltha.extensions.omci.omci_entities import entity_id_to_class_map
 
 
 class MockOnuHandler(MockDevice):
@@ -37,7 +38,7 @@
         self._adapter_agent = adapter_agent
 
         self.onu_mock = None
-        self.omci_cc = OMCI_CC(adapter_agent, device_id)
+        self.omci_cc = OMCI_CC(adapter_agent, device_id, me_map=entity_id_to_class_map)
 
         # Items that you can change to perform various test failures
 
diff --git a/tests/utests/voltha/extensions/omci/test_mib_db_ext.py b/tests/utests/voltha/extensions/omci/test_mib_db_ext.py
index f84a757..60dedef 100644
--- a/tests/utests/voltha/extensions/omci/test_mib_db_ext.py
+++ b/tests/utests/voltha/extensions/omci/test_mib_db_ext.py
@@ -18,6 +18,7 @@
 from voltha.extensions.omci.database.mib_db_ext import *
 from voltha.extensions.omci.database.mib_db_api import MODIFIED_KEY, CREATED_KEY,\
     DEVICE_ID_KEY, MDS_KEY, LAST_SYNC_KEY
+from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
 from mock.mock_adapter_agent import MockAdapterAgent, MockDevice
 from nose.tools import raises, assert_raises
 import time
@@ -514,6 +515,23 @@
         self.assertTrue(all(data['received_frame_vlan_tagging_operation_table'][k] == table_as_dict[k]
                             for k in table_as_dict.keys()))
 
+    def test_unknown_me_serialization(self):
+        self.db.start()
+        self.db.add(_DEVICE_ID)
+
+        blob = '00010000000c0000000000000000000000000000000000000000'
+        class_id = 0xff78
+        inst_id = 0x101
+        attributes = {
+            UNKNOWN_CLASS_ATTRIBUTE_KEY: blob
+        }
+        self.db.set(_DEVICE_ID, class_id, inst_id, attributes)
+
+        data = self.db.query(_DEVICE_ID, class_id, inst_id, attributes.keys())
+        self.assertTrue(isinstance(UNKNOWN_CLASS_ATTRIBUTE_KEY, basestring))
+        self.assertTrue(all(isinstance(attributes[k], basestring) for k in attributes.keys()))
+        self.assertTrue(all(data[k] == attributes[k] for k in attributes.keys()))
+
 
 if __name__ == '__main__':
     main()
diff --git a/tests/utests/voltha/extensions/omci/test_omci.py b/tests/utests/voltha/extensions/omci/test_omci.py
index dd5108e..a702c88 100644
--- a/tests/utests/voltha/extensions/omci/test_omci.py
+++ b/tests/utests/voltha/extensions/omci/test_omci.py
@@ -1109,24 +1109,6 @@
             "000000000000000000000028a6bc6e48",
             "01042e0a00020000014904011800ffffffff0000000000000000000000000000"
             "000000000000000000000028f747c739",
-            "01062e0a00020000015b0002c00000001018aaaa000000000000000000000000"
-            "0000000000000000000000288abbdf19",
-            "01072e0a00020000015b00022000000000000000000000000000000000000000"
-            "0000000000000000000000284dbaf3c9",
-            "01082e0a00020000015b00021000000000000000000000000000000000000000"
-            "0000000000000000000000289f8ea0a4",
-            "01092e0a00020000015b00020800000000000000000000000000000000000000"
-            "0000000000000000000000289c36f098",
-            "010a2e0a00020000015b00020400000000000000000000000000000000000000"
-            "000000000000000000000028d04565ce",
-            "010b2e0a00020000015b00020200000000000000000000000000000000000000"
-            "0000000000000000000000281fa624ed",
-            "010c2e0a00020000015b00020100000000000000000000000000000000000000"
-            "000000000000000000000028c5c1d814",
-            "010d2e0a00020000015b00020010000000000000000000000000000000000000"
-            "000000000000000000000028a469a9d5",
-            "010e2e0a00020000015b00020008000000000000000000000000000000000000"
-            "000000000000000000000028da4dc6f1",
         ]
         mask = "%5s %9s %20s %9s %s"
         print
diff --git a/tests/utests/voltha/extensions/omci/test_omci_cc.py b/tests/utests/voltha/extensions/omci/test_omci_cc.py
index 2152f04..2a7fc5f 100644
--- a/tests/utests/voltha/extensions/omci/test_omci_cc.py
+++ b/tests/utests/voltha/extensions/omci/test_omci_cc.py
@@ -20,6 +20,7 @@
 from mock.mock_onu import MockOnu
 from voltha.extensions.omci.omci_defs import *
 from voltha.extensions.omci.omci_frame import *
+from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
 
 DEFAULT_OLT_DEVICE_ID = 'default_olt_mock'
 DEFAULT_ONU_DEVICE_ID = 'default_onu_mock'
@@ -31,6 +32,15 @@
 RC = ReasonCodes
 
 
+def chunk(indexable, chunk_size):
+    for i in range(0, len(indexable), chunk_size):
+        yield indexable[i:i + chunk_size]
+
+
+def hex2raw(hex_string):
+    return ''.join(chr(int(byte, 16)) for byte in chunk(hex_string, 2))
+
+
 class TestOmciCc(TestCase):
     """
     Test the Open OMCI Communication channels
@@ -114,6 +124,7 @@
             'rx_avc_overflow': omci_cc.rx_avc_overflow,
             'rx_onu_discards': omci_cc.rx_onu_discards,
             'rx_timeouts': omci_cc.rx_timeouts,
+            'rx_unknown_me': omci_cc.rx_unknown_me,
             'tx_errors': omci_cc.tx_errors,
             'consecutive_errors': omci_cc.consecutive_errors,
             'reply_min': omci_cc.reply_min,
@@ -145,6 +156,7 @@
         self.assertEqual(omci_cc.rx_alarm_overflow, 0)
         self.assertEqual(omci_cc.rx_avc_overflow, 0)
         self.assertEqual(omci_cc.rx_onu_discards, 0)
+        self.assertEqual(omci_cc.rx_unknown_me, 0)
         self.assertEqual(omci_cc.rx_timeouts, 0)
         self.assertEqual(omci_cc.tx_errors, 0)
         self.assertEqual(omci_cc.consecutive_errors, 0)
@@ -177,6 +189,27 @@
         self.assertFalse(omci_cc.enabled)
         self.assertIsNone(omci_cc._proxy_address)
 
+    def test_rx_discard_if_disabled(self):
+        # ME without a known decoder
+        self.setup_one_of_each()
+
+        omci_cc = self.onu_handler.omci_cc
+        omci_cc.enabled = False
+        snapshot = self._snapshot_stats()
+
+        msg = '00fc2e0a00020000ff780000e00000010000000c' \
+              '0000000000000000000000000000000000000000' \
+              '00000028105a86ef'
+
+        omci_cc.receive_message(hex2raw(msg))
+
+        # Note: No counter increments
+        self.assertEqual(omci_cc.rx_frames, snapshot['rx_frames'])
+        self.assertEqual(omci_cc.rx_unknown_me, snapshot['rx_unknown_me'])
+        self.assertEqual(omci_cc.rx_unknown_tid, snapshot['rx_unknown_tid'])
+        self.assertEqual(omci_cc.rx_onu_frames, snapshot['rx_onu_frames'])
+        self.assertEqual(omci_cc.rx_unknown_tid, snapshot['rx_unknown_tid'])
+
     def test_message_send_get(self):
         # Various tests of sending an OMCI message and it either
         # getting a response or send catching some errors of
@@ -202,6 +235,7 @@
         # d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        # d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         # d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         # d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -233,6 +267,7 @@
         # d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        # d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         # d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         # d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -271,6 +306,7 @@
         # d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        # d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         # d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         # d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -302,6 +338,7 @@
         # d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        # d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         # d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         # d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -330,6 +367,7 @@
         d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -359,6 +397,7 @@
         d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -388,6 +427,7 @@
         # d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         # d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        # d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         # d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         # d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -413,6 +453,7 @@
         d.addCallback(self._check_stats, snapshot, 'rx_alarm_overflow', snapshot['rx_alarm_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_avc_overflow', snapshot['rx_avc_overflow'])
         d.addCallback(self._check_stats, snapshot, 'rx_onu_discards', snapshot['rx_onu_discards'])
+        d.addCallback(self._check_stats, snapshot, 'rx_unknown_me', snapshot['rx_unknown_me'])
         d.addCallback(self._check_stats, snapshot, 'rx_timeouts', snapshot['rx_timeouts'])
         d.addCallback(self._check_stats, snapshot, 'tx_errors', snapshot['tx_errors'])
         d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
@@ -507,6 +548,68 @@
         # todo: Test zero consecutive errors
         # d.addCallback(self._check_value_equal, 'consecutive_errors', 0, omci_cc.consecutive_errors)
 
+    def test_rx_unknown_me(self):
+        # ME without a known decoder
+        self.setup_one_of_each()
+
+        omci_cc = self.onu_handler.omci_cc
+        omci_cc.enabled = True
+        snapshot = self._snapshot_stats()
+
+        # This is the ID ------+
+        #                      v
+        msg = '00fc2e0a00020000ff780000e00000010000000c' \
+              '0000000000000000000000000000000000000000' \
+              '00000028'
+
+        omci_cc.receive_message(hex2raw(msg))
+
+        # Note: After successful frame decode, a lookup of the corresponding request by
+        #       TID is performed. None should be found, so we should see the Rx Unknown TID
+        #       increment.
+        self.assertEqual(omci_cc.rx_frames, snapshot['rx_frames'])
+        self.assertEqual(omci_cc.rx_unknown_me, snapshot['rx_unknown_me'] + 1)
+        self.assertEqual(omci_cc.rx_unknown_tid, snapshot['rx_unknown_tid'] + 1)
+        self.assertEqual(omci_cc.rx_onu_frames, snapshot['rx_onu_frames'])
+        self.assertEqual(omci_cc.consecutive_errors, 0)
+
+    def test_rx_decode_unknown_me(self):
+        # ME without a known decoder
+        self.setup_one_of_each()
+
+        omci_cc = self.onu_handler.omci_cc
+        omci_cc.enabled = True
+        snapshot = self._snapshot_stats()
+
+        # This is a MIB Upload Next Response. Where we would probably first see an
+        # unknown Class ID
+        #
+        # This is the ID ------+
+        #                      v
+        msg = '00fc2e0a00020000ff780001e000'
+        blob = '00010000000c0000000000000000000000000000000000000000'
+        msg += blob + '00000028'
+
+        # Dig into the internal method so we can get the returned frame
+        frame = omci_cc._decode_unknown_me(hex2raw(msg))
+
+        self.assertEqual(frame.fields['transaction_id'], 0x00fc)
+        self.assertEqual(frame.fields['message_type'], 0x2e)
+
+        omci_fields = frame.fields['omci_message'].fields
+
+        self.assertEqual(omci_fields['entity_class'], 0x0002)
+        self.assertEqual(omci_fields['entity_id'], 0x00)
+        self.assertEqual(omci_fields['object_entity_class'], 0x0ff78)
+        self.assertEqual(omci_fields['object_entity_id'], 0x01)
+        self.assertEqual(omci_fields['object_attributes_mask'], 0xe000)
+
+        data_fields = omci_fields['object_data']
+
+        decoded_blob = data_fields.get(UNKNOWN_CLASS_ATTRIBUTE_KEY)
+        self.assertIsNotNone(decoded_blob)
+        self.assertEqual(decoded_blob, blob)
+
     def test_flush(self):
         # Test flush of autonomous ONU queues
         self.setup_one_of_each()
diff --git a/voltha/extensions/omci/database/mib_db_dict.py b/voltha/extensions/omci/database/mib_db_dict.py
index 58d81c2..663ce36 100644
--- a/voltha/extensions/omci/database/mib_db_dict.py
+++ b/voltha/extensions/omci/database/mib_db_dict.py
@@ -263,7 +263,7 @@
                     value = value.to_json()
 
                 # Other complex packet types may be a repeated list field (FieldListField)
-                elif isinstance(value, list):
+                elif isinstance(value, (list, dict)):
                     value = json.dumps(value, separators=(',', ':'))
 
                 db_value = instance_db[ATTRIBUTES_KEY].get(attribute) \
diff --git a/voltha/extensions/omci/database/mib_db_ext.py b/voltha/extensions/omci/database/mib_db_ext.py
index c0eebab..c1b84dd 100644
--- a/voltha/extensions/omci/database/mib_db_ext.py
+++ b/voltha/extensions/omci/database/mib_db_ext.py
@@ -99,10 +99,16 @@
         """
         try:
             me_map = self._omci_agent.get_device(device_id).me_map
-            entity = me_map[class_id]
-            attr_index = entity.attribute_name_to_index_map[attr_name]
-            eca = entity.attributes[attr_index]
-            field = eca.field
+
+            if class_id in me_map:
+                entity = me_map[class_id]
+                attr_index = entity.attribute_name_to_index_map[attr_name]
+                eca = entity.attributes[attr_index]
+                field = eca.field
+            else:
+                # Here for auto-defined MEs (ones not defined in ME Map)
+                from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
+                field = StrFixedLenField(UNKNOWN_CLASS_ATTRIBUTE_KEY, None, 24)
 
             if isinstance(field, StrFixedLenField):
                 #  For StrFixedLenField, value is an str already (or possibly JSON encoded object)
@@ -156,10 +162,16 @@
         """
         try:
             me_map = self._omci_agent.get_device(device_id).me_map
-            entity = me_map[class_id]
-            attr_index = entity.attribute_name_to_index_map[attr_name]
-            eca = entity.attributes[attr_index]
-            field = eca.field
+
+            if class_id in me_map:
+                entity = me_map[class_id]
+                attr_index = entity.attribute_name_to_index_map[attr_name]
+                eca = entity.attributes[attr_index]
+                field = eca.field
+            else:
+                # Here for auto-defined MEs (ones not defined in ME Map)
+                from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
+                field = StrFixedLenField(UNKNOWN_CLASS_ATTRIBUTE_KEY, None, 24)
 
             if isinstance(field, StrFixedLenField):
                 from scapy.base_classes import Packet_metaclass
diff --git a/voltha/extensions/omci/omci_cc.py b/voltha/extensions/omci/omci_cc.py
index 69f7646..c873ef3 100644
--- a/voltha/extensions/omci/omci_cc.py
+++ b/voltha/extensions/omci/omci_cc.py
@@ -40,6 +40,7 @@
 CONNECTED_KEY = 'connected'
 TX_REQUEST_KEY = 'tx-request'
 RX_RESPONSE_KEY = 'rx-response'
+UNKNOWN_CLASS_ATTRIBUTE_KEY = 'voltha-unknown-blob'
 
 
 class OmciCCRxEvents(IntEnum):
@@ -97,6 +98,7 @@
         self._rx_avc_overflow = 0     # Autonomously generated ONU AVC rx overflow
         self._rx_onu_discards = 0     # Autonomously generated ONU unknown message types
         self._rx_timeouts = 0
+        self._rx_unknown_me = 0       # Number of managed entities Rx without a decode definition
         self._tx_errors = 0           # Exceptions during tx request
         self._consecutive_errors = 0  # Rx & Tx errors in a row, a good RX resets this to 0
         self._reply_min = sys.maxint  # Fastest successful tx -> rx
@@ -158,6 +160,10 @@
         return self._rx_unknown_tid         # Tx TID not found
 
     @property
+    def rx_unknown_me(self):
+        return self._rx_unknown_me
+
+    @property
     def rx_onu_frames(self):
         return self._rx_onu_frames
 
@@ -347,9 +353,10 @@
 
                 except KeyError as e:
                     # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
-                    # TODO: Can we create a temporary one to hold it so upload does not always fail on new ME's?
-                    self.log.exception('frame-decode-key-error', msg=hexlify(msg), e=e)
-                    return
+                    self.log.debug('frame-decode-key-error', msg=hexlify(msg), e=e)
+                    rx_frame = self._decode_unknown_me(msg)
+                    self._rx_unknown_me += 1
+                    rx_tid = rx_frame.fields.get('transaction_id')
 
                 except Exception as e:
                     self.log.exception('frame-decode', msg=hexlify(msg), e=e)
@@ -392,6 +399,64 @@
             except Exception as e:
                 self.log.exception('rx-msg', e=e)
 
+    def _decode_unknown_me(self, msg):
+        """
+        Decode an ME for an unsupported class ID.  This should only occur for a subset
+        of message types (Get, Set, MIB Upload Next, ...) and they should only be
+        responses as well.
+
+        There are some times below that are commented out. For VOLTHA 2.0, it is
+        expected that any get, set, create, delete for unique (often vendor) MEs
+        will be coded by the ONU utilizing it and supplied to OpenOMCI as a
+        vendor-specific ME during device initialization.
+
+        :param msg: (str) Binary data
+        :return: (OmciFrame) resulting frame
+        """
+        from struct import unpack
+
+        (tid, msg_type, framing) = unpack('!HBB', msg[0:4])
+
+        assert framing == 0xa, 'Only basic OMCI framing supported at this time'
+        msg = msg[4:]
+
+        # TODO: Commented out items below are future work (not expected for VOLTHA v2.0)
+        (msg_class, kwargs) = {
+            # OmciCreateResponse.message_id: (OmciCreateResponse, None),
+            # OmciDeleteResponse.message_id: (OmciDeleteResponse, None),
+            # OmciSetResponse.message_id: (OmciSetResponse, None),
+            # OmciGetResponse.message_id: (OmciGetResponse, None),
+            # OmciGetAllAlarmsNextResponse.message_id: (OmciGetAllAlarmsNextResponse, None),
+            OmciMibUploadNextResponse.message_id: (OmciMibUploadNextResponse,
+                                                   {
+                                                       'entity_class': unpack('!H', msg[0:2])[0],
+                                                       'entity_id': unpack('!H', msg[2:4])[0],
+                                                       'object_entity_class': unpack('!H', msg[4:6])[0],
+                                                       'object_entity_id': unpack('!H', msg[6:8])[0],
+                                                       'object_attributes_mask': unpack('!H', msg[8:10])[0],
+                                                       'object_data': {
+                                                           UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[10:-4])
+                                                       },
+                                                   }),
+            # OmciAlarmNotification.message_id: (OmciAlarmNotification, None),
+            # OmciAttributeValueChange.message_id: (OmciAttributeValueChange,
+            #                                       {
+            #                                           'entity_class': unpack('!H', msg[0:2])[0],
+            #                                           'entity_id': unpack('!H', msg[2:4])[0],
+            #                                           'data': {
+            #                                               UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[4:-8])
+            #                                           },
+            #                                       }),
+            # OmciTestResult.message_id: (OmciTestResult, None),
+        }.get(msg_type, None)
+
+        if msg_class is None:
+            raise TypeError('Unsupport Message Type for Unknown Decode: {}',
+                            msg_type)
+
+        return OmciFrame(transaction_id=tid, message_type=msg_type,
+                         omci_message=msg_class(**kwargs))
+
     def _publish_rx_frame(self, tx_frame, rx_frame):
         """
         Notify listeners of successful response frame
diff --git a/voltha/extensions/omci/omci_entities.py b/voltha/extensions/omci/omci_entities.py
index 1a29ecf..0e4f52d 100644
--- a/voltha/extensions/omci/omci_entities.py
+++ b/voltha/extensions/omci/omci_entities.py
@@ -1056,14 +1056,6 @@
     mandatory_operations = {OP.Set, OP.Get, OP.GetNext}
     notifications = {OP.AttributeValueChange}
 
-
-class Unknown347(EntityClass):
-    class_id = 347
-    attributes = [
-
-    ]
-
-
 # entity class lookup table from entity_class values
 entity_classes_name_map = dict(
     inspect.getmembers(sys.modules[__name__],
diff --git a/voltha/extensions/omci/tasks/mib_resync_task.py b/voltha/extensions/omci/tasks/mib_resync_task.py
index e39e96b..06b45ec 100644
--- a/voltha/extensions/omci/tasks/mib_resync_task.py
+++ b/voltha/extensions/omci/tasks/mib_resync_task.py
@@ -60,7 +60,8 @@
         super(MibResyncTask, self).__init__(MibResyncTask.name,
                                             omci_agent,
                                             device_id,
-                                            priority=MibResyncTask.task_priority)
+                                            priority=MibResyncTask.task_priority,
+                                            exclusive=False)
         self._local_deferred = None
         self._device = omci_agent.get_device(device_id)
         self._db_active = MibDbVolatileDict(omci_agent)
@@ -360,8 +361,13 @@
             # will only show up it the OpenOMCI (OLT-side) database if it changed and
             # an AVC Notification was sourced by the ONU
             # TODO: These could be calculated once at ONU startup (device add)
-            ro_attrs = {attr.field.name for attr in me_map[cls_id].attributes
-                        if attr.access == ro_set}
+            if cls_id in me_map:
+                ro_attrs = {attr.field.name for attr in me_map[cls_id].attributes
+                            if attr.access == ro_set}
+            else:
+                # Here if partially defined ME (not defined in ME Map)
+                from voltha.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
+                ro_attrs = {UNKNOWN_CLASS_ATTRIBUTE_KEY}
 
             # Get set of common instance IDs
             inst_ids = {inst_id for inst_id, _ in olt_cls.items()