blob: 832a821d6acd67f38a74ca5d550b282c00ccf247 [file] [log] [blame]
Chip Bolingc7d3e2d2018-04-06 10:16:29 -05001#
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#
Chip Bolingc7d3e2d2018-04-06 10:16:29 -050016from mib_db_api import *
17from voltha.protos.omci_mib_db_pb2 import MibInstanceData, MibClassData, \
18 MibDeviceData, MibAttributeData
19from voltha.extensions.omci.omci_entities import *
20from scapy.fields import StrField
21
22
23class MibDbExternal(MibDbApi):
24 """
25 A persistent external OpenOMCI MIB Database
26 """
27 CURRENT_VERSION = 1 # VOLTHA v1.3.0 release
28
29 _TIME_FORMAT = '%Y%m%d-%H%M%S.%f'
30
31 # Paths from root proxy
32 MIB_PATH = '/omci_mibs'
33 DEVICE_PATH = MIB_PATH + '/{}' # .format(device_id)
34
35 # Classes, Instances, and Attributes as lists from root proxy
36 CLASSES_PATH = DEVICE_PATH + '/classes' # .format(device_id)
37 INSTANCES_PATH = DEVICE_PATH +'/classes/{}/instances' # .format(device_id, class_id)
38 ATTRIBUTES_PATH = DEVICE_PATH + '/classes/{}/instances/{}/attributes' # .format(device_id, class_id, instance_id)
39
40 # Single Class, Instance, and Attribute as objects from device proxy
41 CLASS_PATH = '/classes/{}' # .format(class_id)
42 INSTANCE_PATH = '/classes/{}/instances/{}' # .format(class_id, instance_id)
43 ATTRIBUTE_PATH = '/classes/{}/instances/{}/attributes/{}' # .format(class_id, instance_id
44 # attribute_name)
45
46 def __init__(self, omci_agent):
47 """
48 Class initializer
49 :param omci_agent: (OpenOMCIAgent) OpenOMCI Agent
50 """
51 super(MibDbExternal, self).__init__(omci_agent)
52 self._core = omci_agent.core
53
54 def start(self):
55 """
56 Start up/restore the database
57 """
58 self.log.debug('start')
59
60 if not self._started:
61 super(MibDbExternal, self).start()
62 root_proxy = self._core.get_proxy('/')
63
64 try:
65 base = root_proxy.get(MibDbExternal.MIB_PATH)
66 self.log.info('db-exists', num_devices=len(base))
67
68 except Exception as e:
69 self.log.exception('start-failure', e=e)
70 raise
71
72 def stop(self):
73 """
74 Start up the database
75 """
76 self.log.debug('stop')
77
78 if self._started:
79 super(MibDbExternal, self).stop()
80 # TODO: Delete this method if nothing else is done except calling the base class
81
82 def _time_to_string(self, time):
83 return time.strftime(MibDbExternal._TIME_FORMAT) if time is not None else ''
84
85 def _string_to_time(self, time):
86 return datetime.strptime(time, MibDbExternal._TIME_FORMAT) if len(time) else None
87
88 def _attribute_to_string(self, device_id, class_id, attr_name, value):
89 """
90 Convert an ME's attribute value to string representation
91
92 :param device_id: (str) ONU Device ID
93 :param class_id: (int) Class ID
94 :param attr_name: (str) Attribute Name (see EntityClasses)
95 :param value: (various) Attribute Value
96
97 :return: (str) String representation of the value
98 :raises KeyError: Device, Class ID, or Attribute does not exist
99 """
100 try:
101 me_map = self._omci_agent.get_device(device_id).me_map
102 entity = me_map[class_id]
103 attr_index = entity.attribute_name_to_index_map[attr_name]
104 eca = entity.attributes[attr_index]
105 field = eca.field
106
Chip Bolingbed4c6b2018-05-09 08:52:16 -0500107 if isinstance(field, StrFixedLenField):
108 # For StrFixedLenField, value is an str already (or possibly JSON encoded object)
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500109 if hasattr(value, 'to_json'):
110 str_value = value.to_json()
111 else:
112 str_value = str(value)
113
Chip Bolingbed4c6b2018-05-09 08:52:16 -0500114 elif isinstance(field, (StrField, MACField, IPField)):
115 # For StrField, value is an str already
116 # For MACField, value is a string in ':' delimited form
117 # For IPField, value is a string in '.' delimited form
118 str_value = str(value)
119
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500120 elif isinstance(field, (ByteField, ShortField, IntField, LongField)):
121 # For ByteField, ShortField, IntField, and LongField value is an int
122 str_value = str(value)
123
124 elif isinstance(field, BitField):
125 # For BitField, value is a long
126 #
127 str_value = str(value)
128
129 else:
130 self.log.warning('default-conversion', type=type(field),
131 class_id=class_id, attribute=attr_name, value=str(value))
132 str_value = str(value)
133
134 return str_value
135
136 except Exception as e:
137 self.log.exception('attr-to-string', device_id=device_id,
138 class_id=class_id, attr=attr_name,
139 value=value, e=e)
140 raise
141
142 def _string_to_attribute(self, device_id, class_id, attr_name, str_value):
143 """
144 Convert an ME's attribute value-string to its Scapy decode equivalent
145
146 :param device_id: (str) ONU Device ID
147 :param class_id: (int) Class ID
148 :param attr_name: (str) Attribute Name (see EntityClasses)
149 :param str_value: (str) Attribute Value in string form
150
151 :return: (various) String representation of the value
152 :raises KeyError: Device, Class ID, or Attribute does not exist
153 """
154 try:
155 me_map = self._omci_agent.get_device(device_id).me_map
156 entity = me_map[class_id]
157 attr_index = entity.attribute_name_to_index_map[attr_name]
158 eca = entity.attributes[attr_index]
159 field = eca.field
160
161 if isinstance(field, StrFixedLenField):
Chip Bolingbed4c6b2018-05-09 08:52:16 -0500162 from scapy.base_classes import Packet_metaclass
163 if isinstance(field.default, Packet_metaclass) and \
164 hasattr(field.default, 'to_json'):
165 value = json.loads(str_value)
166 else:
167 value = str_value
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500168
169 elif isinstance(field, MACField):
170 value = str_value
171
172 elif isinstance(field, IPField):
173 value = str_value
174
175 elif isinstance(field, (ByteField, ShortField, IntField, LongField)):
Chip Bolingbed4c6b2018-05-09 08:52:16 -0500176 if str_value.lower() in ('true', 'false'):
177 str_value = '1' if str_value.lower() == 'true' else '0'
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500178 value = int(str_value)
179
180 elif isinstance(field, BitField):
181 value = long(str_value)
182
183 else:
184 self.log.warning('default-conversion', type=type(field),
185 class_id=class_id, attribute=attr_name, value=str_value)
186 value = None
187
188 return value
189
190 except Exception as e:
191 self.log.exception('attr-to-string', device_id=device_id,
192 class_id=class_id, attr=attr_name,
193 value=str_value, e=e)
194 raise
195
196 def add(self, device_id, overwrite=False):
197 """
198 Add a new ONU to database
199
200 :param device_id: (str) Device ID of ONU to add
201 :param overwrite: (bool) Overwrite existing entry if found.
202
203 :raises KeyError: If device already exists and 'overwrite' is False
204 """
205 self.log.debug('add-device', device_id=device_id, overwrite=overwrite)
206
207 now = datetime.utcnow()
208 found = False
209 root_proxy = self._core.get_proxy('/')
210 data = MibDeviceData(device_id=device_id,
211 created=self._time_to_string(now),
212 last_sync_time='',
213 mib_data_sync=0,
214 version=MibDbExternal.CURRENT_VERSION)
215 try:
216 dev_proxy = self._device_proxy(device_id)
217 found = True
218
219 if not overwrite:
220 # Device already exists
221 raise KeyError('Device with ID {} already exists in MIB database'.
222 format(device_id))
223
224 # Overwrite with new data
225 data = dev_proxy.get('/', depth=0)
226 self._root_proxy.update(MibDbExternal.DEVICE_PATH.format(device_id), data)
227 self._modified = now
228
229 except KeyError:
230 if found:
231 raise
232 # Did not exist, add it now
233 root_proxy.add(MibDbExternal.MIB_PATH, data)
234 self._created = now
235 self._modified = now
236
237 def remove(self, device_id):
238 """
239 Remove an ONU from the database
240
241 :param device_id: (str) Device ID of ONU to remove from database
242 """
243 self.log.debug('remove-device', device_id=device_id)
244
245 if not self._started:
246 raise DatabaseStateError('The Database is not currently active')
247
248 if not isinstance(device_id, basestring):
249 raise TypeError('Device ID should be an string')
250
251 try:
252 # self._root_proxy.get(MibDbExternal.DEVICE_PATH.format(device_id))
253 self._root_proxy.remove(MibDbExternal.DEVICE_PATH.format(device_id))
254 self._modified = datetime.utcnow()
255
256 except KeyError:
257 # Did not exists, which is not a failure
258 pass
259
260 except Exception as e:
261 self.log.exception('remove-exception', device_id=device_id, e=e)
262 raise
263
264 @property
265 def _root_proxy(self):
266 return self._core.get_proxy('/')
267
268 def _device_proxy(self, device_id):
269 """
270 Return a config proxy to the OMCI MIB_DB leaf for a given device
271
272 :param device_id: (str) ONU Device ID
273 :return: (ConfigProxy) Configuration proxy rooted at OMCI MIB DB
274 :raises KeyError: If the device does not exist in the database
275 """
276 if not isinstance(device_id, basestring):
277 raise TypeError('Device ID should be an string')
278
279 if not self._started:
280 raise DatabaseStateError('The Database is not currently active')
281
282 return self._core.get_proxy(MibDbExternal.DEVICE_PATH.format(device_id))
283
284 def _class_proxy(self, device_id, class_id, create=False):
285 """
286 Get a config proxy to a specific managed entity class
287 :param device_id: (str) ONU Device ID
288 :param class_id: (int) Class ID
289 :param create: (bool) If true, create default instance (and class)
290 :return: (ConfigProxy) Class configuration proxy
291
292 :raises DatabaseStateError: If database is not started
293 :raises KeyError: If Instance does not exist and 'create' is False
294 """
295 if not self._started:
296 raise DatabaseStateError('The Database is not currently active')
297
298 if not 0 <= class_id <= 0xFFFF:
299 raise ValueError('class-id is 0..0xFFFF')
300
301 fmt = MibDbExternal.DEVICE_PATH + MibDbExternal.CLASS_PATH
302 path = fmt.format(device_id, class_id)
303
304 try:
305 return self._core.get_proxy(path)
306
307 except KeyError:
308 if not create:
309 self.log.error('class-proxy-does-not-exist', device_id=device_id,
310 class_id=class_id)
311 raise
312
313 # Create class
314 data = MibClassData(class_id=class_id)
315 root_path = MibDbExternal.CLASSES_PATH.format(device_id)
316 self._root_proxy.add(root_path, data)
317
318 return self._core.get_proxy(path)
319
320 def _instance_proxy(self, device_id, class_id, instance_id, create=False):
321 """
322 Get a config proxy to a specific managed entity instance
323 :param device_id: (str) ONU Device ID
324 :param class_id: (int) Class ID
325 :param instance_id: (int) Instance ID
326 :param create: (bool) If true, create default instance (and class)
327 :return: (ConfigProxy) Instance configuration proxy
328
329 :raises DatabaseStateError: If database is not started
330 :raises KeyError: If Instance does not exist and 'create' is False
331 """
332 if not self._started:
333 raise DatabaseStateError('The Database is not currently active')
334
335 if not isinstance(device_id, basestring):
336 raise TypeError('Device ID is a string')
337
338 if not 0 <= class_id <= 0xFFFF:
339 raise ValueError('class-id is 0..0xFFFF')
340
341 if not 0 <= instance_id <= 0xFFFF:
342 raise ValueError('instance-id is 0..0xFFFF')
343
344 fmt = MibDbExternal.DEVICE_PATH + MibDbExternal.INSTANCE_PATH
345 path = fmt.format(device_id, class_id, instance_id)
346
347 try:
348 return self._core.get_proxy(path)
349
350 except KeyError:
351 if not create:
352 self.log.error('instance-proxy-does-not-exist', device_id=device_id,
353 class_id=class_id, instance_id=instance_id)
354 raise
355
356 # Create instance, first make sure class exists
357 self._class_proxy(device_id, class_id, create=True)
358
359 now = self._time_to_string(datetime.utcnow())
360 data = MibInstanceData(instance_id=instance_id, created=now, modified=now)
361 root_path = MibDbExternal.INSTANCES_PATH.format(device_id, class_id)
362 self._root_proxy.add(root_path, data)
363
364 return self._core.get_proxy(path)
365
366 def on_mib_reset(self, device_id):
367 """
368 Reset/clear the database for a specific Device
369
370 :param device_id: (str) ONU Device ID
371 :raises DatabaseStateError: If the database is not enabled
Chip Bolingbed4c6b2018-05-09 08:52:16 -0500372 :raises KeyError: If the device does not exist in the database
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500373 """
374 self.log.debug('on-mib-reset', device_id=device_id)
375
376 try:
377 device_proxy = self._device_proxy(device_id)
378 data = device_proxy.get(depth=2)
379
380 # Wipe out any existing class IDs
381 class_ids = [c.class_id for c in data.classes]
382
383 if len(class_ids):
384 for class_id in class_ids:
385 device_proxy.remove(MibDbExternal.CLASS_PATH.format(class_id))
386
387 # Reset MIB Data Sync to zero
388 now = datetime.utcnow()
389 data = MibDeviceData(device_id=device_id,
390 created=data.created,
391 last_sync_time=data.last_sync_time,
392 mib_data_sync=0,
393 version=MibDbExternal.CURRENT_VERSION)
394 # Update
395 self._root_proxy.update(MibDbExternal.DEVICE_PATH.format(device_id),
396 data)
397 self._modified = now
398 self.log.debug('mib-reset-complete', device_id=device_id)
399
400 except Exception as e:
401 self.log.exception('mib-reset-exception', device_id=device_id, e=e)
402 raise
403
404 def save_mib_data_sync(self, device_id, value):
405 """
406 Save the MIB Data Sync to the database in an easy location to access
407
408 :param device_id: (str) ONU Device ID
409 :param value: (int) Value to save
410 """
411 self.log.debug('save-mds', device_id=device_id, value=value)
412
413 try:
414 if not isinstance(value, int):
415 raise TypeError('MIB Data Sync is an integer')
416
417 if not 0 <= value <= 255:
418 raise ValueError('Invalid MIB-data-sync value {}. Must be 0..255'.
419 format(value))
420 device_proxy = self._device_proxy(device_id)
421 data = device_proxy.get(depth=0)
422
423 now = datetime.utcnow()
424 data.mib_data_sync = value
425
426 # Update
427 self._root_proxy.update(MibDbExternal.DEVICE_PATH.format(device_id),
428 data)
429 self._modified = now
430 self.log.debug('save-mds-complete', device_id=device_id)
431
432 except Exception as e:
433 self.log.exception('save-mds-exception', device_id=device_id, e=e)
434 raise
435
436 def get_mib_data_sync(self, device_id):
437 """
438 Get the MIB Data Sync value last saved to the database for a device
439
440 :param device_id: (str) ONU Device ID
441 :return: (int) The Value or None if not found
442 """
443 self.log.debug('get-mds', device_id=device_id)
444
445 try:
446 device_proxy = self._device_proxy(device_id)
447 data = device_proxy.get(depth=0)
448 return int(data.mib_data_sync)
449
450 except KeyError:
451 return None # OMCI MIB_DB entry has not yet been created
452
453 except Exception as e:
454 self.log.exception('get-mds-exception', device_id=device_id, e=e)
455 raise
456
457 def save_last_sync(self, device_id, value):
458 """
459 Save the Last Sync time to the database in an easy location to access
460
461 :param device_id: (str) ONU Device ID
462 :param value: (DateTime) Value to save
463 """
464 self.log.debug('save-last-sync', device_id=device_id, time=str(value))
465
466 try:
467 if not isinstance(value, datetime):
468 raise TypeError('Expected a datetime object, got {}'.
469 format(type(datetime)))
470
471 device_proxy = self._device_proxy(device_id)
472 data = device_proxy.get(depth=0)
473
474 now = datetime.utcnow()
475 data.last_sync_time = self._time_to_string(value)
476
477 # Update
478 self._root_proxy.update(MibDbExternal.DEVICE_PATH.format(device_id),
479 data)
480 self._modified = now
481 self.log.debug('save-mds-complete', device_id=device_id)
482
483 except Exception as e:
484 self.log.exception('save-last-sync-exception', device_id=device_id, e=e)
485 raise
486
487 def get_last_sync(self, device_id):
488 """
489 Get the Last Sync Time saved to the database for a device
490
491 :param device_id: (str) ONU Device ID
492 :return: (int) The Value or None if not found
493 """
494 self.log.debug('get-last-sync', device_id=device_id)
495
496 try:
497 device_proxy = self._device_proxy(device_id)
498 data = device_proxy.get(depth=0)
499 return self._string_to_time(data.last_sync_time)
500
501 except KeyError:
502 return None # OMCI MIB_DB entry has not yet been created
503
504 except Exception as e:
505 self.log.exception('get-last-sync-exception', e=e)
506 raise
507
508 def _add_new_class(self, device_id, class_id, instance_id, attributes):
509 """
510 Create an entry for a new class in the external database
511
512 :param device_id: (str) ONU Device ID
513 :param class_id: (int) ME Class ID
514 :param instance_id: (int) ME Entity ID
515 :param attributes: (dict) Attribute dictionary
516
517 :returns: (bool) True if the value was saved to the database. False if the
518 value was identical to the current instance
519 """
520 self.log.debug('add', device_id=device_id, class_id=class_id,
521 instance_id=instance_id, attributes=attributes)
522
523 now = self._time_to_string(datetime.utcnow())
524 attrs = [MibAttributeData(name=k,
525 value=self._attribute_to_string(device_id,
526 class_id,
527 k,
528 v)) for k, v in attributes.items()]
529 class_data = MibClassData(class_id=class_id,
530 instances=[MibInstanceData(instance_id=instance_id,
531 created=now,
532 modified=now,
533 attributes=attrs)])
534
535 self._root_proxy.add(MibDbExternal.CLASSES_PATH.format(device_id), class_data)
536 self.log.debug('set-complete', device_id=device_id, class_id=class_id,
537 entity_id=instance_id, attributes=attributes)
538 return True
539
540 def _add_new_instance(self, device_id, class_id, instance_id, attributes):
541 """
542 Create an entry for a instance of an existing class in the external database
543
544 :param device_id: (str) ONU Device ID
545 :param class_id: (int) ME Class ID
546 :param instance_id: (int) ME Entity ID
547 :param attributes: (dict) Attribute dictionary
548
549 :returns: (bool) True if the value was saved to the database. False if the
550 value was identical to the current instance
551 """
552 self.log.debug('add', device_id=device_id, class_id=class_id,
553 instance_id=instance_id, attributes=attributes)
554
555 now = self._time_to_string(datetime.utcnow())
556 attrs = [MibAttributeData(name=k,
557 value=self._attribute_to_string(device_id,
558 class_id,
559 k,
560 v)) for k, v in attributes.items()]
561 instance_data = MibInstanceData(instance_id=instance_id,
562 created=now,
563 modified=now,
564 attributes=attrs)
565
566 self._root_proxy.add(MibDbExternal.INSTANCES_PATH.format(device_id, class_id),
567 instance_data)
568
569 self.log.debug('set-complete', device_id=device_id, class_id=class_id,
570 entity_id=instance_id, attributes=attributes)
571 return True
572
573 def set(self, device_id, class_id, instance_id, attributes):
574 """
575 Set a database value. This should only be called by the MIB synchronizer
576 and its related tasks
577
578 :param device_id: (str) ONU Device ID
579 :param class_id: (int) ME Class ID
580 :param instance_id: (int) ME Entity ID
581 :param attributes: (dict) Attribute dictionary
582
583 :returns: (bool) True if the value was saved to the database. False if the
584 value was identical to the current instance
585
586 :raises KeyError: If device does not exist
587 :raises DatabaseStateError: If the database is not enabled
588 """
589 self.log.debug('set', device_id=device_id, class_id=class_id,
590 instance_id=instance_id, attributes=attributes)
591 try:
592 if not isinstance(device_id, basestring):
593 raise TypeError('Device ID should be a string')
594
595 if not 0 <= class_id <= 0xFFFF:
596 raise ValueError("Invalid Class ID: {}, should be 0..65535".format(class_id))
597
598 if not 0 <= instance_id <= 0xFFFF:
599 raise ValueError("Invalid Instance ID: {}, should be 0..65535".format(instance_id))
600
601 if not isinstance(attributes, dict):
602 raise TypeError("Attributes should be a dictionary")
603
604 if not self._started:
605 raise DatabaseStateError('The Database is not currently active')
606
607 # Determine the best strategy to add the information
608 dev_proxy = self._device_proxy(device_id)
609
610 try:
611 class_data = dev_proxy.get(MibDbExternal.CLASS_PATH.format(class_id), deep=True)
612
613 inst_data = next((inst for inst in class_data.instances
614 if inst.instance_id == instance_id), None)
615
616 if inst_data is None:
617 return self._add_new_instance(device_id, class_id, instance_id, attributes)
618
619 # Possibly adding to or updating an existing instance
620 # Get instance proxy, creating it if needed
621
622 exist_attr_indexes = dict()
623 attr_len = len(inst_data.attributes)
624
625 for index in xrange(0, attr_len):
626 exist_attr_indexes[inst_data.attributes[index].name] = index
627
628 modified = False
629 new_attributes = []
630
631 for k, v in attributes.items():
632 str_value = self._attribute_to_string(device_id, class_id, k, v)
633 new_attributes.append(MibAttributeData(name=k, value=str_value))
634
635 if k not in exist_attr_indexes or \
636 inst_data.attributes[exist_attr_indexes[k]].value != str_value:
637 modified = True
638
639 if modified:
640 now = datetime.utcnow()
641 new_data = MibInstanceData(instance_id=instance_id,
642 created=inst_data.created,
643 modified=self._time_to_string(now),
644 attributes=new_attributes)
645 dev_proxy.remove(MibDbExternal.INSTANCE_PATH.format(class_id, instance_id))
646 self._root_proxy.add(MibDbExternal.INSTANCES_PATH.format(device_id,
647 class_id), new_data)
648
649 self.log.debug('set-complete', device_id=device_id, class_id=class_id,
650 entity_id=instance_id, attributes=attributes, modified=modified)
651
652 return modified
653
654 except KeyError:
655 # Here if the class-id does not yet exist in the database
656 return self._add_new_class(device_id, class_id, instance_id,
657 attributes)
658 except Exception as e:
659 self.log.exception('set-exception', device_id=device_id, class_id=class_id,
660 instance_id=instance_id, attributes=attributes, e=e)
661 raise
662
663 def delete(self, device_id, class_id, entity_id):
664 """
665 Delete an entity from the database if it exists. If all instances
666 of a class are deleted, the class is deleted as well.
667
668 :param device_id: (str) ONU Device ID
669 :param class_id: (int) ME Class ID
670 :param entity_id: (int) ME Entity ID
671
672 :returns: (bool) True if the instance was found and deleted. False
673 if it did not exist.
674
675 :raises KeyError: If device does not exist
676 :raises DatabaseStateError: If the database is not enabled
677 """
678 self.log.debug('delete', device_id=device_id, class_id=class_id,
679 entity_id=entity_id)
680
681 if not self._started:
682 raise DatabaseStateError('The Database is not currently active')
683
684 if not isinstance(device_id, basestring):
685 raise TypeError('Device ID should be an string')
686
687 if not 0 <= class_id <= 0xFFFF:
688 raise ValueError('class-id is 0..0xFFFF')
689
690 if not 0 <= entity_id <= 0xFFFF:
691 raise ValueError('instance-id is 0..0xFFFF')
692
693 try:
694 # Remove instance
695 self._instance_proxy(device_id, class_id, entity_id).remove('/')
696 now = datetime.utcnow()
697
698 # If resulting class has no instance, remove it as well
699 class_proxy = self._class_proxy(device_id, class_id)
700 class_data = class_proxy.get('/', depth=1)
701
702 if len(class_data.instances) == 0:
703 class_proxy.remove('/')
704
705 self._modified = now
706 return True
707
708 except KeyError:
709 return False # Not found
710
711 except Exception as e:
712 self.log.exception('get-last-sync-exception', device_id=device_id, e=e)
713 raise
714
715 def query(self, device_id, class_id=None, instance_id=None, attributes=None):
716 """
717 Get database information.
718
719 This method can be used to request information from the database to the detailed
720 level requested
721
722 :param device_id: (str) ONU Device ID
723 :param class_id: (int) Managed Entity class ID
724 :param instance_id: (int) Managed Entity instance
725 :param attributes: (list/set or str) Managed Entity instance's attributes
726
727 :return: (dict) The value(s) requested. If class/inst/attribute is
728 not found, an empty dictionary is returned
729 :raises KeyError: If the requested device does not exist
730 :raises DatabaseStateError: If the database is not enabled
731 """
732 self.log.debug('query', device_id=device_id, class_id=class_id,
733 instance_id=instance_id, attributes=attributes)
734 try:
735 if class_id is None:
736 # Get full device info
737 dev_data = self._device_proxy(device_id).get('/', depth=-1)
738 data = self._device_to_dict(dev_data)
739
740 elif instance_id is None:
741 # Get all instances of the class
742 try:
743 cls_data = self._class_proxy(device_id, class_id).get('/', depth=-1)
744 data = self._class_to_dict(device_id, cls_data)
745
746 except KeyError:
747 data = dict()
748
749 else:
750 # Get all attributes of a specific ME
751 try:
752 inst_data = self._instance_proxy(device_id, class_id, instance_id).\
753 get('/', depth=-1)
754
755 if attributes is None:
756 # All Attributes
757 data = self._instance_to_dict(device_id, class_id, inst_data)
758
759 else:
760 # Specific attribute(s)
Chip Bolingc7d3e2d2018-04-06 10:16:29 -0500761 if isinstance(attributes, basestring):
762 attributes = {attributes}
763
764 data = {
765 attr.name: self._string_to_attribute(device_id,
766 class_id,
767 attr.name,
768 attr.value)
769 for attr in inst_data.attributes if attr.name in attributes}
770
771 except KeyError:
772 data = dict()
773
774 return data
775
776 except KeyError:
777 self.log.warn('query-no-device', device_id=device_id)
778 raise
779
780 except Exception as e:
781 self.log.exception('get-last-sync-exception', device_id=device_id, e=e)
782 raise
783
784 def _instance_to_dict(self, device_id, class_id, instance):
785 if not isinstance(instance, MibInstanceData):
786 raise TypeError('{} is not of type MibInstanceData'.format(type(instance)))
787
788 data = {
789 INSTANCE_ID_KEY: instance.instance_id,
790 CREATED_KEY: self._string_to_time(instance.created),
791 MODIFIED_KEY: self._string_to_time(instance.modified),
792 ATTRIBUTES_KEY: dict()
793 }
794 for attribute in instance.attributes:
795 data[ATTRIBUTES_KEY][attribute.name] = self._string_to_attribute(device_id,
796 class_id,
797 attribute.name,
798 attribute.value)
799 return data
800
801 def _class_to_dict(self, device_id, val):
802 if not isinstance(val, MibClassData):
803 raise TypeError('{} is not of type MibClassData'.format(type(val)))
804
805 data = {
806 CLASS_ID_KEY: val.class_id,
807 }
808 for instance in val.instances:
809 data[instance.instance_id] = self._instance_to_dict(device_id,
810 val.class_id,
811 instance)
812 return data
813
814 def _device_to_dict(self, val):
815 if not isinstance(val, MibDeviceData):
816 raise TypeError('{} is not of type MibDeviceData'.format(type(val)))
817
818 data = {
819 DEVICE_ID_KEY: val.device_id,
820 CREATED_KEY: self._string_to_time(val.created),
821 LAST_SYNC_KEY: self._string_to_time(val.last_sync_time),
822 MDS_KEY: val.mib_data_sync,
823 VERSION_KEY: val.version
824 }
825 for class_data in val.classes:
826 data[class_data.class_id] = self._class_to_dict(val.device_id,
827 class_data)
828 return data