diff options
author | Devaev Maxim <[email protected]> | 2018-07-09 03:27:13 +0000 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2018-07-09 03:27:13 +0000 |
commit | 1fe9b4276d28a269d2f6654137b92e21a4fedfb7 (patch) | |
tree | 28702a33dcfe7d7c466c90509f6d054f254a8f99 | |
parent | 957c987b796f8c558232c79154f522cfce472228 (diff) |
better msd api; refactoring
-rw-r--r-- | kvmd/kvmd/msd.py | 140 | ||||
-rw-r--r-- | kvmd/kvmd/server.py | 4 |
2 files changed, 81 insertions, 63 deletions
diff --git a/kvmd/kvmd/msd.py b/kvmd/kvmd/msd.py index 5553e8f9..4f750193 100644 --- a/kvmd/kvmd/msd.py +++ b/kvmd/kvmd/msd.py @@ -48,63 +48,73 @@ class IsBusyError(MassStorageError): super().__init__("Mass-storage is busy (write in progress)") -class MassStorageDeviceInfo(NamedTuple): +# ===== +class _HardwareInfo(NamedTuple): + manufacturer: str + product: str + serial: str + + +class _ImageInfo(NamedTuple): + name: str + size: int + complete: bool + + +class _MassStorageDeviceInfo(NamedTuple): path: str real: str size: int - image_name: str - image_complete: bool - manufacturer: str = "" - product: str = "" - serial: str = "" - - -_DISK_META_SIZE = 4096 -_DISK_META_MAGIC_SIZE = 16 -_DISK_META_IMAGE_NAME_SIZE = 256 -_DISK_META_PADS_SIZE = _DISK_META_SIZE - _DISK_META_IMAGE_NAME_SIZE - 1 - _DISK_META_MAGIC_SIZE * 8 -_DISK_META_FORMAT = ">%dL%dc?%dx%dL" % ( - _DISK_META_MAGIC_SIZE, - _DISK_META_IMAGE_NAME_SIZE, - _DISK_META_PADS_SIZE, - _DISK_META_MAGIC_SIZE, + hw: Optional[_HardwareInfo] + image: Optional[_ImageInfo] + + +_IMAGE_INFO_SIZE = 4096 +_IMAGE_INFO_MAGIC_SIZE = 16 +_IMAGE_INFO_IMAGE_NAME_SIZE = 256 +_IMAGE_INFO_PADS_SIZE = _IMAGE_INFO_SIZE - _IMAGE_INFO_IMAGE_NAME_SIZE - 1 - 8 - _IMAGE_INFO_MAGIC_SIZE * 8 +_IMAGE_INFO_FORMAT = ">%dL%dc?Q%dx%dL" % ( + _IMAGE_INFO_MAGIC_SIZE, + _IMAGE_INFO_IMAGE_NAME_SIZE, + _IMAGE_INFO_PADS_SIZE, + _IMAGE_INFO_MAGIC_SIZE, ) -_DISK_META_MAGIC = [0x1ACE1ACE] * _DISK_META_MAGIC_SIZE +_IMAGE_INFO_MAGIC = [0x1ACE1ACE] * _IMAGE_INFO_MAGIC_SIZE -def _make_disk_meta(image_name: str, image_complete: bool) -> bytes: +def _make_image_info_bytes(name: str, size: int, complete: bool) -> bytes: return struct.pack( - _DISK_META_FORMAT, - *_DISK_META_MAGIC, + _IMAGE_INFO_FORMAT, + *_IMAGE_INFO_MAGIC, *memoryview(( # type: ignore - image_name.encode("utf-8") - + b"\x00" * _DISK_META_IMAGE_NAME_SIZE - )[:_DISK_META_IMAGE_NAME_SIZE]).cast("c"), - image_complete, - *_DISK_META_MAGIC, + name.encode("utf-8") + + b"\x00" * _IMAGE_INFO_IMAGE_NAME_SIZE + )[:_IMAGE_INFO_IMAGE_NAME_SIZE]).cast("c"), + complete, + size, + *_IMAGE_INFO_MAGIC, ) -def _parse_disk_meta(data: bytes) -> Dict: - disk_meta = { - "image_name": "", - "image_complete": False, - } +def _parse_image_info_bytes(data: bytes) -> Optional[_ImageInfo]: try: - parsed = list(struct.unpack(_DISK_META_FORMAT, data)) + parsed = list(struct.unpack(_IMAGE_INFO_FORMAT, data)) except struct.error: pass else: - magic_begin = parsed[:_DISK_META_MAGIC_SIZE] - magic_end = parsed[-_DISK_META_MAGIC_SIZE:] - if magic_begin == magic_end == _DISK_META_MAGIC: - image_name_bytes = b"".join(parsed[_DISK_META_MAGIC_SIZE:_DISK_META_MAGIC_SIZE + _DISK_META_IMAGE_NAME_SIZE]) - disk_meta["image_name"] = image_name_bytes.decode("utf-8", errors="ignore").strip("\x00").strip() - disk_meta["image_complete"] = parsed[_DISK_META_MAGIC_SIZE + _DISK_META_IMAGE_NAME_SIZE] - return disk_meta - - -def explore_device(device_path: str) -> Optional[MassStorageDeviceInfo]: + magic_begin = parsed[:_IMAGE_INFO_MAGIC_SIZE] + magic_end = parsed[-_IMAGE_INFO_MAGIC_SIZE:] + if magic_begin == magic_end == _IMAGE_INFO_MAGIC: + image_name_bytes = b"".join(parsed[_IMAGE_INFO_MAGIC_SIZE:_IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_IMAGE_NAME_SIZE]) + return _ImageInfo( + name=image_name_bytes.decode("utf-8", errors="ignore").strip("\x00").strip(), + size=parsed[_IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_IMAGE_NAME_SIZE + 1], + complete=parsed[_IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_IMAGE_NAME_SIZE], + ) + return None + + +def _explore_device(device_path: str) -> Optional[_MassStorageDeviceInfo]: # udevadm info -a -p $(udevadm info -q path -n /dev/sda) ctx = pyudev.Context() @@ -115,22 +125,25 @@ def explore_device(device_path: str) -> Optional[MassStorageDeviceInfo]: size = device.attributes.asint("size") * 512 except KeyError: return None + + hw_info: Optional[_HardwareInfo] = None usb_device = device.find_parent("usb", "usb_device") + if usb_device: + hw_info = _HardwareInfo(**{ + attr: usb_device.attributes.asstring(attr).strip() + for attr in ["manufacturer", "product", "serial"] + }) with open(device_path, "rb") as device_file: - device_file.seek(size - _DISK_META_SIZE) - disk_meta = _parse_disk_meta(device_file.read()) + device_file.seek(size - _IMAGE_INFO_SIZE) + image_info = _parse_image_info_bytes(device_file.read()) - return MassStorageDeviceInfo( # type: ignore + return _MassStorageDeviceInfo( path=device_path, real=os.path.realpath(device_path), size=size, - **disk_meta, - **{ - attr: usb_device.attributes.asstring(attr).strip() - for attr in ["manufacturer", "product", "serial"] - if usb_device - }, + image=image_info, + hw=hw_info, ) @@ -145,6 +158,7 @@ def _operated_and_locked(method: Callable) -> Callable: return wrap +# ===== class MassStorageDevice: # pylint: disable=too-many-instance-attributes def __init__( self, @@ -159,7 +173,7 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes self.__write_meta = write_meta self.__loop = loop - self.__device_info: Optional[MassStorageDeviceInfo] = None + self.__device_info: Optional[_MassStorageDeviceInfo] = None self._lock = asyncio.Lock() self._device_file: Optional[aiofiles.base.AiofilesContextManager] = None self.__writed = 0 @@ -168,7 +182,7 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes if self._device_path: logger.info("Using %r as mass-storage device", self._device_path) try: - logger.info("Enabled metadata writing") + logger.info("Enabled image metadata writing") loop.run_until_complete(self.connect_to_kvm(no_delay=True)) except Exception as err: if isinstance(err, MassStorageError): @@ -199,12 +213,16 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes get_logger().info("Mass-storage device switched to Server") def get_state(self) -> Dict: + info = (self.__device_info._asdict() if self.__device_info else None) + if info: + info["hw"] = (info["hw"]._asdict() if info["hw"] else None) + info["image"] = (info["image"]._asdict() if info["image"] else None) return { "in_operate": bool(self._device_path), "connected_to": ("kvm" if self.__device_info else "server"), - "is_busy": bool(self._device_file), + "busy": bool(self._device_file), "writed": self.__writed, - "info": (self.__device_info._asdict() if self.__device_info else None), + "info": info, } async def cleanup(self) -> None: @@ -220,18 +238,18 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes self.__writed = 0 return self - async def write_image_meta(self, image_name: str, image_complete: bool) -> None: + async def write_image_info(self, name: str, complete: bool) -> None: async with self._lock: assert self._device_file assert self.__device_info if self.__write_meta: - if self.__device_info.size - self.__writed > _DISK_META_SIZE: - await self._device_file.seek(self.__device_info.size - _DISK_META_SIZE) - await self.__write_to_device_file(_make_disk_meta(image_name, image_complete)) + if self.__device_info.size - self.__writed > _IMAGE_INFO_SIZE: + await self._device_file.seek(self.__device_info.size - _IMAGE_INFO_SIZE) + await self.__write_to_device_file(_make_image_info_bytes(name, self.__writed, complete)) await self._device_file.seek(0) await self.__load_device_info() else: - get_logger().error("Can't write image meta because device is full") + get_logger().error("Can't write image info because device is full") async def write_image_chunk(self, chunk: bytes) -> int: async with self._lock: @@ -255,7 +273,7 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes await self.__loop.run_in_executor(None, os.fsync, self._device_file.fileno()) async def __load_device_info(self) -> None: - device_info = await self.__loop.run_in_executor(None, explore_device, self._device_path) + device_info = await self.__loop.run_in_executor(None, _explore_device, self._device_path) if not device_info: raise MassStorageError("Can't explore device %r" % (self._device_path)) self.__device_info = device_info diff --git a/kvmd/kvmd/server.py b/kvmd/kvmd/server.py index 62a13560..4574efd5 100644 --- a/kvmd/kvmd/server.py +++ b/kvmd/kvmd/server.py @@ -184,13 +184,13 @@ class Server: # pylint: disable=too-many-instance-attributes async with self.__msd: await self.__broadcast_event("msd_state", state="busy") # type: ignore logger.info("Writing image %r to mass-storage device ...", image_name) - await self.__msd.write_image_meta(image_name, False) + await self.__msd.write_image_info(image_name, False) while True: chunk = await field.read_chunk(self.__msd_chunk_size) if not chunk: break writed = await self.__msd.write_image_chunk(chunk) - await self.__msd.write_image_meta(image_name, True) + await self.__msd.write_image_info(image_name, True) await self.__broadcast_event("msd_state", state="free") # type: ignore finally: if writed != 0: |