blob: c9782802bbcbf712b471f6389d0875d787836077 [file] [log] [blame]
Chip Boling72bbcfe2018-02-14 14:27:59 -06001#
Chip Boling86221f62018-08-09 15:24:48 -05002# Copyright 2017-present Open Networking Foundation
Chip Boling72bbcfe2018-02-14 14:27:59 -06003#
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#
16"""
17OMCI Message support
18"""
19
20import sys
21import arrow
22from twisted.internet import reactor, defer
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060023from twisted.internet.defer import TimeoutError, CancelledError, failure, fail, succeed, inlineCallbacks
Chip Boling72bbcfe2018-02-14 14:27:59 -060024from common.frameio.frameio import hexify
25from voltha.extensions.omci.omci import *
lcui33d6a8e2018-08-28 12:51:38 -070026from voltha.extensions.omci.omci_me import OntGFrame, OntDataFrame, SoftwareImageFrame
Craig Lutgenaeb22b22018-11-13 10:05:45 -060027from voltha.extensions.omci.me_frame import MEFrame
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060028from voltha.extensions.omci.omci_defs import EntityOperations, ReasonCodes
Chip Boling2a059952018-03-12 15:50:00 -050029from common.event_bus import EventBusClient
30from enum import IntEnum
Chip Boling336247f2018-05-15 12:49:41 -050031from binascii import hexlify
Chip Boling72bbcfe2018-02-14 14:27:59 -060032
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060033
lcui33d6a8e2018-08-28 12:51:38 -070034def hexify(buffer):
35 """Return a hexadecimal string encoding of input buffer"""
36 return ''.join('%02x' % ord(c) for c in buffer)
Chip Boling72bbcfe2018-02-14 14:27:59 -060037
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060038
Shad Ansari942ef0d2019-04-30 15:50:25 -070039DEFAULT_OMCI_TIMEOUT = 300 # Seconds
40MAX_OMCI_REQUEST_AGE = 6000 # Seconds
Chip Boling9003cf92019-02-27 12:44:07 -060041DEFAULT_OMCI_DOWNLOAD_SECTION_SIZE = 31 # Bytes
Chip Boling72bbcfe2018-02-14 14:27:59 -060042
Chip Boling2a059952018-03-12 15:50:00 -050043CONNECTED_KEY = 'connected'
44TX_REQUEST_KEY = 'tx-request'
45RX_RESPONSE_KEY = 'rx-response'
Chip Boling6be00362018-05-30 10:15:41 -050046UNKNOWN_CLASS_ATTRIBUTE_KEY = 'voltha-unknown-blob'
Chip Boling2a059952018-03-12 15:50:00 -050047
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060048
Chip Boling2a059952018-03-12 15:50:00 -050049class OmciCCRxEvents(IntEnum):
50 AVC_Notification = 0,
51 MIB_Upload = 1,
52 MIB_Upload_Next = 2,
53 Create = 3,
54 Delete = 4,
55 Set = 5,
56 Alarm_Notification = 6,
57 Test_Result = 7,
58 MIB_Reset = 8,
jasonhuang5f3e63b2018-07-27 01:32:48 +080059 Connectivity = 9,
60 Get_ALARM_Get = 10,
Chip Boling9003cf92019-02-27 12:44:07 -060061 Get_ALARM_Get_Next = 11,
62 Start_Software_Download = 12,
63 Download_Section = 13,
64 End_Software_Download = 14,
65 Activate_Software = 15,
66 Commit_Software = 15,
Chip Boling2a059952018-03-12 15:50:00 -050067
68
Chip Boling72bbcfe2018-02-14 14:27:59 -060069# abbreviations
70OP = EntityOperations
Chip Boling2a059952018-03-12 15:50:00 -050071RxEvent = OmciCCRxEvents
Chip Boling72bbcfe2018-02-14 14:27:59 -060072
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060073
Chip Boling72bbcfe2018-02-14 14:27:59 -060074class OMCI_CC(object):
75 """ Handle OMCI Communication Channel specifics for Adtran ONUs"""
76
Chip Boling9003cf92019-02-27 12:44:07 -060077 MIN_OMCI_TX_ID_LOW_PRIORITY = 0x0001 # 2 Octets max
78 MAX_OMCI_TX_ID_LOW_PRIORITY = 0x7FFF # 2 Octets max
Chip Bolingc0e4d3b2018-12-27 13:43:10 -060079 MIN_OMCI_TX_ID_HIGH_PRIORITY = 0x8000 # 2 Octets max
80 MAX_OMCI_TX_ID_HIGH_PRIORITY = 0xFFFF # 2 Octets max
81 LOW_PRIORITY = 0
82 HIGH_PRIORITY = 1
83
84 # Offset into some tuples for pending lists and tx in progress
85 PENDING_DEFERRED = 0
86 PENDING_FRAME = 1
87 PENDING_TIMEOUT = 2
88 PENDING_RETRY = 3
89
90 REQUEST_TIMESTAMP = 0
91 REQUEST_DEFERRED = 1
92 REQUEST_FRAME = 2
93 REQUEST_TIMEOUT = 3
94 REQUEST_RETRY = 4
95 REQUEST_DELAYED_CALL = 5
96
Chip Boling2a059952018-03-12 15:50:00 -050097 _frame_to_event_type = {
98 OmciMibResetResponse.message_id: RxEvent.MIB_Reset,
99 OmciMibUploadResponse.message_id: RxEvent.MIB_Upload,
100 OmciMibUploadNextResponse.message_id: RxEvent.MIB_Upload_Next,
101 OmciCreateResponse.message_id: RxEvent.Create,
102 OmciDeleteResponse.message_id: RxEvent.Delete,
103 OmciSetResponse.message_id: RxEvent.Set,
jasonhuang5f3e63b2018-07-27 01:32:48 +0800104 OmciGetAllAlarmsResponse.message_id: RxEvent.Get_ALARM_Get,
105 OmciGetAllAlarmsNextResponse.message_id: RxEvent.Get_ALARM_Get_Next
Chip Boling2a059952018-03-12 15:50:00 -0500106 }
107
lcui33d6a8e2018-08-28 12:51:38 -0700108 def __init__(self, adapter_agent, device_id, me_map=None,
109 clock=None):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600110 self.log = structlog.get_logger(device_id=device_id)
111 self._adapter_agent = adapter_agent
112 self._device_id = device_id
113 self._proxy_address = None
Chip Boling72bbcfe2018-02-14 14:27:59 -0600114 self._enabled = False
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600115 self._extended_messaging = False
Chip Boling72bbcfe2018-02-14 14:27:59 -0600116 self._me_map = me_map
lcui33d6a8e2018-08-28 12:51:38 -0700117 if clock is None:
118 self.reactor = reactor
119 else:
120 self.reactor = clock
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600121
122 # Support 2 levels of priority since only baseline message set supported
123 self._tx_tid = [OMCI_CC.MIN_OMCI_TX_ID_LOW_PRIORITY, OMCI_CC.MIN_OMCI_TX_ID_HIGH_PRIORITY]
124 self._tx_request = [None, None] # Tx in progress (timestamp, defer, frame, timeout, retry, delayedCall)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600125 self._pending = [list(), list()] # pending queue (deferred, tx_frame, timeout, retry)
126 self._rx_response = [None, None]
127
Chip Boling72bbcfe2018-02-14 14:27:59 -0600128 # Statistics
129 self._tx_frames = 0
130 self._rx_frames = 0
131 self._rx_unknown_tid = 0 # Rx OMCI with no Tx TID match
132 self._rx_onu_frames = 0 # Autonomously generated ONU frames
Chip Boling72bbcfe2018-02-14 14:27:59 -0600133 self._rx_onu_discards = 0 # Autonomously generated ONU unknown message types
134 self._rx_timeouts = 0
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600135 self._rx_late = 0 # Frame response received after timeout on Tx
Chip Boling6be00362018-05-30 10:15:41 -0500136 self._rx_unknown_me = 0 # Number of managed entities Rx without a decode definition
Chip Boling72bbcfe2018-02-14 14:27:59 -0600137 self._tx_errors = 0 # Exceptions during tx request
138 self._consecutive_errors = 0 # Rx & Tx errors in a row, a good RX resets this to 0
139 self._reply_min = sys.maxint # Fastest successful tx -> rx
140 self._reply_max = 0 # Longest successful tx -> rx
141 self._reply_sum = 0.0 # Total seconds for successful tx->rx (float for average)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600142 self._max_hp_tx_queue = 0 # Maximum size of high priority tx pending queue
143 self._max_lp_tx_queue = 0 # Maximum size of low priority tx pending queue
Chip Boling72bbcfe2018-02-14 14:27:59 -0600144
Chip Boling2a059952018-03-12 15:50:00 -0500145 self.event_bus = EventBusClient()
146
Chip Boling72bbcfe2018-02-14 14:27:59 -0600147 # If a list of custom ME Entities classes were provided, insert them into
148 # main class_id to entity map.
149 # TODO: If this class becomes hidden from the ONU DA, move this to the OMCI State Machine runner
150
151 def __str__(self):
152 return "OMCISupport: {}".format(self._device_id)
153
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600154 def _get_priority_index(self, high_priority):
155 """ Centralized logic to help make extended message support easier in the future"""
156 return OMCI_CC.HIGH_PRIORITY if high_priority and not self._extended_messaging \
157 else OMCI_CC.LOW_PRIORITY
158
159 def _tid_is_high_priority(self, tid):
160 """ Centralized logic to help make extended message support easier in the future"""
161
162 return not self._extended_messaging and \
163 OMCI_CC.MIN_OMCI_TX_ID_HIGH_PRIORITY <= tid <= OMCI_CC.MAX_OMCI_TX_ID_HIGH_PRIORITY
164
Chip Boling2a059952018-03-12 15:50:00 -0500165 @staticmethod
166 def event_bus_topic(device_id, event):
167 """
168 Get the topic name for a given event Frame Type
169 :param device_id: (str) ONU Device ID
170 :param event: (OmciCCRxEvents) Type of event
171 :return: (str) Topic string
172 """
173 assert event in OmciCCRxEvents, \
Chip Bolingac7b5622018-07-12 18:53:55 -0500174 'Event {} is not an OMCI-CC Rx Event'.format(event.name)
Chip Boling2a059952018-03-12 15:50:00 -0500175
176 return 'omci-rx:{}:{}'.format(device_id, event.name)
177
Chip Boling72bbcfe2018-02-14 14:27:59 -0600178 @property
179 def enabled(self):
180 return self._enabled
181
182 @enabled.setter
183 def enabled(self, value):
184 """
185 Enable/disable the OMCI Communications Channel
186
187 :param value: (boolean) True to enable, False to disable
188 """
189 assert isinstance(value, bool), 'enabled is a boolean'
190
191 if self._enabled != value:
192 self._enabled = value
193 if self._enabled:
194 self._start()
195 else:
196 self._stop()
197
198 @property
199 def tx_frames(self):
200 return self._tx_frames
201
202 @property
203 def rx_frames(self):
204 return self._rx_frames
205
206 @property
207 def rx_unknown_tid(self):
208 return self._rx_unknown_tid # Tx TID not found
209
210 @property
Chip Boling6be00362018-05-30 10:15:41 -0500211 def rx_unknown_me(self):
212 return self._rx_unknown_me
213
214 @property
Chip Boling72bbcfe2018-02-14 14:27:59 -0600215 def rx_onu_frames(self):
216 return self._rx_onu_frames
217
218 @property
Chip Boling72bbcfe2018-02-14 14:27:59 -0600219 def rx_onu_discards(self):
220 return self._rx_onu_discards # Attribute Value change autonomous overflows
221
222 @property
223 def rx_timeouts(self):
224 return self._rx_timeouts
225
226 @property
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600227 def rx_late(self):
228 return self._rx_late
229
230 @property
Chip Boling72bbcfe2018-02-14 14:27:59 -0600231 def tx_errors(self):
232 return self._tx_errors
233
234 @property
235 def consecutive_errors(self):
236 return self._consecutive_errors
237
238 @property
239 def reply_min(self):
240 return int(round(self._reply_min * 1000.0)) # Milliseconds
241
242 @property
243 def reply_max(self):
244 return int(round(self._reply_max * 1000.0)) # Milliseconds
245
246 @property
247 def reply_average(self):
248 avg = self._reply_sum / self._rx_frames if self._rx_frames > 0 else 0.0
249 return int(round(avg * 1000.0)) # Milliseconds
250
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600251 @property
252 def hp_tx_queue_len(self):
253 return len(self._pending[OMCI_CC.HIGH_PRIORITY])
254
255 @property
256 def lp_tx_queue_len(self):
257 return len(self._pending[OMCI_CC.LOW_PRIORITY])
258
259 @property
260 def max_hp_tx_queue(self):
261 return self._max_hp_tx_queue
262
263 @property
264 def max_lp_tx_queue(self):
265 return self._max_lp_tx_queue
266
Chip Boling72bbcfe2018-02-14 14:27:59 -0600267 def _start(self):
268 """
269 Start the OMCI Communications Channel
270 """
271 assert self._enabled, 'Start should only be called if enabled'
Chip Boling72bbcfe2018-02-14 14:27:59 -0600272 self.flush()
273
274 device = self._adapter_agent.get_device(self._device_id)
275 self._proxy_address = device.proxy_address
276
277 def _stop(self):
278 """
279 Stop the OMCI Communications Channel
280 """
281 assert not self._enabled, 'Stop should only be called if disabled'
Chip Boling72bbcfe2018-02-14 14:27:59 -0600282 self.flush()
283 self._proxy_address = None
284
Chip Boling72bbcfe2018-02-14 14:27:59 -0600285 def _receive_onu_message(self, rx_frame):
286 """ Autonomously generated ONU frame Rx handler"""
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600287 self.log.debug('rx-onu-frame', frame_type=type(rx_frame))
Chip Boling72bbcfe2018-02-14 14:27:59 -0600288
Chip Boling72bbcfe2018-02-14 14:27:59 -0600289 msg_type = rx_frame.fields['message_type']
Chip Boling72bbcfe2018-02-14 14:27:59 -0600290 self._rx_onu_frames += 1
291
Chip Boling2a059952018-03-12 15:50:00 -0500292 msg = {TX_REQUEST_KEY: None,
293 RX_RESPONSE_KEY: rx_frame}
294
Chip Boling72bbcfe2018-02-14 14:27:59 -0600295 if msg_type == EntityOperations.AlarmNotification.value:
Chip Boling2a059952018-03-12 15:50:00 -0500296 topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Alarm_Notification)
lcui33d6a8e2018-08-28 12:51:38 -0700297 self.reactor.callLater(0, self.event_bus.publish, topic, msg)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600298
299 elif msg_type == EntityOperations.AttributeValueChange.value:
Chip Boling2a059952018-03-12 15:50:00 -0500300 topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.AVC_Notification)
lcui33d6a8e2018-08-28 12:51:38 -0700301 self.reactor.callLater(0, self.event_bus.publish, topic, msg)
Chip Boling2a059952018-03-12 15:50:00 -0500302
303 elif msg_type == EntityOperations.TestResult.value:
304 topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Test_Result)
lcui33d6a8e2018-08-28 12:51:38 -0700305 self.reactor.callLater(0, self.event_bus.publish, topic, msg)
Chip Boling2a059952018-03-12 15:50:00 -0500306
Chip Boling72bbcfe2018-02-14 14:27:59 -0600307 else:
Chip Boling72bbcfe2018-02-14 14:27:59 -0600308 self.log.warn('onu-unsupported-autonomous-message', type=msg_type)
309 self._rx_onu_discards += 1
310
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600311 def _update_rx_tx_stats(self, now, ts):
312 ts_diff = now - arrow.Arrow.utcfromtimestamp(ts)
313 secs = ts_diff.total_seconds()
314 self._reply_sum += secs
315 if secs < self._reply_min:
316 self._reply_min = secs
317 if secs > self._reply_max:
318 self._reply_max = secs
319 return secs
320
Chip Boling72bbcfe2018-02-14 14:27:59 -0600321 def receive_message(self, msg):
322 """
323 Receive and OMCI message from the proxy channel to the OLT.
324
325 Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
Chip Bolingdce68942018-09-12 09:54:37 -0500326 :param msg: (str) OMCI binary message (used as input to Scapy packet decoder)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600327 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600328 if not self.enabled:
329 return
330
331 try:
332 now = arrow.utcnow()
333 d = None
334
335 # NOTE: Since we may need to do an independent ME map on a per-ONU basis
336 # save the current value of the entity_id_to_class_map, then
337 # replace it with our custom one before decode, and then finally
338 # restore it later. Tried other ways but really made the code messy.
339 saved_me_map = omci_entities.entity_id_to_class_map
340 omci_entities.entity_id_to_class_map = self._me_map
341
Chip Boling72bbcfe2018-02-14 14:27:59 -0600342 try:
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600343 rx_frame = msg if isinstance(msg, OmciFrame) else OmciFrame(msg)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600344
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600345 except KeyError as e:
346 # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
347 self.log.debug('frame-decode-key-error', msg=hexlify(msg), e=e)
348 rx_frame = self._decode_unknown_me(msg)
349 self._rx_unknown_me += 1
Chip Boling72bbcfe2018-02-14 14:27:59 -0600350
351 except Exception as e:
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600352 self.log.exception('frame-decode', msg=hexlify(msg), e=e)
353 return
354
355 finally:
356 omci_entities.entity_id_to_class_map = saved_me_map # Always restore it.
357
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600358 rx_tid = rx_frame.fields['transaction_id']
359 if rx_tid == 0:
360 return self._receive_onu_message(rx_frame)
361
362 # Previously unreachable if this is the very first round-trip Rx or we
363 # have been running consecutive errors
364 if self._rx_frames == 0 or self._consecutive_errors != 0:
365 self.reactor.callLater(0, self._publish_connectivity_event, True)
366
367 self._rx_frames += 1
368 self._consecutive_errors = 0
369
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600370 try:
371 high_priority = self._tid_is_high_priority(rx_tid)
372 index = self._get_priority_index(high_priority)
373
374 # (timestamp, defer, frame, timeout, retry, delayedCall)
375 last_tx_tuple = self._tx_request[index]
376
377 if last_tx_tuple is None or \
378 last_tx_tuple[OMCI_CC.REQUEST_FRAME].fields.get('transaction_id') != rx_tid:
379 # Possible late Rx on a message that timed-out
380 self._rx_unknown_tid += 1
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600381 self._rx_late += 1
382 return
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600383
384 ts, d, tx_frame, timeout, retry, dc = last_tx_tuple
385 if dc is not None and not dc.cancelled and not dc.called:
386 dc.cancel()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600387
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600388 _secs = self._update_rx_tx_stats(now, ts)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600389
390 # Late arrival already serviced by a timeout?
391 if d.called:
392 self._rx_late += 1
393 return
394
395 except Exception as e:
396 self.log.exception('frame-match', msg=hexlify(msg), e=e)
397 if d is not None:
398 return d.errback(failure.Failure(e))
399 return
400
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600401 # Publish Rx event to listeners in a different task
402 reactor.callLater(0, self._publish_rx_frame, tx_frame, rx_frame)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600403
Chip Bolinga5e6aa32019-02-11 12:16:38 -0600404 # begin success callback chain (will cancel timeout and queue next Tx message)
405 self._rx_response[index] = rx_frame
406 d.callback(rx_frame)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600407
408 except Exception as e:
409 self.log.exception('rx-msg', e=e)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600410
Chip Boling6be00362018-05-30 10:15:41 -0500411 def _decode_unknown_me(self, msg):
412 """
413 Decode an ME for an unsupported class ID. This should only occur for a subset
414 of message types (Get, Set, MIB Upload Next, ...) and they should only be
415 responses as well.
416
417 There are some times below that are commented out. For VOLTHA 2.0, it is
418 expected that any get, set, create, delete for unique (often vendor) MEs
419 will be coded by the ONU utilizing it and supplied to OpenOMCI as a
420 vendor-specific ME during device initialization.
421
422 :param msg: (str) Binary data
423 :return: (OmciFrame) resulting frame
424 """
425 from struct import unpack
426
427 (tid, msg_type, framing) = unpack('!HBB', msg[0:4])
428
429 assert framing == 0xa, 'Only basic OMCI framing supported at this time'
430 msg = msg[4:]
431
432 # TODO: Commented out items below are future work (not expected for VOLTHA v2.0)
433 (msg_class, kwargs) = {
434 # OmciCreateResponse.message_id: (OmciCreateResponse, None),
435 # OmciDeleteResponse.message_id: (OmciDeleteResponse, None),
436 # OmciSetResponse.message_id: (OmciSetResponse, None),
437 # OmciGetResponse.message_id: (OmciGetResponse, None),
438 # OmciGetAllAlarmsNextResponse.message_id: (OmciGetAllAlarmsNextResponse, None),
439 OmciMibUploadNextResponse.message_id: (OmciMibUploadNextResponse,
440 {
441 'entity_class': unpack('!H', msg[0:2])[0],
442 'entity_id': unpack('!H', msg[2:4])[0],
443 'object_entity_class': unpack('!H', msg[4:6])[0],
444 'object_entity_id': unpack('!H', msg[6:8])[0],
445 'object_attributes_mask': unpack('!H', msg[8:10])[0],
446 'object_data': {
447 UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[10:-4])
448 },
449 }),
450 # OmciAlarmNotification.message_id: (OmciAlarmNotification, None),
Craig Lutgen66dbf622018-11-18 12:24:54 -0600451 OmciAttributeValueChange.message_id: (OmciAttributeValueChange,
452 {
453 'entity_class': unpack('!H', msg[0:2])[0],
454 'entity_id': unpack('!H', msg[2:4])[0],
455 'data': {
456 UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[4:-8])
457 },
458 }),
Chip Boling6be00362018-05-30 10:15:41 -0500459 # OmciTestResult.message_id: (OmciTestResult, None),
460 }.get(msg_type, None)
461
462 if msg_class is None:
463 raise TypeError('Unsupport Message Type for Unknown Decode: {}',
464 msg_type)
465
466 return OmciFrame(transaction_id=tid, message_type=msg_type,
467 omci_message=msg_class(**kwargs))
468
Chip Boling2a059952018-03-12 15:50:00 -0500469 def _publish_rx_frame(self, tx_frame, rx_frame):
470 """
471 Notify listeners of successful response frame
472 :param tx_frame: (OmciFrame) Original request frame
473 :param rx_frame: (OmciFrame) Response frame
474 """
475 if self._enabled and isinstance(rx_frame, OmciFrame):
476 frame_type = rx_frame.fields['omci_message'].message_id
477 event_type = OMCI_CC._frame_to_event_type.get(frame_type)
478
479 if event_type is not None:
480 topic = OMCI_CC.event_bus_topic(self._device_id, event_type)
481 msg = {TX_REQUEST_KEY: tx_frame,
482 RX_RESPONSE_KEY: rx_frame}
483
484 self.event_bus.publish(topic=topic, msg=msg)
485
486 def _publish_connectivity_event(self, connected):
487 """
488 Notify listeners of Rx/Tx connectivity over OMCI
489 :param connected: (bool) True if connectivity transitioned from unreachable
490 to reachable
491 """
492 if self._enabled:
493 topic = OMCI_CC.event_bus_topic(self._device_id,
494 RxEvent.Connectivity)
495 msg = {CONNECTED_KEY: connected}
496 self.event_bus.publish(topic=topic, msg=msg)
497
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600498 def flush(self):
499 """Flush/cancel in active or pending Tx requests"""
500 requests = []
Chip Boling72bbcfe2018-02-14 14:27:59 -0600501
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600502 for priority in {OMCI_CC.HIGH_PRIORITY, OMCI_CC.LOW_PRIORITY}:
503 next_frame, self._tx_request[priority] = self._tx_request[priority], None
504 if next_frame is not None:
505 requests.append((next_frame[OMCI_CC.REQUEST_DEFERRED], next_frame[OMCI_CC.REQUEST_DELAYED_CALL]))
506
507 requests += [(next_frame[OMCI_CC.PENDING_DEFERRED], None)
508 for next_frame in self._pending[priority]]
509 self._pending[priority] = list()
510
511 # Cancel them...
512 def cleanup_unhandled_error(_):
513 pass # So the cancel below does not flag an unhandled error
514
515 for d, dc in requests:
Chip Boling72bbcfe2018-02-14 14:27:59 -0600516 if d is not None and not d.called:
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600517 d.addErrback(cleanup_unhandled_error)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600518 d.cancel()
519
lcui33d6a8e2018-08-28 12:51:38 -0700520 if dc is not None and not dc.called and not dc.cancelled:
521 dc.cancel()
522
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600523 def _get_tx_tid(self, high_priority=False):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600524 """
525 Get the next Transaction ID for a tx. Note TID=0 is reserved
526 for autonomously generated messages from an ONU
527
528 :return: (int) TID
529 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600530 if self._extended_messaging or not high_priority:
531 index = OMCI_CC.LOW_PRIORITY
532 min_tid = OMCI_CC.MIN_OMCI_TX_ID_LOW_PRIORITY
533 max_tid = OMCI_CC.MAX_OMCI_TX_ID_LOW_PRIORITY
534 else:
535 index = OMCI_CC.HIGH_PRIORITY
536 min_tid = OMCI_CC.MIN_OMCI_TX_ID_HIGH_PRIORITY
537 max_tid = OMCI_CC.MAX_OMCI_TX_ID_HIGH_PRIORITY
538
539 tx_tid, self._tx_tid[index] = self._tx_tid[index], self._tx_tid[index] + 1
540
541 if self._tx_tid[index] > max_tid:
542 self._tx_tid[index] = min_tid
Chip Boling72bbcfe2018-02-14 14:27:59 -0600543
544 return tx_tid
545
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600546 def _request_failure(self, value, tx_tid, high_priority):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600547 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600548 Handle a transmit failure. Rx Timeouts are handled on the 'dc' deferred and
549 will call a different method that may retry if requested. This routine
550 will be called after the final (if any) timeout or other error
Chip Boling72bbcfe2018-02-14 14:27:59 -0600551
552 :param value: (Failure) Twisted failure
553 :param tx_tid: (int) Associated Tx TID
554 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600555 index = self._get_priority_index(high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600556
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600557 if self._tx_request[index] is not None:
558 tx_frame = self._tx_request[index][OMCI_CC.REQUEST_FRAME]
559 tx_frame_tid = tx_frame.fields['transaction_id']
Chip Boling2a059952018-03-12 15:50:00 -0500560
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600561 if tx_frame_tid == tx_tid:
562 timeout = self._tx_request[index][OMCI_CC.REQUEST_TIMEOUT]
563 dc = self._tx_request[index][OMCI_CC.REQUEST_DELAYED_CALL]
564 self._tx_request[index] = None
Chip Boling2a059952018-03-12 15:50:00 -0500565
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600566 if dc is not None and not dc.called and not dc.cancelled:
567 dc.cancel()
Chip Boling72bbcfe2018-02-14 14:27:59 -0600568
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600569 if isinstance(value, failure.Failure):
570 value.trap(CancelledError)
571 self._rx_timeouts += 1
572 self._consecutive_errors += 1
573 if self._consecutive_errors == 1:
574 reactor.callLater(0, self._publish_connectivity_event, False)
575
576 self.log.debug('timeout', tx_id=tx_tid, timeout=timeout)
577 value = failure.Failure(TimeoutError(timeout, "Deferred"))
578 else:
579 # Search pending queue. This may be a cancel coming in from the original
580 # task that requested the Tx. If found, remove
581 # from pending queue
582 for index, request in enumerate(self._pending[index]):
583 req = request.get(OMCI_CC.PENDING_DEFERRED)
584 if req is not None and req.fields['transaction_id'] == tx_tid:
585 self._pending[index].pop(index)
586 break
587
588 self._send_next_request(high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600589 return value
590
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600591 def _request_success(self, rx_frame, high_priority):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600592 """
593 Handle transmit success (a matching Rx was received)
594
595 :param rx_frame: (OmciFrame) OMCI response frame with matching TID
596 :return: (OmciFrame) OMCI response frame with matching TID
597 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600598 index = self._get_priority_index(high_priority)
599
600 if rx_frame is None:
601 rx_frame = self._rx_response[index]
602
603 rx_tid = rx_frame.fields.get('transaction_id')
604
605 if rx_tid is not None:
606 if self._tx_request[index] is not None:
607 tx_frame = self._tx_request[index][OMCI_CC.REQUEST_FRAME]
608 tx_tid = tx_frame.fields['transaction_id']
609
610 if rx_tid == tx_tid:
611 # Remove this request. Next callback in chain initiates next Tx
612 self._tx_request[index] = None
613 else:
614 self._rx_late += 1
615 else:
616 self._rx_late += 1
617
618 self._send_next_request(high_priority)
619
620 # Return rx_frame (to next item in callback list)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600621 return rx_frame
622
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600623 def _request_timeout(self, tx_tid, high_priority):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600624 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600625 Tx Request timed out. Resend immediately if there retries is non-zero. A
626 separate deferred (dc) is used on each actual Tx which is not the deferred
627 (d) that is returned to the caller of the 'send()' method.
628
629 If the timeout if the transmitted frame was zero, this is just cleanup of
630 that transmit request and not necessarily a transmit timeout
631
632 :param tx_tid: (int) TID of frame
633 :param high_priority: (bool) True if high-priority queue
634 """
635 self.log.debug("_request_timeout", tx_tid=tx_tid)
636 index = self._get_priority_index(high_priority)
637
638 if self._tx_request[index] is not None:
639 # (0: timestamp, 1: defer, 2: frame, 3: timeout, 4: retry, 5: delayedCall)
640 ts, d, frame, timeout, retry, _dc = self._tx_request[index]
641
642 if frame.fields.get('transaction_id', 0) == tx_tid:
643 self._tx_request[index] = None
644
645 if timeout > 0:
646 self._rx_timeouts += 1
647
648 if retry > 0:
649 # Push on front of TX pending queue so that it transmits next with the
650 # original TID
651 self._queue_frame(d, frame, timeout, retry - 1, high_priority, front=True)
652
653 elif not d.called:
654 d.errback(failure.Failure(TimeoutError(timeout, "Send OMCI TID -{}".format(tx_tid))))
655 else:
656 self.log.warn('timeout-but-not-the-tx-frame') # Statement mainly for debugging
657
658 self._send_next_request(high_priority)
659
660 def _queue_frame(self, d, frame, timeout, retry, high_priority, front=False):
661 index = self._get_priority_index(high_priority)
662 tx_tuple = (d, frame, timeout, retry) # Pending -> (deferred, tx_frame, timeout, retry)
663
664 if front:
665 self._pending[index].insert(0, tuple)
666 else:
667 self._pending[index].append(tx_tuple)
668
669 # Monitor queue stats
670 qlen = len(self._pending[index])
671
672 if high_priority:
673 if self._max_hp_tx_queue < qlen:
674 self._max_hp_tx_queue = qlen
675
676 elif self._max_lp_tx_queue < qlen:
677 self._max_lp_tx_queue = qlen
678
679 self.log.debug("queue-size", index=index, pending_qlen=qlen)
680
681 def send(self, frame, timeout=DEFAULT_OMCI_TIMEOUT, retry=0, high_priority=False):
682 """
683 Queue the OMCI Frame for a transmit to the ONU via the proxy_channel
Chip Boling72bbcfe2018-02-14 14:27:59 -0600684
685 :param frame: (OMCIFrame) Message to send
lcui33d6a8e2018-08-28 12:51:38 -0700686 :param timeout: (int) Rx Timeout. 0=No response needed
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600687 :param retry: (int) Additional retry attempts on channel failure, default=0
688 :param high_priority: (bool) High Priority requests
Chip Boling72bbcfe2018-02-14 14:27:59 -0600689 :return: (deferred) A deferred that fires when the response frame is received
690 or if an error/timeout occurs
691 """
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600692 if not self.enabled or self._proxy_address is None:
Chip Boling72bbcfe2018-02-14 14:27:59 -0600693 # TODO custom exceptions throughout this code would be helpful
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600694 self._tx_errors += 1
Chip Boling72bbcfe2018-02-14 14:27:59 -0600695 return fail(result=failure.Failure(Exception('OMCI is not enabled')))
696
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600697 timeout = float(timeout)
698 if timeout > float(MAX_OMCI_REQUEST_AGE):
699 self._tx_errors += 1
700 msg = 'Maximum timeout is {} seconds'.format(MAX_OMCI_REQUEST_AGE)
701 return fail(result=failure.Failure(Exception(msg)))
702
703 if not isinstance(frame, OmciFrame):
704 self._tx_errors += 1
705 msg = "Invalid frame class '{}'".format(type(frame))
706 return fail(result=failure.Failure(Exception(msg)))
Chip Boling72bbcfe2018-02-14 14:27:59 -0600707 try:
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600708 index = self._get_priority_index(high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600709 tx_tid = frame.fields['transaction_id']
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600710
Chip Boling72bbcfe2018-02-14 14:27:59 -0600711 if tx_tid is None:
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600712 tx_tid = self._get_tx_tid(high_priority=high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600713 frame.fields['transaction_id'] = tx_tid
714
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600715 assert tx_tid not in self._pending[index], 'TX TID {} is already exists'.format(tx_tid)
716 assert tx_tid > 0, 'Invalid Tx TID: {}'.format(tx_tid)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600717
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600718 # Queue it and request next Tx if tx channel is free
719 d = defer.Deferred()
lcui33d6a8e2018-08-28 12:51:38 -0700720
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600721 self._queue_frame(d, frame, timeout, retry, high_priority, front=False)
722 self._send_next_request(high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600723
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600724 if timeout == 0:
Craig Lutgen26c23f62018-12-17 16:12:26 -0600725 self.log.debug("send-timeout-zero", tx_tid=tx_tid)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600726 self.reactor.callLater(0, d.callback, 'queued')
727
728 return d
Chip Boling72bbcfe2018-02-14 14:27:59 -0600729
730 except Exception as e:
731 self._tx_errors += 1
732 self._consecutive_errors += 1
Chip Boling2a059952018-03-12 15:50:00 -0500733
734 if self._consecutive_errors == 1:
lcui33d6a8e2018-08-28 12:51:38 -0700735 self.reactor.callLater(0, self._publish_connectivity_event, False)
Chip Boling2a059952018-03-12 15:50:00 -0500736
Chip Boling72bbcfe2018-02-14 14:27:59 -0600737 self.log.exception('send-omci', e=e)
738 return fail(result=failure.Failure(e))
739
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600740 def _ok_to_send(self, tx_request, high_priority):
741 """
742 G.988 specifies not to issue a MIB upload or a Software download request
743 when a similar action is in progress on the other channel. To keep the
744 logic here simple, a new upload/download will not be allowed if either a
745 upload/download is going on
746
747 :param tx_request (OmciFrame) Frame to send
748 :param high_priority: (bool) for queue selection
749 :return: True if okay to dequeue and send frame
750 """
751 other = self._get_priority_index(not high_priority)
752
753 if self._tx_request[other] is None:
754 return True
755
756 this_msg_type = tx_request.fields['message_type'] & 0x1f
757 not_allowed = {OP.MibUpload.value,
758 OP.MibUploadNext.value,
759 OP.StartSoftwareDownload.value,
760 OP.DownloadSection.value,
761 OP.EndSoftwareDownload.value}
762
763 if this_msg_type not in not_allowed:
764 return True
765
766 other_msg_type = self._tx_request[other][OMCI_CC.REQUEST_FRAME].fields['message_type'] & 0x1f
767 return other_msg_type not in not_allowed
768
769 def _send_next_request(self, high_priority):
770 """
771 Pull next tx request and send it
772
773 :param high_priority: (bool) True if this was a high priority request
774 :return: results, so callback chain continues if needed
775 """
776 index = self._get_priority_index(high_priority)
777
778 if self._tx_request[index] is None: # TODO or self._tx_request[index][OMCI_CC.REQUEST_DEFERRED].called:
779 d = None
780 try:
781 if len(self._pending[index]) and \
782 not self._ok_to_send(self._pending[index][0][OMCI_CC.PENDING_FRAME],
783 high_priority):
784 reactor.callLater(0.05, self._send_next_request, high_priority)
785 return
786
787 next_frame = self._pending[index].pop(0)
788
789 d = next_frame[OMCI_CC.PENDING_DEFERRED]
790 frame = next_frame[OMCI_CC.PENDING_FRAME]
791 timeout = next_frame[OMCI_CC.PENDING_TIMEOUT]
792 retry = next_frame[OMCI_CC.PENDING_RETRY]
793
794 tx_tid = frame.fields['transaction_id']
795
796 # NOTE: Since we may need to do an independent ME map on a per-ONU basis
797 # save the current value of the entity_id_to_class_map, then
798 # replace it with our custom one before decode, and then finally
799 # restore it later. Tried other ways but really made the code messy.
800 saved_me_map = omci_entities.entity_id_to_class_map
801 omci_entities.entity_id_to_class_map = self._me_map
802
803 ts = arrow.utcnow().float_timestamp
804 try:
805 self._rx_response[index] = None
806 self._adapter_agent.send_proxied_message(self._proxy_address,
807 hexify(str(frame)))
808 finally:
809 omci_entities.entity_id_to_class_map = saved_me_map
810
811 self._tx_frames += 1
812
813 # Note: the 'd' deferred in the queued request we just got will
814 # already have its success callback queued (callLater -> 0) with a
815 # result of "queued". Here we need time it out internally so
816 # we can call cleanup appropriately. G.988 mentions that most ONUs
817 # will process an request in < 1 second.
818 dc_timeout = timeout if timeout > 0 else 1.0
819
820 # Timeout on internal deferred to support internal retries if requested
821 dc = self.reactor.callLater(dc_timeout, self._request_timeout, tx_tid, high_priority)
822
823 # (timestamp, defer, frame, timeout, retry, delayedCall)
824 self._tx_request[index] = (ts, d, frame, timeout, retry, dc)
825
826 if timeout > 0:
827 d.addCallbacks(self._request_success, self._request_failure,
828 callbackArgs=(high_priority,),
829 errbackArgs=(tx_tid, high_priority))
830
831 except IndexError:
832 pass # Nothing pending in this queue
833
834 except Exception as e:
835 self.log.exception('send-proxy-exception', e=e)
836 self._tx_request[index] = None
837 self.reactor.callLater(0, self._send_next_request, high_priority)
838
839 if d is not None:
840 d.errback(failure.Failure(e))
841 else:
842 self.log.debug("tx-request-occupied", index=index)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600843
844 ###################################################################################
845 # MIB Action shortcuts
846
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600847 def send_mib_reset(self, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600848 """
849 Perform a MIB Reset
850 """
851 self.log.debug('send-mib-reset')
852
853 frame = OntDataFrame().mib_reset()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600854 return self.send(frame, timeout=timeout, high_priority=high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600855
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600856 def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600857 self.log.debug('send-mib-upload')
858
859 frame = OntDataFrame().mib_upload()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600860 return self.send(frame, timeout=timeout, high_priority=high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600861
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600862 def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600863 self.log.debug('send-mib-upload-next')
864
Chip Boling9b7a11a2018-04-15 13:33:21 -0500865 frame = OntDataFrame(sequence_number=seq_no).mib_upload_next()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600866 return self.send(frame, timeout=timeout, high_priority=high_priority)
Chip Boling72bbcfe2018-02-14 14:27:59 -0600867
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600868 def send_reboot(self, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
Chip Boling72bbcfe2018-02-14 14:27:59 -0600869 """
870 Send an ONU Device reboot request (ONU-G ME).
Chip Boling21f88222018-07-17 13:25:11 -0500871
872 NOTICE: This method is being deprecated and replaced with a tasks to preform this function
Chip Boling72bbcfe2018-02-14 14:27:59 -0600873 """
874 self.log.debug('send-mib-reboot')
875
876 frame = OntGFrame().reboot()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600877 return self.send(frame, timeout=timeout, high_priority=high_priority)
jasonhuang5f3e63b2018-07-27 01:32:48 +0800878
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600879 def send_get_all_alarm(self, alarm_retrieval_mode=0, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
jasonhuang5f3e63b2018-07-27 01:32:48 +0800880 self.log.debug('send_get_alarm')
881
Chip Boling86221f62018-08-09 15:24:48 -0500882 frame = OntDataFrame().get_all_alarm(alarm_retrieval_mode)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600883 return self.send(frame, timeout=timeout, high_priority=high_priority)
jasonhuang5f3e63b2018-07-27 01:32:48 +0800884
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600885 def send_get_all_alarm_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
jasonhuang5f3e63b2018-07-27 01:32:48 +0800886 self.log.debug('send_get_alarm_next')
887
888 frame = OntDataFrame().get_all_alarm_next(seq_no)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600889 return self.send(frame, timeout=timeout, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700890
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600891 def send_start_software_download(self, image_inst_id, image_size, window_size, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
lcui33d6a8e2018-08-28 12:51:38 -0700892 frame = SoftwareImageFrame(image_inst_id).start_software_download(image_size, window_size-1)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600893 return self.send(frame, timeout, 3, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700894
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600895 def send_download_section(self, image_inst_id, section_num, data, size=DEFAULT_OMCI_DOWNLOAD_SECTION_SIZE, timeout=0, high_priority=False):
lcui33d6a8e2018-08-28 12:51:38 -0700896 """
897 # timeout=0 indicates no repons needed
898 """
899 # self.log.debug("send_download_section", instance_id=image_inst_id, section=section_num, timeout=timeout)
900 if timeout > 0:
901 frame = SoftwareImageFrame(image_inst_id).download_section(True, section_num, data)
902 else:
903 frame = SoftwareImageFrame(image_inst_id).download_section(False, section_num, data)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600904 return self.send(frame, timeout, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700905
906 # if timeout > 0:
907 # self.reactor.callLater(0, self.sim_receive_download_section_resp,
908 # frame.fields["transaction_id"],
909 # frame.fields["omci_message"].fields["section_number"])
910 # return d
911
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600912 def send_end_software_download(self, image_inst_id, crc32, image_size, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
lcui33d6a8e2018-08-28 12:51:38 -0700913 frame = SoftwareImageFrame(image_inst_id).end_software_download(crc32, image_size)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600914 return self.send(frame, timeout, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700915 # self.reactor.callLater(0, self.sim_receive_end_software_download_resp, frame.fields["transaction_id"])
916 # return d
917
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600918 def send_active_image(self, image_inst_id, flag=0, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
lcui33d6a8e2018-08-28 12:51:38 -0700919 frame = SoftwareImageFrame(image_inst_id).activate_image(flag)
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600920 return self.send(frame, timeout, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700921
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600922 def send_commit_image(self, image_inst_id, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
lcui33d6a8e2018-08-28 12:51:38 -0700923 frame = SoftwareImageFrame(image_inst_id).commit_image()
Chip Bolingc0e4d3b2018-12-27 13:43:10 -0600924 return self.send(frame, timeout, high_priority=high_priority)
lcui33d6a8e2018-08-28 12:51:38 -0700925