blob: 9f2a0a436aac412207c12164f6e11f76e9be95e4 [file] [log] [blame]
Chip Bolingf5af85d2019-02-12 15:36:17 -06001#
2# Copyright 2018 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Resource Manager will be unique for each OLT device.
19
20It exposes APIs to create/free alloc_ids/onu_ids/gemport_ids. Resource Manager
21uses a KV store in backend to ensure resiliency of the data.
22"""
23from bitstring import BitArray
24import json
25from pyvoltha.adapters.common.pon_resource_manager.resource_manager import PONResourceManager
26import adtranolt_platform as platform
27
28
29class AdtranPONResourceManager(PONResourceManager):
30 """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
31
32 # Constants for internal usage.
33 ONU_MAP = 'onu_map'
34
35 def init_device_resource_pool(self):
36 """
37 Initialize resource pool for all PON ports.
38 """
39 for pon_id in self.intf_ids:
40 self.init_resource_id_pool(
41 pon_intf_id=pon_id,
42 resource_type=PONResourceManager.ONU_ID,
43 start_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_START_IDX],
44 end_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_END_IDX])
45
46 alloc_id_map = dict()
47 for onu_id in range(platform.MAX_ONUS_PER_PON):
48 alloc_id_map[onu_id] = [platform.mk_alloc_id(pon_id, onu_id, idx)
49 for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
50
51 self.init_resource_id_pool(pon_intf_id=pon_id,
52 resource_type=PONResourceManager.ALLOC_ID,
53 resource_map=alloc_id_map)
54
55 self.init_resource_id_pool(
56 pon_intf_id=pon_id,
57 resource_type=PONResourceManager.GEMPORT_ID,
58 start_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX],
59 end_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_END_IDX])
60
61 def clear_device_resource_pool(self):
62 """
63 Clear resource pool of all PON ports.
64 """
65 for pon_id in self.intf_ids:
66 self.clear_resource_id_pool(pon_intf_id=pon_id,
67 resource_type=PONResourceManager.ONU_ID)
68
69 self.clear_resource_id_pool(
70 pon_intf_id=pon_id,
71 resource_type=PONResourceManager.ALLOC_ID,
72 )
73
74 self.clear_resource_id_pool(
75 pon_intf_id=pon_id,
76 resource_type=PONResourceManager.GEMPORT_ID,
77 )
78 self.clear_resource_id_pool(
79 pon_intf_id=pon_id,
80 resource_type=PONResourceManager.FLOW_ID,
81 )
82
83 def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx=None,
84 end_idx=None, resource_map=None):
85 """
86 Initialize Resource ID pool for a given Resource Type on a given PON Port
87
88 :param pon_intf_id: OLT PON interface id
89 :param resource_type: String to identify type of resource
90 :param start_idx: start index for onu id pool
91 :param end_idx: end index for onu id pool
92 :param resource_map: (dict) Resource map if per-ONU specific
93 :return boolean: True if resource id pool initialized else false
94 """
95 status = False
96 path = self._get_path(pon_intf_id, resource_type)
97 if path is None:
98 return status
99
100 try:
101 # In case of adapter reboot and reconciliation resource in kv store
102 # checked for its presence if not kv store update happens
103 resource = self._get_resource(path)
104
105 if resource is not None:
106 self._log.info("Resource-already-present-in-store", path=path)
107 status = True
108
109 else:
110 if resource_map is None:
111 resource = self._format_resource(pon_intf_id, start_idx, end_idx)
112 self._log.info("Resource-initialized", path=path)
113
114 else:
115 resource = self._format_map_resource(pon_intf_id, resource_map)
116
117 # Add resource as json in kv store.
118 status = self._kv_store.update_to_kv_store(path, resource)
119
120 except Exception as e:
121 self._log.exception("error-initializing-resource-pool", e=e)
122
123 return status
124
125 def _generate_next_id(self, resource, onu_id=None):
126 """
127 Generate unique id having OFFSET as start index.
128
129 :param resource: resource used to generate ID
130 :return int: generated id
131 """
132 if onu_id is not None:
133 resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
134
135 pos = resource[PONResourceManager.POOL].find('0b0')
136 resource[PONResourceManager.POOL].set(1, pos)
137 return pos[0] + resource[PONResourceManager.START_IDX]
138
139 def _release_id(self, resource, unique_id, onu_id=None):
140 """
141 Release unique id having OFFSET as start index.
142
143 :param resource: resource used to release ID
144 :param unique_id: id need to be released
145 :param onu_id: ONU ID if unique per ONU
146 """
147 if onu_id is not None:
148 resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
149
150 pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
151 resource[PONResourceManager.POOL].set(0, pos)
152
153 def get_resource_id(self, pon_intf_id, resource_type, onu_id=None, num_of_id=1):
154 """
155 Create alloc/gemport/onu id for given OLT PON interface.
156
157 :param pon_intf_id: OLT PON interface id
158 :param resource_type: String to identify type of resource
159 :param num_of_id: required number of ids
160 :param onu_id: ONU ID if unique per ONU (Used for Alloc IDs)
161 :return list/int/None: list, int or None if resource type is
162 alloc_id/gemport_id, onu_id or invalid type
163 respectively
164 """
165 result = None
166
167 if num_of_id < 1:
168 self._log.error("invalid-num-of-resources-requested")
169 return result
170
171 path = self._get_path(pon_intf_id, resource_type)
172 if path is None:
173 return result
174
175 try:
176 resource = self._get_resource(path, onu_id)
177 if resource is not None and \
178 (resource_type == PONResourceManager.ONU_ID or
179 resource_type == PONResourceManager.FLOW_ID):
180 result = self._generate_next_id(resource)
181
182 elif resource is not None and \
183 resource_type == PONResourceManager.GEMPORT_ID:
184 if num_of_id == 1:
185 result = self._generate_next_id(resource)
186 else:
187 result = [self._generate_next_id(resource) for _ in range(num_of_id)]
188
189 elif resource is not None and \
190 resource_type == PONResourceManager.ALLOC_ID:
191 if num_of_id == 1:
192 result = self._generate_next_id(resource, onu_id)
193 else:
194 result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
195 else:
196 raise Exception("get-resource-failed")
197
198 self._log.debug("Get-" + resource_type + "-success", result=result,
199 path=path)
200 # Update resource in kv store
201 self._update_resource(path, resource, onu_id=onu_id)
202
203 except Exception as e:
204 self._log.exception("Get-" + resource_type + "-id-failed",
205 path=path, e=e)
206 return result
207
208 def free_resource_id(self, pon_intf_id, resource_type, release_content, onu_id=None):
209 """
210 Release alloc/gemport/onu id for given OLT PON interface.
211
212 :param pon_intf_id: OLT PON interface id
213 :param resource_type: String to identify type of resource
214 :param release_content: required number of ids
215 :param onu_id: ONU ID if unique per ONU
216 :return boolean: True if all IDs in given release_content released
217 else False
218 """
219 status = False
220
221 path = self._get_path(pon_intf_id, resource_type)
222 if path is None:
223 return status
224
225 try:
226 resource = self._get_resource(path, onu_id=onu_id)
227 if resource is None:
228 raise Exception("get-resource-for-free-failed")
229
230 if resource_type == PONResourceManager.ONU_ID:
231 self._release_id(resource, release_content)
232
233 elif resource_type == PONResourceManager.ALLOC_ID:
234 for content in release_content:
235 self._release_id(resource, content)
236
237 elif resource_type == PONResourceManager.GEMPORT_ID:
238 for content in release_content:
239 self._release_id(resource, content, onu_id)
240 else:
241 raise Exception("get-resource-for-free-failed")
242
243 self._log.debug("Free-" + resource_type + "-success", path=path)
244
245 # Update resource in kv store
246 status = self._update_resource(path, resource, onu_id=onu_id)
247
248 except Exception as e:
249 self._log.exception("Free-" + resource_type + "-failed",
250 path=path, e=e)
251 return status
252
253 def _update_resource(self, path, resource, onu_id=None):
254 """
255 Update resource in resource kv store.
256
257 :param path: path to update resource
258 :param resource: resource need to be updated
259 :return boolean: True if resource updated in kv store else False
260 """
261 if 'alloc_id' in path.lower():
262 assert onu_id is not None
263 poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
264 poolResource[PONResourceManager.POOL] = \
265 poolResource[PONResourceManager.POOL].bin
266 else:
267 resource[PONResourceManager.POOL] = \
268 resource[PONResourceManager.POOL].bin
269
270 return self._kv_store.update_to_kv_store(path, json.dumps(resource))
271
272 def _get_resource(self, path, onu_id=None):
273 """
274 Get resource from kv store.
275
276 :param path: path to get resource
277 :return: resource if resource present in kv store else None
278 """
279 # get resource from kv store
280 result = self._kv_store.get_from_kv_store(path)
281 if result is None:
282 return result
283
284 self._log.info("dumping-resource", result=result)
285 resource = result
286
287 if resource is not None:
288 # decode resource fetched from backend store to dictionary
289 resource = json.loads(resource)
290
291 if 'alloc_id' in path.lower():
292 assert onu_id is not None
293 poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
294 poolResource[PONResourceManager.POOL] = \
295 BitArray('0b' + poolResource[PONResourceManager.POOL])
296 else:
297 # resource pool in backend store stored as binary string whereas to
298 # access the pool to generate/release IDs it need to be converted
299 # as BitArray
300 resource[PONResourceManager.POOL] = \
301 BitArray('0b' + resource[PONResourceManager.POOL])
302
303 return resource
304
305 def _format_resource(self, pon_intf_id, start_idx, end_idx):
306 """
307 Format resource as json.
308
309 :param pon_intf_id: OLT PON interface id
310 :param start_idx: start index for id pool
311 :param end_idx: end index for id pool
312 :return dictionary: resource formatted as dictionary
313 """
314 # Format resource as json to be stored in backend store
315 resource = dict()
316 resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
317 resource[PONResourceManager.START_IDX] = start_idx
318 resource[PONResourceManager.END_IDX] = end_idx
319
320 # resource pool stored in backend store as binary string
321 resource[PONResourceManager.POOL] = BitArray(end_idx-start_idx).bin
322
323 return json.dumps(resource)
324
325 def _format_map_resource(self, pon_intf_id, resource_map):
326 """
327 Format resource as json.
328 # TODO: Refactor the resource BitArray to be just a list of the resources.
329 # This is used to store available alloc-id's on a per-onu/pon basis
330 # which in BitArray string form, is a 768 byte string for just 4 possible
331 # alloc-IDs. This equates to 1.57 MB of storage when you take into
332 # account 128 ONUs and 16 PONs pre-provisioneed
333 :param pon_intf_id: OLT PON interface id
334 :param resource_map: (dict) ONU ID -> Scattered list of IDs
335 :return dictionary: resource formatted as dictionary
336 """
337 # Format resource as json to be stored in backend store
338 resource = dict()
339 resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
340
341 onu_dict = dict()
342 for onu_id, resources in resource_map.items():
343 start_idx = min(resources)
344 end_idx = max(resources) + 1
345
346 onu_dict[onu_id] = {
347 PONResourceManager.START_IDX: start_idx,
348 PONResourceManager.END_IDX: end_idx,
349 }
350 # Set non-allowed values as taken
351 resource_map = BitArray(end_idx - start_idx)
352 not_available = {pos for pos in xrange(end_idx-start_idx)
353 if pos + start_idx not in resources}
354 resource_map.set(True, not_available)
355 onu_dict[onu_id][PONResourceManager.POOL] = resource_map.bin
356
357 resource[AdtranPONResourceManager.ONU_MAP] = onu_dict
358 return json.dumps(resource)