blob: 62a1e51792e3da55c7e76b8dc26151a10d94427d [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
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050016from __future__ import absolute_import
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050017import structlog
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050018from twisted.internet import reactor
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050019from twisted.internet.defer import inlineCallbacks, TimeoutError, failure
20from pyvoltha.adapters.extensions.omci.omci_me import Ont2G, OmciNullPointer, PriorityQueueFrame, \
21 Ieee8021pMapperServiceProfileFrame
Matt Jeanneret72f96fc2019-02-11 10:53:05 -050022from pyvoltha.adapters.extensions.omci.tasks.task import Task
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050023from pyvoltha.adapters.extensions.omci.omci_defs import EntityOperations, ReasonCodes
24from pyvoltha.adapters.extensions.omci.omci_entities import OntG, Tcont, PriorityQueueG
25from pon_port import TASK_PRIORITY
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050026
aishwaryarana0180594ce2019-07-26 15:31:14 -050027
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050028OP = EntityOperations
29RC = ReasonCodes
30
31
32class TechProfileDownloadFailure(Exception):
33 """
34 This error is raised by default when the download fails
35 """
36
37
38class TechProfileResourcesFailure(Exception):
39 """
40 This error is raised by when one or more resources required is not available
41 """
42
43
44class BrcmTpServiceSpecificTask(Task):
45 """
46 OpenOMCI Tech-Profile Download Task
47
48 """
49
50 name = "Broadcom Tech-Profile Download Task"
51
52 def __init__(self, omci_agent, handler, uni_id):
53 """
54 Class initialization
55
56 :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
Matt Jeanneret810148b2019-09-29 12:44:01 -040057 :param handler: (BrcmOpenomciOnuHandler) ONU Device Handler Instance
58 :param uni_id: (int) numeric id of the uni port on the onu device, starts at 0
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050059 """
60 log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id)
61 log.debug('function-entry')
62
63 super(BrcmTpServiceSpecificTask, self).__init__(BrcmTpServiceSpecificTask.name,
64 omci_agent,
65 handler.device_id,
66 priority=TASK_PRIORITY,
67 exclusive=True)
68
69 self.log = log
70
71 self._onu_device = omci_agent.get_device(handler.device_id)
72 self._local_deferred = None
73
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050074 self._uni_port = handler.uni_ports[uni_id]
75 assert self._uni_port.uni_id == uni_id
76
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050077 # Entity IDs. IDs with values can probably be most anything for most ONUs,
78 # IDs set to None are discovered/set
79
80 self._mac_bridge_service_profile_entity_id = \
81 handler.mac_bridge_service_profile_entity_id
82 self._ieee_mapper_service_profile_entity_id = \
83 handler.pon_port.ieee_mapper_service_profile_entity_id
84 self._mac_bridge_port_ani_entity_id = \
85 handler.pon_port.mac_bridge_port_ani_entity_id
86 self._gal_enet_profile_entity_id = \
87 handler.gal_enet_profile_entity_id
88
89 # Extract the current set of TCONT and GEM Ports from the Handler's pon_port that are
90 # relevant to this task's UNI. It won't change. But, the underlying pon_port may change
91 # due to additional tasks on different UNIs. So, it we cannot use the pon_port affter
92 # this initializer
93 self._tconts = []
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050094 for tcont in list(handler.pon_port.tconts.values()):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050095 if tcont.uni_id is not None and tcont.uni_id != self._uni_port.uni_id: continue
96 self._tconts.append(tcont)
97
98 self._gem_ports = []
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050099 for gem_port in list(handler.pon_port.gem_ports.values()):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500100 if gem_port.uni_id is not None and gem_port.uni_id != self._uni_port.uni_id: continue
101 self._gem_ports.append(gem_port)
102
103 self.tcont_me_to_queue_map = dict()
104 self.uni_port_to_queue_map = dict()
105
106 def cancel_deferred(self):
107 self.log.debug('function-entry')
108 super(BrcmTpServiceSpecificTask, self).cancel_deferred()
109
110 d, self._local_deferred = self._local_deferred, None
111 try:
112 if d is not None and not d.called:
113 d.cancel()
114 except:
115 pass
116
117 def start(self):
118 """
119 Start the Tech-Profile Download
120 """
121 self.log.debug('function-entry')
122 super(BrcmTpServiceSpecificTask, self).start()
123 self._local_deferred = reactor.callLater(0, self.perform_service_specific_steps)
124
125 def stop(self):
126 """
127 Shutdown Tech-Profile download tasks
128 """
129 self.log.debug('function-entry')
130 self.log.debug('stopping')
131
132 self.cancel_deferred()
133 super(BrcmTpServiceSpecificTask, self).stop()
134
135 def check_status_and_state(self, results, operation=''):
136 """
137 Check the results of an OMCI response. An exception is thrown
138 if the task was cancelled or an error was detected.
139
140 :param results: (OmciFrame) OMCI Response frame
141 :param operation: (str) what operation was being performed
142 :return: True if successful, False if the entity existed (already created)
143 """
144 self.log.debug('function-entry')
145
146 omci_msg = results.fields['omci_message'].fields
147 status = omci_msg['success_code']
148 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
149 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
150 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
151
Matt Jeannerete8fc53e2019-04-13 15:58:33 -0400152 self.log.debug("OMCI Result", operation=operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500153 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
154
155 if status == RC.Success:
156 self.strobe_watchdog()
157 return True
158
159 elif status == RC.InstanceExists:
160 return False
161
162 raise TechProfileDownloadFailure(
163 '{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
164 .format(operation, status, error_mask, failed_mask, unsupported_mask))
165
166 @inlineCallbacks
167 def perform_service_specific_steps(self):
168 self.log.debug('function-entry')
169
170 omci_cc = self._onu_device.omci_cc
aishwaryarana0180594ce2019-07-26 15:31:14 -0500171 gem_pq_associativity = dict()
172 pq_to_related_port = dict()
173 is_related_ports_configurable = False
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500174
175 try:
176 ################################################################################
177 # TCONTS
178 #
179 # EntityID will be referenced by:
180 # - GemPortNetworkCtp
181 # References:
182 # - ONU created TCONT (created on ONU startup)
183
184 tcont_idents = self._onu_device.query_mib(Tcont.class_id)
185 self.log.debug('tcont-idents', tcont_idents=tcont_idents)
186
187 for tcont in self._tconts:
188 self.log.debug('tcont-loop', tcont=tcont)
189
190 if tcont.entity_id is None:
191 free_entity_id = None
192 for k, v in tcont_idents.items():
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -0500193 if not isinstance(v, dict):
194 continue
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500195 alloc_check = v.get('attributes', {}).get('alloc_id', 0)
196 # Some onu report both to indicate an available tcont
197 if alloc_check == 0xFF or alloc_check == 0xFFFF:
198 free_entity_id = k
199 break
200
201 self.log.debug('tcont-loop-free', free_entity_id=free_entity_id, alloc_id=tcont.alloc_id)
202
203 if free_entity_id is None:
204 self.log.error('no-available-tconts')
205 break
206
207 # Also assign entity id within tcont object
208 results = yield tcont.add_to_hardware(omci_cc, free_entity_id)
209 self.check_status_and_state(results, 'new-tcont-added')
210 else:
211 # likely already added given entity_id is set, but no harm in doing it again
212 results = yield tcont.add_to_hardware(omci_cc, tcont.entity_id)
213 self.check_status_and_state(results, 'existing-tcont-added')
214
215 ################################################################################
216 # GEMS (GemPortNetworkCtp and GemInterworkingTp)
217 #
218 # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
219 # GemInterworkingTp ME could be different since it has an attribute to specify
220 # the GemPortNetworkCtp entity id.
221 #
222 # for the GemPortNetworkCtp ME
223 #
224 # GemPortNetworkCtp
225 # EntityID will be referenced by:
226 # - GemInterworkingTp
227 # References:
228 # - TCONT
229 # - Hardcoded upstream TM Entity ID
230 # - (Possibly in Future) Upstream Traffic descriptor profile pointer
231 #
232 # GemInterworkingTp
233 # EntityID will be referenced by:
234 # - Ieee8021pMapperServiceProfile
235 # References:
236 # - GemPortNetworkCtp
237 # - Ieee8021pMapperServiceProfile
238 # - GalEthernetProfile
239 #
240
241 onu_g = self._onu_device.query_mib(OntG.class_id)
242 # If the traffic management option attribute in the ONU-G ME is 0
243 # (priority controlled) or 2 (priority and rate controlled), this
244 # pointer specifies the priority queue ME serving this GEM port
245 # network CTP. If the traffic management option attribute is 1
246 # (rate controlled), this attribute redundantly points to the
247 # T-CONT serving this GEM port network CTP.
248 traffic_mgmt_opt = \
249 onu_g.get('attributes', {}).get('traffic_management_options', 0)
250 self.log.debug("traffic-mgmt-option", traffic_mgmt_opt=traffic_mgmt_opt)
251
252 prior_q = self._onu_device.query_mib(PriorityQueueG.class_id)
253 for k, v in prior_q.items():
254 self.log.debug("prior-q", k=k, v=v)
255
256 try:
257 _ = iter(v)
258 except TypeError:
259 continue
260
261 if 'instance_id' in v:
262 related_port = v['attributes']['related_port']
aishwaryarana0180594ce2019-07-26 15:31:14 -0500263 pq_to_related_port[k] = related_port
264
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500265 if v['instance_id'] & 0b1000000000000000:
266 tcont_me = (related_port & 0xffff0000) >> 16
267 if tcont_me not in self.tcont_me_to_queue_map:
268 self.log.debug("prior-q-related-port-and-tcont-me",
269 related_port=related_port,
270 tcont_me=tcont_me)
271 self.tcont_me_to_queue_map[tcont_me] = list()
272
273 self.tcont_me_to_queue_map[tcont_me].append(k)
274 else:
275 uni_port = (related_port & 0xffff0000) >> 16
276 if uni_port == self._uni_port.entity_id:
277 if uni_port not in self.uni_port_to_queue_map:
278 self.log.debug("prior-q-related-port-and-uni-port-me",
279 related_port=related_port,
280 uni_port_me=uni_port)
281 self.uni_port_to_queue_map[uni_port] = list()
282
283 self.uni_port_to_queue_map[uni_port].append(k)
284
285
286 self.log.debug("ul-prior-q", ul_prior_q=self.tcont_me_to_queue_map)
287 self.log.debug("dl-prior-q", dl_prior_q=self.uni_port_to_queue_map)
288
289 for gem_port in self._gem_ports:
290 # TODO: Traffic descriptor will be available after meter bands are available
291 tcont = gem_port.tcont
292 if tcont is None:
293 self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
294 continue
295
296 ul_prior_q_entity_id = None
297 dl_prior_q_entity_id = None
298 if gem_port.direction == "upstream" or \
299 gem_port.direction == "bi-directional":
300
301 # Sort the priority queue list in order of priority.
302 # 0 is highest priority and 0x0fff is lowest.
303 self.tcont_me_to_queue_map[tcont.entity_id].sort()
304 self.uni_port_to_queue_map[self._uni_port.entity_id].sort()
aishwaryarana0180594ce2019-07-26 15:31:14 -0500305 # Get the priority queue by indexing the priority value of the gemport.
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500306 # The self.tcont_me_to_queue_map and self.uni_port_to_queue_map
307 # have priority queue entities ordered in descending order
308 # of priority
aishwaryarana0180594ce2019-07-26 15:31:14 -0500309
310 ul_prior_q_entity_id = \
311 self.tcont_me_to_queue_map[tcont.entity_id][gem_port.priority_q]
312 dl_prior_q_entity_id = \
313 self.uni_port_to_queue_map[self._uni_port.entity_id][gem_port.priority_q]
314
315 pq_attributes = dict()
316 pq_attributes["pq_entity_id"] = ul_prior_q_entity_id
317 pq_attributes["weight"] = gem_port.weight
318 pq_attributes["scheduling_policy"] = gem_port.scheduling_policy
319 pq_attributes["priority_q"] = gem_port.priority_q
320 gem_pq_associativity[gem_port.entity_id] = pq_attributes
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500321
322 assert ul_prior_q_entity_id is not None and \
323 dl_prior_q_entity_id is not None
324
325 # TODO: Need to restore on failure. Need to check status/results
326 results = yield gem_port.add_to_hardware(omci_cc,
327 tcont.entity_id,
328 self._ieee_mapper_service_profile_entity_id +
329 self._uni_port.mac_bridge_port_num,
330 self._gal_enet_profile_entity_id,
331 ul_prior_q_entity_id, dl_prior_q_entity_id)
332 self.check_status_and_state(results, 'assign-gem-port')
333 elif gem_port.direction == "downstream":
334 # Downstream is inverse of upstream
335 # TODO: could also be a case of multicast. Not supported for now
336 self.log.debug("skipping-downstream-gem", gem_port=gem_port)
337 pass
338
339 ################################################################################
aishwaryarana0180594ce2019-07-26 15:31:14 -0500340 # Update the PriorityQeue Attributes for the PQ Associated with Gemport
341 #
342 # Entityt ID was created prior to this call. This is a set
343 #
344 #
345
346 ont2g = self._onu_device.query_mib(Ont2G.class_id)
347 qos_config_flexibility_ie = ont2g.get(0, {}).get('attributes', {}).\
348 get('qos_configuration_flexibility', None)
349 self.log.debug("qos_config_flexibility",
350 qos_config_flexibility=qos_config_flexibility_ie)
351
352 if qos_config_flexibility_ie & 0b100000:
353 is_related_ports_configurable = True
354
355 for k, v in gem_pq_associativity.items():
356 if v["scheduling_policy"] == "WRR":
357 self.log.debug("updating-pq-weight")
358 msg = PriorityQueueFrame(v["pq_entity_id"], weight=v["weight"])
359 frame = msg.set()
360 results = yield omci_cc.send(frame)
361 self.check_status_and_state(results, 'set-priority-queues-weight')
362 elif v["scheduling_policy"] == "StrictPriority" and \
363 is_related_ports_configurable:
364 self.log.debug("updating-pq-priority")
365 related_port = pq_to_related_port[v["pq_entity_id"]]
366 related_port = related_port & 0xffff0000
367 related_port = related_port | v['priority_q'] # Set priority
368 msg = PriorityQueueFrame(v["pq_entity_id"], related_port=related_port)
369 frame = msg.set()
370 results = yield omci_cc.send(frame)
371 self.check_status_and_state(results, 'set-priority-queues-priority')
372
373
374 ################################################################################
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500375 # Update the IEEE 802.1p Mapper Service Profile config
376 #
377 # EntityID was created prior to this call. This is a set
378 #
379 # References:
380 # - Gem Interwork TPs are set here
381 #
382
383 gem_entity_ids = [OmciNullPointer] * 8
384 for gem_port in self._gem_ports:
385 self.log.debug("tp-gem-port", entity_id=gem_port.entity_id, uni_id=gem_port.uni_id)
386
387 if gem_port.direction == "upstream" or \
388 gem_port.direction == "bi-directional":
389 for i, p in enumerate(reversed(gem_port.pbit_map)):
390 if p == '1':
391 gem_entity_ids[i] = gem_port.entity_id
392 elif gem_port.direction == "downstream":
393 # Downstream gem port p-bit mapper is inverse of upstream
394 # TODO: Could also be a case of multicast. Not supported for now
395 pass
396
397 msg = Ieee8021pMapperServiceProfileFrame(
398 self._ieee_mapper_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # 802.1p mapper Service Mapper Profile ID
399 interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
400 )
401 frame = msg.set()
402 self.log.debug('openomci-msg', omci_msg=msg)
403 results = yield omci_cc.send(frame)
404 self.check_status_and_state(results, 'set-8021p-mapper-service-profile-ul')
405
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500406 self.deferred.callback("tech-profile-download-success")
407
408 except TimeoutError as e:
Matt Jeanneret810148b2019-09-29 12:44:01 -0400409 self.log.warn('rx-timeout-tech-profile', e=e)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500410 self.deferred.errback(failure.Failure(e))
411
412 except Exception as e:
Matt Jeanneret810148b2019-09-29 12:44:01 -0400413 self.log.exception('omci-setup-tech-profile', e=e)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500414 self.deferred.errback(failure.Failure(e))