QUESTION: should we parse and ignore <mirror> on input, rather than
rejecting it? By rejecting it, I can't add a unit test, since the
unit test framework currently doesn't expose a way to trigger
internal parsing.
In order to track a block copy job across libvirtd restarts, we
need to save internal XML that tracks the name of the file
holding the mirror. Displaying this name in dumpxml might also
be useful to the user, even if we don't yet have a way to (re-)
start a domain with mirroring enabled up front. This is done
with a new <mirror> sub-element to <disk>, as in:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/original.img'/>
<mirror file='/var/lib/libvirt/images/copy.img'
format='qcow2'/>
...
</disk>
Internally, an additional attribute is used to track the state of
the job; this attribute does not need to be part of the RNG since
it is not exposed to the user.
* docs/schemas/domaincommon.rng (diskspec): Add diskMirror.
* docs/formatdomain.html.in (elementsDisks): Document it.
* src/conf/domain_conf.h (_virDomainDiskDef): New members.
* src/conf/domain_conf.c (virDomainDiskDefFree): Clean them.
(virDomainDiskDefParseXML): Parse them, but only internally.
(virDomainDiskDefFormat): Output them, partially internally.
---
docs/formatdomain.html.in | 11 +++++++
docs/schemas/domaincommon.rng | 19 ++++++++++--
src/conf/domain_conf.c | 65 +++++++++++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 13 ++++++++
4 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index a382d30..534c44b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1296,6 +1296,17 @@
</table>
<span class="since">Since 0.9.7</span>
</dd>
+ <dt><code>mirror</code></dt>
+ <dd>
+ This element is present if the hypervisor has started a block
+ copy operation (via the <code>virDomainBlockCopy</code> API),
+ where the mirror location in attribute <code>file</code> will
+ eventually have the same contents as the source, and with the
+ file format in attribute <code>format</code> (which might
+ differ from the format of the source). For now, this element
+ only valid in output; it is rejected on
+ input. <span class="since">Since 0.9.12</span>
+ </dd>
<dt><code>target</code></dt>
<dd>The <code>target</code> 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 0cc04af..66c91a2 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -772,6 +772,9 @@
<ref name="driver"/>
</optional>
<optional>
+ <ref name='diskMirror'/>
+ </optional>
+ <optional>
<ref name="diskAuth"/>
</optional>
<ref name="target"/>
@@ -1013,9 +1016,7 @@
</element>
</define>
<!--
- Disk may use a special driver for access. Currently this is
- only defined for Xen for tap/aio and file, but will certainly be
- extended in the future, and libvirt doesn't look for specific values.
+ Disk may use a special driver for access.
-->
<define name="driver">
<element name="driver">
@@ -3024,6 +3025,18 @@
<empty/>
</element>
</define>
+ <define name='diskMirror'>
+ <element name='mirror'>
+ <attribute name='file'>
+ <ref name='absFilePath'/>
+ </attribute>
+ <optional>
+ <attribute name='format'>
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
<define name="diskAuth">
<element name="auth">
<attribute name="username">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index cca757d..83b9655 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -640,6 +640,11 @@ VIR_ENUM_IMPL(virDomainDiskTray, VIR_DOMAIN_DISK_TRAY_LAST,
"closed",
"open");
+VIR_ENUM_IMPL(virDomainDiskMirrorStage, VIR_DOMAIN_DISK_MIRROR_STAGE_LAST,
+ "error",
+ "pulling",
+ "mirroring");
+
#define virDomainReportError(code, ...) \
virReportErrorHelper(VIR_FROM_DOMAIN, code, __FILE__, \
__FUNCTION__, __LINE__, __VA_ARGS__)
@@ -933,6 +938,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);
@@ -3318,6 +3325,9 @@ virDomainDiskDefParseXML(virCapsPtr caps,
char *ioeventfd = NULL;
char *event_idx = NULL;
char *copy_on_read = NULL;
+ char *mirror = NULL;
+ char *mirrorFormat = NULL;
+ char *mirrorStage = NULL;
char *devaddr = NULL;
virStorageEncryptionPtr encryption = NULL;
char *serial = NULL;
@@ -3453,6 +3463,23 @@ virDomainDiskDefParseXML(virCapsPtr caps,
ioeventfd = virXMLPropString(cur, "ioeventfd");
event_idx = virXMLPropString(cur, "event_idx");
copy_on_read = virXMLPropString(cur, "copy_on_read");
+ } else if ((mirror == NULL) &&
+ (xmlStrEqual(cur->name, BAD_CAST "mirror"))) {
+ if (flags & VIR_DOMAIN_XML_INTERNAL_STATUS) {
+ mirror = virXMLPropString(cur, "file");
+ if (!mirror) {
+ virDomainReportError(VIR_ERR_XML_ERROR, "%s",
+ _("mirror requires file name"));
+ goto error;
+ }
+ mirrorFormat = virXMLPropString(cur, "format");
+ mirrorStage = virXMLPropString(cur, "stage");
+ } else {
+ virDomainReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Cannot handle disk mirror on "
+ "input yet"));
+ goto error;
+ }
} else if (xmlStrEqual(cur->name, BAD_CAST "auth")) {
authUsername = virXMLPropString(cur, "username");
if (authUsername == NULL) {
@@ -3867,6 +3894,19 @@ virDomainDiskDefParseXML(virCapsPtr caps,
driverName = NULL;
def->driverType = driverType;
driverType = NULL;
+ def->mirror = mirror;
+ mirror = NULL;
+ def->mirrorFormat = mirrorFormat;
+ mirrorFormat = NULL;
+ if (mirrorStage) {
+ int stage = virDomainDiskMirrorStageTypeFromString(mirrorStage);
+ if (stage < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown mirror stage '%s'"),
mirrorStage);
+ goto cleanup;
+ }
+ def->mirrorStage = stage;
+ }
def->encryption = encryption;
encryption = NULL;
def->serial = serial;
@@ -3882,6 +3922,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;
@@ -3906,6 +3952,9 @@ cleanup:
VIR_FREE(authUsage);
VIR_FREE(driverType);
VIR_FREE(driverName);
+ VIR_FREE(mirror);
+ VIR_FREE(mirrorFormat);
+ VIR_FREE(mirrorStage);
VIR_FREE(cachetag);
VIR_FREE(error_policy);
VIR_FREE(rerror_policy);
@@ -10828,6 +10877,22 @@ virDomainDiskDefFormat(virBufferPtr buf,
}
}
+ /* For now, mirroring is currently output-only: we always output
+ * it, but refuse to parse it on input except for internal parse
+ * on libvirtd restart. Mirror stage is internal use only. */
+ if (def->mirror) {
+ virBufferEscapeString(buf, " <mirror file='%s'",
def->mirror);
+ if (def->mirrorFormat)
+ virBufferAsprintf(buf, " format='%s'",
def->mirrorFormat);
+ if (flags & VIR_DOMAIN_XML_INTERNAL_STATUS) {
+ const char *stage;
+
+ stage = virDomainDiskMirrorStageTypeToString(def->mirrorStage);
+ virBufferEscapeString(buf, " stage='%s'", stage);
+ }
+ virBufferAddLit(buf, ">\n");
+ }
+
virBufferAsprintf(buf, " <target dev='%s'
bus='%s'",
def->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 0eed60e..d4b0338 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -540,6 +540,14 @@ struct _virDomainBlockIoTuneInfo {
};
typedef virDomainBlockIoTuneInfo *virDomainBlockIoTuneInfoPtr;
+typedef enum {
+ VIR_DOMAIN_DISK_MIRROR_STAGE_ERROR,
+ VIR_DOMAIN_DISK_MIRROR_STAGE_PULLING,
+ VIR_DOMAIN_DISK_MIRROR_STAGE_MIRRORING,
+
+ VIR_DOMAIN_DISK_MIRROR_STAGE_LAST,
+} virDomainDiskMirrorStage;
+
/* Stores the virtual disk configuration */
struct _virDomainDiskDef {
int type;
@@ -563,6 +571,10 @@ struct _virDomainDiskDef {
char *driverName;
char *driverType;
+ char *mirror;
+ char *mirrorFormat;
+ int mirrorStage; /* enum virDomainDiskMirrorStage */
+
virDomainBlockIoTuneInfo blkdeviotune;
char *serial;
@@ -2125,6 +2137,7 @@ VIR_ENUM_DECL(virDomainDiskIo)
VIR_ENUM_DECL(virDomainDiskSecretType)
VIR_ENUM_DECL(virDomainDiskSnapshot)
VIR_ENUM_DECL(virDomainDiskTray)
+VIR_ENUM_DECL(virDomainDiskMirrorStage)
VIR_ENUM_DECL(virDomainIoEventFd)
VIR_ENUM_DECL(virDomainVirtioEventIdx)
VIR_ENUM_DECL(virDomainDiskCopyOnRead)
--
1.7.7.6