[dts] [‘dts-v1’ 5/9] Add qemu-agent-guest for QEMU VM

Liu, Yong yong.liu at intel.com
Mon May 18 16:00:05 CEST 2015


Need check copyright whether qemu-agent can be used here.

> -----Original Message-----
> From: dts [mailto:dts-bounces at dpdk.org] On Behalf Of sjiajiax
> Sent: Monday, May 18, 2015 1:07 PM
> To: dts at dpdk.org
> Subject: [dts] [‘dts-v1’ 5/9] Add qemu-agent-guest for QEMU VM
> 
> Signed-off-by: sjiajiax <sunx.jiajia at intel.com>
> ---
>  dep/QMP/qemu-ga-client | 299
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  dep/QMP/qmp.py         | 193 +++++++++++++++++++++++++++++++
>  2 files changed, 492 insertions(+)
>  create mode 100644 dep/QMP/qemu-ga-client
>  create mode 100644 dep/QMP/qmp.py
> 
> diff --git a/dep/QMP/qemu-ga-client b/dep/QMP/qemu-ga-client
> new file mode 100644
> index 0000000..46676c3
> --- /dev/null
> +++ b/dep/QMP/qemu-ga-client
> @@ -0,0 +1,299 @@
> +#!/usr/bin/python
> +
> +# QEMU Guest Agent Client
> +#
> +# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota at gmail.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2.  See
> +# the COPYING file in the top-level directory.
> +#
> +# Usage:
> +#
> +# Start QEMU with:
> +#
> +# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
> +#   -device virtio-serial -device
> virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
> +#
> +# Run the script:
> +#
> +# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
> +#
> +# or
> +#
> +# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
> +# $ qemu-ga-client <command> [args...]
> +#
> +# For example:
> +#
> +# $ qemu-ga-client cat /etc/resolv.conf
> +# # Generated by NetworkManager
> +# nameserver 10.0.2.3
> +# $ qemu-ga-client fsfreeze status
> +# thawed
> +# $ qemu-ga-client fsfreeze freeze
> +# 2 filesystems frozen
> +#
> +# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
> +#
> +
> +import base64
> +import random
> +
> +import qmp
> +
> +
> +class QemuGuestAgent(qmp.QEMUMonitorProtocol):
> +    def __getattr__(self, name):
> +        def wrapper(**kwds):
> +            return self.command('guest-' + name.replace('_', '-'), **kwds)
> +        return wrapper
> +
> +
> +class QemuGuestAgentClient:
> +    error = QemuGuestAgent.error
> +
> +    def __init__(self, address):
> +        self.qga = QemuGuestAgent(address)
> +        self.qga.connect(negotiate=False)
> +
> +    def sync(self, timeout=3):
> +        # Avoid being blocked forever
> +        if not self.ping(timeout):
> +            raise EnvironmentError('Agent seems not alive')
> +        uid = random.randint(0, (1 << 32) - 1)
> +        while True:
> +            ret = self.qga.sync(id=uid)
> +            if isinstance(ret, int) and int(ret) == uid:
> +                break
> +
> +    def __file_read_all(self, handle):
> +        eof = False
> +        data = ''
> +        while not eof:
> +            ret = self.qga.file_read(handle=handle, count=1024)
> +            _data = base64.b64decode(ret['buf-b64'])
> +            data += _data
> +            eof = ret['eof']
> +        return data
> +
> +    def read(self, path):
> +        handle = self.qga.file_open(path=path)
> +        try:
> +            data = self.__file_read_all(handle)
> +        finally:
> +            self.qga.file_close(handle=handle)
> +        return data
> +
> +    def info(self):
> +        info = self.qga.info()
> +
> +        msgs = []
> +        msgs.append('version: ' + info['version'])
> +        msgs.append('supported_commands:')
> +        enabled = [c['name'] for c in info['supported_commands'] if
> c['enabled']]
> +        msgs.append('\tenabled: ' + ', '.join(enabled))
> +        disabled = [c['name'] for c in info['supported_commands'] if not
> c['enabled']]
> +        msgs.append('\tdisabled: ' + ', '.join(disabled))
> +
> +        return '\n'.join(msgs)
> +
> +    def __gen_ipv4_netmask(self, prefixlen):
> +        mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
> +        return '.'.join([str(mask >> 24),
> +                         str((mask >> 16) & 0xff),
> +                         str((mask >> 8) & 0xff),
> +                         str(mask & 0xff)])
> +
> +    def ifconfig(self):
> +        nifs = self.qga.network_get_interfaces()
> +
> +        msgs = []
> +        for nif in nifs:
> +            msgs.append(nif['name'] + ':')
> +            if 'ip-addresses' in nif:
> +                for ipaddr in nif['ip-addresses']:
> +                    if ipaddr['ip-address-type'] == 'ipv4':
> +                        addr = ipaddr['ip-address']
> +                        mask =
> self.__gen_ipv4_netmask(int(ipaddr['prefix']))
> +                        msgs.append("\tinet %s  netmask %s" % (addr,
> mask))
> +                    elif ipaddr['ip-address-type'] == 'ipv6':
> +                        addr = ipaddr['ip-address']
> +                        prefix = ipaddr['prefix']
> +                        msgs.append("\tinet6 %s  prefixlen %s" % (addr,
> prefix))
> +            if nif['hardware-address'] != '00:00:00:00:00:00':
> +                msgs.append("\tether " + nif['hardware-address'])
> +
> +        return '\n'.join(msgs)
> +
> +    def ping(self, timeout):
> +        self.qga.settimeout(timeout)
> +        try:
> +            self.qga.ping()
> +        except self.qga.timeout:
> +            return False
> +        return True
> +
> +    def fsfreeze(self, cmd):
> +        if cmd not in ['status', 'freeze', 'thaw']:
> +            raise StandardError('Invalid command: ' + cmd)
> +
> +        return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
> +
> +    def fstrim(self, minimum=0):
> +        return getattr(self.qga, 'fstrim')(minimum=minimum)
> +
> +    def suspend(self, mode):
> +        if mode not in ['disk', 'ram', 'hybrid']:
> +            raise StandardError('Invalid mode: ' + mode)
> +
> +        try:
> +            getattr(self.qga, 'suspend' + '_' + mode)()
> +            # On error exception will raise
> +        except self.qga.timeout:
> +            # On success command will timed out
> +            return
> +
> +    def shutdown(self, mode='powerdown'):
> +        if mode not in ['powerdown', 'halt', 'reboot']:
> +            raise StandardError('Invalid mode: ' + mode)
> +
> +        try:
> +            self.qga.shutdown(mode=mode)
> +        except self.qga.timeout:
> +            return
> +
> +
> +def _cmd_cat(client, args):
> +    if len(args) != 1:
> +        print('Invalid argument')
> +        print('Usage: cat <file>')
> +        sys.exit(1)
> +    print(client.read(args[0]))
> +
> +
> +def _cmd_fsfreeze(client, args):
> +    usage = 'Usage: fsfreeze status|freeze|thaw'
> +    if len(args) != 1:
> +        print('Invalid argument')
> +        print(usage)
> +        sys.exit(1)
> +    if args[0] not in ['status', 'freeze', 'thaw']:
> +        print('Invalid command: ' + args[0])
> +        print(usage)
> +        sys.exit(1)
> +    cmd = args[0]
> +    ret = client.fsfreeze(cmd)
> +    if cmd == 'status':
> +        print(ret)
> +    elif cmd == 'freeze':
> +        print("%d filesystems frozen" % ret)
> +    else:
> +        print("%d filesystems thawed" % ret)
> +
> +
> +def _cmd_fstrim(client, args):
> +    if len(args) == 0:
> +        minimum = 0
> +    else:
> +        minimum = int(args[0])
> +    print(client.fstrim(minimum))
> +
> +
> +def _cmd_ifconfig(client, args):
> +    print(client.ifconfig())
> +
> +
> +def _cmd_info(client, args):
> +    print(client.info())
> +
> +
> +def _cmd_ping(client, args):
> +    if len(args) == 0:
> +        timeout = 3
> +    else:
> +        timeout = float(args[0])
> +    alive = client.ping(timeout)
> +    if not alive:
> +        print("Not responded in %s sec" % args[0])
> +        sys.exit(1)
> +
> +
> +def _cmd_suspend(client, args):
> +    usage = 'Usage: suspend disk|ram|hybrid'
> +    if len(args) != 1:
> +        print('Less argument')
> +        print(usage)
> +        sys.exit(1)
> +    if args[0] not in ['disk', 'ram', 'hybrid']:
> +        print('Invalid command: ' + args[0])
> +        print(usage)
> +        sys.exit(1)
> +    client.suspend(args[0])
> +
> +
> +def _cmd_shutdown(client, args):
> +    client.shutdown()
> +_cmd_powerdown = _cmd_shutdown
> +
> +
> +def _cmd_halt(client, args):
> +    client.shutdown('halt')
> +
> +
> +def _cmd_reboot(client, args):
> +    client.shutdown('reboot')
> +
> +
> +commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
> +
> +
> +def main(address, cmd, args):
> +    if not os.path.exists(address):
> +        print('%s not found' % address)
> +        sys.exit(1)
> +
> +    if cmd not in commands:
> +        print('Invalid command: ' + cmd)
> +        print('Available commands: ' + ', '.join(commands))
> +        sys.exit(1)
> +
> +    try:
> +        client = QemuGuestAgentClient(address)
> +    except QemuGuestAgent.error, e:
> +        import errno
> +
> +        print(e)
> +        if e.errno == errno.ECONNREFUSED:
> +            print('Hint: qemu is not running?')
> +        sys.exit(1)
> +
> +    if cmd != 'ping':
> +        client.sync()
> +
> +    globals()['_cmd_' + cmd](client, args)
> +
> +
> +if __name__ == '__main__':
> +    import sys
> +    import os
> +    import optparse
> +
> +    address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in
> os.environ else None
> +
> +    usage = "%prog [--address=<unix_path>|<ipv4_address>] <command>
> [args...]\n"
> +    usage += '<command>: ' + ', '.join(commands)
> +    parser = optparse.OptionParser(usage=usage)
> +    parser.add_option('--address', action='store', type='string',
> +                      default=address, help='Specify a ip:port pair or a
> unix socket path')
> +    options, args = parser.parse_args()
> +
> +    address = options.address
> +    if address is None:
> +        parser.error('address is not specified')
> +        sys.exit(1)
> +
> +    if len(args) == 0:
> +        parser.error('Less argument')
> +        sys.exit(1)
> +
> +    main(address, args[0], args[1:])
> diff --git a/dep/QMP/qmp.py b/dep/QMP/qmp.py
> new file mode 100644
> index 0000000..4ade3ce
> --- /dev/null
> +++ b/dep/QMP/qmp.py
> @@ -0,0 +1,193 @@
> +# QEMU Monitor Protocol Python class
> +#
> +# Copyright (C) 2009, 2010 Red Hat Inc.
> +#
> +# Authors:
> +#  Luiz Capitulino <lcapitulino at redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2.  See
> +# the COPYING file in the top-level directory.
> +
> +import json
> +import errno
> +import socket
> +
> +class QMPError(Exception):
> +    pass
> +
> +class QMPConnectError(QMPError):
> +    pass
> +
> +class QMPCapabilitiesError(QMPError):
> +    pass
> +
> +class QEMUMonitorProtocol:
> +    def __init__(self, address, server=False):
> +        """
> +        Create a QEMUMonitorProtocol class.
> +
> +        @param address: QEMU address, can be either a unix socket path
> (string)
> +                        or a tuple in the form ( address, port ) for a
> TCP
> +                        connection
> +        @param server: server mode listens on the socket (bool)
> +        @raise socket.error on socket connection errors
> +        @note No connection is established, this is done by the connect()
> or
> +              accept() methods
> +        """
> +        self.__events = []
> +        self.__address = address
> +        self.__sock = self.__get_sock()
> +        if server:
> +            self.__sock.bind(self.__address)
> +            self.__sock.listen(1)
> +
> +    def __get_sock(self):
> +        if isinstance(self.__address, tuple):
> +            family = socket.AF_INET
> +        else:
> +            family = socket.AF_UNIX
> +        return socket.socket(family, socket.SOCK_STREAM)
> +
> +    def __negotiate_capabilities(self):
> +        greeting = self.__json_read()
> +        if greeting is None or not greeting.has_key('QMP'):
> +            raise QMPConnectError
> +        # Greeting seems ok, negotiate capabilities
> +        resp = self.cmd('qmp_capabilities')
> +        if "return" in resp:
> +            return greeting
> +        raise QMPCapabilitiesError
> +
> +    def __json_read(self, only_event=False):
> +        while True:
> +            data = self.__sockfile.readline()
> +            if not data:
> +                return
> +            resp = json.loads(data)
> +            if 'event' in resp:
> +                self.__events.append(resp)
> +                if not only_event:
> +                    continue
> +            return resp
> +
> +    error = socket.error
> +
> +    def connect(self, negotiate=True):
> +        """
> +        Connect to the QMP Monitor and perform capabilities negotiation.
> +
> +        @return QMP greeting dict
> +        @raise socket.error on socket connection errors
> +        @raise QMPConnectError if the greeting is not received
> +        @raise QMPCapabilitiesError if fails to negotiate capabilities
> +        """
> +        self.__sock.connect(self.__address)
> +        self.__sockfile = self.__sock.makefile()
> +        if negotiate:
> +            return self.__negotiate_capabilities()
> +
> +    def accept(self):
> +        """
> +        Await connection from QMP Monitor and perform capabilities
> negotiation.
> +
> +        @return QMP greeting dict
> +        @raise socket.error on socket connection errors
> +        @raise QMPConnectError if the greeting is not received
> +        @raise QMPCapabilitiesError if fails to negotiate capabilities
> +        """
> +        self.__sock, _ = self.__sock.accept()
> +        self.__sockfile = self.__sock.makefile()
> +        return self.__negotiate_capabilities()
> +
> +    def cmd_obj(self, qmp_cmd):
> +        """
> +        Send a QMP command to the QMP Monitor.
> +
> +        @param qmp_cmd: QMP command to be sent as a Python dict
> +        @return QMP response as a Python dict or None if the connection
> has
> +                been closed
> +        """
> +        try:
> +            self.__sock.sendall(json.dumps(qmp_cmd))
> +        except socket.error, err:
> +            if err[0] == errno.EPIPE:
> +                return
> +            raise socket.error(err)
> +        return self.__json_read()
> +
> +    def cmd(self, name, args=None, id=None):
> +        """
> +        Build a QMP command and send it to the QMP Monitor.
> +
> +        @param name: command name (string)
> +        @param args: command arguments (dict)
> +        @param id: command id (dict, list, string or int)
> +        """
> +        qmp_cmd = { 'execute': name }
> +        if args:
> +            qmp_cmd['arguments'] = args
> +        if id:
> +            qmp_cmd['id'] = id
> +        return self.cmd_obj(qmp_cmd)
> +
> +    def command(self, cmd, **kwds):
> +        ret = self.cmd(cmd, kwds)
> +        if not ret:
> +            return
> +        else:
> +            if ret.has_key('error'):
> +                raise Exception(ret['error']['desc'])
> +            return ret['return']
> +
> +    def pull_event(self, wait=False):
> +        """
> +        Get and delete the first available QMP event.
> +
> +        @param wait: block until an event is available (bool)
> +        """
> +        self.__sock.setblocking(0)
> +        try:
> +            self.__json_read()
> +        except socket.error, err:
> +            if err[0] == errno.EAGAIN:
> +                # No data available
> +                pass
> +        self.__sock.setblocking(1)
> +        if not self.__events and wait:
> +            self.__json_read(only_event=True)
> +        event = self.__events[0]
> +        del self.__events[0]
> +        return event
> +
> +    def get_events(self, wait=False):
> +        """
> +        Get a list of available QMP events.
> +
> +        @param wait: block until an event is available (bool)
> +        """
> +        self.__sock.setblocking(0)
> +        try:
> +            self.__json_read()
> +        except socket.error, err:
> +            if err[0] == errno.EAGAIN:
> +                # No data available
> +                pass
> +        self.__sock.setblocking(1)
> +        if not self.__events and wait:
> +            self.__json_read(only_event=True)
> +        return self.__events
> +
> +    def clear_events(self):
> +        """
> +        Clear current list of pending events.
> +        """
> +        self.__events = []
> +
> +    def close(self):
> +        self.__sock.close()
> +        self.__sockfile.close()
> +
> +    timeout = socket.timeout
> +
> +    def settimeout(self, timeout):
> +        self.__sock.settimeout(timeout)
> --
> 1.9.0



More information about the dts mailing list