]>
Commit | Line | Data |
---|---|---|
c750c028 JS |
1 | """ |
2 | QEMU Object Model testing tools. | |
3 | ||
176c5490 | 4 | usage: qom [-h] {set,get,list,tree,fuse} ... |
c750c028 JS |
5 | |
6 | Query and manipulate QOM data | |
7 | ||
8 | optional arguments: | |
9 | -h, --help show this help message and exit | |
10 | ||
11 | QOM commands: | |
176c5490 | 12 | {set,get,list,tree,fuse} |
c750c028 JS |
13 | set Set a QOM property value |
14 | get Get a QOM property value | |
15 | list List QOM properties at a given path | |
16 | tree Show QOM tree from a given path | |
176c5490 | 17 | fuse Mount a QOM tree as a FUSE filesystem |
c750c028 JS |
18 | """ |
19 | ## | |
20 | # Copyright John Snow 2020, for Red Hat, Inc. | |
21 | # Copyright IBM, Corp. 2011 | |
22 | # | |
23 | # Authors: | |
24 | # John Snow <jsnow@redhat.com> | |
25 | # Anthony Liguori <aliguori@amazon.com> | |
26 | # | |
27 | # This work is licensed under the terms of the GNU GPL, version 2 or later. | |
28 | # See the COPYING file in the top-level directory. | |
29 | # | |
30 | # Based on ./scripts/qmp/qom-[set|get|tree|list] | |
31 | ## | |
32 | ||
33 | import argparse | |
34 | ||
37094b6d | 35 | from qemu.qmp import ExecuteError |
8d6cdc51 | 36 | |
c750c028 JS |
37 | from .qom_common import QOMCommand |
38 | ||
39 | ||
176c5490 JS |
40 | try: |
41 | from .qom_fuse import QOMFuse | |
00376d13 JS |
42 | except ModuleNotFoundError as _err: |
43 | if _err.name != 'fuse': | |
176c5490 JS |
44 | raise |
45 | else: | |
46 | assert issubclass(QOMFuse, QOMCommand) | |
47 | ||
48 | ||
c750c028 JS |
49 | class QOMSet(QOMCommand): |
50 | """ | |
51 | QOM Command - Set a property to a given value. | |
52 | ||
53 | usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value> | |
54 | ||
55 | Set a QOM property value | |
56 | ||
57 | positional arguments: | |
58 | <path>.<property> QOM path and property, separated by a period '.' | |
59 | <value> new QOM property value | |
60 | ||
61 | optional arguments: | |
62 | -h, --help show this help message and exit | |
63 | --socket SOCKET, -s SOCKET | |
64 | QMP socket path or address (addr:port). May also be | |
65 | set via QMP_SOCKET environment variable. | |
66 | """ | |
67 | name = 'set' | |
68 | help = 'Set a QOM property value' | |
69 | ||
70 | @classmethod | |
71 | def configure_parser(cls, parser: argparse.ArgumentParser) -> None: | |
72 | super().configure_parser(parser) | |
73 | cls.add_path_prop_arg(parser) | |
74 | parser.add_argument( | |
75 | 'value', | |
76 | metavar='<value>', | |
77 | action='store', | |
78 | help='new QOM property value' | |
79 | ) | |
80 | ||
81 | def __init__(self, args: argparse.Namespace): | |
82 | super().__init__(args) | |
83 | self.path, self.prop = args.path_prop.rsplit('.', 1) | |
84 | self.value = args.value | |
85 | ||
86 | def run(self) -> int: | |
684750ab | 87 | rsp = self.qmp.cmd( |
c750c028 JS |
88 | 'qom-set', |
89 | path=self.path, | |
90 | property=self.prop, | |
91 | value=self.value | |
92 | ) | |
93 | print(rsp) | |
94 | return 0 | |
95 | ||
96 | ||
97 | class QOMGet(QOMCommand): | |
98 | """ | |
99 | QOM Command - Get a property's current value. | |
100 | ||
101 | usage: qom-get [-h] [--socket SOCKET] <path>.<property> | |
102 | ||
103 | Get a QOM property value | |
104 | ||
105 | positional arguments: | |
106 | <path>.<property> QOM path and property, separated by a period '.' | |
107 | ||
108 | optional arguments: | |
109 | -h, --help show this help message and exit | |
110 | --socket SOCKET, -s SOCKET | |
111 | QMP socket path or address (addr:port). May also be | |
112 | set via QMP_SOCKET environment variable. | |
113 | """ | |
114 | name = 'get' | |
115 | help = 'Get a QOM property value' | |
116 | ||
117 | @classmethod | |
118 | def configure_parser(cls, parser: argparse.ArgumentParser) -> None: | |
119 | super().configure_parser(parser) | |
120 | cls.add_path_prop_arg(parser) | |
121 | ||
122 | def __init__(self, args: argparse.Namespace): | |
123 | super().__init__(args) | |
124 | try: | |
125 | tmp = args.path_prop.rsplit('.', 1) | |
126 | except ValueError as err: | |
127 | raise ValueError('Invalid format for <path>.<property>') from err | |
128 | self.path = tmp[0] | |
129 | self.prop = tmp[1] | |
130 | ||
131 | def run(self) -> int: | |
684750ab | 132 | rsp = self.qmp.cmd( |
c750c028 JS |
133 | 'qom-get', |
134 | path=self.path, | |
135 | property=self.prop | |
136 | ) | |
137 | if isinstance(rsp, dict): | |
138 | for key, value in rsp.items(): | |
139 | print(f"{key}: {value}") | |
140 | else: | |
141 | print(rsp) | |
142 | return 0 | |
143 | ||
144 | ||
145 | class QOMList(QOMCommand): | |
146 | """ | |
147 | QOM Command - List the properties at a given path. | |
148 | ||
149 | usage: qom-list [-h] [--socket SOCKET] <path> | |
150 | ||
151 | List QOM properties at a given path | |
152 | ||
153 | positional arguments: | |
154 | <path> QOM path | |
155 | ||
156 | optional arguments: | |
157 | -h, --help show this help message and exit | |
158 | --socket SOCKET, -s SOCKET | |
159 | QMP socket path or address (addr:port). May also be | |
160 | set via QMP_SOCKET environment variable. | |
161 | """ | |
162 | name = 'list' | |
163 | help = 'List QOM properties at a given path' | |
164 | ||
165 | @classmethod | |
166 | def configure_parser(cls, parser: argparse.ArgumentParser) -> None: | |
167 | super().configure_parser(parser) | |
168 | parser.add_argument( | |
169 | 'path', | |
170 | metavar='<path>', | |
171 | action='store', | |
172 | help='QOM path', | |
173 | ) | |
174 | ||
175 | def __init__(self, args: argparse.Namespace): | |
176 | super().__init__(args) | |
177 | self.path = args.path | |
178 | ||
179 | def run(self) -> int: | |
180 | rsp = self.qom_list(self.path) | |
181 | for item in rsp: | |
182 | if item.child: | |
183 | print(f"{item.name}/") | |
184 | elif item.link: | |
185 | print(f"@{item.name}/") | |
186 | else: | |
187 | print(item.name) | |
188 | return 0 | |
189 | ||
190 | ||
191 | class QOMTree(QOMCommand): | |
192 | """ | |
193 | QOM Command - Show the full tree below a given path. | |
194 | ||
195 | usage: qom-tree [-h] [--socket SOCKET] [<path>] | |
196 | ||
197 | Show QOM tree from a given path | |
198 | ||
199 | positional arguments: | |
200 | <path> QOM path | |
201 | ||
202 | optional arguments: | |
203 | -h, --help show this help message and exit | |
204 | --socket SOCKET, -s SOCKET | |
205 | QMP socket path or address (addr:port). May also be | |
206 | set via QMP_SOCKET environment variable. | |
207 | """ | |
208 | name = 'tree' | |
209 | help = 'Show QOM tree from a given path' | |
210 | ||
211 | @classmethod | |
212 | def configure_parser(cls, parser: argparse.ArgumentParser) -> None: | |
213 | super().configure_parser(parser) | |
214 | parser.add_argument( | |
215 | 'path', | |
216 | metavar='<path>', | |
217 | action='store', | |
218 | help='QOM path', | |
219 | nargs='?', | |
220 | default='/' | |
221 | ) | |
222 | ||
223 | def __init__(self, args: argparse.Namespace): | |
224 | super().__init__(args) | |
225 | self.path = args.path | |
226 | ||
227 | def _list_node(self, path: str) -> None: | |
228 | print(path) | |
229 | items = self.qom_list(path) | |
230 | for item in items: | |
231 | if item.child: | |
232 | continue | |
233 | try: | |
684750ab VSO |
234 | rsp = self.qmp.cmd('qom-get', path=path, |
235 | property=item.name) | |
c750c028 | 236 | print(f" {item.name}: {rsp} ({item.type})") |
8d6cdc51 | 237 | except ExecuteError as err: |
c750c028 JS |
238 | print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})") |
239 | print('') | |
240 | for item in items: | |
241 | if not item.child: | |
242 | continue | |
243 | if path == '/': | |
244 | path = '' | |
245 | self._list_node(f"{path}/{item.name}") | |
246 | ||
247 | def run(self) -> int: | |
248 | self._list_node(self.path) | |
249 | return 0 | |
250 | ||
251 | ||
252 | def main() -> int: | |
253 | """QOM script main entry point.""" | |
254 | parser = argparse.ArgumentParser( | |
255 | description='Query and manipulate QOM data' | |
256 | ) | |
257 | subparsers = parser.add_subparsers( | |
258 | title='QOM commands', | |
259 | dest='command' | |
260 | ) | |
261 | ||
262 | for command in QOMCommand.__subclasses__(): | |
263 | command.register(subparsers) | |
264 | ||
265 | args = parser.parse_args() | |
266 | ||
267 | if args.command is None: | |
268 | parser.error('Command not specified.') | |
269 | return 1 | |
270 | ||
271 | cmd_class = args.cmd_class | |
272 | assert isinstance(cmd_class, type(QOMCommand)) | |
273 | return cmd_class.command_runner(args) |