blob: 4b6c044639b0402d33fadae5d4b8767143a21adb [file] [log] [blame]
Chip Bolingfd1fd372017-12-20 13:34:12 -06001# Copyright 2017-present Adtran, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import structlog
16from twisted.internet.defer import inlineCallbacks, returnValue, succeed, TimeoutError
17from twisted.internet import reactor
18
19from voltha.protos.common_pb2 import AdminState
20from voltha.protos.device_pb2 import Port
21
22from voltha.protos.common_pb2 import OperStatus, ConnectStatus
23
24from omci.omci_me import *
Chip Boling8536d1b2018-01-19 12:44:54 -060025from omci.deprecated import * # TODO: Remove this once OMCI_CC and ME_Frame refactoring is complete
26
27###################################################################################
28#
29# TODO: Notes -> This version is the fifth attempt. All calls converted with the
30# exception of the mib-reset and upload.
31#
32# Saving this off before moving things around.
33#
34#
35###################################################################################
36
Chip Bolingfd1fd372017-12-20 13:34:12 -060037
38_STARTUP_RETRY_WAIT = 5
39BRDCM_DEFAULT_VLAN = 4091 # TODO: Deprecate later...
40
41# abbreviations
42OP = EntityOperations
43
44
45class PonPort(object):
46 """Wraps northbound-port / vlan bridge support for ONU"""
47
48 def __init__(self, handler, port_no):
49 self._enabled = False
50 self._valid = True
51 self._handler = handler
52 self._deferred = None
53 self._port = None
54 self._port_number = port_no
55 self._bridge_initialized = False
56 self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
57
58 self._admin_state = AdminState.ENABLED
59 self._oper_status = OperStatus.ACTIVE
60
61 self._gem_ports = {} # gem-id -> GemPort
62 self._tconts = {} # alloc-id -> TCont
63
Chip Boling8536d1b2018-01-19 12:44:54 -060064 # TODO: Until we have an external database, just save it here
65 self.mib_data_store = dict() # TODO: Improve and make class attribute/property
Chip Bolingfd1fd372017-12-20 13:34:12 -060066 # TODO: Add stats, alarm reference, ...
67
68 pass
69
70 def __str__(self):
71 return "PonPort" # TODO: Encode current state
72
73 @staticmethod
74 def create(handler, port_no):
75 port = PonPort(handler, port_no)
76 return port
77
78 def _start(self):
79 self._cancel_deferred()
80
81 self._admin_state = AdminState.ENABLED
82 self._oper_status = OperStatus.ACTIVE
83 self._update_adapter_agent()
84
85 # Begin ONU Activation sequence
Chip Boling8536d1b2018-01-19 12:44:54 -060086 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
87 self._initial_message_exchange)
Chip Bolingfd1fd372017-12-20 13:34:12 -060088 # TODO: start h/w sync
89 pass
90
91 def _stop(self):
92 self._cancel_deferred()
93
94 self._bridge_initialized = False
95 self._admin_state = AdminState.DISABLED
96 self._oper_status = OperStatus.UNKNOWN
97 self._update_adapter_agent()
98 # TODO: stop h/w sync
99 pass
100
101 def _cancel_deferred(self):
102 d, self._deferred = self._deferred, None
103
104 try:
105 if d is not None and not d.called:
106 d.cancel()
107 except:
108 pass
109
110 def delete(self):
111 self.enabled = False
112 self._valid = False
113 self._handler = None
114 # TODO: anything else
115
116 @property
117 def enabled(self):
118 return self._enabled
119
120 @enabled.setter
121 def enabled(self, value):
122 if self._enabled != value:
123 self._enabled = value
124
125 if value:
126 self._start()
127 else:
128 self._stop()
129
130 @property
131 def bridge_initialized(self):
132 return self._bridge_initialized
133
134 @property
135 def port_number(self):
136 return self._port_number
137
138 def get_port(self):
139 """
140 Get the VOLTHA PORT object for this port
141 :return: VOLTHA Port object
142 """
143 if self._port is None:
144 device = self._handler.adapter_agent.get_device(self._handler.device_id)
145
146 self._port = Port(port_no=self.port_number,
147 label='PON port',
148 type=Port.PON_ONU,
149 admin_state=self._admin_state,
150 oper_status=self._oper_status,
Chip Boling8536d1b2018-01-19 12:44:54 -0600151 peers=[Port.PeerPort(device_id=device.parent_id,
152 port_no=device.parent_port_no)])
Chip Bolingfd1fd372017-12-20 13:34:12 -0600153 return self._port
154
155 def _update_adapter_agent(self):
156 # TODO: Currently does the adapter_agent allow 'update' of port status
157 # self.adapter_agent.update_port(self.olt.device_id, self.get_port())
158 pass
159
160 @inlineCallbacks
Chip Boling8536d1b2018-01-19 12:44:54 -0600161 def _initial_message_exchange(self):
162 """
163 Perform a MIB Reset and then collect some basic (read-only) attributes.
164 Upon success, begin MIB upload sequence
165 """
166 self.log.info('initial-message-exchange')
Chip Bolingfd1fd372017-12-20 13:34:12 -0600167 self._deferred = None
168
169 if self._handler.device_id is None or not self.enabled:
170 returnValue(succeed('deleted'))
171
172 elif not self.enabled:
173 # Wait until enabled
174 returnValue('not-enabled')
175
176 omci = self._handler.omci
177
178 try:
179 # reset incoming message queue
180 omci.flush()
181
182 ####################################################
183 # Start by getting some useful device information
184
185 device = self._handler.adapter_agent.get_device(self._handler.device_id)
186 device.oper_status = OperStatus.ACTIVATING
187 device.connect_status = ConnectStatus.UNREACHABLE
Chip Boling8536d1b2018-01-19 12:44:54 -0600188 device.reason = 'Initial OMCI message exchange in progress'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600189
190 except Exception as e:
191 self.log.exception('top-of-msg-exch', e=e)
192 device = None
193
194 if device is None:
195 # Wait until enabled
196 returnValue('no-device')
197
198 try:
Chip Boling8536d1b2018-01-19 12:44:54 -0600199 # Note: May timeout to ONU not fully discovered (can happen in xPON case)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600200 # or other errors.
201 # Decode fields in response and update device info
202
Chip Bolingfd1fd372017-12-20 13:34:12 -0600203 response = yield omci.send(OntGFrame('vendor_id').get())
Chip Boling8536d1b2018-01-19 12:44:54 -0600204 # TODO: Get status for this and others below before getting other values...
Chip Bolingfd1fd372017-12-20 13:34:12 -0600205 omci_response = response.getfieldval("omci_message")
206 data = omci_response.getfieldval("data")
207 vendor = data["vendor_id"]
208 assert vendor == 'ADTN', \
209 "Invalid Device/Wrong device adapter assigned: '{}'".format(vendor)
210
211 # TODO: Get serial number and validate!
212
213 # Mark as reachable if at least first message gets through
214 device.connect_status = ConnectStatus.REACHABLE
215 self._handler.adapter_agent.update_device(device)
216
Chip Bolingfd1fd372017-12-20 13:34:12 -0600217 response = yield omci.send(CardholderFrame(True, 1,
218 'actual_plugin_unit_type').get())
219
220 omci_response = response.getfieldval("omci_message")
221 data = omci_response.getfieldval("data")
222 # device.type = str(data["actual_plugin_unit_type"])
223
Chip Bolingfd1fd372017-12-20 13:34:12 -0600224 response = yield omci.send(CircuitPackFrame(257, 'number_of_ports').get())
225
226 omci_response = response.getfieldval("omci_message")
227 data = omci_response.getfieldval("data")
228 num_ports = data["number_of_ports"]
229 assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
230
Chip Bolingfd1fd372017-12-20 13:34:12 -0600231 response = yield omci.send(IpHostConfigDataFrame(515, 'mac_address').get())
232
233 omci_response = response.getfieldval("omci_message")
234 data = omci_response.getfieldval("data")
235 device.mac_address = str(data["mac_address"])
236
Chip Bolingfd1fd372017-12-20 13:34:12 -0600237 response = yield omci.send(Ont2GFrame('equipment_id').get())
238
239 omci_response = response.getfieldval("omci_message")
240 data = omci_response.getfieldval("data")
241 eqptId_bootVersion = str(data["equipment_id"])
242 eqptId = eqptId_bootVersion[0:10] # ie) BVMDZ10DRA
243 bootVersion = eqptId_bootVersion[12:20] # ie) CML.D55~
244
245 # response = yield omci.send_get_Ont2G('omcc_version', 0)
246 response = yield omci.send(Ont2GFrame('omcc_version').get())
247
248 omci_response = response.getfieldval("omci_message")
249 data = omci_response.getfieldval("data")
250 # decimal version
251 omciVersion = str(data["omcc_version"])
252
Chip Bolingfd1fd372017-12-20 13:34:12 -0600253 response = yield omci.send(Ont2GFrame('vendor_product_code').get())
254
255 omci_response = response.getfieldval("omci_message")
256 data = omci_response.getfieldval("data")
257 # decimal value
258 vendorProductCode = str(data["vendor_product_code"])
259
Chip Bolingfd1fd372017-12-20 13:34:12 -0600260 response = yield omci.send(OntGFrame('version').get())
261
262 omci_response = response.getfieldval("omci_message")
263 data = omci_response.getfieldval("data")
264 device.model = str(data["version"]) # such as 1287800F1
265
Chip Boling8536d1b2018-01-19 12:44:54 -0600266 # TODO: Combine ONTG calls into a single call with multiple attributes
267 # TODO: Combine ONT2G calls into a single call with multiple attributes
268
269 # TODO: Look into ONTG and ONT2G to see if we can get other items of interest
270 # such as max tconts, max gem ports, and so on. Make use of them
271
Chip Bolingfd1fd372017-12-20 13:34:12 -0600272 # Possibility of bug in ONT Firmware. uncomment this code after it is fixed.
273 # response = yield omci.send_get_SoftwareImage('version', 0)
274 #
275 # omci_response = response.getfieldval("omci_message")
276 # data = omci_response.getfieldval("data")
277 # device.firmware_version = str(data["version"])
278 # is_committed = data["is_committed"]
279 # is_active = data["is_active"]
280 # is_valid = data["is_valid"]
281
282 # TODO: May have some issue with the next one...
283 # response = yield omci.send_set_adminState(257)
284
285 # device.hardware_version = 'TODO: to be filled'
286 # TODO: Support more versions as needed
287 # images = Image(version=results.get('software_version', 'unknown'))
288 # device.images.image.extend([images])
289
290 # self.adapter_agent.update_device(device)
291 device.oper_status = OperStatus.ACTIVE
292 device.connect_status = ConnectStatus.REACHABLE
Chip Boling8536d1b2018-01-19 12:44:54 -0600293 device.reason = 'Initial OMCI message exchange complete'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600294 self._handler.adapter_agent.update_device(device)
295
Chip Boling8536d1b2018-01-19 12:44:54 -0600296 # Start MIB synchronization
297 self._deferred = reactor.callLater(0, self._perform_mib_upload)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600298 self.log.info('onu-activated')
299
300 # These exceptions are not recoverable
Chip Boling8536d1b2018-01-19 12:44:54 -0600301 except (TypeError, ValueError) as e:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600302 self.log.exception('Failed', e=e)
303 device.oper_status = OperStatus.FAILED
Chip Boling8536d1b2018-01-19 12:44:54 -0600304 device.reason = 'Initial message sequence failure: ' + e.message
Chip Bolingfd1fd372017-12-20 13:34:12 -0600305 self._handler.adapter_agent.update_device(device)
306
307 except TimeoutError as e:
308 self.log.debug('Failed', e=e)
309 self._handler.adapter_agent.update_device(device)
310 # Try again later. May not have been discovered
311 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
Chip Boling8536d1b2018-01-19 12:44:54 -0600312 self._initial_message_exchange)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600313
314 except Exception as e:
315 self.log.exception('Failed', e=e)
Chip Boling8536d1b2018-01-19 12:44:54 -0600316 device.reason = 'Initial message sequence failure: ' + e.message
Chip Bolingfd1fd372017-12-20 13:34:12 -0600317 self._handler.adapter_agent.update_device(device)
318 # Try again later. May not have been discovered
319 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
Chip Boling8536d1b2018-01-19 12:44:54 -0600320 self._initial_message_exchange)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600321
322 @inlineCallbacks
Chip Boling8536d1b2018-01-19 12:44:54 -0600323 def _perform_mib_upload(self):
324 """
325 Called after basic OMCI MIB RESET message startup/exchange.
Chip Bolingfd1fd372017-12-20 13:34:12 -0600326
Chip Boling8536d1b2018-01-19 12:44:54 -0600327 Upon successful completion, proceed to establish a few basic structures
328 that we know will be required. Once OpenOMCI is created, this sequence
329 should be skipped (go directly to MIB upload) and this info is available
330 from the uploaded MIB.
331
332 On failure, restart the initial message exchange
333 """
334 self.log.info('perform-mib-upload')
335 self._deferred = None
336
337 if self._handler.device_id is None or not self.enabled:
338 returnValue('not-enabled')
339
340 device = None
341 omci = self._handler.omci
342
343 if self._handler.is_mock:
344 self._deferred = reactor.callLater(0, self._perform_mib_download)
345 returnValue('is-mock')
346
347 try:
348 device = self._handler.adapter_agent.get_device(self._handler.device_id)
349 device.reason = 'Performing MIB Synchronization'
350 self._handler.adapter_agent.update_device(device)
351
352 #########################################
353 # MIB Reset
354 results = yield omci.send_mib_reset()
355 status = results.fields['omci_message'].fields['success_code']
356 assert status == 0, 'Unexpected MIB reset response status: {}'.format(status)
357
358 # TODO: On a real system, need to flush the external MIB database
359 # TODO: Also would need to watch for any AVC being handled between the MIB reset and the DB flush
360 self.mib_data_store = dict()
361
362 ########################################
363 # Begin MIB Upload
364 results = yield omci.send_mib_upload()
365 number_of_commands = results.fields['omci_message'].fields['number_of_commands']
366
367 for seq_no in xrange(number_of_commands):
368 results = yield omci.send_mib_upload_next(seq_no)
369
370 object_entity_class = results.fields['omci_message'].fields['object_entity_class']
371 object_entity_id = results.fields['omci_message'].fields['object_entity_id']
372 object_attributes_mask = results.fields['omci_message'].fields['object_attributes_mask']
373 object_data = results.fields['omci_message'].fields['object_data']
374
375 key = (object_entity_class, object_entity_id)
376
377 if key not in self.mib_data_store:
378 self.mib_data_store[key] = (object_attributes_mask, object_data)
379 else:
380 pass
381
382 # Successful if here
383
384 device.reason = 'MIB Synchronization Complete'
385 self._handler.adapter_agent.update_device(device)
386
387 # Start up non-critical message exchange
388 self._deferred = reactor.callLater(0, self._perform_mib_download)
389 self.log.info('mib-synchronized')
390
391 except TimeoutError as e:
392 self.log.warn('mib-upload', e=e)
393
394 if device is not None:
395 device.reason = 'mib-upload-failure: Response Timeout'
396 self._handler.adapter_agent.update_device(device)
397
398 # Try again later
399 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
400 self._initial_message_exchange)
401
402 except Exception as e:
403 self.log.exception('mib-upload', e=e)
404 device.reason = 'MIB upload sequence failure: ' + e.message
405 self._handler.adapter_agent.update_device(device)
406
407 # Try again later
408 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
409 self._initial_message_exchange)
410
411 @inlineCallbacks
412 def _perform_mib_download(self):
413 """
414 Called after basic OMCI Synchronization (MIB upload). Begin to set up
415 some basic OMCI settings common for most expected configurations
416
417 Upon successful completion, any xPON information received so far will be
418 acted upon.
419
420 On failure, restart the initial message exchange
421 """
422 self.log.info('mib-download-start')
Chip Bolingfd1fd372017-12-20 13:34:12 -0600423 self._deferred = None
424
425 if self._handler.device_id is None or not self.enabled:
426 returnValue('not-enabled')
427
428 omci = self._handler.omci
429
Chip Boling8536d1b2018-01-19 12:44:54 -0600430 if self._handler.is_mock:
431 self._bridge_initialized = True
432 self._deferred = reactor.callLater(0, self._sync_existing_xpon)
433 returnValue('is-mock')
Chip Bolingfd1fd372017-12-20 13:34:12 -0600434
Chip Boling8536d1b2018-01-19 12:44:54 -0600435 # reset incoming message queue
436 omci.flush()
437 device = self._handler.adapter_agent.get_device(self._handler.device_id)
438
439 device.reason = 'Performing MIB Download'
440 self._handler.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600441
442 if not self.enabled or device is None:
443 returnValue('not-enabled')
444
Chip Boling8536d1b2018-01-19 12:44:54 -0600445 #############################################
446 # All our variables here
447 # TODO: Move elsewhere in future version of this software
448 frame = None
449 gal_enet_profile_entity_id = 0x100 # Any Unique Entity ID BP: old value 1
450 ieee_mapper_service_profile_entity_id = 0x100 # Entity ID BP: old value 0x8001
451 mac_bridge_service_profile_entity_id = 0x100 # Entity ID BP: old value 0x201
452 mac_bridge_port_ani_entity_id = 0x100 # BP: oldvalue 0x201
453 ethernet_uni_entity_id = 0x101
454 vlan_tcis_1 = 0x900
455 cvid = 2 # TODO: Get from xPON and/or device adapter
456 tcont_entity_id = 0x100 # Entity ID, ONT is set to 0x100
457 tcont_alloc_id = 0x400 # Alloc ID, 1024 - Tcont
458 gem_entity_id = 0x4900 # Entity ID, unique Id
459 gem_port_id = 0x400 # Port ID, 2304 - Gem Id
460 gem_interworking_entity_id = 0x4900
461 vlan_config_entity_id = vlan_tcis_1 # Entity ID BP: Oldvalue 0x202
462
Chip Bolingfd1fd372017-12-20 13:34:12 -0600463 try:
Chip Boling8536d1b2018-01-19 12:44:54 -0600464 ################################################################################
465 #
466 #
467 # EntityID will be referenced by:
468 # -
469 # -
470 # -
471 # -
472 # References:
473 # -
474 # -
Chip Bolingfd1fd372017-12-20 13:34:12 -0600475
Chip Boling8536d1b2018-01-19 12:44:54 -0600476 frame = TcontFrame(tcont_entity_id, tcont_alloc_id).set()
477 results = yield omci.send(frame)
478 # results = yield send_set_tcont(omci, 0x100, # Entity ID, ONT is set to 0x100
479 # 0x400) # Alloc ID, 1024 - Tcont
Chip Bolingfd1fd372017-12-20 13:34:12 -0600480
Chip Boling8536d1b2018-01-19 12:44:54 -0600481 status = results.fields['omci_message'].fields['success_code']
482 failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
483 unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
484 self.log.debug('set-tcont', status=status,
485 failed_attributes_mask=failed_attributes_mask,
486 unsupported_attributes_mask=unsupported_attributes_mask)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600487
Chip Boling8536d1b2018-01-19 12:44:54 -0600488 ################################################################################
489 direction = "bi-directional"
Chip Bolingfd1fd372017-12-20 13:34:12 -0600490
Chip Boling8536d1b2018-01-19 12:44:54 -0600491 # TODO: For TM, is this the entity ID for a traffic descriptor?
492 frame = GemPortNetworkCtpFrame(
493 gem_entity_id,
494 port_id=gem_port_id, # Port ID, 2304 - Gem ID
495 tcont_id=tcont_entity_id, # TCONT Entity ID, as set in TCONT set
496 direction=direction, # Direction, bidirectional
497 upstream_tm=0x8000 # TM ID, 32768 unique ID set in TD set TODO: Parameterize
498 ).create()
499 results = yield omci.send(frame)
500 # results = yield send_create_gem_port_network_ctp(omci, 0x4900, # Entity ID, unique Id
501 # 0x400, # Port ID, 2304 - Gem Id
502 # 0x100, # TCONT Entity ID, as set in TCONT set
503 # direction, # Direction, bidirectional
504 # 0x8000) # TM ID, 32768 unique Id set in TD set
Chip Bolingfd1fd372017-12-20 13:34:12 -0600505
Chip Boling8536d1b2018-01-19 12:44:54 -0600506 status = results.fields['omci_message'].fields['success_code']
507 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
508 self.log.debug('create-gem-port-network-ctp', status=status, error_mask=error_mask)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600509
Chip Boling8536d1b2018-01-19 12:44:54 -0600510 ################################################################################
511 # GEM Interworking config
512 #
513 #
514 # EntityID will be referenced by:
515 # -
516 # -
517 # -
518 # -
519 # References:
520 # -
521 # -
522 # TODO: for the service_profile_pointer=0x100, is this create/set somewhere later
Chip Bolingfd1fd372017-12-20 13:34:12 -0600523
Chip Boling8536d1b2018-01-19 12:44:54 -0600524 frame = GemInterworkingTpFrame(
525 gem_interworking_entity_id,
526 gem_port_network_ctp_pointer=gem_entity_id, # GEMPort NET CTP ID, as set in CTP create
527 interworking_option=5, # IEEE 802.1
528 service_profile_pointer=ieee_mapper_service_profile_entity_id,
529 interworking_tp_pointer=0x0,
530 pptp_counter=1,
531 gal_profile_pointer=0 # TODO: make? -> gal_enet_profile_entity_id # BP: HACK old value 0x1 (TODO: Balaji had this set to 0 in his test sequence)
532 ).create()
533 results = yield omci.send(frame)
534 # results = yield send_create_gem_inteworking_tp(omci, 0x4900, # any Unique Entity ID
535 # 0x4900, # GEMPort NET CTP ID, as set in CTP create
536 # 0x100) # 802.1p mapper Service Mapper Profile ID
Chip Bolingfd1fd372017-12-20 13:34:12 -0600537
Chip Boling8536d1b2018-01-19 12:44:54 -0600538 status = results.fields['omci_message'].fields['success_code']
539 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
540 self.log.debug('create-gem-interworking-tp', status=status, error_mask=error_mask)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600541
Chip Boling8536d1b2018-01-19 12:44:54 -0600542 ########################################################################################
543 # Create GalEthernetProfile - Once per ONU/PON interface
544 #
545 # EntityID will be referenced by:
546 # - GEM Interworking TPs when a new GEM Port is created
547 # References:
548 # - Nothing
Chip Bolingfd1fd372017-12-20 13:34:12 -0600549
Chip Boling8536d1b2018-01-19 12:44:54 -0600550 frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
551 max_gem_payload_size=1518).create() # Max GEM Payload size
552 results = yield omci.send(frame)
553 # results = yield send_create_gal_ethernet_profile(omci,
554 # 0x100, # Any Unique Entity ID BP: old value 1
555 # 1518) # Max GEM Payload size
Chip Bolingfd1fd372017-12-20 13:34:12 -0600556
Chip Boling8536d1b2018-01-19 12:44:54 -0600557 status = results.fields['omci_message'].fields['success_code']
558 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
559 self.log.debug('create-gal-ethernet-profile', status=status, error_mask=error_mask)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600560
Chip Boling8536d1b2018-01-19 12:44:54 -0600561 ################################################################################
562 # MAC Bridge Service Profile - Once per UNI
563 #
564 # EntityID will be referenced by:
565 # - MAC Bridge Port Configuration Data
566 # References:
567 # - Nothing
Chip Bolingfd1fd372017-12-20 13:34:12 -0600568
Chip Boling8536d1b2018-01-19 12:44:54 -0600569 attributes = {
570 'spanning_tree_ind': False,
571 # TODO: CB: see if we need or can use any of the following...
572 # 'learning_ind': True,
573 # 'priority': 0x8000,
574 # 'max_age': 20 * 256,
575 # 'hello_time': 2 * 256,
576 # 'forward_delay': 15 * 256,
577 # 'unknown_mac_address_discard': True
578 }
579 frame = MacBridgeServiceProfileFrame(mac_bridge_service_profile_entity_id,
580 attributes).create()
581 results = yield omci.send(frame)
582 # results = yield send_create_mac_bridge_service_profile(omci, 0x100) # Entity ID BP: old value 0x201
Chip Bolingfd1fd372017-12-20 13:34:12 -0600583
Chip Boling8536d1b2018-01-19 12:44:54 -0600584 status = results.fields['omci_message'].fields['success_code']
585 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
586 self.log.debug('create-mac-bridge-service-profile', status=status, error_mask=error_mask)
587
588 ################################################################################
589 # IEEE 802.1 Mapper Service config - Once per PON
590 #
591 # EntityID will be referenced by:
592 # - MAC Bridge Port Configuration Data for the PON port
593 # References:
594 # - Nothing at this point. When a GEM port is created, this entity will
595 # be updated to reference the GEM Interworking TP
596
597 frame = Ieee8021pMapperServiceProfileFrame(ieee_mapper_service_profile_entity_id).create()
598 results = yield omci.send(frame)
599 # results = yield send_create_8021p_mapper_service_profile(omci, 0x100) # Entity ID BP: old value 0x8001
600
601 status = results.fields['omci_message'].fields['success_code']
602 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
603 self.log.debug('create-8021p-mapper-service-profile', status=status, error_mask=error_mask)
604
605 ################################################################################
606 # Create MAC Bridge Port Configuration Data for the PON port via IEEE 802.1
607 # mapper service. Upon receipt by the ONU, the ONU will create an instance
608 # of the following before returning the response.
609 #
610 # - MAC bridge port designation data
611 # - MAC bridge port filter table data
612 # - MAC bridge port bridge table data
613 #
614 # EntityID will be referenced by:
615 # - Implicitly by the VLAN tagging filter data
616 # -
617 # -
618 # -
619 # References:
620 # - MAC Bridge Service Profile (the bridge)
621 # - IEEE 802.1p mapper service profile for PON port
622
623 frame = MacBridgePortConfigurationDataFrame(
624 mac_bridge_port_ani_entity_id, # Entity ID
625 bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
626 # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
627 port_num=0, # Port ID BP: oldvalue 2
628 tp_type=3, # TP Type (IEEE 802.1p mapper service) BP: oldvalue 1, 802.1 mapper GPON intf
629 tp_pointer=ieee_mapper_service_profile_entity_id # TP ID, 8021p mapper ID BP: oldvalue 0x102
630 ).create()
631 results = yield omci.send(frame)
632 # results = yield send_create_mac_bridge_port_configuration_data(omci,
633 # 0x100, # Entity ID BP: oldvalue 0x201
634 # 0x100, # Bridge Entity ID BP: oldvalue 0x201
635 # 0, # Port ID BP: oldvalue 2
636 # 3, # TP Type BP: oldvalue 1, 802.1 mapper GPON interface
637 # 0x100) # TP ID, 8021p mapper Id BP: oldvalue 0x102
638
639 status = results.fields['omci_message'].fields['success_code']
640 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
641 self.log.debug('create-mac-bridge-port-configuration-data-part-1', status=status, error_mask=error_mask)
642
643 ################################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600644 # MAC Bridge Port config
Chip Boling8536d1b2018-01-19 12:44:54 -0600645 # This configuration is for Ethernet UNI
646 #
647 # EntityID will be referenced by:
648 # -
649 # -
650 # -
651 # -
652 # References:
653 # - MAC Bridge Service Profile (the bridge)
654 # - PPTP Ethernet UNI
Chip Bolingfd1fd372017-12-20 13:34:12 -0600655
Chip Boling8536d1b2018-01-19 12:44:54 -0600656 frame = MacBridgePortConfigurationDataFrame(
657 0x000, # Entity ID BP: oldvalue 0x201
658 bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
659 port_num=0, # Port ID BP: oldvalue 3
660 tp_type=1, # PPTP Ethernet UNI BP: oldvalue 3
661 tp_pointer=ethernet_uni_entity_id # TP ID, 8021p mapper Id BP: oldvalue 0x8001
662 ).create()
663 results = yield omci.send(frame)
664 # results = yield send_create_mac_bridge_port_configuration_data(omci,
665 # 0x000, # Entity ID BP: oldvalue 0x2102
666 # 0x100, # Bridge Entity ID BP: oldvalue 0x201
667 # 0, # Port ID BP: oldvalue 3
668 # 1, # TP Type, Ethernet UNI BP: oldvalue 3
669 # 0x101) # TP ID, PPTP UNI Entity Id BP: oldvalue 0x8001
Chip Bolingfd1fd372017-12-20 13:34:12 -0600670
Chip Boling8536d1b2018-01-19 12:44:54 -0600671 status = results.fields['omci_message'].fields['success_code']
672 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
673 self.log.debug('create-mac-bridge-port-configuration-data-part-2', status=status, error_mask=error_mask)
674
675 ################################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600676 # VLAN Tagging Filter config
Chip Boling8536d1b2018-01-19 12:44:54 -0600677 #
678 # EntityID will be referenced by:
679 # - Nothing
680 # References:
681 # - Implicitly linked to an instance of the MAC bridge port configuration data
682 # for the PON port
Chip Bolingfd1fd372017-12-20 13:34:12 -0600683 # TODO: Probably need to get VLAN ID from device.vlan
Chip Boling8536d1b2018-01-19 12:44:54 -0600684 # Set anything, this request will not be used when using Extended Vlan
Chip Bolingfd1fd372017-12-20 13:34:12 -0600685
Chip Boling8536d1b2018-01-19 12:44:54 -0600686 frame = VlanTaggingFilterDataFrame(
687 mac_bridge_port_ani_entity_id, # Entity ID BP: Oldvalue 0x2102
688 vlan_tcis=[vlan_tcis_1], # VLAN IDs BP: cvid
689 forward_operation=0x10
690 ).create()
691 results = yield omci.send(frame)
692 # results = yield send_create_vlan_tagging_filter_data(omci, 0x100, # Entity ID BP: Oldvalue 0x2102
693 # 0x900) # VLAN ID BP: cvid
694
695 status = results.fields['omci_message'].fields['success_code']
696 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
697 self.log.debug('create-vlan-tagging-filter-data', status=status, error_mask=error_mask)
698
699 ################################################################################
700 # Update the IEEE 802.1p Mapper Service Profile config
701 #
702 # EntityID was created prior to this call
703 # References:
704 # -
705 # -
706 # TODO: All p-bits currently go to the one and only GEMPORT ID for now
707
708 frame = Ieee8021pMapperServiceProfileFrame(
709 ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
710 interwork_tp_pointers=[gem_entity_id] # Interworking TP IDs BP: oldvalue self.gemid
711 ).set()
712 results = yield omci.send(frame)
713 # results = yield send_set_8021p_mapper_service_profile(omci, 0x100, 0x4900)
714
715 status = results.fields['omci_message'].fields['success_code']
716 failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
717 unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
718 self.log.debug('set-8021p-mapper-service-profile', status=status,
719 failed_attributes_mask=failed_attributes_mask,
720 unsupported_attributes_mask=unsupported_attributes_mask)
721
722 ################################################################################
723 # Unlock UNI
724 #
725 # EntityID will be referenced by:
726 # - MAC bridge port configuration data for the UNI side
727 # References:
728 # - Nothing
729
730 attributes = dict(
731 administrative_state=0 # 0 - Unlock
732 )
733 frame = PptpEthernetUniFrame(
734 ethernet_uni_entity_id, # Entity ID
735 attributes=attributes # See above
736 ).set()
737 results = yield omci.send(frame)
738 #results = yield send_set_pptp_ethernet_uni(omci, 0x101) # Entity ID
739
740 status = results.fields['omci_message'].fields['success_code']
741 failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
742 unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
743 self.log.debug('set-pptp-ethernet-uni', status=status,
744 failed_attributes_mask=failed_attributes_mask,
745 unsupported_attributes_mask=unsupported_attributes_mask)
746
747 ################################################################################
748 # Create Extended VLAN Tagging Operation config
749 #
750 # EntityID relates to the VLAN TCIS
751 # References:
752 # - VLAN TCIS from previously created VLAN Tagging filter data
753 # - PPTP Ethernet UNI
754 #
755 # TODO: add entry here for additional UNI interfaces
756
757 attributes = dict(
758 association_type=2, # Assoc Type, PPTP Ethernet UNI BP: Oldvalue 2
759 associated_me_pointer=ethernet_uni_entity_id # Assoc ME, PPTP Entity Id BP: Oldvalue 0x102
760 )
761
762 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
763 vlan_config_entity_id,
764 attributes=attributes # See above
765 ).create()
766 results = yield omci.send(frame)
767 # results = yield send_create_extended_vlan_tagging_operation_configuration_data(omci,
768 # 0x900, # Entity ID BP: Oldvalue 0x202
769 # 2, # Assoc Type, PPTP Ethernet UNI BP: Oldvalue 2
770 # 0x101) # Assoc ME, PPTP Entity Id BP: Oldvalue 0x102
771
772 status = results.fields['omci_message'].fields['success_code']
773 error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
774 self.log.debug('create-extended-vlan-tagging-operation-configuration-data', status=status, error_mask=error_mask)
775
776 ################################################################################
777 # Update Extended VLAN Tagging Operation Config Data
778 #
779 # Specifies the TPIDs in use and that operations in the downstream direction are
780 # inverse to the operations in the upstream direction
781 # TODO: Downstream mode may need to be modified once we work more on the flow rules
782
783 attributes = dict(
784 input_tpid=0x8100, # input TPID
785 output_tpid=0x8100, # output TPID
786 downstream_mode=0, # inverse of upstream
787 )
788 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
789 vlan_config_entity_id, # Entity ID BP: Oldvalue 0x202
790 attributes=attributes # See above
791 ).set()
792 results = yield omci.send(frame)
793 # results = yield send_set_extended_vlan_tagging_operation_tpid_configuration_data(omci,
794 # 0x900, # Entity ID BP: Oldvalue 0x202
795 # 0x8100, # input TPID
796 # 0x8100) # output TPID
797
798 status = results.fields['omci_message'].fields['success_code']
799 failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
800 unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
801 self.log.debug('set-extended-vlan-tagging-operation-configuration-data', status=status,
802 failed_attributes_mask=failed_attributes_mask,
803 unsupported_attributes_mask=unsupported_attributes_mask)
804
805 ################################################################################
806 # Update Extended VLAN Tagging Operation Config Data
807 #
808 # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
809 # Treatment Inner Vlan Id : 2
810
811 attributes = dict(
812 received_frame_vlan_tagging_operation_table=
813 VlanTaggingOperation(
814 filter_outer_priority=15, # This entry is not a double-tag rule
815 filter_outer_vid=4096, # Do not filter on the outer VID value
816 filter_outer_tpid_de=0, # Do not filter on the outer TPID field
817
818 filter_inner_priority=15, # This is a no-tag rule, ignore all other VLAN tag filter fields
819 filter_inner_vid=0x1000, # Do not filter on the inner VID
820 filter_inner_tpid_de=0, # Do not filter on inner TPID field
821 filter_ether_type=0, # Do not filter on EtherType
822
823 treatment_tags_to_remove=0, # Remove 0 tags
824 treatment_outer_priority=15, # Do not add an outer tag
825 treatment_outer_vid=0, # n/a
826 treatment_outer_tpid_de=0, # n/a
827
828 treatment_inner_priority=0, # Add an inner tag and insert this value as the priority
829 treatment_inner_vid=cvid, # use this value as the VID in the inner VLAN tag
830 treatment_inner_tpid_de=4 # set TPID = 0x8100
831 )
832 )
833 frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
834 vlan_config_entity_id, # Entity ID BP: Oldvalue 0x202
835 attributes=attributes # See above
836 ).set()
837 results = yield omci.send(frame)
838 # results = yield send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(omci, 0x900,
839 # 0x1000,
840 # 2)
841 status = results.fields['omci_message'].fields['success_code']
842 failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
843 unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
844 self.log.debug('set-extended-vlan-tagging-operation-configuration-data-untagged', status=status,
845 failed_attributes_mask=failed_attributes_mask,
846 unsupported_attributes_mask=unsupported_attributes_mask)
847
848 # BP: This is for AT&T RG's
849 #
850 # TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
851 #
852 # Set AR - ExtendedVlanTaggingOperationConfigData
853 # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
854 # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
855 # 0x900, # Entity ID
856 # 8, # Filter Inner Priority, do not filter on Inner Priority
857 # 0, # Filter Inner VID, this will be 0 in CORD
858 # 0, # Filter Inner TPID DE
859 # 1, # Treatment tags, number of tags to remove
860 # 8, # Treatment inner priority, copy Inner Priority
861 # 2) # Treatment inner VID, this will be 2 in CORD
Chip Bolingfd1fd372017-12-20 13:34:12 -0600862
863 # Set AR - ExtendedVlanTaggingOperationConfigData
864 # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
865 # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
Chip Boling8536d1b2018-01-19 12:44:54 -0600866 # 0x200, # Entity ID
Chip Bolingfd1fd372017-12-20 13:34:12 -0600867 # 8, # Filter Inner Priority
868 # 0, # Filter Inner VID
869 # 0, # Filter Inner TPID DE
870 # 1, # Treatment tags to remove
871 # 8, # Treatment inner priority
872 # cvid) # Treatment inner VID
873 #
874 # Set AR - ExtendedVlanTaggingOperationConfigData
875 # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
Chip Boling8536d1b2018-01-19 12:44:54 -0600876 #results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
877 # 0x100, # Entity ID BP: Oldvalue 0x202
878 # 0x1000, # Filter Inner VID BP: Oldvalue 0x1000
879 # cvid) # Treatment inner VID BP: cvid
Chip Bolingfd1fd372017-12-20 13:34:12 -0600880
881 # success = results.fields['omci_message'].fields['success_code'] == 0
882 # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
883
Chip Boling8536d1b2018-01-19 12:44:54 -0600884 ###############################################################################
885 # If here, we are done
886 device.reason = ''
887 self._handler.adapter_agent.update_device(device)
888
Chip Bolingfd1fd372017-12-20 13:34:12 -0600889 ######################################################################
Chip Boling8536d1b2018-01-19 12:44:54 -0600890 # If here, we can add TCONTs/GEM Ports/... as needed
Chip Bolingfd1fd372017-12-20 13:34:12 -0600891
892 self._bridge_initialized = True
Chip Boling8536d1b2018-01-19 12:44:54 -0600893 self._deferred = reactor.callLater(0, self._sync_existing_xpon)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600894
Chip Boling8536d1b2018-01-19 12:44:54 -0600895 except TimeoutError as e:
896 self.log.warn('rx-timeout', frame=frame)
897 # Try again later. May not have been discovered
898 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
899 self._initial_message_exchange)
900 returnValue('retry-pending')
Chip Bolingfd1fd372017-12-20 13:34:12 -0600901
902 except Exception as e:
Chip Boling8536d1b2018-01-19 12:44:54 -0600903 self.log.exception('mib-download', e=e)
904 device.reason = 'MIB download sequence failure: ' + e.message
Chip Bolingfd1fd372017-12-20 13:34:12 -0600905 self._handler.adapter_agent.update_device(device)
Chip Boling8536d1b2018-01-19 12:44:54 -0600906
907 # Try again later
Chip Bolingfd1fd372017-12-20 13:34:12 -0600908 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
Chip Boling8536d1b2018-01-19 12:44:54 -0600909 self._initial_message_exchange)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600910
911 @inlineCallbacks
Chip Boling8536d1b2018-01-19 12:44:54 -0600912 def _sync_existing_xpon(self):
Chip Bolingfd1fd372017-12-20 13:34:12 -0600913 """
914 Run through existing TCONT and GEM Ports and push into hardware
915 """
Chip Boling8536d1b2018-01-19 12:44:54 -0600916 # for tcont in self._tconts.itervalues():
917 # try:
918 # yield self.add_tcont(tcont, reflow=True)
919 # except Exception as e:
920 # self.log.exception('tcont-reflow', e=e, tcont=tcont)
921 #
922 # for gem_port in self._gem_ports.itervalues():
923 # try:
924 # yield self.add_gem_port(gem_port, reflow=True)
925 #
926 # except Exception as e:
927 # self.log.exception('gem-port-reflow', e=e, gem_port=gem_port)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600928
929 returnValue('Done')
930
931 @inlineCallbacks
932 def add_tcont(self, tcont, reflow=False):
933 """
934 Creates/ a T-CONT with the given alloc-id
935
936 :param tcont: (TCont) Object that maintains the TCONT properties
937 :param reflow: (boolean) If true, force add (used during h/w resync)
938 :return: (deferred)
939 """
940 if not self._valid:
941 returnValue('Deleting')
942
943 if not reflow and tcont.alloc_id in self._tconts:
944 returnValue('already created')
945
946 self.log.info('add', tcont=tcont, reflow=reflow)
947 self._tconts[tcont.alloc_id] = tcont
948
949 if not self.bridge_initialized:
950 returnValue('Bridge Not Initialized')
951
952 try:
953 results = yield tcont.add_to_hardware(self._handler.omci)
954
955 except Exception as e:
956 self.log.exception('tcont', tcont=tcont, reflow=reflow, e=e)
957 # May occur with xPON provisioning, use hw-resync to recover
958 results = 'resync needed'
959
960 returnValue(results)
961
962 @inlineCallbacks
963 def update_tcont_td(self, alloc_id, new_td):
964 tcont = self._tconts.get(alloc_id)
965
966 if tcont is None:
967 returnValue('not-found')
968
969 tcont.traffic_descriptor = new_td
970
971 if not self.bridge_initialized:
972 returnValue('Bridge Not Initialized')
973
974 try:
975 results = yield tcont.add_to_hardware(self._handler.omci)
976
977 except Exception as e:
978 self.log.exception('tcont', tcont=tcont, e=e)
979 # May occur with xPON provisioning, use hw-resync to recover
980 results = 'resync needed'
981
982 returnValue(results)
983
984 @inlineCallbacks
985 def remove_tcont(self, alloc_id):
986 tcont = self._tconts.get(alloc_id)
987
988 if tcont is None:
989 returnValue('nop')
990
991 del self._tconts[alloc_id]
992
993 if not self.bridge_initialized:
994 returnValue('Bridge Not Initialized')
995
996 try:
997 results = yield tcont.remove_from_hardware(self._handler.omci)
998
999 except Exception as e:
1000 self.log.exception('delete', e=e)
1001 results = e
1002 # raise
1003
1004 returnValue(results)
1005
1006 def gem_port(self, gem_id):
1007 return self._gem_ports.get(gem_id)
1008
1009 @property
1010 def gem_ids(self):
1011 """Get all GEM Port IDs used by this ONU"""
1012 return sorted([gem_id for gem_id, gem in self._gem_ports.items()])
1013
1014 @inlineCallbacks
1015 def add_gem_port(self, gem_port, reflow=False):
1016 """
1017 Add a GEM Port to this ONU
1018
1019 :param gem_port: (GemPort) GEM Port to add
1020 :param reflow: (boolean) If true, force add (used during h/w resync)
1021 :return: (deferred)
1022 """
1023 if not self._valid:
1024 returnValue('Deleting')
1025
1026 if not reflow and gem_port.gem_id in self._gem_ports:
1027 returnValue('nop')
1028
1029 self.log.info('add', gem_port=gem_port, reflow=reflow)
1030 self._gem_ports[gem_port.gem_id] = gem_port
1031
1032 if not self.bridge_initialized:
1033 returnValue('Bridge Not Initialized')
1034
1035 try:
1036 results = yield gem_port.add_to_hardware(self._handler.omci)
1037 # TODO: Are flows affected by this change?
1038
1039 except Exception as e:
1040 self.log.exception('gem-port', gem_port=gem_port, reflow=reflow, e=e)
1041 # This can happen with xPON if the ONU has been provisioned, but the PON Discovery
1042 # has not occurred for the ONU. Rely on hw sync to recover
1043 results = 'resync needed'
1044
1045 returnValue(results)
1046
1047 @inlineCallbacks
1048 def remove_gem_id(self, gem_id):
1049 gem_port = self._gem_ports.get(gem_id)
1050
1051 if gem_port is None:
1052 returnValue('nop')
1053
1054 del self._gem_ports[gem_id]
1055
1056 if not self.bridge_initialized:
1057 returnValue('Bridge Not Initialized')
1058
1059 try:
1060 results = yield gem_port.remove_from_hardware(self._handler.omci)
1061 # TODO: Are flows affected by this change?
1062
1063 except Exception as ex:
1064 self.log.exception('gem-port-delete', e=ex)
1065 raise
1066
1067 returnValue(results)