mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00
Python Pull request
Moves QMP-related tools not used for build or automatic testing from scripts/ to python/qemu/qmp/ where they will be protected from bitrot by the check-python-* CI jobs. stub forwarders are left in the old locations for now. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmDNJhAACgkQfe+BBqr8 OQ5uZg/8CxwwDVskXya2BWa0WHrqQGmods2XMVupp9qI5HQAPE6AzlypUzvoA+6o jr5uenG/CGSNZgWUfGN3UxCw85knEGLekU9u3Rx/N+CmlHvNPZz8UUg0oOy9+QIV owFYXPq4Uc9siUjvFhFNmC6ZIUGf2y5vXOECqjTZL4SM9Qq0F3mdrFUdT78AR2jw HTUipCiaZQLFArlxqIFvIv/wR4i+zh9cXs4A31THjoLBz6kkgX89D+yb9cUDwcjp fDBYWuAN7dJzt1jqxuD1yUj3rqwKj0MZa6QXd0pKBQt3fHJCE4geY+mGzmGd9nTx ZZ8RI64CEjH946eNpRFCl+DPqLNE88pTbRpUHXy3YOvNj1ZWGjPtA6aDqcpQdySL UcQxc5K0zKROh9vUIMmmXW4oGlv9xgilO0BnMq5oMTvolaflaF1JdPpmjd+0pHJ2 u9/I/+Bc5tJEmSYPu4ASh0UVllAVSSjnopLcjLJv9g8yApDxuodfyNafdctbsVmL YjiSedsPI2Hawm7O54cE1D6fkjopxwFaEuv1CyTqiDcULk6qWSDj3KtnrDOp0BNV qpN6ReK75UHBzhkdgoKWLm3WuZZk9s2r58e9GAPb7UxR3EiaPOBLOYrKawooqiI7 +LDTqXNbVQJBpe1IgkygZiy2GuQ8unQCsxqhjy7nn6M01/SrPEc= =YTLz -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jsnow-gitlab/tags/python-pull-request' into staging Python Pull request Moves QMP-related tools not used for build or automatic testing from scripts/ to python/qemu/qmp/ where they will be protected from bitrot by the check-python-* CI jobs. stub forwarders are left in the old locations for now. # gpg: Signature made Sat 19 Jun 2021 00:02:40 BST # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full] # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * remotes/jsnow-gitlab/tags/python-pull-request: (72 commits) scripts/qmp-shell: add redirection shim python: add qmp-shell entry point scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py scripts/qmp-shell: add docstrings scripts/qmp-shell: make QMPShellError inherit QMPError scripts/qmp-shell: remove double-underscores scripts/qmp-shell: convert usage comment to docstring scripts/qmp-shell: Remove too-broad-exception scripts/qmp-shell: Fix empty-transaction invocation scripts/qmp-shell: remove TODO scripts/qmp-shell: use logging to show warnings scripts/qmp-shell: Use context manager instead of atexit python/qmp: return generic type from context manager scripts/qmp-shell: unprivatize 'pretty' property scripts/qmp-shell: Accept SocketAddrT instead of string scripts/qmp-shell: add mypy types python/qmp: add QMPObject type alias scripts/qmp-shell: initialize completer early scripts/qmp-shell: refactor QMPCompleter scripts/qmp-shell: Fix "FuzzyJSON" parser ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0add99ea3e
15 changed files with 1713 additions and 1155 deletions
|
@ -1,304 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# 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=on,wait=off,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: https://wiki.qemu.org/Features/QAPI/GuestAgent
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import base64
|
||||
import random
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu 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 Exception('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 Exception('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 Exception('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 as 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)
|
||||
from qemu.qmp import qemu_ga_client
|
||||
|
||||
|
||||
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:])
|
||||
sys.exit(qemu_ga_client.main())
|
||||
|
|
|
@ -1,459 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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)
|
||||
#
|
||||
# key=value pairs also support Python or JSON object literal subset notations,
|
||||
# without spaces. Dictionaries/objects {} are supported as are arrays [].
|
||||
#
|
||||
# example-command arg-name1={'key':'value','obj'={'prop':"value"}}
|
||||
#
|
||||
# Both JSON and Python formatting should work, including both styles of
|
||||
# string literal quotes. Both paradigms of literal values should work,
|
||||
# including null/true/false for JSON and None/True/False for Python.
|
||||
#
|
||||
#
|
||||
# Transactions have the following multi-line format:
|
||||
#
|
||||
# transaction(
|
||||
# action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ]
|
||||
# ...
|
||||
# action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ]
|
||||
# )
|
||||
#
|
||||
# One line transactions are also supported:
|
||||
#
|
||||
# transaction( action-name1 ... )
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# (QEMU) transaction(
|
||||
# TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
|
||||
# TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
|
||||
# TRANS> )
|
||||
# {"return": {}}
|
||||
# (QEMU)
|
||||
#
|
||||
# Use the -v and -p options to activate the verbose and pretty-print options,
|
||||
# which will echo back the properly formatted JSON-compliant QMP that is being
|
||||
# sent to QEMU, which is useful for debugging and documentation generation.
|
||||
|
||||
import json
|
||||
import ast
|
||||
import readline
|
||||
import sys
|
||||
import os
|
||||
import errno
|
||||
import atexit
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import qmp
|
||||
from qemu.qmp import qmp_shell
|
||||
|
||||
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
|
||||
|
||||
class FuzzyJSON(ast.NodeTransformer):
|
||||
'''This extension of ast.NodeTransformer filters literal "true/false/null"
|
||||
values in an AST and replaces them by proper "True/False/None" values that
|
||||
Python can properly evaluate.'''
|
||||
def visit_Name(self, node):
|
||||
if node.id == 'true':
|
||||
node.id = 'True'
|
||||
if node.id == 'false':
|
||||
node.id = 'False'
|
||||
if node.id == 'null':
|
||||
node.id = 'None'
|
||||
return node
|
||||
|
||||
# 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, pretty=False):
|
||||
super(QMPShell, self).__init__(self.__get_address(address))
|
||||
self._greeting = None
|
||||
self._completer = None
|
||||
self._pretty = pretty
|
||||
self._transmode = False
|
||||
self._actions = list()
|
||||
self._histfile = os.path.join(os.path.expanduser('~'),
|
||||
'.qmp-shell_history')
|
||||
|
||||
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):
|
||||
cmds = self.cmd('query-commands')
|
||||
if 'error' in cmds:
|
||||
return
|
||||
for cmd in cmds['return']:
|
||||
self._completer.append(cmd['name'])
|
||||
|
||||
def __completer_setup(self):
|
||||
self._completer = QMPCompleter()
|
||||
self._fill_completion()
|
||||
readline.set_history_length(1024)
|
||||
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('')
|
||||
try:
|
||||
readline.read_history_file(self._histfile)
|
||||
except Exception as e:
|
||||
if isinstance(e, IOError) and e.errno == errno.ENOENT:
|
||||
# File not found. No problem.
|
||||
pass
|
||||
else:
|
||||
print("Failed to read history '%s'; %s" % (self._histfile, e))
|
||||
atexit.register(self.__save_history)
|
||||
|
||||
def __save_history(self):
|
||||
try:
|
||||
readline.write_history_file(self._histfile)
|
||||
except Exception as e:
|
||||
print("Failed to save history file '%s'; %s" % (self._histfile, e))
|
||||
|
||||
def __parse_value(self, val):
|
||||
try:
|
||||
return int(val)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if val.lower() == 'true':
|
||||
return True
|
||||
if val.lower() == 'false':
|
||||
return False
|
||||
if val.startswith(('{', '[')):
|
||||
# Try first as pure JSON:
|
||||
try:
|
||||
return json.loads(val)
|
||||
except ValueError:
|
||||
pass
|
||||
# Try once again as FuzzyJSON:
|
||||
try:
|
||||
st = ast.parse(val, mode='eval')
|
||||
return ast.literal_eval(FuzzyJSON().visit(st))
|
||||
except SyntaxError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
return val
|
||||
|
||||
def __cli_expr(self, tokens, parent):
|
||||
for arg in tokens:
|
||||
(key, sep, val) = arg.partition('=')
|
||||
if sep != '=':
|
||||
raise QMPShellError("Expected a key=value pair, got '%s'" % arg)
|
||||
|
||||
value = self.__parse_value(val)
|
||||
optpath = key.split('.')
|
||||
curpath = []
|
||||
for p in optpath[:-1]:
|
||||
curpath.append(p)
|
||||
d = parent.get(p, {})
|
||||
if type(d) is not dict:
|
||||
raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
|
||||
parent[p] = d
|
||||
parent = d
|
||||
if optpath[-1] in parent:
|
||||
if type(parent[optpath[-1]]) is dict:
|
||||
raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
|
||||
else:
|
||||
raise QMPShellError('Cannot set "%s" multiple times' % key)
|
||||
parent[optpath[-1]] = value
|
||||
|
||||
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 = re.findall(r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+''', cmdline)
|
||||
|
||||
# Transactional CLI entry/exit:
|
||||
if cmdargs[0] == 'transaction(':
|
||||
self._transmode = True
|
||||
cmdargs.pop(0)
|
||||
elif cmdargs[0] == ')' and self._transmode:
|
||||
self._transmode = False
|
||||
if len(cmdargs) > 1:
|
||||
raise QMPShellError("Unexpected input after close of Transaction sub-shell")
|
||||
qmpcmd = { 'execute': 'transaction',
|
||||
'arguments': { 'actions': self._actions } }
|
||||
self._actions = list()
|
||||
return qmpcmd
|
||||
|
||||
# Nothing to process?
|
||||
if not cmdargs:
|
||||
return None
|
||||
|
||||
# Parse and then cache this Transactional Action
|
||||
if self._transmode:
|
||||
finalize = False
|
||||
action = { 'type': cmdargs[0], 'data': {} }
|
||||
if cmdargs[-1] == ')':
|
||||
cmdargs.pop(-1)
|
||||
finalize = True
|
||||
self.__cli_expr(cmdargs[1:], action['data'])
|
||||
self._actions.append(action)
|
||||
return self.__build_cmd(')') if finalize else None
|
||||
|
||||
# Standard command: parse and return it to be executed.
|
||||
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
|
||||
self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
|
||||
return qmpcmd
|
||||
|
||||
def _print(self, qmp):
|
||||
indent = None
|
||||
if self._pretty:
|
||||
indent = 4
|
||||
jsobj = json.dumps(qmp, indent=indent, sort_keys=self._pretty)
|
||||
print(str(jsobj))
|
||||
|
||||
def _execute_cmd(self, cmdline):
|
||||
try:
|
||||
qmpcmd = self.__build_cmd(cmdline)
|
||||
except Exception as e:
|
||||
print('Error while parsing command line: %s' % e)
|
||||
print('command format: <command-name> ', end=' ')
|
||||
print('[arg-name1=arg1] ... [arg-nameN=argN]')
|
||||
return True
|
||||
# For transaction mode, we may have just cached the action:
|
||||
if qmpcmd is None:
|
||||
return True
|
||||
if self._verbose:
|
||||
self._print(qmpcmd)
|
||||
resp = self.cmd_obj(qmpcmd)
|
||||
if resp is None:
|
||||
print('Disconnected')
|
||||
return False
|
||||
self._print(resp)
|
||||
return True
|
||||
|
||||
def connect(self, negotiate):
|
||||
self._greeting = super(QMPShell, self).connect(negotiate)
|
||||
self.__completer_setup()
|
||||
|
||||
def show_banner(self, msg='Welcome to the QMP low-level shell!'):
|
||||
print(msg)
|
||||
if not self._greeting:
|
||||
print('Connected')
|
||||
return
|
||||
version = self._greeting['QMP']['version']['qemu']
|
||||
print('Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']))
|
||||
|
||||
def get_prompt(self):
|
||||
if self._transmode:
|
||||
return "TRANS> "
|
||||
return "(QEMU) "
|
||||
|
||||
def read_exec_command(self, prompt):
|
||||
"""
|
||||
Read and execute a command.
|
||||
|
||||
@return True if execution was ok, return False if disconnected.
|
||||
"""
|
||||
try:
|
||||
cmdline = 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)
|
||||
|
||||
def set_verbosity(self, verbose):
|
||||
self._verbose = verbose
|
||||
|
||||
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'], end=' ')
|
||||
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('qmp-shell [ -v ] [ -p ] [ -H ] [ -N ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.stderr.write(' -v Verbose (echo command sent and received)\n')
|
||||
sys.stderr.write(' -p Pretty-print JSON\n')
|
||||
sys.stderr.write(' -H Use HMP interface\n')
|
||||
sys.stderr.write(' -N Skip negotiate (for qemu-ga)\n')
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
addr = ''
|
||||
qemu = None
|
||||
hmp = False
|
||||
pretty = False
|
||||
verbose = False
|
||||
negotiate = True
|
||||
|
||||
try:
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == "-H":
|
||||
if qemu is not None:
|
||||
fail_cmdline(arg)
|
||||
hmp = True
|
||||
elif arg == "-p":
|
||||
pretty = True
|
||||
elif arg == "-N":
|
||||
negotiate = False
|
||||
elif arg == "-v":
|
||||
verbose = True
|
||||
else:
|
||||
if qemu is not None:
|
||||
fail_cmdline(arg)
|
||||
if hmp:
|
||||
qemu = HMPShell(arg)
|
||||
else:
|
||||
qemu = QMPShell(arg, pretty)
|
||||
addr = arg
|
||||
|
||||
if qemu is None:
|
||||
fail_cmdline()
|
||||
except QMPShellBadPort:
|
||||
die('bad port number in command-line')
|
||||
|
||||
try:
|
||||
qemu.connect(negotiate)
|
||||
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()
|
||||
qemu.set_verbosity(verbose)
|
||||
while qemu.read_exec_command(qemu.get_prompt()):
|
||||
pass
|
||||
qemu.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
qmp_shell.main()
|
||||
|
|
|
@ -1,147 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2012
|
||||
# Copyright (C) 2020 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
# Markus Armbruster <armbru@redhat.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, FuseOSError, Operations
|
||||
import os, posix, sys
|
||||
from errno import *
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
from qemu.qmp.qom_fuse import QOMFuse
|
||||
|
||||
fuse.fuse_python_api = (0, 2)
|
||||
|
||||
class QOMFS(Operations):
|
||||
def __init__(self, qmp):
|
||||
self.qmp = qmp
|
||||
self.qmp.connect()
|
||||
self.ino_map = {}
|
||||
self.ino_count = 1
|
||||
|
||||
def get_ino(self, path):
|
||||
if path in self.ino_map:
|
||||
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):
|
||||
path, prop = path.rsplit('/', 1)
|
||||
if path == '':
|
||||
path = '/'
|
||||
try:
|
||||
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):
|
||||
path, prop = path.rsplit('/', 1)
|
||||
if path == '':
|
||||
path = '/'
|
||||
try:
|
||||
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, fh):
|
||||
if not self.is_property(path):
|
||||
return -ENOENT
|
||||
|
||||
path, prop = path.rsplit('/', 1)
|
||||
if path == '':
|
||||
path = '/'
|
||||
try:
|
||||
data = self.qmp.command('qom-get', path=path, property=prop)
|
||||
data += '\n' # make values shell friendly
|
||||
except:
|
||||
raise FuseOSError(EPERM)
|
||||
|
||||
if offset > len(data):
|
||||
return ''
|
||||
|
||||
return bytes(data[offset:][:length], encoding='utf-8')
|
||||
|
||||
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, fh=None):
|
||||
if self.is_link(path):
|
||||
value = { 'st_mode': 0o755 | stat.S_IFLNK,
|
||||
'st_ino': self.get_ino(path),
|
||||
'st_dev': 0,
|
||||
'st_nlink': 2,
|
||||
'st_uid': 1000,
|
||||
'st_gid': 1000,
|
||||
'st_size': 4096,
|
||||
'st_atime': 0,
|
||||
'st_mtime': 0,
|
||||
'st_ctime': 0 }
|
||||
elif self.is_object(path):
|
||||
value = { 'st_mode': 0o755 | stat.S_IFDIR,
|
||||
'st_ino': self.get_ino(path),
|
||||
'st_dev': 0,
|
||||
'st_nlink': 2,
|
||||
'st_uid': 1000,
|
||||
'st_gid': 1000,
|
||||
'st_size': 4096,
|
||||
'st_atime': 0,
|
||||
'st_mtime': 0,
|
||||
'st_ctime': 0 }
|
||||
elif self.is_property(path):
|
||||
value = { 'st_mode': 0o644 | stat.S_IFREG,
|
||||
'st_ino': self.get_ino(path),
|
||||
'st_dev': 0,
|
||||
'st_nlink': 1,
|
||||
'st_uid': 1000,
|
||||
'st_gid': 1000,
|
||||
'st_size': 4096,
|
||||
'st_atime': 0,
|
||||
'st_mtime': 0,
|
||||
'st_ctime': 0 }
|
||||
else:
|
||||
raise FuseOSError(ENOENT)
|
||||
return value
|
||||
|
||||
def readdir(self, path, fh):
|
||||
yield '.'
|
||||
yield '..'
|
||||
for item in self.qmp.command('qom-list', path=path):
|
||||
yield str(item['name'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
|
||||
fuse = FUSE(QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET'])),
|
||||
sys.argv[1], foreground=True)
|
||||
sys.exit(QOMFuse.entry_point())
|
||||
|
|
|
@ -1,69 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
##
|
||||
# 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
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
from qemu.qmp.qom import QOMGet
|
||||
|
||||
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 'QMP_SOCKET' in os.environ:
|
||||
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)
|
||||
if __name__ == '__main__':
|
||||
sys.exit(QOMGet.entry_point())
|
||||
|
|
|
@ -1,66 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
##
|
||||
# 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
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
from qemu.qmp.qom import QOMList
|
||||
|
||||
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 'QMP_SOCKET' in os.environ:
|
||||
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'])
|
||||
if __name__ == '__main__':
|
||||
sys.exit(QOMList.entry_point())
|
||||
|
|
|
@ -1,66 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
##
|
||||
# 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
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
from qemu.qmp.qom import QOMSet
|
||||
|
||||
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 'QMP_SOCKET' in os.environ:
|
||||
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=value))
|
||||
if __name__ == '__main__':
|
||||
sys.exit(QOMSet.entry_point())
|
||||
|
|
|
@ -1,77 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
# Copyright (c) 2013 SUSE LINUX Products GmbH
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@amazon.com>
|
||||
# Andreas Faerber <afaerber@suse.de>
|
||||
#
|
||||
# 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
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
from qemu.qmp.qom import QOMTree
|
||||
|
||||
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 'QMP_SOCKET' in os.environ:
|
||||
socket_path = os.environ['QMP_SOCKET']
|
||||
else:
|
||||
usage_error("no QMP socket path or address given");
|
||||
|
||||
srv = QEMUMonitorProtocol(socket_path)
|
||||
srv.connect()
|
||||
|
||||
def list_node(path):
|
||||
print('%s' % path)
|
||||
items = srv.command('qom-list', path=path)
|
||||
for item in items:
|
||||
if not item['type'].startswith('child<'):
|
||||
try:
|
||||
print(' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type']))
|
||||
except:
|
||||
print(' %s: <EXCEPTION> (%s)' % (item['name'], item['type']))
|
||||
print('')
|
||||
for item in items:
|
||||
if item['type'].startswith('child<'):
|
||||
list_node((path if (path != '/') else '') + '/' + item['name'])
|
||||
|
||||
if len(args) == 0:
|
||||
path = '/'
|
||||
else:
|
||||
path = args[0]
|
||||
|
||||
list_node(path)
|
||||
if __name__ == '__main__':
|
||||
sys.exit(QOMTree.entry_point())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue