]> git.proxmox.com Git - mirror_qemu.git/blame - python/qemu/qmp/qom_common.py
Python/aqmp: fix type definitions for mypy 0.920
[mirror_qemu.git] / python / qemu / qmp / qom_common.py
CommitLineData
c750c028
JS
1"""
2QOM Command abstractions.
3"""
4##
5# Copyright John Snow 2020, for Red Hat, Inc.
6# Copyright IBM, Corp. 2011
7#
8# Authors:
9# John Snow <jsnow@redhat.com>
10# Anthony Liguori <aliguori@amazon.com>
11#
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.
14#
15# Based on ./scripts/qmp/qom-[set|get|tree|list]
16##
17
18import argparse
19import os
20import sys
21from typing import (
22 Any,
23 Dict,
24 List,
25 Optional,
26 Type,
27 TypeVar,
28)
29
30from . import QEMUMonitorProtocol, QMPError
31
32
33# The following is needed only for a type alias.
34Subparsers = argparse._SubParsersAction # pylint: disable=protected-access
35
36
37class ObjectPropertyInfo:
38 """
39 Represents the return type from e.g. qom-list.
40 """
41 def __init__(self, name: str, type_: str,
42 description: Optional[str] = None,
43 default_value: Optional[object] = None):
44 self.name = name
45 self.type = type_
46 self.description = description
47 self.default_value = default_value
48
49 @classmethod
50 def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
51 """
52 Build an ObjectPropertyInfo from a Dict with an unknown shape.
53 """
54 assert value.keys() >= {'name', 'type'}
55 assert value.keys() <= {'name', 'type', 'description', 'default-value'}
56 return cls(value['name'], value['type'],
57 value.get('description'),
58 value.get('default-value'))
59
60 @property
61 def child(self) -> bool:
62 """Is this property a child property?"""
63 return self.type.startswith('child<')
64
65 @property
66 def link(self) -> bool:
67 """Is this property a link property?"""
68 return self.type.startswith('link<')
69
70
71CommandT = TypeVar('CommandT', bound='QOMCommand')
72
73
74class QOMCommand:
75 """
76 Represents a QOM sub-command.
77
78 :param args: Parsed arguments, as returned from parser.parse_args.
79 """
80 name: str
81 help: str
82
83 def __init__(self, args: argparse.Namespace):
84 if args.socket is None:
85 raise QMPError("No QMP socket path or address given")
86 self.qmp = QEMUMonitorProtocol(
87 QEMUMonitorProtocol.parse_address(args.socket)
88 )
89 self.qmp.connect()
90
91 @classmethod
92 def register(cls, subparsers: Subparsers) -> None:
93 """
94 Register this command with the argument parser.
95
96 :param subparsers: argparse subparsers object, from "add_subparsers".
97 """
98 subparser = subparsers.add_parser(cls.name, help=cls.help,
99 description=cls.help)
100 cls.configure_parser(subparser)
101
102 @classmethod
103 def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
104 """
105 Configure a parser with this command's arguments.
106
107 :param parser: argparse parser or subparser object.
108 """
109 default_path = os.environ.get('QMP_SOCKET')
110 parser.add_argument(
111 '--socket', '-s',
112 dest='socket',
113 action='store',
114 help='QMP socket path or address (addr:port).'
115 ' May also be set via QMP_SOCKET environment variable.',
116 default=default_path
117 )
118 parser.set_defaults(cmd_class=cls)
119
120 @classmethod
121 def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
122 """
123 Add the <path>.<proptery> positional argument to this command.
124
125 :param parser: The parser to add the argument to.
126 """
127 parser.add_argument(
128 'path_prop',
129 metavar='<path>.<property>',
130 action='store',
131 help="QOM path and property, separated by a period '.'"
132 )
133
134 def run(self) -> int:
135 """
136 Run this command.
137
138 :return: 0 on success, 1 otherwise.
139 """
140 raise NotImplementedError
141
142 def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
143 """
144 :return: a strongly typed list from the 'qom-list' command.
145 """
146 rsp = self.qmp.command('qom-list', path=path)
147 # qom-list returns List[ObjectPropertyInfo]
148 assert isinstance(rsp, list)
149 return [ObjectPropertyInfo.make(x) for x in rsp]
150
151 @classmethod
152 def command_runner(
153 cls: Type[CommandT],
154 args: argparse.Namespace
155 ) -> int:
156 """
157 Run a fully-parsed subcommand, with error-handling for the CLI.
158
5c02c865 159 :return: The return code from `run()`.
c750c028
JS
160 """
161 try:
162 cmd = cls(args)
163 return cmd.run()
164 except QMPError as err:
165 print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
166 return -1
167
168 @classmethod
169 def entry_point(cls) -> int:
170 """
171 Build this command's parser, parse arguments, and run the command.
172
173 :return: `run`'s return code.
174 """
175 parser = argparse.ArgumentParser(description=cls.help)
176 cls.configure_parser(parser)
177 args = parser.parse_args()
178 return cls.command_runner(args)