[libvirt] Supporting vhost-net and macvtap in libvirt for QEMU
by Anthony Liguori
Disclaimer: I am neither an SR-IOV nor a vhost-net expert, but I've CC'd
people that are who can throw tomatoes at me for getting bits wrong :-)
I wanted to start a discussion about supporting vhost-net in libvirt.
vhost-net has not yet been merged into qemu but I expect it will be soon
so it's a good time to start this discussion.
There are two modes worth supporting for vhost-net in libvirt. The
first mode is where vhost-net backs to a tun/tap device. This is
behaves in very much the same way that -net tap behaves in qemu today.
Basically, the difference is that the virtio backend is in the kernel
instead of in qemu so there should be some performance improvement.
Current, libvirt invokes qemu with -net tap,fd=X where X is an already
open fd to a tun/tap device. I suspect that after we merge vhost-net,
libvirt could support vhost-net in this mode by just doing -net
vhost,fd=X. I think the only real question for libvirt is whether to
provide a user visible switch to use vhost or to just always use vhost
when it's available and it makes sense. Personally, I think the later
makes sense.
The more interesting invocation of vhost-net though is one where the
vhost-net device backs directly to a physical network card. In this
mode, vhost should get considerably better performance than the current
implementation. I don't know the syntax yet, but I think it's
reasonable to assume that it will look something like -net
tap,dev=eth0. The effect will be that eth0 is dedicated to the guest.
On most modern systems, there is a small number of network devices so
this model is not all that useful except when dealing with SR-IOV
adapters. In that case, each physical device can be exposed as many
virtual devices (VFs). There are a few restrictions here though. The
biggest is that currently, you can only change the number of VFs by
reloading a kernel module so it's really a parameter that must be set at
startup time.
I think there are a few ways libvirt could support vhost-net in this
second mode. The simplest would be to introduce a new tag similar to
<source network='br0'>. In fact, if you probed the device type for the
network parameter, you could probably do something like <source
network='eth0'> and have it Just Work.
Another model would be to have libvirt see an SR-IOV adapter as a
network pool whereas it handled all of the VF management. Considering
how inflexible SR-IOV is today, I'm not sure whether this is the best model.
Has anyone put any more thought into this problem or how this should be
modeled in libvirt? Michael, could you share your current thinking for
-net syntax?
--
Regards,
Anthony Liguori
1 year, 1 month
[libvirt] [PATCH 0/4] Multiple problems with saving to block devices
by Daniel P. Berrange
This patch series makes it possible to save to a block device,
instead of a plain file. There were multiple problems
- WHen save failed, we might de-reference a NULL pointer
- When save failed, we unlinked the device node !!
- The approach of using >> to append, doesn't work with block devices
- CGroups was blocking QEMU access to the block device when enabled
One remaining problem is not in libvirt, but rather QEMU. The QEMU
exec: based migration often fails to detect failure of the command
and will thus hang forever attempting a migration that'll never
succeed! Fortunately you can now work around this in libvirt using
the virsh domjobabort command
11 years, 9 months
[libvirt] [PATCHv4 00/51] another round of snapshot patches
by Eric Blake
I think I've addressed most findings from round 3 - by implementing
the ability to redefine a snapshot, it becomes possible to restore
snapshot hierarchy when recreating a transient domain by the same
name. New goodies in this round: several bug fixes, add virsh
snapshot-edit, drop undefine --snapshots-full (you can only remove
snapshot metadata on undefine). I tested as I went, but this went
through so many rebases that there may be some nasties that snuck
in; but I wanted to get this posted now. I also know that I'm
missing at least one major feature requested in the v3 review:
namely, transient domains _should_ auto-remove snapshot metadata
files when they halt, but right now aren't doing that.
v3 was at:
https://www.redhat.com/archives/libvir-list/2011-August/msg01132.html
Also available here:
git fetch git://repo.or.cz/libvirt/ericb.git snapshot
or browse online at:
http://repo.or.cz/w/libvirt/ericb.git/shortlog/refs/heads/snapshot
I'm also trying to group things by several bugzilla related to
various patches (looks like I still need to create a few):
Eric Blake (51):
https://bugzilla.redhat.com/show_bug.cgi?id=674537
snapshot: fix corner case on OOM during creation
https://bugzilla.redhat.com/show_bug.cgi?id=733762
snapshot: better events when starting paused
snapshot: fine-tune ability to start paused
snapshot: expose --running and --paused in virsh
snapshot: fine-tune qemu saved images starting paused
snapshot: improve reverting to qemu paused snapshots
snapshot: properly revert qemu to offline snapshots
snapshot: fine-tune qemu snapshot revert states
no bug filed yet... should be one about no stale metadata
snapshot: allow deletion of just snapshot metadata
snapshot: add snapshot-list --parent to virsh
https://bugzilla.redhat.com/show_bug.cgi?id=733529
snapshot: speed up snapshot location
snapshot: avoid crash when deleting qemu snapshots
snapshot: track current domain across deletion of children
snapshot: simplify acting on just children
no bug filed yet... should be one about no stale metadata
snapshot: let qemu discard only snapshot metadata
snapshot: identify which snapshots have metadata
snapshot: reflect new dumpxml and list options in virsh
snapshot: identify qemu snapshot roots
snapshot: allow recreation of metadata
snapshot: refactor virsh snapshot creation
snapshot: improve virsh snapshot-create, add snapshot-edit
snapshot: add qemu snapshot creation without metadata
no bug filed yet... should be one about snapshot migration
snapshot: add qemu snapshot redefine support
snapshot: prevent stranding snapshot data on domain destruction
snapshot: teach virsh about new undefine flags
snapshot: refactor some qemu code
snapshot: cache qemu-img location
snapshot: support new undefine flags in qemu
snapshot: prevent migration from stranding snapshot data
https://bugzilla.redhat.com/show_bug.cgi?id=638510
snapshot: refactor domain xml output
snapshot: allow full domain xml in snapshot
snapshot: correctly escape generated xml
snapshot: update rng to support full domain in xml
snapshot: store qemu domain details in xml
snapshot: additions to domain xml for disks
snapshot: reject transient disks where code is not ready
snapshot: introduce new deletion flag
snapshot: expose new delete flag in virsh
snapshot: allow halting after snapshot
snapshot: expose halt-after-creation in virsh
snapshot: wire up new qemu monitor command
snapshot: support extra state in snapshots
snapshot: add <disks> to snapshot xml
snapshot: also support disks by path
snapshot: add virsh domblklist command
snapshot: add flag for requesting disk snapshot
snapshot: wire up disk-only flag to snapshot-create
snapshot: reject unimplemented disk snapshot features
snapshot: make it possible to audit external snapshot
snapshot: wire up live qemu disk snapshots
snapshot: use SELinux and lock manager with external snapshots
docs/formatdomain.html.in | 40 +-
docs/formatsnapshot.html.in | 269 ++-
docs/schemas/Makefile.am | 1 +
docs/schemas/domain.rng | 2555 +-------------------
docs/schemas/{domain.rng => domaincommon.rng} | 32 +-
docs/schemas/domainsnapshot.rng | 84 +-
examples/domain-events/events-c/event-test.c | 37 +-
include/libvirt/libvirt.h.in | 66 +-
src/conf/domain_audit.c | 12 +-
src/conf/domain_audit.h | 4 +-
src/conf/domain_conf.c | 902 ++++++--
src/conf/domain_conf.h | 76 +-
src/esx/esx_driver.c | 38 +-
src/libvirt.c | 256 ++-
src/libvirt_private.syms | 8 +
src/libxl/libxl_conf.c | 5 +
src/libxl/libxl_driver.c | 11 +-
src/qemu/qemu_command.c | 5 +
src/qemu/qemu_conf.h | 1 +
src/qemu/qemu_driver.c | 1532 +++++++++---
src/qemu/qemu_hotplug.c | 18 +-
src/qemu/qemu_migration.c | 48 +-
src/qemu/qemu_migration.h | 2 -
src/qemu/qemu_monitor.c | 24 +
src/qemu/qemu_monitor.h | 4 +
src/qemu/qemu_monitor_json.c | 33 +
src/qemu/qemu_monitor_json.h | 4 +
src/qemu/qemu_monitor_text.c | 40 +
src/qemu/qemu_monitor_text.h | 4 +
src/qemu/qemu_process.c | 11 +-
src/uml/uml_driver.c | 56 +-
src/vbox/vbox_tmpl.c | 43 +-
src/xen/xend_internal.c | 12 +-
src/xenxs/xen_sxpr.c | 5 +
src/xenxs/xen_xm.c | 5 +
tests/domainsnapshotxml2xmlin/disk_snapshot.xml | 16 +
tests/domainsnapshotxml2xmlout/disk_snapshot.xml | 77 +
tests/domainsnapshotxml2xmlout/full_domain.xml | 35 +
.../qemuxml2argv-disk-snapshot.args | 7 +
.../qemuxml2argv-disk-snapshot.xml | 39 +
.../qemuxml2argv-disk-transient.xml | 27 +
tests/qemuxml2argvtest.c | 2 +
tests/virsh-optparse | 20 +
tools/virsh.c | 772 +++++-
tools/virsh.pod | 214 ++-
45 files changed, 3978 insertions(+), 3474 deletions(-)
copy docs/schemas/{domain.rng => domaincommon.rng} (98%)
create mode 100644 tests/domainsnapshotxml2xmlin/disk_snapshot.xml
create mode 100644 tests/domainsnapshotxml2xmlout/disk_snapshot.xml
create mode 100644 tests/domainsnapshotxml2xmlout/full_domain.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-snapshot.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-snapshot.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-transient.xml
--
1.7.4.4
12 years, 1 month
[libvirt] virsh bash completion file
by Serge E. Hallyn
Hi,
I've been trying out a bash autocompletion file by Geoff Low (slight hack
by me, don't blame him for my hack), and it's working pretty nicely.
I'm not sure where to put it in the git tree, but it seems like it'd be
nice to have upstream?
thanks,
-serge
====================================================
#!/bin/bash
#############################################################################
#
# virsh bash completion
# Author: Geoff Low (glow(a)cmedresearch.com)
#
############################################################################
VIRSH=$(which virsh)
function get_main_option_list
{
# assume options are specified as [--option1 | --option2 | ...]
OPTIONS=$(${VIRSH} help | grep '|' | sed -e 's/.*\[\(.*\)\]/\1/g;s/|//g')
}
function get_option_list
{
# get the options for a choice
local option=$1;
# assume options are specified as [--option1 | --option2 | ...]
OPTIONS=$(${VIRSH} help ${option} 2> /dev/null | grep '|' | sed -e 's/.*\[\(.*\)\]/\1/g;s/|//g')
}
function _virsh_complete()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
cnxn=""
i=0
while [[ $i -lt $COMP_CWORD ]]; do
if [ ${COMP_WORDS[i]} = "-c" ]; then
tgt=`echo ${COMP_LINE} | sed -e 's/^.* -c \([^ \t]*\).*$/\1/'`
cnxn="-c $tgt"
break
fi
let i++
done
if [ ! -z $tgt ]; then
if [ $COMP_CWORD -eq 5 ]; then
prev="${COMP_WORDS[0]}"
fi
fi
_all_domains=$(${VIRSH} ${cnxn} --q list --all 2> /dev/null | grep -E "(running|shut off)$" | awk '{print $2,$3}')
all_domains=$(echo "$_all_domains" | cut -d " " -f 1)
all_networks=$(${VIRSH} --q net-list --all 2> /dev/null | grep 'active' | cut -d " " -f 1)
live_domains=$(echo "$_all_domains" | grep -E "running$" | awk '{print $1}')
shutoff_domains=$(echo "$_all_domains" | grep -E "shut$" | awk '{print $1}')
case "${prev}" in
virsh | --help)
get_main_option_list
ALL_OPTS=$(${VIRSH} -q help 2> /dev/null | sed '1,2d' | awk '{print $1}')
COMPREPLY=( $(compgen -W "$OPTIONS $ALL_OPTS" -- ${cur}) )
;;
list)
get_option_list ${prev}
COMPREPLY=( $(compgen -W "$OPTIONS" -- ${cur}) )
;;
autostart | dominfo | domuuid | domid | dominfo | domname | dumpxml | setmem )
get_option_list ${prev}
COMPREPLY=( $(compgen -W "$OPTIONS $all_domains" -- ${cur}) )
;;
start | undefine)
get_option_list ${prev}
COMPREPLY=( $(compgen -W "$OPTIONS $shutoff_domains" -- ${cur}) )
;;
create | define)
get_option_list ${prev}
xml_files=$(ls *.xml)
COMPREPLY=( $(compgen -W "$OPTIONS $xml_files" -- ${cur}) )
;;
shutdown | domstate | console | destroy | reboot | save | suspend | resume | vcpuinfo | vncdisplay)
get_option_list ${prev}
COMPREPLY=( $(compgen -W "$OPTIONS $live_domains" -- ${cur}) )
;;
connect)
get_option_list ${prev}
URI_TEMPLATES="xen:// qemu:// test:// xen+ssh:// test+tcp:// qemu+unix:// qemu+tls:// qemu+ssh://"
COMPREPLY=( $(compgen -W "$OPTIONS $URI_TEMPLATES" -- ${cur}) )
;;
*)
COMPREPLY=( $(compgen -f -- ${cur}) )
return 0
;;
esac
}
complete -F _virsh_complete virsh
12 years, 10 months
[libvirt] [PATCH 0/2] Introduce two new virsh commands
by Osier Yang
These two patches is to introduce two new virsh commands, one is
eject-media, which is to eject media from CD or floppy drive, the other
is insert-media, which is to insert media into CD or floppy drive.
There are commands existed can be used to eject/insert media, such as
"update-device", but it's not quite easy to use. That's the original
intention of these patches.
Both of the two commands only allow to operate on CDROM or floppy disk.
[PATCH 1/2] virsh: Introduce two new commands to insert or eject media
[PATCH 2/2] doc: Add docs for two new introduced commands
Regards
Osier
13 years
[libvirt] [PATCH] add a default event handle, to passthough the new events come from qemu
by shaohef@linux.vnet.ibm.com
From: Shaohe Feng <shaohef(a)linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough.
That way, if we use new commands in the monitor that generate new events, we want
some way to receive those new events too.
In order to test this patch, see the attached python test case. When domains are started,
it will be able to catch RESUME events.
Signed-off-by: Shaohe Feng <shaohef(a)linux.vnet.ibm.com>
---
daemon/remote.c | 34 ++++++++++++++++++++++
include/libvirt/libvirt.h.in | 14 +++++++++
python/libvirt-override-virConnect.py | 12 ++++++++
python/libvirt-override.c | 50 +++++++++++++++++++++++++++++++++
src/conf/domain_event.c | 46 ++++++++++++++++++++++++++++++
src/conf/domain_event.h | 5 +++
src/libvirt_private.syms | 2 +
src/qemu/qemu_monitor.c | 9 ++++++
src/qemu/qemu_monitor.h | 6 ++++
src/qemu/qemu_monitor_json.c | 31 ++++++++++++++++++++
src/qemu/qemu_process.c | 23 +++++++++++++++
src/remote/remote_driver.c | 31 ++++++++++++++++++++
src/remote/remote_protocol.x | 8 ++++-
src/remote_protocol-structs | 5 +++
14 files changed, 275 insertions(+), 1 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 245d41c..ef7d513 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -426,6 +426,38 @@ mem_error:
return -1;
}
+static int remoteRelayDomainEventDefault(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virDomainPtr dom,
+ const char *rawEvent,
+ void *opaque)
+{
+ virNetServerClientPtr client = opaque;
+ remote_domain_event_default_event_msg data;
+
+ if (!client)
+ return -1;
+
+ VIR_DEBUG("Relaying domain default event event %s %d %s",
+ dom->name, dom->id, rawEvent);
+
+ /* build return data */
+ memset(&data, 0, sizeof data);
+ data.rawEvent = (char*)strdup(rawEvent);
+ if (data.rawEvent == NULL)
+ goto mem_error;
+ make_nonnull_domain(&data.dom, dom);
+ remoteDispatchDomainEventSend(client, remoteProgram,
+ REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT,
+ (xdrproc_t)xdr_remote_domain_event_default_event_msg, &data);
+
+ return 0;
+
+mem_error:
+ virReportOOMError();
+ VIR_FREE(data.rawEvent);
+ return -1;
+}
+
static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
@@ -461,6 +493,8 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
+ VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDefault),
+
};
verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 07617be..5ccf8c7 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2975,6 +2975,19 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn,
int type,
int status,
void *opaque);
+/**
+ * virConnectDomainEventDefaultCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @rawEvent: the content of the unknow or un-implementation event
+ *
+ * The callback signature to use when registering for an event of type
+ * VIR_DOMAIN_EVENT_ID_DEFAULT with virConnectDomainEventRegisterAny()
+ */
+typedef void (*virConnectDomainEventDefaultCallback)(virConnectPtr conn,
+ virDomainPtr dom,
+ const char *rawEvent,
+ void *opaque);
/**
* VIR_DOMAIN_EVENT_CALLBACK:
@@ -2995,6 +3008,7 @@ typedef enum {
VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON = 6, /* virConnectDomainEventIOErrorReasonCallback */
VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */
VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */
+ VIR_DOMAIN_EVENT_ID_DEFAULT = 9, /* virConnectDomainEventDefaultCallback */
/*
* NB: this enum value will increase over time as new events are
diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py
index 65b5342..f00cbb9 100644
--- a/python/libvirt-override-virConnect.py
+++ b/python/libvirt-override-virConnect.py
@@ -125,6 +125,18 @@
except AttributeError:
pass
+ def dispatchDomainEventDefaultCallback(self, dom, path, cbData):
+ """Dispatches events to python user Default event callbacks
+ """
+ try:
+ cb = cbData["cb"]
+ opaque = cbData["opaque"]
+
+ cb(self, virDomain(self, _obj=dom), path, opaque)
+ return 0
+ except AttributeError:
+ pass
+
def domainEventDeregisterAny(self, callbackID):
"""Removes a Domain Event Callback. De-registering for a
domain callback will disable delivery of this event type """
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index d65423d..c674390 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -4329,6 +4329,53 @@ libvirt_virConnectDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSE
return ret;
}
+static int
+libvirt_virConnectDomainEventDefaultCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virDomainPtr dom,
+ const char *rawEvent,
+ void *opaque)
+{
+ PyObject *pyobj_cbData = (PyObject*)opaque;
+ PyObject *pyobj_dom;
+ PyObject *pyobj_ret;
+ PyObject *pyobj_conn;
+ PyObject *dictKey;
+ int ret = -1;
+
+ LIBVIRT_ENSURE_THREAD_STATE;
+
+ /* Create a python instance of this virDomainPtr */
+ virDomainRef(dom);
+ pyobj_dom = libvirt_virDomainPtrWrap(dom);
+ Py_INCREF(pyobj_cbData);
+
+ dictKey = libvirt_constcharPtrWrap("conn");
+ pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey);
+ Py_DECREF(dictKey);
+
+ /* Call the Callback Dispatcher */
+ pyobj_ret = PyObject_CallMethod(pyobj_conn,
+ (char*)"dispatchDomainEventDefaultCallback",
+ (char*)"OsO",
+ pyobj_dom, rawEvent, pyobj_cbData);
+
+ Py_DECREF(pyobj_cbData);
+ Py_DECREF(pyobj_dom);
+
+ if (!pyobj_ret) {
+#if DEBUG_ERROR
+ printf("%s - ret:%p\n", __FUNCTION__, pyobj_ret);
+#endif
+ PyErr_Print();
+ } else {
+ Py_DECREF(pyobj_ret);
+ ret = 0;
+ }
+
+ LIBVIRT_RELEASE_THREAD_STATE;
+ return ret;
+}
+
static PyObject *
libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self,
PyObject * args)
@@ -4386,6 +4433,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self,
case VIR_DOMAIN_EVENT_ID_BLOCK_JOB:
cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBlockJobCallback);
break;
+ case VIR_DOMAIN_EVENT_ID_DEFAULT:
+ cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventDefaultCallback);
+ break;
}
if (!cb) {
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index 3189346..04f44fb 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -88,6 +88,9 @@ struct _virDomainEvent {
int type;
int status;
} blockJob;
+ struct {
+ char *rawEvent;
+ }defaultEvent;
} data;
};
@@ -509,6 +512,9 @@ void virDomainEventFree(virDomainEventPtr event)
case VIR_DOMAIN_EVENT_ID_BLOCK_JOB:
VIR_FREE(event->data.blockJob.path);
break;
+ case VIR_DOMAIN_EVENT_ID_DEFAULT:
+ VIR_FREE(event->data.defaultEvent.rawEvent);
+ break;
}
VIR_FREE(event->dom.name);
@@ -923,6 +929,40 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom,
path, type, status);
}
+static virDomainEventPtr
+virDomainEventDefaultNew(int id, const char *name, unsigned char *uuid,
+ const char *rawEvent)
+{
+ virDomainEventPtr ev =
+ virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_DEFAULT,
+ id, name, uuid);
+ if (ev) {
+ if (!(ev->data.defaultEvent.rawEvent = strdup(rawEvent))) {
+ virReportOOMError();
+ VIR_FREE(ev->dom.name);
+ VIR_FREE(ev);
+ return NULL;
+ }
+ }
+
+ return ev;
+}
+
+virDomainEventPtr virDomainEventDefaultNewFromObj(virDomainObjPtr obj,
+ const char *rawEvent)
+{
+
+ return virDomainEventDefaultNew(obj->def->id, obj->def->name,
+ obj->def->uuid, rawEvent);
+}
+
+virDomainEventPtr virDomainEventDefaultNewFromDom(virDomainPtr dom,
+ const char *rawEvent)
+{
+ return virDomainEventDefaultNew(dom->id, dom->name, dom->uuid,
+ rawEvent);
+}
+
virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom)
{
virDomainEventPtr ev =
@@ -1083,6 +1123,12 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn,
cbopaque);
break;
+ case VIR_DOMAIN_EVENT_ID_DEFAULT:
+ ((virConnectDomainEventDefaultCallback)cb)(conn, dom,
+ event->data.defaultEvent.rawEvent,
+ cbopaque);
+ break;
+
default:
VIR_WARN("Unexpected event ID %d", event->eventID);
break;
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
index b06be16..401f781 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -178,6 +178,11 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom,
int type,
int status);
+virDomainEventPtr virDomainEventDefaultNewFromObj(virDomainObjPtr obj,
+ const char *rawEvent);
+virDomainEventPtr virDomainEventDefaultNewFromDom(virDomainPtr dom,
+ const char *rawEvent);
+
int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue,
virDomainEventPtr event);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1ac486f..1dcfc3e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -457,6 +457,8 @@ virDomainWatchdogModelTypeToString;
# domain_event.h
virDomainEventBlockJobNewFromObj;
virDomainEventBlockJobNewFromDom;
+virDomainEventDefaultNewFromObj;
+virDomainEventDefaultNewFromDom;
virDomainEventCallbackListAdd;
virDomainEventCallbackListAddID;
virDomainEventCallbackListCount;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index c9dd69e..a2b4036 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -976,6 +976,15 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
return ret;
}
+int qemuMonitorEmitDefaultEvent(qemuMonitorPtr mon,
+ const char *rawEvent)
+{
+ int ret = -1;
+ VIR_DEBUG("mon=%p", mon);
+ QEMU_MONITOR_CALLBACK(mon, ret, domainDefaultEvent, mon->vm,
+ rawEvent);
+ return ret;
+}
int qemuMonitorSetCapabilities(qemuMonitorPtr mon)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3ec78ad..23a03e5 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -123,6 +123,9 @@ struct _qemuMonitorCallbacks {
const char *diskAlias,
int type,
int status);
+ int (*domainDefaultEvent)(qemuMonitorPtr mon,
+ virDomainObjPtr vm,
+ const char *rawEvent)
};
@@ -194,6 +197,9 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
int type,
int status);
+int qemuMonitorEmitDefaultEvent(qemuMonitorPtr mon,
+ const char *rawEvent);
+
int qemuMonitorStartCPUs(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 3d383c8..dab03cd 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -58,6 +58,7 @@ static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleDefaultEvent(qemuMonitorPtr mon, virJSONValuePtr data);
struct {
const char *type;
@@ -74,6 +75,7 @@ struct {
{ "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
{ "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, },
+ { "DEFAULT_UNKNOW_EVENT", qemuMonitorJSONHandleDefaultEvent, },
};
@@ -83,6 +85,7 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
{
const char *type;
int i;
+ int findEventFlag = -1;
VIR_DEBUG("mon=%p obj=%p", mon, obj);
type = virJSONValueObjectGetString(obj, "event");
@@ -98,9 +101,24 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
VIR_DEBUG("handle %s handler=%p data=%p", type,
eventHandlers[i].handler, data);
(eventHandlers[i].handler)(mon, data);
+ findEventFlag = 0;
break;
}
}
+ if (findEventFlag != 0) {
+ if (!STREQ(eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].type, "DEFAULT_UNKNOW_EVENT")) {
+ VIR_ERROR("the last element is not the default event handler");
+ }
+ else {
+ char *event = NULL;
+ event = virJSONValueToString(obj);
+ if (event != NULL){
+ VIR_DEBUG("Unknow event,call default event handler %s",event);
+ free(event);
+ }
+ (eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].handler)(mon, obj);
+ }
+ }
return 0;
}
@@ -720,6 +738,19 @@ out:
}
+static void qemuMonitorJSONHandleDefaultEvent(qemuMonitorPtr mon, virJSONValuePtr data)
+{
+ char *defaultEventStr = NULL;
+ defaultEventStr = virJSONValueToString(data);
+ if (defaultEventStr == NULL){
+ VIR_ERROR("Can not get string form JSONValue");
+ return;
+ }
+ qemuMonitorEmitDefaultEvent(mon, defaultEventStr);
+ free(defaultEventStr);
+}
+
+
int
qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
const char *cmd_str,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index a7fe86c..46881eb 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -713,6 +713,28 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
}
static int
+qemuProcessHandleDefaultEvent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ const char *rawEvent)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event = NULL;
+
+ virDomainObjLock(vm);
+ event = virDomainEventDefaultNewFromObj(vm, rawEvent);
+
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+static int
qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDomainObjPtr vm,
int phase,
@@ -829,6 +851,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
.domainIOError = qemuProcessHandleIOError,
.domainGraphics = qemuProcessHandleGraphics,
.domainBlockJob = qemuProcessHandleBlockJob,
+ .domainDefaultEvent = qemuProcessHandleDefaultEvent,
};
static int
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 2b2f41e..9648661 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -228,6 +228,11 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog,
virNetClientPtr client,
void *evdata, void *opaque);
+static void
+remoteDomainBuildEventDefaultEvent(virNetClientProgramPtr prog,
+ virNetClientPtr client,
+ void *evdata, void *opaque);
+
static virNetClientProgramEvent remoteDomainEvents[] = {
{ REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
remoteDomainBuildEventRTCChange,
@@ -265,6 +270,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = {
remoteDomainBuildEventBlockJob,
sizeof(remote_domain_event_block_job_msg),
(xdrproc_t)xdr_remote_domain_event_block_job_msg },
+ { REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT,
+ remoteDomainBuildEventDefaultEvent,
+ sizeof(remote_domain_event_default_event_msg),
+ (xdrproc_t)xdr_remote_domain_event_default_event_msg },
};
enum virDrvOpenRemoteFlags {
@@ -3220,6 +3229,28 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
}
static void
+remoteDomainBuildEventDefaultEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque)
+{
+ virConnectPtr conn = opaque;
+ struct private_data *priv = conn->privateData;
+ remote_domain_event_default_event_msg *msg = evdata;
+ virDomainPtr dom;
+ virDomainEventPtr event = NULL;
+
+ dom = get_nonnull_domain(conn, msg->dom);
+ if (!dom)
+ return;
+
+ event = virDomainEventDefaultNewFromDom(dom, msg->rawEvent);
+
+ virDomainFree(dom);
+
+ remoteDomainEventQueue(priv, event);
+}
+
+static void
remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetClientPtr client ATTRIBUTE_UNUSED,
void *evdata, void *opaque)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index c8a92fd..81c6a4e 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2010,6 +2010,11 @@ struct remote_domain_event_block_job_msg {
int status;
};
+struct remote_domain_event_default_event_msg {
+ remote_nonnull_domain dom;
+ remote_nonnull_string rawEvent;
+};
+
struct remote_domain_managed_save_args {
remote_nonnull_domain dom;
unsigned int flags;
@@ -2525,7 +2530,8 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_MIGRATE_GET_MAX_SPEED = 242, /* autogen autogen */
REMOTE_PROC_DOMAIN_BLOCK_STATS_FLAGS = 243, /* skipgen skipgen */
REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244, /* autogen autogen */
- REMOTE_PROC_DOMAIN_RESET = 245 /* autogen autogen */
+ REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */
+ REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT = 246 /* skipgen skipgen */
/*
* Notice how the entries are grouped in sets of 10 ?
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 69175cc..7d0d0d4 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -1509,6 +1509,10 @@ struct remote_domain_event_block_job_msg {
int type;
int status;
};
+struct remote_domain_event_default_event_msg {
+ remote_nonnull_domain dom;
+ remote_nonnull_string rawEvent;
+};
struct remote_domain_managed_save_args {
remote_nonnull_domain dom;
u_int flags;
@@ -1971,4 +1975,5 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_BLOCK_STATS_FLAGS = 243,
REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244,
REMOTE_PROC_DOMAIN_RESET = 245,
+ REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT = 246,
};
--
1.7.6
13 years
[libvirt] [PATCH 0/4] Add support for QEMU guest agent control
by Daniel P. Berrange
The QEMU guest agent "/usr/bin/qemu-ga" has some handy functions
for controlling the guest, not least, shutdown/reboot and filesystem
freeze/thaw.
In Fedora 15/16 the semantics of the ACPI power button have been
changed to suspend-to-RAM which breaks our current shutdown
implementation.
By adding support for the agent we gain a more predictable way
to shutdown / reboot guests.
NB: the code currently has the same "flaw" as the monitor, in
so much as we wait forever for a guest agent reply. We need to
add a timeout ability to the agent code
13 years
[libvirt] [test-API][PATCH 1/2] Add support for spice graphics
by Nan Zhang
* utils/Python/xmlgenerator.py: This extends graphics element for spice
XML composing, and support sub-elements settings for audio, images,
streaming and so on:
<graphics type='spice' autoport='yes'>
<image compression='auto_glz'/>
<jpeg compression='auto'/>
<zlib compression='auto'/>
<playback compression='on'/>
<streaming mode='filter'/>
<clipboard copypaste='no'/>
</graphics>
* utils/Python/xmlbuilder.py: Add 2 methods add_graphics() and
build_graphics() to XmlBuilder class.
---
utils/Python/xmlbuilder.py | 36 +++++++++++++++++++++++-
utils/Python/xmlgenerator.py | 62 +++++++++++++++++++++++++++++++++++++----
2 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/utils/Python/xmlbuilder.py b/utils/Python/xmlbuilder.py
index 5a0f8c8..739eccb 100644
--- a/utils/Python/xmlbuilder.py
+++ b/utils/Python/xmlbuilder.py
@@ -64,6 +64,13 @@ class XmlBuilder:
hostdev_node, domain.getElementsByTagName("console")[0])
return hostdev
+ def add_graphics(self, params, domain):
+ graphics = xmlgenerator.graphics_xml(params)
+ graphics_node = domain.importNode(graphics.childNodes[0], True)
+ domain.getElementsByTagName("devices")[0].insertBefore(
+ graphics_node, domain.getElementsByTagName("console")[0])
+ return graphics
+
def build_domain_install(self, params):
domain = xmlgenerator.domain_xml(params, True)
self.add_disk(params, domain)
@@ -151,6 +158,12 @@ class XmlBuilder:
self.write_toxml(hostdev)
return hostdev.toxml()
+ def build_graphics(self, params):
+ graphics = xmlgenerator.graphics_xml(params)
+ if __DEBUG__:
+ self.write_toxml(graphics)
+ return graphics.toxml()
+
def build_pool(self, params):
pool = xmlgenerator.pool_xml(params)
if __DEBUG__:
@@ -242,6 +255,20 @@ if __name__ == "__main__":
interfacexml = xmlobj.build_interface(params)
+ #--------------------------
+ # get graphics xml string
+ #--------------------------
+ print '=' * 30, 'graphics xml', '=' * 30
+ params['graphtype'] = 'spice'
+ params['image'] = 'auto_glz'
+ params['jpeg'] = 'auto'
+ params['zlib'] = 'auto'
+ params['playback'] = 'on'
+ params['streaming'] = 'filter'
+ params['clipboard'] = 'no'
+
+ graphicsxml = xmlobj.build_graphics(params)
+
#---------------------
# get pool xml string
#---------------------
@@ -297,6 +324,13 @@ if __name__ == "__main__":
params['memory'] = '1048576'
params['vcpu'] = '2'
params['inputbus'] = 'usb'
+ params['graphtype'] = 'spice'
+ params['image'] = 'auto_glz'
+ params['jpeg'] = 'auto'
+ params['zlib'] = 'auto'
+ params['playback'] = 'on'
+ params['streaming'] = 'filter'
+ params['clipboard'] = 'no'
params['sound'] = 'ac97'
params['bootcd'] = '/iso/rhel5.iso'
@@ -367,7 +401,7 @@ if __name__ == "__main__":
#----------------------------------------
# get domain snapshot xml string
#----------------------------------------
- params['name'] = 'hello'
+ params['snapshotname'] = 'hello'
params['description'] = 'hello snapshot'
snapshot_xml = xmlobj.build_domain_snapshot(params)
diff --git a/utils/Python/xmlgenerator.py b/utils/Python/xmlgenerator.py
index d57dd33..460f2e5 100644
--- a/utils/Python/xmlgenerator.py
+++ b/utils/Python/xmlgenerator.py
@@ -233,12 +233,6 @@ def domain_xml(params, install = False):
input_element.setAttribute('bus', 'ps2')
devices_element.appendChild(input_element)
- # <graphics>
- graphics_element = domain.createElement('graphics')
- graphics_element.setAttribute('type', 'vnc')
- graphics_element.setAttribute('port', '-1')
- graphics_element.setAttribute('keymap', 'en-us')
- devices_element.appendChild(graphics_element)
domain_element.appendChild(devices_element)
# <sound>
@@ -253,6 +247,62 @@ def domain_xml(params, install = False):
return domain
+def graphics_xml(params):
+ graphics = xml.dom.minidom.Document()
+ # <graphics>
+ graphics_element = graphics.createElement('graphics')
+ if not params.has_key('graphtype'):
+ params['graphtype'] == 'vnc'
+
+ graphics_element.setAttribute('type', params['graphtype'])
+ graphics.appendChild(graphics_element)
+
+ if params['graphtype'] == 'vnc':
+ graphics_element.setAttribute('port', '-1')
+ graphics_element.setAttribute('keymap', 'en-us')
+ elif params['graphtype'] == 'spice':
+ graphics_element.setAttribute('autoport', 'yes')
+ if params.has_key('image'):
+ image_element = graphics.createElement('image')
+ # image to set image compression (accepts
+ # auto_glz, auto_lz, quic, glz, lz, off)
+ image_element.setAttribute('compression', params['image'])
+ graphics_element.appendChild(image_element)
+ if params.has_key('jpeg'):
+ jpeg_element = graphics.createElement('jpeg')
+ # jpeg for JPEG compression for images over wan (accepts
+ # auto, never, always)
+ jpeg_element.setAttribute('compression', params['jpeg'])
+ graphics_element.appendChild(jpeg_element)
+ if params.has_key('zlib'):
+ zlib_element = graphics.createElement('zlib')
+ # zlib for configuring wan image compression (accepts
+ # auto, never, always)
+ zlib_element.setAttribute('compression', params['zlib'])
+ graphics_element.appendChild(zlib_element)
+ if params.has_key('playback'):
+ playback_element = graphics.createElement('playback')
+ # playback for enabling audio stream compression (accepts on or off)
+ playback_element.setAttribute('compression', params['playback'])
+ graphics_element.appendChild(playback_element)
+ if params.has_key('streaming'):
+ streaming_element = graphics.createElement('streaming')
+ # streamming for settings it's mode attribute to one of
+ # filter, all or off
+ streaming_element.setAttribute('mode', params['streaming'])
+ graphics_element.appendChild(streaming_element)
+ if params.has_key('clipboard'):
+ clipboard_element = graphics.createElement('clipboard')
+ # Copy & Paste functionality is enabled by default, and can
+ # be disabled by setting the copypaste property to no
+ clipboard_element.setAttribute('copypaste', params['clipboard'])
+ graphics_element.appendChild(clipboard_element)
+ else:
+ print 'Wrong graphics type was specified.'
+ sys.exit(1)
+
+ return graphics
+
def disk_xml(params, cdrom = False):
disk = xml.dom.minidom.Document()
# <disk> -- START
--
1.7.4.4
13 years