]>
git.proxmox.com Git - mirror_qemu.git/blob - python/qemu/utils/qom_fuse.py
8dcd59fcde61f98a50317fd728617bad8d31369d
2 QEMU Object Model FUSE filesystem tool
4 This script offers a simple FUSE filesystem within which the QOM tree
5 may be browsed, queried and edited using traditional shell tooling.
7 This script requires the 'fusepy' python package.
10 usage: qom-fuse [-h] [--socket SOCKET] <mount>
12 Mount a QOM tree as a FUSE filesystem
18 -h, --help show this help message and exit
19 --socket SOCKET, -s SOCKET
20 QMP socket path or address (addr:port). May also be
21 set via QMP_SOCKET environment variable.
24 # Copyright IBM, Corp. 2012
25 # Copyright (C) 2020 Red Hat, Inc.
28 # Anthony Liguori <aliguori@us.ibm.com>
29 # Markus Armbruster <armbru@redhat.com>
31 # This work is licensed under the terms of the GNU GPL, version 2 or later.
32 # See the COPYING file in the top-level directory.
36 from errno
import ENOENT
, EPERM
49 from fuse
import FUSE
, FuseOSError
, Operations
51 from qemu
.qmp
import ExecuteError
53 from .qom_common
import QOMCommand
56 fuse
.fuse_python_api
= (0, 2)
59 class QOMFuse(QOMCommand
, Operations
):
61 QOMFuse implements both fuse.Operations and QOMCommand.
63 Operations implements the FS, and QOMCommand implements the CLI command.
66 help = 'Mount a QOM tree as a FUSE filesystem'
70 def configure_parser(cls
, parser
: argparse
.ArgumentParser
) -> None:
71 super().configure_parser(parser
)
79 def __init__(self
, args
: argparse
.Namespace
):
80 super().__init
__(args
)
81 self
.mount
= args
.mount
82 self
.ino_map
: Dict
[str, int] = {}
86 print(f
"Mounting QOMFS to '{self.mount}'", file=sys
.stderr
)
87 self
.fuse
= FUSE(self
, self
.mount
, foreground
=True)
90 def get_ino(self
, path
: str) -> int:
91 """Get an inode number for a given QOM path."""
92 if path
in self
.ino_map
:
93 return self
.ino_map
[path
]
94 self
.ino_map
[path
] = self
.ino_count
96 return self
.ino_map
[path
]
98 def is_object(self
, path
: str) -> bool:
99 """Is the given QOM path an object?"""
106 def is_property(self
, path
: str) -> bool:
107 """Is the given QOM path a property?"""
108 path
, prop
= path
.rsplit('/', 1)
112 for item
in self
.qom_list(path
):
113 if item
.name
== prop
:
119 def is_link(self
, path
: str) -> bool:
120 """Is the given QOM path a link?"""
121 path
, prop
= path
.rsplit('/', 1)
125 for item
in self
.qom_list(path
):
126 if item
.name
== prop
and item
.link
:
132 def read(self
, path
: str, size
: int, offset
: int, fh
: IO
[bytes
]) -> bytes
:
133 if not self
.is_property(path
):
134 raise FuseOSError(ENOENT
)
136 path
, prop
= path
.rsplit('/', 1)
140 data
= str(self
.qmp
.command('qom-get', path
=path
, property=prop
))
141 data
+= '\n' # make values shell friendly
142 except ExecuteError
as err
:
143 raise FuseOSError(EPERM
) from err
145 if offset
> len(data
):
148 return bytes(data
[offset
:][:size
], encoding
='utf-8')
150 def readlink(self
, path
: str) -> Union
[bool, str]:
151 if not self
.is_link(path
):
153 path
, prop
= path
.rsplit('/', 1)
154 prefix
= '/'.join(['..'] * (len(path
.split('/')) - 1))
155 return prefix
+ str(self
.qmp
.command('qom-get', path
=path
,
158 def getattr(self
, path
: str,
159 fh
: Optional
[IO
[bytes
]] = None) -> Mapping
[str, object]:
160 if self
.is_link(path
):
162 'st_mode': 0o755 | stat
.S_IFLNK
,
163 'st_ino': self
.get_ino(path
),
173 elif self
.is_object(path
):
175 'st_mode': 0o755 | stat
.S_IFDIR
,
176 'st_ino': self
.get_ino(path
),
186 elif self
.is_property(path
):
188 'st_mode': 0o644 | stat
.S_IFREG
,
189 'st_ino': self
.get_ino(path
),
200 raise FuseOSError(ENOENT
)
203 def readdir(self
, path
: str, fh
: IO
[bytes
]) -> Iterator
[str]:
206 for item
in self
.qom_list(path
):