blob: 1a9eb1e1486576a205399d15ee0a6a46de87d23c [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
16import structlog
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050017from twisted.internet import reactor
18from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
Matt Jeanneret72f96fc2019-02-11 10:53:05 -050019from pyvoltha.adapters.extensions.omci.omci_me import *
20from pyvoltha.adapters.extensions.omci.tasks.task import Task
21from pyvoltha.adapters.extensions.omci.omci_defs import *
22from adapters.brcm_openomci_onu.uni_port import *
23from adapters.brcm_openomci_onu.pon_port \
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050024 import BRDCM_DEFAULT_VLAN, TASK_PRIORITY, DEFAULT_TPID, DEFAULT_GEM_PAYLOAD
25
26OP = EntityOperations
27RC = ReasonCodes
28
29
30class TechProfileDownloadFailure(Exception):
31 """
32 This error is raised by default when the download fails
33 """
34
35
36class TechProfileResourcesFailure(Exception):
37 """
38 This error is raised by when one or more resources required is not available
39 """
40
41
42class BrcmTpServiceSpecificTask(Task):
43 """
44 OpenOMCI Tech-Profile Download Task
45
46 """
47
48 name = "Broadcom Tech-Profile Download Task"
49
50 def __init__(self, omci_agent, handler, uni_id):
51 """
52 Class initialization
53
54 :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
55 :param device_id: (str) ONU Device ID
56 """
57 log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id)
58 log.debug('function-entry')
59
60 super(BrcmTpServiceSpecificTask, self).__init__(BrcmTpServiceSpecificTask.name,
61 omci_agent,
62 handler.device_id,
63 priority=TASK_PRIORITY,
64 exclusive=True)
65
66 self.log = log
67
68 self._onu_device = omci_agent.get_device(handler.device_id)
69 self._local_deferred = None
70
71 # Frame size
72 self._max_gem_payload = DEFAULT_GEM_PAYLOAD
73
74 self._uni_port = handler.uni_ports[uni_id]
75 assert self._uni_port.uni_id == uni_id
76
77 # Port numbers
78 self._input_tpid = DEFAULT_TPID
79 self._output_tpid = DEFAULT_TPID
80
81 self._vlan_tcis_1 = BRDCM_DEFAULT_VLAN
82 self._cvid = BRDCM_DEFAULT_VLAN
83 self._vlan_config_entity_id = self._vlan_tcis_1
84
85 # Entity IDs. IDs with values can probably be most anything for most ONUs,
86 # IDs set to None are discovered/set
87
88 self._mac_bridge_service_profile_entity_id = \
89 handler.mac_bridge_service_profile_entity_id
90 self._ieee_mapper_service_profile_entity_id = \
91 handler.pon_port.ieee_mapper_service_profile_entity_id
92 self._mac_bridge_port_ani_entity_id = \
93 handler.pon_port.mac_bridge_port_ani_entity_id
94 self._gal_enet_profile_entity_id = \
95 handler.gal_enet_profile_entity_id
96
97 # Extract the current set of TCONT and GEM Ports from the Handler's pon_port that are
98 # relevant to this task's UNI. It won't change. But, the underlying pon_port may change
99 # due to additional tasks on different UNIs. So, it we cannot use the pon_port affter
100 # this initializer
101 self._tconts = []
102 for tcont in handler.pon_port.tconts.itervalues():
103 if tcont.uni_id is not None and tcont.uni_id != self._uni_port.uni_id: continue
104 self._tconts.append(tcont)
105
106 self._gem_ports = []
107 for gem_port in handler.pon_port.gem_ports.itervalues():
108 if gem_port.uni_id is not None and gem_port.uni_id != self._uni_port.uni_id: continue
109 self._gem_ports.append(gem_port)
110
111 self.tcont_me_to_queue_map = dict()
112 self.uni_port_to_queue_map = dict()
113
114 def cancel_deferred(self):
115 self.log.debug('function-entry')
116 super(BrcmTpServiceSpecificTask, self).cancel_deferred()
117
118 d, self._local_deferred = self._local_deferred, None
119 try:
120 if d is not None and not d.called:
121 d.cancel()
122 except:
123 pass
124
125 def start(self):
126 """
127 Start the Tech-Profile Download
128 """
129 self.log.debug('function-entry')
130 super(BrcmTpServiceSpecificTask, self).start()
131 self._local_deferred = reactor.callLater(0, self.perform_service_specific_steps)
132
133 def stop(self):
134 """
135 Shutdown Tech-Profile download tasks
136 """
137 self.log.debug('function-entry')
138 self.log.debug('stopping')
139
140 self.cancel_deferred()
141 super(BrcmTpServiceSpecificTask, self).stop()
142
143 def check_status_and_state(self, results, operation=''):
144 """
145 Check the results of an OMCI response. An exception is thrown
146 if the task was cancelled or an error was detected.
147
148 :param results: (OmciFrame) OMCI Response frame
149 :param operation: (str) what operation was being performed
150 :return: True if successful, False if the entity existed (already created)
151 """
152 self.log.debug('function-entry')
153
154 omci_msg = results.fields['omci_message'].fields
155 status = omci_msg['success_code']
156 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
157 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
158 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
159
160 self.log.debug("OMCI Result: %s", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
161 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
162
163 if status == RC.Success:
164 self.strobe_watchdog()
165 return True
166
167 elif status == RC.InstanceExists:
168 return False
169
170 raise TechProfileDownloadFailure(
171 '{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
172 .format(operation, status, error_mask, failed_mask, unsupported_mask))
173
174 @inlineCallbacks
175 def perform_service_specific_steps(self):
176 self.log.debug('function-entry')
177
178 omci_cc = self._onu_device.omci_cc
179
180 try:
181 ################################################################################
182 # TCONTS
183 #
184 # EntityID will be referenced by:
185 # - GemPortNetworkCtp
186 # References:
187 # - ONU created TCONT (created on ONU startup)
188
189 tcont_idents = self._onu_device.query_mib(Tcont.class_id)
190 self.log.debug('tcont-idents', tcont_idents=tcont_idents)
191
192 for tcont in self._tconts:
193 self.log.debug('tcont-loop', tcont=tcont)
194
195 if tcont.entity_id is None:
196 free_entity_id = None
197 for k, v in tcont_idents.items():
198 alloc_check = v.get('attributes', {}).get('alloc_id', 0)
199 # Some onu report both to indicate an available tcont
200 if alloc_check == 0xFF or alloc_check == 0xFFFF:
201 free_entity_id = k
202 break
203
204 self.log.debug('tcont-loop-free', free_entity_id=free_entity_id, alloc_id=tcont.alloc_id)
205
206 if free_entity_id is None:
207 self.log.error('no-available-tconts')
208 break
209
210 # Also assign entity id within tcont object
211 results = yield tcont.add_to_hardware(omci_cc, free_entity_id)
212 self.check_status_and_state(results, 'new-tcont-added')
213 else:
214 # likely already added given entity_id is set, but no harm in doing it again
215 results = yield tcont.add_to_hardware(omci_cc, tcont.entity_id)
216 self.check_status_and_state(results, 'existing-tcont-added')
217
218 ################################################################################
219 # GEMS (GemPortNetworkCtp and GemInterworkingTp)
220 #
221 # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
222 # GemInterworkingTp ME could be different since it has an attribute to specify
223 # the GemPortNetworkCtp entity id.
224 #
225 # for the GemPortNetworkCtp ME
226 #
227 # GemPortNetworkCtp
228 # EntityID will be referenced by:
229 # - GemInterworkingTp
230 # References:
231 # - TCONT
232 # - Hardcoded upstream TM Entity ID
233 # - (Possibly in Future) Upstream Traffic descriptor profile pointer
234 #
235 # GemInterworkingTp
236 # EntityID will be referenced by:
237 # - Ieee8021pMapperServiceProfile
238 # References:
239 # - GemPortNetworkCtp
240 # - Ieee8021pMapperServiceProfile
241 # - GalEthernetProfile
242 #
243
244 onu_g = self._onu_device.query_mib(OntG.class_id)
245 # If the traffic management option attribute in the ONU-G ME is 0
246 # (priority controlled) or 2 (priority and rate controlled), this
247 # pointer specifies the priority queue ME serving this GEM port
248 # network CTP. If the traffic management option attribute is 1
249 # (rate controlled), this attribute redundantly points to the
250 # T-CONT serving this GEM port network CTP.
251 traffic_mgmt_opt = \
252 onu_g.get('attributes', {}).get('traffic_management_options', 0)
253 self.log.debug("traffic-mgmt-option", traffic_mgmt_opt=traffic_mgmt_opt)
254
255 prior_q = self._onu_device.query_mib(PriorityQueueG.class_id)
256 for k, v in prior_q.items():
257 self.log.debug("prior-q", k=k, v=v)
258
259 try:
260 _ = iter(v)
261 except TypeError:
262 continue
263
264 if 'instance_id' in v:
265 related_port = v['attributes']['related_port']
266 if v['instance_id'] & 0b1000000000000000:
267 tcont_me = (related_port & 0xffff0000) >> 16
268 if tcont_me not in self.tcont_me_to_queue_map:
269 self.log.debug("prior-q-related-port-and-tcont-me",
270 related_port=related_port,
271 tcont_me=tcont_me)
272 self.tcont_me_to_queue_map[tcont_me] = list()
273
274 self.tcont_me_to_queue_map[tcont_me].append(k)
275 else:
276 uni_port = (related_port & 0xffff0000) >> 16
277 if uni_port == self._uni_port.entity_id:
278 if uni_port not in self.uni_port_to_queue_map:
279 self.log.debug("prior-q-related-port-and-uni-port-me",
280 related_port=related_port,
281 uni_port_me=uni_port)
282 self.uni_port_to_queue_map[uni_port] = list()
283
284 self.uni_port_to_queue_map[uni_port].append(k)
285
286
287 self.log.debug("ul-prior-q", ul_prior_q=self.tcont_me_to_queue_map)
288 self.log.debug("dl-prior-q", dl_prior_q=self.uni_port_to_queue_map)
289
290 for gem_port in self._gem_ports:
291 # TODO: Traffic descriptor will be available after meter bands are available
292 tcont = gem_port.tcont
293 if tcont is None:
294 self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
295 continue
296
297 ul_prior_q_entity_id = None
298 dl_prior_q_entity_id = None
299 if gem_port.direction == "upstream" or \
300 gem_port.direction == "bi-directional":
301
302 # Sort the priority queue list in order of priority.
303 # 0 is highest priority and 0x0fff is lowest.
304 self.tcont_me_to_queue_map[tcont.entity_id].sort()
305 self.uni_port_to_queue_map[self._uni_port.entity_id].sort()
306 # Get the priority queue associated with p-bit that is
307 # mapped to the gem port.
308 # p-bit-7 is highest priority and p-bit-0 is lowest
309 # Gem port associated with p-bit-7 should be mapped to
310 # highest priority queue and gem port associated with p-bit-0
311 # should be mapped to lowest priority queue.
312 # The self.tcont_me_to_queue_map and self.uni_port_to_queue_map
313 # have priority queue entities ordered in descending order
314 # of priority
315 for i, p in enumerate(gem_port.pbit_map):
316 if p == '1':
317 ul_prior_q_entity_id = \
318 self.tcont_me_to_queue_map[tcont.entity_id][i]
319 dl_prior_q_entity_id = \
320 self.uni_port_to_queue_map[self._uni_port.entity_id][i]
321 break
322
323 assert ul_prior_q_entity_id is not None and \
324 dl_prior_q_entity_id is not None
325
326 # TODO: Need to restore on failure. Need to check status/results
327 results = yield gem_port.add_to_hardware(omci_cc,
328 tcont.entity_id,
329 self._ieee_mapper_service_profile_entity_id +
330 self._uni_port.mac_bridge_port_num,
331 self._gal_enet_profile_entity_id,
332 ul_prior_q_entity_id, dl_prior_q_entity_id)
333 self.check_status_and_state(results, 'assign-gem-port')
334 elif gem_port.direction == "downstream":
335 # Downstream is inverse of upstream
336 # TODO: could also be a case of multicast. Not supported for now
337 self.log.debug("skipping-downstream-gem", gem_port=gem_port)
338 pass
339
340 ################################################################################
341 # Update the IEEE 802.1p Mapper Service Profile config
342 #
343 # EntityID was created prior to this call. This is a set
344 #
345 # References:
346 # - Gem Interwork TPs are set here
347 #
348
349 gem_entity_ids = [OmciNullPointer] * 8
350 for gem_port in self._gem_ports:
351 self.log.debug("tp-gem-port", entity_id=gem_port.entity_id, uni_id=gem_port.uni_id)
352
353 if gem_port.direction == "upstream" or \
354 gem_port.direction == "bi-directional":
355 for i, p in enumerate(reversed(gem_port.pbit_map)):
356 if p == '1':
357 gem_entity_ids[i] = gem_port.entity_id
358 elif gem_port.direction == "downstream":
359 # Downstream gem port p-bit mapper is inverse of upstream
360 # TODO: Could also be a case of multicast. Not supported for now
361 pass
362
363 msg = Ieee8021pMapperServiceProfileFrame(
364 self._ieee_mapper_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # 802.1p mapper Service Mapper Profile ID
365 interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
366 )
367 frame = msg.set()
368 self.log.debug('openomci-msg', omci_msg=msg)
369 results = yield omci_cc.send(frame)
370 self.check_status_and_state(results, 'set-8021p-mapper-service-profile-ul')
371
372 ################################################################################
373 # Create Extended VLAN Tagging Operation config (PON-side)
374 #
375 # EntityID relates to the VLAN TCIS
376 # References:
377 # - VLAN TCIS from previously created VLAN Tagging filter data
378 # - PPTP Ethernet or VEIP UNI
379 #
380
381 # TODO: do this for all uni/ports...
382 # TODO: magic. static variable for assoc_type
383
384 # default to PPTP
385 if self._uni_port.type is UniType.VEIP:
386 association_type = 10
387 elif self._uni_port.type is UniType.PPTP:
388 association_type = 2
389 else:
390 association_type = 2
391
392 attributes = dict(
393 association_type=association_type, # Assoc Type, PPTP/VEIP Ethernet UNI
394 associated_me_pointer=self._uni_port.entity_id, # Assoc ME, PPTP/VEIP Entity Id
395
396 # See VOL-1311 - Need to set table during create to avoid exception
397 # trying to read back table during post-create-read-missing-attributes
398 # But, because this is a R/W attribute. Some ONU may not accept the
399 # value during create. It is repeated again in a set below.
400 input_tpid=self._input_tpid, # input TPID
401 output_tpid=self._output_tpid, # output TPID
402 )
403
404 msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
405 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
406 attributes=attributes
407 )
408
409 frame = msg.create()
410 self.log.debug('openomci-msg', omci_msg=msg)
411 results = yield omci_cc.send(frame)
412 self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
413
414 attributes = dict(
415 # Specifies the TPIDs in use and that operations in the downstream direction are
416 # inverse to the operations in the upstream direction
417 input_tpid=self._input_tpid, # input TPID
418 output_tpid=self._output_tpid, # output TPID
419 downstream_mode=0, # inverse of upstream
420 )
421
422 msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
423 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
424 attributes=attributes
425 )
426
427 frame = msg.set()
428 self.log.debug('openomci-msg', omci_msg=msg)
429 results = yield omci_cc.send(frame)
430 self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
431
432 attributes = dict(
433 # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
434 # Treatment Inner Vlan Id : 2
435
436 # Update uni side extended vlan filter
437 # filter for untagged
438 # probably for eapol
439 # TODO: lots of magic
440 # TODO: magic 0x1000 / 4096?
441 received_frame_vlan_tagging_operation_table=
442 VlanTaggingOperation(
443 filter_outer_priority=15, # This entry is not a double-tag rule
444 filter_outer_vid=4096, # Do not filter on the outer VID value
445 filter_outer_tpid_de=0, # Do not filter on the outer TPID field
446
447 filter_inner_priority=15,
448 filter_inner_vid=4096,
449 filter_inner_tpid_de=0,
450 filter_ether_type=0,
451
452 treatment_tags_to_remove=0,
453 treatment_outer_priority=15,
454 treatment_outer_vid=0,
455 treatment_outer_tpid_de=0,
456
457 treatment_inner_priority=0,
458 treatment_inner_vid=self._cvid,
459 treatment_inner_tpid_de=4,
460 )
461 )
462
463 msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
464 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
465 attributes=attributes
466 )
467
468 frame = msg.set()
469 self.log.debug('openomci-msg', omci_msg=msg)
470 results = yield omci_cc.send(frame)
471 self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-table')
472
473 self.deferred.callback("tech-profile-download-success")
474
475 except TimeoutError as e:
476 self.log.warn('rx-timeout-2', e=e)
477 self.deferred.errback(failure.Failure(e))
478
479 except Exception as e:
480 self.log.exception('omci-setup-2', e=e)
481 self.deferred.errback(failure.Failure(e))