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