[libvirt] [PATCH] trivial libvirt example code
by Dave Allan
The examples directory doesn't have a trivial example of how to connect
to a hypervisor, make a few calls, and disconnect, so I put one
together. I would appreciate any suggestions on anything that I've done
wrong as well as suggestions for other fundamental API calls that should
be illustrated.
Dave
commit 14531c0fa3680890086d61ae4d66aec525f9c9a0
Author: David Allan <dallan(a)redhat.com>
Date: Fri Jan 23 14:27:39 2009 -0500
Added a trivial example program to illustrate connecting to the hypervisor and making some simple API calls.
diff --git a/examples/hellolibvirt/hellolibvirt.c b/examples/hellolibvirt/hellolibvirt.c
new file mode 100644
index 0000000..aae79b8
--- /dev/null
+++ b/examples/hellolibvirt/hellolibvirt.c
@@ -0,0 +1,137 @@
+/* This file contains trivial example code to connect to the running
+ * hypervisor and gather a few bits of information. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libvirt/libvirt.h>
+
+static int
+showHypervisorInfo(virConnectPtr conn)
+{
+ int ret = 0;
+ unsigned long hvVer, major, minor, release;
+ const char *hvType;
+
+ /* virConnectGetType returns a pointer to a static string, so no
+ * allocation or freeing is necessary; it is possible for the call
+ * to fail if, for example, there is no connection to a
+ * hypervisor, so check what it returns. */
+ hvType = virConnectGetType(conn);
+ if (NULL == hvType) {
+ ret = 1;
+ printf("Failed to get hypervisor type\n");
+ goto out;
+ }
+
+ if (0 != virConnectGetVersion(conn, &hvVer)) {
+ ret = 1;
+ printf("Failed to get hypervisor version\n");
+ goto out;
+ }
+
+ major = hvVer / 1000000;
+ hvVer %= 1000000;
+ minor = hvVer / 1000;
+ release = hvVer % 1000;
+
+ printf("Hypervisor: \"%s\" version: %lu.%lu.%lu\n",
+ hvType,
+ major,
+ minor,
+ release);
+
+out:
+ return ret;
+}
+
+
+static int
+showDomains(virConnectPtr conn)
+{
+ int ret = 0, i, numNames, numInactiveDomains, numActiveDomains;
+ char **nameList = NULL;
+
+ numActiveDomains = virConnectNumOfDomains(conn);
+ numInactiveDomains = virConnectNumOfDefinedDomains(conn);
+
+ printf("There are %d active and %d inactive domains\n",
+ numActiveDomains, numInactiveDomains);
+
+ nameList = malloc(sizeof(char *) * (unsigned int)numInactiveDomains);
+
+ if (NULL == nameList) {
+ ret = 1;
+ printf("Could not allocate memory for list of inactive domains\n");
+ goto out;
+ }
+
+ numNames = virConnectListDefinedDomains(conn,
+ nameList,
+ numInactiveDomains);
+
+ if (-1 == numNames) {
+ ret = 1;
+ printf("Could not get list of defined domains from hypervisor\n");
+ goto out;
+ }
+
+ if (numNames > 0) {
+ printf("Inactive domains:\n");
+ }
+
+ for (i = 0 ; i < numNames ; i++) {
+ printf(" %s\n", *(nameList + i));
+ /* The API documentation doesn't say so, but the names
+ * returned by virConnectListDefinedDomains are strdup'd and
+ * must be freed here. */
+ free(*(nameList + i));
+ }
+
+out:
+ if (NULL != nameList) {
+ free(nameList);
+ }
+
+ return ret;
+}
+
+
+int
+main(void)
+{
+ int ret = 0;
+ virConnectPtr conn = NULL;
+
+ printf("Attempting to connect to hypervisor\n");
+
+ /* virConnectOpenAuth called here with all default parameters */
+ conn = virConnectOpenAuth(NULL, virConnectAuthPtrDefault, 0);
+
+ if (NULL == conn) {
+ ret = 1;
+ printf("No connection to hypervisor\n");
+ goto out;
+ }
+
+ printf("Connected to hypervisor\n");
+
+ if (0 != showHypervisorInfo(conn)) {
+ ret = 1;
+ goto disconnect;
+ }
+
+ if (0 != showDomains(conn)) {
+ ret = 1;
+ goto disconnect;
+ }
+
+disconnect:
+ if (0 != virConnectClose(conn)) {
+ printf("Failed to disconnect from hypervisor\n");
+ } else {
+ printf("Disconnected from hypervisor\n");
+ }
+
+out:
+ return ret;
+}
15 years, 9 months
[libvirt] [PATCH]: don't hardcode default port 22 for ssh protocol
by Guido Günther
Hi,
attached patch removes the hardcoded port 22 when going through ssh, ssh
uses it by default. This makes it easier to override this via
.ssh/config on a per host basis instead of having to add this to the
connection URI explicitly.
While at that I cleaned up some free vs. VIR_FREE usage and replaced
pointer intializations with 0 by NULL.
Cheers,
-- Guido
P.S.: this originated from Debian Bug #513605
15 years, 9 months
[libvirt] failed test on Fedora 8
by John Levon
17) QEMU XML-2-ARGV disk-drive-shared ... FAILED
I have VIR_TEST_DEBUG set, but this is all I get. It's new
regards
john
15 years, 9 months
[libvirt] [PATCH] eliminate strerror from qemu_driver.c: use virReportSystemError instead
by Jim Meyering
I've begun eliminating the remaining problematic uses of strerror.
For example, those in virsh.c aren't a problem.
This is required for thread safety.
After this patch, there are about 60 uses left.
Note while reviewing:
- are there places where I've added uses of "conn" that I should not have?
- are there places where I've used NULL but it should be "conn"?
(there may well be -- I haven't tried hard, but that matters less)
- I've assumed that qemudEscapeMonitorArg failure will always be due to OOM
Currently it is.
I removed the calls to qemudLog from each of the qemudSet* functions
because now each caller of those functions already diagnoses failure, and
includes strerror(errno) information, while the qemudLog calls did not.
In addition, the eliminated qemudLog calls would clobber errno, making
each callers' attempt to report strerror(errno) use a potentially invalid
and misleading errno value.
>From ebe2ee1fbf20edbb07a78ecce76de6866e2ef558 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Wed, 28 Jan 2009 19:20:08 +0100
Subject: [PATCH] eliminate strerror from qemu_driver.c: use virReportSystemError instead
* src/qemu_driver.c (qemudSetCloseExec): Don't use qemudLog here.
Now, every caller diagnoses the failure.
Simplify, now that there's no logging.
* src/qemu_driver.c (qemudSetNonBlock): Rewrite not to use qemudLog.
---
src/qemu_driver.c | 126 ++++++++++++++++++++++++-----------------------------
1 files changed, 57 insertions(+), 69 deletions(-)
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 36e12b2..8fd789d 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -89,31 +89,19 @@ static void qemuDriverUnlock(struct qemud_driver *driver)
static int qemudSetCloseExec(int fd) {
int flags;
- if ((flags = fcntl(fd, F_GETFD)) < 0)
- goto error;
- flags |= FD_CLOEXEC;
- if ((fcntl(fd, F_SETFD, flags)) < 0)
- goto error;
- return 0;
- error:
- qemudLog(QEMUD_ERR,
- "%s", _("Failed to set close-on-exec file descriptor flag\n"));
- return -1;
+ return ((flags = fcntl(fd, F_GETFD)) < 0
+ || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0
+ ? -1
+ : 0);
}
static int qemudSetNonBlock(int fd) {
int flags;
- if ((flags = fcntl(fd, F_GETFL)) < 0)
- goto error;
- flags |= O_NONBLOCK;
- if ((fcntl(fd, F_SETFL, flags)) < 0)
- goto error;
- return 0;
- error:
- qemudLog(QEMUD_ERR,
- "%s", _("Failed to set non-blocking file descriptor flag\n"));
- return -1;
+ return ((flags = fcntl(fd, F_GETFL)) < 0
+ || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0
+ ? -1
+ : 0);
}
@@ -198,22 +186,21 @@ qemudLogReadFD(virConnectPtr conn, const char* logDir, const char* name, off_t p
if ((fd = open(logfile, logmode)) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("failed to create logfile %s: %s"),
- logfile, strerror(errno));
+ virReportSystemError(conn, errno,
+ _("failed to create logfile %s"),
+ logfile);
return -1;
}
if (qemudSetCloseExec(fd) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Unable to set VM logfile close-on-exec flag: %s"),
- strerror(errno));
+ virReportSystemError(conn, errno, "%s",
+ _("Unable to set VM logfile close-on-exec flag"));
close(fd);
return -1;
}
if (lseek(fd, pos, SEEK_SET) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Unable to seek to %lld in %s: %s"),
- (long long) pos, logfile, strerror(errno));
+ virReportSystemError(conn, errno,
+ _("Unable to seek to %lld in %s"),
+ (long long) pos, logfile);
close(fd);
}
return fd;
@@ -441,8 +428,9 @@ qemudStartup(void) {
}
if (virFileMakePath(qemu_driver->stateDir) < 0) {
- qemudLog(QEMUD_ERR, _("Failed to create state dir '%s': %s\n"),
- qemu_driver->stateDir, strerror(errno));
+ virReportSystemError(NULL, errno,
+ _("Failed to create state dir '%s'"),
+ qemu_driver->stateDir);
goto error;
}
@@ -854,8 +842,7 @@ static int qemudWaitForMonitor(virConnectPtr conn,
qemudFindCharDevicePTYs,
"console", 3000);
if (close(logfd) < 0)
- qemudLog(QEMUD_WARN, _("Unable to close logfile: %s\n"),
- strerror(errno));
+ virReportSystemError(NULL, errno, "%s", _("Unable to close logfile"));
return ret;
}
@@ -1134,30 +1121,30 @@ static int qemudStartVMDaemon(virConnectPtr conn,
tmp = progenv;
while (*tmp) {
if (safewrite(vm->logfile, *tmp, strlen(*tmp)) < 0)
- qemudLog(QEMUD_WARN, _("Unable to write envv to logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to write envv to logfile"));
if (safewrite(vm->logfile, " ", 1) < 0)
- qemudLog(QEMUD_WARN, _("Unable to write envv to logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to write envv to logfile"));
tmp++;
}
tmp = argv;
while (*tmp) {
if (safewrite(vm->logfile, *tmp, strlen(*tmp)) < 0)
- qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to write argv to logfile"));
if (safewrite(vm->logfile, " ", 1) < 0)
- qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to write argv to logfile"));
tmp++;
}
if (safewrite(vm->logfile, "\n", 1) < 0)
- qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to write argv to logfile"));
if ((pos = lseek(vm->logfile, 0, SEEK_END)) < 0)
- qemudLog(QEMUD_WARN, _("Unable to seek to end of logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to seek to end of logfile"));
for (i = 0 ; i < ntapfds ; i++)
FD_SET(tapfds[i], &keepfd);
@@ -1214,7 +1201,8 @@ static int qemudStartVMDaemon(virConnectPtr conn,
static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
- struct qemud_driver *driver, virDomainObjPtr vm) {
+ struct qemud_driver *driver,
+ virDomainObjPtr vm) {
if (!virDomainIsActive(vm))
return;
@@ -1222,14 +1210,14 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
if (virKillProcess(vm->pid, 0) == 0 &&
virKillProcess(vm->pid, SIGTERM) < 0)
- qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"),
- vm->def->name, vm->pid, strerror(errno));
+ virReportSystemError(conn, errno,
+ _("Failed to send SIGTERM to %s (%d)"),
+ vm->def->name, vm->pid);
virEventRemoveHandle(vm->monitor_watch);
if (close(vm->logfile) < 0)
- qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"),
- errno, strerror(errno));
+ virReportSystemError(conn, errno, "%s", _("Unable to close logfile"));
if (vm->monitor != -1)
close(vm->monitor);
vm->logfile = -1;
@@ -1384,8 +1372,8 @@ qemudMonitorCommand (const virDomainObjPtr vm,
/* Log, but ignore failures to write logfile for VM */
if (safewrite(vm->logfile, buf, strlen(buf)) < 0)
- qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"),
- strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to log VM console data"));
*reply = buf;
return 0;
@@ -1394,8 +1382,8 @@ qemudMonitorCommand (const virDomainObjPtr vm,
if (buf) {
/* Log, but ignore failures to write logfile for VM */
if (safewrite(vm->logfile, buf, strlen(buf)) < 0)
- qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"),
- strerror(errno));
+ virReportSystemError(NULL, errno,
+ "%s", _("Unable to log VM console data"));
VIR_FREE(buf);
}
return -1;
@@ -1492,7 +1480,7 @@ static int kvmGetMaxVCPUs(void) {
fd = open(KVM_DEVICE, O_RDONLY);
if (fd < 0) {
- qemudLog(QEMUD_WARN, _("Unable to open %s: %s\n"), KVM_DEVICE, strerror(errno));
+ virReportSystemError(NULL, errno, _("Unable to open %s"), KVM_DEVICE);
return maxvcpus;
}
@@ -1735,8 +1723,8 @@ qemudGetHostname (virConnectPtr conn)
result = virGetHostname();
if (result == NULL) {
- qemudReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (conn, errno,
+ "%s", _("failed to determine host name"));
return NULL;
}
/* Caller frees this string. */
@@ -3821,8 +3809,8 @@ qemudDomainBlockPeek (virDomainPtr dom,
/* The path is correct, now try to open it and get its size. */
fd = open (path, O_RDONLY);
if (fd == -1) {
- qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (dom->conn, errno,
+ _("%s: failed to open"), path);
goto cleanup;
}
@@ -3832,8 +3820,8 @@ qemudDomainBlockPeek (virDomainPtr dom,
*/
if (lseek (fd, offset, SEEK_SET) == (off_t) -1 ||
saferead (fd, buffer, size) == (ssize_t) -1) {
- qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (dom->conn, errno,
+ _("%s: failed to seek or read"), path);
goto cleanup;
}
@@ -3887,8 +3875,8 @@ qemudDomainMemoryPeek (virDomainPtr dom,
/* Create a temporary filename. */
if ((fd = mkstemp (tmp)) == -1) {
- qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (dom->conn, errno,
+ _("mkstemp(\"%s\") failed"), tmp);
goto cleanup;
}
@@ -3904,8 +3892,9 @@ qemudDomainMemoryPeek (virDomainPtr dom,
/* Read the memory file into buffer. */
if (saferead (fd, buffer, size) == (ssize_t) -1) {
- qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (dom->conn, errno,
+ _("failed to read temporary file "
+ "created with template %s"), tmp);
goto cleanup;
}
@@ -4064,8 +4053,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
/* Get hostname */
if (gethostname (hostname, HOST_NAME_MAX+1) == -1) {
- qemudReportError (dconn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportSystemError (dconn, errno,
+ "%s", _("failed to determine host name"));
goto cleanup;
}
@@ -4235,8 +4224,7 @@ qemudDomainMigratePerform (virDomainPtr dom,
/* Issue the migrate command. */
safe_uri = qemudEscapeMonitorArg (uri);
if (!safe_uri) {
- qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
- "%s", strerror (errno));
+ virReportOOMError (dom->conn);
goto cleanup;
}
snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri);
--
1.6.1.2.418.gd79e6
15 years, 9 months
[libvirt] [PATCH] Fix floppy definition for HVM guests
by john.levon@sun.com
# HG changeset patch
# User john.levon(a)sun.com
# Date 1233177786 28800
# Node ID 2b9283e83c20ca2a18f2cafcb963ce254e11dbb4
# Parent 9a9bd34ec485ebaf4c3861bdfffd2953fd91a1fc
Fix floppy definition for HVM guests
Code was missing to define floppy disks for Xen HVM guests. Refuse to
attach disks that aren't supported by direct attach.
Signed-off-by: John Levon <john.levon(a)sun.com>
diff --git a/src/xend_internal.c b/src/xend_internal.c
--- a/src/xend_internal.c
+++ b/src/xend_internal.c
@@ -5018,14 +5018,26 @@ xenDaemonFormatSxprDisk(virConnectPtr co
* under the hvm (image (os)) block
*/
if (hvm &&
- def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
+ def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
+ if (isAttach) {
+ virXendError(conn, VIR_ERR_INVALID_ARG,
+ _("Cannot directly attach floppy %s"), def->src);
+ return -1;
+ }
return 0;
+ }
/* Xend <= 3.0.2 doesn't include cdrom config here */
if (hvm &&
def->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
- xendConfigVersion == 1)
+ xendConfigVersion == 1) {
+ if (isAttach) {
+ virXendError(conn, VIR_ERR_INVALID_ARG,
+ _("Cannot directly attach CDROM %s"), def->src);
+ return -1;
+ }
return 0;
+ }
if (!isAttach)
virBufferAddLit(buf, "(device ");
@@ -5374,17 +5386,29 @@ xenDaemonFormatSxpr(virConnectPtr conn,
}
virBufferVSprintf(&buf, "(boot %s)", bootorder);
- /* get the cdrom device file */
- /* Only XenD <= 3.0.2 wants cdrom config here */
- if (xendConfigVersion == 1) {
- for (i = 0 ; i < def->ndisks ; i++) {
- if (def->disks[i]->type == VIR_DOMAIN_DISK_DEVICE_CDROM &&
- STREQ(def->disks[i]->dst, "hdc") &&
- def->disks[i]->src) {
- virBufferVSprintf(&buf, "(cdrom '%s')",
- def->disks[i]->src);
+ /* some disk devices are defined here */
+ for (i = 0 ; i < def->ndisks ; i++) {
+ switch (def->disks[i]->device) {
+ case VIR_DOMAIN_DISK_DEVICE_CDROM:
+ /* Only xend <= 3.0.2 wants cdrom config here */
+ if (xendConfigVersion != 1)
break;
- }
+ if (!STREQ(def->disks[i]->dst, "hdc") ||
+ def->disks[i]->src == NULL)
+ break;
+
+ virBufferVSprintf(&buf, "(cdrom '%s')",
+ def->disks[i]->src);
+ break;
+
+ case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
+ /* all xend versions define floppies here */
+ virBufferVSprintf(&buf, "(%s '%s')", def->disks[i]->dst,
+ def->disks[i]->src);
+ break;
+
+ default:
+ break;
}
}
15 years, 9 months
[libvirt] PATCH: Fix infinite loop when QEMU quits at startup
by Daniel P. Berrange
The recent refactoring of the QEMU startup process now reads the monitor
TTY from the logfile. Unfortunately in this refactoring we lost the check
for the 'ret == 0' scenario in the read() return value. So if QEMU quits
at startup, eg due to missing disk image, we loop forever on read() == 0
because we hit end-of-file and QEMU has quit.
This patch adds back handling for this scenario, and takes care to
propagate the contents of the log to the user as an error message
# start demo
libvir: QEMU error : internal error unable to start guest: qemu: could
not open disk image /home/berrange/Fedora-9-i686-Live.iso
error: Failed to start domain demo
In addition, there were a couple of other bugs
- a memory leak where we set the 'monitorpath' variable, even
though we'd just set it moments before.
- a missing check for whether the driver VNC password was present
when initializing passwords at VM startupo
- missing initialization of the monitor_watch field, and missing
checking for whether the watch was set before removing it.
- a gratuitous LOG_INFO when shutting down any VM, which could
just be LOG_DEBUG.
Daniel
diff -r 826e6ed70ee0 src/domain_conf.c
--- a/src/domain_conf.c Fri Jan 30 10:58:34 2009 +0000
+++ b/src/domain_conf.c Fri Jan 30 11:00:43 2009 +0000
@@ -497,6 +497,7 @@ virDomainObjPtr virDomainAssignDef(virCo
virDomainObjLock(domain);
domain->state = VIR_DOMAIN_SHUTOFF;
domain->def = def;
+ domain->monitor_watch = -1;
if (VIR_REALLOC_N(doms->objs, doms->count + 1) < 0) {
virReportOOMError(conn);
diff -r 826e6ed70ee0 src/qemu_driver.c
--- a/src/qemu_driver.c Fri Jan 30 10:58:34 2009 +0000
+++ b/src/qemu_driver.c Fri Jan 30 11:00:43 2009 +0000
@@ -355,10 +355,9 @@ qemudReconnectVMs(struct qemud_driver *d
qemudLog(QEMUD_ERR, _("Failed to reconnect monitor for %s: %d\n"),
vm->def->name, rc);
goto next_error;
- } else
- vm->monitorpath = status->monitorpath;
-
- if((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name)) < 0)
+ }
+
+ if ((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name)) < 0)
return -1;
if (vm->def->id >= driver->nextvmid)
@@ -376,6 +375,8 @@ next_error:
vm->newDef = NULL;
next:
virDomainObjUnlock(vm);
+ if (status)
+ VIR_FREE(status->monitorpath);
VIR_FREE(status);
VIR_FREE(config);
}
@@ -617,6 +618,9 @@ typedef int qemudHandlerMonitorOutput(vi
const char *output,
int fd);
+/*
+ * Returns -1 for error, 0 on end-of-file, 1 for success
+ */
static int
qemudReadMonitorOutput(virConnectPtr conn,
virDomainObjPtr vm,
@@ -630,7 +634,7 @@ qemudReadMonitorOutput(virConnectPtr con
int got = 0;
buf[0] = '\0';
- /* Consume & discard the initial greeting */
+ /* Consume & discard the initial greeting */
while (got < (buflen-1)) {
int ret;
@@ -670,11 +674,17 @@ qemudReadMonitorOutput(virConnectPtr con
_("Failure while reading %s startup output"), what);
return -1;
}
+ } else if (ret == 0) {
+ return 0;
} else {
got += ret;
buf[got] = '\0';
- if ((ret = func(conn, vm, buf, fd)) != 1)
- return ret;
+ ret = func(conn, vm, buf, fd);
+ if (ret == -1)
+ return -1;
+ if (ret == 1)
+ continue;
+ return 1;
}
}
@@ -724,11 +734,14 @@ static int qemudOpenMonitor(virConnectPt
}
if (!reconnect) {
- ret = qemudReadMonitorOutput(conn,
- vm, monfd,
- buf, sizeof(buf),
- qemudCheckMonitorPrompt,
- "monitor", 10000);
+ if (qemudReadMonitorOutput(conn,
+ vm, monfd,
+ buf, sizeof(buf),
+ qemudCheckMonitorPrompt,
+ "monitor", 10000) <= 0)
+ ret = -1;
+ else
+ ret = 0;
} else {
vm->monitor = monfd;
ret = 0;
@@ -858,7 +871,7 @@ static int qemudWaitForMonitor(virConnec
if ((logfd = qemudLogReadFD(conn, driver->logDir, vm->def->name, pos))
< 0)
- return logfd;
+ return -1;
ret = qemudReadMonitorOutput(conn, vm, logfd, buf, sizeof(buf),
qemudFindCharDevicePTYs,
@@ -866,7 +879,17 @@ static int qemudWaitForMonitor(virConnec
if (close(logfd) < 0)
qemudLog(QEMUD_WARN, _("Unable to close logfile: %s\n"),
strerror(errno));
- return ret;
+
+ if (ret == 1) /* Success */
+ return 0;
+
+ if (ret == -1)
+ return -1;
+
+ /* Unexpected end of file - inform user of QEMU log data */
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unable to start guest: %s"), buf);
+ return -1;
}
static int
@@ -1033,7 +1056,7 @@ qemudInitPasswords(virConnectPtr conn,
if (vm->def->graphics &&
vm->def->graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- vm->def->graphics->data.vnc.passwd) {
+ (vm->def->graphics->data.vnc.passwd || driver->vncPassword)) {
if (qemudMonitorCommandExtra(vm, "change vnc password",
vm->def->graphics->data.vnc.passwd ?
@@ -1212,14 +1235,25 @@ static int qemudStartVMDaemon(virConnect
/* wait for qemu process to to show up */
if (ret == 0) {
int retries = 100;
- while (retries) {
- if ((ret = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) == 0)
- break;
- usleep(100*1000);
- retries--;
- }
- if (ret)
- qemudLog(QEMUD_WARN, _("Domain %s didn't show up\n"), vm->def->name);
+ int childstat;
+
+ while (waitpid(child, &childstat, 0) == -1 &&
+ errno == EINTR);
+
+ if (childstat == 0) {
+ while (retries) {
+ if ((ret = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) == 0)
+ break;
+ usleep(100*1000);
+ retries--;
+ }
+ if (ret)
+ qemudLog(QEMUD_WARN, _("Domain %s didn't show up\n"), vm->def->name);
+ } else {
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to daemonize QEMU process"));
+ ret = -1;
+ }
}
if (ret == 0) {
@@ -1262,14 +1296,17 @@ static void qemudShutdownVMDaemon(virCon
if (!virDomainIsActive(vm))
return;
- qemudLog(QEMUD_INFO, _("Shutting down VM '%s'\n"), vm->def->name);
+ qemudLog(QEMUD_DEBUG, _("Shutting down VM '%s'\n"), vm->def->name);
if (virKillProcess(vm->pid, 0) == 0 &&
virKillProcess(vm->pid, SIGTERM) < 0)
qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"),
vm->def->name, vm->pid, strerror(errno));
- virEventRemoveHandle(vm->monitor_watch);
+ if (vm->monitor_watch != -1) {
+ virEventRemoveHandle(vm->monitor_watch);
+ vm->monitor_watch = -1;
+ }
if (close(vm->logfile) < 0)
qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"),
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
15 years, 9 months
[libvirt] Release of libvirt-0.6.0
by Daniel Veillard
I had planned to release it a couple of week ago but a lot of patches
and fixes were needed, but finally it's here. The changelog is huge,
there is a lot of improvements in this release, that also probably
implies there is a few new bugs hidden left and right too, at this point
the best is to push a release to increase testing especially for the
new features. Available as usual at:
ftp://libvirt.org/libvirt/
I guess that's the longuest changelog ever for a libvirt release,
and I cheated by aggregating a bunch of cleanups from Jim and Dan in
a single generic entry :-)
* New features:
- thread safety of the API and event handling (Daniel Berrange)
- allow QEmu domains to survive daemon restart (Guido Gunther)
- extended logging capabilities
- support copy-on-write storage volumes (Daniel Berrange)
- support of storage cache control options for QEmu/KVM (Daniel
Berrange)
* Portability:
- fix old DBus API problem
- Debian portability fix (Daniel Berrange)
- fix distcheck (Jim Meyering)
- build in debug mode (Jim Meyering)
- libnuma API portability (Jim Meyering)
- many portability fixes pointed by Solaris (John Levon)
- non-gcc portability fixes (John Levon)
- various include fixes (Jim Meyering)
- various Windows and Mingw portability fixes (Daniel Berrange)
- solaris Xen fixes (John Levon)
- RPC portability to Solaris (Daniel Berrange)
* Documentation:
- typo fixes (Richard Jones)
- logging support
- vnc keymap attributes (Guido Gunther)
- HACKING file updates (Jim Meyering)
- new PCI passthrough format
- libvirt-qpid and UML driver documentation (Daniel Berrange)
- provide RNG schemas for all XML formats used in libvirt APIs (Daniel
Berrange)
* Bug fixes:
- segfault on virtual network without bridge name (Cole Robinson)
- various locking fixes (Cole Robinson)
- fix serial and parallel devices on tcp/unix/telnet (Guido Gunther)
- leak in daemon (Jim Meyering)
- storage driver segfault (Miloslav TrmaC)
- missing check in read-only connections (Daniel Berrange)
- OpenVZ crash and mutex fixes (Anton Protopopov)
- couple of daemon bug fixes (John Levon)
- OpenVZ MAC addresses generation (Evgeniy Sokolov)
- poll call initialization fix (Daniel Berrange)
- various Xen driver fixes (John Levon)
- segfault on device back compat (Cole Robinson)
- couple Xen bug fixes coming from RHEL (Markus Armbruster)
- buffer overflow in libvirt proxy (rasputin(a)email.ru)
- vnc port report (John Levon)
- repair save and restore on recent KVM versions (Daniel Berrange)
- Xen cpu pinning XML fix (John Levon)
- various xen driver fixes (Daniel Berrange)
- some memory leak fixes (Daniel Berrange)
* Improvements:
- driver infrastructure and locking (Daniel Berrange)
- Test driver infrastructure (Daniel Berrange)
- parallelism in the daemon and associated config (Daniel Berrange)
- virsh help cleanups (Jim Meyering)
- logrotate daemon logs (Guido Gunther)
- more regression tests (Jim Meyering)
- QEmu SDL graphics (Itamar Heim)
- add --version flag to daemon (Dave Allan)
- memory consumption cleanup (Dave Allan)
- QEmu pid file and XML states for daemon restart (Guido Gunther)
- gnulib updates (Jim Meyering and Dan Berrange)
- PCI passthrough for KVM (Jason Krieg)
- generic internal thread API (Daniel Berrange)
- RHEL-5 specific Xen configure option and code (Markus Armbruster)
- save domain state as string in status file (Guido Gunther)
- add locking to all API entry points (Daniel Berrange)
- new ref counting APIs (Daniel Berrange)
- IP address for Xen bridges (John Levon)
- driver format for disk file types (Daniel Berrange)
- improve QEmu/KVM tun/tap performances (Mark McLoughlin)
- enable floppies for Xen fully virt (John Levon)
- support VNC password settings for QEmu/KVM (Daniel Berrange)
- qemu driver version reporting (Daniel Berrange)
* Cleanups:
- converting linked lists to arrays (Daniel Berrange)
- daemon RPC handling refactoring (Daniel Berrange)
- strings cleanups (Jim Meyering)
- gethostby* cleanup and test (Jim Meyering)
- some code fixes (Dave Allan)
- various code cleanup (Jim Meyering)
- virsh argument handling cleanup (Jim Meyering)
- virAsprintf cleanup replacement (Guido Gunther)
- QEmu monitor reads (Cole Robinson)
- Makefile cleanups (Guido Gunther)
- Xen code cleanups (John Levon)
- revamp of ELF export scripts (John Levon)
- domain event callback args (John Levon)
- enforce use of pid_t (John Levon)
- virsh pool-*-as XML code merge (Cole Robinson)
- xgettext warnings (Jim Meyering)
- add virKillProcess (Guido Gunther)
- add virGetHostname (David Lutterkort)
- add flags argument to the full XML parsing stack (Guido Gunther)
- various daemon code cleanups (Guido Gunther)
- handling of daemon missing config file (Jim Meyering)
- rpcgen invocation cleanup (Richard Jones)
- devhelp builkd makefile cleanups (John Levon)
- update error handling for threading (Daniel Berrange)
- remove all non-rentrant POSIX calls usage (Daniel Berrange)
- many small cleanups (Jim Meyering and Daniel Berrange)
- examples Makefile generator (John Levon)
- mis-use of PF_UNIX as a protocol (John Levon)
- cleanup OOM error paths (Jim Meyering)
- temporary fix fro valgrind on lxc (Daniel Berrange)
- QEmu driver init cleanups (Daniel Berrange)
Thanks a lot to everybody who contributed to this release, with a
special welcome to the people who gained commit right access !
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
15 years, 9 months
[libvirt] libvirtd hang while saving domain ??
by Daniel Schwager
Hi,
if i save a kvm domain, libvirt will not answer to any command (e.g.
virsh list).
While saving, the dd process works ... after finishing,
it possible to execute virsh commands. Why does libvirtd blocks ?
Is there a way to process the requests in concurrency ?
25766 ? S 0:00 sh -c dd
of='/tmp/storage11.POOL401-w2-VM323.suspendRAM' oflag=append
conv=notrunc 2>/dev/null
25767 ? D 0:03 dd of /tmp/storage11.suspendRAM oflag append
conv notrunc
regards
Danny
15 years, 9 months
[libvirt] PATCH: Support storage copy on write volumes
by Daniel P. Berrange
This is a follow up on Miloslav's proposal to add copy on write
support to the storage APIs, changing the XML to that described
here:
http://www.redhat.com/archives/libvir-list/2009-January/msg00231.html
In addition to the original QCOW/VMDK support, I have done an impl which
can extract the backing store for LVM volumes
docs/formatstorage.html | 45 +++++
docs/formatstorage.html.in | 52 ++++++
src/libvirt_private.syms | 1
src/storage_backend.c | 140 ++++++++++-------
src/storage_backend.h | 13 +
src/storage_backend_fs.c | 336 +++++++++++++++++++++++++++++++++++++-----
src/storage_backend_iscsi.c | 6
src/storage_backend_logical.c | 61 +++++--
src/storage_conf.c | 101 +++++++++---
src/storage_conf.h | 1
10 files changed, 609 insertions(+), 147 deletions(-)
Daniel
diff --git a/docs/formatstorage.html b/docs/formatstorage.html
--- a/docs/formatstorage.html
+++ b/docs/formatstorage.html
@@ -131,6 +131,8 @@
<a href="#StorageVolFirst">General metadata</a>
</li><li>
<a href="#StorageVolTarget">Target elements</a>
+ </li><li>
+ <a href="#StorageVolBacking">Backing store elements</a>
</li></ul>
</li><li>
<a href="#examples">Example configuration</a>
@@ -328,14 +330,14 @@
...
<target>
<path>/var/lib/virt/images/sparse.img</path>
+ <format>qcow2</format>
<permissions>
<owner>0744</owner>
<group>0744</group>
<mode>0744</mode>
<label>virt_image_t</label>
</permissions>
- </target>
- </volume></pre>
+ </target></pre>
<dl><dt><code>path</code></dt><dd>Provides the location at which the volume can be accessed on
the local filesystem, as an absolute path. This is a readonly
attribute, so shouldn't be specified when creating a volume.
@@ -356,6 +358,45 @@
contains the MAC (eg SELinux) label string.
<span class="since">Since 0.4.1</span>
</dd></dl>
+ <h3>
+ <a name="StorageVolBacking" id="StorageVolBacking">Backing store elements</a>
+ </h3>
+ <p>
+ A single <code>backingStore</code> element is contained within the top level
+ <code>volume</code> element. This tag is used to describe the optional copy
+ on write, backing store for the storage volume. It can contain the following
+ child elements:
+ </p>
+ <pre>
+ ...
+ <backingStore>
+ <path>/var/lib/virt/images/master.img</path>
+ <format>raw</format>
+ <permissions>
+ <owner>0744</owner>
+ <group>0744</group>
+ <mode>0744</mode>
+ <label>virt_image_t</label>
+ </permissions>
+ </backingStore>
+ </volume></pre>
+ <dl><dt><code>path</code></dt><dd>Provides the location at which the backing store can be accessed on
+ the local filesystem, as an absolute path. If omitted, there is no
+ backing store for this volume.
+ <span class="since">Since 0.6.0</span></dd><dt><code>format</code></dt><dd>Provides information about the pool specific backing store format.
+ For disk pools it will provide the partition type. For filesystem
+ or directory pools it will provide the file format type, eg cow,
+ qcow, vmdk, raw. Consult the pool-specific docs for the list of valid
+ values. Most file formats require a backing store of the same format,
+ however, the qcow2 format allows a different backing store format.
+ <span class="since">Since 0.6.0</span></dd><dt><code>permissions</code></dt><dd>Provides information about the permissions of the backing file.
+ It contains 4 child elements. The
+ <code>mode</code> element contains the octal permission set. The
+ <code>owner</code> element contains the numeric user ID. The <code>group</code>
+ element contains the numeric group ID. The <code>label</code> element
+ contains the MAC (eg SELinux) label string.
+ <span class="since">Since 0.6.0</span>
+ </dd></dl>
<h2>
<a name="examples" id="examples">Example configuration</a>
</h2>
diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in
--- a/docs/formatstorage.html.in
+++ b/docs/formatstorage.html.in
@@ -234,14 +234,14 @@
...
<target>
<path>/var/lib/virt/images/sparse.img</path>
+ <format>qcow2</format>
<permissions>
<owner>0744</owner>
<group>0744</group>
<mode>0744</mode>
<label>virt_image_t</label>
</permissions>
- </target>
- </volume></pre>
+ </target></pre>
<dl>
<dt><code>path</code></dt>
@@ -271,6 +271,54 @@
</dd>
</dl>
+ <h3><a name="StorageVolBacking">Backing store elements</a></h3>
+
+ <p>
+ A single <code>backingStore</code> element is contained within the top level
+ <code>volume</code> element. This tag is used to describe the optional copy
+ on write, backing store for the storage volume. It can contain the following
+ child elements:
+ </p>
+
+ <pre>
+ ...
+ <backingStore>
+ <path>/var/lib/virt/images/master.img</path>
+ <format>raw</format>
+ <permissions>
+ <owner>0744</owner>
+ <group>0744</group>
+ <mode>0744</mode>
+ <label>virt_image_t</label>
+ </permissions>
+ </backingStore>
+ </volume></pre>
+
+ <dl>
+ <dt><code>path</code></dt>
+ <dd>Provides the location at which the backing store can be accessed on
+ the local filesystem, as an absolute path. If omitted, there is no
+ backing store for this volume.
+ <span class="since">Since 0.6.0</span></dd>
+ <dt><code>format</code></dt>
+ <dd>Provides information about the pool specific backing store format.
+ For disk pools it will provide the partition type. For filesystem
+ or directory pools it will provide the file format type, eg cow,
+ qcow, vmdk, raw. Consult the pool-specific docs for the list of valid
+ values. Most file formats require a backing store of the same format,
+ however, the qcow2 format allows a different backing store format.
+ <span class="since">Since 0.6.0</span></dd>
+ <dt><code>permissions</code></dt>
+ <dd>Provides information about the permissions of the backing file.
+ It contains 4 child elements. The
+ <code>mode</code> element contains the octal permission set. The
+ <code>owner</code> element contains the numeric user ID. The <code>group</code>
+ element contains the numeric group ID. The <code>label</code> element
+ contains the MAC (eg SELinux) label string.
+ <span class="since">Since 0.6.0</span>
+ </dd>
+ </dl>
+
<h2><a name="examples">Example configuration</a></h2>
<p>
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -258,6 +258,7 @@ virStoragePoolFormatDiskTypeToString;
virStoragePoolFormatFileSystemTypeToString;
virStoragePoolFormatFileSystemNetTypeToString;
virStorageVolFormatFileSystemTypeToString;
+virStorageVolFormatFileSystemTypeFromString;
virStoragePoolTypeFromString;
virStoragePoolObjLock;
virStoragePoolObjUnlock;
diff --git a/src/storage_backend.c b/src/storage_backend.c
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -99,27 +99,51 @@ virStorageBackendForType(int type) {
int
+virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
+ virStorageVolTargetPtr target,
+ unsigned long long *allocation,
+ unsigned long long *capacity)
+{
+ int ret, fd;
+
+ if ((fd = open(target->path, O_RDONLY)) < 0) {
+ virReportSystemError(conn, errno,
+ _("cannot open volume '%s'"),
+ target->path);
+ return -1;
+ }
+
+ ret = virStorageBackendUpdateVolTargetInfoFD(conn,
+ target,
+ fd,
+ allocation,
+ capacity);
+
+ close(fd);
+
+ return ret;
+}
+
+int
virStorageBackendUpdateVolInfo(virConnectPtr conn,
virStorageVolDefPtr vol,
int withCapacity)
{
- int ret, fd;
+ int ret;
- if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
- virReportSystemError(conn, errno,
- _("cannot open volume '%s'"),
- vol->target.path);
- return -1;
- }
+ if ((ret = virStorageBackendUpdateVolTargetInfo(conn,
+ &vol->target,
+ &vol->allocation,
+ withCapacity ? &vol->capacity : NULL)) < 0)
+ return ret;
- ret = virStorageBackendUpdateVolInfoFD(conn,
- vol,
- fd,
- withCapacity);
+ if (vol->backingStore.path &&
+ (ret = virStorageBackendUpdateVolTargetInfo(conn,
+ &vol->backingStore,
+ NULL, NULL)) < 0)
+ return ret;
- close(fd);
-
- return ret;
+ return 0;
}
struct diskType {
@@ -154,10 +178,11 @@ static struct diskType const disk_types[
};
int
-virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
- virStorageVolDefPtr vol,
- int fd,
- int withCapacity)
+virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
+ virStorageVolTargetPtr target,
+ int fd,
+ unsigned long long *allocation,
+ unsigned long long *capacity)
{
struct stat sb;
#if HAVE_SELINUX
@@ -167,7 +192,7 @@ virStorageBackendUpdateVolInfoFD(virConn
if (fstat(fd, &sb) < 0) {
virReportSystemError(conn, errno,
_("cannot stat file '%s'"),
- vol->target.path);
+ target->path);
return -1;
}
@@ -176,38 +201,41 @@ virStorageBackendUpdateVolInfoFD(virConn
!S_ISBLK(sb.st_mode))
return -2;
- if (S_ISREG(sb.st_mode)) {
+ if (allocation) {
+ if (S_ISREG(sb.st_mode)) {
#ifndef __MINGW32__
- vol->allocation = (unsigned long long)sb.st_blocks *
- (unsigned long long)sb.st_blksize;
+ *allocation = (unsigned long long)sb.st_blocks *
+ (unsigned long long)sb.st_blksize;
#else
- vol->allocation = sb.st_size;
+ *allocation = sb.st_size;
#endif
- /* Regular files may be sparse, so logical size (capacity) is not same
- * as actual allocation above
- */
- if (withCapacity)
- vol->capacity = sb.st_size;
- } else {
- off_t end;
- /* XXX this is POSIX compliant, but doesn't work for for CHAR files,
- * only BLOCK. There is a Linux specific ioctl() for getting
- * size of both CHAR / BLOCK devices we should check for in
- * configure
- */
- end = lseek(fd, 0, SEEK_END);
- if (end == (off_t)-1) {
- virReportSystemError(conn, errno,
- _("cannot seek to end of file '%s'"),
- vol->target.path);
- return -1;
+ /* Regular files may be sparse, so logical size (capacity) is not same
+ * as actual allocation above
+ */
+ if (capacity)
+ *capacity = sb.st_size;
+ } else {
+ off_t end;
+ /* XXX this is POSIX compliant, but doesn't work for for CHAR files,
+ * only BLOCK. There is a Linux specific ioctl() for getting
+ * size of both CHAR / BLOCK devices we should check for in
+ * configure
+ */
+ end = lseek(fd, 0, SEEK_END);
+ if (end == (off_t)-1) {
+ virReportSystemError(conn, errno,
+ _("cannot seek to end of file '%s'"),
+ target->path);
+ return -1;
+ }
+ *allocation = end;
+ if (capacity)
+ *capacity = end;
}
- vol->allocation = end;
- if (withCapacity) vol->capacity = end;
}
/* make sure to set the target format "unknown" to begin with */
- vol->target.format = VIR_STORAGE_POOL_DISK_UNKNOWN;
+ target->format = VIR_STORAGE_POOL_DISK_UNKNOWN;
if (S_ISBLK(sb.st_mode)) {
off_t start;
@@ -219,14 +247,14 @@ virStorageBackendUpdateVolInfoFD(virConn
if (start < 0) {
virReportSystemError(conn, errno,
_("cannot seek to beginning of file '%s'"),
- vol->target.path);
+ target->path);
return -1;
}
bytes = saferead(fd, buffer, sizeof(buffer));
if (bytes < 0) {
virReportSystemError(conn, errno,
_("cannot read beginning of file '%s'"),
- vol->target.path);
+ target->path);
return -1;
}
@@ -235,38 +263,38 @@ virStorageBackendUpdateVolInfoFD(virConn
continue;
if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
disk_types[i].length) == 0) {
- vol->target.format = disk_types[i].part_table_type;
+ target->format = disk_types[i].part_table_type;
break;
}
}
}
- vol->target.perms.mode = sb.st_mode;
- vol->target.perms.uid = sb.st_uid;
- vol->target.perms.gid = sb.st_gid;
+ target->perms.mode = sb.st_mode & (0x200-1);
+ target->perms.uid = sb.st_uid;
+ target->perms.gid = sb.st_gid;
- VIR_FREE(vol->target.perms.label);
+ VIR_FREE(target->perms.label);
#if HAVE_SELINUX
if (fgetfilecon(fd, &filecon) == -1) {
if (errno != ENODATA && errno != ENOTSUP) {
virReportSystemError(conn, errno,
_("cannot get file context of '%s'"),
- vol->target.path);
+ target->path);
return -1;
} else {
- vol->target.perms.label = NULL;
+ target->perms.label = NULL;
}
} else {
- vol->target.perms.label = strdup(filecon);
- if (vol->target.perms.label == NULL) {
+ target->perms.label = strdup(filecon);
+ if (target->perms.label == NULL) {
virReportOOMError(conn);
return -1;
}
freecon(filecon);
}
#else
- vol->target.perms.label = NULL;
+ target->perms.label = NULL;
#endif
return 0;
diff --git a/src/storage_backend.h b/src/storage_backend.h
--- a/src/storage_backend.h
+++ b/src/storage_backend.h
@@ -64,10 +64,15 @@ int virStorageBackendUpdateVolInfo(virCo
virStorageVolDefPtr vol,
int withCapacity);
-int virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
- virStorageVolDefPtr vol,
- int fd,
- int withCapacity);
+int virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
+ virStorageVolTargetPtr target,
+ unsigned long long *allocation,
+ unsigned long long *capacity);
+int virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
+ virStorageVolTargetPtr target,
+ int fd,
+ unsigned long long *allocation,
+ unsigned long long *capacity);
void virStorageBackendWaitForDevices(virConnectPtr conn);
diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c
--- a/src/storage_backend_fs.c
+++ b/src/storage_backend_fs.c
@@ -49,6 +49,19 @@ enum lv_endian {
LV_BIG_ENDIAN /* 4321 */
};
+enum {
+ BACKING_STORE_OK,
+ BACKING_STORE_INVALID,
+ BACKING_STORE_ERROR,
+};
+
+static int cowGetBackingStore(virConnectPtr, char **,
+ const unsigned char *, size_t);
+static int qcowXGetBackingStore(virConnectPtr, char **,
+ const unsigned char *, size_t);
+static int vmdk4GetBackingStore(virConnectPtr, char **,
+ const unsigned char *, size_t);
+
/* Either 'magic' or 'extension' *must* be provided */
struct FileTypeInfo {
int type; /* One of the constants above */
@@ -65,85 +78,234 @@ struct FileTypeInfo {
* -1 to use st_size as capacity */
int sizeBytes; /* Number of bytes for size field */
int sizeMultiplier; /* A scaling factor if size is not in bytes */
+ /* Store a COW base image path (possibly relative),
+ * or NULL if there is no COW base image, to RES;
+ * return BACKING_STORE_* */
+ int (*getBackingStore)(virConnectPtr conn, char **res,
+ const unsigned char *buf, size_t buf_size);
};
const struct FileTypeInfo const fileTypeInfo[] = {
/* Bochs */
/* XXX Untested
{ VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL,
LV_LITTLE_ENDIAN, 64, 0x20000,
- 32+16+16+4+4+4+4+4, 8, 1 },*/
+ 32+16+16+4+4+4+4+4, 8, 1, NULL },*/
/* CLoop */
/* XXX Untested
{ VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL,
LV_LITTLE_ENDIAN, -1, 0,
- -1, 0, 0 }, */
+ -1, 0, 0, NULL }, */
/* Cow */
{ VIR_STORAGE_VOL_FILE_COW, "OOOM", NULL,
LV_BIG_ENDIAN, 4, 2,
- 4+4+1024+4, 8, 1 },
+ 4+4+1024+4, 8, 1, cowGetBackingStore },
/* DMG */
/* XXX QEMU says there's no magic for dmg, but we should check... */
{ VIR_STORAGE_VOL_FILE_DMG, NULL, ".dmg",
0, -1, 0,
- -1, 0, 0 },
+ -1, 0, 0, NULL },
/* XXX there's probably some magic for iso we can validate too... */
{ VIR_STORAGE_VOL_FILE_ISO, NULL, ".iso",
0, -1, 0,
- -1, 0, 0 },
+ -1, 0, 0, NULL },
/* Parallels */
/* XXX Untested
{ VIR_STORAGE_VOL_FILE_PARALLELS, "WithoutFreeSpace", NULL,
LV_LITTLE_ENDIAN, 16, 2,
- 16+4+4+4+4, 4, 512 },
+ 16+4+4+4+4, 4, 512, NULL },
*/
/* QCow */
{ VIR_STORAGE_VOL_FILE_QCOW, "QFI", NULL,
LV_BIG_ENDIAN, 4, 1,
- 4+4+8+4+4, 8, 1 },
+ 4+4+8+4+4, 8, 1, qcowXGetBackingStore },
/* QCow 2 */
{ VIR_STORAGE_VOL_FILE_QCOW2, "QFI", NULL,
LV_BIG_ENDIAN, 4, 2,
- 4+4+8+4+4, 8, 1 },
+ 4+4+8+4+4, 8, 1, qcowXGetBackingStore },
/* VMDK 3 */
/* XXX Untested
{ VIR_STORAGE_VOL_FILE_VMDK, "COWD", NULL,
LV_LITTLE_ENDIAN, 4, 1,
- 4+4+4, 4, 512 },
+ 4+4+4, 4, 512, NULL },
*/
/* VMDK 4 */
{ VIR_STORAGE_VOL_FILE_VMDK, "KDMV", NULL,
LV_LITTLE_ENDIAN, 4, 1,
- 4+4+4, 8, 512 },
+ 4+4+4, 8, 512, vmdk4GetBackingStore },
/* Connectix / VirtualPC */
/* XXX Untested
{ VIR_STORAGE_VOL_FILE_VPC, "conectix", NULL,
LV_BIG_ENDIAN, -1, 0,
- -1, 0, 0},
+ -1, 0, 0, NULL},
*/
};
#define VIR_FROM_THIS VIR_FROM_STORAGE
+static int
+cowGetBackingStore(virConnectPtr conn,
+ char **res,
+ const unsigned char *buf,
+ size_t buf_size)
+{
+ size_t len;
+
+ *res = NULL;
+ if (buf_size < 4+4+1024)
+ return BACKING_STORE_INVALID;
+ if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */
+ return BACKING_STORE_OK;
+
+ len = 1024;
+ if (VIR_ALLOC_N(*res, len + 1) < 0) {
+ virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path"));
+ return BACKING_STORE_ERROR;
+ }
+ memcpy(*res, buf + 4+4, len); /* cow_header_v2.backing_file */
+ (*res)[len] = '\0';
+ if (VIR_REALLOC_N(*res, strlen(*res) + 1) < 0) {
+ /* Ignore failure */
+ }
+ return BACKING_STORE_OK;
+}
+
+static int
+qcowXGetBackingStore(virConnectPtr conn,
+ char **res,
+ const unsigned char *buf,
+ size_t buf_size)
+{
+ unsigned long long offset;
+ unsigned long size;
+
+ *res = NULL;
+ if (buf_size < 4+4+8+4)
+ return BACKING_STORE_INVALID;
+ offset = (((unsigned long long)buf[4+4] << 56)
+ | ((unsigned long long)buf[4+4+1] << 48)
+ | ((unsigned long long)buf[4+4+2] << 40)
+ | ((unsigned long long)buf[4+4+3] << 32)
+ | ((unsigned long long)buf[4+4+4] << 24)
+ | ((unsigned long long)buf[4+4+5] << 16)
+ | ((unsigned long long)buf[4+4+6] << 8)
+ | buf[4+4+7]); /* QCowHeader.backing_file_offset */
+ if (offset > buf_size)
+ return BACKING_STORE_INVALID;
+ size = ((buf[4+4+8] << 24)
+ | (buf[4+4+8+1] << 16)
+ | (buf[4+4+8+2] << 8)
+ | buf[4+4+8+3]); /* QCowHeader.backing_file_size */
+ if (size == 0)
+ return BACKING_STORE_OK;
+ if (offset + size > buf_size || offset + size < offset)
+ return BACKING_STORE_INVALID;
+ if (size + 1 == 0)
+ return BACKING_STORE_INVALID;
+ if (VIR_ALLOC_N(*res, size + 1) < 0) {
+ virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path"));
+ return BACKING_STORE_ERROR;
+ }
+ memcpy(*res, buf + offset, size);
+ (*res)[size] = '\0';
+ return BACKING_STORE_OK;
+}
+
+
+static int
+vmdk4GetBackingStore(virConnectPtr conn,
+ char **res,
+ const unsigned char *buf,
+ size_t buf_size)
+{
+ static const char prefix[] = "parentFileNameHint=\"";
+
+ char desc[20*512 + 1], *start, *end;
+ size_t len;
+
+ *res = NULL;
+
+ if (buf_size <= 0x200)
+ return BACKING_STORE_INVALID;
+ len = buf_size - 0x200;
+ if (len > sizeof(desc) - 1)
+ len = sizeof(desc) - 1;
+ memcpy(desc, buf + 0x200, len);
+ desc[len] = '\0';
+ start = strstr(desc, prefix);
+ if (start == NULL)
+ return BACKING_STORE_OK;
+ start += strlen(prefix);
+ end = strchr(start, '"');
+ if (end == NULL)
+ return BACKING_STORE_INVALID;
+ if (end == start)
+ return BACKING_STORE_OK;
+ *end = '\0';
+ *res = strdup(start);
+ if (*res == NULL) {
+ virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path"));
+ return BACKING_STORE_ERROR;
+ }
+ return BACKING_STORE_OK;
+}
+
+/**
+ * Return an absolute path corresponding to PATH, which is absolute or relative
+ * to the directory containing BASE_FILE, or NULL on error
+ */
+static char *absolutePathFromBaseFile(const char *base_file, const char *path)
+{
+ size_t base_size, path_size;
+ char *res, *p;
+
+ if (*path == '/')
+ return strdup(path);
+
+ base_size = strlen(base_file) + 1;
+ path_size = strlen(path) + 1;
+ if (VIR_ALLOC_N(res, base_size - 1 + path_size) < 0)
+ return NULL;
+ memcpy(res, base_file, base_size);
+ p = strrchr(res, '/');
+ if (p != NULL)
+ p++;
+ else
+ p = res;
+ memcpy(p, path, path_size);
+ if (VIR_REALLOC_N(res, (p + path_size) - res) < 0) {
+ /* Ignore failure */
+ }
+ return res;
+}
+
/**
* Probe the header of a file to determine what type of disk image
* it is, and info about its capacity if available.
*/
-static int virStorageBackendProbeFile(virConnectPtr conn,
- virStorageVolDefPtr def) {
+static int virStorageBackendProbeTarget(virConnectPtr conn,
+ virStorageVolTargetPtr target,
+ char **backingStore,
+ unsigned long long *allocation,
+ unsigned long long *capacity) {
int fd;
- unsigned char head[4096];
+ unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
int len, i, ret;
- if ((fd = open(def->target.path, O_RDONLY)) < 0) {
+ if (backingStore)
+ *backingStore = NULL;
+
+ if ((fd = open(target->path, O_RDONLY)) < 0) {
virReportSystemError(conn, errno,
_("cannot open volume '%s'"),
- def->target.path);
+ target->path);
return -1;
}
- if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) {
+ if ((ret = virStorageBackendUpdateVolTargetInfoFD(conn, target, fd,
+ allocation,
+ capacity)) < 0) {
close(fd);
return ret; /* Take care to propagate ret, it is not always -1 */
}
@@ -151,7 +313,7 @@ static int virStorageBackendProbeFile(vi
if ((len = read(fd, head, sizeof(head))) < 0) {
virReportSystemError(conn, errno,
_("cannot read header '%s'"),
- def->target.path);
+ target->path);
close(fd);
return -1;
}
@@ -191,9 +353,9 @@ static int virStorageBackendProbeFile(vi
}
/* Optionally extract capacity from file */
- if (fileTypeInfo[i].sizeOffset != -1) {
+ if (fileTypeInfo[i].sizeOffset != -1 && capacity) {
if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
- def->capacity =
+ *capacity =
((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) |
@@ -203,7 +365,7 @@ static int virStorageBackendProbeFile(vi
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
} else {
- def->capacity =
+ *capacity =
((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) |
@@ -214,13 +376,37 @@ static int virStorageBackendProbeFile(vi
((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
}
/* Avoid unlikely, but theoretically possible overflow */
- if (def->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
+ if (*capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
continue;
- def->capacity *= fileTypeInfo[i].sizeMultiplier;
+ *capacity *= fileTypeInfo[i].sizeMultiplier;
}
/* Validation passed, we know the file format now */
- def->target.format = fileTypeInfo[i].type;
+ target->format = fileTypeInfo[i].type;
+ if (fileTypeInfo[i].getBackingStore != NULL && backingStore) {
+ char *base;
+
+ switch (fileTypeInfo[i].getBackingStore(conn, &base, head, len)) {
+ case BACKING_STORE_OK:
+ break;
+
+ case BACKING_STORE_INVALID:
+ continue;
+
+ case BACKING_STORE_ERROR:
+ return -1;
+ }
+ if (base != NULL) {
+ *backingStore
+ = absolutePathFromBaseFile(target->path, base);
+ VIR_FREE(base);
+ if (*backingStore == NULL) {
+ virStorageReportError(conn, VIR_ERR_NO_MEMORY,
+ _("backing store path"));
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -229,15 +415,15 @@ static int virStorageBackendProbeFile(vi
if (fileTypeInfo[i].extension == NULL)
continue;
- if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension))
+ if (!virFileHasSuffix(target->path, fileTypeInfo[i].extension))
continue;
- def->target.format = fileTypeInfo[i].type;
+ target->format = fileTypeInfo[i].type;
return 0;
}
/* All fails, so call it a raw file */
- def->target.format = VIR_STORAGE_VOL_FILE_RAW;
+ target->format = VIR_STORAGE_VOL_FILE_RAW;
return 0;
}
@@ -636,6 +822,7 @@ virStorageBackendFileSystemRefresh(virCo
while ((ent = readdir(dir)) != NULL) {
int ret;
+ char *backingStore;
if (VIR_ALLOC(vol) < 0)
goto no_memory;
@@ -655,7 +842,11 @@ virStorageBackendFileSystemRefresh(virCo
if ((vol->key = strdup(vol->target.path)) == NULL)
goto no_memory;
- if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) {
+ if ((ret = virStorageBackendProbeTarget(conn,
+ &vol->target,
+ &backingStore,
+ &vol->allocation,
+ &vol->capacity) < 0)) {
if (ret == -1)
goto no_memory;
else {
@@ -667,6 +858,48 @@ virStorageBackendFileSystemRefresh(virCo
}
}
+ if (backingStore != NULL) {
+ if (vol->target.format == VIR_STORAGE_VOL_FILE_QCOW2 &&
+ STRPREFIX("fmt:", backingStore)) {
+ char *fmtstr = backingStore + 4;
+ char *path = strchr(fmtstr, ':');
+ if (!path) {
+ VIR_FREE(backingStore);
+ } else {
+ *path = '\0';
+ if ((vol->backingStore.format =
+ virStorageVolFormatFileSystemTypeFromString(fmtstr)) < 0) {
+ VIR_FREE(backingStore);
+ } else {
+ memmove(backingStore, path, strlen(path) + 1);
+ vol->backingStore.path = backingStore;
+
+ if (virStorageBackendUpdateVolTargetInfo(conn,
+ &vol->backingStore,
+ NULL,
+ NULL) < 0)
+ VIR_FREE(vol->backingStore);
+ }
+ }
+ } else {
+ vol->backingStore.path = backingStore;
+
+ if ((ret = virStorageBackendProbeTarget(conn,
+ &vol->backingStore,
+ NULL, NULL, NULL)) < 0) {
+ if (ret == -1)
+ goto no_memory;
+ else {
+ /* Silently ignore non-regular files,
+ * eg '.' '..', 'lost+found' */
+ VIR_FREE(vol->backingStore);
+ }
+ }
+ }
+ }
+
+
+
if (VIR_REALLOC_N(pool->volumes.objs,
pool->volumes.count+1) < 0)
goto no_memory;
@@ -837,8 +1070,10 @@ virStorageBackendFileSystemVolCreate(vir
} else {
#if HAVE_QEMU_IMG
const char *type;
+ const char *backingType = NULL;
char size[100];
- const char *imgargv[7];
+ const char *imgargv[9];
+ size_t i;
if ((type = virStorageVolFormatFileSystemTypeToString(vol->target.format)) == NULL) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -846,17 +1081,36 @@ virStorageBackendFileSystemVolCreate(vir
vol->target.format);
return -1;
}
+ if (vol->backingStore.path) {
+ if ((backingType = virStorageVolFormatFileSystemTypeToString(vol->backingStore.format)) == NULL) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown storage vol backing store type %d"),
+ vol->backingStore.format);
+ return -1;
+ }
+ if (access(vol->backingStore.path, R_OK) != 0) {
+ virReportSystemError(conn, errno,
+ _("inaccessible backing store volume %s"),
+ vol->backingStore.path);
+ return -1;
+ }
+ }
/* Size in KB */
snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
- imgargv[0] = QEMU_IMG;
- imgargv[1] = "create";
- imgargv[2] = "-f";
- imgargv[3] = type;
- imgargv[4] = vol->target.path;
- imgargv[5] = size;
- imgargv[6] = NULL;
+ i = 0;
+ imgargv[i++] = QEMU_IMG;
+ imgargv[i++] = "create";
+ imgargv[i++] = "-f";
+ imgargv[i++] = type;
+ if (vol->backingStore.path != NULL) {
+ imgargv[i++] = "-b";
+ imgargv[i++] = vol->backingStore.path;
+ }
+ imgargv[i++] = vol->target.path;
+ imgargv[i++] = size;
+ imgargv[i++] = NULL;
if (virRun(conn, imgargv, NULL) < 0) {
unlink(vol->target.path);
@@ -884,6 +1138,12 @@ virStorageBackendFileSystemVolCreate(vir
vol->target.format);
return -1;
}
+ if (vol->target.backingStore != NULL) {
+ virStorageReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("copy-on-write image not supported with "
+ "qcow-create"));
+ return -1;
+ }
/* Size in MB - yes different units to qemu-img :-( */
snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024);
@@ -934,7 +1194,9 @@ virStorageBackendFileSystemVolCreate(vir
}
/* Refresh allocation / permissions info, but not capacity */
- if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 0) < 0) {
+ if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd,
+ &vol->allocation,
+ NULL) < 0) {
unlink(vol->target.path);
close(fd);
return -1;
diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c
--- a/src/storage_backend_iscsi.c
+++ b/src/storage_backend_iscsi.c
@@ -226,7 +226,11 @@ virStorageBackendISCSINewLun(virConnectP
VIR_FREE(devpath);
- if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 1) < 0)
+ if (virStorageBackendUpdateVolTargetInfoFD(conn,
+ &vol->target,
+ fd,
+ &vol->allocation,
+ &vol->capacity) < 0)
goto cleanup;
/* XXX use unique iSCSI id instead */
diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c
--- a/src/storage_backend_logical.c
+++ b/src/storage_backend_logical.c
@@ -116,8 +116,22 @@ virStorageBackendLogicalMakeVol(virConne
strcat(vol->target.path, vol->name);
}
+ if (groups[1] && !STREQ(groups[1], "")) {
+ if (VIR_ALLOC_N(vol->backingStore.path, strlen(pool->def->target.path) +
+ 1 + strlen(groups[1]) + 1) < 0) {
+ virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
+ return -1;
+ }
+ strcpy(vol->backingStore.path, pool->def->target.path);
+ strcat(vol->backingStore.path, "/");
+ strcat(vol->backingStore.path, groups[1]);
+
+ vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2;
+ }
+
+
if (vol->key == NULL &&
- (vol->key = strdup(groups[1])) == NULL) {
+ (vol->key = strdup(groups[2])) == NULL) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
return -1;
}
@@ -134,22 +148,22 @@ virStorageBackendLogicalMakeVol(virConne
}
if ((vol->source.extents[vol->source.nextent].path =
- strdup(groups[2])) == NULL) {
+ strdup(groups[3])) == NULL) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("extents"));
return -1;
}
- if (virStrToLong_ull(groups[3], NULL, 10, &offset) < 0) {
+ if (virStrToLong_ull(groups[4], NULL, 10, &offset) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent offset value"));
return -1;
}
- if (virStrToLong_ull(groups[4], NULL, 10, &length) < 0) {
+ if (virStrToLong_ull(groups[5], NULL, 10, &length) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent length value"));
return -1;
}
- if (virStrToLong_ull(groups[5], NULL, 10, &size) < 0) {
+ if (virStrToLong_ull(groups[6], NULL, 10, &size) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent size value"));
return -1;
@@ -168,14 +182,14 @@ virStorageBackendLogicalFindLVs(virConne
virStorageVolDefPtr vol)
{
/*
- * # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME
- * RootLV,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
- * SwapLV,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
- * Test2,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
- * Test3,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
- * Test3,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
+ * # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,origin,uuid,devices,seg_size,vg_extent_size" VGNAME
+ * RootLV,,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
+ * SwapLV,,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
+ * Test2,,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
+ * Test3,,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
+ * Test3,Test2,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
*
- * Pull out name & uuid, device, device extent start #, segment size, extent size.
+ * Pull out name, origin, & uuid, device, device extent start #, segment size, extent size.
*
* NB can be multiple rows per volume if they have many extents
*
@@ -185,15 +199,15 @@ virStorageBackendLogicalFindLVs(virConne
* not a suitable separator (rhbz 470693).
*/
const char *regexes[] = {
- "^\\s*(\\S+),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
+ "^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
};
int vars[] = {
- 6
+ 7
};
const char *prog[] = {
LVS, "--separator", ",", "--noheadings", "--units", "b",
"--unbuffered", "--nosuffix", "--options",
- "lv_name,uuid,devices,seg_size,vg_extent_size",
+ "lv_name,origin,uuid,devices,seg_size,vg_extent_size",
pool->def->source.name, NULL
};
@@ -565,10 +579,25 @@ virStorageBackendLogicalCreateVol(virCon
{
int fd = -1;
char size[100];
- const char *cmdargv[] = {
+ const char *cmdargvnew[] = {
LVCREATE, "--name", vol->name, "-L", size,
pool->def->target.path, NULL
};
+ const char *cmdargvsnap[] = {
+ LVCREATE, "--name", vol->name, "-L", size,
+ "-s", vol->backingStore.path, NULL
+ };
+ const char **cmdargv = cmdargvnew;
+
+ if (vol->backingStore.path) {
+ if (vol->backingStore.format !=
+ VIR_STORAGE_POOL_LOGICAL_LVM2) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+ _("LVM snapshots must be backed by another LVM volume"));
+ return -1;
+ }
+ cmdargv = cmdargvsnap;
+ }
snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024);
size[sizeof(size)-1] = '\0';
diff --git a/src/storage_conf.c b/src/storage_conf.c
--- a/src/storage_conf.c
+++ b/src/storage_conf.c
@@ -249,6 +249,8 @@ virStorageVolDefFree(virStorageVolDefPtr
VIR_FREE(def->target.path);
VIR_FREE(def->target.perms.label);
+ VIR_FREE(def->backingStore.path);
+ VIR_FREE(def->backingStore.perms.label);
VIR_FREE(def);
}
@@ -998,6 +1000,28 @@ virStorageVolDefParseDoc(virConnectPtr c
if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0)
goto cleanup;
+
+
+ ret->backingStore.path = virXPathString(conn, "string(/volume/backingStore/path)", ctxt);
+ if (options->formatFromString) {
+ char *format = virXPathString(conn, "string(/volume/backingStore/format/@type)", ctxt);
+ if (format == NULL)
+ ret->backingStore.format = options->defaultFormat;
+ else
+ ret->backingStore.format = (options->formatFromString)(format);
+
+ if (ret->backingStore.format < 0) {
+ virStorageReportError(conn, VIR_ERR_XML_ERROR,
+ _("unknown volume format type %s"), format);
+ VIR_FREE(format);
+ goto cleanup;
+ }
+ VIR_FREE(format);
+ }
+
+ if (virStorageVolDefParsePerms(conn, ctxt, &ret->backingStore.perms) < 0)
+ goto cleanup;
+
return ret;
cleanup:
@@ -1069,6 +1093,47 @@ virStorageVolDefParse(virConnectPtr conn
}
+static int
+virStorageVolTargetDefFormat(virConnectPtr conn,
+ virStorageVolOptionsPtr options,
+ virBufferPtr buf,
+ virStorageVolTargetPtr def,
+ const char *type) {
+ virBufferVSprintf(buf, " <%s>\n", type);
+
+ if (def->path)
+ virBufferVSprintf(buf," <path>%s</path>\n", def->path);
+
+ if (options->formatToString) {
+ const char *format = (options->formatToString)(def->format);
+ if (!format) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown volume format number %d"),
+ def->format);
+ return -1;
+ }
+ virBufferVSprintf(buf," <format type='%s'/>\n", format);
+ }
+
+ virBufferAddLit(buf," <permissions>\n");
+ virBufferVSprintf(buf," <mode>0%o</mode>\n",
+ def->perms.mode);
+ virBufferVSprintf(buf," <owner>%d</owner>\n",
+ def->perms.uid);
+ virBufferVSprintf(buf," <group>%d</group>\n",
+ def->perms.gid);
+
+
+ if (def->perms.label)
+ virBufferVSprintf(buf," <label>%s</label>\n",
+ def->perms.label);
+
+ virBufferAddLit(buf," </permissions>\n");
+
+ virBufferVSprintf(buf, " </%s>\n", type);
+
+ return 0;
+}
char *
virStorageVolDefFormat(virConnectPtr conn,
@@ -1116,37 +1181,15 @@ virStorageVolDefFormat(virConnectPtr con
virBufferVSprintf(&buf," <allocation>%llu</allocation>\n",
def->allocation);
- virBufferAddLit(&buf, " <target>\n");
+ if (virStorageVolTargetDefFormat(conn, options, &buf,
+ &def->target, "target") < 0)
+ goto cleanup;
- if (def->target.path)
- virBufferVSprintf(&buf," <path>%s</path>\n", def->target.path);
+ if (def->backingStore.path &&
+ virStorageVolTargetDefFormat(conn, options, &buf,
+ &def->backingStore, "backingStore") < 0)
+ goto cleanup;
- if (options->formatToString) {
- const char *format = (options->formatToString)(def->target.format);
- if (!format) {
- virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
- _("unknown volume format number %d"),
- def->target.format);
- goto cleanup;
- }
- virBufferVSprintf(&buf," <format type='%s'/>\n", format);
- }
-
- virBufferAddLit(&buf," <permissions>\n");
- virBufferVSprintf(&buf," <mode>0%o</mode>\n",
- def->target.perms.mode);
- virBufferVSprintf(&buf," <owner>%d</owner>\n",
- def->target.perms.uid);
- virBufferVSprintf(&buf," <group>%d</group>\n",
- def->target.perms.gid);
-
-
- if (def->target.perms.label)
- virBufferVSprintf(&buf," <label>%s</label>\n",
- def->target.perms.label);
-
- virBufferAddLit(&buf," </permissions>\n");
- virBufferAddLit(&buf, " </target>\n");
virBufferAddLit(&buf,"</volume>\n");
if (virBufferError(&buf))
diff --git a/src/storage_conf.h b/src/storage_conf.h
--- a/src/storage_conf.h
+++ b/src/storage_conf.h
@@ -89,6 +89,7 @@ struct _virStorageVolDef {
virStorageVolSource source;
virStorageVolTarget target;
+ virStorageVolTarget backingStore;
};
typedef struct _virStorageVolDefList virStorageVolDefList;
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
15 years, 9 months
[libvirt] [PATCH] Fix "empty declaration" compiler error
by john.levon@sun.com
# HG changeset patch
# User John Levon <john.levon(a)sun.com>
# Date 1233339660 28800
# Node ID 69992f43b6f634fa46c7ae2039666378ce378eea
# Parent 1b00b26880ad7feae71bb8df9ae86cfcaa428458
Fix "empty declaration" compiler error
Signed-off-by: John Levon <john.levon(a)sun.com>
diff --git a/src/domain_conf.c b/src/domain_conf.c
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -95,7 +95,7 @@ VIR_ENUM_IMPL(virDomainDiskCache, VIR_DO
"default",
"none",
"writethrough",
- "writeback");
+ "writeback")
VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
"mount",
15 years, 9 months