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