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.
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
19 # $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
23 # $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
24 # $ qemu-ga-client <command> [args...]
28 # $ qemu-ga-client cat /etc/resolv.conf
29 # # Generated by NetworkManager
31 # $ qemu-ga-client fsfreeze status
33 # $ qemu-ga-client fsfreeze freeze
34 # 2 filesystems frozen
36 # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
44 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
48 class QemuGuestAgent(qmp.QEMUMonitorProtocol):
49 def __getattr__(self, name):
51 return self.command('guest-' + name.replace('_', '-'), **kwds)
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)
68 ret = self.qga.sync(id=uid)
69 if isinstance(ret, int) and int(ret) == uid:
72 def __file_read_all(self, handle):
76 ret = self.qga.file_read(handle=handle, count=1024)
77 _data = base64.b64decode(ret['buf-b64'])
83 handle = self.qga.file_open(path=path)
85 data = self.__file_read_all(handle)
87 self.qga.file_close(handle=handle)
91 info = self.qga.info()
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),
111 nifs = self.qga.network_get_interfaces()
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)
135 except self.qga.timeout:
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)
153 getattr(self.qga, 'suspend' + '_' + mode)()
154 # On error exception will raise
155 except self.qga.timeout:
156 # On success command will timed out
159 def shutdown(self, mode='powerdown'):
160 if mode not in ['powerdown', 'halt', 'reboot']:
161 raise Exception('Invalid mode: ' + mode)
164 self.qga.shutdown(mode=mode)
165 except self.qga.timeout:
169 def _cmd_cat(client, args):
171 print('Invalid argument')
172 print('Usage: cat <file>')
174 print(client.read(args[0]))
177 def _cmd_fsfreeze(client, args):
178 usage = 'Usage: fsfreeze status|freeze|thaw'
180 print('Invalid argument')
183 if args[0] not in ['status', 'freeze', 'thaw']:
184 print('Invalid command: ' + args[0])
188 ret = client.fsfreeze(cmd)
191 elif cmd == 'freeze':
192 print("%d filesystems frozen" % ret)
194 print("%d filesystems thawed" % ret)
197 def _cmd_fstrim(client, args):
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):
213 def _cmd_ping(client, args):
217 timeout = float(args[0])
218 alive = client.ping(timeout)
220 print("Not responded in %s sec" % args[0])
224 def _cmd_suspend(client, args):
225 usage = 'Usage: suspend disk|ram|hybrid'
227 print('Less argument')
230 if args[0] not in ['disk', 'ram', 'hybrid']:
231 print('Invalid command: ' + args[0])
234 client.suspend(args[0])
237 def _cmd_shutdown(client, args):
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)
258 if cmd not in commands:
259 print('Invalid command: ' + cmd)
260 print('Available commands: ' + ', '.join(commands))
264 client = QemuGuestAgentClient(address)
265 except QemuGuestAgent.error as e:
269 if e.errno == errno.ECONNREFUSED:
270 print('Hint: qemu is not running?')
273 if cmd == 'fsfreeze' and args[0] == 'freeze':
278 globals()['_cmd_' + cmd](client, args)
281 if __name__ == '__main__':
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
297 parser.error('address is not specified')
301 parser.error('Less argument')
304 main(address, args[0], args[1:])