def _handle_client_sync(self, req: Request): """ Handles a client update request from any foreign source :param req: Request containing the client update request :return: None """ try: self._validator.validate(req.get_payload(), "api_client_sync_request") except ValidationError as err: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.sync_client.uri}': '{err.message}'") return client_id = req.get_sender() self._logger.info(f"Syncing client {client_id}") decoder = ApiDecoder() new_client = decoder.decode_client(req.get_payload()["client"], req.get_sender()) gadget_data = req.get_payload()["gadgets"] for gadget in gadget_data: self._update_gadget(client_id, gadget) self._delegate.handle_client_sync(new_client)
def _handle_client_reboot(self, req: Request): """ Handles a client reboot request :param req: Request containing the client id to reboot :return: None """ try: self._validator.validate(req.get_payload(), "api_client_reboot_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.update_gadget.uri}'") return try: self.send_client_reboot(req.get_payload()["id"]) except UnknownClientException: self._respond_with_error(req, "UnknownClientException", f"Nee client with the id: {req.get_payload()['id']} exists") except NoClientResponseException: self._respond_with_error(req, "NoClientResponseException", f"Client did not respond to reboot request") except ClientRebootError: self._respond_with_error(req, "ClientRebootError", f"Client could not be rebooted for some reason")
def _handle_heartbeat(self, req: Request): try: self._validator.validate(req.get_payload(), "bridge_heartbeat_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.heartbeat.uri}'") return rt_id = req.get_payload()["runtime_id"] self._delegate.handle_heartbeat(req.get_sender(), rt_id)
def _handle_check_bridge_for_update(self, req: Request): """ Checks whether the remote, the bridge is currently running on, is an older version :param req: empty request :return: None """ try: self._validator.validate(req.get_payload(), "api_empty_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at {ApiURIs.bridge_update_check.uri}") return encoder = ApiEncoder() try: updater = BridgeUpdateManager(os.getcwd()) bridge_meta = updater.check_for_update() except UpdateNotPossibleException: self._respond_with_error(req, "UpdateNotPossibleException", "bridge could not be updated") except NoUpdateAvailableException: self._respond_with_status(req, True, "Bridge is up to date") else: payload = encoder.encode_bridge_update_info(bridge_meta) req.respond(payload) return
def _handle_get_all_configs(self, req: Request): """ Responds with the names and descriptions of all available configs :param req: empty Request :return: None """ try: self._validator.validate(req.get_payload(), "api_empty_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.config_storage_get_all.uri}'") return manager = ClientConfigManager() config_names = manager.get_config_names() all_configs = {} self._logger.info("fetching all configs") for config in config_names: if not config == "Example": try: conf = manager.get_config(config) all_configs[config] = conf["description"] except ConfigDoesNotExistException: self._logger.error("congratz, something went wrong, abort, abort, return to the moon base") pass else: pass payload = {"configs": all_configs} req.respond(payload)
def _log_request(self, req: Request): short_json = json.dumps(req.get_payload()) if len(short_json) > 35: short_json = short_json[:35] + f"... + {len(short_json) - 35} bytes" auth_type = "No Auth" if req.get_auth() is None else req.get_auth().__class__.__name__[:-13] self._logger.info(f"Received request from '{req.get_sender()}' at '{req.get_path()}' " f"(Auth type: '{auth_type}'): {short_json}")
def receive(self, req: Request): if req.get_receiver() == self._network.get_hostname(): # Normal Request self._logger.info(f"Received Request at '{req.get_path()}'") elif req.get_receiver() is None: # Broadcast self._logger.info(f"Received Broadcast at '{req.get_path()}'") else: return req.respond(req.get_payload())
def _handle_update_gadget(self, req: Request): """ Handles a characteristic update request, for a gadget, from any foreign source :param req: Request containing the gadget update request :return: None """ try: self._validator.validate(req.get_payload(), "api_gadget_update_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.update_gadget.uri}'") return try: gadget_update_info = ApiDecoder().decode_gadget_update(req.get_payload()) except GadgetDecodeError: self._respond_with_error(req, "GadgetDecodeError", f"Gadget update decode error at '{ApiURIs.sync_client.uri}'") return client_id = req.get_sender() gadget_info = [x for x in self._delegate.get_gadget_info() if x.get_name() == req.get_payload()["id"]] if not gadget_info: self._respond_with_error(req, "GagdetDoesNeeExist", "Sadly, no gadget with the given id exists") return gadget = gadget_info[0] updated_characteristics = [x.id for x in gadget_update_info.characteristics] buf_characteristics = [x for x in gadget.get_characteristics() if x.get_type() in updated_characteristics] for c in buf_characteristics: value = [x.step_value for x in gadget_update_info.characteristics if x.id == c.get_type()][0] c.set_step_value(value) out_gadget = AnyGadget(gadget_update_info.id, req.get_sender(), buf_characteristics) self._logger.info(f"Updating {len(buf_characteristics)} gadget characteristics from '{client_id}'") self._delegate.handle_gadget_update(out_gadget)
def _handle_split_request(self, received_request: Request) -> Optional[Request]: req_payload = received_request.get_payload() id_str = str(received_request.get_session_id()) p_index = req_payload["package_index"] split_payload = req_payload["split_payload"] if p_index == 0: if "last_index" in req_payload: l_index = req_payload["last_index"] buf_json = { "start_req": received_request, "last_index": l_index, "payload_bits": [] } for i in range(l_index + 1): buf_json["payload_bits"].append(None) buf_json["payload_bits"][0] = split_payload self._part_data[id_str] = buf_json else: self._logger.error( "Received first block of split request without last_index") else: if id_str in self._part_data: req_data = self._part_data[id_str] req_data["payload_bits"][p_index] = split_payload if p_index >= req_data["last_index"] - 1: # TODO: Split requests should be recognized whether the last package is received last or not end_data = "" for str_data in req_data["payload_bits"]: if str_data is None: self._logger.error( "Detected missing data block in split request") break end_data += str_data try: end_data = end_data.replace("$*$", '"') json_data = json.loads(end_data) first_req: Request = req_data["start_req"] out_req = Request(first_req.get_path(), first_req.get_session_id(), first_req.get_sender(), first_req.get_receiver(), json_data) out_req.set_callback_method(first_req.get_callback()) del self._part_data[id_str] return out_req except json.decoder.JSONDecodeError: self._logger.error("Received illegal payload") else: self._logger.error( "Received a followup-block with no entry in storage") return None
def _handle_get_config(self, req: Request): """ Responds with the config for a given name, if there is none an error is returned :param req: Request containing the name of the requested config :return: None """ try: self._validator.validate(req.get_payload(), "api_config_delete_get") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.config_storage_get.uri}'") return try: name = req.get_payload()["name"] config = ClientConfigManager().get_config(name) payload = {"config": config} req.respond(payload) except ConfigDoesNotExistException as err: self._respond_with_error(req=req, err_type="ConfigDoesNotExistException", message=err.args[0])
def _handle_client_config_delete(self, req: Request): """ Handles a client config delete request :param req: Request containing the client config to delete :return: None """ try: self._validator.validate(req.get_payload(), "api_client_config_delete") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.update_gadget.uri}'") return
def _handle_delete_config(self, req: Request): """ Deletes the config for a given name, if there is no config, an error is returned :param req: Request containing name of the config :return: None """ try: self._validator.validate(req.get_payload(), "api_config_delete_get") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.config_storage_delete.uri}'") return name = req.get_payload()["name"] manager = ClientConfigManager() try: manager.delete_config_file(name) except ConfigDoesNotExistException as err: self._respond_with_error(req=req, err_type="ConfigDoesNotExistException", message=err.args[0]) return self._respond_with_status(req, True, "Config was deleted successfully")
def _handle_save_config(self, req: Request): """ Saves the given config or overwrites an already existing config :param req: Request containing the config :return: None """ try: self._validator.validate(req.get_payload(), "api_config_save") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at '{ApiURIs.config_storage_save.uri}'") return config = req.get_payload()["config"] overwrite = req.get_payload()["overwrite"] manager = ClientConfigManager() try: manager.write_config(config, overwrite=overwrite) except ConfigAlreadyExistsException as err: self._respond_with_error(req=req, err_type="ConfigAlreadyExistsException", message=err.args[0]) return self._respond_with_status(req, True, "Config was saved successfully")
def _handle_bridge_update(self, req: Request): """ Updates the Bridge to a newer version or another remote, remote has to be specified in request :param req: empty Request :return: None """ try: self._validator.validate(req.get_payload(), "api_empty_request") except ValidationError: self._respond_with_error(req, "ValidationError", f"Request validation error at {ApiURIs.bridge_update_execute.uri}") return try: updater = BridgeUpdateManager(os.getcwd()) updater.execute_update() self._respond_with_status(req, True, "Update was successful, rebooting system now...") updater.reboot() except UpdateNotSuccessfulException: self._respond_with_error(req, "UpdateNotSuccessfulException", "Update failed for some reason")
def handle(self, req: Request) -> Optional[Request]: req_payload = req.get_payload() if "package_index" in req_payload and "split_payload" in req_payload: return self._handle_split_request(req) else: return req
def send_request_split(self, path: str, receiver: str, payload: dict, part_max_size: int = 30, timeout: Optional[int] = None) -> Optional[Request]: """ Sends a request to all attached networks. The request will be split into individual parts with a maximum length. :param path: Path to send the request on :param receiver: Receiver for the Request :param payload: Payload to be send :param part_max_size: Maximum size in bytes for each individual payload chunk :param timeout: Maximum timeout to wait for an answer :return: The response if there is any """ if timeout is None: timeout = self._default_timeout req = Request(path, None, self._hostname, receiver, payload) session_id = req.get_session_id() path = req.get_path() sender = req.get_sender() receiver = req.get_receiver() payload_str = json.dumps(req.get_payload()) # Make string ready to be contained in json itself payload_str = payload_str.replace('"', "$*$") payload_len = len(payload_str) parts = [] start = 0 package_index = 0 while start < payload_len: end = start + part_max_size payload_part = payload_str[start:( end if end < payload_len else payload_len)] parts.append(payload_part) start = end last_index = len(parts) for payload_part in parts: out_dict = { "package_index": package_index, "split_payload": payload_part } if package_index == 0: out_dict["last_index"] = last_index out_req = Request(path, session_id, sender, receiver, out_dict) if package_index == last_index - 1: responses = self._send_request_obj(out_req, timeout, 1) if not responses: return None return responses[0] else: self._send_request_obj(out_req, 0, 0) package_index += 1 sleep(0.1) return None