otg msd: notify about free space while uploading

This commit is contained in:
Devaev Maxim 2019-11-08 03:53:00 +03:00
parent 13dcbc0c62
commit fa40676136
3 changed files with 88 additions and 19 deletions

View File

@ -24,6 +24,7 @@ import os
import asyncio
import contextlib
import dataclasses
import time
from typing import List
from typing import Dict
@ -56,10 +57,10 @@ from .. import MsdImageExistsError
from .. import MsdIsBusyError
from .. import BaseMsd
from .drive import Drive
from . import fs
from . import helpers
from .helpers import remount_storage
from .helpers import unlock_drive
from .drive import Drive
# =====
@ -152,6 +153,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
self.__new_file: Optional[aiofiles.base.AiofilesContextManager] = None
self.__new_file_written = 0
self.__new_file_tick = 0.0
self.__changes_queue: asyncio.queues.Queue = asyncio.Queue()
@ -179,7 +181,12 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
for name in list(storage["images"]):
del storage["images"][name]["path"]
del storage["images"][name]["in_storage"]
storage["uploading"] = bool(self.__new_file)
if self.__new_file: # При загрузке файла показываем размер вручную
space = fs.get_fs_space(self.__storage_path, fatal=False)
if space:
storage.update(dataclasses.asdict(space))
vd: Optional[Dict] = None
if self.__state.vd:
@ -331,6 +338,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
except Exception:
pass
finally:
# Между закрытием файла и эвентом айнотифи состояние может быть не обновлено,
# так что форсим обновление вручную, чтобы получить актуальное состояние.
await self.__reload_state()
await self.__changes_queue.put(None)
@aiotools.atomic
@ -338,6 +348,11 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
assert self.__new_file
await aiotools.afile_write_now(self.__new_file, chunk)
self.__new_file_written += len(chunk)
now = time.time()
if self.__new_file_tick + 1 < now:
# Это нужно для ручного оповещения о свободном пространстве на диске, см. get_state()
self.__new_file_tick = now
await self.__changes_queue.put(None)
return self.__new_file_written
async def remove(self, name: str) -> None:
@ -469,7 +484,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
for name in os.listdir(self.__images_path):
path = os.path.join(self.__images_path, name)
if os.path.exists(path):
size = self.__get_file_size(path)
size = fs.get_file_size(path)
if size >= 0:
images[name] = _DriveImage(
name=name,
@ -478,10 +493,11 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
complete=self.__is_image_complete(name),
in_storage=True,
)
st = os.statvfs(self.__storage_path)
space = fs.get_fs_space(self.__storage_path, fatal=True)
assert space
return _StorageState(
size=(st.f_blocks * st.f_frsize),
free=(st.f_bavail * st.f_frsize),
size=space.size,
free=space.free,
images=images,
)
@ -494,7 +510,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
image = _DriveImage(
name=name,
path=path,
size=max(self.__get_file_size(path), 0),
size=max(fs.get_file_size(path), 0),
complete=(self.__is_image_complete(name) if in_storage else True),
in_storage=in_storage,
)
@ -506,13 +522,6 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
# =====
def __get_file_size(self, path: str) -> int:
try:
return os.path.getsize(path)
except Exception as err:
get_logger().warning("Can't get size of file %s: %s", path, err)
return -1
def __is_image_complete(self, name: str) -> bool:
return os.path.exists(os.path.join(self.__meta_path, name + ".complete"))
@ -527,7 +536,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
# =====
async def __remount_storage(self, rw: bool) -> None:
await remount_storage(self.__remount_cmd, rw)
await helpers.remount_storage(self.__remount_cmd, rw)
async def __unlock_drive(self) -> None:
await unlock_drive(self.__unlock_cmd)
await helpers.unlock_drive(self.__unlock_cmd)

View File

@ -0,0 +1,58 @@
# ========================================================================== #
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
# ========================================================================== #
import os
import dataclasses
from typing import Optional
from ....logging import get_logger
# =====
@dataclasses.dataclass(frozen=True)
class FsSpace:
size: int
free: int
# =====
def get_file_size(path: str) -> int:
try:
return os.path.getsize(path)
except Exception as err:
get_logger().warning("Can't get size of file %s: %s", path, err)
return -1
def get_fs_space(path: str, fatal: bool) -> Optional[FsSpace]:
try:
st = os.statvfs(path)
except Exception as err:
if fatal:
raise
get_logger().warning("Can't get free space of filesystem %s: %s", path, err)
return None
return FsSpace(
size=(st.f_blocks * st.f_frsize),
free=(st.f_bavail * st.f_frsize),
)

View File

@ -195,8 +195,10 @@ export function Msd() {
$("msd-emulate-cdrom-checkbox").checked = (__state.online && __state.features.cdrom && __state.drive.cdrom);
$("msd-new-image").style.display = (__image_file ? "block" : "none");
$("msd-uploading-progress").setAttribute("data-label", "Waiting for upload ...");
$("msd-uploading-progress-value").style.width = "0%";
if (!__upload_http) {
$("msd-uploading-progress").setAttribute("data-label", "Waiting for upload ...");
$("msd-uploading-progress-value").style.width = "0%";
}
$("msd-new-image-name").innerHTML = (__image_file ? __image_file.name : "");
$("msd-new-image-size").innerHTML = (__image_file ? __formatSize(__image_file.size) : "");