scripts/qemu.py: use a more consistent docstring style
[qemu/ar7.git] / scripts / qmp / qemu-ga-client
blobe8cb7646a0be1127c6fecaa41085448ae787156a
1 #!/usr/bin/python
3 # QEMU Guest Agent Client
5 # Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
7 # This work is licensed under the terms of the GNU GPL, version 2. See
8 # the COPYING file in the top-level directory.
10 # Usage:
12 # Start QEMU with:
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
17 # Run the script:
19 # $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
21 # or
23 # $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
24 # $ qemu-ga-client <command> [args...]
26 # For example:
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
36 # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
39 from __future__ import print_function
40 import base64
41 import random
43 import qmp
46 class QemuGuestAgent(qmp.QEMUMonitorProtocol):
47 def __getattr__(self, name):
48 def wrapper(**kwds):
49 return self.command('guest-' + name.replace('_', '-'), **kwds)
50 return wrapper
53 class QemuGuestAgentClient:
54 error = QemuGuestAgent.error
56 def __init__(self, address):
57 self.qga = QemuGuestAgent(address)
58 self.qga.connect(negotiate=False)
60 def sync(self, timeout=3):
61 # Avoid being blocked forever
62 if not self.ping(timeout):
63 raise EnvironmentError('Agent seems not alive')
64 uid = random.randint(0, (1 << 32) - 1)
65 while True:
66 ret = self.qga.sync(id=uid)
67 if isinstance(ret, int) and int(ret) == uid:
68 break
70 def __file_read_all(self, handle):
71 eof = False
72 data = ''
73 while not eof:
74 ret = self.qga.file_read(handle=handle, count=1024)
75 _data = base64.b64decode(ret['buf-b64'])
76 data += _data
77 eof = ret['eof']
78 return data
80 def read(self, path):
81 handle = self.qga.file_open(path=path)
82 try:
83 data = self.__file_read_all(handle)
84 finally:
85 self.qga.file_close(handle=handle)
86 return data
88 def info(self):
89 info = self.qga.info()
91 msgs = []
92 msgs.append('version: ' + info['version'])
93 msgs.append('supported_commands:')
94 enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
95 msgs.append('\tenabled: ' + ', '.join(enabled))
96 disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
97 msgs.append('\tdisabled: ' + ', '.join(disabled))
99 return '\n'.join(msgs)
101 def __gen_ipv4_netmask(self, prefixlen):
102 mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
103 return '.'.join([str(mask >> 24),
104 str((mask >> 16) & 0xff),
105 str((mask >> 8) & 0xff),
106 str(mask & 0xff)])
108 def ifconfig(self):
109 nifs = self.qga.network_get_interfaces()
111 msgs = []
112 for nif in nifs:
113 msgs.append(nif['name'] + ':')
114 if 'ip-addresses' in nif:
115 for ipaddr in nif['ip-addresses']:
116 if ipaddr['ip-address-type'] == 'ipv4':
117 addr = ipaddr['ip-address']
118 mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
119 msgs.append("\tinet %s netmask %s" % (addr, mask))
120 elif ipaddr['ip-address-type'] == 'ipv6':
121 addr = ipaddr['ip-address']
122 prefix = ipaddr['prefix']
123 msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
124 if nif['hardware-address'] != '00:00:00:00:00:00':
125 msgs.append("\tether " + nif['hardware-address'])
127 return '\n'.join(msgs)
129 def ping(self, timeout):
130 self.qga.settimeout(timeout)
131 try:
132 self.qga.ping()
133 except self.qga.timeout:
134 return False
135 return True
137 def fsfreeze(self, cmd):
138 if cmd not in ['status', 'freeze', 'thaw']:
139 raise Exception('Invalid command: ' + cmd)
141 return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
143 def fstrim(self, minimum=0):
144 return getattr(self.qga, 'fstrim')(minimum=minimum)
146 def suspend(self, mode):
147 if mode not in ['disk', 'ram', 'hybrid']:
148 raise Exception('Invalid mode: ' + mode)
150 try:
151 getattr(self.qga, 'suspend' + '_' + mode)()
152 # On error exception will raise
153 except self.qga.timeout:
154 # On success command will timed out
155 return
157 def shutdown(self, mode='powerdown'):
158 if mode not in ['powerdown', 'halt', 'reboot']:
159 raise Exception('Invalid mode: ' + mode)
161 try:
162 self.qga.shutdown(mode=mode)
163 except self.qga.timeout:
164 return
167 def _cmd_cat(client, args):
168 if len(args) != 1:
169 print('Invalid argument')
170 print('Usage: cat <file>')
171 sys.exit(1)
172 print(client.read(args[0]))
175 def _cmd_fsfreeze(client, args):
176 usage = 'Usage: fsfreeze status|freeze|thaw'
177 if len(args) != 1:
178 print('Invalid argument')
179 print(usage)
180 sys.exit(1)
181 if args[0] not in ['status', 'freeze', 'thaw']:
182 print('Invalid command: ' + args[0])
183 print(usage)
184 sys.exit(1)
185 cmd = args[0]
186 ret = client.fsfreeze(cmd)
187 if cmd == 'status':
188 print(ret)
189 elif cmd == 'freeze':
190 print("%d filesystems frozen" % ret)
191 else:
192 print("%d filesystems thawed" % ret)
195 def _cmd_fstrim(client, args):
196 if len(args) == 0:
197 minimum = 0
198 else:
199 minimum = int(args[0])
200 print(client.fstrim(minimum))
203 def _cmd_ifconfig(client, args):
204 print(client.ifconfig())
207 def _cmd_info(client, args):
208 print(client.info())
211 def _cmd_ping(client, args):
212 if len(args) == 0:
213 timeout = 3
214 else:
215 timeout = float(args[0])
216 alive = client.ping(timeout)
217 if not alive:
218 print("Not responded in %s sec" % args[0])
219 sys.exit(1)
222 def _cmd_suspend(client, args):
223 usage = 'Usage: suspend disk|ram|hybrid'
224 if len(args) != 1:
225 print('Less argument')
226 print(usage)
227 sys.exit(1)
228 if args[0] not in ['disk', 'ram', 'hybrid']:
229 print('Invalid command: ' + args[0])
230 print(usage)
231 sys.exit(1)
232 client.suspend(args[0])
235 def _cmd_shutdown(client, args):
236 client.shutdown()
237 _cmd_powerdown = _cmd_shutdown
240 def _cmd_halt(client, args):
241 client.shutdown('halt')
244 def _cmd_reboot(client, args):
245 client.shutdown('reboot')
248 commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
251 def main(address, cmd, args):
252 if not os.path.exists(address):
253 print('%s not found' % address)
254 sys.exit(1)
256 if cmd not in commands:
257 print('Invalid command: ' + cmd)
258 print('Available commands: ' + ', '.join(commands))
259 sys.exit(1)
261 try:
262 client = QemuGuestAgentClient(address)
263 except QemuGuestAgent.error as e:
264 import errno
266 print(e)
267 if e.errno == errno.ECONNREFUSED:
268 print('Hint: qemu is not running?')
269 sys.exit(1)
271 if cmd == 'fsfreeze' and args[0] == 'freeze':
272 client.sync(60)
273 elif cmd != 'ping':
274 client.sync()
276 globals()['_cmd_' + cmd](client, args)
279 if __name__ == '__main__':
280 import sys
281 import os
282 import optparse
284 address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
286 usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
287 usage += '<command>: ' + ', '.join(commands)
288 parser = optparse.OptionParser(usage=usage)
289 parser.add_option('--address', action='store', type='string',
290 default=address, help='Specify a ip:port pair or a unix socket path')
291 options, args = parser.parse_args()
293 address = options.address
294 if address is None:
295 parser.error('address is not specified')
296 sys.exit(1)
298 if len(args) == 0:
299 parser.error('Less argument')
300 sys.exit(1)
302 main(address, args[0], args[1:])