VOL-1289 - Update Resource Manager to support per-interface ranges

Updated Resource Manager and OpenOLT to support constructs for managing
resource pools by 1) each interface, 2) by all interfaces that share
a common technology, and 3) by all interfaces on the board.

The OpenOLT agent now supports returning, via DeviceInfo, a list of
1 or more range specifications. Each range corresponds to a unique
technology and identifies which interfaces on the board use that
technology and the start/end specifications for each resource type,
e.g. resource "pool". Options are provided for each pool to
independently be configured for (1) pool-per-interface, (2) pool-
per-technology, or (3) pool-per-device, a.k.a global.

A separate Resource Manager instance is used for each technology
range specification that manages (1) and (2) options. For (3),
one of the Resource Managers (arbitrarily chosen) is designated
as the "global" Resource Manager that is delegated to from
the subordinate RMs for those pools configured for (3) pool-per-
device.

For all pools, the Tech Profile is first read, if present. Then,
the device's technology range specification is overlayed to form a
subset of overlapping ranges. Then, globally-shared pools overalyed
again to form the overlapping ranges amongst all learned DeviceInfo
pools and TechProfile ranges.

Presently, behavior is undefined if no overlapping range exists
between TP and DevInfo ranges.

Backwards compatibility with OpenOLT agent drivers that only
support the original, single technology interface by synthesizing
a single range encompassing all interfaces with the start/end
specifications. In this case, the EdgeCore/BAL limitation for
resource sharing is set for Alloc ID and GEM Port ID.

Change-Id: I507ac013a114950dc0155b98a38406a42e2fba87
diff --git a/.gitignore b/.gitignore
index a315b07..383aa18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,5 +88,5 @@
 coverage.xml
 nosetests.xml
 
-# openolt repo
+# OpenOLT repo
 voltha/adapters/openolt/openolt
diff --git a/common/pon_resource_manager/resource_manager.py b/common/pon_resource_manager/resource_manager.py
index 17b2871..ebe3995 100644
--- a/common/pon_resource_manager/resource_manager.py
+++ b/common/pon_resource_manager/resource_manager.py
@@ -66,12 +66,15 @@
     '''
     # constants used as keys to reference the resource range parameters from
     # and external KV store.
-    ONU_START_IDX = "onu_id_start"
-    ONU_END_IDX = "onu_id_end"
+    ONU_ID_START_IDX = "onu_id_start"
+    ONU_ID_END_IDX = "onu_id_end"
+    ONU_ID_SHARED_IDX = "onu_id_shared"
     ALLOC_ID_START_IDX = "alloc_id_start"
     ALLOC_ID_END_IDX = "alloc_id_end"
-    GEM_PORT_ID_START_IDX = "gemport_id_start"
-    GEM_PORT_ID_END_IDX = "gemport_id_end"
+    ALLOC_ID_SHARED_IDX = "alloc_id_shared"
+    GEMPORT_ID_START_IDX = "gemport_id_start"
+    GEMPORT_ID_END_IDX = "gemport_id_end"
+    GEMPORT_ID_SHARED_IDX = "gemport_id_shared"
     NUM_OF_PON_PORT = "pon_ports"
 
     # PON Resource range configuration on the KV store.
@@ -124,11 +127,29 @@
             self.host = host
             self.port = port
             self.olt_vendor = None
+
             self._kv_store = ResourceKvStore(technology, device_id, backend,
                                              host, port)
+
             # Below attribute, pon_resource_ranges, should be initialized
             # by reading from KV store.
             self.pon_resource_ranges = dict()
+            self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX] = None
+            self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX] = None
+            self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX] = None
+
+            self.shared_resource_mgrs = dict()
+            self.shared_resource_mgrs[PONResourceManager.ONU_ID_SHARED_IDX] = None
+            self.shared_resource_mgrs[PONResourceManager.ALLOC_ID_SHARED_IDX] = None
+            self.shared_resource_mgrs[PONResourceManager.GEMPORT_ID_SHARED_IDX] = None
+
+            self.shared_idx_by_type = dict()
+            self.shared_idx_by_type[PONResourceManager.ONU_ID] = PONResourceManager.ONU_ID_SHARED_IDX
+            self.shared_idx_by_type[PONResourceManager.ALLOC_ID] = PONResourceManager.ALLOC_ID_SHARED_IDX
+            self.shared_idx_by_type[PONResourceManager.GEMPORT_ID] = PONResourceManager.GEMPORT_ID_SHARED_IDX
+
+	    self.intf_ids = None
+
         except Exception as e:
             self._log.exception("exception-in-init")
             raise Exception(e)
@@ -169,95 +190,162 @@
                                 e=e)
         return False
 
-    def init_default_pon_resource_ranges(self, onu_start_idx=1,
-                                         onu_end_idx=127,
+
+    def update_range_(self, start_idx, start, end_idx, end, shared_idx, shared_pool_id, shared_resource_mgr):
+        if (start is not None) and \
+           (start_idx not in self.pon_resource_ranges or self.pon_resource_ranges[start_idx] < start):
+              self.pon_resource_ranges[start_idx] = start
+        if (end is not None) and \
+           (end_idx not in self.pon_resource_ranges or self.pon_resource_ranges[end_idx] > end):
+              self.pon_resource_ranges[end_idx] = end
+        if (shared_pool_id is not None) and \
+           (shared_idx not in self.pon_resource_ranges or self.pon_resource_ranges[shared_idx] is None):
+            self.pon_resource_ranges[shared_idx] = shared_pool_id
+        if (shared_resource_mgr is not None) and \
+           (shared_idx not in self.shared_resource_mgrs or self.shared_resource_mgrs[shared_idx] is None):
+            self.shared_resource_mgrs[shared_idx] = shared_resource_mgr
+
+    def update_ranges(self,
+                      onu_id_start_idx=None,
+                      onu_id_end_idx=None,
+                      onu_id_shared_pool_id=None,
+                      onu_id_shared_resource_mgr=None,
+                      alloc_id_start_idx=None,
+                      alloc_id_end_idx=None,
+                      alloc_id_shared_pool_id=None,
+                      alloc_id_shared_resource_mgr=None,
+                      gemport_id_start_idx=None,
+                      gemport_id_end_idx=None,
+                      gemport_id_shared_pool_id=None,
+                      gemport_id_shared_resource_mgr=None):
+
+        self.update_range_(PONResourceManager.ONU_ID_START_IDX, onu_id_start_idx,
+                          PONResourceManager.ONU_ID_END_IDX, onu_id_end_idx,
+                          PONResourceManager.ONU_ID_SHARED_IDX, onu_id_shared_pool_id,
+                          onu_id_shared_resource_mgr)
+
+        self.update_range_(PONResourceManager.ALLOC_ID_START_IDX, alloc_id_start_idx,
+                          PONResourceManager.ALLOC_ID_END_IDX, alloc_id_end_idx,
+                          PONResourceManager.ALLOC_ID_SHARED_IDX, alloc_id_shared_pool_id,
+                          alloc_id_shared_resource_mgr)
+
+        self.update_range_(PONResourceManager.GEMPORT_ID_START_IDX, gemport_id_start_idx,
+                          PONResourceManager.GEMPORT_ID_END_IDX, gemport_id_end_idx,
+                          PONResourceManager.GEMPORT_ID_SHARED_IDX, gemport_id_shared_pool_id,
+                          gemport_id_shared_resource_mgr)
+
+    def init_default_pon_resource_ranges(self,
+                                         onu_id_start_idx=1,
+                                         onu_id_end_idx=127,
+                                         onu_id_shared_pool_id=None,
                                          alloc_id_start_idx=1024,
                                          alloc_id_end_idx=2816,
-                                         gem_port_id_start_idx=1024,
-                                         gem_port_id_end_idx=8960,
-                                         num_of_pon_ports=16):
+                                         alloc_id_shared_pool_id=None,
+                                         gemport_id_start_idx=1024,
+                                         gemport_id_end_idx=8960,
+                                         gemport_id_shared_pool_id=None,
+                                         num_of_pon_ports=16,
+                                         intf_ids=None):
         """
         Initialize default PON resource ranges
 
-        :param onu_start_idx: onu id start index
-        :param onu_end_idx: onu id end index
+        :param onu_id_start_idx: onu id start index
+        :param onu_id_end_idx: onu id end index
+        :param onu_id_shared_pool_id: pool idx for id shared by all intfs or None for no sharing
         :param alloc_id_start_idx: alloc id start index
         :param alloc_id_end_idx: alloc id end index
-        :param gem_port_id_start_idx: gemport id start index
-        :param gem_port_id_end_idx: gemport id end index
+        :param alloc_id_shared_pool_id: pool idx for alloc id shared by all intfs or None for no sharing
+        :param gemport_id_start_idx: gemport id start index
+        :param gemport_id_end_idx: gemport id end index
+        :param gemport_id_shared_pool_id: pool idx for gemport id shared by all intfs or None for no sharing
         :param num_of_pon_ports: number of PON ports
+        :param intf_ids: interfaces serviced by this manager
         """
         self._log.info("initialize-default-resource-range-values")
-        self.pon_resource_ranges[
-            PONResourceManager.ONU_START_IDX] = onu_start_idx
-        self.pon_resource_ranges[PONResourceManager.ONU_END_IDX] = onu_end_idx
-        self.pon_resource_ranges[
-            PONResourceManager.ALLOC_ID_START_IDX] = alloc_id_start_idx
-        self.pon_resource_ranges[
-            PONResourceManager.ALLOC_ID_END_IDX] = alloc_id_end_idx
-        self.pon_resource_ranges[
-            PONResourceManager.GEM_PORT_ID_START_IDX] = gem_port_id_start_idx
-        self.pon_resource_ranges[
-            PONResourceManager.GEM_PORT_ID_END_IDX] = gem_port_id_end_idx
-        self.pon_resource_ranges[
-            PONResourceManager.NUM_OF_PON_PORT] = num_of_pon_ports
+
+        self.update_ranges(onu_id_start_idx, onu_id_end_idx, onu_id_shared_pool_id, None,
+                           alloc_id_start_idx, alloc_id_end_idx, alloc_id_shared_pool_id, None,
+                           gemport_id_start_idx, gemport_id_end_idx, gemport_id_shared_pool_id, None)
+
+        if intf_ids is None:
+            intf_ids = range(0, num_of_pon_ports)
+
+        self.intf_ids = intf_ids
 
     def init_device_resource_pool(self):
         """
         Initialize resource pool for all PON ports.
         """
-        i = 0
-        while i < self.pon_resource_ranges[PONResourceManager.NUM_OF_PON_PORT]:
+
+        self._log.info("init-device-resource-pool", technology=self.technology,
+            pon_resource_ranges=self.pon_resource_ranges)
+
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
             self.init_resource_id_pool(
                 pon_intf_id=i,
                 resource_type=PONResourceManager.ONU_ID,
                 start_idx=self.pon_resource_ranges[
-                    PONResourceManager.ONU_START_IDX],
+                    PONResourceManager.ONU_ID_START_IDX],
                 end_idx=self.pon_resource_ranges[
-                    PONResourceManager.ONU_END_IDX])
+                    PONResourceManager.ONU_ID_END_IDX])
+            if shared_pool_id is not None: break
 
-            i += 1
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
+            self.init_resource_id_pool(
+                pon_intf_id=i,
+                resource_type=PONResourceManager.ALLOC_ID,
+                start_idx=self.pon_resource_ranges[
+                    PONResourceManager.ALLOC_ID_START_IDX],
+                end_idx=self.pon_resource_ranges[
+                    PONResourceManager.ALLOC_ID_END_IDX])
+            if shared_pool_id is not None: break
 
-        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
-        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
-        # for both the resource types. This may need to change later.
-        self.init_resource_id_pool(
-            pon_intf_id=0,
-            resource_type=PONResourceManager.ALLOC_ID,
-            start_idx=self.pon_resource_ranges[
-                PONResourceManager.ALLOC_ID_START_IDX],
-            end_idx=self.pon_resource_ranges[
-                PONResourceManager.ALLOC_ID_END_IDX])
-
-        self.init_resource_id_pool(
-            pon_intf_id=0,
-            resource_type=PONResourceManager.GEMPORT_ID,
-            start_idx=self.pon_resource_ranges[
-                PONResourceManager.GEM_PORT_ID_START_IDX],
-            end_idx=self.pon_resource_ranges[
-                PONResourceManager.GEM_PORT_ID_END_IDX])
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
+            self.init_resource_id_pool(
+                pon_intf_id=i,
+                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])
+            if shared_pool_id is not None: break
 
     def clear_device_resource_pool(self):
         """
         Clear resource pool of all PON ports.
         """
-        i = 0
-        while i < self.pon_resource_ranges[PONResourceManager.NUM_OF_PON_PORT]:
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
             self.clear_resource_id_pool(
                 pon_intf_id=i,
                 resource_type=PONResourceManager.ONU_ID,
             )
-            i += 1
+            if shared_pool_id is not None: break
 
-        self.clear_resource_id_pool(
-            pon_intf_id=0,
-            resource_type=PONResourceManager.ALLOC_ID,
-        )
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
+            self.clear_resource_id_pool(
+                pon_intf_id=i,
+                resource_type=PONResourceManager.ALLOC_ID,
+            )
+            if shared_pool_id is not None: break
 
-        self.clear_resource_id_pool(
-            pon_intf_id=0,
-            resource_type=PONResourceManager.GEMPORT_ID,
-        )
+        for i in self.intf_ids:
+            shared_pool_id = self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX]
+            if shared_pool_id is not None: i = shared_pool_id
+            self.clear_resource_id_pool(
+                pon_intf_id=i,
+                resource_type=PONResourceManager.GEMPORT_ID,
+            )
+            if shared_pool_id is not None: break
 
     def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx,
                               end_idx):
@@ -271,6 +359,13 @@
         :return boolean: True if resource id pool initialized else false
         """
         status = False
+
+        # delegate to the master instance if sharing enabled across instances
+        shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
+        if shared_resource_mgr is not None and shared_resource_mgr is not self:
+            return shared_resource_mgr.init_resource_id_pool(pon_intf_id, resource_type,
+                start_idx, end_idx)
+
         path = self._get_path(pon_intf_id, resource_type)
         if path is None:
             return status
@@ -311,13 +406,10 @@
         """
         result = None
 
-        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
-        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
-        # for both the resource types. This may need to change later.
-        # Override the incoming pon_intf_id to PON0
-        if resource_type == PONResourceManager.GEMPORT_ID or \
-                resource_type == PONResourceManager.ALLOC_ID:
-            pon_intf_id = 0
+        # delegate to the master instance if sharing enabled across instances
+        shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
+        if shared_resource_mgr is not None and shared_resource_mgr is not self:
+            return shared_resource_mgr.get_resource_id(pon_intf_id, resource_type)
 
         path = self._get_path(pon_intf_id, resource_type)
         if path is None:
@@ -360,13 +452,10 @@
         """
         status = False
 
-        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
-        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
-        # for both the resource types. This may need to change later.
-        # Override the incoming pon_intf_id to PON0
-        if resource_type == PONResourceManager.GEMPORT_ID or \
-                resource_type == PONResourceManager.ALLOC_ID:
-            pon_intf_id = 0
+        # delegate to the master instance if sharing enabled across instances
+        shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
+        if shared_resource_mgr is not None and shared_resource_mgr is not self:
+            return shared_resource_mgr.free_resource_id(pon_intf_id, resource_type)
 
         path = self._get_path(pon_intf_id, resource_type)
         if path is None:
@@ -401,6 +490,12 @@
 
         :return boolean: True if removed else False
         """
+
+        # delegate to the master instance if sharing enabled across instances
+        shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
+        if shared_resource_mgr is not None and shared_resource_mgr is not self:
+            return shared_resource_mgr.clear_resource_id_pool(pon_intf_id, resource_type)
+
         path = self._get_path(pon_intf_id, resource_type)
         if path is None:
             return False
@@ -532,7 +627,8 @@
         if self.extra_args and len(self.extra_args) > 0:
             parser = OltVendorArgumentParser(add_help=False)
             parser.add_argument('--olt_vendor', '-o', action='store',
-                                choices=['default', 'asfvolt16', 'cigolt24'],
+                                choices=['default', 'asfvolt16', 'cigolt24',
+                                'tlabvolt4', 'tlabvolt8', 'tlabvolt16', 'tlabvolt24'],
                                 default='default')
             try:
                 args = parser.parse_args(shlex.split(self.extra_args))
@@ -574,6 +670,10 @@
         :param resource_type: String to identify type of resource
         :return: path for given resource type
         """
+
+        shared_pool_id = self.pon_resource_ranges[self.shared_idx_by_type[resource_type]]
+        if shared_pool_id is not None: pon_intf_id = shared_pool_id
+
         path = None
         if resource_type == PONResourceManager.ONU_ID:
             path = self._get_onu_id_resource_path(pon_intf_id)
diff --git a/voltha/adapters/openolt/openolt_platform.py b/voltha/adapters/openolt/openolt_platform.py
index f6b6c2d..c62bf8b 100644
--- a/voltha/adapters/openolt/openolt_platform.py
+++ b/voltha/adapters/openolt/openolt_platform.py
@@ -20,33 +20,6 @@
 """
 Encoding of identifiers
 =======================
-GEM port ID
-
-    GEM port id is unique per PON port
-
-     10              3      0
-    +--+--------------+------+
-    |1 |     onu id   | GEM  |
-    |  |              | idx  |
-    +--+--------------+------+
-
-    GEM port id range (0, 1023) is reserved
-    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 0 to 4095
-    Unique per PON interface
-
-     12         6            0
-    +------------+------------+
-    |   onu id   | Alloc idx  |
-    +------------+------------+
-
-    onu id = 7 bits = 128 ONUs per PON
-    Alloc index = 6 bits = 64 GEM ports per ONU
 
 Flow id
 
@@ -112,20 +85,6 @@
         self.log = log
         self.device_info = device_info
 
-    def mk_alloc_id(self, intf_id, onu_id, idx=0):
-        # FIXME - driver should do prefixing 1 << 10 as it is Maple specific
-        # return 1<<10 | onu_id<<6 | idx
-        if(self.device_info.technology == "gpon"): # Will be replaced with resource manager
-            return 511 + intf_id * MAX_ONUS_PER_PON + onu_id
-        return 1023 + intf_id * MAX_ONUS_PER_PON + onu_id  # FIXME
-
-
-    def mk_gemport_id(self, intf_id, onu_id, idx=0):
-        return 1024 + (((MAX_ONUS_PER_PON * intf_id + onu_id - 1) * 7) + idx)
-
-    def onu_id_from_gemport_id(self, gemport_id):
-        return (((gemport_id - 1024) // 7) % MAX_ONUS_PER_PON) + 1
-
     def mk_uni_port_num(self, intf_id, onu_id):
         return intf_id << 11 | onu_id << 4
 
diff --git a/voltha/adapters/openolt/openolt_resource_manager.py b/voltha/adapters/openolt/openolt_resource_manager.py
index 64232d7..a59c7cd 100644
--- a/voltha/adapters/openolt/openolt_resource_manager.py
+++ b/voltha/adapters/openolt/openolt_resource_manager.py
@@ -20,7 +20,7 @@
 from voltha.registry import registry
 from voltha.core.config.config_backend import ConsulStore
 from voltha.core.config.config_backend import EtcdStore
-
+from voltha.adapters.openolt.protos import openolt_pb2
 
 class OpenOltResourceMgr(object):
     GEMPORT_IDS = "gemport_ids"
@@ -50,30 +50,68 @@
             self.log.error('Invalid-backend')
             raise Exception("Invalid-backend-for-kv-store")
 
-        self.resource_mgr = PONResourceManager(
-            self.device_info.technology,
-            self.extra_args,
-            self.device_id, self.args.backend,
-            host, port
-        )
+        ranges = dict()
+        resource_mgrs_by_tech = dict()
+        self.resource_mgrs = dict()
 
-        # Flag to indicate whether information fetched from device should
-        # be used to intialize PON Resource Ranges
-        self.use_device_info = False
+        # If a legacy driver returns protobuf without any ranges,s synthesize one from
+        # the legacy global per-device informaiton. This, in theory, is temporary until
+        # the legacy drivers are upgrade to support pool ranges.
+        if len(self.device_info.ranges) == 0:
+            arange = self.device_info.ranges.add()
+            arange.technology = self.device_info.technology
+            arange.intf_ids.extend(range(0, device_info.pon_ports))
 
-        self.initialize_device_resource_range_and_pool()
+            pool = arange.pools.add()
+            pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ONU_ID
+            pool.start = self.device_info.onu_id_start
+            pool.end = self.device_info.onu_id_end
+            pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.DEDICATED_PER_INTF
+
+            pool = arange.pools.add()
+            pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ALLOC_ID
+            pool.start = self.device_info.alloc_id_start
+            pool.end = self.device_info.alloc_id_end
+            pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH
+
+            pool = arange.pools.add()
+            pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.GEMPORT_ID
+            pool.start = self.device_info.gemport_id_start
+            pool.end = self.device_info.gemport_id_end
+            pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH
+
+        # Create a separate Resource Manager instance for each range. This assumes that
+        # each technology is represented by only a single range
+        global_resource_mgr = None
+        for arange in self.device_info.ranges:
+            technology = arange.technology
+            self.log.info("device-info", technology=technology)
+            ranges[technology] = arange
+            resource_mgr = PONResourceManager(technology,
+                self.extra_args, self.device_id, self.args.backend, host, port)
+            resource_mgrs_by_tech[technology] = resource_mgr
+            if global_resource_mgr is None: global_resource_mgr = resource_mgr
+            for intf_id in arange.intf_ids:
+                self.resource_mgrs[intf_id] = resource_mgrs_by_tech[technology]
+            self.initialize_device_resource_range_and_pool(resource_mgr, global_resource_mgr, arange)
+
+        # After we have initialized resource ranges, initialize the
+        # resource pools accordingly.
+        for technology, resource_mgr in resource_mgrs_by_tech.iteritems():
+            resource_mgr.init_device_resource_pool()
 
     def __del__(self):
         self.log.info("clearing-device-resource-pool")
-        self.resource_mgr.clear_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(
+        onu_id = self.resource_mgrs[pon_intf_id].get_resource_id(
             pon_intf_id, PONResourceManager.ONU_ID, 1)
 
         if onu_id is not None:
             pon_intf_onu_id = (pon_intf_id, onu_id)
-            self.resource_mgr.init_resource_map(
+            self.resource_mgrs[pon_intf_id].init_resource_map(
                 pon_intf_onu_id)
 
         return onu_id
@@ -81,7 +119,7 @@
     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]
-        alloc_id_list = self.resource_mgr.get_current_alloc_ids_for_onu(
+        alloc_id_list = self.resource_mgrs[pon_intf].get_current_alloc_ids_for_onu(
             pon_intf_onu_id)
 
         if alloc_id_list and len(alloc_id_list) > 0:
@@ -90,7 +128,7 @@
             # ONU.
             return alloc_id_list[0]
 
-        alloc_id_list = self.resource_mgr.get_resource_id(
+        alloc_id_list = self.resource_mgrs[pon_intf].get_resource_id(
             pon_intf_id=pon_intf,
             resource_type=PONResourceManager.ALLOC_ID,
             num_of_id=1
@@ -101,7 +139,7 @@
 
         # 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,
+        self.resource_mgrs[pon_intf].update_alloc_ids_for_onu(pon_intf_onu_id,
                                                    alloc_id_list)
 
         # Since we request only one alloc id, we refer the 0th
@@ -115,7 +153,7 @@
         pon_intf = pon_intf_onu_id[0]
         onu_id = pon_intf_onu_id[1]
 
-        gemport_id_list = self.resource_mgr.get_current_gemport_ids_for_onu(
+        gemport_id_list = self.resource_mgrs[pon_intf].get_current_gemport_ids_for_onu(
             pon_intf_onu_id)
         if gemport_id_list and len(gemport_id_list) > 0:
             # Since we support only one gemport_id for the ONU at the moment,
@@ -123,7 +161,7 @@
             # ONU.
             return gemport_id_list[0]
 
-        gemport_id_list = self.resource_mgr.get_resource_id(
+        gemport_id_list = self.resource_mgrs[pon_intf].get_resource_id(
             pon_intf_id=pon_intf,
             resource_type=PONResourceManager.GEMPORT_ID,
             num_of_id=1
@@ -135,7 +173,7 @@
 
         # update the resource map on KV store with the list of gemport_id
         # allocated for the pon_intf_onu_id tuple
-        self.resource_mgr.update_gemport_ids_for_onu(pon_intf_onu_id,
+        self.resource_mgrs[pon_intf].update_gemport_ids_for_onu(pon_intf_onu_id,
                                                      gemport_id_list)
 
         # We currently use only one gemport
@@ -150,62 +188,123 @@
         return gemport
 
     def free_onu_id(self, pon_intf_id, onu_id):
-        result = self.resource_mgr.free_resource_id(
+        result = self.resource_mgrs[pon_intf_id].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(
+        self.resource_mgrs[pon_intf_id].remove_resource_map(
             pon_intf_onu_id)
 
     def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
 
-        alloc_ids = \
-            self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
         pon_intf_id = pon_intf_id_onu_id[0]
         onu_id = pon_intf_id_onu_id[1]
-        self.resource_mgr.free_resource_id(pon_intf_id,
+        alloc_ids = \
+            self.resource_mgrs[pon_intf_id].get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
+        self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
                                            PONResourceManager.ALLOC_ID,
                                            alloc_ids)
 
         gemport_ids = \
-            self.resource_mgr.get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
-        self.resource_mgr.free_resource_id(pon_intf_id,
+            self.resource_mgrs[pon_intf_id].get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
+        self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
                                            PONResourceManager.GEMPORT_ID,
                                            gemport_ids)
 
-        self.resource_mgr.free_resource_id(pon_intf_id,
+        self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
                                            PONResourceManager.ONU_ID,
                                            onu_id)
 
         # Clear resource map associated with (pon_intf_id, gemport_id) tuple.
-        self.resource_mgr.remove_resource_map(pon_intf_id_onu_id)
+        self.resource_mgrs[pon_intf_id].remove_resource_map(pon_intf_id_onu_id)
 
         # Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
         for gemport_id in gemport_ids:
             del self.kv_store[str((pon_intf_id, gemport_id))]
 
-    def initialize_device_resource_range_and_pool(self):
-        if not self.use_device_info:
-            status = self.resource_mgr.init_resource_ranges_from_kv_store()
-            if not status:
-                self.log.error("failed-to-load-resource-range-from-kv-store")
-                # When we have failed to read the PON Resource ranges from KV
-                # store, use the information fetched from device.
-                self.use_device_info = True
+    def initialize_device_resource_range_and_pool(self, resource_mgr, global_resource_mgr, arange):
+        self.log.info("resource-range-pool-init", technology=resource_mgr.technology)
 
-        if self.use_device_info:
-            self.log.info("using-device-info-to-init-pon-resource-ranges")
-            self.resource_mgr.init_default_pon_resource_ranges(
-                self.device_info.onu_id_start,
-                self.device_info.onu_id_end,
-                self.device_info.alloc_id_start,
-                self.device_info.alloc_id_end,
-                self.device_info.gemport_id_start,
-                self.device_info.gemport_id_end,
-                self.device_info.pon_ports
+        # first load from KV profiles
+        status = resource_mgr.init_resource_ranges_from_kv_store()
+        if not status:
+            self.log.info("failed-to-load-resource-range-from-kv-store", technology=resource_mgr.technology)
+
+        # Then apply device specific information. If KV doesn't exist
+        # or is broader than the device, the device's informationw ill
+        # dictate the range limits
+        self.log.info("using-device-info-to-init-pon-resource-ranges", technology=resource_mgr.technology)
+
+        onu_id_start = self.device_info.onu_id_start
+        onu_id_end = self.device_info.onu_id_end
+        onu_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.DEDICATED_PER_INTF
+        onu_id_shared_pool_id = None
+        alloc_id_start = self.device_info.alloc_id_start
+        alloc_id_end = self.device_info.alloc_id_end
+        alloc_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH # TODO EdgeCore/BAL limitation
+        alloc_id_shared_pool_id = None
+        gemport_id_start = self.device_info.gemport_id_start
+        gemport_id_end = self.device_info.gemport_id_end
+        gemport_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH # TODO EdgeCore/BAL limitation
+        gemport_id_shared_pool_id = None
+
+        global_pool_id = 0
+        for first_intf_pool_id in arange.intf_ids: break;
+
+        for pool in arange.pools:
+            shared_pool_id = global_pool_id if pool.sharing == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH else \
+                   first_intf_pool_id if  pool.sharing == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_SAME_TECH else \
+                   None
+
+            if pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ONU_ID:
+                onu_id_start = pool.start
+                onu_id_end = pool.end
+                onu_id_shared = pool.sharing
+                onu_id_shared_pool_id = shared_pool_id
+            elif pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ALLOC_ID:
+                alloc_id_start = pool.start
+                alloc_id_end = pool.end
+                alloc_id_shared = pool.sharing
+                alloc_id_shared_pool_id = shared_pool_id
+            elif pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.GEMPORT_ID:
+                gemport_id_start = pool.start
+                gemport_id_end = pool.end
+                gemport_id_shared = pool.sharing
+                gemport_id_shared_pool_id = shared_pool_id
+
+        self.log.info("device-info-init", technology=arange.technology,
+                onu_id_start=onu_id_start, onu_id_end=onu_id_end, onu_id_shared_pool_id=onu_id_shared_pool_id,
+                alloc_id_start=alloc_id_start, alloc_id_end=alloc_id_end, alloc_id_shared_pool_id=alloc_id_shared_pool_id,
+                gemport_id_start=gemport_id_start, gemport_id_end=gemport_id_end, gemport_id_shared_pool_id=gemport_id_shared_pool_id,
+                intf_ids=arange.intf_ids)
+
+        resource_mgr.init_default_pon_resource_ranges(
+                onu_id_start_idx=onu_id_start,
+                onu_id_end_idx=onu_id_end,
+                onu_id_shared_pool_id=onu_id_shared_pool_id,
+                alloc_id_start_idx=alloc_id_start,
+                alloc_id_end_idx=alloc_id_end,
+                alloc_id_shared_pool_id=alloc_id_shared_pool_id,
+                gemport_id_start_idx=gemport_id_start,
+                gemport_id_end_idx=gemport_id_end,
+                gemport_id_shared_pool_id=gemport_id_shared_pool_id,
+                num_of_pon_ports=self.device_info.pon_ports,
+                intf_ids=arange.intf_ids
             )
 
-        # After we have initialized resource ranges, initialize the
-        # resource pools accordingly.
-        self.resource_mgr.init_device_resource_pool()
+        # For global sharing, make sure to refresh both local and global resource manager instances' range
+        if global_resource_mgr is not self:
+            if onu_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
+                global_resource_mgr.update_ranges(onu_id_start_idx=onu_id_start, onu_id_end_idx=onu_id_end)
+                resource_mgr.update_ranges(onu_id_start_idx=onu_id_start, onu_id_end_idx=onu_id_end,
+                    onu_id_shared_resource_mgr=global_resource_mgr)
 
+            if alloc_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
+                global_resource_mgr.update_ranges(alloc_id_start_idx=alloc_id_start, alloc_id_end_idx=alloc_id_end)
+                resource_mgr.update_ranges(alloc_id_start_idx=alloc_id_start, alloc_id_end_idx=alloc_id_end,
+                    alloc_id_shared_resource_mgr=global_resource_mgr)
+
+            if gemport_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
+                global_resource_mgr.update_ranges(gemport_id_start_idx=gemport_id_start, gemport_id_end_idx=gemport_id_end)
+                resource_mgr.update_ranges(gemport_id_start_idx=gemport_id_start, gemport_id_end_idx=gemport_id_end,
+                    gemport_id_shared_resource_mgr=global_resource_mgr)
diff --git a/voltha/adapters/openolt/protos/openolt.proto b/voltha/adapters/openolt/protos/openolt.proto
index 60bab72..ac9093b 100644
--- a/voltha/adapters/openolt/protos/openolt.proto
+++ b/voltha/adapters/openolt/protos/openolt.proto
@@ -278,7 +278,7 @@
 
             enum SharingType {
                 DEDICATED_PER_INTF = 0;
-                SHARED_BY_ALL_INTF_ALL_TECH = 1; // Shared across all interfaces in all technologiesi in all ranges
+                SHARED_BY_ALL_INTF_ALL_TECH = 1; // Shared across all interfaces in all technologies in all ranges
                 SHARED_BY_ALL_INTF_SAME_TECH = 2; // Shared across all interfaces of the same technology used in this range
             }