VOL-598: Updates to Adtran ONU OMCI library

Change-Id: Ibb494bb79fd60ee728e4f81408dc375a45eaee62
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index c1ff799..c0d9ae9 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -36,7 +36,7 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='Adtran, Inc.',
-                                               version='0.3',
+                                               version='0.4',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN')
 
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 8f8f75d..2231dd4 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -17,7 +17,8 @@
 import arrow
 
 from voltha.adapters.adtran_olt.xpon.adtran_xpon import AdtranXPON
-from omci.omci_cc import OMCISupport
+from omci.omci_cc import OMCI_CC
+from omci.omci_entities import onu_custom_entity_classes
 from pon_port import PonPort
 from uni_port import UniPort
 from heartbeat import HeartBeat
@@ -119,7 +120,7 @@
 
     @property
     def is_mock(self):
-        return self._is_mock        # Not pointing to any real hardware
+        return self._is_mock        # Not pointing to real hardware
 
     @property
     def olt_created(self):
@@ -166,7 +167,9 @@
         #
         self._cancel_deferred()
 
-        self._omci = OMCISupport(self, self.adapter, self.device_id)
+        self._omci = OMCI_CC(self.adapter_agent,
+                             self.device_id,
+                             custom_me_entries=onu_custom_entity_classes)
         self._omci.enabled = True
 
         # Handle received ONU event messages
@@ -241,6 +244,7 @@
         device.model = 'n/a'
         device.hardware_version = 'n/a'
         device.firmware_version = 'n/a'
+        device.reason = ''
 
         # TODO: Support more versions as needed
         images = Image(version='NOT AVAILABLE')
@@ -287,14 +291,13 @@
             self.adapter_agent.add_port(device.id, uni_port.get_port())
 
             device.serial_number = uuid4().hex
-            # self._add_logical_port(device.vlan, control_vlan=device.vlan)
             uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)
 
             # Start things up for this ONU Handler.
             self.enabled = True
 
         # Start collecting stats from the device after a brief pause
-        reactor.callLater(10, self.start_kpi_collection, device.id)
+        reactor.callLater(30, self.start_kpi_collection, device.id)
 
         self.adapter_agent.update_device(device)
 
@@ -325,6 +328,7 @@
         device = self.adapter_agent.get_device(device.id)
         device.connect_status = ConnectStatus.REACHABLE
         device.oper_status = OperStatus.ACTIVE
+        device.reason = ''
         self.adapter_agent.update_device(device)
 
         self.log.info('reconciling-ONU-device-ends')
@@ -554,6 +558,7 @@
         previous_conn_status = device.connect_status
         device.oper_status = OperStatus.ACTIVATING
         device.connect_status = ConnectStatus.UNREACHABLE
+        device.reason = 'Rebooting'
 
         self.adapter_agent.update_device(device)
 
@@ -571,6 +576,7 @@
         device = self.adapter_agent.get_device(self.device_id)
         device.oper_status = previous_oper_status
         device.connect_status = previous_conn_status
+        device.reason = ''
         self.adapter_agent.update_device(device)
         self.log.info('rebooted', device_id=self.device_id)
 
@@ -598,6 +604,7 @@
         # Update the device operational status to UNKNOWN
         device.oper_status = OperStatus.UNKNOWN
         device.connect_status = ConnectStatus.UNREACHABLE
+        device.reason = 'Disabled'
         self.adapter_agent.update_device(device)
 
         # Remove the uni logical port from the OLT, if still present
@@ -681,6 +688,7 @@
 
             device = self.adapter_agent.get_device(device.id)
             device.oper_status = OperStatus.ACTIVE
+            device.reason = ''
 
             self.enabled = True
             self.adapter_agent.update_device(device)
@@ -908,7 +916,8 @@
 
         td = self.traffic_descriptors.get(tcont.get('td-ref'))
         traffic_descriptor = td['object'] if td is not None else None
-        tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor)
+        tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor,
+                                          is_mock=self.is_mock)
 
         # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
         pon_port = self.pon_ports[0]
@@ -998,7 +1007,7 @@
     def on_gemport_create(self, gem_port):
         from onu_gem_port import OnuGemPort
 
-        gem_port['object'] = OnuGemPort.create(self, gem_port)
+        gem_port['object'] = OnuGemPort.create(self, gem_port, is_mock=self.is_mock)
         # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
         pon_port = self.pon_ports[0]
         if pon_port is not None:
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index 02fec22..4e54459 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -61,10 +61,10 @@
         if self._enabled != value:
             self._enabled = value
 
-            if value:
-                self._start()
-            else:
-                self._stop()
+            # if value:
+            #     self._start()
+            # else:
+            #     self._stop()
 
     @property
     def check_item(self):
diff --git a/voltha/adapters/adtran_onu/omci/README.md b/voltha/adapters/adtran_onu/omci/README.md
index 2d030fe..babd884 100644
--- a/voltha/adapters/adtran_onu/omci/README.md
+++ b/voltha/adapters/adtran_onu/omci/README.md
@@ -1,12 +1,97 @@
 #OMCI Support
 
 This directory contains classes to assist in the creation, transmission,
-and reception of OMCI frames on this ONU Adapter.
+and reception of OMCI frames on this ONU Adapter. A number of these files (but
+not all) could be moved into the common *.../voltha/extensions/omci* subdirectory.
 
 ##Files
+### omci_cc.py
 
-*TODO*: Add info on each file and what it is for
+The *omci_cc.py* file contains the OMCI communications channel for sending and receiving
+OMCI messages.  For transmits, it will send the OMCI message to the proper proxy channel,
+but for received, OMCI frames, your device adapter will need to call the
+*receive_message()* method.
+
+The communications channel will return a deferred on a Tx request which will fire when
+a corresponding response is received or if a timeout or other error occurs. When a
+successful response is received, the *OMCI_CC* will also look at *Get, Set, Create*, and
+*Delete* messages prior to calling any additional callbacks so that the MIB Database can be
+checked or updated as needed.  Note that the MIB Database is not yet implemented.
+
+ONU Autonomous messages are also handled (Test Results messages are TBD) and these are
+placed
+
+A collection of statistics are available in case the ONU wishes to publish a
+PM group containing these statistics. The Adtran ONU does so in the *onu_pm_metrics.py*
+file.
+
+Finally, a list of vendor-specific ME's can be passed to the class initializer so that
+they are registered in the class_id map. This allows for successful decode of custom MEs.
+See the Adtran ONU's instantiation of the *OMCI_CC* class as an example of how to
+add vendor-specific MEs.
+
+### me_frame.py
+
+This file contains the base class implementation that helps to transform defined
+Managed EntityClasses into OMCI Frames with specific actions (*get, set, create, 
+delete, ...*). Prior this class, frames to do each action were hand created methods.
+
+Besides providing methods for creating OMCI frames, the ME attributes names, access
+attributes, and allowed operations are checked to verify that the action is valid
+for both the ME as well as the specific attribute.
+
+What is currently missing is other OMCI actions have not been coded:
+ - GetNext
+ - GetCurrentData
+ - GetAllAlarms
+ - GetAllAlarmsNext
+ - MibUpload
+ - MibUploadNext
+ - MibReset
+ - Test
+ - StartSoftwareDownload
+ - DownloadSection
+ - EndSoftwareDownload
+ - ActivateSoftware
+ - CommitSoftware
+ - SynchronizeTime
+ - Reboot
+ 
+For many of these actions, such as MibReset, these are only performed on a specific
+ME and it may be best to provide these as explicit static methods.
+
+### omci_me.py
+
+This file is a collection of ME classes derived from the MEFrame base class. For many
+of the currently defined ME's in *omci_entities.py*
+
+### omci_defs.py
+
+This file contains an additional status code enumeration which could be merged with
+the main OMCI extensions directory.
+
+### omci_entities.py
+
+This is an Adtran ONU specific file to add custom OMCI **OMCI_CC** entities and a function
+that can be called by the **OMCI_CC** class to install them into the appropriate locations
+such that OMCI frame decode works as expected during MIB uploads.
+
+Eventually I envision the **OMCI_CC** to be mostly hidden from an ONU device adapter, so
+a method to register these custom ME's needs to be provided.
+ 
+### deprecated.py
+
+This file contains some of the original _old-style_ OMCI frame creation and send
+commands for the Adtran ONU. These were originally copied over from the BroadCom
+ONU Device Adapter and modified for use in the Adtran ONU. After the **OMCI_CC** class
+was created to handle OMCI Tx/Rx, a reference to the **OMCI_CC** was passed in so that
+these methods could use the *OMCI_CC.send()* method
+
+If you look at the current Adtran ONU **pon_port.py** file, it still contains the original
+calls to these are still in place (commented out) next to how to do the same calls with
+the new **ME_Frame** and **OMCI_CC** classes.
 
 ##Unit Tests
 
-*TODO*: Add info on how to run unit tests on these files
\ No newline at end of file
+After some peer review and any needed refactoring of code, the plan is to create unit tests
+to cover the **OMCI_CC** and **ME_Frame** classes with a target of _90%+_ line coverage.
\ No newline at end of file
diff --git a/voltha/adapters/adtran_onu/omci/deprecated.py b/voltha/adapters/adtran_onu/omci/deprecated.py
new file mode 100644
index 0000000..f93df0b
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/deprecated.py
@@ -0,0 +1,498 @@
+# Copyright 2017-present Adtran, Inc.
+#
+# 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 voltha.extensions.omci.omci import *
+
+
+DEFAULT_OMCI_TIMEOUT = 3            # Seconds
+
+# TODO: These are the older-style OMCI commands to send get/create/... frames
+
+
+def send_get_OntG(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_OntG')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=OntG.class_id,
+            entity_id=entity_id,
+            attributes_mask=OntG.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_vlan_tagging_filter_data(omci_cc, entity_id, vlan_id,
+                                         timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=VlanTaggingFilterData.class_id,
+            entity_id=entity_id,
+            data=dict(
+                vlan_filter_0=vlan_id,
+                forward_operation=0x10,
+                number_of_entries=1
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+# TODO: Deprecated: replaced with send_set_pptp_ethernet_uni  (need to clean up)
+
+
+def send_set_adminState(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_set_AdminState')
+    data = dict(
+        administrative_state=0
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=PptpEthernetUni.class_id,
+            entity_id=entity_id,
+            attributes_mask=PptpEthernetUni.mask_for(*data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_SoftwareImage(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_SoftwareImage')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=SoftwareImage.class_id,
+            entity_id=entity_id,
+            attributes_mask=SoftwareImage.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(omci_cc,
+                                                                              entity_id,
+                                                                              filter_inner_vid,
+                                                                              treatment_inner_vid,
+                                                                              timeout=DEFAULT_OMCI_TIMEOUT):
+    data = dict(
+        received_frame_vlan_tagging_operation_table=
+        VlanTaggingOperation(
+            filter_outer_priority=15,
+            filter_outer_vid=4096,
+            filter_outer_tpid_de=0,
+
+            filter_inner_priority=15,
+            filter_inner_vid=filter_inner_vid,
+            filter_inner_tpid_de=0,
+            filter_ether_type=0,
+
+            treatment_tags_to_remove=0,
+            treatment_outer_priority=15,
+            treatment_outer_vid=0,
+            treatment_outer_tpid_de=0,
+
+            treatment_inner_priority=0,
+            treatment_inner_vid=treatment_inner_vid,
+            treatment_inner_tpid_de=4
+        )
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=
+            ExtendedVlanTaggingOperationConfigurationData.class_id,
+            entity_id=entity_id,
+            attributes_mask=
+            ExtendedVlanTaggingOperationConfigurationData.mask_for(
+                *data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(omci_cc,
+                                                                                entity_id,
+                                                                                filter_inner_priority,
+                                                                                filter_inner_vid,
+                                                                                filter_inner_tpid_de,
+                                                                                treatment_tags_to_remove,
+                                                                                treatment_inner_priority,
+                                                                                treatment_inner_vid,
+                                                                                timeout=DEFAULT_OMCI_TIMEOUT):
+    data = dict(
+        received_frame_vlan_tagging_operation_table=
+        VlanTaggingOperation(
+            filter_outer_priority=15,
+            filter_outer_vid=4096,
+            filter_outer_tpid_de=0,
+            filter_inner_priority=filter_inner_priority,
+            filter_inner_vid=filter_inner_vid,
+            filter_inner_tpid_de=filter_inner_tpid_de,
+            filter_ether_type=0,
+            treatment_tags_to_remove=treatment_tags_to_remove,
+            treatment_outer_priority=15,
+            treatment_outer_vid=0,
+            treatment_outer_tpid_de=0,
+            treatment_inner_priority=treatment_inner_priority,
+            treatment_inner_vid=treatment_inner_vid,
+            treatment_inner_tpid_de=4
+        )
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=
+            ExtendedVlanTaggingOperationConfigurationData.class_id,
+            entity_id=entity_id,
+            attributes_mask=
+            ExtendedVlanTaggingOperationConfigurationData.mask_for(
+                *data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_delete_vlan_tagging_filter_data(omci_cc,
+                                      entity_id):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciDelete.message_id,
+        omci_message=OmciDelete(
+            entity_class=VlanTaggingFilterData.class_id,
+            entity_id=entity_id
+        )
+    )
+    return omci_cc.send(frame)
+
+# xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+
+def send_set_tcont(omci_cc, entity_id, alloc_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    data = dict(
+        alloc_id=alloc_id
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=Tcont.class_id,
+            entity_id=entity_id,
+            attributes_mask=Tcont.mask_for(*data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_gem_port_network_ctp(omci_cc, entity_id, port_id,
+                                     tcont_id, direction, tm,
+                                     timeout=DEFAULT_OMCI_TIMEOUT):
+
+    _directions = {"upstream": 1, "downstream": 2, "bi-directional": 3}
+
+    if _directions.has_key(direction):
+        _direction = _directions[direction]
+    else:
+        omci_cc.log.error('invalid-gem-port-direction', direction=direction)
+        raise ValueError('Invalid GEM port direction: {_dir}'.format(_dir=direction))
+
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=GemPortNetworkCtp.class_id,
+            entity_id=entity_id,
+            data=dict(
+                port_id=port_id,
+                tcont_pointer=tcont_id,
+                direction=_direction,
+                traffic_management_pointer_upstream=tm
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_set_8021p_mapper_service_profile(omci_cc, entity_id,
+                                          interwork_tp_id,
+                                          timeout=DEFAULT_OMCI_TIMEOUT):
+    data = dict(
+        interwork_tp_pointer_for_p_bit_priority_0=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_1=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_2=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_3=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_4=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_5=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_6=interwork_tp_id,
+        interwork_tp_pointer_for_p_bit_priority_7=interwork_tp_id
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=Ieee8021pMapperServiceProfile.class_id,
+            entity_id=entity_id,
+            attributes_mask=Ieee8021pMapperServiceProfile.mask_for(
+                *data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_8021p_mapper_service_profile(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=Ieee8021pMapperServiceProfile.class_id,
+            entity_id=entity_id,
+            data=dict(
+                tp_pointer=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_0=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_1=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_2=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_3=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_4=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_5=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_6=OmciNullPointer,
+                interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_mac_bridge_service_profile(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=MacBridgeServiceProfile.class_id,
+            entity_id=entity_id,
+            data=dict(
+                spanning_tree_ind=False,
+                # BP: Hack , this was not set in ADT configuration
+                # learning_ind=True,
+                # priority=0x8000,
+                # max_age=20 * 256,
+                # hello_time=2 * 256,
+                # forward_delay=15 * 256,
+                # unknown_mac_address_discard=True
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_gal_ethernet_profile(omci_cc, entity_id, max_gem_payload_size,
+                                     timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=GalEthernetProfile.class_id,
+            entity_id=entity_id,
+            data=dict(
+                max_gem_payload_size=max_gem_payload_size
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_gem_inteworking_tp(omci_cc, entity_id, gem_port_net_ctp_id,
+                                   service_profile_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=GemInterworkingTp.class_id,
+            entity_id=entity_id,
+            data=dict(
+                gem_port_network_ctp_pointer=gem_port_net_ctp_id,
+                interworking_option=5,
+                service_profile_pointer=service_profile_id,
+                interworking_tp_pointer=0x0,
+                pptp_counter=1,
+                gal_profile_pointer=0x0   # BP: HACK old value 0x1
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_mac_bridge_port_configuration_data(omci_cc, entity_id, bridge_id,
+                                                   port_id, tp_type, tp_id,
+                                                   timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=MacBridgePortConfigurationData.class_id,
+            entity_id=entity_id,
+            data=dict(
+                bridge_id_pointer=bridge_id,
+                port_num=port_id,
+                tp_type=tp_type,
+                tp_pointer=tp_id
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_circuit_pack(omci_cc, attribute, entity_id=0,
+                          timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_circuit_pack')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=CircuitPack.class_id,
+            entity_id=entity_id,
+            attributes_mask=CircuitPack.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_device_info(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=CircuitPack.class_id,
+            entity_id=entity_id,
+            attributes_mask=CircuitPack.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_Ont2G(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_Ont2G')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=Ont2G.class_id,
+            entity_id=entity_id,
+            attributes_mask=Ont2G.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_cardHolder(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_cardHolder')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=Cardholder.class_id,
+            entity_id=entity_id,
+            attributes_mask=Cardholder.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_set_pptp_ethernet_uni(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_set_AdminState')
+    data = dict(
+        administrative_state=0
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=PptpEthernetUni.class_id,
+            entity_id=entity_id,
+            attributes_mask=PptpEthernetUni.mask_for(*data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_get_IpHostConfigData(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
+    omci_cc.log.debug('send_get_IpHostConfigData')
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciGet.message_id,
+        omci_message=OmciGet(
+            entity_class=IpHostConfigData.class_id,
+            entity_id=entity_id,
+            attributes_mask=IpHostConfigData.mask_for(attribute)
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_create_extended_vlan_tagging_operation_configuration_data(omci_cc,
+                                                                   entity_id,
+                                                                   assoc_type,
+                                                                   assoc_me,
+                                                                   timeout=DEFAULT_OMCI_TIMEOUT):
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciCreate.message_id,
+        omci_message=OmciCreate(
+            entity_class=
+            ExtendedVlanTaggingOperationConfigurationData.class_id,
+            entity_id=entity_id,
+            data=dict(
+                association_type=assoc_type,
+                associated_me_pointer=assoc_me
+            )
+        )
+    )
+    return omci_cc.send(frame, timeout)
+
+
+def send_set_extended_vlan_tagging_operation_tpid_configuration_data(omci_cc,
+                                                                     entity_id,
+                                                                     input_tpid,
+                                                                     output_tpid,
+                                                                     timeout=DEFAULT_OMCI_TIMEOUT):
+    data = dict(
+        input_tpid=input_tpid,
+        output_tpid=output_tpid,
+        downstream_mode=0,  # inverse of upstream
+    )
+    frame = OmciFrame(
+        transaction_id=omci_cc._get_tx_tid(),
+        message_type=OmciSet.message_id,
+        omci_message=OmciSet(
+            entity_class=
+            ExtendedVlanTaggingOperationConfigurationData.class_id,
+            entity_id=entity_id,
+            attributes_mask=
+            ExtendedVlanTaggingOperationConfigurationData.mask_for(
+                *data.keys()),
+            data=data
+        )
+    )
+    return omci_cc.send(frame, timeout)
diff --git a/voltha/adapters/adtran_onu/omci/me_frame.py b/voltha/adapters/adtran_onu/omci/me_frame.py
new file mode 100644
index 0000000..bac4c33
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/me_frame.py
@@ -0,0 +1,212 @@
+#
+# Copyright 2017 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.
+#
+"""
+OMCI Managed Entity Message support base class
+"""
+from voltha.extensions.omci.omci import *
+
+# abbreviations
+OP = EntityOperations
+AA = AttributeAccess
+
+
+class MEFrame(object):
+    """Base class to help simplify Frame Creation"""
+    def __init__(self, entity_class, entity_id, data):
+        assert issubclass(entity_class, EntityClass), \
+            "'{}' must be a subclass of MEFrame".format(entity_class)
+        self.check_type(entity_id, int)
+
+        if not 0 <= entity_id <= 0xFFFF:
+            raise ValueError('entity_id should be 0..65535')
+
+        self._class = entity_class
+        self._entity_id = entity_id
+        self.data = data
+
+    def __str__(self):
+        return '{}: Entity_ID: {}, Data: {}'.\
+            format(self.entity_class_name, self._entity_id, self.data)
+
+    @property
+    def entity_class(self):
+        """
+        The Entity Class for this ME
+        :return: (EntityClass) Entity class
+        """
+        return self._class
+
+    @property
+    def entity_class_name(self):
+        return self._class.__class__.__name__
+
+    @property
+    def entity_id(self):
+        """
+        The Entity ID for this ME frame
+        :return: (int) Entity ID (0..0xFFFF)
+        """
+        return self._entity_id
+
+    @staticmethod
+    def check_type(param, types):
+        if not isinstance(param, types):
+            raise TypeError("param '{}' should be a {}".format(param, types))
+
+    def _check_operation(self, operation):
+        allowed = self.entity_class.mandatory_operations | self.entity_class.optional_operations
+        assert operation in allowed, "{} not allowed for '{}'".format(str(operation).split('.')[1],
+                                                                      self.entity_class_name)
+
+    def _check_attributes(self, attributes, access):
+        for attribute in attributes:
+            index = self.entity_class.attribute_name_to_index_map.get(attribute)
+            # Bad attribute name (invalid or spelling error)?
+            assert index is not None, "Attribute '{}' is not valid for '{}'".format(attribute,
+                                                                                    self.entity_class_name)
+            # Invalid access?
+            # TODO: Add read-only access to EntityClass _access set. Currently it is protected
+            assert access in self.entity_class.attributes[index]._access,\
+                "No '{} access for attribute '{}".format(str(access).split('.')[1], attribute)
+
+    @staticmethod
+    def _attr_to_data(attributes):
+        """
+        Convert an object into the 'data' set or dictionary for get/set/create/delete
+        requests.
+
+        This method takes a 'string', 'list', or 'set' for get requests and
+        converts it to a 'set' of attributes.
+
+        For create/set requests a dictionary of attribute/value pairs is required
+
+        :param attributes: (basestring, list, set, dict) attributes. For gets
+                           a string, list, set, or dict can be provided. For create/set
+                           operations, a dictionary should be provided. For delete
+                           the attributes may be None since they are ignored.
+
+        :return: (set, dict) set for get/deletes, dict for create/set
+        """
+        if isinstance(attributes, basestring):
+            # data = [str(attributes)]
+            data = set()
+            data.add(str(attributes))
+
+        elif isinstance(attributes, list):
+            assert all(isinstance(attr, basestring) for attr in attributes),\
+                'attribute list must be strings'
+            data = {str(attr) for attr in attributes}
+            assert len(data) == len(attributes), 'Attributes were not unique'
+
+        elif isinstance(attributes, set):
+            assert all(isinstance(attr, basestring) for attr in attributes),\
+                'attribute set must be strings'
+            data = {str(attr) for attr in attributes}
+
+        elif isinstance(attributes, (dict, type(None))):
+            data = attributes
+
+        else:
+            raise TypeError("Unsupported attributes type '{}'".format(type(attributes)))
+
+        return data
+
+    def create(self):
+        """
+        Create a Create request frame for this ME
+        :return: (OmciFrame) OMCI Frame
+        """
+        assert hasattr(self.entity_class, 'class_id'), 'class_id required for Create actions'
+        assert hasattr(self, 'entity_id'), 'entity_id required for Create actions'
+        assert hasattr(self, 'data'), 'data required for Create actions'
+
+        data = getattr(self, 'data')
+        MEFrame.check_type(data, dict)
+        assert len(data) > 0, 'No attributes supplied'
+
+        self._check_operation(OP.Create)
+        self._check_attributes(data.keys(), AA.Writable)
+
+        return OmciFrame(
+            transaction_id=None,
+            message_type=OmciCreate.message_id,
+            omci_message=OmciCreate(
+                entity_class=getattr(self.entity_class, 'class_id'),
+                entity_id=getattr(self, 'entity_id'),
+                data=data
+            ))
+
+    def delete(self):
+        """
+        Create a Delete request frame for this ME
+        :return: (OmciFrame) OMCI Frame
+        """
+        self._check_operation(OP.Delete)
+
+        return OmciFrame(
+            transaction_id=None,
+            message_type=OmciGet.message_id,
+            omci_message=OmciGet(
+                entity_class=getattr(self.entity_class, 'class_id'),
+                entity_id=getattr(self, 'entity_id')
+            ))
+
+    def set(self):
+        """
+        Create a Set request frame for this ME
+        :return: (OmciFrame) OMCI Frame
+        """
+        assert hasattr(self, 'data'), 'data required for Set actions'
+        data = getattr(self, 'data')
+        MEFrame.check_type(data, dict)
+        assert len(data) > 0, 'No attributes supplied'
+
+        self._check_operation(OP.Set)
+        self._check_attributes(data.keys(), AA.Writable)
+
+        return OmciFrame(
+            transaction_id=None,
+            message_type=OmciSet.message_id,
+            omci_message=OmciSet(
+                entity_class=getattr(self.entity_class, 'class_id'),
+                entity_id=getattr(self, 'entity_id'),
+                attributes_mask=self.entity_class.mask_for(*data.keys()),
+                data=data
+            ))
+
+    def get(self):
+        """
+        Create a Get request frame for this ME
+        :return: (OmciFrame) OMCI Frame
+        """
+        assert hasattr(self, 'data'), 'data required for Get actions'
+        data = getattr(self, 'data')
+        MEFrame.check_type(data, (list, set, dict))
+        assert len(data) > 0, 'No attributes supplied'
+
+        mask_set = data.keys() if isinstance(data, dict) else data
+
+        self._check_operation(OP.Get)
+        self._check_attributes(mask_set, AA.Readable)
+
+        return OmciFrame(
+            transaction_id=None,
+            message_type=OmciGet.message_id,
+            omci_message=OmciGet(
+                entity_class=getattr(self.entity_class, 'class_id'),
+                entity_id=getattr(self, 'entity_id'),
+                attributes_mask=self.entity_class.mask_for(*mask_set)
+            ))
diff --git a/voltha/adapters/adtran_onu/omci/omci_cc.py b/voltha/adapters/adtran_onu/omci/omci_cc.py
index 4e08a61..9ee5a64 100644
--- a/voltha/adapters/adtran_onu/omci/omci_cc.py
+++ b/voltha/adapters/adtran_onu/omci/omci_cc.py
@@ -21,14 +21,18 @@
 import sys
 import arrow
 from twisted.internet import reactor, defer
-from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue, TimeoutError, CancelledError, failure, fail
+from twisted.internet.defer import DeferredQueue, TimeoutError, CancelledError, failure, fail
 from voltha.protos import third_party
 from common.frameio.frameio import hexify
 from voltha.extensions.omci.omci import *
+from omci_entities import add_onu_me_entities
 
 _ = third_party
 
-_MAX_INCOMING_OMCI_MESSAGES = 256
+_MAX_INCOMING_ALARM_MESSAGES = 256
+_MAX_INCOMING_AVC_MESSAGES = 256
+_MAX_INCOMING_TEST_RESULT_MESSAGES = 64
+
 DEFAULT_OMCI_TIMEOUT = 3            # Seconds
 MAX_OMCI_REQUEST_AGE = 60           # Seconds
 MAX_OMCI_TX_ID = 0xFFFF             # 2 Octets max
@@ -39,56 +43,70 @@
 OP = EntityOperations
 
 
-class OMCISupport(object):
-    """ Handle OMCI Specifics for Adtran ONUs"""
+class OMCI_CC(object):
+    """ Handle OMCI Communication Channel specifics for Adtran ONUs"""
 
-    def __init__(self, handler, adapter, device_id):
+    def __init__(self, adapter_agent, device_id,
+                 custom_me_entries=None,
+                 alarm_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
+                 avc_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
+                 test_results_queue_limit=_MAX_INCOMING_TEST_RESULT_MESSAGES):
+
         self.log = structlog.get_logger(device_id=device_id)
-        self._handler = handler
-        self._adapter = adapter
+        self._adapter_agent = adapter_agent
         self._device_id = device_id
         self._proxy_address = None
         self._tx_tid = 1
-        self._deferred = None         # TODO: Remove later if never used
         self._enabled = False
         self._requests = dict()       # Tx ID -> (timestamp, deferred, tx_frame, timeout)
-        self._onu_messages = DeferredQueue(size=_MAX_INCOMING_OMCI_MESSAGES)
+        self._alarm_queue = DeferredQueue(size=alarm_queue_limit)
+        self._avc_queue = DeferredQueue(size=avc_queue_limit)
+        self._test_results_queue = DeferredQueue(size=test_results_queue_limit)
 
         # Statistics
         self._tx_frames = 0
         self._rx_frames = 0
+        self._rx_unknown_tid = 0      # Rx OMCI with no Tx TID match
         self._rx_onu_frames = 0       # Autonomously generated ONU frames
+        self._rx_alarm_overflow = 0   # Autonomously generated ONU alarms rx overflow
+        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._tx_errors = 0           # Exceptions during tx request
-        self._consecutive_errors = 0  # Rx & Tx errors in a row, good rx resets this to 0
+        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
         self._reply_max = 0           # Longest successful tx -> rx
         self._reply_sum = 0.0         # Total seconds for successful tx->rx (float for average)
 
+        # If a list of custom ME Entities classes were provided, insert them into
+        # main class_id to entity map.
+        # TODO: If this class becomes hidden from the ONU DA, move this to the OMCI State Machine runner
+
+        if custom_me_entries is not None:
+            add_onu_me_entities(custom_me_entries)
+
     def __str__(self):
         return "OMCISupport: {}".format(self._device_id)
 
-    def _cancel_deferred(self):
-        d, self._deferred = self._deferred, None
-        try:
-            if d is not None and not d.called:
-                d.cancel()
-        except:
-            pass
-
     @property
     def enabled(self):
         return self._enabled
 
     @enabled.setter
     def enabled(self, value):
+        """
+        Enable/disable the OMCI Communications Channel
+
+        :param value: (boolean) True to enable, False to disable
+        """
         assert isinstance(value, bool), 'enabled is a boolean'
+
         if self._enabled != value:
             self._enabled = value
             if self._enabled:
-                self.start()
+                self._start()
             else:
-                self.stop()
+                self._stop()
 
     @property
     def tx_frames(self):
@@ -99,10 +117,26 @@
         return self._rx_frames
 
     @property
+    def rx_unknown_tid(self):
+        return self._rx_unknown_tid         # Tx TID not found
+
+    @property
     def rx_onu_frames(self):
         return self._rx_onu_frames
 
     @property
+    def rx_alarm_overflow(self):
+        return self._rx_alarm_overflow      # Alarm ONU autonomous overflows
+
+    @property
+    def rx_avc_overflow(self):
+        return self._rx_avc_overflow        # Attribute Value change autonomous overflows
+
+    @property
+    def rx_onu_discards(self):
+        return self._rx_onu_discards        # Attribute Value change autonomous overflows
+
+    @property
     def rx_timeouts(self):
         return self._rx_timeouts
 
@@ -128,47 +162,105 @@
         return int(round(avg * 1000.0))     # Milliseconds
 
     @property
-    def get_onu_autonomous_message(self):
+    def get_alarm_message(self):
         """
-        Attempt to retrieve and remove an object from the ONU autonomous
-        message queue.
+        Attempt to retrieve and remove an ONU Alarm Message from the ONU
+        autonomous message queue.
 
-        :return: a Deferred which fires with the next OmciFrame available in
+        :return: a Deferred which fires with the next Alarm Frame available in
                  the queue.
         """
-        return self._onu_messages.get()
+        return self._alarm_queue.get()
 
-    def start(self):
+    @property
+    def get_avc_message(self):
+        """
+        Attempt to retrieve and remove an ONU Attribute Value Change (AVC)
+        Message from the ONU autonomous message queue.
+
+        :return: a Deferred which fires with the next AVC Frame available in
+                 the queue.
+        """
+        return self._avc_queue.get()
+
+    @property
+    def get_test_results(self):
+        """
+        Attempt to retrieve and remove an ONU Test Results Message from the
+        ONU autonomous message queue.
+
+        :return: a Deferred which fires with the next Test Results Frame is
+                 available in the queue.
+        """
+        return self._test_results_queue.get()
+
+    def _start(self):
+        """
+        Start the OMCI Communications Channel
+        """
         assert self._enabled, 'Start should only be called if enabled'
         #
-        # TODO: Perform common startup tasks here
+        # TODO: Perform any other common startup tasks here
         #
-        self._cancel_deferred()
         self.flush()
 
-        device = self._adapter.adapter_agent.get_device(self._device_id)
+        device = self._adapter_agent.get_device(self._device_id)
         self._proxy_address = device.proxy_address
 
-    def stop(self):
+    def _stop(self):
+        """
+        Stop the OMCI Communications Channel
+        """
         assert not self._enabled, 'Stop should only be called if disabled'
         #
         # TODO: Perform common shutdown tasks here
         #
-        self._cancel_deferred()
         self.flush()
         self._proxy_address = None
-        pass
+
+        # TODO: What is best way to clean up any outstanding futures for these queues
+        self._alarm_queue = None
+        self._avc_queue = None
+        self._test_results_queue = None
 
     def _receive_onu_message(self, rx_frame):
         """ Autonomously generated ONU frame Rx handler"""
-        self.log.debug('rx-onu-frame', frame=rx_frame)
+        from twisted.internet.defer import QueueOverflow
+
+        self.log.debug('rx-onu-frame', frame_type=type(rx_frame),
+                       frame=hexify(str(rx_frame)))
+
+        # TODO: Signal, via defer if Alarm Overflow or just an event?
+        msg_type = rx_frame.fields['message_type']
 
         self._rx_onu_frames += 1
-        self._onu_messages.put((rx_frame, arrow.utcnow().float_timestamp))
+
+        if msg_type == EntityOperations.AlarmNotification:
+            try:
+                self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))
+
+            except QueueOverflow:
+                self._rx_alarm_overflow += 1
+                self.log.warn('onu-rx-alarm-overflow', cnt=self._rx_alarm_overflow)
+
+        elif msg_type == EntityOperations.AttributeValueChange:
+            try:
+                self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))
+
+            except QueueOverflow:
+                self._rx_avc_overflow += 1
+                self.log.warn('onu-rx-avc-overflow', cnt=self._rx_avc_overflow)
+        else:
+            # TODO: Need to add test results message support
+
+            self.log.warn('onu-unsupported-autonomous-message', type=msg_type)
+            self._rx_onu_discards += 1
 
     def receive_message(self, msg):
         """
-        Receive and OMCI message from the proxy channel to the OLT
+        Receive and OMCI message from the proxy channel to the OLT.
+
+        Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
         """
         if self.enabled:
             try:
@@ -180,14 +272,19 @@
                     rx_tid = rx_frame.fields['transaction_id']
 
                     if rx_tid == 0:
-
                         return self._receive_onu_message(rx_frame)
 
                     self._rx_frames += 1
                     self._consecutive_errors = 0
 
+                except KeyError as e:
+                    # TODO: Investigate.  Probably an unknown/unsupported ME
+                    # 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
+
                 except Exception as e:
-                    self.log.exception('frame-decode', e=e)
+                    self.log.exception('frame-decode', msg=hexlify(msg), e=e)
                     return
 
                 try:
@@ -206,11 +303,13 @@
                     # TODO: Could also validate response type based on request action
 
                 except KeyError:
-                    self.log.warn('message-missing', rx_id=rx_tid)
+                    # Possible late Rx on a message that timed-out
+                    self._rx_unknown_tid += 1
+                    self.log.warn('tx-message-missing', rx_id=rx_tid, msg=hexlify(msg))
                     return
 
                 except Exception as e:
-                    self.log.exception('frame-decode', e=e)
+                    self.log.exception('frame-match', msg=hexlify(msg), e=e)
                     if d is not None:
                         return d.errback(failure.Failure(e))
                     return
@@ -233,14 +332,17 @@
         self._requests = dict()
 
         if max_age == 0:
-            # Flush autonomous messages
-            while self._onu_messages.pending:
-                _ = yield self._onu_messages.get()
+            # Flush autonomous messages (Alarms & AVCs)
+            while self._alarm_queue.pending:
+                _ = yield self._alarm_queue.get()
+
+            while self._avc_queue.pending:
+                _ = yield self._avc_queue.get()
 
     def _get_tx_tid(self):
         """
-        Get the next Transaction ID for a tx.  Note 0 is reserved
-        for autonomously generated message from an ONU
+        Get the next Transaction ID for a tx.  Note TID=0 is reserved
+        for autonomously generated messages from an ONU
 
         :return: (int) TID
         """
@@ -251,10 +353,15 @@
         return tx_tid
 
     def _request_failure(self, value, tx_tid):
+        """
+        Handle a transmit failure and/or Rx timeout
+
+        :param value: (Failure) Twisted failure
+        :param tx_tid: (int) Associated Tx TID
+        """
         if tx_tid in self._requests:
             (_, _, _, timeout) = self._requests.pop(tx_tid)
         else:
-            # tx_msg = None
             timeout = 0
 
         if isinstance(value, failure.Failure):
@@ -266,7 +373,44 @@
 
         return value
 
+    def _request_success(self, rx_frame):
+        """
+        Handle transmit success (a matching Rx was received)
+
+        :param rx_frame: (OmciFrame) OMCI response frame with matching TID
+        :return: (OmciFrame) OMCI response frame with matching TID
+        """
+        #
+        # TODO: Here we could update the MIB database if we did a set/create/delete
+        #       or perhaps a verify if a GET.  Also could increment mib counter
+        #
+        try:
+            if isinstance(rx_frame.omci_message, OmciGetResponse):
+                pass    # TODO: Implement MIB check or remove
+
+            elif isinstance(rx_frame.omci_message, OmciSetResponse):
+                pass    # TODO: Implement MIB update
+
+            elif isinstance(rx_frame.omci_message, OmciCreateResponse):
+                pass    # TODO: Implement MIB update
+
+            elif isinstance(rx_frame.omci_message, OmciDeleteResponse):
+                pass    # TODO: Implement MIB update
+
+        except Exception as e:
+            self.log.exception('omci-message', e=e)
+
+        return rx_frame
+
     def send(self, frame, timeout=DEFAULT_OMCI_TIMEOUT):
+        """
+        Send the OMCI Frame to the ONU via the proxy_channel
+
+        :param frame: (OMCIFrame) Message to send
+        :param timeout: (int) Rx Timeout. 0=Forever
+        :return: (deferred) A deferred that fires when the response frame is received
+                            or if an error/timeout occurs
+        """
         self.flush(max_age=MAX_OMCI_REQUEST_AGE)
 
         assert timeout <= MAX_OMCI_REQUEST_AGE, \
@@ -284,18 +428,19 @@
                 tx_tid = self._get_tx_tid()
                 frame.fields['transaction_id'] = tx_tid
 
-            assert tx_tid not in self._requests, 'TX TID is already exists'
+            assert tx_tid not in self._requests, 'TX TID {} is already exists'.format(tx_tid)
             assert tx_tid >= 0, 'Invalid Tx TID: {}'.format(tx_tid)
 
             ts = arrow.utcnow().float_timestamp
             d = defer.Deferred()
 
-            self._adapter.adapter_agent.send_proxied_message(self._proxy_address,
-                                                             hexify(str(frame)))
+            self._adapter_agent.send_proxied_message(self._proxy_address,
+                                                     hexify(str(frame)))
             self._tx_frames += 1
             self._requests[tx_tid] = (ts, d, frame, timeout)
 
-            d.addErrback(self._request_failure, tx_tid)
+            d.addCallbacks(self._request_success, self._request_failure,
+                           errbackArgs=(tx_tid,))
 
             if timeout > 0:
                 d.addTimeout(timeout, reactor)
@@ -308,21 +453,12 @@
 
         return d
 
-    def send_get_OntG(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
-        self.log.debug('send_get_OntG')
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciGet.message_id,
-            omci_message=OmciGet(
-                entity_class=OntG.class_id,
-                entity_id=entity_id,
-                attributes_mask=OntG.mask_for(attribute)
-            )
-        )
-        return self.send(frame, timeout)
+    ###################################################################################
+    # TODO: The following three need to be ported to the new OMCI_CC and ME_Frame style
+    #       or perhaps made into static methods in the base ME_Frame class.
 
     def send_mib_reset(self, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
-        self.log.debug('send_mib_reset')
+        self.log.debug('send-mib-reset')
         frame = OmciFrame(
             transaction_id=self._get_tx_tid(),
             message_type=OmciMibReset.message_id,
@@ -333,357 +469,27 @@
         )
         return self.send(frame, timeout)
 
-    def send_set_tcont(self, entity_id, alloc_id, timeout=DEFAULT_OMCI_TIMEOUT):
-        data = dict(
-            alloc_id=alloc_id
-        )
+    def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT):
+        self.log.debug('send-mib-upload')
         frame = OmciFrame(
             transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=Tcont.class_id,
-                entity_id=entity_id,
-                attributes_mask=Tcont.mask_for(*data.keys()),
-                data=data
+            message_type=OmciMibUpload.message_id,
+            omci_message=OmciMibUpload(
+                entity_class=OntData.class_id,
+                entity_id=0
             )
         )
         return self.send(frame, timeout)
 
-    def send_create_gem_port_network_ctp(self, entity_id, port_id,
-                                         tcont_id, direction, tm,
-                                         timeout=DEFAULT_OMCI_TIMEOUT):
-
-        _directions = {"upstream": 1, "downstream": 2, "bi-directional": 3}
-
-        if _directions.has_key(direction):
-            _direction = _directions[direction]
-        else:
-            self.log.error('invalid-gem-port-direction', direction=direction)
-            raise ValueError('Invalid GEM port direction: {_dir}'.format(_dir=direction))
-
+    def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT):
+        self.log.debug('send-mib-upload-next')
         frame = OmciFrame(
             transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=GemPortNetworkCtp.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    port_id=port_id,
-                    tcont_pointer=tcont_id,
-                    direction=_direction,
-                    traffic_management_pointer_upstream=tm
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_set_8021p_mapper_service_profile(self, entity_id,
-                                              interwork_tp_id,
-                                              timeout=DEFAULT_OMCI_TIMEOUT):
-        data = dict(
-            interwork_tp_pointer_for_p_bit_priority_0=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_1=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_2=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_3=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_4=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_5=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_6=interwork_tp_id,
-            interwork_tp_pointer_for_p_bit_priority_7=interwork_tp_id
-        )
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=Ieee8021pMapperServiceProfile.class_id,
-                entity_id=entity_id,
-                attributes_mask=Ieee8021pMapperServiceProfile.mask_for(
-                    *data.keys()),
-                data=data
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_8021p_mapper_service_profile(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=Ieee8021pMapperServiceProfile.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    tp_pointer=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_0=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_1=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_2=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_3=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_4=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_5=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_6=OmciNullPointer,
-                    interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_mac_bridge_service_profile(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=MacBridgeServiceProfile.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    spanning_tree_ind=False,
-                    learning_ind=True,
-                    priority=0x8000,
-                    max_age=20 * 256,
-                    hello_time=2 * 256,
-                    forward_delay=15 * 256,
-                    unknown_mac_address_discard=True
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_gal_ethernet_profile(self, entity_id, max_gem_payload_size,
-                                         timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=GalEthernetProfile.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    max_gem_payload_size=max_gem_payload_size
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_gem_inteworking_tp(self, entity_id, gem_port_net_ctp_id,
-                                       service_profile_id, timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=GemInterworkingTp.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    gem_port_network_ctp_pointer=gem_port_net_ctp_id,
-                    interworking_option=5,
-                    service_profile_pointer=service_profile_id,
-                    interworking_tp_pointer=0x0,
-                    gal_profile_pointer=0x1
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_mac_bridge_port_configuration_data(self, entity_id, bridge_id,
-                                                       port_id, tp_type, tp_id,
-                                                       timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=MacBridgePortConfigurationData.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    bridge_id_pointer=bridge_id,
-                    port_num=port_id,
-                    tp_type=tp_type,
-                    tp_pointer=tp_id
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_vlan_tagging_filter_data(self, entity_id, vlan_id,
-                                             timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=VlanTaggingFilterData.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    vlan_filter_0=vlan_id,
-                    forward_operation=0x10,
-                    number_of_entries=1
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    # def send_get_device_info(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
-    #     # TODO: Can this be combined with send_get_circuit_pack above?
-    #     frame = OmciFrame(
-    #         transaction_id=self._get_tx_tid(),
-    #         message_type=OmciGet.message_id,
-    #         omci_message=OmciGet(
-    #             entity_class=CircuitPack.class_id,
-    #             entity_id=entity_id,
-    #             attributes_mask=CircuitPack.mask_for(attribute)
-    #         )
-    #     )
-    #     return self.send(frame, timeout)
-
-    def send_set_adminState(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
-        self.log.debug('send_set_AdminState')
-        data = dict(
-            administrative_state=0
-        )
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=PptpEthernetUni.class_id,
-                entity_id=entity_id,
-                attributes_mask=PptpEthernetUni.mask_for(*data.keys()),
-                data=data
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_get_SoftwareImage(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
-        self.log.debug('send_get_SoftwareImage')
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciGet.message_id,
-            omci_message=OmciGet(
-                entity_class=SoftwareImage.class_id,
-                entity_id=entity_id,
-                attributes_mask=SoftwareImage.mask_for(attribute)
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_create_extended_vlan_tagging_operation_configuration_data(self,
-                                                                       entity_id,
-                                                                       assoc_type,
-                                                                       assoc_me,
-                                                                       timeout=DEFAULT_OMCI_TIMEOUT):
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=
-                ExtendedVlanTaggingOperationConfigurationData.class_id,
-                entity_id=entity_id,
-                data=dict(
-                    association_type=assoc_type,
-                    associated_me_pointer=assoc_me
-                )
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_set_extended_vlan_tagging_operation_tpid_configuration_data(self,
-                                                                         entity_id,
-                                                                         input_tpid,
-                                                                         output_tpid,
-                                                                         timeout=DEFAULT_OMCI_TIMEOUT):
-        data = dict(
-            input_tpid=input_tpid,
-            output_tpid=output_tpid,
-            downstream_mode=0,  # inverse of upstream
-        )
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=
-                ExtendedVlanTaggingOperationConfigurationData.class_id,
-                entity_id=entity_id,
-                attributes_mask=
-                ExtendedVlanTaggingOperationConfigurationData.mask_for(
-                    *data.keys()),
-                data=data
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(self,
-                                                                                  entity_id,
-                                                                                  filter_inner_vid,
-                                                                                  treatment_inner_vid,
-                                                                                  timeout=DEFAULT_OMCI_TIMEOUT):
-        data = dict(
-            received_frame_vlan_tagging_operation_table=
-            VlanTaggingOperation(
-                filter_outer_priority=15,
-                filter_outer_vid=4096,
-                filter_outer_tpid_de=0,
-
-                filter_inner_priority=15,
-                filter_inner_vid=filter_inner_vid,
-                filter_inner_tpid_de=0,
-                filter_ether_type=0,
-
-                treatment_tags_to_remove=0,
-                treatment_outer_priority=15,
-                treatment_outer_vid=0,
-                treatment_outer_tpid_de=0,
-
-                treatment_inner_priority=0,
-                treatment_inner_vid=treatment_inner_vid,
-                treatment_inner_tpid_de=4
-            )
-        )
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=
-                ExtendedVlanTaggingOperationConfigurationData.class_id,
-                entity_id=entity_id,
-                attributes_mask=
-                ExtendedVlanTaggingOperationConfigurationData.mask_for(
-                    *data.keys()),
-                data=data
-            )
-        )
-        return self.send(frame, timeout)
-
-    def send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(self,
-                                                                                    entity_id,
-                                                                                    filter_inner_priority,
-                                                                                    filter_inner_vid,
-                                                                                    filter_inner_tpid_de,
-                                                                                    treatment_tags_to_remove,
-                                                                                    treatment_inner_priority,
-                                                                                    treatment_inner_vid,
-                                                                                    timeout=DEFAULT_OMCI_TIMEOUT):
-        data = dict(
-            received_frame_vlan_tagging_operation_table=
-            VlanTaggingOperation(
-                filter_outer_priority=15,
-                filter_outer_vid=4096,
-                filter_outer_tpid_de=0,
-                filter_inner_priority=filter_inner_priority,
-                filter_inner_vid=filter_inner_vid,
-                filter_inner_tpid_de=filter_inner_tpid_de,
-                filter_ether_type=0,
-                treatment_tags_to_remove=treatment_tags_to_remove,
-                treatment_outer_priority=15,
-                treatment_outer_vid=0,
-                treatment_outer_tpid_de=0,
-                treatment_inner_priority=treatment_inner_priority,
-                treatment_inner_vid=treatment_inner_vid,
-                treatment_inner_tpid_de=4
-            )
-        )
-        frame = OmciFrame(
-            transaction_id=self._get_tx_tid(),
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=
-                ExtendedVlanTaggingOperationConfigurationData.class_id,
-                entity_id=entity_id,
-                attributes_mask=
-                ExtendedVlanTaggingOperationConfigurationData.mask_for(
-                    *data.keys()),
-                data=data
+            message_type=OmciMibUploadNext.message_id,
+            omci_message=OmciMibUploadNext(
+                entity_class=OntData.class_id,
+                entity_id=0,
+                command_sequence_number=seq_no
             )
         )
         return self.send(frame, timeout)
diff --git a/voltha/adapters/adtran_onu/omci/omci_entities.py b/voltha/adapters/adtran_onu/omci/omci_entities.py
new file mode 100644
index 0000000..f38f55b
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/omci_entities.py
@@ -0,0 +1,110 @@
+# Copyright 2017-present Adtran, Inc.
+#
+# 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.
+#
+""" Adtran vendor-specific OMCI Entities"""
+
+import inspect
+
+import sys
+from scapy.fields import ByteField, ShortField
+from scapy.fields import IntField, StrFixedLenField
+
+
+from voltha.extensions.omci.omci_entities import EntityClassAttribute, \
+    AttributeAccess, EntityOperations, EntityClass
+
+# abbreviations
+ECA = EntityClassAttribute
+AA = AttributeAccess
+OP = EntityOperations
+
+
+class OntSystem(EntityClass):
+    class_id = 65300
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get}
+
+
+class VerizonOpenOMCI(EntityClass):
+    class_id = 65400
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get}
+
+
+class TwdmSystemProfile(EntityClass):
+    class_id = 65401
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get}
+
+
+class TwdmChannel(EntityClass):
+    class_id = 65402
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get}
+
+
+class WatchdogConfigData(EntityClass):
+    class_id = 65403
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get}
+
+
+class FlexibleConfigurationStatusPortal(EntityClass):
+    class_id = 65420
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Get, OP.Set, OP.Create, OP.Delete}
+
+
+class ONU3G(EntityClass):
+    class_id = 65422
+    attributes = [
+        # TODO: Fix access for all attributes below
+        ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+    ]
+    mandatory_operations = {OP.Set, OP.Get, OP.Create, OP.Delete}
+
+
+#################################################################################
+# entity class lookup table from entity_class values
+_onu_entity_classes_name_map = dict(
+    inspect.getmembers(sys.modules[__name__],
+    lambda o: inspect.isclass(o) and
+              issubclass(o, EntityClass) and
+              o is not EntityClass)
+)
+
+onu_custom_entity_classes = [c for c in _onu_entity_classes_name_map.itervalues()]
+
+
+def add_onu_me_entities(new_me_classes):
+    from voltha.extensions.omci.omci_entities import entity_classes, entity_id_to_class_map
+
+    for entity_class in new_me_classes:
+        assert entity_class.class_id not in entity_id_to_class_map, \
+            "Class ID '{}' already exists in the class map".format(entity_class.class_id)
+        entity_id_to_class_map[entity_class.class_id] = entity_class
+
+    entity_classes.extend(new_me_classes)
diff --git a/voltha/adapters/adtran_onu/omci/omci_me.py b/voltha/adapters/adtran_onu/omci/omci_me.py
index f51bbd8..c3716cf 100644
--- a/voltha/adapters/adtran_onu/omci/omci_me.py
+++ b/voltha/adapters/adtran_onu/omci/omci_me.py
@@ -14,186 +14,10 @@
 # limitations under the License.
 #
 """
-OMCI Message support
+OMCI Managed Entity Frame support
 """
 from voltha.extensions.omci.omci import *
-
-# abbreviations
-OP = EntityOperations
-
-
-class MEFrame(object):
-    """Base class to help simplify Frame Creation"""
-    def __init__(self, entity_class, entity_id, data):
-        assert issubclass(entity_class, EntityClass), \
-            "'{}' must be a subclass of MEFrame".format(entity_class)
-        self.check_type(entity_id, int)
-
-        if not 0 <= entity_id <= 0xFFFF:
-            raise ValueError('entity_id should be 0..65535')
-
-        self._class = entity_class
-        self._entity_id = entity_id
-        self.data = data
-
-        # TODO: add a required attributes check list for various operations
-        #       that each derive class can set as required. Then check these
-        #       in the appropriate operation method in this class and assert
-        #       if something is missing
-
-    def __str__(self):
-        return '{}: Entity_ID: {}, Data: {}'.\
-            format(type(self.entity_class), self._entity_id, self.data)
-
-    @staticmethod
-    def check_type(param, types):
-        if not isinstance(param, types):
-            raise TypeError("param '{}' should be a {}".format(param, types))
-
-    @property
-    def entity_class(self):
-        """
-        The Entity Class for this ME
-        :return: (EntityClass) Entity class
-        """
-        return self._class
-
-    @property
-    def entity_id(self):
-        """
-        The Entity ID for this ME frame
-        :return: (int) Entity ID (0..0xFFFF)
-        """
-        return self._entity_id
-
-    def create(self):
-        """
-        Create a Create request frame for this ME
-        :return: (OmciFrame) OMCI Frame
-        """
-        assert OP.Create in self.entity_class.mandatory_operations, \
-            "Set not allowed for '{}'".format(self.entity_class)
-        assert hasattr(self.entity_class, 'class_id'), 'class_id required for Create actions'
-        assert hasattr(self, 'entity_id'), 'entity_id required for Create actions'
-        assert hasattr(self, 'data'), 'data required for Create actions'
-
-        data = getattr(self, 'data')
-        MEFrame.check_type(data, dict)
-        assert len(data) > 0, 'No attributes supplied'
-
-        return OmciFrame(
-            transaction_id=None,
-            message_type=OmciCreate.message_id,
-            omci_message=OmciCreate(
-                entity_class=getattr(self.entity_class, 'class_id'),
-                entity_id=getattr(self, 'entity_id'),
-                data=data
-            ))
-
-    def delete(self):
-        """
-        Create a Delete request frame for this ME
-        :return: (OmciFrame) OMCI Frame
-        """
-        assert OP.Delete in self.entity_class.mandatory_operations, \
-            "Delete not allowed for '{}'".format(self.entity_class)
-
-        return OmciFrame(
-            transaction_id=None,
-            message_type=OmciGet.message_id,
-            omci_message=OmciGet(
-                entity_class=getattr(self.entity_class, 'class_id'),
-                entity_id=getattr(self, 'entity_id')
-            ))
-
-    def set(self):
-        """
-        Create a Set request frame for this ME
-        :return: (OmciFrame) OMCI Frame
-        """
-        assert OP.Set in self.entity_class.mandatory_operations, \
-            "Set not allowed for '{}'".format(self.entity_class)
-        assert hasattr(self, 'data'), 'data required for Set actions'
-
-        data = getattr(self, 'data')
-        MEFrame.check_type(data, dict)
-        assert len(data) > 0, 'No attributes supplied'
-
-        return OmciFrame(
-            transaction_id=None,
-            message_type=OmciSet.message_id,
-            omci_message=OmciSet(
-                entity_class=getattr(self.entity_class, 'class_id'),
-                entity_id=getattr(self, 'entity_id'),
-                attributes_mask=self.entity_class.mask_for(*data.keys()),
-                data=data
-            ))
-
-    def get(self):
-        """
-        Create a Get request frame for this ME
-        :return: (OmciFrame) OMCI Frame
-        """
-        assert OP.Get in self.entity_class.mandatory_operations, \
-            "Get not allowed for '{}'".format(self.entity_class)
-        assert hasattr(self, 'data'), 'data required for Get actions'
-
-        data = getattr(self, 'data')
-        MEFrame.check_type(data, (list, set, dict))
-        assert len(data) > 0, 'No attributes supplied'
-
-        mask_set = data.keys() if isinstance(data, dict) else data
-
-        return OmciFrame(
-            transaction_id=None,
-            message_type=OmciGet.message_id,
-            omci_message=OmciGet(
-                entity_class=getattr(self.entity_class, 'class_id'),
-                entity_id=getattr(self, 'entity_id'),
-                attributes_mask=self.entity_class.mask_for(*mask_set)
-            ))
-
-    @staticmethod
-    def _attr_to_data(attributes):
-        """
-        Convert an object into the 'data' set or dictionary for get/set/create/delete
-        requests.
-
-        This method takes a 'string', 'list', or 'set' for get requests and
-        converts it to a 'set' of attributes.
-
-        For create/set requests a dictionary of attribute/value pairs is required
-
-        :param attributes: (basestring, list, set, dict) attributes. For gets
-                           a string, list, set, or dict can be provided. For create/set
-                           operations, a dictionary should be provided. For delete
-                           the attributes may be None since they are ignored.
-
-        :return: (set, dict) set for get/deletes, dict for create/set
-        """
-        if isinstance(attributes, basestring):
-            # data = [str(attributes)]
-            data = set()
-            data.add(str(attributes))
-
-        elif isinstance(attributes, list):
-            assert all(isinstance(attr, basestring) for attr in attributes),\
-                'attribute list must be strings'
-            data = {str(attr) for attr in attributes}
-            assert len(data) == len(attributes), 'Attributes were not unique'
-
-        elif isinstance(attributes, set):
-            assert all(isinstance(attr, basestring) for attr in attributes),\
-                'attribute set must be strings'
-            data = {str(attr) for attr in attributes}
-
-        elif isinstance(attributes, (dict, type(None))):
-            data = attributes
-
-        else:
-            raise TypeError("Unsupported attributes type '{}'".format(type(attributes)))
-
-        return data
+from me_frame import MEFrame
 
 
 class CardholderFrame(MEFrame):
@@ -245,6 +69,30 @@
                                                MEFrame._attr_to_data(attributes))
 
 
+class ExtendedVlanTaggingOperationConfigurationDataFrame(MEFrame):
+    """
+    This managed entity organizes data associated with VLAN tagging. Regardless
+    of its point of attachment, the specified tagging operations refer to the
+     upstream direction.
+    """
+    def __init__(self, entity_id, attributes):
+        """
+        :param entity_id: (int) This attribute uniquely identifies each instance of
+                                this managed entity. Its value is the same as that
+                                of the cardholder managed entity containing this
+                                circuit pack instance. (0..65535)
+
+        :param attributes: (basestring, list, set, dict) attributes. For gets
+                           a string, list, or set can be provided. For create/set
+                           operations, a dictionary should be provided, for
+                           deletes None may be specified.
+        """
+        super(ExtendedVlanTaggingOperationConfigurationDataFrame,
+              self).__init__(ExtendedVlanTaggingOperationConfigurationData,
+                             entity_id,
+                             MEFrame._attr_to_data(attributes))
+
+
 class IpHostConfigDataFrame(MEFrame):
     """
     The IP host config data configures IPv4 based services offered on the ONU.
@@ -264,6 +112,33 @@
                                                     MEFrame._attr_to_data(attributes))
 
 
+class GalEthernetProfileFrame(MEFrame):
+    """
+    This managed entity organizes data that describe the GTC adaptation layer
+    processing functions of the ONU for Ethernet services.
+    """
+    def __init__(self, entity_id, max_gem_payload_size=None):
+        """
+        :param entity_id: (int) This attribute uniquely identifies each instance of
+                                this managed entity. (0..65535)
+
+        :param max_gem_payload_size: (int) This attribute defines the maximum payload
+                                     size generated in the associated GEM interworking
+                                     termination point managed entity. (0..65535
+        """
+        MEFrame.check_type(max_gem_payload_size, (int, type(None)))
+        if max_gem_payload_size is not None and not 0 <= max_gem_payload_size <= 0xFFFF:  # TODO: verify min/max
+            raise ValueError('max_gem_payload_size should be 0..0xFFFF')
+
+        data = None if max_gem_payload_size is None else\
+            {
+                'max_gem_payload_size': max_gem_payload_size
+            }
+        super(GalEthernetProfileFrame, self).__init__(GalEthernetProfile,
+                                                      entity_id,
+                                                      data)
+
+
 class GemInterworkingTpFrame(MEFrame):
     """
     An instance of this managed entity represents a point in the ONU where the
@@ -275,6 +150,7 @@
                  interworking_option=None,
                  service_profile_pointer=None,
                  interworking_tp_pointer=None,
+                 pptp_counter=None,
                  gal_profile_pointer=None,
                  attributes=None):
         """
@@ -319,11 +195,12 @@
                            deletes None may be specified..
         """
         # Validate
-        self.check_type(gem_port_network_ctp_pointer, [int, type(None)])
-        self.check_type(interworking_option, [int, type(None)])
-        self.check_type(service_profile_pointer, [int, type(None)])
-        self.check_type(interworking_tp_pointer, [int, type(None)])
-        self.check_type(gal_profile_pointer, [int, type(None)])
+        self.check_type(gem_port_network_ctp_pointer, (int, type(None)))
+        self.check_type(interworking_option, (int, type(None)))
+        self.check_type(service_profile_pointer, (int, type(None)))
+        self.check_type(interworking_tp_pointer,(int, type(None)))
+        self.check_type(pptp_counter,(int, type(None)))
+        self.check_type(gal_profile_pointer, (int, type(None)))
 
         if gem_port_network_ctp_pointer is not None and not 0 <= gem_port_network_ctp_pointer <= 0xFFFE:  # TODO: Verify max
             raise ValueError('gem_port_network_ctp_pointer should be 0..0xFFFE')
@@ -337,6 +214,9 @@
         if interworking_tp_pointer is not None and not 0 <= interworking_tp_pointer <= 0xFFFE:  # TODO: Verify max
             raise ValueError('interworking_tp_pointer should be 0..0xFFFE')
 
+        if pptp_counter is not None and not 0 <= pptp_counter <= 255:  # TODO: Verify max
+            raise ValueError('pptp_counter should be 0..255')
+
         if gal_profile_pointer is not None and not 0 <= gal_profile_pointer <= 0xFFFE:  # TODO: Verify max
             raise ValueError('gal_profile_pointer should be 0..0xFFFE')
 
@@ -351,19 +231,19 @@
             data = data or dict()
 
             if gem_port_network_ctp_pointer is not None:
-                data[gem_port_network_ctp_pointer] = gem_port_network_ctp_pointer
+                data['gem_port_network_ctp_pointer'] = gem_port_network_ctp_pointer
 
             if interworking_option is not None:
-                data[interworking_option] = interworking_option
+                data['interworking_option'] = interworking_option
 
             if service_profile_pointer is not None:
-                data[service_profile_pointer] = service_profile_pointer
+                data['service_profile_pointer'] = service_profile_pointer
 
             if interworking_tp_pointer is not None:
-                data[interworking_tp_pointer] = interworking_tp_pointer
+                data['interworking_tp_pointer'] = interworking_tp_pointer
 
             if gal_profile_pointer is not None:
-                data[gal_profile_pointer] = gal_profile_pointer
+                data['gal_profile_pointer'] = gal_profile_pointer
 
         super(GemInterworkingTpFrame, self).__init__(GemInterworkingTp,
                                                      entity_id,
@@ -408,10 +288,10 @@
         _directions = {"upstream": 1, "downstream": 2, "bi-directional": 3}
 
         # Validate
-        self.check_type(port_id, [int, type(None)])
-        self.check_type(tcont_id, [int, type(None)])
-        self.check_type(direction, [basestring, type(None)])
-        self.check_type(upstream_tm, [int, type(None)])
+        self.check_type(port_id, (int, type(None)))
+        self.check_type(tcont_id, (int, type(None)))
+        self.check_type(direction, (basestring, type(None)))
+        self.check_type(upstream_tm, (int, type(None)))
 
         if port_id is not None and not 0 <= port_id <= 0xFFFE:  # TODO: Verify max
             raise ValueError('port_id should be 0..0xFFFE')
@@ -433,13 +313,13 @@
             data = data or dict()
 
             if port_id is not None:
-                data[port_id] = port_id
+                data['port_id'] = port_id
             if tcont_id is not None:
-                data[tcont_id] = tcont_id
+                data['tcont_pointer'] = tcont_id
             if direction is not None:
-                data[direction] = direction
+                data['direction'] = _directions[str(direction).lower()]
             if upstream_tm is not None:
-                data[upstream_tm] = upstream_tm
+                data['traffic_management_pointer_upstream'] = upstream_tm
 
         super(GemPortNetworkCtpFrame, self).__init__(GemPortNetworkCtp,
                                                      entity_id,
@@ -478,13 +358,13 @@
                     interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
                 )
         else:
-            self.check_type(tp_pointer, [list, type(None)])
-            self.check_type(interwork_tp_pointers, [list, type(None)])
+            self.check_type(tp_pointer, (list, type(None)))
+            self.check_type(interwork_tp_pointers, (list, type(None)))
 
             data = dict()
 
             if tp_pointer is not None:
-                data[tp_pointer] = tp_pointer
+                data['tp_pointer'] = tp_pointer
 
             if interwork_tp_pointers is not None:
                 assert all(isinstance(tp, int) and 0 <= tp <= 0xFFFF
@@ -495,11 +375,11 @@
 
                 data = dict()
                 for pbit in range(0, len(interwork_tp_pointers)):
-                    data['interwork_tp_pointer_for_p_bit_priority_'.format(pbit)] = \
+                    data['interwork_tp_pointer_for_p_bit_priority_{}'.format(pbit)] = \
                         interwork_tp_pointers[pbit]
 
                 for pbit in range(len(interwork_tp_pointers), 7):
-                    data['interwork_tp_pointer_for_p_bit_priority_'.format(pbit)] = \
+                    data['interwork_tp_pointer_for_p_bit_priority_{}'.format(pbit)] = \
                         interwork_tp_pointers[len(interwork_tp_pointers) - 1]
 
         super(Ieee8021pMapperServiceProfileFrame, self).__init__(Ieee8021pMapperServiceProfile,
@@ -547,10 +427,10 @@
                            deletes None may be specified.
         """
         # Validate
-        self.check_type(bridge_id_pointer, [int, type(None)])
-        self.check_type(port_num, [int, type(None)])
-        self.check_type(tp_type, [int, type(None)])
-        self.check_type(tp_pointer, [int, type(None)])
+        self.check_type(bridge_id_pointer, (int, type(None)))
+        self.check_type(port_num, (int, type(None)))
+        self.check_type(tp_type, (int, type(None)))
+        self.check_type(tp_pointer, (int, type(None)))
 
         if bridge_id_pointer is not None and not 0 <= bridge_id_pointer <= 0xFFFE:  # TODO: Verify max
             raise ValueError('bridge_id_pointer should be 0..0xFFFE')
@@ -574,21 +454,42 @@
             data = data or dict()
 
             if bridge_id_pointer is not None:
-                data[bridge_id_pointer] = bridge_id_pointer
+                data['bridge_id_pointer'] = bridge_id_pointer
 
             if port_num is not None:
-                data[port_num] = port_num
+                data['port_num'] = port_num
 
             if tp_type is not None:
-                data[tp_type] = tp_type
+                data['tp_type'] = tp_type
 
             if tp_pointer is not None:
-                data[tp_pointer] = tp_pointer
+                data['tp_pointer'] = tp_pointer
 
         super(MacBridgePortConfigurationDataFrame, self).\
             __init__(MacBridgePortConfigurationData, entity_id, data)
 
 
+class MacBridgeServiceProfileFrame(MEFrame):
+    """
+    This managed entity models a MAC bridge in its entirety; any number
+    of ports may be associated with the bridge through pointers to the
+    MAC bridge service profile managed entity.
+    """
+    def __init__(self, entity_id, attributes=None):
+        """
+        :param entity_id: (int) This attribute uniquely identifies each instance of
+                                this managed entity. (0..65535)
+
+        :param attributes: (basestring, list, set, dict) attributes. For gets
+                           a string, list, or set can be provided. For create/set
+                           operations, a dictionary should be provided, for
+                           deletes None may be specified.
+        """
+        super(MacBridgeServiceProfileFrame, self).__init__(MacBridgeServiceProfile,
+                                                           entity_id,
+                                                           MEFrame._attr_to_data(attributes))
+
+
 class OntGFrame(MEFrame):
     """
     This managed entity represents the ONU as equipment.
@@ -625,7 +526,7 @@
     This managed entity represents the point at an Ethernet UNI where the physical path
     terminates and Ethernet physical level functions are performed.
     """
-    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. (0..65535)
@@ -672,8 +573,8 @@
                                 2 - WRR - Weighted round robin
         """
         # Validate
-        self.check_type(alloc_id, [int, type(None)])
-        self.check_type(policy, [int, type(None)])
+        self.check_type(alloc_id, (int, type(None)))
+        self.check_type(policy, (int, type(None)))
 
         if alloc_id is not None and not 0 <= alloc_id <= 0xFFF:
             raise ValueError('alloc_id should be 0..0xFFF')
@@ -687,10 +588,10 @@
             data = dict()
 
             if alloc_id is not None:
-                data[alloc_id] = alloc_id
+                data['alloc_id'] = alloc_id
 
             if policy is not None:
-                data[policy] = policy
+                data['policy'] = policy
 
         super(TcontFrame, self).__init__(Tcont, entity_id, data)
 
@@ -713,30 +614,32 @@
 
         """
         # Validate
-        self.check_type(vlan_tcis, [list, type(None)])
-        self.check_type(forward_operation, [int, type(None)])
+        self.check_type(vlan_tcis, (list, type(None)))
+        self.check_type(forward_operation, (int, type(None)))
 
         if forward_operation is not None and not 0 <= forward_operation <= 0x21:
             raise ValueError('forward_operation should be 0..0x21')
 
         if vlan_tcis is None and forward_operation is None:
             data = None
+
         else:
             data = dict()
 
             if vlan_tcis is not None:
                 assert all(isinstance(tci, int) and 0 <= tci <= 0xFFFF
                            for tci in vlan_tcis), "VLAN TCI's are 0..0xFFFF"
+                assert 1 <= len(vlan_tcis) <= 12, 'Number of VLAN TCI values is 1..12'
+
                 for index in range(0, len(vlan_tcis)):
                     data['vlan_filter_{}'.format(index)] = vlan_tcis[index]
+
                 data['number_of_entries'] = len(vlan_tcis)
 
             if forward_operation is not None:
-                data[forward_operation] = forward_operation
+                assert 0 <= forward_operation <= 0x21, 'forwarding_operation must be 0x00..0x21'
+                data['forward_operation'] = forward_operation
 
         super(VlanTaggingFilterDataFrame, self).__init__(VlanTaggingFilterData,
                                                          entity_id,
                                                          data)
-
-
-# TODO: Wednesday - Start with send_create_extended_vlan_tagging_operation_configuration_data
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index b0aecd1..c0fb759 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -60,14 +60,15 @@
             omci = None     # TODO: Get from handler
 
     @staticmethod
-    def create(handler, gem_port):
+    def create(handler, gem_port, is_mock=False):
         return OnuGemPort(gem_port['gemport-id'],
                           None,
                           encryption=gem_port['encryption'],  # aes_indicator,
                           tcont_ref=gem_port['tcont-ref'],
                           name=gem_port['name'],
                           traffic_class=gem_port['traffic-class'],
-                          handler=handler)
+                          handler=handler,
+                          is_mock=is_mock)
 
     @inlineCallbacks
     def add_to_hardware(self, omci):
@@ -86,12 +87,12 @@
 
             # TODO: For TCONT ID, get the TCONT's entity ID that you programmed
             # TODO: For TM, is this the entity ID for a traffic descriptor?
-            results = yield omci.send_create_gem_port_network_ctp(self.gem_id,      # Entity ID
-                                                                  self.gem_id,      # Port ID
-                                                                  tcont.entity_id,  # TCONT ID
-                                                                  direction,        # Direction
-                                                                  0x100)            # TM
-
+            # results = yield omci.send_create_gem_port_network_ctp(self.gem_id,      # Entity ID
+            #                                                       self.gem_id,      # Port ID
+            #                                                       tcont.entity_id,  # TCONT ID
+            #                                                       direction,        # Direction
+            #                                                       0x100)            # TM
+            results = None
             # results = yield omci.send(GemPortNetworkCtpFrame(self.gem_id,      # Entity ID
             #                                                  self.gem_id,      # Port ID
             #                                                  tcont.entity_id,  # TCONT ID
@@ -106,9 +107,10 @@
             # GEM Interworking config
             # TODO: For service mapper ID, always hardcoded or does it come from somewhere else
             #       It is probably the TCONT entity ID
-            results = yield omci.send_create_gem_inteworking_tp(self.gem_id,      # Entity ID
-                                                                self.gem_id,      # GEMPort NET CTP ID
-                                                                tcont.entity_id)  # Service Mapper Profile ID
+            results = None
+            # results = yield omci.send_create_gem_inteworking_tp(self.gem_id,      # Entity ID
+            #                                                     self.gem_id,      # GEMPort NET CTP ID
+            #                                                     tcont.entity_id)  # Service Mapper Profile ID
         except Exception as e:
             self.log.exception('interworking-create', e=e)
             raise
@@ -117,8 +119,9 @@
             # Mapper Service Profile config
             # TODO: All p-bits currently go to the one and only GEMPORT ID for now
             # TODO: The entity ID is probably the TCONT entity ID
-            results = omci.send_set_8021p_mapper_service_profile(tcont.entity_id,  # Entity ID
-                                                                 self.gem_id)      # Interworking TP ID
+            results = None
+            # results = omci.send_set_8021p_mapper_service_profile(tcont.entity_id,  # Entity ID
+            #                                                      self.gem_id)      # Interworking TP ID
         except Exception as e:
             self.log.exception('mapper-set', e=e)
             raise
@@ -152,4 +155,4 @@
         #                                                   self.gem_id)
         # name = 'onu-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
         # return session.request('PATCH', uri, data=data, name=name)
-        pass # TODO: Implement me
\ No newline at end of file
+        pass # TODO: Implement me
diff --git a/voltha/adapters/adtran_onu/onu_pm_metrics.py b/voltha/adapters/adtran_onu/onu_pm_metrics.py
index c4078bf..ac0b02e 100644
--- a/voltha/adapters/adtran_onu/onu_pm_metrics.py
+++ b/voltha/adapters/adtran_onu/onu_pm_metrics.py
@@ -27,7 +27,11 @@
             ('tx_frames', PmConfig.COUNTER),
             ('tx_errors', PmConfig.COUNTER),
             ('rx_frames', PmConfig.COUNTER),
-            ('rx_onu_frames', PmConfig.COUNTER),
+            ('rx_unknown_tid', PmConfig.COUNTER),
+            ('rx_onu_frames', PmConfig.COUNTER),        # Rx ONU autonomouse messages
+            ('rx_alarm_overflow', PmConfig.COUNTER),    # Autonomous ONU generated alarm message overflows
+            ('rx_avc_overflow', PmConfig.COUNTER),      # Autonomous ONU generated AVC message overflows
+            ('rx_onu_discards', PmConfig.COUNTER),      # Autonomous ONU message unknown type discards
             ('rx_timeouts', PmConfig.COUNTER),
             ('consecutive_errors', PmConfig.COUNTER),
             ('reply_min', PmConfig.GUAGE),      # Milliseconds
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index ec51246..3d3e2bf 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -38,7 +38,7 @@
         return self._entity_id
 
     @staticmethod
-    def create(handler, tcont, td):
+    def create(handler, tcont, td, is_mock=False):
         assert isinstance(tcont, dict), 'TCONT should be a dictionary'
         assert isinstance(td, TrafficDescriptor), 'Invalid Traffic Descriptor data type'
 
@@ -50,7 +50,8 @@
                         td,
                         entity_id,
                         name=tcont['name'],
-                        vont_ani=tcont['vont-ani'])
+                        vont_ani=tcont['vont-ani'],
+                        is_mock=is_mock)
 
     @inlineCallbacks
     def add_to_hardware(self, omci):
@@ -62,8 +63,9 @@
             #
             # NOTE: Entity ID should be computed. For NGPON2, they were starting
             #       at 256 and incrementing.
-            results = yield self._handler.omci.send_set_tcont(self._entity_id,  # Entity ID
-                                                              self.alloc_id)    # Alloc ID
+            results = None
+            # results = yield self._handler.omci.send_set_tcont(self._entity_id,  # Entity ID
+            #                                                   self.alloc_id)    # Alloc ID
 
             # response = yield omci.send(TcontFrame(self._entity_id,
             #                                       alloc_id=self.alloc_id).get())
@@ -79,7 +81,8 @@
         if self._is_mock:
             returnValue('mock')
 
-        results = yield omci.send(TcontFrame(self._entity_id).delete())
+        results = None
+        # results = yield omci.send(TcontFrame(self._entity_id).delete())
         returnValue(results)
 
 
diff --git a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
index d24de7b..d41d632 100644
--- a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
+++ b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
@@ -46,12 +46,12 @@
         else:
             best_effort = None
 
-        return TrafficDescriptor(traffic_disc['fixed-bandwidth'],
-                                 traffic_disc['assured-bandwidth'],
-                                 traffic_disc['maximum-bandwidth'],
-                                 name=traffic_disc['name'],
-                                 best_effort=best_effort,
-                                 additional=additional)
+        return OnuTrafficDescriptor(traffic_disc['fixed-bandwidth'],
+                                    traffic_disc['assured-bandwidth'],
+                                    traffic_disc['maximum-bandwidth'],
+                                    name=traffic_disc['name'],
+                                    best_effort=best_effort,
+                                    additional=additional)
 
     @inlineCallbacks
     def add_to_hardware(self, omci):
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index 3bbbe67..4b6c044 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -22,6 +22,18 @@
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
 
 from omci.omci_me import *
+from omci.deprecated import *       # TODO: Remove this once OMCI_CC and ME_Frame refactoring is complete
+
+###################################################################################
+#
+# TODO: Notes -> This version is the fifth attempt. All calls converted with the
+#                exception of the mib-reset and upload.
+#
+#                Saving this off before moving things around.
+#
+#
+###################################################################################
+
 
 _STARTUP_RETRY_WAIT = 5
 BRDCM_DEFAULT_VLAN = 4091       # TODO: Deprecate later...
@@ -49,6 +61,8 @@
         self._gem_ports = {}                           # gem-id -> GemPort
         self._tconts = {}                              # alloc-id -> TCont
 
+        # TODO: Until we have an external database, just save it here
+        self.mib_data_store = dict()  # TODO: Improve and make class attribute/property
         # TODO: Add stats, alarm reference, ...
 
         pass
@@ -69,8 +83,8 @@
         self._update_adapter_agent()
 
         # Begin ONU Activation sequence
-        self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self.message_exchange)
-
+        self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                           self._initial_message_exchange)
         # TODO: start h/w sync
         pass
 
@@ -134,8 +148,8 @@
                               type=Port.PON_ONU,
                               admin_state=self._admin_state,
                               oper_status=self._oper_status,
-                              peers = [Port.PeerPort(device_id=device.parent_id,
-                                                     port_no=device.parent_port_no)])
+                              peers=[Port.PeerPort(device_id=device.parent_id,
+                                                   port_no=device.parent_port_no)])
         return self._port
 
     def _update_adapter_agent(self):
@@ -144,8 +158,12 @@
         pass
 
     @inlineCallbacks
-    def message_exchange(self):
-        self.log.info('message-exchange')
+    def _initial_message_exchange(self):
+        """
+        Perform a MIB Reset and then collect some basic (read-only) attributes.
+        Upon success, begin MIB upload sequence
+        """
+        self.log.info('initial-message-exchange')
         self._deferred = None
 
         if self._handler.device_id is None or not self.enabled:
@@ -167,6 +185,7 @@
             device = self._handler.adapter_agent.get_device(self._handler.device_id)
             device.oper_status = OperStatus.ACTIVATING
             device.connect_status = ConnectStatus.UNREACHABLE
+            device.reason = 'Initial OMCI message exchange in progress'
 
         except Exception as e:
             self.log.exception('top-of-msg-exch', e=e)
@@ -177,20 +196,12 @@
             returnValue('no-device')
 
         try:
-            # May timeout to ONU not fully discovered (can happen in xPON case)
+            # Note: May timeout to ONU not fully discovered (can happen in xPON case)
             # or other errors.
             # Decode fields in response and update device info
 
-            response = yield omci.send_get_OntG('vendor_id')
-
-            omci_response = response.getfieldval("omci_message")
-            data = omci_response.getfieldval("data")
-            vendor = data["vendor_id"]
-            assert vendor == 'ADTN', \
-                "Invalid Device/Wrong device adapter assigned: '{}'".format(vendor)
-
             response = yield omci.send(OntGFrame('vendor_id').get())
-
+            # TODO: Get status for this and others below before getting other values...
             omci_response = response.getfieldval("omci_message")
             data = omci_response.getfieldval("data")
             vendor = data["vendor_id"]
@@ -203,7 +214,6 @@
             device.connect_status = ConnectStatus.REACHABLE
             self._handler.adapter_agent.update_device(device)
 
-            # response = yield omci.send_get_cardHolder('actual_plugin_unit_type', 257)
             response = yield omci.send(CardholderFrame(True, 1,
                                                        'actual_plugin_unit_type').get())
 
@@ -211,7 +221,6 @@
             data = omci_response.getfieldval("data")
             # device.type = str(data["actual_plugin_unit_type"])
 
-            # response = yield omci.send_get_circuit_pack('number_of_ports', 257)
             response = yield omci.send(CircuitPackFrame(257, 'number_of_ports').get())
 
             omci_response = response.getfieldval("omci_message")
@@ -219,14 +228,12 @@
             num_ports = data["number_of_ports"]
             assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
 
-            # response = yield omci.send_get_IpHostConfigData('mac_address', 515)
             response = yield omci.send(IpHostConfigDataFrame(515, 'mac_address').get())
 
             omci_response = response.getfieldval("omci_message")
             data = omci_response.getfieldval("data")
             device.mac_address = str(data["mac_address"])
 
-            # response = yield omci.send_get_Ont2G('equipment_id', 0)
             response = yield omci.send(Ont2GFrame('equipment_id').get())
 
             omci_response = response.getfieldval("omci_message")
@@ -243,7 +250,6 @@
             # decimal version
             omciVersion = str(data["omcc_version"])
 
-            # response = yield omci.send_get_Ont2G('vendor_product_code', 0)
             response = yield omci.send(Ont2GFrame('vendor_product_code').get())
 
             omci_response = response.getfieldval("omci_message")
@@ -251,13 +257,18 @@
             # decimal value
             vendorProductCode = str(data["vendor_product_code"])
 
-            # response = yield omci.send(OntGFrame('version').get())
             response = yield omci.send(OntGFrame('version').get())
 
             omci_response = response.getfieldval("omci_message")
             data = omci_response.getfieldval("data")
             device.model = str(data["version"])             # such as 1287800F1
 
+            # TODO: Combine ONTG calls into a single call with multiple attributes
+            # TODO: Combine ONT2G calls into a single call with multiple attributes
+
+            # TODO: Look into ONTG and ONT2G to see if we can get other items of interest
+            #       such as max tconts, max gem ports, and so on. Make use of them
+
             # Possibility of bug in ONT Firmware. uncomment this code after it is fixed.
             # response = yield omci.send_get_SoftwareImage('version', 0)
             #
@@ -279,17 +290,18 @@
             # self.adapter_agent.update_device(device)
             device.oper_status = OperStatus.ACTIVE
             device.connect_status = ConnectStatus.REACHABLE
+            device.reason = 'Initial OMCI message exchange complete'
             self._handler.adapter_agent.update_device(device)
 
-            # Start up non-critical message exchange
-            self._deferred = reactor.callLater(0, self.message_exchange_part_2)
+            # Start MIB synchronization
+            self._deferred = reactor.callLater(0, self._perform_mib_upload)
             self.log.info('onu-activated')
 
         # These exceptions are not recoverable
-        except (AssertionError, TypeError, ValueError, AttributeError) as e:
+        except (TypeError, ValueError) as e:
             self.log.exception('Failed', e=e)
             device.oper_status = OperStatus.FAILED
-            device.reason = e.message
+            device.reason = 'Initial message sequence failure: ' + e.message
             self._handler.adapter_agent.update_device(device)
 
         except TimeoutError as e:
@@ -297,20 +309,117 @@
             self._handler.adapter_agent.update_device(device)
             # Try again later. May not have been discovered
             self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                               self.message_exchange)
+                                               self._initial_message_exchange)
 
         except Exception as e:
             self.log.exception('Failed', e=e)
+            device.reason = 'Initial message sequence failure: ' + e.message
             self._handler.adapter_agent.update_device(device)
             # Try again later. May not have been discovered
             self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                               self.message_exchange)
+                                               self._initial_message_exchange)
 
     @inlineCallbacks
-    def message_exchange_part_2(self):
-        """ Called after basic OMCI message startup/exchange """
+    def _perform_mib_upload(self):
+        """
+        Called after basic OMCI MIB RESET message startup/exchange.
 
-        self.log.info('message-exchange-part-2')
+        Upon successful completion, proceed to establish a few basic structures
+        that we know will be required.  Once OpenOMCI is created, this sequence
+        should be skipped (go directly to MIB upload) and this info is available
+        from the uploaded MIB.
+
+        On failure, restart the initial message exchange
+        """
+        self.log.info('perform-mib-upload')
+        self._deferred = None
+
+        if self._handler.device_id is None or not self.enabled:
+            returnValue('not-enabled')
+
+        device = None
+        omci = self._handler.omci
+
+        if self._handler.is_mock:
+            self._deferred = reactor.callLater(0, self._perform_mib_download)
+            returnValue('is-mock')
+
+        try:
+            device = self._handler.adapter_agent.get_device(self._handler.device_id)
+            device.reason = 'Performing MIB Synchronization'
+            self._handler.adapter_agent.update_device(device)
+
+            #########################################
+            # MIB Reset
+            results = yield omci.send_mib_reset()
+            status = results.fields['omci_message'].fields['success_code']
+            assert status == 0, 'Unexpected MIB reset response status: {}'.format(status)
+
+            # TODO: On a real system, need to flush the external MIB database
+            # TODO: Also would need to watch for any AVC being handled between the MIB reset and the DB flush
+            self.mib_data_store = dict()
+
+            ########################################
+            # Begin MIB Upload
+            results = yield omci.send_mib_upload()
+            number_of_commands = results.fields['omci_message'].fields['number_of_commands']
+
+            for seq_no in xrange(number_of_commands):
+                results = yield omci.send_mib_upload_next(seq_no)
+
+                object_entity_class = results.fields['omci_message'].fields['object_entity_class']
+                object_entity_id = results.fields['omci_message'].fields['object_entity_id']
+                object_attributes_mask = results.fields['omci_message'].fields['object_attributes_mask']
+                object_data = results.fields['omci_message'].fields['object_data']
+
+                key = (object_entity_class, object_entity_id)
+
+                if key not in self.mib_data_store:
+                    self.mib_data_store[key] = (object_attributes_mask, object_data)
+                else:
+                    pass
+
+            # Successful if here
+
+            device.reason = 'MIB Synchronization Complete'
+            self._handler.adapter_agent.update_device(device)
+
+            # Start up non-critical message exchange
+            self._deferred = reactor.callLater(0, self._perform_mib_download)
+            self.log.info('mib-synchronized')
+
+        except TimeoutError as e:
+            self.log.warn('mib-upload', e=e)
+
+            if device is not None:
+                device.reason = 'mib-upload-failure: Response Timeout'
+                self._handler.adapter_agent.update_device(device)
+
+            # Try again later
+            self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                               self._initial_message_exchange)
+
+        except Exception as e:
+            self.log.exception('mib-upload', e=e)
+            device.reason = 'MIB upload sequence failure: ' + e.message
+            self._handler.adapter_agent.update_device(device)
+
+            # Try again later
+            self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                               self._initial_message_exchange)
+
+    @inlineCallbacks
+    def _perform_mib_download(self):
+        """
+        Called after basic OMCI Synchronization (MIB upload). Begin to set up
+        some basic OMCI settings common for most expected configurations
+
+        Upon successful completion, any xPON information received so far will be
+        acted upon.
+
+        On failure, restart the initial message exchange
+        """
+        self.log.info('mib-download-start')
         self._deferred = None
 
         if self._handler.device_id is None or not self.enabled:
@@ -318,100 +427,443 @@
 
         omci = self._handler.omci
 
-        try:
-            # reset incoming message queue
-            omci.flush()
-            device = self._handler.adapter_agent.get_device(self._handler.device_id)
+        if self._handler.is_mock:
+            self._bridge_initialized = True
+            self._deferred = reactor.callLater(0, self._sync_existing_xpon)
+            returnValue('is-mock')
 
-        except Exception as e:
-            self.log.exception('top-of-msg-exch', e=e)
-            device = None
+        # reset incoming message queue
+        omci.flush()
+        device = self._handler.adapter_agent.get_device(self._handler.device_id)
+
+        device.reason = 'Performing MIB Download'
+        self._handler.adapter_agent.update_device(device)
 
         if not self.enabled or device is None:
             returnValue('not-enabled')
 
+        #############################################
+        #  All our variables here
+        #  TODO: Move elsewhere in future version of this software
+        frame = None
+        gal_enet_profile_entity_id = 0x100       # Any Unique Entity ID BP: old value 1
+        ieee_mapper_service_profile_entity_id = 0x100         # Entity ID BP: old value 0x8001
+        mac_bridge_service_profile_entity_id = 0x100  # Entity ID BP: old value 0x201
+        mac_bridge_port_ani_entity_id = 0x100       # BP: oldvalue 0x201
+        ethernet_uni_entity_id = 0x101
+        vlan_tcis_1 = 0x900
+        cvid = 2            # TODO: Get from xPON and/or device adapter
+        tcont_entity_id = 0x100   # Entity ID, ONT is set to 0x100
+        tcont_alloc_id = 0x400    # Alloc ID, 1024 - Tcont
+        gem_entity_id = 0x4900          # Entity ID, unique Id
+        gem_port_id = 0x400             # Port ID, 2304 - Gem Id
+        gem_interworking_entity_id = 0x4900
+        vlan_config_entity_id = vlan_tcis_1               # Entity ID       BP: Oldvalue 0x202
+
         try:
-            cvid = BRDCM_DEFAULT_VLAN           # TODO: What should this be?
+            ################################################################################
+            #
+            #
+            #  EntityID will be referenced by:
+            #            -
+            #            -
+            #            -
+            #            -
+            #  References:
+            #            -
+            #            -
 
-            # construct message
-            # MIB Reset - OntData - 0
-            # results = yield omci.send_mib_reset()
+            frame = TcontFrame(tcont_entity_id, tcont_alloc_id).set()
+            results = yield omci.send(frame)
+            # results = yield send_set_tcont(omci, 0x100,  # Entity ID, ONT is set to 0x100
+            #                                0x400)  # Alloc ID, 1024 - Tcont
 
-            # Create AR - GalEthernetProfile - 1
-            results = yield omci.send_create_gal_ethernet_profile(
-                                                    1,   # Entity ID
-                                                    48)  # Max GEM Payload size
+            status = results.fields['omci_message'].fields['success_code']
+            failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
+            self.log.debug('set-tcont', status=status,
+                           failed_attributes_mask=failed_attributes_mask,
+                           unsupported_attributes_mask=unsupported_attributes_mask)
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            ################################################################################
+            direction = "bi-directional"
 
-            # Port 2
-            # Extended VLAN Tagging Operation config
-            # TODO: add entry here for additional UNI interfaces
-            results = yield omci.send_create_extended_vlan_tagging_operation_configuration_data(
-                                                    0x202,  # Entity ID
-                                                    2,      # Assoc Type
-                                                    0x102)  # Assoc ME
+            # TODO: For TM, is this the entity ID for a traffic descriptor?
+            frame = GemPortNetworkCtpFrame(
+                    gem_entity_id,
+                    port_id=gem_port_id,       # Port ID, 2304 - Gem ID
+                    tcont_id=tcont_entity_id,  # TCONT Entity ID, as set in TCONT set
+                    direction=direction,       # Direction, bidirectional
+                    upstream_tm=0x8000         # TM ID, 32768 unique ID set in TD set  TODO: Parameterize
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_gem_port_network_ctp(omci, 0x4900,    # Entity ID, unique Id
+            #                                                  0x400,     # Port ID, 2304 - Gem Id
+            #                                                  0x100,     # TCONT Entity ID, as set in TCONT set
+            #                                                  direction, # Direction, bidirectional
+            #                                                  0x8000)    # TM ID, 32768 unique Id set in TD set
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-gem-port-network-ctp', status=status, error_mask=error_mask)
 
-            # Set AR - ExtendedVlanTaggingOperationConfigData - 514 - 8100 - 8100
-            results = yield omci.send_set_extended_vlan_tagging_operation_tpid_configuration_data(
-                                                    0x202,   # Entity ID
-                                                    0x8100,  # input TPID
-                                                    0x8100)  # output TPID
+            ################################################################################
+            # GEM Interworking config
+            #
+            #
+            #  EntityID will be referenced by:
+            #            -
+            #            -
+            #            -
+            #            -
+            #  References:
+            #            -
+            #            -
+            # TODO: for the service_profile_pointer=0x100, is this create/set somewhere later
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # unsupported_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
-            # failed_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            frame = GemInterworkingTpFrame(
+                gem_interworking_entity_id,
+                gem_port_network_ctp_pointer=gem_entity_id,  # GEMPort NET CTP ID, as set in CTP create
+                interworking_option=5,                             # IEEE 802.1
+                service_profile_pointer=ieee_mapper_service_profile_entity_id,
+                interworking_tp_pointer=0x0,
+                pptp_counter=1,
+                gal_profile_pointer=0   # TODO:  make? -> gal_enet_profile_entity_id     # BP: HACK old value 0x1  (TODO: Balaji had this set to 0 in his test sequence)
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_gem_inteworking_tp(omci, 0x4900, # any Unique Entity ID
+            #                                                0x4900, # GEMPort NET CTP ID, as set in CTP create
+            #                                                0x100)  # 802.1p mapper Service Mapper Profile ID
 
-            # MAC Bridge Service config
-            results = yield omci.send_create_mac_bridge_service_profile(0x201)   # Entity ID
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-gem-interworking-tp', status=status, error_mask=error_mask)
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            ########################################################################################
+            # Create GalEthernetProfile - Once per ONU/PON interface
+            #
+            #  EntityID will be referenced by:
+            #            - GEM Interworking TPs when a new GEM Port is created
+            #  References:
+            #            - Nothing
 
-            # Create AR - MacBridgePortConfigData
-            results = yield omci.send_create_mac_bridge_port_configuration_data(
-                                            0x201,   # Entity ID
-                                            0x201,   # Bridge ID
-                                            2,       # Port ID
-                                            1,       # TP Type
-                                            0x102)   # TP ID
+            frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
+                                            max_gem_payload_size=1518).create()  # Max GEM Payload size
+            results = yield omci.send(frame)
+            # results = yield send_create_gal_ethernet_profile(omci,
+            #                                         0x100,   # Any Unique Entity ID BP: old value 1
+            #                                         1518)    # Max GEM Payload size
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-gal-ethernet-profile', status=status, error_mask=error_mask)
 
-            # Mapper Service config
-            results = yield omci.send_create_8021p_mapper_service_profile(0x8001)   # Entity ID
+            ################################################################################
+            # MAC Bridge Service Profile - Once per UNI
+            #
+            #  EntityID will be referenced by:
+            #            - MAC Bridge Port Configuration Data
+            #  References:
+            #            - Nothing
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            attributes = {
+                'spanning_tree_ind': False,
+                #  TODO: CB: see if we need or can use any of the following...
+                # 'learning_ind': True,
+                # 'priority': 0x8000,
+                # 'max_age': 20 * 256,
+                # 'hello_time': 2 * 256,
+                # 'forward_delay': 15 * 256,
+                # 'unknown_mac_address_discard': True
+            }
+            frame = MacBridgeServiceProfileFrame(mac_bridge_service_profile_entity_id,
+                                                 attributes).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_mac_bridge_service_profile(omci, 0x100)   # Entity ID BP: old value 0x201
 
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-mac-bridge-service-profile', status=status, error_mask=error_mask)
+
+            ################################################################################
+            # IEEE 802.1 Mapper Service config - Once per PON
+            #
+            #  EntityID will be referenced by:
+            #            - MAC Bridge Port Configuration Data for the PON port
+            #  References:
+            #            - Nothing at this point. When a GEM port is created, this entity will
+            #              be updated to reference the GEM Interworking TP
+
+            frame = Ieee8021pMapperServiceProfileFrame(ieee_mapper_service_profile_entity_id).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_8021p_mapper_service_profile(omci, 0x100)   # Entity ID BP: old value 0x8001
+
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-8021p-mapper-service-profile', status=status, error_mask=error_mask)
+
+            ################################################################################
+            # Create MAC Bridge Port Configuration Data for the PON port via IEEE 802.1
+            # mapper service. Upon receipt by the ONU, the ONU will create an instance
+            # of the following before returning the response.
+            #
+            #     - MAC bridge port designation data
+            #     - MAC bridge port filter table data
+            #     - MAC bridge port bridge table data
+            #
+            #  EntityID will be referenced by:
+            #            - Implicitly by the VLAN tagging filter data
+            #            -
+            #            -
+            #            -
+            #  References:
+            #            - MAC Bridge Service Profile (the bridge)
+            #            - IEEE 802.1p mapper service profile for PON port
+
+            frame = MacBridgePortConfigurationDataFrame(
+                mac_bridge_port_ani_entity_id,                           # Entity ID
+                bridge_id_pointer=mac_bridge_service_profile_entity_id,  # Bridge Entity ID BP: oldvalue 0x201
+                # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
+                port_num=0,                                              # Port ID          BP: oldvalue 2
+                tp_type=3,                                               # TP Type (IEEE 802.1p mapper service)  BP: oldvalue 1, 802.1 mapper GPON intf
+                tp_pointer=ieee_mapper_service_profile_entity_id         # TP ID, 8021p mapper ID   BP: oldvalue 0x102
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_mac_bridge_port_configuration_data(omci,
+            #                                 0x100,   # Entity ID   BP: oldvalue 0x201
+            #                                 0x100,   # Bridge Entity ID   BP: oldvalue 0x201
+            #                                 0,       # Port ID     BP: oldvalue 2
+            #                                 3,       # TP Type    BP: oldvalue  1, 802.1 mapper GPON interface
+            #                                 0x100)   # TP ID, 8021p mapper Id      BP: oldvalue 0x102
+
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-mac-bridge-port-configuration-data-part-1', status=status, error_mask=error_mask)
+
+            ################################################################################
             # MAC Bridge Port config
-            results = yield omci.send_create_mac_bridge_port_configuration_data(
-                                            0x2102,  # Entity ID
-                                            0x201,   # Bridge ID
-                                            3,       # Port ID
-                                            3,       # TP Type
-                                            0x8001)  # TP ID
+            # This configuration is for Ethernet UNI
+            #
+            #  EntityID will be referenced by:
+            #            -
+            #            -
+            #            -
+            #            -
+            #  References:
+            #            - MAC Bridge Service Profile (the bridge)
+            #            - PPTP Ethernet UNI
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            frame = MacBridgePortConfigurationDataFrame(
+                0x000,                             # Entity ID                BP: oldvalue 0x201
+                bridge_id_pointer=mac_bridge_service_profile_entity_id,  # Bridge Entity ID BP: oldvalue 0x201
+                port_num=0,                        # Port ID                  BP: oldvalue 3
+                tp_type=1,                         # PPTP Ethernet UNI        BP: oldvalue 3
+                tp_pointer=ethernet_uni_entity_id  # TP ID, 8021p mapper Id   BP: oldvalue 0x8001
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_mac_bridge_port_configuration_data(omci,
+            #                                 0x000,   # Entity ID     BP: oldvalue 0x2102
+            #                                 0x100,   # Bridge Entity ID     BP: oldvalue 0x201
+            #                                 0,       # Port ID     BP: oldvalue 3
+            #                                 1,       # TP Type, Ethernet UNI     BP: oldvalue 3
+            #                                 0x101)   # TP ID, PPTP UNI Entity Id     BP: oldvalue 0x8001
 
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-mac-bridge-port-configuration-data-part-2', status=status, error_mask=error_mask)
+
+            ################################################################################
             # VLAN Tagging Filter config
+            #
+            #  EntityID will be referenced by:
+            #            - Nothing
+            #  References:
+            #            - Implicitly linked to an instance of the MAC bridge port configuration data
+            #              for the PON port
             # TODO: Probably need to get VLAN ID from device.vlan
-            results = yield omci.send_create_vlan_tagging_filter_data(0x2102,  # Entity ID
-                                                                      cvid)    # VLAN ID
+            # Set anything, this request will not be used when using Extended Vlan
 
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            frame = VlanTaggingFilterDataFrame(
+                mac_bridge_port_ani_entity_id,       # Entity ID   BP: Oldvalue 0x2102
+                vlan_tcis=[vlan_tcis_1],             # VLAN IDs     BP: cvid
+                forward_operation=0x10
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_vlan_tagging_filter_data(omci, 0x100,  # Entity ID   BP: Oldvalue 0x2102
+            #                                                      0x900)        # VLAN ID     BP: cvid
+
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-vlan-tagging-filter-data', status=status, error_mask=error_mask)
+
+            ################################################################################
+            # Update the IEEE 802.1p Mapper Service Profile config
+            #
+            #  EntityID was created prior to this call
+            #  References:
+            #            -
+            #            -
+            # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+
+            frame = Ieee8021pMapperServiceProfileFrame(
+                ieee_mapper_service_profile_entity_id,      # 802.1p mapper Service Mapper Profile ID
+                interwork_tp_pointers=[gem_entity_id]  # Interworking TP IDs  BP: oldvalue self.gemid
+            ).set()
+            results = yield omci.send(frame)
+            # results = yield send_set_8021p_mapper_service_profile(omci, 0x100, 0x4900)
+
+            status = results.fields['omci_message'].fields['success_code']
+            failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
+            self.log.debug('set-8021p-mapper-service-profile', status=status,
+                           failed_attributes_mask=failed_attributes_mask,
+                           unsupported_attributes_mask=unsupported_attributes_mask)
+
+            ################################################################################
+            #  Unlock UNI
+            #
+            #  EntityID will be referenced by:
+            #            - MAC bridge port configuration data for the UNI side
+            #  References:
+            #            - Nothing
+
+            attributes = dict(
+                administrative_state=0  # 0 - Unlock
+            )
+            frame = PptpEthernetUniFrame(
+                ethernet_uni_entity_id,  # Entity ID
+                attributes=attributes    # See above
+            ).set()
+            results = yield omci.send(frame)
+            #results = yield send_set_pptp_ethernet_uni(omci, 0x101)  # Entity ID
+
+            status = results.fields['omci_message'].fields['success_code']
+            failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
+            self.log.debug('set-pptp-ethernet-uni', status=status,
+                           failed_attributes_mask=failed_attributes_mask,
+                           unsupported_attributes_mask=unsupported_attributes_mask)
+
+            ################################################################################
+            # Create Extended VLAN Tagging Operation config
+            #
+            #  EntityID relates to the VLAN TCIS
+            #  References:
+            #            - VLAN TCIS from previously created VLAN Tagging filter data
+            #            - PPTP Ethernet UNI
+            #
+            # TODO: add entry here for additional UNI interfaces
+
+            attributes = dict(
+                association_type=2,                           # Assoc Type, PPTP Ethernet UNI   BP: Oldvalue 2
+                associated_me_pointer=ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id        BP: Oldvalue 0x102
+            )
+
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                vlan_config_entity_id,
+                attributes=attributes     # See above
+            ).create()
+            results = yield omci.send(frame)
+            # results = yield send_create_extended_vlan_tagging_operation_configuration_data(omci,
+            #                                         0x900,  # Entity ID       BP: Oldvalue 0x202
+            #                                         2,      # Assoc Type, PPTP Ethernet UNI   BP: Oldvalue 2
+            #                                         0x101)  # Assoc ME, PPTP Entity Id   BP: Oldvalue 0x102
+
+            status = results.fields['omci_message'].fields['success_code']
+            error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+            self.log.debug('create-extended-vlan-tagging-operation-configuration-data', status=status, error_mask=error_mask)
+
+            ################################################################################
+            # Update Extended VLAN Tagging Operation Config Data
+            #
+            # Specifies the TPIDs in use and that operations in the downstream direction are
+            # inverse to the operations in the upstream direction
+            # TODO: Downstream mode may need to be modified once we work more on the flow rules
+
+            attributes = dict(
+                input_tpid=0x8100,   # input TPID
+                output_tpid=0x8100,  # output TPID
+                downstream_mode=0,   # inverse of upstream
+            )
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                vlan_config_entity_id,    # Entity ID       BP: Oldvalue 0x202
+                attributes=attributes     # See above
+            ).set()
+            results = yield omci.send(frame)
+            # results = yield send_set_extended_vlan_tagging_operation_tpid_configuration_data(omci,
+            #                                         0x900,   # Entity ID      BP: Oldvalue 0x202
+            #                                         0x8100,  # input TPID
+            #                                         0x8100)  # output TPID
+
+            status = results.fields['omci_message'].fields['success_code']
+            failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
+            self.log.debug('set-extended-vlan-tagging-operation-configuration-data', status=status,
+                           failed_attributes_mask=failed_attributes_mask,
+                           unsupported_attributes_mask=unsupported_attributes_mask)
+
+            ################################################################################
+            # Update Extended VLAN Tagging Operation Config Data
+            #
+            # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
+            #             Treatment Inner Vlan Id : 2
+
+            attributes = dict(
+                received_frame_vlan_tagging_operation_table=
+                VlanTaggingOperation(
+                    filter_outer_priority=15,       # This entry is not a double-tag rule
+                    filter_outer_vid=4096,          # Do not filter on the outer VID value
+                    filter_outer_tpid_de=0,         # Do not filter on the outer TPID field
+
+                    filter_inner_priority=15,       # This is a no-tag rule, ignore all other VLAN tag filter fields
+                    filter_inner_vid=0x1000,        # Do not filter on the inner VID
+                    filter_inner_tpid_de=0,         # Do not filter on inner TPID field
+                    filter_ether_type=0,            # Do not filter on EtherType
+
+                    treatment_tags_to_remove=0,     # Remove 0 tags
+                    treatment_outer_priority=15,    # Do not add an outer tag
+                    treatment_outer_vid=0,          # n/a
+                    treatment_outer_tpid_de=0,      # n/a
+
+                    treatment_inner_priority=0,     # Add an inner tag and insert this value as the priority
+                    treatment_inner_vid=cvid,       # use this value as the VID in the inner VLAN tag
+                    treatment_inner_tpid_de=4       # set TPID = 0x8100
+                )
+            )
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                vlan_config_entity_id,    # Entity ID       BP: Oldvalue 0x202
+                attributes=attributes     # See above
+            ).set()
+            results = yield omci.send(frame)
+            # results = yield send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(omci, 0x900,
+            #                                                                                           0x1000,
+            #                                                                                           2)
+            status = results.fields['omci_message'].fields['success_code']
+            failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+            unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
+            self.log.debug('set-extended-vlan-tagging-operation-configuration-data-untagged', status=status,
+                           failed_attributes_mask=failed_attributes_mask,
+                           unsupported_attributes_mask=unsupported_attributes_mask)
+
+            # BP: This is for AT&T RG's
+            #
+            #   TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
+            #
+            # Set AR - ExtendedVlanTaggingOperationConfigData
+            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
+            #                                 0x900,  # Entity ID
+            #                                 8,      # Filter Inner Priority, do not filter on Inner Priority
+            #                                 0,    # Filter Inner VID, this will be 0 in CORD
+            #                                 0,      # Filter Inner TPID DE
+            #                                 1,      # Treatment tags, number of tags to remove
+            #                                 8,      # Treatment inner priority, copy Inner Priority
+            #                                 2)   # Treatment inner VID, this will be 2 in CORD
 
             # Set AR - ExtendedVlanTaggingOperationConfigData
             #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
             # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
-            #                                 0x202,  # Entity ID
+            #                                 0x200,  # Entity ID
             #                                 8,      # Filter Inner Priority
             #                                 0,      # Filter Inner VID
             #                                 0,      # Filter Inner TPID DE
@@ -421,103 +873,58 @@
             #
             # Set AR - ExtendedVlanTaggingOperationConfigData
             #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
-            results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
-                                            0x202,   # Entity ID
-                                            0x1000,  # Filter Inner VID
-                                            cvid)    # Treatment inner VID
+            #results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
+            #                                0x100,   # Entity ID            BP: Oldvalue 0x202
+            #                                0x1000,  # Filter Inner VID     BP: Oldvalue 0x1000
+            #                                cvid)    # Treatment inner VID  BP: cvid
 
             # success = results.fields['omci_message'].fields['success_code'] == 0
             # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
 
+            ###############################################################################
+            # If here, we are done
+            device.reason = ''
+            self._handler.adapter_agent.update_device(device)
+
             ######################################################################
-            # If here, we can add TCONTs/GEM Ports as needed
+            # If here, we can add TCONTs/GEM Ports/... as needed
 
             self._bridge_initialized = True
-            self._deferred = reactor.callLater(0, self.sync_existing_xpon)
-            #       that xPON may have already sent us
+            self._deferred = reactor.callLater(0, self._sync_existing_xpon)
 
-            # ###############################################################################
-            # # Multicast related MEs
-            # # Set AR - MulticastOperationsProfile - Dynamic Access Control List table
-            # # Create AR - MacBridgePortConfigData - 9000 - 513 - 6 - 6 - 6
-            # results = yield omci.send_create_mac_bridge_port_configuration_data(
-            #                                 0x2328,
-            #                                 0x201,
-            #                                 6,
-            #                                 6,
-            #                                 6)
-            #
-            # # Multicast Operation Profile config
-            # # Create AR - MulticastOperationsProfile
-            # results = yield omci.send_create_multicast_operations_profile(
-            #                                 0x201,
-            #                                 3)
-            #
-            # # Multicast Subscriber config
-            # # Create AR - MulticastSubscriberConfigInfo
-            # results = yield omci.send_create_multicast_subscriber_config_info(
-            #                                 0x201,
-            #                                 0,
-            #                                 0x201)
-            #
-            # # Create AR - GemPortNetworkCtp - 260 - 4000 - 0 Multicast
-            # results = yield omci.send_create_gem_port_network_ctp(
-            #                                 0x104,
-            #                                 0x0FA0,
-            #                                 0,
-            #                                 "downstream",
-            #                                 0)
-            #
-            # # Multicast GEM Interworking config Multicast
-            # # Create AR - MulticastGemInterworkingTp - 6 - 260
-            # results = yield omci.send_create_multicast_gem_interworking_tp(0x6, 0x104)
-            #
-            # results = yield omci.send_set_multicast_operations_profile_acl_row0(
-            #                                 0x201,
-            #                                 'dynamic',
-            #                                 0,
-            #                                 0x0fa0,
-            #                                 0x0fa0,
-            #                                 '0.0.0.0',
-            #                                 '224.0.0.0',
-            #                                 '239.255.255.255')
-            #
-            # # Multicast Operation Profile config
-            # # Set AR - MulticastOperationsProfile - Downstream IGMP Multicast TCI
-            # results = yield omci.send_set_multicast_operations_profile_ds_igmp_mcast_tci(
-            #                                 0x201,
-            #                                 4,
-            #                                 cvid)
-
-        except AssertionError as e:
-            self.log.exception('Failed', e=e)
-            # TODO: get message and report back
-            # TODO: get message and report back
+        except TimeoutError as e:
+            self.log.warn('rx-timeout', frame=frame)
+            # Try again later. May not have been discovered
+            self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                               self._initial_message_exchange)
+            returnValue('retry-pending')
 
         except Exception as e:
-            self.log.debug('Failed', e=e)
+            self.log.exception('mib-download', e=e)
+            device.reason = 'MIB download sequence failure: ' + e.message
             self._handler.adapter_agent.update_device(device)
-            # Try again later. TODO: Do we want to restart at part 1 here ?
+
+            # Try again later
             self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                               self.message_exchange_part_2)
+                                               self._initial_message_exchange)
 
     @inlineCallbacks
-    def sync_existing_xpon(self):
+    def _sync_existing_xpon(self):
         """
         Run through existing TCONT and GEM Ports and push into hardware
         """
-        for tcont in self._tconts.itervalues():
-            try:
-                yield self.add_tcont(tcont, reflow=True)
-            except Exception as e:
-                self.log.exception('tcont-reflow', e=e, tcont=tcont)
-
-        for gem_port in self._gem_ports.itervalues():
-            try:
-                yield self.add_gem_port(gem_port, reflow=True)
-
-            except Exception as e:
-                self.log.exception('gem-port-reflow', e=e, gem_port=gem_port)
+        # for tcont in self._tconts.itervalues():
+        #     try:
+        #         yield self.add_tcont(tcont, reflow=True)
+        #     except Exception as e:
+        #         self.log.exception('tcont-reflow', e=e, tcont=tcont)
+        #
+        # for gem_port in self._gem_ports.itervalues():
+        #     try:
+        #         yield self.add_gem_port(gem_port, reflow=True)
+        #
+        #     except Exception as e:
+        #         self.log.exception('gem-port-reflow', e=e, gem_port=gem_port)
 
         returnValue('Done')
 
@@ -658,14 +1065,3 @@
             raise
 
         returnValue(results)
-
-
-
-
-
-
-
-
-
-
-
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index b93bbb9..43ccfee 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -138,15 +138,11 @@
         :return: VOLTHA Port object
         """
         if self._port is None:
-            device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
             self._port = Port(port_no=self.port_number,
                               label='Ethernet port',
                               type=Port.ETHERNET_UNI,
                               admin_state=self._admin_state,
-                              oper_status=self._oper_status,
-                              peers=[Port.PeerPort(device_id=device.parent_id,
-                                                   port_no=device.parent_port_no)])
+                              oper_status=self._oper_status)
         return self._port
 
     def add_logical_port(self, openflow_port_no, control_vlan=None,