Пример #1
0
def _execute_register(device: dict, created: str, log: list):
    """
    Tries to POST the device and updates the `device` dict with the resource from the database; if the device could
    not be uploaded the `device` param will contain the database version of the device, not the inputting one. This is
    because the majority of the information of a device is immutable (in concrete the fields used to compute
    the ETAG).

    :param device: Inputting device. It is replaced (keeping the reference) with the db version.
    :param created: Set the _created value to be the same for the device as for the register
    :param log: A log where to append the resulting device if execute_register has been successful
    :raise InnerRequestError: any internal error in the POST that is not about the device already existing.
    """
    device['hid'] = 'dummy'
    new = True
    try:
        if created:
            device['created'] = created
        db_device = execute_post_internal(Naming.resource(device['@type']), device)
    except InnerRequestError as e:
        new = False
        try:
            db_device = _get_existing_device(e)
            # We add a benchmark todo move to another place?
            device['_id'] = db_device['_id']
            ComponentDomain.benchmark(device)
        except DeviceNotFound:
            raise e
    else:
        log.append(db_device)
    device.clear()
    device.update(db_device)
    device['new'] = new  # Note that the device is 'cleared' before
    return db_device
Пример #2
0
    def update_devices(original: set, updated: set, replaced_place_id: str
                       or None):
        """
        Given the original set of devices of a place, and the updated version, it updates the database to achieve
        the updated set of devices.

        :param original:
        :param updated:
        :param replaced_place_id: The identifier of the replaced place. If updated is empty, this value is not used.
        :return:
        """
        devices_to_remove_id = original - updated
        children_to_remove_id = ComponentDomain.get_components_in_set(
            list(devices_to_remove_id))
        devices_to_remove_id |= children_to_remove_id

        devices_to_add_id = updated - original
        children_to_add_id = ComponentDomain.get_components_in_set(
            list(devices_to_add_id))
        devices_to_add_id |= children_to_add_id

        for device_id in devices_to_remove_id:
            PlaceDomain.device_unset_place(device_id)
        for device_id in devices_to_add_id:
            PlaceDomain.device_set_place(device_id, replaced_place_id)
Пример #3
0
def inherit_place(place_id: ObjectId, device_id: str or ObjectId,
                  components: list):
    """Copies the place from the parent device to the new components and materializes them in the place"""
    ComponentDomain.update_raw(components, {'$set': {'place': place_id}})
    PlaceDomain.update_one_raw(place_id,
                               {'$addToSet': {
                                   'components': components
                               }})
Пример #4
0
def _execute_register(device: dict, created: str, log: list):
    """
    Tries to POST the device and updates the `device` dict with the resource from the database; if the device could
    not be uploaded the `device` param will contain the database version of the device, not the inputting one. This is
    because the majority of the information of a device is immutable (in concrete the fields used to compute
    the ETAG).

    :param device: Inputting device. It is replaced (keeping the reference) with the db version.
    :param created: Set the _created value to be the same for the device as for the register
    :param log: A log where to append the resulting device if execute_register has been successful
    :raise InnerRequestError: any internal error in the POST that is not about the device already existing.
    """
    new = True
    try:
        if created:
            device['created'] = created
        db_device = execute_post_internal(Naming.resource(device['@type']),
                                          device)
    except InnerRequestError as e:
        new = False
        try:
            db_device = _get_existing_device(e)
            # We add a benchmark todo move to another place?
            device['_id'] = db_device['_id']
            ComponentDomain.benchmark(device)
            external_synthetic_id_fields = pick(
                device, *DeviceDomain.external_synthetic_ids)
            # If the db_device was a placeholder
            # We want to override it with the new device
            if db_device.get('placeholder', False):
                # Eve do not generate defaults from sub-resources
                # And we really need the placeholder default set, specially when
                # discovering a device
                device['placeholder'] = False
                # We create hid when we validate (wrong thing) so we need to manually set it here as we won't
                # validate in this db operation
                device['hid'] = DeviceDomain.hid(device['manufacturer'],
                                                 device['serialNumber'],
                                                 device['model'])
                DeviceDomain.update_one_raw(db_device['_id'], {'$set': device})
            elif not is_empty(external_synthetic_id_fields):
                # External Synthetic identifiers are not intrinsically inherent
                # of devices, and thus can be added later in other Snapshots
                # Note that the device / post and _get_existing_device() have already validated those ids
                DeviceDomain.update_one_raw(
                    db_device['_id'], {'$set': external_synthetic_id_fields})
        except DeviceNotFound:
            raise e
    else:
        log.append(db_device)
    device.clear()
    device.update(db_device)
    device['new'] = new  # Note that the device is 'cleared' before
    return db_device
Пример #5
0
    def _remove_nonexistent_components(self):
        """
        This function needs to be executed at the end, after all components are in the DB and have _id

        In this stage, we have already added the components to the parent device. In the materialized components field
        of device we still have the old components. We need to remove those that are not present in this new snapshot.
        """
        if 'components' in self.device:
            full_old_components = [ComponentDomain.get_one(component) for component in self.device['components']]
            for component_to_remove in ComponentDomain.difference(full_old_components, self.components):
                self.events.append_remove(component_to_remove, self.device)
Пример #6
0
    def _remove_nonexistent_components(self):
        """
        This function needs to be executed at the end, after all components are in the DB and have _id

        In this stage, we have already added the components to the parent device. In the materialized components field
        of device we still have the old components. We need to remove those that are not present in this new snapshot.
        """
        if 'components' in self.device:
            full_old_components = [
                ComponentDomain.get_one(component)
                for component in self.device['components']
            ]
            for component_to_remove in ComponentDomain.difference(
                    full_old_components, self.components):
                self.events.append_remove(component_to_remove, self.device)
Пример #7
0
    def _validate_type_hid(self, field, value):
        """
        General validation for inserting devices (the name of the function is forced by Cerberus, not a good one).

        - Tries to create hid and validates it.
        - If hid cannot be created:
            - If it has a parent, ensures that the device is unique.
            - If it has not a parent, validates that the device has an user provided _id.
        """
        from ereuse_devicehub.resources.device.component.domain import ComponentDomain
        from ereuse_devicehub.resources.device.exceptions import DeviceNotFound
        try:
            self.document['hid'] = Naming.url_word(self.document['manufacturer']) + \
                                   '-' + Naming.url_word(self.document['serialNumber']) + \
                                   '-' + Naming.url_word(self.document['model'])
        except KeyError:
            del self.document['hid']
            self.document['isUidSecured'] = False
            if '_id' not in self.document:  # We do not validate here the unique constraint of _id
                if 'parent' in self.document:
                    with suppress(KeyError, DeviceNotFound):
                        component = ComponentDomain.get_similar_component(self.document, self.document['parent'])
                        self._error('model', json_util.dumps({'NotUnique': component}))
                else:
                    # If device has no parent and no hid, user needs to: or provide _id or forcing creating it
                    if 'forceCreation' not in self.document or not self.document['forceCreation']:
                        self._error('_id', json_util.dumps({'NeedsId': self.document}))
                        # else: user forces us to create the device, it will be assigned an _id
                        # else: user provided _id. We accept this, however is unsecured.
        else:
            self._validate_regex(HID_REGEX, field, self.document['hid'])
            self._validate_unique(True, field, self.document['hid'])
Пример #8
0
def materialize_parent(resource_name: str, events: list):
    """
    Materializes the field 'parent' of events that only affect components (such as TestHardDrive or EraseBasic)
    :param resource_name:
    :param events:
    :return:
    """
    if resource_name in Event.resource_types:
        for event in events:
            sub_schema = current_app.config["DOMAIN"][resource_name]["schema"]
            if "parent" in sub_schema:
                event["parent"] = ComponentDomain.get_parent(event["device"])["_id"]
Пример #9
0
def materialize_parent(resource_name: str, events: list):
    """
    Materializes the field 'parent' of events that only affect components (such as TestHardDrive or EraseBasic)
    :param resource_name:
    :param events:
    :return:
    """
    if resource_name in Event.resource_types:
        for event in events:
            sub_schema = current_app.config['DOMAIN'][resource_name]['schema']
            if 'parent' in sub_schema:
                event['parent'] = ComponentDomain.get_parent(event['device'])['_id']
Пример #10
0
def materialize_components(resource_name: str, events: list):
    """
    Materializes the field 'components' of selected events (not all of them) with the union of all the affected
    components, when the event is performed to computers
    :param resource_name:
    :param events:
    :return:
    """
    if resource_name in Event.resource_types:
        for event in events:
            sub_schema = current_app.config["DOMAIN"][resource_name]["schema"]
            if "components" in sub_schema and sub_schema["components"].get("readonly", False):
                event["components"] = list(ComponentDomain.get_components_in_set(event["devices"]))
Пример #11
0
    def update_devices(original: set, updated: set, replaced_place_id: str or None):
        """
        Given the original set of devices of a place, and the updated version, it updates the database to achieve
        the updated set of devices.

        :param original:
        :param updated:
        :param replaced_place_id: The identifier of the replaced place. If updated is empty, this value is not used.
        :return:
        """
        devices_to_remove_id = original - updated
        children_to_remove_id = ComponentDomain.get_components_in_set(list(devices_to_remove_id))
        devices_to_remove_id |= children_to_remove_id

        devices_to_add_id = updated - original
        children_to_add_id = ComponentDomain.get_components_in_set(list(devices_to_add_id))
        devices_to_add_id |= children_to_add_id

        for device_id in devices_to_remove_id:
            PlaceDomain.device_unset_place(device_id)
        for device_id in devices_to_add_id:
            PlaceDomain.device_set_place(device_id, replaced_place_id)
Пример #12
0
def materialize_components(resource_name: str, events: list):
    """
    Materializes the field 'components' of selected events (not all of them) with the union of all the affected
    components, when the event is performed to computers
    :param resource_name:
    :param events:
    :return:
    """
    if resource_name in Event.resource_types:
        for event in events:
            sub_schema = current_app.config['DOMAIN'][resource_name]['schema']
            if 'components' in sub_schema and sub_schema['components'].get('readonly', False):
                event['components'] = list(ComponentDomain.get_components_in_set(event['devices']))
Пример #13
0
    def get_add_remove(self, device: dict, new_parent: dict):
        """
        Get the changes (events) that will need to be triggered for the given device.
        Changes will we saved in the same device in the reserved key '_change'.
        The function doesn't execute any event per se or validate that the user can do it.

        :param device: The device must have an hid
        :param new_parent:
        """
        if not device['new']:
            try:
                old_parent = ComponentDomain.get_parent(device['_id'])
            except DeviceNotFound:  # The component exists but had no parent device, until now
                self.events.append_add(device, new_parent)
            else:
                if not DeviceDomain.seem_equal(old_parent, new_parent):
                    self.events.append_remove(device, old_parent)
                    self.events.append_add(device, new_parent)
Пример #14
0
    def get_add_remove(self, device: dict, new_parent: dict):
        """
        Get the changes (events) that will need to be triggered for the given device.
        Changes will we saved in the same device in the reserved key '_change'.
        The function doesn't execute any event per se or validate that the user can do it.

        :param device: The device must have an hid
        :param new_parent:
        """
        if not device['new']:
            try:
                old_parent = ComponentDomain.get_parent(device['_id'])
            except DeviceNotFound:  # The component exists but had no parent device, until now
                self.events.append_add(device, new_parent)
            else:
                if not DeviceDomain.seem_equal(old_parent, new_parent):
                    self.events.append_remove(device, old_parent)
                    self.events.append_add(device, new_parent)
Пример #15
0
    def _validate_type_hid(self, field, _):
        """
        General validation for inserting devices (the name of the function is forced by Cerberus, not a good one).

        - Tries to create hid and validates it.
        - If hid cannot be created:
            - If it has a parent, ensures that the device is unique.
            - If it has not a parent, validates that the device has an user provided _id.
        """
        from ereuse_devicehub.resources.device.component.domain import ComponentDomain
        from ereuse_devicehub.resources.device.exceptions import DeviceNotFound
        try:
            from ereuse_devicehub.resources.device.domain import DeviceDomain
            # todo this should not be done in the validation. Prove of this is that it needs to be done in
            # register/hooks again for placeholders
            self.document['hid'] = DeviceDomain.hid(
                self.document['manufacturer'], self.document['serialNumber'],
                self.document['model'])
        except KeyError:
            self.document['isUidSecured'] = False
            if '_id' not in self.document:  # We do not validate here the unique constraint of _id
                if 'parent' in self.document:
                    with suppress(KeyError, DeviceNotFound):
                        component = ComponentDomain.get_similar_component(
                            self.document, self.document['parent'])
                        self._error('model',
                                    json_util.dumps({'NotUnique': component}))
                else:
                    # If device has no parent and no hid, user needs to: or provide _id or forcing creating it
                    if 'forceCreation' not in self.document or not self.document[
                            'forceCreation']:
                        self._error(
                            '_id', json_util.dumps({'NeedsId': self.document}))
                        # else: user forces us to create the device, it will be assigned an _id
                        # else: user provided _id. We accept this, however is unsecured.
        else:
            from ereuse_devicehub.resources.device.settings import HID_REGEX
            self._validate_regex(HID_REGEX, field, self.document['hid'])
            self._validate_unique(True, field, self.document['hid'])
Пример #16
0
def update_materialized_computer(device_id: str, components_id: list, add: bool = True):
    update_query = {}
    inc = defaultdict(int)
    total_types = {RamModule.type_name, HardDrive.type_name}
    for component in ComponentDomain.get({'_id': {'$in': components_id}}):
        _type = component['@type']
        if _type in total_types:
            inc[_type] += component.get('size', 0)
        elif _type == Processor.type_name and 'model' in component:
            update_query['$set' if add else '$unset'] = {'processorModel': component['model']}

    sign = 1 if add else -1  # We will add or subtract values depending if adding or removing components
    update_query['$inc'] = {
        'totalRamSize': sign * inc[RamModule.type_name],
        'totalHardDriveSize': sign * inc[HardDrive.type_name]
    }

    if add:
        component_query = {'$addToSet': {'components': {'$each': components_id}}}
    else:
        component_query = {'$pull': {'components': {'$in': components_id}}}
    update_query.update(component_query)
    current_app.data.driver.db['devices'].update({'_id': device_id}, update_query)