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
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 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
def flask_update_log_level(): try: content_type = request.headers.get('Content-Type') data_request = request.data _, json_payload = parse_payload(content_type, data_request, log_schema) LoggerHandler.update_log_level(json_payload['level']) return make_response('', 200) except HTTPRequestError as error: if isinstance(error.message, dict): return make_response(jsonify(error.message), error.error_code) return format_response(error.error_code, error.message)
def update_template(cls, params, template_id, token): """ Updates a single template. :param params: Parameters received from request (content_type, data) as created by Flask :param template_id: The template to be updated. :param token: The authorization token (JWT). :return The old version of this template (previous to the update). :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. """ service = init_tenant_context(token, db) content_type = params.get('content_type') data_request = params.get('data') # find old version of the template, if any old = assert_template_exists(template_id) # parse updated version from payload updated, json_payload = parse_payload(content_type, data_request, template_schema) LOGGER.debug(f" Current json payload: {json_payload}") old.label = updated['label'] new = json_payload['attrs'] LOGGER.debug(f" Checking old template attributes") def attrs_match(attr_from_db, attr_from_request): return ((attr_from_db.label == attr_from_request["label"]) and (attr_from_db.type == attr_from_request["type"])) def update_attr(attrs_from_db, attrs_from_request): attrs_from_db.value_type = attrs_from_request.get('value_type', None) attrs_from_db.static_value = attrs_from_request.get('static_value', None) def validate_attr(attr_from_request, is_meta): if is_meta is False: attr_schema.load(attr_from_request) else: metaattr_schema.load(attr_from_request) def analyze_attrs(attrs_from_db, attrs_from_request, parentAttr=None): for attr_from_db in attrs_from_db: found = False for idx, attr_from_request in enumerate(attrs_from_request): validate_attr(attr_from_request, parentAttr is not None) if attrs_match(attr_from_db, attr_from_request): update_attr(attr_from_db, attr_from_request) if "metadata" in attr_from_request: analyze_attrs(attr_from_db.children, attr_from_request["metadata"], attr_from_db) attrs_from_request.pop(idx) found = True break if not found: LOGGER.debug(f" Removing attribute {attr_from_db.label}") db.session.delete(attr_from_db) if parentAttr and attrs_from_request is not None: for attr_from_request in attrs_from_request: orm_child = DeviceAttr(parent=parentAttr, **attr_from_request) db.session.add(orm_child) return attrs_from_request to_be_added = analyze_attrs(old.attrs, new) for attr in to_be_added: LOGGER.debug(f" Adding new attribute {attr}") if "id" in attr: del attr["id"] child = DeviceAttr(template=old, **attr) db.session.add(child) if "metadata" in attr and attr["metadata"] is not None: for metadata in attr["metadata"]: LOGGER.debug(f" Adding new metadata {metadata}") orm_child = DeviceAttr(parent=child, **metadata) db.session.add(orm_child) try: LOGGER.debug(f" Commiting new data...") refresh_template_update_column(db, old) db.session.commit() LOGGER.debug("... data committed.") except IntegrityError as error: LOGGER.debug(f" ConsistencyException was thrown.") handle_consistency_exception(error) # notify interested parties that a set of devices might have been implicitly updated affected = db.session.query(DeviceTemplateMap) \ .filter(DeviceTemplateMap.template_id==template_id) \ .all() affected_devices = [] kafka_handler_instance = cls.kafka.getInstance(cls.kafka.kafkaNotifier) for device in affected: orm_device = assert_device_exists(device.device_id) kafka_handler_instance.update(serialize_full_device(orm_device, service), meta={"service": service}) affected_devices.append(device.device_id) event = { "event": DeviceEvent.TEMPLATE, "data": { "affected": affected_devices, "template": template_schema.dump(old) }, "meta": {"service": service} } kafka_handler_instance.kafkaNotifier.send_raw(event, service) results = { 'updated': template_schema.dump(old), 'result': 'ok' } return results
def update_device(req, device_id): """ Updated the information about a particular device :param req: The received HTTP request, as created by Flask. :param device_id: The device to be updated. :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. """ device_data, json_payload = parse_payload(req, device_schema) tenant = init_tenant_context(req, 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) try: db.session.commit() except IntegrityError as error: # This will throw an exception. handle_consistency_exception(error) full_device = serialize_full_device(updated_orm_device, tenant) if CONFIG.orion: # Create subscription pointing to history service # (STH, logstash based persister) subs_handler = PersistenceHandler(service=tenant) subs_handler.remove(old_orm_device.persistence) # Generating 'device type' field for subscription request type_descr = "template" for dev_type in full_device['attrs'].keys(): type_descr += "_" + str(dev_type) updated_orm_device.persistence = subs_handler.create( device_id, type_descr) ctx_broker_handler = OrionHandler(service=tenant) ctx_broker_handler.update(serialize_full_device(old_orm_device, tenant), type_descr) kafka_handler = KafkaHandler() kafka_handler.update(full_device, meta={"service": tenant}) result = { 'message': 'device updated', 'device': serialize_full_device(updated_orm_device, tenant) } return result
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
def update_template(req, template_id): """ Updates a single template. :param req: The received HTTP request, as created by Flask. :param template_id: The template to be updated. :return The old version of this template (previous to the update). :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. """ service = init_tenant_context(req, db) # find old version of the template, if any old = assert_template_exists(template_id) # parse updated version from payload updated, json_payload = parse_payload(req, template_schema) LOGGER.debug(f" Current json payload: {json_payload}") old.label = updated['label'] new = json_payload['attrs'] LOGGER.debug(f" Checking old template attributes") for a in old.attrs: LOGGER.debug(f" Checking attribute {a}...") found = False for idx, b in enumerate(new): LOGGER.debug(f" Comparing against new attribute {b}") if (a.label == b['label']) and (a.type == b['type']): found = True a.value_type = b.get('value_type', None) a.static_value = b.get('static_value', None) new.pop(idx) LOGGER.debug(f" They match. Attribute data will be updated.") break if not found: LOGGER.debug(f" No match for this attribute. It will be removed.") db.session.delete(a) for a in new: LOGGER.debug(f" Adding new attribute {a}") if "id" in a: del a["id"] db.session.add(DeviceAttr(template=old, **a)) try: LOGGER.debug(f" Commiting new data...") db.session.commit() LOGGER.debug("... data committed.") except IntegrityError as error: LOGGER.debug(f" ConsistencyException was thrown.") handle_consistency_exception(error) # notify interested parties that a set of devices might have been implicitly updated affected = db.session.query(DeviceTemplateMap) \ .filter(DeviceTemplateMap.template_id==template_id) \ .all() affected_devices = [] for device in affected: affected_devices.append(device.device_id) event = { "event": DeviceEvent.TEMPLATE, "data": { "affected": affected_devices, "template": template_schema.dump(old) }, "meta": {"service": service} } send_raw(event, service) results = { 'updated': template_schema.dump(old), 'result': 'ok' } return results
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