[Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument

Eduardo Habkost posted 5 patches 8 years, 6 months ago
[Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Eduardo Habkost 8 years, 6 months ago
This is useful for testing QMP commands in scripts.

Example usage, combined with 'jq' for filtering the results:

  $ ./scripts/qmp/qmp-shell /tmp/qmp qom-list path=/ | jq -r .return[].name
  machine
  type
  chardevs
  backend
  $

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
Changes v1 -> v2:
* Rewritten using optparse module
---
 scripts/qmp/qmp-shell | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 4b9a420..4b7374e 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -400,7 +400,7 @@ def die(msg):
 
 def main():
     parser = optparse.OptionParser(description='QMP shell utility')
-    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port>")
+    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port> [COMMAND [ARG=VALUE]...]")
     parser.add_option('-v', action='store_true', dest='verbose',
         help='Verbose (echo command sent and received)')
     parser.add_option('-p', action='store_true', dest='pretty',
@@ -411,10 +411,11 @@ def main():
         default=True, help='Skip negotiate (for qemu-ga)')
     opts,args = parser.parse_args()
 
-    if len(args) != 1:
+    if len(args) < 1:
         parser.print_help(sys.stderr)
         sys.exit(1)
     addr = args[0]
+    cmdargs = args[1:]
 
     try:
         if opts.hmp:
@@ -433,10 +434,13 @@ def main():
     except qemu.error:
         die('Could not connect to %s' % addr)
 
-    qemu.show_banner()
     qemu.set_verbosity(opts.verbose)
-    while qemu.read_exec_command(qemu.get_prompt()):
-        pass
+    if len(cmdargs):
+        qemu.execute_cmdargs(cmdargs)
+    else:
+        qemu.show_banner()
+        while qemu.read_exec_command(qemu.get_prompt()):
+            pass
     qemu.close()
 
 if __name__ == '__main__':
-- 
2.9.4


Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Stefan Hajnoczi 8 years, 6 months ago
On Tue, Aug 08, 2017 at 05:39:34PM -0300, Eduardo Habkost wrote:
> This is useful for testing QMP commands in scripts.
> 
> Example usage, combined with 'jq' for filtering the results:
> 
>   $ ./scripts/qmp/qmp-shell /tmp/qmp qom-list path=/ | jq -r .return[].name
>   machine
>   type
>   chardevs
>   backend
>   $
> 
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
> Changes v1 -> v2:
> * Rewritten using optparse module
> ---
>  scripts/qmp/qmp-shell | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Markus Armbruster 8 years, 5 months ago
Eduardo Habkost <ehabkost@redhat.com> writes:

Suggest to insert here:

  If additional arguments QMP-COMMAND ARG=VAL... are given, run just
  that QMP command instead of the REPL.

Question: is this limited to simple arguments?  If no, how would I write
an object argument?  For instance, how would I do

    { "execute": "blockdev-add", "arguments": { "node-name": "foo", "driver": "nbd", "server": { "type": "inet", "host": "localhost", "port": "12345" } } }

?

> This is useful for testing QMP commands in scripts.
>
> Example usage, combined with 'jq' for filtering the results:
>
>   $ ./scripts/qmp/qmp-shell /tmp/qmp qom-list path=/ | jq -r .return[].name
>   machine
>   type
>   chardevs
>   backend

What's jq?

>   $

Let's drop this line.

>
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
> Changes v1 -> v2:
> * Rewritten using optparse module
> ---
>  scripts/qmp/qmp-shell | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
> index 4b9a420..4b7374e 100755
> --- a/scripts/qmp/qmp-shell
> +++ b/scripts/qmp/qmp-shell
> @@ -400,7 +400,7 @@ def die(msg):
>  
>  def main():
>      parser = optparse.OptionParser(description='QMP shell utility')
> -    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port>")
> +    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port> [COMMAND [ARG=VALUE]...]")
>      parser.add_option('-v', action='store_true', dest='verbose',
>          help='Verbose (echo command sent and received)')
>      parser.add_option('-p', action='store_true', dest='pretty',
> @@ -411,10 +411,11 @@ def main():
>          default=True, help='Skip negotiate (for qemu-ga)')
>      opts,args = parser.parse_args()
>  
> -    if len(args) != 1:
> +    if len(args) < 1:
>          parser.print_help(sys.stderr)
>          sys.exit(1)
>      addr = args[0]
> +    cmdargs = args[1:]
>  
>      try:
>          if opts.hmp:
> @@ -433,10 +434,13 @@ def main():
>      except qemu.error:
>          die('Could not connect to %s' % addr)
>  
> -    qemu.show_banner()
>      qemu.set_verbosity(opts.verbose)
> -    while qemu.read_exec_command(qemu.get_prompt()):
> -        pass
> +    if len(cmdargs):

Superfluous len().

> +        qemu.execute_cmdargs(cmdargs)
> +    else:
> +        qemu.show_banner()
> +        while qemu.read_exec_command(qemu.get_prompt()):
> +            pass
>      qemu.close()
>  
>  if __name__ == '__main__':

Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Eduardo Habkost 8 years, 5 months ago
On Tue, Aug 15, 2017 at 12:03:53PM +0200, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> Suggest to insert here:
> 
>   If additional arguments QMP-COMMAND ARG=VAL... are given, run just
>   that QMP command instead of the REPL.
> 
> Question: is this limited to simple arguments?  If no, how would I write
> an object argument?  For instance, how would I do
> 
>     { "execute": "blockdev-add", "arguments": { "node-name": "foo", "driver": "nbd", "server": { "type": "inet", "host": "localhost", "port": "12345" } } }
> 
> ?

Exactly the same way you would write it when running qmp-shell in
interactive mode.  e.g.:

  $ ./scripts/qmp/qmp-shell /tmp/qmp blockdev-add driver=qcow2 node-name=node-E 'file={"driver":"file","filename":"/path/to/file.qcow2"}'


> 
> > This is useful for testing QMP commands in scripts.
> >
> > Example usage, combined with 'jq' for filtering the results:
> >
> >   $ ./scripts/qmp/qmp-shell /tmp/qmp qom-list path=/ | jq -r .return[].name
> >   machine
> >   type
> >   chardevs
> >   backend
> 
> What's jq?

https://stedolan.github.io/jq/

"like sed for JSON data"

> 
> >   $
> 
> Let's drop this line.

Will do it.

> 
> >
> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> > ---
> > Changes v1 -> v2:
> > * Rewritten using optparse module
> > ---
> >  scripts/qmp/qmp-shell | 14 +++++++++-----
> >  1 file changed, 9 insertions(+), 5 deletions(-)
> >
> > diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
> > index 4b9a420..4b7374e 100755
> > --- a/scripts/qmp/qmp-shell
> > +++ b/scripts/qmp/qmp-shell
> > @@ -400,7 +400,7 @@ def die(msg):
> >  
> >  def main():
> >      parser = optparse.OptionParser(description='QMP shell utility')
> > -    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port>")
> > +    parser.set_usage("%prog [options] <UNIX socket path> | <TCP address:port> [COMMAND [ARG=VALUE]...]")
> >      parser.add_option('-v', action='store_true', dest='verbose',
> >          help='Verbose (echo command sent and received)')
> >      parser.add_option('-p', action='store_true', dest='pretty',
> > @@ -411,10 +411,11 @@ def main():
> >          default=True, help='Skip negotiate (for qemu-ga)')
> >      opts,args = parser.parse_args()
> >  
> > -    if len(args) != 1:
> > +    if len(args) < 1:
> >          parser.print_help(sys.stderr)
> >          sys.exit(1)
> >      addr = args[0]
> > +    cmdargs = args[1:]
> >  
> >      try:
> >          if opts.hmp:
> > @@ -433,10 +434,13 @@ def main():
> >      except qemu.error:
> >          die('Could not connect to %s' % addr)
> >  
> > -    qemu.show_banner()
> >      qemu.set_verbosity(opts.verbose)
> > -    while qemu.read_exec_command(qemu.get_prompt()):
> > -        pass
> > +    if len(cmdargs):
> 
> Superfluous len().

Will fix it in the next version.

> 
> > +        qemu.execute_cmdargs(cmdargs)
> > +    else:
> > +        qemu.show_banner()
> > +        while qemu.read_exec_command(qemu.get_prompt()):
> > +            pass
> >      qemu.close()
> >  
> >  if __name__ == '__main__':

-- 
Eduardo

Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Markus Armbruster 8 years, 5 months ago
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Tue, Aug 15, 2017 at 12:03:53PM +0200, Markus Armbruster wrote:
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> Suggest to insert here:
>> 
>>   If additional arguments QMP-COMMAND ARG=VAL... are given, run just
>>   that QMP command instead of the REPL.
>> 
>> Question: is this limited to simple arguments?  If no, how would I write
>> an object argument?  For instance, how would I do
>> 
>>     { "execute": "blockdev-add", "arguments": { "node-name": "foo", "driver": "nbd", "server": { "type": "inet", "host": "localhost", "port": "12345" } } }
>> 
>> ?
>
> Exactly the same way you would write it when running qmp-shell in
> interactive mode.  e.g.:
>
>   $ ./scripts/qmp/qmp-shell /tmp/qmp blockdev-add driver=qcow2 node-name=node-E 'file={"driver":"file","filename":"/path/to/file.qcow2"}'

I see.

The QEMU command line uses dotted key syntax instead.

To be honest, the less qmp-shell is used, the happier I am.  Would you
like to serve as its sub-maintainer?

>> > This is useful for testing QMP commands in scripts.
>> >
>> > Example usage, combined with 'jq' for filtering the results:
>> >
>> >   $ ./scripts/qmp/qmp-shell /tmp/qmp qom-list path=/ | jq -r .return[].name
>> >   machine
>> >   type
>> >   chardevs
>> >   backend
>> 
>> What's jq?
>
> https://stedolan.github.io/jq/
>
> "like sed for JSON data"

A fine addition to your commit message.

>> 
>> >   $
>> 
>> Let's drop this line.
>
> Will do it.
>
>> 
>> >
>> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
[...]

Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Eduardo Habkost 8 years, 5 months ago
On Wed, Aug 16, 2017 at 08:25:41AM +0200, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > On Tue, Aug 15, 2017 at 12:03:53PM +0200, Markus Armbruster wrote:
> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> 
> >> Suggest to insert here:
> >> 
> >>   If additional arguments QMP-COMMAND ARG=VAL... are given, run just
> >>   that QMP command instead of the REPL.
> >> 
> >> Question: is this limited to simple arguments?  If no, how would I write
> >> an object argument?  For instance, how would I do
> >> 
> >>     { "execute": "blockdev-add", "arguments": { "node-name": "foo", "driver": "nbd", "server": { "type": "inet", "host": "localhost", "port": "12345" } } }
> >> 
> >> ?
> >
> > Exactly the same way you would write it when running qmp-shell in
> > interactive mode.  e.g.:
> >
> >   $ ./scripts/qmp/qmp-shell /tmp/qmp blockdev-add driver=qcow2 node-name=node-E 'file={"driver":"file","filename":"/path/to/file.qcow2"}'
> 
> I see.
> 
> The QEMU command line uses dotted key syntax instead.

You mean -blockdev, or is there another way to send QMP commands
to QEMU that I'm not aware of?

> 
> To be honest, the less qmp-shell is used, the happier I am.

What people would use instead of it?

>                                                              Would you
> like to serve as its sub-maintainer?

I'd be glad to.

-- 
Eduardo

Re: [Qemu-devel] [PATCH for-2.11 v2 4/5] qmp-shell: Accept QMP command as argument
Posted by Markus Armbruster 8 years, 5 months ago
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Wed, Aug 16, 2017 at 08:25:41AM +0200, Markus Armbruster wrote:
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> > On Tue, Aug 15, 2017 at 12:03:53PM +0200, Markus Armbruster wrote:
>> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> 
>> >> Suggest to insert here:
>> >> 
>> >>   If additional arguments QMP-COMMAND ARG=VAL... are given, run just
>> >>   that QMP command instead of the REPL.
>> >> 
>> >> Question: is this limited to simple arguments?  If no, how would I write
>> >> an object argument?  For instance, how would I do
>> >> 
>> >>     { "execute": "blockdev-add", "arguments": { "node-name": "foo", "driver": "nbd", "server": { "type": "inet", "host": "localhost", "port": "12345" } } }
>> >> 
>> >> ?
>> >
>> > Exactly the same way you would write it when running qmp-shell in
>> > interactive mode.  e.g.:
>> >
>> >   $ ./scripts/qmp/qmp-shell /tmp/qmp blockdev-add driver=qcow2 node-name=node-E 'file={"driver":"file","filename":"/path/to/file.qcow2"}'
>> 
>> I see.
>> 
>> The QEMU command line uses dotted key syntax instead.
>
> You mean -blockdev, or is there another way to send QMP commands
> to QEMU that I'm not aware of?

Dotted keys were invented for the block layer's -drive.  I adopted them
for -blockdev after considerable discussion.  The -blockdev code is all
new, and it even has documentation and tests.

>> To be honest, the less qmp-shell is used, the happier I am.
>
> What people would use instead of it?

The syntactic sugar qmp-shell provides over plain QMP doesn't simplify
its use all that much, and it can get in the way.

For my interactive QMP needs, I use

    $ socat UNIX:/wherever/qmp-socket READLINE,history=$HOME/.qmp_history,prompt='QMP> '

In programs, I send or receive either plain text or JSON objects,
depending on the program's needs.  Libqtest supports the latter.

>>                                                              Would you
>> like to serve as its sub-maintainer?
>
> I'd be glad to.

Great!  Please post a suitable MAINTAINERS stanza.