scripts: qmp-shell: add transaction subshell

Add a special processing mode to craft transactions.

By entering "transaction(" the shell will enter a special
mode where each subsequent command will be saved as a transaction
instead of executed as an individual command.

The transaction can be submitted by entering ")" on a line by itself.

Examples:

Separate lines:

(QEMU) transaction(
TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
TRANS> )

With a transaction action included on the first line:

(QEMU) transaction( block-dirty-bitmap-add node=drive0 name=bitmap2
TRANS> block-dirty-bitmap-add node=drive0 name=bitmap3
TRANS> )

As a one-liner, with just one transaction action:

(QEMU) transaction( block-dirty-bitmap-add node=drive0 name=bitmap0 )

As a side-effect of this patch, blank lines are now parsed as no-ops,
regardless of which shell mode you are in.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Tested-by: Kashyap Chamarthy <kchamart@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
This commit is contained in:
John Snow 2015-04-29 15:14:03 -04:00 committed by Luiz Capitulino
parent 6092c3ecc4
commit 30bd6815ef

View File

@ -73,6 +73,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
self._greeting = None self._greeting = None
self._completer = None self._completer = None
self._pp = pp self._pp = pp
self._transmode = False
self._actions = list()
def __get_address(self, arg): def __get_address(self, arg):
""" """
@ -159,6 +161,36 @@ class QMPShell(qmp.QEMUMonitorProtocol):
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
""" """
cmdargs = cmdline.split() cmdargs = cmdline.split()
# 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': {} } qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
self.__cli_expr(cmdargs[1:], qmpcmd['arguments']) self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
return qmpcmd return qmpcmd
@ -171,6 +203,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
print 'command format: <command-name> ', print 'command format: <command-name> ',
print '[arg-name1=arg1] ... [arg-nameN=argN]' print '[arg-name1=arg1] ... [arg-nameN=argN]'
return True return True
# For transaction mode, we may have just cached the action:
if qmpcmd is None:
return True
resp = self.cmd_obj(qmpcmd) resp = self.cmd_obj(qmpcmd)
if resp is None: if resp is None:
print 'Disconnected' print 'Disconnected'
@ -191,6 +226,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
version = self._greeting['QMP']['version']['qemu'] version = self._greeting['QMP']['version']['qemu']
print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) 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): def read_exec_command(self, prompt):
""" """
Read and execute a command. Read and execute a command.
@ -330,7 +370,7 @@ def main():
die('Could not connect to %s' % addr) die('Could not connect to %s' % addr)
qemu.show_banner() qemu.show_banner()
while qemu.read_exec_command('(QEMU) '): while qemu.read_exec_command(qemu.get_prompt()):
pass pass
qemu.close() qemu.close()