Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions mtda-www
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
34 changes: 2 additions & 32 deletions mtda/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down
51 changes: 42 additions & 9 deletions mtda/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@
});
}
uploadWindow = new WinBox("Upload", {
html: "<div id=dropzone>Drag and drop your file here</div><button id='upload' disabled></button>",
html: "<div id=dropzone>Drag and drop your files here (e.g. image + bmap)</div><button id='upload' disabled></button>",
class: ["no-full", "no-max", "modern"],
width: "600px",
height: "340px",
Expand Down Expand Up @@ -634,17 +634,35 @@
const upload = document.getElementById('upload');
const CHUNK_SIZE = 512 * 1024;
let selectedFile = null;
let selectedBmapFile = null;
let maySend = false;

$(function() {
$('#upload').bind('click', function() {
$.getJSON('./storage-open', {
async function storage_set_bmap() {
const formData = new FormData();
formData.append('filename', selectedBmapFile);
formData.append('session', localStorage.getItem('session'));

await fetch('./storage-set-bmap', {
method: 'POST',
body: formData
});
}

async function storage_open() {
await new Promise(done => $.getJSON('./storage-open', {
file: selectedFile.name,
size: selectedFile.size,
session: localStorage.getItem('session')
}, function(data) {
// do nothing
});
}, function (data) { done(); }));
}

$(function() {
$('#upload').bind('click', function() {
if (selectedBmapFile) {
storage_set_bmap().then(storage_open());
} else {
storage_open();
}
return false;
});
});
Expand Down Expand Up @@ -843,11 +861,26 @@

dropzone.addEventListener('drop', (event) => {
const files = event.dataTransfer.files;
if (files.length) {
selectedFile = files[0];
selectedFile = null;
selectedBmapFile = null;
for (const f of files) {
if (f.name.endsWith('.bmap'))
selectedBmapFile = f;
else
selectedFile = f;
}
if (selectedFile && selectedBmapFile) {
console.log(`will upload ${selectedFile.name} with bmap ${selectedBmapFile.name}`);
dropzone.textContent = `${selectedFile.name} (+ bmap)`;
upload.disabled = false;
} else if (selectedFile) {
console.log('will upload '+selectedFile.name);
dropzone.textContent = `${selectedFile.name}`;
upload.disabled = false;
} else if (selectedBmapFile) {
console.log('only bmap file provided '+selectedBmapFile.name);
dropzone.textContent = `Only bmap file ${selectedBmapFile.name} provided`;
upload.disabled = true;
}
});

Expand Down
32 changes: 32 additions & 0 deletions mtda/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@
import mtda.constants as CONSTS


class BmapUtils:
def parseBmap(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


class Compression:
def from_extension(path):
if path.endswith(".bz2"):
Expand Down
Loading