From: "Liu, Yong" <yong.liu@intel.com>
To: "Jiajia, SunX" <sunx.jiajia@intel.com>, "dts@dpdk.org" <dts@dpdk.org>
Subject: Re: [dts] [‘dts-v1’ 5/9] Add qemu-agent-guest for QEMU VM
Date: Mon, 18 May 2015 14:00:05 +0000 [thread overview]
Message-ID: <86228AFD5BCD8E4EBFD2B90117B5E81E10DE4D38@SHSMSX103.ccr.corp.intel.com> (raw)
In-Reply-To: <1431925646-1314-6-git-send-email-sunx.jiajia@intel.com>
Need check copyright whether qemu-agent can be used here.
> -----Original Message-----
> From: dts [mailto:dts-bounces@dpdk.org] On Behalf Of sjiajiax
> Sent: Monday, May 18, 2015 1:07 PM
> To: dts@dpdk.org
> Subject: [dts] [‘dts-v1’ 5/9] Add qemu-agent-guest for QEMU VM
>
> Signed-off-by: sjiajiax <sunx.jiajia@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@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@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
next prev parent reply other threads:[~2015-05-18 14:00 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-05-18 5:07 [dts] [‘dts-v1’ 0/9] sjiajiax
2015-05-18 5:07 ` [dts] [‘dts-v1’ 1/9] Abstract the NIC device as the single class NetDevice sjiajiax
2015-05-18 7:46 ` Xu, HuilongX
2015-05-18 8:58 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 2/9] Optimize ssh connection sjiajiax
2015-05-18 7:06 ` Liu, Yong
2015-05-18 7:43 ` Jiajia, SunX
2015-05-19 0:38 ` Liu, Yong
2015-05-19 7:05 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 3/9] Add some params and functions related to the virtual test sjiajiax
2015-05-18 7:26 ` Liu, Yong
2015-05-18 8:08 ` Jiajia, SunX
2015-05-18 7:59 ` Xu, HuilongX
2015-05-18 9:08 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 4/9] Add VM class and the virtual DUT class and the virtual resource module sjiajiax
2015-05-18 8:23 ` Xu, HuilongX
2015-05-18 13:57 ` Liu, Yong
2015-05-19 5:46 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 5/9] Add qemu-agent-guest for QEMU VM sjiajiax
2015-05-18 14:00 ` Liu, Yong [this message]
2015-05-18 5:07 ` [dts] [‘dts-v1’ 6/9] Add a global virtual configure sjiajiax
2015-05-18 6:32 ` Liu, Yong
2015-05-18 6:48 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 7/9] add some pmd functions for tester to code the testpmd cases sjiajiax
2015-05-18 8:28 ` Xu, HuilongX
2015-05-18 8:45 ` Liu, Yong
2015-05-18 9:05 ` Jiajia, SunX
2015-05-18 9:20 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 8/9] Add two tar files for ACL testing sjiajiax
2015-05-18 14:02 ` Liu, Yong
2015-05-19 5:49 ` Jiajia, SunX
2015-05-18 5:07 ` [dts] [‘dts-v1’ 9/9] Add a suite to test SRIOV mirror with KVM sjiajiax
2015-05-18 6:29 ` [dts] [‘dts-v1’ 0/9] Liu, Yong
2015-05-18 6:47 ` Jiajia, SunX
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=86228AFD5BCD8E4EBFD2B90117B5E81E10DE4D38@SHSMSX103.ccr.corp.intel.com \
--to=yong.liu@intel.com \
--cc=dts@dpdk.org \
--cc=sunx.jiajia@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).