[libvirt] [PATCH] xenapi: Improve error reporting in xenapiOpen once again
by Matthias Bolte
privP->session->error_description is a list and in order to get the
complete error message all parts of the list should be concatenated.
xenapiSessionErrorHandler does this when its third parameter is NULL.
The current code discards all but the first part of the error message
resulting in a potentially incomplete error message.
This partly reverts 006be75ee214f9b4, that tried to avoid reporting
a (null) in the error message. The actual problem is more general in
returnErrorFromSession that might return NULL if there is no error.
Make sure that returnErrorFromSession return non-NULL always. Also
don't skip the last error message part.
---
src/xenapi/xenapi_driver.c | 5 +----
src/xenapi/xenapi_utils.c | 4 +++-
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c
index 97da1d1..77bf82d 100644
--- a/src/xenapi/xenapi_driver.c
+++ b/src/xenapi/xenapi_driver.c
@@ -193,10 +193,7 @@ xenapiOpen (virConnectPtr conn, virConnectAuthPtr auth,
return VIR_DRV_OPEN_SUCCESS;
}
- xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED,
- *privP->session->error_description != NULL ?
- *privP->session->error_description :
- _("unknown error"));
+ xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED, NULL);
error:
VIR_FREE(username);
diff --git a/src/xenapi/xenapi_utils.c b/src/xenapi/xenapi_utils.c
index 342ae5b..79fd946 100644
--- a/src/xenapi/xenapi_utils.c
+++ b/src/xenapi/xenapi_utils.c
@@ -272,12 +272,14 @@ returnErrorFromSession(xen_session *session)
{
int i;
virBuffer buf = VIR_BUFFER_INITIALIZER;
- for (i = 0; i < session->error_description_count - 1; i++) {
+ for (i = 0; i < session->error_description_count; i++) {
if (!i)
virBufferEscapeString(&buf, "%s", session->error_description[i]);
else
virBufferEscapeString(&buf, " : %s", session->error_description[i]);
}
+ if (virBufferUse(&buf) < 1)
+ virBufferAdd(&buf, _("unknown error"), -1);
return virBufferContentAndReset(&buf);
}
--
1.7.4.1
13 years
[libvirt] [PATCH] Allow use of file images for LXC container filesystems
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
A previous commit gave the LXC driver the ability to mount
block devices for the container filesystem. Through use of
the loopback device functionality, we can build on this to
support use of plain file images for LXC filesytems.
By setting the LO_FLAGS_AUTOCLEAR flag we can ensure that
the loop device automatically disappears when the container
dies / shuts down
* src/lxc/lxc_container.c: Raise error if we see a file
based filesystem, since it should have been turned into
a loopback device already
* src/lxc/lxc_controller.c: Rewrite any filesystems of
type=file, into type=block, by binding the file image
to a free loop device
---
src/lxc/lxc_container.c | 5 ++
src/lxc/lxc_controller.c | 176 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 179 insertions(+), 2 deletions(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index f6ab407..bf772e5 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -797,6 +797,11 @@ static int lxcContainerMountFS(virDomainFSDefPtr fs,
if (lxcContainerMountFSBlock(fs, srcprefix) < 0)
return -1;
break;
+ case VIR_DOMAIN_FS_TYPE_FILE:
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ _("Unexpected filesystem type %s"),
+ virDomainFSTypeToString(fs->type));
+ break;
default:
lxcError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Cannot mount filesystem type %s"),
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 8848ae2..45b4c70 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -39,6 +39,8 @@
#include <getopt.h>
#include <sys/mount.h>
#include <locale.h>
+#include <linux/loop.h>
+#include <dirent.h>
#if HAVE_CAPNG
# include <cap-ng.h>
@@ -63,6 +65,160 @@ struct cgroup_device_policy {
int minor;
};
+
+static int lxcGetLoopFD(char **devname)
+{
+ int fd = -1;
+ DIR *dh = NULL;
+ struct dirent *de;
+ char *looppath;
+ struct loop_info64 lo;
+
+ VIR_DEBUG("Looking for loop devices in /dev");
+
+ if (!(dh = opendir("/dev"))) {
+ virReportSystemError(errno, "%s",
+ _("Unable to read /dev"));
+ goto cleanup;
+ }
+
+ while ((de = readdir(dh)) != NULL) {
+ if (!STRPREFIX(de->d_name, "loop"))
+ continue;
+
+ if (virAsprintf(&looppath, "/dev/%s", de->d_name) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Checking up on device %s", looppath);
+ if ((fd = open(looppath, O_RDWR)) < 0) {
+ virReportSystemError(errno,
+ _("Unable to open %s"), looppath);
+ goto cleanup;
+ }
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &lo) < 0) {
+ /* Got a free device, return the fd */
+ if (errno == ENXIO)
+ goto cleanup;
+
+ VIR_FORCE_CLOSE(fd);
+ virReportSystemError(errno,
+ _("Unable to get loop status on %s"),
+ looppath);
+ goto cleanup;
+ }
+
+ /* Oh well, try the next device */
+ VIR_FORCE_CLOSE(fd);
+ VIR_FREE(looppath);
+ }
+
+ lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to find a free loop device in /dev"));
+
+cleanup:
+ if (fd != -1) {
+ VIR_DEBUG("Got free loop device %s %d", looppath, fd);
+ *devname = looppath;
+ } else {
+ VIR_DEBUG("No free loop devices available");
+ VIR_FREE(looppath);
+ }
+ if (dh)
+ closedir(dh);
+ return fd;
+}
+
+static int lxcSetupLoopDevice(virDomainFSDefPtr fs)
+{
+ int lofd = -1;
+ int fsfd = -1;
+ struct loop_info64 lo;
+ char *loname = NULL;
+ int ret = -1;
+
+ if ((lofd = lxcGetLoopFD(&loname)) < 0)
+ return -1;
+
+ memset(&lo, 0, sizeof(lo));
+ lo.lo_flags = LO_FLAGS_AUTOCLEAR;
+
+ if ((fsfd = open(fs->src, O_RDWR)) < 0) {
+ virReportSystemError(errno,
+ _("Unable to open %s"), fs->src);
+ goto cleanup;
+ }
+
+ if (ioctl(lofd, LOOP_SET_FD, fsfd) < 0) {
+ virReportSystemError(errno,
+ _("Unable to attach %s to loop device"),
+ fs->src);
+ goto cleanup;
+ }
+
+ if (ioctl(lofd, LOOP_SET_STATUS64, &lo) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to mark loop device as autoclear"));
+
+ if (ioctl(lofd, LOOP_CLR_FD, 0) < 0)
+ VIR_WARN("Unable to detach %s from loop device", fs->src);
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Attached loop device %s %d to %s", fs->src, lofd, loname);
+ /*
+ * We now change it into a block device type, so that
+ * the rest of container setup 'just works'
+ */
+ fs->type = VIR_DOMAIN_FS_TYPE_BLOCK;
+ VIR_FREE(fs->src);
+ fs->src = loname;
+ loname = NULL;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(loname);
+ VIR_FORCE_CLOSE(fsfd);
+ if (ret == -1)
+ VIR_FORCE_CLOSE(lofd);
+ return lofd;
+}
+
+
+static int lxcSetupLoopDevices(virDomainDefPtr def, size_t *nloopDevs, int **loopDevs)
+{
+ size_t i;
+ int ret = -1;
+
+ for (i = 0 ; i < def->nfss ; i++) {
+ int fd;
+
+ if (def->fss[i]->type != VIR_DOMAIN_FS_TYPE_FILE)
+ continue;
+
+ fd = lxcSetupLoopDevice(def->fss[i]);
+ if (fd < 0)
+ goto cleanup;
+
+ VIR_DEBUG("Saving loop fd %d", fd);
+ if (VIR_REALLOC_N(*loopDevs, *nloopDevs+1) < 0) {
+ VIR_FORCE_CLOSE(fd);
+ virReportOOMError();
+ goto cleanup;
+ }
+ (*loopDevs)[*nloopDevs++] = fd;
+ }
+
+ VIR_DEBUG("Setup all loop devices");
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+
/**
* lxcSetContainerResources
* @def: pointer to virtual machine structure
@@ -641,6 +797,9 @@ lxcControllerRun(virDomainDefPtr def,
virDomainFSDefPtr root;
char *devpts = NULL;
char *devptmx = NULL;
+ size_t nloopDevs = 0;
+ int *loopDevs = NULL;
+ size_t i;
if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) {
virReportSystemError(errno, "%s",
@@ -654,6 +813,9 @@ lxcControllerRun(virDomainDefPtr def,
goto cleanup;
}
+ if (lxcSetupLoopDevices(def, &nloopDevs, &loopDevs) < 0)
+ goto cleanup;
+
root = virDomainGetRootFilesystem(def);
if (lxcSetContainerResources(def) < 0)
@@ -778,8 +940,14 @@ lxcControllerRun(virDomainDefPtr def,
goto cleanup;
}
- /* Now the container is running, there's no need for us to keep
- any elevated capabilities */
+ /* Now the container is fully setup... */
+
+ /* ...we can close the loop devices... */
+
+ for (i = 0 ; i < nloopDevs ; i++)
+ VIR_FORCE_CLOSE(loopDevs[i]);
+
+ /* ...and reduce our privileges */
if (lxcControllerClearCapabilities() < 0)
goto cleanup;
@@ -803,6 +971,10 @@ cleanup:
VIR_FORCE_CLOSE(containerhandshake[0]);
VIR_FORCE_CLOSE(containerhandshake[1]);
+ for (i = 0 ; i < nloopDevs ; i++)
+ VIR_FORCE_CLOSE(loopDevs[i]);
+ VIR_FREE(loopDevs);
+
if (container > 1) {
int status;
kill(container, SIGTERM);
--
1.7.6
13 years, 1 month
[libvirt] qemu-namespace handling?
by Philipp Hahn
Hello,
some time ago I hand to manipulate the domain XML description using Pythons
Elemtree XML implementation, which had problems generating the right format
for libvirt: elemtree just supports adding Qname elements (that
is "{http://libvirt.org/schemas/domain/qemu/1.0}commandline") which
internally would create a temporary binding of this namespace to the "ns0"
Prefix.
My work-around for Elemtree was the add the name-space mapping for "qemu"
to "http://libvirt.org/schemas/domain/qemu/1.0" to ETs internal mapping table
and add an "xmlns:qemu" attribute by hand:
ET._namespace_map[QEMU_URI] = 'qemu'
domain.attrib['xmlns:qemu'] = QEMU_URI
libvirt on the other hand expects the prefix to be "qemu" and only checks,
that this prefix is bound to the URI mentioned above at the root node).
The following examples would be XML valid, but are not accepted by libvirt:
<domain>...
<qemu:commandline xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
...</qemu:commandline>
</domain>
<domain xmlns:ns0="http://libvirt.org/schemas/domain/qemu/1.0">...
<ns0:commandline>
...</ns0:commandline>
</domain>
The following (esoteric) example might be wrongly accepted by libvirt
(untested):
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
<qemu:commandline xmlns:qemu="urn:foo">
...</qemu:commandline>
</domain>
I don't know if this is worth fixing, but I still encountered the first two
problems myself and had to spend some time to detecting what I did wrong. So
at least I want to share my finding with others, so they don't do the same
mistake.
Sincerely
Philipp Hahn
--
Philipp Hahn Open Source Software Engineer hahn(a)univention.de
Univention GmbH Linux for Your Business fon: +49 421 22 232- 0
Mary-Somerville-Str.1 D-28359 Bremen fax: +49 421 22 232-99
http://www.univention.de/
13 years, 1 month
[libvirt] Deadlock when using custom handlers
by Guido Günther
Hi,
virt-viewer is using it's own virEventRegisterImpl. With current libvirt
this can deadlock when connection to nonexistant URIs like
qemu+ssh:///unknownhost.example.com/system
like:
23:47:00.338: 1526: debug : doRemoteOpen:503 : proceeding with name = qemu:///system
23:47:00.338: 1526: debug : doRemoteOpen:513 : Connecting with transport 2
23:47:00.338: 1526: debug : virCommandRunAsync:2042 : About to run LC_ALL=C LD_LIBRARY_PATH=/var/scratch/debian/libvirt/upstream/libvirt/src/.libs/ PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/home/agx/bin:/sbin:/usr/sbin:/usr/lib/git-core HOME=/home/agx USER=agx LOGNAME=agx SSH_AUTH_SOCK=/tmp/keyring-RNvlnT/ssh DISPLAY=:0 ssh pustekiste nc -U /var/run/libvirt/libvirt-sock-ro
23:47:00.339: 1526: debug : virCommandRunAsync:2058 : Command result 0, with PID 1527
23:47:00.339: 1526: debug : virNetSocketNew:115 : localAddr=(nil) remoteAddr=(nil) fd=6 errfd=8 pid=1527
23:47:00.339: 1526: debug : virNetSocketNew:173 : sock=0x8926d20 localAddrStr=(null) remoteAddrStr=(null)
** (virt-viewer:1526): DEBUG: Add handle 6 1 0x8926d20
23:47:00.339: 1526: debug : virNetClientNew:160 : client=0xb5b5f008 refs=2
23:47:00.339: 1526: debug : doRemoteOpen:640 : Trying authentication
23:47:00.339: 1526: debug : virNetMessageNew:44 : msg=0xb5b1e008
23:47:00.340: 1526: debug : virNetMessageEncodePayload:255 : Encode length as 28
23:47:00.340: 1526: debug : virNetClientIO:1035 : program=536903814 version=1 serial=0 proc=66 type=0 length=28 dispatch=(nil)
23:47:00.340: 1526: debug : virNetClientIO:1103 : We have the buck 0x8939940 0x8939940
23:47:00.350: 1526: debug : virNetClientIOEventLoop:979 : Giving up the buck due to I/O error 0x8939940 (nil)
23:47:00.350: 1526: debug : virNetClientIO:1130 : All done with our call (nil) 0x8939940 -1
23:47:00.350: 1526: debug : virNetMessageFree:57 : msg=0xb5b1e008
** (virt-viewer:1526): DEBUG: Remove handle 1 6
<deadlock>
Gdb displays the deadlock nicely:
#0 0xb7710424 in __kernel_vsyscall ()
#1 0xb692cf02 in __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/lowlevellock.S:142
#2 0xb692839b in _L_lock_728 () from /lib/i386-linux-gnu/i686/cmov/libpthread.so.0
#3 0xb69281c1 in __pthread_mutex_lock (mutex=0x95e1c30) at pthread_mutex_lock.c:61
#4 0xb698fd07 in virMutexLock (m=0x95e1c30) at util/threads-pthread.c:85
#5 0xb6a5dd56 in virNetSocketEventFree (opaque=0x95e1c30) at rpc/virnetsocket.c:1147
#6 0x080514a1 in virt_viewer_events_remove_handle (watch=1) at virt-viewer-events.c:178
#7 0xb697264e in virEventRemoveHandle (watch=1) at util/event.c:84
#8 0xb6a608ed in virNetSocketRemoveIOCallback (sock=0x95e1c30) at rpc/virnetsocket.c:1221
#9 0xb6a57114 in virNetClientClose (client=0xb5b7e008) at rpc/virnetclient.c:280
#10 0xb6a46fc4 in doRemoteOpen (conn=0x95e1aa8, priv=0x95b1370, auth=0xbfc3b178, flags=1) at remote/remote_driver.c:705
#11 0xb6a49612 in remoteOpen (conn=0x95e1aa8, auth=0xbfc3b178, flags=1) at remote/remote_driver.c:820
#12 0xb69f70c6 in do_open (name=0x95aac10 "qemu+ssh://pustekiste:2222/system", auth=0xbfc3b178, flags=1) at libvirt.c:1054
#13 0xb69f9b88 in virConnectOpenAuth (name=0x95aac10 "qemu+ssh://pustekiste:2222/system", auth=0xbfc3b178, flags=1) at libvirt.c:1280
#14 0x080500cf in virt_viewer_start (app=0x95a1010) at virt-viewer.c:483
#15 0x08053be8 in virt_viewer_app_start (self=0x95a1010) at virt-viewer-app.c:1044
#16 0x0804f54d in main (argc=1, argv=0xbfc3b3f4) at virt-viewer-main.c:120
which is the sock->lock:
virNetSocketRemoveIOCallback holds sock->lock
-> virEventRemoveHandle
-> impl_remove_handle
-> opaque->ff
-> virNetSocketEventFree want to hold sock->lock
Working around this by removing the locks from
virNetSocketRemoveIOCallback leads to another deadlock:
#0 0xb77de424 in __kernel_vsyscall ()
#1 0xb69faf02 in __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/lowlevellock.S:142
#2 0xb69f639b in _L_lock_728 () from /lib/i386-linux-gnu/i686/cmov/libpthread.so.0
#3 0xb69f61c1 in __pthread_mutex_lock (mutex=0xb5c4c00c) at pthread_mutex_lock.c:61
#4 0xb6a5dd07 in virMutexLock (m=0xb5c4c00c) at util/threads-pthread.c:85
#5 0xb6b24a98 in virNetClientLock (client=0xb5c4c008) at rpc/virnetclient.c:99
#6 virNetClientFree (client=0xb5c4c008) at rpc/virnetclient.c:243
#7 0xb6b250b7 in virNetClientEventFree (opaque=0xb5c4c008) at rpc/virnetclient.c:117
#8 0xb6b2bd82 in virNetSocketEventFree (opaque=0x8aabc30) at rpc/virnetsocket.c:1156
#9 0x080514a1 in virt_viewer_events_remove_handle (watch=1) at virt-viewer-events.c:178
#10 0xb6a4064e in virEventRemoveHandle (watch=1) at util/event.c:84
#11 0xb6b2e8e5 in virNetSocketRemoveIOCallback (sock=0x8aabc30) at rpc/virnetsocket.c:1219
#12 0xb6b25114 in virNetClientClose (client=0xb5c4c008) at rpc/virnetclient.c:280
#13 0xb6b14fc4 in doRemoteOpen (conn=0x8aabaa8, priv=0x8a7b370, auth=0xbfbf1798, flags=1) at remote/remote_driver.c:705
#14 0xb6b17612 in remoteOpen (conn=0x8aabaa8, auth=0xbfbf1798, flags=1) at remote/remote_driver.c:820
#15 0xb6ac50c6 in do_open (name=0x8a74c10 "qemu+ssh://pustekiste:2222/system", auth=0xbfbf1798, flags=1) at libvirt.c:1054
#16 0xb6ac7b88 in virConnectOpenAuth (name=0x8a74c10 "qemu+ssh://pustekiste:2222/system", auth=0xbfbf1798, flags=1) at libvirt.c:1280
#17 0x080500cf in virt_viewer_start (app=0x8a6b010) at virt-viewer.c:483
#18 0x08053be8 in virt_viewer_app_start (self=0x8a6b010) at virt-viewer-app.c:1044
#19 0x0804f54d in main (argc=1, argv=0xbfbf1a14) at virt-viewer-main.c:120
which is the virNetClient lock:
virNetClientClose holds client->lock
-> virNetSocketRemoveIOCallback
-> virEventRemoveHandle
-> impl_remove_handle
-> virNetSocketEventFree
-> virNetClientEventFree wants the lock
I didn't see a simple way to fix this but would welcome any suggestions.
Cheers,
-- Guido
13 years, 1 month
[libvirt] LPC2011 Virtualization Micro Conf
by Jes Sorensen
Hi,
With the success of last year's Virtualization micro-conference track
at Linux Plumbers 2010, I have accepted to organize a similar track
for Linux Plumbers 2011 in Santa Rosa. Please see the official Linux
Plumbers 2011 website for full details about the conference:
http://www.linuxplumbersconf.org/2011/
The Linux Plumbers 2011 Virtualization track is focusing on general
free software Linux Virtualization. It is not reserved for a specific
hypervisor, but will focus on general virtualization issues and in
particular collaboration amongst projects. This would include KVM,
Xen, QEMU, containers etc.
Deadline:
---------
The deadline for submissions is April 30th. Please visit the following
link to submit your proposal:
http://www.linuxplumbersconf.org/2011/ocw/events/LPC2011MC/proposals
Example topics:
---------------
- Kernel and Hypervisor KVM/QEMU/Xen interaction
- QEMU integration, sharing of code between the different projects
- IO Performance and scalability
- Live Migration
- Managing and supporting enterprise storage
- Support for new hardware features, and/or provide guest access to
these features.
- Guest agents
- Virtualization management tools, libvirt, etc.
- Desktop integration
- Consumer Electronics device emulation
- Custom platform configuration and coordination with the kernel
Audience:
---------
Virtualization hypervisor developers, developers of virtualization
management tools and applications, embedded virtualization developers,
vendors and others.
Best regards,
Jes
13 years, 1 month
[libvirt] specifying rbd images in libvirt
by Sage Weil
Hi all,
Currently, you can specify an rbd (or nbd, sheepdog) image with xml
that looks like so:
<disk type='network' device='disk'>
<driver name='qemu' type='raw' cache='writeback'/>
<source protocol='rbd' name='mypool/myimage'>
<host name='monhost1.mydomain.com' port='6789'/>
<host name='monhost2.mydomain.com' port='6789'/>
<host name='monhost3.mydomain.com' port='6789'/>
</source>
<target dev='vda' bus='virtio'/>
</disk>
This works okay if you have authentication disabled and all of the default
settings are okay. Usually, though, there are other options you need to
specify to librbd to make it do what you want.
The current schema can be abused by adding options after the image name
like so:
<disk type='network' device='disk'>
<driver name='qemu' type='raw' cache='writeback'/>
<source protocol='rbd' name='mypool/myimage:conf=/etc/ceph/ceph.conf:id=admin:this=that:foo=bar'>
<host name='monhost1.mydomain.com' port='6789'/>
<host name='monhost2.mydomain.com' port='6789'/>
<host name='monhost3.mydomain.com' port='6789'/>
</source>
<target dev='vda' bus='virtio'/>
</disk>
This works only because that's what the qemu incantation looks like. In
general, though, this is ugly. I also doesn't generalize well to the
kernel-level rbd driver, which we'd like to also support, as that will
work with hypervisors other than qemu.
What about something more like this?
<disk type='network' device='disk'>
<driver name='qemu' type='raw' cache='writeback'/>
<source protocol='rbd' name='mypool/myimage'>
<option name='conf'>/etc/ceph/ceph.conf</option>
<option name='id'>myusername</option>
<option name='foo'>bar</option>
<host name='monhost1.mydomain.com' port='6789'/>
<host name='monhost2.mydomain.com' port='6789'/>
<host name='monhost3.mydomain.com' port='6789'/>
</source>
<target dev='vda' bus='virtio'/>
</disk>
I'm not married to any particular syntax/schema, as long as there is a
generic way to specify name/value pairs to configure the driver. I think
the above would generalize well to other network block devices as well,
which presumably also want a way to feed in information other than a
server address (e.g. for authentication).
Does that look reasonable? If there are no objections we can work up some
patches and send them along!
Thanks-
sage
13 years, 2 months
[libvirt] [PATCH 0/8] Add virDomainMigrateGetMaxSpeed API
by Jim Fehlig
As per previous request [1], this patch series introduces
virDomainMigrateGetMaxSpeed API.
Patches 1-5 contain usual new API stuff. In patch 6,
qemuDomainMigrateSetMaxSpeed is made to work with inactive domains and set
the new value in domain conf. Patch 7 changes migration speed to unlimited
when target is a file *and* no user defined migration speed is set, and
reverts to previous value after migration completes. Patch 8 considers
user-defined migration speed in qemuMigrationRun() when explicit bandwidth
is not provided in 'resource' paramter.
Note that a patch to fix the remote protocol generator [2] is required
for this patch series.
[1] https://www.redhat.com/archives/libvir-list/2011-August/msg00224.html
[2] https://www.redhat.com/archives/libvir-list/2011-August/msg01367.html
Jim Fehlig (8):
Add on_migrate element to domainXML
Add public API for getting migration speed
Add max migration bandwidth to domain_conf
Impl virDomainMigrateGetMaxSpeed in qemu driver
virsh: Expose virDomainMigrateGetMaxSpeed API
Save migration speed to domain conf in qemuDomainMigrateSetMaxSpeed
Set qemu migration speed unlimited when migrating to file
Use max speed specified in domain conf when migrating
docs/apibuild.py | 3 +-
docs/formatdomain.html.in | 21 +++++++
docs/schemas/domain.rng | 13 +++++
include/libvirt/libvirt.h.in | 4 ++
python/generator.py | 1 +
python/libvirt-override-api.xml | 6 ++
python/libvirt-override.c | 24 ++++++++
src/conf/domain_conf.c | 11 ++++
src/conf/domain_conf.h | 2 +
src/driver.h | 6 ++
src/libvirt.c | 51 ++++++++++++++++++
src/libvirt_public.syms | 5 ++
src/qemu/qemu_driver.c | 76 ++++++++++++++++++++-------
src/qemu/qemu_migration.c | 28 +++++++++-
src/qemu/qemu_migration.h | 3 +
src/remote/remote_driver.c | 1 +
src/remote/remote_protocol.x | 13 ++++-
src/remote_protocol-structs | 9 +++
src/rpc/gendispatch.pl | 1 +
tests/domainschemadata/migration-params.xml | 34 ++++++++++++
tools/virsh.c | 41 ++++++++++++++
tools/virsh.pod | 4 ++
22 files changed, 333 insertions(+), 24 deletions(-)
create mode 100644 tests/domainschemadata/migration-params.xml
--
1.7.5.4
13 years, 2 months
[libvirt] libvirt(-java): virDomainMigrateSetMaxDowntime
by Thomas Treutner
Hi,
I'm facing some troubles with virDomainMigrate &
virDomainMigrateSetMaxDowntime. The core problem is that KVM's default
value for the maximum allowed downtime is 30ms (max_downtime in
migration.c, it's nanoseconds there; 0.12.3) which is too low for my VMs
when they're busy (~50% CPU util and above). Migrations then take
literally forever, I had to abort them after 15 minutes or so. I'm using
GBit Ethernet, so plenty bandwidth should be available. Increasing the
allowed downtime to 50ms seems to help, but I have not tested situations
where the VM is completely utilized. Anyways, the default value is too
low for me, so I tried virDomainMigrateSetMaxDowntime resp. the Java
wrapper function.
Here I'm facing a problem I can overcome only with a quite crude hack:
org.libvirt.Domain.migrate(..) blocks until the migration is done, which
is of course reasonable. So I tried calling migrateSetMaxDowntime(..)
before migrating, causing an error:
"Requested operation is not valid: domain is not being migrated"
This tells me that calling migrateSetMaxDowntime is only allowed during
migrations. As I'm migrating VMs automatically and without any user
intervention I'd need to create some glue code that runs in an extra
thread, waiting "some time" hoping that the migration was kicked off in
the main thread yet and then calling migrateSetMaxDowntime. I'd like to
avoid such quirks in the long run, if possible.
So my question: Would it be possible to extend the migrate() method
resp. virDomainMigrate() function with an optional maxDowntime parameter
that is passed down as QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME so that
qemuDomainWaitForMigrationComplete would set the value? Or are there
easier ways?
Thanks and regards,
-t
13 years, 2 months
[libvirt] [PATCH v3] virsh: Increase device-detach intelligence
by Michal Privoznik
From: Michal Prívozník <mprivozn(a)redhat.com>
Up to now users have to give a full XML description on input when
device-detaching. If they omitted something it lead to unclear
error messages (like generated MAC wasn't found, etc.).
With this patch users can specify only those information which
specify one device sufficiently precise. Remaining information is
completed from domain.
---
diff to v2:
-rebase to current HEAD
diff to v1:
-rebase to current HEAD
-add a little bit comments
tools/virsh.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 250 insertions(+), 16 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 1ad84a2..aae8e4e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -10351,6 +10351,226 @@ cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
return true;
}
+/**
+ * Check if n1 is superset of n2, meaning n1 contains all elements and
+ * attributes as n2 at lest. Including children.
+ * @n1 first node
+ * @n2 second node
+ * return 1 in case n1 covers n2, 0 otherwise.
+ */
+static int
+vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2) {
+ xmlNodePtr child1, child2;
+ xmlAttrPtr attr1, attr2;
+ int found;
+
+ if (!n1 && !n2)
+ return 1;
+
+ if (!n1 || !n2)
+ return 0;
+
+ if (!xmlStrEqual(n1->name, n2->name))
+ return 0;
+
+ /* Iterate over n2 attributes and check if n1 contains them*/
+ attr2 = n2->properties;
+ while (attr2) {
+ if (attr2->type == XML_ATTRIBUTE_NODE) {
+ attr1 = n1->properties;
+ found = 0;
+ while (attr1) {
+ if (xmlStrEqual(attr1->name, attr2->name)) {
+ found = 1;
+ break;
+ }
+ attr1 = attr1->next;
+ }
+ if (!found)
+ return 0;
+ if (!xmlStrEqual(BAD_CAST virXMLPropString(n1, (const char *) attr1->name),
+ BAD_CAST virXMLPropString(n2, (const char *) attr2->name)))
+ return 0;
+ }
+ attr2 = attr2->next;
+ }
+
+ /* and now iterate over n2 children */
+ child2 = n2->children;
+ while (child2) {
+ if (child2->type == XML_ELEMENT_NODE) {
+ child1 = n1->children;
+ found = 0;
+ while (child1) {
+ if (child1->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(child1->name, child2->name)) {
+ found = 1;
+ break;
+ }
+ child1 = child1->next;
+ }
+ if (!found)
+ return 0;
+ if (!vshNodeIsSuperset(child1, child2))
+ return 0;
+ }
+ child2 = child2->next;
+ }
+
+ return 1;
+}
+
+/**
+ * To given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible when given device XML is sufficiently precise so it addresses only
+ * one device.
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ * (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ * is touched only in case of success.
+ */
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
+ char **newXML) {
+ int funcRet = -1;
+ char *domXML = NULL;
+ xmlDocPtr domDoc = NULL, devDoc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+ xmlNodePtr *devices = NULL;
+ xmlSaveCtxtPtr sctxt = NULL;
+ int devices_size;
+ char *xpath;
+ xmlBufferPtr buf = NULL;
+
+ if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("couldn't get XML description of domain %s"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ if (!(domDoc = xmlReadDoc(BAD_CAST domXML, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+ vshError(ctl, "%s", _("could not parse domain XML"));
+ goto cleanup;
+ }
+
+ if (!(devDoc = xmlReadDoc(BAD_CAST oldXML, "device.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+ vshError(ctl, "%s", _("could not parse device XML"));
+ goto cleanup;
+ }
+
+ node = xmlDocGetRootElement(domDoc);
+ if (!node) {
+ vshError(ctl, "%s", _("failed to get domain root element"));
+ goto cleanup;
+ }
+
+ domCtxt = xmlXPathNewContext(domDoc);
+ if (!domCtxt) {
+ vshError(ctl, "%s", _("failed to create context on domain XML"));
+ goto cleanup;
+ }
+ domCtxt->node = node;
+
+ node = xmlDocGetRootElement(devDoc);
+ if (!node) {
+ vshError(ctl, "%s", _("failed to get device root element"));
+ goto cleanup;
+ }
+
+ devCtxt = xmlXPathNewContext(devDoc);
+ if (!devCtxt) {
+ vshError(ctl, "%s", _("failed to create context on device XML"));
+ goto cleanup;
+ }
+ devCtxt->node = node;
+
+ buf = xmlBufferCreate();
+ if (!buf) {
+ vshError(ctl, "%s", _("out of memory"));
+ goto cleanup;
+ }
+
+ xmlBufferCat(buf, BAD_CAST "/domain/devices/");
+ xmlBufferCat(buf, node->name);
+ xpath = (char *) xmlBufferContent(buf);
+ /* Get all possible devices */
+ devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+ xmlBufferEmpty(buf);
+
+ if (devices_size < 0) {
+ /* error */
+ vshError(ctl, "%s", _("error when selecting nodes"));
+ goto cleanup;
+ } else if (devices_size == 0) {
+ /* no such device */
+ funcRet = -2;
+ goto cleanup;
+ }
+
+ /* and refine */
+ int i = 0;
+ while (i < devices_size) {
+ if (!vshNodeIsSuperset(devices[i], node)) {
+ if (devices_size == 1) {
+ VIR_FREE(devices);
+ devices_size = 0;
+ } else {
+ memmove(devices + i, devices + i + 1,
+ sizeof(*devices) * (devices_size-i-1));
+ devices_size--;
+ if (VIR_REALLOC_N(devices, devices_size) < 0) {
+ /* ignore, harmless */
+ }
+ }
+ } else {
+ i++;
+ }
+ }
+
+ if (!devices_size) {
+ /* no such device */
+ funcRet = -2;
+ goto cleanup;
+ } else if (devices_size > 1) {
+ /* ambiguous */
+ funcRet = -3;
+ goto cleanup;
+ }
+
+ if (newXML) {
+ sctxt = xmlSaveToBuffer(buf, NULL, 0);
+ if (!sctxt) {
+ vshError(ctl, "%s", _("failed to create document saving context"));
+ goto cleanup;
+ }
+
+ xmlSaveTree(sctxt, devices[0]);
+ xmlSaveClose(sctxt);
+ *newXML = (char *) xmlBufferContent(buf);
+ buf->content = NULL;
+ }
+
+ funcRet = 0;
+
+cleanup:
+ xmlBufferFree(buf);
+ VIR_FREE(devices);
+ xmlXPathFreeContext(devCtxt);
+ xmlXPathFreeContext(domCtxt);
+ xmlFreeDoc(devDoc);
+ xmlFreeDoc(domDoc);
+ VIR_FREE(domXML);
+ return funcRet;
+}
/*
* "detach-device" command
@@ -10371,10 +10591,11 @@ static const vshCmdOptDef opts_detach_device[] = {
static bool
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virDomainPtr dom = NULL;
const char *from = NULL;
- char *buffer;
+ char *buffer = NULL, *new_buffer = NULL;
int ret;
+ bool funcRet = false;
unsigned int flags;
if (!vshConnectionUsability(ctl, ctl->conn))
@@ -10383,37 +10604,50 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virDomainFree(dom);
- return false;
- }
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ goto cleanup;
if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
virshReportError(ctl);
- virDomainFree(dom);
- return false;
+ goto cleanup;
+ }
+
+ ret = vshCompleteXMLFromDomain(ctl, dom, buffer, &new_buffer);
+ if (ret < 0) {
+ if (ret == -2) {
+ vshError(ctl, _("no such device in %s"), virDomainGetName(dom));
+ } else if (ret == -3) {
+ vshError(ctl, "%s", _("given XML selects too many devices. "
+ "Please, be more specific"));
+ } else {
+ /* vshCompleteXMLFromDomain() already printed error message,
+ * so nothing to do here. */
+ }
+ goto cleanup;
}
if (vshCommandOptBool(cmd, "persistent")) {
flags = VIR_DOMAIN_AFFECT_CONFIG;
if (virDomainIsActive(dom) == 1)
flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+ ret = virDomainDetachDeviceFlags(dom, new_buffer, flags);
} else {
- ret = virDomainDetachDevice(dom, buffer);
+ ret = virDomainDetachDevice(dom, new_buffer);
}
- VIR_FREE(buffer);
if (ret < 0) {
vshError(ctl, _("Failed to detach device from %s"), from);
- virDomainFree(dom);
- return false;
- } else {
- vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ goto cleanup;
}
+ vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ funcRet = true;
+
+cleanup:
+ VIR_FREE(new_buffer);
+ VIR_FREE(buffer);
virDomainFree(dom);
- return true;
+ return funcRet;
}
--
1.7.3.4
13 years, 2 months