VOL-400: configurable alloc-id and gemport-id with min gemid=1024
change requests for previous patch set. Working on itest coverage
added itests for new parameters including explicit zero test
so that if zero is used it is same as if not used and it will auto
select the next available id (alloc-id or gemport-id)

Change-Id: I2c41e0a0811614aa9c1c77ad52a13b0dd504385b
diff --git a/cli/xpon.py b/cli/xpon.py
index 7e3d8c2..b6d5c03 100644
--- a/cli/xpon.py
+++ b/cli/xpon.py
@@ -1479,7 +1479,7 @@
         self.poutput(
 '''
 tcont [get | create | update | delete] [-n <name>] [-r <interface reference>]
-      [-t <traffic descriptor profile reference>]
+      [-t <traffic descriptor profile reference>] [-a alloc-id ]
 
 get:    displays existing tconts
         Required flags: None
@@ -1495,10 +1495,12 @@
 -n: <string> name of tcont.
 -r: <string> reference to vont ani interface.
 -t: <string> reference to an existing traffic descriptor profile.
+-a: <int>    allocation ID (alloc-id) for the TCONT. If not provided, xPON
+             Manager will provide the alloc-id for the TCONT.
 
 Example:
 
-tcont create -n "TCont 1" -r "Golden User" -t "TDP 1"
+tcont create -n "TCont 1" -r "Golden User" -t "TDP 1" -a 1024
 '''
         )
 
@@ -1512,6 +1514,8 @@
                     dest='traffic_descriptor_profile_ref', type='string',
                     help='reference to an existing traffic descriptor profile',
                     default=None),
+        make_option('-a', '--alloc_id', action='store', dest='alloc_id',
+                    type='int', help='alloc-id', default=None),
     ])
 
     def do_tcont(self, line, opts):
@@ -1545,6 +1549,8 @@
             if opts.traffic_descriptor_profile_ref:
                 tcont.traffic_descriptor_profile_ref = \
                     opts.traffic_descriptor_profile_ref
+            if opts.alloc_id:
+                tcont.alloc_id = opts.alloc_id
             if line.strip() == "create":
                 stub.CreateTcontsConfigData(tcont)
             elif line.strip() == "update":
@@ -1585,10 +1591,11 @@
 -t: <string> tcont reference that is for the purpose of upstream scheduling in
              the ONU, a gemport needs to refer to the tcont into which it feeds
              upstream traffic.
+-g: <int>    gemport ID. If not provided, xPON Manager will provide the gemport ID.
 
 Example:
 
-gem_port create -n "GEMPORT 1" -r "Enet UNI 1" -c 0 -a true -t "TCont 1"
+gem_port create -n "GEMPORT 1" -r "Enet UNI 1" -c 0 -a true -t "TCont 1" -g 2044
 '''
         )
 
@@ -1609,6 +1616,8 @@
                     type='string',
                     help='tcont reference for purpose of us scheduling in ONU',
                     default=None),
+        make_option('-g', '--gemport_id', action='store', dest='gemport_id',
+                    type='int', help='GEMPORT ID', default=None),
     ])
 
     def do_gem_port(self, line, opts):
@@ -1656,6 +1665,8 @@
                     return
             if opts.tcont_ref:
                 gemport.tcont_ref = opts.tcont_ref
+            if opts.gemport_id:
+                gemport.gemport_id = opts.gemport_id
             if line.strip() == "create":
                 stub.CreateGemportsConfigData(gemport)
             elif line.strip() == "update":
diff --git a/common/utils/indexpool.py b/common/utils/indexpool.py
index 24f57ff..d85d1ef 100644
--- a/common/utils/indexpool.py
+++ b/common/utils/indexpool.py
@@ -18,6 +18,21 @@
             log.info("exception-fail-to-allocate-id-all-bits-in-use")
             return None
 
+    def allocate(self, index):
+        try:
+            _pos = index - self.offset
+            if not (0 <= _pos < self.max_entries):
+                log.info("{}-out-of-range".format(index))
+                return None
+            if self.indices[_pos]:
+                log.info("{}-is-already-allocated".format(index))
+                return None
+            self.indices.set(1, _pos)
+            return index
+
+        except IndexError:
+            return None
+
     def release(self, index):
         index -= self.offset
         _pos = (index,)
diff --git a/tests/itests/voltha/test_voltha_xpon.py b/tests/itests/voltha/test_voltha_xpon.py
index 2413457..39dcdfc 100644
--- a/tests/itests/voltha/test_voltha_xpon.py
+++ b/tests/itests/voltha/test_voltha_xpon.py
@@ -170,6 +170,26 @@
             }
         }
     },
+    {'tcont-add-with-alloc-id': {
+        'pb2': tcont.TcontsConfigData(),
+        'rpc': {
+            "interface_reference": "Golden User",
+            "traffic_descriptor_profile_ref": "TDP 1",
+            "name": "TCont 2",
+            "alloc_id": 1234
+            }
+        }
+    },
+    {'tcont-add-with-alloc-id-zero': {
+        'pb2': tcont.TcontsConfigData(),
+        'rpc': {
+            "interface_reference": "Golden User",
+            "traffic_descriptor_profile_ref": "TDP 1",
+            "name": "TCont 3",
+            "alloc_id": 0
+            }
+        }
+    },
     {'gemport-add': {
         'pb2': gemport.GemportsConfigData(),
         'rpc': {
@@ -180,6 +200,30 @@
             "tcont_ref": "TCont 1",
             }
         }
+    },
+    {'gemport-add-with-gemport-id': {
+        'pb2': gemport.GemportsConfigData(),
+        'rpc': {
+            "aes_indicator": True,
+            "name": "GEMPORT 2",
+            "traffic_class": 0,
+            "itf_ref": "Enet UNI 1",
+            "tcont_ref": "TCont 2",
+            "gemport_id": 2345
+            }
+        }
+    },
+    {'gemport-add-with-gemport-id-zero': {
+        'pb2': gemport.GemportsConfigData(),
+        'rpc': {
+            "aes_indicator": True,
+            "name": "GEMPORT 2",
+            "traffic_class": 0,
+            "itf_ref": "Enet UNI 1",
+            "tcont_ref": "TCont 2",
+            "gemport_id": 0
+            }
+        }
     }
 ]
 
diff --git a/voltha/core/xpon_handler.py b/voltha/core/xpon_handler.py
index c102c1a..babaa7c 100644
--- a/voltha/core/xpon_handler.py
+++ b/voltha/core/xpon_handler.py
@@ -915,9 +915,16 @@
             assert self.validate_interface(request, context)
             cg_name = self.extract_channel_group_from_request(request,
                         'v_ont_anis', request.interface_reference)
-            _id = self.cg_dict[cg_name]['alloc_id'].get_next()
-            assert _id != None
-            request.alloc_id = _id
+            if request.alloc_id == 0:
+                _id = self.cg_dict[cg_name]['alloc_id'].get_next()
+                assert _id is not None, \
+                    'Fail to allocate id for TCont'
+                request.alloc_id = _id
+            else:
+                _id = self.cg_dict[cg_name]['alloc_id'].allocate(request.alloc_id)
+                assert _id == request.alloc_id, \
+                    'Fail to allocate id for TCont'
+
             log.debug('creating-tcont', name=request.name)
             self.root.add('/tconts', request)
             return Empty()
@@ -1012,10 +1019,16 @@
             assert self.validate_interface(request, context)
             cg_name = self.extract_channel_group_from_request(request,
                         'v_enets', request.itf_ref)
-            _id = self.cg_dict[cg_name]['gemport_id'].get_next()
-            assert _id != None, \
-                'Fail to allocate id for CemPort'
-            request.gemport_id = _id
+            if request.gemport_id == 0:
+                _id = self.cg_dict[cg_name]['gemport_id'].get_next()
+                assert _id is not None, \
+                    'Fail to allocate id for GemPort'
+                request.gemport_id = _id
+            else:
+                _id = self.cg_dict[cg_name]['gemport_id'].allocate(request.gemport_id)
+                assert _id == request.gemport_id, \
+                    'Fail to allocate id for GemPort'
+
             log.debug('creating-gemport', name=request.name)
             self.root.add('/gemports', request)
             return Empty()
diff --git a/voltha/protos/bbf_fiber_gemport_body.proto b/voltha/protos/bbf_fiber_gemport_body.proto
index b78d84d..b08af06 100644
--- a/voltha/protos/bbf_fiber_gemport_body.proto
+++ b/voltha/protos/bbf_fiber_gemport_body.proto
@@ -10,7 +10,7 @@
     uint32 traffic_class = 4;
     bool aes_indicator = 5;
     string tcont_ref = 6;
-    uint32 gemport_id = 7 [(voltha.access) = READ_ONLY];
+    uint32 gemport_id = 7;
 }
 message GemportsOperData {
     string id = 1 [(voltha.access) = READ_ONLY];
diff --git a/voltha/protos/bbf_fiber_tcont_body.proto b/voltha/protos/bbf_fiber_tcont_body.proto
index 03f42b0..2c24e32 100644
--- a/voltha/protos/bbf_fiber_tcont_body.proto
+++ b/voltha/protos/bbf_fiber_tcont_body.proto
@@ -8,7 +8,7 @@
     string name = 2;
     string interface_reference = 3;
     string traffic_descriptor_profile_ref = 4;
-    uint32 alloc_id = 5 [(voltha.access) = READ_ONLY];
+    uint32 alloc_id = 5;
 }
 message TcontsOperData {
     string id = 1 [(voltha.access) = READ_ONLY];