def _create_new_instance(self, device_id, class_id, instance_id, attributes): """ Create an entry for a instance of an existing class in the external database :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param instance_id: (int) ME Entity ID :param attributes: (dict) Attribute dictionary :returns: (AlarmInstanceData) The new instance object """ self.log.debug('create-new-instance', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes) now = self._time_to_string(datetime.utcnow()) attrs = [ AlarmAttributeData(name=k, value=self._attribute_to_string(v)) for k, v in attributes.items() ] instance_data = AlarmInstanceData(instance_id=instance_id, created=now, modified=now, attributes=attrs) self.log.debug('create-new-instance-complete', device_id=device_id, class_id=class_id, entity_id=instance_id, attributes=attributes) return instance_data
def _instance_proxy(self, device_id, class_id, instance_id, create=False): """ Get a config proxy to a specific managed entity instance :param device_id: (str) ONU Device ID :param class_id: (int) Class ID :param instance_id: (int) Instance ID :param create: (bool) If true, create default instance (and class) :return: (ConfigProxy) Instance configuration proxy :raises DatabaseStateError: If database is not started :raises KeyError: If Instance does not exist and 'create' is False """ if not self._started: raise DatabaseStateError('The Database is not currently active') if not isinstance(device_id, basestring): raise TypeError('Device ID is a string') if not 0 <= class_id <= 0xFFFF: raise ValueError('class-id is 0..0xFFFF') if not 0 <= instance_id <= 0xFFFF: raise ValueError('instance-id is 0..0xFFFF') fmt = AlarmDbExternal.DEVICE_PATH + AlarmDbExternal.INSTANCE_PATH path = fmt.format(device_id, class_id, instance_id) try: return self._core.get_proxy(path) except KeyError: if not create: self.log.error('instance-proxy-does-not-exist', device_id=device_id, class_id=class_id, instance_id=instance_id) raise # Create instance, first make sure class exists self._class_proxy(device_id, class_id, create=True) now = self._time_to_string(datetime.utcnow()) data = AlarmInstanceData(instance_id=instance_id, created=now, modified=now) root_path = AlarmDbExternal.INSTANCES_PATH.format(device_id, class_id) self._root_proxy.add(root_path, data) return self._core.get_proxy(path)
def _add_new_class(self, device_id, class_id, instance_id, attributes): """ Create an entry for a new class in the external database :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param instance_id: (int) ME Entity ID :param attributes: (dict) Attribute dictionary :returns: (bool) True if the value was saved to the database. False if the value was identical to the current instance """ self.log.debug('add', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes) now = self._time_to_string(datetime.utcnow()) attrs = [ AlarmAttributeData(name=k, value=self._attribute_to_string(v)) for k, v in attributes.items() ] class_data = AlarmClassData(class_id=class_id, instances=[ AlarmInstanceData( instance_id=instance_id, created=now, modified=now, attributes=attrs) ]) self._root_proxy.add(AlarmDbExternal.CLASSES_PATH.format(device_id), class_data) self.log.debug('set-complete', device_id=device_id, class_id=class_id, entity_id=instance_id, attributes=attributes) return True
def query(self, device_id, class_id=None, instance_id=None, attributes=None): """ Get database information. This method can be used to request information from the database to the detailed level requested :param device_id: (str) ONU Device ID :param class_id: (int) Managed Entity class ID :param instance_id: (int) Managed Entity instance :param attributes: (list/set or str) Managed Entity instance's attributes :return: (dict) The value(s) requested. If class/inst/attribute is not found, an empty dictionary is returned :raises KeyError: If the requested device does not exist :raises DatabaseStateError: If the database is not enabled """ self.log.debug('query', device_id=device_id, class_id=class_id, entity_instance_id=instance_id, attributes=attributes) data = dict() try: if class_id is None: # Get full device info self.log.warn('get-all-instances-by-device-not-supported') try: dev_data = AlarmDeviceData() device_path = self._get_device_path(device_id) dev_data.ParseFromString(self._kv_store[device_path]) data = self._device_to_dict(dev_data) except KeyError: data = dict() elif instance_id is None: # Get all instances of the class self.log.warn('get-all-instances-by-class-not-supported') try: cls_data = AlarmClassData() class_path = self._get_class_path(device_id, class_id) cls_data.ParseFromString(self._kv_store[class_path]) data = self._class_to_dict(cls_data) except KeyError: data = dict() else: # Get all attributes of a specific ME try: inst_data = AlarmInstanceData() inst_path = self._get_instance_path( device_id, class_id, instance_id) inst_data.ParseFromString(self._kv_store[inst_path]) if attributes is None: # All Attributes data = self._instance_to_dict(inst_data) self.log.debug('query-result-all', data=data) else: # Specific attribute(s) if isinstance(attributes, basestring): attributes = {attributes} data = { attr.name: self._string_to_attribute(attr.value) for attr in inst_data.attributes if attr.name in attributes } except KeyError: self.log.debug('no-instance-data-keyError') data = dict() except Exception as e: self.log.exception('get-last-sync-exception', device_id=device_id, e=e) raise self.log.debug('query-result', data=data) return data except KeyError: self.log.warn('query-no-device', device_id=device_id) raise except Exception as e: self.log.exception('get-last-sync-exception', device_id=device_id, e=e) raise
def set(self, device_id, class_id, instance_id, attributes): """ Set a database value. This should only be called by the Alarm synchronizer and its related tasks :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param instance_id: (int) ME Entity ID :param attributes: (dict) Attribute dictionary :returns: (bool) True if the value was saved to the database. False if the value was identical to the current instance :raises KeyError: If device does not exist :raises DatabaseStateError: If the database is not enabled """ self.log.debug('set', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes) try: if not isinstance(device_id, basestring): raise TypeError('Device ID should be a string') if not 0 <= class_id <= 0xFFFF: raise ValueError( "Invalid Class ID: {}, should be 0..65535".format( class_id)) if not 0 <= instance_id <= 0xFFFF: raise ValueError( "Invalid Instance ID: {}, should be 0..65535".format( instance_id)) if not isinstance(attributes, dict): raise TypeError("Attributes should be a dictionary") if not self._started: raise DatabaseStateError( 'The Database is not currently active') try: class_data = AlarmClassData() class_path = AlarmDbExternal.CLASS_PATH.format( device_id, class_id) class_data.ParseFromString(self._kv_store[class_path]) modified = False new_data = None try: inst_data = AlarmInstanceData() inst_path = AlarmDbExternal.INSTANCE_PATH.format( device_id, class_id, instance_id) inst_data.ParseFromString(self._kv_store[inst_path]) exist_attr_indexes = dict() attr_len = len(inst_data.attributes) for index in xrange(0, attr_len): exist_attr_indexes[ inst_data.attributes[index].name] = index str_value = '' new_attributes = [] for k, v in attributes.items(): try: str_value = self._attribute_to_string(v) new_attributes.append( AlarmAttributeData(name=k, value=str_value)) except Exception as e: self.log.exception('save-error', e=e, class_id=class_id, attr=k, value_type=type(v)) if k not in exist_attr_indexes or \ inst_data.attributes[exist_attr_indexes[k]].value != str_value: modified = True self.log.debug('different-attributes', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=inst_data.attributes, new_attributes=new_attributes) if modified: now = datetime.utcnow() new_data = AlarmInstanceData( instance_id=instance_id, created=inst_data.created, modified=self._time_to_string(now), attributes=new_attributes) except KeyError: # Here if the instance_id does not yet exist in the database new_data = self._create_new_instance( device_id, class_id, instance_id, attributes) modified = True if modified: del self._kv_store[inst_path] self._kv_store[inst_path] = new_data.SerializeToString() return modified except KeyError: # Here if the class-id does not yet exist in the database return self._add_new_class_and_instance( device_id, class_id, instance_id, attributes) except Exception as e: self.log.exception('set-exception', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes, e=e) raise
def set(self, device_id, class_id, instance_id, attributes): """ Set a database value. This should only be called by the Alarm synchronizer and its related tasks :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param instance_id: (int) ME Entity ID :param attributes: (dict) Attribute dictionary :returns: (bool) True if the value was saved to the database. False if the value was identical to the current instance :raises KeyError: If device does not exist :raises DatabaseStateError: If the database is not enabled """ self.log.debug('set', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes) try: if not isinstance(device_id, basestring): raise TypeError('Device ID should be a string') if not 0 <= class_id <= 0xFFFF: raise ValueError( "Invalid Class ID: {}, should be 0..65535".format( class_id)) if not 0 <= instance_id <= 0xFFFF: raise ValueError( "Invalid Instance ID: {}, should be 0..65535".format( instance_id)) if not isinstance(attributes, dict): raise TypeError("Attributes should be a dictionary") if not self._started: raise DatabaseStateError( 'The Database is not currently active') # Determine the best strategy to add the information dev_proxy = self._device_proxy(device_id) try: class_data = dev_proxy.get( AlarmDbExternal.CLASS_PATH.format(class_id), deep=True) inst_data = next((inst for inst in class_data.instances if inst.instance_id == instance_id), None) if inst_data is None: return self._add_new_instance(device_id, class_id, instance_id, attributes) # Possibly adding to or updating an existing instance # Get instance proxy, creating it if needed exist_attr_indexes = dict() attr_len = len(inst_data.attributes) for index in xrange(0, attr_len): exist_attr_indexes[ inst_data.attributes[index].name] = index modified = False str_value = '' new_attributes = [] for k, v in attributes.items(): try: str_value = self._attribute_to_string(v) new_attributes.append( AlarmAttributeData(name=k, value=str_value)) except Exception as e: self.log.exception('save-error', e=e, class_id=class_id, attr=k, value_type=type(v)) if k not in exist_attr_indexes or \ inst_data.attributes[exist_attr_indexes[k]].value != str_value: modified = True if modified: now = datetime.utcnow() new_data = AlarmInstanceData( instance_id=instance_id, created=inst_data.created, modified=self._time_to_string(now), attributes=new_attributes) dev_proxy.remove( AlarmDbExternal.INSTANCE_PATH.format( class_id, instance_id)) self._root_proxy.add( AlarmDbExternal.INSTANCES_PATH.format( device_id, class_id), new_data) self.log.debug('set-complete', device_id=device_id, class_id=class_id, entity_id=instance_id, attributes=attributes, modified=modified) return modified except KeyError: # Here if the class-id does not yet exist in the database return self._add_new_class(device_id, class_id, instance_id, attributes) except Exception as e: self.log.exception('set-exception', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes, e=e) raise