blob: bdb45ee831a394d1e29f65d6efe1d80fadae91d1 [file] [log] [blame]
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -05001#
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"""
23import json
24import ast
25import structlog
26from bitstring import BitArray
27import shlex
28from argparse import ArgumentParser, ArgumentError
29
30from common.pon_resource_manager.resource_kv_store import ResourceKvStore
31from common.tech_profile.tech_profile import TechProfile
32
33
34# Used to parse extra arguments to OpenOlt adapter from the NBI
35class OltVendorArgumentParser(ArgumentParser):
36 # Must override the exit command to prevent it from
37 # calling sys.exit(). Return exception instead.
38 def exit(self, status=0, message=None):
39 raise Exception(message)
40
41
42class PONResourceManager(object):
43 """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
44
45 # Constants to identify resource pool
46 UNI_ID = 'UNI_ID'
47 ONU_ID = 'ONU_ID'
48 ALLOC_ID = 'ALLOC_ID'
49 GEMPORT_ID = 'GEMPORT_ID'
50 FLOW_ID = 'FLOW_ID'
51
52 # Constants for passing command line arugments
53 OLT_MODEL_ARG = '--olt_model'
54
55 # The resource ranges for a given device model should be placed
56 # at 'resource_manager/<technology>/resource_ranges/<olt_model_type>'
57 # path on the KV store.
58 # If Resource Range parameters are to be read from the external KV store,
59 # they are expected to be stored in the following format.
60 # Note: All parameters are MANDATORY for now.
61 '''
62 {
63 "onu_id_start": 1,
64 "onu_id_end": 127,
65 "alloc_id_start": 1024,
66 "alloc_id_end": 2816,
67 "gemport_id_start": 1024,
68 "gemport_id_end": 8960,
69 "flow_id_start": 1,
70 "flow_id_end": 16383,
71 "uni_id_start": 0,
72 "uni_id_end": 0,
73 "pon_ports": 16
74 }
75
76 '''
77 # constants used as keys to reference the resource range parameters from
78 # and external KV store.
79 UNI_ID_START_IDX = "uni_id_start"
80 UNI_ID_END_IDX = "uni_id_end"
81 ONU_ID_START_IDX = "onu_id_start"
82 ONU_ID_END_IDX = "onu_id_end"
83 ONU_ID_SHARED_IDX = "onu_id_shared"
84 ALLOC_ID_START_IDX = "alloc_id_start"
85 ALLOC_ID_END_IDX = "alloc_id_end"
86 ALLOC_ID_SHARED_IDX = "alloc_id_shared"
87 GEMPORT_ID_START_IDX = "gemport_id_start"
88 GEMPORT_ID_END_IDX = "gemport_id_end"
89 GEMPORT_ID_SHARED_IDX = "gemport_id_shared"
90 FLOW_ID_START_IDX = "flow_id_start"
91 FLOW_ID_END_IDX = "flow_id_end"
92 FLOW_ID_SHARED_IDX = "flow_id_shared"
93 NUM_OF_PON_PORT = "pon_ports"
94
95 # PON Resource range configuration on the KV store.
96 # Format: 'resource_manager/<technology>/resource_ranges/<olt_model_type>'
97 # The KV store backend is initialized with a path prefix and we need to
98 # provide only the suffix.
99 PON_RESOURCE_RANGE_CONFIG_PATH = 'resource_ranges/{}'
100
101 # resource path suffix
102 ALLOC_ID_POOL_PATH = '{}/alloc_id_pool/{}'
103 GEMPORT_ID_POOL_PATH = '{}/gemport_id_pool/{}'
104 ONU_ID_POOL_PATH = '{}/onu_id_pool/{}'
105 FLOW_ID_POOL_PATH = '{}/flow_id_pool/{}'
106
107 # Path on the KV store for storing list of alloc IDs for a given ONU
108 # Format: <device_id>/<(pon_intf_id, onu_id)>/alloc_ids
109 ALLOC_ID_RESOURCE_MAP_PATH = '{}/{}/alloc_ids'
110
111 # Path on the KV store for storing list of gemport IDs for a given ONU
112 # Format: <device_id>/<(pon_intf_id, onu_id)>/gemport_ids
113 GEMPORT_ID_RESOURCE_MAP_PATH = '{}/{}/gemport_ids'
114
115 # Path on the KV store for storing list of Flow IDs for a given ONU
116 # Format: <device_id>/<(pon_intf_id, onu_id)>/flow_ids
117 FLOW_ID_RESOURCE_MAP_PATH = '{}/{}/flow_ids'
118
119 # Flow Id info: Use to store more metadata associated with the flow_id
120 # Format: <device_id>/<(pon_intf_id, onu_id)>/flow_id_info/<flow_id>
121 FLOW_ID_INFO_PATH = '{}/{}/flow_id_info/{}'
122
123 # Constants for internal usage.
124 PON_INTF_ID = 'pon_intf_id'
125 START_IDX = 'start_idx'
126 END_IDX = 'end_idx'
127 POOL = 'pool'
128
129 def __init__(self, technology, extra_args, device_id,
130 backend, host, port):
131 """
132 Create PONResourceManager object.
133
134 :param technology: PON technology
135 :param: extra_args: This string contains extra arguments passed during
136 pre-provisioning of OLT and specifies the OLT Vendor type
137 :param device_id: OLT device id
138 :param backend: backend store
139 :param host: ip of backend store
140 :param port: port on which backend store listens
141 :raises exception when invalid backend store passed as an argument
142 """
143 # logger
144 self._log = structlog.get_logger()
145
146 try:
147 self.technology = technology
148 self.extra_args = extra_args
149 self.device_id = device_id
150 self.backend = backend
151 self.host = host
152 self.port = port
153 self.olt_model = None
154
155 self._kv_store = ResourceKvStore(technology, device_id, backend,
156 host, port)
157 self.tech_profile = TechProfile(self)
158
159 # Below attribute, pon_resource_ranges, should be initialized
160 # by reading from KV store.
161 self.pon_resource_ranges = dict()
162 self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX] = None
163 self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX] = None
164 self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX] = None
165 self.pon_resource_ranges[PONResourceManager.FLOW_ID_SHARED_IDX] = None
166
167 self.shared_resource_mgrs = dict()
168 self.shared_resource_mgrs[PONResourceManager.ONU_ID_SHARED_IDX] = None
169 self.shared_resource_mgrs[PONResourceManager.ALLOC_ID_SHARED_IDX] = None
170 self.shared_resource_mgrs[PONResourceManager.GEMPORT_ID_SHARED_IDX] = None
171 self.shared_resource_mgrs[PONResourceManager.FLOW_ID_SHARED_IDX] = None
172
173 self.shared_idx_by_type = dict()
174 self.shared_idx_by_type[PONResourceManager.ONU_ID] = PONResourceManager.ONU_ID_SHARED_IDX
175 self.shared_idx_by_type[PONResourceManager.ALLOC_ID] = PONResourceManager.ALLOC_ID_SHARED_IDX
176 self.shared_idx_by_type[PONResourceManager.GEMPORT_ID] = PONResourceManager.GEMPORT_ID_SHARED_IDX
177 self.shared_idx_by_type[PONResourceManager.FLOW_ID] = PONResourceManager.FLOW_ID_SHARED_IDX
178
179 self.intf_ids = None
180
181 except Exception as e:
182 self._log.exception("exception-in-init")
183 raise Exception(e)
184
185 def init_resource_ranges_from_kv_store(self):
186 """
187 Initialize PON resource ranges with config fetched from kv store.
188
189 :return boolean: True if PON resource ranges initialized else false
190 """
191 self.olt_model = self._get_olt_model()
192 # Try to initialize the PON Resource Ranges from KV store based on the
193 # OLT model key, if available
194 if self.olt_model is None:
195 self._log.info("device-model-unavailable--not-reading-from-kv-store")
196 return False
197
198 path = self.PON_RESOURCE_RANGE_CONFIG_PATH.format(self.olt_model)
199 try:
200 # get resource from kv store
201 result = self._kv_store.get_from_kv_store(path)
202
203 if result is None:
204 self._log.debug("resource-range-config-unavailable-on-kvstore")
205 return False
206
207 resource_range_config = result
208
209 if resource_range_config is not None:
210 # update internal ranges from kv ranges. If there are missing
211 # values in the KV profile, continue to use the defaults
212 for key,value in json.loads(resource_range_config): self.pon_resource_ranges[key] = value
213
214 # initialize optional elements that may not be in the profile
215 if self.pon_resource_ranges[PONResourceManager.UNI_ID_START_IDX] is None:
216 self.pon_resource_ranges[PONResourceManager.UNI_ID_START_IDX] = 0
217 if self.pon_resource_ranges[PONResourceManager.UNI_ID_END_IDX] is None:
218 self.pon_resource_ranges[PONResourceManager.UNI_ID_END_IDX] = 0
219
220 self._log.debug("Init-resource-ranges-from-kvstore-success",
221 pon_resource_ranges=self.pon_resource_ranges,
222 path=path)
223 return True
224
225 except Exception as e:
226 self._log.exception("error-initializing-resource-range-from-kv-store",
227 e=e)
228 return False
229
230 def update_range_(self, start_idx, start, end_idx, end, shared_idx = None, shared_pool_id = None,
231 shared_resource_mgr = None):
232 if (start is not None) and \
233 (start_idx not in self.pon_resource_ranges or self.pon_resource_ranges[start_idx] < start):
234 self.pon_resource_ranges[start_idx] = start
235 if (end is not None) and \
236 (end_idx not in self.pon_resource_ranges or self.pon_resource_ranges[end_idx] > end):
237 self.pon_resource_ranges[end_idx] = end
238 if (shared_pool_id is not None) and \
239 (shared_idx not in self.pon_resource_ranges or self.pon_resource_ranges[shared_idx] is None):
240 self.pon_resource_ranges[shared_idx] = shared_pool_id
241 if (shared_resource_mgr is not None) and \
242 (shared_idx not in self.shared_resource_mgrs or self.shared_resource_mgrs[shared_idx] is None):
243 self.shared_resource_mgrs[shared_idx] = shared_resource_mgr
244
245 def update_ranges(self,
246 onu_id_start_idx=None,
247 onu_id_end_idx=None,
248 onu_id_shared_pool_id=None,
249 onu_id_shared_resource_mgr=None,
250 alloc_id_start_idx=None,
251 alloc_id_end_idx=None,
252 alloc_id_shared_pool_id=None,
253 alloc_id_shared_resource_mgr=None,
254 gemport_id_start_idx=None,
255 gemport_id_end_idx=None,
256 gemport_id_shared_pool_id=None,
257 gemport_id_shared_resource_mgr=None,
258 flow_id_start_idx=None,
259 flow_id_end_idx=None,
260 flow_id_shared_pool_id=None,
261 flow_id_shared_resource_mgr=None,
262 uni_id_start_idx=None,
263 uni_id_end_idx=None):
264
265 self.update_range_(PONResourceManager.ONU_ID_START_IDX, onu_id_start_idx,
266 PONResourceManager.ONU_ID_END_IDX, onu_id_end_idx,
267 PONResourceManager.ONU_ID_SHARED_IDX, onu_id_shared_pool_id,
268 onu_id_shared_resource_mgr)
269
270 self.update_range_(PONResourceManager.ALLOC_ID_START_IDX, alloc_id_start_idx,
271 PONResourceManager.ALLOC_ID_END_IDX, alloc_id_end_idx,
272 PONResourceManager.ALLOC_ID_SHARED_IDX, alloc_id_shared_pool_id,
273 alloc_id_shared_resource_mgr)
274
275 self.update_range_(PONResourceManager.GEMPORT_ID_START_IDX, gemport_id_start_idx,
276 PONResourceManager.GEMPORT_ID_END_IDX, gemport_id_end_idx,
277 PONResourceManager.GEMPORT_ID_SHARED_IDX, gemport_id_shared_pool_id,
278 gemport_id_shared_resource_mgr)
279
280 self.update_range_(PONResourceManager.FLOW_ID_START_IDX, flow_id_start_idx,
281 PONResourceManager.FLOW_ID_END_IDX, flow_id_end_idx,
282 PONResourceManager.FLOW_ID_SHARED_IDX, flow_id_shared_pool_id,
283 flow_id_shared_resource_mgr)
284
285 self.update_range_(PONResourceManager.UNI_ID_START_IDX, uni_id_start_idx,
286 PONResourceManager.UNI_ID_END_IDX, uni_id_end_idx)
287
288 def init_default_pon_resource_ranges(self,
289 onu_id_start_idx=1,
290 onu_id_end_idx=127,
291 onu_id_shared_pool_id=None,
292 alloc_id_start_idx=1024,
293 alloc_id_end_idx=2816,
294 alloc_id_shared_pool_id=None,
295 gemport_id_start_idx=1024,
296 gemport_id_end_idx=8960,
297 gemport_id_shared_pool_id=None,
298 flow_id_start_idx=1,
299 flow_id_end_idx=16383,
300 flow_id_shared_pool_id=None,
301 uni_id_start_idx=0,
302 uni_id_end_idx=0,
303 num_of_pon_ports=16,
304 intf_ids=None):
305 """
306 Initialize default PON resource ranges
307
308 :param onu_id_start_idx: onu id start index
309 :param onu_id_end_idx: onu id end index
310 :param onu_id_shared_pool_id: pool idx for id shared by all intfs or None for no sharing
311 :param alloc_id_start_idx: alloc id start index
312 :param alloc_id_end_idx: alloc id end index
313 :param alloc_id_shared_pool_id: pool idx for alloc id shared by all intfs or None for no sharing
314 :param gemport_id_start_idx: gemport id start index
315 :param gemport_id_end_idx: gemport id end index
316 :param gemport_id_shared_pool_id: pool idx for gemport id shared by all intfs or None for no sharing
317 :param flow_id_start_idx: flow id start index
318 :param flow_id_end_idx: flow id end index
319 :param flow_id_shared_pool_id: pool idx for flow id shared by all intfs or None for no sharing
320 :param num_of_pon_ports: number of PON ports
321 :param intf_ids: interfaces serviced by this manager
322 """
323 self._log.info("initialize-default-resource-range-values")
324
325 self.update_ranges(onu_id_start_idx, onu_id_end_idx, onu_id_shared_pool_id, None,
326 alloc_id_start_idx, alloc_id_end_idx, alloc_id_shared_pool_id, None,
327 gemport_id_start_idx, gemport_id_end_idx, gemport_id_shared_pool_id, None,
328 flow_id_start_idx, flow_id_end_idx, flow_id_shared_pool_id, None,
329 uni_id_start_idx, uni_id_end_idx)
330
331 if intf_ids is None:
332 intf_ids = range(0, num_of_pon_ports)
333
334 self.intf_ids = intf_ids
335
336 def init_device_resource_pool(self):
337 """
338 Initialize resource pool for all PON ports.
339 """
340
341 self._log.info("init-device-resource-pool", technology=self.technology,
342 pon_resource_ranges=self.pon_resource_ranges)
343
344 for i in self.intf_ids:
345 shared_pool_id = self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX]
346 if shared_pool_id is not None:
347 i = shared_pool_id
348 self.init_resource_id_pool(
349 pon_intf_id=i,
350 resource_type=PONResourceManager.ONU_ID,
351 start_idx=self.pon_resource_ranges[
352 PONResourceManager.ONU_ID_START_IDX],
353 end_idx=self.pon_resource_ranges[
354 PONResourceManager.ONU_ID_END_IDX])
355 if shared_pool_id is not None:
356 break
357
358 for i in self.intf_ids:
359 shared_pool_id = self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX]
360 if shared_pool_id is not None:
361 i = shared_pool_id
362 self.init_resource_id_pool(
363 pon_intf_id=i,
364 resource_type=PONResourceManager.ALLOC_ID,
365 start_idx=self.pon_resource_ranges[
366 PONResourceManager.ALLOC_ID_START_IDX],
367 end_idx=self.pon_resource_ranges[
368 PONResourceManager.ALLOC_ID_END_IDX])
369 if shared_pool_id is not None:
370 break
371
372 for i in self.intf_ids:
373 shared_pool_id = self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX]
374 if shared_pool_id is not None:
375 i = shared_pool_id
376 self.init_resource_id_pool(
377 pon_intf_id=i,
378 resource_type=PONResourceManager.GEMPORT_ID,
379 start_idx=self.pon_resource_ranges[
380 PONResourceManager.GEMPORT_ID_START_IDX],
381 end_idx=self.pon_resource_ranges[
382 PONResourceManager.GEMPORT_ID_END_IDX])
383 if shared_pool_id is not None:
384 break
385
386 for i in self.intf_ids:
387 shared_pool_id = self.pon_resource_ranges[PONResourceManager.FLOW_ID_SHARED_IDX]
388 if shared_pool_id is not None:
389 i = shared_pool_id
390 self.init_resource_id_pool(
391 pon_intf_id=i,
392 resource_type=PONResourceManager.FLOW_ID,
393 start_idx=self.pon_resource_ranges[
394 PONResourceManager.FLOW_ID_START_IDX],
395 end_idx=self.pon_resource_ranges[
396 PONResourceManager.FLOW_ID_END_IDX])
397 if shared_pool_id is not None:
398 break
399
400 def clear_device_resource_pool(self):
401 """
402 Clear resource pool of all PON ports.
403 """
404 for i in self.intf_ids:
405 shared_pool_id = self.pon_resource_ranges[PONResourceManager.ONU_ID_SHARED_IDX]
406 if shared_pool_id is not None:
407 i = shared_pool_id
408 self.clear_resource_id_pool(
409 pon_intf_id=i,
410 resource_type=PONResourceManager.ONU_ID,
411 )
412 if shared_pool_id is not None:
413 break
414
415 for i in self.intf_ids:
416 shared_pool_id = self.pon_resource_ranges[PONResourceManager.ALLOC_ID_SHARED_IDX]
417 if shared_pool_id is not None:
418 i = shared_pool_id
419 self.clear_resource_id_pool(
420 pon_intf_id=i,
421 resource_type=PONResourceManager.ALLOC_ID,
422 )
423 if shared_pool_id is not None:
424 break
425
426 for i in self.intf_ids:
427 shared_pool_id = self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_SHARED_IDX]
428 if shared_pool_id is not None:
429 i = shared_pool_id
430 self.clear_resource_id_pool(
431 pon_intf_id=i,
432 resource_type=PONResourceManager.GEMPORT_ID,
433 )
434 if shared_pool_id is not None:
435 break
436
437 for i in self.intf_ids:
438 shared_pool_id = self.pon_resource_ranges[PONResourceManager.FLOW_ID_SHARED_IDX]
439 if shared_pool_id is not None:
440 i = shared_pool_id
441 self.clear_resource_id_pool(
442 pon_intf_id=i,
443 resource_type=PONResourceManager.FLOW_ID,
444 )
445 if shared_pool_id is not None:
446 break
447
448 def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx,
449 end_idx):
450 """
451 Initialize Resource ID pool for a given Resource Type on a given PON Port
452
453 :param pon_intf_id: OLT PON interface id
454 :param resource_type: String to identify type of resource
455 :param start_idx: start index for onu id pool
456 :param end_idx: end index for onu id pool
457 :return boolean: True if resource id pool initialized else false
458 """
459 status = False
460
461 # delegate to the master instance if sharing enabled across instances
462 shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
463 if shared_resource_mgr is not None and shared_resource_mgr is not self:
464 return shared_resource_mgr.init_resource_id_pool(pon_intf_id, resource_type,
465 start_idx, end_idx)
466
467 path = self._get_path(pon_intf_id, resource_type)
468 if path is None:
469 return status
470
471 try:
472 # In case of adapter reboot and reconciliation resource in kv store
473 # checked for its presence if not kv store update happens
474 resource = self._get_resource(path)
475
476 if resource is not None:
477 self._log.info("Resource-already-present-in-store", path=path)
478 status = True
479 else:
480 resource = self._format_resource(pon_intf_id, start_idx,
481 end_idx)
482 self._log.info("Resource-initialized", path=path)
483
484 # Add resource as json in kv store.
485 result = self._kv_store.update_to_kv_store(path, resource)
486 if result is True:
487 status = True
488
489 except Exception as e:
490 self._log.exception("error-initializing-resource-pool", e=e)
491
492 return status
493
494 def assert_resource_limits(self, id, resource_type):
495 """
496 Assert the specified id value is in the limit bounds of he requested resource type.
497
498 :param id: The value to assert is in limits
499 :param resource_type: String to identify type of resource
500 """
501 start_idx = PONResourceManager.ONU_ID_START_IDX if resource_type == PONResourceManager.ONU_ID \
502 else PONResourceManager.ALLOC_ID_START_IDX if resource_type == PONResourceManager.ALLOC_ID \
503 else PONResourceManager.GEMPORT_ID_START_IDX if resource_type == PONResourceManager.GEMPORT_ID \
504 else PONResourceManager.FLOW_ID_START_IDX if resource_type == PONResourceManager.FLOW_ID \
505 else PONResourceManager.UNI_ID_START_IDX if resource_type == PONResourceManager.UNI_ID \
506 else None
507 end_idx = PONResourceManager.ONU_ID_END_IDX if resource_type == PONResourceManager.ONU_ID \
508 else PONResourceManager.ALLOC_ID_END_IDX if resource_type == PONResourceManager.ALLOC_ID \
509 else PONResourceManager.GEMPORT_ID_END_IDX if resource_type == PONResourceManager.GEMPORT_ID \
510 else PONResourceManager.FLOW_ID_END_IDX if resource_type == PONResourceManager.FLOW_ID \
511 else PONResourceManager.UNI_ID_END_IDX if resource_type == PONResourceManager.UNI_ID \
512 else None
513 assert id >= self.pon_resource_ranges[start_idx] and id <= self.pon_resource_ranges[end_idx]
514
515 def get_resource_id(self, pon_intf_id, resource_type, num_of_id=1):
516 """
517 Create alloc/gemport/onu/flow id for given OLT PON interface.
518
519 :param pon_intf_id: OLT PON interface id
520 :param resource_type: String to identify type of resource
521 :param num_of_id: required number of ids
522 :return list/int/None: list, int or None if resource type is
523 alloc_id/gemport_id, onu_id or invalid type
524 respectively
525 """
526 result = None
527
528 if num_of_id < 1:
529 self._log.error("invalid-num-of-resources-requested")
530 return result
531
532 # delegate to the master instance if sharing enabled across instances
533 shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
534 if shared_resource_mgr is not None and shared_resource_mgr is not self:
535 return shared_resource_mgr.get_resource_id(pon_intf_id, resource_type, num_of_id)
536
537 path = self._get_path(pon_intf_id, resource_type)
538 if path is None:
539 return result
540
541 try:
542 resource = self._get_resource(path)
543 if resource is not None and \
544 (resource_type == PONResourceManager.ONU_ID or
545 resource_type == PONResourceManager.FLOW_ID):
546 result = self._generate_next_id(resource)
547 elif resource is not None and (
548 resource_type == PONResourceManager.GEMPORT_ID or
549 resource_type == PONResourceManager.ALLOC_ID):
550 if num_of_id == 1:
551 result = self._generate_next_id(resource)
552 else:
553 result = list()
554 while num_of_id > 0:
555 result.append(self._generate_next_id(resource))
556 num_of_id -= 1
557 else:
558 raise Exception("get-resource-failed")
559
560 self._log.debug("Get-" + resource_type + "-success", result=result,
561 path=path)
562 # Update resource in kv store
563 self._update_resource(path, resource)
564
565 except Exception as e:
566 self._log.exception("Get-" + resource_type + "-id-failed",
567 path=path, e=e)
568 return result
569
570 def free_resource_id(self, pon_intf_id, resource_type, release_content):
571 """
572 Release alloc/gemport/onu/flow id for given OLT PON interface.
573
574 :param pon_intf_id: OLT PON interface id
575 :param resource_type: String to identify type of resource
576 :param release_content: required number of ids
577 :return boolean: True if all IDs in given release_content released
578 else False
579 """
580 status = False
581 known_resource_types = [PONResourceManager.ONU_ID,
582 PONResourceManager.ALLOC_ID,
583 PONResourceManager.GEMPORT_ID,
584 PONResourceManager.FLOW_ID]
585 if resource_type not in known_resource_types:
586 self._log.error("unknown-resource-type",
587 resource_type=resource_type)
588 return status
589 if release_content is None:
590 self._log.debug("nothing-to-release")
591 return status
592 # delegate to the master instance if sharing enabled across instances
593 shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
594 if shared_resource_mgr is not None and shared_resource_mgr is not self:
595 return shared_resource_mgr.free_resource_id(pon_intf_id, resource_type)
596
597 path = self._get_path(pon_intf_id, resource_type)
598 if path is None:
599 return status
600
601 try:
602 resource = self._get_resource(path)
603 if resource is None:
604 raise Exception("get-resource-failed")
605 if isinstance(release_content, list):
606 for content in release_content:
607 self._release_id(resource, content)
608 else:
609 self._release_id(resource, release_content)
610
611 self._log.debug("Free-" + resource_type + "-success", path=path)
612
613 # Update resource in kv store
614 status = self._update_resource(path, resource)
615
616 except Exception as e:
617 self._log.exception("Free-" + resource_type + "-failed",
618 path=path, e=e)
619 return status
620
621 def clear_resource_id_pool(self, pon_intf_id, resource_type):
622 """
623 Clear Resource Pool for a given Resource Type on a given PON Port.
624
625 :return boolean: True if removed else False
626 """
627
628 # delegate to the master instance if sharing enabled across instances
629 shared_resource_mgr = self.shared_resource_mgrs[self.shared_idx_by_type[resource_type]]
630 if shared_resource_mgr is not None and shared_resource_mgr is not self:
631 return shared_resource_mgr.clear_resource_id_pool(pon_intf_id, resource_type)
632
633 path = self._get_path(pon_intf_id, resource_type)
634 if path is None:
635 return False
636
637 try:
638 result = self._kv_store.remove_from_kv_store(path)
639 if result is True:
640 self._log.debug("Resource-pool-cleared",
641 device_id=self.device_id,
642 path=path)
643 return True
644 except Exception as e:
645 self._log.exception("error-clearing-resource-pool", e=e)
646
647 self._log.error("Clear-resource-pool-failed", device_id=self.device_id,
648 path=path)
649 return False
650
651 def init_resource_map(self, pon_intf_onu_id):
652 """
653 Initialize resource map
654
655 :param pon_intf_onu_id: reference of PON interface id and onu id
656 """
657 # initialize pon_intf_onu_id tuple to alloc_ids map
658 alloc_id_path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
659 self.device_id, str(pon_intf_onu_id)
660 )
661 alloc_ids = list()
662 self._kv_store.update_to_kv_store(
663 alloc_id_path, json.dumps(alloc_ids)
664 )
665
666 # initialize pon_intf_onu_id tuple to gemport_ids map
667 gemport_id_path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
668 self.device_id, str(pon_intf_onu_id)
669 )
670 gemport_ids = list()
671 self._kv_store.update_to_kv_store(
672 gemport_id_path, json.dumps(gemport_ids)
673 )
674
675 def remove_resource_map(self, pon_intf_onu_id):
676 """
677 Remove resource map
678
679 :param pon_intf_onu_id: reference of PON interface id and onu id
680 """
681 # remove pon_intf_onu_id tuple to alloc_ids map
682 try:
683 alloc_id_path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
684 self.device_id, str(pon_intf_onu_id)
685 )
686 self._kv_store.remove_from_kv_store(alloc_id_path)
687 except Exception as e:
688 self._log.error("error-removing-alloc-id", e=e)
689
690 try:
691 # remove pon_intf_onu_id tuple to gemport_ids map
692 gemport_id_path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
693 self.device_id, str(pon_intf_onu_id)
694 )
695 self._kv_store.remove_from_kv_store(gemport_id_path)
696 except Exception as e:
697 self._log.error("error-removing-gem-ports", e=e)
698
699 flow_id_path = PONResourceManager.FLOW_ID_RESOURCE_MAP_PATH.format(
700 self.device_id, str(pon_intf_onu_id))
701 flow_ids = self._kv_store.get_from_kv_store(flow_id_path)
702
703 if flow_ids and isinstance(flow_ids, list):
704 for flow_id in flow_ids:
705 try:
706 flow_id_info_path = PONResourceManager.FLOW_ID_INFO_PATH.format(
707 self.device_id, str(pon_intf_onu_id), flow_id)
708 self._kv_store.remove_from_kv_store(flow_id_info_path)
709 except Exception as e:
710 self._log.error("error-removing-flow-info", flow_id=flow_id, e=e)
711 continue
712 try:
713 self._kv_store.remove_from_kv_store(flow_id_path)
714 except Exception as e:
715 self._log.error("error-removing-flow-ids", e=e)
716
717 def get_current_alloc_ids_for_onu(self, pon_intf_onu_id):
718 """
719 Get currently configured alloc ids for given pon_intf_onu_id
720
721 :param pon_intf_onu_id: reference of PON interface id and onu id
722
723 :return list: List of alloc_ids if available, else None
724 """
725 path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
726 self.device_id,
727 str(pon_intf_onu_id))
728 value = self._kv_store.get_from_kv_store(path)
729 if value is not None:
730 alloc_id_list = json.loads(value)
731 if len(alloc_id_list) > 0:
732 return alloc_id_list
733
734 return None
735
736 def get_current_gemport_ids_for_onu(self, pon_intf_onu_id):
737 """
738 Get currently configured gemport ids for given pon_intf_onu_id
739
740 :param pon_intf_onu_id: reference of PON interface id and onu id
741
742 :return list: List of gemport IDs if available, else None
743 """
744
745 path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
746 self.device_id,
747 str(pon_intf_onu_id))
748 value = self._kv_store.get_from_kv_store(path)
749 if value is not None:
750 gemport_id_list = json.loads(value)
751 if len(gemport_id_list) > 0:
752 return gemport_id_list
753
754 return None
755
756 def get_current_flow_ids_for_onu(self, pon_intf_onu_id):
757 """
758 Get currently configured flow ids for given pon_intf_onu_id
759
760 :param pon_intf_onu_id: reference of PON interface id and onu id
761
762 :return list: List of Flow IDs if available, else None
763 """
764
765 path = PONResourceManager.FLOW_ID_RESOURCE_MAP_PATH.format(
766 self.device_id,
767 str(pon_intf_onu_id))
768 value = self._kv_store.get_from_kv_store(path)
769 if value is not None:
770 flow_id_list = json.loads(value)
771 assert(isinstance(flow_id_list, list))
772 if len(flow_id_list) > 0:
773 return flow_id_list
774
775 return None
776
777 def get_flow_id_info(self, pon_intf_onu_id, flow_id):
778 """
779 Get flow_id details configured for the ONU.
780
781 :param pon_intf_onu_id: reference of PON interface id and onu id
782 :param flow_id: Flow Id reference
783
784 :return blob: Flow data blob if available, else None
785 """
786
787 path = PONResourceManager.FLOW_ID_INFO_PATH.format(
788 self.device_id,
789 str(pon_intf_onu_id),
790 flow_id)
791 value = self._kv_store.get_from_kv_store(path)
792 if value is not None:
793 return ast.literal_eval(value)
794
795 return None
796
797 def remove_flow_id_info(self, pon_intf_onu_id, flow_id):
798 """
799 Get flow_id details configured for the ONU.
800
801 :param pon_intf_onu_id: reference of PON interface id and onu id
802 :param flow_id: Flow Id reference
803
804 """
805
806 path = PONResourceManager.FLOW_ID_INFO_PATH.format(
807 self.device_id,
808 str(pon_intf_onu_id),
809 flow_id)
810 self._kv_store.remove_from_kv_store(path)
811
812 def update_alloc_ids_for_onu(self, pon_intf_onu_id, alloc_ids):
813 """
814 Update currently configured alloc ids for given pon_intf_onu_id
815
816 :param pon_intf_onu_id: reference of PON interface id and onu id
817 :param alloc_ids: list of alloc ids
818 """
819 path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
820 self.device_id, str(pon_intf_onu_id)
821 )
822 self._kv_store.update_to_kv_store(
823 path, json.dumps(alloc_ids)
824 )
825
826 def update_gemport_ids_for_onu(self, pon_intf_onu_id, gemport_ids):
827 """
828 Update currently configured gemport ids for given pon_intf_onu_id
829
830 :param pon_intf_onu_id: reference of PON interface id and onu id
831 :param gemport_ids: list of gem port ids
832 """
833 path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
834 self.device_id, str(pon_intf_onu_id)
835 )
836 self._kv_store.update_to_kv_store(
837 path, json.dumps(gemport_ids)
838 )
839
840 def update_flow_id_for_onu(self, pon_intf_onu_id, flow_id, add=True):
841 """
842 Update the flow_id list of the ONU (add or remove flow_id from the list)
843
844 :param pon_intf_onu_id: reference of PON interface id and onu id
845 :param flow_id: flow ID
846 :param add: Boolean flag to indicate whether the flow_id should be
847 added or removed from the list. Defaults to adding the flow.
848 """
849 path = PONResourceManager.FLOW_ID_RESOURCE_MAP_PATH.format(
850 self.device_id, str(pon_intf_onu_id)
851 )
852 current_flow_ids = self.get_current_flow_ids_for_onu(pon_intf_onu_id)
853 if not isinstance(current_flow_ids, list):
854 # When the first flow_id is being added, the current_flow_ids is None
855 current_flow_ids = list()
856
857 if add:
858 if flow_id not in current_flow_ids:
859 current_flow_ids.append(flow_id)
860 else:
861 if flow_id in current_flow_ids:
862 current_flow_ids.remove(flow_id)
863
864 self._kv_store.update_to_kv_store(path, current_flow_ids)
865
866 def update_flow_id_info_for_onu(self, pon_intf_onu_id, flow_id, flow_data):
867 """
868 Update any metadata associated with the flow_id. The flow_data could be json
869 or any of other data structure. The resource manager doesnt care
870
871 :param pon_intf_onu_id: reference of PON interface id and onu id
872 :param flow_id: Flow ID
873 :param flow_data: Flow data blob
874 """
875 path = PONResourceManager.FLOW_ID_INFO_PATH.format(
876 self.device_id, str(pon_intf_onu_id), flow_id
877 )
878
879 if not self._kv_store.update_to_kv_store(path, flow_data):
880 self._log.error("flow-info-update-failed", path=path, flow_id=flow_id)
881
882 def _get_olt_model(self):
883 """
884 Get olt model variant
885
886 :return: type of olt model
887 """
888 olt_model = None
889 if self.extra_args and len(self.extra_args) > 0:
890 parser = OltVendorArgumentParser(add_help=False)
891 parser.add_argument(PONResourceManager.OLT_MODEL_ARG, '-m', action='store', default='default')
892 try:
893 args = parser.parse_args(shlex.split(self.extra_args))
894 self._log.debug('parsing-extra-arguments', args=args)
895 olt_model = args.olt_model
896 except ArgumentError as e:
897 self._log.exception('invalid-arguments: {}', e=e)
898 except Exception as e:
899 self._log.exception('option-parsing-error: {}', e=e)
900
901 self._log.debug('olt-model', olt_model=olt_model)
902 return olt_model
903
904 def _generate_next_id(self, resource):
905 """
906 Generate unique id having OFFSET as start index.
907
908 :param resource: resource used to generate ID
909 :return int: generated id
910 """
911 pos = resource[PONResourceManager.POOL].find('0b0')
912 resource[PONResourceManager.POOL].set(1, pos)
913 return pos[0] + resource[PONResourceManager.START_IDX]
914
915 def _release_id(self, resource, unique_id):
916 """
917 Release unique id having OFFSET as start index.
918
919 :param resource: resource used to release ID
920 :param unique_id: id need to be released
921 """
922 pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
923 resource[PONResourceManager.POOL].set(0, pos)
924
925 def _get_path(self, pon_intf_id, resource_type):
926 """
927 Get path for given resource type.
928
929 :param pon_intf_id: OLT PON interface id
930 :param resource_type: String to identify type of resource
931 :return: path for given resource type
932 """
933
934 shared_pool_id = self.pon_resource_ranges[self.shared_idx_by_type[resource_type]]
935 if shared_pool_id is not None:
936 pon_intf_id = shared_pool_id
937
938 path = None
939 if resource_type == PONResourceManager.ONU_ID:
940 path = self._get_onu_id_resource_path(pon_intf_id)
941 elif resource_type == PONResourceManager.ALLOC_ID:
942 path = self._get_alloc_id_resource_path(pon_intf_id)
943 elif resource_type == PONResourceManager.GEMPORT_ID:
944 path = self._get_gemport_id_resource_path(pon_intf_id)
945 elif resource_type == PONResourceManager.FLOW_ID:
946 path = self._get_flow_id_resource_path(pon_intf_id)
947 else:
948 self._log.error("invalid-resource-pool-identifier")
949 return path
950
951 def _get_flow_id_resource_path(self, pon_intf_id):
952 """
953 Get flow id resource path.
954
955 :param pon_intf_id: OLT PON interface id
956 :return: flow id resource path
957 """
958 return PONResourceManager.FLOW_ID_POOL_PATH.format(
959 self.device_id, pon_intf_id)
960
961 def _get_alloc_id_resource_path(self, pon_intf_id):
962 """
963 Get alloc id resource path.
964
965 :param pon_intf_id: OLT PON interface id
966 :return: alloc id resource path
967 """
968 return PONResourceManager.ALLOC_ID_POOL_PATH.format(
969 self.device_id, pon_intf_id)
970
971 def _get_gemport_id_resource_path(self, pon_intf_id):
972 """
973 Get gemport id resource path.
974
975 :param pon_intf_id: OLT PON interface id
976 :return: gemport id resource path
977 """
978 return PONResourceManager.GEMPORT_ID_POOL_PATH.format(
979 self.device_id, pon_intf_id)
980
981 def _get_onu_id_resource_path(self, pon_intf_id):
982 """
983 Get onu id resource path.
984
985 :param pon_intf_id: OLT PON interface id
986 :return: onu id resource path
987 """
988 return PONResourceManager.ONU_ID_POOL_PATH.format(
989 self.device_id, pon_intf_id)
990
991 def _update_resource(self, path, resource):
992 """
993 Update resource in resource kv store.
994
995 :param path: path to update resource
996 :param resource: resource need to be updated
997 :return boolean: True if resource updated in kv store else False
998 """
999 resource[PONResourceManager.POOL] = \
1000 resource[PONResourceManager.POOL].bin
1001 result = self._kv_store.update_to_kv_store(path, json.dumps(resource))
1002 if result is True:
1003 return True
1004 return False
1005
1006 def _get_resource(self, path):
1007 """
1008 Get resource from kv store.
1009
1010 :param path: path to get resource
1011 :return: resource if resource present in kv store else None
1012 """
1013 # get resource from kv store
1014 result = self._kv_store.get_from_kv_store(path)
1015 if result is None:
1016 return result
1017 self._log.info("dumping resource", result=result)
1018 resource = result
1019
1020 if resource is not None:
1021 # decode resource fetched from backend store to dictionary
1022 resource = json.loads(resource)
1023
1024 # resource pool in backend store stored as binary string whereas to
1025 # access the pool to generate/release IDs it need to be converted
1026 # as BitArray
1027 resource[PONResourceManager.POOL] = \
1028 BitArray('0b' + resource[PONResourceManager.POOL])
1029
1030 return resource
1031
1032 def _format_resource(self, pon_intf_id, start_idx, end_idx):
1033 """
1034 Format resource as json.
1035
1036 :param pon_intf_id: OLT PON interface id
1037 :param start_idx: start index for id pool
1038 :param end_idx: end index for id pool
1039 :return dictionary: resource formatted as dictionary
1040 """
1041 # Format resource as json to be stored in backend store
1042 resource = dict()
1043 resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
1044 resource[PONResourceManager.START_IDX] = start_idx
1045 resource[PONResourceManager.END_IDX] = end_idx
1046
1047 # resource pool stored in backend store as binary string
1048 resource[PONResourceManager.POOL] = BitArray(end_idx).bin
1049
1050 return json.dumps(resource)