Patchwork [14/15] QMP: Introduce qmp-shell

login
register
mail settings
Submitter Luiz Capitulino
Date Nov. 19, 2009, 3:13 p.m.
Message ID <1258643623-8636-15-git-send-email-lcapitulino@redhat.com>
Download mbox | patch
Permalink /patch/38857/
State New
Headers show

Comments

Luiz Capitulino - Nov. 19, 2009, 3:13 p.m.
This is a very simple shell written in Python which works
on top of QMP.

Unfortunately it's a bit awkward right now, as the user has
to specify the arguments names, for example:

(QEMU) info item=version
0.11.50
(QEMU)

Also, if the output is not a string or integer, the user is
is going to get a low-level dictionary or list.

It's worth to note that the shell is broken into two files.
One is the shell itself, the other is the QMP class which
handles the communication with QEMU.

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
---
 QMP/qmp-shell |   66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 QMP/qmp.py    |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 0 deletions(-)
 create mode 100755 QMP/qmp-shell
 create mode 100644 QMP/qmp.py

Patch

diff --git a/QMP/qmp-shell b/QMP/qmp-shell
new file mode 100755
index 0000000..2faf79b
--- /dev/null
+++ b/QMP/qmp-shell
@@ -0,0 +1,66 @@ 
+#!/usr/bin/python
+#
+# Simple QEMU shell on top of QMP
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# $ qemu [...] -monitor control,unix:./qmp,server
+#
+# Run the shell:
+#
+# $ qmp-shell ./qmp
+#
+# Commands have the following format:
+#
+# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+#
+# For example:
+#
+# (QEMU) info item=network
+#
+# Luiz Capitulino <lcapitulino@redhat.com>
+
+import qmp
+import readline
+from sys import argv,exit
+
+def shell_help():
+    print 'bye  exit from the shell'
+
+def main():
+    if len(argv) != 2:
+        print 'qemu-shell <unix-socket>'
+        exit(1)
+
+    qemu = qmp.QEMUMonitorProtocol(argv[1])
+    qemu.connect()
+
+    print 'Connected!'
+
+    while True:
+        try:
+            cmd = raw_input('(QEMU) ')
+        except EOFError:
+            print
+            break
+        if cmd == '':
+            continue
+        elif cmd == 'bye':
+            break
+        elif cmd == 'help':
+            shell_help()
+        else:
+            try:
+                resp = qemu.send(cmd)
+                if resp == None:
+                    print 'Disconnected'
+                    break
+                print resp
+            except IndexError:
+                print '-> command format: <command-name> ',
+                print '[arg-name1=arg1] ... [arg-nameN=argN]'
+
+if __name__ == '__main__':
+    main()
diff --git a/QMP/qmp.py b/QMP/qmp.py
new file mode 100644
index 0000000..c56f143
--- /dev/null
+++ b/QMP/qmp.py
@@ -0,0 +1,54 @@ 
+import socket, json
+
+class QMPError(Exception):
+    pass
+
+class QMPConnectError(QMPError):
+    pass
+
+class QEMUMonitorProtocol:
+    def connect(self):
+        self.sock.connect(self.filename)
+        data = self.__json_read()
+        if data == None:
+            raise QMPConnectError
+        if not data.has_key('QMP'):
+            raise QMPConnectError
+        return data['QMP']['capabilities']
+
+    def send(self, cmdline):
+        cmd = self.__build_cmd(cmdline)
+        self.__json_send(cmd)
+        resp = self.__json_read()
+        if resp == None:
+            return
+        elif resp.has_key('error'):
+            return resp['error']
+        else:
+            return resp['return']
+
+    def __build_cmd(self, cmdline):
+        cmdargs = cmdline.split()
+        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
+        for arg in cmdargs[1:]:
+            opt = arg.split('=')
+            try:
+                value = int(opt[1])
+            except ValueError:
+                value = opt[1]
+            qmpcmd['arguments'][opt[0]] = value
+        return qmpcmd
+
+    def __json_send(self, cmd):
+        self.sock.send(json.dumps(cmd) + '\n')
+
+    def __json_read(self):
+        try:
+            return json.loads(self.sock.recv(1024))
+        except ValueError:
+            return
+
+    def __init__(self, filename):
+        self.filename = filename
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+