VOL-1238: Adtran OLT initial support of Resource Manager
Includes some significant cleanup of deprecated xPON functionality
and stale v1.0 features

Change-Id: I7f5a48a49e04c857699b39c7f6336352d13fbfd6
diff --git a/voltha/adapters/adtran_olt/README.md b/voltha/adapters/adtran_olt/README.md
index 9e109a7..737b73b 100644
--- a/voltha/adapters/adtran_olt/README.md
+++ b/voltha/adapters/adtran_olt/README.md
@@ -14,9 +14,8 @@
 |  -T   | --rc_port          | 8081       | REST TCP Port |
 |  -z   | --zmq_port         | 5656       | ZeroMQ OMCI Proxy Port |
 |  -M   | --multicast_vlan   | 4000       | Multicast VLANs (comma-delimited) |
-|  -v   | --untagged_vlan    | 4092       | VLAN wrapper for untagged ONU frames |
 |  -Z   | --pio_port         | 5657       | PIO Service ZeroMQ Port |
-|  -X   | --xpon_enable      | False      | Support BBF WT-386 xPON CLI/NBI provisioning |
+|  -o   | --resource_mgr_key | adtran_olt | OLT Type to look up associated resource manager configuration |
 
 For example, if your Adtran OLT is address 10.17.174.193 with the default TCP ports and
 NETCONF credentials of admin/admin and REST credentials of ADMIN/ADMIN, the command line
@@ -43,6 +42,57 @@
     preprovision_olt -t adtran_olt --host_and_port 10.17.174.193:830
 ```
 
+## Resource Manager Provisioning Support
+Starting in Fall of 2018, Resource Manager Support was added as the default provisioning mechanism
+for the Adtran OLT as the xPON provisioning support will be deprecated by the v2.0 release in
+late-2018/early-2019.
+
+The Resource Manager is used to manage device PON resource pool and allocate PON resources from
+such pools. Resource Manager module currently manages assignment of ONU-ID, ALLOC-ID and
+GEM-PORT ID. The Resource Manager uses the KV store to back-up all the resource pool allocation data.
+
+The Adtran OLT adapter interacts with Resource Manager module for PON resource assignments. The
+adtranolt_resource_manager module is responsible for interfacing with the Resource Manager.
+
+The Resource Manager optionally uses olt_vendor_type specific resource ranges to initialize the
+PON resource pools. In order to utilize this option, create an entry for olt_vendor_type specific
+PON resource ranges on the KV store. Please make sure to use the same KV store used by the VOLTHA core.
+
+### For example
+To specify **ADTRAN OLT** device specific resource ranges, first create a JSON file
+_adtran_olt_resource_range.json_ with the following entry
+
+{
+    "onu_start_idx": 0,
+    "onu_end_idx": 127,
+    "alloc_id_start_idx": 1024,
+    "alloc_id_end_idx": 4222,
+    "gem_port_id_start_idx": 2176,
+    "gem_port_id_end_idx": 16383,
+    "num_of_pon_port": 16
+}
+This data should be put on the KV store location _resource_manager/xgspon/resource_ranges/adtran_olt_
+
+The format of the KV store location is resource_manager/<technology>/resource_ranges/<resource_mgr_key>
+
+In the below example the KV store is assumed to be Consul. However the same is applicable to be
+etcd or any other KV store. Please make sure to use the same KV store used by the VOLTHA core.
+
+```bash
+curl -X PUT -H "Content-Type: application/json" \
+   http://127.0.0.1:8500/v1/kv/resource_manager/xgspon/resource_ranges/adtran_olt \
+   -d @./adtran_olt_resource_range.json 
+```
+The olt_vendor_type should be referred to during the preprovisioning step as shown below. The
+olt_vendor_type is an extra option and should be specified after --. The -o specifies the resource_mgr_key.
+
+ (voltha) preprovision_olt -t adtran -H 192.168.1.100:830 -- -o adtran_olt
+Once the OLT device is enabled, any further PON Resource assignments will happen within the PON Resource ranges defined in asfvolt16_resource_range.json and placed on the KV store.
+
+Additional Notes
+If a default resource range profile should be used with all olt_vendor_types, then place such Resource Range profile at the below path on the KV store.
+
+resource_manager/xgspon/resource_ranges/default
 
 ## xPON Provisioning Support
 
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index 4740f93..ad735b3 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -19,7 +19,6 @@
 import shlex
 import time
 
-import arrow
 import structlog
 from twisted.internet import reactor, defer
 from twisted.internet.defer import inlineCallbacks, returnValue
@@ -35,13 +34,13 @@
 from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
 from voltha.extensions.kpi.olt.olt_pm_metrics import OltPmMetrics
 from common.utils.asleep import asleep
+from flow.flow_tables import DeviceFlows, DownstreamFlows
 
 _ = third_party
 
 DEFAULT_MULTICAST_VLAN = 4000
-BROADCOM_UNTAGGED_VLAN = 4091                     # SEBA - For BBWF demo (BroadCom Default VLAN)
-DEFAULT_UTILITY_VLAN = 4094
-DEFAULT_UNTAGGED_VLAN = BROADCOM_UNTAGGED_VLAN    # if RG does not send priority tagged frames
+BROADCOM_UNTAGGED_VLAN = 4091
+DEFAULT_UTILITY_VLAN = BROADCOM_UNTAGGED_VLAN
 
 _DEFAULT_RESTCONF_USERNAME = ""
 _DEFAULT_RESTCONF_PASSWORD = ""
@@ -52,8 +51,7 @@
 _DEFAULT_NETCONF_PORT = 830
 
 _STARTUP_RETRY_TIMEOUT = 5       # 5 seconds delay after activate failed before we
-_DEFAULT_XPON_SUPPORTED = False  # LOOK for the keywords 'xpon_support', SEBA
-                                 # for areas to clean up once xPON is deprecated
+_DEFAULT_RESOURCE_MGR_KEY = "adtran"
 
 
 class AdtranDeviceHandler(object):
@@ -107,11 +105,11 @@
         self.pm_metrics = None
         self.alarms = None
         self.multicast_vlans = [DEFAULT_MULTICAST_VLAN]
-        self.untagged_vlan = DEFAULT_UNTAGGED_VLAN
         self.utility_vlan = DEFAULT_UTILITY_VLAN
         self.mac_address = '00:13:95:00:00:00'
         self._rest_support = None
         self._initial_enable_complete = False
+        self.resource_mgr = None
 
         # Northbound and Southbound ports
         self.northbound_ports = {}  # port number -> Port
@@ -123,6 +121,7 @@
         # self.num_management_ports = None
 
         self.ip_address = None
+        self.host_and_port = None
         self.timeout = timeout
         self.restart_failure_timeout = 5 * 60   # 5 Minute timeout
 
@@ -138,10 +137,14 @@
         self.netconf_password = _DEFAULT_NETCONF_PASSWORD
         self._netconf_client = None
 
-        # TODO: Decrement xPON once Technology Profiles completed
-        self.xpon_support = _DEFAULT_XPON_SUPPORTED
+        # Flow entries
+        self.upstream_flows = DeviceFlows()
+        self.downstream_flows = DownstreamFlows()
+
         self.max_nni_ports = 1  # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
                                 # and logical_device_agent.py
+
+        self.resource_manager_key = _DEFAULT_RESOURCE_MGR_KEY
         # OMCI ZMQ Channel
         self.pon_agent_port = DEFAULT_PON_AGENT_TCP_PORT
         self.pio_port = DEFAULT_PIO_TCP_PORT
@@ -226,11 +229,12 @@
 
         if device.ipv4_address:
             self.ip_address = device.ipv4_address
-
+            self.host_and_port = '{}:{}'.format(self.ip_address,
+                                                self.netconf_port)
         elif device.host_and_port:
-            host_and_port = device.host_and_port.split(":")
-            self.ip_address = host_and_port[0]
-            self.netconf_port = int(host_and_port[1])
+            self.host_and_port = device.host_and_port.split(":")
+            self.ip_address = self.host_and_port[0]
+            self.netconf_port = int(self.host_and_port[1])
             self.adapter_agent.update_device(device)
 
         else:
@@ -238,7 +242,6 @@
 
         #############################################################
         # Now optional parameters
-
         def check_tcp_port(value):
             ivalue = int(value)
             if ivalue <= 0 or ivalue > 65535:
@@ -247,8 +250,8 @@
 
         def check_vid(value):
             ivalue = int(value)
-            if ivalue <= 1 or ivalue > 4094:
-                raise argparse.ArgumentTypeError("Valid VLANs are 2..4094")
+            if ivalue < 1 or ivalue > 4094:
+                raise argparse.ArgumentTypeError("Valid VLANs are 1..4094")
             return ivalue
 
         parser = argparse.ArgumentParser(description='Adtran Device Adapter')
@@ -256,14 +259,14 @@
                             help='NETCONF username')
         parser.add_argument('--nc_password', '-p', action='store', default=_DEFAULT_NETCONF_PASSWORD,
                             help='NETCONF Password')
-        parser.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT, type=check_tcp_port,
-                            help='NETCONF TCP Port')
+        parser.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT,
+                            type=check_tcp_port, help='NETCONF TCP Port')
         parser.add_argument('--rc_username', '-U', action='store', default=_DEFAULT_RESTCONF_USERNAME,
                             help='REST username')
         parser.add_argument('--rc_password', '-P', action='store', default=_DEFAULT_RESTCONF_PASSWORD,
                             help='REST Password')
-        parser.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT, type=check_tcp_port,
-                            help='RESTCONF TCP Port')
+        parser.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT,
+                            type=check_tcp_port, help='RESTCONF TCP Port')
         parser.add_argument('--zmq_port', '-z', action='store', default=DEFAULT_PON_AGENT_TCP_PORT,
                             type=check_tcp_port, help='PON Agent ZeroMQ Port')
         parser.add_argument('--pio_port', '-Z', action='store', default=DEFAULT_PIO_TCP_PORT,
@@ -271,15 +274,12 @@
         parser.add_argument('--multicast_vlan', '-M', action='store',
                             default='{}'.format(DEFAULT_MULTICAST_VLAN),
                             help='Multicast VLAN'),
-        parser.add_argument('--untagged_vlan', '-v', action='store',
-                            default='{}'.format(DEFAULT_UNTAGGED_VLAN),
-                            help='VLAN for Untagged Frames from ONUs'),
         parser.add_argument('--utility_vlan', '-B', action='store',
                             default='{}'.format(DEFAULT_UTILITY_VLAN),
-                            help='VLAN for Untagged Frames from ONUs')
-        parser.add_argument('--xpon_enable', '-X', action='store_true',
-                            default=_DEFAULT_XPON_SUPPORTED,
-                            help='enable xPON (BBF WT-385) provisioning support')
+                            type=check_vid, help='VLAN for Controller based upstream flows from ONUs')
+        parser.add_argument('--resource_mgr_key', '-o', action='store',
+                            default=_DEFAULT_RESOURCE_MGR_KEY,
+                            help='OLT Type to look up associated resource manager configuration')
         try:
             args = parser.parse_args(shlex.split(device.extra_args))
 
@@ -296,10 +296,7 @@
 
             self.pon_agent_port = args.zmq_port
             self.pio_port = args.pio_port
-            self.xpon_support = args.xpon_enable
-
-            if not self.xpon_support:
-                self.untagged_vlan = BROADCOM_UNTAGGED_VLAN
+            self.resource_manager_key = args.resource_mgr_key
 
             if not self.rest_username:
                 self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
@@ -382,7 +379,6 @@
 
                 ############################################################################
                 # Get the device Information
-
                 if reconciling:
                     device.connect_status = ConnectStatus.REACHABLE
                     self.adapter_agent.update_device(device)
@@ -410,7 +406,6 @@
 
                 try:
                     # Enumerate and create Northbound NNI interfaces
-
                     device.reason = 'enumerating northbound interfaces'
                     self.adapter_agent.update_device(device)
                     self.startup = self.enumerate_northbound_ports(device)
@@ -432,7 +427,6 @@
 
                 try:
                     # Enumerate and create southbound interfaces
-
                     device.reason = 'enumerating southbound interfaces'
                     self.adapter_agent.update_device(device)
                     self.startup = self.enumerate_southbound_ports(device)
@@ -452,6 +446,9 @@
                     self.log.exception('PON_enumeration', e=e)
                     returnValue(self.restart_activate(done_deferred, reconciling))
 
+                # Initialize resource manager
+                self.initialize_resource_manager()
+
                 if reconciling:
                     if device.admin_state == AdminState.ENABLED:
                         if device.parent_id:
@@ -469,7 +466,6 @@
                 else:
                     # Complete activation by setting up logical device for this OLT and saving
                     # off the devices parent_id
-
                     ld_initialized = self.create_logical_device(device)
 
                 ############################################################################
@@ -525,7 +521,6 @@
 
                 ############################################################################
                 # Setup Alarm handler
-
                 device.reason = 'setting up adapter alarms'
                 self.adapter_agent.update_device(device)
 
@@ -534,7 +529,6 @@
                 ############################################################################
                 # Register for ONU detection
                 # self.adapter_agent.register_for_onu_detect_state(device.id)
-
                 # Complete device specific steps
                 try:
                     self.log.debug('device-activation-procedures')
@@ -734,8 +728,6 @@
                 self.startup = yield EVC.remove_all(self.netconf_client)
                 from flow.utility_evc import UtilityEVC
                 self.startup = yield UtilityEVC.remove_all(self.netconf_client)
-                from flow.untagged_evc import UntaggedEVC
-                self.startup = yield UntaggedEVC.remove_all(self.netconf_client)
 
             except Exception as e:
                 self.log.exception('evc-cleanup', e=e)
@@ -756,7 +748,7 @@
                 self.log.exception('acl-cleanup', e=e)
 
             from flow.flow_entry import FlowEntry
-            FlowEntry.clear_all(device.id)
+            FlowEntry.clear_all(self)
 
             from download import Download
             Download.clear_all(self.netconf_client)
@@ -873,6 +865,9 @@
     def get_port_name(self, port):
         raise NotImplementedError('implement in derived class')
 
+    def initialize_resource_manager(self):
+        raise NotImplementedError('implement in derived class')
+
     @inlineCallbacks
     def complete_device_specific_activation(self, _device, _reconciling):
         # NOTE: Override this in your derived class for any device startup completion
@@ -989,7 +984,9 @@
         for port in self.southbound_ports.itervalues():
             self.log.debug('reenable-checking-pon-port', pon_id=port.pon_id)
 
-            gpon_info = self.get_xpon_info(port.pon_id)         # SEBA
+            # TODO: Need to implement this now that XPON is deprecated
+            #gpon_info = self.get_xpon_info(port.pon_id)         # SEBA
+            gpon_info = None
             if gpon_info is not None and \
                 gpon_info['channel-terminations'] is not None and \
                 len(gpon_info['channel-terminations']) > 0:
@@ -1183,7 +1180,6 @@
         self.adapter_agent.update_child_devices_state(self.device_id,
                                                       connect_status=ConnectStatus.REACHABLE)
         # Restart ports to previous state
-
         dl = []
 
         for port in self.northbound_ports.itervalues():
@@ -1194,14 +1190,13 @@
 
         try:
             yield defer.gatherResults(dl, consumeErrors=True)
+
         except Exception as e:
             self.log.exception('port-restart', e=e)
 
         # Re-subscribe for ONU detection
         # self.adapter_agent.register_for_onu_detect_state(self.device.id)
-
         # Request reflow of any EVC/EVC-MAPs
-
         if len(self._evcs) > 0:
             dl = []
             for evc in self.evcs:
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 9bbf497..ee2ede8 100644
--- a/voltha/adapters/adtran_olt/adtran_olt.py
+++ b/voltha/adapters/adtran_olt/adtran_olt.py
@@ -52,7 +52,7 @@
         self.descriptor = Adapter(
             id=self.name,
             vendor='Adtran Inc.',
-            version='1.27',
+            version='1.29',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
@@ -60,6 +60,11 @@
         self.interface = registry('main').get_args().interface
         self.logical_device_id_to_root_device_id = dict()
 
+    @property
+    def devices(self):
+        """ BroadCom adapter needs to reach in and mess with us. """
+        return self.devices_handlers
+
     def start(self):
         """
         Called once after adapter instance is loaded. Can be used to async
@@ -468,193 +473,49 @@
 
     # PON Mgnt APIs #
     def create_interface(self, device, data):
-        """
-        API to create various interfaces (only some PON interfaces as of now)
-        in the devices
-        """
-        log.debug('create-interface', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_interface(self, device, data):
-        """
-        API to update various interfaces (only some PON interfaces as of now)
-        in the devices
-        """
-        log.debug('update-interface', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_interface(self, device, data):
-        """
-        API to delete various interfaces (only some PON interfaces as of now)
-        in the devices
-        """
-        log.debug('remove-interface', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def receive_onu_detect_state(self, proxy_address, state):
-        """
-        Receive onu detect state in ONU adapter
-        :param proxy_address: ONU device address
-        :param state: ONU detect state (bool)
-        :return: None
-        """
         raise NotImplementedError()
 
     def create_tcont(self, device, tcont_data, traffic_descriptor_data):
-        """
-        API to create tcont object in the devices
-        :param device: device id
-        :param tcont_data: tcont data object
-        :param traffic_descriptor_data: traffic descriptor data object
-        :return: None
-        """
-        log.info('create-tcont', tcont_data=tcont_data,
-                 traffic_descriptor_data=traffic_descriptor_data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.create_tcont(tcont_data, traffic_descriptor_data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_tcont(self, device, tcont_data, traffic_descriptor_data):
-        """
-        API to update tcont object in the devices
-        :param device: device id
-        :param tcont_data: tcont data object
-        :param traffic_descriptor_data: traffic descriptor data object
-        :return: None
-        """
-        log.info('update-tcont', tcont_data=tcont_data,
-                 traffic_descriptor_data=traffic_descriptor_data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.update_tcont(tcont_data, traffic_descriptor_data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
-        """
-        API to delete tcont object in the devices
-        :param device: device id
-        :param tcont_data: tcont data object
-        :param traffic_descriptor_data: traffic descriptor data object
-        :return: None
-        """
-        log.info('remove-tcont', tcont_data=tcont_data,
-                 traffic_descriptor_data=traffic_descriptor_data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.remove_tcont(tcont_data, traffic_descriptor_data)
+        raise NotImplemented('xPON has been deprecated')
 
     def create_gemport(self, device, data):
-        """
-        API to create gemport object in the devices
-        :param device: device id
-        :param data: gemport data object
-        :return: None
-        """
-        log.debug('create-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_gemport(self, device, data):
-        """
-        API to update gemport object in the devices
-        :param device: device id
-        :param data: gemport data object
-        :return: None
-        """
-        log.info('update-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_gemport(self, device, data):
-        """
-        API to delete gemport object in the devices
-        :param device: device id
-        :data: gemport data object
-        :return: None
-        """
-        log.info('remove-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def create_multicast_gemport(self, device, data):
-        """
-        API to create multicast gemport object in the devices
-        :param device: device id
-        :data: multicast gemport data object
-        :return: None
-        """
-        log.info('create-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_multicast_gemport(self, device, data):
-        """
-        API to update  multicast gemport object in the devices
-        :param device: device id
-        :data: multicast gemport data object
-        :return: None
-        """
-        log.info('update-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_multicast_gemport(self, device, data):
-        """
-        API to delete multicast gemport object in the devices
-        :param device: device id
-        :data: multicast gemport data object
-        :return: None
-        """
-        log.info('remove-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def create_multicast_distribution_set(self, device, data):
-        """
-        API to create multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :data: multicast distribution data object
-        :return: None
-        """
-        log.info('create-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_multicast_distribution_set(self, device, data):
-        """
-        API to update multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :data: multicast distribution data object
-        :return: None
-        """
-        log.info('update-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_multicast_distribution_set(self, device, data):
-        """
-        API to delete multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :data: multicast distribution data object
-        :return: None
-        """
-        log.info('remove-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 462db97..2df0387 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -18,25 +18,23 @@
 
 from twisted.internet import reactor
 from twisted.internet.defer import returnValue, inlineCallbacks, succeed
-from voltha.protos.device_pb2 import Port
 
 from adtran_device_handler import AdtranDeviceHandler
-import adtranolt_platform as platform
+from voltha.adapters.adtran_olt.resources import adtranolt_platform as platform
 from download import Download
-from xpon.adtran_olt_xpon import AdtranOltXPON
 from codec.olt_state import OltState
 from flow.flow_entry import FlowEntry
+from resources.adtran_olt_resource_manager import AdtranOltResourceMgr
 from net.pio_zmq import PioClient
 from net.pon_zmq import PonClient
 from voltha.core.flow_decomposer import *
 from voltha.extensions.omci.omci import *
 from voltha.protos.common_pb2 import AdminState, OperStatus
 from voltha.protos.device_pb2 import ImageDownload, Image
-
-ATT_NETWORK = True  # Use AT&T cVlan scheme
+from voltha.protos.openflow_13_pb2 import OFPP_MAX
 
 
-class AdtranOltHandler(AdtranDeviceHandler, AdtranOltXPON):
+class AdtranOltHandler(AdtranDeviceHandler):
     """
     The OLT Handler is used to wrap a single instance of a 10G OLT 1-U pizza-box
     """
@@ -82,22 +80,22 @@
         self._downloads = {}        # name -> Download obj
         self._pio_exception_map = []
 
+        # FIXME:  Remove once we containerize.  Only exists to keep BroadCom OpenOMCI ONU Happy
+        #         when it reaches up our rear and tries to yank out a UNI port number
+        self.platform_class = None
+
+        # To keep broadcom ONU happy
+        from voltha.adapters.adtran_olt.resources.adtranolt_platform import adtran_platform
+        self.platform = adtran_platform()
+
     def __del__(self):
         # OLT Specific things here.
         #
         # If you receive this during 'enable' of the object, you probably threw an
-        # uncaught exception which trigged an errback in the VOLTHA core.
-
+        # uncaught exception which triggered an errback in the VOLTHA core.
         d, self.status_poll = self.status_poll, None
 
-        # TODO Any OLT device specific cleanup here
-        #     def get_channel(self):
-        #         if self.channel is None:
-        #             device = self.adapter_agent.get_device(self.device_id)
-        #         return self.channel
-        #
         # Clean up base class as well
-
         AdtranDeviceHandler.__del__(self)
 
     def _cancel_deferred(self):
@@ -209,10 +207,37 @@
                                                 device['software-images'].append(image)
 
         except Exception as e:
-            self.log.exception('get-pe-state', e=e)
+            self.log.exception('dev-info-failure', e=e)
+            raise
 
         returnValue(device)
 
+    def initialize_resource_manager(self):
+        # Initialize the resource manager
+        extra_args = '--olt_vendor {}'.format(self.resource_manager_key)
+        self.resource_mgr = AdtranOltResourceMgr(self.device_id,
+                                                 self.host_and_port,
+                                                 extra_args,
+                                                 self.default_resource_mgr_device_info)
+
+    @property
+    def default_resource_mgr_device_info(self):
+        class AdtranOltDevInfo(object):
+            def __init__(self, pon_ports):
+                self.technology = "gpon"
+                self.onu_id_start = 0
+                self.onu_id_end = platform.MAX_ONUS_PER_PON
+                self.alloc_id_start = platform.MIN_TCONT_ALLOC_ID
+                self.alloc_id_end = platform.MAX_TCONT_ALLOC_ID
+                self.gemport_id_start = platform.MIN_GEM_PORT_ID
+                self.gemport_id_end = platform.MAX_GEM_PORT_ID
+                self.pon_ports = len(pon_ports)
+                self.max_tconts = platform.MAX_TCONTS_PER_ONU
+                self.max_gem_ports = platform.MAX_GEM_PORTS_PER_ONU
+                self.intf_ids = pon_ports.keys()    # PON IDs
+
+        return AdtranOltDevInfo(self.southbound_ports)
+
     @inlineCallbacks
     def enumerate_northbound_ports(self, device):
         """
@@ -342,7 +367,7 @@
                 port = ports[pon_id + 1]
                 port['pon-id'] = pon_id
                 port['admin_state'] = AdminState.ENABLED \
-                    if data.get('enabled', not self.xpon_support)\
+                    if data.get('enabled', True)\
                     else AdminState.DISABLED
 
         except Exception as e:
@@ -593,8 +618,6 @@
         super(AdtranOltHandler, self).delete()
 
     def rx_pa_packet(self, packets):
-        self.log.debug('rx-pon-agent-packet')
-
         if self._pon_agent is not None:
             for packet in packets:
                 try:
@@ -717,7 +740,7 @@
                 pkt = Ether(msg)
 
             if self._pio_agent is not None:
-                port, ctag, vlan_id, evcmapname = FlowEntry.get_packetout_info(self.device_id, egress_port)
+                port, ctag, vlan_id, evcmapname = FlowEntry.get_packetout_info(self, egress_port)
                 exceptiontype = None
                 if pkt.type == FlowEntry.EtherType.EAPOL:
                     exceptiontype = 'eapol'
@@ -812,20 +835,6 @@
 
         self.status_poll = reactor.callLater(delay, self.poll_for_status)
 
-    def _create_untagged_flow(self):
-        nni_port = self.northbound_ports.get(1).port_no
-        pon_port = self.southbound_ports.get(0).port_no
-
-        return mk_flow_stat(
-            priority=100,
-            match_fields=[
-                in_port(nni_port),
-                vlan_vid(ofp.OFPVID_PRESENT + self.untagged_vlan),
-                # eth_type(FlowEntry.EtherType.EAPOL)       ?? TODO: is this needed
-            ],
-            actions=[output(pon_port)]
-        )
-
     def _create_utility_flow(self):
         nni_port = self.northbound_ports.get(1).port_no
         pon_port = self.southbound_ports.get(0).port_no
@@ -850,7 +859,6 @@
         :param device: A voltha.Device object, with possible device-type
                        specific extensions.
         """
-
         self.log.debug('bulk-flow-update', num_flows=len(flows),
                        device_id=device.id, flows=flows)
 
@@ -858,21 +866,20 @@
 
         if flows:
             # Special helper egress Packet In/Out flows
-            for special_flow in (self._create_untagged_flow(),
-                                 self._create_utility_flow()):
-                valid_flow, evc = FlowEntry.create(special_flow, self)
+            special_flow = self._create_utility_flow()
+            valid_flow, evc = FlowEntry.create(special_flow, self)
 
-                if valid_flow is not None:
-                    valid_flows.append(valid_flow.flow_id)
+            if valid_flow is not None:
+                valid_flows.append(valid_flow.flow_id)
 
-                if evc is not None:
-                    try:
-                        evc.schedule_install()
-                        self.add_evc(evc)
+            if evc is not None:
+                try:
+                    evc.schedule_install()
+                    self.add_evc(evc)
 
-                    except Exception as e:
-                        evc.status = 'EVC Install Exception: {}'.format(e.message)
-                        self.log.exception('EVC-install', e=e)
+                except Exception as e:
+                    evc.status = 'EVC Install Exception: {}'.format(e.message)
+                    self.log.exception('EVC-install', e=e)
 
         # verify exception flows were installed by OLT PET process
         reactor.callLater(5, self.send_packet_exceptions_request)
@@ -909,7 +916,7 @@
 
         # Now drop all flows from this device that were not in this bulk update
         try:
-            yield FlowEntry.drop_missing_flows(device.id, valid_flows)
+            yield FlowEntry.drop_missing_flows(self, valid_flows)
 
         except Exception as e:
             self.log.exception('bulk-flow-update-remove', e=e)
@@ -941,17 +948,6 @@
             else:
                 self.log.debug('pon-invalid-or-disabled', pon_id=pon_id)
 
-    def get_onu_vid(self, onu_id):  # TODO: Deprecate this when packet-in/out is supportted and UNI ports on the OLT
-        if ATT_NETWORK:
-            return (onu_id * 120) + 2
-
-        return None
-
-    def get_channel_id(self, pon_id, onu_id):   # TODO: Make this more unique. Just don't call the ONU VID method
-        from pon_port import PonPort
-        return self.get_onu_vid(onu_id)
-        # return self._onu_offset(onu_id) + (pon_id * PonPort.MAX_ONUS_SUPPORTED)
-
     def _onu_offset(self, onu_id):
         # Start ONU's just past the southbound PON port numbers. Since ONU ID's start
         # at zero, add one
@@ -982,11 +978,10 @@
         return pon_id, onu_id
 
     def _pon_id_to_port_number(self, pon_id):
-        # return pon_id + 1 + self.num_northbound_ports
         return pon_id + 1 + 4   # Skip over uninitialized ports
 
     def _port_number_to_pon_id(self, port):
-        if not self.xpon_support and self.is_uni_port(port):
+        if self.is_uni_port(port):
             # Convert to OLT device port
             port = platform.intf_id_from_uni_port_num(port)
 
@@ -996,67 +991,7 @@
         return self._port_number_to_pon_id(port) in self.southbound_ports
 
     def is_uni_port(self, port):
-        if self.xpon_support:
-            return port >= self._onu_offset(0)  # TODO: Really need to rework this one...
-        else:
-            return port >= (5 << 11)
-
-    def get_onu_port_and_vlans(self, flow_entry):
-        """
-        Get the logical port (OpenFlow port) for a given southbound port of an ONU
-
-        :param flow_entry: (FlowEntry) Flow to parse
-        :return: None or openflow port number and the actual VLAN IDs we should use
-        """
-        if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-            # Upstream will have VID=Logical_port until VOL-460 is addressed
-            ingress_port = flow_entry.in_port
-            vid = flow_entry.vlan_id
-        else:
-            ingress_port = flow_entry.output
-            vid = flow_entry.inner_vid
-
-        pon_port = self.get_southbound_port(ingress_port)
-        if pon_port is None:
-            return None, None, None
-
-        if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-            self.log.debug('upstream-flow-search', acl=flow_entry.is_acl_flow,
-                           vid=vid)
-            # User data flows have the correct VID / C-Tag.
-            if flow_entry.is_acl_flow:
-                if self.xpon_support:
-                    # Upstream ACLs will have VID=Logical_port until VOL-460 is addressed
-                    onu = next((onu for onu in pon_port.onus if
-                                onu.logical_port == vid), None)
-                else:
-                    # Upstream ACLs will be placed on the untagged vlan or ONU VID
-                    # TODO: Do we need an onu_vid set here for DHCP?
-                    # TODO: For non-xPON, we could really just match the UNI PORT number !!!!
-                    onu = next((onu for onu in pon_port.onus if
-                                vid == onu.untagged_vlan), None)
-            elif self.xpon_support:
-                onu = next((onu for onu in pon_port.onus if
-                            onu.onu_vid == vid), None)
-            else:
-                onu = next((onu for onu in pon_port.onus if
-                            flow_entry.in_port in onu.uni_ports), None)
-
-        elif flow_entry.vlan_id in (FlowEntry.LEGACY_CONTROL_VLAN,
-                                    flow_entry.handler.untagged_vlan):
-            # User data flows have inner_vid=correct C-tag. Legacy control VLANs
-            # have inner_vid == logical_port until VOL-460 is addressed
-            onu = next((onu for onu in pon_port.onus if
-                        onu.logical_port == vid), None)
-        else:
-            onu = next((onu for onu in pon_port.onus if
-                        onu.onu_vid == vid), None)
-
-        self.log.debug('search-results', onu=onu)
-        if onu is None:
-            return None, None, None
-
-        return onu.logical_port, onu.onu_vid, onu.untagged_vlan
+            return OFPP_MAX >= port >= (5 << 11)
 
     def get_southbound_port(self, port):
         pon_id = self._port_number_to_pon_id(port)
@@ -1286,9 +1221,7 @@
         self.adapter_agent.update_device(device)
         return done
 
-    # SEBA -- New way to launch ONU without xPON support
     def add_onu_device(self, intf_id, onu_id, serial_number, tconts, gem_ports):
-        assert not self.xpon_support
         onu_device = self.adapter_agent.get_child_device(self.device_id,
                                                          serial_number=serial_number)
         if onu_device is not None:
@@ -1314,14 +1247,12 @@
                 root=True,
                 serial_number=serial_number,
                 admin_state=AdminState.ENABLED,
-                # vlan=self.get_onu_vid(onu_id)         # TODO: a hack, need a decent flow decomposer
             )
             assert serial_number is not None, 'ONU does not have a serial number'
 
             onu_device = self.adapter_agent.get_child_device(self.device_id,
                                                              serial_number=serial_number)
 
-            # self._seba_xpon_create(onu_device, intf_id, onu_id, tconts, gem_ports)
             reactor.callLater(0, self._seba_xpon_create, onu_device, intf_id, onu_id, tconts, gem_ports)
             return onu_device
 
@@ -1350,370 +1281,27 @@
         try:
             onu_adapter_agent.create_interface(onu_device,
                                                OnuIndication(intf_id, onu_id))
-        except:
+        except Exception as _e:
             pass
-
-        for tcont in tconts.itervalues():
-            td = tcont.traffic_descriptor.data if tcont.traffic_descriptor is not None else None
-            onu_adapter_agent.create_tcont(onu_device, tcont.data, traffic_descriptor_data=td)
-
-        for gem_port in gem_ports.itervalues():
-            onu_adapter_agent.create_gemport(onu_device, gem_port.data)
-
-    def on_channel_group_modify(self, cgroup, update, diffs):
-        valid_keys = ['enable',
-                      'polling-period',
-                      'system-id']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("channel-group leaf '{}' is read-only or write-once".format(invalid_key))
-
-        pons = self.get_related_pons(cgroup)
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                pass  # TODO: ?
-
-            elif k == 'polling-period':
-                for pon in pons:
-                    pon.discovery_tick = update[k]
-
-            elif k == 'system-id':
-                self.system_id(update[k])
-
-        return update
-
-    def on_channel_partition_modify(self, cpartition, update, diffs):
-        valid_keys = ['enabled', 'fec-downstream', 'mcast-aes', 'differential-fiber-distance']
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("channel-partition leaf '{}' is read-only or write-once".format(invalid_key))
-
-        pons = self.get_related_pons(cpartition)
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                pass  # TODO: ?
-
-            elif k == 'fec-downstream':
-                for pon in pons:
-                    pon.downstream_fec_enable = update[k]
-
-            elif k == 'mcast-aes':
-                for pon in pons:
-                    pon.mcast_aes = update[k]
-
-            elif k == 'differential-fiber-distance':
-                for pon in pons:
-                    pon.deployment_range = update[k] * 1000  # pon-agent uses meters
-        return update
-
-    def on_channel_pair_modify(self, cpair, update, diffs):
-        valid_keys = ['enabled', 'line-rate']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("channel-pair leaf '{}' is read-only or write-once".format(invalid_key))
-
-        pons = self.get_related_pons(cpair)
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                pass                        # TODO: ?
-
-            elif k == 'line-rate':
-                for pon in pons:
-                    pon.line_rate = update[k]
-        return update
-
-    def on_channel_termination_create(self, ct, pon_type='xgs-ponid'):
-        pons = self.get_related_pons(ct, pon_type=pon_type)
-        pon_port = pons[0] if len(pons) == 1 else None
-
-        if pon_port is None:
-            raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
-        assert ct['channel-pair'] in self.channel_pairs, \
-            '{} is not a channel-pair'.format(ct['channel-pair'])
-        cpair = self.channel_pairs[ct['channel-pair']]
-
-        assert cpair['channel-group'] in self.channel_groups, \
-            '{} is not a -group'.format(cpair['channel-group'])
-        assert cpair['channel-partition'] in self.channel_partitions, \
-            '{} is not a channel-partition'.format(cpair('channel-partition'))
-        cg = self.channel_groups[cpair['channel-group']]
-        cpart = self.channel_partitions[cpair['channel-partition']]
-
-        polling_period = cg['polling-period']
-        system_id = cg['system-id']
-        authentication_method = cpart['authentication-method']
-        # line_rate = cpair['line-rate']
-        downstream_fec = cpart['fec-downstream']
-        deployment_range = cpart['differential-fiber-distance']
-        mcast_aes = cpart['mcast-aes']
-        # TODO: Support BER calculation period
-
-        pon_port.xpon_name = ct['name']
-        pon_port.discovery_tick = polling_period
-        pon_port.authentication_method = authentication_method
-        pon_port.deployment_range = deployment_range * 1000  # pon-agent uses meters
-        pon_port.downstream_fec_enable = downstream_fec
-        pon_port.mcast_aes = mcast_aes
-        # pon_port.line_rate = line_rate            # TODO: support once 64-bits
-        self.system_id = system_id
-
-        # Enabled 'should' be a logical 'and' of all referenced items but
-        # there is no easy way to detected changes in referenced items.
-        # enabled = ct['enabled'] and cpair['enabled'] and cg['enabled'] and cpart['enabled']
-        enabled = ct['enabled']
-        pon_port.admin_state = AdminState.ENABLED if enabled else AdminState.DISABLED
-        return ct
-
-    def on_channel_termination_modify(self, ct, update, diffs, pon_type='xgs-ponid'):
-        valid_keys = ['enabled']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("channel-termination leaf '{}' is read-only or write-once".format(invalid_key))
-
-        pons = self.get_related_pons(ct, pon_type=pon_type)
-        pon_port = pons[0] if len(pons) == 1 else None
-
-        if pon_port is None:
-            raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                enabled = update[k]
-                pon_port.admin_state = AdminState.ENABLED if enabled else AdminState.DISABLED
-        return update
-
-    def on_channel_termination_delete(self, ct, pon_type='xgs-ponid'):
-        pons = self.get_related_pons(ct, pon_type=pon_type)
-        pon_port = pons[0] if len(pons) == 1 else None
-
-        if pon_port is None:
-            raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
-        pon_port.admin_state = AdminState.DISABLED
-        return None
-
-    def on_ont_ani_modify(self, ont_ani, update, diffs):
-        valid_keys = ['enabled', 'upstream-fec']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("ont-ani leaf '{}' is read-only or write-once".format(invalid_key))
-
-        onus = self.get_related_onus(ont_ani)
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                pass      # TODO: Have only ONT use this value?
-
-            elif k == 'upstream-fec':
-                for onu in onus:
-                    onu.upstream_fec_enable = update[k]
-        return update
-
-    def on_vont_ani_modify(self, vont_ani, update, diffs):
-        valid_keys = ['enabled',
-                      'expected-serial-number',
-                      'upstream-channel-speed',
-                      'expected-registration-id',
-                      ]  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("vont-ani leaf '{}' is read-only or write-once".format(invalid_key))
-
-        onus = self.get_related_onus(vont_ani)
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                for onu in onus:
-                    onu.enabled = update[k]
-            elif k == 'expected-serial-number':
-                for onu in onus:
-                    if onu.serial_number != update[k]:
-                        onu.pon.delete_onu(onu.onu_id)
-            elif k == 'upstream-channel-speed':
-                for onu in onus:
-                    onu.upstream_channel_speed = update[k]
-            elif k == 'expected-registration-id':
-                for onu in onus:
-                    onu.password = update[k].encode('base64')
-        return update
-
-    def on_vont_ani_delete(self, vont_ani):
-        onus = self.get_related_onus(vont_ani)
-
-        for onu in onus:
-            try:
-                onu.pon.delete_onu(onu.onu_id)
-
-            except Exception as e:
-                self.log.exception('onu', onu=onu, e=e)
-
-        return None
-
-    def _get_tcont_onu(self, vont_ani):
-        onu = None
         try:
-            vont_ani = self.v_ont_anis.get(vont_ani)
-            ch_pair = self.channel_pairs.get(vont_ani['preferred-channel-pair'])
-            ch_term = next((term for term in self.channel_terminations.itervalues()
-                            if term['channel-pair'] == ch_pair['name']), None)
+            # TODO: deprecate the xPON TCONT/TD/GEMPort once we do not have to call into ONU
+            last_alloc_id = None
+            for tcont_dict in tconts:
+                tcont = tcont_dict['object']
+                td = tcont.traffic_descriptor.data if tcont.traffic_descriptor is not None else None
+                onu_adapter_agent.create_tcont(onu_device, tcont.data, traffic_descriptor_data=td)
+                last_alloc_id = tcont.alloc_id
 
-            pon = self.pon(ch_term['xgs-ponid'])
-            onu = pon.onu(vont_ani['onu-id'])
-
-        except Exception:
+            for gem_port_dict in gem_ports:
+                gem_port = gem_port_dict['object']
+                if onu_device.adapter.lower() == 'brcm_openomci_onu':
+                    # BroadCom OpenOMCI ONU adapter uses the tcont alloc_id as the tcont_ref and currently
+                    # they only assign one.
+                    gem_port.data.tcont_ref = str(last_alloc_id)
+                onu_adapter_agent.create_gemport(onu_device, gem_port.data)
+        except Exception as _e:
             pass
 
-        return onu
-
-    def on_tcont_create(self, tcont):
-        from xpon.olt_tcont import OltTCont
-
-        td = self.traffic_descriptors.get(tcont.get('td-ref'))
-        traffic_descriptor = td['object'] if td is not None else None
-
-        tcont['object'] = OltTCont.create(tcont, traffic_descriptor)
-
-        # Look up any ONU associated with this TCONT (should be only one if any)
-        onu = self._get_tcont_onu(tcont['vont-ani'])
-
-        if onu is not None:                 # Has it been discovered yet?
-            onu.add_tcont(tcont['object'])
-
-        return tcont
-
-    def on_tcont_modify(self, tcont, update, diffs):
-        valid_keys = ['td-ref']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("TCONT leaf '{}' is read-only or write-once".format(invalid_key))
-
-        tc = tcont.get('object')
-        assert tc is not None, 'TCONT not found'
-
-        update['object'] = tc
-
-        # Look up any ONU associated with this TCONT (should be only one if any)
-        onu = self._get_tcont_onu(tcont['vont-ani'])
-
-        if onu is not None:                 # Has it been discovered yet?
-            keys = [k for k in diffs.keys() if k in valid_keys]
-
-            for k in keys:
-                if k == 'td-ref':
-                    td = self.traffic_descriptors.get(update['td-ref'])
-                    if td is not None:
-                        onu.update_tcont_td(tcont['alloc-id'], td)
-
-        return update
-
-    def on_tcont_delete(self, tcont):
-        onu = self._get_tcont_onu(tcont['vont-ani'])
-
-        if onu is not None:
-            onu.remove_tcont(tcont['alloc-id'])
-
-        return None
-
-    def on_td_create(self, traffic_disc):
-        from xpon.olt_traffic_descriptor import OltTrafficDescriptor
-        traffic_disc['object'] = OltTrafficDescriptor.create(traffic_disc)
-        return traffic_disc
-
-    def on_td_modify(self, traffic_disc, update, diffs):
-        from xpon.olt_traffic_descriptor import OltTrafficDescriptor
-
-        valid_keys = ['fixed-bandwidth',
-                      'assured-bandwidth',
-                      'maximum-bandwidth',
-                      'priority',
-                      'weight',
-                      'additional-bw-eligibility-indicator']
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("traffic-descriptor leaf '{}' is read-only or write-once".format(invalid_key))
-
-        # New traffic descriptor
-        update['object'] = OltTrafficDescriptor.create(update)
-
-        td_name = traffic_disc['name']
-        tconts = {key: val for key, val in self.tconts.iteritems()
-                  if val['td-ref'] == td_name and td_name is not None}
-
-        for tcont in tconts.itervalues():
-            # Look up any ONU associated with this TCONT (should be only one if any)
-            onu = self._get_tcont_onu(tcont['vont-ani'])
-            if onu is not None:
-                onu.update_tcont_td(tcont['alloc-id'], update['object'])
-
-        return update
-
-    def on_td_delete(self, traffic_desc):
-        # TD may be used by more than one TCONT. Only delete if the last one
-        td_name = traffic_desc['name']
-        num_tconts = len([val for val in self.tconts.itervalues()
-                          if val['td-ref'] == td_name and td_name is not None])
-        return None if num_tconts <= 1 else traffic_desc
-
-    def on_gemport_create(self, gem_port):
-        from xpon.olt_gem_port import OltGemPort
-        # Create an GemPort object to wrap the dictionary
-        gem_port['object'] = OltGemPort.create(self, gem_port)
-
-        onus = self.get_related_onus(gem_port)
-        assert len(onus) <= 1, 'Too many ONUs: {}'.format(len(onus))
-
-        if len(onus) == 1:
-            onus[0].add_gem_port(gem_port['object'])
-
-        return gem_port
-
-    def on_gemport_modify(self, gem_port, update, diffs):
-        valid_keys = ['encryption',
-                      'traffic-class']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("GEM Port leaf '{}' is read-only or write-once".format(invalid_key))
-
-        port = gem_port.get('object')
-        assert port is not None, 'GemPort not found'
-
-        keys = [k for k in diffs.keys() if k in valid_keys]
-        update['object'] = port
-
-        for k in keys:
-            if k == 'encryption':
-                port.encryption = update[k]
-            elif k == 'traffic-class':
-                pass                    # TODO: Implement
-
-        return update
-
-    def on_gemport_delete(self, gem_port):
-        onus = self.get_related_onus(gem_port)
-        assert len(onus) <= 1, 'Too many ONUs: {}'.format(len(onus))
-        if len(onus) == 1:
-            onus[0].remove_gem_id(gem_port['gemport-id'])
-        return None
-
 
 class OnuIndication(object):
     def __init__(self, intf_id, onu_id):
diff --git a/voltha/adapters/adtran_olt/flow/acl.py b/voltha/adapters/adtran_olt/flow/acl.py
index 42975d8..1845b0f 100644
--- a/voltha/adapters/adtran_olt/flow/acl.py
+++ b/voltha/adapters/adtran_olt/flow/acl.py
@@ -22,8 +22,9 @@
 
 _acl_list = {}      # Key -> device-id -> Name: List of encoded EVCs
 
-ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}'  # format(flow_entry.handler.device_id, flow_entry.flow.id)
+ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}'  # format(flow_entry.flow_id, flow-entry-hash)
 ACL_NAME_REGEX_ALL = 'VOLTHA-ACL-*'
+ACE_NAME_FORMAT = 'VOLTHA-ACE-{}-{}'  # format(flow_entry.flow_id, flow-entry-hash)
 
 
 class ACL(object):
@@ -194,11 +195,24 @@
 
     @staticmethod
     def flow_to_name(flow_entry):
-        return 'VOLTHA-ACL-{}-{}'.format(flow_entry.handler.device_id, flow_entry.flow.id)
+        return ACL_NAME_FORMAT.format(flow_entry.flow_id, ACL.acl_hash(flow_entry))
 
     @staticmethod
     def flow_to_ace_name(flow_entry):
-        return 'VOLTHA-ACE-{}-{}'.format(flow_entry.handler.device_id, flow_entry.flow.id)
+        return ACE_NAME_FORMAT.format(flow_entry.flow_id, ACL.acl_hash(flow_entry))
+
+    @staticmethod
+    def acl_hash(flow_entry):
+        from hashlib import md5
+        in_port = flow_entry.in_port or 0
+        eth_type = flow_entry.eth_type or 0
+        ip_protocol = flow_entry.ip_protocol or 0
+        ipv4_dst = flow_entry.ipv4_dst or 0
+        src_port = flow_entry.udp_src or 0
+        dst_port = flow_entry.udp_dst or 0
+        hex_string = md5('{},{},{},{},{},{}'.format(in_port, eth_type, ip_protocol,
+                                                    ipv4_dst, src_port, dst_port)).hexdigest()
+        return hex_string
 
     @property
     def valid(self):
@@ -222,8 +236,8 @@
 
             acls_installed = _acl_list[self._handler.device_id]
             if self._name in acls_installed:
-                self._status_message = "ACL '{}' id already installed".format(self._name)
-                raise Exception(self._status_message)
+                # Return OK
+                returnValue(self._enabled)
 
             try:
                 acl_xml = self._install_xml()
@@ -334,13 +348,13 @@
 
                     pairs = []
                     if isinstance(entries['acl'], list):
-                        pairs = { (entry['acl-type'], entry['acl-name']) for entry in entries['acl']
+                        pairs = {(entry['acl-type'], entry['acl-name']) for entry in entries['acl']
                                  if 'acl-name' in entry and 'acl-type' in entry and p.match(entry['acl-name'])}
                     else:
                         if 'acl' in entries:
                             entry = entries['acl']
                             if 'acl-name' in entry and 'acl-type' in entry and p.match(entry['acl-name']):
-                                pairs = [ (entry['acl-type'], entry['acl-name']) ]
+                                pairs = [(entry['acl-type'], entry['acl-name'])]
 
                     if len(pairs) > 0:
                         del_xml = '<access-lists xmlns="http://www.adtran.com/ns/yang/adtran-ietf-access-control-list">'
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 1cafe66..d3e25dc 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -247,11 +247,12 @@
 
         for onu_or_vlan_id, gem_ids_and_vid in onu_s_gem_ids_and_vid.iteritems():
             first_gem_id = True
+            gem_ids = gem_ids_and_vid[0]
             vid = gem_ids_and_vid[1]
             ident = '{}.{}'.format(self._pon_id, onu_or_vlan_id) if vid is None \
                 else onu_or_vlan_id
 
-            for gem_id in gem_ids_and_vid[0]:
+            for gem_id in gem_ids:
                 xml += '<evc-map{}>'.format('' if not create else ' xc:operation="create"')
                 xml += '<name>{}.{}.{}</name>'.format(self.name, ident, gem_id)
                 xml += '<ce-vlan-id>{}</ce-vlan-id>'.format(Onu.gem_id_to_gvid(gem_id))
@@ -342,7 +343,7 @@
                     results = yield self._handler.netconf_client.edit_config(map_xml)
                     self._installed = results.ok
                     self._needs_update = results.ok
-                    self.status = '' if results.ok else results.error
+                    self._status_message = '' if results.ok else results.error
 
                     if results.ok:
                         self._existing_acls.update(work_acls)
@@ -502,13 +503,13 @@
         """
         from flow_entry import FlowEntry
         # Create temporary EVC-MAP
-        assert flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM, \
+        assert flow.flow_direction in FlowEntry.upstream_flow_types, \
             'Only Upstream flows additions are supported at this time'
 
         log.debug('add-flow-to-evc', flow=flow, evc=evc)
 
         tmp_map = EVCMap.create_ingress_map(flow, evc, dry_run=True) \
-            if flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM \
+            if flow.flow_direction in FlowEntry.upstream_flow_types \
             else EVCMap.create_egress_map(flow, evc, dry_run=True)
 
         if tmp_map is None or not tmp_map.valid:
@@ -542,7 +543,7 @@
         try:
             del self._flows[flow.flow_id]
 
-            log('remove-flow-to-evc', flow=flow, evc=evc)
+            log('remove-flow-to-evc', flow=flow)
             # Remove any ACLs
             acl_name = ACL.flow_to_name(flow)
             acl = None
@@ -570,7 +571,7 @@
 
                         self._evc.remove_evc_map(self)
                         first_flow = self._flows.itervalues().next()
-                        self._evc = first_flow.get_utility_evc(None, True)
+                        self._evc = first_flow.get_utility_evc(True)
                         self._evc.add_evc_map(self)
                         log.debug('moved-acl-flows-to-utility-evc', newevcname=self._evc.name)
 
@@ -592,6 +593,8 @@
 
     @staticmethod
     def create_evc_map_name(flow):
+        # Note: When actually installed into the OLT, the .onu_id.gem_port is
+        #       appended to the name
         return EVC_MAP_NAME_FORMAT.format(flow.logical_port, flow.flow_id)
 
     @staticmethod
@@ -605,8 +608,10 @@
         """
         items = name.split('-') if name is not None else dict()
 
+        # Note: When actually installed into the OLT, the .onu_id.gem_port is
+        #       appended to the name
         return {'ingress-port': items[1],
-                'flow-id': items[2]} if len(items) == 3 else dict()
+                'flow-id': items[2].split('.')[0]} if len(items) == 3 else dict()
 
     def add_gem_port(self, gem_port, reflow=False):
         # TODO: Refactor
@@ -650,8 +655,6 @@
         return succeed('nop')
 
     def _setup_gem_ids(self):
-        from flow_entry import FlowEntry
-
         # all flows should have same GEM port setup
         flow = self._flows.itervalues().next()
         is_pon = flow.handler.is_pon_port(flow.in_port)
@@ -661,9 +664,8 @@
 
             if pon_port is not None:
                 self._pon_id = pon_port.pon_id
-                untagged_gem = flow.eth_type == FlowEntry.EtherType.EAPOL and\
-                    flow.handler.untagged_vlan != flow.handler.utility_vlan
-                self._gem_ids_and_vid = pon_port.gem_ids(flow.logical_port, untagged_gem,
+                self._gem_ids_and_vid = pon_port.gem_ids(flow.logical_port,
+                                                         flow.vlan_id,
                                                          flow.is_multicast_flow)
 
     def _decode(self, evc):
@@ -692,7 +694,6 @@
             return False    # UNI Ports handled in the EVC Maps
 
         # ACL logic
-
         self._eth_type = flow.eth_type
 
         if self._eth_type == FlowEntry.EtherType.IPv4:
@@ -705,7 +706,6 @@
 
         # If no match of VLAN this may be for untagged traffic or upstream and needs to
         # match the gem-port vid
-
         self._setup_gem_ids()
 
         # self._match_untagged = flow.vlan_id is None and flow.inner_vid is None
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index fa4fbed..a0d67fe 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -15,7 +15,6 @@
 from evc import EVC
 from evc_map import EVCMap
 from enum import IntEnum
-from untagged_evc import UntaggedEVC
 from utility_evc import UtilityEVC
 import voltha.core.flow_decomposer as fd
 from voltha.core.flow_decomposer import *
@@ -32,18 +31,6 @@
     17,         # UDP
 ]
 
-_existing_downstream_flow_entries = {}  # device-id -> signature-table
-                                        #              |
-                                        #              +-> downstream-signature
-                                        #                  |
-                                        #                  +-> 'evc' -> EVC
-                                        #                       |
-                                        #                       +-> flow-ids -> flow-entry
-
-_existing_upstream_flow_entries = {}  # device-id -> flow dictionary
-                                      #              |
-                                      #              +-> flow-id -> flow-entry
-
 
 class FlowEntry(object):
     """
@@ -73,9 +60,7 @@
         NNI_NNI = 6           # NNI port to NNI Port
         UNI_UNI = 7           # UNI port to UNI Port
         OTHER = 9             # Unable to determine
-        NNI = 10              # Deprecate in v2.0
-        UNI = 11              # Deprecate in v2.0
-        
+
     upstream_flow_types = {FlowDirection.UPSTREAM, FlowDirection.CONTROLLER_UNI}
     downstream_flow_types = {FlowDirection.DOWNSTREAM, FlowDirection.NNI_PON}
 
@@ -122,9 +107,9 @@
 
         # Actions
         self.output = None
-        self.pop_vlan = 0
-        self.push_vlan_tpid = []
-        self.push_vlan_id = []
+        self.pop_vlan = False
+        self.push_vlan_tpid = None
+        self.push_vlan_id = None
 
         self._name = self.create_flow_name()
 
@@ -193,73 +178,75 @@
         try:
             flow_entry = FlowEntry(flow, handler)
 
-            if not flow_entry._decode():
+            ######################################################################
+            # Decode the flow entry
+            if not flow_entry._decode(flow):
+                # TODO: When we support individual flow mods, we will need to return
+                #       this flow back always
                 return None, None
 
-            if flow_entry.device_id not in _existing_downstream_flow_entries:
-                _existing_downstream_flow_entries[flow_entry.device_id] = {}
-
-            if flow_entry.device_id not in _existing_upstream_flow_entries:
-                _existing_upstream_flow_entries[flow_entry.device_id] = {}
-
-            downstream_sig_table = _existing_downstream_flow_entries[flow_entry.device_id]
-            upstream_flow_table = _existing_upstream_flow_entries[flow_entry.device_id]
+            ######################################################################
+            # Initialize flow_entry database (dicts) if needed and determine if
+            # the flows have already been handled.
+            downstream_sig_table = handler.downstream_flows
+            upstream_flow_table = handler.upstream_flows
 
             log.debug('flow-entry-decoded', flow=flow_entry, signature=flow_entry.signature,
                       downstream_signature=flow_entry.downstream_signature)
 
-            if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM and\
+            if flow_entry.flow_direction in FlowEntry.upstream_flow_types and\
                     flow_entry.flow_id in upstream_flow_table:
                 log.debug('flow-entry-upstream-exists', flow=flow_entry)
                 return flow_entry, None
 
-            if flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM and\
-                    flow_entry.signature in downstream_sig_table and\
-                    flow_entry.flow_id in downstream_sig_table[flow_entry.signature]:
-                log.debug('flow-entry-downstream-exists', flow=flow_entry)
-                return flow_entry, None
+            if flow_entry.flow_direction in FlowEntry.downstream_flow_types:
+                sig_table = downstream_sig_table.get(flow_entry.signature)
+                if sig_table is not None and flow_entry in sig_table.flows:
+                    log.debug('flow-entry-downstream-exists', flow=flow_entry)
+                    return flow_entry, None
 
-            # Look for any matching flows in the other direction that might help make an EVC
-            # and then save it off in the device specific flow table
+            ######################################################################
+            # Look for any matching flows in the other direction that might help
+            # make an EVC and then save it off in the device specific flow table
+            #
             # TODO: For now, only support for E-LINE services between NNI and UNI
-            log.debug('flow-entry-search-for-match', flow=flow_entry)
             downstream_flow = None
             upstream_flows = None
             downstream_sig = None
 
             if flow_entry._is_multicast:        # Uni-directional flow
-                assert flow_entry._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM, \
+                assert flow_entry._flow_direction in FlowEntry.downstream_flow_types, \
                     'Only downstream Multicast supported'
                 downstream_flow = flow_entry
                 downstream_sig = flow_entry.signature
                 upstream_flows = []
 
-            elif flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
+            elif flow_entry.flow_direction in FlowEntry.downstream_flow_types:
                 downstream_flow = flow_entry
                 downstream_sig = flow_entry.signature
 
-            elif flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
+            elif flow_entry.flow_direction in FlowEntry.upstream_flow_types:
                 downstream_sig = flow_entry.downstream_signature
 
             if downstream_sig is None:
+                # TODO: When we support individual flow mods, we will need to return
+                #       this flow back always
                 log.debug('flow-entry-empty-downstream', flow=flow_entry)
                 return None, None
 
-            if downstream_sig not in downstream_sig_table:
-                downstream_sig_table[downstream_sig] = {}
-                downstream_sig_table[downstream_sig]['evc'] = None
+            # Make sure a slot exists for the downstream signature and get its flow table
+            downstream_sig_table = downstream_sig_table.add(downstream_sig)
+            evc = downstream_sig_table.evc
 
-            downstream_flow_table = downstream_sig_table[downstream_sig]
-            evc = downstream_flow_table['evc']
-
-            # Save to proper flow table
-            if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-                upstream_flow_table[flow_entry.flow_id] = flow_entry
+            # Save the new flow_entry to proper flow table
+            if flow_entry.flow_direction in FlowEntry.upstream_flow_types:
+                upstream_flow_table.add(flow_entry)
                 downstream_flow = evc.flow_entry if evc is not None else \
-                    next((_flow for _flow in downstream_flow_table.itervalues() if isinstance(_flow, FlowEntry)), None)
+                    next((_flow for _flow in downstream_sig_table.flows.itervalues()
+                          if isinstance(_flow, FlowEntry)), None)
 
-            elif flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-                downstream_flow_table[flow_entry.flow_id] = flow_entry
+            elif flow_entry.flow_direction in FlowEntry.downstream_flow_types:
+                downstream_sig_table.flows.add(flow_entry)
 
             # Now find all the upstream flows
             if downstream_flow is not None:
@@ -271,10 +258,13 @@
             log.debug('flow-entry-search-results', flow=flow_entry,
                       downstream_flow=downstream_flow, upstream_flows=upstream_flows)
 
+            ######################################################################
             # Compute EVC and and maps
             evc = FlowEntry._create_evc_and_maps(evc, downstream_flow, upstream_flows)
-            if evc is not None and evc.valid and downstream_flow_table['evc'] is None:
-                downstream_flow_table['evc'] = evc
+
+            # Save off EVC (if we have one) for this flow if it is new
+            if evc is not None and evc.valid and downstream_sig_table.evc is None:
+                downstream_sig_table.evc = evc
 
             return flow_entry, evc
 
@@ -300,7 +290,6 @@
             return None
 
         # Get any existing EVC if a flow is already created
-
         if downstream_flow.evc is None:
             if evc is not None:
                 downstream_flow.evc = evc
@@ -310,7 +299,7 @@
                 downstream_flow.evc = MCastEVC.create(downstream_flow)
 
             elif downstream_flow.is_acl_flow:
-                downstream_flow.evc = downstream_flow.get_utility_evc(upstream_flows)
+                downstream_flow.evc = downstream_flow.get_utility_evc()
             else:
                 downstream_flow.evc = EVC(downstream_flow)
 
@@ -335,11 +324,12 @@
         for flow in upstream_flows:
             if flow.evc_map is None:
                 if flow.signature in sig_map_map:
-                    # Found an explicity matching existing EVC-MAP. Add flow to this EVC-MAP
+                    # Found an explicitly matching existing EVC-MAP. Add flow to this EVC-MAP
                     flow.evc_map = sig_map_map[flow.signature].add_flow(flow, downstream_flow.evc)
                 else:
                     # May need to create a MAP or search for an existing ACL/user EVC-Map
-                    upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
+                    # upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
+                    upstream_flow_table = flow.handler.upstream_flows
                     existing_flow = EVCMap.find_matching_ingress_flow(flow, upstream_flow_table)
 
                     if existing_flow is None:
@@ -356,13 +346,8 @@
 
         return downstream_flow.evc if all_maps_valid else None
 
-    def get_utility_evc(self, upstream_flows=None, use_default_vlan_id=False):
+    def get_utility_evc(self, use_default_vlan_id=False):
         assert self.is_acl_flow, 'Utility evcs are for acl flows only'
-        if upstream_flows is not None and\
-            any(flow.eth_type == FlowEntry.EtherType.EAPOL for flow in upstream_flows) and\
-            self.handler.utility_vlan != self.handler.untagged_vlan:
-            return UntaggedEVC.create(self, use_default_vlan_id)
-
         return UtilityEVC.create(self, use_default_vlan_id)
 
     @property
@@ -373,140 +358,97 @@
         return self.eth_type is not None or self.ip_protocol is not None or\
             self.ipv4_dst is not None or self.udp_dst is not None or self.udp_src is not None
 
-    def _decode(self):
+    def _decode(self, flow):
         """
         Examine flow rules and extract appropriate settings
         """
         log.debug('start-decode')
-        status = self._decode_traffic_selector() and self._decode_traffic_treatment()
+        status = self._decode_traffic_selector(flow) and self._decode_traffic_treatment(flow)
 
+        # Determine direction of the flow and apply appropriate modifications
+        # to the decoded flows
         if status:
-            # Determine direction of the flow
-            def port_type(port_number):
-                if port_number in self._handler.northbound_ports:
-                    return FlowEntry.FlowDirection.NNI
-                elif port_number <= OFPP_MAX:
-                    return FlowEntry.FlowDirection.UNI
-                return FlowEntry.FlowDirection.OTHER
+            if not self._decode_flow_direction():
+                return False
 
-            self._flow_direction = FlowEntry._flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
-                                                               FlowEntry.FlowDirection.OTHER)
+            if self._flow_direction in FlowEntry.downstream_flow_types:
+                status = self._apply_downstream_mods()
 
-            # Modify flow entry for newer utility/untagged VLAN support
-            # New Packet In/Out support
-            if self._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM and\
-               self.vlan_id in (FlowEntry.LEGACY_CONTROL_VLAN,
-                                self.handler.untagged_vlan):
-                # May be for to controller flow downstream (no ethType) or multicast (ethType = IP)
-                if self.eth_type is None or self._needs_acl_support:
-                    self._is_multicast = False
-                    self._is_acl_flow = True
-                    if self.inner_vid is not None:
-                        logical_port, subscriber_vlan, untagged_vlan = \
-                            self._handler.get_onu_port_and_vlans(self)
-                        self.inner_vid = subscriber_vlan
-                        self.vlan_id = self.handler.utility_vlan
-                    else:
-                        self.vlan_id = self.handler.untagged_vlan
-            elif self._flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-                try:
-                    # TODO: Need to support flow retry if the ONU is not yet activated   !!!!
-                    # Get the correct logical port and subscriber VLAN for this UNI
-                    self._logical_port, uni_vid, untagged_vlan = \
-                        self._handler.get_onu_port_and_vlans(self)
+            elif self._flow_direction in FlowEntry.upstream_flow_types:
+                status = self._apply_upstream_mods()
 
-                    if self._needs_acl_support:
-                        self._is_acl_flow = True
-                        if self.eth_type == FlowEntry.EtherType.EAPOL and \
-                                self.handler.untagged_vlan != self.handler.utility_vlan:
-                            self.vlan_id = None
-                            self.push_vlan_id[0] = self.handler.untagged_vlan
-                        else:
-                            self.push_vlan_id[0] = self.handler.utility_vlan
-                    elif self._handler.xpon_support:
-                        self.vlan_id = uni_vid
+            else:
+                # TODO: Need to code this - Perhaps this is an NNI_PON for Multicast support?
+                log.error('unsupported-flow-direction')
+                status = False
 
-                except Exception as e:
-                    # TODO: Need to support flow retry if the ONU is not yet activated   !!!!
-                    log.exception('tag-fixup', e=e)
-
-        log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
-                  inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
-                  push_vids=self.push_vlan_id)
+            log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
+                      inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
+                      push_vid=self.push_vlan_id, status=status)
 
         # Create a signature that will help locate related flow entries on a device.
-        # These are not exact, just ones that may be put together to make an EVC. The
-        # basic rules are:
-        # 1 - Same device
-        dev_id = self._handler.device_id
+        if status:
+            # These are not exact, just ones that may be put together to make an EVC. The
+            # basic rules are:
+            #
+            # 1 - Port numbers in increasing order
+            ports = [self.in_port, self.output]
+            ports.sort()
+            assert len(ports) == 2, 'Invalid port count: {}'.format(len(ports))
 
-        # 2 - Port numbers in increasing order
-        ports = [self.in_port, self.output]
-        ports.sort()
-
-        # 3 - The outer VID
-        # 4 - The inner VID.  Wildcard if downstream
-        push_len = len(self.push_vlan_id)
-        if push_len == 0:
-            outer = self.vlan_id
-            inner = self.inner_vid
-        else:
-            outer = self.push_vlan_id[-1]
-            if push_len == 1:
-                inner = self.vlan_id
+            # 3 - The outer VID
+            # 4 - The inner VID.  Wildcard if downstream
+            if self.push_vlan_id is None:
+                outer = self.vlan_id
+                inner = self.inner_vid
             else:
-                inner = self.push_vlan_id[-2]
+                outer = self.push_vlan_id
+                inner = self.vlan_id
 
-        upstream_sig = '{}'.format(dev_id)
-        downstream_sig = '{}'.format(dev_id)
+            upstream_sig = '{}'.format(ports[0])
+            downstream_sig = '{}'.format(ports[0])
+            upstream_sig += '.{}'.format(ports[1])
+            downstream_sig += '.{}'.format(ports[1] if self.handler.is_nni_port(ports[1]) else '*')
 
-        for port in ports:
-            upstream_sig += '.{}'.format(port)
-            downstream_sig += '.{}'.format(port if self.handler.is_nni_port(port) else '*')
+            upstream_sig += '.{}.{}'.format(outer, inner)
+            downstream_sig += '.{}.*'.format(outer)
 
-        upstream_sig += '.{}.{}'.format(outer, inner)
-        downstream_sig += '.{}.*'.format(outer)
+            if self._flow_direction in FlowEntry.downstream_flow_types:
+                self.signature = downstream_sig
 
-        if self._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-            self.signature = downstream_sig
-        else:
-            self.signature = upstream_sig
-            self.downstream_signature = downstream_sig
+            elif self._flow_direction in FlowEntry.upstream_flow_types:
+                self.signature = upstream_sig
+                self.downstream_signature = downstream_sig
 
-        log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
+            else:
+                log.error('unsupported-flow')
+                status = False
 
+            log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
         return status
 
-    def _decode_traffic_selector(self):
+    def _decode_traffic_selector(self, flow):
         """
         Extract EVC related traffic selection settings
         """
-        self.in_port = fd.get_in_port(self._flow)
+        self.in_port = fd.get_in_port(flow)
 
         if self.in_port > OFPP_MAX:
-            log.warn('logical-input-ports-not-supported')
+            log.warn('logical-input-ports-not-supported', in_port=self.in_port)
             return False
 
-        for field in fd.get_ofb_fields(self._flow):
+        for field in fd.get_ofb_fields(flow):
             if field.type == IN_PORT:
-                assert self.in_port == field.port, 'Multiple Input Ports found in flow rule'
-
-                if self._handler.is_nni_port(self.in_port):
-                    self._logical_port = self.in_port       # TODO: This should be a lookup
+                if self._handler.is_nni_port(self.in_port) or self._handler.is_uni_port(self.in_port):
+                    self._logical_port = self.in_port
 
             elif field.type == VLAN_VID:
-                # log.info('*** field.type == VLAN_VID', value=field.vlan_vid & 0xfff)
-                if self.handler.xpon_support:
-                    # Traditional xPON way
-                    self.vlan_id = field.vlan_vid & 0xfff
+                if field.vlan_vid >= ofp.OFPVID_PRESENT + 4095:
+                    self.vlan_id = None             # pre-ONOS v1.13.5 or old EAPOL Rule
                 else:
-                    if field.vlan_vid > ofp.OFPVID_PRESENT + 4095:      # Is it a UNI PORT on the PON?
-                        self.vlan_id = self.handler.untagged_vlan
-                    else:
-                        self.vlan_id = field.vlan_vid & 0xfff
+                    self.vlan_id = field.vlan_vid & 0xfff
 
                 log.debug('*** field.type == VLAN_VID', value=field.vlan_vid, vlan_id=self.vlan_id)
-                self._is_multicast = self.vlan_id in self._handler.multicast_vlans
 
             elif field.type == VLAN_PCP:
                 log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
@@ -521,7 +463,7 @@
                 self.ip_protocol = field.ip_proto
 
                 if self.ip_protocol not in _supported_ip_protocols:
-                    # log.error('Unsupported IP Protocol')
+                    log.error('Unsupported IP Protocol', protocol=self.ip_protocol)
                     return False
 
             elif field.type == IPV4_DST:
@@ -538,17 +480,21 @@
 
             elif field.type == METADATA:
                 log.debug('*** field.type == METADATA', value=field.table_metadata)
-                if self.handler.xpon_support:
-                    # Traditional xPON way
-                    self.inner_vid = field.table_metadata
+
+                if 0xFFFFFFFF >= field.table_metadata > ofp.OFPVID_PRESENT + 4095:
+                    # Default flows for old-style controller flows
+                    self.inner_vid = None
+
+                elif field.table_metadata > 0xFFFFFFFF:
+                    # ONOS v1.13.5 or later. c-vid in upper 32-bits
+                    self.inner_vid = field.table_metadata >> 32
+
                 else:
-                    if field.table_metadata > ofp.OFPVID_PRESENT + 4095:      # Is it a UNI PORT on the PON?
-                        self.inner_vid = self.handler.untagged_vlan
-                    else:
-                        self.inner_vid = field.table_metadata
+                    # Pre- ONOS v1.13.5
+                    self.inner_vid = field.table_metadata
 
-                log.debug('*** field.type == METADATA', value=field.table_metadata, inner_vid=self.inner_vid)
-
+                log.debug('*** field.type == METADATA', value=field.table_metadata,
+                          inner_vid=self.inner_vid)
             else:
                 log.warn('unsupported-selection-field', type=field.type)
                 self._status_message = 'Unsupported field.type={}'.format(field.type)
@@ -556,61 +502,149 @@
 
         return True
 
-    def _decode_traffic_treatment(self):
-        self.output = fd.get_out_port(self._flow)
-
-        if self.output > OFPP_MAX:
-            log.warn('logical-output-ports-not-supported')
-            return False
-
-        for act in fd.get_actions(self._flow):
+    def _decode_traffic_treatment(self, flow):
+        # Loop through traffic treatment
+        for act in fd.get_actions(flow):
             if act.type == fd.OUTPUT:
-                assert self.output == act.output.port, 'Multiple Output Ports found in flow rule'
-                pass           # Handled earlier
+                self.output = act.output.port
 
             elif act.type == POP_VLAN:
                 log.debug('*** action.type == POP_VLAN')
-                self.pop_vlan += 1
+                self.pop_vlan = True
 
             elif act.type == PUSH_VLAN:
                 log.debug('*** action.type == PUSH_VLAN', value=act.push)
-                # TODO: Do we want to test the ethertype for support?
                 tpid = act.push.ethertype
-                self.push_vlan_tpid.append(tpid)
+                self.push_vlan_tpid = tpid
 
             elif act.type == SET_FIELD:
                 log.debug('*** action.type == SET_FIELD', value=act.set_field.field)
                 assert (act.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC)
                 field = act.set_field.field.ofb_field
-                if field.type == VLAN_VID:
-                    self.push_vlan_id.append(field.vlan_vid & 0xfff)
 
+                if field.type == VLAN_VID:
+                    self.push_vlan_id = field.vlan_vid & 0xfff
+                else:
+                    log.debug('unsupported-set-field')
             else:
-                # TODO: May need to modify ce-preservation
                 log.warn('unsupported-action', action=act)
                 self._status_message = 'Unsupported action.type={}'.format(act.type)
                 return False
 
         return True
 
-    @staticmethod
-    def drop_missing_flows(device_id, valid_flow_ids):
-        dl = []
-        try:
-            flow_table = _existing_upstream_flow_entries.get(device_id)
-            if flow_table is not None:
-                flows_to_drop = [flow for flow_id, flow in flow_table.items()
-                                 if flow_id not in valid_flow_ids]
-                dl.extend([flow.remove() for flow in flows_to_drop])
+    def _decode_flow_direction(self):
+        # Determine direction of the flow
+        def port_type(port_number):
+            if port_number in self._handler.northbound_ports:
+                return FlowEntry.PortType.NNI
 
-            sig_table = _existing_downstream_flow_entries.get(device_id)
-            if sig_table is not None:
-                for flow_table in sig_table.itervalues():
-                    flows_to_drop = [flow for flow_id, flow in flow_table.items()
-                                     if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
-                    dl.extend([flow.remove() for flow in flows_to_drop])
+            elif port_number in self._handler.southbound_ports:
+                return FlowEntry.PortType.PON
+
+            elif port_number <= OFPP_MAX:
+                return FlowEntry.PortType.UNI
+
+            elif port_number in {OFPP_CONTROLLER, 0xFFFFFFFD}:      # OFPP_CONTROLLER is wrong in proto-file
+                return FlowEntry.PortType.CONTROLLER
+
+            return FlowEntry.PortType.OTHER
+
+        flow_dir_map = {
+            (FlowEntry.PortType.UNI, FlowEntry.PortType.NNI):        FlowEntry.FlowDirection.UPSTREAM,
+            (FlowEntry.PortType.NNI, FlowEntry.PortType.UNI):        FlowEntry.FlowDirection.DOWNSTREAM,
+            (FlowEntry.PortType.UNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_UNI,
+            (FlowEntry.PortType.NNI, FlowEntry.PortType.PON):        FlowEntry.FlowDirection.NNI_PON,
+            # The following are not yet supported
+            # (FlowEntry.PortType.NNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_NNI,
+            # (FlowEntry.PortType.PON, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_PON,
+            # (FlowEntry.PortType.NNI, FlowEntry.PortType.NNI):        FlowEntry.FlowDirection.NNI_NNI,
+            # (FlowEntry.PortType.UNI, FlowEntry.PortType.UNI):        FlowEntry.FlowDirection.UNI_UNI,
+        }
+        self._flow_direction = flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
+                                                FlowEntry.FlowDirection.OTHER)
+        return self._flow_direction != FlowEntry.FlowDirection.OTHER
+
+    def _apply_downstream_mods(self):
+        # This is a downstream flow.  It could be any one of the following:
+        #
+        #   Legacy control VLAN:
+        #       This is the old VLAN 4000 that was used to attach EAPOL and other
+        #       controller flows to. Eventually these will change to CONTROLLER_UNI
+        #       flows.  For these, use the 'utility' VLAN instead so 4000 if available
+        #       for other uses (AT&T uses it for downstream multicast video).
+        #
+        #   Multicast VLAN:
+        #       This is downstream multicast data.
+        #       TODO: Test this to see if this needs to be in a separate NNI_PON mod-method
+        #
+        #   User Data flow:
+        #       This is for user data.  Eventually we may need to support ACLs?
+        #
+        # May be for to controller flow downstream (no ethType)
+        if self.vlan_id == FlowEntry.LEGACY_CONTROL_VLAN and self.eth_type is None and self.pcp == 0:
+            return False    # Do not install this flow.  Utility VLAN is in charge
+
+        elif self.vlan_id in self._handler.multicast_vlans:
+            #  multicast (ethType = IP)                         # TODO: May need to be an NNI_PON flow
+            self._is_multicast = True
+            self._is_acl_flow = True
+
+        else:
+            # Currently do not support ACLs on user data flows downstream
+            assert not self._needs_acl_support    # User data, no special modifications needed at this time
+
+        return True
+
+    def _apply_upstream_mods(self):
+        #
+        # This is an upstream flow.  It could be any of the following
+        #
+        #   ACL/Packet capture:
+        #       This is either a legacy (FlowDirection.UPSTREAM) or a new one
+        #       that specifies an output port of controller (FlowDirection.CONTROLLER_UNI).
+        #       Either way, these need to be placed on the Utility VLAN if the ONU attached
+        #       does not have a user-data flow (C-Tag).  If there is a C-Tag available,
+        #       then place it on that VLAN.
+        #
+        #       Once a user-data flow is established, move any of the ONUs ACL flows
+        #       over to that VLAN (this is handled elsewhere).
+        #
+        #   User Data flows:
+        #       No special modifications are needed
+        #
+        try:
+            # Do not handle PON level ACLs in this method
+            assert(self._flow_direction != FlowEntry.FlowDirection.CONTROLLER_PON)
+
+            # Is this a legacy (VLAN 4000) upstream to-controller flow
+            if self._needs_acl_support and FlowEntry.LEGACY_CONTROL_VLAN == self.push_vlan_id:
+                self._flow_direction = FlowEntry.FlowDirection.CONTROLLER_UNI
+                self._is_acl_flow = True
+                self.push_vlan_id = self.handler.utility_vlan
+
+            return True
 
         except Exception as e:
+            # TODO: Need to support flow retry if the ONU is not yet activated   !!!!
+            log.exception('tag-fixup', e=e)
+            return False
+
+    @staticmethod
+    def drop_missing_flows(handler, valid_flow_ids):
+        dl = []
+        try:
+            flow_table = handler.upstream_flows
+            flows_to_drop = [flow for flow_id, flow in flow_table.items()
+                             if flow_id not in valid_flow_ids]
+            dl.extend([flow.remove() for flow in flows_to_drop])
+
+            for sig_table in handler.downstream_flows.itervalues():
+                flows_to_drop = [flow for flow_id, flow in sig_table.flows.items()
+                                 if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
+                dl.extend([flow.remove() for flow in flows_to_drop])
+
+        except Exception as _e:
             pass
 
         return gatherResults(dl, consumeErrors=True) if len(dl) > 0 else returnValue('no-flows-to-drop')
@@ -622,37 +656,30 @@
         if needed
         """
         # Remove from exiting table list
-
-        device_id = self._handler.device_id
-        flow_id = self._flow.id
+        flow_id = self.flow_id
         flow_table = None
-        sig_table = None
 
-        if self.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-            flow_table = _existing_upstream_flow_entries.get(device_id)
+        if self.flow_direction in FlowEntry.upstream_flow_types:
+            flow_table = self._handler.upstream_flows
 
-        elif self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-            sig_table = _existing_downstream_flow_entries.get(device_id)
-            flow_table = sig_table.get(self.signature)
+        elif self.flow_direction in FlowEntry.downstream_flow_types:
+            sig_table = self._handler.downstream_flows.get(self.signature)
+            flow_table = sig_table.flows if sig_table is not None else None
 
         if flow_table is None or flow_id not in flow_table:
             returnValue('NOP')
 
         # Remove from flow table and clean up flow table if empty
-
-        del flow_table[flow_id]
+        flow_table.remove(flow_id)
         evc_map, self.evc_map = self.evc_map, None
         evc = None
 
-        if self.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-            if len(flow_table) == 0:
-                del _existing_upstream_flow_entries[device_id]
-
-        elif self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-            if len(flow_table) == 1:   # Only 'evc' entry present
-                evc = flow_table['evc']
+        if self.flow_direction in FlowEntry.downstream_flow_types:
+            sig_table = self._handler.downstream_flows.get(self.signature)
+            if len(flow_table) == 0:   # Only 'evc' entry present
+                evc = sig_table.evc
             else:
-                assert flow_table['evc'] is not None, 'EVC flow re-assignment error'
+                assert sig_table.evc is not None, 'EVC flow re-assignment error'
 
         # Remove flow from the hardware
         try:
@@ -668,23 +695,20 @@
         except Exception as e:
             log.exception('removal', e=e)
 
-        if self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-
+        if self.flow_direction in FlowEntry.downstream_flow_types:
             # If this flow owns the EVC, assign it to a remaining flow
-            flow_evc = flow_table['evc']
+            sig_table = self._handler.downstream_flows.get(self.signature)
+            flow_evc = sig_table.evc
 
-            if flow_evc is not None and flow_evc.flow_entry is not None and \
-                flow_id == flow_evc.flow_entry.flow_id:
-                flow_table['evc'].flow_entry = next((_flow for _flow in flow_table.itervalues()
-                                                     if isinstance(_flow, FlowEntry)
-                                                     and _flow.flow_id != flow_id), None)
+            if flow_evc is not None and flow_evc.flow_entry is not None and flow_id == flow_evc.flow_entry.flow_id:
+                flow_evc.flow_entry = next((_flow for _flow in flow_table.itervalues()
+                                           if isinstance(_flow, FlowEntry)
+                                           and _flow.flow_id != flow_id), None)
 
-        # If evc was deleted, remove the flow table reference
+        # If evc was deleted, remove the signature table since now flows exist with
+        # that signature
         if evc is not None:
-            del flow_table['evc']
-            del sig_table[self.signature]
-            if len(sig_table) == 0:
-                del _existing_downstream_flow_entries[device_id]
+            self._handler.downstream_flows.remove(self.signature)
 
         self.evc = None
         returnValue('Done')
@@ -696,12 +720,10 @@
         :param onu: (Onu) onu
         :return: (list) of matching flows
         """
-        # EVCs are only in the downstream table, EVC Map are in upstream
-
-        device_id = onu.device_id
+        # EVCs are only in the downstream table, EVC Maps are in upstream
         onu_ports = onu.uni_ports
 
-        all_flow_entries = _existing_upstream_flow_entries.get(device_id) or {}
+        all_flow_entries = onu.olt.upstream_flows
         evc_maps = [flow_entry.evc_map for flow_entry in all_flow_entries.itervalues()
                     if flow_entry.in_port in onu_ports
                     and flow_entry.evc_map is not None
@@ -740,33 +762,28 @@
     # Bulk operations
 
     @staticmethod
-    def clear_all(device_id):
+    def clear_all(handler):
         """
         Remove all flows for the device.
 
-        :param device_id: voltha adapter device id
+        :param handler: voltha adapter device handler
         """
-
-        if device_id in _existing_downstream_flow_entries:
-            del _existing_downstream_flow_entries[device_id]
-
-        if device_id in _existing_upstream_flow_entries:
-            del _existing_upstream_flow_entries[device_id]
+        handler.downstream_flows.clear_all()
+        handler.upstream_flows.clear_all()
 
     @staticmethod
-    def get_packetout_info(device_id, logical_port):
+    def get_packetout_info(handler, logical_port):
         """
-        Find parameters needed to send packet out succesfully to the OLT.
+        Find parameters needed to send packet out successfully to the OLT.
 
-        :param device_id: A Voltha.Device object.
+        :param handler: voltha adapter device handler
         :param logical_port: (int) logical port number for packet to go out.
 
         :return: physical port number, ctag, stag, evcmap name
         """
         from ..onu import Onu
 
-        all_flow_entries = _existing_upstream_flow_entries.get(device_id) or {}
-        for flow_entry in all_flow_entries.itervalues():
+        for flow_entry in handler.upstream_flows.itervalues():
             log.debug('get-packetout-info', flow_entry=flow_entry)
 
             # match logical port
@@ -785,5 +802,5 @@
                             ctag = gem_ids_with_vid[1]
                             gem_id = gem_ids[0]     # TODO: always grab fist in list
                             return flow_entry.in_port, ctag, Onu.gem_id_to_gvid(gem_id), \
-                                   evc_map.get_evcmap_name(onu_id, gem_id)
+                                evc_map.get_evcmap_name(onu_id, gem_id)
         return None, None, None, None
diff --git a/voltha/adapters/adtran_olt/flow/flow_tables.py b/voltha/adapters/adtran_olt/flow/flow_tables.py
new file mode 100644
index 0000000..e90e69b
--- /dev/null
+++ b/voltha/adapters/adtran_olt/flow/flow_tables.py
@@ -0,0 +1,162 @@
+# Copyright 2017-present Adtran, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from flow_entry import FlowEntry
+from evc import EVC
+
+
+class DeviceFlows(object):
+    """ Tracks existing flows on the device """
+
+    def __init__(self):
+        self._flow_table = dict()   # Key = (str)Flow ID, Value = FlowEntry
+
+    def __getitem__(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table[flow_id]
+
+    def __iter__(self):
+        for _flow_id, _flow in self._flow_table.items():
+            yield _flow_id, _flow
+
+    def itervalues(self):
+        for _flow in self._flow_table.values():
+            yield _flow
+
+    def iterkeys(self):
+        for _id in self._flow_table.keys():
+            yield _id
+
+    def items(self):
+        return self._flow_table.items()
+
+    def values(self):
+        return self._flow_table.values()
+
+    def keys(self):
+        return self._flow_table.keys()
+
+    def __len__(self):
+        return len(self._flow_table)
+
+    def add(self, flow):
+        assert isinstance(flow, FlowEntry)
+        self._flow_table[flow.flow_id] = flow
+        return flow
+
+    def get(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table.get(flow_id)
+
+    def remove(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table.pop(flow_id, default=None)
+
+    def clear_all(self):
+        self._flow_table = dict()
+
+
+class DownstreamFlows(object):
+    """
+    Tracks existing flows that are downstream (NNI as source port)
+
+    The downstream table is slightly different than the base DeviceFlows
+    table as it is used to track flows that will become EVCs.  The base
+    table tracks flows that will be EVC-maps (or related to them).
+
+    The downstream table is also indexed by a downstream signature that
+    is composed as follows:
+
+        <dev-id>.<ingress-port-number>.<s-tag>.*
+
+    In comparison, the upstream flows is similar, but instead of '*' it has the
+    c-tag (if any).
+
+    TODO: Drop device ID from signatures once flow tables are unique to a device handler
+    """
+    def __init__(self):
+        self._signature_table = dict()  # Key = (str)Downstream signature
+                                        #  |
+                                        #  +-> downstream-signature
+                                        #      |
+                                        #      +-> 'evc' -> EVC
+                                        #      |
+                                        #      +-> flow-ids -> flow-entries...
+
+    def __getitem__(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table[signature]
+
+    def __iter__(self):
+        for _flow_id, _flow in self._signature_table.items():
+            yield _flow_id, _flow
+
+    def itervalues(self):
+        for _flow in self._signature_table.values():
+            yield _flow
+
+    def iterkeys(self):
+        for _id in self._signature_table.keys():
+            yield _id
+
+    def items(self):
+        return self._signature_table.items()
+
+    def values(self):
+        return self._signature_table.values()
+
+    def keys(self):
+        return self._signature_table.keys()
+
+    def __len__(self):
+        return len(self._signature_table)
+
+    def get(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table.get(signature)
+
+    def add(self, signature):
+        assert isinstance(signature, str)
+        """
+        Can be called by upstream flow to reserve a slot
+        """
+        if signature not in self._signature_table:
+            self._signature_table[signature] = DownstreamFlows.SignatureTableEntry(signature)
+        return self._signature_table[signature]
+
+    def remove(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table.pop(signature)
+
+    def clear_all(self):
+        self._signature_table = dict()
+
+    class SignatureTableEntry(object):
+        def __init__(self, signature):
+            self._signature = signature
+            self._evc = None
+            self._flow_table = DeviceFlows()
+
+        @property
+        def evc(self):
+            return self._evc
+
+        @evc.setter
+        def evc(self, evc):
+            assert isinstance(evc, (EVC, type(None)))
+            self._evc = evc
+
+        @property
+        def flows(self):
+            return self._flow_table
diff --git a/voltha/adapters/adtran_olt/flow/mcast.py b/voltha/adapters/adtran_olt/flow/mcast.py
index 124a262..164d0f4 100644
--- a/voltha/adapters/adtran_olt/flow/mcast.py
+++ b/voltha/adapters/adtran_olt/flow/mcast.py
@@ -173,11 +173,11 @@
         self.in_port, self.output = self.output, self.in_port
         self.flow_id = '{}-MCAST'.format(self.vlan_id)
         self._logical_port = self.vlan_id
-        self.push_vlan_id = [self.vlan_id]
+        self.push_vlan_id = self.vlan_id
         self.vlan_id = None
         self.signature = None
         self.inner_vid = None
-        self.pop_vlan = 0
+        self.pop_vlan = False
 
     def create_flow_name(self):
         return 'flow-{}-{}-MCAST'.format(self.device_id, self.vlan_id)
diff --git a/voltha/adapters/adtran_olt/flow/untagged_evc.py b/voltha/adapters/adtran_olt/flow/untagged_evc.py
deleted file mode 100644
index c51459f..0000000
--- a/voltha/adapters/adtran_olt/flow/untagged_evc.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from evc import EVC
-from twisted.internet import defer
-from twisted.internet.defer import returnValue, inlineCallbacks
-import voltha.core.flow_decomposer as fd
-from voltha.core.flow_decomposer import *
-
-log = structlog.get_logger()
-
-EVC_NAME_FORMAT = 'VOLTHA-UNTAGGED-{}'            # format(flow.vlan_id)
-EVC_NAME_REGEX_ALL = EVC_NAME_FORMAT.format('*')
-
-
-_untagged_evcs = {}  # device-id -> flow dictionary
-                     #                  |
-                     #                  +-> untagged-vlan-id -> evcs
-
-
-class UntaggedEVC(EVC):
-    """
-    Class to wrap Untagged (no C-Tag) EVC functionality
-    """
-    def __init__(self, flow_entry):
-        super(UntaggedEVC, self).__init__(flow_entry)
-        # No Inner-VID
-        self._switching_method = EVC.SwitchingMethod.SINGLE_TAGGED
-        self._downstream_flows = {flow_entry.flow_id}     # Matching Downstream Flow IDs
-        self.service_evc = True
-
-    def __str__(self):
-        return "VOLTHA-UNTAGGED-{}: MEN: {}, VLAN: {}".format(self._name, self._men_ports, self._s_tag)
-
-    def _create_name(self, vlan_id=None):
-        #
-        # TODO: Take into account selection criteria and output to make the name
-        #
-        return EVC_NAME_FORMAT.format(self._flow.vlan_id if vlan_id is None else vlan_id)
-
-    @staticmethod
-    def create(flow_entry, use_default_vlan_id=False):
-        device_id = flow_entry.device_id
-        vlan_id = flow_entry.vlan_id if use_default_vlan_id else flow_entry.handler.untagged_vlan
-        evc_table = _untagged_evcs.get(device_id)
-
-        if evc_table is None:
-            _untagged_evcs[device_id] = dict()
-            evc_table = _untagged_evcs[device_id]
-
-        try:
-            evc = evc_table.get(flow_entry.vlan_id)
-
-            if evc is None:
-                # Create EVC and initial EVC Map
-                evc = UntaggedEVC(flow_entry)
-
-                # reapply the stag and name if forced vlan id
-                if use_default_vlan_id:
-                    evc._s_tag = vlan_id
-                    evc._create_name(vlan_id)
-
-                evc_table[flow_entry.vlan_id] = evc
-            else:
-                if flow_entry.flow_id in evc.downstream_flows:    # TODO: Debug only to see if flow_ids are unique
-                    pass
-                else:
-                    evc.add_downstream_flows(flow_entry.flow_id)
-
-            return evc
-
-        except Exception as e:
-            log.exception('untagged-create', e=e)
-            return None
-
-    @property
-    def downstream_flows(self):
-        return frozenset(self._downstream_flows)
-
-    def add_downstream_flows(self, flow_id):
-        self._downstream_flows.add(flow_id)
-
-    def remove_downstream_flows(self, flow_id):
-        self._downstream_flows.discard(flow_id)
-
-    def remove(self, remove_maps=True):
-        """
-        Remove EVC (and optional associated EVC-MAPs) from hardware
-        :param remove_maps: (boolean)
-        :return: (deferred)
-        """
-        log.info('removing', evc=self, remove_maps=remove_maps)
-
-        device_id = self._flow.handler.device_id
-        flow_id = self._flow.flow_id
-        evc_table = _untagged_evcs.get(device_id)
-
-        if evc_table is None:
-            return defer.succeed('NOP')
-
-        # Remove flow reference
-        if self._flow.flow_id in self._downstream_flows:
-            self._downstream_flows.discard(self._flow.flow_id)
-
-        if len(self._downstream_flows) == 0:
-            # Use base class to clean up
-            return super(UntaggedEVC, self).remove(remove_maps=True)
-
-        return defer.succeed('More references')
-
-    @inlineCallbacks
-    def delete(self, delete_maps=True):
-        """
-        Remove from hardware and delete/clean-up EVC Object
-        """
-        log.info('deleting', evc=self, delete_maps=delete_maps)
-
-        assert self._flow, 'Delete EVC must have flow reference'
-        try:
-            dl = [self.remove()]
-            if delete_maps:
-                for evc_map in self.evc_maps:
-                    dl.append(evc_map.delete(None))   # TODO: implement bulk-flow procedures
-
-            yield defer.gatherResults(dl, consumeErrors=True)
-
-            self._evc_maps = None
-            f, self._flow = self._flow, None
-            if f is not None and f.handler is not None:
-                f.handler.remove_evc(self)
-
-        except Exception as e:
-            log.exception('removal', e=e)
-
-    def reflow(self, reflow_maps=True):
-        pass    # TODO: Implement or use base class?
-
-    @staticmethod
-    def remove_all(client, regex_=EVC_NAME_REGEX_ALL):
-        """
-        Remove all matching EVCs from hardware
-        :param client: (ncclient) NETCONF Client to use
-        :param regex_: (String) Regular expression for name matching
-        :return: (deferred)
-        """
-        _untagged_evcs.clear()
-        EVC.remove_all(client, regex_)
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index 362688a..41e8b0f 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -31,8 +31,6 @@
     """
     Wraps an ONU
     """
-    MIN_ONU_ID = 0
-    MAX_ONU_ID = 1020          # 1021..2013 reserved
     DEFAULT_PASSWORD = ''
 
     def __init__(self, onu_info):
@@ -41,98 +39,47 @@
             raise ValueError('No ONU ID available')
 
         pon = onu_info['pon']
+        self._olt = pon.olt
+        self._pon_id = pon.pon_id
+        self._name = '{}@{}'.format(pon.physical_port_name, self._onu_id)
+        self.log = structlog.get_logger(pon_id=self._pon_id, onu_id=self._onu_id)
+
+        self._valid = True          # Set false during delete/cleanup
         self._serial_number_base64 = Onu.string_to_serial_number(onu_info['serial-number'])
         self._serial_number_string = onu_info['serial-number']
         self._device_id = onu_info['device-id']
         self._password = onu_info['password']
-        self._olt = pon.olt
-        self._pon_id = pon.pon_id
-        self._name = '{}@{}'.format(pon.name, self._onu_id)
-        self._xpon_name = onu_info['xpon-name']
-        self._gem_ports = {}                           # gem-id -> GemPort
-        self._tconts = {}                              # alloc-id -> TCont
-        if self.olt.xpon_support:
-            self._onu_vid = onu_info['onu-vid']  # SEBA (xpon-mode may be only one using this)
-            self.untagged_vlan = self._onu_vid
-            self._uni_ports = [onu_info['onu-vid']]     # TODO: Get rid of this         # SEBA (xpon-mode may be only one using this)
-            assert len(self._uni_ports) == 1, 'Only one UNI port supported at this time'
-        else:
-            self._onu_vid = None
-            self.untagged_vlan = self.olt.untagged_vlan         # SEBA - BBWF has this hard coded to 4091
-            self._uni_ports = onu_info['uni-ports']
-        self._channel_id = onu_info['channel-id']                    # SEBA (xpon-mode may be only one using this)
-        self._enabled = onu_info['enabled']
-        self._vont_ani = onu_info.get('vont-ani')
-        self._rssi = -9999
-        self._equalization_delay = 0
-        self._fiber_length = 0
-        self._valid = True          # Set false during delete/cleanup
+
         self._created = False
         self._proxy_address = None
-        self._upstream_fec_enable = onu_info.get('upstream-fec')
-        self._upstream_channel_speed = onu_info['upstream-channel-speed']
-        # TODO: how do we want to enforce upstream channel speed (if at all)?
-        self._include_multicast = True   # TODO: May need to add multicast on a per-ONU basis
         self._sync_tick = _HW_SYNC_SECS
         self._expedite_sync = False
         self._expedite_count = 0
         self._resync_flows = False
         self._sync_deferred = None     # For sync of ONT config to hardware
-        self._password = None
-        self._timestamp = None
 
-        if onu_info['venet'] is not None:
-            port_no, subscriber_vlan, self.untagged_vlan = Onu.decode_venet(onu_info['venet'],
-                                                                            self.olt.untagged_vlan)
-            if port_no is not None:
-                self._uni_ports = [port_no]
-            if subscriber_vlan is not None:
-                self._onu_vid = subscriber_vlan
+        # Resources
+        self._gem_ports = {}  # gem-id -> GemPort
+        self._tconts = {}  # alloc-id -> TCont
+        self._uni_ports = onu_info['uni-ports']
 
-        self.log = structlog.get_logger(pon_id=self._pon_id, onu_id=self._onu_id)
+        # Provisionable items
+        self._enabled = onu_info['enabled']
+        self._upstream_fec_enable = onu_info.get('upstream-fec')
+        self._upstream_channel_speed = onu_info['upstream-channel-speed']
+        # TODO: how do we want to enforce upstream channel speed (if at all)?
 
-    def __del__(self):
-        # self.stop()
-        pass
+        # KPI related items
+        self._rssi = -9999
+        self._equalization_delay = 0
+        self._fiber_length = 0
+        self._timestamp = None     # Last time the KPI items were updated
 
     def __str__(self):
-        return "ONU-{}:{}, SN: {}/{}".format(self._onu_id, self._pon_id,
+        return "ONU-{}:{}, SN: {}/{}".format(self._pon_id, self._onu_id,
                                              self._serial_number_string, self._serial_number_base64)
 
     @staticmethod
-    def decode_venet(venet_info, untagged_vlan):
-        # TODO: Move this one and ONU one into venet decode to dict() area
-        try:
-            # Allow spaces or dashes as separator, select last as the
-            # port number.  UNI-1,  UNI 1, and UNI 3-2-1 are the same
-            port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
-            subscriber_vlan = port_no
-            try:
-                # Subscriber VLAN and Untagged vlan are comma separated
-                parts = venet_info['description'].split(',')
-                sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
-                untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
-                try:
-                    if sub_part is not None:
-                        subscriber_vlan = int(sub_part.split(':')[-1:][0])
-                except Exception as e:
-                    pass
-                try:
-                    if untagged_part is not None:
-                        untagged_vlan = int(untagged_part.split(':')[-1:][0])
-                except Exception as e:
-                    pass
-            except Exception as e:
-                pass
-
-            return port_no, subscriber_vlan, untagged_vlan
-
-        except ValueError:
-            pass
-        except KeyError:
-            pass
-
-    @staticmethod
     def serial_number_to_string(value):
         sval = base64.decodestring(value)
         unique = [elem.encode("hex") for elem in sval[4:8]]
@@ -174,14 +121,6 @@
         return self._name
 
     @property
-    def xpon_name(self):
-        return self._xpon_name
-
-    @property
-    def v_ont_ani(self):
-        return self._vont_ani
-
-    @property
     def upstream_fec_enable(self):
         return self._upstream_fec_enable
 
@@ -200,7 +139,7 @@
 
     @upstream_channel_speed.setter
     def upstream_channel_speed(self, value):
-        assert isinstance(value, (int,float)), 'upstream speed is a numeric value'
+        assert isinstance(value, (int, float)), 'upstream speed is a numeric value'
         if self._upstream_channel_speed != value:
             self._upstream_channel_speed = value
 
@@ -219,10 +158,9 @@
         """
         if self._password is None and value is not None:
             self._password = value
-            # reg_id = value.decode('base64')
             reg_id = (value.decode('base64')).rstrip('\00').lstrip('\00')
             # Must remove any non-printable characters
-            reg_id = ''.join([i if ord(i) < 127 and ord(i) > 31 else '_' for i in reg_id])
+            reg_id = ''.join([i if 127 > ord(i) > 31 else '_' for i in reg_id])
             # Generate alarm here for regID
             from voltha.extensions.alarms.onu.onu_active_alarm import OnuActiveAlarm
             self.log.info('onu-Active-Alarm', serial_number=self._serial_number_string)
@@ -253,10 +191,6 @@
         self.pon.upstream_fec_enable = self.pon.any_upstream_fec_enabled
 
     @property
-    def onu_vid(self):
-        return self._onu_vid
-
-    @property
     def uni_ports(self):
         return self._uni_ports
 
@@ -276,52 +210,12 @@
             from voltha.protos.device_pb2 import Device
 
             device_id = self.olt.device_id
-
-            try:
-                if self.olt.xpon_support:
-                    v_ont_ani = self._vont_ani
-                    voltha_core = self.olt.adapter_agent.core
-                    xpon_agent = voltha_core.xpon_agent
-                    channel_group_id = xpon_agent.get_channel_group_for_vont_ani(v_ont_ani)
-                    parent_chnl_pair_id = xpon_agent.get_port_num(device_id,
-                                                                  v_ont_ani.data.preferred_chanpair)
-                    self._proxy_address = Device.ProxyAddress(
-                        device_id=device_id,
-                        channel_group_id=channel_group_id,
-                        channel_id=parent_chnl_pair_id,
-                        channel_termination=v_ont_ani.data.preferred_chanpair,
-                        onu_id=self.onu_id,
-                        onu_session_id=self.onu_id)
-                else:
-                    self._proxy_address = Device.ProxyAddress(device_id=device_id,
-                                                              channel_id=self.pon.port_no,
-                                                              onu_id=self.onu_id,
-                                                              onu_session_id=self.onu_id)
-            except Exception:
-                pass
-
+            self._proxy_address = Device.ProxyAddress(device_id=device_id,
+                                                      channel_id=self.pon.port_no,
+                                                      onu_id=self.onu_id,
+                                                      onu_session_id=self.onu_id)
         return self._proxy_address
 
-    def _get_v_ont_ani(self, olt):
-        onu = None
-        try:
-            vont_ani = olt.v_ont_anis.get(self.vont_ani)
-            ch_pair = olt.channel_pairs.get(vont_ani['preferred-channel-pair'])
-            ch_term = next((term for term in olt.channel_terminations.itervalues()
-                            if term['channel-pair'] == ch_pair['name']), None)
-
-            pon = olt.pon(ch_term['xgs-ponid'])
-            onu = pon.onu(vont_ani['onu-id'])
-
-        except Exception:
-            pass
-
-        return onu
-
-    @property
-    def channel_id(self):
-        return self._channel_id             # SEBA    (xPON mode may be only one using this. May also be no one)
-
     @property
     def serial_number_64(self):
         return self._serial_number_base64
@@ -332,6 +226,7 @@
 
     @property
     def timestamp(self):
+        # Last time the KPI items were updated
         return self._timestamp
 
     @timestamp.setter
@@ -384,8 +279,8 @@
     def create(self, tconts, gem_ports, reflow=False):
         """
         Create (or reflow) this ONU to hardware
-        :param tconts: (TCont) Current TCONT information
-        :param gem_ports: (GemPort) Current GEM Port configuration information
+        :param tconts: (dict) Current TCONT information
+        :param gem_ports: (dict) Current GEM Port configuration information
         :param reflow: (boolean) Flag, if True, indicating if this is a reflow ONU
                                  information after an unmanaged OLT hardware reboot
         """
@@ -399,7 +294,7 @@
         name = 'onu-create-{}-{}-{}: {}'.format(self._pon_id, self._onu_id,
                                                 self._serial_number_base64, self._enabled)
 
-        first_sync = self._sync_tick if self._created else 3
+        first_sync = self._sync_tick if self._created else 5
 
         if not self._created or reflow:
             try:
@@ -421,21 +316,19 @@
                 except Exception as e:
                     self.log.warn('onu-exists-check', pon_id=self.pon_id, onu_id=self.onu_id,
                                   serial_number=self.serial_number)
-                    if self.olt.xpon_support:                   # xPON mode does rediscovery...
-                        raise
 
         # Now set up all TConts & GEM-ports
-        for _, tcont in tconts.items():
+        for tcont in tconts:
             try:
-                _results = yield self.add_tcont(tcont, reflow=reflow)
+                _results = yield self.add_tcont(tcont['object'], reflow=reflow)
 
             except Exception as e:
                 self.log.exception('add-tcont', tcont=tcont, e=e)
                 first_sync = 2    # Expedite first hw-sync
 
-        for _, gem_port in gem_ports.items():
+        for gem_port in gem_ports:
             try:
-                _results = yield self.add_gem_port(gem_port, reflow=reflow)
+                _results = yield self.add_gem_port(gem_port['object'], reflow=reflow)
 
             except Exception as e:
                 self.log.exception('add-gem-port', gem_port=gem_port, reflow=reflow, e=e)
@@ -457,7 +350,6 @@
         self._cancel_deferred()
 
         # Remove from H/W
-
         gem_ids = self._gem_ports.keys()
         alloc_ids = self._tconts.keys()
 
@@ -496,7 +388,6 @@
             self.log.exception('onu-delete', e=e)
 
         self._olt = None
-        self._channel_id = None
         returnValue('deleted')
 
     def start(self):
@@ -579,8 +470,8 @@
                                       if alloc_id in matching_alloc_ids}
                 dl.extend(sync_matching_tconts(matching_hw_tconts))
 
-            except Exception as e:
-                self.log.exception('hw-sync-tconts', e=e)
+            except Exception as e2:
+                self.log.exception('hw-sync-tconts', e=e2)
 
             return dl
 
@@ -625,9 +516,7 @@
                         my_be.weight != hw_be.weight
 
                 if reflow:
-                    dl.append(my_tcont.add_to_hardware(self.olt.rest_client,
-                                                       self._pon_id,
-                                                       self._onu_id))
+                    dl.append(my_tcont.add_to_hardware(self.olt.rest_client))
             return dl
 
         def sync_gem_ports(hw_gem_ports):
@@ -671,8 +560,6 @@
                         gem_port.encryption != hw_gem_port.encryption or\
                         gem_port.omci_transport != hw_gem_port.omci_transport:
                     dl.append(gem_port.add_to_hardware(self.olt.rest_client,
-                                                       self.pon.pon_id,
-                                                       self.onu_id,
                                                        operation='PATCH'))
             return dl
 
@@ -693,7 +580,6 @@
             # Speed up sequential resync a limited number of times if out of sync
             # With 60 second initial an typical worst case resync of 4 times, this
             # should resync an ONU and all it's gem-ports and tconts within <90 seconds
-
             if self._expedite_sync and self._enabled:
                 self._expedite_count += 1
                 if self._expedite_count < _MAX_EXPEDITE_COUNT:
@@ -707,7 +593,6 @@
 
         # If PON is not enabled, skip hw-sync. If ONU not enabled, do it but less
         # frequently
-
         if not self.pon.enabled:
             return reschedule('not-enabled')
 
@@ -758,8 +643,7 @@
         self._tconts[tcont.alloc_id] = tcont
 
         try:
-            results = yield tcont.add_to_hardware(self.olt.rest_client,
-                                                  self._pon_id, self._onu_id)
+            results = yield tcont.add_to_hardware(self.olt.rest_client)
 
         except Exception as e:
             self.log.exception('tcont', tcont=tcont, reflow=reflow, e=e)
@@ -777,9 +661,7 @@
 
         tcont.traffic_descriptor = new_td
         try:
-            results = yield tcont.add_to_hardware(self.olt.rest_client,
-                                                  self._pon_id,
-                                                  self._onu_id)
+            results = yield tcont.add_to_hardware(self.olt.rest_client)
         except Exception as e:
             self.log.exception('tcont', tcont=tcont, e=e)
             # May occur with xPON provisioning, use hw-resync to recover
@@ -796,9 +678,7 @@
 
         del self._tconts[alloc_id]
         try:
-            results = yield tcont.remove_from_hardware(self.olt.rest_client,
-                                                       self._pon_id,
-                                                       self._onu_id)
+            results = yield tcont.remove_from_hardware(self.olt.rest_client)
         except RestInvalidResponseCode as e:
             results = None
             if e.code != 404:
@@ -813,15 +693,10 @@
     def gem_port(self, gem_id):
         return self._gem_ports.get(gem_id)
 
-    def gem_ids(self, untagged_gem):
+    def gem_ids(self):
         """Get all GEM Port IDs used by this ONU"""
-        if untagged_gem:
-            gem_ids = sorted([gem_id for gem_id, gem in self._gem_ports.items()
-                             if gem.untagged and not gem.multicast])
-            return gem_ids
-        else:
-            return sorted([gem_id for gem_id, gem in self._gem_ports.items()
-                          if not gem.multicast and not gem.untagged])
+        return sorted([gem_id for gem_id, gem in self._gem_ports.items()
+                       if not gem.multicast])
 
     @inlineCallbacks
     def add_gem_port(self, gem_port, reflow=False):
@@ -842,13 +717,13 @@
         gem_port.onu_id = self.onu_id if self.onu_id is not None else -1
         gem_port.intf_id = self.intf_id
 
+        # TODO: Currently only support a single UNI. Need to support multiple and track their GEM Ports
+        #       Probably best done by having a UNI-Port class (keep it simple)
         self.log.info('add', gem_port=gem_port, reflow=reflow)
         self._gem_ports[gem_port.gem_id] = gem_port
 
         try:
-            results = yield gem_port.add_to_hardware(self.olt.rest_client,
-                                                     self._pon_id,
-                                                     self.onu_id)
+            results = yield gem_port.add_to_hardware(self.olt.rest_client)
             # May need to update flow tables/evc-maps
             if gem_port.alloc_id in self._tconts:
                 from flow.flow_entry import FlowEntry
@@ -886,9 +761,8 @@
                 for evc_map in evc_maps:
                     evc_map.remove_gem_port(gem_port)
 
-            yield gem_port.remove_from_hardware(self.olt.rest_client,
-                                                self._pon_id,
-                                                self.onu_id)
+            yield gem_port.remove_from_hardware(self.olt.rest_client)
+
         except RestInvalidResponseCode as e:
             if e.code != 404:
                 self.log.exception('onu-delete', e=e)
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index dd1b7e3..0b18dc2 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -20,7 +20,6 @@
 from port import AdtnPort
 from twisted.internet import reactor, defer
 from twisted.internet.defer import inlineCallbacks, returnValue
-from common.utils.indexpool import IndexPool
 from adtran_olt_handler import AdtranOltHandler
 from net.adtran_rest import RestInvalidResponseCode
 from codec.olt_config import OltConfig
@@ -28,6 +27,12 @@
 from voltha.extensions.alarms.onu.onu_los_alarm import OnuLosAlarm
 from voltha.protos.common_pb2 import AdminState
 from voltha.protos.device_pb2 import Port
+from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
+from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
+from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
+from xpon.olt_traffic_descriptor import OltTrafficDescriptor
+import resources.adtranolt_platform as platform
+
 
 try:
     from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
@@ -39,8 +44,7 @@
     """
     GPON Port
     """
-    MAX_ONUS_SUPPORTED = 256
-    DEFAULT_ENABLED = False
+    MAX_ONUS_SUPPORTED = 128
     MAX_DEPLOYMENT_RANGE = 25000    # Meters (OLT-PB maximum)
 
     _MCAST_ONU_ID = 253
@@ -52,7 +56,6 @@
 
     def __init__(self, parent, **kwargs):
         super(PonPort, self).__init__(parent, **kwargs)
-
         assert 'pon-id' in kwargs, 'PON ID not found'
 
         self._parent = parent
@@ -69,36 +72,34 @@
         self._discovery_tick = 20.0
         self._no_onu_discover_tick = self._discovery_tick / 2
         self._discovered_onus = []  # List of serial numbers
+        self._discovery_deferred = None     # Specifically for ONU discovery
 
-        self._onus = {}         # serial_number-base64 -> ONU  (allowed list)
-        self._onu_by_id = {}    # onu-id -> ONU
-        self._next_onu_id = Onu.MIN_ONU_ID
-        self._mcast_gem_ports = {}                # VLAN -> GemPort
-        self._onu_id_pool = IndexPool(Onu.MAX_ONU_ID - Onu.MIN_ONU_ID + 1, Onu.MIN_ONU_ID)
+        self._onus = {}                     # serial_number-base64 -> ONU  (allowed list)
+        self._onu_by_id = {}                # onu-id -> ONU
+        self._mcast_gem_ports = {}          # VLAN -> GemPort
 
-        self._discovery_deferred = None           # Specifically for ONU discovery
-        self._active_los_alarms = set()           # ONU-ID
+        self._active_los_alarms = set()     # ONU-ID
 
-        # xPON configuration        # SEBA
-        self._activation_method = 'autodiscovery' if parent.xpon_support else 'autoactivate'
+        # xPON configuration
+        self._activation_method = 'autoactivate'
 
-        if self.olt.xpon_support:
-            self._xpon_name = None
-            self._downstream_fec_enable = False
-            self._upstream_fec_enable = False
-        else:
-            self._xpon_name = 'channel-termination {}'.format(self._pon_id)
-            self._downstream_fec_enable = True
-            self._upstream_fec_enable = True
-
+        self._downstream_fec_enable = True
+        self._upstream_fec_enable = True
         self._deployment_range = 25000
         self._authentication_method = 'serial-number'
         self._mcast_aes = False
-        self._line_rate = 'down_10_up_10'
 
         # Statistics
         self.tx_bip_errors = 0
 
+        # xPON Configuration (TODO: Move Tcont/GEMPort to ONU eventually)
+        #                     TODO: TD may be system-wide, wait for Service Profiles
+        self._v_ont_anis = {}             # Name -> dict
+        self._ont_anis = {}               # Name -> dict
+        self._tconts = {}                 # Name -> dict
+        self._traffic_descriptors = {}    # Name -> dict
+        self._gem_ports = {}              # Name -> dict
+
     def __str__(self):
         return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
                                                                  self._admin_state,
@@ -120,15 +121,6 @@
         return self._port
 
     @property
-    def xpon_name(self):
-        return self._xpon_name
-
-    @xpon_name.setter
-    def xpon_name(self, value):
-        assert '/' not in value, "xPON names cannot have embedded forward slashes '/'"
-        self._xpon_name = value
-
-    @property
     def pon_id(self):
         return self._pon_id
 
@@ -211,19 +203,6 @@
                 pass    # TODO
 
     @property
-    def line_rate(self):
-        return self._line_rate
-
-    @line_rate.setter
-    def line_rate(self, value):
-        assert isinstance(value, (str, unicode)), 'Line Rate is a string'
-        # TODO cast to enum
-        if self._line_rate != value:
-            self._line_rate = value
-            if self.state == AdtnPort.State.RUNNING:
-                pass    # TODO
-
-    @property
     def deployment_range(self):
         """Maximum deployment range (in meters)"""
         return self._deployment_range
@@ -273,9 +252,6 @@
         if value not in PonPort._SUPPORTED_ACTIVATION_METHODS:
             raise ValueError('Invalid ONU activation method')
 
-        if not self._parent.xpon_support and value != 'autoactivate':
-            raise ValueError('Only autoactivate is supported in non-xPON mode')
-
         self._activation_method = value
 
     @property
@@ -434,8 +410,7 @@
         Set the PON Port to a known good state on initial port startup.  Actual
         PON 'Start' is done elsewhere
         """
-        initial_port_state = AdminState.DISABLED if self._parent.xpon_support \
-            else AdminState.ENABLED
+        initial_port_state = AdminState.ENABLED
         self.log.info('reset', initial_state=initial_port_state)
 
         try:
@@ -481,14 +456,14 @@
 
         returnValue('Reset complete')
 
-    def gem_ids(self, logical_port, untagged_gem, multicast_gems=False):
+    def gem_ids(self, logical_port, flow_vlan, multicast_gems=False):
         """
         Get all GEM Port IDs used on a given PON
 
-        :param logical_port: (int) Logical port umber of ONU. None if for all ONUs
+        :param logical_port: (int) Logical port number of ONU. None if for all ONUs
                           on PON, if Multicast, VID for Multicast, or None for all
                           Multicast GEMPorts
-        :param untagged_gem: (boolean) Select from special purpose untagged GEM Port
+        :param flow_vlan: (int) If not None, this is the ingress tag (c-tag)
         :param multicast_gems: (boolean) Select from available Multicast GEM Ports
         :return: (dict) data_gem -> key -> onu-id, value -> tuple(sorted list of GEM Port IDs, onu_vid)
                         mcast_gem-> key -> mcast-vid, value -> GEM Port IDs
@@ -497,16 +472,14 @@
 
         if multicast_gems:
             # Multicast GEMs belong to the PON, but we may need to register them on
-            # all ONUs. Rework when BBF MCAST Gems are supported
-            for vlan, gem_port in self._mcast_gem_ports.iteritems():    # TODO: redo logic
+            # all ONUs. TODO: Rework when BBF MCAST is addressed in VOLTHA v2.O+
+            for vlan, gem_port in self._mcast_gem_ports.iteritems():
                 if logical_port is None or (logical_port == vlan and logical_port in self.olt.multicast_vlans):
                     gem_ids[vlan] = ([gem_port.gem_id], None)
         else:
             for onu_id, onu in self._onu_by_id.iteritems():
                 if logical_port is None or logical_port == onu.logical_port:
-                    gem_ids[onu_id] = (onu.gem_ids(untagged_gem),
-                                       onu.onu_vid if not untagged_gem
-                                       else self.olt.untagged_vlan)
+                    gem_ids[onu_id] = (onu.gem_ids(), flow_vlan)
         return gem_ids
 
     def _get_pon_config(self):
@@ -663,6 +636,7 @@
         new, rediscovered_onus = self._process_status_onu_discovered_list(status.discovered_onu)
 
         # Process newly discovered ONU list and rediscovered ONUs
+
         for serial_number in new | rediscovered_onus:
             reactor.callLater(0, self.add_onu, serial_number, status)
 
@@ -677,44 +651,6 @@
         # Process GEM Port information
         self._update_gem_status(status.gems, timestamp)
 
-    def _handle_discovered_onu(self, child_device, ind_info):
-        pon_id = ind_info['_pon_id']
-        olt_id = ind_info['_olt_id']
-
-        if ind_info['_sub_group_type'] == 'onu_discovery':
-            self.log.info('Activation-is-in-progress', olt_id=olt_id,
-                          pon_ni=pon_id, onu_data=ind_info,
-                          onu_id=child_device.proxy_address.onu_id)
-
-        elif ind_info['_sub_group_type'] == 'sub_term_indication':
-            self.log.info('ONU-activation-is-completed', olt_id=olt_id,
-                          pon_ni=pon_id, onu_data=ind_info)
-
-            msg = {'proxy_address': child_device.proxy_address,
-                   'event': 'activation-completed', 'event_data': ind_info}
-
-            # Send the event message to the ONU adapter
-            self.adapter_agent.publish_inter_adapter_message(child_device.id,
-                                                             msg)
-            if ind_info['activation_successful'] is True:
-                for key, v_ont_ani in dict():       # self.v_ont_anis.items():
-                    if v_ont_ani.v_ont_ani.data.onu_id == \
-                            child_device.proxy_address.onu_id:
-                        for tcont_key, tcont in v_ont_ani.tconts.items():
-                            owner_info = dict()
-                            # To-Do: Right Now use alloc_id as schduler ID. Need to
-                            # find way to generate uninqe number.
-                            id = tcont.alloc_id
-                            owner_info['type'] = 'agg_port'
-                            owner_info['intf_id'] = \
-                                child_device.proxy_address.channel_id
-                            owner_info['onu_id'] = \
-                                child_device.proxy_address.onu_id
-                            owner_info['alloc_id'] = tcont.alloc_id
-        else:
-            self.log.info('Invalid-ONU-event', olt_id=olt_id,
-                          pon_ni=ind_info['_pon_id'], onu_data=ind_info)
-
     def _process_statistics(self, status, timestamp):
         self.timestamp = timestamp
         self.rx_packets = status.rx_packets
@@ -797,69 +733,26 @@
         :param serial_number: (string) Decoded (not base64) serial number string
         :return: (dict) onu config data or None on lookup failure
         """
-        activate_onu = False
         try:
             if self.activation_method == "autodiscovery":
-                if self.authentication_method == 'serial-number':
-                    gpon_info = self.olt.get_xpon_info(self.pon_id)
-                    try:
-                        # TODO: Change iteration to itervalues below
-                        vont_info = next(info for _, info in gpon_info['vont-anis'].items()
-                                         if info.get('expected-serial-number') == serial_number)
+                # if self.authentication_method == 'serial-number':
+                raise NotImplemented('TODO: Not supported at this time')
 
-                        # ont_info = next(info for _, info in gpon_info['ont-anis'].items()
-                        #                 if info.get('name') == vont_info['name'])
-
-                        vont_ani = vont_info['data']
-                        onu_id = vont_info['onu-id']
-                        enabled = vont_info['enabled']
-                        channel_speed = vont_info['upstream-channel-speed']
-                        xpon_name = vont_info['name']
-                        upstream_fec_enabled = True   # TODO: ont_info.get('upstream-fec', False)
-
-                        tconts = {key: val for key, val in gpon_info['tconts'].iteritems()
-                                  if val.vont_ani == vont_info['name']}
-
-                        gem_ports = {key: val for key, val in gpon_info['gem-ports'].iteritems()
-                                     if val.tcont_ref in tconts.keys()}
-
-                        venet = next((val for val in gpon_info['v-enets'].itervalues()
-                                      if val['vont-ani'] == vont_info['name']), None)
-                        # TODO: need to handle case where ont_ani, gems, venets, tconts are assigned
-                        #       after activation is started. only vont-ani needs to be set to get here
-
-                    except StopIteration:
-                        # Can happen if vont-ani or ont-ani has not yet been configured
-                        self.log.debug('no-vont-or-ont')
-                        return None, False
-
-                    except Exception as e:
-                        self.log.exception('autodiscovery', e=e)
-                        raise
-                else:
-                    self.log.debug('not-serial-number-authentication')
-                    return None, False
             elif self.activation_method == "autoactivate":
                 # TODO: Currently a somewhat copy of the xPON way to do things
                 #       update here with Technology profile info when it becomes available
-                gpon_info, activate_onu = self.olt.get_device_profile_info(self,
-                                                                           serial_number)
+                activate_onu, tconts, gem_ports = self.get_device_profile_info(serial_number)
+
                 try:
                     # TODO: (SEBA) All of this can be greatly simplified once we fully
                     #       deprecate xPON support
-                    vont_ani = next(info for _, info in gpon_info['vont-anis'].items()
+                    vont_ani = next(info for _, info in self._v_ont_anis.items()
                                     if info.get('expected-serial-number') == serial_number)
 
-                    # ont_ani = gpon_info['ont-anis'][vont_ani['name']]
                     onu_id = vont_ani['onu-id']
                     enabled = vont_ani['enabled']
                     channel_speed = vont_ani['upstream-channel-speed']
-                    xpon_name = vont_ani['name']
-                    upstream_fec_enabled = gpon_info['ont-anis'][vont_ani['name']]['upstream-fec']
-
-                    tconts = gpon_info['tconts']
-                    gem_ports = gpon_info['gem-ports']
-                    venet = None
+                    upstream_fec_enabled = self._ont_anis[vont_ani['name']]['upstream-fec']
 
                 except StopIteration:
                     # Can happen if vont-ani or ont-ani has not yet been configured
@@ -876,7 +769,6 @@
             onu_info = {
                 'device-id': self.olt.device_id,
                 'serial-number': serial_number,
-                'xpon-name': xpon_name,
                 'pon': self,
                 'onu-id': onu_id,
                 'enabled': enabled,
@@ -885,17 +777,11 @@
                 'password': Onu.DEFAULT_PASSWORD,
                 't-conts': tconts,
                 'gem-ports': gem_ports,
-                'channel-id': self.olt.get_channel_id(self._pon_id, onu_id),  # TODO: Is this used anywhere?
-                'vont-ani': vont_ani,
-                'venet': venet
             }
-            if self.olt.xpon_support:
-                onu_info['onu-vid'] = self.olt.get_onu_vid(onu_id)
-            else:
-                import adtranolt_platform as platform
-                intf_id = platform.intf_id_to_port_no(self._pon_id, Port.PON_OLT)
-                # TODO: Currently only one ONU port and it is hardcoded to port 0
-                onu_info['uni-ports'] = [platform.mk_uni_port_num(intf_id, onu_id)]
+            intf_id = platform.intf_id_to_port_no(self._pon_id, Port.PON_OLT)
+
+            # TODO: Currently only one ONU port and it is hardcoded to port 0
+            onu_info['uni-ports'] = [platform.mk_uni_port_num(intf_id, onu_id)]
 
             # Hold off ONU activation until at least one GEM Port is defined.
             self.log.debug('onu-info-tech-profiles', gem_ports=gem_ports)
@@ -918,27 +804,16 @@
         self.log.info('add-onu', serial_number=serial_number,
                       serial_number_64=serial_number_64, status=status)
 
-        # For non-XPON mode, it takes a little while for a new ONU to be removed from
-        # the discovery list. Return early here so extra ONU IDs are not allocated
-        if not self.olt.xpon_support and serial_number_64 in self._onus:
+        # It takes a little while for a new ONU to be removed from the discovery
+        # list. Return early here so extra ONU IDs are not allocated
+        if serial_number_64 in self._onus:
             returnValue('wait-for-fpga')
 
         onu_info, activate_onu = self._get_onu_info(serial_number)
 
         if activate_onu:
-            # SEBA  - This is the new no-xPON way
             alarm = OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number)
             reactor.callLater(0, alarm.raise_alarm)
-            # Fall through. We do not want to return and wait for the next discovery poll
-            # as that will consume an additional ONU ID (currently allocated during
-            # add_onu_device).
-
-        elif onu_info is None:
-            # SEBA  - This is the OLD xPON way
-            self.log.info('onu-lookup-failure', serial_number=serial_number,
-                          serial_number_64=serial_number_64)
-            OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number).raise_alarm()
-            returnValue('new-onu')
 
         if serial_number_64 not in status.onus or onu_info['onu-id'] in self._active_los_alarms:
             onu = None
@@ -952,17 +827,14 @@
                     (serial_number_64 not in self._onus and onu_id in self._onu_by_id):
                 # May be here due to unmanaged power-cycle on OLT or fiber bounced for a
                 # previously activated ONU.
-                if self.olt.xpon_support:                           # xPON will reassign the same ONU ID
-                    # Drop it and add back on next discovery cycle
-                    self.delete_onu(onu_id)
-                else:
-                    # TODO: Track when the ONU was discovered, and if > some maximum amount
-                    #       place the ONU (with serial number & ONU ID) on a wait list and
-                    #       use that to recover the ONU ID should it show up within a
-                    #       reasonable amount of time.  Periodically groom the wait list and
-                    #       delete state ONUs so we can reclaim the ONU ID.
-                    #
-                    returnValue('waiting-for-fpga')    # non-XPON mode will not
+                #
+                # TODO: Track when the ONU was discovered, and if > some maximum amount
+                #       place the ONU (with serial number & ONU ID) on a wait list and
+                #       use that to recover the ONU ID should it show up within a
+                #       reasonable amount of time.  Periodically groom the wait list and
+                #       delete state ONUs so we can reclaim the ONU ID.
+                #
+                returnValue('waiting-for-fpga')    # non-XPON mode will not
 
             elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
                 self.log.warning('max-onus-provisioned', count=len(self._onus))
@@ -975,30 +847,31 @@
                 self._onu_by_id[onu.onu_id] = onu
 
             if onu is not None:
-                tconts = onu_info['t-conts']
-                gem_ports = onu_info['gem-ports']
+                tconts = onu_info.pop('t-conts')
+                gem_ports = onu_info.pop('gem-ports')
 
                 if activate_onu:
-                    # SEBA  - This is the new no-xPON way to start an ONU device handler
-                    _onu_device = self._parent.add_onu_device(self._port_no,        # PON ID
-                                                              onu_info['onu-id'],   # ONU ID
+                    _onu_device = self._parent.add_onu_device(self._port_no,     # PON ID
+                                                              onu.onu_id,        # ONU ID
                                                               serial_number,
-                                                              tconts, gem_ports)
+                                                              tconts,
+                                                              gem_ports)
                 try:
-                    # Add Multicast to PON on a per-ONU basis until xPON multicast support is ready
+                    # Add Multicast to PON on a per-ONU basis until xPON multicast support
+                    # is ready
                     # In xPON/BBF, mcast gems tie back to the channel-pair
                     # MCAST VLAN IDs stored as a negative value
 
-                    for id_or_vid, gem_port in gem_ports.iteritems():  # TODO: Deprecate this when BBF ready
-                        try:
-                            if gem_port.multicast:
-                                self.log.debug('id-or-vid', id_or_vid=id_or_vid)
-                                vid = self.olt.multicast_vlans[0] if len(self.olt.multicast_vlans) else None
-                                if vid is not None:
-                                    self.add_mcast_gem_port(gem_port, vid)
-
-                        except Exception as e:
-                            self.log.exception('id-or-vid', e=e)
+                    # for id_or_vid, gem_port in gem_ports.iteritems():  # TODO: Deprecate this when BBF ready
+                    #     try:
+                    #         if gem_port.multicast:
+                    #             self.log.debug('id-or-vid', id_or_vid=id_or_vid)
+                    #             vid = self.olt.multicast_vlans[0] if len(self.olt.multicast_vlans) else None
+                    #             if vid is not None:
+                    #                 self.add_mcast_gem_port(gem_port, vid)
+                    #
+                    #     except Exception as e:
+                    #         self.log.exception('id-or-vid', e=e)
 
                     # TODO: Need to clean up TCont and GEM-Port on ONU delete in non-xPON mode
                     _results = yield onu.create(tconts, gem_ports)
@@ -1009,12 +882,12 @@
 
     @property
     def get_next_onu_id(self):
-        assert not self.olt.xpon_support, 'Only non-XPON mode allocates ONU IDs.  xPON assigns tem'
-        return self._onu_id_pool.get_next()
+        return self._parent.resource_mgr.get_onu_id(self._pon_id)
+        # return self._onu_id_pool.get_next()
 
     def release_onu_id(self, onu_id):
-        if not self.olt.xpon_support:
-            self._onu_id_pool.release(onu_id)
+        self._parent.resource_mgr.free_onu_id(self._pon_id, onu_id)
+        # self._onu_id_pool.release(onu_id)
 
     @inlineCallbacks
     def _remove_from_hardware(self, onu_id):
@@ -1071,3 +944,124 @@
         assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
 
         self._mcast_gem_ports[vlan] = mcast_gem
+
+    #  ===========================================================================
+    #
+    #  Some xPON methods that need refactoring
+
+    @property
+    def tconts(self):
+        return self._tconts
+
+    @property
+    def traffic_descriptors(self):
+        return self._traffic_descriptors
+
+    @property
+    def gem_ports(self):
+        return self._gem_ports
+
+    def get_device_profile_info(self, serial_number):
+        vont_ani_info = next((info for _, info in self._v_ont_anis.items()
+                             if info.get('expected-serial-number') == serial_number), None)
+        # New ONU?
+        activate_onu = vont_ani_info is None
+
+        tconts = list()
+        gem_ports = list()
+
+        if activate_onu:
+            onu_id = self.get_next_onu_id
+            name = 'customer-{}-{}'.format(self.pon_id, onu_id)
+            vont_ani_info = {
+                'name'                    : name,
+                'enabled'                 : True,
+                'description'             : '',
+                'pon-id'                  : self.pon_id,
+                'onu-id'                  : onu_id,
+                'expected-serial-number'  : serial_number,
+                'expected-registration-id': '',                         # TODO: How about this?
+                'upstream-channel-speed'  : 10000000000,
+            }
+            ont_ani_info = {
+                'name':             name,
+                'description':      '',
+                'enabled':          True,
+                'upstream-fec':     True,
+                'mgnt-gemport-aes': False
+            }
+            assert name not in self._v_ont_anis
+            assert name not in self._ont_anis
+            self._v_ont_anis[name] = vont_ani_info
+            self._ont_anis[name] = ont_ani_info
+
+            tcont, tc, td = self.create_xpon_tcont(onu_id)
+            from xpon.olt_tcont import OltTCont
+            tc['object'] = OltTCont.create(tc,
+                                           OltTrafficDescriptor.create(tc['td-ref']),
+                                           self.pon_id, onu_id)
+            self._tconts[tcont.name] = tc['object']
+            tconts.append(tc)
+
+            # Now create the User-Data GEM-Ports
+            num_priorities = 1                          # TODO: Pull from tech-profile later
+            for index in range(0, num_priorities):
+                gem_port, gp = self.create_xpon_gem_port(onu_id, index, tcont)
+
+                from xpon.olt_gem_port import OltGemPort
+                gp['object'] = OltGemPort.create(self, gp, self.pon_id, onu_id)
+                self._gem_ports[gem_port.name] = gp['object']
+                gem_ports.append(gp)
+
+        return activate_onu, tconts, gem_ports
+
+    def create_xpon_gem_port(self, onu_id, index, tcont):
+        # gem port creation (this initial one is for untagged ONU data support / EAPOL)
+        gem_port = GemportsConfigData()
+        gem_port.gemport_id = platform.mk_gemport_id(self.pon_id, onu_id, idx=index)
+        gem_port.name = 'gem-{}-{}-{}'.format(self.pon_id, onu_id, gem_port.gemport_id)
+
+        gem_port.tcont_ref = tcont.name
+        gp = {
+            'name': gem_port.name,
+            'gemport-id': gem_port.gemport_id,
+            'tcont-ref': gem_port.tcont_ref,
+            'encryption': False,
+            'traffic-class': 0,
+            'data': gem_port
+        }
+        return gem_port, gp
+
+    def create_xpon_tcont(self, onu_id):
+        """ Create the xPON TCONT Config data """
+        tcont = TcontsConfigData()
+        tcont.name = 'tcont-{}-{}-data'.format(self.pon_id, onu_id)
+        pon_intf_onu_id = (self.pon_id, onu_id)
+        tcont.alloc_id = self._parent.resource_mgr.get_alloc_id(pon_intf_onu_id)
+        # TODO: Add release of alloc_id on ONU delete and/or TCONT delete
+
+        traffic_desc = TrafficDescriptorProfileData(name='BestEffort',
+                                                    fixed_bandwidth=0,
+                                                    assured_bandwidth=0,
+                                                    maximum_bandwidth=10000000000,
+                                                    priority=0,
+                                                    weight=0,
+                                                    additional_bw_eligibility_indicator=0)
+        tc = {
+            'name': tcont.name,
+            'alloc-id': tcont.alloc_id,
+            'pon-id': self.pon_id,
+            'onu-id': onu_id,
+            'td-ref': {  # TODO: This should be the TD Name and the TD installed in xpon cache
+                'name': traffic_desc.name,
+                'fixed-bandwidth': traffic_desc.fixed_bandwidth,
+                'assured-bandwidth': traffic_desc.assured_bandwidth,
+                'maximum-bandwidth': traffic_desc.maximum_bandwidth,
+                'priority': traffic_desc.priority,
+                'weight': traffic_desc.weight,
+                'additional-bw-eligibility-indicator': 0,
+                'data': traffic_desc
+            },
+            'data': tcont
+        }
+        return tcont, tc, traffic_desc
diff --git a/voltha/adapters/adtran_olt/port.py b/voltha/adapters/adtran_olt/port.py
index 1eb0a13..9509556 100644
--- a/voltha/adapters/adtran_olt/port.py
+++ b/voltha/adapters/adtran_olt/port.py
@@ -56,12 +56,8 @@
 
         # TODO: Deprecate 'enabled' and use admin_state instead may want initial to always be
         # disabled and then in derived classes, set it in the 'reset' method called on startup.
-        if parent.xpon_support:
-            self._enabled = not parent.xpon_support
-            self._admin_state = AdminState.DISABLED
-        else:
-            self._enabled = not parent.xpon_support
-            self._admin_state = AdminState.ENABLED
+        self._enabled = True
+        self._admin_state = AdminState.ENABLED
 
         self._oper_status = OperStatus.DISCOVERED
         self._state = AdtnPort.State.INITIAL
@@ -73,7 +69,7 @@
         self.rx_bytes = 0
         self.tx_packets = 0
         self.tx_bytes = 0
-        self.timestamp = 0
+        self.timestamp = 0      # UTC when KPI items last updated
 
     def __del__(self):
         self.stop()
diff --git a/voltha/adapters/adtran_olt/resources/__init__.py b/voltha/adapters/adtran_olt/resources/__init__.py
new file mode 100644
index 0000000..9c454e3
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/__init__.py
@@ -0,0 +1,14 @@
+# 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.
+#
diff --git a/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
new file mode 100644
index 0000000..97ec852
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
@@ -0,0 +1,211 @@
+#
+# 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.
+#
+
+import structlog
+
+from common.pon_resource_manager.resource_manager import PONResourceManager
+from voltha.registry import registry
+from voltha.core.config.config_backend import ConsulStore
+from voltha.core.config.config_backend import EtcdStore
+from adtran_resource_manager import AdtranPONResourceManager
+
+
+class AdtranOltResourceMgr(object):
+
+    GEMPORT_IDS = "gemport_ids"
+    ALLOC_IDS = "alloc_ids"
+    BASE_PATH_KV_STORE = "adtran_olt/{}"       # adtran_olt/<device_id>
+
+    def __init__(self, device_id, host_and_port, extra_args, device_info):
+        self.log = structlog.get_logger(id=device_id,
+                                        ip=host_and_port)
+        self.device_id = device_id
+        self.host_and_port = host_and_port
+        self.extra_args = extra_args
+        self.device_info = device_info
+        self.args = registry('main').get_args()
+
+        # KV store's IP Address and PORT
+        # host, port = '127.0.0.1', 8500
+        if self.args.backend == 'etcd':
+            host, port = self.args.etcd.split(':', 1)
+            self.kv_store = EtcdStore(host, port,
+                                      AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+        elif self.args.backend == 'consul':
+            host, port = self.args.consul.split(':', 1)
+            self.kv_store = ConsulStore(host, port,
+                                        AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+        else:
+            self.log.error('Invalid-backend')
+            raise Exception("Invalid-backend-for-kv-store")
+
+        self.resource_mgr = AdtranPONResourceManager(
+            self.device_info.technology,
+            self.extra_args,
+            self.device_id, self.args.backend,
+            host, port
+        )
+        # Flag to indicate whether information fetched from device should
+        # be used to initialize PON Resource Ranges
+        self.use_device_info = False
+
+        self.initialize_device_resource_range_and_pool()
+
+    def __del__(self):
+        self.log.info("clearing-device-resource-pool")
+        self.resource_mgr.clear_device_resource_pool()
+
+    def get_onu_id(self, pon_intf_id):
+        onu_id = self.resource_mgr.get_resource_id(pon_intf_id,
+                                                   PONResourceManager.ONU_ID,
+                                                   1)
+        if onu_id is not None:
+            pon_intf_onu_id = (pon_intf_id, onu_id)
+            self.resource_mgr.init_resource_map(pon_intf_onu_id)
+
+        return onu_id
+
+    def free_onu_id(self, pon_intf_id, onu_id):
+        self.resource_mgr.free_resource_id(pon_intf_id,
+                                           PONResourceManager.ONU_ID,
+                                           onu_id)
+        pon_intf_onu_id = (pon_intf_id, onu_id)
+        self.resource_mgr.remove_resource_map(pon_intf_onu_id)
+
+    def get_alloc_id(self, pon_intf_onu_id):
+        # Derive the pon_intf from the pon_intf_onu_id tuple
+        pon_intf = pon_intf_onu_id[0]
+        onu_id = pon_intf_onu_id[1]
+        alloc_id_list = self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_onu_id)
+
+        if alloc_id_list and len(alloc_id_list) > 0:
+            # Since we support only one alloc_id for the ONU at the moment,
+            # return the first alloc_id in the list, if available, for that
+            # ONU.
+            return alloc_id_list[0]
+
+        alloc_id_list = self.resource_mgr.get_resource_id(
+            pon_intf_id=pon_intf,
+            onu_id=onu_id,
+            resource_type=PONResourceManager.ALLOC_ID,
+            num_of_id=1
+        )
+        if alloc_id_list and len(alloc_id_list) == 0:
+            self.log.error("no-alloc-id-available")
+            return None
+
+        # update the resource map on KV store with the list of alloc_id
+        # allocated for the pon_intf_onu_id tuple
+        self.resource_mgr.update_alloc_ids_for_onu(pon_intf_onu_id,
+                                                   alloc_id_list)
+
+        # Since we request only one alloc id, we refer the 0th
+        # index
+        alloc_id = alloc_id_list[0]
+
+        return alloc_id
+
+    def get_gemport_id(self, pon_intf_onu_id):
+        # Derive the pon_intf and onu_id from the pon_intf_onu_id tuple
+        pon_intf = pon_intf_onu_id[0]
+        onu_id = pon_intf_onu_id[1]
+
+        gemport_id_list = self.resource_mgr.get_current_gemport_ids_for_onu(
+            pon_intf_onu_id)
+
+        if gemport_id_list and len(gemport_id_list) > 0:
+            # Since we support only one gemport_id for the ONU at the moment,
+            # return the first gemport_id in the list, if available, for that
+            # ONU.
+            return gemport_id_list[0]
+
+        gemport_id_list = self.resource_mgr.get_resource_id(
+            pon_intf_id=pon_intf,
+            resource_type=PONResourceManager.GEMPORT_ID,
+            num_of_id=1
+        )
+        if gemport_id_list and len(gemport_id_list) == 0:
+            self.log.error("no-gemport-id-available")
+            return None
+
+        # update the resource map on KV store with the list of gemport_id
+        # allocated for the pon_intf_onu_id tuple
+        self.resource_mgr.update_gemport_ids_for_onu(pon_intf_onu_id,
+                                                     gemport_id_list)
+
+        # We currently use only one gemport
+        gemport = gemport_id_list[0]
+
+        pon_intf_gemport = (pon_intf, gemport)
+        # This information is used when packet_indication is received and
+        # we need to derive the ONU Id for which the packet arrived based
+        # on the pon_intf and gemport available in the packet_indication
+        self.kv_store[str(pon_intf_gemport)] = str(onu_id)
+
+        return gemport
+
+    def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
+
+        alloc_ids = \
+            self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
+        pon_intf_id = pon_intf_id_onu_id[0]
+        onu_id = pon_intf_id_onu_id[1]
+        self.resource_mgr.free_resource_id(pon_intf_id,
+                                           PONResourceManager.ALLOC_ID,
+                                           alloc_ids)
+
+        gemport_ids = \
+            self.resource_mgr.get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
+        self.resource_mgr.free_resource_id(pon_intf_id,
+                                           PONResourceManager.GEMPORT_ID,
+                                           gemport_ids)
+
+        self.resource_mgr.free_resource_id(pon_intf_id,
+                                           PONResourceManager.ONU_ID,
+                                           onu_id)
+
+        # Clear resource map associated with (pon_intf_id, gemport_id) tuple.
+        self.resource_mgr.remove_resource_map(pon_intf_id_onu_id)
+
+        # Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
+        for gemport_id in gemport_ids:
+            del self.kv_store[str((pon_intf_id, gemport_id))]
+
+    def initialize_device_resource_range_and_pool(self):
+        if not self.use_device_info:
+            status = self.resource_mgr.init_resource_ranges_from_kv_store()
+            if not status:
+                self.log.error("failed-to-load-resource-range-from-kv-store")
+                # When we have failed to read the PON Resource ranges from KV
+                # store, use the information selected as the default.
+                self.use_device_info = True
+
+        if self.use_device_info:
+            self.log.info("using-device-info-to-init-pon-resource-ranges")
+            self.resource_mgr.init_default_pon_resource_ranges(
+                onu_id_start_idx=self.device_info.onu_id_start,
+                onu_id_end_idx=self.device_info.onu_id_end,
+                alloc_id_start_idx=self.device_info.alloc_id_start,
+                alloc_id_end_idx=self.device_info.alloc_id_end,
+                gemport_id_start_idx=self.device_info.gemport_id_start,
+                gemport_id_end_idx=self.device_info.gemport_id_end,
+                num_of_pon_ports=self.device_info.pon_ports,
+                intf_ids=self.device_info.intf_ids
+            )
+
+        # After we have initialized resource ranges, initialize the
+        # resource pools accordingly.
+        self.resource_mgr.init_device_resource_pool()
diff --git a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
new file mode 100644
index 0000000..83f8e33
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
@@ -0,0 +1,351 @@
+#
+# 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.
+#
+
+"""
+Resource Manager will be unique for each OLT device.
+
+It exposes APIs to create/free alloc_ids/onu_ids/gemport_ids. Resource Manager
+uses a KV store in backend to ensure resiliency of the data.
+"""
+from bitstring import BitArray
+import json
+from common.pon_resource_manager.resource_manager import PONResourceManager
+import adtranolt_platform as platform
+
+
+class AdtranPONResourceManager(PONResourceManager):
+    """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
+
+    # Constants for internal usage.
+    ONU_MAP = 'onu_map'
+
+    def init_device_resource_pool(self):
+        """
+        Initialize resource pool for all PON ports.
+        """
+        for pon_id in self.intf_ids:
+            self.init_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.ONU_ID,
+                start_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_START_IDX],
+                end_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_END_IDX])
+
+            alloc_id_map = dict()
+            for onu_id in range(platform.MAX_ONUS_PER_PON):
+                alloc_id_map[onu_id] = [platform.mk_alloc_id(pon_id, onu_id, idx)
+                                        for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
+
+            self.init_resource_id_pool(pon_intf_id=pon_id,
+                                       resource_type=PONResourceManager.ALLOC_ID,
+                                       resource_map=alloc_id_map)
+
+            # TODO: I believe that only alloc_id's are constrained to ONU ID.  If so, use
+            #       the generic pool approach
+            # gemport_id_map = dict()
+            # for onu_id in range(platform.MAX_GEM_PORTS_PER_ONU):
+            #     gemport_id_map[onu_id] = [platform.mk_gemport_id(pon_id, onu_id, idx)
+            #                               for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
+            #
+            # self.init_resource_id_pool(pon_intf_id=pon_id,
+            #                            resource_type=PONResourceManager.GEMPORT_ID,
+            #                            resource_map=gemport_id_map)
+            self.init_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.GEMPORT_ID,
+                start_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX],
+                end_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX])
+
+    def clear_device_resource_pool(self):
+        """
+        Clear resource pool of all PON ports.
+        """
+        for pon_id in self.intf_ids:
+            self.clear_resource_id_pool(pon_intf_id=pon_id,
+                                        resource_type=PONResourceManager.ONU_ID)
+
+            self.clear_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.ALLOC_ID,
+            )
+
+            self.clear_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.GEMPORT_ID,
+            )
+
+    def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx=None,
+                              end_idx=None, resource_map=None):
+        """
+        Initialize Resource ID pool for a given Resource Type on a given PON Port
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param start_idx: start index for onu id pool
+        :param end_idx: end index for onu id pool
+        :param resource_map: (dict) Resource map if per-ONU specific
+        :return boolean: True if resource id pool initialized else false
+        """
+        status = False
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            # In case of adapter reboot and reconciliation resource in kv store
+            # checked for its presence if not kv store update happens
+            resource = self._get_resource(path)
+
+            if resource is not None:
+                self._log.info("Resource-already-present-in-store", path=path)
+                status = True
+
+            else:
+                if resource_map is None:
+                    resource = self._format_resource(pon_intf_id, start_idx, end_idx)
+                    self._log.info("Resource-initialized", path=path)
+
+                else:
+                    resource = self._format_map_resource(pon_intf_id, resource_map)
+
+                # Add resource as json in kv store.
+                status = self._kv_store.update_to_kv_store(path, resource)
+
+        except Exception as e:
+            self._log.exception("error-initializing-resource-pool", e=e)
+
+        return status
+
+    def _generate_next_id(self, resource, onu_id=None):
+        """
+        Generate unique id having OFFSET as start index.
+
+        :param resource: resource used to generate ID
+        :return int: generated id
+        """
+        if onu_id is not None:
+            resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+        pos = resource[PONResourceManager.POOL].find('0b0')
+        resource[PONResourceManager.POOL].set(1, pos)
+        return pos[0] + resource[PONResourceManager.START_IDX]
+
+    def _release_id(self, resource, unique_id, onu_id=None):
+        """
+        Release unique id having OFFSET as start index.
+
+        :param resource: resource used to release ID
+        :param unique_id: id need to be released
+        :param onu_id: ONU ID if unique per ONU
+        """
+        if onu_id is not None:
+            resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+        pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
+        resource[PONResourceManager.POOL].set(0, pos)
+
+    def get_resource_id(self, pon_intf_id, resource_type, onu_id=None, num_of_id=1):
+        """
+        Create alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param num_of_id: required number of ids
+        :param onu_id: ONU ID if unique per ONU
+        :return list/int/None: list, int or None if resource type is
+                               alloc_id/gemport_id, onu_id or invalid type
+                               respectively
+        """
+        result = None
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return result
+
+        try:
+            resource = self._get_resource(path, onu_id)
+            if resource is None:
+                raise Exception("get-resource-failed")
+
+            if resource_type == PONResourceManager.ONU_ID:
+                result = self._generate_next_id(resource)
+
+            elif resource_type == PONResourceManager.GEMPORT_ID:
+                result = [self._generate_next_id(resource) for _ in range(num_of_id)]
+
+            elif resource_type == PONResourceManager.ALLOC_ID:
+                result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
+
+            self._log.debug("Get-" + resource_type + "-success", result=result,
+                            path=path)
+            # Update resource in kv store
+            self._update_resource(path, resource, onu_id=onu_id)
+
+        except Exception as e:
+            self._log.exception("Get-" + resource_type + "-id-failed",
+                                path=path, e=e)
+        return result
+
+    def free_resource_id(self, pon_intf_id, resource_type, release_content, onu_id=None):
+        """
+        Release alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param release_content: required number of ids
+        :param onu_id: ONU ID if unique per ONU
+        :return boolean: True if all IDs in given release_content released
+                         else False
+        """
+        status = False
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            resource = self._get_resource(path, onu_id=onu_id)
+            if resource is None:
+                raise Exception("get-resource-failed")
+
+            if resource_type == PONResourceManager.ONU_ID:
+                self._release_id(resource, release_content)
+
+            elif resource_type == PONResourceManager.ALLOC_ID:
+                for content in release_content:
+                    self._release_id(resource, content)
+
+            elif resource_type == PONResourceManager.GEMPORT_ID:
+                for content in release_content:
+                    self._release_id(resource, content, onu_id)
+            else:
+                raise Exception("get-resource-failed")
+
+            self._log.debug("Free-" + resource_type + "-success", path=path)
+
+            # Update resource in kv store
+            status = self._update_resource(path, resource, onu_id=onu_id)
+
+        except Exception as e:
+            self._log.exception("Free-" + resource_type + "-failed",
+                                path=path, e=e)
+        return status
+
+    def _update_resource(self, path, resource, onu_id=None):
+        """
+        Update resource in resource kv store.
+
+        :param path: path to update resource
+        :param resource: resource need to be updated
+        :return boolean: True if resource updated in kv store else False
+        """
+        if 'alloc_id' in path.lower():
+            assert onu_id is not None
+            poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+            poolResource[PONResourceManager.POOL] = \
+                poolResource[PONResourceManager.POOL].bin
+        else:
+            resource[PONResourceManager.POOL] = \
+                resource[PONResourceManager.POOL].bin
+
+        return self._kv_store.update_to_kv_store(path, json.dumps(resource))
+
+    def _get_resource(self, path, onu_id=None):
+        """
+        Get resource from kv store.
+
+        :param path: path to get resource
+        :return: resource if resource present in kv store else None
+        """
+        # get resource from kv store
+        result = self._kv_store.get_from_kv_store(path)
+        if result is None:
+            return result
+
+        self._log.info("dumping-resource", result=result)
+        resource = result
+
+        if resource is not None:
+            # decode resource fetched from backend store to dictionary
+            resource = json.loads(resource)
+
+            if 'alloc_id' in path.lower():
+                assert onu_id is not None
+                poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+                poolResource[PONResourceManager.POOL] = \
+                    BitArray('0b' + poolResource[PONResourceManager.POOL])
+            else:
+                # resource pool in backend store stored as binary string whereas to
+                # access the pool to generate/release IDs it need to be converted
+                # as BitArray
+                resource[PONResourceManager.POOL] = \
+                    BitArray('0b' + resource[PONResourceManager.POOL])
+
+        return resource
+
+    def _format_resource(self, pon_intf_id, start_idx, end_idx):
+        """
+        Format resource as json.
+
+        :param pon_intf_id: OLT PON interface id
+        :param start_idx: start index for id pool
+        :param end_idx: end index for id pool
+        :return dictionary: resource formatted as dictionary
+        """
+        # Format resource as json to be stored in backend store
+        resource = dict()
+        resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+        resource[PONResourceManager.START_IDX] = start_idx
+        resource[PONResourceManager.END_IDX] = end_idx
+
+        # resource pool stored in backend store as binary string
+        resource[PONResourceManager.POOL] = BitArray(end_idx-start_idx).bin
+
+        return json.dumps(resource)
+
+    def _format_map_resource(self, pon_intf_id, resource_map):
+        """
+        Format resource as json.
+        # TODO: Refactor the resource BitArray to be just a list of the resources.
+        #       This is used to store available alloc-id's on a per-onu/pon basis
+        #       which in BitArray string form, is a 768 byte string for just 4 possible
+        #       alloc-IDs.  This equates to 1.57 MB of storage when you take into
+        #       account 128 ONUs and 16 PONs pre-provisioneed
+        :param pon_intf_id: OLT PON interface id
+        :param resource_map: (dict) ONU ID -> Scattered list of IDs
+        :return dictionary: resource formatted as dictionary
+        """
+        # Format resource as json to be stored in backend store
+        resource = dict()
+        resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+
+        onu_dict = dict()
+        for onu_id, resources in resource_map.items():
+            start_idx = min(resources)
+            end_idx = max(resources) + 1
+
+            onu_dict[onu_id] = {
+                PONResourceManager.START_IDX: start_idx,
+                PONResourceManager.END_IDX: end_idx,
+            }
+            # Set non-allowed values as taken
+            resource_map = BitArray(end_idx - start_idx)
+            not_available = {pos for pos in xrange(end_idx-start_idx)
+                             if pos + start_idx not in resources}
+            resource_map.set(True, not_available)
+            onu_dict[onu_id][PONResourceManager.POOL] = resource_map.bin
+
+        resource[AdtranPONResourceManager.ONU_MAP] = onu_dict
+        return json.dumps(resource)
diff --git a/voltha/adapters/adtran_olt/adtranolt_platform.py b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
similarity index 72%
rename from voltha/adapters/adtran_olt/adtranolt_platform.py
rename to voltha/adapters/adtran_olt/resources/adtranolt_platform.py
index 317d448..22bc8c1 100644
--- a/voltha/adapters/adtran_olt/adtranolt_platform.py
+++ b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
@@ -18,16 +18,16 @@
 import voltha.protos.device_pb2 as dev_pb2
 
 #######################################################################
-##
-##  This is a copy of the OpenOLT file of a similar name and is used
-##  when running in non-xPON (OpenOLT/SEBA) mode.  We need to closely
-##  watch for changes in the OpenOLT and eventually work together to
-##  have a better way to do things (and more ONUs than 112)
-##
-##  TODO: These duplicate some methods in the OLT Handler.  Clean up
-##        and use a separate file and include it into OLT Handler object
-##        as something it derives from.
-##
+#
+#  This is a copy of the OpenOLT file of a similar name and is used
+#  when running in non-xPON (OpenOLT/SEBA) mode.  We need to closely
+#  watch for changes in the OpenOLT and eventually work together to
+#  have a better way to do things (and more ONUs than 112)
+#
+#  TODO: These duplicate some methods in the OLT Handler.  Clean up
+#        and use a separate file and include it into OLT Handler object
+#        as something it derives from.
+#
 #######################################################################
 """
 Encoding of identifiers
@@ -55,13 +55,13 @@
     For Adtran, 1024..1919
     Unique per PON interface
 
-     9   7 6          0
-    +-----+------------+
-    | idx |   onu id   | + (Min Alloc ID)
-    +-----+------------+
+     9   8 7        0
+    +-----+----------+
+    | idx |  onu_id  | + (Min Alloc ID)
+    +-----+----------+
 
-    onu id = 7 bits = 128 ONUs per PON
-    Alloc index = 3 bits = 64 GEM ports per ONU
+    onu id = 8 bit
+    Alloc index = 2 bits (max 4 TCONTs/ONU)
 
 Flow id
 
@@ -117,7 +117,16 @@
 MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
 
 MAX_ONUS_PER_PON = 128
-MAX_TCONTS_PER_ONU = 7
+MAX_TCONTS_PER_ONU = 4
+MAX_GEM_PORTS_PER_ONU = 16          # Hardware can handle more
+
+
+class adtran_platform(object):
+    def __init__(self):
+        pass
+
+    def mk_uni_port_num(self, intf_id, onu_id):
+        return intf_id << 11 | onu_id << 4
 
 
 def mk_uni_port_num(intf_id, onu_id):
@@ -130,15 +139,6 @@
     return intf_id << 11 | onu_id << 4
 
 
-# def onu_id_from_uni_port_num(port_num):
-#     """
-#     Extract the ONU ID from a virtual UNI Port Number
-#     :param port_num: (int) virtual UNI / vENET port number on OLT PON
-#     :return: (int) onu ID
-#     """
-#     return (port_num >> 4) & 0x7F
-
-
 def intf_id_from_uni_port_num(port_num):
     """
     Extract the PON device port number from a virtual UNI Port number
@@ -159,7 +159,7 @@
     """
     assert 0 <= onu_id < MAX_ONUS_PER_PON, 'Invalid ONU ID. Expect 0..{}'.format(MAX_ONUS_PER_PON-1)
     assert 0 <= idx <= MAX_TCONTS_PER_ONU, 'Invalid TCONT instance. Expect 0..{}'.format(MAX_TCONTS_PER_ONU)
-    alloc_id = MIN_TCONT_ALLOC_ID + (onu_id << 3) + idx
+    alloc_id = MIN_TCONT_ALLOC_ID + (idx << 8) + onu_id
     return alloc_id
 
 
@@ -178,23 +178,6 @@
     return MIN_GEM_PORT_ID + (onu_id << 4) + idx
 
 
-# def onu_id_from_gemport_id(gemport_id):
-#     """
-#     Determine ONU ID from a GEM PORT ID.    This is only called by the OLT
-#
-#     :param gemport_id: (int)  GEM Port ID
-#     """
-#     return (gemport_id - MIN_GEM_PORT_ID) >> 4
-
-
-# def mk_flow_id(intf_id, onu_id, idx):
-#     return intf_id << 11 | onu_id << 4 | idx
-
-
-# def intf_id_from_pon_id(port_no):
-#     return port_no - 5
-
-
 def intf_id_to_port_no(intf_id, intf_type):
     if intf_type is Port.ETHERNET_NNI:
         # OpenOLT starts at 128.  We start at 1 (one-to-one mapping)
@@ -226,29 +209,6 @@
         raise Exception('Invalid intf_id value')
 
 
-# def intf_id_to_port_type_name(intf_id):
-#     try:
-#         return  port_type_name_by_port_index(intf_id_to_intf_type(intf_id))
-#     except Exception as err:
-#         raise Exception(err)
-
-
-# def port_type_name_by_port_index(port_index):
-#     try:
-#         return dev_pb2._PORT_PORTTYPE.values_by_number[port_index].name
-#     except Exception as err:
-#         raise Exception(err)
-
-
-# def extract_access_from_flow(in_port, out_port):
-#     if is_upstream(in_port, out_port):
-#         return (intf_id_from_uni_port_num(in_port), onu_id_from_uni_port_num(
-#             in_port))
-#     else:
-#         return (intf_id_from_uni_port_num(out_port), onu_id_from_uni_port_num(
-#             out_port))
-
-
 def is_upstream(in_port, out_port):
     # FIXME
     # if out_port in [128, 129, 130, 131, 0xfffd, 0xfffffffd]:
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
deleted file mode 100644
index 1bfe009..0000000
--- a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
+++ /dev/null
@@ -1,567 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import structlog
-from adtran_xpon import AdtranXPON
-from voltha.protos.bbf_fiber_base_pb2 import \
-    ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
-    ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
-from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
-from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
-from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
-from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
-    MulticastGemportsConfigData
-from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
-    MulticastDistributionSetData
-import voltha.adapters.adtran_olt.adtranolt_platform as platform
-
-log = structlog.get_logger()
-
-
-# SEBA
-class AdtranOltXPON(AdtranXPON):
-    """
-    Class to for OLT and XPON operations
-    """
-    def __init__(self, **kwargs):
-        super(AdtranOltXPON, self).__init__(**kwargs)
-
-        # xPON config dictionaries
-        self._channel_groups = {}         # Name -> dict
-        self._channel_partitions = {}     # Name -> dict
-        self._channel_pairs = {}          # Name -> dict
-        self._channel_terminations = {}   # Name -> dict
-
-    @property
-    def channel_terminations(self):
-        return self._channel_terminations
-
-    @property
-    def channel_groups(self):
-        return self._channel_groups
-
-    @property
-    def channel_pairs(self):
-        return self._channel_pairs
-
-    @property
-    def channel_partitions(self):
-        return self._channel_partitions
-
-    # SEBA
-    def get_device_profile_info(self, pon, serial_number):
-        """ TODO: For now, push into legacy xPON structures.  Clean up once we deprecate xPON and remove it"""
-        pon_id = pon.pon_id
-        if pon_id not in self._cached_xpon_pon_info:
-            venets = dict()
-            v_ont_anis = dict()
-            ont_anis = dict()
-            tconts = dict()
-            gem_ports = dict()
-            groups = {
-                "group-{}".format(pon_id):
-                    {
-                        'name'          : "group-{}".format(pon_id),
-                        'system-id'     : '000000',
-                        'enabled'       : True,
-                        'polling-period': 200
-                    }
-            }
-            partitions = {
-                "partition-{}".format(pon_id):
-                    {
-                        'name'                       : "partition-{}".format(pon_id),
-                        'enabled'                    : True,
-                        'fec-downstream'             : True,
-                        'mcast-aes'                  : False,
-                        'channel-group'              : 'group-{}'.format(pon_id),
-                        'authentication-method'      : 'serial-number',
-                        'differential-fiber-distance': 20
-                    }
-            }
-            pairs = {
-                'pon-{}'.format(pon_id):
-                    {
-                        'name'             : 'pon-{}.format(pon_id)',
-                        'enabled'          : True,
-                        'channel-group'    : 'group-{}'.format(pon_id),
-                        'channel-partition': 'partition-{}'.format(pon_id),
-                        'line-rate'        : 'down_10_up_10',
-
-                    }
-            }
-            terminations = {
-                'channel-termination {}'.format(pon_id):
-                {
-                    'name'           : 'channel-termination {}'.format(pon_id),
-                    'enabled'        : True,
-                    'channel-pair'   : 'pon-{}'.format(pon_id),
-                    'xgpon-ponid'    : pon_id,
-                    'xgs-ponid'      : pon_id,
-                    'ber-calc-period': 10,
-                }
-            }
-            # Save this to the cache
-            self._cached_xpon_pon_info[pon_id] = {
-                'channel-terminations': terminations,
-                'channel-pairs'       : pairs,
-                'channel-partitions'  : partitions,
-                'channel-groups'      : groups,
-                'vont-anis'           : v_ont_anis,
-                'ont-anis'            : ont_anis,
-                'v-enets'             : venets,
-                'tconts'              : tconts,
-                'gem-ports'           : gem_ports
-            }
-        # Now update vont_ani and ont_ani information if needed
-        xpon_info = self._cached_xpon_pon_info[pon_id]
-
-        vont_ani_info = next((info for _, info in xpon_info['vont-anis'].items()
-                             if info.get('expected-serial-number') == serial_number), None)
-        # New ONU?
-        activate_onu = vont_ani_info is None
-
-        if activate_onu:
-            onu_id = pon.get_next_onu_id
-            name = 'customer-000000-{}-{}'.format(pon_id, onu_id)
-            vont_ani_info = {
-                'name'                    : name,
-                'enabled'                 : True,
-                'description'             : '',
-                'onu-id'                  : onu_id,
-                'expected-serial-number'  : serial_number,
-                'expected-registration-id': '',                         # TODO: How about this?
-                'channel-partition'       : 'partition-{}'.format(pon_id),
-                'upstream-channel-speed'  : 10000000000,
-                'preferred-channel-pair'  : 'pon-{}'.format(pon_id)
-            }
-            ont_ani_info = {
-                'name':             name,
-                'description':      '',
-                'enabled':          True,
-                'upstream-fec':     True,
-                'mgnt-gemport-aes': False
-            }
-            xpon_info['vont-anis'][name] = vont_ani_info
-            xpon_info['ont-anis'][name] = ont_ani_info
-
-            from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
-            from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
-            from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
-
-            tcont = TcontsConfigData()
-            tcont.name = 'tcont-{}-{}-data'.format(pon_id, onu_id)
-            tcont.alloc_id = platform.mk_alloc_id(pon_id, onu_id)
-
-            traffic_desc = TrafficDescriptorProfileData(name='BestEffort',
-                                                        fixed_bandwidth=0,
-                                                        assured_bandwidth=0,
-                                                        maximum_bandwidth=10000000000,
-                                                        priority=0,
-                                                        weight=0,
-                                                        additional_bw_eligibility_indicator=0)
-            tc = {
-                'name': tcont.name,
-                'alloc-id': tcont.alloc_id,
-                'vont-ani': name,
-                'td-ref': {                    # TODO: This should be the TD Name and the TD installed in xpon cache
-                    'name': traffic_desc.name,
-                    'fixed-bandwidth': traffic_desc.fixed_bandwidth,
-                    'assured-bandwidth': traffic_desc.assured_bandwidth,
-                    'maximum-bandwidth': traffic_desc.maximum_bandwidth,
-                    'priority': traffic_desc.priority,
-                    'weight': traffic_desc.weight,
-                    'additional-bw-eligibility-indicator': 0,
-                    'data': traffic_desc
-                },
-                'data': tcont
-            }
-            from olt_tcont import OltTCont
-            from olt_traffic_descriptor import OltTrafficDescriptor
-            tc['object'] = OltTCont.create(tc, OltTrafficDescriptor.create(tc['td-ref']))
-            xpon_info['tconts'][tcont.name] = tc['object']
-
-            # gem port creation (this initial one is for untagged ONU data support / EAPOL)
-            gem_port, gp = self.create_gem_port(pon_id, onu_id, 0, tcont, untagged=True)
-            gem_ports = [gem_port]
-
-            from olt_gem_port import OltGemPort
-            gp['object'] = OltGemPort.create(self, gp)
-            xpon_info['gem-ports'][gem_port.name] = gp['object']
-
-            # Now create the User-Data GEM-Ports
-            nub_priorities = 1                          # TODO: Pull form tech-profile later
-            for index in range(1, nub_priorities + 1):
-                gem_port, gp = self.create_gem_port(pon_id, onu_id, index, tcont)
-                gem_ports.append(gem_port)
-
-                from olt_gem_port import OltGemPort
-                gp['object'] = OltGemPort.create(self, gp)
-                xpon_info['gem-ports'][gem_port.name] = gp['object']
-
-            self.create_tcont(tcont, traffic_desc)
-            for gem_port in gem_ports:
-                self.xpon_create(gem_port)
-
-        return xpon_info, activate_onu
-
-    def create_gem_port(self, pon_id, onu_id, index, tcont, untagged=False):
-        # gem port creation (this initial one is for untagged ONU data support / EAPOL)
-        gem_port = GemportsConfigData()
-        gem_port.gemport_id = platform.mk_gemport_id(pon_id, onu_id, idx=index)
-        if untagged:
-            gem_port.name = 'gemport-{}-{}-untagged-{}'.format(pon_id, onu_id, gem_port.gemport_id)
-        else:
-            gem_port.name = 'gemport-{}-{}-data-{}'.format(pon_id, onu_id, gem_port.gemport_id)
-
-        gem_port.tcont_ref = tcont.name
-
-        gp = {
-            'name': gem_port.name,
-            'gemport-id': gem_port.gemport_id,
-            'tcont-ref': gem_port.tcont_ref,
-            'encryption': False,
-            'traffic-class': 0,
-            'data': gem_port
-        }
-        return gem_port, gp
-
-    # SEBA
-    def get_xpon_info(self, pon_id, pon_id_type='xgs-ponid'):
-        """
-        Lookup all xPON configuration data for a specific pon-id / channel-termination
-        :param pon_id: (int) PON Identifier
-        :return: (dict) reduced xPON information for the specific PON port
-        """
-        if pon_id not in self._cached_xpon_pon_info:
-            terminations = {key: val for key, val in self._channel_terminations.iteritems()
-                            if val[pon_id_type] == pon_id}
-
-            pair_names = set([term['channel-pair'] for term in terminations.itervalues()])
-            pairs = {key: val for key, val in self.channel_pairs.iteritems()
-                     if key in pair_names}
-
-            partition_names = set([pair['channel-partition'] for pair in pairs.itervalues()])
-            partitions = {key: val for key, val in self.channel_partitions.iteritems()
-                          if key in partition_names}
-
-            v_ont_anis = {key: val for key, val in self.v_ont_anis.iteritems()
-                          if val['preferred-channel-pair'] in pair_names}
-            v_ont_ani_names = set(v_ont_anis.keys())
-
-            ont_anis = {key: val for key, val in self.ont_anis.iteritems()
-                        if key in v_ont_ani_names}
-
-            group_names = set(pair['channel-group'] for pair in pairs.itervalues())
-            groups = {key: val for key, val in self.channel_groups.iteritems()
-                      if key in group_names}
-
-            venets = {key: val for key, val in self.v_enets.iteritems()
-                      if val['vont-ani'] in v_ont_ani_names}
-
-            tconts = {key: val['object'] for key, val in self.tconts.iteritems()
-                      if val['vont-ani'] in v_ont_ani_names and 'object' in val}
-            tcont_names = set(tconts.keys())
-
-            gem_ports = {key: val['object'] for key, val in self.gem_ports.iteritems()
-                         if val['tcont-ref'] in tcont_names and 'object' in val}
-
-            self._cached_xpon_pon_info[pon_id] = {
-                'channel-terminations': terminations,
-                'channel-pairs': pairs,
-                'channel-partitions': partitions,
-                'channel-groups': groups,
-                'vont-anis': v_ont_anis,
-                'ont-anis': ont_anis,
-                'v-enets': venets,
-                'tconts': tconts,
-                'gem-ports': gem_ports
-            }
-        return self._cached_xpon_pon_info[pon_id]
-
-    def get_related_pons(self, item, pon_type='xgs-ponid'):
-        pon_ids = set()
-        ports = []
-        data = item['data']
-
-        if isinstance(data, ChannelgroupConfig):
-            group_name = item['name']
-            pair_names = {val['name'] for val in self.channel_pairs.itervalues()
-                          if val['channel-group'] == group_name}
-            pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                       if val['channel-pair'] in pair_names}
-
-        elif isinstance(data, ChannelpartitionConfig):
-            part_name = item['name']
-            pair_names = {val['name'] for val in self.channel_pairs.itervalues()
-                          if val['channel-partition'] == part_name}
-            pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                       if val['channel-pair'] in pair_names}
-
-        elif isinstance(data, ChannelpairConfig):
-            pair_name = item['name']
-            pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                       if val['channel-pair'] == pair_name}
-
-        elif isinstance(data, ChannelterminationConfig):
-            pon_ids = [item[pon_type]]
-
-        elif isinstance(data, (OntaniConfig, VOntaniConfig)):
-            # ont_ani name == vont_ani name since no link table support yet
-            vont_name = item['name']
-            pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair'] \
-                if vont_name in self.v_ont_anis else None
-
-            if pair_name is not None:
-                pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                           if val['channel-pair'] == pair_name}
-
-        elif isinstance(data, VEnetConfig):
-            venet_name = item['name']
-            vont_name = self.v_enets[venet_name]['vont-ani'] \
-                if venet_name in self.v_enets else None
-
-            if vont_name is not None and vont_name in self.v_ont_anis:
-                pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
-                pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                           if val['channel-pair'] == pair_name}
-
-        elif isinstance(data, TcontsConfigData):
-            tcont_name = item['name']
-            vont_name = self.tconts[tcont_name]['vont-ani'] \
-                if tcont_name in self.tconts else None
-
-            if vont_name is not None and vont_name in self.v_ont_anis:
-                pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
-                pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                           if val['channel-pair'] == pair_name}
-
-        elif isinstance(data, TrafficDescriptorProfileData):
-            td_name = item['name']
-
-        elif isinstance(data, GemportsConfigData):
-            gem_name = item['name']
-            venet_name = self.gem_ports[gem_name]['venet-ref'] \
-                if gem_name in self.gem_ports else None
-
-            vont_name = self.v_enets[venet_name]['vont-ani'] \
-                if venet_name in self.v_enets else None
-
-            if vont_name is not None and vont_name in self.v_ont_anis:
-                pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
-                pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
-                           if val['channel-pair'] == pair_name}
-
-        elif isinstance(data, MulticastGemportsConfigData):
-            raise NotImplementedError('TODO')
-
-        elif isinstance(data, MulticastDistributionSetData):
-            raise NotImplementedError('TODO')
-
-        for pon_id in pon_ids:
-            pon_port = self.southbound_ports.get(pon_id, None)
-            if pon_port is not None:
-                ports.append(pon_port)
-
-        return ports
-
-    def get_related_onus(self, item, pon_type='xgs-ponid'):
-        onus = []
-        pons = self.get_related_pons(item, pon_type=pon_type)
-        data = item['data']
-
-        for pon in pons:
-            if isinstance(data, (OntaniConfig, VOntaniConfig)):
-                # ont_ani name == vont_ani name since no link table support yet
-                vont_name = item['name']
-                for onu in pon.onus:
-                    if onu.xpon_name == vont_name:
-                        onus.append(onu)
-
-            elif isinstance(data, VEnetConfig):
-                venet_name = item['name']
-                vont_name = self.v_enets[venet_name]['vont-ani'] \
-                    if venet_name in self.v_enets else None
-
-                if vont_name is not None and vont_name in self.v_ont_anis:
-                    for onu in pon.onus:
-                        if onu.xpon_name == vont_name:
-                            onus.append(onu)
-
-            elif isinstance(data, TcontsConfigData):
-                tcont_name = item['name']
-                vont_name = self.tconts[tcont_name]['vont-ani'] \
-                    if tcont_name in self.tconts else None
-
-                if vont_name is not None and vont_name in self.v_ont_anis:
-                    for onu in pon.onus:
-                        if onu.xpon_name == vont_name:
-                            onus.append(onu)
-
-            elif isinstance(data, TrafficDescriptorProfileData):
-                pass
-
-            elif isinstance(data, GemportsConfigData):
-                gem_name = item['name']
-                venet_name = self.gem_ports[gem_name]['venet-ref'] \
-                    if gem_name in self.gem_ports else None
-
-                vont_name = self.v_enets[venet_name]['vont-ani'] \
-                    if venet_name in self.v_enets else None
-
-                if vont_name is not None and vont_name in self.v_ont_anis:
-                    for onu in pon.onus:
-                        if onu.xpon_name == vont_name:
-                            onus.append(onu)
-
-            elif isinstance(data, MulticastGemportsConfigData):
-                raise NotImplementedError('TODO')
-
-            elif isinstance(data, MulticastDistributionSetData):
-                raise NotImplementedError('TODO')
-
-        return onus
-
-    def _get_xpon_collection(self, data):
-        collection, create, modify, delete = super(AdtranOltXPON, self)._get_xpon_collection(data)
-
-        if collection is not None:
-            return collection, create, modify, delete
-
-        elif isinstance(data, ChannelgroupConfig):
-            return self.channel_groups, \
-                   self.on_channel_group_create,\
-                   self.on_channel_group_modify, \
-                   self.on_channel_group_delete
-
-        elif isinstance(data, ChannelpartitionConfig):
-            return self.channel_partitions,\
-                   self.on_channel_partition_create,\
-                   self.on_channel_partition_modify,\
-                   self.on_channel_partition_delete
-
-        elif isinstance(data, ChannelpairConfig):
-            return self.channel_pairs, \
-                   self.on_channel_pair_create,\
-                   self.on_channel_pair_modify, \
-                   self.on_channel_pair_delete
-
-        elif isinstance(data, ChannelterminationConfig):
-            return self.channel_terminations,\
-                   self.on_channel_termination_create,\
-                   self.on_channel_termination_modify,\
-                   self.on_channel_termination_delete
-        return None, None, None, None
-
-    def _data_to_dict(self, data, td=None):
-        result = super(AdtranOltXPON, self)._data_to_dict(data, td=td)
-
-        if result is not None:
-            return result
-
-        name = data.name
-        interface = data.interface
-        inst_data = data.data
-
-        if isinstance(data, ChannelgroupConfig):
-            return 'channel-group', {
-                'name': name,
-                'enabled': interface.enabled,
-                'system-id': inst_data.system_id,
-                'polling-period': inst_data.polling_period,
-                'data': data
-            }
-
-        elif isinstance(data, ChannelpartitionConfig):
-            def _auth_method_enum_to_string(value):
-                from voltha.protos.bbf_fiber_types_pb2 import SERIAL_NUMBER, LOID, \
-                    REGISTRATION_ID, OMCI, DOT1X
-                return {
-                    SERIAL_NUMBER: 'serial-number',
-                    LOID: 'loid',
-                    REGISTRATION_ID: 'registration-id',
-                    OMCI: 'omci',
-                    DOT1X: 'dot1x'
-                }.get(value, 'unknown')
-
-            return 'channel-partition', {
-                'name': name,
-                'enabled': interface.enabled,
-                'authentication-method': _auth_method_enum_to_string(inst_data.authentication_method),
-                'channel-group': inst_data.channelgroup_ref,
-                'fec-downstream': inst_data.fec_downstream,
-                'mcast-aes': inst_data.multicast_aes_indicator,
-                'differential-fiber-distance': inst_data.differential_fiber_distance,
-                'data': data
-            }
-
-        elif isinstance(data, ChannelpairConfig):
-            return 'channel-pair', {
-                'name': name,
-                'enabled': interface.enabled,
-                'channel-group': inst_data.channelgroup_ref,
-                'channel-partition': inst_data.channelpartition_ref,
-                'line-rate': inst_data.channelpair_linerate,
-                'data': data
-            }
-
-        elif isinstance(data, ChannelterminationConfig):
-            return 'channel-termination', {
-                'name': name,
-                'enabled': interface.enabled,
-                'xgs-ponid': inst_data.xgs_ponid,
-                'xgpon-ponid': inst_data.xgpon_ponid,
-                'channel-pair': inst_data.channelpair_ref,
-                'ber-calc-period': inst_data.ber_calc_period,
-                'data': data
-            }
-
-        else:
-            raise NotImplementedError('Unknown data type')
-
-    def on_channel_group_create(self, cgroup):
-        return cgroup   # Implement in your OLT, if needed
-
-    def on_channel_group_modify(self, cgroup, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_channel_group_delete(self, cgroup):
-        return None   # Implement in your OLT, if needed
-
-    def on_channel_partition_create(self, cpartition):
-        return cpartition   # Implement in your OLT, if needed
-
-    def on_channel_partition_modify(self, cpartition, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_channel_partition_delete(self, cpartition):
-        return None   # Implement in your OLT, if needed
-
-    def on_channel_pair_create(self, cpair):
-        return cpair   # Implement in your OLT, if needed
-
-    def on_channel_pair_modify(self, cpair, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_channel_pair_delete(self, cpair):
-        return None   # Implement in your OLT, if needed
-
-    def on_channel_termination_create(self, cterm):
-        return cterm   # Implement in your OLT, if needed
-
-    def on_channel_termination_modify(self, cterm, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_channel_termination_delete(self, cterm):
-        return None   # Implement in your OLT, if needed
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
index 61756d2..30e5919 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
@@ -14,20 +14,13 @@
 
 import structlog
 from traffic_descriptor import TrafficDescriptor
-from voltha.protos.bbf_fiber_base_pb2 import \
-    OntaniConfig, VOntaniConfig, VEnetConfig
 from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
 from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
 from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
-from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
-    MulticastGemportsConfigData
-from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
-    MulticastDistributionSetData
 
 log = structlog.get_logger()
 
 
-# SEBA
 class AdtranXPON(object):
     """
     Class to abstract common OLT and ONU xPON operations
@@ -36,25 +29,9 @@
         # xPON config dictionaries
         self._v_ont_anis = {}             # Name -> dict
         self._ont_anis = {}               # Name -> dict
-        self._v_enets = {}                # Name -> dict
         self._tconts = {}                 # Name -> dict
         self._traffic_descriptors = {}    # Name -> dict
         self._gem_ports = {}              # Name -> dict
-        self._mcast_gem_ports = {}        # Name -> dict
-        self._mcast_dist_sets = {}        # Name -> dict
-        self._cached_xpon_pon_info = {}   # PON-id -> dict
-
-    @property
-    def v_ont_anis(self):
-        return self._v_ont_anis
-
-    @property
-    def ont_anis(self):
-        return self._ont_anis
-
-    @property
-    def v_enets(self):
-        return self._v_enets
 
     @property
     def tconts(self):
@@ -71,27 +48,12 @@
     def _get_xpon_collection(self, data):
         """
         Get the collection for the object type and handler routines
-        :param data: xPON object
+        :param data: xPON object          TODO: These three are still needed for the ONU until
+                                                xPON is deprecated as the OLT calls into the ONU
+                                                to start it up and passes these three ProtoBuf
+                                                messages.
         """
-        if isinstance(data, OntaniConfig):
-            return self.ont_anis, \
-                   self.on_ont_ani_create,\
-                   self.on_ont_ani_modify, \
-                   self.on_ont_ani_delete
-
-        elif isinstance(data, VOntaniConfig):
-            return self.v_ont_anis, \
-                   self.on_vont_ani_create,\
-                   self.on_vont_ani_modify, \
-                   self.on_vont_ani_delete
-
-        elif isinstance(data, VEnetConfig):
-            return self.v_enets, \
-                   self.on_venet_create,\
-                   self.on_venet_modify, \
-                   self.on_venet_delete
-
-        elif isinstance(data, TcontsConfigData):
+        if isinstance(data, TcontsConfigData):
             return self.tconts, \
                    self.on_tcont_create,\
                    self.on_tcont_modify, \
@@ -109,64 +71,10 @@
                    self.on_gemport_modify, \
                    self.on_gemport_delete
 
-        elif isinstance(data, MulticastGemportsConfigData):
-            return self.mcast_gem_ports, \
-                   self.on_mcast_gemport_create,\
-                   self.on_mcast_gemport_modify, \
-                   self.on_mcast_gemport_delete
-
-        elif isinstance(data, MulticastDistributionSetData):
-            return self.mcast_dist_sets, \
-                   self.on_mcast_dist_set_create,\
-                   self.on_mcast_dist_set_modify, \
-                   self.on_mcast_dist_set_delete
-
         return None, None, None, None
 
     def _data_to_dict(self, data, td=None):
-        if isinstance(data, OntaniConfig):
-            name = data.name
-            interface = data.interface
-            inst_data = data.data
-
-            return 'ont-ani', {
-                'name': name,
-                'description': interface.description,
-                'enabled': interface.enabled,
-                'upstream-fec': inst_data.upstream_fec_indicator,
-                'mgnt-gemport-aes': inst_data.mgnt_gemport_aes_indicator,
-                'data': data
-            }
-        elif isinstance(data, VOntaniConfig):
-            name = data.name
-            interface = data.interface
-            inst_data = data.data
-
-            return 'vOnt-ani', {
-                'name': name,
-                'description': interface.description,
-                'enabled': interface.enabled,
-                'onu-id': inst_data.onu_id,
-                'expected-serial-number': inst_data.expected_serial_number,
-                'expected-registration-id': inst_data.expected_registration_id,
-                'preferred-channel-pair': inst_data.preferred_chanpair,
-                'channel-partition': inst_data.parent_ref,
-                'upstream-channel-speed': inst_data.upstream_channel_speed,
-                'data': data
-            }
-        elif isinstance(data, VEnetConfig):
-            name = data.name
-            interface = data.interface
-            inst_data = data.data
-
-            return 'vEnet', {
-                'name': name,
-                'description': interface.description,
-                'enabled': interface.enabled,
-                'vont-ani': inst_data.v_ontani_ref,
-                'data': data
-            }
-        elif isinstance(data, TcontsConfigData):
+        if isinstance(data, TcontsConfigData):
             return 'TCONT', {
                 'name': data.name,
                 'alloc-id': data.alloc_id,
@@ -198,26 +106,6 @@
                 'venet-ref': data.itf_ref,                # vENET
                 'data': data
             }
-        elif isinstance(data, MulticastGemportsConfigData):
-            return 'MCAST-GEM', {
-                'name': data.name,
-                'gemport-id': data.gemport_id,
-                'traffic-class': data.traffic_class,
-                'is-broadcast': data.is_broadcast,
-                'channel-pair-ref': data.itf_ref,                 # channel-pair
-                'data': data
-            }
-        elif isinstance(data, MulticastDistributionSetData):
-            data_dict = {
-                'name': data.name,
-                'multicast-gemport-ref': data.multicast_gemport_ref,
-                'multicast-vlans-all': None,
-                'multicast-vlans-list': [],
-                'data': data
-            }
-            assert True is False, 'Need to decode multicast-vlans parameter'
-            return 'MCAST-Distribution', data_dict
-
         return None
 
     def create_tcont(self, tcont_data, td_data):
@@ -307,7 +195,6 @@
 
         log.debug('new-item', item_type=item_type, item=new_item)
         items[name] = new_item
-        self._cached_xpon_pon_info = {}  # Clear cached data
 
         if create_method is not None:
             try:
@@ -322,9 +209,6 @@
 
     def xpon_update(self, data, td=None):
         log.debug('xpon-update', data=data)
-        if not self.xpon_support:
-            raise NotImplementedError("xPON support has been disabled")
-
         name = data.name
         items, create, update_method, delete = self._get_xpon_collection(data)
 
@@ -361,7 +245,6 @@
             return
 
         items[name] = update_item
-        self._cached_xpon_pon_info = {}  # Clear cached data
 
         # Act on any changed items
         if update_method is not None:
@@ -379,87 +262,7 @@
 
     def xpon_remove(self, data):
         log.debug('xpon_remove', data=data)
-        if not self.xpon_support:
-            raise NotImplementedError("xPON support has been disabled")
-
-        name = data.name
-
-        items, create, update, delete_method = self._get_xpon_collection(data)
-        item = items.get(name)
-
-        if item is not None:
-            if delete_method is None:
-                item = None
-            else:
-                try:
-                    item = delete_method(item)
-
-                except Exception as e:
-                    log.exception('xpon-remove', item=items, e=e)
-
-            self._cached_xpon_pon_info = {}  # Clear cached data
-
-            if item is None:
-                del items[name]
-            else:
-                # Update item in collection (still referenced somewhere)
-                items[name] = item
-
-    def on_ont_ani_create(self, ont_ani):
-        """
-        A new ONT-ani is being created. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) new ONT-ani
-        :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
-        """
-        return ont_ani   # Implement in your OLT, if needed
-
-    def on_ont_ani_modify(self, ont_ani, update, diffs):
-        """
-        A existing ONT-ani is being updated. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) existing ONT-ani item dictionary
-        :param update: (dict) updated (changed) ONT-ani
-        :param diffs: (dict) collection of items different in the update
-        :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
-        """
-        return update   # Implement in your OLT, if needed
-
-    def on_ont_ani_delete(self, ont_ani):
-        """
-        A existing ONT-ani is being deleted. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) ONT-ani to delete
-        :return: (dict) None if item should be deleted
-        """
-        return None   # Implement in your OLT, if needed
-
-    def on_vont_ani_create(self, vont_ani):
-        return vont_ani   # Implement in your OLT, if needed
-
-    def on_vont_ani_modify(self, vont_ani, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_vont_ani_delete(self, vont_ani):
-        return None   # Implement in your OLT, if needed
-
-    def on_venet_create(self, venet):
-        return venet   # Implement in your OLT, if needed
-
-    def on_venet_modify(self, venet, update, diffs):
-        return update   # Implement in your OLT, if needed
-
-    def on_venet_delete(self, venet):
-        return None   # Implement in your OLT, if needed
+        raise NotImplementedError("xPON support has been disabled")
 
     def on_tcont_create(self, tcont):
         return tcont   # Implement in your OLT, if needed
@@ -487,21 +290,3 @@
 
     def on_gemport_delete(self, gem_port):
         return None   # Implement in your OLT, if needed
-
-    def on_mcast_gemport_create(self, mcast_gem_port):
-        return mcast_gem_port  # Implement in your OLT, if needed
-
-    def on_mcast_gemport_modify(self, mcast_gem_port, update, diffs):
-        return update  # Implement in your OLT, if needed
-
-    def on_mcast_gemport_delete(self, mcast_gem_port):
-        return None  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_create(self, dist_set):
-        return dist_set  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_modify(self, dist_set, update, diffs):
-        return update  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_delete(self, dist_set):
-        return None  # Implement in your OLT, if needed
diff --git a/voltha/adapters/adtran_olt/xpon/gem_port.py b/voltha/adapters/adtran_olt/xpon/gem_port.py
index 5cc84b2..50149b5 100644
--- a/voltha/adapters/adtran_olt/xpon/gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/gem_port.py
@@ -23,20 +23,16 @@
                  multicast=False,
                  tcont_ref=None,
                  traffic_class=None,
-                 intf_ref=None,
-                 untagged=False,
                  name=None,
                  handler=None):
         self.name = name
         self.gem_id = gem_id
         self._alloc_id = alloc_id
         self.tcont_ref = tcont_ref
-        self.intf_ref = intf_ref
         self.traffic_class = traffic_class
         self._encryption = encryption
         self._omci_transport = omci_transport
         self.multicast = multicast
-        self.untagged = untagged
         self._handler = handler
 
         # TODO: Make this a base class and derive OLT and ONU specific classes from it
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index c50a858..6e37474 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -26,14 +26,12 @@
     """
     Adtran OLT specific implementation
     """
-    def __init__(self, gem_id, alloc_id,
+    def __init__(self, gem_id, alloc_id, pon_id, onu_id,
                  encryption=False,
                  omci_transport=False,
                  multicast=False,
                  tcont_ref=None,
                  traffic_class=None,
-                 intf_ref=None,
-                 untagged=False,
                  name=None,
                  handler=None,
                  is_mock=False,
@@ -44,32 +42,27 @@
                                          multicast=multicast,
                                          tcont_ref=tcont_ref,
                                          traffic_class=traffic_class,
-                                         intf_ref=intf_ref,
-                                         untagged=untagged,
                                          name=name,
                                          handler=handler)
         self._is_mock = is_mock
         self._timestamp = None
+        self.pon_id = pon_id
+        self.onu_id = onu_id
         self.data = pb_data     # Needed for non-xPON mode
 
     @staticmethod
-    def create(handler, gem_port):
-        mcast = gem_port['gemport-id'] in [4095]    # TODO: Perform proper lookup
-        untagged = 'untagged' in gem_port['name'].lower()
-        # TODO: Use next once real BBF mcast available.
-        # port_ref = 'channel-pair-ref 'if mcast else 'venet-ref'
-        port_ref = 'venet-ref 'if mcast else 'venet-ref'
+    def create(handler, gem_port, pon_id, onu_id):
+        mcast = False           # gem_port['gemport-id'] in [4095]    # TODO: Perform proper lookup
 
         return OltGemPort(gem_port['gemport-id'],
                           None,
+                          pon_id, onu_id,
                           encryption=gem_port['encryption'],  # aes_indicator,
                           tcont_ref=gem_port['tcont-ref'],
                           name=gem_port['name'],
                           traffic_class=gem_port['traffic-class'],
-                          intf_ref=gem_port.get(port_ref),
                           handler=handler,
                           multicast=mcast,
-                          untagged=untagged,
                           pb_data=gem_port['data'])
 
     @property
@@ -93,33 +86,30 @@
             self.set_config(self._handler.rest_client, 'encryption', value)
 
     @inlineCallbacks
-    def add_to_hardware(self, session, pon_id, onu_id, operation='POST'):
+    def add_to_hardware(self, session, operation='POST'):
         from ..adtran_olt_handler import AdtranOltHandler
-        log.info('add-gem-port-2-hw', pon_id=pon_id, onu_id=onu_id,
-                 operation=operation, gem_port=self)
-        uri = AdtranOltHandler.GPON_GEM_CONFIG_LIST_URI.format(pon_id, onu_id)
+
+        uri = AdtranOltHandler.GPON_GEM_CONFIG_LIST_URI.format(self.pon_id, self.onu_id)
         data = json.dumps(self.to_dict())
-        name = 'gem-port-create-{}-{}: {}/{}'.format(pon_id, onu_id,
+        name = 'gem-port-create-{}-{}: {}/{}'.format(self.pon_id, self.onu_id,
                                                      self.gem_id,
                                                      self.alloc_id)
         try:
             results = yield session.request(operation, uri, data=data, name=name)
+            returnValue(results)
 
         except Exception as e:
             if operation == 'POST':
-                returnValue(self.add_to_hardware(session, pon_id, onu_id,
-                                                 operation='PATCH'))
+                returnValue(self.add_to_hardware(session, operation='PATCH'))
             else:
                 log.exception('add-2-hw', gem=self, e=e)
                 raise
 
-        returnValue(results)
-
-    def remove_from_hardware(self, session, pon_id, onu_id):
+    def remove_from_hardware(self, session):
         from ..adtran_olt_handler import AdtranOltHandler
 
-        uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(pon_id, onu_id, self.gem_id)
-        name = 'gem-port-delete-{}-{}: {}'.format(pon_id, onu_id, self.gem_id)
+        uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(self.pon_id, self.onu_id, self.gem_id)
+        name = 'gem-port-delete-{}-{}: {}'.format(self.pon_id, self.onu_id, self.gem_id)
         return session.request('DELETE', uri, name=name)
 
     def set_config(self, session, leaf, value):
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index a409026..804c8c2 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -24,37 +24,35 @@
     """
     Adtran OLT specific implementation
     """
-    def __init__(self, alloc_id, traffic_descriptor,
-                 name=None, vont_ani=None, is_mock=False,
-                 pb_data = None):
-        super(OltTCont, self).__init__(alloc_id, traffic_descriptor,
-                                       name=name, vont_ani=vont_ani)
+    def __init__(self, alloc_id, traffic_descriptor, pon_id, onu_id,
+                 name=None, is_mock=False,
+                 pb_data=None):
+        super(OltTCont, self).__init__(alloc_id, traffic_descriptor, name=name)
         self._is_mock = is_mock
+        self.pon_id = pon_id
+        self.onu_id = onu_id
         self.data = pb_data             # Needed for non-xPON mode
 
     @staticmethod
-    def create(tcont, td):
+    def create(tcont, td, pon_id, onu_id):
         from olt_traffic_descriptor import OltTrafficDescriptor
 
         assert isinstance(tcont, dict), 'TCONT should be a dictionary'
         assert isinstance(td, OltTrafficDescriptor), 'Invalid Traffic Descriptor data type'
 
-        return OltTCont(tcont['alloc-id'], td,
+        return OltTCont(tcont['alloc-id'], td, pon_id, onu_id,
                         name=tcont['name'],
-                        vont_ani=tcont['vont-ani'],
                         pb_data=tcont['data'])
 
     @inlineCallbacks
-    def add_to_hardware(self, session, pon_id, onu_id):
+    def add_to_hardware(self, session):
+        from ..adtran_olt_handler import AdtranOltHandler
         if self._is_mock:
             returnValue('mock')
 
-        from ..adtran_olt_handler import AdtranOltHandler
-        log.info('add-tcont-2-hw', pon_id=pon_id, onu_id=onu_id, tcont=self)
-
-        uri = AdtranOltHandler.GPON_TCONT_CONFIG_LIST_URI.format(pon_id, onu_id)
+        uri = AdtranOltHandler.GPON_TCONT_CONFIG_LIST_URI.format(self.pon_id, self.onu_id)
         data = json.dumps({'alloc-id': self.alloc_id})
-        name = 'tcont-create-{}-{}: {}'.format(pon_id, onu_id, self.alloc_id)
+        name = 'tcont-create-{}-{}: {}'.format(self.pon_id, self.onu_id, self.alloc_id)
 
         # For TCONT, only leaf is the key. So only post needed
         try:
@@ -66,7 +64,7 @@
         if self.traffic_descriptor is not None:
             try:
                 results = yield self.traffic_descriptor.add_to_hardware(session,
-                                                                        pon_id, onu_id,
+                                                                        self.pon_id, self.onu_id,
                                                                         self.alloc_id)
             except Exception as e:
                 log.exception('traffic-descriptor', tcont=self,
@@ -75,11 +73,14 @@
 
         returnValue(results)
 
-    def remove_from_hardware(self, session, pon_id, onu_id):
+    def remove_from_hardware(self, session):
         from ..adtran_olt_handler import AdtranOltHandler
 
+        pon_id = self.pon_id
+        onu_id = self.onu_id        # TODO: Cleanup parameters
+
         uri = AdtranOltHandler.GPON_TCONT_CONFIG_URI.format(pon_id, onu_id, self.alloc_id)
-        name = 'tcont-delete-{}-{}: {}'.format(pon_id, onu_id, self.alloc_id)
+        name = 'tcont-delete-{}-{}: {}'.format(self.pon_id, self.onu_id, self.alloc_id)
         return session.request('DELETE', uri, name=name)
 
 
diff --git a/voltha/adapters/adtran_olt/xpon/tcont.py b/voltha/adapters/adtran_olt/xpon/tcont.py
index 5f2d810..fd65adf 100644
--- a/voltha/adapters/adtran_olt/xpon/tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/tcont.py
@@ -17,11 +17,10 @@
     """
     Class to wrap TCont capabilities
     """
-    def __init__(self, alloc_id, traffic_descriptor, name=None, vont_ani=None):
+    def __init__(self, alloc_id, traffic_descriptor, name=None):
         self.alloc_id = alloc_id
         self.traffic_descriptor = traffic_descriptor
         self.name = name
-        self.vont_ani = vont_ani        # (string) reference
 
         # TODO: Make this a base class and derive OLT and ONU specific classes from it
         #       The primary thing difference is the add/remove from hardware methods
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index 53addc2..b573137 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -42,7 +42,7 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='Adtran Inc.',
-                                               version='1.19',
+                                               version='1.20',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN',
                                                accepts_add_remove_flow_updates=False),  # TODO: Support flow-mods
@@ -147,7 +147,7 @@
                 handler.receive_message(msg)
 
     ######################################################################
-    # PON Mgnt APIs
+    # PON Mgnt APIs  (Eventually will be deprecated)
 
     def create_interface(self, device, data):
         """
@@ -259,76 +259,19 @@
             handler.xpon_remove(data)
 
     def create_multicast_gemport(self, device, data):
-        """
-        API to create multicast gemport object in the devices
-        :param device: device id
-        :param data: multicast gemport data object
-        :return: None
-        """
-        self.log.info('create-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_multicast_gemport(self, device, data):
-        """
-        API to update  multicast gemport object in the devices
-        :param device: device id
-        :param data: multicast gemport data object
-        :return: None
-        """
-        self.log.info('update-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_multicast_gemport(self, device, data):
-        """
-        API to delete multicast gemport object in the devices
-        :param device: device id
-        :param data: multicast gemport data object
-        :return: None
-        """
-        self.log.info('remove-mcast-gemport', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def create_multicast_distribution_set(self, device, data):
-        """
-        API to create multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :param data: multicast distribution data object
-        :return: None
-        """
-        self.log.info('create-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_create(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def update_multicast_distribution_set(self, device, data):
-        """
-        API to update multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :param data: multicast distribution data object
-        :return: None
-        """
-        self.log.info('update-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_update(data)
+        raise NotImplemented('xPON has been deprecated')
 
     def remove_multicast_distribution_set(self, device, data):
-        """
-        API to delete multicast distribution rule to specify
-        the multicast VLANs that ride on the multicast gemport
-        :param device: device id
-        :param data: multicast distribution data object
-        :return: None
-        """
-        self.log.info('remove-mcast-distribution-set', data=data)
-        handler = self.devices_handlers.get(device.id)
-        if handler is not None:
-            handler.xpon_remove(data)
+        raise NotImplemented('xPON has been deprecated')
\ No newline at end of file
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 4d3b5db..045c559 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -33,7 +33,7 @@
 from common.utils.indexpool import IndexPool
 from voltha.extensions.omci.omci_me import *
 
-import voltha.adapters.adtran_olt.adtranolt_platform as platform
+import voltha.adapters.adtran_olt.resources.adtranolt_platform as platform
 from voltha.adapters.adtran_onu.flow.flow_entry import FlowEntry
 from omci.adtn_install_flow import AdtnInstallFlowTask
 from omci.adtn_remove_flow import AdtnRemoveFlowTask
@@ -54,7 +54,6 @@
         self.log = structlog.get_logger(device_id=device_id)
         self.logical_device_id = None
         self.proxy_address = None
-        self._event_messages = None
         self._enabled = False
         self.pm_metrics = None
         self.alarms = None
@@ -63,7 +62,7 @@
 
         self._openomci = OMCI(self, adapter.omci_agent)
         self._in_sync_subscription = None
-        # TODO: Need to find a way to sync with OLT. It is part of OpenFlow Port number as well
+
         self._onu_port_number = 0
         self._pon_port_number = 1
         self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
@@ -71,9 +70,7 @@
         self._unis = dict()         # Port # -> UniPort
         self._pon = PonPort.create(self, self._pon_port_number)
         self._heartbeat = HeartBeat.create(self, device_id)
-
         self._deferred = None
-        self._event_deferred = None
 
         # Flow entries
         self._flows = dict()
@@ -84,22 +81,16 @@
         self.mac_bridge_service_profile_entity_id = self.vlan_tcis_1
         self.gal_enet_profile_entity_id = 0     # Was 0x100, but ONU seems to overwrite and use zero
 
-        # Assume no XPON support unless we get an vont-ani/ont-ani/venet create
-        self.xpon_support = False    # xPON no longer available
-
     def __str__(self):
         return "AdtranOnuHandler: {}".format(self.device_id)
 
     def _cancel_deferred(self):
-        d1, self._deferred = self._deferred, None
-        d2, self._event_deferred = self._event_deferred, None
-
-        for d in [d1, d2]:
-            try:
-                if d is not None and not d.called:
-                    d.cancel()
-            except:
-                pass
+        d, self._deferred = self._deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
 
     @property
     def enabled(self):
@@ -171,15 +162,10 @@
 
     def start(self):
         assert self._enabled, 'Start should only be called if enabled'
-
         self._cancel_deferred()
 
-        # Handle received ONU event messages   TODO: Deprecate this....
-        self._event_messages = DeferredQueue()
-        self._event_deferred = reactor.callLater(0, self._handle_onu_events)
-
         # Register for adapter messages
-        self.adapter_agent.register_for_inter_adapter_messages()
+        #self.adapter_agent.register_for_inter_adapter_messages()
 
         # OpenOMCI Startup
         self._subscribe_to_events()
@@ -201,7 +187,7 @@
         self._cancel_deferred()
 
         # Drop registration for adapter messages
-        self.adapter_agent.unregister_for_inter_adapter_messages()
+        #self.adapter_agent.unregister_for_inter_adapter_messages()
 
         # Heartbeat
         self._heartbeat.stop()
@@ -217,11 +203,6 @@
         if self._pon is not None:
             self._pon.enabled = False
 
-        queue, self._event_deferred = self._event_deferred, None
-        if queue is not None:
-            while queue.pending:
-                _ = yield queue.get()
-
     def receive_message(self, msg):
         if self.enabled:
             # TODO: Have OpenOMCI actually receive the messages
@@ -252,23 +233,21 @@
             self.adapter_agent.add_port(device.id, self._pon.get_port())
 
             def xpon_not_found():
-                if not self.xpon_support:
-                    # Start things up for this ONU Handler.
-                    self.enabled = True
+                self.enabled = True
 
             # Schedule xPON 'not found' startup for 10 seconds from now. We will
             # easily get a vONT-ANI create within that time if xPON is being used
             # as this is how we are initially launched and activated in the first
             # place if xPON is in use.
-            reactor.callLater(10, xpon_not_found)
+            reactor.callLater(10, xpon_not_found)   # TODO: Clean up old xPON delay
 
             # reference of uni_port is required when re-enabling the device if
             # it was disabled previously
             # Need to query ONU for number of supported uni ports
             # For now, temporarily set number of ports to 1 - port #2
             parent_device = self.adapter_agent.get_device(device.parent_id)
+
             self.logical_device_id = parent_device.parent_id
-            assert self.logical_device_id, 'Invalid logical device ID'
             self.adapter_agent.update_device(device)
 
             ############################################################################
@@ -290,7 +269,8 @@
             ############################################################################
             # Setup Alarm handler
             self.alarms = AdapterAlarms(self.adapter_agent, device.id, self.logical_device_id)
-
+            self.openomci.onu_omci_device.alarm_synchronizer.set_alarm_params(mgr=self.alarms,
+                                                                              ani_ports=[self._pon])
             ############################################################################
             # Start collecting stats from the device after a brief pause
             reactor.callLater(30, self.pm_metrics.start_collector)
@@ -316,7 +296,7 @@
         self.adapter_agent.register_for_proxied_messages(device.proxy_address)
 
         # Register for adapter messages
-        self.adapter_agent.register_for_inter_adapter_messages()
+        #self.adapter_agent.register_for_inter_adapter_messages()
 
         # Set the connection status to REACHABLE
         device.connect_status = ConnectStatus.REACHABLE
@@ -364,7 +344,6 @@
             # Ignore untagged upstream etherType flows. These are trapped at the
             # OLT and the default flows during initial OMCI service download will
             # send them to the Default VLAN (4091) port for us
-            #
             if is_upstream and flow_entry.vlan_vid is None and flow_entry.etype is not None:
                 continue
 
@@ -372,21 +351,11 @@
             # since that is already installed and any user-data flows for upstream
             # priority tag data will be at a higher level.  Also should ignore the
             # corresponding priority-tagged to priority-tagged flow as well.
-
             if (flow_entry.vlan_vid == 0 and flow_entry.set_vlan_vid == 0) or \
                     (flow_entry.vlan_vid is None and flow_entry.set_vlan_vid == 0
                      and not is_upstream):
                 continue
 
-            # Is it the first user-data flow downstream with a non-zero/non-None VID
-            # to match on?  If so, use as the device VLAN
-            # TODO: When multicast is supported, skip the multicast VLAN here?
-
-            if not is_upstream and flow_entry.vlan_vid:
-                uni = self.uni_port(flow_entry.out_port)
-                if uni is not None:
-                    uni.subscriber_vlan = flow_entry.vlan_vid
-
             # Add it to hardware
             try:
                 def failed(_reason, fid):
@@ -423,13 +392,14 @@
         self.log.info('rebooting', device_id=self.device_id)
         self._cancel_deferred()
 
-        reregister = True
-        try:
-            # Drop registration for adapter messages
-            self.adapter_agent.unregister_for_inter_adapter_messages()
-
-        except KeyError:
-            reregister = False
+        reregister = False
+        # try:
+        #     # Drop registration for adapter messages
+        #     reregister = True
+        #     self.adapter_agent.unregister_for_inter_adapter_messages()
+        #
+        # except KeyError:
+        #     reregister = False
 
         # Update the operational status to ACTIVATING and connect status to
         # UNREACHABLE
@@ -456,7 +426,6 @@
         # Reboot in progress. A reboot may take up to 3 min 30 seconds
         # Go ahead and pause less than that and start to look
         # for it being alive
-
         device.reason = 'reboot in progress'
         self.adapter_agent.update_device(device)
 
@@ -471,19 +440,17 @@
     @inlineCallbacks
     def _finish_reboot(self, previous_oper_status, previous_conn_status,
                        reregister):
-
         # Restart OpenOMCI
         self.omci.enabled = True
 
         device = self.adapter_agent.get_device(self.device_id)
-
         device.oper_status = previous_oper_status
         device.connect_status = previous_conn_status
         device.reason = ''
         self.adapter_agent.update_device(device)
 
-        if reregister:
-            self.adapter_agent.register_for_inter_adapter_messages()
+        # if reregister:
+        #     self.adapter_agent.register_for_inter_adapter_messages()
 
         self.log.info('reboot-complete', device_id=self.device_id)
 
@@ -543,9 +510,7 @@
         self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
 
         # TODO:
-        # 1) Remove all flows from the device
-        # 2) Remove the device from ponsim
-
+        # 1) Remove all flows from the device? or is it done before we are called
         self.log.info('disabled', device_id=device.id)
 
     def reenable(self):
@@ -585,7 +550,7 @@
             # reestablish logical ports for each UNI
             for uni in self.uni_ports:
                 self.adapter_agent.add_port(device.id, uni.get_port())
-                uni.add_logical_port(uni.logical_port_number, subscriber_vlan=uni.subscriber_vlan)
+                uni.add_logical_port(uni.logical_port_number)
 
             device = self.adapter_agent.get_device(device.id)
             device.oper_status = OperStatus.ACTIVE
@@ -603,7 +568,7 @@
     def delete(self):
         self.log.info('deleting', device_id=self.device_id)
 
-        for uni in self._unis.itervalues():
+        for uni in self._unis.values():
             uni.stop()
             uni.delete()
 
@@ -613,208 +578,31 @@
         # OpenOMCI cleanup
         self._openomci.delete()
 
-    def on_ont_ani_create(self, ont_ani):
-        """
-        A new ONT-ani is being created. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) new ONT-ani
-        :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
-        """
-        self.xpon_support = True
-
-        self.log.info('ont-ani-create', ont_ani=ont_ani)
-        self.enabled = ont_ani['enabled']
-
-        return ont_ani   # Implement in your OLT, if needed
-
-    def on_ont_ani_modify(self, ont_ani, update, diffs):
-        """
-        A existing ONT-ani is being updated. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) existing ONT-ani item dictionary
-        :param update: (dict) updated (changed) ONT-ani
-        :param diffs: (dict) collection of items different in the update
-        :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
-        """
-        if not self.xpon_support:
-            return
-
-        valid_keys = ['enabled', 'mgnt-gemport-aes']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("ont_ani leaf '{}' is read-only or write-once".format(invalid_key))
-
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'enabled':
-                self.enabled = update[k]
-
-            elif k == 'mgnt-gemport-aes':
-                self.mgmt_gemport_aes = update[k]
-
-        return update
-
-    def on_ont_ani_delete(self, ont_ani):
-        """
-        A existing ONT-ani is being deleted. You can override this method to
-        perform custom operations as needed. If you override this method, you can add
-        additional items to the item dictionary to track additional implementation
-        key/value pairs.
-
-        :param ont_ani: (dict) ONT-ani to delete
-        :return: (dict) None if item should be deleted
-        """
-        if not self.xpon_support:
-            return
-
-        # TODO: Is this ever called or is the iAdapter 'delete' called first?
-        return None   # Implement in your OLT, if needed
-
-    def on_vont_ani_create(self, vont_ani):
-        self.xpon_support = True
-        self.log.info('vont-ani-create', vont_ani=vont_ani)
-        # TODO: look up PON port and update 'upstream-channel-speed'
-        return vont_ani   # Implement in your OLT, if needed
-
-    def on_vont_ani_modify(self, vont_ani, update, diffs):
-        if not self.xpon_support:
-            return
-
-        valid_keys = ['upstream-channel-speed']  # Modify of these keys supported
-
-        invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-        if invalid_key is not None:
-            raise KeyError("vont_ani leaf '{}' is read-only or write-once".format(invalid_key))
-
-        keys = [k for k in diffs.keys() if k in valid_keys]
-
-        for k in keys:
-            if k == 'upstream-channel-speed':
-                self.upstream_channel_speed = update[k]
-
-        return update
-
-    def on_vont_ani_delete(self, vont_ani):
-        if not self.xpon_support:
-            return
-
-        return self.delete()
-
-    def on_venet_create(self, venet):
-        self.xpon_support = True
-
-        self.log.info('venet-create', venet=venet)
-
-        # TODO: This first set is copied over from BroadCOM ONU. For testing, actual work
-        #       is the last 7 lines.  The 'test' code below assumes we have not registered
-        #       any UNI ports during 'activate' but we want to create them as the vEnet
-        #       information comes in.
-        # onu_device = self.adapter_agent.get_device(self.device_id)
-        # existing_uni_ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
-        #
-        # parent_port_num = None
-        # for uni in existing_uni_ports:
-        #     if uni.label == venet['name']:   #  TODO: was -> data.interface.name:
-        #         parent_port_num = uni.port_no
-        #         break
-        #
-        # # Create both the physical and logical ports for the UNI now
-        # parent_device = self.adapter_agent.get_device(onu_device.parent_id)
-        # logical_device_id = parent_device.parent_id
-        # assert logical_device_id, 'Invalid logical device ID'
-        # # self.add_uni_port(onu_device, logical_device_id, venet['name'], parent_port_num)
-        #
-        # pon_ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
-        # if pon_ports:
-        #     # TODO: Assumed only one PON port and UNI port per ONU.
-        #     pon_port = pon_ports[0]
-        # else:
-        #     self.log.error("No-Pon-port-configured-yet")
-        #     return
-        #
-        # self.adapter_agent.delete_port_reference_from_parent(self.device_id, pon_port)
-        # pon_port.peers[0].device_id = onu_device.parent_id
-        # pon_port.peers[0].port_no = parent_port_num
-        # self.adapter_agent.add_port_reference_to_parent(self.device_id, pon_port)
-
-        #################################################################################
-        # Start of actual work (what actually does something)
-        # TODO: Clean this up.  Use looked up UNI
-
-        # vlan non-zero if created via legacy method (not xPON). Also
-        # Set a random serial number since not xPON based
-
-        ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
-
-        self._add_uni_port(self, venet['name'], ofp_port_no, subscriber_vlan,
-                           untagged_vlan, venet['enabled'])
-        return venet
-
-    # SEBA - Below is used by xPON mode
-    def _add_uni_port(self, port_name, ofp_port_no, subscriber_vlan, untagged_vlan, enable):
-        uni_port = UniPort.create(self, port_name,
-                                  self._onu_port_number,  # TODO: self._next_port_number,
-                                  ofp_port_no,
-                                  subscriber_vlan,
-                                  untagged_vlan)
-
-        device = self.adapter_agent.get_device(self.device_id)
-        self.adapter_agent.add_port(device.id, uni_port.get_port())
-
-        self._unis[uni_port.port_number] = uni_port
-
-        # If the PON has already synchronized, add the logical port now
-        # since we know we have been activated
-        if self._pon is not None and self.openomci.connected:
-            uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
-
-        # TODO: Next is just for debugging to see what this call returns after
-        #       we add a UNI
-        # existing_uni_ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
-
-        uni_port.enabled = enable
-
     def add_uni_ports(self):
         """ Called after in-sync achieved and not in xPON mode"""
+        # TODO: We have to methods adding UNI ports.  Go to one
         # TODO: Should this be moved to the omci.py module for this ONU?
 
         # This is only for working WITHOUT xPON
-        assert not self.xpon_support
         pptp_entities = self.openomci.onu_omci_device.configuration.pptp_entities
-
         device = self.adapter_agent.get_device(self.device_id)
-        subscriber_vlan = device.vlan
-        untagged_vlan = OMCI.DEFAULT_UNTAGGED_VLAN
 
         for entity_id, pptp in pptp_entities.items():
             intf_id = self.proxy_address.channel_id
             onu_id = self.proxy_address.onu_id
-
             uni_no_start = platform.mk_uni_port_num(intf_id, onu_id)
 
             working_port = self._next_port_number
             uni_no = uni_no_start + working_port        # OpenFlow port number
             uni_name = "uni-{}".format(uni_no)
-
             mac_bridge_port_num = working_port + 1
-
             self.log.debug('live-port-number-ready', uni_no=uni_no, uni_name=uni_name)
 
-            uni_port = UniPort.create(self, uni_name, uni_no, uni_name,
-                                      subscriber_vlan, untagged_vlan)
-
+            uni_port = UniPort.create(self, uni_name, uni_no, uni_name)
             uni_port.entity_id = entity_id
             uni_port.enabled = True
             uni_port.mac_bridge_port_num = mac_bridge_port_num
-            uni_port.add_logical_port(uni_port.port_number, subscriber_vlan=subscriber_vlan)
+            uni_port.add_logical_port(uni_port.port_number)
 
             self.log.debug("created-uni-port", uni=uni_port)
 
@@ -828,7 +616,8 @@
             parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
 
             self._unis[uni_port.port_number] = uni_port
-
+            self.openomci.onu_omci_device.alarm_synchronizer.set_alarm_params(onu_id=self.proxy_address.onu_id,
+                                                                              uni_ports=self._unis.values())
             # TODO: this should be in the PonPort class
             pon_port = self._pon.get_port()
             self.adapter_agent.delete_port_reference_from_parent(self.device_id,
@@ -842,46 +631,9 @@
                 self.adapter_agent.add_port_reference_to_parent(self.device_id,
                                                                 pon_port)
             self.adapter_agent.update_device(device)
-
+            uni_port.enabled = True
             # TODO: only one uni/pptp for now. flow bug in openolt
 
-    def on_venet_modify(self, venet, update, diffs):
-        if not self.xpon_support:
-            return
-
-        # Look up the associated UNI port
-        uni_port = self.uni_port(venet['name'])
-
-        if uni_port is not None:
-            valid_keys = ['enabled']  # Modify of these keys supported
-
-            invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
-            if invalid_key is not None:
-                raise KeyError("venet leaf '{}' is read-only or write-once".format(invalid_key))
-
-            keys = [k for k in diffs.keys() if k in valid_keys]
-
-            for k in keys:
-                if k == 'enabled':
-                    uni_port.enabled = update[k]
-
-        return update
-
-    def on_venet_delete(self, venet):
-        if not self.xpon_support:
-            return
-
-        # Look up the associated UNI port
-        uni_port = self.uni_port(venet['name'])
-
-        if uni_port is not None:
-            port_no = uni_port.port_number
-            del self._unis[port_no]
-            uni_port.delete()
-            self._release_port_number(port_no)
-
-        return None
-
     def on_tcont_create(self, tcont):
         from onu_tcont import OnuTCont
 
@@ -1003,80 +755,8 @@
 
         return None
 
-    def on_mcast_gemport_create(self, mcast_gem_port):
-        return mcast_gem_port  # Implement in your OLT, if needed
-
-    def on_mcast_gemport_modify(self, mcast_gem_port, update, diffs):
-        return mcast_gem_port  # Implement in your OLT, if needed
-
-    def on_mcast_gemport_delete(self, mcast_gem_port):
-        return None  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_create(self, dist_set):
-        return dist_set  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_modify(self, dist_set, update, diffs):
-        return update  # Implement in your OLT, if needed
-
-    def on_mcast_dist_set_delete(self, dist_set):
-        return None  # Implement in your OLT, if needed
-
     def rx_inter_adapter_message(self, msg):
-        if self.enabled and self._event_messages is not None:
-            self._event_messages.put(msg)
-
-    @inlineCallbacks
-    def _handle_onu_events(self):
-        #
-        # TODO: From broadcom ONU. This is from the 'receive_inter_adapter_message()'
-        #       method.
-        #
-        event_msg = yield self._event_messages.get()
-
-        if self._event_deferred is None:
-            returnValue('cancelled')
-
-        if event_msg['event'] == 'activation-completed':
-            # if event_msg['event_data']['activation_successful']:
-            #     for uni in self.uni_ports:
-            #         port_no = self.proxy_address.channel_id + uni
-            #         reactor.callLater(1,
-            #                           self.message_exchange,
-            #                           self.proxy_address.onu_id,
-            #                           self.proxy_address.onu_session_id,
-            #                           port_no)
-            #
-            #     device = self.adapter_agent.get_device(self.device_id)
-            #     device.oper_status = OperStatus.ACTIVE
-            #     self.adapter_agent.update_device(device)
-            #
-            # else:
-            #     device = self.adapter_agent.get_device(self.device_id)
-            #     device.oper_status = OperStatus.FAILED
-            #     self.adapter_agent.update_device(device)
-            pass
-
-        elif event_msg['event'] == 'deactivation-completed':
-            # device = self.adapter_agent.get_device(self.device_id)
-            # device.oper_status = OperStatus.DISCOVERED
-            # self.adapter_agent.update_device(device)
-            pass
-
-        elif event_msg['event'] == 'ranging-completed':
-            # if event_msg['event_data']['ranging_successful']:
-            #     device = self.adapter_agent.get_device(self.device_id)
-            #     device.oper_status = OperStatus.ACTIVATING
-            #     self.adapter_agent.update_device(device)
-            #
-            # else:
-            #     device = self.adapter_agent.get_device(self.device_id)
-            #     device.oper_status = OperStatus.FAILED
-            #     self.adapter_agent.update_device(device)
-            pass
-
-        # Handle next event (self._event_deferred is None if we got stopped)
-
-        self._event_deferred = reactor.callLater(0, self.handle_onu_events)
+        raise NotImplemented('Not currently supported')
 
     def _subscribe_to_events(self):
         from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
@@ -1097,7 +777,6 @@
 
     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
@@ -1105,7 +784,7 @@
                 if msg[IN_SYNC_KEY]:
                     # Do not proceed if we have not got our vENET information yet.
 
-                    if len(self.uni_ports) > 0 or not self.xpon_support:
+                    if len(self.uni_ports) == 0:
                         # Drop subscription....
                         insync, self._in_sync_subscription = self._in_sync_subscription, None
 
@@ -1117,14 +796,7 @@
                         # 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.
-                        if not self.xpon_support:
-                            self.add_uni_ports()
-                        else:
-                            for uni in self.uni_ports:
-                                uni.add_logical_port(None, None)
-                    else:
-                        # SEBA - drop this one once xPON deprecated
-                        self._deferred = reactor.callLater(5, self.in_sync_handler, _topic, msg)
+                        self.add_uni_ports()
 
             except Exception as e:
                 self.log.exception('in-sync', e=e)
diff --git a/voltha/adapters/adtran_onu/flow/flow_entry.py b/voltha/adapters/adtran_onu/flow/flow_entry.py
index 7a90daf..0c37d1d 100644
--- a/voltha/adapters/adtran_onu/flow/flow_entry.py
+++ b/voltha/adapters/adtran_onu/flow/flow_entry.py
@@ -49,6 +49,7 @@
         (FlowDirection.UNI, FlowDirection.ANI): FlowDirection.UPSTREAM,
         (FlowDirection.ANI, FlowDirection.UNI): FlowDirection.DOWNSTREAM
     }
+
     # Well known EtherTypes
     class EtherType(IntEnum):
         EAPOL = 0x888E
@@ -63,7 +64,6 @@
         UDP = 17
 
     def __init__(self, flow, handler):
-        self._flow = flow               # TODO: Drop this reference once debugging done
         self._handler = handler
         self.flow_id = flow.id
         self._flow_direction = FlowEntry.FlowDirection.OTHER
diff --git a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
index 5f155f1..4b920de 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
@@ -138,9 +138,6 @@
         """
         self.log.info('perform-flow-install', vlan_vid=self._flow_entry.vlan_vid)
 
-        if self._handler.xpon_support:
-            self.deferred.callback('flow-install-nop')  # xPON mode does not need this
-
         def resources_available():
             # TODO: Rework for non-xpon mode
             return (len(self._handler.uni_ports) > 0 and
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
index b2d3db4..e0d6b82 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
@@ -21,7 +21,7 @@
     OpenOMCI MIB Synchronizer state machine for Adtran ONUs
     """
     ADTN_RESYNC_DELAY = 120     # Periodically force a resync (lower for debugging)
-    ADTN_AUDIT_DELAY = 0        # Disabled until after BBWF
+    ADTN_AUDIT_DELAY = 60
 
     def __init__(self, agent, device_id, mib_sync_tasks, db, advertise_events=False):
         """
diff --git a/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py b/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
index 214a5aa..53db01a 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
@@ -148,11 +148,7 @@
         """
         self.log.info('perform-flow-removall')
 
-        if self._handler.xpon_support:
-            self.deferred.callback('flow-removal-nop')  # xPON mode does not need this
-
         def resources_available():
-            # TODO: Rework for non-xpon mode
             return (len(self._handler.uni_ports) > 0 and
                     len(self._pon.tconts) and
                     len(self._pon.gem_ports))
diff --git a/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
index e4839e5..e2e6b54 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
@@ -78,13 +78,9 @@
         self._input_tpid = AdtnServiceDownloadTask.default_tpid
         self._output_tpid = AdtnServiceDownloadTask.default_tpid
 
-        if self._handler.xpon_support:
-            device = self._handler.adapter_agent.get_device(self.device_id)
-            self._vid = device.vlan
-        else:
-            # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
-            # self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
-            self._vid = OMCI.DEFAULT_UNTAGGED_VLAN
+        # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+        # self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
+        self._vid = OMCI.DEFAULT_UNTAGGED_VLAN
 
         # Entity IDs. IDs with values can probably be most anything for most ONUs,
         #             IDs set to None are discovered/set
@@ -164,16 +160,9 @@
         device = self._handler.adapter_agent.get_device(self.device_id)
 
         def resources_available():
-            # TODO: Rework for non-xpon mode
-            if self._handler.xpon_support:
-                return (device.vlan > 0 and
-                        len(self._handler.uni_ports) > 0 and
-                        len(self._pon.tconts) and
-                        len(self._pon.gem_ports))
-            else:
-                return (len(self._handler.uni_ports) > 0 and
-                        len(self._pon.tconts) and
-                        len(self._pon.gem_ports))
+            return (len(self._handler.uni_ports) > 0 and
+                    len(self._pon.tconts) and
+                    len(self._pon.gem_ports))
 
         if self._handler.enabled and resources_available():
             device.reason = 'Performing Service OMCI Download'
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index 5966d2c..a72a991 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -191,6 +191,8 @@
             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
 
+            # For the UNI ports below, they are created after the MIB Sync event occurs
+            # and the onu handler adds the ONU
             assert pon_ports == 1, 'Expected one PON/ANI port, got {}'.format(pon_ports)
             assert uni_ports == len(self._handler.uni_ports), \
                 'Expected {} UNI port(s), got {}'.format(len(self._handler.uni_ports), uni_ports)
@@ -201,16 +203,6 @@
             # Save entity_id of PON ports
             self._handler.pon_ports[0].entity_id = ani_g.keys()[0]
 
-            # Save entity_id for UNI ports (this is only for xPON mode code).  For the
-            # non-xPON mode, we save the entity IDs during the mib-in-sync handler when
-            # we create the UNI ports.
-
-            if self._handler.xpon_support:
-                uni_entity_ids = uni_g.keys()
-                uni_entity_ids.sort()
-                for uni in self._handler.uni_ports:
-                    uni.entity_id = uni_entity_ids.pop(0)
-
             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
@@ -279,6 +271,10 @@
                                         OmciCCRxEvents.Connectivity)
         self._connectivity_subscription = bus.subscribe(topic, self.onu_is_reachable)
 
+        # TODO: Watch for any MIB RESET events or detection of an ONU reboot.
+        #       If it occurs, set _service_downloaded and _mib_download to false
+        #       and make sure that we get 'new' capabilities
+
     def _unsubscribe_to_events(self):
         insync, self._in_sync_subscription = self._in_sync_subscription, None
         connect, self._connectivity_subscription = self._connectivity_subscription, None
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index a8fe3ed..d2c3dad 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -30,8 +30,6 @@
                  multicast=False,
                  tcont_ref=None,
                  traffic_class=None,
-                 intf_ref=None,
-                 untagged=False,
                  name=None,
                  handler=None):
         super(OnuGemPort, self).__init__(gem_id, alloc_id,
@@ -40,8 +38,6 @@
                                          multicast=multicast,
                                          tcont_ref=tcont_ref,
                                          traffic_class=traffic_class,
-                                         intf_ref=intf_ref,
-                                         untagged=untagged,
                                          name=name,
                                          handler=handler)
         self._entity_id = entity_id
@@ -77,8 +73,7 @@
                           tcont_ref=gem_port['tcont-ref'],
                           name=gem_port['name'],
                           traffic_class=gem_port['traffic-class'],
-                          handler=handler,
-                          untagged='untagged' in gem_port['name'].lower())
+                          handler=handler)
 
     @inlineCallbacks
     def add_to_hardware(self, omci,
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index c94a339..f89829f 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -20,15 +20,15 @@
 from voltha.extensions.omci.omci_me import TcontFrame
 from voltha.extensions.omci.omci_defs import ReasonCodes
 
+
 class OnuTCont(TCont):
     """
     Adtran ONU specific implementation
     """
     free_tcont_alloc_id = 0xFFFF
 
-    def __init__(self, handler, alloc_id, traffic_descriptor, name=None, vont_ani=None):
-        super(OnuTCont, self).__init__(alloc_id, traffic_descriptor,
-                                       name=name, vont_ani=vont_ani)
+    def __init__(self, handler, alloc_id, traffic_descriptor, name=None):
+        super(OnuTCont, self).__init__(alloc_id, traffic_descriptor, name=name)
         self._handler = handler
         self._entity_id = None
         self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
@@ -41,12 +41,7 @@
     def create(handler, tcont, td):
         assert isinstance(tcont, dict), 'TCONT should be a dictionary'
         assert isinstance(td, TrafficDescriptor), 'Invalid Traffic Descriptor data type'
-
-        return OnuTCont(handler,
-                        tcont['alloc-id'],
-                        td,
-                        name=tcont['name'],
-                        vont_ani=tcont['vont-ani'])
+        return OnuTCont(handler, tcont['alloc-id'], td, name=tcont['name'])
 
     @inlineCallbacks
     def add_to_hardware(self, omci, tcont_entity_id):
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index dc7baa5..5451e8a 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -20,15 +20,12 @@
 from voltha.protos.logical_device_pb2 import LogicalPort
 from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER
 from voltha.protos.openflow_13_pb2 import ofp_port
-from omci.omci import OMCI
 
 
 class UniPort(object):
     """Wraps southbound-port(s) support for ONU"""
-    def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
-                 untagged_vlan=None):
-        self.log = structlog.get_logger(device_id=handler.device_id,
-                                        port_no=port_no)
+    def __init__(self, handler, name, port_no, ofp_port_no):
+        self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
         self._enabled = False
         self._handler = handler
         self._name = name
@@ -36,14 +33,11 @@
         self._port_number = port_no
         self._ofp_port_no = ofp_port_no         # Set at by creator (vENET create)
         self._logical_port_number = None        # Set at time of logical port creation
-        self._subscriber_vlan = subscriber_vlan
-        self._untagged_vlan = untagged_vlan
         self._entity_id = None                  # TODO: Use port number from UNI-G entity ID
         self._mac_bridge_port_num = 0
 
         self._admin_state = AdminState.ENABLED
         self._oper_status = OperStatus.ACTIVE
-
         # TODO Add state, stats, alarm reference, ...
         pass
 
@@ -51,8 +45,8 @@
         return "UniPort: {}:{}".format(self.name, self.port_number)
 
     @staticmethod
-    def create(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan):
-        port = UniPort(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan)
+    def create(handler, name, port_no, ofp_port_no):
+        port = UniPort(handler, name, port_no, ofp_port_no)
         return port
 
     def _start(self):
@@ -60,7 +54,6 @@
 
         self._admin_state = AdminState.ENABLED
         self._oper_status = OperStatus.ACTIVE
-
         self._update_adapter_agent()
         # TODO: start h/w sync
         # TODO: Enable the actual physical port?
@@ -71,7 +64,6 @@
 
         self._admin_state = AdminState.DISABLED
         self._oper_status = OperStatus.UNKNOWN
-
         self._update_adapter_agent()
         # TODO: Disable/power-down the actual physical port?
         pass
@@ -79,7 +71,6 @@
     def delete(self):
         self.enabled = False
         self._handler = None
-        # TODO: anything else
 
     def _cancel_deferred(self):
         pass
@@ -137,20 +128,6 @@
         self._entity_id = value
 
     @property
-    def subscriber_vlan(self):
-        """
-        Subscriber vlan assigned to this UNI
-        :return: (int) subscriber vlan
-        """
-        return self._subscriber_vlan
-
-    @subscriber_vlan.setter
-    def subscriber_vlan(self, value):
-        if value:
-            if self._subscriber_vlan is None or self._subscriber_vlan != value:
-                self._subscriber_vlan = value
-
-    @property
     def logical_port_number(self):
         """
         Logical device port number (used as OpenFlow port for UNI)
@@ -173,43 +150,9 @@
             # adapter_agent add_port also does an update of existing port
             self._handler.adapter_agent.add_port(self._handler.device_id,
                                                  self.get_port())
-
         except Exception as e:
             self.log.exception('update-port', e=e)
 
-    @staticmethod
-    def decode_venet(venet_info):
-        try:
-            # Allow spaces or dashes as separator, select last as the
-            # port number.  UNI-1,  UNI 1, and UNI 3-2-1 are the same
-            port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
-            subscriber_vlan = port_no
-            untagged_vlan = OMCI.DEFAULT_UNTAGGED_VLAN
-            try:
-                # Subscriber VLAN and Untagged vlan are comma separated
-                parts = venet_info['description'].split(',')
-                sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
-                untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
-                try:
-                    if sub_part is not None:
-                        subscriber_vlan = int(sub_part.split(':')[-1:][0])
-                except Exception as e:
-                    pass
-                try:
-                    if untagged_part is not None:
-                        untagged_vlan = int(untagged_part.split(':')[-1:][0])
-                except Exception as e:
-                    pass
-            except Exception as e:
-                pass
-
-            return port_no, subscriber_vlan, untagged_vlan
-
-        except ValueError:
-            pass
-        except KeyError:
-            pass
-
     def get_port(self):
         """
         Get the VOLTHA PORT object for this port
@@ -226,7 +169,7 @@
     def port_id_name(self):
         return 'uni-{}'.format(self._logical_port_number)
 
-    def add_logical_port(self, openflow_port_no, subscriber_vlan=None,
+    def add_logical_port(self, openflow_port_no,
                          capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
                          speed=OFPPF_10GB_FD):
 
@@ -245,18 +188,11 @@
 
         # Use vENET provisioned values if none supplied
         port_no = openflow_port_no or self._ofp_port_no
-        vlan = subscriber_vlan or self._subscriber_vlan
 
         if self._logical_port_number is None and port_no is not None:
             self._logical_port_number = port_no
-            self._subscriber_vlan = vlan
-
             device = self._handler.adapter_agent.get_device(self._handler.device_id)
 
-            if vlan is not None and device.vlan != vlan:
-                device.vlan = vlan
-                self._handler.adapter_agent.update_device(device)
-
             openflow_port = ofp_port(
                 port_no=port_no,
                 hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
@@ -279,4 +215,3 @@
                                                              ofp_port=openflow_port,
                                                              device_id=device.id,
                                                              device_port_no=self._port_number))
-            # TODO: Should we use the UNI object 'name' as the id for OpenFlow?