aboutsummaryrefslogtreecommitdiff
path: root/scripts/qmp
diff options
context:
space:
mode:
authorLuiz Capitulino <lcapitulino@redhat.com>2013-09-10 16:39:23 -0400
committerLuiz Capitulino <lcapitulino@redhat.com>2013-09-18 08:57:02 -0400
commit22f3946bc5db2090ffc4ea41f2b83d09e58b665e (patch)
treed6685f78fc7eb68479d13ea92968c67f96247f09 /scripts/qmp
parent6c2679fc19560699679200fb42ab4659bcbe7f79 (diff)
QMP: add scripts/qmp
Populate it with all scripts stored in QMP/. Also fixes trailing whitespaces in qmp-shell and qmp.py. Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
Diffstat (limited to 'scripts/qmp')
-rwxr-xr-xscripts/qmp/qemu-ga-client301
-rwxr-xr-xscripts/qmp/qmp126
-rwxr-xr-xscripts/qmp/qmp-shell286
-rw-r--r--scripts/qmp/qmp.py196
-rwxr-xr-xscripts/qmp/qom-fuse138
-rwxr-xr-xscripts/qmp/qom-get67
-rwxr-xr-xscripts/qmp/qom-list64
-rwxr-xr-xscripts/qmp/qom-set64
8 files changed, 1242 insertions, 0 deletions
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
new file mode 100755
index 0000000000..b5f7e7c5ff
--- /dev/null
+++ b/scripts/qmp/qemu-ga-client
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+
+# QEMU Guest Agent Client
+#
+# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
+# -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
+#
+# Run the script:
+#
+# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
+#
+# or
+#
+# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
+# $ qemu-ga-client <command> [args...]
+#
+# For example:
+#
+# $ qemu-ga-client cat /etc/resolv.conf
+# # Generated by NetworkManager
+# nameserver 10.0.2.3
+# $ qemu-ga-client fsfreeze status
+# thawed
+# $ qemu-ga-client fsfreeze freeze
+# 2 filesystems frozen
+#
+# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
+#
+
+import base64
+import random
+
+import qmp
+
+
+class QemuGuestAgent(qmp.QEMUMonitorProtocol):
+ def __getattr__(self, name):
+ def wrapper(**kwds):
+ return self.command('guest-' + name.replace('_', '-'), **kwds)
+ return wrapper
+
+
+class QemuGuestAgentClient:
+ error = QemuGuestAgent.error
+
+ def __init__(self, address):
+ self.qga = QemuGuestAgent(address)
+ self.qga.connect(negotiate=False)
+
+ def sync(self, timeout=3):
+ # Avoid being blocked forever
+ if not self.ping(timeout):
+ raise EnvironmentError('Agent seems not alive')
+ uid = random.randint(0, (1 << 32) - 1)
+ while True:
+ ret = self.qga.sync(id=uid)
+ if isinstance(ret, int) and int(ret) == uid:
+ break
+
+ def __file_read_all(self, handle):
+ eof = False
+ data = ''
+ while not eof:
+ ret = self.qga.file_read(handle=handle, count=1024)
+ _data = base64.b64decode(ret['buf-b64'])
+ data += _data
+ eof = ret['eof']
+ return data
+
+ def read(self, path):
+ handle = self.qga.file_open(path=path)
+ try:
+ data = self.__file_read_all(handle)
+ finally:
+ self.qga.file_close(handle=handle)
+ return data
+
+ def info(self):
+ info = self.qga.info()
+
+ msgs = []
+ msgs.append('version: ' + info['version'])
+ msgs.append('supported_commands:')
+ enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
+ msgs.append('\tenabled: ' + ', '.join(enabled))
+ disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
+ msgs.append('\tdisabled: ' + ', '.join(disabled))
+
+ return '\n'.join(msgs)
+
+ def __gen_ipv4_netmask(self, prefixlen):
+ mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
+ return '.'.join([str(mask >> 24),
+ str((mask >> 16) & 0xff),
+ str((mask >> 8) & 0xff),
+ str(mask & 0xff)])
+
+ def ifconfig(self):
+ nifs = self.qga.network_get_interfaces()
+
+ msgs = []
+ for nif in nifs:
+ msgs.append(nif['name'] + ':')
+ if 'ip-addresses' in nif:
+ for ipaddr in nif['ip-addresses']:
+ if ipaddr['ip-address-type'] == 'ipv4':
+ addr = ipaddr['ip-address']
+ mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
+ msgs.append("\tinet %s netmask %s" % (addr, mask))
+ elif ipaddr['ip-address-type'] == 'ipv6':
+ addr = ipaddr['ip-address']
+ prefix = ipaddr['prefix']
+ msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
+ if nif['hardware-address'] != '00:00:00:00:00:00':
+ msgs.append("\tether " + nif['hardware-address'])
+
+ return '\n'.join(msgs)
+
+ def ping(self, timeout):
+ self.qga.settimeout(timeout)
+ try:
+ self.qga.ping()
+ except self.qga.timeout:
+ return False
+ return True
+
+ def fsfreeze(self, cmd):
+ if cmd not in ['status', 'freeze', 'thaw']:
+ raise StandardError('Invalid command: ' + cmd)
+
+ return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
+
+ def fstrim(self, minimum=0):
+ return getattr(self.qga, 'fstrim')(minimum=minimum)
+
+ def suspend(self, mode):
+ if mode not in ['disk', 'ram', 'hybrid']:
+ raise StandardError('Invalid mode: ' + mode)
+
+ try:
+ getattr(self.qga, 'suspend' + '_' + mode)()
+ # On error exception will raise
+ except self.qga.timeout:
+ # On success command will timed out
+ return
+
+ def shutdown(self, mode='powerdown'):
+ if mode not in ['powerdown', 'halt', 'reboot']:
+ raise StandardError('Invalid mode: ' + mode)
+
+ try:
+ self.qga.shutdown(mode=mode)
+ except self.qga.timeout:
+ return
+
+
+def _cmd_cat(client, args):
+ if len(args) != 1:
+ print('Invalid argument')
+ print('Usage: cat <file>')
+ sys.exit(1)
+ print(client.read(args[0]))
+
+
+def _cmd_fsfreeze(client, args):
+ usage = 'Usage: fsfreeze status|freeze|thaw'
+ if len(args) != 1:
+ print('Invalid argument')
+ print(usage)
+ sys.exit(1)
+ if args[0] not in ['status', 'freeze', 'thaw']:
+ print('Invalid command: ' + args[0])
+ print(usage)
+ sys.exit(1)
+ cmd = args[0]
+ ret = client.fsfreeze(cmd)
+ if cmd == 'status':
+ print(ret)
+ elif cmd == 'freeze':
+ print("%d filesystems frozen" % ret)
+ else:
+ print("%d filesystems thawed" % ret)
+
+
+def _cmd_fstrim(client, args):
+ if len(args) == 0:
+ minimum = 0
+ else:
+ minimum = int(args[0])
+ print(client.fstrim(minimum))
+
+
+def _cmd_ifconfig(client, args):
+ print(client.ifconfig())
+
+
+def _cmd_info(client, args):
+ print(client.info())
+
+
+def _cmd_ping(client, args):
+ if len(args) == 0:
+ timeout = 3
+ else:
+ timeout = float(args[0])
+ alive = client.ping(timeout)
+ if not alive:
+ print("Not responded in %s sec" % args[0])
+ sys.exit(1)
+
+
+def _cmd_suspend(client, args):
+ usage = 'Usage: suspend disk|ram|hybrid'
+ if len(args) != 1:
+ print('Less argument')
+ print(usage)
+ sys.exit(1)
+ if args[0] not in ['disk', 'ram', 'hybrid']:
+ print('Invalid command: ' + args[0])
+ print(usage)
+ sys.exit(1)
+ client.suspend(args[0])
+
+
+def _cmd_shutdown(client, args):
+ client.shutdown()
+_cmd_powerdown = _cmd_shutdown
+
+
+def _cmd_halt(client, args):
+ client.shutdown('halt')
+
+
+def _cmd_reboot(client, args):
+ client.shutdown('reboot')
+
+
+commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
+
+
+def main(address, cmd, args):
+ if not os.path.exists(address):
+ print('%s not found' % address)
+ sys.exit(1)
+
+ if cmd not in commands:
+ print('Invalid command: ' + cmd)
+ print('Available commands: ' + ', '.join(commands))
+ sys.exit(1)
+
+ try:
+ client = QemuGuestAgentClient(address)
+ except QemuGuestAgent.error, e:
+ import errno
+
+ print(e)
+ if e.errno == errno.ECONNREFUSED:
+ print('Hint: qemu is not running?')
+ sys.exit(1)
+
+ if cmd == 'fsfreeze' and args[0] == 'freeze':
+ client.sync(60)
+ elif cmd != 'ping':
+ client.sync()
+
+ globals()['_cmd_' + cmd](client, args)
+
+
+if __name__ == '__main__':
+ import sys
+ import os
+ import optparse
+
+ address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
+
+ usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
+ usage += '<command>: ' + ', '.join(commands)
+ parser = optparse.OptionParser(usage=usage)
+ parser.add_option('--address', action='store', type='string',
+ default=address, help='Specify a ip:port pair or a unix socket path')
+ options, args = parser.parse_args()
+
+ address = options.address
+ if address is None:
+ parser.error('address is not specified')
+ sys.exit(1)
+
+ if len(args) == 0:
+ parser.error('Less argument')
+ sys.exit(1)
+
+ main(address, args[0], args[1:])
diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp
new file mode 100755
index 0000000000..1db3c7ffeb
--- /dev/null
+++ b/scripts/qmp/qmp
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+#
+# QMP command line tool
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys, os
+from qmp import QEMUMonitorProtocol
+
+def print_response(rsp, prefix=[]):
+ if type(rsp) == list:
+ i = 0
+ for item in rsp:
+ if prefix == []:
+ prefix = ['item']
+ print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
+ i += 1
+ elif type(rsp) == dict:
+ for key in rsp.keys():
+ print_response(rsp[key], prefix + [key])
+ else:
+ if len(prefix):
+ print '%s: %s' % ('.'.join(prefix), rsp)
+ else:
+ print '%s' % (rsp)
+
+def main(args):
+ path = None
+
+ # Use QMP_PATH if it's set
+ if os.environ.has_key('QMP_PATH'):
+ path = os.environ['QMP_PATH']
+
+ while len(args):
+ arg = args[0]
+
+ if arg.startswith('--'):
+ arg = arg[2:]
+ if arg.find('=') == -1:
+ value = True
+ else:
+ arg, value = arg.split('=', 1)
+
+ if arg in ['path']:
+ if type(value) == str:
+ path = value
+ elif arg in ['help']:
+ os.execlp('man', 'man', 'qmp')
+ else:
+ print 'Unknown argument "%s"' % arg
+
+ args = args[1:]
+ else:
+ break
+
+ if not path:
+ print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
+ return 1
+
+ if len(args):
+ command, args = args[0], args[1:]
+ else:
+ print 'No command found'
+ print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
+ return 1
+
+ if command in ['help']:
+ os.execlp('man', 'man', 'qmp')
+
+ srv = QEMUMonitorProtocol(path)
+ srv.connect()
+
+ def do_command(srv, cmd, **kwds):
+ rsp = srv.cmd(cmd, kwds)
+ if rsp.has_key('error'):
+ raise Exception(rsp['error']['desc'])
+ return rsp['return']
+
+ commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
+
+ srv.close()
+
+ if command not in commands:
+ fullcmd = 'qmp-%s' % command
+ try:
+ os.environ['QMP_PATH'] = path
+ os.execvp(fullcmd, [fullcmd] + args)
+ except OSError, (errno, msg):
+ if errno == 2:
+ print 'Command "%s" not found.' % (fullcmd)
+ return 1
+ raise
+ return 0
+
+ srv = QEMUMonitorProtocol(path)
+ srv.connect()
+
+ arguments = {}
+ for arg in args:
+ if not arg.startswith('--'):
+ print 'Unknown argument "%s"' % arg
+ return 1
+
+ arg = arg[2:]
+ if arg.find('=') == -1:
+ value = True
+ else:
+ arg, value = arg.split('=', 1)
+
+ if arg in ['help']:
+ os.execlp('man', 'man', 'qmp-%s' % command)
+ return 1
+
+ arguments[arg] = value
+
+ rsp = do_command(srv, command, **arguments)
+ print_response(rsp)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
new file mode 100755
index 0000000000..d6b420f18a
--- /dev/null
+++ b/scripts/qmp/qmp-shell
@@ -0,0 +1,286 @@
+#!/usr/bin/python
+#
+# Low-level QEMU shell on top of QMP.
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# # qemu [...] -qmp unix:./qmp-sock,server
+#
+# Run the shell:
+#
+# $ qmp-shell ./qmp-sock
+#
+# Commands have the following format:
+#
+# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+#
+# For example:
+#
+# (QEMU) device_add driver=e1000 id=net1
+# {u'return': {}}
+# (QEMU)
+
+import qmp
+import readline
+import sys
+import pprint
+
+class QMPCompleter(list):
+ def complete(self, text, state):
+ for cmd in self:
+ if cmd.startswith(text):
+ if not state:
+ return cmd
+ else:
+ state -= 1
+
+class QMPShellError(Exception):
+ pass
+
+class QMPShellBadPort(QMPShellError):
+ pass
+
+# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
+# _execute_cmd()). Let's design a better one.
+class QMPShell(qmp.QEMUMonitorProtocol):
+ def __init__(self, address, pp=None):
+ qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
+ self._greeting = None
+ self._completer = None
+ self._pp = pp
+
+ def __get_address(self, arg):
+ """
+ Figure out if the argument is in the port:host form, if it's not it's
+ probably a file path.
+ """
+ addr = arg.split(':')
+ if len(addr) == 2:
+ try:
+ port = int(addr[1])
+ except ValueError:
+ raise QMPShellBadPort
+ return ( addr[0], port )
+ # socket path
+ return arg
+
+ def _fill_completion(self):
+ for cmd in self.cmd('query-commands')['return']:
+ self._completer.append(cmd['name'])
+
+ def __completer_setup(self):
+ self._completer = QMPCompleter()
+ self._fill_completion()
+ readline.set_completer(self._completer.complete)
+ readline.parse_and_bind("tab: complete")
+ # XXX: default delimiters conflict with some command names (eg. query-),
+ # clearing everything as it doesn't seem to matter
+ readline.set_completer_delims('')
+
+ def __build_cmd(self, cmdline):
+ """
+ Build a QMP input object from a user provided command-line in the
+ following format:
+
+ < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+ """
+ cmdargs = cmdline.split()
+ qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
+ for arg in cmdargs[1:]:
+ opt = arg.split('=')
+ try:
+ if(len(opt) > 2):
+ opt[1] = '='.join(opt[1:])
+ value = int(opt[1])
+ except ValueError:
+ if opt[1] == 'true':
+ value = True
+ elif opt[1] == 'false':
+ value = False
+ else:
+ value = opt[1]
+ qmpcmd['arguments'][opt[0]] = value
+ return qmpcmd
+
+ def _execute_cmd(self, cmdline):
+ try:
+ qmpcmd = self.__build_cmd(cmdline)
+ except:
+ print 'command format: <command-name> ',
+ print '[arg-name1=arg1] ... [arg-nameN=argN]'
+ return True
+ resp = self.cmd_obj(qmpcmd)
+ if resp is None:
+ print 'Disconnected'
+ return False
+
+ if self._pp is not None:
+ self._pp.pprint(resp)
+ else:
+ print resp
+ return True
+
+ def connect(self):
+ self._greeting = qmp.QEMUMonitorProtocol.connect(self)
+ self.__completer_setup()
+
+ def show_banner(self, msg='Welcome to the QMP low-level shell!'):
+ print msg
+ version = self._greeting['QMP']['version']['qemu']
+ print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
+
+ def read_exec_command(self, prompt):
+ """
+ Read and execute a command.
+
+ @return True if execution was ok, return False if disconnected.
+ """
+ try:
+ cmdline = raw_input(prompt)
+ except EOFError:
+ print
+ return False
+ if cmdline == '':
+ for ev in self.get_events():
+ print ev
+ self.clear_events()
+ return True
+ else:
+ return self._execute_cmd(cmdline)
+
+class HMPShell(QMPShell):
+ def __init__(self, address):
+ QMPShell.__init__(self, address)
+ self.__cpu_index = 0
+
+ def __cmd_completion(self):
+ for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
+ if cmd and cmd[0] != '[' and cmd[0] != '\t':
+ name = cmd.split()[0] # drop help text
+ if name == 'info':
+ continue
+ if name.find('|') != -1:
+ # Command in the form 'foobar|f' or 'f|foobar', take the
+ # full name
+ opt = name.split('|')
+ if len(opt[0]) == 1:
+ name = opt[1]
+ else:
+ name = opt[0]
+ self._completer.append(name)
+ self._completer.append('help ' + name) # help completion
+
+ def __info_completion(self):
+ for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
+ if cmd:
+ self._completer.append('info ' + cmd.split()[1])
+
+ def __other_completion(self):
+ # special cases
+ self._completer.append('help info')
+
+ def _fill_completion(self):
+ self.__cmd_completion()
+ self.__info_completion()
+ self.__other_completion()
+
+ def __cmd_passthrough(self, cmdline, cpu_index = 0):
+ return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
+ { 'command-line': cmdline,
+ 'cpu-index': cpu_index } })
+
+ def _execute_cmd(self, cmdline):
+ if cmdline.split()[0] == "cpu":
+ # trap the cpu command, it requires special setting
+ try:
+ idx = int(cmdline.split()[1])
+ if not 'return' in self.__cmd_passthrough('info version', idx):
+ print 'bad CPU index'
+ return True
+ self.__cpu_index = idx
+ except ValueError:
+ print 'cpu command takes an integer argument'
+ return True
+ resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
+ if resp is None:
+ print 'Disconnected'
+ return False
+ assert 'return' in resp or 'error' in resp
+ if 'return' in resp:
+ # Success
+ if len(resp['return']) > 0:
+ print resp['return'],
+ else:
+ # Error
+ print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
+ return True
+
+ def show_banner(self):
+ QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
+
+def die(msg):
+ sys.stderr.write('ERROR: %s\n' % msg)
+ sys.exit(1)
+
+def fail_cmdline(option=None):
+ if option:
+ sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
+ sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
+ sys.exit(1)
+
+def main():
+ addr = ''
+ qemu = None
+ hmp = False
+ pp = None
+
+ try:
+ for arg in sys.argv[1:]:
+ if arg == "-H":
+ if qemu is not None:
+ fail_cmdline(arg)
+ hmp = True
+ elif arg == "-p":
+ if pp is not None:
+ fail_cmdline(arg)
+ pp = pprint.PrettyPrinter(indent=4)
+ else:
+ if qemu is not None:
+ fail_cmdline(arg)
+ if hmp:
+ qemu = HMPShell(arg)
+ else:
+ qemu = QMPShell(arg, pp)
+ addr = arg
+
+ if qemu is None:
+ fail_cmdline()
+ except QMPShellBadPort:
+ die('bad port number in command-line')
+
+ try:
+ qemu.connect()
+ except qmp.QMPConnectError:
+ die('Didn\'t get QMP greeting message')
+ except qmp.QMPCapabilitiesError:
+ die('Could not negotiate capabilities')
+ except qemu.error:
+ die('Could not connect to %s' % addr)
+
+ qemu.show_banner()
+ while qemu.read_exec_command('(QEMU) '):
+ pass
+ qemu.close()
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py
new file mode 100644
index 0000000000..5c9717594f
--- /dev/null
+++ b/scripts/qmp/qmp.py
@@ -0,0 +1,196 @@
+# QEMU Monitor Protocol Python class
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+import json
+import errno
+import socket
+
+class QMPError(Exception):
+ pass
+
+class QMPConnectError(QMPError):
+ pass
+
+class QMPCapabilitiesError(QMPError):
+ pass
+
+class QEMUMonitorProtocol:
+ def __init__(self, address, server=False):
+ """
+ Create a QEMUMonitorProtocol class.
+
+ @param address: QEMU address, can be either a unix socket path (string)
+ or a tuple in the form ( address, port ) for a TCP
+ connection
+ @param server: server mode listens on the socket (bool)
+ @raise socket.error on socket connection errors
+ @note No connection is established, this is done by the connect() or
+ accept() methods
+ """
+ self.__events = []
+ self.__address = address
+ self.__sock = self.__get_sock()
+ if server:
+ self.__sock.bind(self.__address)
+ self.__sock.listen(1)
+
+ def __get_sock(self):
+ if isinstance(self.__address, tuple):
+ family = socket.AF_INET
+ else:
+ family = socket.AF_UNIX
+ return socket.socket(family, socket.SOCK_STREAM)
+
+ def __negotiate_capabilities(self):
+ greeting = self.__json_read()
+ if greeting is None or not greeting.has_key('QMP'):
+ raise QMPConnectError
+ # Greeting seems ok, negotiate capabilities
+ resp = self.cmd('qmp_capabilities')
+ if "return" in resp:
+ return greeting
+ raise QMPCapabilitiesError
+
+ def __json_read(self, only_event=False):
+ while True:
+ data = self.__sockfile.readline()
+ if not data:
+ return
+ resp = json.loads(data)
+ if 'event' in resp:
+ self.__events.append(resp)
+ if not only_event:
+ continue
+ return resp
+
+ error = socket.error
+
+ def connect(self, negotiate=True):
+ """
+ Connect to the QMP Monitor and perform capabilities negotiation.
+
+ @return QMP greeting dict
+ @raise socket.error on socket connection errors
+ @raise QMPConnectError if the greeting is not received
+ @raise QMPCapabilitiesError if fails to negotiate capabilities
+ """
+ self.__sock.connect(self.__address)
+ self.__sockfile = self.__sock.makefile()
+ if negotiate:
+ return self.__negotiate_capabilities()
+
+ def accept(self):
+ """
+ Await connection from QMP Monitor and perform capabilities negotiation.
+
+ @return QMP greeting dict
+ @raise socket.error on socket connection errors
+ @raise QMPConnectError if the greeting is not received
+ @raise QMPCapabilitiesError if fails to negotiate capabilities
+ """
+ self.__sock, _ = self.__sock.accept()
+ self.__sockfile = self.__sock.makefile()
+ return self.__negotiate_capabilities()
+
+ def cmd_obj(self, qmp_cmd):
+ """
+ Send a QMP command to the QMP Monitor.
+
+ @param qmp_cmd: QMP command to be sent as a Python dict
+ @return QMP response as a Python dict or None if the connection has
+ been closed
+ """
+ try:
+ self.__sock.sendall(json.dumps(qmp_cmd))
+ except socket.error, err:
+ if err[0] == errno.EPIPE:
+ return
+ raise socket.error(err)
+ return self.__json_read()
+
+ def cmd(self, name, args=None, id=None):
+ """
+ Build a QMP command and send it to the QMP Monitor.
+
+ @param name: command name (string)
+ @param args: command arguments (dict)
+ @param id: command id (dict, list, string or int)
+ """
+ qmp_cmd = { 'execute': name }
+ if args:
+ qmp_cmd['arguments'] = args
+ if id:
+ qmp_cmd['id'] = id
+ return self.cmd_obj(qmp_cmd)
+
+ def command(self, cmd, **kwds):
+ ret = self.cmd(cmd, kwds)
+ if ret.has_key('error'):
+ raise Exception(ret['error']['desc'])
+ return ret['return']
+
+ def pull_event(self, wait=False):
+ """
+ Get and delete the first available QMP event.
+
+ @param wait: block until an event is available (bool)
+ """
+ self.__sock.setblocking(0)
+ try:
+ self.__json_read()
+ except socket.error, err:
+ if err[0] == errno.EAGAIN:
+ # No data available
+ pass
+ self.__sock.setblocking(1)
+ if not self.__events and wait:
+ self.__json_read(only_event=True)
+ event = self.__events[0]
+ del self.__events[0]
+ return event
+
+ def get_events(self, wait=False):
+ """
+ Get a list of available QMP events.
+
+ @param wait: block until an event is available (bool)
+ """
+ self.__sock.setblocking(0)
+ try:
+ self.__json_read()
+ except socket.error, err:
+ if err[0] == errno.EAGAIN:
+ # No data available
+ pass
+ self.__sock.setblocking(1)
+ if not self.__events and wait:
+ self.__json_read(only_event=True)
+ return self.__events
+
+ def clear_events(self):
+ """
+ Clear current list of pending events.
+ """
+ self.__events = []
+
+ def close(self):
+ self.__sock.close()
+ self.__sockfile.close()
+
+ timeout = socket.timeout
+
+ def settimeout(self, timeout):
+ self.__sock.settimeout(timeout)
+
+ def get_sock_fd(self):
+ return self.__sock.fileno()
+
+ def is_scm_available(self):
+ return self.__sock.family == socket.AF_UNIX
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
new file mode 100755
index 0000000000..5c6754aa63
--- /dev/null
+++ b/scripts/qmp/qom-fuse
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2012
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later. See
+# the COPYING file in the top-level directory.
+##
+
+import fuse, stat
+from fuse import Fuse
+import os, posix
+from errno import *
+from qmp import QEMUMonitorProtocol
+
+fuse.fuse_python_api = (0, 2)
+
+class QOMFS(Fuse):
+ def __init__(self, qmp, *args, **kwds):
+ Fuse.__init__(self, *args, **kwds)
+ self.qmp = qmp
+ self.qmp.connect()
+ self.ino_map = {}
+ self.ino_count = 1
+
+ def get_ino(self, path):
+ if self.ino_map.has_key(path):
+ return self.ino_map[path]
+ self.ino_map[path] = self.ino_count
+ self.ino_count += 1
+ return self.ino_map[path]
+
+ def is_object(self, path):
+ try:
+ items = self.qmp.command('qom-list', path=path)
+ return True
+ except:
+ return False
+
+ def is_property(self, path):
+ try:
+ path, prop = path.rsplit('/', 1)
+ for item in self.qmp.command('qom-list', path=path):
+ if item['name'] == prop:
+ return True
+ return False
+ except:
+ return False
+
+ def is_link(self, path):
+ try:
+ path, prop = path.rsplit('/', 1)
+ for item in self.qmp.command('qom-list', path=path):
+ if item['name'] == prop:
+ if item['type'].startswith('link<'):
+ return True
+ return False
+ return False
+ except:
+ return False
+
+ def read(self, path, length, offset):
+ if not self.is_property(path):
+ return -ENOENT
+
+ path, prop = path.rsplit('/', 1)
+ try:
+ data = str(self.qmp.command('qom-get', path=path, property=prop))
+ data += '\n' # make values shell friendly
+ except:
+ return -EPERM
+
+ if offset > len(data):
+ return ''
+
+ return str(data[offset:][:length])
+
+ def readlink(self, path):
+ if not self.is_link(path):
+ return False
+ path, prop = path.rsplit('/', 1)
+ prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
+ return prefix + str(self.qmp.command('qom-get', path=path,
+ property=prop))
+
+ def getattr(self, path):
+ if self.is_link(path):
+ value = posix.stat_result((0755 | stat.S_IFLNK,
+ self.get_ino(path),
+ 0,
+ 2,
+ 1000,
+ 1000,
+ 4096,
+ 0,
+ 0,
+ 0))
+ elif self.is_object(path):
+ value = posix.stat_result((0755 | stat.S_IFDIR,
+ self.get_ino(path),
+ 0,
+ 2,
+ 1000,
+ 1000,
+ 4096,
+ 0,
+ 0,
+ 0))
+ elif self.is_property(path):
+ value = posix.stat_result((0644 | stat.S_IFREG,
+ self.get_ino(path),
+ 0,
+ 1,
+ 1000,
+ 1000,
+ 4096,
+ 0,
+ 0,
+ 0))
+ else:
+ value = -ENOENT
+ return value
+
+ def readdir(self, path, offset):
+ yield fuse.Direntry('.')
+ yield fuse.Direntry('..')
+ for item in self.qmp.command('qom-list', path=path):
+ yield fuse.Direntry(str(item['name']))
+
+if __name__ == '__main__':
+ import sys, os
+
+ fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
+ fs.main(sys.argv)
diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get
new file mode 100755
index 0000000000..0172c69441
--- /dev/null
+++ b/scripts/qmp/qom-get
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later. See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+
+def usage():
+ return '''environment variables:
+ QMP_SOCKET=<path | addr:port>
+usage:
+ %s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+ sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+ exit(1)
+
+if len(args) > 0:
+ if args[0] == "-h":
+ print usage()
+ exit(0);
+ elif args[0] == "-s":
+ try:
+ socket_path = args[1]
+ except:
+ usage_error("missing argument: QMP socket path or address");
+ args = args[2:]
+
+if not socket_path:
+ if os.environ.has_key('QMP_SOCKET'):
+ socket_path = os.environ['QMP_SOCKET']
+ else:
+ usage_error("no QMP socket path or address given");
+
+if len(args) > 0:
+ try:
+ path, prop = args[0].rsplit('.', 1)
+ except:
+ usage_error("invalid format for path/property/value")
+else:
+ usage_error("not enough arguments")
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+rsp = srv.command('qom-get', path=path, property=prop)
+if type(rsp) == dict:
+ for i in rsp.keys():
+ print '%s: %s' % (i, rsp[i])
+else:
+ print rsp
diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list
new file mode 100755
index 0000000000..1e7cc6cb2d
--- /dev/null
+++ b/scripts/qmp/qom-list
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later. See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+
+def usage():
+ return '''environment variables:
+ QMP_SOCKET=<path | addr:port>
+usage:
+ %s [-h] [-s <QMP socket path | addr:port>] [<path>]
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+ sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+ exit(1)
+
+if len(args) > 0:
+ if args[0] == "-h":
+ print usage()
+ exit(0);
+ elif args[0] == "-s":
+ try:
+ socket_path = args[1]
+ except:
+ usage_error("missing argument: QMP socket path or address");
+ args = args[2:]
+
+if not socket_path:
+ if os.environ.has_key('QMP_SOCKET'):
+ socket_path = os.environ['QMP_SOCKET']
+ else:
+ usage_error("no QMP socket path or address given");
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+if len(args) == 0:
+ print '/'
+ sys.exit(0)
+
+for item in srv.command('qom-list', path=args[0]):
+ if item['type'].startswith('child<'):
+ print '%s/' % item['name']
+ elif item['type'].startswith('link<'):
+ print '@%s/' % item['name']
+ else:
+ print '%s' % item['name']
diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set
new file mode 100755
index 0000000000..54ecfecc53
--- /dev/null
+++ b/scripts/qmp/qom-set
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later. See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+value = None
+
+def usage():
+ return '''environment variables:
+ QMP_SOCKET=<path | addr:port>
+usage:
+ %s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+ sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+ exit(1)
+
+if len(args) > 0:
+ if args[0] == "-h":
+ print usage()
+ exit(0);
+ elif args[0] == "-s":
+ try:
+ socket_path = args[1]
+ except:
+ usage_error("missing argument: QMP socket path or address");
+ args = args[2:]
+
+if not socket_path:
+ if os.environ.has_key('QMP_SOCKET'):
+ socket_path = os.environ['QMP_SOCKET']
+ else:
+ usage_error("no QMP socket path or address given");
+
+if len(args) > 1:
+ try:
+ path, prop = args[0].rsplit('.', 1)
+ except:
+ usage_error("invalid format for path/property/value")
+ value = args[1]
+else:
+ usage_error("not enough arguments")
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])