| """ |
| QEMU Object Model testing tools. |
| |
| usage: qom [-h] {set,get,list,tree,fuse} ... |
| |
| Query and manipulate QOM data |
| |
| optional arguments: |
| -h, --help show this help message and exit |
| |
| QOM commands: |
| {set,get,list,tree,fuse} |
| set Set a QOM property value |
| get Get a QOM property value |
| list List QOM properties at a given path |
| tree Show QOM tree from a given path |
| fuse Mount a QOM tree as a FUSE filesystem |
| """ |
| ## |
| # Copyright John Snow 2020, for Red Hat, Inc. |
| # Copyright IBM, Corp. 2011 |
| # |
| # Authors: |
| # John Snow <jsnow@redhat.com> |
| # Anthony Liguori <aliguori@amazon.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. |
| # |
| # Based on ./scripts/qmp/qom-[set|get|tree|list] |
| ## |
| |
| import argparse |
| |
| from qemu.qmp import ExecuteError |
| |
| from .qom_common import QOMCommand |
| |
| |
| try: |
| from .qom_fuse import QOMFuse |
| except ModuleNotFoundError as _err: |
| if _err.name != 'fuse': |
| raise |
| else: |
| assert issubclass(QOMFuse, QOMCommand) |
| |
| |
| class QOMSet(QOMCommand): |
| """ |
| QOM Command - Set a property to a given value. |
| |
| usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value> |
| |
| Set a QOM property value |
| |
| positional arguments: |
| <path>.<property> QOM path and property, separated by a period '.' |
| <value> new QOM property value |
| |
| 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. |
| """ |
| name = 'set' |
| help = 'Set a QOM property value' |
| |
| @classmethod |
| def configure_parser(cls, parser: argparse.ArgumentParser) -> None: |
| super().configure_parser(parser) |
| cls.add_path_prop_arg(parser) |
| parser.add_argument( |
| 'value', |
| metavar='<value>', |
| action='store', |
| help='new QOM property value' |
| ) |
| |
| def __init__(self, args: argparse.Namespace): |
| super().__init__(args) |
| self.path, self.prop = args.path_prop.rsplit('.', 1) |
| self.value = args.value |
| |
| def run(self) -> int: |
| rsp = self.qmp.cmd( |
| 'qom-set', |
| path=self.path, |
| property=self.prop, |
| value=self.value |
| ) |
| print(rsp) |
| return 0 |
| |
| |
| class QOMGet(QOMCommand): |
| """ |
| QOM Command - Get a property's current value. |
| |
| usage: qom-get [-h] [--socket SOCKET] <path>.<property> |
| |
| Get a QOM property value |
| |
| positional arguments: |
| <path>.<property> QOM path and property, separated by a period '.' |
| |
| 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. |
| """ |
| name = 'get' |
| help = 'Get a QOM property value' |
| |
| @classmethod |
| def configure_parser(cls, parser: argparse.ArgumentParser) -> None: |
| super().configure_parser(parser) |
| cls.add_path_prop_arg(parser) |
| |
| def __init__(self, args: argparse.Namespace): |
| super().__init__(args) |
| try: |
| tmp = args.path_prop.rsplit('.', 1) |
| except ValueError as err: |
| raise ValueError('Invalid format for <path>.<property>') from err |
| self.path = tmp[0] |
| self.prop = tmp[1] |
| |
| def run(self) -> int: |
| rsp = self.qmp.cmd( |
| 'qom-get', |
| path=self.path, |
| property=self.prop |
| ) |
| if isinstance(rsp, dict): |
| for key, value in rsp.items(): |
| print(f"{key}: {value}") |
| else: |
| print(rsp) |
| return 0 |
| |
| |
| class QOMList(QOMCommand): |
| """ |
| QOM Command - List the properties at a given path. |
| |
| usage: qom-list [-h] [--socket SOCKET] <path> |
| |
| List QOM properties at a given path |
| |
| positional arguments: |
| <path> QOM path |
| |
| 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. |
| """ |
| name = 'list' |
| help = 'List QOM properties at a given path' |
| |
| @classmethod |
| def configure_parser(cls, parser: argparse.ArgumentParser) -> None: |
| super().configure_parser(parser) |
| parser.add_argument( |
| 'path', |
| metavar='<path>', |
| action='store', |
| help='QOM path', |
| ) |
| |
| def __init__(self, args: argparse.Namespace): |
| super().__init__(args) |
| self.path = args.path |
| |
| def run(self) -> int: |
| rsp = self.qom_list(self.path) |
| for item in rsp: |
| if item.child: |
| print(f"{item.name}/") |
| elif item.link: |
| print(f"@{item.name}/") |
| else: |
| print(item.name) |
| return 0 |
| |
| |
| class QOMTree(QOMCommand): |
| """ |
| QOM Command - Show the full tree below a given path. |
| |
| usage: qom-tree [-h] [--socket SOCKET] [<path>] |
| |
| Show QOM tree from a given path |
| |
| positional arguments: |
| <path> QOM path |
| |
| 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. |
| """ |
| name = 'tree' |
| help = 'Show QOM tree from a given path' |
| |
| @classmethod |
| def configure_parser(cls, parser: argparse.ArgumentParser) -> None: |
| super().configure_parser(parser) |
| parser.add_argument( |
| 'path', |
| metavar='<path>', |
| action='store', |
| help='QOM path', |
| nargs='?', |
| default='/' |
| ) |
| |
| def __init__(self, args: argparse.Namespace): |
| super().__init__(args) |
| self.path = args.path |
| |
| def _list_node(self, path: str) -> None: |
| print(path) |
| items = self.qom_list(path) |
| for item in items: |
| if item.child: |
| continue |
| try: |
| rsp = self.qmp.cmd('qom-get', path=path, |
| property=item.name) |
| print(f" {item.name}: {rsp} ({item.type})") |
| except ExecuteError as err: |
| print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})") |
| print('') |
| for item in items: |
| if not item.child: |
| continue |
| if path == '/': |
| path = '' |
| self._list_node(f"{path}/{item.name}") |
| |
| def run(self) -> int: |
| self._list_node(self.path) |
| return 0 |
| |
| |
| def main() -> int: |
| """QOM script main entry point.""" |
| parser = argparse.ArgumentParser( |
| description='Query and manipulate QOM data' |
| ) |
| subparsers = parser.add_subparsers( |
| title='QOM commands', |
| dest='command' |
| ) |
| |
| for command in QOMCommand.__subclasses__(): |
| command.register(subparsers) |
| |
| args = parser.parse_args() |
| |
| if args.command is None: |
| parser.error('Command not specified.') |
| return 1 |
| |
| cmd_class = args.cmd_class |
| assert isinstance(cmd_class, type(QOMCommand)) |
| return cmd_class.command_runner(args) |