|  | """ | 
|  | 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) |