def _update_existing_instance(self, device_id, class_id, entity_id, attributes, existing_instance): """ Update the attributes of an existing instance of a class and returning the modified instance proto object :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param entity_id: (int) ME Entity ID :param existing_instance: (MibInstanceData) current instance object :param attributes: (dict) Attribute dictionary :returns: (MibInstanceData) The updated instance object or None if nothing changed """ new_attributes = [] exist_attr_indexes = dict() attr_len = len(existing_instance.attributes) modified = False for index in range(0, attr_len): name = existing_instance.attributes[index].name value = existing_instance.attributes[index].value exist_attr_indexes[name] = index new_attributes.append(MibAttributeData(name=name, value=value)) for k, v in attributes.items(): try: old_value = None if k not in exist_attr_indexes \ else new_attributes[exist_attr_indexes[k]].value str_value = self._attribute_to_string(device_id, class_id, k, v, old_value) if k not in exist_attr_indexes: new_attributes.append( MibAttributeData(name=k, value=str_value)) modified = True elif new_attributes[exist_attr_indexes[k]].value != str_value: new_attributes[exist_attr_indexes[k]].value = str_value modified = True except Exception as e: self.log.exception('save-error', e=e, class_id=class_id, attr=k, value_type=type(v)) if modified: now = self._time_to_string(datetime.utcnow()) new_instance_data = MibInstanceData( instance_id=entity_id, created=existing_instance.created, modified=now, attributes=new_attributes) return new_instance_data else: return None
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: (MibInstanceDate) 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 = [] for k, v in attributes.items(): if k == 'serial_number': vendor_id = str(v[0:4]) vendor_specific_ext = v[4:] vendor_specific = codecs.encode(vendor_specific_ext, 'hex') str_value = vendor_id + vendor_specific attrs.append(MibAttributeData(name=k, value=str_value)) else: str_value = self._attribute_to_string(device_id, class_id, k, v) attrs.append(MibAttributeData(name=k, value=str_value)) instance_data = MibInstanceData(instance_id=instance_id, created=now, modified=now, attributes=attrs) #update class with instance #class_path = self._get_class_path(device_id, class_id) #class_data = MibClassData() #class_data.ParseFromString(self._kv_store[class_path]) #class_data.instances.extend([instance_data]) #self._kv_store[class_path] = class_data.SerializeToString() 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 = MibDbExternal.DEVICE_PATH + MibDbExternal.INSTANCE_PATH path = fmt.format(device_id, class_id, instance_id) try: return self._core.get_proxy(path) except KeyError: if not create: # This can occur right after a MIB Reset if the ONU publishes AVCs right away # and during the MIB audit resync for ONU created MEs in response to an OLT # created ME. Fail since for these test cases they occur during a verification # 'query' and not the ME creation during resync. Calling code should handle # they exception if it is expected to occur on occasion. self.log.info('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 = MibInstanceData(instance_id=instance_id, created=now, modified=now) root_path = MibDbExternal.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-new-class', device_id=device_id, class_id=class_id, instance_id=instance_id, attributes=attributes) now = self._time_to_string(datetime.utcnow()) attrs = [] for k, v in attributes.items(): if k == 'serial_number': vendor_id = str(v[0:4]) vendor_specific_ext = v[4:] vendor_specific = codecs.encode(vendor_specific_ext, 'hex') str_value = vendor_id + vendor_specific attrs.append(MibAttributeData(name=k, value=str_value)) else: str_value = self._attribute_to_string(device_id, class_id, k, v) attrs.append(MibAttributeData(name=k, value=str_value)) class_data = MibClassData(class_id=class_id, instances=[ MibInstanceData(instance_id=instance_id, created=now, modified=now, attributes=attrs) ]) classpath = MibDbExternal.CLASS_PATH.format(device_id, class_id) self._kv_store[classpath] = class_data.SerializeToString() self.log.debug('set-complete', device_id=device_id, class_id=class_id, entity_id=instance_id, attributes=attributes) return True
def _get_instance(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 """ path = self._get_instance_path(device_id, class_id, instance_id) try: return self._kv_store[path] except KeyError: if not create: # This can occur right after a MIB Reset if the ONU publishes AVCs right away # and during the MIB audit resync for ONU created MEs in response to an OLT # created ME. Fail since for these test cases they occur during a verification # 'query' and not the ME creation during resync. Calling code should handle # they exception if it is expected to occur on occasion. self.log.info('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._get_class(device_id, class_id, create=True) now = self._time_to_string(datetime.utcnow()) data = MibInstanceData(instance_id=instance_id, created=now, modified=now) root_path = MibDbExternal.INSTANCE_PATH.format(device_id, class_id) self._kv_store[root_path] = data return data
def _add_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: (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 = [] for k, v in attributes.items(): if k == 'serial_number': vendor_id = str(v[0:4]) vendor_specific = v[4:] vendor_specific = str(vendor_specific.encode('hex')) str_value = vendor_id+vendor_specific attrs.append(MibAttributeData(name=k, value=str_value)) else: str_value = self._attribute_to_string(device_id, class_id, k, v) attrs.append(MibAttributeData(name=k, value=str_value)) instance_data = MibInstanceData(instance_id=instance_id, created=now, modified=now, attributes=attrs) self._root_proxy.add(MibDbExternal.INSTANCES_PATH.format(device_id, class_id), instance_data) self.log.debug('set-complete', device_id=device_id, class_id=class_id, entity_id=instance_id, attributes=attributes) return True
def _create_new_instance(self, device_id, class_id, entity_id, attributes): """ Create an entry for a instance of a class and returning instance proto object :param device_id: (str) ONU Device ID :param class_id: (int) ME Class ID :param entity_id: (int) ME Entity ID :param attributes: (dict) Attribute dictionary :returns: (MibInstanceData) The new populated instance object """ attrs = [] for k, v in attributes.items(): str_value = self._attribute_to_string(device_id, class_id, k, v) attrs.append(MibAttributeData(name=k, value=str_value)) now = self._time_to_string(datetime.utcnow()) instance_data = MibInstanceData(instance_id=entity_id, created=now, modified=now, attributes=attrs) return instance_data
def set(self, device_id, class_id, instance_id, attributes): """ Set a database value. This should only be called by the MIB 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) operation = 'set' start_time = None try: class_data = dev_proxy.get(MibDbExternal.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: operation = 'create' start_time = datetime.utcnow() 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 modified = False new_attributes = [] exist_attr_indexes = dict() attr_len = len(inst_data.attributes) for index in xrange(0, attr_len): name = inst_data.attributes[index].name value = inst_data.attributes[index].value exist_attr_indexes[name] = index new_attributes.append(MibAttributeData(name=name, value=value)) for k, v in attributes.items(): try: old_value = None if k not in exist_attr_indexes \ else new_attributes[exist_attr_indexes[k]].value str_value = self._attribute_to_string(device_id, class_id, k, v, old_value) if k not in exist_attr_indexes: new_attributes.append(MibAttributeData(name=k, value=str_value)) modified = True elif new_attributes[exist_attr_indexes[k]].value != str_value: new_attributes[exist_attr_indexes[k]].value = str_value modified = True except Exception as e: self.log.exception('save-error', e=e, class_id=class_id, attr=k, value_type=type(v)) if modified: now = datetime.utcnow() start_time = now new_data = MibInstanceData(instance_id=instance_id, created=inst_data.created, modified=self._time_to_string(now), attributes=new_attributes) dev_proxy.remove(MibDbExternal.INSTANCE_PATH.format(class_id, instance_id)) self._root_proxy.add(MibDbExternal.INSTANCES_PATH.format(device_id, class_id), new_data) return modified except KeyError: # Here if the class-id does not yet exist in the database self.log.debug("adding-key-not-found", class_id=class_id) return self._add_new_class(device_id, class_id, instance_id, attributes) finally: if start_time is not None: diff = datetime.utcnow() - start_time # NOTE: Change to 'debug' when checked in, manually change to 'info' # for development testing. self.log.debug('db-{}-time'.format(operation), milliseconds=diff.microseconds/1000) self._statistics[operation].increment(diff.microseconds/1000) 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