Annotation of qemu/QMP/qmp-shell, revision 1.1.1.3

1.1       root        1: #!/usr/bin/python
                      2: #
1.1.1.3 ! root        3: # Low-level QEMU shell on top of QMP.
1.1       root        4: #
1.1.1.3 ! root        5: # Copyright (C) 2009, 2010 Red Hat Inc.
1.1       root        6: #
                      7: # Authors:
                      8: #  Luiz Capitulino <[email protected]>
                      9: #
                     10: # This work is licensed under the terms of the GNU GPL, version 2.  See
                     11: # the COPYING file in the top-level directory.
                     12: #
                     13: # Usage:
                     14: #
                     15: # Start QEMU with:
                     16: #
1.1.1.3 ! root       17: # # qemu [...] -qmp unix:./qmp-sock,server
1.1       root       18: #
                     19: # Run the shell:
                     20: #
1.1.1.3 ! root       21: # $ qmp-shell ./qmp-sock
1.1       root       22: #
                     23: # Commands have the following format:
                     24: #
1.1.1.3 ! root       25: #    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
1.1       root       26: #
                     27: # For example:
                     28: #
1.1.1.3 ! root       29: # (QEMU) device_add driver=e1000 id=net1
        !            30: # {u'return': {}}
        !            31: # (QEMU)
1.1       root       32: 
                     33: import qmp
                     34: import readline
1.1.1.3 ! root       35: import sys
1.1       root       36: 
1.1.1.3 ! root       37: class QMPCompleter(list):
        !            38:     def complete(self, text, state):
        !            39:         for cmd in self:
        !            40:             if cmd.startswith(text):
        !            41:                 if not state:
        !            42:                     return cmd
        !            43:                 else:
        !            44:                     state -= 1
1.1       root       45: 
1.1.1.3 ! root       46: class QMPShellError(Exception):
        !            47:     pass
        !            48: 
        !            49: class QMPShellBadPort(QMPShellError):
        !            50:     pass
        !            51: 
        !            52: # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
        !            53: #       _execute_cmd()). Let's design a better one.
        !            54: class QMPShell(qmp.QEMUMonitorProtocol):
        !            55:     def __init__(self, address):
        !            56:         qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
        !            57:         self._greeting = None
        !            58:         self._completer = None
        !            59: 
        !            60:     def __get_address(self, arg):
        !            61:         """
        !            62:         Figure out if the argument is in the port:host form, if it's not it's
        !            63:         probably a file path.
        !            64:         """
        !            65:         addr = arg.split(':')
        !            66:         if len(addr) == 2:
        !            67:             try:
        !            68:                 port = int(addr[1])
        !            69:             except ValueError:
        !            70:                 raise QMPShellBadPort
        !            71:             return ( addr[0], port )
        !            72:         # socket path
        !            73:         return arg
        !            74: 
        !            75:     def _fill_completion(self):
        !            76:         for cmd in self.cmd('query-commands')['return']:
        !            77:             self._completer.append(cmd['name'])
        !            78: 
        !            79:     def __completer_setup(self):
        !            80:         self._completer = QMPCompleter()
        !            81:         self._fill_completion()
        !            82:         readline.set_completer(self._completer.complete)
        !            83:         readline.parse_and_bind("tab: complete")
        !            84:         # XXX: default delimiters conflict with some command names (eg. query-),
        !            85:         # clearing everything as it doesn't seem to matter
        !            86:         readline.set_completer_delims('')
1.1       root       87: 
1.1.1.3 ! root       88:     def __build_cmd(self, cmdline):
        !            89:         """
        !            90:         Build a QMP input object from a user provided command-line in the
        !            91:         following format:
        !            92:     
        !            93:             < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
        !            94:         """
        !            95:         cmdargs = cmdline.split()
        !            96:         qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
        !            97:         for arg in cmdargs[1:]:
        !            98:             opt = arg.split('=')
        !            99:             try:
        !           100:                 value = int(opt[1])
        !           101:             except ValueError:
        !           102:                 value = opt[1]
        !           103:             qmpcmd['arguments'][opt[0]] = value
        !           104:         return qmpcmd
        !           105: 
        !           106:     def _execute_cmd(self, cmdline):
        !           107:         try:
        !           108:             qmpcmd = self.__build_cmd(cmdline)
        !           109:         except:
        !           110:             print 'command format: <command-name> ',
        !           111:             print '[arg-name1=arg1] ... [arg-nameN=argN]'
        !           112:             return True
        !           113:         resp = self.cmd_obj(qmpcmd)
        !           114:         if resp is None:
        !           115:             print 'Disconnected'
        !           116:             return False
        !           117:         print resp
        !           118:         return True
        !           119: 
        !           120:     def connect(self):
        !           121:         self._greeting = qmp.QEMUMonitorProtocol.connect(self)
        !           122:         self.__completer_setup()
1.1       root      123: 
1.1.1.3 ! root      124:     def show_banner(self, msg='Welcome to the QMP low-level shell!'):
        !           125:         print msg
        !           126:         version = self._greeting['QMP']['version']['qemu']
        !           127:         print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
1.1       root      128: 
1.1.1.3 ! root      129:     def read_exec_command(self, prompt):
        !           130:         """
        !           131:         Read and execute a command.
        !           132: 
        !           133:         @return True if execution was ok, return False if disconnected.
        !           134:         """
1.1       root      135:         try:
1.1.1.3 ! root      136:             cmdline = raw_input(prompt)
1.1       root      137:         except EOFError:
                    138:             print
1.1.1.3 ! root      139:             return False
        !           140:         if cmdline == '':
        !           141:             for ev in self.get_events():
        !           142:                 print ev
        !           143:             self.clear_events()
        !           144:             return True
1.1       root      145:         else:
1.1.1.3 ! root      146:             return self._execute_cmd(cmdline)
        !           147: 
        !           148: class HMPShell(QMPShell):
        !           149:     def __init__(self, address):
        !           150:         QMPShell.__init__(self, address)
        !           151:         self.__cpu_index = 0
        !           152: 
        !           153:     def __cmd_completion(self):
        !           154:         for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
        !           155:             if cmd and cmd[0] != '[' and cmd[0] != '\t':
        !           156:                 name = cmd.split()[0] # drop help text
        !           157:                 if name == 'info':
        !           158:                     continue
        !           159:                 if name.find('|') != -1:
        !           160:                     # Command in the form 'foobar|f' or 'f|foobar', take the
        !           161:                     # full name
        !           162:                     opt = name.split('|')
        !           163:                     if len(opt[0]) == 1:
        !           164:                         name = opt[1]
        !           165:                     else:
        !           166:                         name = opt[0]
        !           167:                 self._completer.append(name)
        !           168:                 self._completer.append('help ' + name) # help completion
        !           169: 
        !           170:     def __info_completion(self):
        !           171:         for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
        !           172:             if cmd:
        !           173:                 self._completer.append('info ' + cmd.split()[1])
        !           174: 
        !           175:     def __other_completion(self):
        !           176:         # special cases
        !           177:         self._completer.append('help info')
        !           178: 
        !           179:     def _fill_completion(self):
        !           180:         self.__cmd_completion()
        !           181:         self.__info_completion()
        !           182:         self.__other_completion()
        !           183: 
        !           184:     def __cmd_passthrough(self, cmdline, cpu_index = 0):
        !           185:         return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
        !           186:                               { 'command-line': cmdline,
        !           187:                                 'cpu-index': cpu_index } })
        !           188: 
        !           189:     def _execute_cmd(self, cmdline):
        !           190:         if cmdline.split()[0] == "cpu":
        !           191:             # trap the cpu command, it requires special setting
1.1       root      192:             try:
1.1.1.3 ! root      193:                 idx = int(cmdline.split()[1])
        !           194:                 if not 'return' in self.__cmd_passthrough('info version', idx):
        !           195:                     print 'bad CPU index'
        !           196:                     return True
        !           197:                 self.__cpu_index = idx
        !           198:             except ValueError:
        !           199:                 print 'cpu command takes an integer argument'
        !           200:                 return True
        !           201:         resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
        !           202:         if resp is None:
        !           203:             print 'Disconnected'
        !           204:             return False
        !           205:         assert 'return' in resp or 'error' in resp
        !           206:         if 'return' in resp:
        !           207:             # Success
        !           208:             if len(resp['return']) > 0:
        !           209:                 print resp['return'],
        !           210:         else:
        !           211:             # Error
        !           212:             print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
        !           213:         return True
        !           214: 
        !           215:     def show_banner(self):
        !           216:         QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
        !           217: 
        !           218: def die(msg):
        !           219:     sys.stderr.write('ERROR: %s\n' % msg)
        !           220:     sys.exit(1)
        !           221: 
        !           222: def fail_cmdline(option=None):
        !           223:     if option:
        !           224:         sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
        !           225:     sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
        !           226:     sys.exit(1)
        !           227: 
        !           228: def main():
        !           229:     addr = ''
        !           230:     try:
        !           231:         if len(sys.argv) == 2:
        !           232:             qemu = QMPShell(sys.argv[1])
        !           233:             addr = sys.argv[1]
        !           234:         elif len(sys.argv) == 3:
        !           235:             if sys.argv[1] != '-H':
        !           236:                 fail_cmdline(sys.argv[1])
        !           237:             qemu = HMPShell(sys.argv[2])
        !           238:             addr = sys.argv[2]
        !           239:         else:
        !           240:                 fail_cmdline()
        !           241:     except QMPShellBadPort:
        !           242:         die('bad port number in command-line')
        !           243: 
        !           244:     try:
        !           245:         qemu.connect()
        !           246:     except qmp.QMPConnectError:
        !           247:         die('Didn\'t get QMP greeting message')
        !           248:     except qmp.QMPCapabilitiesError:
        !           249:         die('Could not negotiate capabilities')
        !           250:     except qemu.error:
        !           251:         die('Could not connect to %s' % addr)
        !           252: 
        !           253:     qemu.show_banner()
        !           254:     while qemu.read_exec_command('(QEMU) '):
        !           255:         pass
        !           256:     qemu.close()
1.1       root      257: 
                    258: if __name__ == '__main__':
                    259:     main()

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.