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