]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qmp/qemu-ga-client
microbit: Eliminate two local variables in microbit_init()
[mirror_qemu.git] / scripts / qmp / qemu-ga-client
1 #!/usr/bin/env python3
2
3 # QEMU Guest Agent Client
4 #
5 # Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
6 #
7 # This work is licensed under the terms of the GNU GPL, version 2. See
8 # the COPYING file in the top-level directory.
9 #
10 # Usage:
11 #
12 # Start QEMU with:
13 #
14 # # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
15 # -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
16 #
17 # Run the script:
18 #
19 # $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
20 #
21 # or
22 #
23 # $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
24 # $ qemu-ga-client <command> [args...]
25 #
26 # For example:
27 #
28 # $ qemu-ga-client cat /etc/resolv.conf
29 # # Generated by NetworkManager
30 # nameserver 10.0.2.3
31 # $ qemu-ga-client fsfreeze status
32 # thawed
33 # $ qemu-ga-client fsfreeze freeze
34 # 2 filesystems frozen
35 #
36 # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
37 #
38
39 import os
40 import sys
41 import base64
42 import random
43
44 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
45 from qemu import qmp
46
47
48 class QemuGuestAgent(qmp.QEMUMonitorProtocol):
49 def __getattr__(self, name):
50 def wrapper(**kwds):
51 return self.command('guest-' + name.replace('_', '-'), **kwds)
52 return wrapper
53
54
55 class QemuGuestAgentClient:
56 error = QemuGuestAgent.error
57
58 def __init__(self, address):
59 self.qga = QemuGuestAgent(address)
60 self.qga.connect(negotiate=False)
61
62 def sync(self, timeout=3):
63 # Avoid being blocked forever
64 if not self.ping(timeout):
65 raise EnvironmentError('Agent seems not alive')
66 uid = random.randint(0, (1 << 32) - 1)
67 while True:
68 ret = self.qga.sync(id=uid)
69 if isinstance(ret, int) and int(ret) == uid:
70 break
71
72 def __file_read_all(self, handle):
73 eof = False
74 data = ''
75 while not eof:
76 ret = self.qga.file_read(handle=handle, count=1024)
77 _data = base64.b64decode(ret['buf-b64'])
78 data += _data
79 eof = ret['eof']
80 return data
81
82 def read(self, path):
83 handle = self.qga.file_open(path=path)
84 try:
85 data = self.__file_read_all(handle)
86 finally:
87 self.qga.file_close(handle=handle)
88 return data
89
90 def info(self):
91 info = self.qga.info()
92
93 msgs = []
94 msgs.append('version: ' + info['version'])
95 msgs.append('supported_commands:')
96 enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
97 msgs.append('\tenabled: ' + ', '.join(enabled))
98 disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
99 msgs.append('\tdisabled: ' + ', '.join(disabled))
100
101 return '\n'.join(msgs)
102
103 def __gen_ipv4_netmask(self, prefixlen):
104 mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
105 return '.'.join([str(mask >> 24),
106 str((mask >> 16) & 0xff),
107 str((mask >> 8) & 0xff),
108 str(mask & 0xff)])
109
110 def ifconfig(self):
111 nifs = self.qga.network_get_interfaces()
112
113 msgs = []
114 for nif in nifs:
115 msgs.append(nif['name'] + ':')
116 if 'ip-addresses' in nif:
117 for ipaddr in nif['ip-addresses']:
118 if ipaddr['ip-address-type'] == 'ipv4':
119 addr = ipaddr['ip-address']
120 mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
121 msgs.append("\tinet %s netmask %s" % (addr, mask))
122 elif ipaddr['ip-address-type'] == 'ipv6':
123 addr = ipaddr['ip-address']
124 prefix = ipaddr['prefix']
125 msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
126 if nif['hardware-address'] != '00:00:00:00:00:00':
127 msgs.append("\tether " + nif['hardware-address'])
128
129 return '\n'.join(msgs)
130
131 def ping(self, timeout):
132 self.qga.settimeout(timeout)
133 try:
134 self.qga.ping()
135 except self.qga.timeout:
136 return False
137 return True
138
139 def fsfreeze(self, cmd):
140 if cmd not in ['status', 'freeze', 'thaw']:
141 raise Exception('Invalid command: ' + cmd)
142
143 return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
144
145 def fstrim(self, minimum=0):
146 return getattr(self.qga, 'fstrim')(minimum=minimum)
147
148 def suspend(self, mode):
149 if mode not in ['disk', 'ram', 'hybrid']:
150 raise Exception('Invalid mode: ' + mode)
151
152 try:
153 getattr(self.qga, 'suspend' + '_' + mode)()
154 # On error exception will raise
155 except self.qga.timeout:
156 # On success command will timed out
157 return
158
159 def shutdown(self, mode='powerdown'):
160 if mode not in ['powerdown', 'halt', 'reboot']:
161 raise Exception('Invalid mode: ' + mode)
162
163 try:
164 self.qga.shutdown(mode=mode)
165 except self.qga.timeout:
166 return
167
168
169 def _cmd_cat(client, args):
170 if len(args) != 1:
171 print('Invalid argument')
172 print('Usage: cat <file>')
173 sys.exit(1)
174 print(client.read(args[0]))
175
176
177 def _cmd_fsfreeze(client, args):
178 usage = 'Usage: fsfreeze status|freeze|thaw'
179 if len(args) != 1:
180 print('Invalid argument')
181 print(usage)
182 sys.exit(1)
183 if args[0] not in ['status', 'freeze', 'thaw']:
184 print('Invalid command: ' + args[0])
185 print(usage)
186 sys.exit(1)
187 cmd = args[0]
188 ret = client.fsfreeze(cmd)
189 if cmd == 'status':
190 print(ret)
191 elif cmd == 'freeze':
192 print("%d filesystems frozen" % ret)
193 else:
194 print("%d filesystems thawed" % ret)
195
196
197 def _cmd_fstrim(client, args):
198 if len(args) == 0:
199 minimum = 0
200 else:
201 minimum = int(args[0])
202 print(client.fstrim(minimum))
203
204
205 def _cmd_ifconfig(client, args):
206 print(client.ifconfig())
207
208
209 def _cmd_info(client, args):
210 print(client.info())
211
212
213 def _cmd_ping(client, args):
214 if len(args) == 0:
215 timeout = 3
216 else:
217 timeout = float(args[0])
218 alive = client.ping(timeout)
219 if not alive:
220 print("Not responded in %s sec" % args[0])
221 sys.exit(1)
222
223
224 def _cmd_suspend(client, args):
225 usage = 'Usage: suspend disk|ram|hybrid'
226 if len(args) != 1:
227 print('Less argument')
228 print(usage)
229 sys.exit(1)
230 if args[0] not in ['disk', 'ram', 'hybrid']:
231 print('Invalid command: ' + args[0])
232 print(usage)
233 sys.exit(1)
234 client.suspend(args[0])
235
236
237 def _cmd_shutdown(client, args):
238 client.shutdown()
239 _cmd_powerdown = _cmd_shutdown
240
241
242 def _cmd_halt(client, args):
243 client.shutdown('halt')
244
245
246 def _cmd_reboot(client, args):
247 client.shutdown('reboot')
248
249
250 commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
251
252
253 def main(address, cmd, args):
254 if not os.path.exists(address):
255 print('%s not found' % address)
256 sys.exit(1)
257
258 if cmd not in commands:
259 print('Invalid command: ' + cmd)
260 print('Available commands: ' + ', '.join(commands))
261 sys.exit(1)
262
263 try:
264 client = QemuGuestAgentClient(address)
265 except QemuGuestAgent.error as e:
266 import errno
267
268 print(e)
269 if e.errno == errno.ECONNREFUSED:
270 print('Hint: qemu is not running?')
271 sys.exit(1)
272
273 if cmd == 'fsfreeze' and args[0] == 'freeze':
274 client.sync(60)
275 elif cmd != 'ping':
276 client.sync()
277
278 globals()['_cmd_' + cmd](client, args)
279
280
281 if __name__ == '__main__':
282 import sys
283 import os
284 import optparse
285
286 address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
287
288 usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
289 usage += '<command>: ' + ', '.join(commands)
290 parser = optparse.OptionParser(usage=usage)
291 parser.add_option('--address', action='store', type='string',
292 default=address, help='Specify a ip:port pair or a unix socket path')
293 options, args = parser.parse_args()
294
295 address = options.address
296 if address is None:
297 parser.error('address is not specified')
298 sys.exit(1)
299
300 if len(args) == 0:
301 parser.error('Less argument')
302 sys.exit(1)
303
304 main(address, args[0], args[1:])