blob: 743ee1abd31836d81bce15f78b24425f21c305ea [file] [log] [blame]
Chip Boling72bbcfe2018-02-14 14:27:59 -06001#
2# Copyright 2017 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#
16"""
17OMCI Managed Entity Message support base class
18"""
19from voltha.extensions.omci.omci import *
20
21# abbreviations
22OP = EntityOperations
23AA = AttributeAccess
24
25
26class MEFrame(object):
27 """Base class to help simplify Frame Creation"""
28 def __init__(self, entity_class, entity_id, data):
29 assert issubclass(entity_class, EntityClass), \
30 "'{}' must be a subclass of MEFrame".format(entity_class)
31 self.check_type(entity_id, int)
32
33 if not 0 <= entity_id <= 0xFFFF:
34 raise ValueError('entity_id should be 0..65535')
35
36 self._class = entity_class
37 self._entity_id = entity_id
38 self.data = data
39
40 def __str__(self):
41 return '{}: Entity_ID: {}, Data: {}'.\
42 format(self.entity_class_name, self._entity_id, self.data)
43
44 @property
45 def entity_class(self):
46 """
47 The Entity Class for this ME
48 :return: (EntityClass) Entity class
49 """
50 return self._class
51
52 @property
53 def entity_class_name(self):
54 return self._class.__name__
55
56 @property
57 def entity_id(self):
58 """
59 The Entity ID for this ME frame
60 :return: (int) Entity ID (0..0xFFFF)
61 """
62 return self._entity_id
63
64 @staticmethod
65 def check_type(param, types):
66 if not isinstance(param, types):
67 raise TypeError("Parameter '{}' should be a {}".format(param, types))
68
69 def _check_operation(self, operation):
70 allowed = self.entity_class.mandatory_operations | self.entity_class.optional_operations
71 assert operation in allowed, "{} not allowed for '{}'".format(operation.name,
72 self.entity_class_name)
73
74 def _check_attributes(self, attributes, access):
75 keys = attributes.keys() if isinstance(attributes, dict) else attributes
76 for attr_name in keys:
77 # Bad attribute name (invalid or spelling error)?
78 index = self.entity_class.attribute_name_to_index_map.get(attr_name)
79 if index is None:
80 raise KeyError("Attribute '{}' is not valid for '{}'".
81 format(attr_name, self.entity_class_name))
82 # Invalid access?
83 assert access in self.entity_class.attributes[index].access, \
84 "Access '{}' for attribute '{}' is not valid for '{}'".format(access.name,
85 attr_name,
86 self.entity_class_name)
87
88 if access.value in [AA.W.value, AA.SBC.value] and isinstance(attributes, dict):
89 for attr_name, value in attributes.iteritems():
90 index = self.entity_class.attribute_name_to_index_map.get(attr_name)
91 attribute = self.entity_class.attributes[index]
92 if not attribute.valid(value):
93 raise ValueError("Invalid value '{}' for attribute '{}' of '{}".
94 format(value, attr_name, self.entity_class_name))
95
96 @staticmethod
97 def _attr_to_data(attributes):
98 """
99 Convert an object into the 'data' set or dictionary for get/set/create/delete
100 requests.
101
102 This method takes a 'string', 'list', or 'set' for get requests and
103 converts it to a 'set' of attributes.
104
105 For create/set requests a dictionary of attribute/value pairs is required
106
107 :param attributes: (basestring, list, set, dict) attributes. For gets
108 a string, list, set, or dict can be provided. For create/set
109 operations, a dictionary should be provided. For delete
110 the attributes may be None since they are ignored.
111
112 :return: (set, dict) set for get/deletes, dict for create/set
113 """
114 if isinstance(attributes, basestring):
115 # data = [str(attributes)]
116 data = set()
117 data.add(str(attributes))
118
119 elif isinstance(attributes, list):
120 assert all(isinstance(attr, basestring) for attr in attributes),\
121 'attribute list must be strings'
122 data = {str(attr) for attr in attributes}
123 assert len(data) == len(attributes), 'Attributes were not unique'
124
125 elif isinstance(attributes, set):
126 assert all(isinstance(attr, basestring) for attr in attributes),\
127 'attribute set must be strings'
128 data = {str(attr) for attr in attributes}
129
130 elif isinstance(attributes, (dict, type(None))):
131 data = attributes
132
133 else:
134 raise TypeError("Unsupported attributes type '{}'".format(type(attributes)))
135
136 return data
137
138 def create(self):
139 """
140 Create a Create request frame for this ME
141 :return: (OmciFrame) OMCI Frame
142 """
143 assert hasattr(self.entity_class, 'class_id'), 'class_id required for Create actions'
144 assert hasattr(self, 'entity_id'), 'entity_id required for Create actions'
145 assert hasattr(self, 'data'), 'data required for Create actions'
146
147 data = getattr(self, 'data')
148 MEFrame.check_type(data, dict)
149 assert len(data) > 0, 'No attributes supplied'
150
151 self._check_operation(OP.Create)
152 self._check_attributes(data, AA.Writable)
153
154 return OmciFrame(
155 transaction_id=None,
156 message_type=OmciCreate.message_id,
157 omci_message=OmciCreate(
158 entity_class=getattr(self.entity_class, 'class_id'),
159 entity_id=getattr(self, 'entity_id'),
160 data=data
161 ))
162
163 def delete(self):
164 """
165 Create a Delete request frame for this ME
166 :return: (OmciFrame) OMCI Frame
167 """
168 self._check_operation(OP.Delete)
169
170 return OmciFrame(
171 transaction_id=None,
Chip Boling2a059952018-03-12 15:50:00 -0500172 message_type=OmciDelete.message_id,
173 omci_message=OmciDelete(
Chip Boling72bbcfe2018-02-14 14:27:59 -0600174 entity_class=getattr(self.entity_class, 'class_id'),
175 entity_id=getattr(self, 'entity_id')
176 ))
177
178 def set(self):
179 """
180 Create a Set request frame for this ME
181 :return: (OmciFrame) OMCI Frame
182 """
183 assert hasattr(self, 'data'), 'data required for Set actions'
184 data = getattr(self, 'data')
185 MEFrame.check_type(data, dict)
186 assert len(data) > 0, 'No attributes supplied'
187
188 self._check_operation(OP.Set)
189 self._check_attributes(data, AA.Writable)
190
191 return OmciFrame(
192 transaction_id=None,
193 message_type=OmciSet.message_id,
194 omci_message=OmciSet(
195 entity_class=getattr(self.entity_class, 'class_id'),
196 entity_id=getattr(self, 'entity_id'),
197 attributes_mask=self.entity_class.mask_for(*data.keys()),
198 data=data
199 ))
200
201 def get(self):
202 """
203 Create a Get request frame for this ME
204 :return: (OmciFrame) OMCI Frame
205 """
206 assert hasattr(self, 'data'), 'data required for Get actions'
207 data = getattr(self, 'data')
208 MEFrame.check_type(data, (list, set, dict))
209 assert len(data) > 0, 'No attributes supplied'
210
211 mask_set = data.keys() if isinstance(data, dict) else data
212
213 self._check_operation(OP.Get)
214 self._check_attributes(mask_set, AA.Readable)
215
216 return OmciFrame(
217 transaction_id=None,
218 message_type=OmciGet.message_id,
219 omci_message=OmciGet(
220 entity_class=getattr(self.entity_class, 'class_id'),
221 entity_id=getattr(self, 'entity_id'),
222 attributes_mask=self.entity_class.mask_for(*mask_set)
223 ))
224
225 def reboot(self):
226 """
227 Create a Reboot request from for this ME
228 :return: (OmciFrame) OMCI Frame
229 """
230 self._check_operation(OP.Reboot)
231
232 return OmciFrame(
233 transaction_id=None,
234 message_type=OmciReboot.message_id,
235 omci_message=OmciReboot(
236 entity_class=getattr(self.entity_class, 'class_id'),
237 entity_id=getattr(self, 'entity_id')
238 ))
239
240 def mib_reset(self):
241 """
242 Create a MIB Reset request from for this ME
243 :return: (OmciFrame) OMCI Frame
244 """
245 self._check_operation(OP.MibReset)
246
247 return OmciFrame(
248 transaction_id=None,
249 message_type=OmciMibReset.message_id,
250 omci_message=OmciMibReset(
251 entity_class=getattr(self.entity_class, 'class_id'),
252 entity_id=getattr(self, 'entity_id')
253 ))
254
255 def mib_upload(self):
256 """
257 Create a MIB Upload request from for this ME
258 :return: (OmciFrame) OMCI Frame
259 """
260 self._check_operation(OP.MibUpload)
261
262 return OmciFrame(
263 transaction_id=None,
264 message_type=OmciMibUpload.message_id,
265 omci_message=OmciMibUpload(
266 entity_class=getattr(self.entity_class, 'class_id'),
267 entity_id=getattr(self, 'entity_id')
268 ))
269
270 def mib_upload_next(self):
271 """
272 Create a MIB Upload Next request from for this ME
273 :return: (OmciFrame) OMCI Frame
274 """
275 assert hasattr(self, 'data'), 'data required for Set actions'
276 data = getattr(self, 'data')
277 MEFrame.check_type(data, dict)
278 assert len(data) > 0, 'No attributes supplied'
279 assert 'mib_data_sync' in data, "'mib_data_sync' not in attributes list"
280
281 self._check_operation(OP.MibUploadNext)
282 self._check_attributes(data, AA.Writable)
283
284 return OmciFrame(
285 transaction_id=None,
286 message_type=OmciMibUploadNext.message_id,
287 omci_message=OmciMibUploadNext(
288 entity_class=getattr(self.entity_class, 'class_id'),
289 entity_id=getattr(self, 'entity_id'),
290 command_sequence_number=data['mib_data_sync']
291 ))
Chip Boling28155862018-06-07 11:13:39 -0500292
293 def get_next(self):
294 """
295 Create a Get Next request frame for this ME
296 :return: (OmciFrame) OMCI Frame
297 """
298 assert hasattr(self, 'data'), 'data required for Get Next actions'
299 data = getattr(self, 'data')
300 MEFrame.check_type(data, dict)
301 assert len(data) == 1, 'Only one attribute should be specified'
302
303 mask_set = data.keys() if isinstance(data, dict) else data
304
305 self._check_operation(OP.GetNext)
306 self._check_attributes(mask_set, AA.Readable)
307
308 return OmciFrame(
309 transaction_id=None,
310 message_type=OmciGetNext.message_id,
311 omci_message=OmciGetNext(
312 entity_class=getattr(self.entity_class, 'class_id'),
313 entity_id=getattr(self, 'entity_id'),
314 attributes_mask=self.entity_class.mask_for(*mask_set),
315 command_sequence_number=data.values()[0]
316 ))