Message ID | 1320678675-21379-1-git-send-email-aliguori@us.ibm.com |
---|---|
State | New |
Headers | show |
On Mon, 7 Nov 2011 09:11:15 -0600 Anthony Liguori <aliguori@us.ibm.com> wrote: > I 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> Acked-by: Luiz Capitulino <lcapitulino@redhat.com> > --- > QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 120 insertions(+), 0 deletions(-) > create mode 100755 QMP/qmp > > diff --git a/QMP/qmp b/QMP/qmp > new file mode 100755 > index 0000000..7b2a3c7 > --- /dev/null > +++ b/QMP/qmp > @@ -0,0 +1,120 @@ > +#!/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']: > + 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 or set QMP_PATH" > + return 1 > + > + command, args = args[0], args[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:]))
On 11/07/2011 10:08 AM, Luiz Capitulino wrote: > On Mon, 7 Nov 2011 09:11:15 -0600 > Anthony Liguori<aliguori@us.ibm.com> wrote: > >> I 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> > > Acked-by: Luiz Capitulino<lcapitulino@redhat.com> BTW, one thing I'd like to try at some point soon is to generate man pages from qapi-schema.json. If you notice in the script, it does online help by invoking man. Regards, Anthony Liguori > >> --- >> QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 120 insertions(+), 0 deletions(-) >> create mode 100755 QMP/qmp >> >> diff --git a/QMP/qmp b/QMP/qmp >> new file mode 100755 >> index 0000000..7b2a3c7 >> --- /dev/null >> +++ b/QMP/qmp >> @@ -0,0 +1,120 @@ >> +#!/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']: >> + 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 or set QMP_PATH" >> + return 1 >> + >> + command, args = args[0], args[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:])) > >
On Mon, 07 Nov 2011 10:09:55 -0600 Anthony Liguori <anthony@codemonkey.ws> wrote: > On 11/07/2011 10:08 AM, Luiz Capitulino wrote: > > On Mon, 7 Nov 2011 09:11:15 -0600 > > Anthony Liguori<aliguori@us.ibm.com> wrote: > > > >> I 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> > > > > Acked-by: Luiz Capitulino<lcapitulino@redhat.com> > > BTW, one thing I'd like to try at some point soon is to generate man pages from > qapi-schema.json. If you notice in the script, it does online help by invoking man. Yes, I did notice it. I didn't comment on it because I imagined you had plans about it. PS: I don't think this needs to go through my tree. > > Regards, > > Anthony Liguori > > > > >> --- > >> QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > >> 1 files changed, 120 insertions(+), 0 deletions(-) > >> create mode 100755 QMP/qmp > >> > >> diff --git a/QMP/qmp b/QMP/qmp > >> new file mode 100755 > >> index 0000000..7b2a3c7 > >> --- /dev/null > >> +++ b/QMP/qmp > >> @@ -0,0 +1,120 @@ > >> +#!/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']: > >> + 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 or set QMP_PATH" > >> + return 1 > >> + > >> + command, args = args[0], args[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:])) > > > > >
On 11/07/2011 10:30 AM, Luiz Capitulino wrote: > On Mon, 07 Nov 2011 10:09:55 -0600 > Anthony Liguori<anthony@codemonkey.ws> wrote: > >> On 11/07/2011 10:08 AM, Luiz Capitulino wrote: >>> On Mon, 7 Nov 2011 09:11:15 -0600 >>> Anthony Liguori<aliguori@us.ibm.com> wrote: >>> >>>> I 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> >>> >>> Acked-by: Luiz Capitulino<lcapitulino@redhat.com> >> >> BTW, one thing I'd like to try at some point soon is to generate man pages from >> qapi-schema.json. If you notice in the script, it does online help by invoking man. > > Yes, I did notice it. I didn't comment on it because I imagined you had plans > about it. > > PS: I don't think this needs to go through my tree. What do you want to do with qmp.py? Do you feel comfortable installing it in $PYTHONPATH and treating it as a supported API? Regards, Anthony Liguori
On Mon, 07 Nov 2011 10:35:51 -0600 Anthony Liguori <anthony@codemonkey.ws> wrote: > On 11/07/2011 10:30 AM, Luiz Capitulino wrote: > > On Mon, 07 Nov 2011 10:09:55 -0600 > > Anthony Liguori<anthony@codemonkey.ws> wrote: > > > >> On 11/07/2011 10:08 AM, Luiz Capitulino wrote: > >>> On Mon, 7 Nov 2011 09:11:15 -0600 > >>> Anthony Liguori<aliguori@us.ibm.com> wrote: > >>> > >>>> I 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> > >>> > >>> Acked-by: Luiz Capitulino<lcapitulino@redhat.com> > >> > >> BTW, one thing I'd like to try at some point soon is to generate man pages from > >> qapi-schema.json. If you notice in the script, it does online help by invoking man. > > > > Yes, I did notice it. I didn't comment on it because I imagined you had plans > > about it. > > > > PS: I don't think this needs to go through my tree. > > What do you want to do with qmp.py? Do you feel comfortable installing it in > $PYTHONPATH and treating it as a supported API? I probably don't. I coded it as demo in the very beginning of QMP, maybe we should first define what we expect from a QMP Python class then we can see whether it fits or not... I feel it needs to be revamped.
On Mon, Nov 07, 2011 at 02:39:29PM -0200, Luiz Capitulino wrote: > On Mon, 07 Nov 2011 10:35:51 -0600 > Anthony Liguori <anthony@codemonkey.ws> wrote: > > > On 11/07/2011 10:30 AM, Luiz Capitulino wrote: > > > On Mon, 07 Nov 2011 10:09:55 -0600 > > > Anthony Liguori<anthony@codemonkey.ws> wrote: > > > > > >> On 11/07/2011 10:08 AM, Luiz Capitulino wrote: > > >>> On Mon, 7 Nov 2011 09:11:15 -0600 > > >>> Anthony Liguori<aliguori@us.ibm.com> wrote: > > >>> > > >>>> I 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> > > >>> > > >>> Acked-by: Luiz Capitulino<lcapitulino@redhat.com> > > >> > > >> BTW, one thing I'd like to try at some point soon is to generate man pages from > > >> qapi-schema.json. If you notice in the script, it does online help by invoking man. > > > > > > Yes, I did notice it. I didn't comment on it because I imagined you had plans > > > about it. > > > > > > PS: I don't think this needs to go through my tree. > > > > What do you want to do with qmp.py? Do you feel comfortable installing it in > > $PYTHONPATH and treating it as a supported API? > > I probably don't. I coded it as demo in the very beginning of QMP, maybe > we should first define what we expect from a QMP Python class then we > can see whether it fits or not... I feel it needs to be revamped. > It should not blocking, i.e. for event notification. I have a patch that fixes that but breaks tab-completion.
When I run this tool, I got two python exceptions. It turned out that both of them were caused by wrong usage. Do you think we need add validation for input to handle these cases? Thanks. 1. Not using '=' for path: $ ./QMP/qmp --path monitor-address Traceback (most recent call last): File "./QMP/qmp", line 120, in <module> sys.exit(main(sys.argv[1:])) File "./QMP/qmp", line 71, in main srv.connect() File "/home/mark/work/source/qemu/QMP/qmp.py", line 85, in connect self.__sock.connect(self.__address) File "<string>", line 1, in connect TypeError: argument must be string or read-only character buffer, not bool Proposed patch: @@ -48,7 +48,8 @@ def main(args): arg, value = arg.split('=', 1) if arg in ['path']: - path = value + if isinstance(value, basestring): + path = value 2. No qmp comand given in command line $ ./QMP/qmp --path=monitor-address Traceback (most recent call last): File "./QMP/qmp", line 120, in <module> sys.exit(main(sys.argv[1:])) File "./QMP/qmp", line 65, in main command, args = args[0], args[1:] IndexError: list index out of range @@ -62,11 +63,17 @@ def main(args): print "QMP path isn't set, use --path or set QMP_PATH" return 1 Proposed patch: - command, args = args[0], args[1:] + if len(args): + command, args = args[0], args[1:] + else: + print 'No command found' + print 'Usage: "qmp [--path=monitor-address] qmp-cmd arguments"' + return 1 On 11/07/2011 11:11 PM, Anthony Liguori wrote: > I 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> > --- > QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 120 insertions(+), 0 deletions(-) > create mode 100755 QMP/qmp > > diff --git a/QMP/qmp b/QMP/qmp > new file mode 100755 > index 0000000..7b2a3c7 > --- /dev/null > +++ b/QMP/qmp > @@ -0,0 +1,120 @@ > +#!/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']: > + 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 or set QMP_PATH" > + return 1 > + > + command, args = args[0], args[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:]))
On Tue, 08 Nov 2011 16:36:55 +0800 Mark Wu <wudxw@linux.vnet.ibm.com> wrote: > When I run this tool, I got two python exceptions. It turned out that > both of them were caused by wrong usage. Do you think we need add > validation for input to handle these cases? Yes. > > Thanks. > > 1. Not using '=' for path: > $ ./QMP/qmp --path monitor-address > Traceback (most recent call last): > File "./QMP/qmp", line 120, in <module> > sys.exit(main(sys.argv[1:])) > File "./QMP/qmp", line 71, in main > srv.connect() > File "/home/mark/work/source/qemu/QMP/qmp.py", line 85, in connect > self.__sock.connect(self.__address) > File "<string>", line 1, in connect > TypeError: argument must be string or read-only character buffer, not bool > > Proposed patch: > > @@ -48,7 +48,8 @@ def main(args): > arg, value = arg.split('=', 1) > > if arg in ['path']: > - path = value > + if isinstance(value, basestring): > + path = value > > > 2. No qmp comand given in command line > $ ./QMP/qmp --path=monitor-address > Traceback (most recent call last): > File "./QMP/qmp", line 120, in <module> > sys.exit(main(sys.argv[1:])) > File "./QMP/qmp", line 65, in main > command, args = args[0], args[1:] > IndexError: list index out of range > > @@ -62,11 +63,17 @@ def main(args): > print "QMP path isn't set, use --path or set QMP_PATH" > return 1 > > Proposed patch: > - command, args = args[0], args[1:] > + if len(args): > + command, args = args[0], args[1:] > + else: > + print 'No command found' > + print 'Usage: "qmp [--path=monitor-address] qmp-cmd arguments"' > + return 1 > > > > > > > > > On 11/07/2011 11:11 PM, Anthony Liguori wrote: > > I 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> > > --- > > QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > 1 files changed, 120 insertions(+), 0 deletions(-) > > create mode 100755 QMP/qmp > > > > diff --git a/QMP/qmp b/QMP/qmp > > new file mode 100755 > > index 0000000..7b2a3c7 > > --- /dev/null > > +++ b/QMP/qmp > > @@ -0,0 +1,120 @@ > > +#!/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']: > > + 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 or set QMP_PATH" > > + return 1 > > + > > + command, args = args[0], args[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:])) >
On 11/08/2011 02:36 AM, Mark Wu wrote: > When I run this tool, I got two python exceptions. It turned out that both of > them were caused by wrong usage. Do you think we need add validation for input > to handle these cases? Definitely. Could you just send out a version of the patch with your changes, a (v2) in the subject, and your Signed-off-by? Thanks! Regards, Anthony Liguori > > Thanks. > > 1. Not using '=' for path: > $ ./QMP/qmp --path monitor-address > Traceback (most recent call last): > File "./QMP/qmp", line 120, in <module> > sys.exit(main(sys.argv[1:])) > File "./QMP/qmp", line 71, in main > srv.connect() > File "/home/mark/work/source/qemu/QMP/qmp.py", line 85, in connect > self.__sock.connect(self.__address) > File "<string>", line 1, in connect > TypeError: argument must be string or read-only character buffer, not bool > > Proposed patch: > > @@ -48,7 +48,8 @@ def main(args): > arg, value = arg.split('=', 1) > > if arg in ['path']: > - path = value > + if isinstance(value, basestring): > + path = value > > > 2. No qmp comand given in command line > $ ./QMP/qmp --path=monitor-address > Traceback (most recent call last): > File "./QMP/qmp", line 120, in <module> > sys.exit(main(sys.argv[1:])) > File "./QMP/qmp", line 65, in main > command, args = args[0], args[1:] > IndexError: list index out of range > > @@ -62,11 +63,17 @@ def main(args): > print "QMP path isn't set, use --path or set QMP_PATH" > return 1 > > Proposed patch: > - command, args = args[0], args[1:] > + if len(args): > + command, args = args[0], args[1:] > + else: > + print 'No command found' > + print 'Usage: "qmp [--path=monitor-address] qmp-cmd arguments"' > + return 1 > > > > > > > > > On 11/07/2011 11:11 PM, Anthony Liguori wrote: >> I 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> >> --- >> QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 120 insertions(+), 0 deletions(-) >> create mode 100755 QMP/qmp >> >> diff --git a/QMP/qmp b/QMP/qmp >> new file mode 100755 >> index 0000000..7b2a3c7 >> --- /dev/null >> +++ b/QMP/qmp >> @@ -0,0 +1,120 @@ >> +#!/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']: >> + 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 or set QMP_PATH" >> + return 1 >> + >> + command, args = args[0], args[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:])) > >
diff --git a/QMP/qmp b/QMP/qmp new file mode 100755 index 0000000..7b2a3c7 --- /dev/null +++ b/QMP/qmp @@ -0,0 +1,120 @@ +#!/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']: + 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 or set QMP_PATH" + return 1 + + command, args = args[0], args[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:]))
I 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> --- QMP/qmp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 120 insertions(+), 0 deletions(-) create mode 100755 QMP/qmp