1""" 2QEMU Object Model testing tools. 3 4usage: qom [-h] {set,get,list,tree,fuse} ... 5 6Query and manipulate QOM data 7 8optional arguments: 9 -h, --help show this help message and exit 10 11QOM 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 33import argparse 34 35from qemu.qmp import ExecuteError 36 37from .qom_common import QOMCommand 38 39 40try: 41 from .qom_fuse import QOMFuse 42except ModuleNotFoundError as _err: 43 if _err.name != 'fuse': 44 raise 45else: 46 assert issubclass(QOMFuse, QOMCommand) 47 48 49class 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: 87 rsp = self.qmp.cmd( 88 'qom-set', 89 path=self.path, 90 property=self.prop, 91 value=self.value 92 ) 93 print(rsp) 94 return 0 95 96 97class 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: 132 rsp = self.qmp.cmd( 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 145class 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 191class 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: 234 rsp = self.qmp.cmd('qom-get', path=path, 235 property=item.name) 236 print(f" {item.name}: {rsp} ({item.type})") 237 except ExecuteError as err: 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 252def 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) 274