Patchwork [v2] qmp: add test tool for QMP

login
register
mail settings
Submitter Mark Wu
Date Nov. 9, 2011, 5:55 a.m.
Message ID <1320818133-8930-1-git-send-email-wudxw@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/124478/
State New
Headers show

Comments

Mark Wu - Nov. 9, 2011, 5:55 a.m.
Anthony wrote this quickly to aid in testing.  It's similar to qmp-shell with
a few important differences:

1) It is not interactive.  That makes it useful for scripting.

2) qmp-shell:

(QEMU) set_password protocol=vnc password=foo

3) qmp:

$ qmp set_password --protocol=vnc --password=foo

4) Extensible, git-style interface.  If an invalid command name is
passed, it will try to exec qmp-$1.

5) It attempts to pretty print the JSON responses in a shell friendly
format such that tools can work with the output.

Hope others will also find it useful.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com>
Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
---
 QMP/qmp |  126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 126 insertions(+), 0 deletions(-)
 create mode 100755 QMP/qmp
Anthony Liguori - Nov. 9, 2011, 8:15 p.m.
On 11/08/2011 11:55 PM, Mark Wu wrote:
> Anthony wrote this quickly to aid in testing.  It's similar to qmp-shell with
> a few important differences:
>
> 1) It is not interactive.  That makes it useful for scripting.
>
> 2) qmp-shell:
>
> (QEMU) set_password protocol=vnc password=foo
>
> 3) qmp:
>
> $ qmp set_password --protocol=vnc --password=foo
>
> 4) Extensible, git-style interface.  If an invalid command name is
> passed, it will try to exec qmp-$1.
>
> 5) It attempts to pretty print the JSON responses in a shell friendly
> format such that tools can work with the output.
>
> Hope others will also find it useful.
>
> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
> Signed-off-by: Mark Wu<wudxw@linux.vnet.ibm.com>
> Acked-by: Luiz Capitulino<lcapitulino@redhat.com>

Looks good.  I'll apply this once the 1.1 window opens up.

Regards,

Anthony Liguori

> ---
>   QMP/qmp |  126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 files changed, 126 insertions(+), 0 deletions(-)
>   create mode 100755 QMP/qmp
>
> diff --git a/QMP/qmp b/QMP/qmp
> new file mode 100755
> index 0000000..1db3c7f
> --- /dev/null
> +++ b/QMP/qmp
> @@ -0,0 +1,126 @@
> +#!/usr/bin/python
> +#
> +# QMP command line tool
> +#
> +# Copyright IBM, Corp. 2011
> +#
> +# Authors:
> +#  Anthony Liguori<aliguori@us.ibm.com>
> +#
> +# This work is licensed under the terms of the GNU GPLv2 or later.
> +# See the COPYING file in the top-level directory.
> +
> +import sys, os
> +from qmp import QEMUMonitorProtocol
> +
> +def print_response(rsp, prefix=[]):
> +    if type(rsp) == list:
> +        i = 0
> +        for item in rsp:
> +            if prefix == []:
> +                prefix = ['item']
> +            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
> +            i += 1
> +    elif type(rsp) == dict:
> +        for key in rsp.keys():
> +            print_response(rsp[key], prefix + [key])
> +    else:
> +        if len(prefix):
> +            print '%s: %s' % ('.'.join(prefix), rsp)
> +        else:
> +            print '%s' % (rsp)
> +
> +def main(args):
> +    path = None
> +
> +    # Use QMP_PATH if it's set
> +    if os.environ.has_key('QMP_PATH'):
> +        path = os.environ['QMP_PATH']
> +
> +    while len(args):
> +        arg = args[0]
> +
> +        if arg.startswith('--'):
> +            arg = arg[2:]
> +            if arg.find('=') == -1:
> +                value = True
> +            else:
> +                arg, value = arg.split('=', 1)
> +
> +            if arg in ['path']:
> +                if type(value) == str:
> +                    path = value
> +            elif arg in ['help']:
> +                os.execlp('man', 'man', 'qmp')
> +            else:
> +                print 'Unknown argument "%s"' % arg
> +
> +            args = args[1:]
> +        else:
> +            break
> +
> +    if not path:
> +        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
> +        return 1
> +
> +    if len(args):
> +        command, args = args[0], args[1:]
> +    else:
> +        print 'No command found'
> +        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
> +        return 1
> +
> +    if command in ['help']:
> +        os.execlp('man', 'man', 'qmp')
> +
> +    srv = QEMUMonitorProtocol(path)
> +    srv.connect()
> +
> +    def do_command(srv, cmd, **kwds):
> +        rsp = srv.cmd(cmd, kwds)
> +        if rsp.has_key('error'):
> +            raise Exception(rsp['error']['desc'])
> +        return rsp['return']
> +
> +    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
> +
> +    srv.close()
> +
> +    if command not in commands:
> +        fullcmd = 'qmp-%s' % command
> +        try:
> +            os.environ['QMP_PATH'] = path
> +            os.execvp(fullcmd, [fullcmd] + args)
> +        except OSError, (errno, msg):
> +            if errno == 2:
> +                print 'Command "%s" not found.' % (fullcmd)
> +                return 1
> +            raise
> +        return 0
> +
> +    srv = QEMUMonitorProtocol(path)
> +    srv.connect()
> +
> +    arguments = {}
> +    for arg in args:
> +        if not arg.startswith('--'):
> +            print 'Unknown argument "%s"' % arg
> +            return 1
> +
> +        arg = arg[2:]
> +        if arg.find('=') == -1:
> +            value = True
> +        else:
> +            arg, value = arg.split('=', 1)
> +
> +        if arg in ['help']:
> +            os.execlp('man', 'man', 'qmp-%s' % command)
> +            return 1
> +
> +        arguments[arg] = value
> +
> +    rsp = do_command(srv, command, **arguments)
> +    print_response(rsp)
> +
> +if __name__ == '__main__':
> +    sys.exit(main(sys.argv[1:]))
Luiz Capitulino - Nov. 9, 2011, 11:58 p.m.
On Wed, 09 Nov 2011 14:15:04 -0600
Anthony Liguori <aliguori@us.ibm.com> wrote:

> On 11/08/2011 11:55 PM, Mark Wu wrote:
> > Anthony wrote this quickly to aid in testing.  It's similar to qmp-shell with
> > a few important differences:
> >
> > 1) It is not interactive.  That makes it useful for scripting.
> >
> > 2) qmp-shell:
> >
> > (QEMU) set_password protocol=vnc password=foo
> >
> > 3) qmp:
> >
> > $ qmp set_password --protocol=vnc --password=foo
> >
> > 4) Extensible, git-style interface.  If an invalid command name is
> > passed, it will try to exec qmp-$1.
> >
> > 5) It attempts to pretty print the JSON responses in a shell friendly
> > format such that tools can work with the output.
> >
> > Hope others will also find it useful.
> >
> > Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
> > Signed-off-by: Mark Wu<wudxw@linux.vnet.ibm.com>
> > Acked-by: Luiz Capitulino<lcapitulino@redhat.com>
> 
> Looks good.  I'll apply this once the 1.1 window opens up.

I thought you were planning to use it for 1.0 testing and so wanted it
merged in 1.0.

As that's not the case I've applied it in my qmp-next queue.

Thanks Mark.

> 
> Regards,
> 
> Anthony Liguori
> 
> > ---
> >   QMP/qmp |  126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >   1 files changed, 126 insertions(+), 0 deletions(-)
> >   create mode 100755 QMP/qmp
> >
> > diff --git a/QMP/qmp b/QMP/qmp
> > new file mode 100755
> > index 0000000..1db3c7f
> > --- /dev/null
> > +++ b/QMP/qmp
> > @@ -0,0 +1,126 @@
> > +#!/usr/bin/python
> > +#
> > +# QMP command line tool
> > +#
> > +# Copyright IBM, Corp. 2011
> > +#
> > +# Authors:
> > +#  Anthony Liguori<aliguori@us.ibm.com>
> > +#
> > +# This work is licensed under the terms of the GNU GPLv2 or later.
> > +# See the COPYING file in the top-level directory.
> > +
> > +import sys, os
> > +from qmp import QEMUMonitorProtocol
> > +
> > +def print_response(rsp, prefix=[]):
> > +    if type(rsp) == list:
> > +        i = 0
> > +        for item in rsp:
> > +            if prefix == []:
> > +                prefix = ['item']
> > +            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
> > +            i += 1
> > +    elif type(rsp) == dict:
> > +        for key in rsp.keys():
> > +            print_response(rsp[key], prefix + [key])
> > +    else:
> > +        if len(prefix):
> > +            print '%s: %s' % ('.'.join(prefix), rsp)
> > +        else:
> > +            print '%s' % (rsp)
> > +
> > +def main(args):
> > +    path = None
> > +
> > +    # Use QMP_PATH if it's set
> > +    if os.environ.has_key('QMP_PATH'):
> > +        path = os.environ['QMP_PATH']
> > +
> > +    while len(args):
> > +        arg = args[0]
> > +
> > +        if arg.startswith('--'):
> > +            arg = arg[2:]
> > +            if arg.find('=') == -1:
> > +                value = True
> > +            else:
> > +                arg, value = arg.split('=', 1)
> > +
> > +            if arg in ['path']:
> > +                if type(value) == str:
> > +                    path = value
> > +            elif arg in ['help']:
> > +                os.execlp('man', 'man', 'qmp')
> > +            else:
> > +                print 'Unknown argument "%s"' % arg
> > +
> > +            args = args[1:]
> > +        else:
> > +            break
> > +
> > +    if not path:
> > +        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
> > +        return 1
> > +
> > +    if len(args):
> > +        command, args = args[0], args[1:]
> > +    else:
> > +        print 'No command found'
> > +        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
> > +        return 1
> > +
> > +    if command in ['help']:
> > +        os.execlp('man', 'man', 'qmp')
> > +
> > +    srv = QEMUMonitorProtocol(path)
> > +    srv.connect()
> > +
> > +    def do_command(srv, cmd, **kwds):
> > +        rsp = srv.cmd(cmd, kwds)
> > +        if rsp.has_key('error'):
> > +            raise Exception(rsp['error']['desc'])
> > +        return rsp['return']
> > +
> > +    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
> > +
> > +    srv.close()
> > +
> > +    if command not in commands:
> > +        fullcmd = 'qmp-%s' % command
> > +        try:
> > +            os.environ['QMP_PATH'] = path
> > +            os.execvp(fullcmd, [fullcmd] + args)
> > +        except OSError, (errno, msg):
> > +            if errno == 2:
> > +                print 'Command "%s" not found.' % (fullcmd)
> > +                return 1
> > +            raise
> > +        return 0
> > +
> > +    srv = QEMUMonitorProtocol(path)
> > +    srv.connect()
> > +
> > +    arguments = {}
> > +    for arg in args:
> > +        if not arg.startswith('--'):
> > +            print 'Unknown argument "%s"' % arg
> > +            return 1
> > +
> > +        arg = arg[2:]
> > +        if arg.find('=') == -1:
> > +            value = True
> > +        else:
> > +            arg, value = arg.split('=', 1)
> > +
> > +        if arg in ['help']:
> > +            os.execlp('man', 'man', 'qmp-%s' % command)
> > +            return 1
> > +
> > +        arguments[arg] = value
> > +
> > +    rsp = do_command(srv, command, **arguments)
> > +    print_response(rsp)
> > +
> > +if __name__ == '__main__':
> > +    sys.exit(main(sys.argv[1:]))
>
Anthony Liguori - Nov. 10, 2011, 12:45 a.m.
On 11/09/2011 05:58 PM, Luiz Capitulino wrote:
> On Wed, 09 Nov 2011 14:15:04 -0600
> Anthony Liguori<aliguori@us.ibm.com>  wrote:
>
>> On 11/08/2011 11:55 PM, Mark Wu wrote:
>>> Anthony wrote this quickly to aid in testing.  It's similar to qmp-shell with
>>> a few important differences:
>>>
>>> 1) It is not interactive.  That makes it useful for scripting.
>>>
>>> 2) qmp-shell:
>>>
>>> (QEMU) set_password protocol=vnc password=foo
>>>
>>> 3) qmp:
>>>
>>> $ qmp set_password --protocol=vnc --password=foo
>>>
>>> 4) Extensible, git-style interface.  If an invalid command name is
>>> passed, it will try to exec qmp-$1.
>>>
>>> 5) It attempts to pretty print the JSON responses in a shell friendly
>>> format such that tools can work with the output.
>>>
>>> Hope others will also find it useful.
>>>
>>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>>> Signed-off-by: Mark Wu<wudxw@linux.vnet.ibm.com>
>>> Acked-by: Luiz Capitulino<lcapitulino@redhat.com>
>>
>> Looks good.  I'll apply this once the 1.1 window opens up.
>
> I thought you were planning to use it for 1.0 testing and so wanted it
> merged in 1.0.

It doesn't need to be in the tree for that.  Yes, I am using it for testing though.

> As that's not the case I've applied it in my qmp-next queue.

Even better!  Thanks.

Regards,

Anthony Liguori

> Thanks Mark.
>
>>
>> Regards,
>>
>> Anthony Liguori
>>
>>> ---
>>>    QMP/qmp |  126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>    1 files changed, 126 insertions(+), 0 deletions(-)
>>>    create mode 100755 QMP/qmp
>>>
>>> diff --git a/QMP/qmp b/QMP/qmp
>>> new file mode 100755
>>> index 0000000..1db3c7f
>>> --- /dev/null
>>> +++ b/QMP/qmp
>>> @@ -0,0 +1,126 @@
>>> +#!/usr/bin/python
>>> +#
>>> +# QMP command line tool
>>> +#
>>> +# Copyright IBM, Corp. 2011
>>> +#
>>> +# Authors:
>>> +#  Anthony Liguori<aliguori@us.ibm.com>
>>> +#
>>> +# This work is licensed under the terms of the GNU GPLv2 or later.
>>> +# See the COPYING file in the top-level directory.
>>> +
>>> +import sys, os
>>> +from qmp import QEMUMonitorProtocol
>>> +
>>> +def print_response(rsp, prefix=[]):
>>> +    if type(rsp) == list:
>>> +        i = 0
>>> +        for item in rsp:
>>> +            if prefix == []:
>>> +                prefix = ['item']
>>> +            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
>>> +            i += 1
>>> +    elif type(rsp) == dict:
>>> +        for key in rsp.keys():
>>> +            print_response(rsp[key], prefix + [key])
>>> +    else:
>>> +        if len(prefix):
>>> +            print '%s: %s' % ('.'.join(prefix), rsp)
>>> +        else:
>>> +            print '%s' % (rsp)
>>> +
>>> +def main(args):
>>> +    path = None
>>> +
>>> +    # Use QMP_PATH if it's set
>>> +    if os.environ.has_key('QMP_PATH'):
>>> +        path = os.environ['QMP_PATH']
>>> +
>>> +    while len(args):
>>> +        arg = args[0]
>>> +
>>> +        if arg.startswith('--'):
>>> +            arg = arg[2:]
>>> +            if arg.find('=') == -1:
>>> +                value = True
>>> +            else:
>>> +                arg, value = arg.split('=', 1)
>>> +
>>> +            if arg in ['path']:
>>> +                if type(value) == str:
>>> +                    path = value
>>> +            elif arg in ['help']:
>>> +                os.execlp('man', 'man', 'qmp')
>>> +            else:
>>> +                print 'Unknown argument "%s"' % arg
>>> +
>>> +            args = args[1:]
>>> +        else:
>>> +            break
>>> +
>>> +    if not path:
>>> +        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
>>> +        return 1
>>> +
>>> +    if len(args):
>>> +        command, args = args[0], args[1:]
>>> +    else:
>>> +        print 'No command found'
>>> +        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
>>> +        return 1
>>> +
>>> +    if command in ['help']:
>>> +        os.execlp('man', 'man', 'qmp')
>>> +
>>> +    srv = QEMUMonitorProtocol(path)
>>> +    srv.connect()
>>> +
>>> +    def do_command(srv, cmd, **kwds):
>>> +        rsp = srv.cmd(cmd, kwds)
>>> +        if rsp.has_key('error'):
>>> +            raise Exception(rsp['error']['desc'])
>>> +        return rsp['return']
>>> +
>>> +    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
>>> +
>>> +    srv.close()
>>> +
>>> +    if command not in commands:
>>> +        fullcmd = 'qmp-%s' % command
>>> +        try:
>>> +            os.environ['QMP_PATH'] = path
>>> +            os.execvp(fullcmd, [fullcmd] + args)
>>> +        except OSError, (errno, msg):
>>> +            if errno == 2:
>>> +                print 'Command "%s" not found.' % (fullcmd)
>>> +                return 1
>>> +            raise
>>> +        return 0
>>> +
>>> +    srv = QEMUMonitorProtocol(path)
>>> +    srv.connect()
>>> +
>>> +    arguments = {}
>>> +    for arg in args:
>>> +        if not arg.startswith('--'):
>>> +            print 'Unknown argument "%s"' % arg
>>> +            return 1
>>> +
>>> +        arg = arg[2:]
>>> +        if arg.find('=') == -1:
>>> +            value = True
>>> +        else:
>>> +            arg, value = arg.split('=', 1)
>>> +
>>> +        if arg in ['help']:
>>> +            os.execlp('man', 'man', 'qmp-%s' % command)
>>> +            return 1
>>> +
>>> +        arguments[arg] = value
>>> +
>>> +    rsp = do_command(srv, command, **arguments)
>>> +    print_response(rsp)
>>> +
>>> +if __name__ == '__main__':
>>> +    sys.exit(main(sys.argv[1:]))
>>
>

Patch

diff --git a/QMP/qmp b/QMP/qmp
new file mode 100755
index 0000000..1db3c7f
--- /dev/null
+++ b/QMP/qmp
@@ -0,0 +1,126 @@ 
+#!/usr/bin/python
+#
+# QMP command line tool
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+#  Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys, os
+from qmp import QEMUMonitorProtocol
+
+def print_response(rsp, prefix=[]):
+    if type(rsp) == list:
+        i = 0
+        for item in rsp:
+            if prefix == []:
+                prefix = ['item']
+            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
+            i += 1
+    elif type(rsp) == dict:
+        for key in rsp.keys():
+            print_response(rsp[key], prefix + [key])
+    else:
+        if len(prefix):
+            print '%s: %s' % ('.'.join(prefix), rsp)
+        else:
+            print '%s' % (rsp)
+
+def main(args):
+    path = None
+
+    # Use QMP_PATH if it's set
+    if os.environ.has_key('QMP_PATH'):
+        path = os.environ['QMP_PATH']
+
+    while len(args):
+        arg = args[0]
+
+        if arg.startswith('--'):
+            arg = arg[2:]
+            if arg.find('=') == -1:
+                value = True
+            else:
+                arg, value = arg.split('=', 1)
+
+            if arg in ['path']:
+                if type(value) == str:
+                    path = value
+            elif arg in ['help']:
+                os.execlp('man', 'man', 'qmp')
+            else:
+                print 'Unknown argument "%s"' % arg
+
+            args = args[1:]
+        else:
+            break
+
+    if not path:
+        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
+        return 1
+
+    if len(args):
+        command, args = args[0], args[1:]
+    else:
+        print 'No command found'
+        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
+        return 1
+
+    if command in ['help']:
+        os.execlp('man', 'man', 'qmp')
+
+    srv = QEMUMonitorProtocol(path)
+    srv.connect()
+
+    def do_command(srv, cmd, **kwds):
+        rsp = srv.cmd(cmd, kwds)
+        if rsp.has_key('error'):
+            raise Exception(rsp['error']['desc'])
+        return rsp['return']
+
+    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
+
+    srv.close()
+
+    if command not in commands:
+        fullcmd = 'qmp-%s' % command
+        try:
+            os.environ['QMP_PATH'] = path
+            os.execvp(fullcmd, [fullcmd] + args)
+        except OSError, (errno, msg):
+            if errno == 2:
+                print 'Command "%s" not found.' % (fullcmd)
+                return 1
+            raise
+        return 0
+
+    srv = QEMUMonitorProtocol(path)
+    srv.connect()
+
+    arguments = {}
+    for arg in args:
+        if not arg.startswith('--'):
+            print 'Unknown argument "%s"' % arg
+            return 1
+
+        arg = arg[2:]
+        if arg.find('=') == -1:
+            value = True
+        else:
+            arg, value = arg.split('=', 1)
+
+        if arg in ['help']:
+            os.execlp('man', 'man', 'qmp-%s' % command)
+            return 1
+
+        arguments[arg] = value
+
+    rsp = do_command(srv, command, **arguments)
+    print_response(rsp)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))