diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 6f19da05a5..e1fe0c4a17 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1296,6 +1296,19 @@ Since 0.9.7 +
mirror
+
+ This element is present if the hypervisor has started a block + copy operation (via the virDomainBlockCopy API), + where the mirror location in attribute file will + eventually have the same contents as the source, and with the + file format in attribute format (which might + differ from the format of the source). If + attribute ready is present, then it is known the + disk is ready to pivot; otherwise, the disk is probably still + copying. For now, this element only valid in output; it is + ignored on input. Since 0.9.12 +
target
The target element controls the bus / device under which the disk is exposed to the guest diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index f116710f34..8419ccc4e7 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -771,6 +771,9 @@ + + + @@ -1013,9 +1016,7 @@ @@ -3018,6 +3019,23 @@ + + + + + + + + + + + + + yes + + + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ac877f9676..184ff2391a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -933,6 +933,8 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->dst); VIR_FREE(def->driverName); VIR_FREE(def->driverType); + VIR_FREE(def->mirror); + VIR_FREE(def->mirrorFormat); VIR_FREE(def->auth.username); if (def->auth.secretType == VIR_DOMAIN_DISK_SECRET_TYPE_USAGE) VIR_FREE(def->auth.secret.usage); @@ -3319,6 +3321,9 @@ virDomainDiskDefParseXML(virCapsPtr caps, char *ioeventfd = NULL; char *event_idx = NULL; char *copy_on_read = NULL; + char *mirror = NULL; + char *mirrorFormat = NULL; + bool mirroring = false; char *devaddr = NULL; virStorageEncryptionPtr encryption = NULL; char *serial = NULL; @@ -3454,6 +3459,21 @@ virDomainDiskDefParseXML(virCapsPtr caps, ioeventfd = virXMLPropString(cur, "ioeventfd"); event_idx = virXMLPropString(cur, "event_idx"); copy_on_read = virXMLPropString(cur, "copy_on_read"); + } else if (!mirror && xmlStrEqual(cur->name, BAD_CAST "mirror") && + !(flags & VIR_DOMAIN_XML_INACTIVE)) { + char *ready; + mirror = virXMLPropString(cur, "file"); + if (!mirror) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("mirror requires file name")); + goto error; + } + mirrorFormat = virXMLPropString(cur, "format"); + ready = virXMLPropString(cur, "ready"); + if (ready) { + mirroring = true; + VIR_FREE(ready); + } } else if (xmlStrEqual(cur->name, BAD_CAST "auth")) { authUsername = virXMLPropString(cur, "username"); if (authUsername == NULL) { @@ -3868,6 +3888,11 @@ virDomainDiskDefParseXML(virCapsPtr caps, driverName = NULL; def->driverType = driverType; driverType = NULL; + def->mirror = mirror; + mirror = NULL; + def->mirrorFormat = mirrorFormat; + mirrorFormat = NULL; + def->mirroring = mirroring; def->encryption = encryption; encryption = NULL; def->serial = serial; @@ -3883,6 +3908,12 @@ virDomainDiskDefParseXML(virCapsPtr caps, !(def->driverName = strdup(caps->defaultDiskDriverName))) goto no_memory; + + if (def->mirror && !def->mirrorFormat && + caps->defaultDiskDriverType && + !(def->mirrorFormat = strdup(caps->defaultDiskDriverType))) + goto no_memory; + if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDiskDefAssignAddress(caps, def) < 0) goto error; @@ -3908,6 +3939,8 @@ cleanup: VIR_FREE(authUsage); VIR_FREE(driverType); VIR_FREE(driverName); + VIR_FREE(mirror); + VIR_FREE(mirrorFormat); VIR_FREE(cachetag); VIR_FREE(error_policy); VIR_FREE(rerror_policy); @@ -10839,6 +10872,18 @@ virDomainDiskDefFormat(virBufferPtr buf, } } + /* For now, mirroring is currently output-only: we only output it + * for live domains, therefore we ignore it on input except for + * the internal parse on libvirtd restart. */ + if (def->mirror && !(flags & VIR_DOMAIN_XML_INACTIVE)) { + virBufferEscapeString(buf, " mirror); + if (def->mirrorFormat) + virBufferAsprintf(buf, " format='%s'", def->mirrorFormat); + if (def->mirroring) + virBufferAddLit(buf, " ready='yes'"); + virBufferAddLit(buf, "/>\n"); + } + virBufferAsprintf(buf, " dst, bus); if ((def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0eed60e02a..5aa8fc1530 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -563,6 +563,10 @@ struct _virDomainDiskDef { char *driverName; char *driverType; + char *mirror; + char *mirrorFormat; + bool mirroring; + virDomainBlockIoTuneInfo blkdeviotune; char *serial; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml new file mode 100644 index 0000000000..0c9572429f --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml @@ -0,0 +1,42 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-mirror.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-mirror.xml new file mode 100644 index 0000000000..00111bef58 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-mirror.xml @@ -0,0 +1,40 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu + + + +
+ + + + + +
+ + + + + + + + + + + + + + diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 42e4d9df58..9bca066e64 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -54,10 +54,16 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) return ret; } +enum { + WHEN_INACTIVE = 1, + WHEN_ACTIVE = 2, + WHEN_EITHER = 3, +}; + struct testInfo { const char *name; - int different; - bool inactive_only; + bool different; + int when; }; static int @@ -74,17 +80,15 @@ testCompareXMLToXMLHelper(const void *data) abs_srcdir, info->name) < 0) goto cleanup; - if (info->different) { - ret = testCompareXMLToXMLFiles(xml_in, xml_out, false); - } else { - ret = testCompareXMLToXMLFiles(xml_in, xml_in, false); + if (info->when & WHEN_INACTIVE) { + ret = testCompareXMLToXMLFiles(xml_in, + info->different ? xml_out : xml_in, + false); } - if (!info->inactive_only) { - if (info->different) { - ret = testCompareXMLToXMLFiles(xml_in, xml_out, true); - } else { - ret = testCompareXMLToXMLFiles(xml_in, xml_in, true); - } + if (info->when & WHEN_ACTIVE) { + ret = testCompareXMLToXMLFiles(xml_in, + info->different ? xml_out : xml_in, + true); } cleanup: @@ -102,19 +106,19 @@ mymain(void) if ((driver.caps = testQemuCapsInit()) == NULL) return EXIT_FAILURE; -# define DO_TEST_FULL(name, is_different, inactive) \ +# define DO_TEST_FULL(name, is_different, when) \ do { \ - const struct testInfo info = {name, is_different, inactive}; \ + const struct testInfo info = {name, is_different, when}; \ if (virtTestRun("QEMU XML-2-XML " name, \ 1, testCompareXMLToXMLHelper, &info) < 0) \ ret = -1; \ } while (0) # define DO_TEST(name) \ - DO_TEST_FULL(name, 0, false) + DO_TEST_FULL(name, false, WHEN_EITHER) # define DO_TEST_DIFFERENT(name) \ - DO_TEST_FULL(name, 1, false) + DO_TEST_FULL(name, true, WHEN_EITHER) /* Unset or set all envvars here that are copied in qemudBuildCommandLine * using ADD_ENV_COPY, otherwise these tests may fail due to unexpected @@ -151,6 +155,8 @@ mymain(void) DO_TEST("disk-scsi-device"); DO_TEST("disk-scsi-vscsi"); DO_TEST("disk-scsi-virtio-scsi"); + DO_TEST_FULL("disk-mirror", false, WHEN_ACTIVE); + DO_TEST_FULL("disk-mirror", true, WHEN_INACTIVE); DO_TEST("graphics-listen-network"); DO_TEST("graphics-vnc"); DO_TEST("graphics-vnc-sasl"); @@ -208,8 +214,8 @@ mymain(void) DO_TEST("usb-redir"); DO_TEST("blkdeviotune"); - DO_TEST_FULL("seclabel-dynamic-baselabel", false, true); - DO_TEST_FULL("seclabel-dynamic-override", false, true); + DO_TEST_FULL("seclabel-dynamic-baselabel", false, WHEN_INACTIVE); + DO_TEST_FULL("seclabel-dynamic-override", false, WHEN_INACTIVE); DO_TEST("seclabel-static"); DO_TEST("seclabel-none");