conf: Add support to parse/format <source> for NVRAM

This patch introduces the logic to format and parse remote NVRAM.

Update NVRAM element schema, and docs for supporting network backed
NVRAM. NVRAM backed over network would give the flexibility to start
the VM on any host without having to worry about where to get the latest
nvram image.

<nvram type='network'>
  <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
    <host name='example.com' port='6000'/>
  </source>
</nvram>

or

<nvram type='file'>
  <source file='/var/lib/libvirt/nvram/guest_VARS.fd'/>
</nvram>

In the qemu driver we will support the new definition only with qemu's
supporting -blockdev.

Signed-off-by: Prerna Saxena <prerna.saxena@nutanix.com>
Signed-off-by: Florian Schmidt <flosch@nutanix.com>
Signed-off-by: Rohit Kumar <rohit.kumar3@nutanix.com>
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Tested-by: Rohit Kumar <rohit.kumar3@nutanix.com>
This commit is contained in:
Rohit Kumar 2022-05-04 09:51:12 -07:00 committed by Peter Krempa
parent 1cc5777874
commit 468a0a6027
5 changed files with 147 additions and 24 deletions

View File

@ -135,6 +135,35 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
</os> </os>
... ...
<!-- QEMU with UEFI manual firmware, secure boot and with NVRAM type 'file'-->
...
<os>
<type>hvm</type>
<loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram type='file' template='/usr/share/OVMF/OVMF_VARS.fd'>
<source file='/var/lib/libvirt/nvram/guest_VARS.fd'/>
</nvram>
<boot dev='hd'/>
</os>
...
<!-- QEMU with UEFI manual firmware, secure boot and with network backed NVRAM'-->
...
<os>
<type>hvm</type>
<loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram type='network'>
<source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
<host name='example.com' port='6000'/>
<auth username='myname'>
<secret type='iscsi' usage='mycluster_myname'/>
</auth>
</source>
</nvram>
<boot dev='hd'/>
</os>
...
<!-- QEMU with automatic UEFI firmware and secure boot --> <!-- QEMU with automatic UEFI firmware and secure boot -->
... ...
<os firmware='efi'> <os firmware='efi'>
@ -224,6 +253,15 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
if the NVRAM file has been created by libvirt it is left behind and it is if the NVRAM file has been created by libvirt it is left behind and it is
management application's responsibility to save and remove file (if needed to management application's responsibility to save and remove file (if needed to
be persistent). :since:`Since 1.2.8` be persistent). :since:`Since 1.2.8`
:since:`Since 8.5.0`, it's possible for the element to have ``type`` attribute
(accepts values ``file``, ``block`` and ``network``) in that case the NVRAM
storage is described by a ``<source>`` sub-element with the same syntax as
``disk``'s source. See `Hard drives, floppy disks, CDROMs`_.
**Note:** ``network`` backed NVRAM the variables are not instantiated from
the ``template`` and it's user's responsibility to provide a valid NVRAM image.
``boot`` ``boot``
The ``dev`` attribute takes one of the values "fd", "hd", "cdrom" or The ``dev`` attribute takes one of the values "fd", "hd", "cdrom" or
"network" and is used to specify the next boot device to consider. The "network" and is used to specify the next boot device to consider. The

View File

@ -18013,6 +18013,51 @@ virDomainLoaderDefParseXML(xmlNodePtr node,
} }
static int
virDomainNvramDefParseXML(virDomainLoaderDef *loader,
xmlXPathContextPtr ctxt,
virDomainXMLOption *xmlopt,
unsigned int flags)
{
g_autofree char *nvramType = virXPathString("string(./os/nvram/@type)", ctxt);
g_autoptr(virStorageSource) src = virStorageSourceNew();
src->type = VIR_STORAGE_TYPE_FILE;
src->format = VIR_STORAGE_FILE_RAW;
if (!nvramType) {
char *nvramPath = NULL;
if (!(nvramPath = virXPathString("string(./os/nvram[1])", ctxt)))
return 0; /* no nvram */
src->path = nvramPath;
} else {
xmlNodePtr sourceNode;
if ((src->type = virStorageTypeFromString(nvramType)) <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown disk type '%s'"), nvramType);
return -1;
}
if (!(sourceNode = virXPathNode("./os/nvram/source[1]", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing source element for nvram"));
return -1;
}
if (virDomainStorageSourceParse(sourceNode, ctxt, src, flags, xmlopt) < 0)
return -1;
loader->newStyleNVRAM = true;
}
loader->nvram = g_steal_pointer(&src);
return 0;
}
static int static int
virDomainSchedulerParseCommonAttrs(xmlNodePtr node, virDomainSchedulerParseCommonAttrs(xmlNodePtr node,
virProcessSchedPolicy *policy, virProcessSchedPolicy *policy,
@ -18398,11 +18443,12 @@ virDomainDefParseBootFirmwareOptions(virDomainDef *def,
static int static int
virDomainDefParseBootLoaderOptions(virDomainDef *def, virDomainDefParseBootLoaderOptions(virDomainDef *def,
xmlXPathContextPtr ctxt) xmlXPathContextPtr ctxt,
virDomainXMLOption *xmlopt,
unsigned int flags)
{ {
xmlNodePtr loader_node = virXPathNode("./os/loader[1]", ctxt); xmlNodePtr loader_node = virXPathNode("./os/loader[1]", ctxt);
const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE; const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE;
g_autofree char *nvramPath = NULL;
if (!loader_node) if (!loader_node)
return 0; return 0;
@ -18414,12 +18460,8 @@ virDomainDefParseBootLoaderOptions(virDomainDef *def,
fwAutoSelect) < 0) fwAutoSelect) < 0)
return -1; return -1;
if ((nvramPath = virXPathString("string(./os/nvram[1])", ctxt))) { if (virDomainNvramDefParseXML(def->os.loader, ctxt, xmlopt, flags) < 0)
def->os.loader->nvram = virStorageSourceNew(); return -1;
def->os.loader->nvram->path = g_steal_pointer(&nvramPath);
def->os.loader->nvram->type = VIR_STORAGE_TYPE_FILE;
def->os.loader->nvram->format = VIR_STORAGE_FILE_RAW;
}
if (!fwAutoSelect) if (!fwAutoSelect)
def->os.loader->nvramTemplate = virXPathString("string(./os/nvram[1]/@template)", ctxt); def->os.loader->nvramTemplate = virXPathString("string(./os/nvram[1]/@template)", ctxt);
@ -18474,7 +18516,9 @@ virDomainDefParseBootAcpiOptions(virDomainDef *def,
static int static int
virDomainDefParseBootOptions(virDomainDef *def, virDomainDefParseBootOptions(virDomainDef *def,
xmlXPathContextPtr ctxt) xmlXPathContextPtr ctxt,
virDomainXMLOption *xmlopt,
unsigned int flags)
{ {
/* /*
* Booting options for different OS types.... * Booting options for different OS types....
@ -18492,7 +18536,7 @@ virDomainDefParseBootOptions(virDomainDef *def,
if (virDomainDefParseBootFirmwareOptions(def, ctxt) < 0) if (virDomainDefParseBootFirmwareOptions(def, ctxt) < 0)
return -1; return -1;
if (virDomainDefParseBootLoaderOptions(def, ctxt) < 0) if (virDomainDefParseBootLoaderOptions(def, ctxt, xmlopt, flags) < 0)
return -1; return -1;
if (virDomainDefParseBootAcpiOptions(def, ctxt) < 0) if (virDomainDefParseBootAcpiOptions(def, ctxt) < 0)
@ -18508,7 +18552,7 @@ virDomainDefParseBootOptions(virDomainDef *def,
case VIR_DOMAIN_OSTYPE_UML: case VIR_DOMAIN_OSTYPE_UML:
virDomainDefParseBootKernelOptions(def, ctxt); virDomainDefParseBootKernelOptions(def, ctxt);
if (virDomainDefParseBootLoaderOptions(def, ctxt) < 0) if (virDomainDefParseBootLoaderOptions(def, ctxt, xmlopt, flags) < 0)
return -1; return -1;
break; break;
@ -19808,7 +19852,7 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt,
if (virDomainDefClockParse(def, ctxt) < 0) if (virDomainDefClockParse(def, ctxt) < 0)
return NULL; return NULL;
if (virDomainDefParseBootOptions(def, ctxt) < 0) if (virDomainDefParseBootOptions(def, ctxt, xmlopt, flags) < 0)
return NULL; return NULL;
/* analysis of the disk devices */ /* analysis of the disk devices */
@ -27161,26 +27205,48 @@ virDomainHugepagesFormat(virBuffer *buf,
} }
static void static int
virDomainLoaderDefFormatNvram(virBuffer *buf, virDomainLoaderDefFormatNvram(virBuffer *buf,
virDomainLoaderDef *loader) virDomainLoaderDef *loader,
virDomainXMLOption *xmlopt,
unsigned int flags)
{ {
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) childBufDirect = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) childBufChild = VIR_BUFFER_INIT_CHILD(buf);
virBuffer *childBuf = &childBufDirect;
bool childNewline = false;
virBufferEscapeString(&attrBuf, " template='%s'", loader->nvramTemplate); virBufferEscapeString(&attrBuf, " template='%s'", loader->nvramTemplate);
if (loader->nvram) { if (loader->nvram) {
if (loader->nvram->type == VIR_STORAGE_TYPE_FILE) virStorageSource *src = loader->nvram;
virBufferEscapeString(&childBuf, "%s", loader->nvram->path);
if (!loader->newStyleNVRAM) {
virBufferEscapeString(&childBufDirect, "%s", src->path);
} else {
childNewline = true;
childBuf = &childBufChild;
virBufferAsprintf(&attrBuf, " type='%s'", virStorageTypeToString(src->type));
if (virDomainDiskSourceFormat(&childBufChild, src, "source", 0,
false, flags, false, false, xmlopt) < 0)
return -1;
}
} }
virXMLFormatElementInternal(buf, "nvram", &attrBuf, &childBuf, false, false); virXMLFormatElementInternal(buf, "nvram", &attrBuf, childBuf, false, childNewline);
return 0;
} }
static void static int
virDomainLoaderDefFormat(virBuffer *buf, virDomainLoaderDefFormat(virBuffer *buf,
virDomainLoaderDef *loader) virDomainLoaderDef *loader,
virDomainXMLOption *xmlopt,
unsigned int flags)
{ {
g_auto(virBuffer) loaderAttrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) loaderAttrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) loaderChildBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) loaderChildBuf = VIR_BUFFER_INITIALIZER;
@ -27201,7 +27267,10 @@ virDomainLoaderDefFormat(virBuffer *buf,
virXMLFormatElementInternal(buf, "loader", &loaderAttrBuf, &loaderChildBuf, false, false); virXMLFormatElementInternal(buf, "loader", &loaderAttrBuf, &loaderChildBuf, false, false);
virDomainLoaderDefFormatNvram(buf, loader); if (virDomainLoaderDefFormatNvram(buf, loader, xmlopt, flags) < 0)
return -1;
return 0;
} }
static void static void
@ -28454,8 +28523,9 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def,
if (def->os.initgroup) if (def->os.initgroup)
virBufferAsprintf(buf, "<initgroup>%s</initgroup>\n", def->os.initgroup); virBufferAsprintf(buf, "<initgroup>%s</initgroup>\n", def->os.initgroup);
if (def->os.loader) if (def->os.loader &&
virDomainLoaderDefFormat(buf, def->os.loader); virDomainLoaderDefFormat(buf, def->os.loader, xmlopt, flags) < 0)
return -1;
virBufferEscapeString(buf, "<kernel>%s</kernel>\n", virBufferEscapeString(buf, "<kernel>%s</kernel>\n",
def->os.kernel); def->os.kernel);
virBufferEscapeString(buf, "<initrd>%s</initrd>\n", virBufferEscapeString(buf, "<initrd>%s</initrd>\n",

View File

@ -2254,6 +2254,7 @@ struct _virDomainLoaderDef {
virDomainLoader type; virDomainLoader type;
virTristateBool secure; virTristateBool secure;
virStorageSource *nvram; virStorageSource *nvram;
bool newStyleNVRAM;
char *nvramTemplate; /* user override of path to master nvram */ char *nvramTemplate; /* user override of path to master nvram */
}; };

View File

@ -333,7 +333,14 @@
</attribute> </attribute>
</optional> </optional>
<optional> <optional>
<ref name="absFilePath"/> <choice>
<group>
<ref name="absFilePath"/>
</group>
<group>
<ref name="diskSource"/>
</group>
</choice>
</optional> </optional>
</element> </element>
</optional> </optional>

View File

@ -609,6 +609,13 @@ qemuValidateDomainDefNvram(const virDomainDef *def,
if (!src) if (!src)
return 0; return 0;
if (def->os.loader->newStyleNVRAM &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("modern nvram specification is not supported by this qemu"));
return -1;
}
switch (src->type) { switch (src->type) {
case VIR_STORAGE_TYPE_FILE: case VIR_STORAGE_TYPE_FILE:
case VIR_STORAGE_TYPE_BLOCK: case VIR_STORAGE_TYPE_BLOCK: