]> git.proxmox.com Git - qemu.git/blob - QMP/qemu-ga-client
qmp: fix handling of cmd with Equals in qmp-shell
[qemu.git] / QMP / qemu-ga-client
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 #
36 # See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
37 #
38
39 import base64
40 import random
41
42 import qmp
43
44
45 class 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
52 class 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
166 def _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
174 def _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
194 def _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
202 def _cmd_ifconfig(client, args):
203 print(client.ifconfig())
204
205
206 def _cmd_info(client, args):
207 print(client.info())
208
209
210 def _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
221 def _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
234 def _cmd_shutdown(client, args):
235 client.shutdown()
236 _cmd_powerdown = _cmd_shutdown
237
238
239 def _cmd_halt(client, args):
240 client.shutdown('halt')
241
242
243 def _cmd_reboot(client, args):
244 client.shutdown('reboot')
245
246
247 commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
248
249
250 def 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)
262 except QemuGuestAgent.error, e:
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
270 if cmd != 'ping':
271 client.sync()
272
273 globals()['_cmd_' + cmd](client, args)
274
275
276 if __name__ == '__main__':
277 import sys
278 import os
279 import optparse
280
281 address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
282
283 usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
284 usage += '<command>: ' + ', '.join(commands)
285 parser = optparse.OptionParser(usage=usage)
286 parser.add_option('--address', action='store', type='string',
287 default=address, help='Specify a ip:port pair or a unix socket path')
288 options, args = parser.parse_args()
289
290 address = options.address
291 if address is None:
292 parser.error('address is not specified')
293 sys.exit(1)
294
295 if len(args) == 0:
296 parser.error('Less argument')
297 sys.exit(1)
298
299 main(address, args[0], args[1:])