Source code for qemu.qmp.qom_fuse

"""
QEMU Object Model FUSE filesystem tool

This script offers a simple FUSE filesystem within which the QOM tree
may be browsed, queried and edited using traditional shell tooling.

This script requires the 'fusepy' python package.


usage: qom-fuse [-h] [--socket SOCKET] <mount>

Mount a QOM tree as a FUSE filesystem

positional arguments:
  <mount>               Mount point

optional arguments:
  -h, --help            show this help message and exit
  --socket SOCKET, -s SOCKET
                        QMP socket path or address (addr:port). May also be
                        set via QMP_SOCKET environment variable.
"""
##
# Copyright IBM, Corp. 2012
# Copyright (C) 2020 Red Hat, Inc.
#
# Authors:
#  Anthony Liguori   <aliguori@us.ibm.com>
#  Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
##

import argparse
from errno import ENOENT, EPERM
import stat
import sys
from typing import (
    IO,
    Dict,
    Iterator,
    Mapping,
    Optional,
    Union,
)

import fuse
from fuse import FUSE, FuseOSError, Operations

from . import QMPResponseError
from .qom_common import QOMCommand


fuse.fuse_python_api = (0, 2)


[docs]class QOMFuse(QOMCommand, Operations): """ QOMFuse implements both fuse.Operations and QOMCommand. Operations implements the FS, and QOMCommand implements the CLI command. """ name = 'fuse' help = 'Mount a QOM tree as a FUSE filesystem' fuse: FUSE
[docs] @classmethod def configure_parser(cls, parser: argparse.ArgumentParser) -> None: super().configure_parser(parser) parser.add_argument( 'mount', metavar='<mount>', action='store', help="Mount point", )
def __init__(self, args: argparse.Namespace): super().__init__(args) self.mount = args.mount self.ino_map: Dict[str, int] = {} self.ino_count = 1
[docs] def run(self) -> int: print(f"Mounting QOMFS to '{self.mount}'", file=sys.stderr) self.fuse = FUSE(self, self.mount, foreground=True) return 0
[docs] def get_ino(self, path: str) -> int: """Get an inode number for a given QOM path.""" if path in self.ino_map: return self.ino_map[path] self.ino_map[path] = self.ino_count self.ino_count += 1 return self.ino_map[path]
[docs] def is_object(self, path: str) -> bool: """Is the given QOM path an object?""" try: self.qom_list(path) return True except QMPResponseError: return False
[docs] def is_property(self, path: str) -> bool: """Is the given QOM path a property?""" path, prop = path.rsplit('/', 1) if path == '': path = '/' try: for item in self.qom_list(path): if item.name == prop: return True return False except QMPResponseError: return False
[docs] def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes: if not self.is_property(path): raise FuseOSError(ENOENT) path, prop = path.rsplit('/', 1) if path == '': path = '/' try: data = str(self.qmp.command('qom-get', path=path, property=prop)) data += '\n' # make values shell friendly except QMPResponseError as err: raise FuseOSError(EPERM) from err if offset > len(data): return b'' return bytes(data[offset:][:size], encoding='utf-8')
[docs] def getattr(self, path: str, fh: Optional[IO[bytes]] = None) -> Mapping[str, object]: if self.is_link(path): value = { 'st_mode': 0o755 | stat.S_IFLNK, 'st_ino': self.get_ino(path), 'st_dev': 0, 'st_nlink': 2, 'st_uid': 1000, 'st_gid': 1000, 'st_size': 4096, 'st_atime': 0, 'st_mtime': 0, 'st_ctime': 0 } elif self.is_object(path): value = { 'st_mode': 0o755 | stat.S_IFDIR, 'st_ino': self.get_ino(path), 'st_dev': 0, 'st_nlink': 2, 'st_uid': 1000, 'st_gid': 1000, 'st_size': 4096, 'st_atime': 0, 'st_mtime': 0, 'st_ctime': 0 } elif self.is_property(path): value = { 'st_mode': 0o644 | stat.S_IFREG, 'st_ino': self.get_ino(path), 'st_dev': 0, 'st_nlink': 1, 'st_uid': 1000, 'st_gid': 1000, 'st_size': 4096, 'st_atime': 0, 'st_mtime': 0, 'st_ctime': 0 } else: raise FuseOSError(ENOENT) return value
[docs] def readdir(self, path: str, fh: IO[bytes]) -> Iterator[str]: yield '.' yield '..' for item in self.qom_list(path): yield item.name