VOL-1330:  Update openomci to voltha 1.x master.

Includes mib resync/reconcile fixes:

	Author: Chip Boling <chip@bcsw.net>
	Date:   Fri Feb 22 13:06:25 2019 -0600
	VOL-1482: Fix Scapy definition for OMCI GetResponse message
	Original-Change-Id: I155ff3f5914b81f9a09aede97c2a7cafc1b088fe

	Author: Chip Boling <chip@bcsw.net>
	Date:   Mon Mar 4 13:33:22 2019 -0600
	VOL-1504: fix for TimeSynchronization Request frame
	Original-Change-Id: I5350b765506ef9d19639c54281d38911a6f4c323

	Author: Chip Boling <chip@bcsw.net>
	Date:   Wed Feb 27 12:44:07 2019 -0600
	VOL-1439: Fixes for proper table attribute handling
	during MIB audit/resynchronization.  Also includes a fix to
	properly count MIB-DATA-SYNC increments on sets and software-download
	operations
	Original-Change-Id: I30a343aae91d5bcac56d068a37c18b29265d3bd9

Change-Id: If30bd6ea0fd59db5dbf51ecd617d000baf538728
diff --git a/VERSION b/VERSION
index 7e541ae..2d2a95e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.2.2
\ No newline at end of file
+2.2.3-dev0
diff --git a/pyvoltha/adapters/extensions/omci/database/mib_db_ext.py b/pyvoltha/adapters/extensions/omci/database/mib_db_ext.py
index 7523f58..7120dde 100644
--- a/pyvoltha/adapters/extensions/omci/database/mib_db_ext.py
+++ b/pyvoltha/adapters/extensions/omci/database/mib_db_ext.py
@@ -548,7 +548,7 @@
 
         try:
             data = MibDeviceData()
-            path = get_device_path(device_id)
+            path = self._get_device_path(device_id)
             data.ParseFromString(self._kv_store[path])
             return int(data.mib_data_sync)
 
diff --git a/pyvoltha/adapters/extensions/omci/me_frame.py b/pyvoltha/adapters/extensions/omci/me_frame.py
index 06ae2bb..71e342f 100644
--- a/pyvoltha/adapters/extensions/omci/me_frame.py
+++ b/pyvoltha/adapters/extensions/omci/me_frame.py
@@ -339,6 +339,7 @@
                     entity_id=getattr(self, 'entity_id'),
                     year=dt.year,
                     month=dt.month,
+                    day=dt.day,
                     hour=dt.hour,
                     minute=dt.minute,
                     second=dt.second,
diff --git a/pyvoltha/adapters/extensions/omci/omci_cc.py b/pyvoltha/adapters/extensions/omci/omci_cc.py
index 4b695a5..76aa41e 100644
--- a/pyvoltha/adapters/extensions/omci/omci_cc.py
+++ b/pyvoltha/adapters/extensions/omci/omci_cc.py
@@ -60,7 +60,12 @@
     MIB_Reset = 8,
     Connectivity = 9,
     Get_ALARM_Get = 10,
-    Get_ALARM_Get_Next = 11
+    Get_ALARM_Get_Next = 11,
+    Start_Software_Download = 12,
+    Download_Section = 13,
+    End_Software_Download = 14,
+    Activate_Software = 15,
+    Commit_Software = 15,
 
 
 # abbreviations
@@ -71,8 +76,8 @@
 class OMCI_CC(object):
     """ Handle OMCI Communication Channel specifics for Adtran ONUs"""
 
-    MIN_OMCI_TX_ID_LOW_PRIORITY = 0x0001  # 2 Octets max
-    MAX_OMCI_TX_ID_LOW_PRIORITY = 0x7FFF  # 2 Octets max
+    MIN_OMCI_TX_ID_LOW_PRIORITY = 0x0001   # 2 Octets max
+    MAX_OMCI_TX_ID_LOW_PRIORITY = 0x7FFF   # 2 Octets max
     MIN_OMCI_TX_ID_HIGH_PRIORITY = 0x8000  # 2 Octets max
     MAX_OMCI_TX_ID_HIGH_PRIORITY = 0xFFFF  # 2 Octets max
     LOW_PRIORITY = 0
@@ -352,7 +357,7 @@
 
             rx_tid = rx_frame.fields['transaction_id']
             msg_type = rx_frame.fields['message_type']
-            self.log.debug('Received message for rx_tid', rx_tid = rx_tid)
+            self.log.debug('Received message for rx_tid', rx_tid = rx_tid, msg_type = msg_type)
             # Filter the Test Result frame and route through receive onu
             # message method.
             if rx_tid == 0 or msg_type == EntityOperations.TestResult.value:
diff --git a/pyvoltha/adapters/extensions/omci/omci_fields.py b/pyvoltha/adapters/extensions/omci/omci_fields.py
index 09cf465..8fc8a4c 100644
--- a/pyvoltha/adapters/extensions/omci/omci_fields.py
+++ b/pyvoltha/adapters/extensions/omci/omci_fields.py
@@ -250,4 +250,31 @@
         for k, v in sorted(key_value_pairs.iteritems()):
             table.append(v)
 
-        return table
\ No newline at end of file
+        return table
+
+
+class OmciVariableLenZeroPadField(Field):
+    __slots__ = ["_pad_to", "_omci_hdr_len"]
+
+    def __init__(self, name, pad_to):
+        Field.__init__(self, name, 0, 'B')
+        self._pad_to = pad_to
+        self._omci_hdr_len = 4
+
+    def addfield(self, pkt, s, _val):
+        count = self._pad_to - self._omci_hdr_len - len(s)
+        if count < 0:
+            from scapy.error import Scapy_Exception
+            raise Scapy_Exception("%s: Already past pad_to offset" %
+                                  self.__class__.__name__)
+        padding = bytearray(count)
+        import struct
+        return s + struct.pack("%iB" % count, *padding)
+
+    def getfield(self, pkt, s):
+        count = len(s) - self._omci_hdr_len
+        if count < 0:
+            from scapy.error import Scapy_Exception
+            raise Scapy_Exception("%s: Already past pad_to offset" %
+                                  self.__class__.__name__)
+        return s[count:], s[-count:]
diff --git a/pyvoltha/adapters/extensions/omci/omci_me.py b/pyvoltha/adapters/extensions/omci/omci_me.py
index ea62c2a..4a28d27 100644
--- a/pyvoltha/adapters/extensions/omci/omci_me.py
+++ b/pyvoltha/adapters/extensions/omci/omci_me.py
@@ -78,7 +78,7 @@
     of its point of attachment, the specified tagging operations refer to the
      upstream direction.
     """
-    def __init__(self, entity_id, attributes):
+    def __init__(self, entity_id, attributes=None):
         """
         :param entity_id: (int) This attribute uniquely identifies each instance of
                                 this managed entity. Its value is the same as that
diff --git a/pyvoltha/adapters/extensions/omci/omci_messages.py b/pyvoltha/adapters/extensions/omci/omci_messages.py
index c8526da..2b51bf8 100644
--- a/pyvoltha/adapters/extensions/omci/omci_messages.py
+++ b/pyvoltha/adapters/extensions/omci/omci_messages.py
@@ -19,7 +19,7 @@
 from scapy.packet import Packet
 
 from pyvoltha.adapters.extensions.omci.omci_defs import AttributeAccess, OmciSectionDataSize
-from pyvoltha.adapters.extensions.omci.omci_fields import OmciTableField
+from pyvoltha.adapters.extensions.omci.omci_fields import OmciTableField, OmciVariableLenZeroPadField
 import pyvoltha.adapters.extensions.omci.omci_entities as omci_entities
 
 
@@ -198,15 +198,16 @@
         ShortField("entity_id", 0),
         ByteField("success_code", 0),
         ShortField("attributes_mask", None),
-        ConditionalField(
-            ShortField("unsupported_attributes_mask", 0),
-            lambda pkt: pkt.success_code == 9),
-        ConditionalField(
-            ShortField("failed_attributes_mask", 0),
-            lambda pkt: pkt.success_code == 9),
-        ConditionalField(
-            OmciMaskedData("data"),
-            lambda pkt: pkt.success_code == 0 or pkt.success_code == 9)
+        ConditionalField(OmciMaskedData("data"),
+                         lambda pkt: pkt.success_code in (0, 9)),
+        ConditionalField(OmciVariableLenZeroPadField("zero_padding", 36),
+                         lambda pkt: pkt.success_code == 9),
+
+        # These fields are only valid if attribute error (status == 9)
+        ConditionalField(ShortField("unsupported_attributes_mask", 0),
+                         lambda pkt: pkt.success_code == 9),
+        ConditionalField(ShortField("failed_attributes_mask", 0),
+                         lambda pkt: pkt.success_code == 9)
     ]
 
 
@@ -431,6 +432,7 @@
             OmciMaskedData("data"), lambda pkt: pkt.success_code == 0)
     ]
 
+
 class OmciStartSoftwareDownload(OmciMessage):
     name = "OmciStartSoftwareDownload"
     message_id = 0x53
@@ -443,6 +445,7 @@
         ShortField("instance_id", None) # should be same as "entity_id"        
     ]
 
+
 class OmciStartSoftwareDownloadResponse(OmciMessage):
     name = "OmciStartSoftwareDownloadResponse"
     message_id = 0x33
@@ -455,6 +458,7 @@
         ShortField("instance_id", None) # should be same as "entity_id"        
     ]
 
+
 class OmciEndSoftwareDownload(OmciMessage):
     name = "OmciEndSoftwareDownload"
     message_id = 0x55
@@ -467,6 +471,7 @@
         ShortField("instance_id", None),# should be same as "entity_id"
     ]
 
+
 class OmciEndSoftwareDownloadResponse(OmciMessage):
     name = "OmciEndSoftwareDownload"
     message_id = 0x35
@@ -479,6 +484,7 @@
         ByteField("result0", 0)         # same as result 
     ]
 
+
 class OmciDownloadSection(OmciMessage):
     name = "OmciDownloadSection"
     message_id = 0x14
@@ -489,6 +495,7 @@
         StrFixedLenField("data", 0, length=OmciSectionDataSize) # section data
     ]
 
+
 class OmciDownloadSectionLast(OmciMessage):
     name = "OmciDownloadSection"
     message_id = 0x54
@@ -499,6 +506,7 @@
         StrFixedLenField("data", 0, length=OmciSectionDataSize) # section data
     ]
 
+
 class OmciDownloadSectionResponse(OmciMessage):
     name = "OmciDownloadSectionResponse"
     message_id = 0x34
@@ -509,6 +517,7 @@
         ByteField("section_number", 0),  # Always only 1 in parallel
     ]
 
+
 class OmciActivateImage(OmciMessage):
     name = "OmciActivateImage"
     message_id = 0x56
@@ -518,6 +527,7 @@
         ByteField("activate_flag", 0)    # Activate image unconditionally
     ]
 
+
 class OmciActivateImageResponse(OmciMessage):
     name = "OmciActivateImageResponse"
     message_id = 0x36
@@ -527,6 +537,7 @@
         ByteField("result", 0)           # Activate image unconditionally
     ]
 
+
 class OmciCommitImage(OmciMessage):
     name = "OmciCommitImage"
     message_id = 0x57
@@ -535,6 +546,7 @@
         ShortField("entity_id", None),
     ]
 
+
 class OmciCommitImageResponse(OmciMessage):
     name = "OmciCommitImageResponse"
     message_id = 0x37
diff --git a/pyvoltha/adapters/extensions/omci/openomci_agent.py b/pyvoltha/adapters/extensions/omci/openomci_agent.py
index 672af5b..e416d58 100644
--- a/pyvoltha/adapters/extensions/omci/openomci_agent.py
+++ b/pyvoltha/adapters/extensions/omci/openomci_agent.py
@@ -41,7 +41,7 @@
     'mib-synchronizer': {
         'state-machine': MibSynchronizer,  # Implements the MIB synchronization state machine
         'database': MibDbVolatileDict,     # Implements volatile ME MIB database
-        #'database': MibDbExternal,         # Implements persistent ME MIB database
+        # 'database': MibDbExternal,         # Implements persistent ME MIB database
         'advertise-events': True,          # Advertise events on OpenOMCI event bus
         'tasks': {
             'mib-upload': MibUploadTask,
@@ -55,7 +55,7 @@
         'state-machine': OnuOmciCapabilities,   # Implements OMCI capabilities state machine
         'advertise-events': False,              # Advertise events on OpenOMCI event bus
         'tasks': {
-            'get-capabilities': OnuCapabilitiesTask  # Get supported ME and Commands
+            'get-capabilities': OnuCapabilitiesTask # Get supported ME and Commands
         }
     },
     'performance-intervals': {
diff --git a/pyvoltha/adapters/extensions/omci/state_machines/mib_sync.py b/pyvoltha/adapters/extensions/omci/state_machines/mib_sync.py
index 3f18aa4..a5b0e2a 100644
--- a/pyvoltha/adapters/extensions/omci/state_machines/mib_sync.py
+++ b/pyvoltha/adapters/extensions/omci/state_machines/mib_sync.py
@@ -143,6 +143,10 @@
             RxEvent.Create: None,
             RxEvent.Delete: None,
             RxEvent.Set: None,
+            RxEvent.Start_Software_Download: None,
+            RxEvent.End_Software_Download: None,
+            RxEvent.Activate_Software: None,
+            RxEvent.Commit_Software: None,
         }
         self._omci_cc_sub_mapping = {
             RxEvent.MIB_Reset: self.on_mib_reset_response,
@@ -152,6 +156,10 @@
             RxEvent.Create: self.on_create_response,
             RxEvent.Delete: self.on_delete_response,
             RxEvent.Set: self.on_set_response,
+            RxEvent.Start_Software_Download: self.on_software_event,
+            RxEvent.End_Software_Download: self.on_software_event,
+            RxEvent.Activate_Software: self.on_software_event,
+            RxEvent.Commit_Software: self.on_software_event,
         }
         self._onu_dev_subscriptions = {               # DevEvent.enum -> Subscription Object
             DevEvent.OmciCapabilitiesEvent: None
@@ -216,6 +224,7 @@
         if self._database is not None:
             self._database.save_mib_data_sync(self._device_id,
                                               self._mib_data_sync)
+            self.log.info("mds-updated", device=self._device_id, mds=self._mib_data_sync)
 
     @property
     def last_mib_db_sync(self):
@@ -522,8 +531,9 @@
             self._attr_diffs = attr_diffs if attr_diffs and len(attr_diffs) else None
             self._audited_olt_db = olt_db
             self._audited_onu_db = onu_db
+            audited_mds = self._audited_onu_db[MDS_KEY]
 
-            mds_equal = self.mib_data_sync == self._audited_onu_db[MDS_KEY]
+            mds_equal = self.mib_data_sync == audited_mds
 
             if mds_equal and all(diff is None for diff in [self._on_olt_only_diffs,
                                                            self._on_onu_only_diffs,
@@ -609,14 +619,6 @@
                 # Save the changed data to the MIB.
                 self._database.set(self.device_id, class_id, instance_id, data)
 
-                # Autonomous creation and deletion of managed entities do not
-                # result in an increment of the MIB data sync value. However,
-                # AVC's in response to a change by the Operator do incur an
-                # increment of the MIB Data Sync.  If here during uploading,
-                # we issued a MIB-Reset which may generate AVC.  (TODO: Focus testing during hardening)
-                if self.state == 'uploading':
-                    self.increment_mib_data_sync()
-
             except KeyError:
                 pass            # NOP
 
@@ -711,17 +713,16 @@
                                   status=omci_msg.fields['success_code'],
                                   status_text=self._status_to_text(omci_msg.fields['success_code']),
                                   parameter_error_attributes_mask=omci_msg.fields['parameter_error_attributes_mask'])
-                else:
+
+                elif status != RC.InstanceExists:
                     omci_msg = request.fields['omci_message'].fields
                     class_id = omci_msg['entity_class']
                     entity_id = omci_msg['entity_id']
                     attributes = {k: v for k, v in omci_msg['data'].items()}
 
                     # Save to the database
-                    created = self._database.set(self._device_id, class_id, entity_id, attributes)
-
-                    if created:
-                        self.increment_mib_data_sync()
+                    self._database.set(self._device_id, class_id, entity_id, attributes)
+                    self.increment_mib_data_sync()
 
                     # If the ME contains set-by-create or writeable values that were
                     # not specified in the create command, the ONU will have
@@ -803,10 +804,8 @@
                     entity_id = omci_msg['entity_id']
 
                     # Remove from the database
-                    deleted = self._database.delete(self._device_id, class_id, entity_id)
-
-                    if deleted:
-                        self.increment_mib_data_sync()
+                    self._database.delete(self._device_id, class_id, entity_id)
+                    self.increment_mib_data_sync()
 
             except KeyError as e:
                 pass            # NOP
@@ -825,36 +824,85 @@
         if self._omci_cc_subscriptions[RxEvent.Set]:
             if self.state in ['disabled', 'uploading']:
                 self.log.error('rx-in-invalid-state', state=self.state)
+                return
             try:
                 request = msg[TX_REQUEST_KEY]
                 response = msg[RX_RESPONSE_KEY]
+                tx_omci_msg = request.fields['omci_message'].fields
+                rx_omci_msg = response.fields['omci_message'].fields
 
-                if response.fields['omci_message'].fields['success_code'] != RC.Success:
-                    # TODO: Support offline ONTs in post VOLTHA v1.3.0
-                    omci_msg = response.fields['omci_message']
-                    self.log.warn('set-response-failure',
-                                  class_id=omci_msg.fields['entity_class'],
-                                  instance_id=omci_msg.fields['entity_id'],
-                                  status=omci_msg.fields['success_code'],
-                                  status_text=self._status_to_text(omci_msg.fields['success_code']),
-                                  unsupported_attribute_mask=omci_msg.fields['unsupported_attributes_mask'],
-                                  failed_attribute_mask=omci_msg.fields['failed_attributes_mask'])
+                rx_status = rx_omci_msg['success_code']
+                class_id = rx_omci_msg['entity_class']
+                entity_id = rx_omci_msg['entity_id']
+                attributes = dict()
+
+                tx_mask = tx_omci_msg['attributes_mask']
+                rx_fail_mask = 0
+                if rx_status == RC.AttributeFailure:
+                    rx_fail_mask = rx_omci_msg['unsupported_attributes_mask'] | rx_omci_msg['failed_attributes_mask']
+
+                if rx_status == RC.Success:
+                    attributes = {k: v for k, v in tx_omci_msg['data'].items()}
+
+                elif RC.AttributeFailure and tx_mask != rx_fail_mask:
+                    # Partial success, set only those that were good
+                    entity = self._device.me_map[class_id]
+                    good_mask = tx_mask & ~rx_fail_mask
+                    good_attr_indexes = entity.attribute_indices_from_mask(good_mask)
+                    good_attr_names = {attr.field.name for index, attr in enumerate(entity.attributes)
+                                       if index in good_attr_indexes}
+
+                    attributes = {k: v for k, v in tx_omci_msg['data'].items()
+                                  if k in good_attr_names}
                 else:
-                    omci_msg = request.fields['omci_message'].fields
-                    class_id = omci_msg['entity_class']
-                    entity_id = omci_msg['entity_id']
-                    attributes = {k: v for k, v in omci_msg['data'].items()}
+                    self.log.warn('set-response-failure',
+                                  class_id=rx_omci_msg['entity_class'],
+                                  instance_id=rx_omci_msg['entity_id'],
+                                  status=rx_status,
+                                  status_text=self._status_to_text(rx_status),
+                                  unsupported_attribute_mask=rx_omci_msg['unsupported_attributes_mask'],
+                                  failed_attribute_mask=rx_omci_msg['failed_attributes_mask'])
 
-                    # Save to the database (Do not save 'sets' of the mib-data-sync however)
-                    if class_id != OntData.class_id:
-                        modified = self._database.set(self._device_id, class_id, entity_id, attributes)
-                        if modified:
-                            self.increment_mib_data_sync()
+                # Save to the database. A set of MDS in the OntData class results in
+                # an increment. However, we do not save that within the class/entity
+                # portion of the database.
+                if class_id == OntData.class_id and len(attributes) > 0:
+                    self.increment_mib_data_sync()
+
+                elif len(attributes) > 0:
+                    self._database.set(self._device_id, class_id, entity_id, attributes)
+                    self.increment_mib_data_sync()
 
             except KeyError as _e:
                 pass            # NOP
             except Exception as e:
                 self.log.exception('set', e=e)
+
+    def on_software_event(self, _topic, msg):
+        """
+        Process a Software Start, End, Activate, and Commit
+
+        :param _topic: (str) OMCI-RX topic
+        :param msg: (dict) Dictionary with 'rx-response' and 'tx-request' (if any)
+        """
+        self.log.debug('on-software-event', state=self.state)
+
+        # All events for software run this method, checking for one is good enough
+        if self._omci_cc_subscriptions[RxEvent.Start_Software_Download]:
+            if self.state in ['disabled', 'uploading']:
+                self.log.error('rx-in-invalid-state', state=self.state)
+                return
+            try:
+                # Note: all download responses we subscribe to have a 'result' field
+                response = msg[RX_RESPONSE_KEY]
+                if response.fields['omci_message'].fields['success_code'] == RC.Success:
+                    self.increment_mib_data_sync()
+
+            except KeyError as _e:
+                pass            # NOP
+            except Exception as e:
+                self.log.exception('set', e=e)
+
     def on_capabilities_event(self, _topic, msg):
         """
         Process a OMCI capabilties event
diff --git a/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py b/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
index 8080274..421879b 100644
--- a/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
+++ b/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
@@ -615,6 +615,17 @@
         returnValue((successes, failures))
 
     @inlineCallbacks
+    def _get_current_mds(self):
+        self.strobe_watchdog()
+        results = yield self._device.omci_cc.send(OntDataFrame().get())
+
+        omci_msg = results.fields['omci_message'].fields
+        status = omci_msg['success_code']
+        mds = (omci_msg['data']['mib_data_sync'] >> 8) & 0xFF \
+            if status == 0 and 'data' in omci_msg and 'mib_data_sync' in omci_msg['data'] else -1
+        returnValue(mds)
+
+    @inlineCallbacks
     def update_mib_data_sync(self):
         """
         As the final step of MIB resynchronization, the OLT sets the MIB data sync
@@ -624,21 +635,27 @@
 
         :return: (int, int) success, failure counts
         """
-        # Get MDS to set, do not user zero
-
+        # Get MDS to set
+        self._sync_sm.increment_mib_data_sync()
         new_mds_value = self._sync_sm.mib_data_sync
-        if new_mds_value == 0:
-            self._sync_sm.increment_mib_data_sync()
-            new_mds_value = self._sync_sm.mib_data_sync
 
         # Update it.  The set response will be sent on the OMCI-CC pub/sub bus
         # and the MIB Synchronizer will update this MDS value in the database
         # if successful.
         try:
+            # previous_mds = yield self._get_current_mds()
+
             frame = OntDataFrame(mib_data_sync=new_mds_value).set()
 
             results = yield self._device.omci_cc.send(frame)
             self.check_status_and_state(results, 'ont-data-mbs-update')
+
+            #########################################
+            # Debug.  Verify new MDS value was received. Should be 1 greater
+            #         than what was sent
+            # new_mds = yield self._get_current_mds()
+            # self.log.info('mds-update', previous=previous_mds, new=new_mds_value, now=new_mds)
+            # Done
             returnValue((1, 0))
 
         except TimeoutError as e:
diff --git a/pyvoltha/adapters/extensions/omci/tasks/mib_resync_task.py b/pyvoltha/adapters/extensions/omci/tasks/mib_resync_task.py
index 13cb8ef..fdce7e6 100644
--- a/pyvoltha/adapters/extensions/omci/tasks/mib_resync_task.py
+++ b/pyvoltha/adapters/extensions/omci/tasks/mib_resync_task.py
@@ -18,8 +18,10 @@
 from twisted.internet import reactor
 from pyvoltha.common.utils.asleep import asleep
 from pyvoltha.adapters.extensions.omci.database.mib_db_dict import *
-from pyvoltha.adapters.extensions.omci.omci_entities import OntData
+from pyvoltha.adapters.extensions.omci.omci_entities import OntData, Omci
 from pyvoltha.adapters.extensions.omci.omci_defs import AttributeAccess, EntityOperations
+from pyvoltha.adapters.extensions.omci.omci_fields import OmciTableField
+from pyvoltha.adapters.extensions.omci.omci_me import OntDataFrame
 
 AA = AttributeAccess
 OP = EntityOperations
@@ -231,6 +233,13 @@
             if number_of_commands is None or number_of_commands <= 0:
                 raise ValueError('Number of commands was {}'.format(number_of_commands))
 
+            # Get the current MIB-DATA-SYNC on the ONU
+            self.strobe_watchdog()
+            results = yield self._device.omci_cc.send(OntDataFrame().get())
+            omci_msg = results.fields['omci_message'].fields
+            mds = (omci_msg['data']['mib_data_sync'] >> 8) & 0xFF
+            self._db_active.save_mib_data_sync(self.device_id, mds)
+
             returnValue(number_of_commands)
 
         except TimeoutError as e:
@@ -259,7 +268,7 @@
                     # the device level and do not want it showing up during a re-sync
                     # during data comparison
                     from binascii import hexlify
-                    if class_id == OntData.class_id:
+                    if class_id in (OntData.class_id, Omci.class_id):
                         break
 
                     # The T&W ONU reports an ME with class ID 0 but only on audit. Perhaps others do as well.
@@ -396,13 +405,16 @@
             olt_cls = omci_copy[cls_id]
             onu_cls = onu_copy[cls_id]
 
-            # Weed out read-only attributes. Attributes on onu may be read-only. These
-            # will only show up it the OpenOMCI (OLT-side) database if it changed and
-            # an AVC Notification was sourced by the ONU
+            # Weed out read-only and table attributes. Attributes on onu may be read-only.
+            # These will only show up it the OpenOMCI (OLT-side) database if it changed
+            # and an AVC Notification was sourced by the ONU
             # TODO: These class IDs could be calculated once at ONU startup (at device add)
             if cls_id in me_map:
                 ro_attrs = {attr.field.name for attr in me_map[cls_id].attributes
                             if attr.access == ro_set}
+                table_attrs = {attr.field.name for attr in me_map[cls_id].attributes
+                               if isinstance(attr.field, OmciTableField)}
+
             else:
                 # Here if partially defined ME (not defined in ME Map)
                 from pyvoltha.adapters.extensions.omci.omci_cc import UNKNOWN_CLASS_ATTRIBUTE_KEY
diff --git a/pyvoltha/adapters/extensions/omci/tasks/omci_get_request.py b/pyvoltha/adapters/extensions/omci/tasks/omci_get_request.py
index 7950944..4f21154 100644
--- a/pyvoltha/adapters/extensions/omci/tasks/omci_get_request.py
+++ b/pyvoltha/adapters/extensions/omci/tasks/omci_get_request.py
@@ -18,9 +18,8 @@
 from twisted.internet.defer import failure, inlineCallbacks, TimeoutError, returnValue
 from pyvoltha.adapters.extensions.omci.omci_defs import ReasonCodes, EntityOperations
 from pyvoltha.adapters.extensions.omci.omci_me import MEFrame
-from pyvoltha.adapters.extensions.omci.omci_frame import OmciFrame, OmciGetNext
-from pyvoltha.adapters.extensions.omci.omci_cc import DEFAULT_OMCI_TIMEOUT
-from pyvoltha.adapters.extensions.omci.omci_messages import OmciGet
+from pyvoltha.adapters.extensions.omci.omci_frame import OmciFrame
+from pyvoltha.adapters.extensions.omci.omci_messages import OmciGet, OmciGetNext
 from pyvoltha.adapters.extensions.omci.omci_fields import OmciTableField
 
 RC = ReasonCodes
@@ -151,6 +150,28 @@
         attr_def = self._entity_class.attributes[index]
         return isinstance(attr_def.field, OmciTableField)
 
+    def select_first_attributes(self):
+        """
+        For the requested attributes, get as many as possible in the first requst
+        :return: (set) attributes that will fit in one frame
+        """
+        octets_available = 25   # Max GET Response baseline payload size
+        first_attributes = set()
+
+        for attr in self._attributes:
+            index = self._entity_class.attribute_name_to_index_map[attr]
+            attr_def = self._entity_class.attributes[index]
+
+            if isinstance(attr_def.field, OmciTableField):
+                continue   # No table attributes
+
+            size = attr_def.field.sz
+            if size <= octets_available:
+                first_attributes.add(attr)
+                octets_available -= size
+
+        return first_attributes
+
     @inlineCallbacks
     def perform_get_omci(self):
         """
@@ -160,8 +181,7 @@
                       entity_id=self._entity_id, attributes=self._attributes)
         try:
             # If one or more attributes is a table attribute, get it separately
-
-            first_attributes = {attr for attr in self._attributes if not self.is_table_attr(attr)}
+            first_attributes = self.select_first_attributes()
             table_attributes = {attr for attr in self._attributes if self.is_table_attr(attr)}
 
             if len(first_attributes):
@@ -189,10 +209,14 @@
                     results_omci = results.fields['omci_message'].fields
 
                     # Were all attributes fetched?
-                    missing_attr = frame.fields['omci_message'].fields['attributes_mask'] ^ \
-                        results_omci['attributes_mask']
+                    requested_attr = frame.fields['omci_message'].fields['attributes_mask']
+                    retrieved_attr = results_omci['attributes_mask']
+                    unsupported_attr = results_omci.get('unsupported_attributes_mask', 0) or 0
+                    failed_attr = results_omci.get('failed_attributes_mask', 0) or 0
+                    not_avail_attr = unsupported_attr | failed_attr
+                    missing_attr = requested_attr & ~(retrieved_attr | not_avail_attr)
 
-                    if missing_attr > 0 or len(table_attributes) > 0:
+                    if missing_attr != 0 or len(table_attributes) > 0:
                         self.log.info('perform-get-missing', num_missing=missing_attr,
                                       table_attr=table_attributes)
                         self.strobe_watchdog()
@@ -268,16 +292,16 @@
                     status = get_omci['success_code']
 
                     if status == RC.AttributeFailure.value:
-                        # TODO: update failed & unknown attributes set
+                        unsupported_attr = get_omci.get('unsupported_attributes_mask', 0) or 0
+                        failed_attr = get_omci.get('failed_attributes_mask', 0) or 0
+                        results_omci['unsupported_attributes_mask'] |= unsupported_attr
+                        results_omci['failed_attributes_mask'] |= failed_attr
                         continue
 
                     elif status != RC.Success.value:
                         raise GetException('Get failed with status code: {}'.format(status))
 
-                    # assert attr_mask == get_omci['attributes_mask'], 'wrong attribute'
-                    if attr_mask != get_omci['attributes_mask']:
-                        self.log.debug('attr mask does not match expected mask', attr_mask=attr_mask,
-                            expected_mask = get_omci['attributes_mask'])
+                    assert attr_mask == get_omci['attributes_mask'], 'wrong attribute'
                     results_omci['attributes_mask'] |= attr_mask
 
                     if results_omci.get('data') is None:
@@ -297,8 +321,7 @@
         # Now any table attributes
         if len(table_attributes):
             self.strobe_watchdog()
-            self._local_deferred = reactor.callLater(0,
-                                                     self.process_get_table,
+            self._local_deferred = reactor.callLater(0, self.process_get_table,
                                                      table_attributes)
             returnValue(self._local_deferred)
 
@@ -339,8 +362,10 @@
                 if omci_fields['success_code'] == RC.AttributeFailure.value:
                     # Copy over any failed or unsupported attribute masks for final result
                     results_fields = results_omci.fields['omci_message'].fields
-                    results_fields['unsupported_attributes_mask'] |= omci_fields['unsupported_attributes_mask']
-                    results_fields['failed_attributes_mask'] |= omci_fields['failed_attributes_mask']
+                    unsupported_attr = results_omci.get('unsupported_attributes_mask', 0) or 0
+                    failed_attr = results_omci.get('failed_attributes_mask', 0) or 0
+                    results_fields['unsupported_attributes_mask'] |= unsupported_attr
+                    results_fields['failed_attributes_mask'] |= failed_attr
 
                 if omci_fields['success_code'] != RC.Success.value:
                     raise GetException('Get table attribute failed with status code: {}'.
diff --git a/test/unit/extensions/omci/test_mib_resync_task.py b/test/unit/extensions/omci/test_mib_resync_task.py
index 43b27d9..892cd97 100644
--- a/test/unit/extensions/omci/test_mib_resync_task.py
+++ b/test/unit/extensions/omci/test_mib_resync_task.py
@@ -303,11 +303,11 @@
         class_id = PriorityQueueG.class_id
         inst_id = 0
         attributes_olt = {
-            'related_port': int(1234),      # IntField (R/O)
+            'related_port': int(1234),      # IntField (R/W)
             'maximum_queue_size': int(222)  # Only on OLT but read-only
         }
         attributes_onu = {
-            'related_port': int(5678)    # IntField (R/O)
+            'related_port': int(1234)    # IntField (R/W)
         }
         self.onu_db.set(_DEVICE_ID, class_id, inst_id, attributes_onu)
         self.olt_db.set(_DEVICE_ID, class_id, inst_id, attributes_olt)
diff --git a/test/unit/extensions/omci/test_omci.py b/test/unit/extensions/omci/test_omci.py
index 6df072b..b6397a8 100644
--- a/test/unit/extensions/omci/test_omci.py
+++ b/test/unit/extensions/omci/test_omci.py
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 from unittest import TestCase, main
+from binascii import unhexlify
 
 from pyvoltha.adapters.extensions.omci.omci import *
 
@@ -1136,7 +1137,7 @@
             message_type=OmciReboot.message_id,
             omci_message=OmciReboot(
                 entity_class=OntG.class_id,
-                 entity_id=0
+                entity_id=0
             )
         )
         self.assertGeneratedFrameEquals(frame, ref)
@@ -1157,6 +1158,128 @@
             self.assertTrue(AA.SBC not in mei_attr.access or
                             mei_attr.field.name == 'managed_entity_id')
 
+    def test_get_response_without_error_but_too_big(self):
+        # This test is related to a bug that I believe is in the BroadCom
+        # ONU stack software, or at least it was seen on both an Alpha and
+        # an T&W BCM-based onu.  The IEEE 802.1p Mapper Service Profile ME
+        # (#130) sent by the ONUs have a payload of 27 octets based on the
+        # Attribute Mask in the encoding.  However, get-response baseline
+        # messages have the last 4 octets reserved for failed/errored attribute
+        # masks so only 25 octets should be allowed.  Of course the 4 octets
+        # are only valid if the status code == 9, but they still should
+        # be reserved.
+        #
+        # This test verifies that we can still parse the 27 octet payload
+        # since the first rule of interoperability is to be lenient with
+        # what you receive and strict with what you transmit.
+        #
+        ref = '017d290a008280020000780000000000000000000000' +\
+              '0000000000000000000000000000' +\
+              '01' +\
+              '02' +\
+              '0000' +\
+              '00000028'
+        zeros_24 = '000000000000000000000000000000000000000000000000'
+        bytes_24 = unhexlify(zeros_24)
+        attributes = {
+            "unmarked_frame_option": 0,         # 1 octet
+            "dscp_to_p_bit_mapping": bytes_24,  # 24 octets
+            "default_p_bit_marking": 1,         # 1 octet   - This is too much
+            "tp_type": 2,                       # 1 octet
+        }
+        frame = OmciFrame(
+            transaction_id=0x017d,
+            message_type=OmciGetResponse.message_id,
+            omci_message=OmciGetResponse(
+                entity_class=Ieee8021pMapperServiceProfile.class_id,
+                success_code=0,
+                entity_id=0x8002,
+                attributes_mask=Ieee8021pMapperServiceProfile.mask_for(*attributes.keys()),
+                data=attributes
+            )
+        )
+        self.assertGeneratedFrameEquals(frame, ref)
+
+    def test_get_response_with_errors_max_data(self):
+        # First a frame with maximum data used up. This aligns the fields up perfectly
+        # with the simplest definition of a Get Response
+        ref = '017d290a008280020900600000000000000000000000' +\
+              '0000000000000000000000000000' +\
+              '0010' +\
+              '0008' +\
+              '00000028'
+        zeros_24 = '000000000000000000000000000000000000000000000000'
+        bytes_24 = unhexlify(zeros_24)
+        good_attributes = {
+            "unmarked_frame_option": 0,         # 1 octet
+            "dscp_to_p_bit_mapping": bytes_24,  # 24 octets
+        }
+        unsupported_attributes = ["default_p_bit_marking"]
+        failed_attributes_mask = ["tp_type"]
+
+        the_class = Ieee8021pMapperServiceProfile
+        frame = OmciFrame(
+            transaction_id=0x017d,
+            message_type=OmciGetResponse.message_id,
+            omci_message=OmciGetResponse(
+                entity_class=the_class.class_id,
+                success_code=9,
+                entity_id=0x8002,
+                attributes_mask=the_class.mask_for(*good_attributes.keys()),
+                unsupported_attributes_mask=the_class.mask_for(*unsupported_attributes),
+                failed_attributes_mask=the_class.mask_for(*failed_attributes_mask),
+                data=good_attributes
+            )
+        )
+        self.assertGeneratedFrameEquals(frame, ref)
+
+    def test_get_response_with_errors_min_data(self):
+        # Next a frame with only a little data used up. This aligns will require
+        # the encoder and decoder to skip to the last 8 octets of the data field
+        # and encode the failed masks there
+        ref = '017d290a00828002090040' +\
+              '01' + '00000000000000000000' +\
+              '0000000000000000000000000000' +\
+              '0010' +\
+              '0028' +\
+              '00000028'
+
+        good_attributes = {
+            "unmarked_frame_option": 1,         # 1 octet
+        }
+        unsupported_attributes = ["default_p_bit_marking"]
+        failed_attributes_mask = ["dscp_to_p_bit_mapping", "tp_type"]
+
+        the_class = Ieee8021pMapperServiceProfile
+        frame = OmciFrame(
+            transaction_id=0x017d,
+            message_type=OmciGetResponse.message_id,
+            omci_message=OmciGetResponse(
+                entity_class=the_class.class_id,
+                success_code=9,
+                entity_id=0x8002,
+                attributes_mask=the_class.mask_for(*good_attributes.keys()),
+                unsupported_attributes_mask=the_class.mask_for(*unsupported_attributes),
+                failed_attributes_mask=the_class.mask_for(*failed_attributes_mask),
+                data=good_attributes
+            )
+        )
+        self.assertGeneratedFrameEquals(frame, ref)
+
+        # Now test decode of the packet
+        decoded = OmciFrame(unhexlify(ref))
+
+        orig_fields = frame.fields['omci_message'].fields
+        omci_fields = decoded.fields['omci_message'].fields
+
+        for field in ['entity_class', 'entity_id', 'attributes_mask',
+                      'success_code', 'unsupported_attributes_mask',
+                      'failed_attributes_mask']:
+            self.assertEqual(omci_fields[field], orig_fields[field])
+
+        self.assertEqual(omci_fields['data']['unmarked_frame_option'],
+                         orig_fields['data']['unmarked_frame_option'])
+
 
 if __name__ == '__main__':
     main()