blob: c0ccb14d72a80647b922ea599a9ad87858ab8a86 [file] [log] [blame]
William Kurkian6f436d02019-02-06 16:25:01 -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
17import structlog
18
William Kurkian44cd7bb2019-02-11 16:39:12 -050019from pyvoltha.adapters.common.pon_resource_manager.resource_manager import PONResourceManager
20from pyvoltha.common.utils.registry import registry
21from pyvoltha.common.config.config_backend import ConsulStore
22from pyvoltha.common.config.config_backend import EtcdStore
23from openolt_flow_mgr import *
William Kurkian6f436d02019-02-06 16:25:01 -050024
William Kurkian8b1690c2019-03-04 16:53:22 -050025from voltha_protos import openolt_pb2
William Kurkian44cd7bb2019-02-11 16:39:12 -050026from openolt_platform import OpenOltPlatform
William Kurkian6f436d02019-02-06 16:25:01 -050027
28
29class OpenOltResourceMgr(object):
30 BASE_PATH_KV_STORE = "service/voltha/openolt/{}" # service/voltha/openolt/<device_id>
31
32 def __init__(self, device_id, host_and_port, extra_args, device_info):
33 self.log = structlog.get_logger(id=device_id,
34 ip=host_and_port)
35 self.device_id = device_id
36 self.host_and_port = host_and_port
37 self.extra_args = extra_args
38 self.device_info = device_info
39 self.args = registry('main').get_args()
40
41 # KV store's IP Address and PORT
42 if self.args.backend == 'etcd':
43 host, port = self.args.etcd.split(':', 1)
44 self.kv_store = EtcdStore(host, port,
45 OpenOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
46 elif self.args.backend == 'consul':
47 host, port = self.args.consul.split(':', 1)
48 self.kv_store = ConsulStore(host, port,
49 OpenOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
50 else:
51 self.log.error('Invalid-backend')
52 raise Exception("Invalid-backend-for-kv-store")
53
54 ranges = dict()
55 resource_mgrs_by_tech = dict()
56 self.resource_mgrs = dict()
57
58 # If a legacy driver returns protobuf without any ranges,s synthesize one from
59 # the legacy global per-device informaiton. This, in theory, is temporary until
60 # the legacy drivers are upgrade to support pool ranges.
61 if len(self.device_info.ranges) == 0:
62 arange = self.device_info.ranges.add()
63 arange.technology = self.device_info.technology
64 arange.intf_ids.extend(range(0, device_info.pon_ports))
65
66 pool = arange.pools.add()
67 pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ONU_ID
68 pool.start = self.device_info.onu_id_start
69 pool.end = self.device_info.onu_id_end
70 pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.DEDICATED_PER_INTF
71
72 pool = arange.pools.add()
73 pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ALLOC_ID
74 pool.start = self.device_info.alloc_id_start
75 pool.end = self.device_info.alloc_id_end
76 pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH
77
78 pool = arange.pools.add()
79 pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.GEMPORT_ID
80 pool.start = self.device_info.gemport_id_start
81 pool.end = self.device_info.gemport_id_end
82 pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH
83
84 pool = arange.pools.add()
85 pool.type = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.FLOW_ID
86 pool.start = self.device_info.flow_id_start
87 pool.end = self.device_info.flow_id_end
88 pool.sharing = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH
89
90 # Create a separate Resource Manager instance for each range. This assumes that
91 # each technology is represented by only a single range
92 global_resource_mgr = None
93 for arange in self.device_info.ranges:
94 technology = arange.technology
95 self.log.info("device-info", technology=technology)
96 ranges[technology] = arange
97 extra_args = self.extra_args + ' ' + PONResourceManager.OLT_MODEL_ARG + ' {}'.format(self.device_info.model)
98 resource_mgr = PONResourceManager(technology,
99 extra_args, self.device_id, self.args.backend, host, port)
100 resource_mgrs_by_tech[technology] = resource_mgr
101 if global_resource_mgr is None:
102 global_resource_mgr = resource_mgr
103 for intf_id in arange.intf_ids:
104 self.resource_mgrs[intf_id] = resource_mgrs_by_tech[technology]
105 self.initialize_device_resource_range_and_pool(resource_mgr, global_resource_mgr, arange)
106
107 # After we have initialized resource ranges, initialize the
108 # resource pools accordingly.
109 for technology, resource_mgr in resource_mgrs_by_tech.iteritems():
110 resource_mgr.init_device_resource_pool()
111
112 def __del__(self):
113 self.log.info("clearing-device-resource-pool")
114 for key, resource_mgr in self.resource_mgrs.iteritems():
115 resource_mgr.clear_device_resource_pool()
116
117 def assert_pon_id_limit(self, pon_intf_id):
118 assert pon_intf_id in self.resource_mgrs
119
120 def assert_onu_id_limit(self, pon_intf_id, onu_id):
121 self.assert_pon_id_limit(pon_intf_id)
122 self.resource_mgrs[pon_intf_id].assert_resource_limits(onu_id, PONResourceManager.ONU_ID)
123
124 @property
125 def max_uni_id_per_onu(self):
126 return 0 #OpenOltPlatform.MAX_UNIS_PER_ONU-1, zero-based indexing Uncomment or override to make default multi-uni
127
128 def assert_uni_id_limit(self, pon_intf_id, onu_id, uni_id):
129 self.assert_onu_id_limit(pon_intf_id, onu_id)
130 self.resource_mgrs[pon_intf_id].assert_resource_limits(uni_id, PONResourceManager.UNI_ID)
131
132 def get_onu_id(self, pon_intf_id):
133 onu_id = self.resource_mgrs[pon_intf_id].get_resource_id(
134 pon_intf_id, PONResourceManager.ONU_ID, 1)
135
136 if onu_id is not None:
137 pon_intf_onu_id = (pon_intf_id, onu_id)
138 self.resource_mgrs[pon_intf_id].init_resource_map(
139 pon_intf_onu_id)
140
141 return onu_id
142
143 def get_flow_id(self, pon_intf_id, onu_id, uni_id, flow_store_cookie,
144 flow_category=None):
145 pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
Matt Jeanneret9dbce8c2019-03-23 14:35:00 -0400146 self.log.debug("get-flow-id", pon_intf_onu_id=pon_intf_onu_id)
William Kurkian6f436d02019-02-06 16:25:01 -0500147 try:
148 flow_ids = self.resource_mgrs[pon_intf_id]. \
149 get_current_flow_ids_for_onu(pon_intf_onu_id)
Matt Jeanneret9dbce8c2019-03-23 14:35:00 -0400150 self.log.debug("get-current-flow-ids-for-onu", flow_ids=flow_ids)
William Kurkian6f436d02019-02-06 16:25:01 -0500151 if flow_ids is not None:
152 for flow_id in flow_ids:
153 flows = self.get_flow_id_info(pon_intf_id, onu_id, uni_id, flow_id)
Matt Jeanneret9dbce8c2019-03-23 14:35:00 -0400154 self.log.debug("get-flow-id-info", flows=flows)
William Kurkian6f436d02019-02-06 16:25:01 -0500155 assert (isinstance(flows, list))
156 for flow in flows:
157
158 if flow_category is not None and \
159 'flow_category' in flow and \
160 flow['flow_category'] == flow_category:
161 return flow_id
162 if flow['flow_store_cookie'] == flow_store_cookie:
163 return flow_id
164 except Exception as e:
165 self.log.error("error-retrieving-flow-info", e=e)
166
167 flow_id = self.resource_mgrs[pon_intf_id].get_resource_id(
168 pon_intf_onu_id[0], PONResourceManager.FLOW_ID)
169 if flow_id is not None:
170 self.resource_mgrs[pon_intf_id].update_flow_id_for_onu(
171 pon_intf_onu_id, flow_id
172 )
173
Matt Jeanneret9dbce8c2019-03-23 14:35:00 -0400174 self.log.debug("return-flow-id", flow_id=flow_id)
William Kurkian6f436d02019-02-06 16:25:01 -0500175 return flow_id
176
177 def get_flow_id_info(self, pon_intf_id, onu_id, uni_id, flow_id):
178 pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
179 return self.resource_mgrs[pon_intf_id].get_flow_id_info(pon_intf_onu_id, flow_id)
180
181 def get_current_flow_ids_for_uni(self, pon_intf_id, onu_id, uni_id):
182 pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
183 return self.resource_mgrs[pon_intf_id].get_current_flow_ids_for_onu(pon_intf_onu_id)
184
185 def update_flow_id_info_for_uni(self, pon_intf_id, onu_id, uni_id, flow_id, flow_data):
186 pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
187 return self.resource_mgrs[pon_intf_id].update_flow_id_info_for_onu(
188 pon_intf_onu_id, flow_id, flow_data)
189
190 def get_alloc_id(self, pon_intf_onu_id):
191 # Derive the pon_intf from the pon_intf_onu_id tuple
192 pon_intf = pon_intf_onu_id[0]
193 alloc_id_list = self.resource_mgrs[pon_intf].get_current_alloc_ids_for_onu(
194 pon_intf_onu_id)
195
196 if alloc_id_list and len(alloc_id_list) > 0:
197 # Since we support only one alloc_id for the ONU at the moment,
198 # return the first alloc_id in the list, if available, for that
199 # ONU.
200 return alloc_id_list[0]
201
202 alloc_id = self.resource_mgrs[pon_intf].get_resource_id(
203 pon_intf_id=pon_intf,
204 resource_type=PONResourceManager.ALLOC_ID,
205 num_of_id=1
206 )
207 if alloc_id is None:
208 self.log.error("no-alloc-id-available")
209 return None
210
211 # update the resource map on KV store with the list of alloc_id
212 # allocated for the pon_intf_onu_id tuple
213 self.resource_mgrs[pon_intf].update_alloc_ids_for_onu(pon_intf_onu_id,
214 list(alloc_id))
215
216 return alloc_id
217
218 def get_current_gemport_ids_for_onu(self, pon_intf_onu_id):
219 pon_intf_id = pon_intf_onu_id[0]
220 return self.resource_mgrs[pon_intf_id].get_current_gemport_ids_for_onu(pon_intf_onu_id)
221
222 def get_current_alloc_ids_for_onu(self, pon_intf_onu_id):
223 pon_intf_id = pon_intf_onu_id[0]
224 alloc_ids = self.resource_mgrs[pon_intf_id].get_current_alloc_ids_for_onu(pon_intf_onu_id)
225 if alloc_ids is None:
226 return None
227 # We support only one tcont at the moment
228 return alloc_ids[0]
229
230 def update_gemports_ponport_to_onu_map_on_kv_store(self, gemport_list, pon_port, onu_id, uni_id):
231 for gemport in gemport_list:
232 pon_intf_gemport = (pon_port, gemport)
233 # This information is used when packet_indication is received and
234 # we need to derive the ONU Id for which the packet arrived based
235 # on the pon_intf and gemport available in the packet_indication
236 self.kv_store[str(pon_intf_gemport)] = ' '.join(map(str, (onu_id, uni_id)))
237
238 def get_onu_uni_from_ponport_gemport(self, pon_port, gemport):
239 pon_intf_gemport = (pon_port, gemport)
240 return tuple(map(int, self.kv_store[str(pon_intf_gemport)].split(' ')))
241
242 def get_gemport_id(self, pon_intf_onu_id, num_of_id=1):
243 # Derive the pon_intf and onu_id from the pon_intf_onu_id tuple
244 pon_intf = pon_intf_onu_id[0]
245 onu_id = pon_intf_onu_id[1]
246 uni_id = pon_intf_onu_id[2]
247 assert False, 'unused function'
248
249 gemport_id_list = self.resource_mgrs[pon_intf].get_current_gemport_ids_for_onu(
250 pon_intf_onu_id)
251 if gemport_id_list and len(gemport_id_list) > 0:
252 return gemport_id_list
253
254 gemport_id_list = self.resource_mgrs[pon_intf].get_resource_id(
255 pon_intf_id=pon_intf,
256 resource_type=PONResourceManager.GEMPORT_ID,
257 num_of_id=num_of_id
258 )
259
260 if gemport_id_list and len(gemport_id_list) == 0:
261 self.log.error("no-gemport-id-available")
262 return None
263
264 # update the resource map on KV store with the list of gemport_id
265 # allocated for the pon_intf_onu_id tuple
266 self.resource_mgrs[pon_intf].update_gemport_ids_for_onu(pon_intf_onu_id,
267 gemport_id_list)
268
269 self.update_gemports_ponport_to_onu_map_on_kv_store(gemport_id_list,
270 pon_intf, onu_id, uni_id)
271 return gemport_id_list
272
273 def free_onu_id(self, pon_intf_id, onu_id):
274 _ = self.resource_mgrs[pon_intf_id].free_resource_id(
275 pon_intf_id, PONResourceManager.ONU_ID, onu_id)
276
277 pon_intf_onu_id = (pon_intf_id, onu_id)
278 self.resource_mgrs[pon_intf_id].remove_resource_map(
279 pon_intf_onu_id)
280
281 def free_flow_id_for_uni(self, pon_intf_id, onu_id, uni_id, flow_id):
282 self.resource_mgrs[pon_intf_id].free_resource_id(
283 pon_intf_id, PONResourceManager.FLOW_ID, flow_id)
284 pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
285 self.resource_mgrs[pon_intf_id].update_flow_id_for_onu(pon_intf_onu_id,
286 flow_id, False)
287 self.resource_mgrs[pon_intf_id].remove_flow_id_info(pon_intf_onu_id,
288 flow_id)
289
290 def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
291
292 pon_intf_id = pon_intf_id_onu_id[0]
293 onu_id = pon_intf_id_onu_id[1]
294 alloc_ids = \
295 self.resource_mgrs[pon_intf_id].get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
296 self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
297 PONResourceManager.ALLOC_ID,
298 alloc_ids)
299
300 gemport_ids = \
301 self.resource_mgrs[pon_intf_id].get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
302 self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
303 PONResourceManager.GEMPORT_ID,
304 gemport_ids)
305
306 flow_ids = \
307 self.resource_mgrs[pon_intf_id].get_current_flow_ids_for_onu(pon_intf_id_onu_id)
308 self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
309 PONResourceManager.FLOW_ID,
310 flow_ids)
311
312 self.resource_mgrs[pon_intf_id].free_resource_id(pon_intf_id,
313 PONResourceManager.ONU_ID,
314 onu_id)
315
316 # Clear resource map associated with (pon_intf_id, gemport_id) tuple.
317 self.resource_mgrs[pon_intf_id].remove_resource_map(pon_intf_id_onu_id)
318
319 # Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
320 for gemport_id in gemport_ids:
321 del self.kv_store[str((pon_intf_id, gemport_id))]
322
323 def initialize_device_resource_range_and_pool(self, resource_mgr, global_resource_mgr, arange):
324 self.log.info("resource-range-pool-init", technology=resource_mgr.technology)
325
326 # first load from KV profiles
327 status = resource_mgr.init_resource_ranges_from_kv_store()
328 if not status:
329 self.log.info("failed-to-load-resource-range-from-kv-store", technology=resource_mgr.technology)
330
331 # Then apply device specific information. If KV doesn't exist
332 # or is broader than the device, the device's informationw ill
333 # dictate the range limits
334 self.log.info("using-device-info-to-init-pon-resource-ranges", technology=resource_mgr.technology)
335
336 onu_id_start = self.device_info.onu_id_start
337 onu_id_end = self.device_info.onu_id_end
338 onu_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.DEDICATED_PER_INTF
339 onu_id_shared_pool_id = None
340 alloc_id_start = self.device_info.alloc_id_start
341 alloc_id_end = self.device_info.alloc_id_end
342 alloc_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH # TODO EdgeCore/BAL limitation
343 alloc_id_shared_pool_id = None
344 gemport_id_start = self.device_info.gemport_id_start
345 gemport_id_end = self.device_info.gemport_id_end
346 gemport_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH # TODO EdgeCore/BAL limitation
347 gemport_id_shared_pool_id = None
348 flow_id_start = self.device_info.flow_id_start
349 flow_id_end = self.device_info.flow_id_end
350 flow_id_shared = openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH # TODO EdgeCore/BAL limitation
351 flow_id_shared_pool_id = None
352
353 global_pool_id = 0
354 for first_intf_pool_id in arange.intf_ids:
355 break
356
357 for pool in arange.pools:
358 shared_pool_id = global_pool_id if pool.sharing == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH else \
359 first_intf_pool_id if pool.sharing == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_SAME_TECH else \
360 None
361
362 if pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ONU_ID:
363 onu_id_start = pool.start
364 onu_id_end = pool.end
365 onu_id_shared = pool.sharing
366 onu_id_shared_pool_id = shared_pool_id
367 elif pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.ALLOC_ID:
368 alloc_id_start = pool.start
369 alloc_id_end = pool.end
370 alloc_id_shared = pool.sharing
371 alloc_id_shared_pool_id = shared_pool_id
372 elif pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.GEMPORT_ID:
373 gemport_id_start = pool.start
374 gemport_id_end = pool.end
375 gemport_id_shared = pool.sharing
376 gemport_id_shared_pool_id = shared_pool_id
377 elif pool.type == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.FLOW_ID:
378 flow_id_start = pool.start
379 flow_id_end = pool.end
380 flow_id_shared = pool.sharing
381 flow_id_shared_pool_id = shared_pool_id
382
383 self.log.info("device-info-init", technology=arange.technology,
384 onu_id_start=onu_id_start, onu_id_end=onu_id_end, onu_id_shared_pool_id=onu_id_shared_pool_id,
385 alloc_id_start=alloc_id_start, alloc_id_end=alloc_id_end,
386 alloc_id_shared_pool_id=alloc_id_shared_pool_id,
387 gemport_id_start=gemport_id_start, gemport_id_end=gemport_id_end,
388 gemport_id_shared_pool_id=gemport_id_shared_pool_id,
389 flow_id_start_idx=flow_id_start,
390 flow_id_end_idx=flow_id_end,
391 flow_id_shared_pool_id=flow_id_shared_pool_id,
392 intf_ids=arange.intf_ids,
393 uni_id_start_idx=0,
394 uni_id_end_idx=self.max_uni_id_per_onu)
395
396 resource_mgr.init_default_pon_resource_ranges(
397 onu_id_start_idx=onu_id_start,
398 onu_id_end_idx=onu_id_end,
399 onu_id_shared_pool_id=onu_id_shared_pool_id,
400 alloc_id_start_idx=alloc_id_start,
401 alloc_id_end_idx=alloc_id_end,
402 alloc_id_shared_pool_id=alloc_id_shared_pool_id,
403 gemport_id_start_idx=gemport_id_start,
404 gemport_id_end_idx=gemport_id_end,
405 gemport_id_shared_pool_id=gemport_id_shared_pool_id,
406 flow_id_start_idx=flow_id_start,
407 flow_id_end_idx=flow_id_end,
408 flow_id_shared_pool_id=flow_id_shared_pool_id,
409 uni_id_start_idx=0, uni_id_end_idx=self.max_uni_id_per_onu,
410 num_of_pon_ports=self.device_info.pon_ports,
411 intf_ids=arange.intf_ids
412 )
413
414 # For global sharing, make sure to refresh both local and global resource manager instances' range
415 if global_resource_mgr is not self:
416 if onu_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
417 global_resource_mgr.update_ranges(onu_id_start_idx=onu_id_start, onu_id_end_idx=onu_id_end)
418 resource_mgr.update_ranges(onu_id_start_idx=onu_id_start, onu_id_end_idx=onu_id_end,
419 onu_id_shared_resource_mgr=global_resource_mgr)
420
421 if alloc_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
422 global_resource_mgr.update_ranges(alloc_id_start_idx=alloc_id_start, alloc_id_end_idx=alloc_id_end)
423 resource_mgr.update_ranges(alloc_id_start_idx=alloc_id_start, alloc_id_end_idx=alloc_id_end,
424 alloc_id_shared_resource_mgr=global_resource_mgr)
425
426 if gemport_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
427 global_resource_mgr.update_ranges(gemport_id_start_idx=gemport_id_start,
428 gemport_id_end_idx=gemport_id_end)
429 resource_mgr.update_ranges(gemport_id_start_idx=gemport_id_start, gemport_id_end_idx=gemport_id_end,
430 gemport_id_shared_resource_mgr=global_resource_mgr)
431
432 if flow_id_shared == openolt_pb2.DeviceInfo.DeviceResourceRanges.Pool.SHARED_BY_ALL_INTF_ALL_TECH:
433 global_resource_mgr.update_ranges(flow_id_start_idx=flow_id_start,
434 flow_id_end_idx=flow_id_end)
435 resource_mgr.update_ranges(flow_id_start_idx=flow_id_start, flow_id_end_idx=flow_id_end,
436 flow_id_shared_resource_mgr=global_resource_mgr)
437
438 # Make sure loaded range fits the platform bit encoding ranges
439 resource_mgr.update_ranges(uni_id_start_idx=0, uni_id_end_idx=OpenOltPlatform.MAX_UNIS_PER_ONU-1)