qom.py (7580B)
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 . import QMPResponseError 36from .qom_common import QOMCommand 37 38 39try: 40 from .qom_fuse import QOMFuse 41except ModuleNotFoundError as _err: 42 if _err.name != 'fuse': 43 raise 44else: 45 assert issubclass(QOMFuse, QOMCommand) 46 47 48class 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.command( 87 'qom-set', 88 path=self.path, 89 property=self.prop, 90 value=self.value 91 ) 92 print(rsp) 93 return 0 94 95 96class 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.command( 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 144class 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 190class 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_node(self, path: str) -> None: 227 print(path) 228 items = self.qom_list(path) 229 for item in items: 230 if item.child: 231 continue 232 try: 233 rsp = self.qmp.command('qom-get', path=path, 234 property=item.name) 235 print(f" {item.name}: {rsp} ({item.type})") 236 except QMPResponseError as err: 237 print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})") 238 print('') 239 for item in items: 240 if not item.child: 241 continue 242 if path == '/': 243 path = '' 244 self._list_node(f"{path}/{item.name}") 245 246 def run(self) -> int: 247 self._list_node(self.path) 248 return 0 249 250 251def main() -> int: 252 """QOM script main entry point.""" 253 parser = argparse.ArgumentParser( 254 description='Query and manipulate QOM data' 255 ) 256 subparsers = parser.add_subparsers( 257 title='QOM commands', 258 dest='command' 259 ) 260 261 for command in QOMCommand.__subclasses__(): 262 command.register(subparsers) 263 264 args = parser.parse_args() 265 266 if args.command is None: 267 parser.error('Command not specified.') 268 return 1 269 270 cmd_class = args.cmd_class 271 assert isinstance(cmd_class, type(QOMCommand)) 272 return cmd_class.command_runner(args)