mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 17:20:30 +08:00
async msd image
This commit is contained in:
parent
27f38ef086
commit
74a19e40da
2
PKGBUILD
2
PKGBUILD
@ -43,7 +43,7 @@ depends=(
|
|||||||
"python<3.11"
|
"python<3.11"
|
||||||
python-yaml
|
python-yaml
|
||||||
"python-aiohttp>=3.7.4.post0-1.1"
|
"python-aiohttp>=3.7.4.post0-1.1"
|
||||||
python-aiofiles
|
"python-aiofiles>=23.1.0-1"
|
||||||
python-passlib
|
python-passlib
|
||||||
python-pyotp
|
python-pyotp
|
||||||
python-qrcode
|
python-qrcode
|
||||||
|
|||||||
@ -399,7 +399,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
async def __STORAGE_create_new_image(self, name: str) -> Image: # pylint: disable=invalid-name
|
async def __STORAGE_create_new_image(self, name: str) -> Image: # pylint: disable=invalid-name
|
||||||
assert self.__state.storage
|
assert self.__state.storage
|
||||||
image = self.__storage.get_image_by_name(name)
|
image = await self.__storage.get_image_by_name(name)
|
||||||
if image.name in self.__state.storage.images or (await image.exists()):
|
if image.name in self.__state.storage.images or (await image.exists()):
|
||||||
raise MsdImageExistsError()
|
raise MsdImageExistsError()
|
||||||
return image
|
return image
|
||||||
@ -461,7 +461,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
|
|||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
async with self.__state._lock: # pylint: disable=protected-access
|
async with self.__state._lock: # pylint: disable=protected-access
|
||||||
try:
|
try:
|
||||||
drive_state = self.__get_drive_state()
|
drive_state = await self.__get_drive_state()
|
||||||
|
|
||||||
if self.__state.vd is None and drive_state.image is None:
|
if self.__state.vd is None and drive_state.image is None:
|
||||||
# Если только что включились и образ не подключен - попробовать
|
# Если только что включились и образ не подключен - попробовать
|
||||||
@ -500,7 +500,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
|
|||||||
async def __setup_initial(self) -> None:
|
async def __setup_initial(self) -> None:
|
||||||
if self.__initial_image:
|
if self.__initial_image:
|
||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
image = self.__storage.get_image_by_name(self.__initial_image)
|
image = await self.__storage.get_image_by_name(self.__initial_image)
|
||||||
if (await image.exists()):
|
if (await image.exists()):
|
||||||
logger.info("Setting up initial image %r ...", self.__initial_image)
|
logger.info("Setting up initial image %r ...", self.__initial_image)
|
||||||
try:
|
try:
|
||||||
@ -524,10 +524,10 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
|
|||||||
images=images,
|
images=images,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __get_drive_state(self) -> _DriveState:
|
async def __get_drive_state(self) -> _DriveState:
|
||||||
path = self.__drive.get_image_path()
|
path = self.__drive.get_image_path()
|
||||||
return _DriveState(
|
return _DriveState(
|
||||||
image=(self.__storage.get_image_by_path(path) if path else None),
|
image=((await self.__storage.get_image_by_path(path)) if path else None),
|
||||||
cdrom=self.__drive.get_cdrom_flag(),
|
cdrom=self.__drive.get_cdrom_flag(),
|
||||||
rw=self.__drive.get_rw_flag(),
|
rw=self.__drive.get_rw_flag(),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import asyncio
|
||||||
import operator
|
import operator
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ from .. import MsdError
|
|||||||
class _Image:
|
class _Image:
|
||||||
name: str
|
name: str
|
||||||
path: str
|
path: str
|
||||||
in_storage: bool = dataclasses.field(init=False)
|
in_storage: bool = dataclasses.field(init=False, compare=False)
|
||||||
complete: bool = dataclasses.field(init=False, compare=False)
|
complete: bool = dataclasses.field(init=False, compare=False)
|
||||||
removable: bool = dataclasses.field(init=False, compare=False)
|
removable: bool = dataclasses.field(init=False, compare=False)
|
||||||
size: int = dataclasses.field(init=False, compare=False)
|
size: int = dataclasses.field(init=False, compare=False)
|
||||||
@ -56,39 +57,55 @@ class Image(_Image):
|
|||||||
self.__storage = storage
|
self.__storage = storage
|
||||||
(self.__dir_path, file_name) = os.path.split(path)
|
(self.__dir_path, file_name) = os.path.split(path)
|
||||||
self.__complete_path = os.path.join(self.__dir_path, f".__{file_name}.complete")
|
self.__complete_path = os.path.join(self.__dir_path, f".__{file_name}.complete")
|
||||||
self.__adopted = (storage._is_adopted(self) if storage else True)
|
self.__adopted = False
|
||||||
|
|
||||||
@property
|
async def _update(self) -> None:
|
||||||
def in_storage(self) -> bool:
|
# adopted используется в последующих проверках
|
||||||
return bool(self.__storage)
|
self.__adopted = await aiotools.run_async(self.__is_adopted)
|
||||||
|
(complete, removable, (size, mod_ts)) = await asyncio.gather(
|
||||||
|
self.__is_complete(),
|
||||||
|
self.__is_removable(),
|
||||||
|
self.__get_stat(),
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "complete", complete)
|
||||||
|
object.__setattr__(self, "removable", removable)
|
||||||
|
object.__setattr__(self, "size", size)
|
||||||
|
object.__setattr__(self, "mod_ts", mod_ts)
|
||||||
|
|
||||||
@property
|
def __is_adopted(self) -> bool:
|
||||||
def complete(self) -> bool:
|
# True, если образ находится вне хранилища
|
||||||
|
# или в другой точке монтирования под ним
|
||||||
|
if self.__storage is None:
|
||||||
|
return True
|
||||||
|
path = self.path
|
||||||
|
while not os.path.ismount(path):
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
return (self.__storage.get_root_path() != path)
|
||||||
|
|
||||||
|
async def __is_complete(self) -> bool:
|
||||||
if self.__storage:
|
if self.__storage:
|
||||||
return os.path.exists(self.__complete_path)
|
return (await aiofiles.os.path.exists(self.__complete_path))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
async def __is_removable(self) -> bool:
|
||||||
def removable(self) -> bool:
|
|
||||||
if not self.__storage:
|
if not self.__storage:
|
||||||
return False
|
return False
|
||||||
if not self.__adopted:
|
if not self.__adopted:
|
||||||
return True
|
return True
|
||||||
return os.access(self.__dir_path, os.W_OK)
|
return (await aiofiles.os.access(self.__dir_path, os.W_OK)) # type: ignore
|
||||||
|
|
||||||
|
async def __get_stat(self) -> tuple[int, float]:
|
||||||
|
try:
|
||||||
|
st = (await aiofiles.os.stat(self.path))
|
||||||
|
return (st.st_size, st.st_mtime)
|
||||||
|
except Exception:
|
||||||
|
return (0, 0.0)
|
||||||
|
|
||||||
|
# =====
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self) -> int:
|
def in_storage(self) -> bool:
|
||||||
try:
|
return bool(self.__storage)
|
||||||
return os.stat(self.path).st_size
|
|
||||||
except Exception:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mod_ts(self) -> float:
|
|
||||||
try:
|
|
||||||
return os.stat(self.path).st_mtime
|
|
||||||
except Exception:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
async def exists(self) -> bool:
|
async def exists(self) -> bool:
|
||||||
return (await aiofiles.os.path.exists(self.path))
|
return (await aiofiles.os.path.exists(self.path))
|
||||||
@ -119,6 +136,7 @@ class Image(_Image):
|
|||||||
await aiofiles.os.remove(self.__complete_path)
|
await aiofiles.os.remove(self.__complete_path)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
await self._update()
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
@ -132,22 +150,27 @@ class Storage:
|
|||||||
self.__path = path
|
self.__path = path
|
||||||
self.__remount_cmd = remount_cmd
|
self.__remount_cmd = remount_cmd
|
||||||
|
|
||||||
|
def get_root_path(self) -> str:
|
||||||
|
return self.__path
|
||||||
|
|
||||||
async def get_watchable_paths(self) -> list[str]:
|
async def get_watchable_paths(self) -> list[str]:
|
||||||
return (await aiotools.run_async(self.__get_watchable_paths))
|
return (await aiotools.run_async(self.__inner_get_watchable_paths))
|
||||||
|
|
||||||
async def get_images(self) -> dict[str, Image]:
|
async def get_images(self) -> dict[str, Image]:
|
||||||
return (await aiotools.run_async(self.__get_images))
|
return {
|
||||||
|
name: (await self.get_image_by_name(name))
|
||||||
|
for name in (await aiotools.run_async(self.__inner_get_images))
|
||||||
|
}
|
||||||
|
|
||||||
def __get_watchable_paths(self) -> list[str]:
|
def __inner_get_watchable_paths(self) -> list[str]:
|
||||||
return list(map(operator.itemgetter(0), self.__walk(with_files=False)))
|
return list(map(operator.itemgetter(0), self.__walk(with_files=False)))
|
||||||
|
|
||||||
def __get_images(self) -> dict[str, Image]:
|
def __inner_get_images(self) -> list[str]:
|
||||||
images: dict[str, Image] = {}
|
return [
|
||||||
for (_, files) in self.__walk(with_files=True):
|
os.path.relpath(path, self.__path) # == name
|
||||||
for path in files:
|
for (_, files) in self.__walk(with_files=True)
|
||||||
name = os.path.relpath(path, self.__path)
|
for path in files
|
||||||
images[name] = self.get_image_by_name(name)
|
]
|
||||||
return images
|
|
||||||
|
|
||||||
def __walk(self, with_files: bool, root_path: (str | None)=None) -> Generator[tuple[str, list[str]], None, None]:
|
def __walk(self, with_files: bool, root_path: (str | None)=None) -> Generator[tuple[str, list[str]], None, None]:
|
||||||
if root_path is None:
|
if root_path is None:
|
||||||
@ -169,24 +192,26 @@ class Storage:
|
|||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
def get_image_by_name(self, name: str) -> Image:
|
async def get_image_by_name(self, name: str) -> Image:
|
||||||
assert name
|
assert name
|
||||||
path = os.path.join(self.__path, name)
|
path = os.path.join(self.__path, name)
|
||||||
return self.__get_image(name, path, True)
|
return (await self.__get_image(name, path, True))
|
||||||
|
|
||||||
def get_image_by_path(self, path: str) -> Image:
|
async def get_image_by_path(self, path: str) -> Image:
|
||||||
assert path
|
assert path
|
||||||
in_storage = (os.path.commonpath([self.__path, path]) == self.__path)
|
in_storage = (os.path.commonpath([self.__path, path]) == self.__path)
|
||||||
if in_storage:
|
if in_storage:
|
||||||
name = os.path.relpath(path, self.__path)
|
name = os.path.relpath(path, self.__path)
|
||||||
else:
|
else:
|
||||||
name = os.path.basename(path)
|
name = os.path.basename(path)
|
||||||
return self.__get_image(name, path, in_storage)
|
return (await self.__get_image(name, path, in_storage))
|
||||||
|
|
||||||
def __get_image(self, name: str, path: str, in_storage: bool) -> Image:
|
async def __get_image(self, name: str, path: str, in_storage: bool) -> Image:
|
||||||
assert name
|
assert name
|
||||||
assert path
|
assert path
|
||||||
return Image(name, path, (self if in_storage else None))
|
image = Image(name, path, (self if in_storage else None))
|
||||||
|
await image._update() # pylint: disable=protected-access
|
||||||
|
return image
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
@ -203,16 +228,6 @@ class Storage:
|
|||||||
free=(st.f_bavail * st.f_frsize),
|
free=(st.f_bavail * st.f_frsize),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _is_adopted(self, image: Image) -> bool:
|
|
||||||
# True, если образ находится вне хранилища
|
|
||||||
# или в другой точке монтирования под ним
|
|
||||||
if not image.in_storage:
|
|
||||||
return True
|
|
||||||
path = image.path
|
|
||||||
while not os.path.ismount(path):
|
|
||||||
path = os.path.dirname(path)
|
|
||||||
return (self.__path != path)
|
|
||||||
|
|
||||||
async def remount_rw(self, rw: bool, fatal: bool=True) -> None:
|
async def remount_rw(self, rw: bool, fatal: bool=True) -> None:
|
||||||
if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)):
|
if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)):
|
||||||
if fatal:
|
if fatal:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user