2 QOM Command abstractions.
5 # Copyright John Snow 2020, for Red Hat, Inc.
6 # Copyright IBM, Corp. 2011
12 # This work is licensed under the terms of the GNU GPL, version 2 or later.
13 # See the COPYING file in the top-level directory.
15 # Based on ./scripts/qmp/qom-[set|get|tree|list]
30 from qemu.aqmp import QMPError
31 from qemu.aqmp.legacy import QEMUMonitorProtocol
34 class ObjectPropertyInfo:
36 Represents the return type from e.g. qom-list.
38 def __init__(self, name: str, type_: str,
39 description: Optional[str] = None,
40 default_value: Optional[object] = None):
43 self.description = description
44 self.default_value = default_value
47 def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
49 Build an ObjectPropertyInfo from a Dict with an unknown shape.
51 assert value.keys() >= {'name', 'type'}
52 assert value.keys() <= {'name', 'type', 'description', 'default-value'}
53 return cls(value['name'], value['type'],
54 value.get('description'),
55 value.get('default-value'))
58 def child(self) -> bool:
59 """Is this property a child property?"""
60 return self.type.startswith('child<')
63 def link(self) -> bool:
64 """Is this property a link property?"""
65 return self.type.startswith('link<')
68 CommandT = TypeVar('CommandT', bound='QOMCommand')
73 Represents a QOM sub-command.
75 :param args: Parsed arguments, as returned from parser.parse_args.
80 def __init__(self, args: argparse.Namespace):
81 if args.socket is None:
82 raise QMPError("No QMP socket path or address given")
83 self.qmp = QEMUMonitorProtocol(
84 QEMUMonitorProtocol.parse_address(args.socket)
89 def register(cls, subparsers: Any) -> None:
91 Register this command with the argument parser.
93 :param subparsers: argparse subparsers object, from "add_subparsers".
95 subparser = subparsers.add_parser(cls.name, help=cls.help,
97 cls.configure_parser(subparser)
100 def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
102 Configure a parser with this command's arguments.
104 :param parser: argparse parser or subparser object.
106 default_path = os.environ.get('QMP_SOCKET')
111 help='QMP socket path or address (addr:port).'
112 ' May also be set via QMP_SOCKET environment variable.',
115 parser.set_defaults(cmd_class=cls)
118 def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
120 Add the <path>.<proptery> positional argument to this command.
122 :param parser: The parser to add the argument to.
126 metavar='<path>.<property>',
128 help="QOM path and property, separated by a period '.'"
131 def run(self) -> int:
135 :return: 0 on success, 1 otherwise.
137 raise NotImplementedError
139 def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
141 :return: a strongly typed list from the 'qom-list' command.
143 rsp = self.qmp.command('qom-list', path=path)
144 # qom-list returns List[ObjectPropertyInfo]
145 assert isinstance(rsp, list)
146 return [ObjectPropertyInfo.make(x) for x in rsp]
151 args: argparse.Namespace
154 Run a fully-parsed subcommand, with error-handling for the CLI.
156 :return: The return code from `run()`.
161 except QMPError as err:
162 print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
166 def entry_point(cls) -> int:
168 Build this command's parser, parse arguments, and run the command.
170 :return: `run`'s return code.
172 parser = argparse.ArgumentParser(description=cls.help)
173 cls.configure_parser(parser)
174 args = parser.parse_args()
175 return cls.command_runner(args)