]>
git.proxmox.com Git - mirror_qemu.git/blob - scripts/qmp/qemu-ga-client
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
_ipv
4_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:])