Patchwork Add a '-r' option to qmp-shell to print out raw JSON replies

login
register
mail settings
Submitter Daniel P. Berrange
Date Aug. 20, 2012, 2:02 p.m.
Message ID <1345471343-21497-1-git-send-email-berrange@redhat.com>
Download mbox | patch
Permalink /patch/178813/
State New
Headers show

Comments

Daniel P. Berrange - Aug. 20, 2012, 2:02 p.m.
From: "Daniel P. Berrange" <berrange@redhat.com>

By default, the JSON reply is parsed and the corresponding python
object printed on the console. When developing JSON client apps
for QEMU though, it is handy to see the raw JSON document instead.
Add a '-r' option that will cause the raw JSON to be printed,
instead of the parsed object. eg

$ qmp-shell -r /tmp/qemu
Welcome to the QMP low-level shell!
Connected to QEMU 1.1.50

(QEMU) stop
{
    "return": {
    }
}

(QEMU)
{
    "timestamp": {
        "seconds": 1345471168,
        "microseconds": 493463
    },
    "event": "STOP"
}

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 QMP/qmp-shell | 40 +++++++++++++++++++++++++++-------------
 QMP/qmp.py    | 12 ++++++------
 2 files changed, 33 insertions(+), 19 deletions(-)
Luiz Capitulino - Aug. 20, 2012, 6:22 p.m.
On Mon, 20 Aug 2012 15:02:23 +0100
"Daniel P. Berrange" <berrange@redhat.com> wrote:

> From: "Daniel P. Berrange" <berrange@redhat.com>
> 
> By default, the JSON reply is parsed and the corresponding python
> object printed on the console. When developing JSON client apps
> for QEMU though, it is handy to see the raw JSON document instead.
> Add a '-r' option that will cause the raw JSON to be printed,
> instead of the parsed object. eg

Looks good, but depends on the previous patch (which is being discussed).

Btw, I wonder if we should make this the default, or add -v (instead of
-r) which would allow for extra verbosity by printing both: the reply
as read by qmp-shell and the parsed.

> 
> $ qmp-shell -r /tmp/qemu
> Welcome to the QMP low-level shell!
> Connected to QEMU 1.1.50
> 
> (QEMU) stop
> {
>     "return": {
>     }
> }
> 
> (QEMU)
> {
>     "timestamp": {
>         "seconds": 1345471168,
>         "microseconds": 493463
>     },
>     "event": "STOP"
> }
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  QMP/qmp-shell | 40 +++++++++++++++++++++++++++-------------
>  QMP/qmp.py    | 12 ++++++------
>  2 files changed, 33 insertions(+), 19 deletions(-)
> 
> diff --git a/QMP/qmp-shell b/QMP/qmp-shell
> index 24b665c..6c7d8c3 100755
> --- a/QMP/qmp-shell
> +++ b/QMP/qmp-shell
> @@ -53,11 +53,12 @@ class QMPShellBadPort(QMPShellError):
>  # 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):
> +    def __init__(self, address, pp=None, raw=False):
>          qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
>          self._greeting = None
>          self._completer = None
>          self._pp = pp
> +        self._raw = raw
>  
>      def __get_address(self, arg):
>          """
> @@ -75,7 +76,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
>          return arg
>  
>      def _fill_completion(self):
> -        for cmd in self.cmd('query-commands')['return']:
> +        cmds = self.cmd('query-commands')
> +        for cmd in cmds[0]['return']:
>              self._completer.append(cmd['name'])
>  
>      def __completer_setup(self):
> @@ -105,6 +107,15 @@ class QMPShell(qmp.QEMUMonitorProtocol):
>              qmpcmd['arguments'][opt[0]] = value
>          return qmpcmd
>  
> +    def _print(self, data):
> +        if self._raw:
> +            print data[1]
> +        else:
> +            if self._pp is not None:
> +                self._pp.pprint(data[0])
> +            else:
> +                print data[0]
> +
>      def _execute_cmd(self, cmdline):
>          try:
>              qmpcmd = self.__build_cmd(cmdline)
> @@ -113,14 +124,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
>              print '[arg-name1=arg1] ... [arg-nameN=argN]'
>              return True
>          resp = self.cmd_obj(qmpcmd)
> -        if resp is None:
> +        if resp[0] is None:
>              print 'Disconnected'
>              return False
>  
> -        if self._pp is not None:
> -            self._pp.pprint(resp)
> -        else:
> -            print resp
> +        self._print(resp)
> +
>          return True
>  
>      def connect(self):
> @@ -144,8 +153,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
>              print
>              return False
>          if cmdline == '':
> -            for ev in self.get_events():
> -                print ev
> +            events = self.get_events()
> +            for ev in events:
> +                self._print(ev)
>              self.clear_events()
>              return True
>          else:
> @@ -188,9 +198,10 @@ class HMPShell(QMPShell):
>          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 } })
> +        resp = self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
> +                                  { 'command-line': cmdline,
> +                                    'cpu-index': cpu_index } })
> +        return resp[0]
>  
>      def _execute_cmd(self, cmdline):
>          if cmdline.split()[0] == "cpu":
> @@ -236,6 +247,7 @@ def main():
>      qemu = None
>      hmp = False
>      pp = None
> +    raw = False
>  
>      try:
>          for arg in sys.argv[1:]:
> @@ -247,13 +259,15 @@ def main():
>                  if pp is not None:
>                      fail_cmdline(arg)
>                  pp = pprint.PrettyPrinter(indent=4)
> +            elif arg == "-r":
> +                raw = True
>              else:
>                  if qemu is not None:
>                      fail_cmdline(arg)
>                  if hmp:
>                      qemu = HMPShell(arg)
>                  else:
> -                    qemu = QMPShell(arg, pp)
> +                    qemu = QMPShell(arg, pp, raw)
>                  addr = arg
>  
>          if qemu is None:
> diff --git a/QMP/qmp.py b/QMP/qmp.py
> index 464a01a..3a0efd8 100644
> --- a/QMP/qmp.py
> +++ b/QMP/qmp.py
> @@ -50,13 +50,13 @@ class QEMUMonitorProtocol:
>  
>      def __negotiate_capabilities(self):
>          self.__sockfile = self.__sock.makefile()
> -        greeting = self.__json_read()
> -        if greeting is None or not greeting.has_key('QMP'):
> +        greeting  = self.__json_read()
> +        if greeting[0] is None or not greeting[0].has_key('QMP'):
>              raise QMPConnectError
>          # Greeting seems ok, negotiate capabilities
>          resp = self.cmd('qmp_capabilities')
> -        if "return" in resp:
> -            return greeting
> +        if "return" in resp[0]:
> +            return greeting[0]
>          raise QMPCapabilitiesError
>  
>      def __json_read(self, only_event=False):
> @@ -74,10 +74,10 @@ class QEMUMonitorProtocol:
>                      pass
>  
>              if 'event' in resp:
> -                self.__events.append(resp)
> +                self.__events.append((resp, data))
>                  if not only_event:
>                      continue
> -            return resp
> +            return (resp, data)
>  
>      error = socket.error
>
Daniel P. Berrange - Aug. 20, 2012, 6:26 p.m.
On Mon, Aug 20, 2012 at 03:22:35PM -0300, Luiz Capitulino wrote:
> On Mon, 20 Aug 2012 15:02:23 +0100
> "Daniel P. Berrange" <berrange@redhat.com> wrote:
> 
> > From: "Daniel P. Berrange" <berrange@redhat.com>
> > 
> > By default, the JSON reply is parsed and the corresponding python
> > object printed on the console. When developing JSON client apps
> > for QEMU though, it is handy to see the raw JSON document instead.
> > Add a '-r' option that will cause the raw JSON to be printed,
> > instead of the parsed object. eg
> 
> Looks good, but depends on the previous patch (which is being discussed).
> 
> Btw, I wonder if we should make this the default, or add -v (instead of
> -r) which would allow for extra verbosity by printing both: the reply
> as read by qmp-shell and the parsed.

I dunno, I feel the current default is reasonable and printing both
would be a bit too verbose for commands with larger output.


Daniel

Patch

diff --git a/QMP/qmp-shell b/QMP/qmp-shell
index 24b665c..6c7d8c3 100755
--- a/QMP/qmp-shell
+++ b/QMP/qmp-shell
@@ -53,11 +53,12 @@  class QMPShellBadPort(QMPShellError):
 # 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):
+    def __init__(self, address, pp=None, raw=False):
         qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
         self._greeting = None
         self._completer = None
         self._pp = pp
+        self._raw = raw
 
     def __get_address(self, arg):
         """
@@ -75,7 +76,8 @@  class QMPShell(qmp.QEMUMonitorProtocol):
         return arg
 
     def _fill_completion(self):
-        for cmd in self.cmd('query-commands')['return']:
+        cmds = self.cmd('query-commands')
+        for cmd in cmds[0]['return']:
             self._completer.append(cmd['name'])
 
     def __completer_setup(self):
@@ -105,6 +107,15 @@  class QMPShell(qmp.QEMUMonitorProtocol):
             qmpcmd['arguments'][opt[0]] = value
         return qmpcmd
 
+    def _print(self, data):
+        if self._raw:
+            print data[1]
+        else:
+            if self._pp is not None:
+                self._pp.pprint(data[0])
+            else:
+                print data[0]
+
     def _execute_cmd(self, cmdline):
         try:
             qmpcmd = self.__build_cmd(cmdline)
@@ -113,14 +124,12 @@  class QMPShell(qmp.QEMUMonitorProtocol):
             print '[arg-name1=arg1] ... [arg-nameN=argN]'
             return True
         resp = self.cmd_obj(qmpcmd)
-        if resp is None:
+        if resp[0] is None:
             print 'Disconnected'
             return False
 
-        if self._pp is not None:
-            self._pp.pprint(resp)
-        else:
-            print resp
+        self._print(resp)
+
         return True
 
     def connect(self):
@@ -144,8 +153,9 @@  class QMPShell(qmp.QEMUMonitorProtocol):
             print
             return False
         if cmdline == '':
-            for ev in self.get_events():
-                print ev
+            events = self.get_events()
+            for ev in events:
+                self._print(ev)
             self.clear_events()
             return True
         else:
@@ -188,9 +198,10 @@  class HMPShell(QMPShell):
         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 } })
+        resp = self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
+                                  { 'command-line': cmdline,
+                                    'cpu-index': cpu_index } })
+        return resp[0]
 
     def _execute_cmd(self, cmdline):
         if cmdline.split()[0] == "cpu":
@@ -236,6 +247,7 @@  def main():
     qemu = None
     hmp = False
     pp = None
+    raw = False
 
     try:
         for arg in sys.argv[1:]:
@@ -247,13 +259,15 @@  def main():
                 if pp is not None:
                     fail_cmdline(arg)
                 pp = pprint.PrettyPrinter(indent=4)
+            elif arg == "-r":
+                raw = True
             else:
                 if qemu is not None:
                     fail_cmdline(arg)
                 if hmp:
                     qemu = HMPShell(arg)
                 else:
-                    qemu = QMPShell(arg, pp)
+                    qemu = QMPShell(arg, pp, raw)
                 addr = arg
 
         if qemu is None:
diff --git a/QMP/qmp.py b/QMP/qmp.py
index 464a01a..3a0efd8 100644
--- a/QMP/qmp.py
+++ b/QMP/qmp.py
@@ -50,13 +50,13 @@  class QEMUMonitorProtocol:
 
     def __negotiate_capabilities(self):
         self.__sockfile = self.__sock.makefile()
-        greeting = self.__json_read()
-        if greeting is None or not greeting.has_key('QMP'):
+        greeting  = self.__json_read()
+        if greeting[0] is None or not greeting[0].has_key('QMP'):
             raise QMPConnectError
         # Greeting seems ok, negotiate capabilities
         resp = self.cmd('qmp_capabilities')
-        if "return" in resp:
-            return greeting
+        if "return" in resp[0]:
+            return greeting[0]
         raise QMPCapabilitiesError
 
     def __json_read(self, only_event=False):
@@ -74,10 +74,10 @@  class QEMUMonitorProtocol:
                     pass
 
             if 'event' in resp:
-                self.__events.append(resp)
+                self.__events.append((resp, data))
                 if not only_event:
                     continue
-            return resp
+            return (resp, data)
 
     error = socket.error