blob: 048382c1b5c820912dd86417ef570ae1d61b5908 [file] [log] [blame]
Chip Boling32aab302019-01-23 10:50:18 -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 task import Task
17from binascii import hexlify
18from twisted.internet.defer import inlineCallbacks, failure, returnValue
19from twisted.internet import reactor
20from voltha.extensions.omci.omci_defs import ReasonCodes
21from voltha.extensions.omci.omci_me import OmciFrame
22from voltha.extensions.omci.omci import EntityOperations
23
24
25class GetNextException(Exception):
26 pass
27
28
29class GetCapabilitiesFailure(Exception):
30 pass
31
32
33class OnuCapabilitiesTask(Task):
34 """
35 OpenOMCI MIB Capabilities Task
36
37 This task requests information on supported MEs via the OMCI (ME#287)
38 Managed entity.
39
40 This task should be ran after MIB Synchronization and before any MIB
41 Downloads to the ONU.
42
43 Upon completion, the Task deferred callback is invoked with dictionary
44 containing the supported managed entities and message types.
45
46 results = {
47 'supported-managed-entities': {set of supported managed entities},
48 'supported-message-types': {set of supported message types}
49 }
50 """
51 task_priority = 240
52 name = "ONU Capabilities Task"
53
54 max_mib_get_next_retries = 3
55 mib_get_next_delay = 5
56 DEFAULT_OCTETS_PER_MESSAGE = 29
57
58 def __init__(self, omci_agent, device_id, omci_pdu_size=DEFAULT_OCTETS_PER_MESSAGE):
59 """
60 Class initialization
61
62 :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
63 :param device_id: (str) ONU Device ID
64 :param omci_pdu_size: (int) OMCI Data payload size (not counting any trailers)
65 """
66 super(OnuCapabilitiesTask, self).__init__(OnuCapabilitiesTask.name,
67 omci_agent,
68 device_id,
69 priority=OnuCapabilitiesTask.task_priority)
70 self._local_deferred = None
71 self._device = omci_agent.get_device(device_id)
72 self._pdu_size = omci_pdu_size
73 self._supported_entities = set()
74 self._supported_msg_types = set()
75
76 def cancel_deferred(self):
77 super(OnuCapabilitiesTask, self).cancel_deferred()
78
79 d, self._local_deferred = self._local_deferred, None
80 try:
81 if d is not None and not d.called:
82 d.cancel()
83 except:
84 pass
85
86 @property
87 def supported_managed_entities(self):
88 """
89 Return a set of the Managed Entity class IDs supported on this ONU
90
91 None is returned if no MEs have been discovered
92
93 :return: (set of ints)
94 """
95 return frozenset(self._supported_entities) if len(self._supported_entities) else None
96
97 @property
98 def supported_message_types(self):
99 """
100 Return a set of the Message Types supported on this ONU
101
102 None is returned if no message types have been discovered
103
104 :return: (set of EntityOperations)
105 """
106 return frozenset(self._supported_msg_types) if len(self._supported_msg_types) else None
107
108 def start(self):
109 """
110 Start MIB Capabilities task
111 """
112 super(OnuCapabilitiesTask, self).start()
113 self._local_deferred = reactor.callLater(0, self.perform_get_capabilities)
114
115 def stop(self):
116 """
117 Shutdown MIB Capabilities task
118 """
119 self.log.debug('stopping')
120
121 self.cancel_deferred()
122 self._device = None
123 super(OnuCapabilitiesTask, self).stop()
124
125 @inlineCallbacks
126 def perform_get_capabilities(self):
127 """
128 Perform the MIB Capabilities sequence.
129
130 The sequence is to perform a Get request with the attribute mask equal
131 to 'me_type_table'. The response to this request will carry the size
132 of (number of get-next sequences).
133
134 Then a loop is entered and get-next commands are sent for each sequence
135 requested.
136 """
137 self.log.debug('perform-get')
138
139 try:
140 self.strobe_watchdog()
141 self._supported_entities = yield self.get_supported_entities()
142
143 self.strobe_watchdog()
144 self._supported_msg_types = yield self.get_supported_message_types()
145
146 self.log.debug('get-success',
147 supported_entities=self.supported_managed_entities,
148 supported_msg_types=self.supported_message_types)
149 results = {
150 'supported-managed-entities': self.supported_managed_entities,
151 'supported-message-types': self.supported_message_types
152 }
153 self.deferred.callback(results)
154
155 except Exception as e:
156 self.log.exception('perform-get', e=e)
157 self.deferred.errback(failure.Failure(e))
158
159 def get_count_from_data_buffer(self, data):
160 """
161 Extract the 4 octet buffer length from the OMCI PDU contents
162 """
163 self.log.debug('get-count-buffer', data=hexlify(data))
164 return int(hexlify(data[:4]), 16)
165
166 @inlineCallbacks
167 def get_supported_entities(self):
168 """
169 Get the supported ME Types for this ONU.
170 """
171 try:
172 # Get the number of requests needed
173 frame = OmciFrame(me_type_table=True).get()
174 self.strobe_watchdog()
175 results = yield self._device.omci_cc.send(frame)
176
177 omci_msg = results.fields['omci_message']
178 status = omci_msg.fields['success_code']
179
180 if status != ReasonCodes.Success.value:
181 raise GetCapabilitiesFailure('Get count of supported entities failed with status code: {}'.
182 format(status))
183 data = omci_msg.fields['data']['me_type_table']
184 count = self.get_count_from_data_buffer(bytearray(data))
185
186 seq_no = 0
187 data_buffer = bytearray(0)
188 self.log.debug('me-type-count', octets=count, data=hexlify(data))
189
190 # Start the loop
191 for offset in xrange(0, count, self._pdu_size):
192 frame = OmciFrame(me_type_table=seq_no).get_next()
193 seq_no += 1
194 self.strobe_watchdog()
195 results = yield self._device.omci_cc.send(frame)
196
197 omci_msg = results.fields['omci_message']
198 status = omci_msg.fields['success_code']
199
200 if status != ReasonCodes.Success.value:
201 raise GetCapabilitiesFailure(
202 'Get supported entities request at offset {} of {} failed with status code: {}'.
203 format(offset + 1, count, status))
204
205 # Extract the data
206 num_octets = count - offset
207 if num_octets > self._pdu_size:
208 num_octets = self._pdu_size
209
210 data = omci_msg.fields['data']['me_type_table']
211 data_buffer += bytearray(data[:num_octets])
212
213 me_types = {(data_buffer[x] << 8) + data_buffer[x + 1]
214 for x in xrange(0, len(data_buffer), 2)}
215 returnValue(me_types)
216
217 except Exception as e:
218 self.log.exception('get-entities', e=e)
219 self.deferred.errback(failure.Failure(e))
220
221 @inlineCallbacks
222 def get_supported_message_types(self):
223 """
224 Get the supported Message Types (actions) for this ONU.
225 """
226 try:
227 # Get the number of requests needed
228 frame = OmciFrame(message_type_table=True).get()
229 self.strobe_watchdog()
230 results = yield self._device.omci_cc.send(frame)
231
232 omci_msg = results.fields['omci_message']
233 status = omci_msg.fields['success_code']
234
235 if status != ReasonCodes.Success.value:
236 raise GetCapabilitiesFailure('Get count of supported msg types failed with status code: {}'.
237 format(status))
238
239 data = omci_msg.fields['data']['message_type_table']
240 count = self.get_count_from_data_buffer(bytearray(data))
241
242 seq_no = 0
243 data_buffer = list()
244 self.log.debug('me-type-count', octets=count, data=hexlify(data))
245
246 # Start the loop
247 for offset in xrange(0, count, self._pdu_size):
248 frame = OmciFrame(message_type_table=seq_no).get_next()
249 seq_no += 1
250 self.strobe_watchdog()
251 results = yield self._device.omci_cc.send(frame)
252
253 omci_msg = results.fields['omci_message']
254 status = omci_msg.fields['success_code']
255
256 if status != ReasonCodes.Success.value:
257 raise GetCapabilitiesFailure(
258 'Get supported msg types request at offset {} of {} failed with status code: {}'.
259 format(offset + 1, count, status))
260
261 # Extract the data
262 num_octets = count - offset
263 if num_octets > self._pdu_size:
264 num_octets = self._pdu_size
265
266 data = omci_msg.fields['data']['message_type_table']
267 data_buffer += data[:num_octets]
268
269 def buffer_to_message_type(value):
270 """
271 Convert an integer value to the appropriate EntityOperations enumeration
272 :param value: (int) Message type value (4..29)
273 :return: (EntityOperations) Enumeration, None on failure
274 """
275 next((v for k, v in EntityOperations.__members__.items() if v.value == value), None)
276
277 msg_types = {buffer_to_message_type(v) for v in data_buffer if v is not None}
278 returnValue({msg_type for msg_type in msg_types if msg_type is not None})
279
280 except Exception as e:
281 self.log.exception('get-msg-types', e=e)
282 self.deferred.errback(failure.Failure(e))