VOL-1397: Adtran-OLT - Initial containerization commit
 - Need to move VERSION to base directory

Change-Id: I9d62d0607a011ce642e379fd92b35ec48b300070
diff --git a/adapters/adtran_olt/resources/__init__.py b/adapters/adtran_olt/resources/__init__.py
new file mode 100644
index 0000000..9c454e3
--- /dev/null
+++ b/adapters/adtran_olt/resources/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2018-present Adtran, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/adapters/adtran_olt/resources/adtran_olt_resource_manager.py b/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
new file mode 100644
index 0000000..caf5a46
--- /dev/null
+++ b/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
@@ -0,0 +1,295 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import structlog
+
+from pyvoltha.adapters.common.pon_resource_manager.resource_manager import PONResourceManager
+from pyvoltha.common.utils.registry import registry
+# from voltha.core.config.config_backend import ConsulStore
+# from voltha.core.config.config_backend import EtcdStore
+from pyvoltha.adapters.common.kvstore.kvstore import create_kv_client
+from adtran_resource_manager import AdtranPONResourceManager
+
+
+class AdtranOltResourceMgr(object):
+
+    GEMPORT_IDS = "gemport_ids"
+    ALLOC_IDS = "alloc_ids"
+    BASE_PATH_KV_STORE = "adtran_olt/{}"       # adtran_olt/<device_id>
+
+    def __init__(self, device_id, host_and_port, extra_args, device_info):
+        self.log = structlog.get_logger(id=device_id,
+                                        ip=host_and_port)
+        self.device_id = device_id
+        self.host_and_port = host_and_port
+        self.extra_args = extra_args
+        self.device_info = device_info
+        self.args = registry('main').get_args()
+        self._path_prefix = AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id)
+
+        # KV store's IP Address and PORT
+        # host, port = '127.0.0.1', 8500
+        if self.args.backend == 'etcd':
+            host, port = self.args.etcd.split(':', 1)
+            self.kv_store = create_kv_client('etcd', host, port)
+            # self.kv_store = EtcdStore(host, port,
+            #                           AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+        elif self.args.backend == 'consul':
+            host, port = self.args.consul.split(':', 1)
+            self.kv_store = create_kv_client('consul', host, port)
+            # self.kv_store = ConsulStore(host, port,
+            #                             AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+        else:
+            self.log.error('Invalid-backend')
+            raise Exception("Invalid-backend-for-kv-store")
+
+        self.resource_mgr = AdtranPONResourceManager(
+            self.device_info.technology,
+            self.extra_args,
+            self.device_id, self.args.backend,
+            host, port
+        )
+        # Tech profiles uses this resource manager to retrieve information on a per-interface
+        # basis
+        self.resource_managers = {intf_id: self.resource_mgr for intf_id in device_info.intf_ids}
+
+        # Flag to indicate whether information fetched from device should
+        # be used to initialize PON Resource Ranges
+        self.use_device_info = False
+
+        self.initialize_device_resource_range_and_pool()
+
+    def __del__(self):
+        self.log.info("clearing-device-resource-pool")
+        for key, resource_mgr in self.resource_mgrs.iteritems():
+            resource_mgr.clear_device_resource_pool()
+
+    def get_onu_id(self, pon_intf_id):
+        onu_id = self.resource_mgr.get_resource_id(pon_intf_id,
+                                                   PONResourceManager.ONU_ID,
+                                                   onu_id=None,
+                                                   num_of_id=1)
+        if onu_id is not None:
+            pon_intf_onu_id = (pon_intf_id, onu_id)
+            self.resource_mgr.init_resource_map(pon_intf_onu_id)
+
+        return onu_id
+
+    def free_onu_id(self, pon_intf_id, onu_id):
+        self.resource_mgr.free_resource_id(pon_intf_id,
+                                           PONResourceManager.ONU_ID,
+                                           onu_id)
+        pon_intf_onu_id = (pon_intf_id, onu_id)
+        self.resource_mgr.remove_resource_map(pon_intf_onu_id)
+
+    def get_alloc_id(self, pon_intf_onu_id):
+        # Derive the pon_intf from the pon_intf_onu_id tuple
+        pon_intf = pon_intf_onu_id[0]
+        onu_id = pon_intf_onu_id[1]
+        alloc_id_list = self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_onu_id)
+
+        if alloc_id_list and len(alloc_id_list) > 0:
+            # Since we support only one alloc_id for the ONU at the moment,
+            # return the first alloc_id in the list, if available, for that
+            # ONU.
+            return alloc_id_list[0]
+
+        alloc_id_list = self.resource_mgr.get_resource_id(pon_intf,
+                                                          PONResourceManager.ALLOC_ID,
+                                                          onu_id=onu_id,
+                                                          num_of_id=1)
+        if alloc_id_list and len(alloc_id_list) == 0:
+            self.log.error("no-alloc-id-available")
+            return None
+
+        # update the resource map on KV store with the list of alloc_id
+        # allocated for the pon_intf_onu_id tuple
+        self.resource_mgr.update_alloc_ids_for_onu(pon_intf_onu_id,
+                                                   alloc_id_list)
+
+        # Since we request only one alloc id, we refer the 0th
+        # index
+        alloc_id = alloc_id_list[0]
+
+        return alloc_id
+
+    def get_gemport_id(self, pon_intf_onu_id, num_of_id=1):
+        # TODO: Remove this if never used
+        # Derive the pon_intf and onu_id from the pon_intf_onu_id tuple
+        pon_intf = pon_intf_onu_id[0]
+        onu_id = pon_intf_onu_id[1]
+        uni_id = pon_intf_onu_id[2]
+        assert False, 'unused function'
+
+        # gemport_id_list = self.resource_managers[pon_intf].get_current_gemport_ids_for_onu(
+        #     pon_intf_onu_id)
+        # if gemport_id_list and len(gemport_id_list) > 0:
+        #     return gemport_id_list
+        #
+        # gemport_id_list = self.resource_mgrs[pon_intf].get_resource_id(
+        #     pon_intf_id=pon_intf,
+        #     resource_type=PONResourceManager.GEMPORT_ID,
+        #     num_of_id=num_of_id
+        # )
+        #
+        # if gemport_id_list and len(gemport_id_list) == 0:
+        #     self.log.error("no-gemport-id-available")
+        #     return None
+        #
+        # # update the resource map on KV store with the list of gemport_id
+        # # allocated for the pon_intf_onu_id tuple
+        # self.resource_managers[pon_intf].update_gemport_ids_for_onu(pon_intf_onu_id,
+        #                                                             gemport_id_list)
+        #
+        # self.update_gemports_ponport_to_onu_map_on_kv_store(gemport_id_list,
+        #                                                     pon_intf, onu_id, uni_id)
+        # return gemport_id_list
+
+    def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
+        """ Typically called on ONU delete """
+
+        pon_intf_id = pon_intf_id_onu_id[0]
+        onu_id = pon_intf_id_onu_id[1]
+        try:
+            alloc_ids = self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
+            if alloc_ids is not None:
+                self.resource_mgr.free_resource_id(pon_intf_id,
+                                                   PONResourceManager.ALLOC_ID,
+                                                   alloc_ids, onu_id=onu_id)
+        except:
+            pass
+
+        try:
+            gemport_ids = self.resource_mgr.get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
+            if gemport_ids is not None:
+                self.resource_mgr.free_resource_id(pon_intf_id,
+                                                   PONResourceManager.GEMPORT_ID,
+                                                   gemport_ids)
+        except:
+            pass
+
+        try:
+            self.resource_mgr.free_resource_id(pon_intf_id,
+                                               PONResourceManager.ONU_ID,
+                                               onu_id)
+        except:
+            pass
+
+        # Clear resource map associated with (pon_intf_id, gemport_id) tuple.
+        self.resource_mgr.remove_resource_map(pon_intf_id_onu_id)
+
+        # Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
+        if gemport_ids is not None:
+            for gemport_id in gemport_ids:
+                try:
+                    self.kv_store.delete(self._make_path(str((pon_intf_id, gemport_id))))
+                    # del self.kv_store[str((pon_intf_id, gemport_id))]
+                except:
+                    pass
+
+    def initialize_device_resource_range_and_pool(self):
+        if not self.use_device_info:
+            status = self.resource_mgr.init_resource_ranges_from_kv_store()
+            if not status:
+                self.log.error("failed-to-load-resource-range-from-kv-store")
+                # When we have failed to read the PON Resource ranges from KV
+                # store, use the information selected as the default.
+                self.use_device_info = True
+
+        if self.use_device_info:
+            self.log.info("using-device-info-to-init-pon-resource-ranges")
+            self.resource_mgr.init_default_pon_resource_ranges(
+                onu_id_start_idx=self.device_info.onu_id_start,
+                onu_id_end_idx=self.device_info.onu_id_end,
+                alloc_id_start_idx=self.device_info.alloc_id_start,
+                alloc_id_end_idx=self.device_info.alloc_id_end,
+                gemport_id_start_idx=self.device_info.gemport_id_start,
+                gemport_id_end_idx=self.device_info.gemport_id_end,
+                num_of_pon_ports=self.device_info.pon_ports,
+                intf_ids=self.device_info.intf_ids
+            )
+
+        # After we have initialized resource ranges, initialize the
+        # resource pools accordingly.
+        self.resource_mgr.init_device_resource_pool()
+
+    def get_current_gemport_ids_for_onu(self, pon_intf_onu_id):
+        pon_intf_id = pon_intf_onu_id[0]
+        return self.resource_managers[pon_intf_id].get_current_gemport_ids_for_onu(pon_intf_onu_id)
+
+    def get_current_alloc_ids_for_onu(self, pon_intf_onu_id):
+        pon_intf_id = pon_intf_onu_id[0]
+        alloc_ids = self.resource_managers[pon_intf_id].get_current_alloc_ids_for_onu(pon_intf_onu_id)
+        if alloc_ids is None:
+            return None
+        # We support only one tcont at the moment
+        return alloc_ids[0]
+
+    def update_gemports_ponport_to_onu_map_on_kv_store(self, gemport_list, pon_port, onu_id, uni_id):
+        for gemport in gemport_list:
+            pon_intf_gemport = (pon_port, gemport)
+            # This information is used when packet_indication is received and
+            # we need to derive the ONU Id for which the packet arrived based
+            # on the pon_intf and gemport available in the packet_indication
+            # self.kv_store[str(pon_intf_gemport)] = ' '.join(map(str, (onu_id, uni_id)))
+            self.kv_store.put(self._make_path(str(pon_intf_gemport)), ' '.join(map(str, (onu_id, uni_id)))
+
+    def get_onu_uni_from_ponport_gemport(self, pon_port, gemport):
+        pon_intf_gemport = (pon_port, gemport)
+        #return tuple(map(int, self.kv_store[str(pon_intf_gemport)].split(' ')))
+        return tuple(map(int, self.kv_store.get(self._make_path(str(pon_intf_gemport))).split(' ')))
+
+    def get_flow_id(self, pon_intf_id, onu_id, uni_id, flow_store_cookie, flow_category=None):
+        pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+        try:
+            flow_ids = self.resource_managers[pon_intf_id]. \
+                get_current_flow_ids_for_onu(pon_intf_onu_id)
+            if flow_ids is not None:
+                for flow_id in flow_ids:
+                    flows = self.get_flow_id_info(pon_intf_id, onu_id, uni_id, flow_id)
+                    assert (isinstance(flows, list))
+                    for flow in flows:
+
+                        if flow_category is not None and \
+                                'flow_category' in flow and \
+                                flow['flow_category'] == flow_category:
+                            return flow_id
+                        if flow['flow_store_cookie'] == flow_store_cookie:
+                            return flow_id
+        except Exception as e:
+            self.log.error("error-retrieving-flow-info", e=e)
+
+        flow_id = self.resource_managers[pon_intf_id].get_resource_id(
+            pon_intf_onu_id[0], PONResourceManager.FLOW_ID)
+        if flow_id is not None:
+            self.resource_managers[pon_intf_id].update_flow_id_for_onu(
+                pon_intf_onu_id, flow_id
+            )
+
+        return flow_id
+
+    def get_flow_id_info(self, pon_intf_id, onu_id, uni_id, flow_id):
+        pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+        return self.resource_managers[pon_intf_id].get_flow_id_info(pon_intf_onu_id, flow_id)
+
+    def get_current_flow_ids_for_uni(self, pon_intf_id, onu_id, uni_id):
+        pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+        return self.resource_managers[pon_intf_id].get_current_flow_ids_for_onu(pon_intf_onu_id)
+
+    def update_flow_id_info_for_uni(self, pon_intf_id, onu_id, uni_id, flow_id, flow_data):
+        pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+        return self.resource_managers[pon_intf_id].update_flow_id_info_for_onu(
+            pon_intf_onu_id, flow_id, flow_data)
\ No newline at end of file
diff --git a/adapters/adtran_olt/resources/adtran_resource_manager.py b/adapters/adtran_olt/resources/adtran_resource_manager.py
new file mode 100644
index 0000000..9f2a0a4
--- /dev/null
+++ b/adapters/adtran_olt/resources/adtran_resource_manager.py
@@ -0,0 +1,358 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Resource Manager will be unique for each OLT device.
+
+It exposes APIs to create/free alloc_ids/onu_ids/gemport_ids. Resource Manager
+uses a KV store in backend to ensure resiliency of the data.
+"""
+from bitstring import BitArray
+import json
+from pyvoltha.adapters.common.pon_resource_manager.resource_manager import PONResourceManager
+import adtranolt_platform as platform
+
+
+class AdtranPONResourceManager(PONResourceManager):
+    """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
+
+    # Constants for internal usage.
+    ONU_MAP = 'onu_map'
+
+    def init_device_resource_pool(self):
+        """
+        Initialize resource pool for all PON ports.
+        """
+        for pon_id in self.intf_ids:
+            self.init_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.ONU_ID,
+                start_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_START_IDX],
+                end_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_END_IDX])
+
+            alloc_id_map = dict()
+            for onu_id in range(platform.MAX_ONUS_PER_PON):
+                alloc_id_map[onu_id] = [platform.mk_alloc_id(pon_id, onu_id, idx)
+                                        for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
+
+            self.init_resource_id_pool(pon_intf_id=pon_id,
+                                       resource_type=PONResourceManager.ALLOC_ID,
+                                       resource_map=alloc_id_map)
+
+            self.init_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.GEMPORT_ID,
+                start_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX],
+                end_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_END_IDX])
+
+    def clear_device_resource_pool(self):
+        """
+        Clear resource pool of all PON ports.
+        """
+        for pon_id in self.intf_ids:
+            self.clear_resource_id_pool(pon_intf_id=pon_id,
+                                        resource_type=PONResourceManager.ONU_ID)
+
+            self.clear_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.ALLOC_ID,
+            )
+
+            self.clear_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.GEMPORT_ID,
+            )
+            self.clear_resource_id_pool(
+                pon_intf_id=pon_id,
+                resource_type=PONResourceManager.FLOW_ID,
+            )
+
+    def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx=None,
+                              end_idx=None, resource_map=None):
+        """
+        Initialize Resource ID pool for a given Resource Type on a given PON Port
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param start_idx: start index for onu id pool
+        :param end_idx: end index for onu id pool
+        :param resource_map: (dict) Resource map if per-ONU specific
+        :return boolean: True if resource id pool initialized else false
+        """
+        status = False
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            # In case of adapter reboot and reconciliation resource in kv store
+            # checked for its presence if not kv store update happens
+            resource = self._get_resource(path)
+
+            if resource is not None:
+                self._log.info("Resource-already-present-in-store", path=path)
+                status = True
+
+            else:
+                if resource_map is None:
+                    resource = self._format_resource(pon_intf_id, start_idx, end_idx)
+                    self._log.info("Resource-initialized", path=path)
+
+                else:
+                    resource = self._format_map_resource(pon_intf_id, resource_map)
+
+                # Add resource as json in kv store.
+                status = self._kv_store.update_to_kv_store(path, resource)
+
+        except Exception as e:
+            self._log.exception("error-initializing-resource-pool", e=e)
+
+        return status
+
+    def _generate_next_id(self, resource, onu_id=None):
+        """
+        Generate unique id having OFFSET as start index.
+
+        :param resource: resource used to generate ID
+        :return int: generated id
+        """
+        if onu_id is not None:
+            resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+        pos = resource[PONResourceManager.POOL].find('0b0')
+        resource[PONResourceManager.POOL].set(1, pos)
+        return pos[0] + resource[PONResourceManager.START_IDX]
+
+    def _release_id(self, resource, unique_id, onu_id=None):
+        """
+        Release unique id having OFFSET as start index.
+
+        :param resource: resource used to release ID
+        :param unique_id: id need to be released
+        :param onu_id: ONU ID if unique per ONU
+        """
+        if onu_id is not None:
+            resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+        pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
+        resource[PONResourceManager.POOL].set(0, pos)
+
+    def get_resource_id(self, pon_intf_id, resource_type, onu_id=None, num_of_id=1):
+        """
+        Create alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param num_of_id: required number of ids
+        :param onu_id: ONU ID if unique per ONU  (Used for Alloc IDs)
+        :return list/int/None: list, int or None if resource type is
+                               alloc_id/gemport_id, onu_id or invalid type
+                               respectively
+        """
+        result = None
+
+        if num_of_id < 1:
+            self._log.error("invalid-num-of-resources-requested")
+            return result
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return result
+
+        try:
+            resource = self._get_resource(path, onu_id)
+            if resource is not None and \
+                    (resource_type == PONResourceManager.ONU_ID or
+                     resource_type == PONResourceManager.FLOW_ID):
+                result = self._generate_next_id(resource)
+
+            elif resource is not None and \
+                    resource_type == PONResourceManager.GEMPORT_ID:
+                if num_of_id == 1:
+                    result = self._generate_next_id(resource)
+                else:
+                    result = [self._generate_next_id(resource) for _ in range(num_of_id)]
+
+            elif resource is not None and \
+                    resource_type == PONResourceManager.ALLOC_ID:
+                if num_of_id == 1:
+                    result = self._generate_next_id(resource, onu_id)
+                else:
+                    result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
+            else:
+                raise Exception("get-resource-failed")
+
+            self._log.debug("Get-" + resource_type + "-success", result=result,
+                            path=path)
+            # Update resource in kv store
+            self._update_resource(path, resource, onu_id=onu_id)
+
+        except Exception as e:
+            self._log.exception("Get-" + resource_type + "-id-failed",
+                                path=path, e=e)
+        return result
+
+    def free_resource_id(self, pon_intf_id, resource_type, release_content, onu_id=None):
+        """
+        Release alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param release_content: required number of ids
+        :param onu_id: ONU ID if unique per ONU
+        :return boolean: True if all IDs in given release_content released
+                         else False
+        """
+        status = False
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            resource = self._get_resource(path, onu_id=onu_id)
+            if resource is None:
+                raise Exception("get-resource-for-free-failed")
+
+            if resource_type == PONResourceManager.ONU_ID:
+                self._release_id(resource, release_content)
+
+            elif resource_type == PONResourceManager.ALLOC_ID:
+                for content in release_content:
+                    self._release_id(resource, content)
+
+            elif resource_type == PONResourceManager.GEMPORT_ID:
+                for content in release_content:
+                    self._release_id(resource, content, onu_id)
+            else:
+                raise Exception("get-resource-for-free-failed")
+
+            self._log.debug("Free-" + resource_type + "-success", path=path)
+
+            # Update resource in kv store
+            status = self._update_resource(path, resource, onu_id=onu_id)
+
+        except Exception as e:
+            self._log.exception("Free-" + resource_type + "-failed",
+                                path=path, e=e)
+        return status
+
+    def _update_resource(self, path, resource, onu_id=None):
+        """
+        Update resource in resource kv store.
+
+        :param path: path to update resource
+        :param resource: resource need to be updated
+        :return boolean: True if resource updated in kv store else False
+        """
+        if 'alloc_id' in path.lower():
+            assert onu_id is not None
+            poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+            poolResource[PONResourceManager.POOL] = \
+                poolResource[PONResourceManager.POOL].bin
+        else:
+            resource[PONResourceManager.POOL] = \
+                resource[PONResourceManager.POOL].bin
+
+        return self._kv_store.update_to_kv_store(path, json.dumps(resource))
+
+    def _get_resource(self, path, onu_id=None):
+        """
+        Get resource from kv store.
+
+        :param path: path to get resource
+        :return: resource if resource present in kv store else None
+        """
+        # get resource from kv store
+        result = self._kv_store.get_from_kv_store(path)
+        if result is None:
+            return result
+
+        self._log.info("dumping-resource", result=result)
+        resource = result
+
+        if resource is not None:
+            # decode resource fetched from backend store to dictionary
+            resource = json.loads(resource)
+
+            if 'alloc_id' in path.lower():
+                assert onu_id is not None
+                poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+                poolResource[PONResourceManager.POOL] = \
+                    BitArray('0b' + poolResource[PONResourceManager.POOL])
+            else:
+                # resource pool in backend store stored as binary string whereas to
+                # access the pool to generate/release IDs it need to be converted
+                # as BitArray
+                resource[PONResourceManager.POOL] = \
+                    BitArray('0b' + resource[PONResourceManager.POOL])
+
+        return resource
+
+    def _format_resource(self, pon_intf_id, start_idx, end_idx):
+        """
+        Format resource as json.
+
+        :param pon_intf_id: OLT PON interface id
+        :param start_idx: start index for id pool
+        :param end_idx: end index for id pool
+        :return dictionary: resource formatted as dictionary
+        """
+        # Format resource as json to be stored in backend store
+        resource = dict()
+        resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+        resource[PONResourceManager.START_IDX] = start_idx
+        resource[PONResourceManager.END_IDX] = end_idx
+
+        # resource pool stored in backend store as binary string
+        resource[PONResourceManager.POOL] = BitArray(end_idx-start_idx).bin
+
+        return json.dumps(resource)
+
+    def _format_map_resource(self, pon_intf_id, resource_map):
+        """
+        Format resource as json.
+        # TODO: Refactor the resource BitArray to be just a list of the resources.
+        #       This is used to store available alloc-id's on a per-onu/pon basis
+        #       which in BitArray string form, is a 768 byte string for just 4 possible
+        #       alloc-IDs.  This equates to 1.57 MB of storage when you take into
+        #       account 128 ONUs and 16 PONs pre-provisioneed
+        :param pon_intf_id: OLT PON interface id
+        :param resource_map: (dict) ONU ID -> Scattered list of IDs
+        :return dictionary: resource formatted as dictionary
+        """
+        # Format resource as json to be stored in backend store
+        resource = dict()
+        resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+
+        onu_dict = dict()
+        for onu_id, resources in resource_map.items():
+            start_idx = min(resources)
+            end_idx = max(resources) + 1
+
+            onu_dict[onu_id] = {
+                PONResourceManager.START_IDX: start_idx,
+                PONResourceManager.END_IDX: end_idx,
+            }
+            # Set non-allowed values as taken
+            resource_map = BitArray(end_idx - start_idx)
+            not_available = {pos for pos in xrange(end_idx-start_idx)
+                             if pos + start_idx not in resources}
+            resource_map.set(True, not_available)
+            onu_dict[onu_id][PONResourceManager.POOL] = resource_map.bin
+
+        resource[AdtranPONResourceManager.ONU_MAP] = onu_dict
+        return json.dumps(resource)
diff --git a/adapters/adtran_olt/resources/adtranolt_platform.py b/adapters/adtran_olt/resources/adtranolt_platform.py
new file mode 100644
index 0000000..3ec7b81
--- /dev/null
+++ b/adapters/adtran_olt/resources/adtranolt_platform.py
@@ -0,0 +1,182 @@
+#
+# 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 pyvoltha.protos.device_pb2 import Port
+import pyvoltha.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
+=======================
+
+Alloc ID
+
+    Uniquely identifies a T-CONT
+    Ranges from 1024..16383 per ITU Standard
+    For Adtran, 1024..1919
+    Unique per PON interface
+
+     9   8 7        0
+    +-----+----------+
+    | idx |  onu_id  | + (Min Alloc ID)
+    +-----+----------+
+
+    onu id = 8 bit
+    Alloc index = 2 bits (max 4 TCONTs/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 = 2176                      # 2176..4222
+MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
+
+MAX_ONUS_PER_PON = 128
+MAX_TCONTS_PER_ONU = 4
+MAX_GEM_PORTS_PER_ONU = 16          # Hardware can handle more
+
+
+class adtran_platform(object):
+    def __init__(self):
+        pass
+
+    def mk_uni_port_num(self, intf_id, onu_id, uni_id=0):
+        return intf_id << 11 | onu_id << 4 | uni_id
+
+    def uni_id_from_uni_port(self, uni_port):
+        return uni_port & 0xF
+
+
+def mk_uni_port_num(intf_id, onu_id, uni_id=0):
+    """
+    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 | uni_id
+
+
+def uni_id_from_uni_port(uni_port):
+    return uni_port & 0xF
+
+
+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 + (idx << 8) + onu_id
+    return alloc_id
+
+
+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 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)