mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-24 22:55:23 +00:00
Compressed save image format for Qemu.
Implement a compressed save image format for qemu. While ideally we would have the choice between compressed/non-compressed available to the libvirt API, unfortunately there is no "flags" parameter to the virDomainSave() API. Therefore, implement this as a qemu.conf option. gzip, bzip2, and lzma are implemented, and it should be very easy to implement additional compression methods. One open question is if/how we should detect the compression binaries. One way to do it is to do compile-time setting of the paths (via configure.in), but that doesn't seem like a great thing to do. My preferred solution is not to detect at all; when we go to run the commands that need them, if they aren't available, or aren't available in one of the standard paths, then we'll fail. That's also the solution implemented in this patch. In the future, we'll have a more robust (managed) save/restore API, at which time we can expose this functionality properly in the API. V2: get rid of redundant dd command and just use >> to append data. V3: Add back the missing pieces for the enum and bumping the save version. V4: Make the compressed field in the save_header an int. Implement LZMA compression. Signed-off-by: Chris Lalancette <clalance@redhat.com>
This commit is contained in:
parent
444647d1e3
commit
2d6a581960
@ -129,3 +129,13 @@
|
||||
# "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
|
||||
# "/dev/rtc", "/dev/hpet", "/dev/net/tun",
|
||||
#]
|
||||
|
||||
# The default format for Qemu/KVM guest save images is raw; that is, the
|
||||
# memory from the domain is dumped out directly to a file. If you have
|
||||
# guests with a large amount of memory, however, this can take up quite
|
||||
# a bit of space. If you would like to compress the images while they
|
||||
# are being saved to disk, you can also set "gzip", "bzip2", or "lzma"
|
||||
# for save_image_format. Note that this means you slow down the
|
||||
# process of saving a domain in order to save disk space.
|
||||
#
|
||||
# save_image_format = "raw"
|
||||
|
@ -279,6 +279,17 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
|
||||
driver->cgroupDeviceACL[i] = NULL;
|
||||
}
|
||||
|
||||
p = virConfGetValue (conf, "save_image_format");
|
||||
CHECK_TYPE ("save_image_format", VIR_CONF_STRING);
|
||||
if (p && p->str) {
|
||||
VIR_FREE(driver->saveImageFormat);
|
||||
if (!(driver->saveImageFormat = strdup(p->str))) {
|
||||
virReportOOMError(NULL);
|
||||
virConfFree(conf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
virConfFree (conf);
|
||||
return 0;
|
||||
}
|
||||
|
@ -109,6 +109,8 @@ struct qemud_driver {
|
||||
|
||||
char *securityDriverName;
|
||||
virSecurityDriverPtr securityDriver;
|
||||
|
||||
char *saveImageFormat;
|
||||
};
|
||||
|
||||
|
||||
|
@ -3485,18 +3485,27 @@ static char *qemudEscapeShellArg(const char *in)
|
||||
}
|
||||
|
||||
#define QEMUD_SAVE_MAGIC "LibvirtQemudSave"
|
||||
#define QEMUD_SAVE_VERSION 1
|
||||
#define QEMUD_SAVE_VERSION 2
|
||||
|
||||
enum qemud_save_formats {
|
||||
QEMUD_SAVE_FORMAT_RAW,
|
||||
QEMUD_SAVE_FORMAT_GZIP,
|
||||
QEMUD_SAVE_FORMAT_BZIP2,
|
||||
QEMUD_SAVE_FORMAT_LZMA,
|
||||
};
|
||||
|
||||
struct qemud_save_header {
|
||||
char magic[sizeof(QEMUD_SAVE_MAGIC)-1];
|
||||
int version;
|
||||
int xml_len;
|
||||
int was_running;
|
||||
int unused[16];
|
||||
int compressed;
|
||||
int unused[15];
|
||||
};
|
||||
|
||||
static int qemudDomainSave(virDomainPtr dom,
|
||||
const char *path) {
|
||||
const char *path)
|
||||
{
|
||||
struct qemud_driver *driver = dom->conn->privateData;
|
||||
virDomainObjPtr vm;
|
||||
char *command = NULL;
|
||||
@ -3507,11 +3516,28 @@ static int qemudDomainSave(virDomainPtr dom,
|
||||
struct qemud_save_header header;
|
||||
int ret = -1;
|
||||
virDomainEventPtr event = NULL;
|
||||
int internalret;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic));
|
||||
header.version = QEMUD_SAVE_VERSION;
|
||||
|
||||
if (driver->saveImageFormat == NULL)
|
||||
header.compressed = QEMUD_SAVE_FORMAT_RAW;
|
||||
else if (STREQ(driver->saveImageFormat, "raw"))
|
||||
header.compressed = QEMUD_SAVE_FORMAT_RAW;
|
||||
else if (STREQ(driver->saveImageFormat, "gzip"))
|
||||
header.compressed = QEMUD_SAVE_FORMAT_GZIP;
|
||||
else if (STREQ(driver->saveImageFormat, "bzip2"))
|
||||
header.compressed = QEMUD_SAVE_FORMAT_BZIP2;
|
||||
else if (STREQ(driver->saveImageFormat, "lzma"))
|
||||
header.compressed = QEMUD_SAVE_FORMAT_LZMA;
|
||||
else {
|
||||
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||
"%s", _("Invalid save image format specified in configuration file"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemuDriverLock(driver);
|
||||
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
|
||||
|
||||
@ -3584,11 +3610,28 @@ static int qemudDomainSave(virDomainPtr dom,
|
||||
virReportOOMError(dom->conn);
|
||||
goto cleanup;
|
||||
}
|
||||
if (virAsprintf(&command, "migrate \"exec:"
|
||||
|
||||
if (header.compressed == QEMUD_SAVE_FORMAT_RAW)
|
||||
internalret = virAsprintf(&command, "migrate \"exec:"
|
||||
"dd of='%s' oflag=append conv=notrunc 2>/dev/null"
|
||||
"\"", safe_path) == -1) {
|
||||
"\"", safe_path);
|
||||
else if (header.compressed == QEMUD_SAVE_FORMAT_GZIP)
|
||||
internalret = virAsprintf(&command, "migrate \"exec:"
|
||||
"gzip -c >> '%s' 2>/dev/null\"", safe_path);
|
||||
else if (header.compressed == QEMUD_SAVE_FORMAT_BZIP2)
|
||||
internalret = virAsprintf(&command, "migrate \"exec:"
|
||||
"bzip2 -c >> '%s' 2>/dev/null\"", safe_path);
|
||||
else if (header.compressed == QEMUD_SAVE_FORMAT_LZMA)
|
||||
internalret = virAsprintf(&command, "migrate \"exec:"
|
||||
"lzma -c >> '%s' 2>/dev/null\"", safe_path);
|
||||
else {
|
||||
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("Invalid compress format %d"),
|
||||
header.compressed);
|
||||
goto cleanup;
|
||||
}
|
||||
if (internalret < 0) {
|
||||
virReportOOMError(dom->conn);
|
||||
command = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -4103,6 +4146,9 @@ static int qemudDomainRestore(virConnectPtr conn,
|
||||
char *xml = NULL;
|
||||
struct qemud_save_header header;
|
||||
virDomainEventPtr event = NULL;
|
||||
int intermediatefd = -1;
|
||||
pid_t intermediate_pid = -1;
|
||||
int childstat;
|
||||
|
||||
qemuDriverLock(driver);
|
||||
/* Verify the header and read the XML */
|
||||
@ -4192,8 +4238,41 @@ static int qemudDomainRestore(virConnectPtr conn,
|
||||
}
|
||||
def = NULL;
|
||||
|
||||
if (header.version == 2) {
|
||||
const char *intermediate_argv[3] = { NULL, "-dc", NULL };
|
||||
if (header.compressed == QEMUD_SAVE_FORMAT_GZIP)
|
||||
intermediate_argv[0] = "gzip";
|
||||
else if (header.compressed == QEMUD_SAVE_FORMAT_BZIP2)
|
||||
intermediate_argv[0] = "bzip2";
|
||||
else if (header.compressed == QEMUD_SAVE_FORMAT_LZMA)
|
||||
intermediate_argv[0] = "lzma";
|
||||
else if (header.compressed != QEMUD_SAVE_FORMAT_RAW) {
|
||||
qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||
_("Unknown compressed save format %d"),
|
||||
header.compressed);
|
||||
goto cleanup;
|
||||
}
|
||||
if (intermediate_argv[0] != NULL) {
|
||||
intermediatefd = fd;
|
||||
fd = -1;
|
||||
if (virExec(conn, intermediate_argv, NULL, NULL,
|
||||
&intermediate_pid, intermediatefd, &fd, NULL, 0) < 0) {
|
||||
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("Failed to start decompression binary %s"),
|
||||
intermediate_argv[0]);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set the migration source and start it up. */
|
||||
ret = qemudStartVMDaemon(conn, driver, vm, "stdio", fd);
|
||||
if (intermediate_pid != -1) {
|
||||
/* Wait for intermediate process to exit */
|
||||
while (waitpid(intermediate_pid, &childstat, 0) == -1 &&
|
||||
errno == EINTR);
|
||||
}
|
||||
if (intermediatefd != -1)
|
||||
close(intermediatefd);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
if (ret < 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user