blob: d41c1d05baffffb4a8786b765f9cb3222f7ed2a7 [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 datetime import datetime
18from twisted.internet import reactor
19from twisted.internet.defer import inlineCallbacks, TimeoutError, failure
20from voltha.extensions.omci.omci_defs import ReasonCodes
21from voltha.extensions.omci.omci_frame import OmciFrame, OmciGet
22
23
24class IntervalDataTaskFailure(Exception):
25 pass
26
27
28class IntervalDataTask(Task):
29 """
30 OpenOMCI Performance Interval Get Request
31 """
32 task_priority = Task.DEFAULT_PRIORITY
33 name = "Interval Data Task"
34 max_payload = 29
35
36 def __init__(self, omci_agent, device_id, class_id, entity_id,
37 max_get_response_payload=max_payload,
38 parent_class_id=None,
39 parent_entity_id=None,
40 upstream=None):
41 """
42 Class initialization
43
44 :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
45 :param device_id: (str) ONU Device ID
46 :param class_id: (int) ME Class ID
47 :param entity_id: (int) ME entity ID
48 :param max_get_response_payload: (int) Maximum number of octets in a
49 single GET response frame
50 """
51 super(IntervalDataTask, self).__init__(IntervalDataTask.name,
52 omci_agent,
53 device_id,
54 priority=IntervalDataTask.task_priority,
55 exclusive=False)
56 self._local_deferred = None
57 self._class_id = class_id
58 self._entity_id = entity_id
59
60 self._parent_class_id = parent_class_id
61 self._parent_entity_id = parent_entity_id
62 self._upstream = upstream
63
64 me_map = self.omci_agent.get_device(self.device_id).me_map
65 if self._class_id not in me_map:
66 msg = "The requested ME Class () does not exist in the ONU's ME Map".format(self._class_id)
67 self.log.warn('unknown-pm-me', msg=msg)
68 raise IntervalDataTaskFailure(msg)
69
70 self._entity = me_map[self._class_id]
71 self._counter_attributes = self.get_counter_attributes_names_and_size()
72 self._max_payload = max_get_response_payload
73
74 def cancel_deferred(self):
75 super(IntervalDataTask, self).cancel_deferred()
76
77 d, self._local_deferred = self._local_deferred, None
78 try:
79 if d is not None and not d.called:
80 d.cancel()
81 except:
82 pass
83
84 def start(self):
85 """
86 Start the tasks
87 """
88 super(IntervalDataTask, self).start()
89 self._local_deferred = reactor.callLater(0, self.perform_get_interval)
90
91 def stop(self):
92 """
93 Shutdown the tasks
94 """
95 self.log.debug('stopping')
96
97 self.cancel_deferred()
98 super(IntervalDataTask, self).stop()
99
100 def get_counter_attributes_names_and_size(self):
101 """
102 Get all of the counter attributes names and the amount of storage they take
103
104 :return: (dict) Attribute name -> length
105 """
106 return {name: self._entity.attributes[attr_index].field.sz
107 for name, attr_index in self._entity.attribute_name_to_index_map.items()
108 if self._entity.attributes[attr_index].is_counter}
109
110 @inlineCallbacks
111 def perform_get_interval(self):
112 """
113 Sync the time
114 """
115 self.log.info('perform-get-interval', class_id=self._class_id,
116 entity_id=self._entity_id)
117
118 device = self.omci_agent.get_device(self.device_id)
119 attr_names = self._counter_attributes.keys()
120
121 final_results = {
122 'class_id': self._class_id,
123 'entity_id': self._entity_id,
124 'me_name': self._entity.__name__, # Mostly for debugging...
125 'interval_utc_time': None,
126 'parent_class_id': self._parent_class_id,
127 'parent_entity_id': self._parent_entity_id,
128 'upstream': self._upstream
129 # Counters added here as they are retrieved
130 }
131 last_end_time = None
132
133 while len(attr_names) > 0:
134 # Get as many attributes that will fit. Always include the 1 octet
135 # Interval End Time Attribute and 2 octets for the Entity ID
136
137 remaining_payload = self._max_payload - 3
138 attributes = list()
139 for name in attr_names:
140 if self._counter_attributes[name] > remaining_payload:
141 break
142
143 attributes.append(name)
144 remaining_payload -= self._counter_attributes[name]
145
146 attr_names = attr_names[len(attributes):]
147 attributes.append('interval_end_time')
148
149 frame = OmciFrame(
150 transaction_id=None,
151 message_type=OmciGet.message_id,
152 omci_message=OmciGet(
153 entity_class=self._class_id,
154 entity_id=self._entity_id,
155 attributes_mask=self._entity.mask_for(*attributes)
156 )
157 )
158 self.log.debug('interval-get-request', class_id=self._class_id,
159 entity_id=self._entity_id)
160 try:
161 self.strobe_watchdog()
162 results = yield device.omci_cc.send(frame)
163
164 omci_msg = results.fields['omci_message'].fields
165 status = omci_msg['success_code']
166 end_time = omci_msg['data'].get('interval_end_time')
167
168 self.log.debug('interval-get-results', class_id=self._class_id,
169 entity_id=self._entity_id, status=status,
170 end_time=end_time)
171
172 if status != ReasonCodes.Success:
173 raise IntervalDataTaskFailure('Unexpected Response Status: {}, Class ID: {}'.
174 format(status, self._class_id))
175 if last_end_time is None:
176 last_end_time = end_time
177
178 elif end_time != last_end_time:
179 msg = 'Interval End Time Changed during retrieval from {} to {}'\
180 .format(last_end_time, end_time)
181 self.log.info('interval-roll-over', msg=msg, class_id=self._class_id)
182 raise IntervalDataTaskFailure(msg)
183
184 final_results['interval_utc_time'] = datetime.utcnow()
185 for attribute in attributes:
186 final_results[attribute] = omci_msg['data'].get(attribute)
187
188 except TimeoutError as e:
189 self.log.warn('interval-get-timeout', e=e, class_id=self._class_id,
190 entity_id=self._entity_id, attributes=attributes)
191 self.deferred.errback(failure.Failure(e))
192
193 except Exception as e:
194 self.log.exception('interval-get-failure', e=e, class_id=self._class_id)
195 self.deferred.errback(failure.Failure(e))
196
197 # Successful if here
198 self.deferred.callback(final_results)