VOL-1234: Initial support for running without xPON CLI/NBI

Change-Id: I0d632a5d495f76ab4dbcbf473297328541a04bab
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index f6796ed..d506fa2 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -40,8 +40,8 @@
 
 DEFAULT_MULTICAST_VLAN = 4000
 DEFAULT_UTILITY_VLAN = 4094
-DEFAULT_UNTAGGED_VLAN = DEFAULT_UTILITY_VLAN    # if RG does not send priority tagged frames
-#DEFAULT_UNTAGGED_VLAN = 4092
+BROADCOM_UNTAGGED_VLAN = 4091                     # SEBA - For BBWF demo (BroadCom Default VLAN)
+DEFAULT_UNTAGGED_VLAN = BROADCOM_UNTAGGED_VLAN    # if RG does not send priority tagged frames
 
 _DEFAULT_RESTCONF_USERNAME = ""
 _DEFAULT_RESTCONF_PASSWORD = ""
@@ -51,7 +51,9 @@
 _DEFAULT_NETCONF_PASSWORD = ""
 _DEFAULT_NETCONF_PORT = 830
 
-_STARTUP_RETRY_TIMEOUT = 5     # 5 seconds delay after activate failed before we
+_STARTUP_RETRY_TIMEOUT = 5       # 5 seconds delay after activate failed before we
+_DEFAULT_XPON_SUPPORTED = True   # LOOK for the keywords 'xpon_support', SEBA
+                                 # for areas to clean up once xPON is deprecated
 
 
 class AdtranDeviceHandler(object):
@@ -136,6 +138,8 @@
         self.netconf_password = _DEFAULT_NETCONF_PASSWORD
         self._netconf_client = None
 
+        # TODO: Decrement xPON once Technology Profiles completed
+        self.xpon_support = _DEFAULT_XPON_SUPPORTED
         self.max_nni_ports = 1  # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
                                 # and logical_device_agent.py
         # OMCI ZMQ Channel
@@ -273,8 +277,9 @@
         parser.add_argument('--utility_vlan', '-B', action='store',
                             default='{}'.format(DEFAULT_UTILITY_VLAN),
                             help='VLAN for Untagged Frames from ONUs')
-        parser.add_argument('--deprecated_option', '-X', action='store',
-                            default='not-used', help='Deprecated command')
+        parser.add_argument('--xpon_enable', '-X', action='store_true',
+                            default=not _DEFAULT_XPON_SUPPORTED,
+                            help='enable xPON (BBF WT-385) provisioning support')
         try:
             args = parser.parse_args(shlex.split(device.extra_args))
 
@@ -291,6 +296,10 @@
 
             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
 
             if not self.rest_username:
                 self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
@@ -980,7 +989,7 @@
         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)
+            gpon_info = self.get_xpon_info(port.pon_id)         # SEBA
             if gpon_info is not None and \
                 gpon_info['channel-terminations'] is not None and \
                 len(gpon_info['channel-terminations']) > 0:
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 84a39c5..19e8511 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='0.20',
+            version='0.21',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 1403e1a..bf4c654 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -18,8 +18,10 @@
 
 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 download import Download
 from xpon.adtran_olt_xpon import AdtranOltXPON
 from codec.olt_state import OltState
@@ -318,11 +320,11 @@
         self.startup = self.rest_client.request('GET', self.GPON_PON_CONFIG_LIST_URI,
                                                 'pon-config')
         try:
-            results = yield self.startup
-
             from codec.ietf_interfaces import IetfInterfacesState
             from nni_port import MockNniPort
 
+            results = yield self.startup
+
             ietf_interfaces = IetfInterfacesState(self.netconf_client)
 
             if self.is_virtual_olt:
@@ -339,7 +341,8 @@
                 pon_id = data['pon-id']
                 port = ports[pon_id + 1]
                 port['pon-id'] = pon_id
-                port['admin_state'] = AdminState.ENABLED if data.get('enabled', False)\
+                port['admin_state'] = AdminState.ENABLED \
+                    if data.get('enabled', not self.xpon_support)\
                     else AdminState.DISABLED
 
         except Exception as e:
@@ -983,18 +986,24 @@
         return pon_id + 1 + 4   # Skip over uninitialized ports
 
     def _port_number_to_pon_id(self, port):
-        # return port - 1 - self.num_northbound_ports
+        if not self.xpon_support and self.is_uni_port(port):
+            # Convert to OLT device port
+            port = platform.intf_id_from_uni_port_num(port)
+
         return port - 1 - 4  # Skip over uninitialized ports
 
     def is_pon_port(self, port):
         return self._port_number_to_pon_id(port) in self.southbound_ports
 
     def is_uni_port(self, port):
-        return port >= self._onu_offset(0)  # TODO: Really need to rework this one...
+        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
+        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
@@ -1003,7 +1012,6 @@
             # 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
@@ -1013,14 +1021,26 @@
             return None, None, None
 
         if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
-            # Upstream ACLs will have VID=Logical_port until VOL-460 is addressed
-            # but User data flows have the correct VID / C-Tag.
+            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:
-                onu = next((onu for onu in pon_port.onus if
-                            onu.logical_port == vid), None)
-            else:
+                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):
@@ -1032,6 +1052,7 @@
             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
 
@@ -1052,7 +1073,15 @@
             return self.get_southbound_port(port).name
 
         if self.is_uni_port(port):
-            return self.northbound_ports[port].name
+            if self.xpon_support:
+                return self.northbound_ports[port].name
+            else:
+                # try:   TODO: Remove this crap
+                #     import voltha.protos.device_pb2 as dev_pb2
+                #     return dev_pb2._PORT_PORTTYPE.values_by_number[port].name
+                # except Exception as err:
+                #     pass
+                return 'uni-{}'.format(port)
 
         if self.is_logical_port(port):
             raise NotImplemented('TODO: Logical ports not yet supported')
@@ -1252,6 +1281,80 @@
         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:
+            return onu_device
+
+        try:
+            from voltha.protos.voltha_pb2 import Device
+
+            # NOTE - channel_id of onu is set to intf_id
+            proxy_address = Device.ProxyAddress(device_id=self.device_id,
+                                                channel_id=intf_id, onu_id=onu_id,
+                                                onu_session_id=onu_id)
+
+            self.log.debug("added-onu", port_no=intf_id,
+                           onu_id=onu_id, serial_number=serial_number,
+                           proxy_address=proxy_address)
+
+            self.adapter_agent.add_onu_device(
+                parent_device_id=self.device_id,
+                parent_port_no=intf_id,
+                vendor_id=serial_number[:4],
+                proxy_address=proxy_address,
+                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
+
+        except Exception as e:
+            self.log.exception('onu-activation-failed', e=e)
+            return None
+
+    def _seba_xpon_create(self, onu_device, intf_id, onu_id, tconts, gem_ports):
+        # For SEBA, send over tcont and gem_port information until tech profiles are ready
+        # tcont creation (onu)
+        from voltha.registry import registry
+        self.log.info('inject-tcont-gem-data-onu-handler', intf_id=intf_id,
+                      onu_id=onu_id, tconts=tconts, gem_ports=gem_ports)
+
+        onu_adapter_agent = registry('adapter_loader').get_agent(onu_device.adapter)
+        if onu_adapter_agent is None:
+            self.log.error('onu_adapter_agent-could-not-be-retrieved',
+                           onu_device=onu_device)
+
+        # In the OpenOLT version, this is where they signal the BroadCom ONU
+        # to enable OMCI. It is a call to the create_interface IAdapter method
+        #
+        # For the Adtran ONU, we are already running OpenOMCI.  On the Adtran ONU, a call
+        # to create_interface vectors into the xpon_create call which should not
+        # recognize the type and raise a value assertion
+        try:
+            onu_adapter_agent.create_interface(onu_device,
+                                               OnuIndication(intf_id, onu_id))
+        except:
+            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',
@@ -1605,3 +1708,12 @@
         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):
+        self.name = 'Dummy ONU Indication'
+        self.intf_id = intf_id
+        self.onu_id = onu_id
+        self.oper_state = 'up'
+        self.admin_state = 'up'
diff --git a/voltha/adapters/adtran_olt/adtranolt_platform.py b/voltha/adapters/adtran_olt/adtranolt_platform.py
new file mode 100644
index 0000000..317d448
--- /dev/null
+++ b/voltha/adapters/adtran_olt/adtranolt_platform.py
@@ -0,0 +1,260 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from voltha.protos.device_pb2 import Port
+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.
+##
+#######################################################################
+"""
+Encoding of identifiers
+=======================
+
+GEM port ID
+
+    GEM port id is unique per PON port and ranges 
+
+     9            3 2    0
+    +--------------+------+
+    |     onu id   | GEM  |
+    |              | idx  |
+    +--------------+------+
+
+    GEM port id range (0..1023) is reserved, by standard
+    Minimum GEM Port on Adtran OLT is 2176 (0x880)
+    onu id = 7 bits = 128 ONUs per PON
+    GEM index = 3 bits = 8 GEM ports per ONU
+
+Alloc ID
+
+    Uniquely identifies a T-CONT
+    Ranges from 1024..16383 per ITU Standard
+    For Adtran, 1024..1919
+    Unique per PON interface
+
+     9   7 6          0
+    +-----+------------+
+    | idx |   onu id   | + (Min Alloc ID)
+    +-----+------------+
+
+    onu id = 7 bits = 128 ONUs per PON
+    Alloc index = 3 bits = 64 GEM ports per ONU
+
+Flow id
+
+    Identifies a flow within a single OLT
+    Flow Id is unique per OLT
+    Multiple GEM ports can map to same flow id
+
+     13    11              4      0
+    +--------+--------------+------+
+    | pon id |    onu id    | Flow |
+    |        |              | idx  |
+    +--------+--------------+------+
+
+    14 bits = 16384 flows (per OLT).
+
+    pon id = 4 bits = 16 PON ports
+    onu id = 7 bits = 128 ONUss per PON port
+    Flow index = 3 bits = 4 bi-directional flows per ONU
+                        = 8 uni-directional flows per ONU
+
+
+Logical (OF) UNI port number
+
+    OpenFlow port number corresponding to PON UNI
+
+     15       11              4      0
+    +--+--------+--------------+------+
+    |0 | pon id |    onu id    |   0  |
+    +--+--------+--------------+------+
+
+    pon id = 4 bits = 16 PON ports
+    onu id = 7 bits = 128 ONUs per PON port
+
+
+PON OLT (OF) port number
+
+    OpenFlow port number corresponding to PON OLT ports
+
+     31    28                                 0
+    +--------+------------------------~~~------+
+    |  0x2   |          pon intf id            |
+    +--------+------------------------~~~------+
+
+"""
+
+MIN_TCONT_ALLOC_ID = 1024                   # 1024..16383
+MAX_TCONT_ALLOC_ID = 16383
+
+# MIN_GEM_PORT_ID = 1023                    # 1023..65534
+# MAX_GEM_PORT_ID = 65534
+
+MIN_GEM_PORT_ID = 2176                      # 2176..4222
+MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
+
+MAX_ONUS_PER_PON = 128
+MAX_TCONTS_PER_ONU = 7
+
+
+def mk_uni_port_num(intf_id, onu_id):
+    """
+    Create a unique virtual UNI port number based up on PON and ONU ID
+    :param intf_id:
+    :param onu_id: (int) ONU ID (0..max)
+    :return: (int) UNI Port number
+    """
+    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
+
+    :param port_num: (int) virtual UNI / vENET port number on OLT PON
+    :return: (int) PON Port number (note, this is not the PON ID)
+    """
+    return (port_num >> 11) & 0xF
+
+
+def mk_alloc_id(_, onu_id, idx=0):
+    """
+    Allocate a TCONT Alloc-ID.    This is only called by the OLT
+
+    :param _: (int)         PON ID (not used)
+    :param onu_id: (int)    ONU ID (0..MAX_ONUS_PER_PON-1)
+    :param idx: (int)       TCONT Index (0..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
+    return alloc_id
+
+
+def mk_gemport_id(_, onu_id, idx=0):
+    """
+    Allocate a GEM-PORT ID.    This is only called by the OLT
+
+    A 4-bit mask was used since we need a gvid for untagged-EAPOL
+    traffic and then up to 8 more for user-user data priority
+    levels.
+
+    :param _: (int)         PON ID (0..n) - not used
+    :param onu_id: (int)    ONU ID (0..MAX_ONUS_PER_PON-1)
+    :param idx: (int)       GEM_PORT Index (0..15)
+    """
+    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)
+        return intf_id
+    elif intf_type is Port.PON_OLT:
+        # OpenOLT sets bit 29 + intf_id. We start at 5 for now for PON 0
+        # return 0x2 << 28 | intf_id
+        return intf_id + 5              # see _pon_id_to_port_number
+    else:
+        raise Exception('Invalid port type')
+
+
+def intf_id_from_nni_port_num(port_num):
+    # OpenOLT starts at 128.  We start at 1 (one-to-one mapping)
+    # return port_num - 128
+    return port_num
+
+
+def intf_id_to_intf_type(intf_id):
+    # if (2 << 28 ^ intf_id) < 16:
+    #     return Port.PON_OLT
+    # elif  128 <= intf_id <= 132:
+    #     return Port.ETHERNET_NNI
+    if 5 <= intf_id <= 20:
+        return Port.PON_OLT
+    elif 1 <= intf_id <= 4:
+        return Port.ETHERNET_NNI
+    else:
+        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]:
+    # Not sure what fffd and the other is
+    return out_port in [1, 2, 3, 4, 0xfffd, 0xfffffffd]
+
+
+def is_downstream(in_port, out_port):
+    return not is_upstream(in_port, out_port)
diff --git a/voltha/adapters/adtran_olt/download.py b/voltha/adapters/adtran_olt/download.py
index 2639bdc..af28fdd 100644
--- a/voltha/adapters/adtran_olt/download.py
+++ b/voltha/adapters/adtran_olt/download.py
@@ -518,7 +518,7 @@
         del_job_xml = """
             <maintenance-jobs operation="delete" xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs"/>
             """
-        dl = [client.edit_config(del_fs_xml),
-              client.edit_config(del_job_xml)]
+        dl = [client.edit_config(del_fs_xml, ignore_delete_error=True),
+              client.edit_config(del_job_xml, ignore_delete_error=True)]
 
         return defer.gatherResults(dl, consumeErrors=True)
diff --git a/voltha/adapters/adtran_olt/flow/acl.py b/voltha/adapters/adtran_olt/flow/acl.py
index 10063bd..42975d8 100644
--- a/voltha/adapters/adtran_olt/flow/acl.py
+++ b/voltha/adapters/adtran_olt/flow/acl.py
@@ -25,6 +25,7 @@
 ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}'  # format(flow_entry.handler.device_id, flow_entry.flow.id)
 ACL_NAME_REGEX_ALL = 'VOLTHA-ACL-*'
 
+
 class ACL(object):
     """
     Class to wrap Trap-to-Controller functionality
@@ -47,10 +48,9 @@
         self._valid = self._decode()
 
     def __str__(self):
-        return 'ACL: {}, Installed: {}, L2: {}, L3/4: {}'.format(self.name,
-                                                                 self._installed,
-                                                                 self.is_l2_exception,
-                                                                 self.is_l3_l4_exception)
+        return 'ACL: {}, Installed: {}, L2: {}, L3/4: {}'.\
+            format(self.name, self._installed, self.is_l2_exception,
+                   self.is_l3_l4_exception)
 
     @property
     def name(self):
diff --git a/voltha/adapters/adtran_olt/flow/evc.py b/voltha/adapters/adtran_olt/flow/evc.py
index 0a762fc..17cf005 100644
--- a/voltha/adapters/adtran_olt/flow/evc.py
+++ b/voltha/adapters/adtran_olt/flow/evc.py
@@ -256,6 +256,7 @@
     @inlineCallbacks
     def _do_install(self):
         # Install the EVC if needed
+        log.debug('do-install', valid=self._valid, installed=self._installed)
 
         if self._valid and not self._installed:
             # TODO: Currently install EVC and then MAPs. Can do it all in a single edit-config operation
@@ -281,8 +282,7 @@
             # xml += EVC.SwitchingMethod.xml(self._switching_method)
             xml += EVC._xml_trailer()
 
-            log.debug("Creating EVC {}: '{}'".format(self.name, xml))
-
+            log.debug('create-evc', name=self.name, xml=xml)
             try:
                 # Set installed to true while request is in progress
                 self._installed = True
@@ -291,7 +291,7 @@
                 self.status = '' if results.ok else results.error
 
             except Exception as e:
-                log.exception('Failed to install EVC', name=self.name, e=e)
+                log.exception('install-failed', name=self.name, e=e)
                 raise
 
         # Install any associated EVC Maps
@@ -303,7 +303,7 @@
 
                 except Exception as e:
                     evc_map.status = 'Exception during EVC-MAP Install: {}'.format(e.message)
-                    log.exception(evc_map.status, e=e)
+                    log.exception('evc-map-install-failed', e=e)
 
         returnValue(self._installed and self._valid)
 
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 9c5cbe3..07e35b2 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -146,10 +146,15 @@
     def installed(self):
         return self._installed
 
-    @installed.setter
-    def installed(self, value):
-        assert not value, 'installed can only be reset'                # Can only reset
-        self._installed = False
+    @property
+    def needs_update(self):
+        """ True if an parameter/ACL/... needs update or map needs to be reflowed after a failure"""
+        return self._needs_update
+
+    @needs_update.setter
+    def needs_update(self, value):
+        assert not value, 'needs update can only be reset'                # Can only reset
+        self._needs_update = False
 
     @property
     def name(self):
@@ -306,6 +311,8 @@
                 ports.extend(gems_and_vids[0])
             return ports
 
+        log.debug('install-evc-map', valid=self._valid, gem_ports=gem_ports())
+
         if self._valid and len(gem_ports()) > 0:
             # Install ACLs first (if not yet installed)
             work_acls = self._new_acls.copy()
@@ -318,12 +325,13 @@
                     #     pass                # TODO : do anything?
 
                 except Exception as e:
-                    log.exception('acl-install', name=self.name, e=e)
+                    log.exception('acl-install-failed', name=self.name, e=e)
                     self._new_acls.update(work_acls)
                     raise
 
             # Now EVC-MAP
             if not self._installed or self._needs_update:
+                log.debug('needs-install-or-update', installed=self._installed, update=self._needs_update)
                 try:
                     self._cancel_deferred()
                     map_xml = self._ingress_install_xml(self._gem_ids_and_vid, work_acls.values(),
@@ -343,7 +351,7 @@
                         self._new_acls.update(work_acls)
 
                 except Exception as e:
-                    log.exception('map-install', name=self.name, e=e)
+                    log.exception('evc-map-install-failed', name=self.name, e=e)
                     self._new_acls.update(work_acls)
                     raise
 
@@ -432,9 +440,12 @@
         returnValue('Done')
 
     def reflow_needed(self):
-        log.debug('reflow-needed')
-        reflow = not self.installed
-        # TODO: implement
+        log.debug('reflow-needed', installed=self.installed, needs_update=self.needs_update)
+        reflow = not self.installed or self.needs_update
+
+        if not reflow:
+            pass  # TODO: implement retrieve & compare of EVC Map parameters
+
         return reflow
 
     @staticmethod
@@ -494,6 +505,8 @@
         assert flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM, \
             'Only Upstream flows additions are supported at this time'
 
+        log('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 \
             else EVCMap.create_egress_map(flow, evc, dry_run=True)
@@ -529,6 +542,7 @@
         try:
             del self._flows[flow.flow_id]
 
+            log('remove-flow-to-evc', flow=flow, evc=evc)
             # Remove any ACLs
             acl_name = ACL.flow_to_name(flow)
             acl = None
@@ -701,8 +715,8 @@
         # flow, then this is a traditional EVC flow
 
         evc.men_to_uni_tag_manipulation = EVC.Men2UniManipulation.POP_OUT_TAG_ONLY
-        evc.switching_method = EVC.SwitchingMethod.DOUBLE_TAGGED  # \
-        #     if self._c_tag is not None else EVC.SwitchingMethod.SINGLE_TAGGED
+        evc.switching_method = EVC.SwitchingMethod.DOUBLE_TAGGED \
+            if self._c_tag is not None else EVC.SwitchingMethod.SINGLE_TAGGED
 
         try:
             acl = ACL.create(flow)
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index dac6e1e..6d5ea37 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -15,13 +15,11 @@
 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 *
 from voltha.protos.openflow_13_pb2 import OFPP_MAX
-from twisted.internet import defer
 from twisted.internet.defer import returnValue, inlineCallbacks, gatherResults
 
 log = structlog.get_logger()
@@ -123,6 +121,9 @@
             self.name, self.in_port, self.output, self.vlan_id, self.inner_vid,
             self.eth_type, self.ip_protocol)
 
+    def __repr__(self):
+        return str(self)
+
     @property
     def name(self):
         return self._name    # TODO: Is a name really needed in production?
@@ -174,7 +175,6 @@
 
         :param flow:   (Flow) Flow entry passed to VOLTHA adapter
         :param handler: (AdtranDeviceHandler) handler for the device
-
         :return: (FlowEntry, EVC)
         """
         # Exit early if it already exists
@@ -204,13 +204,12 @@
             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-upstream-exists', flow=flow_entry)
+                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
             # 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
@@ -231,6 +230,7 @@
                 downstream_sig = flow_entry.downstream_signature
 
             if downstream_sig is None:
+                log.debug('flow-entry-empty-downstream', flow=flow_entry)
                 return None, None
 
             if downstream_sig not in downstream_sig_table:
@@ -256,8 +256,8 @@
                 if len(upstream_flows) == 0 and not downstream_flow.is_multicast_flow:
                     upstream_flows = None
 
-            log.debug('flow-entry-search-results', flow=flow_entry, downstream_flow=downstream_flow,
-                      upstream_flows=upstream_flows)
+            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)
@@ -281,6 +281,9 @@
 
         :return: EVC object
         """
+        log.debug('flow-evc-and-maps', downstream_flow=downstream_flow,
+                  upstream_flows=upstream_flows)
+
         if (evc is None and downstream_flow is None) or upstream_flows is None:
             return None
 
@@ -300,6 +303,9 @@
                 downstream_flow.evc = EVC(downstream_flow)
 
         if not downstream_flow.evc.valid:
+            log.debug('flow-evc-and-maps-downstream-invalid',
+                      downstream_flow=downstream_flow,
+                      upstream_flows=upstream_flows)
             return None
 
         # Create EVC-MAPs. Note upstream_flows is empty list for multicast
@@ -332,6 +338,10 @@
         all_maps_valid = all(flow.evc_map.valid for flow in upstream_flows) \
             or downstream_flow.is_multicast_flow
 
+        log.debug('flow-evc-and-maps-downstream',
+                  downstream_flow=downstream_flow,
+                  upstream_flows=upstream_flows, all_valid=all_maps_valid)
+
         return downstream_flow.evc if all_maps_valid else None
 
     def get_utility_evc(self, upstream_flows=None, use_default_vlan_id=False):
@@ -355,6 +365,7 @@
         """
         Examine flow rules and extract appropriate settings
         """
+        log.debug('start-decode')
         status = self._decode_traffic_selector() and self._decode_traffic_treatment()
 
         if status:
@@ -389,7 +400,7 @@
                 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, self.vlan_id, untagged_vlan = \
+                    self._logical_port, uni_vid, untagged_vlan = \
                         self._handler.get_onu_port_and_vlans(self)
 
                     if self._needs_acl_support:
@@ -400,11 +411,17 @@
                             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
 
                 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)
+
         # 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:
@@ -444,6 +461,8 @@
             self.signature = upstream_sig
             self.downstream_signature = downstream_sig
 
+        log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
+
         return status
 
     def _decode_traffic_selector(self):
@@ -465,19 +484,28 @@
 
             elif field.type == VLAN_VID:
                 # log.info('*** field.type == VLAN_VID', value=field.vlan_vid & 0xfff)
-                self.vlan_id = field.vlan_vid & 0xfff
+                if self.handler.xpon_support:
+                    # Traditional xPON way
+                    self.vlan_id = field.vlan_vid & 0xfff
+                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
+
+                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.info('*** field.type == VLAN_PCP', value=field.vlan_pcp)
+                log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
                 self.pcp = field.vlan_pcp
 
             elif field.type == ETH_TYPE:
-                # log.info('*** field.type == ETH_TYPE', value=field.eth_type)
+                log.debug('*** field.type == ETH_TYPE', value=field.eth_type)
                 self.eth_type = field.eth_type
 
             elif field.type == IP_PROTO:
-                # log.info('*** field.type == IP_PROTO', value=field.ip_proto)
+                log.debug('*** field.type == IP_PROTO', value=field.ip_proto)
                 self.ip_protocol = field.ip_proto
 
                 if self.ip_protocol not in _supported_ip_protocols:
@@ -485,20 +513,29 @@
                     return False
 
             elif field.type == IPV4_DST:
-                # log.info('*** field.type == IPV4_DST', value=field.ipv4_dst)
+                log.debug('*** field.type == IPV4_DST', value=field.ipv4_dst)
                 self.ipv4_dst = field.ipv4_dst
 
             elif field.type == UDP_DST:
-                # log.info('*** field.type == UDP_DST', value=field.udp_dst)
+                log.debug('*** field.type == UDP_DST', value=field.udp_dst)
                 self.udp_dst = field.udp_dst
 
             elif field.type == UDP_SRC:
-                # log.info('*** field.type == UDP_SRC', value=field.udp_src)
+                log.debug('*** field.type == UDP_SRC', value=field.udp_src)
                 self.udp_src = field.udp_src
 
             elif field.type == METADATA:
-                # log.info('*** field.type == METADATA', value=field.table_metadata)
-                self.inner_vid = field.table_metadata
+                log.debug('*** field.type == METADATA', value=field.table_metadata)
+                if self.handler.xpon_support:
+                    # Traditional xPON way
+                    self.inner_vid = field.table_metadata
+                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
+
+                log.debug('*** field.type == METADATA', value=field.table_metadata, inner_vid=self.inner_vid)
 
             else:
                 log.warn('unsupported-selection-field', type=field.type)
@@ -520,17 +557,17 @@
                 pass           # Handled earlier
 
             elif act.type == POP_VLAN:
-                # log.info('*** action.type == POP_VLAN')
+                log.debug('*** action.type == POP_VLAN')
                 self.pop_vlan += 1
 
             elif act.type == PUSH_VLAN:
-                # log.info('*** action.type == PUSH_VLAN', value=act.push)
+                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)
 
             elif act.type == SET_FIELD:
-                # log.info('*** action.type == SET_FIELD', value=act.set_field.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:
@@ -676,7 +713,7 @@
 
         for evc_map in evc_maps:
             if reflow or evc_map.reflow_needed():
-                evc_map.installed = False
+                evc_map.needs_update = False
 
             if not evc_map.installed:
                 evc = evc_map.evc
diff --git a/voltha/adapters/adtran_olt/net/adtran_netconf.py b/voltha/adapters/adtran_olt/net/adtran_netconf.py
index 2671b19..4e39a6a 100644
--- a/voltha/adapters/adtran_olt/net/adtran_netconf.py
+++ b/voltha/adapters/adtran_olt/net/adtran_netconf.py
@@ -117,8 +117,9 @@
         #
         #  self._session.raise_mode = RaiseMode:NONE
         #
-        # and the when you get a response back, you can check   'response.ok' to see if it is 'True'
-        # if it is not, you can enumerate the 'response.errors' list for more information
+        # and the when you get a response back, you can check   'response.ok' to
+        # see if it is 'True' if it is not, you can enumerate the 'response.errors'
+        # list for more information
 
         return self._session
 
@@ -240,7 +241,7 @@
     def unlock(self, source):
         """
         Get the requested data from the server
-        :param rpc_string: RPC request
+        :param source: RPC request
 
         :return: (deferred) for RpcReply
         """
@@ -267,18 +268,24 @@
 
     @inlineCallbacks
     def edit_config(self, config, target='running', default_operation='none',
-                    test_option=None, error_option=None):
+                    test_option=None, error_option=None, ignore_delete_error=False):
         """
-        Loads all or part of the specified config to the target configuration datastore with the ability to lock
-        the datastore during the edit.
+        Loads all or part of the specified config to the target configuration datastore
+        with the ability to lock the datastore during the edit.
 
-        :param config is the configuration, which must be rooted in the config element. It can be specified
-                      either as a string or an Element.format="xml"
+        :param config is the configuration, which must be rooted in the config element.
+                      It can be specified either as a string or an Element.format="xml"
         :param target is the name of the configuration datastore being edited
         :param default_operation if specified must be one of { 'merge', 'replace', or 'none' }
         :param test_option if specified must be one of { 'test_then_set', 'set' }
-        :param error_option if specified must be one of { 'stop-on-error', 'continue-on-error', 'rollback-on-error' }
-                            The 'rollback-on-error' error_option depends on the :rollback-on-error capability.
+        :param error_option if specified must be one of { 'stop-on-error',
+                            'continue-on-error', 'rollback-on-error' } The
+                            'rollback-on-error' error_option depends on the
+                            :rollback-on-error capability.
+        :param ignore_delete_error: (bool) For some startup deletes/clean-ups, we do a
+                                    delete high up in the config to get whole lists. If
+                                    these lists are empty, this helps suppress any error
+                                    message from NETConf on failure to delete an empty list
 
         :return: (deferred) for RpcReply
         """
@@ -298,16 +305,22 @@
                          ' xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
                          config + '</config>'
 
+            log.debug('netconf-request', config=config, target=target,
+                      default_operation=default_operation)
+
             rpc_reply = yield threads.deferToThread(self._do_edit_config, target,
                                                     config, default_operation,
                                                     test_option, error_option)
         except Exception as e:
+            if ignore_delete_error and 'operation="delete"' in config.lower():
+                returnValue('ignoring-delete-error')
             log.exception('edit_config', e=e, config=config, target=target)
             raise
 
         returnValue(rpc_reply)
 
-    def _do_edit_config(self, target, config, default_operation, test_option, error_option):
+    def _do_edit_config(self, target, config, default_operation, test_option, error_option,
+                        ignore_delete_error=False):
         """
         Perform actual edit-config operation
         """
@@ -321,12 +334,13 @@
                                                  # error_option=error_option
                                                  )
 
-            log.debug('response', response=response)
+            log.debug('netconf-response', response=response)
             # To get XML, use response.xml
             # To check status, use response.ok  (boolean)
 
         except RPCError as e:
-            log.exception('do_edit_config', e=e, config=config, target=target)
+            if not ignore_delete_error or 'operation="delete"' not in config.lower():
+                log.exception('do_edit_config', e=e, config=config, target=target)
             raise
 
         return response
diff --git a/voltha/adapters/adtran_olt/net/adtran_rest.py b/voltha/adapters/adtran_olt/net/adtran_rest.py
index 9f2b990..a478d95 100644
--- a/voltha/adapters/adtran_olt/net/adtran_rest.py
+++ b/voltha/adapters/adtran_olt/net/adtran_rest.py
@@ -99,6 +99,7 @@
         :param is_retry: (boolean) True if this method called recursively in order to recover
                                    from a connection loss. Can happen sometimes in debug sessions
                                    and in the real world.
+        :param suppress_error: (boolean) If true, do not output ERROR message on REST request failure
         :return: (dict) On success with the proper results
         """
         log.debug('request', method=method, uri=uri, data=data, retry=is_retry)
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index b855218..362688a 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -24,7 +24,7 @@
 
 _MAX_EXPEDITE_COUNT = 5
 _EXPEDITE_SECS = 2
-_HW_SYNC_SECS = 30
+_HW_SYNC_SECS = 60
 
 
 class Onu(object):
@@ -32,8 +32,7 @@
     Wraps an ONU
     """
     MIN_ONU_ID = 0
-    MAX_ONU_ID = 253            # G.984. 0..253, 254=reserved, 255=broadcast
-    BROADCAST_ONU_ID = 255
+    MAX_ONU_ID = 1020          # 1021..2013 reserved
     DEFAULT_PASSWORD = ''
 
     def __init__(self, onu_info):
@@ -52,11 +51,16 @@
         self._xpon_name = onu_info['xpon-name']
         self._gem_ports = {}                           # gem-id -> GemPort
         self._tconts = {}                              # alloc-id -> TCont
-        self._onu_vid = onu_info['onu-vid']
-        self.untagged_vlan = self._onu_vid
-        self._uni_ports = [onu_info['onu-vid']]     # TODO: Get rid of this
-        assert len(self._uni_ports) == 1, 'Only one UNI port supported at this time'
-        self._channel_id = onu_info['channel-id']
+        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
@@ -259,6 +263,7 @@
     @property
     def logical_port(self):
         """Return the logical PORT number of this ONU's UNI"""
+        # TODO: once we support multiple UNIs, this needs to be revisited
         return self._uni_ports[0]
 
     @property
@@ -273,19 +278,25 @@
             device_id = self.olt.device_id
 
             try:
-                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)
+                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
 
@@ -309,7 +320,7 @@
 
     @property
     def channel_id(self):
-        return self._channel_id
+        return self._channel_id             # SEBA    (xPON mode may be only one using this. May also be no one)
 
     @property
     def serial_number_64(self):
@@ -388,12 +399,14 @@
         name = 'onu-create-{}-{}-{}: {}'.format(self._pon_id, self._onu_id,
                                                 self._serial_number_base64, self._enabled)
 
-        if not self._created:
+        first_sync = self._sync_tick if self._created else 3
+
+        if not self._created or reflow:
             try:
                 yield self.olt.rest_client.request('POST', uri, data=data, name=name)
                 self._created = True
 
-            except Exception as e:  # TODO: Add breakpoint here during unexpected reboot test
+            except Exception as e:
                 self.log.exception('onu-create', e=e)
                 # See if it failed due to already being configured
                 url = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, self._onu_id)
@@ -402,19 +415,19 @@
                 try:
                     results = yield self.olt.rest_client.request('GET', uri, name=name)
                     self.log.debug('onu-create-check', results=results)
-                    if len(results) != 1 or results[0].get('serial-number', '') != self._serial_number_base64:
-                        raise e
+                    if len(results) == 1 and results[0].get('serial-number', '') != self._serial_number_base64:
+                        self._created = True
 
                 except Exception as e:
-                    self.log.exception('onu-exists-check', e=e)
-                    raise
+                    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
-        first_sync = self._sync_tick
-
+        # Now set up all TConts & GEM-ports
         for _, tcont in tconts.items():
             try:
-                yield self.add_tcont(tcont, reflow=reflow)
+                _results = yield self.add_tcont(tcont, reflow=reflow)
 
             except Exception as e:
                 self.log.exception('add-tcont', tcont=tcont, e=e)
@@ -422,7 +435,7 @@
 
         for _, gem_port in gem_ports.items():
             try:
-                yield self.add_gem_port(gem_port, reflow=reflow)
+                _results = yield self.add_gem_port(gem_port, reflow=reflow)
 
             except Exception as e:
                 self.log.exception('add-gem-port', gem_port=gem_port, reflow=reflow, e=e)
@@ -669,8 +682,8 @@
             reflow, self._resync_flows = self._resync_flows, False
             return FlowEntry.sync_flows_by_onu(self, reflow=reflow)
 
-        def failure(reason):
-            # self.log.error('hardware-sync-get-config-failed', reason=reason)
+        def failure(_reason):
+            # self.log.error('hardware-sync-get-config-failed', reason=_reason)
             pass
 
         def reschedule(_):
@@ -873,9 +886,9 @@
                 for evc_map in evc_maps:
                     evc_map.remove_gem_port(gem_port)
 
-            results = 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,
+                                                self._pon_id,
+                                                self.onu_id)
         except RestInvalidResponseCode as e:
             if e.code != 404:
                 self.log.exception('onu-delete', e=e)
@@ -895,5 +908,5 @@
 
     @staticmethod
     def gem_id_to_gvid(gem_id):
-        """Calculate GEM VID for a given GEM port id"""
+        """Calculate GEM VID (gvid) for a given GEM port id"""
         return gem_id - 2048
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index e0f7f61..eb1239f 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -20,7 +20,7 @@
 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
@@ -46,11 +46,11 @@
     _MCAST_ONU_ID = 253
     _MCAST_ALLOC_BASE = 0x500
 
-    _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery']    # , 'autoactivate']
+    # AutoActivate should be used if xPON configuration is not supported
+    _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery', 'autoactivate']
     _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
 
     def __init__(self, parent, **kwargs):
-
         super(PonPort, self).__init__(parent, **kwargs)
 
         assert 'pon-id' in kwargs, 'PON ID not found'
@@ -72,22 +72,29 @@
 
         self._onus = {}         # serial_number-base64 -> ONU  (allowed list)
         self._onu_by_id = {}    # onu-id -> ONU
-        self._next_onu_id = Onu.MIN_ONU_ID + 128
+        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._discovery_deferred = None           # Specifically for ONU discovery
         self._active_los_alarms = set()           # ONU-ID
 
-        # xPON configuration
+        # xPON configuration        # SEBA
+        self._activation_method = 'autodiscovery' if parent.xpon_support else 'autoactivate'
 
-        self._xpon_name = None
-        self._downstream_fec_enable = False
-        self._upstream_fec_enable = False
+        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._deployment_range = 25000
         self._authentication_method = 'serial-number'
         self._mcast_aes = False
         self._line_rate = 'down_10_up_10'
-        self._activation_method = 'autodiscovery'
 
         # Statistics
         self.tx_bip_errors = 0
@@ -265,6 +272,10 @@
         value = value.lower()
         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
@@ -415,7 +426,6 @@
                 self.log.exception('onu-cleanup', onu_id=onu_id, e=e)
 
         dl.append(self._set_pon_config("enabled", False))
-
         return defer.gatherResults(dl, consumeErrors=True)
 
     @inlineCallbacks
@@ -424,7 +434,8 @@
         Set the PON Port to a known good state on initial port startup.  Actual
         PON 'Start' is done elsewhere
         """
-        initial_port_state = AdminState.DISABLED
+        initial_port_state = AdminState.DISABLED if self._parent.xpon_support \
+            else AdminState.ENABLED
         self.log.info('reset', initial_state=initial_port_state)
 
         try:
@@ -516,7 +527,10 @@
         data = json.dumps({leaf: value})
         uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
         name = 'pon-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
-        return self._parent.rest_client.request('PATCH', uri, data=data, name=name)
+        # If no optics on PON, then PON config fails with status 400, suppress this
+        suppress_error = len(self.onu_ids) == 0
+        return self._parent.rest_client.request('PATCH', uri, data=data, name=name,
+                                                suppress_error=suppress_error)
 
     def _discover_onus(self):
         self.log.debug('discovery', state=self._admin_state, in_sync=self._in_sync)
@@ -565,9 +579,11 @@
                         dl.append(self._set_pon_config("deployment-range",
                                                        self.deployment_range))
 
+                    # A little side note: FEC enable/disable cannot be changed and
+                    # will remain in the previous status until an optical module
+                    # is plugged in.
                     if self.downstream_fec_enable != config.downstream_fec_enable:
                         self._in_sync = False
-                        self._expedite_sync = True
                         dl.append(self._set_pon_config("downstream-fec-enable",
                                                        self.downstream_fec_enable))
 
@@ -584,7 +600,7 @@
                     self.log.debug('sync-pon-onu-results', config=hw_onus)
 
                     # ONU's have their own sync task, extra (should be deleted) are
-                    # handled here. Missing are handled by normal discovery mechanisms.
+                    # handled here.
 
                     hw_onu_ids = frozenset(hw_onus.keys())
                     my_onu_ids = frozenset(self._onu_by_id.keys())
@@ -592,6 +608,14 @@
                     extra_onus = hw_onu_ids - my_onu_ids
                     dl = [self.delete_onu(onu_id) for onu_id in extra_onus]
 
+                    if self.activation_method == "autoactivate":
+                        # Autoactivation of ONUs requires missing ONU detection. If
+                        # not found, create them here but let TCont/GEM-Port restore be
+                        # handle by ONU H/w sync logic.
+                        for onu in [self._onu_by_id[onu_id] for onu_id in my_onu_ids - hw_onu_ids
+                                    if self._onu_by_id.get(onu_id) is not None]:
+                            dl.append(onu.create(dict(), dict(), reflow=True))
+
                     return defer.gatherResults(dl, consumeErrors=True)
 
             def failure(reason, what):
@@ -639,7 +663,6 @@
         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)
 
@@ -688,7 +711,6 @@
                             owner_info['onu_id'] = \
                                 child_device.proxy_address.onu_id
                             owner_info['alloc_id'] = tcont.alloc_id
-                            # self.bal.create_scheduler(id, 'upstream', owner_info, 8)
         else:
             self.log.info('Invalid-ONU-event', olt_id=olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
@@ -757,7 +779,7 @@
         self.log.debug('discovered-ONUs', list=discovered_onus)
 
         # Only request discovery if activation is auto-discovery or auto-activate
-        continue_discovery = ['autodiscovery']   # , 'autoactivate']
+        continue_discovery = ['autodiscovery', 'autoactivate']
 
         if self._activation_method not in continue_discovery:
             return set(), set()
@@ -775,11 +797,11 @@
         :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()
@@ -809,17 +831,47 @@
                     except StopIteration:
                         # Can happen if vont-ani or ont-ani has not yet been configured
                         self.log.debug('no-vont-or-ont')
-                        return None
+                        return None, False
 
                     except Exception as e:
                         self.log.exception('autodiscovery', e=e)
                         raise
                 else:
                     self.log.debug('not-serial-number-authentication')
-                    return None
+                    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)
+                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()
+                                    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
+
+                except StopIteration:
+                    # Can happen if vont-ani or ont-ani has not yet been configured
+                    self.log.error('no-vont-or-ont-autoactivate')
+                    return None, False
+
+                except Exception as e:
+                    self.log.exception('autoactivate', e=e)
+                    raise
             else:
-                self.log.debug('not-auto-discovery')
-                return None
+                self.log.debug('unsupported-activation-method', method=self.activation_method)
+                return None, False
 
             onu_info = {
                 'device-id': self.olt.device_id,
@@ -833,29 +885,56 @@
                 'password': Onu.DEFAULT_PASSWORD,
                 't-conts': tconts,
                 'gem-ports': gem_ports,
-                'onu-vid': self.olt.get_onu_vid(onu_id),
-                'channel-id': self.olt.get_channel_id(self._pon_id, onu_id),
+                '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)]
+
             # Hold off ONU activation until at least one GEM Port is defined.
-            self.log.debug('onu-info', gem_ports=gem_ports)
+            self.log.debug('onu-info-tech-profiles', gem_ports=gem_ports)
 
             # return onu_info
-            return onu_info if len(gem_ports) > 0 and venet is not None else None
+            return onu_info, activate_onu
 
         except Exception as e:
-            self.log.exception('get-onu-info', e=e)
-            return None
+            self.log.exception('get-onu-info-tech-profiles', e=e)
+            return None, False
 
     @inlineCallbacks
     def add_onu(self, serial_number_64, status):
+        """
+        Add an ONU to the PON
+
+        TODO:  This needs major refactoring after xPON is deprecated to be more maintainable
+        """
         serial_number = Onu.serial_number_to_string(serial_number_64)
         self.log.info('add-onu', serial_number=serial_number,
                       serial_number_64=serial_number_64, status=status)
-        onu_info = self._get_onu_info(serial_number)
 
-        if onu_info is None:
+        # 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:
+            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()
@@ -872,8 +951,18 @@
             elif (serial_number_64 in self._onus and onu_id not in self._onu_by_id) or \
                     (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. Drop it and add back on next discovery cycle
-                self.delete_onu(onu_id)
+                # 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
 
             elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
                 self.log.warning('max-onus-provisioned', count=len(self._onus))
@@ -886,10 +975,16 @@
                 self._onu_by_id[onu.onu_id] = onu
 
             if onu is not None:
-                try:
-                    tconts = onu_info['t-conts']
-                    gem_ports = onu_info['gem-ports']
+                tconts = onu_info['t-conts']
+                gem_ports = onu_info['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
+                                                              serial_number,
+                                                              tconts, gem_ports)
+                try:
                     # 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
@@ -901,28 +996,25 @@
                                 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)
 
-                    yield onu.create(tconts, gem_ports)
+                    # TODO: Need to clean up TCont and GEM-Port on ONU delete in non-xPON mode
+                    _results = yield onu.create(tconts, gem_ports)
 
                 except Exception as e:
                     self.log.exception('add-onu', serial_number=serial_number_64, e=e)
-                    del self._onus[serial_number_64]
-                    del self._onu_by_id[onu.onu_id]
+                    # allowable exception.  H/w re-sync will recover any issues
 
+    @property
     def get_next_onu_id(self):
-        used_ids = [onu.onu_id for onu in self.onus]
+        assert not self.olt.xpon_support, 'Only non-XPON mode allocates ONU IDs.  xPON assigns tem'
+        return self._onu_id_pool.get_next()
 
-        while True:
-            onu_id = self._next_onu_id
-            self._next_onu_id += 1
-
-            if self._next_onu_id > Onu.MAX_ONU_ID:
-                self._next_onu_id = Onu.MIN_ONU_ID + 128
-
-            if onu_id not in used_ids:
-                return onu_id
+    def release_onu_id(self, onu_id):
+        if not self.olt.xpon_support:
+            self._onu_id_pool.release(onu_id)
 
     @inlineCallbacks
     def _remove_from_hardware(self, onu_id):
@@ -946,6 +1038,7 @@
         # Remove from any local dictionary
         if onu_id in self._onu_by_id:
             del self._onu_by_id[onu_id]
+            self.release_onu_id(onu.onu_id)
 
         for sn_64 in [onu.serial_number_64 for onu in self.onus if onu.onu_id == onu_id]:
             del self._onus[sn_64]
@@ -978,96 +1071,3 @@
         assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
 
         self._mcast_gem_ports[vlan] = mcast_gem
-
-    @inlineCallbacks
-    def channel_partition(self, name, partition=0, xpon_system=0, operation=None):
-        """
-        Delete/enable/disable a specified channel partition on this PON.
-
-        When creating a new Channel Partition, create it disabled, then define any associated
-        Channel Pairs. Then enable the Channel Partition.
-
-        :param name: (string) Name of the channel partition
-        :param partition: (int: 0..15) An index of the operator-specified channel subset
-                          in a NG-PON2 system. For XGS-PON, this is typically 0
-        :param xpon_system: (int: 0..1048575) Identifies a specific xPON system
-        :param operation: (string) 'delete', 'enable', or 'disable'
-        """
-        if operation.lower() not in ['delete', 'enable', 'disable']:
-            raise ValueError('Unsupported operation: {}'.format(operation))
-
-        try:
-            xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
-
-            if operation.lower() is 'delete':
-                xml += '<interface operation="delete">'
-            else:
-                xml += '<interface>'
-                xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
-                       'adtn-xp:xpon-channel-partition</type>'
-                xml += '<adtn-xp:channel-partition xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
-                xml += '  <adtn-xp:partition-id>{}</adtn-xp:partition-id>'.format(partition)
-                xml += '  <adtn-xp:xpon-system>{}</adtn-xp:xpon-system>'.format(xpon_system)
-                xml += '</adtn-xp:channel-partition>'
-                xml += '<enabled>{}</enabled>'.format('true' if operation.lower() == 'enable' else 'false')
-
-            xml += '<name>{}</name>'.format(name)
-            xml += '</interface></interfaces>'
-
-            results = yield self.olt.netconf_client.edit_config(xml)
-            returnValue(results)
-
-        except Exception as e:
-            self.log.exception('channel_partition')
-            raise
-
-    @inlineCallbacks
-    def channel_pair(self, name, partition, operation=None, **kwargs):
-        """
-        Create/delete a channel pair on a specific channel_partition for a PON
-
-        :param name: (string) Name of the channel pair
-        :param partition: (string) Name of the channel partition
-        :param operation: (string) 'delete', 'enable', or 'disable'
-        :param kwargs: (dict) Additional leaf settings if desired
-        """
-        if operation.lower() not in ['delete', 'enable', 'disable']:
-            raise ValueError('Unsupported operation: {}'.format(operation))
-
-        try:
-            xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
-
-            if operation.lower() is 'delete':
-                xml += '<interface operation="delete">'
-            else:
-                xml += '<interface>'
-                xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
-                       'adtn-xp:xpon-channel-pair</type>'
-                xml += '<adtn-xp:channel-pair xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
-                xml += '  <adtn-xp:channel-partition>{}</adtn-xp:channel-partition>'.format(partition)
-                xml += '  <adtn-xp:channel-termination>channel-termination {}</adtn-xp:channel-termination>'.\
-                    format(self.pon_id)
-                xml += '  <adtn-xp:upstream-admin-label>{}</adtn-xp:upstream-admin-label>'.\
-                    format(kwargs.get('upstream-admin-label', 1))
-                xml += '  <adtn-xp:downstream-admin-label>{}</adtn-xp:downstream-admin-label>'.\
-                    format(kwargs.get('downstream-admin-label', 1))
-                xml += '  <adtn-xp:upstream-channel-id>{}</adtn-xp:upstream-channel-id>'.\
-                    format(kwargs.get('upstream-channel-id', 15))
-                xml += '  <adtn-xp:downstream-channel-id>{}</adtn-xp:downstream-channel-id>'.\
-                    format(kwargs.get('downstream-channel-id', 15))
-                xml += '  <adtn-xp:downstream-channel-fec-enable>{}</adtn-xp:downstream-channel-fec-enable>'. \
-                    format('true' if kwargs.get('downstream-channel-fec-enable', True) else 'false')
-                xml += '  <adtn-xp:upstream-channel-fec-enable>{}</adtn-xp:upstream-channel-fec-enable>'. \
-                    format('true' if kwargs.get('upstream-channel-fec-enable', True) else 'false')
-                xml += '</adtn-xp:channel-pair>'
-                # TODO: Add support for upstream/downstream FEC-enable coming from here and not hard-coded
-
-            xml += '<name>{}</name>'.format(name)
-            xml += '</interface></interfaces>'
-
-            results = yield self.olt.netconf_client.edit_config(xml)
-            returnValue(results)
-
-        except Exception as e:
-            self.log.exception('channel_pair')
-            raise
diff --git a/voltha/adapters/adtran_olt/port.py b/voltha/adapters/adtran_olt/port.py
index 0373724..fd94c6d 100644
--- a/voltha/adapters/adtran_olt/port.py
+++ b/voltha/adapters/adtran_olt/port.py
@@ -30,9 +30,6 @@
         STOPPED = 2  # Disabled
         DELETING = 3  # Cleanup
 
-    _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery']  # , 'autoactivate']
-    _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
-
     def __init__(self, parent, **kwargs):
         assert parent, 'parent is None'
         assert 'port_no' in kwargs, 'Port number not found'
@@ -50,9 +47,15 @@
         self.sync_tick = 20.0
         self.sync_deferred = None  # For sync of PON config to hardware
 
-        # TODO: Deprecate 'enabled' and use admin_state instead
-        self._enabled = False
-        self._admin_state = AdminState.DISABLED
+        # 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._oper_status = OperStatus.DISCOVERED
         self._state = AdtnPort.State.INITIAL
 
@@ -120,18 +123,6 @@
         assert isinstance(value, bool), 'enabled is a boolean'
         self.admin_state = AdminState.ENABLED if value else AdminState.DISABLED
 
-    # @property
-    # def enabled(self):
-    #     return self._enabled
-    #
-    # @enabled.setter
-    # def enabled(self, value):
-    #     assert isinstance(value, bool), 'enabled is a boolean'
-    #     if self._enabled != value:
-    #         if value:
-    #             self.start()
-    #         self.stop()
-
     @property
     def oper_status(self):
         return self._oper_status
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
index d1ca2e5..1bfe009 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
@@ -24,10 +24,12 @@
     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
@@ -57,9 +59,188 @@
     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 configuraiton data for a specific pon-id / channel-termination
+        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
         """
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
index 8c92c9b..61756d2 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
@@ -27,6 +27,7 @@
 log = structlog.get_logger()
 
 
+# SEBA
 class AdtranXPON(object):
     """
     Class to abstract common OLT and ONU xPON operations
@@ -219,23 +220,24 @@
 
         return None
 
-    def create_tcont(self, tcont_data, traffic_descriptor_data):
+    def create_tcont(self, tcont_data, td_data):
         """
         Create TCONT information
         :param tcont_data:
-        :param traffic_descriptor_data:
+        :param td_data:
         """
-        log.debug('create-tcont', tcont=tcont_data, td=traffic_descriptor_data)
+        log.debug('create-tcont', tcont=tcont_data, td=td_data)
 
         # Handle TD first, then TCONT
-        try:
-            self.xpon_create(traffic_descriptor_data)
+        if td_data is not None:
+            try:
+                self.xpon_create(td_data)
 
-        except Exception as e:
-            log.exception('td-create', td=traffic_descriptor_data)
+            except Exception as e:
+                log.exception('td-create', td=td_data)
 
         try:
-            td = self.traffic_descriptors.get(traffic_descriptor_data.name)
+            td = self.traffic_descriptors.get(td_data.name) if td_data is not None else None
             self.xpon_create(tcont_data, td=td)
 
         except Exception as e:
@@ -288,11 +290,13 @@
 
     def xpon_create(self, data, td=None):
         log.debug('xpon-create', data=data)
-
         name = data.name
         items, create_method, update_method, _ = self._get_xpon_collection(data)
 
         if items is None:
+            from voltha.adapters.adtran_olt.adtran_olt_handler import OnuIndication
+            if isinstance(data, OnuIndication):   # Ignore this
+                return
             raise ValueError('Unknown data type: {}'.format(type(data)))
 
         item_type, new_item = self._data_to_dict(data, td=td)
@@ -318,6 +322,8 @@
 
     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)
@@ -373,6 +379,9 @@
 
     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)
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index 1f1c916..c50a858 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -36,7 +36,8 @@
                  untagged=False,
                  name=None,
                  handler=None,
-                 is_mock=False):
+                 is_mock=False,
+                 pb_data=None):
         super(OltGemPort, self).__init__(gem_id, alloc_id,
                                          encryption=encryption,
                                          omci_transport=omci_transport,
@@ -49,6 +50,7 @@
                                          handler=handler)
         self._is_mock = is_mock
         self._timestamp = None
+        self.data = pb_data     # Needed for non-xPON mode
 
     @staticmethod
     def create(handler, gem_port):
@@ -67,7 +69,8 @@
                           intf_ref=gem_port.get(port_ref),
                           handler=handler,
                           multicast=mcast,
-                          untagged=untagged)
+                          untagged=untagged,
+                          pb_data=gem_port['data'])
 
     @property
     def timestamp(self):
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index 3156d50..a409026 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -25,10 +25,12 @@
     Adtran OLT specific implementation
     """
     def __init__(self, alloc_id, traffic_descriptor,
-                 name=None, vont_ani=None, is_mock=False):
+                 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)
         self._is_mock = is_mock
+        self.data = pb_data             # Needed for non-xPON mode
 
     @staticmethod
     def create(tcont, td):
@@ -39,7 +41,8 @@
 
         return OltTCont(tcont['alloc-id'], td,
                         name=tcont['name'],
-                        vont_ani=tcont['vont-ani'])
+                        vont_ani=tcont['vont-ani'],
+                        pb_data=tcont['data'])
 
     @inlineCallbacks
     def add_to_hardware(self, session, pon_id, onu_id):
@@ -57,7 +60,7 @@
         try:
             results = yield session.request('POST', uri, data=data, name=name,
                                             suppress_error=False)
-        except:
+        except Exception as _e:
             results = None
 
         if self.traffic_descriptor is not None:
diff --git a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
index 1aa3848..635e5e1 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
@@ -28,12 +28,14 @@
                  additional=TrafficDescriptor.AdditionalBwEligibility.DEFAULT,
                  best_effort=None,
                  name=None,
-                 is_mock=False):
+                 is_mock=False,
+                 pb_data=None):
         super(OltTrafficDescriptor, self).__init__(fixed, assured, maximum,
                                                    additional=additional,
                                                    best_effort=best_effort,
                                                    name=name)
         self._is_mock = is_mock
+        self.data = pb_data
 
     @staticmethod
     def create(traffic_disc):
@@ -56,7 +58,8 @@
                                     traffic_disc['maximum-bandwidth'],
                                     name=traffic_disc['name'],
                                     best_effort=best_effort,
-                                    additional=additional)
+                                    additional=additional,
+                                    pb_data=traffic_disc['data'])
 
     @inlineCallbacks
     def add_to_hardware(self, session, pon_id, onu_id, alloc_id):