VOL-944: ADTRAN ONU: Refactoring to better support new OpenOMCI features

Change-Id: I93e7841924240d872070d7af2978f0e5a35674fc
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index 1f76dfb..81a5f9a 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -23,8 +23,10 @@
 from voltha.protos import third_party
 from adtran_onu_handler import AdtranOnuHandler
 from voltha.extensions.omci.openomci_agent import OpenOMCIAgent, OpenOmciAgentDefaults
-from voltha.extensions.omci.database.mib_db_dict import MibDbVolatileDict
 from twisted.internet import reactor
+from omci.adtn_capabilities_task import AdtnCapabilitiesTask
+from omci.adtn_get_mds_task import AdtnGetMdsTask
+from omci.adtn_mib_sync import AdtnMibSynchronizer
 from copy import deepcopy
 
 _ = third_party
@@ -38,12 +40,17 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='Adtran, Inc.',
-                                               version='0.9',
+                                               version='0.10',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN')
         # Customize OpenOMCI for Adtran ONUs
         self.adtran_omci = deepcopy(OpenOmciAgentDefaults)
 
+        self.adtran_omci['mib-synchronizer']['state-machine'] = AdtnMibSynchronizer
+        self.adtran_omci['mib-synchronizer']['tasks']['get-mds'] = AdtnGetMdsTask
+        self.adtran_omci['mib-synchronizer']['tasks']['mib-audit'] = AdtnGetMdsTask
+        self.adtran_omci['omci-capabilities']['tasks']['get-capabilities'] = AdtnCapabilitiesTask
+
         # TODO: Continue to customize adtran_omci here as needed
 
         self._omci_agent = OpenOMCIAgent(self.adapter_agent.core,
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index bba4872..8e7282f 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -21,6 +21,7 @@
 from pon_port import PonPort
 from uni_port import UniPort
 from heartbeat import HeartBeat
+from omci.omci import OMCI
 
 from voltha.adapters.adtran_olt.alarms.adapter_alarms import AdapterAlarms
 from onu_pm_metrics import OnuPmMetrics
@@ -58,6 +59,9 @@
         self._mgmt_gemport_aes = False
         self._upstream_channel_speed = 0
 
+        self._openomci = OMCI(self, adapter.omci_agent)
+        self._in_sync_subscription = None
+
         self._unis = dict()         # Port # -> UniPort
         self._pon = None
         self._heartbeat = HeartBeat.create(self, device_id)
@@ -127,14 +131,8 @@
         return self._olt_created    # ONU was created with deprecated 'child_device_detected' call
 
     @property
-    def omci_agent(self):
-        return self.adapter.omci_agent
-
-    @property
-    def omci(self):
-        # TODO: Decrement access to Communications channel at this point?  What about current PM stuff?
-        _onu_omci_device = self._pon.onu_omci_device
-        return _onu_omci_device.omci_cc if _onu_omci_device is not None else None
+    def openomci(self):
+        return self._openomci
 
     @property
     def heartbeat(self):
@@ -175,6 +173,10 @@
         # Register for adapter messages
         self.adapter_agent.register_for_inter_adapter_messages()
 
+        # OpenOMCI Startup
+        self._subscribe_to_events()
+        self._openomci.enabled = True
+
         # Port startup
         if self._pon is not None:
             self._pon.enabled = True
@@ -199,8 +201,8 @@
         self._heartbeat.stop()
 
         # OMCI Communications
-        # if self._onu_omci_device is not None:
-        #     self._onu_omci_device.stop()
+        self._unsubscribe_to_events()
+        self._openomci.enabled = False
 
         # Port shutdown
         for port in self.uni_ports:
@@ -215,8 +217,9 @@
                 _ = yield queue.get()
 
     def receive_message(self, msg):
-        if self.omci is not None and self.enabled:
-            self.omci.receive_message(msg)
+        if self.enabled:
+            # TODO: Have OpenOMCI actually receive the messages
+            self.openomci.receive_message(msg)
 
     def activate(self, device):
         self.log.info('activating')
@@ -372,7 +375,7 @@
         def is_upstream(port):
             return not is_downstream(port)
 
-        omci = self.omci
+        omci = self.openomci.omci_cc
 
         for flow in flows:
             _type = None
@@ -565,7 +568,7 @@
                 # MIB Reset - For ADTRAN ONU, we do not get a response
                 #             back (because we are rebooting)
                 pass
-                yield self.omci.send_reboot(timeout=0.1)
+                yield self.openomci.omci_cc.send_reboot(timeout=0.1)
 
             except TimeoutError:
                 # This is expected
@@ -652,9 +655,8 @@
         parent_device = self.adapter_agent.get_device(device.parent_id)
         assert parent_device
 
-
         for uni in self.uni_ports:
-            #port_id = 'uni-{}'.format(uni.port_number)
+            # port_id = 'uni-{}'.format(uni.port_number)
             port_id = uni.port_id_name()
 
             try:
@@ -782,8 +784,7 @@
         self._pon.delete()
 
         # OpenOMCI cleanup
-        if self.omci_agent is not None:
-            self.omci_agent.remove_device(self.device_id, cleanup=True)
+        self._openomci.delete()
 
     def _check_for_mock_config(self, data):
         # Check for MOCK configuration
@@ -1185,3 +1186,51 @@
         # Handle next event (self._event_deferred is None if we got stopped)
 
         self._event_deferred = reactor.callLater(0, self.handle_onu_events)
+
+    def _subscribe_to_events(self):
+        from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
+            OnuDeviceEntry
+
+        # OMCI MIB Database sync status
+        bus = self.openomci.onu_omci_device.event_bus
+        topic = OnuDeviceEntry.event_bus_topic(self.device_id,
+                                               OnuDeviceEvents.MibDatabaseSyncEvent)
+        self._in_sync_subscription = bus.subscribe(topic, self.in_sync_handler)
+
+    def _unsubscribe_to_events(self):
+        insync, self._in_sync_subscription = self._in_sync_subscription, None
+
+        if insync is not None:
+            bus = self.openomci.onu_omci_device.event_bus
+            bus.unsubscribe(insync)
+
+    def in_sync_handler(self, _topic, msg):
+        # Create UNI Ports on first In-Sync event
+
+        if self._in_sync_subscription is not None:
+            try:
+                from voltha.extensions.omci.onu_device_entry import IN_SYNC_KEY
+
+                if msg[IN_SYNC_KEY]:
+                    # Do not proceed if we have not got our vENET information yet.
+
+                    if len(self.uni_ports) > 0:
+                        # Drop subscription....
+                        insync, self._in_sync_subscription = self._in_sync_subscription, None
+
+                        if insync is not None:
+                            bus = self.openomci.onu_omci_device.event_bus
+                            bus.unsubscribe(insync)
+
+                        # Set up UNI Ports. The UNI ports are currently created when the xPON
+                        # vENET information is created. Once xPON is removed, we need to create
+                        # them from the information provided from the MIB upload UNI-G and other
+                        # UNI related MEs.
+
+                        for uni in self.uni_ports:
+                            uni.add_logical_port(None, None)
+                    else:
+                        self._deferred = reactor.callLater(5, self.in_sync_handler, _topic, msg)
+
+            except Exception as e:
+                self.log.exception('in-sync', e=e)
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index b35daab..2d5796f 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -100,7 +100,7 @@
     def check_pulse(self):
         if self.enabled:
             try:
-                self._defer = self._handler.omci.send(OntGFrame(self.check_item).get())
+                self._defer = self._handler.openomci.omci_cc.send(OntGFrame(self.check_item).get())
                 self._defer.addCallbacks(self._heartbeat_success, self._heartbeat_fail)
 
             except Exception as e:
diff --git a/voltha/adapters/adtran_onu/omci/adtn_capabilities_task.py b/voltha/adapters/adtran_onu/omci/adtn_capabilities_task.py
new file mode 100644
index 0000000..6be8fd7
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_capabilities_task.py
@@ -0,0 +1,143 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from voltha.extensions.omci.tasks.onu_capabilities_task import OnuCapabilitiesTask
+from twisted.internet.defer import failure
+
+
+class AdtnCapabilitiesTask(OnuCapabilitiesTask):
+    """
+    OpenOMCI MIB Capabilities Task - ADTRAN ONUs
+
+    This task requests information on supported MEs via the OMCI (ME#287)
+    Managed entity.
+
+    This task should be ran after MIB Synchronization and before any MIB
+    Downloads to the ONU.
+
+    Upon completion, the Task deferred callback is invoked with dictionary
+    containing the supported managed entities and message types.
+
+    results = {
+                'supported-managed-entities': {set of supported managed entities},
+                'supported-message-types': {set of supported message types}
+              }
+    """
+    def __init__(self, omci_agent, device_id):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param device_id: (str) ONU Device ID
+        """
+        super(AdtnCapabilitiesTask, self).__init__(omci_agent, device_id)
+        self._omci_managed = False      # TODO: Look up capabilities/model number
+
+    @property
+    def supported_managed_entities(self):
+        """
+        Return a set of the Managed Entity class IDs supported on this ONU
+
+        None is returned if not MEs have been discovered
+
+        :return: (set of ints)
+        """
+        if self._omci_managed:
+            return super(AdtnCapabilitiesTask, self).supported_managed_entities
+
+        me_1287800f1 = [
+            2, 5, 6, 7, 11, 24, 45, 46, 47, 48, 49, 50, 51, 52, 79, 84, 89, 130,
+            131, 133, 134, 135, 136, 137, 148, 157, 158, 159, 171, 256, 257, 262,
+            263, 264, 266, 268, 272, 273, 274, 277, 278, 279, 280, 281, 297, 298,
+            299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+            329, 330, 332, 334, 336, 340, 341, 342, 343, 348, 425, 426, 65300,
+            65400, 65401, 65402, 65403, 65404, 65406, 65407, 65408, 65409, 65410,
+            65411, 65412, 65413, 65414, 65420, 65421, 65422, 65423, 65424
+        ]
+        return frozenset(list(me_1287800f1))
+
+    @property
+    def supported_message_types(self):
+        """
+        Return a set of the Message Types supported on this ONU
+
+        None is returned if no message types have been discovered
+
+        :return: (set of EntityOperations)
+        """
+        if self._omci_managed:
+            return super(AdtnCapabilitiesTask, self).supported_message_types
+
+        from voltha.extensions.omci.omci_entities import EntityOperations
+        op_11287800f1 = [
+            EntityOperations.Create,
+            EntityOperations.CreateComplete,
+            EntityOperations.Delete,
+            EntityOperations.Set,
+            EntityOperations.Get,
+            EntityOperations.GetComplete,
+            EntityOperations.GetAllAlarms,
+            EntityOperations.GetAllAlarmsNext,
+            EntityOperations.MibUpload,
+            EntityOperations.MibUploadNext,
+            EntityOperations.MibReset,
+            EntityOperations.AlarmNotification,
+            EntityOperations.AttributeValueChange,
+            EntityOperations.Test,
+            EntityOperations.StartSoftwareDownload,
+            EntityOperations.DownloadSection,
+            EntityOperations.EndSoftwareDownload,
+            EntityOperations.ActivateSoftware,
+            EntityOperations.CommitSoftware,
+            EntityOperations.SynchronizeTime,
+            EntityOperations.Reboot,
+            EntityOperations.GetNext,
+        ]
+        return frozenset(op_11287800f1)
+
+    def perform_get_capabilities(self):
+        """
+        Perform the MIB Capabilities sequence.
+
+        The sequence is to perform a Get request with the attribute mask equal
+        to 'me_type_table'.  The response to this request will carry the size
+        of (number of get-next sequences).
+
+        Then a loop is entered and get-next commands are sent for each sequence
+        requested.
+        """
+        self.log.info('perform-get')
+
+        if self._omci_managed:
+            # Return generator deferred/results
+            return super(AdtnCapabilitiesTask, self).perform_get_capabilities()
+
+        # Fixed values, no need to query
+        try:
+            self._supported_entities = self.supported_managed_entities
+            self._supported_msg_types = self.supported_message_types
+
+            self.log.debug('get-success',
+                           supported_entities=self.supported_managed_entities,
+                           supported_msg_types=self.supported_message_types)
+            results = {
+                'supported-managed-entities': self.supported_managed_entities,
+                'supported-message-types': self.supported_message_types
+            }
+            self.deferred.callback(results)
+
+        except Exception as e:
+            self.log.exception('get-failed', e=e)
+            self.deferred.errback(failure.Failure(e))
diff --git a/voltha/adapters/adtran_onu/omci/adtn_get_mds_task.py b/voltha/adapters/adtran_onu/omci/adtn_get_mds_task.py
new file mode 100644
index 0000000..0de236f
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_get_mds_task.py
@@ -0,0 +1,56 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from voltha.extensions.omci.tasks.get_mds_task import GetMdsTask
+
+
+class AdtnGetMdsTask(GetMdsTask):
+    """
+    OpenOMCI Get MIB Data Sync value task - Adtran ONU
+
+    On successful completion, this task will call the 'callback' method of the
+    deferred returned by the start method and return the value of the MIB
+    Data Sync attribute of the ONT Data ME
+    """
+    name = "ADTN: Get MDS Task"
+
+    def __init__(self, omci_agent, device_id):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param device_id: (str) ONU Device ID
+        """
+        super(AdtnGetMdsTask, self).__init__(omci_agent, device_id)
+
+        self.name = AdtnGetMdsTask.name
+        self._device = omci_agent.get_device(device_id)
+        self._omci_managed = False      # TODO: Look up capabilities/model number/check handler
+
+    def perform_get_mds(self):
+        """
+        Get the 'mib_data_sync' attribute of the ONU
+        """
+        self.log.info('perform-get-mds')
+
+        if self._omci_managed:
+            return super(AdtnGetMdsTask, self).perform_get_mds()
+
+        # Non-OMCI managed ADTN ONUs always return 0 for MDS, use the MIB
+        # sync value and depend on an accelerated mib resync to do the
+        # proper comparison
+
+        self.deferred.callback(self._device.mib_synchronizer.mib_data_sync)
+
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
new file mode 100644
index 0000000..a6d6d5c
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
@@ -0,0 +1,522 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from twisted.internet import reactor
+from voltha.extensions.omci.tasks.task import Task
+from twisted.internet.defer import inlineCallbacks, TimeoutError, failure
+from voltha.extensions.omci.omci_me import OntDataFrame
+from voltha.extensions.omci.omci_defs import *
+
+
+class AdtnMibDownloadTask(Task):
+    """
+    OpenOMCI MIB Download Example
+
+    This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
+    and converts it to run as a Task on the OpenOMCI Task runner.  This is
+    in order to begin to decompose service instantiation in preparation for
+    Technology Profile work.
+
+    Once technology profiles are ready, some of this task may hang around or
+    be moved into OpenOMCI if there are any very common settings/configs to do
+    for any profile that may be provided in the v2.0 release
+
+    Currently, the only service tech profiles expected by v2.0 will be for AT&T
+    residential data service and DT residential data service.
+    """
+    task_priority = Task.DEFAULT_PRIORITY
+    name = "ADTRAN MIB Download Example Task"
+
+    def __init__(self, omci_agent, device_id):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param device_id: (str) ONU Device ID
+        """
+        super(AdtnMibDownloadTask, self).__init__(AdtnMibDownloadTask.name,
+                                                  omci_agent,
+                                                  device_id,
+                                                  priority=AdtnMibDownloadTask.task_priority)
+        self._local_deferred = None
+
+    def cancel_deferred(self):
+        super(AdtnMibDownloadTask, self).cancel_deferred()
+
+        d, self._local_deferred = self._local_deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
+
+    def start(self):
+        """
+        Start the MIB Download
+        """
+        super(AdtnMibDownloadTask, self).start()
+        self._local_deferred = reactor.callLater(0, self.perform_mib_download)
+
+    def stop(self):
+        """
+        Shutdown MIB Synchronization tasks
+        """
+        self.log.debug('stopping')
+
+        self.cancel_deferred()
+        super(AdtnMibDownloadTask, self).stop()
+
+    @inlineCallbacks
+    def perform_mib_download(self):
+        """
+        Send the commands
+        """
+        self.log.info('perform-download')
+
+        # if not self.enabled:
+        #     returnValue('not-enabled')
+        #
+        # device = self._handler.adapter_agent.get_device(self._handler.device_id)
+        #
+        # def resources_available():
+        #     return (device.vlan > 0 and
+        #             len(self._handler.uni_ports) > 0 and
+        #             len(self._handler.pon_port.tconts) and
+        #             len(self._handler.pon_port.gem_ports))
+        #
+        # if not self._bridge_initialized and resources_available():
+        #     device.reason = 'Performing OMCI Download'
+        #     self._handler.adapter_agent.update_device(device)
+        #
+        #     omci = self.omci_cc
+        #
+        #     #############################################
+        #     #  All our variables here
+        #     #  TODO: Move elsewhere in future version of this software
+        #     #  TODO: Make as many entity IDs dynamic/discovered as possible
+        #
+        #     frame = None
+        #     gal_enet_profile_entity_id = 0x100
+        #     ieee_mapper_service_profile_entity_id = 0x100
+        #     mac_bridge_service_profile_entity_id = 0x100
+        #     mac_bridge_port_ani_entity_id = 0x100
+        #     ethernet_uni_entity_id = 0x101  # TODO: This can be retrieved from the UNI-G instance_id
+        #     vlan_tcis_1 = 0x900
+        #     vlan_config_entity_id = vlan_tcis_1
+        #     cvid = device.vlan
+        #
+        #     try:
+        #         ################################################################################
+        #         # TCONTS
+        #         #
+        #         #  EntityID will be referenced by:
+        #         #            - GemPortNetworkCtp
+        #         #  References:
+        #         #            - ONU created TCONT (created on ONU startup)
+        #
+        #         omci_dev = self._onu_omci_device
+        #         tcont_idents = omci_dev.query_mib(Tcont.class_id)
+        #         self.log.debug('tcont-idents', tcont_idents=tcont_idents)
+        #
+        #         for tcont in self._handler.pon_port.tconts.itervalues():
+        #             free_entity_id = next((k for k, v in tcont_idents.items()
+        #                                    if isinstance(k, int) and
+        #                                    v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
+        #             if free_entity_id is None:
+        #                 self.log.error('no-available-tconts')
+        #                 break
+        #
+        #             yield tcont.add_to_hardware(omci, free_entity_id)
+        #
+        #         ################################################################################
+        #         # GEMS  (GemPortNetworkCtp and GemInterworkingTp)
+        #         #
+        #         #  For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
+        #         #  GemInterworkingTp ME could be different since it has an attribute to specify
+        #         #  the GemPortNetworkCtp entity id.
+        #         #
+        #         #  TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
+        #         #        for the GemPortNetworkCtp ME
+        #         #
+        #         #  GemPortNetworkCtp
+        #         #    EntityID will be referenced by:
+        #         #              - GemInterworkingTp
+        #         #    References:
+        #         #              - TCONT
+        #         #              - Hardcoded upstream TM Entity ID
+        #         #              - (Possibly in Future) Upstream Traffic descriptor profile pointer
+        #         #
+        #         #  GemInterworkingTp
+        #         #    EntityID will be referenced by:
+        #         #              - Ieee8021pMapperServiceProfile
+        #         #    References:
+        #         #              - GemPortNetworkCtp
+        #         #              - Ieee8021pMapperServiceProfile
+        #         #              - GalEthernetProfile
+        #         #
+        #         for gem_port in self._handler.pon_port.gem_ports.itervalues():
+        #             tcont = gem_port.tcont
+        #             if tcont is None:
+        #                 self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+        #                 continue
+        #
+        #             yield gem_port.add_to_hardware(omci,
+        #                                            tcont.entity_id,
+        #                                            ieee_mapper_service_profile_entity_id,
+        #                                            gal_enet_profile_entity_id)
+        #
+        #         ########################################################################################
+        #         # Create GalEthernetProfile - Once per ONU/PON interface
+        #         #
+        #         #  EntityID will be referenced by:
+        #         #            - GemInterworkingTp
+        #         #  References:
+        #         #            - Nothing
+        #
+        #         frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
+        #                                         max_gem_payload_size=1518).create()  # Max GEM Payload size
+        #         results = yield omci.send(frame)
+        #
+        #         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)
+        #
+        #         ################################################################################
+        #         # MAC Bridge Service Profile - Once per UNI
+        #         #
+        #         #  EntityID will be referenced by:
+        #         #            - MAC Bridge Port Configuration Data
+        #         #  References:
+        #         #            - Nothing
+        #
+        #         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)
+        #
+        #         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)
+        #
+        #         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
+        #             # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
+        #             port_num=0,  # Port ID
+        #             tp_type=3,  # TP Type (IEEE 802.1p mapper service)
+        #             tp_pointer=ieee_mapper_service_profile_entity_id  # TP ID, 8021p mapper ID
+        #         ).create()
+        #         results = yield omci.send(frame)
+        #
+        #         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
+        #         # This configuration is for Ethernet UNI
+        #         #
+        #         #  EntityID will be referenced by:
+        #         #            - Nothing
+        #         #  References:
+        #         #            - MAC Bridge Service Profile (the bridge)
+        #         #            - PPTP Ethernet UNI
+        #
+        #         frame = MacBridgePortConfigurationDataFrame(
+        #             0x000,  # Entity ID - This is read-only/set-by-create !!!
+        #             bridge_id_pointer=mac_bridge_service_profile_entity_id,  # Bridge Entity ID
+        #             port_num=0,  # Port ID
+        #             tp_type=1,  # PPTP Ethernet UNI
+        #             tp_pointer=ethernet_uni_entity_id  # TP ID, 8021p mapper Id
+        #         ).create()
+        #         results = yield omci.send(frame)
+        #
+        #         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:
+        #         #            - MacBridgePortConfigurationData for the ANI/PON side
+        #         #
+        #         # Set anything, this request will not be used when using Extended Vlan
+        #
+        #         frame = VlanTaggingFilterDataFrame(
+        #             mac_bridge_port_ani_entity_id,  # Entity ID
+        #             vlan_tcis=[vlan_tcis_1],  # VLAN IDs
+        #             forward_operation=0x10
+        #         ).create()
+        #         results = yield omci.send(frame)
+        #
+        #         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. This is a set
+        #         #
+        #         #  References:
+        #         #            - Gem Interwork TPs are set here
+        #         #
+        #         # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+        #         gem_ports = self._handler.pon_port.gem_ports
+        #         gem_entity_ids = [gem_port.entity_id for _, gem_port in self._gem_ports.items()] \
+        #             if len(self._gem_ports) else [OmciNullPointer]
+        #
+        #         frame = Ieee8021pMapperServiceProfileFrame(
+        #             ieee_mapper_service_profile_entity_id,  # 802.1p mapper Service Mapper Profile ID
+        #             interwork_tp_pointers=gem_entity_ids  # Interworking TP IDs
+        #         ).set()
+        #         results = yield omci.send(frame)
+        #
+        #         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)
+        #
+        #         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
+        #             associated_me_pointer=ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id
+        #         )
+        #
+        #         frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+        #             vlan_config_entity_id,
+        #             attributes=attributes
+        #         ).create()
+        #         results = yield omci.send(frame)
+        #
+        #         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,
+        #             attributes=attributes
+        #         ).set()
+        #         results = yield omci.send(frame)
+        #
+        #         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)
+        #
+        #         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(
+        #         #                                 0x200,  # Entity ID
+        #         #                                 8,      # Filter Inner Priority
+        #         #                                 0,      # Filter Inner VID
+        #         #                                 0,      # Filter Inner TPID DE
+        #         #                                 1,      # Treatment tags to remove
+        #         #                                 8,      # Treatment inner priority
+        #         #                                 cvid)   # Treatment inner VID
+        #         #
+        #         # 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(
+        #         #                                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
+        #         self._bridge_initialized = True
+        #         device.reason = ''
+        #         self._handler.adapter_agent.update_device(device)
+        #
+        #     except TimeoutError as e:
+        #         self.log.warn('rx-timeout', frame=frame)
+        #         self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+        #                                            self.resync_omci_settings)
+        #         returnValue('retry-pending')
+        #
+        #     except Exception as e:
+        #         self.log.exception('omci-setup', e=e)
+        #         device.reason = 'OMCI setup sequence failure: ' + e.message
+        #         self._handler.adapter_agent.update_device(device)
+        #
+        #         # Try again later
+        #         self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+        #                                            self.resync_omci_settings)
+        # else:
+        #     self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+        #                                        self.resync_omci_settings)
+
+        self.deferred.callback("TODO: Done, what should we provide back that is of value?")
+        #
+        # except TimeoutError as e:
+        #     self.log.warn('download-timeout', e=e)
+        #     self.deferred.errback(failure.Failure(e))
+        #     # TODO: Recover any allocated objects (tconts, priority queues, ...)
+        # except Exception as e:
+        #     self.log.exception('download', e=e)
+        #     self.deferred.errback(failure.Failure(e))
+        #     TODO: Recover any allocated objects (tconts, priority queues, ...)
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
new file mode 100644
index 0000000..21e11d4
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
@@ -0,0 +1,59 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from voltha.extensions.omci.state_machines.mib_sync import MibSynchronizer
+
+
+class AdtnMibSynchronizer(MibSynchronizer):
+    """
+    OpenOMCI MIB Synchronizer state machine for Adtran ONUs
+    """
+    ADTN_RESYNC_DELAY = 60     # Periodically force a resync
+
+    def __init__(self, agent, device_id, mib_sync_tasks, db):
+        """
+        Class initialization
+
+        :param agent: (OpenOmciAgent) Agent
+        :param device_id: (str) ONU Device ID
+        :param db: (MibDbVolatileDict) MIB Database
+        :param mib_sync_tasks: (dict) Tasks to run
+        """
+        super(AdtnMibSynchronizer, self).__init__(agent, device_id, mib_sync_tasks, db,
+                                                  # states=MibSynchronizer.DEFAULT_STATES,
+                                                  # transitions=MibSynchronizer.DEFAULT_TRANSITIONS,
+                                                  # initial_state='disabled',
+                                                  # timeout_delay=MibSynchronizer.DEFAULT_TIMEOUT_RETRY,
+                                                  # audit_delay=MibSynchronizer.DEFAULT_AUDIT_DELAY,
+                                                  resync_delay=AdtnMibSynchronizer.ADTN_RESYNC_DELAY)
+        self._omci_managed = False      # TODO: Look up model number/check handler
+
+    def on_enter_auditing(self):
+        """
+        Perform a MIB Audit.  If our last MIB resync was too long in the
+        past, perform a resynchronization anyway
+        """
+        # Is this a model that supports full OMCI management. If so, use standard
+        # forced resync delay
+
+        if not self._omci_managed and self._check_if_mib_data_sync_supported():
+            self._omci_managed = True
+            # Revert to standard timeouts
+            self._resync_delay = MibSynchronizer.DEFAULT_RESYNC_DELAY
+
+        super(AdtnMibSynchronizer, self).on_enter_auditing()
+
+    def _check_if_mib_data_sync_supported(self):
+        return False    # TODO: Look up to see if we are/check handler
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
new file mode 100644
index 0000000..ea7abaf
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -0,0 +1,784 @@
+# Copyright 2018-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.
+
+import structlog
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError
+from twisted.internet import reactor
+
+from voltha.protos.device_pb2 import Image
+
+from voltha.protos.common_pb2 import OperStatus, ConnectStatus
+from voltha.extensions.omci.onu_configuration import OMCCVersion
+
+from omci_entities import onu_custom_me_entities
+from voltha.extensions.omci.omci_me import *
+
+_STARTUP_RETRY_WAIT = 5
+# abbreviations
+OP = EntityOperations
+
+
+class OMCI(object):
+    """
+    OpenOMCI Support
+    """
+    def __init__(self, handler, omci_agent):
+        self.log = structlog.get_logger(device_id=handler.device_id)
+        self._handler = handler
+        self._openomci_agent = omci_agent
+        self._enabled = False
+        self._connected = False
+        self._deferred = None
+        self._resync_deferred = None    # For TCont/GEM use
+        self._bridge_initialized = False
+        self._in_sync_reached = False
+
+        self._omcc_version = OMCCVersion.Unknown
+        self._total_tcont_count = 0                    # From ANI-G ME
+        self._qos_flexibility = 0                      # From ONT2_G ME
+
+        self._in_sync_subscription = None
+        self._connectivity_subscription = None
+        self._capabilities_subscription = None
+
+        self._mib_download_task = None
+        self._mib_download_deferred = None
+
+        self._onu_omci_device = omci_agent.add_device(handler.device_id,
+                                                      handler.adapter_agent,
+                                                      onu_custom_me_entities(),
+                                                      support_classes=handler.adapter.adtran_omci)
+
+    def __str__(self):
+        return "OMCI"
+
+    @property
+    def omci_agent(self):
+        return self._openomci_agent
+
+    @property
+    def omci_cc(self):
+        # TODO: Decrement access to Communications channel at this point?  What about current PM stuff?
+        return self.onu_omci_device.omci_cc if self._onu_omci_device is not None else None
+
+    def receive_message(self, msg):
+        if self.enabled:
+            # TODO: Have OpenOMCI actually receive the messages
+            self.omci_cc.receive_message(msg)
+
+    def _start(self):
+        self._cancel_deferred()
+
+        # Subscriber to events of interest in OpenOMCI
+        self._subscribe_to_events()
+        self._onu_omci_device.start()
+
+        if self._onu_omci_device.mib_db_in_sync:
+            self._deferred = reactor.callLater(0, self._mib_in_sync)
+
+    def _stop(self):
+        self._cancel_deferred()
+
+        # Unsubscribe to OpenOMCI Events
+        self._unsubscribe_to_events()
+        self._onu_omci_device.stop()        # Will also cancel any running tasks/state-machines
+
+        self._mib_download_task = None
+        self._bridge_initialized = False
+        self._in_sync_reached = False
+
+        # TODO: stop h/w sync
+
+    def _cancel_deferred(self):
+        d1, self._deferred = self._deferred, None
+        d2, self._resync_deferred = self._resync_deferred, None
+        d3, self._mib_download_deferred = self._mib_download_deferred, None
+
+        for d in [d1, d2, d3]:
+            try:
+                if d is not None and not d.called:
+                    d.cancel()
+            except:
+                pass
+
+    def _cancel_resync_deferred(self):
+        d, self._resync_deferred = self._resync_deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
+
+    def delete(self):
+        self.enabled = False
+
+        agent, self._openomci_agent = self._openomci_agent, None
+        device_id = self._handler.device_id
+        self._onu_omci_device = None
+        self._handler = None
+
+        if agent is not None:
+            agent(device_id, cleanup=True)
+
+    @property
+    def enabled(self):
+        return self._enabled
+
+    @enabled.setter
+    def enabled(self, value):
+        if self._enabled != value:
+            self._enabled = value
+
+            if value:
+                self._start()
+            else:
+                self._stop()
+
+    @property
+    def connected(self):
+        return self._connected
+
+    @property
+    def onu_omci_device(self):
+        return self._onu_omci_device
+
+    def _mib_in_sync(self):
+        """
+        This method is ran whenever the ONU MIB database is in-sync. This is often after
+        the initial MIB Upload during ONU startup, or after it has gone out-of-sync and
+        then back in. This second case could be due a reboot of the ONU and a new version
+        of firmware is running on the ONU hardware.
+        """
+        self.log.info('mib-in-sync')
+
+        device = self._handler.adapter_agent.get_device(self._handler.device_id)
+        device.oper_status = OperStatus.ACTIVE
+        device.connect_status = ConnectStatus.REACHABLE
+        device.reason = 'MIB Synchronization complete'
+        self._handler.adapter_agent.update_device(device)
+
+        omci_dev = self._onu_omci_device
+        config = omci_dev.configuration
+
+        # In Sync, we can register logical ports now. Ideally this could occur on
+        # the first time we received a successful (no timeout) OMCI Rx response.
+        try:
+            device = self._handler.adapter_agent.get_device(self._handler.device_id)
+
+            ani_g = config.ani_g_entities
+            uni_g = config.uni_g_entities
+            pon_ports = len(ani_g) if ani_g is not None else 0
+            uni_ports = len(uni_g) if uni_g is not None else 0
+
+            assert pon_ports == 1, 'Expected one PON/ANI port, got {}'.format(pon_ports)
+            assert uni_ports == 1, 'Expected one UNI port, got {}'.format(uni_ports)
+
+            self._total_tcont_count = ani_g.get('total-tcont-count')
+            self._qos_flexibility = config.qos_configuration_flexibility or 0
+            self._omcc_version = config.omcc_version or OMCCVersion.Unknown
+
+            # vendorProductCode = str(config.vendor_product_code or 'unknown').rstrip('\0')
+
+            host_info = omci_dev.query_mib(IpHostConfigData.class_id)
+            mgmt_mac_address = next((host_info[inst].get('attributes').get('mac_address')
+                                     for inst in host_info
+                                     if isinstance(inst, int)), 'unknown')
+            device.mac_address = str(mgmt_mac_address)
+            device.model = str(config.version or 'unknown').rstrip('\0')
+
+            equipment_id = config.equipment_id or " unknown    unknown "
+            eqpt_boot_version = str(equipment_id).rstrip('\0')
+            # eqptId = eqpt_boot_version[:10]         # ie) BVMDZ10DRA
+            boot_version = eqpt_boot_version[12:]     # ie) CML.D55~
+
+            images = [Image(name='boot-code',
+                            version=boot_version.rstrip('\0'),
+                            is_active=False,
+                            is_committed=True,
+                            is_valid=True,
+                            install_datetime='Not Available',
+                            hash='Not Available')] + \
+                config.software_images
+
+            del (device.images.image[:])       # Clear previous entries
+            device.images.image.extend(images)
+
+            # Save our device information
+            self._handler.adapter_agent.update_device(device)
+
+            # Start MIB download  TODO: This will be replaced with a MIB Download task soon
+            self._in_sync_reached = True
+            self._deferred = reactor.callLater(0, self.resync_omci_settings)
+
+        except Exception as e:
+            self.log.exception('device-info-load', e=e)
+            self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync())
+
+    def gem_or_tcont_added(self):
+        if self._in_sync_reached:
+            self._cancel_resync_deferred()
+            self._resync_deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                                      self.resync_omci_settings)
+
+    @inlineCallbacks
+    def resync_omci_settings(self):
+        #
+        # TODO: All of these steps below are being moved into an OpenOMCI Task.  !!!!
+        #
+        #  This will first be the AdtnMibDownloadTask task. As more ONUs are converted
+        #  to OpenOMCI, I am hoping to come up with a shared/generic version all can use
+        #
+        #  Note also that this sets up everything for the user right now. It will be refactored
+        #  once Service Tech Profiles are available.
+        #
+        self._cancel_resync_deferred()
+        self.log.debug('resync-omci-settings', initialized=self._bridge_initialized)
+
+        if not self.enabled:
+            returnValue('not-enabled')
+
+        device = self._handler.adapter_agent.get_device(self._handler.device_id)
+
+        def resources_available():
+            return (device.vlan > 0 and
+                    len(self._handler.uni_ports) > 0 and
+                    len(self._handler.pon_port.tconts) and
+                    len(self._handler.pon_port.gem_ports))
+
+        if not self._bridge_initialized and self._in_sync_reached and resources_available():
+            device.reason = 'Performing OMCI Download'
+            self._handler.adapter_agent.update_device(device)
+
+            omci = self.omci_cc
+
+            #############################################
+            #  All our variables here
+            #  TODO: Move elsewhere in future version of this software
+            #  TODO: Make as many entity IDs dynamic/discovered as possible
+
+            frame = None
+            gal_enet_profile_entity_id = 0x100
+            ieee_mapper_service_profile_entity_id = 0x100
+            mac_bridge_service_profile_entity_id = 0x100
+            mac_bridge_port_ani_entity_id = 0x100
+            ethernet_uni_entity_id = 0x101          # TODO: This can be retrieved from the UNI-G instance_id
+            vlan_tcis_1 = 0x900
+            vlan_config_entity_id = vlan_tcis_1
+            cvid = device.vlan
+
+            try:
+                ################################################################################
+                # TCONTS
+                #
+                #  EntityID will be referenced by:
+                #            - GemPortNetworkCtp
+                #  References:
+                #            - ONU created TCONT (created on ONU startup)
+
+                omci_dev = self._onu_omci_device
+                tcont_idents = omci_dev.query_mib(Tcont.class_id)
+                self.log.debug('tcont-idents', tcont_idents=tcont_idents)
+
+                for tcont in self._handler.pon_port.tconts.itervalues():
+                    free_entity_id = next((k for k, v in tcont_idents.items()
+                                          if isinstance(k, int) and
+                                           v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
+                    if free_entity_id is None:
+                        self.log.error('no-available-tconts')
+                        break
+
+                    yield tcont.add_to_hardware(omci, free_entity_id)
+
+                ################################################################################
+                # GEMS  (GemPortNetworkCtp and GemInterworkingTp)
+                #
+                #  For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
+                #  GemInterworkingTp ME could be different since it has an attribute to specify
+                #  the GemPortNetworkCtp entity id.
+                #
+                #  TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
+                #        for the GemPortNetworkCtp ME
+                #
+                #  GemPortNetworkCtp
+                #    EntityID will be referenced by:
+                #              - GemInterworkingTp
+                #    References:
+                #              - TCONT
+                #              - Hardcoded upstream TM Entity ID
+                #              - (Possibly in Future) Upstream Traffic descriptor profile pointer
+                #
+                #  GemInterworkingTp
+                #    EntityID will be referenced by:
+                #              - Ieee8021pMapperServiceProfile
+                #    References:
+                #              - GemPortNetworkCtp
+                #              - Ieee8021pMapperServiceProfile
+                #              - GalEthernetProfile
+                #
+                for gem_port in self._handler.pon_port.gem_ports.itervalues():
+                    tcont = gem_port.tcont
+                    if tcont is None:
+                        self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+                        continue
+
+                    yield gem_port.add_to_hardware(omci,
+                                                   tcont.entity_id,
+                                                   ieee_mapper_service_profile_entity_id,
+                                                   gal_enet_profile_entity_id)
+
+                ########################################################################################
+                # Create GalEthernetProfile - Once per ONU/PON interface
+                #
+                #  EntityID will be referenced by:
+                #            - GemInterworkingTp
+                #  References:
+                #            - Nothing
+
+                frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
+                                                max_gem_payload_size=1518).create()  # Max GEM Payload size
+                results = yield omci.send(frame)
+
+                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)
+
+                ################################################################################
+                # MAC Bridge Service Profile - Once per UNI
+                #
+                #  EntityID will be referenced by:
+                #            - MAC Bridge Port Configuration Data
+                #  References:
+                #            - Nothing
+
+                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)
+
+                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)
+
+                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
+                    # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
+                    port_num=0,                                              # Port ID
+                    tp_type=3,                                               # TP Type (IEEE 802.1p mapper service)
+                    tp_pointer=ieee_mapper_service_profile_entity_id         # TP ID, 8021p mapper ID
+                ).create()
+                results = yield omci.send(frame)
+
+                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
+                # This configuration is for Ethernet UNI
+                #
+                #  EntityID will be referenced by:
+                #            - Nothing
+                #  References:
+                #            - MAC Bridge Service Profile (the bridge)
+                #            - PPTP Ethernet UNI
+
+                frame = MacBridgePortConfigurationDataFrame(
+                    0x000,                             # Entity ID - This is read-only/set-by-create !!!
+                    bridge_id_pointer=mac_bridge_service_profile_entity_id,  # Bridge Entity ID
+                    port_num=0,                        # Port ID
+                    tp_type=1,                         # PPTP Ethernet UNI
+                    tp_pointer=ethernet_uni_entity_id  # TP ID, 8021p mapper Id
+                ).create()
+                results = yield omci.send(frame)
+
+                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:
+                #            - MacBridgePortConfigurationData for the ANI/PON side
+                #
+                # Set anything, this request will not be used when using Extended Vlan
+
+                frame = VlanTaggingFilterDataFrame(
+                    mac_bridge_port_ani_entity_id,       # Entity ID
+                    vlan_tcis=[vlan_tcis_1],             # VLAN IDs
+                    forward_operation=0x10
+                ).create()
+                results = yield omci.send(frame)
+
+                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. This is a set
+                #
+                #  References:
+                #            - Gem Interwork TPs are set here
+                #
+                # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+                gem_ports = self._handler.pon_port.gem_ports
+                gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
+                    if len(gem_ports) else [OmciNullPointer]
+
+                frame = Ieee8021pMapperServiceProfileFrame(
+                    ieee_mapper_service_profile_entity_id,   # 802.1p mapper Service Mapper Profile ID
+                    interwork_tp_pointers=gem_entity_ids     # Interworking TP IDs
+                ).set()
+                results = yield omci.send(frame)
+
+                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)
+
+                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
+                    associated_me_pointer=ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id
+                )
+
+                frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                    vlan_config_entity_id,
+                    attributes=attributes
+                ).create()
+                results = yield omci.send(frame)
+
+                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,
+                    attributes=attributes
+                ).set()
+                results = yield omci.send(frame)
+
+                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)
+
+                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(
+                #                                 0x200,  # Entity ID
+                #                                 8,      # Filter Inner Priority
+                #                                 0,      # Filter Inner VID
+                #                                 0,      # Filter Inner TPID DE
+                #                                 1,      # Treatment tags to remove
+                #                                 8,      # Treatment inner priority
+                #                                 cvid)   # Treatment inner VID
+                #
+                # 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(
+                #                                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
+                self._bridge_initialized = True
+                device.reason = ''
+                self._handler.adapter_agent.update_device(device)
+
+            except TimeoutError as e:
+                self.log.warn('rx-timeout', frame=frame)
+                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                                   self.resync_omci_settings)
+                returnValue('retry-pending')
+
+            except Exception as e:
+                self.log.exception('omci-setup', e=e)
+                device.reason = 'OMCI setup sequence failure: ' + e.message
+                self._handler.adapter_agent.update_device(device)
+
+                # Try again later
+                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                                   self.resync_omci_settings)
+        else:
+            self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                               self.resync_omci_settings)
+
+    def _subscribe_to_events(self):
+        from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
+            OnuDeviceEntry
+        from voltha.extensions.omci.omci_cc import OMCI_CC, OmciCCRxEvents
+
+        # OMCI MIB Database sync status
+
+        bus = self._onu_omci_device.event_bus
+        topic = OnuDeviceEntry.event_bus_topic(self._handler.device_id,
+                                               OnuDeviceEvents.MibDatabaseSyncEvent)
+        self._in_sync_subscription = bus.subscribe(topic, self.in_sync_handler)
+
+        # OMCI Capabilities (MEs and Message Types
+
+        bus = self._onu_omci_device.event_bus
+        topic = OnuDeviceEntry.event_bus_topic(self._handler.device_id,
+                                               OnuDeviceEvents.OmciCapabilitiesEvent)
+        self._capabilities_subscription = bus.subscribe(topic, self.capabilties_handler)
+
+        # OMCI-CC Connectivity Events (for reachability/heartbeat)
+
+        bus = self._onu_omci_device.omci_cc.event_bus
+        topic = OMCI_CC.event_bus_topic(self._handler.device_id,
+                                        OmciCCRxEvents.Connectivity)
+        self._connectivity_subscription = bus.subscribe(topic, self.onu_is_reachable)
+
+    def _unsubscribe_to_events(self):
+        insync, self._in_sync_subscription = self._in_sync_subscription, None
+        connect, self._connectivity_subscription = self._connectivity_subscription, None
+        caps, self._capabilities_subscription = self._capabilities_subscription, None
+
+        if insync is not None:
+            bus = self._onu_omci_device.event_bus
+            bus.unsubscribe(insync)
+
+        if connect is not None:
+            bus = self._onu_omci_device.omci_cc.event_bus
+            bus.unsubscribe(connect)
+
+        if caps is not None:
+            bus = self._onu_omci_device.event_bus
+            bus.unsubscribe(caps)
+
+    def in_sync_handler(self, _topic, msg):
+        if self._in_sync_subscription is not None:
+            try:
+                from voltha.extensions.omci.onu_device_entry import IN_SYNC_KEY
+
+                if msg[IN_SYNC_KEY]:
+                    # Start up device_info load from MIB DB
+                    reactor.callLater(0, self._mib_in_sync)
+                else:
+                    # Cancel any running/scheduled MIB download task
+                    try:
+                        d, self._mib_download_deferred = self._mib_download_deferred, None
+                        d.cancel()
+                    except:
+                        pass
+
+            except Exception as e:
+                self.log.exception('in-sync', e=e)
+
+    def capabilties_handler(self, _topic, _msg):
+        """
+        This event occurs after an ONU reaches the In-Sync state and the OMCI ME has
+        been queried for supported ME and message types.
+
+        At this point, we can act upon any download device and/or service Technology
+        profiles (when they exist).  For now, just run our somewhat fixed script
+        """
+        if self._capabilities_subscription is not None:
+            from adtn_mib_download_task import AdtnMibDownloadTask
+
+            def success(_results):
+                self._mib_download_task = None
+                pass   # TODO.  What's next...
+
+            def failure(_reason):
+                self._mib_download_task = None
+                # TODO: Handle failure, retry for now?
+                self._mib_download_deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                                                self.capabilties_handler)
+            self._mib_download_task = AdtnMibDownloadTask(self.omci_agent, self._handler.device_id)
+            self._mib_download_deferred = self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
+            self._mib_download_deferred.addCallbacks(success, failure)
+
+    def onu_is_reachable(self, _topic, msg):
+        """
+        Reach-ability change event
+        :param _topic: (str) subscription topic, not used
+        :param msg: (dict) 'connected' key holds True if reachable
+        """
+        from voltha.extensions.omci.omci_cc import CONNECTED_KEY
+        if self._connectivity_subscription is not None:
+            try:
+                connected = msg[CONNECTED_KEY]
+
+                # TODO: For now, only care about the first connect occurrence.
+                # Later we could use this for a heartbeat, but may want some hysteresis
+                # Cancel any 'reachable' subscriptions
+                if connected:
+                    evt_bus = self._onu_omci_device.omci_cc.event_bus
+                    evt_bus.unsubscribe(self._connectivity_subscription)
+                    self._connectivity_subscription = None
+                    self._connected = True
+
+                    device = self._handler.adapter_agent.get_device(self._handler.device_id)
+                    device.oper_status = OperStatus.ACTIVE
+                    device.connect_status = ConnectStatus.REACHABLE
+                    self._handler.adapter_agent.update_device(device)
+
+            except Exception as e:
+                self.log.exception('onu-reachable', e=e)
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index 0cff8ac..03ea477 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -98,7 +98,10 @@
                     port_id=self.gem_id,
                     tcont_id=tcont_entity_id,
                     direction=direction,
-                    upstream_tm=0x8000         # TM ID, 32768 unique ID set in TD set  TODO: Parameterize
+                    upstream_tm=0x8000      # TM ID, 32768 unique ID set in TD set  TODO: Parameterize
+                                            # This is Priority Queue ME with this entity ID
+                                            # and the ME's related port value is 0x01.00.0007
+                                            # which is  slot=0x01, tcont# = 0x00, priority= 0x0007
             ).create()
             results = yield omci.send(frame)
 
diff --git a/voltha/adapters/adtran_onu/onu_pm_metrics.py b/voltha/adapters/adtran_onu/onu_pm_metrics.py
index d4aa5f6..20a45b3 100644
--- a/voltha/adapters/adtran_onu/onu_pm_metrics.py
+++ b/voltha/adapters/adtran_onu/onu_pm_metrics.py
@@ -128,7 +128,7 @@
 
     def collect_port_metrics(self):
         metrics = dict()
-        metrics['omci'] = self.collect_metrics(self.handler.omci,
+        metrics['omci'] = self.collect_metrics(self.handler.openomci.omci_cc,
                                                self.omci_pm_names,
                                                self.omci_metrics_config)
 
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index 63c410e..1cfa003 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -13,54 +13,32 @@
 # limitations under the License.
 
 import structlog
-from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError
-from twisted.internet import reactor
-
-from voltha.protos.common_pb2 import AdminState
-from voltha.protos.device_pb2 import Port, Image
-
-from voltha.protos.common_pb2 import OperStatus, ConnectStatus
-
-from omci.omci_entities import onu_custom_me_entities
-from voltha.extensions.omci.omci_me import *
-
-_STARTUP_RETRY_WAIT = 5
-# abbreviations
-OP = EntityOperations
+from twisted.internet.defer import inlineCallbacks, returnValue
+from voltha.protos.common_pb2 import AdminState, OperStatus
+from voltha.protos.device_pb2 import Port
 
 
 class PonPort(object):
-    """Wraps northbound-port / vlan bridge support for ONU"""
+    """Wraps northbound-port/ANI support for ONU"""
     MIN_GEM_ENTITY_ID = 0x4900
     MAX_GEM_ENTITY_ID = 0x4AFF
 
     def __init__(self, handler, port_no):
+        self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
+
         self._enabled = False
         self._valid = True
         self._handler = handler
         self._deferred = None
-        self._resync_deferred = None    # For TCont/GEM use
         self._port = None
         self._port_number = port_no
-        self._connected = False
-        self._dev_info_loaded = False
-        self._bridge_initialized = False
         self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
-        self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
 
         self._admin_state = AdminState.ENABLED
         self._oper_status = OperStatus.ACTIVE
 
         self._gem_ports = {}                           # gem-id -> GemPort
         self._tconts = {}                              # alloc-id -> TCont
-        self._in_sync_subscription = None
-        self._connectivity_subscription = None
-
-        self._onu_omci_device = handler.omci_agent.add_device(handler.device_id,
-                                                              handler.adapter_agent,
-                                                              onu_custom_me_entities(),
-                                                              support_classes=handler.adapter.adtran_omci)
-        # TODO: Add stats, alarm reference, ...
 
     def __str__(self):
         return "PonPort"      # TODO: Encode current state
@@ -77,55 +55,28 @@
         self._oper_status = OperStatus.ACTIVE
         self._update_adapter_agent()
 
-        # Subscriber to events of interest in OpenOMCI
-        self._subscribe_to_events()
-        self._onu_omci_device.start()
-
-        # Begin ONU Activation sequence if already in sync
-        if self._onu_omci_device.mib_db_in_sync:
-            self._deferred = reactor.callLater(0, self._mib_in_sync)
-        else:
-            device = self._handler.adapter_agent.get_device(self._handler.device_id)
-            device.reason = 'Waiting for MIB upload completion'
-            self._handler.adapter_agent.update_device(device)
-
     def _stop(self):
         self._cancel_deferred()
-        # Unsubscribe to OpenOMCI Events
-        self._unsubscribe_to_events()
-        self._onu_omci_device.stop()
 
         self._admin_state = AdminState.DISABLED
         self._oper_status = OperStatus.UNKNOWN
         self._update_adapter_agent()
 
-        self._dev_info_loaded = False
         # TODO: stop h/w sync
-        pass
 
     def _cancel_deferred(self):
         d1, self._deferred = self._deferred, None
-        d2, self._resync_deferred = self._resync_deferred, None
 
-        for d in [d1, d2]:
+        for d in [d1]:
             try:
                 if d is not None and not d.called:
                     d.cancel()
             except:
                 pass
 
-    def _cancel_resync_deferred(self):
-        d, self._resync_deferred = self._resync_deferred, None
-        try:
-            if d is not None and not d.called:
-                d.cancel()
-        except:
-            pass
-
     def delete(self):
         self.enabled = False
         self._valid = False
-        self._onu_omci_device = None
         self._handler = None
 
     @property
@@ -143,10 +94,6 @@
                 self._stop()
 
     @property
-    def connected(self):
-        return self._connected
-
-    @property
     def port_number(self):
             return self._port_number
 
@@ -160,6 +107,14 @@
 
         return entity_id
 
+    @property
+    def tconts(self):
+        return self._tconts
+
+    @property
+    def gem_ports(self):
+        return self._gem_ports
+
     def get_port(self):
         """
         Get the VOLTHA PORT object for this port
@@ -191,504 +146,9 @@
         # adapter_agent add_port also does an update of port status
         try:
             self._handler.adapter_agent.add_port(self._handler.device_id, self.get_port())
-
         except Exception as e:
             self.log.exception('update-port', e=e)
 
-    @property
-    def onu_omci_device(self):
-        return self._onu_omci_device
-
-    def _mib_in_sync(self):
-        if self._handler.is_mock:
-            return  # Done, Mock has no bridge support
-
-        if not self._dev_info_loaded:
-            # Here if in sync. But verify first
-
-            omci = self._onu_omci_device
-            in_sync = omci.mib_db_in_sync
-            self.log.info('mib-in-sync', in_sync=in_sync, already_loaded=self._dev_info_loaded)
-
-            device = self._handler.adapter_agent.get_device(self._handler.device_id)
-            device.oper_status = OperStatus.ACTIVE
-            device.connect_status = ConnectStatus.REACHABLE
-            device.reason = 'MIB Synchronization complete'
-            self._handler.adapter_agent.update_device(device)
-
-            # In Sync, we can register logical ports now. Ideally this could occur on
-            # the first time we received a successful (no timeout) OMCI Rx response.
-            try:
-                for uni in self._handler.uni_ports:
-                    uni.add_logical_port(None, None)
-
-                #BKP
-                device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
-                vendor = omci.query_mib_single_attribute(OntG.class_id, 0, 'vendor_id') or 'ADTN'
-                assert vendor == 'ADTN', \
-                    "Invalid Device/Wrong device adapter assigned: '{}'".format(vendor)
-
-                # TODO: Get serial number and validate!
-                num_ports = omci.query_mib_single_attribute(CircuitPack.class_id,
-                                                            257, 'number_of_ports') or 1
-                assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
-
-                host_info = omci.query_mib(IpHostConfigData.class_id)
-                mgmt_mac_address = next((host_info[inst].get('attributes').get('mac_address')
-                                         for inst in host_info
-                                         if isinstance(inst, int)), 'unknown')
-                device.mac_address = str(mgmt_mac_address)
-
-                ont2_attributes = omci.query_mib(Ont2G.class_id, 0, ['equipment_id',
-                                                                     'omcc_version',
-                                                                     'vendor_product_code'])
-                equipment_id = ont2_attributes.get('equipment_id') or " unknown    unknown "
-                eqptId_bootVersion = str(equipment_id).rstrip('\0')
-                eqptId = eqptId_bootVersion[0:10]          # ie) BVMDZ10DRA
-                bootVersion = eqptId_bootVersion[12:20]    # ie) CML.D55~
-
-                omcc_version = int(ont2_attributes.get('omcc_version', 0))
-                vendorProductCode = str(ont2_attributes.get('vendor_product_code', 'unknown')).rstrip('\0')
-
-                images = [Image(name='boot-code',
-                                version=bootVersion.rstrip('\0'),
-                                is_active=False,
-                                is_committed=True,
-                                is_valid=True,
-                                install_datetime='Not Available',
-                                hash='Not Available')]
-
-                model = omci.query_mib_single_attribute(OntG.class_id, 0, 'version') or 'unknown'
-                device.model = str(model).rstrip('\0')
-
-                sw_info = {k: v.get('attributes') for k, v in omci.query_mib(SoftwareImage.class_id).items()
-                           if isinstance(k, int)}
-
-                for info in sw_info.itervalues():
-                    is_active = info.get('is_active', False)
-                    if is_active:
-                        device.firmware_version = str(info.get('version', 'Not Available').rstrip('\0'))
-
-                    images.append(Image(name='running-revision' if is_active else 'candidate-revision',
-                                        version=str(info.get('version', 'Not Available').rstrip('\0')),
-                                        is_active=is_active,
-                                        is_committed=info.get('is_committed', False),
-                                        is_valid=info.get('is_valid', False),
-                                        install_datetime='Not Available',
-                                        hash=str(info.get('image_hash', 'Not Available').rstrip('\0'))))
-                device.images.image.extend(images)
-
-                # Save our device information
-                self._handler.adapter_agent.update_device(device)
-                self._dev_info_loaded = True
-                self._bridge_initialized = False
-
-            except Exception as e:
-                self.log.exception('device-info-load', e=e)
-                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync())
-
-            self._deferred = reactor.callLater(0, self.resync_omci_settings)
-
-    @inlineCallbacks
-    def resync_omci_settings(self):
-        self._cancel_resync_deferred()
-
-        device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
-        if not self._bridge_initialized and device.vlan > 0 :
-            self.log.info('resync-omci-settings', initialized=self._bridge_initialized)
-            #device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
-            if not self.enabled or device is None:
-                returnValue('not-enabled')
-
-            device.reason = 'Performing OMCI Setup'
-            self._handler.adapter_agent.update_device(device)
-
-            omci = self._handler.omci
-
-            #############################################
-            #  All our variables here
-            #  TODO: Move elsewhere in future version of this software
-            #  TODO: Make as many entity IDs dynamic/discovered as possible
-            frame = None
-            gal_enet_profile_entity_id = 0x100
-            ieee_mapper_service_profile_entity_id = 0x100
-            mac_bridge_service_profile_entity_id = 0x100
-            mac_bridge_port_ani_entity_id = 0x100
-            ethernet_uni_entity_id = 0x101
-            vlan_tcis_1 = 0x900
-            vlan_config_entity_id = vlan_tcis_1
-            cvid = device.vlan
-
-            try:
-                ################################################################################
-                # TCONTS
-                # get tconts in database
-                omci_dev = self._onu_omci_device
-                tcont_idents = omci_dev.query_mib(Tcont.class_id)
-                self.log.debug('tcont-idents', tcont_idents=tcont_idents)
-
-                for tcont in self._tconts.itervalues():
-                    free_entity_id = next((k for k, v in tcont_idents.items()
-                                          if isinstance(k, int) and v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
-                    if free_entity_id is None:
-                        self.log.error('no-available-tconts')
-                        break
-                    results = yield tcont.add_to_hardware(omci, free_entity_id)
-
-                ################################################################################
-                # GEMS
-                for gem_port in self._gem_ports.itervalues():
-                    tcont = gem_port.tcont
-                    if tcont is None:
-                        self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
-                        continue
-
-                    results = yield gem_port.add_to_hardware(omci,
-                                                             tcont.entity_id,
-                                                             ieee_mapper_service_profile_entity_id,
-                                                             gal_enet_profile_entity_id)
-
-                ########################################################################################
-                # Create GalEthernetProfile - Once per ONU/PON interface
-                #
-                #  EntityID will be referenced by:
-                #            - GEM Interworking TPs when a new GEM Port is created
-                #  References:
-                #            - Nothing
-
-                frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
-                                                max_gem_payload_size=1518).create()  # Max GEM Payload size
-                results = yield omci.send(frame)
-
-                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)
-
-                ################################################################################
-                # MAC Bridge Service Profile - Once per UNI
-                #
-                #  EntityID will be referenced by:
-                #            - MAC Bridge Port Configuration Data
-                #  References:
-                #            - Nothing
-
-                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)
-
-                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)
-
-                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)
-
-                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
-                # This configuration is for Ethernet UNI
-                #
-                #  EntityID will be referenced by:
-                #            -
-                #            -
-                #            -
-                #            -
-                #  References:
-                #            - MAC Bridge Service Profile (the bridge)
-                #            - PPTP Ethernet UNI
-
-                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)
-
-                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
-                # Set anything, this request will not be used when using Extended Vlan
-
-                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)
-
-                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
-
-                gem_entity_ids = [gem_port.entity_id for _, gem_port in self._gem_ports.items()] \
-                    if len(self._gem_ports) else [OmciNullPointer]
-
-                frame = Ieee8021pMapperServiceProfileFrame(
-                    ieee_mapper_service_profile_entity_id,   # 802.1p mapper Service Mapper Profile ID
-                    interwork_tp_pointers=gem_entity_ids     # Interworking TP IDs  BP: oldvalue self.gemid
-                ).set()
-                results = yield omci.send(frame)
-
-                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)
-
-                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
-                    associated_me_pointer=ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id
-                )
-
-                frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
-                    vlan_config_entity_id,
-                    attributes=attributes
-                ).create()
-                results = yield omci.send(frame)
-
-                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,
-                    attributes=attributes
-                ).set()
-                results = yield omci.send(frame)
-
-                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)
-
-                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(
-                #                                 0x200,  # Entity ID
-                #                                 8,      # Filter Inner Priority
-                #                                 0,      # Filter Inner VID
-                #                                 0,      # Filter Inner TPID DE
-                #                                 1,      # Treatment tags to remove
-                #                                 8,      # Treatment inner priority
-                #                                 cvid)   # Treatment inner VID
-                #
-                # 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(
-                #                                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
-                self._bridge_initialized = True
-                device.reason = ''
-                self._handler.adapter_agent.update_device(device)
-
-            except TimeoutError as e:
-                self.log.warn('rx-timeout', frame=frame)
-                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                                   self.resync_omci_settings)
-                returnValue('retry-pending')
-
-            except Exception as e:
-                self.log.exception('omci-setup', e=e)
-                device.reason = 'OMCI setup sequence failure: ' + e.message
-                self._handler.adapter_agent.update_device(device)
-
-                # Try again later
-                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                                   self.resync_omci_settings)
-        else:
-                self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
-                                                   self.resync_omci_settings)
-
     def add_tcont(self, tcont, reflow=False):
         """
         Creates/ a T-CONT with the given alloc-id
@@ -706,11 +166,8 @@
         self.log.info('add', tcont=tcont, reflow=reflow)
         self._tconts[tcont.alloc_id] = tcont
 
-        # only start setting sequence if there are at least one gem add
-        if len(self._gem_ports) > 0 and self._bridge_initialized:
-            self._cancel_resync_deferred()
-            self._bridge_initialized = False
-            self._resync_deferred = reactor.callLater(0, self.resync_omci_settings)
+        # TODO: Refactor once xPON goes away
+        self._handler.openomci.gem_or_tcont_added()
 
     def update_tcont_td(self, alloc_id, new_td):
         tcont = self._tconts.get(alloc_id)
@@ -739,8 +196,7 @@
 
         try:
             del self._tconts[alloc_id]
-
-            results = yield tcont.remove_from_hardware(self._handler.omci)
+            results = yield tcont.remove_from_hardware(self._handler.openomci.omci_cc)
             returnValue(results)
 
         except Exception as e:
@@ -772,11 +228,8 @@
         self.log.info('add', gem_port=gem_port, reflow=reflow)
         self._gem_ports[gem_port.gem_id] = gem_port
 
-        # assuming tcont was already added to start start settings sequence
-        if self._bridge_initialized:
-            self._cancel_resync_deferred()
-            self._bridge_initialized = False
-            self._resync_deferred = reactor.callLater(0, self.resync_omci_settings)
+        # TODO: Refactor once xPON goes away
+        self._handler.openomci.gem_or_tcont_added()
 
     @inlineCallbacks
     def remove_gem_id(self, gem_id):
@@ -793,84 +246,9 @@
 
         try:
             del self._gem_ports[gem_id]
-
-            results = yield gem_port.remove_from_hardware(self._handler.omci)
+            results = yield gem_port.remove_from_hardware(self._handler.openomci.omci_cc)
             returnValue(results)
 
         except Exception as ex:
             self.log.exception('gem-port-delete', e=ex)
             raise
-
-    def _subscribe_to_events(self):
-        from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
-            OnuDeviceEntry, IN_SYNC_KEY
-        from voltha.extensions.omci.omci_cc import OMCI_CC, OmciCCRxEvents, \
-            CONNECTED_KEY
-
-        def in_sync_handler(_topic, msg):
-            if self._in_sync_subscription is not None:
-                try:
-                    in_sync = msg[IN_SYNC_KEY]
-
-                    if in_sync:
-                        # Only call this once as well (after PON enable)
-                        bus = self._onu_omci_device.event_bus
-                        bus.unsubscribe(self._in_sync_subscription)
-                        self._in_sync_subscription = None
-
-                        # Start up device_info load
-                        reactor.callLater(0, self._mib_in_sync)
-
-                except Exception as e:
-                    self.log.exception('in-sync', e=e)
-
-        def onu_is_reachable(_topic, msg):
-            """
-            Reach-ability change event
-            :param _topic: (str) subscription topic, not used
-            :param msg: (dict) 'connected' key holds True if reachable
-            """
-            if self._connectivity_subscription is not None:
-                try:
-                    connected = msg[CONNECTED_KEY]
-
-                    # TODO: For now, only care about the first.
-                    # Later we could use this for a heartbeat, but may want some hysteresis
-                    # Cancel any 'reachable' subscriptions
-                    if connected:
-                        evt_bus = self._onu_omci_device.omci_cc.event_bus
-                        evt_bus.unsubscribe(self._connectivity_subscription)
-                        self._connectivity_subscription = None
-                        self._connected = True
-
-                        device = self._handler.adapter_agent.get_device(self._handler.device_id)
-                        device.oper_status = OperStatus.ACTIVE
-                        device.connect_status = ConnectStatus.REACHABLE
-                        self._handler.adapter_agent.update_device(device)
-
-                except Exception as e:
-                    self.log.exception('onu-reachable', e=e)
-
-        # OMCI MIB Database sync status
-        bus = self._onu_omci_device.event_bus
-        topic = OnuDeviceEntry.event_bus_topic(self._handler.device_id,
-                                               OnuDeviceEvents.MibDatabaseSyncEvent)
-        self._in_sync_subscription = bus.subscribe(topic, in_sync_handler)
-
-        # OMCI-CC Connectivity Events (for reachbility/heartbeat)
-        bus = self._onu_omci_device.omci_cc.event_bus
-        topic = OMCI_CC.event_bus_topic(self._handler.device_id,
-                                        OmciCCRxEvents.Connectivity)
-        self._connectivity_subscription = bus.subscribe(topic, onu_is_reachable)
-
-    def _unsubscribe_to_events(self):
-        if self._in_sync_subscription is not None:
-            bus = self._onu_omci_device.event_bus
-            bus.unsubscribe(self._in_sync_subscription)
-            self._in_sync_subscription = None
-
-        if self._connectivity_subscription is not None:
-            bus = self._onu_omci_device.omci_cc.event_bus
-            bus.unsubscribe(self._connectivity_subscription)
-            self._connectivity_subscription = None
-