diff --git a/mtda-www b/mtda-www index 4360cc74..a828be0a 100755 --- a/mtda-www +++ b/mtda-www @@ -26,7 +26,7 @@ import logging import functools from mtda.client import Client -from mtda.utils import Compression +from mtda.utils import BmapUtils, Compression import mtda.constants as CONSTS from mtda.console.remote import RemoteConsole from mtda.console.screen import ScreenOutput @@ -121,6 +121,9 @@ class RemoteCallHandler(BaseHandler): finally: self.finish() + async def post(self): + await self.get() + async def blocking_call(self, func, *args, **kwargs): """ Shorthand to run a blocking function in the application's @@ -681,6 +684,60 @@ class PowerToggleHandler(RemoteCallHandler): status = await self.blocking_call(target_toggle, session=sid) return 200, {"status": status} +# --------------------------------------------------------------------------- +# /storage-set-bmap +# --------------------------------------------------------------------------- + + +class StorageBmapHandler(RemoteCallHandler): + spec = {"post": { + "summary": "Set the bmap file corresponding to the current file", + "requestBody": { + "required": True, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "format": "binary" + }, + "session": { + "type": "string" + } + }, + "required": ["filename", "session"] + } + } + } + }, + "responses": { + "204": { + "description": "bmap file set" + }, + "400": { + "description": "Invalid input parameters" + } + }}} + + async def _handle_remote_request(self): + @remote_call + def storage_bmap_dict(mtda, bmap, session): + mtda.storage_bmap_dict(bmap, session=session) + + import xml.etree.ElementTree as ET + + sid = self.get_argument('session') + file_info = self.request.files.get('filename')[0] + file_content = file_info['body'].decode('utf-8') + filename = file_info['filename'] + + bmap_xml = ET.fromstring(file_content) + bmap = BmapUtils.parseBmap(bmap_xml, filename) + await self.blocking_call(storage_bmap_dict, bmap, session=sid) + return 204, None + # --------------------------------------------------------------------------- # /storage-open # --------------------------------------------------------------------------- @@ -719,7 +776,15 @@ class StorageOpenHandler(RemoteCallHandler): } }}} - async def _handle_remote_request(self, mtda): + async def _handle_remote_request(self): + @remote_call + def storage_compression(mtda, compr): + return mtda.storage_compression(compr) + + @remote_call + def storage_open(mtda, size, session): + return mtda.storage_open(size, session=session) + compr = CONSTS.IMAGE.RAW.value file = self.get_argument('file') try: @@ -729,11 +794,11 @@ class StorageOpenHandler(RemoteCallHandler): if file: logger.debug(f'file to be uploaded: {file}') compr = Compression.from_extension(file) - await self.blocking_call(mtda.storage_compression, compr) + await self.blocking_call(storage_compression, compr) sid = self.get_argument('session') zmq_socket = await self.blocking_call( - mtda.storage_open, size, session=sid + storage_open, size, session=sid ) self.application.settings['sockets'][sid] = zmq_socket return 204, None @@ -1166,6 +1231,7 @@ class OpenAPIHandler(RemoteCallHandler): "/keyboard-input": KeyboardInputHandler.spec, "/mouse-move": MouseEventHandler.spec, "/power-toggle": PowerToggleHandler.spec, + "/storage-set-bmap": StorageBmapHandler.spec, "/storage-open": StorageOpenHandler.spec, "/storage-close": StorageCloseHandler.spec, "/storage-commit": StorageCommitHandler.spec, @@ -1280,6 +1346,7 @@ class Service: (r"/keyboard-input", KeyboardInputHandler), (r"/mouse-move", MouseEventHandler), (r"/power-toggle", PowerToggleHandler), + (r"/storage-set-bmap", StorageBmapHandler), (r"/storage-close", StorageCloseHandler), (r"/storage-commit", StorageCommitHandler), (r"/storage-open", StorageOpenHandler), diff --git a/mtda/client.py b/mtda/client.py index 1d44c240..0471d235 100644 --- a/mtda/client.py +++ b/mtda/client.py @@ -20,7 +20,7 @@ import zstandard as zstd from mtda.main import MultiTenantDeviceAccess -from mtda.utils import Compression +from mtda.utils import Compression, BmapUtils import mtda.constants as CONSTS # Pyro @@ -216,7 +216,7 @@ def storage_write_image(self, path): bmap = ET.fromstring(bmap) print(f"Discovered bmap file '{bmap_path}'") - bmapDict = self.parseBmap(bmap, bmap_path) + bmapDict = BmapUtils.parseBmap(bmap, bmap_path) self._impl.storage_bmap_dict(bmapDict) image_size = bmapDict['ImageSize'] break @@ -244,36 +244,6 @@ def storage_write_image(self, path): self.storage_close() self._impl.storage_bmap_dict(None) - def parseBmap(self, bmap, bmap_path): - try: - bmapDict = {} - bmapDict["BlockSize"] = int( - bmap.find("BlockSize").text.strip()) - bmapDict["BlocksCount"] = int( - bmap.find("BlocksCount").text.strip()) - bmapDict["MappedBlocksCount"] = int( - bmap.find("MappedBlocksCount").text.strip()) - bmapDict["ImageSize"] = int( - bmap.find("ImageSize").text.strip()) - bmapDict["ChecksumType"] = \ - bmap.find("ChecksumType").text.strip() - bmapDict["BmapFileChecksum"] = \ - bmap.find("BmapFileChecksum").text.strip() - bmapDict["BlockMap"] = [] - for child in bmap.find("BlockMap").findall("Range"): - range = child.text.strip().split("-") - first = range[0] - last = range[0] if len(range) == 1 else range[1] - bmapDict["BlockMap"].append({ - "first": int(first), - "last": int(last), - "chksum": child.attrib["chksum"] - }) - except Exception: - print(f"Error parsing '{bmap_path}', probably not a bmap 2.0 file") - return None - return bmapDict - def start(self): return self._agent.start() diff --git a/mtda/templates/index.html b/mtda/templates/index.html index f85bad35..d15b7654 100644 --- a/mtda/templates/index.html +++ b/mtda/templates/index.html @@ -461,7 +461,7 @@ }); } uploadWindow = new WinBox("Upload", { - html: "