示例#1
0
    def import_data(data, token, content_type):
        """
        Import data.

        :param data: The received data HTTP request, as created by Flask.
        :param token: The authorization token (JWT).
        :param content_type: The content_type of request (application/json)
        :return The status message.
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If import attribute constraints were
        violated. This might happen if two attributes have the same name, for
        instance.
        """

        saved_templates = []
        saved_devices = []

        try:
            tenant = init_tenant_context(token, db)

            ImportHandler.clear_db_config(tenant)

            original_req_data = copy.copy(data)

            original_payload = json.loads(original_req_data)

            data = ImportHandler.replace_ids_by_import_ids(data)

            json_data, json_payload = parse_payload(content_type, data,
                                                    import_schema)

            saved_templates = ImportHandler.save_templates(
                json_data, json_payload)

            saved_devices = ImportHandler.save_devices(json_data,
                                                       original_payload,
                                                       saved_templates)

            ImportHandler.restore_db_config()

            ImportHandler().notifies_creation_to_kafka(saved_devices, tenant)

            db.session.commit()

        except IntegrityError as e:
            LOGGER.error(f' {e}')
            db.session.rollback()
            raise HTTPRequestError(
                400,
                'Template attribute constraints are violated by the request')
        except Exception as e:
            LOGGER.error(f' {e}')
            db.session.rollback()
            raise HTTPRequestError(400, 'Failed to import data')
        finally:
            db.session.close()

        results = {'message': 'data imported!'}
        return results
    def create(self, device_id, device_type='device'):
        """ Returns subscription id on success. """

        try:
            svc = json.dumps({
                "entities": [{
                    "type": device_type,
                    "isPattern": "false",
                    "id": device_id
                }],
                "reference":
                self.targetUrl,
                "duration":
                "P10Y",
                "notifyConditions": [{
                    "type": "ONCHANGE"
                }]
            })
            response = requests.post(self.baseUrl,
                                     headers=self._headers,
                                     data=svc)
            if not (response.status_code == 409 or
                    (200 <= response.status_code < 300)):
                raise HTTPRequestError(500, "Failed to create subscription")

            # return the newly created subs
            reply = response.json()
            return reply['subscribeResponse']['subscriptionId']
        except ValueError:
            LOGGER.error('Failed to create subscription')
            raise HTTPRequestError(500, "Failed to create subscription")
        except requests.ConnectionError:
            raise HTTPRequestError(500, "Broker is not reachable")
    def create(self, device):
        """ Returns boolean indicating device creation success. """

        try:
            svc = json.dumps({
                "services": [{
                    "resource": "devm",
                    "apikey": self.service,
                    "entity_type": 'device'
                }]
            })
            response = requests.post(self.baseUrl + '/services',
                                     headers=self._headers,
                                     data=svc)
            if not (response.status_code == 409 or
                    (200 <= response.status_code < 300)):
                error = "Failed to configure ingestion subsystem: service creation failed"
                raise HTTPRequestError(500, error)
        except requests.ConnectionError:
            raise HTTPRequestError(
                500, "Cannot reach ingestion subsystem (service)")

        try:
            response = requests.post(
                self.baseUrl + '/devices',
                headers=self._headers,
                data=json.dumps({'devices': [self.__get_config(device)]}))
            if not (200 <= response.status_code < 300):
                error = "Failed to configure ingestion subsystem: device creation failed"
                raise HTTPRequestError(500, error)
        except requests.ConnectionError:
            raise HTTPRequestError(
                500, "Cannot reach ingestion subsystem (device)")
示例#4
0
def create_orm_override(attr, orm_device, orm_template):
    try:
        target = int(attr['id'])
    except ValueError:
        LOGGER.error(f" Unknown attribute {attr['id']} in override list")
        raise HTTPRequestError(
            400, 'Unknown attribute {} in override list'.format(attr['id']))

    found = False
    for orm_attr in orm_template.attrs:
        if target == orm_attr.id:
            found = True
            if 'static_value' in attr and attr['static_value'] is not None:
                orm_override = DeviceOverride(
                    device=orm_device,
                    attr=orm_attr,
                    static_value=attr['static_value'])
                db.session.add(orm_override)
                LOGGER.debug(f" Added overrided form {orm_override}")

            # Update possible metadata field
            if 'metadata' in attr:
                for metadata in attr['metadata']:
                    try:
                        metadata_target = int(metadata['id'])
                        LOGGER.debug(f" Updated metadata {metadata_target}")
                    except ValueError:
                        LOGGER.error(
                            f" metadata attribute {attr['id']} in override list"
                        )
                        raise HTTPRequestError(
                            400,
                            'Unknown metadata attribute {} in override list'.
                            format(metadata['id']))

                    found = False
                    # WARNING: Adds no-autoflush here, without it metadata override fail during device update
                    with db.session.no_autoflush:
                        for orm_attr_child in orm_attr.children:
                            if metadata_target == orm_attr_child.id:
                                found = True
                                if 'static_value' in metadata and metadata[
                                        'static_value'] is not None:
                                    orm_override = DeviceOverride(
                                        device=orm_device,
                                        attr=orm_attr_child,
                                        static_value=metadata['static_value'])
                                    db.session.add(orm_override)
                                    LOGGER.debug(
                                        f" Added overrided form {orm_override}"
                                    )

    if not found:
        LOGGER.error(f" Unknown attribute {attr['id']} in override list")
        raise HTTPRequestError(
            400, 'Unknown attribute {} in override list'.format(target))
    def remove(self, subsId):
        """ Returns boolean indicating subscription removal success. """

        try:
            response = requests.delete(self.baseUrl + '/' + subsId,
                                       headers=self._noBodyHeaders)
            if not (200 <= response.status_code < 300):
                raise HTTPRequestError(500, "Failed to remove subscription")
        except requests.ConnectionError:
            raise HTTPRequestError(500, "Broker is not reachable")
示例#6
0
def parse_payload(request, schema):
    try:
        content_type = request.headers.get('Content-Type')
        if (content_type is None) or (content_type != "application/json"):
            raise HTTPRequestError(400, "Payload must be valid JSON, and Content-Type set accordingly")
        json_payload = json.loads(request.data)
        data = schema.load(json_payload)
    except ValueError:
        raise HTTPRequestError(400, "Payload must be valid JSON, and Content-Type set accordingly")
    except ValidationError as errors:
        results = {'message': 'failed to parse input', 'errors': errors.messages}
        raise HTTPRequestError(400, results)
    return data, json_payload
    def remove(self, deviceid):
        """ Returns boolean indicating device removal success. """

        try:
            response = requests.delete(self.baseUrl + '/devices/' + deviceid,
                                       headers=self._noBodyHeaders)
            if 200 <= response.status_code < 300:
                response = requests.delete('%s/%s' % (self.orionUrl, deviceid),
                                           headers=self._noBodyHeaders)
                if not (200 <= response.status_code < 300):
                    error = "Failed to configure ingestion subsystem: device removal failed"
                    raise HTTPRequestError(500, error)
        except requests.ConnectionError:
            raise HTTPRequestError(500, "Cannot reach ingestion subsystem")
示例#8
0
    def delete_all_templates(token):
        """
        Deletes all templates.

        :param token: The authorization token (JWT).
        :raises HTTPRequestError: If this template could not be found in
        database.
        """
        init_tenant_context(token, db)
        json_templates = []

        try:
            templates = db.session.query(DeviceTemplate)
            for template in templates:
                db.session.delete(template)
                json_templates.append(template_schema.dump(template))

            db.session.commit()
        except IntegrityError:
            raise HTTPRequestError(400, "Templates cannot be removed as they are being used by devices")

        results = {
            'result': 'ok',
            'removed': json_templates
        }

        return results
示例#9
0
    def remove_template(template_id, token):
        """
        Deletes a single template.

        :param template_id: The template to be removed.
        :param token: The authorization token (JWT).
        :return The removed template.
        :rtype JSON
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If this template could not be found in
        database.
        :raises HTTPRequestError: If the template is being currently used by
        a device.
        """
        init_tenant_context(token, db)
        tpl = assert_template_exists(template_id)

        json_template = template_schema.dump(tpl)
        try:
            db.session.delete(tpl)
            db.session.commit()
        except IntegrityError:
            raise HTTPRequestError(400, "Templates cannot be removed as they are being used by devices")

        results = {
            'result': 'ok',
            'removed': json_template
        }

        return results
def load_attrs(attr_list, parent_template, base_type, db):
    """

    :rtype:
    """
    for attr in attr_list:
        try:
            entity = attr_schema.load(attr)
            try:
                children = entity.pop('children')
            except KeyError:
                children = []

            orm_entity = base_type(template=parent_template, **entity)
            db.session.add(orm_entity)

            for child in children:
                orm_child = DeviceAttr(parent=orm_entity, **child)
                db.session.add(orm_child)
        except ValidationError as errors:
            results = {
                'message': 'failed to parse attr',
                'errors': errors.messages
            }
            raise HTTPRequestError(400, results)
    def create_template(req):
        """
        Creates a new template.

        :param req: The received HTTP request, as created by Flask.
        :return The created template.
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If template attribute constraints were
        violated. This might happen if two attributes have the same name, for
        instance.
        """
        init_tenant_context(req, db)
        tpl, json_payload = parse_payload(req, template_schema)
        loaded_template = DeviceTemplate(**tpl)
        load_attrs(json_payload['attrs'], loaded_template, DeviceAttr, db)
        db.session.add(loaded_template)

        try:
            db.session.commit()
            LOGGER.debug(f" Created template in database")
        except IntegrityError as e:
            LOGGER.error(f' {e}')
            raise HTTPRequestError(
                400,
                'Template attribute constraints are violated by the request')

        results = {
            'template': template_schema.dump(loaded_template),
            'result': 'ok'
        }
        return results
示例#12
0
def flask_copy_psk(device_id, attr_label):
    try:
        # retrieve the authorization token
        token = retrieve_auth_token(request)

        # retrieve the parameters
        if ((not 'from_dev_id' in request.args.keys()) or
            (not 'from_attr_label' in request.args.keys())):
            raise HTTPRequestError(400, "Missing mandatory parameter: from_dev_id "
                "or/and from_attr_label")

        from_dev_id = request.args['from_dev_id']
        from_attr_label = request.args['from_attr_label']

        DeviceHandler.copy_psk(token,
                                from_dev_id,
                                from_attr_label,
                                device_id,
                                attr_label)

        return make_response("", 204)
    except HTTPRequestError as e:
        if isinstance(e.message, dict):
            return make_response(jsonify(e.message), e.error_code)

        return format_response(e.error_code, e.message)
示例#13
0
def flask_gen_psk(device_id):
    try:
        # retrieve the authorization token
        token = retrieve_auth_token(request)

        # retrieve the key_length parameter (mandatory)
        if not 'key_length' in request.args.keys():
            raise HTTPRequestError(400, "Missing key_length parameter")
        key_length = int(request.args['key_length'])

        # retrieve the attrs parameter (optional)
        target_attributes = None
        if 'attrs' in request.args.keys():
            target_attributes = request.args['attrs'].split(",")

        result = DeviceHandler.gen_psk(token,
                                       device_id,
                                       key_length,
                                       target_attributes)

        LOGGER.info(f' Successfully generated psk for the device: {device_id}.')
        return make_response(jsonify(result), 200)
    except HTTPRequestError as e:
        LOGGER.error(f' {e.message} - {e.error_code}.')
        if isinstance(e.message, dict):
            return make_response(jsonify(e.message), e.error_code)

        return format_response(e.error_code, e.message)
示例#14
0
def assert_device_relation_exists(device_id, template_id):
    try:
        return DeviceTemplateMap.query.filter_by(
            device_id=device_id, template_id=template_id).one()
    except sqlalchemy.orm.exc.NoResultFound:
        raise HTTPRequestError(
            404, "Device %s is not associated with template %s" %
            (device_id, template_id))
def init_tenant_context(request, db):
    try:
        token = request.headers['authorization']
    except KeyError:
        raise HTTPRequestError(401, "No authorization token has been supplied")

    tenant = get_allowed_service(token)
    init_tenant(tenant, db)
    return tenant
示例#16
0
    def update_device(cls, params, device_id, token):
        """
        Updated the information about a particular device

        :param params: Parameters received from request (content_type, data)
        as created by Flask
        :param device_id: The device to be updated.
        :param token: The authorization token (JWT).
        :return The updated device.
        :rtype JSON
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If this device could not be found in
        database.
        """
        try:
            content_type = params.get('content_type')
            data_request = params.get('data')

            device_data, json_payload = parse_payload(content_type,
                                                      data_request,
                                                      device_schema)
            validate_repeated_attrs(json_payload)

            tenant = init_tenant_context(token, db)
            old_orm_device = assert_device_exists(device_id)
            db.session.delete(old_orm_device)
            db.session.flush()

            # handled separately by parse_template_list
            device_data.pop('templates')
            updated_orm_device = Device(**device_data)
            parse_template_list(json_payload.get('templates', []),
                                updated_orm_device)
            auto_create_template(json_payload, updated_orm_device)
            updated_orm_device.id = device_id
            updated_orm_device.updated = datetime.now()
            updated_orm_device.created = old_orm_device.created

            db.session.add(updated_orm_device)

            db.session.commit()
        except IntegrityError as error:
            handle_consistency_exception(error)
        except ValidationError as error:
            raise HTTPRequestError(400, error.messages)

        full_device = serialize_full_device(updated_orm_device, tenant)

        kafka_handler_instance = cls.kafka.getInstance(cls.kafka.kafkaNotifier)
        kafka_handler_instance.update(full_device, meta={"service": tenant})

        result = {
            'message': 'device updated',
            'device': serialize_full_device(updated_orm_device, tenant)
        }
        return result
示例#17
0
def assert_template_exists(template_id, session=None):
    try:
        if session:
            with session.no_autoflush:
                return session.query(DeviceTemplate).filter_by(
                    id=template_id).one()
        else:
            return DeviceTemplate.query.filter_by(id=template_id).one()
    except sqlalchemy.orm.exc.NoResultFound:
        raise HTTPRequestError(404, "No such template: %s" % template_id)
示例#18
0
    def configure_device(cls, params, device_id, token):
        """
        Send actuation commands to the device

        :param params: Parameters received from request (data)
        as created by Flask
        :param device_id: The device which should receive the actuation message
        :param token: The authorization token (JWT).
        :return A status on whether the message was sent to the device or not.
        Note that this is not a guarantee that the device actually performed
        what was requested.
        :rtype JSON
        """
        # Meta information to be published along with device actuation message
        meta = {'service': ''}
        invalid_attrs = []

        meta['service'] = init_tenant_context(token, db)

        meta['timestamp'] = int(time.time() * 1000)

        orm_device = assert_device_exists(device_id)
        full_device = serialize_full_device(orm_device, meta['service'])
        LOGGER.debug(f" Full device: {json.dumps(full_device)}")

        data = params.get('data')
        payload = json.loads(data)
        LOGGER.debug(f' Parsed request payload: {json.dumps(payload)}')

        payload['id'] = orm_device.id

        for attr in payload['attrs']:
            if find_attribute(full_device, attr, 'actuator') is None:
                invalid_attrs.append(attr)

        if not invalid_attrs:
            LOGGER.debug(f' Sending configuration message through Kafka.')
            kafka_handler_instance = cls.kafka.getInstance(
                cls.kafka.kafkaNotifier)
            kafka_handler_instance.configure(payload, meta)
            LOGGER.debug(f' Configuration sent.')
            result = {f' status': 'configuration sent to device'}
        else:
            LOGGER.warning(
                f' invalid attributes detected in command: {invalid_attrs}')
            result = {
                'status': 'some of the attributes are not configurable',
                'attrs': invalid_attrs
            }
            raise HTTPRequestError(403, result)

        return result
示例#19
0
def assert_device_exists(device_id, session=None):
    """
    Assert that a device exists, returning the object retrieved from the
    database.
    """
    try:
        if session:
            with session.no_autoflush:
                return session.query(Device).filter_by(id=device_id).one()
        else:
            return Device.query.filter_by(id=device_id).one()
    except sqlalchemy.orm.exc.NoResultFound:
        raise HTTPRequestError(404, "No such device: %s" % device_id)
示例#20
0
def auto_create_template(json_payload, new_device):
    if ('attrs' in json_payload) and (new_device.templates is None):
        device_template = DeviceTemplate(label="device.%s template" %
                                         new_device.id)
        db.session.add(device_template)
        new_device.templates = [device_template]
        load_attrs(json_payload['attrs'], device_template, DeviceAttr, db)

    # TODO: perhaps it'd be best if all ids were random hex strings?
    if ('attrs' in json_payload) and (new_device.templates is not None):
        for attr in json_payload['attrs']:
            orm_template = find_template(new_device.templates,
                                         attr['template_id'])
            if orm_template is None:
                raise HTTPRequestError(
                    400,
                    'Unknown template "{}" in attr list'.format(orm_template))

            try:
                target = int(attr['id'])
            except ValueError:
                raise HTTPRequestError(
                    400, "Unkown attribute \"{}\" in override list".format(
                        attr['id']))

            found = False
            for orm_attr in orm_template.attrs:
                if target == orm_attr.id:
                    found = True
                    orm_override = DeviceOverride(
                        device=new_device,
                        attr=orm_attr,
                        static_value=attr['static_value'])
                    db.session.add(orm_override)
            if not found:
                raise HTTPRequestError(
                    400,
                    "Unkown attribute \"{}\" in override list".format(target))
示例#21
0
def load_attrs(attr_list, parent_template, base_type, db):
    """

    :rtype:
    """
    for attr in attr_list:
        try:
            entity = attr_schema.load(attr).data
        except ValidationError as errors:
            results = {'message': 'failed to parse attr', 'errors': errors}
            raise HTTPRequestError(400, results)

        orm_attr = base_type(template=parent_template, **entity)
        db.session.add(orm_attr)
示例#22
0
def auto_create_template(json_payload, new_device):
    if ('attrs' in json_payload) and (new_device.templates is None):
        device_template = DeviceTemplate(
            label="device.%s template" % new_device.id)
        db.session.add(device_template)
        LOGGER.debug(f" Adding auto-created template {device_template} into database")
        new_device.templates = [device_template]
        load_attrs(json_payload['attrs'], device_template, DeviceAttr, db)

    # TODO: perhaps it'd be best if all ids were random hex strings?
    if ('attrs' in json_payload) and (new_device.templates is not None):
        for attr in json_payload['attrs']:
            orm_template = find_template(new_device.templates, attr['template_id'])
            if orm_template is None:
                LOGGER.error(f" Unknown template {orm_template} in attr list")
                raise HTTPRequestError(400, 'Unknown template "{}" in attr list'.format(orm_template))
            create_orm_override(attr, new_device, orm_template)
示例#23
0
    def generate_device_id():
        """
        Creates a new device id
        :return The new ID
        :rtype int
        :raises HTTPRequestError: If this function can't generate a new valid
        ID after 10 attempts.
        """

        # TODO this is awful, makes me sad, but for now also makes demoing
        # easier We might want to look into an auto-configuration feature for
        # devices, such that ids are not input manually on devices

        _attempts = 0
        generated_id = ''
        while _attempts < 10 and len(generated_id) == 0:
            _attempts += 1
            new_id = create_id()
            if Device.query.filter_by(id=new_id).first() is None:
                return new_id

        raise HTTPRequestError(500, "Failed to generate unique device_id")
示例#24
0
    def update_log_level(self, LEVEL):
        levelToName = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']

        try:
            self.log = logging.getLogger('device-manager.' + __name__)
            for hdlr in self.log.handlers[:]:
                self.log.removeHandler(hdlr)

            self.stream = logging.StreamHandler()
            self.stream.setLevel(LEVEL)
            self.stream.setFormatter(self.formatter)

            self.log.setLevel(LEVEL)
            self.log.addHandler(self.stream)
            self.log.handler_set = True

            self.level = LEVEL

        except ValueError:
            raise HTTPRequestError(
                400,
                "Unknown level: {} valid are {}".format(LEVEL, levelToName))
    def create_update_device(self, device, type_descr, is_update=True):
        target_url = "%s/%s/attrs?type=%s&options=keyValues" % (
            self.baseUrl, device['id'], type_descr)
        body = json.dumps(OrionHandler.parse_device(device, not is_update))
        if not is_update:
            target_url = self.baseUrl

        try:
            LOGGER.info("about to create device in ctx broker")
            LOGGER.debug("%s", body)
            response = requests.post(target_url,
                                     headers=self._headers,
                                     data=body)
            if 200 <= response.status_code < 300:
                LOGGER.debug("Broker update successful")
            else:
                LOGGER.info("Failed to update ctx broker: %d",
                            response.status_code)
                try:
                    LOGGER.debug("%s", response.json())
                except Exception as e:
                    LOGGER.error(e)
        except requests.ConnectionError:
            raise HTTPRequestError(500, "Broker is not reachable")
示例#26
0
    def create_device(cls, params, token):
        """
        Creates and configures the given device.

        :param params: Parameters received from request (count, verbose, 
        content_type, data)
        :param token: The authorization token (JWT).
        
        :return The created device or a list of device summary. This depends on
        which value the verbose (/?verbose=true) has - if true, only one device
        can be created ("count" argument can't be used or - at least - it must
        be exactly "1") and the full device is returned. If false, "count" can
        be used with higher values and only the devices summary (a structure
        containing all device IDs and their labels) is returned.
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If verbose is used with multiple device
        creation request.
        :raises HTTPRequestError: If count argument ("?count=X") is provided
        and it is not an integer.

        """
        tenant = init_tenant_context(token, db)
        try:
            count = int(params.get('count'))
        except ValueError as e:
            LOGGER.error(e)
            raise HTTPRequestError(400, "If provided, count must be integer")

        c_length = len(str(count))
        verbose = params.get('verbose') in ['true', '1', 'True']
        if verbose and count != 1:
            raise HTTPRequestError(
                400, "Verbose can only be used for single device creation")

        devices = []
        full_device = None
        orm_devices = []

        try:
            for i in range(0, count):
                content_type = params.get('content_type')
                data_request = params.get('data')
                device_data, json_payload = parse_payload(
                    content_type, data_request, device_schema)
                validate_repeated_attrs(json_payload)
                device_data['id'] = DeviceHandler.generate_device_id()
                device_data['label'] = DeviceHandler.indexed_label(
                    count, c_length, device_data['label'], i)
                device_data.pop('templates', None)
                orm_device = Device(**device_data)
                parse_template_list(json_payload.get('templates', []),
                                    orm_device)
                auto_create_template(json_payload, orm_device)
                db.session.add(orm_device)
                orm_devices.append(orm_device)
            db.session.commit()
        except IntegrityError as error:
            handle_consistency_exception(error)
        except ValidationError as error:
            raise HTTPRequestError(400, error.messages)

        for orm_device in orm_devices:
            devices.append({'id': orm_device.id, 'label': orm_device.label})

            full_device = serialize_full_device(orm_device, tenant)

            # Updating handlers
            kafka_handler_instance = cls.kafka.getInstance(
                cls.kafka.kafkaNotifier)
            kafka_handler_instance.create(full_device,
                                          meta={"service": tenant})

        if verbose:
            result = {'message': 'device created', 'devices': [full_device]}
        else:
            result = {'message': 'devices created', 'devices': devices}
        return result
示例#27
0
def handle_consistency_exception(error):
    raise HTTPRequestError(400, error.orig.diag.message_primary)
示例#28
0
    def create_device(req):
        """
        Creates and configures the given device.

        :param req: The received HTTP request, as created by Flask.
        :return The created device or a list of device summary. This depends on
        which value the verbose (/?verbose=true) has - if true, only one device
        can be created ("count" argument can't be used or - at least - it must
        be exactly "1") and the full device is returned. If false, "count" can
        be used with higher values and only the devices summary (a structure
        containing all device IDs and their labels) is returned.
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If verbose is used with multiple device
        creation request.
        :raises HTTPRequestError: If count argument ("?count=X") is provided
        and it is not an integer.

        """

        tenant = init_tenant_context(req, db)
        try:
            count = int(req.args.get('count', '1'))
        except ValueError as e:
            LOGGER.error(e)
            raise HTTPRequestError(400, "If provided, count must be integer")

        c_length = len(str(count))
        verbose = req.args.get('verbose', 'false') in ['true', '1', 'True']
        if verbose and count != 1:
            raise HTTPRequestError(
                400, "Verbose can only be used for single device creation")

        devices = []

        # Handlers
        kafka_handler = KafkaHandler()
        if CONFIG.orion:
            ctx_broker_handler = OrionHandler(service=tenant)
            subs_handler = PersistenceHandler(service=tenant)
        else:
            ctx_broker_handler = None
            subs_handler = None

        full_device = None
        orm_devices = []

        for i in range(0, count):
            device_data, json_payload = parse_payload(req, device_schema)
            device_data['id'] = DeviceHandler.generate_device_id()
            device_data['label'] = DeviceHandler.indexed_label(count, c_length, device_data['label'], i)
            # handled separately by parse_template_list
            device_data.pop('templates', None)
            orm_device = Device(**device_data)
            parse_template_list(json_payload.get('templates', []), orm_device)
            auto_create_template(json_payload, orm_device)
            db.session.add(orm_device)
            orm_devices.append(orm_device)

        try:
            db.session.commit()
        except IntegrityError as error:
            handle_consistency_exception(error)

        for orm_device in orm_devices:
            devices.append(
                {
                    'id': orm_device.id,
                    'label': orm_device.label
                }
            )

            full_device = serialize_full_device(orm_device, tenant)

            # Updating handlers
            kafka_handler.create(full_device, meta={"service": tenant})
            if CONFIG.orion:
                # Generating 'device type' field for history
                type_descr = "template"
                for dev_type in full_device['attrs'].keys():
                    type_descr += "_" + str(dev_type)
                # TODO remove this in favor of kafka as data broker....
                ctx_broker_handler.create(full_device, type_descr)
                sub_id = subs_handler.create(full_device['id'], type_descr)
                orm_device.persistence = sub_id


        if verbose:
            result = {
                'message': 'device created',
                'device': full_device
            }
        else:
            result = {
                'message': 'devices created',
                'devices': devices
            }
        return result
示例#29
0
    def gen_psk(token, device_id, key_length, target_attributes=None):
        """
        Generates pre shared keys to a specifics device

        :param token: The authorization token (JWT).
        :param device_id: The target device.
        :param key_length: The key length to be generated.
        :param target_attributes: A list with the target attributes, None means
        all suitable attributes.
        :return The keys generated.
        :rtype JSON
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        :raises HTTPRequestError: If this device could not be found in
        database.
        """

        tenant = init_tenant_context2(token, db)

        device_orm = assert_device_exists(device_id, db.session)
        if not device_orm:
            raise HTTPRequestError(404, "No such device: {}".format(device_id))

        device = serialize_full_device(device_orm, tenant, True)

        # checks if the key length has been specified
        # todo remove this magic number
        if key_length > 1024 or key_length <= 0:
            raise HTTPRequestError(400, "key_length must be greater than 0 and lesser than {}".format(1024))

        is_all_psk_attr_valid = False
        target_attrs_data = []

        # find the target attributes
        # first case: if there are specified attributes
        if target_attributes:
            for template_id in device["templates"]:
                for attr in device["attrs"][template_id]:
                    if attr["value_type"] == "psk" and attr["label"] in target_attributes:
                        target_attrs_data.append(attr)
                        is_all_psk_attr_valid = True

            if not is_all_psk_attr_valid:
                raise HTTPRequestError(400, "Not found some attributes, "
                    "please check them")

            if len(target_attributes) != len(target_attrs_data):
                if not is_all_psk_attr_valid:
                    raise HTTPRequestError(400,
                                    "Some attribute is not a 'psk' type_value")
                else:
                    raise HTTPRequestError(400, "Not found some attributes, "
                        "please check them")
        else: # second case: if there are not specified attributes
            for template_id in device["templates"]:
                for attr in device["attrs"][template_id]:
                    if attr["value_type"] == "psk":
                        target_attrs_data.append(attr)
                        is_all_psk_attr_valid = True

            if not is_all_psk_attr_valid:
                # there is no psk key, do not worry it is not a problem
                raise HTTPRequestError(204, "")

        # generate the pre shared key on selected attributes
        result = []
        for attr in target_attrs_data:
            psk = secrets.token_hex(key_length)
            psk_hex = psk
            attr["static_value"] = psk_hex
            encrypted_psk = encrypt(psk)
            psk_entry = DeviceAttrsPsk.query.filter_by(device_id=device["id"],
                                                       attr_id=attr["id"]).first()
            if not psk_entry:
                db.session.add(DeviceAttrsPsk(device_id=device["id"],
                                              attr_id=attr["id"],
                                              psk=encrypted_psk))
            else:
                psk_entry.psk = encrypted_psk

            result.append( {'attribute': attr["label"], 'psk': psk_hex} )

        device_orm.updated = datetime.now()
        db.session.commit()

        # send an update message on kafka
        kafka_handler = KafkaHandler()
        kafka_handler.update(device, meta={"service": tenant})

        return result
示例#30
0
    def copy_psk(token, src_device_id, src_attr, dest_device_id, dest_attr):
        """
        Copies a pre shared key from a device attribute to another

        :param token: The authorization token (JWT).
        :param src_device_id: The source device (from).
        :param src_attr: The source attribute (from).
        :param dest_device_id: The destination device (to).
        :param dest_attr: The destination attribute (to).
        :return None.
        :raises HTTPRequestError: If no authorization token was provided (no
        tenant was informed)
        todo list the others exceptions
        """

        tenant = init_tenant_context2(token, db)

        src_device_orm = assert_device_exists(src_device_id, db.session)
        if not src_device_orm:
            raise HTTPRequestError(404, "No such device: {}".format(src_device_id))

        src_device = serialize_full_device(src_device_orm, tenant, True)

        found_attr = False
        src_attr_ref = None
        for template_id in src_device["templates"]:
            for attr in src_device["attrs"][template_id]:
                if attr["label"] == src_attr:
                    if attr["value_type"] == "psk":
                        found_attr = True
                        src_attr_ref = attr
                        break
                    else:
                        raise HTTPRequestError(400,
                                "Attribute {} is not a 'psk' type_value".format(src_attr))

        if not found_attr:
            raise HTTPRequestError(404, "Not found attributes {}".format(src_attr))

        dest_device_orm = assert_device_exists(dest_device_id, db.session)
        if not dest_device_orm:
            raise HTTPRequestError(404, "No such device: {}".format(dest_device_id))

        dest_device = serialize_full_device(dest_device_orm, tenant, True)

        found_attr = False
        dest_attr_ref = None
        for template_id in dest_device["templates"]:
            for attr in dest_device["attrs"][template_id]:
                if attr["label"] == dest_attr:
                    if attr["value_type"] == "psk":
                        found_attr = True
                        dest_attr_ref = attr
                        break
                    else:
                        raise HTTPRequestError(400,
                                "Attribute {} is not a 'psk' type_value".format(dest_attr))

        if not found_attr:
            raise HTTPRequestError(404, "Not found attributes {}".format(dest_attr))

        # copy the pre shared key
        src_psk_entry = DeviceAttrsPsk.query.filter_by(device_id=src_device["id"],
                                                    attr_id=src_attr_ref["id"]).first()
        if not src_psk_entry:
            raise HTTPRequestError(400, "There is not a psk generated to {}".format(src_attr))

        dest_psk_entry = DeviceAttrsPsk.query.filter_by(device_id=dest_device["id"],
            attr_id=dest_attr_ref["id"]).first()
        if not dest_psk_entry:
            db.session.add(DeviceAttrsPsk(device_id=dest_device["id"],
                                            attr_id=dest_attr_ref["id"],
                                            psk=src_psk_entry.psk))
        else:
            dest_psk_entry.psk = src_psk_entry.psk

        dest_device_orm.updated = datetime.now()
        db.session.commit()

        dest_attr_ref['static_value'] = src_attr_ref['static_value']

        # send an update message on kafka
        kafka_handler = KafkaHandler()
        kafka_handler.update(dest_device, meta={"service": tenant})

        return None