blob: a272e3dccefbb8fe6f76497883fce341b9243996 [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, returnValue, TimeoutError, failure
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
22
23OP = EntityOperations
24RC = ReasonCodes
25
26
27class ServiceDownloadFailure(Exception):
28 """
29 This error is raised by default when the download fails
30 """
31
32
33class ServiceResourcesFailure(Exception):
34 """
35 This error is raised by when one or more resources required is not available
36 """
37
38
39class AdtnServiceDownloadTask(Task):
40 """
41 OpenOMCI MIB Download Example - Service specific
42
43 This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
44 and converts it to run as a Task on the OpenOMCI Task runner. This is
45 in order to begin to decompose service instantiation in preparation for
46 Technology Profile work.
47
48 Once technology profiles are ready, some of this task may hang around or
49 be moved into OpenOMCI if there are any very common settings/configs to do
50 for any profile that may be provided in the v2.0 release
51
52 Currently, the only service tech profiles expected by v2.0 will be for AT&T
53 residential data service and DT residential data service.
54 """
55 task_priority = Task.DEFAULT_PRIORITY + 10
56 default_tpid = 0x8100 # TODO: Move to a better location
57 name = "ADTRAN Service Download Task"
58 free_tcont_alloc_id = 0xFFFF
59 free_gpon_tcont_alloc_id = 0xFF
60
61 def __init__(self, omci_agent, handler):
62 """
63 Class initialization
64
65 :param omci_agent: (OpenOMCIAgent) OMCI Adapter agent
66 :param device_id: (str) ONU Device ID
67 """
68 super(AdtnServiceDownloadTask, self).__init__(AdtnServiceDownloadTask.name,
69 omci_agent,
70 handler.device_id,
71 priority=AdtnServiceDownloadTask.task_priority,
72 exclusive=False)
73 self._handler = handler
74 self._onu_device = omci_agent.get_device(handler.device_id)
75 self._local_deferred = None
76 self._pon = handler.pon_port()
77 self._extended_vlan_me_created = False
78
79 self._input_tpid = AdtnServiceDownloadTask.default_tpid
80 self._output_tpid = AdtnServiceDownloadTask.default_tpid
81
82 # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
83 # self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
84 self._vid = OMCI.DEFAULT_UNTAGGED_VLAN
85
86 # Entity IDs. IDs with values can probably be most anything for most ONUs,
87 # IDs set to None are discovered/set
88 #
89 # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
90 #
91 self._ieee_mapper_service_profile_entity_id = self._pon.ieee_mapper_service_profile_entity_id
92 self._gal_enet_profile_entity_id = self._handler.gal_enet_profile_entity_id
93
94 # Next to are specific
95 self._ethernet_uni_entity_id = self._handler.uni_ports[0].entity_id
96 self._mac_bridge_service_profile_entity_id = self._handler.mac_bridge_service_profile_entity_id
97
98 def cancel_deferred(self):
99 super(AdtnServiceDownloadTask, self).cancel_deferred()
100
101 d, self._local_deferred = self._local_deferred, None
102 try:
103 if d is not None and not d.called:
104 d.cancel()
105 except:
106 pass
107
108 def start(self):
109 """
110 Start the MIB Service Download
111 """
112 super(AdtnServiceDownloadTask, self).start()
113 self._local_deferred = reactor.callLater(0, self.perform_service_download)
114
115 def stop(self):
116 """
117 Shutdown MIB Service download
118 """
119 self.log.debug('stopping')
120
121 self.cancel_deferred()
122 super(AdtnServiceDownloadTask, self).stop()
123
124 def check_status_and_state(self, results, operation=''):
125 """
126 Check the results of an OMCI response. An exception is thrown
127 if the task was cancelled or an error was detected.
128
129 :param results: (OmciFrame) OMCI Response frame
130 :param operation: (str) what operation was being performed
131 :return: True if successful, False if the entity existed (already created)
132 """
133 omci_msg = results.fields['omci_message'].fields
134 status = omci_msg['success_code']
135 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
136 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
137 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
138
139 self.log.debug(operation, status=status, error_mask=error_mask,
140 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
141
142 if status == RC.Success:
143 self.strobe_watchdog()
144 return True
145
146 elif status == RC.InstanceExists:
147 return False
148
149 raise ServiceDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
150 .format(operation, status, error_mask, failed_mask, unsupported_mask))
151
152 @inlineCallbacks
153 def perform_service_download(self):
154 """
155 Send the commands to minimally configure the PON, Bridge, and
156 UNI ports for this device. The application of any service flows
157 and other characteristics are done once resources (gem-ports, tconts, ...)
158 have been defined.
159 """
160 self.log.debug('perform-service-download')
161 device = self._handler.adapter_agent.get_device(self.device_id)
162
163 def resources_available():
164 return (len(self._handler.uni_ports) > 0 and
165 len(self._pon.tconts) and
166 len(self._pon.gem_ports))
167
168 if self._handler.enabled and resources_available():
169 device.reason = 'Performing Service OMCI Download'
170 self._handler.adapter_agent.update_device(device)
171
172 try:
173 # Lock the UNI ports to prevent any alarms during initial configuration
174 # of the ONU
175 self.strobe_watchdog()
176 # Provision the initial bridge configuration
177 yield self.perform_service_specific_steps()
178
179 # And re-enable the UNIs if needed
180 yield self.enable_unis(self._handler.uni_ports, False)
181
182 # If here, we are done
183 device = self._handler.adapter_agent.get_device(self.device_id)
184 device.reason = ''
185 self._handler.adapter_agent.update_device(device)
186 self.deferred.callback('service-download-success')
187
188 except TimeoutError as e:
189 self.deferred.errback(failure.Failure(e))
190
191 except Exception as e:
192 self.deferred.errback(failure.Failure(e))
193 else:
194 # TODO: Provide better error reason, what was missing...
195 e = ServiceResourcesFailure('Required resources are not available')
196 self.deferred.errback(failure.Failure(e))
197
198 @inlineCallbacks
199 def perform_service_specific_steps(self):
200 omci_cc = self._onu_device.omci_cc
201 frame = None
202
203 try:
204 ################################################################################
205 # TCONTS
206 #
207 # EntityID will be referenced by:
208 # - GemPortNetworkCtp
209 # References:
210 # - ONU created TCONT (created on ONU startup)
211
212 tcont_idents = self._onu_device.query_mib(Tcont.class_id)
213 self.log.debug('tcont-idents', tcont_idents=tcont_idents)
214
215 for tcont in self._pon.tconts.itervalues():
216 if tcont.entity_id is None:
217 free_ids = {AdtnServiceDownloadTask.free_tcont_alloc_id,
218 AdtnServiceDownloadTask.free_gpon_tcont_alloc_id}
219
220 free_entity_id = next((k for k, v in tcont_idents.items()
221 if isinstance(k, int) and
222 v.get('attributes', {}).get('alloc_id', 0) in
223 free_ids), None)
224
225 if free_entity_id is None:
226 self.log.error('no-available-tconts')
227 raise ServiceResourcesFailure('No Available TConts')
228
229 try:
230 prev_alloc_id = tcont_idents[free_entity_id].get('attributes').get('alloc_id')
231 yield tcont.add_to_hardware(omci_cc, free_entity_id, prev_alloc_id=prev_alloc_id)
232
233 except Exception as e:
234 self.log.exception('tcont-set', e=e, eid=free_entity_id)
235 raise
236
237 ################################################################################
238 # GEMS (GemPortNetworkCtp and GemInterworkingTp)
239 #
240 # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
241 # GemInterworkingTp ME could be different since it has an attribute to specify
242 # the GemPortNetworkCtp entity id.
243 #
244 # TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
245 # for the GemPortNetworkCtp ME
246 #
247 # GemPortNetworkCtp
248 # EntityID will be referenced by:
249 # - GemInterworkingTp
250 # References:
251 # - TCONT
252 # - Hardcoded upstream TM Entity ID
253 # - (Possibly in Future) Upstream Traffic descriptor profile pointer
254 #
255 # GemInterworkingTp
256 # EntityID will be referenced by:
257 # - Ieee8021pMapperServiceProfile
258 # References:
259 # - GemPortNetworkCtp
260 # - Ieee8021pMapperServiceProfile
261 # - GalEthernetProfile
262 #
263 for gem_port in self._pon.gem_ports.itervalues():
264 if not gem_port.in_hardware:
265 tcont = gem_port.tcont
266 if tcont is None:
267 raise Exception('unknown-tcont-reference', gem_id=gem_port.gem_id)
268
269 try:
270 yield gem_port.add_to_hardware(omci_cc,
271 tcont.entity_id,
272 self._ieee_mapper_service_profile_entity_id,
273 self._gal_enet_profile_entity_id)
274 except Exception as e:
275 self.log.exception('gem-add-failed', e=e, gem=gem_port)
276 raise
277
278 ################################################################################
279 # Update the IEEE 802.1p Mapper Service Profile config
280 #
281 # EntityID was created prior to this call. This is a set
282 #
283 # References:
284 # - Gem Interworking TPs are set here
285 #
286 # TODO: All p-bits currently go to the one and only GEMPORT ID for now
287 gem_ports = self._pon.gem_ports
288
289 if len(gem_ports):
290 gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()]
291 else:
292 gem_entity_ids = [OmciNullPointer]
293
294 frame = Ieee8021pMapperServiceProfileFrame(
295 self._ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
296 interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
297 ).set()
298 results = yield omci_cc.send(frame)
299 self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
300
301 ################################################################################
302 # Create Extended VLAN Tagging Operation config (PON-side)
303 #
304 # EntityID relates to the VLAN TCIS
305 # References:
306 # - VLAN TCIS from previously created VLAN Tagging filter data
307 # - PPTP Ethernet UNI
308 #
309 # TODO: add entry here for additional UNI interfaces
310
311 attributes = dict(
312 association_type=2, # Assoc Type, PPTP Ethernet UNI
313 associated_me_pointer=self._ethernet_uni_entity_id # Assoc ME, PPTP Entity Id
314 )
315
316 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
317 self._mac_bridge_service_profile_entity_id,
318 attributes=attributes
319 ).create()
320 results = yield omci_cc.send(frame)
321 self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
322 self._extended_vlan_me_created = True
323
324 ################################################################################
325 # Update Extended VLAN Tagging Operation Config Data
326 #
327 # Specifies the TPIDs in use and that operations in the downstream direction are
328 # inverse to the operations in the upstream direction
329 # TODO: Downstream mode may need to be modified once we work more on the flow rules
330
331 attributes = dict(
332 input_tpid=self._input_tpid, # input TPID
333 output_tpid=self._output_tpid, # output TPID
334 downstream_mode=0, # inverse of upstream
335 )
336 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
337 self._mac_bridge_service_profile_entity_id,
338 attributes=attributes
339 ).set()
340 results = yield omci_cc.send(frame)
341 self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
342
343 ################################################################################
344 # Update Extended VLAN Tagging Operation Config Data
345 #
346 # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
347 # Treatment Inner Vlan Id : 2
348
349 attributes = dict(
350 received_frame_vlan_tagging_operation_table=
351 VlanTaggingOperation(
352 filter_outer_priority=15, # This entry is not a double-tag rule
353 filter_outer_vid=4096, # Do not filter on the outer VID value
354 filter_outer_tpid_de=0, # Do not filter on the outer TPID field
355
356 filter_inner_priority=15, # This is a no-tag rule, ignore all other VLAN tag filter fields
357 filter_inner_vid=0x1000, # Do not filter on the inner VID
358 filter_inner_tpid_de=0, # Do not filter on inner TPID field
359
360 filter_ether_type=0, # Do not filter on EtherType
361 treatment_tags_to_remove=0, # Remove 0 tags
362
363 treatment_outer_priority=15, # Do not add an outer tag
364 treatment_outer_vid=0, # n/a
365 treatment_outer_tpid_de=0, # n/a
366
367 treatment_inner_priority=0, # Add an inner tag and insert this value as the priority
368 treatment_inner_vid=self._vid, # use this value as the VID in the inner VLAN tag
369 treatment_inner_tpid_de=4, # set TPID
370 )
371 )
372 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
373 self._mac_bridge_service_profile_entity_id, # Entity ID
374 attributes=attributes # See above
375 ).set()
376 results = yield omci_cc.send(frame)
377 self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
378
379 ###############################################################################
380
381 except TimeoutError as e:
382 self.log.warn('rx-timeout-download', frame=hexlify(frame))
383 self.cleanup_on_error()
384 raise
385
386 except Exception as e:
387 self.log.exception('omci-setup-2', e=e)
388 self.cleanup_on_error()
389 raise
390
391 returnValue(None)
392
393 @inlineCallbacks
394 def enable_unis(self, unis, force_lock):
395 """
396 Lock or unlock one or more UNI ports
397
398 :param unis: (list) of UNI objects
399 :param force_lock: (boolean) If True, force lock regardless of enabled state
400 """
401 omci_cc = self._onu_device.omci_cc
402 frame = None
403
404 for uni in unis:
405 ################################################################################
406 # Lock/Unlock UNI - 0 to Unlock, 1 to lock
407 #
408 # EntityID is referenced by:
409 # - MAC bridge port configuration data for the UNI side
410 # References:
411 # - Nothing
412 try:
413 state = 1 if force_lock or not uni.enabled else 0
414 frame = PptpEthernetUniFrame(uni.entity_id,
415 attributes=dict(administrative_state=state)).set()
416 results = yield omci_cc.send(frame)
417 self.check_status_and_state(results, 'set-pptp-ethernet-uni-lock-restore')
418
419 except TimeoutError:
420 self.log.warn('rx-timeout-unis', frame=hexlify(frame))
421 raise
422
423 except Exception as e:
424 self.log.exception('omci-failure', e=e)
425 raise
426
427 returnValue(None)
428
429 @inlineCallbacks
430 def cleanup_on_error(self):
431
432 omci_cc = self._onu_device.omci_cc
433
434 if self._extended_vlan_me_created:
435 try:
436 eid = self._mac_bridge_service_profile_entity_id
437 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(eid).delete()
438 results = yield omci_cc.send(frame)
439 status = results.fields['omci_message'].fields['success_code']
440 self.log.debug('delete-extended-vlan-me', status=status)
441
442 except Exception as e:
443 self.log.exception('extended-vlan-cleanup', e=e)
444 # Continue processing
445
446 for gem_port in self._pon.gem_ports.itervalues():
447 if gem_port.in_hardware:
448 try:
449 yield gem_port.remove_from_hardware(omci_cc)
450
451 except Exception as e:
452 self.log.exception('gem-port-cleanup', e=e)
453 # Continue processing
454
455 for tcont in self._pon.tconts.itervalues():
456 if tcont.entity_id != AdtnServiceDownloadTask.free_tcont_alloc_id:
457 try:
458 yield tcont.remove_from_hardware(omci_cc)
459
460 except Exception as e:
461 self.log.exception('tcont-cleanup', e=e)
462 # Continue processing
463
464 returnValue('Cleanup Complete')