xref: /openbmc/qemu/python/qemu/utils/qom.py (revision 3656e761bcdd207b7759cdcd608212d2a6f9c12d)
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
34from typing import List
35
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.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
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.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
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_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
257def 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)
279