Revision 22f3946b

/dev/null
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.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:])
/dev/null
1
#!/usr/bin/python
2
#
3
# QMP command line tool
4
#
5
# Copyright IBM, Corp. 2011
6
#
7
# Authors:
8
#  Anthony Liguori <aliguori@us.ibm.com>
9
#
10
# This work is licensed under the terms of the GNU GPLv2 or later.
11
# See the COPYING file in the top-level directory.
12

  
13
import sys, os
14
from qmp import QEMUMonitorProtocol
15

  
16
def print_response(rsp, prefix=[]):
17
    if type(rsp) == list:
18
        i = 0
19
        for item in rsp:
20
            if prefix == []:
21
                prefix = ['item']
22
            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
23
            i += 1
24
    elif type(rsp) == dict:
25
        for key in rsp.keys():
26
            print_response(rsp[key], prefix + [key])
27
    else:
28
        if len(prefix):
29
            print '%s: %s' % ('.'.join(prefix), rsp)
30
        else:
31
            print '%s' % (rsp)
32

  
33
def main(args):
34
    path = None
35

  
36
    # Use QMP_PATH if it's set
37
    if os.environ.has_key('QMP_PATH'):
38
        path = os.environ['QMP_PATH']
39

  
40
    while len(args):
41
        arg = args[0]
42

  
43
        if arg.startswith('--'):
44
            arg = arg[2:]
45
            if arg.find('=') == -1:
46
                value = True
47
            else:
48
                arg, value = arg.split('=', 1)
49

  
50
            if arg in ['path']:
51
                if type(value) == str:
52
                    path = value
53
            elif arg in ['help']:
54
                os.execlp('man', 'man', 'qmp')
55
            else:
56
                print 'Unknown argument "%s"' % arg
57

  
58
            args = args[1:]
59
        else:
60
            break
61

  
62
    if not path:
63
        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
64
        return 1
65

  
66
    if len(args):
67
        command, args = args[0], args[1:]
68
    else:
69
        print 'No command found'
70
        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
71
        return 1
72

  
73
    if command in ['help']:
74
        os.execlp('man', 'man', 'qmp')
75

  
76
    srv = QEMUMonitorProtocol(path)
77
    srv.connect()
78

  
79
    def do_command(srv, cmd, **kwds):
80
        rsp = srv.cmd(cmd, kwds)
81
        if rsp.has_key('error'):
82
            raise Exception(rsp['error']['desc'])
83
        return rsp['return']
84

  
85
    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
86

  
87
    srv.close()
88

  
89
    if command not in commands:
90
        fullcmd = 'qmp-%s' % command
91
        try:
92
            os.environ['QMP_PATH'] = path
93
            os.execvp(fullcmd, [fullcmd] + args)
94
        except OSError, (errno, msg):
95
            if errno == 2:
96
                print 'Command "%s" not found.' % (fullcmd)
97
                return 1
98
            raise
99
        return 0
100

  
101
    srv = QEMUMonitorProtocol(path)
102
    srv.connect()
103

  
104
    arguments = {}
105
    for arg in args:
106
        if not arg.startswith('--'):
107
            print 'Unknown argument "%s"' % arg
108
            return 1
109

  
110
        arg = arg[2:]
111
        if arg.find('=') == -1:
112
            value = True
113
        else:
114
            arg, value = arg.split('=', 1)
115

  
116
        if arg in ['help']:
117
            os.execlp('man', 'man', 'qmp-%s' % command)
118
            return 1
119

  
120
        arguments[arg] = value
121

  
122
    rsp = do_command(srv, command, **arguments)
123
    print_response(rsp)
124

  
125
if __name__ == '__main__':
126
    sys.exit(main(sys.argv[1:]))
/dev/null
1
#!/usr/bin/python
2
#
3
# Low-level QEMU shell on top of QMP.
4
#
5
# Copyright (C) 2009, 2010 Red Hat Inc.
6
#
7
# Authors:
8
#  Luiz Capitulino <lcapitulino@redhat.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2.  See
11
# the COPYING file in the top-level directory.
12
#
13
# Usage:
14
#
15
# Start QEMU with:
16
#
17
# # qemu [...] -qmp unix:./qmp-sock,server
18
#
19
# Run the shell:
20
#
21
# $ qmp-shell ./qmp-sock
22
#
23
# Commands have the following format:
24
#
25
#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
26
#
27
# For example:
28
#
29
# (QEMU) device_add driver=e1000 id=net1
30
# {u'return': {}}
31
# (QEMU)
32

  
33
import qmp
34
import readline
35
import sys
36
import pprint
37

  
38
class QMPCompleter(list):
39
    def complete(self, text, state):
40
        for cmd in self:
41
            if cmd.startswith(text):
42
                if not state:
43
                    return cmd
44
                else:
45
                    state -= 1
46

  
47
class QMPShellError(Exception):
48
    pass
49

  
50
class QMPShellBadPort(QMPShellError):
51
    pass
52

  
53
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
54
#       _execute_cmd()). Let's design a better one.
55
class QMPShell(qmp.QEMUMonitorProtocol):
56
    def __init__(self, address, pp=None):
57
        qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
58
        self._greeting = None
59
        self._completer = None
60
        self._pp = pp
61

  
62
    def __get_address(self, arg):
63
        """
64
        Figure out if the argument is in the port:host form, if it's not it's
65
        probably a file path.
66
        """
67
        addr = arg.split(':')
68
        if len(addr) == 2:
69
            try:
70
                port = int(addr[1])
71
            except ValueError:
72
                raise QMPShellBadPort
73
            return ( addr[0], port )
74
        # socket path
75
        return arg
76

  
77
    def _fill_completion(self):
78
        for cmd in self.cmd('query-commands')['return']:
79
            self._completer.append(cmd['name'])
80

  
81
    def __completer_setup(self):
82
        self._completer = QMPCompleter()
83
        self._fill_completion()
84
        readline.set_completer(self._completer.complete)
85
        readline.parse_and_bind("tab: complete")
86
        # XXX: default delimiters conflict with some command names (eg. query-),
87
        # clearing everything as it doesn't seem to matter
88
        readline.set_completer_delims('')
89

  
90
    def __build_cmd(self, cmdline):
91
        """
92
        Build a QMP input object from a user provided command-line in the
93
        following format:
94
    
95
            < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
96
        """
97
        cmdargs = cmdline.split()
98
        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
99
        for arg in cmdargs[1:]:
100
            opt = arg.split('=')
101
            try:
102
                if(len(opt) > 2):
103
                    opt[1] = '='.join(opt[1:])
104
                value = int(opt[1])
105
            except ValueError:
106
                if opt[1] == 'true':
107
                    value = True
108
                elif opt[1] == 'false':
109
                    value = False
110
                else:
111
                    value = opt[1]
112
            qmpcmd['arguments'][opt[0]] = value
113
        return qmpcmd
114

  
115
    def _execute_cmd(self, cmdline):
116
        try:
117
            qmpcmd = self.__build_cmd(cmdline)
118
        except:
119
            print 'command format: <command-name> ',
120
            print '[arg-name1=arg1] ... [arg-nameN=argN]'
121
            return True
122
        resp = self.cmd_obj(qmpcmd)
123
        if resp is None:
124
            print 'Disconnected'
125
            return False
126

  
127
        if self._pp is not None:
128
            self._pp.pprint(resp)
129
        else:
130
            print resp
131
        return True
132

  
133
    def connect(self):
134
        self._greeting = qmp.QEMUMonitorProtocol.connect(self)
135
        self.__completer_setup()
136

  
137
    def show_banner(self, msg='Welcome to the QMP low-level shell!'):
138
        print msg
139
        version = self._greeting['QMP']['version']['qemu']
140
        print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
141

  
142
    def read_exec_command(self, prompt):
143
        """
144
        Read and execute a command.
145

  
146
        @return True if execution was ok, return False if disconnected.
147
        """
148
        try:
149
            cmdline = raw_input(prompt)
150
        except EOFError:
151
            print
152
            return False
153
        if cmdline == '':
154
            for ev in self.get_events():
155
                print ev
156
            self.clear_events()
157
            return True
158
        else:
159
            return self._execute_cmd(cmdline)
160

  
161
class HMPShell(QMPShell):
162
    def __init__(self, address):
163
        QMPShell.__init__(self, address)
164
        self.__cpu_index = 0
165

  
166
    def __cmd_completion(self):
167
        for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
168
            if cmd and cmd[0] != '[' and cmd[0] != '\t':
169
                name = cmd.split()[0] # drop help text
170
                if name == 'info':
171
                    continue
172
                if name.find('|') != -1:
173
                    # Command in the form 'foobar|f' or 'f|foobar', take the
174
                    # full name
175
                    opt = name.split('|')
176
                    if len(opt[0]) == 1:
177
                        name = opt[1]
178
                    else:
179
                        name = opt[0]
180
                self._completer.append(name)
181
                self._completer.append('help ' + name) # help completion
182

  
183
    def __info_completion(self):
184
        for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
185
            if cmd:
186
                self._completer.append('info ' + cmd.split()[1])
187

  
188
    def __other_completion(self):
189
        # special cases
190
        self._completer.append('help info')
191

  
192
    def _fill_completion(self):
193
        self.__cmd_completion()
194
        self.__info_completion()
195
        self.__other_completion()
196

  
197
    def __cmd_passthrough(self, cmdline, cpu_index = 0):
198
        return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
199
                              { 'command-line': cmdline,
200
                                'cpu-index': cpu_index } })
201

  
202
    def _execute_cmd(self, cmdline):
203
        if cmdline.split()[0] == "cpu":
204
            # trap the cpu command, it requires special setting
205
            try:
206
                idx = int(cmdline.split()[1])
207
                if not 'return' in self.__cmd_passthrough('info version', idx):
208
                    print 'bad CPU index'
209
                    return True
210
                self.__cpu_index = idx
211
            except ValueError:
212
                print 'cpu command takes an integer argument'
213
                return True
214
        resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
215
        if resp is None:
216
            print 'Disconnected'
217
            return False
218
        assert 'return' in resp or 'error' in resp
219
        if 'return' in resp:
220
            # Success
221
            if len(resp['return']) > 0:
222
                print resp['return'],
223
        else:
224
            # Error
225
            print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
226
        return True
227

  
228
    def show_banner(self):
229
        QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
230

  
231
def die(msg):
232
    sys.stderr.write('ERROR: %s\n' % msg)
233
    sys.exit(1)
234

  
235
def fail_cmdline(option=None):
236
    if option:
237
        sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
238
    sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
239
    sys.exit(1)
240

  
241
def main():
242
    addr = ''
243
    qemu = None
244
    hmp = False
245
    pp = None
246

  
247
    try:
248
        for arg in sys.argv[1:]:
249
            if arg == "-H":
250
                if qemu is not None:
251
                    fail_cmdline(arg)
252
                hmp = True
253
            elif arg == "-p":
254
                if pp is not None:
255
                    fail_cmdline(arg)
256
                pp = pprint.PrettyPrinter(indent=4)
257
            else:
258
                if qemu is not None:
259
                    fail_cmdline(arg)
260
                if hmp:
261
                    qemu = HMPShell(arg)
262
                else:
263
                    qemu = QMPShell(arg, pp)
264
                addr = arg
265

  
266
        if qemu is None:
267
            fail_cmdline()
268
    except QMPShellBadPort:
269
        die('bad port number in command-line')
270

  
271
    try:
272
        qemu.connect()
273
    except qmp.QMPConnectError:
274
        die('Didn\'t get QMP greeting message')
275
    except qmp.QMPCapabilitiesError:
276
        die('Could not negotiate capabilities')
277
    except qemu.error:
278
        die('Could not connect to %s' % addr)
279

  
280
    qemu.show_banner()
281
    while qemu.read_exec_command('(QEMU) '):
282
        pass
283
    qemu.close()
284

  
285
if __name__ == '__main__':
286
    main()
/dev/null
1
# QEMU Monitor Protocol Python class
2
# 
3
# Copyright (C) 2009, 2010 Red Hat Inc.
4
#
5
# Authors:
6
#  Luiz Capitulino <lcapitulino@redhat.com>
7
#
8
# This work is licensed under the terms of the GNU GPL, version 2.  See
9
# the COPYING file in the top-level directory.
10

  
11
import json
12
import errno
13
import socket
14

  
15
class QMPError(Exception):
16
    pass
17

  
18
class QMPConnectError(QMPError):
19
    pass
20

  
21
class QMPCapabilitiesError(QMPError):
22
    pass
23

  
24
class QEMUMonitorProtocol:
25
    def __init__(self, address, server=False):
26
        """
27
        Create a QEMUMonitorProtocol class.
28

  
29
        @param address: QEMU address, can be either a unix socket path (string)
30
                        or a tuple in the form ( address, port ) for a TCP
31
                        connection
32
        @param server: server mode listens on the socket (bool)
33
        @raise socket.error on socket connection errors
34
        @note No connection is established, this is done by the connect() or
35
              accept() methods
36
        """
37
        self.__events = []
38
        self.__address = address
39
        self.__sock = self.__get_sock()
40
        if server:
41
            self.__sock.bind(self.__address)
42
            self.__sock.listen(1)
43

  
44
    def __get_sock(self):
45
        if isinstance(self.__address, tuple):
46
            family = socket.AF_INET
47
        else:
48
            family = socket.AF_UNIX
49
        return socket.socket(family, socket.SOCK_STREAM)
50

  
51
    def __negotiate_capabilities(self):
52
        greeting = self.__json_read()
53
        if greeting is None or not greeting.has_key('QMP'):
54
            raise QMPConnectError
55
        # Greeting seems ok, negotiate capabilities
56
        resp = self.cmd('qmp_capabilities')
57
        if "return" in resp:
58
            return greeting
59
        raise QMPCapabilitiesError
60

  
61
    def __json_read(self, only_event=False):
62
        while True:
63
            data = self.__sockfile.readline()
64
            if not data:
65
                return
66
            resp = json.loads(data)
67
            if 'event' in resp:
68
                self.__events.append(resp)
69
                if not only_event:
70
                    continue
71
            return resp
72

  
73
    error = socket.error
74

  
75
    def connect(self, negotiate=True):
76
        """
77
        Connect to the QMP Monitor and perform capabilities negotiation.
78

  
79
        @return QMP greeting dict
80
        @raise socket.error on socket connection errors
81
        @raise QMPConnectError if the greeting is not received
82
        @raise QMPCapabilitiesError if fails to negotiate capabilities
83
        """
84
        self.__sock.connect(self.__address)
85
        self.__sockfile = self.__sock.makefile()
86
        if negotiate:
87
            return self.__negotiate_capabilities()
88

  
89
    def accept(self):
90
        """
91
        Await connection from QMP Monitor and perform capabilities negotiation.
92

  
93
        @return QMP greeting dict
94
        @raise socket.error on socket connection errors
95
        @raise QMPConnectError if the greeting is not received
96
        @raise QMPCapabilitiesError if fails to negotiate capabilities
97
        """
98
        self.__sock, _ = self.__sock.accept()
99
        self.__sockfile = self.__sock.makefile()
100
        return self.__negotiate_capabilities()
101

  
102
    def cmd_obj(self, qmp_cmd):
103
        """
104
        Send a QMP command to the QMP Monitor.
105

  
106
        @param qmp_cmd: QMP command to be sent as a Python dict
107
        @return QMP response as a Python dict or None if the connection has
108
                been closed
109
        """
110
        try:
111
            self.__sock.sendall(json.dumps(qmp_cmd))
112
        except socket.error, err:
113
            if err[0] == errno.EPIPE:
114
                return
115
            raise socket.error(err)
116
        return self.__json_read()
117

  
118
    def cmd(self, name, args=None, id=None):
119
        """
120
        Build a QMP command and send it to the QMP Monitor.
121

  
122
        @param name: command name (string)
123
        @param args: command arguments (dict)
124
        @param id: command id (dict, list, string or int)
125
        """
126
        qmp_cmd = { 'execute': name }
127
        if args:
128
            qmp_cmd['arguments'] = args
129
        if id:
130
            qmp_cmd['id'] = id
131
        return self.cmd_obj(qmp_cmd)
132

  
133
    def command(self, cmd, **kwds):
134
        ret = self.cmd(cmd, kwds)
135
        if ret.has_key('error'):
136
            raise Exception(ret['error']['desc'])
137
        return ret['return']
138

  
139
    def pull_event(self, wait=False):
140
        """
141
        Get and delete the first available QMP event.
142

  
143
        @param wait: block until an event is available (bool)
144
        """
145
        self.__sock.setblocking(0)
146
        try:
147
            self.__json_read()
148
        except socket.error, err:
149
            if err[0] == errno.EAGAIN:
150
                # No data available
151
                pass
152
        self.__sock.setblocking(1)
153
        if not self.__events and wait:
154
            self.__json_read(only_event=True)
155
        event = self.__events[0]
156
        del self.__events[0]
157
        return event
158

  
159
    def get_events(self, wait=False):
160
        """
161
        Get a list of available QMP events.
162

  
163
        @param wait: block until an event is available (bool)
164
        """
165
        self.__sock.setblocking(0)
166
        try:
167
            self.__json_read()
168
        except socket.error, err:
169
            if err[0] == errno.EAGAIN:
170
                # No data available
171
                pass
172
        self.__sock.setblocking(1)
173
        if not self.__events and wait:
174
            self.__json_read(only_event=True)
175
        return self.__events
176

  
177
    def clear_events(self):
178
        """
179
        Clear current list of pending events.
180
        """
181
        self.__events = []
182

  
183
    def close(self):
184
        self.__sock.close()
185
        self.__sockfile.close()
186

  
187
    timeout = socket.timeout
188

  
189
    def settimeout(self, timeout):
190
        self.__sock.settimeout(timeout)
191

  
192
    def get_sock_fd(self):
193
        return self.__sock.fileno()
194

  
195
    def is_scm_available(self):
196
        return self.__sock.family == socket.AF_UNIX
/dev/null
1
#!/usr/bin/python
2
##
3
# QEMU Object Model test tools
4
#
5
# Copyright IBM, Corp. 2012
6
#
7
# Authors:
8
#  Anthony Liguori   <aliguori@us.ibm.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
11
# the COPYING file in the top-level directory.
12
##
13

  
14
import fuse, stat
15
from fuse import Fuse
16
import os, posix
17
from errno import *
18
from qmp import QEMUMonitorProtocol
19

  
20
fuse.fuse_python_api = (0, 2)
21

  
22
class QOMFS(Fuse):
23
    def __init__(self, qmp, *args, **kwds):
24
        Fuse.__init__(self, *args, **kwds)
25
        self.qmp = qmp
26
        self.qmp.connect()
27
        self.ino_map = {}
28
        self.ino_count = 1
29

  
30
    def get_ino(self, path):
31
        if self.ino_map.has_key(path):
32
            return self.ino_map[path]
33
        self.ino_map[path] = self.ino_count
34
        self.ino_count += 1
35
        return self.ino_map[path]
36

  
37
    def is_object(self, path):
38
        try:
39
            items = self.qmp.command('qom-list', path=path)
40
            return True
41
        except:
42
            return False
43

  
44
    def is_property(self, path):
45
        try:
46
            path, prop = path.rsplit('/', 1)
47
            for item in self.qmp.command('qom-list', path=path):
48
                if item['name'] == prop:
49
                    return True
50
            return False
51
        except:
52
            return False
53

  
54
    def is_link(self, path):
55
        try:
56
            path, prop = path.rsplit('/', 1)
57
            for item in self.qmp.command('qom-list', path=path):
58
                if item['name'] == prop:
59
                    if item['type'].startswith('link<'):
60
                        return True
61
                    return False
62
            return False
63
        except:
64
            return False
65

  
66
    def read(self, path, length, offset):
67
        if not self.is_property(path):
68
            return -ENOENT
69

  
70
        path, prop = path.rsplit('/', 1)
71
        try:
72
            data = str(self.qmp.command('qom-get', path=path, property=prop))
73
            data += '\n' # make values shell friendly
74
        except:
75
            return -EPERM
76

  
77
        if offset > len(data):
78
            return ''
79

  
80
        return str(data[offset:][:length])
81

  
82
    def readlink(self, path):
83
        if not self.is_link(path):
84
            return False
85
        path, prop = path.rsplit('/', 1)
86
        prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
87
        return prefix + str(self.qmp.command('qom-get', path=path,
88
                                             property=prop))
89

  
90
    def getattr(self, path):
91
        if self.is_link(path):
92
            value = posix.stat_result((0755 | stat.S_IFLNK,
93
                                       self.get_ino(path),
94
                                       0,
95
                                       2,
96
                                       1000,
97
                                       1000,
98
                                       4096,
99
                                       0,
100
                                       0,
101
                                       0))
102
        elif self.is_object(path):
103
            value = posix.stat_result((0755 | stat.S_IFDIR,
104
                                       self.get_ino(path),
105
                                       0,
106
                                       2,
107
                                       1000,
108
                                       1000,
109
                                       4096,
110
                                       0,
111
                                       0,
112
                                       0))
113
        elif self.is_property(path):
114
            value = posix.stat_result((0644 | stat.S_IFREG,
115
                                       self.get_ino(path),
116
                                       0,
117
                                       1,
118
                                       1000,
119
                                       1000,
120
                                       4096,
121
                                       0,
122
                                       0,
123
                                       0))
124
        else:
125
            value = -ENOENT
126
        return value
127

  
128
    def readdir(self, path, offset):
129
        yield fuse.Direntry('.')
130
        yield fuse.Direntry('..')
131
        for item in self.qmp.command('qom-list', path=path):
132
            yield fuse.Direntry(str(item['name']))
133

  
134
if __name__ == '__main__':
135
    import sys, os
136

  
137
    fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
138
    fs.main(sys.argv)
/dev/null
1
#!/usr/bin/python
2
##
3
# QEMU Object Model test tools
4
#
5
# Copyright IBM, Corp. 2011
6
#
7
# Authors:
8
#  Anthony Liguori   <aliguori@us.ibm.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
11
# the COPYING file in the top-level directory.
12
##
13

  
14
import sys
15
import os
16
from qmp import QEMUMonitorProtocol
17

  
18
cmd, args = sys.argv[0], sys.argv[1:]
19
socket_path = None
20
path = None
21
prop = None
22

  
23
def usage():
24
    return '''environment variables:
25
    QMP_SOCKET=<path | addr:port>
26
usage:
27
    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
28
''' % cmd
29

  
30
def usage_error(error_msg = "unspecified error"):
31
    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
32
    exit(1)
33

  
34
if len(args) > 0:
35
    if args[0] == "-h":
36
        print usage()
37
        exit(0);
38
    elif args[0] == "-s":
39
        try:
40
            socket_path = args[1]
41
        except:
42
            usage_error("missing argument: QMP socket path or address");
43
        args = args[2:]
44

  
45
if not socket_path:
46
    if os.environ.has_key('QMP_SOCKET'):
47
        socket_path = os.environ['QMP_SOCKET']
48
    else:
49
        usage_error("no QMP socket path or address given");
50

  
51
if len(args) > 0:
52
    try:
53
        path, prop = args[0].rsplit('.', 1)
54
    except:
55
        usage_error("invalid format for path/property/value")
56
else:
57
    usage_error("not enough arguments")
58

  
59
srv = QEMUMonitorProtocol(socket_path)
60
srv.connect()
61

  
62
rsp = srv.command('qom-get', path=path, property=prop)
63
if type(rsp) == dict:
64
    for i in rsp.keys():
65
        print '%s: %s' % (i, rsp[i])
66
else:
67
    print rsp
/dev/null
1
#!/usr/bin/python
2
##
3
# QEMU Object Model test tools
4
#
5
# Copyright IBM, Corp. 2011
6
#
7
# Authors:
8
#  Anthony Liguori   <aliguori@us.ibm.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
11
# the COPYING file in the top-level directory.
12
##
13

  
14
import sys
15
import os
16
from qmp import QEMUMonitorProtocol
17

  
18
cmd, args = sys.argv[0], sys.argv[1:]
19
socket_path = None
20
path = None
21
prop = None
22

  
23
def usage():
24
    return '''environment variables:
25
    QMP_SOCKET=<path | addr:port>
26
usage:
27
    %s [-h] [-s <QMP socket path | addr:port>] [<path>]
28
''' % cmd
29

  
30
def usage_error(error_msg = "unspecified error"):
31
    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
32
    exit(1)
33

  
34
if len(args) > 0:
35
    if args[0] == "-h":
36
        print usage()
37
        exit(0);
38
    elif args[0] == "-s":
39
        try:
40
            socket_path = args[1]
41
        except:
42
            usage_error("missing argument: QMP socket path or address");
43
        args = args[2:]
44

  
45
if not socket_path:
46
    if os.environ.has_key('QMP_SOCKET'):
47
        socket_path = os.environ['QMP_SOCKET']
48
    else:
49
        usage_error("no QMP socket path or address given");
50

  
51
srv = QEMUMonitorProtocol(socket_path)
52
srv.connect()
53

  
54
if len(args) == 0:
55
    print '/'
56
    sys.exit(0)
57

  
58
for item in srv.command('qom-list', path=args[0]):
59
    if item['type'].startswith('child<'):
60
        print '%s/' % item['name']
61
    elif item['type'].startswith('link<'):
62
        print '@%s/' % item['name']
63
    else:
64
        print '%s' % item['name']
/dev/null
1
#!/usr/bin/python
2
##
3
# QEMU Object Model test tools
4
#
5
# Copyright IBM, Corp. 2011
6
#
7
# Authors:
8
#  Anthony Liguori   <aliguori@us.ibm.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
11
# the COPYING file in the top-level directory.
12
##
13

  
14
import sys
15
import os
16
from qmp import QEMUMonitorProtocol
17

  
18
cmd, args = sys.argv[0], sys.argv[1:]
19
socket_path = None
20
path = None
21
prop = None
22
value = None
23

  
24
def usage():
25
    return '''environment variables:
26
    QMP_SOCKET=<path | addr:port>
27
usage:
28
    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
29
''' % cmd
30

  
31
def usage_error(error_msg = "unspecified error"):
32
    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
33
    exit(1)
34

  
35
if len(args) > 0:
36
    if args[0] == "-h":
37
        print usage()
38
        exit(0);
39
    elif args[0] == "-s":
40
        try:
41
            socket_path = args[1]
42
        except:
43
            usage_error("missing argument: QMP socket path or address");
44
        args = args[2:]
45

  
46
if not socket_path:
47
    if os.environ.has_key('QMP_SOCKET'):
48
        socket_path = os.environ['QMP_SOCKET']
49
    else:
50
        usage_error("no QMP socket path or address given");
51

  
52
if len(args) > 1:
53
    try:
54
        path, prop = args[0].rsplit('.', 1)
55
    except:
56
        usage_error("invalid format for path/property/value")
57
    value = args[1]
58
else:
59
    usage_error("not enough arguments")
60

  
61
srv = QEMUMonitorProtocol(socket_path)
62
srv.connect()
63

  
64
print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])
b/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.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

  
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff