summaryrefslogtreecommitdiff
path: root/kvmd/plugins/msd/otg/storage.py
diff options
context:
space:
mode:
Diffstat (limited to 'kvmd/plugins/msd/otg/storage.py')
-rw-r--r--kvmd/plugins/msd/otg/storage.py98
1 files changed, 51 insertions, 47 deletions
diff --git a/kvmd/plugins/msd/otg/storage.py b/kvmd/plugins/msd/otg/storage.py
index b65e3b06..e56107ff 100644
--- a/kvmd/plugins/msd/otg/storage.py
+++ b/kvmd/plugins/msd/otg/storage.py
@@ -31,8 +31,6 @@ from typing import Optional
import aiofiles
import aiofiles.os
-from ....logging import get_logger
-
from .... import aiotools
from .... import aiohelpers
@@ -59,7 +57,7 @@ class Image(_Image):
self.__complete_path = os.path.join(self.__dir_path, f".__{file_name}.complete")
self.__adopted = False
- async def _update(self) -> None:
+ async def _reload(self) -> None: # Only for Storage() and set_complete()
# adopted используется в последующих проверках
self.__adopted = await aiotools.run_async(self.__is_adopted)
(complete, removable, (size, mod_ts)) = await asyncio.gather(
@@ -80,7 +78,7 @@ class Image(_Image):
path = self.path
while not os.path.ismount(path):
path = os.path.dirname(path)
- return (self.__storage._get_root_path() != path)
+ return (self.__storage._get_root_path() != path) # pylint: disable=protected-access
async def __is_complete(self) -> bool:
if self.__storage:
@@ -119,6 +117,7 @@ class Image(_Image):
assert self.__storage
try:
await aiofiles.os.remove(self.path)
+ self.__storage.images.pop(self.name, None)
except FileNotFoundError:
pass
except Exception:
@@ -136,45 +135,60 @@ class Image(_Image):
await aiofiles.os.remove(self.__complete_path)
except FileNotFoundError:
pass
- await self._update()
+ await self._reload()
[email protected](frozen=True)
-class StorageSpace:
[email protected](frozen=True, eq=False)
+class _Storage:
size: int
free: int
+ images: dict[str, Image] = dataclasses.field(init=False)
-class Storage:
+class Storage(_Storage):
def __init__(self, path: str, remount_cmd: list[str]) -> None:
+ super().__init__(0, 0)
self.__path = path
self.__remount_cmd = remount_cmd
+ self.__watchable_paths: (list[str] | None) = None
+ self.__images: (dict[str, Image] | None) = None
- def _get_root_path(self) -> str:
- return self.__path
+ @property
+ def images(self) -> dict[str, Image]:
+ assert self.__watchable_paths is not None
+ assert self.__images is not None
+ return self.__images
+
+ async def reload(self) -> None:
+ self.__watchable_paths = None
+ self.__images = {}
+
+ watchable_paths: list[str] = []
+ images: dict[str, Image] = {}
+ for (root_path, files) in (await aiotools.run_async(self.__walk)):
+ watchable_paths.append(root_path)
+ for path in files:
+ name = os.path.relpath(path, self.__path)
+ images[name] = await self.make_image_by_name(name)
+
+ await self.reload_size_only()
- async def get_watchable_paths(self) -> list[str]:
- return (await aiotools.run_async(self.__inner_get_watchable_paths))
+ self.__watchable_paths = watchable_paths
+ self.__images = images
- async def get_images(self) -> dict[str, Image]:
- return {
- name: (await self.make_image_by_name(name))
- for name in (await aiotools.run_async(self.__inner_get_images))
- }
+ async def reload_size_only(self) -> None:
+ st = os.statvfs(self.__path) # FIXME
+ object.__setattr__(self, "size", st.f_blocks * st.f_frsize)
+ object.__setattr__(self, "free", st.f_bavail * st.f_frsize)
- def __inner_get_watchable_paths(self) -> list[str]:
- return list(map(operator.itemgetter(0), self.__walk(with_files=False)))
+ def get_watchable_paths(self) -> list[str]:
+ assert self.__watchable_paths is not None
+ return list(self.__watchable_paths)
- def __inner_get_images(self) -> list[str]:
- return [
- os.path.relpath(path, self.__path) # == name
- for (_, files) in self.__walk(with_files=True)
- for path in files
- ]
+ def __walk(self) -> list[tuple[str, list[str]]]:
+ return list(self.__inner_walk(self.__path))
- def __walk(self, with_files: bool, root_path: (str | None)=None) -> Generator[tuple[str, list[str]], None, None]:
- if root_path is None:
- root_path = self.__path
+ def __inner_walk(self, root_path: str) -> Generator[tuple[str, list[str]], None, None]:
files: list[str] = []
with os.scandir(root_path) as dir_iter:
for item in sorted(dir_iter, key=operator.attrgetter("name")):
@@ -183,8 +197,8 @@ class Storage:
try:
if item.is_dir(follow_symlinks=False):
item.stat() # Проверяем, не сдохла ли смонтированная NFS
- yield from self.__walk(with_files, item.path)
- elif with_files and item.is_file(follow_symlinks=False):
+ yield from self.__inner_walk(item.path)
+ elif item.is_file(follow_symlinks=False):
files.append(item.path)
except Exception:
pass
@@ -195,7 +209,7 @@ class Storage:
async def make_image_by_name(self, name: str) -> Image:
assert name
path = os.path.join(self.__path, name)
- return (await self.__get_image(name, path, True))
+ return (await self.__make_image(name, path, True))
async def make_image_by_path(self, path: str) -> Image:
assert path
@@ -204,29 +218,19 @@ class Storage:
name = os.path.relpath(path, self.__path)
else:
name = os.path.basename(path)
- return (await self.__get_image(name, path, in_storage))
+ return (await self.__make_image(name, path, in_storage))
- async def __get_image(self, name: str, path: str, in_storage: bool) -> Image:
+ async def __make_image(self, name: str, path: str, in_storage: bool) -> Image:
assert name
assert path
image = Image(name, path, (self if in_storage else None))
- await image._update() # pylint: disable=protected-access
+ await image._reload() # pylint: disable=protected-access
return image
- # =====
+ def _get_root_path(self) -> str: # Only for Image()
+ return self.__path
- def get_space(self, fatal: bool) -> (StorageSpace | None):
- try:
- st = os.statvfs(self.__path)
- except Exception as err:
- if fatal:
- raise
- get_logger().warning("Can't get free space of filesystem %s: %s", self.__path, err)
- return None
- return StorageSpace(
- size=(st.f_blocks * st.f_frsize),
- free=(st.f_bavail * st.f_frsize),
- )
+ # =====
async def remount_rw(self, rw: bool, fatal: bool=True) -> None:
if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)):