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