Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-5.1-20200527' into staging
[qemu/ar7.git] / scripts / qmp / qemu-ga-client
blobce122984a9cae64eaacc93d7416209b875e9b094
1 #!/usr/bin/env python3
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 import os
40 import sys
41 import base64
42 import random
44 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
45 from qemu import qmp
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
55 class QemuGuestAgentClient:
56     error = QemuGuestAgent.error
58     def __init__(self, address):
59         self.qga = QemuGuestAgent(address)
60         self.qga.connect(negotiate=False)
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
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
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
90     def info(self):
91         info = self.qga.info()
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))
101         return '\n'.join(msgs)
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)])
110     def ifconfig(self):
111         nifs = self.qga.network_get_interfaces()
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'])
129         return '\n'.join(msgs)
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
139     def fsfreeze(self, cmd):
140         if cmd not in ['status', 'freeze', 'thaw']:
141             raise Exception('Invalid command: ' + cmd)
143         return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
145     def fstrim(self, minimum=0):
146         return getattr(self.qga, 'fstrim')(minimum=minimum)
148     def suspend(self, mode):
149         if mode not in ['disk', 'ram', 'hybrid']:
150             raise Exception('Invalid mode: ' + mode)
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
159     def shutdown(self, mode='powerdown'):
160         if mode not in ['powerdown', 'halt', 'reboot']:
161             raise Exception('Invalid mode: ' + mode)
163         try:
164             self.qga.shutdown(mode=mode)
165         except self.qga.timeout:
166             return
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]))
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)
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))
205 def _cmd_ifconfig(client, args):
206     print(client.ifconfig())
209 def _cmd_info(client, args):
210     print(client.info())
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)
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])
237 def _cmd_shutdown(client, args):
238     client.shutdown()
239 _cmd_powerdown = _cmd_shutdown
242 def _cmd_halt(client, args):
243     client.shutdown('halt')
246 def _cmd_reboot(client, args):
247     client.shutdown('reboot')
250 commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
253 def main(address, cmd, args):
254     if not os.path.exists(address):
255         print('%s not found' % address)
256         sys.exit(1)
258     if cmd not in commands:
259         print('Invalid command: ' + cmd)
260         print('Available commands: ' + ', '.join(commands))
261         sys.exit(1)
263     try:
264         client = QemuGuestAgentClient(address)
265     except QemuGuestAgent.error as e:
266         import errno
268         print(e)
269         if e.errno == errno.ECONNREFUSED:
270             print('Hint: qemu is not running?')
271         sys.exit(1)
273     if cmd == 'fsfreeze' and args[0] == 'freeze':
274         client.sync(60)
275     elif cmd != 'ping':
276         client.sync()
278     globals()['_cmd_' + cmd](client, args)
281 if __name__ == '__main__':
282     import sys
283     import os
284     import optparse
286     address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
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()
295     address = options.address
296     if address is None:
297         parser.error('address is not specified')
298         sys.exit(1)
300     if len(args) == 0:
301         parser.error('Less argument')
302         sys.exit(1)
304     main(address, args[0], args[1:])