The hardware UUID (hwuuid) element provides a mechanism to supply an external
UUID to the guest, as opposed to the libvirt domain UUID. This is to allow
for the scenario whereby a domain can be stopped, cloned and then started as
a new domain without altering the guest-visible UUID.

Add the element, documentation and core code for the hwuuid feature along
with an implementation for the QEMU driver.

Signed-off-by: Mark Cave-Ayland <mark.caveayl...@nutanix.com>
---
 docs/formatdomain.rst             |  7 ++++++
 src/conf/domain_conf.c            | 38 ++++++++++++++++++++++++++++---
 src/conf/domain_conf.h            |  1 +
 src/conf/schemas/domaincommon.rng |  5 ++++
 src/qemu/qemu_command.c           |  6 ++++-
 5 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9a2f065590..7b10dfa3da 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -54,6 +54,13 @@ General metadata
 
    :since:`Since 0.8.7`, it is also possible to provide the UUID via a
    `SMBIOS System Information`_ specification.
+``hwuuid``
+   The optional ``hwuuid`` element can be used to supply an alternative UUID 
for
+   identifying the virtual machine from the domain ``uuid`` above. The 
difference
+   between using the ``hwuuid`` element and simply providing an alternative 
UUID
+   via a `SMBIOS System Information`_ specification is that the ``hwuuid`` 
affects
+   all devices that expose the UUID to the guest.
+   :since:`Since 11.6.0 QEMU/KVM only`
 ``genid``
    :since:`Since 4.4.0`, the ``genid`` element can be used to add a Virtual
    Machine Generation ID which exposes a 128-bit, cryptographically random,
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index bfc62b6270..63603ca527 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -13098,6 +13098,7 @@ static int
 virSysinfoSystemParseXML(xmlNodePtr node,
                          xmlXPathContextPtr ctxt,
                          virSysinfoSystemDef **sysdef,
+                         unsigned char *hwUUID,
                          unsigned char *domUUID,
                          bool uuid_generated)
 {
@@ -13122,11 +13123,18 @@ virSysinfoSystemParseXML(xmlNodePtr node,
         }
         if (uuid_generated) {
             memcpy(domUUID, uuidbuf, VIR_UUID_BUFLEN);
-        } else if (memcmp(domUUID, uuidbuf, VIR_UUID_BUFLEN) != 0) {
+        } else if (!virUUIDIsValid(hwUUID) &&
+                   memcmp(domUUID, uuidbuf, VIR_UUID_BUFLEN) != 0) {
             virReportError(VIR_ERR_XML_DETAIL, "%s",
                            _("UUID mismatch between <uuid> and <sysinfo>"));
             return -1;
         }
+        if (virUUIDIsValid(hwUUID) &&
+            memcmp(hwUUID, uuidbuf, VIR_UUID_BUFLEN) != 0) {
+                virReportError(VIR_ERR_XML_DETAIL, "%s",
+                    _("UUID mismatch between <hwuuid> and <sysinfo>"));
+                return -1;
+        }
         /* Although we've validated the UUID as good, virUUIDParse() is
          * lax with respect to allowing extraneous "-" and " ", but the
          * underlying hypervisor may be less forgiving. Use virUUIDFormat()
@@ -13263,6 +13271,7 @@ virSysinfoChassisParseXML(xmlNodePtr node,
 static int
 virSysinfoParseSMBIOSDef(virSysinfoDef *def,
                          xmlXPathContextPtr ctxt,
+                         unsigned char *hwUUID,
                          unsigned char *domUUID,
                          bool uuid_generated)
 {
@@ -13276,7 +13285,7 @@ virSysinfoParseSMBIOSDef(virSysinfoDef *def,
 
     /* Extract system related metadata */
     if ((tmpnode = virXPathNode("./system[1]", ctxt)) != NULL) {
-        if (virSysinfoSystemParseXML(tmpnode, ctxt, &def->system,
+        if (virSysinfoSystemParseXML(tmpnode, ctxt, &def->system, hwUUID,
                                      domUUID, uuid_generated) < 0)
             return -1;
     }
@@ -13363,6 +13372,7 @@ virSysinfoParseFWCfgDef(virSysinfoDef *def,
 static virSysinfoDef *
 virSysinfoParseXML(xmlNodePtr node,
                    xmlXPathContextPtr ctxt,
+                   unsigned char *hwUUID,
                    unsigned char *domUUID,
                    bool uuid_generated)
 {
@@ -13377,7 +13387,8 @@ virSysinfoParseXML(xmlNodePtr node,
 
     switch (def->type) {
     case VIR_SYSINFO_SMBIOS:
-        if (virSysinfoParseSMBIOSDef(def, ctxt, domUUID, uuid_generated) < 0)
+        if (virSysinfoParseSMBIOSDef(def, ctxt, hwUUID, domUUID,
+                                     uuid_generated) < 0)
             return NULL;
         break;
 
@@ -18667,6 +18678,21 @@ virDomainDefParseIDs(virDomainDef *def,
         VIR_FREE(tmp);
     }
 
+    /* Extract hardware uuid (optional). For some use cases e.g. cloning a
+     * domain from a snapshot, the hardware uuid must remain constant and
+     * separate from the domain uuid. */
+    tmp = virXPathString("string(./hwuuid[1])", ctxt);
+    if (tmp) {
+        if (virUUIDParse(tmp, def->hw_uuid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("malformed hwuuid element"));
+            return -1;
+        }
+        VIR_FREE(tmp);
+    } else {
+        memset(def->hw_uuid, 0, VIR_UUID_BUFLEN);
+    }
+
     /* Extract domain genid - a genid can either be provided or generated */
     if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0)
         return -1;
@@ -20132,6 +20158,7 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt,
 
     for (i = 0; i < n; i++) {
         virSysinfoDef *sysinfo = virSysinfoParseXML(nodes[i], ctxt,
+                                                    def->hw_uuid,
                                                     def->uuid, uuid_generated);
 
         if (!sysinfo)
@@ -28903,6 +28930,11 @@ virDomainDefFormatInternalSetRootName(virDomainDef 
*def,
     virUUIDFormat(uuid, uuidstr);
     virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
 
+    if (virUUIDIsValid(def->hw_uuid)) {
+        virUUIDFormat(def->hw_uuid, uuidstr);
+        virBufferAsprintf(buf, "<hwuuid>%s</hwuuid>\n", uuidstr);
+    }
+
     if (def->genidRequested) {
         char genidstr[VIR_UUID_STRING_BUFLEN];
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6997cf7c09..a00ba729fd 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3115,6 +3115,7 @@ struct _virDomainDef {
     int virtType; /* enum virDomainVirtType */
     int id;
     unsigned char uuid[VIR_UUID_BUFLEN];
+    unsigned char hw_uuid[VIR_UUID_BUFLEN];
 
     unsigned char genid[VIR_UUID_BUFLEN];
     bool genidRequested;
diff --git a/src/conf/schemas/domaincommon.rng 
b/src/conf/schemas/domaincommon.rng
index 2d6e15f144..e39dacfff7 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -47,6 +47,11 @@
           <ref name="UUID"/>
         </element>
       </optional>
+      <optional>
+        <element name="hwuuid">
+          <ref name="UUID"/>
+        </element>
+      </optional>
       <optional>
         <element name="genid">
           <choice>
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 7658cc4d39..c7fd2eb183 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -10713,7 +10713,11 @@ qemuBuildCommandLine(virDomainObj *vm,
         qemuBuildNumaCommandLine(cfg, def, cmd, priv) < 0)
         return NULL;
 
-    virUUIDFormat(def->uuid, uuid);
+    if (virUUIDIsValid(def->hw_uuid)) {
+        virUUIDFormat(def->hw_uuid, uuid);
+    } else {
+        virUUIDFormat(def->uuid, uuid);
+    }
     virCommandAddArgList(cmd, "-uuid", uuid, NULL);
 
     if (qemuBuildSmbiosCommandLine(cmd, driver, def) < 0)
-- 
2.43.0

Reply via email to