]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qmp/qom-fuse
scripts/qom-fuse: Add docstrings
[mirror_qemu.git] / scripts / qmp / qom-fuse
CommitLineData
3d004a37 1#!/usr/bin/env python3
187be27c
JS
2"""
3QEMU Object Model FUSE filesystem tool
4
5This script offers a simple FUSE filesystem within which the QOM tree
6may be browsed, queried and edited using traditional shell tooling.
7
8This script requires the 'fusepy' python package.
9
10ENV:
11 QMP_SOCKET: Path to the QMP server socket
12
13Usage:
14 qom-fuse /mount/to/here
15"""
5ade7674 16##
5ade7674 17# Copyright IBM, Corp. 2012
f713ed4f 18# Copyright (C) 2020 Red Hat, Inc.
5ade7674
AL
19#
20# Authors:
21# Anthony Liguori <aliguori@us.ibm.com>
f713ed4f 22# Markus Armbruster <armbru@redhat.com>
5ade7674 23#
26c1ccad
JS
24# This work is licensed under the terms of the GNU GPL, version 2 or later.
25# See the COPYING file in the top-level directory.
5ade7674
AL
26##
27
26c1ccad 28from errno import ENOENT, EPERM
c6b7eae9 29import os
c6b7eae9
JS
30import stat
31import sys
32
33import fuse
34from fuse import FUSE, FuseOSError, Operations
35
c7b942d7
JS
36
37sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
7552823a 38from qemu.qmp import QEMUMonitorProtocol, QMPResponseError
5ade7674 39
c6b7eae9 40
5ade7674
AL
41fuse.fuse_python_api = (0, 2)
42
26c1ccad 43
f713ed4f 44class QOMFS(Operations):
187be27c 45 """QOMFS implements fuse.Operations to provide a QOM filesystem."""
f713ed4f 46 def __init__(self, qmp):
5ade7674
AL
47 self.qmp = qmp
48 self.qmp.connect()
49 self.ino_map = {}
50 self.ino_count = 1
51
52 def get_ino(self, path):
187be27c 53 """Get an inode number for a given QOM path."""
d7a4228e 54 if path in self.ino_map:
5ade7674
AL
55 return self.ino_map[path]
56 self.ino_map[path] = self.ino_count
57 self.ino_count += 1
58 return self.ino_map[path]
59
60 def is_object(self, path):
187be27c 61 """Is the given QOM path an object?"""
5ade7674 62 try:
26c1ccad 63 self.qmp.command('qom-list', path=path)
5ade7674 64 return True
7552823a 65 except QMPResponseError:
5ade7674
AL
66 return False
67
68 def is_property(self, path):
187be27c 69 """Is the given QOM path a property?"""
3a14019e
MA
70 path, prop = path.rsplit('/', 1)
71 if path == '':
72 path = '/'
5ade7674 73 try:
5ade7674
AL
74 for item in self.qmp.command('qom-list', path=path):
75 if item['name'] == prop:
76 return True
77 return False
7552823a 78 except QMPResponseError:
5ade7674
AL
79 return False
80
81 def is_link(self, path):
187be27c 82 """Is the given QOM path a link?"""
3a14019e
MA
83 path, prop = path.rsplit('/', 1)
84 if path == '':
85 path = '/'
5ade7674 86 try:
5ade7674
AL
87 for item in self.qmp.command('qom-list', path=path):
88 if item['name'] == prop:
89 if item['type'].startswith('link<'):
90 return True
91 return False
92 return False
7552823a 93 except QMPResponseError:
5ade7674
AL
94 return False
95
7552823a 96 def read(self, path, size, offset, fh):
5ade7674
AL
97 if not self.is_property(path):
98 return -ENOENT
99
100 path, prop = path.rsplit('/', 1)
3a14019e
MA
101 if path == '':
102 path = '/'
5ade7674 103 try:
f713ed4f 104 data = self.qmp.command('qom-get', path=path, property=prop)
26c1ccad 105 data += '\n' # make values shell friendly
7552823a
JS
106 except QMPResponseError as err:
107 raise FuseOSError(EPERM) from err
5ade7674
AL
108
109 if offset > len(data):
110 return ''
111
7552823a 112 return bytes(data[offset:][:size], encoding='utf-8')
5ade7674
AL
113
114 def readlink(self, path):
115 if not self.is_link(path):
116 return False
117 path, prop = path.rsplit('/', 1)
118 prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
119 return prefix + str(self.qmp.command('qom-get', path=path,
120 property=prop))
121
f713ed4f 122 def getattr(self, path, fh=None):
5ade7674 123 if self.is_link(path):
26c1ccad
JS
124 value = {
125 'st_mode': 0o755 | stat.S_IFLNK,
126 'st_ino': self.get_ino(path),
127 'st_dev': 0,
128 'st_nlink': 2,
129 'st_uid': 1000,
130 'st_gid': 1000,
131 'st_size': 4096,
132 'st_atime': 0,
133 'st_mtime': 0,
134 'st_ctime': 0
135 }
5ade7674 136 elif self.is_object(path):
26c1ccad
JS
137 value = {
138 'st_mode': 0o755 | stat.S_IFDIR,
139 'st_ino': self.get_ino(path),
140 'st_dev': 0,
141 'st_nlink': 2,
142 'st_uid': 1000,
143 'st_gid': 1000,
144 'st_size': 4096,
145 'st_atime': 0,
146 'st_mtime': 0,
147 'st_ctime': 0
148 }
5ade7674 149 elif self.is_property(path):
26c1ccad
JS
150 value = {
151 'st_mode': 0o644 | stat.S_IFREG,
152 'st_ino': self.get_ino(path),
153 'st_dev': 0,
154 'st_nlink': 1,
155 'st_uid': 1000,
156 'st_gid': 1000,
157 'st_size': 4096,
158 'st_atime': 0,
159 'st_mtime': 0,
160 'st_ctime': 0
161 }
5ade7674 162 else:
f713ed4f 163 raise FuseOSError(ENOENT)
5ade7674
AL
164 return value
165
f713ed4f
MA
166 def readdir(self, path, fh):
167 yield '.'
168 yield '..'
5ade7674 169 for item in self.qmp.command('qom-list', path=path):
f713ed4f 170 yield str(item['name'])
5ade7674 171
5ade7674 172
26c1ccad 173if __name__ == '__main__':
f713ed4f
MA
174 fuse = FUSE(QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET'])),
175 sys.argv[1], foreground=True)