xref: /qemu/python/qemu/utils/qom.py (revision 5db05230)
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