Add qemu 2.4.0
[kvmfornfv.git] / qemu / scripts / 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-project.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 == 'fsfreeze' and args[0] == 'freeze':
271         client.sync(60)
272     elif cmd != 'ping':
273         client.sync()
274
275     globals()['_cmd_' + cmd](client, args)
276
277
278 if __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:])