[libvirt] [ v3 0/4] Introduce network-backed loader & NVRAM.

Libvirt domain XML allows only local filepaths to specify a loader element or its matching NVRAM. Given that VMs may themselves move across hypervisor hosts, it should be possible to allocate loaders/NVRAM disks on network storage for uninterrupted access. This series extends the loader & NVRAM disk elements to be described as virStorageSource* elements, as discussed in : https://www.redhat.com/archives/libvir-list/2018-March/msg01721.html Sample XML with new annotation: <loader readonly='yes' type='pflash' backing='file'> <source file='/usr/share/OVMF/OVMF_CODE.fd'/> </loader> <nvram backing='network'> <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'> <host name='example.com' port='6000'/> </source> </nvram> References: ---------- v0/ Proposal: https://www.redhat.com/archives/libvir-list/2018-March/msg01721.html.v1 v1: https://www.redhat.com/archives/libvir-list/2018-April/msg02024.html v2: https://www.redhat.com/archives/libvir-list/2018-May/msg00948.html Changelog: --------- Changes since v2: - Consolidated patches with related data structures to avoid build breakage. - Passes make check & make syntax-check. Prerna Saxena (4): Schema: Introduce XML schema for network-backed loader and nvram elements. Loader: Add a more elaborate definition. Test: Add a test snippet to evaluate command line generation for loader/nvram specified via virStorageSource Documentation: Add a blurb for the newly added XML snippets for loader and nvram. docs/formatdomain.html.in | 36 +++- docs/schemas/domaincommon.rng | 108 +++++++++-- src/bhyve/bhyve_command.c | 6 +- src/conf/domain_conf.c | 250 +++++++++++++++++++++++-- src/conf/domain_conf.h | 11 +- src/qemu/qemu_cgroup.c | 13 +- src/qemu/qemu_command.c | 21 ++- src/qemu/qemu_domain.c | 31 ++- src/qemu/qemu_driver.c | 7 +- src/qemu/qemu_parse_command.c | 30 ++- src/qemu/qemu_process.c | 54 ++++-- src/security/security_dac.c | 6 +- src/security/security_selinux.c | 6 +- src/security/virt-aa-helper.c | 14 +- src/vbox/vbox_common.c | 11 +- src/xenapi/xenapi_driver.c | 4 +- src/xenconfig/xen_sxpr.c | 19 +- src/xenconfig/xen_xm.c | 9 +- tests/qemuxml2argvdata/bios-nvram-network.args | 31 +++ tests/qemuxml2argvdata/bios-nvram-network.xml | 42 +++++ tests/qemuxml2argvtest.c | 1 + 21 files changed, 606 insertions(+), 104 deletions(-) create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.args create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.xml -- 1.8.1.2

Today, 'loader' and 'nvram' elements are supposed to be backed by a local disk. Given that NVRAM data could be network-backed for high availability, this patch defines XML spec for serving loader & nvram disks via the network. Signed-off-by: Prerna Saxena <saxenap.ltc@gmail.com> --- docs/schemas/domaincommon.rng | 108 +++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 71ac3d0..64f4319 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -276,7 +276,42 @@ </choice> </attribute> </optional> - <ref name="absFilePath"/> + <optional> + <attribute name="backing"> + <choice> + <value>file</value> + <value>network</value> + </choice> + </attribute> + </optional> + <optional> + <choice> + <group> + <ref name="absFilePath"/> + </group> + <group> + <ref name="diskSourceFileElement"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolNBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolGluster"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolRBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolISCSI"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolHTTP"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolSimple"/> + </group> + </choice> + </optional> </element> </optional> <optional> @@ -287,7 +322,40 @@ </attribute> </optional> <optional> - <ref name="absFilePath"/> + <attribute name="backing"> + <choice> + <value>file</value> + <value>network</value> + </choice> + </attribute> + </optional> + <optional> + <choice> + <group> + <ref name="absFilePath"/> + </group> + <group> + <ref name="diskSourceFileElement"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolNBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolGluster"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolRBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolISCSI"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolHTTP"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolSimple"/> + </group> + </choice> </optional> </element> </optional> @@ -1499,25 +1567,29 @@ </attribute> </optional> <optional> - <element name="source"> - <optional> - <attribute name="file"> - <ref name="absFilePath"/> - </attribute> - </optional> - <optional> - <ref name="storageStartupPolicy"/> - </optional> - <optional> - <ref name="encryption"/> - </optional> - <zeroOrMore> - <ref name='devSeclabel'/> - </zeroOrMore> - </element> + <ref name='diskSourceFileElement'/> </optional> </define> + <define name="diskSourceFileElement"> + <element name="source"> + <optional> + <attribute name="file"> + <ref name="absFilePath"/> + </attribute> + </optional> + <optional> + <ref name="storageStartupPolicy"/> + </optional> + <optional> + <ref name="encryption"/> + </optional> + <zeroOrMore> + <ref name='devSeclabel'/> + </zeroOrMore> + </element> + </define> + <define name="diskSourceBlock"> <attribute name="type"> <value>block</value> -- 1.8.1.2

That's a long $SUBJ.... On 05/21/2018 07:10 AM, Prerna Saxena wrote:
Today, 'loader' and 'nvram' elements are supposed to be backed by a local disk. Given that NVRAM data could be network-backed for high availability, this patch defines XML spec for serving loader & nvram disks via the network.
...and these are long lines too. I'm still not convinced you have the "optimal" split of patches. Eventually though this patch should be split up (more on that in patch 2 review). In the long run, I think you should alter <loader> first, then alter <nvram> - and it needs to be done in a slower, more progressive manner. The schema would be included into a patch when the <source> parse/format is done in domain_conf and tests would be added in order to validate (and as has been pointed out to me recently, we should use the genericxml2xmltest for at least the parse/format tests). After reviewing the patches, I'm still a bit "confused" with respect to how one can uniquely "name" or find the <loader> or <nvram> file when using the network <source> protocol. Eventually "something" has to be opened for the loader (e.g. CODE file) and nvram (e.g. VARS file). The current syntax and the changed "file" syntax would allow whatever name is chosen, but since the network syntax may not (e.g. the case of iSCSI) provide a name, but only a /dev/sgN device. So how is the file found? Isn't that something QEMU will need? Consider these two <loader> examples: tests/qemuxml2argvdata/bios-nvram-secure.xml: <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.secboot.fd</loader> tests/qemuxml2argvdata/bios-nvram.xml: <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader> or these two <nvram> examples: tests/qemuxml2argvdata/q35-acpi-uefi.xml: <nvram>/var/lib/libvirt/qemu/nvram/guest_VARS.fd</nvram> tests/qemuxml2xmloutdata/bios-nvram.xml: <nvram>/usr/share/OVMF/OVMF_VARS.fd</nvram> With a network path such as your example: + <nvram> + <source protocol='iscsi' name='iqn.1992-01.com.example/0'> + <host name='example.org' port='6000'/> + </source> + </nvram> How do you distinguish? Does that mean the someone has to "know" what's on each exposed LUN?
Signed-off-by: Prerna Saxena <saxenap.ltc@gmail.com> --- docs/schemas/domaincommon.rng | 108 +++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 18 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 71ac3d0..64f4319 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -276,7 +276,42 @@ </choice> </attribute> </optional> - <ref name="absFilePath"/> + <optional> + <attribute name="backing"> + <choice> + <value>file</value> + <value>network</value> + </choice> + </attribute> + </optional>
You don't need "backing"... Either the old format is found or some sort of <source...> format is found. If <source> is found, then you know that you have either a "file" or "protocol" attribute and can use that to key or set the StorageSourcePtr->type to FILE or NETWORK prior to calling the storage source parse function.
+ <optional> + <choice> + <group>
NB: Each of the <group> and </group> entries are indented by 1 space too many.
+ <ref name="absFilePath"/> + </group> + <group> + <ref name="diskSourceFileElement"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolNBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolGluster"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolRBD"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolISCSI"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolHTTP"/> + </group> + <group> + <ref name="diskSourceNetworkProtocolSimple"/> + </group> + </choice> + </optional>
This entire hunk is duplicated for loader and nvram, so make it a <define name="sourceLoaderNvram"> (or something like that) and it can then be used to replace the <ref name="absFilePath"/> with <ref name="sourceLoaderNvram"/> John ...

Augment definition to include virStorageSourcePtr that more comprehensively describes the nature of backing element. Also include flags for annotating if input XML definition is old-style or new-style. 2) Parse domain XML to generate virDomainLoaderDef & virDomainNvramDef. This patch is used to interpret domain XML and store the Loader & Nvram's backing definitions as virStorageSource. It also identifies if input XML used old or new-style declaration. (This will later be used by the formatter). 3) Format the loader source appropriately. If the initial XML used the old-style declaration as follows: <loader type='pflash'>/path/to/file</loader> we format it as was read. However, if it used new-style declaration: <loader type='pflash' backing='file'> <source file='path/to/file'/> </loader> The formatter identifies that this is a new-style format and renders it likewise. 4) Plumb the loader source into generation of QEMU command line. Given that nvram & loader elements can now be backed by a non-local source too, adjust all steps leading to generation of QEMU command line. 5) Fix the domain def inference logic to correctly account for network-backed pflash devices. 6) Bhyve: Fix command line generation to correctly pick up local loader path. 7) virt-aa-helper: Adjust references to loader & nvram elements to correctly parse the virStorageSource types. 8) Vbox: Adjust references to 'loader' and 'nvram' elements given that these are now represented by virStorageSourcePtr. 9)Xen: Adjust all references to loader & nvram elements given that they are now backed by virStorageSourcePtr --- src/bhyve/bhyve_command.c | 6 +- src/conf/domain_conf.c | 250 +++++++++++++++++++++++++++++++++++++--- src/conf/domain_conf.h | 11 +- src/qemu/qemu_cgroup.c | 13 ++- src/qemu/qemu_command.c | 21 +++- src/qemu/qemu_domain.c | 31 +++-- src/qemu/qemu_driver.c | 7 +- src/qemu/qemu_parse_command.c | 30 ++++- src/qemu/qemu_process.c | 54 ++++++--- src/security/security_dac.c | 6 +- src/security/security_selinux.c | 6 +- src/security/virt-aa-helper.c | 14 ++- src/vbox/vbox_common.c | 11 +- src/xenapi/xenapi_driver.c | 4 +- src/xenconfig/xen_sxpr.c | 19 +-- src/xenconfig/xen_xm.c | 9 +- 16 files changed, 409 insertions(+), 83 deletions(-) diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index e3f7ded..9e53f40 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -521,10 +521,12 @@ virBhyveProcessBuildBhyveCmd(virConnectPtr conn, virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL); if (def->os.bootloader == NULL && - def->os.loader) { + def->os.loader && + def->os.loader->src && + def->os.loader->src->type == VIR_STORAGE_TYPE_FILE) { if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_LPC_BOOTROM)) { virCommandAddArg(cmd, "-l"); - virCommandAddArgFormat(cmd, "bootrom,%s", def->os.loader->path); + virCommandAddArgFormat(cmd, "bootrom,%s", def->os.loader->src->path); add_lpc = true; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3689ac0..df2ed3a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2884,8 +2884,8 @@ virDomainLoaderDefFree(virDomainLoaderDefPtr loader) if (!loader) return; - VIR_FREE(loader->path); - VIR_FREE(loader->nvram); + virStorageSourceFree(loader->src); + virStorageSourceFree(loader->nvram); VIR_FREE(loader->templt); VIR_FREE(loader); } @@ -18087,30 +18087,81 @@ virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def) static int virDomainLoaderDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, virDomainLoaderDefPtr loader) { int ret = -1; char *readonly_str = NULL; char *secure_str = NULL; char *type_str = NULL; + char *tmp = NULL; + char *lpath = NULL; + xmlNodePtr cur; + xmlXPathContextPtr cur_ctxt = ctxt; + + if (VIR_ALLOC(loader->src) < 0) + goto error; + + loader->src->type = VIR_STORAGE_TYPE_LAST; + loader->oldStyleLoader = false; readonly_str = virXMLPropString(node, "readonly"); secure_str = virXMLPropString(node, "secure"); type_str = virXMLPropString(node, "type"); - loader->path = (char *) xmlNodeGetContent(node); + + if ((tmp = virXMLPropString(node, "backing")) && + (loader->src->type = virStorageTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown loader type '%s'"), tmp); + VIR_FREE(tmp); + goto error; + } + VIR_FREE(tmp); + + for (cur = node->children; cur != NULL; cur = cur->next) { + if (cur->type != XML_ELEMENT_NODE) + continue; + + if (virXMLNodeNameEqual(cur, "source")) { + /* new-style declaration found */ + if (virDomainStorageSourceParse(cur, cur_ctxt, loader->src, 0) < 0) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("Error parsing Loader source element")); + goto error; + } + break; + } + } + + /* Old-style absolute path found ? */ + if (loader->src->type == VIR_STORAGE_TYPE_LAST) { + lpath = (char *) xmlNodeGetContent(node); + if (virStringIsEmpty(lpath)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing loader source")); + VIR_FREE(lpath); + goto error; + } + + loader->src->type = VIR_STORAGE_TYPE_FILE; + if (VIR_STRDUP(loader->src->path, lpath) < 0) + goto error; + VIR_FREE(lpath); + loader->oldStyleLoader = true; + } if (readonly_str && (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown readonly value: %s"), readonly_str); - goto cleanup; + goto error; } if (secure_str && (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown secure value: %s"), secure_str); - goto cleanup; + goto error; } if (type_str) { @@ -18118,19 +18169,97 @@ virDomainLoaderDefParseXML(xmlNodePtr node, if ((type = virDomainLoaderTypeFromString(type_str)) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown type value: %s"), type_str); - goto cleanup; + goto error; } loader->type = type; } ret = 0; - cleanup: + + exit: VIR_FREE(readonly_str); VIR_FREE(secure_str); VIR_FREE(type_str); + return ret; + error: + virStorageSourceFree(loader->src); + VIR_FREE(lpath); + loader->src = NULL; + goto exit; } +static int +virDomainLoaderNvramDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virDomainLoaderDefPtr loader) +{ + int ret = -1; + char *tmp = NULL; + char *npath = NULL; + xmlNodePtr cur; + + if (VIR_ALLOC(loader->nvram) < 0) + goto error; + + loader->nvram->type = VIR_STORAGE_TYPE_LAST; + loader->oldStyleNvram = false; + + if ((tmp = virXMLPropString(node, "backing")) && + (loader->nvram->type = virStorageTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown nvram type '%s'"), tmp); + VIR_FREE(tmp); + goto error; + } + VIR_FREE(tmp); + + loader->templt = virXMLPropString(node, "template"); + + for (cur = node->children; cur != NULL; cur = cur->next) { + if (cur->type != XML_ELEMENT_NODE) + continue; + + if (virXMLNodeNameEqual(cur, "source")) { + if (virDomainStorageSourceParse(cur, ctxt, loader->nvram, 0) < 0) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("Error parsing nvram source element")); + goto error; + } + ret = 0; + break; + } + } + + /* Old-style declaration found */ + if (loader->nvram->type == VIR_STORAGE_TYPE_LAST) { + npath = (char *) xmlNodeGetContent(node); + + /* Suppress failures from lack of NVRAM, + * it will eventually be generated from template*/ + if (virStringIsEmpty(npath)) { + virStorageSourceFree(loader->nvram); + loader->nvram = NULL; + VIR_FREE(npath); + return 0; + } + + if (VIR_STRDUP(loader->nvram->path, npath) < 0) + goto error; + VIR_FREE(npath); + loader->nvram->type = VIR_STORAGE_TYPE_FILE; + loader->oldStyleNvram = true; + ret = 0; + } + return ret; + + error: + virStorageSourceFree(loader->nvram); + loader->nvram = NULL; + VIR_FREE(npath); + VIR_FREE(loader->templt); + return ret; +} static virBitmapPtr virDomainSchedulerParse(xmlNodePtr node, @@ -18523,11 +18652,13 @@ virDomainDefParseBootOptions(virDomainDefPtr def, if (VIR_ALLOC(def->os.loader) < 0) goto error; - if (virDomainLoaderDefParseXML(loader_node, def->os.loader) < 0) + if (virDomainLoaderDefParseXML(loader_node, ctxt, def->os.loader) < 0) goto error; - def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt); - def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt); + if ((loader_node = virXPathNode("./os/nvram[1]", ctxt)) && + (virDomainLoaderNvramDefParseXML(loader_node, ctxt, + def->os.loader) < 0)) + goto error; } } @@ -26217,11 +26348,19 @@ virDomainHugepagesFormat(virBufferPtr buf, static void virDomainLoaderDefFormat(virBufferPtr buf, - virDomainLoaderDefPtr loader) + virDomainLoaderDefPtr loader, + unsigned int flags) { const char *readonly = virTristateBoolTypeToString(loader->readonly); const char *secure = virTristateBoolTypeToString(loader->secure); const char *type = virDomainLoaderTypeToString(loader->type); + const char *backing = NULL; + + virBuffer attrBuf = VIR_BUFFER_INITIALIZER; + virBuffer childBuf = VIR_BUFFER_INITIALIZER; + + virBufferSetChildIndent(&childBuf, buf); + virBufferAddLit(buf, "<loader"); @@ -26231,17 +26370,90 @@ virDomainLoaderDefFormat(virBufferPtr buf, if (loader->secure) virBufferAsprintf(buf, " secure='%s'", secure); - virBufferAsprintf(buf, " type='%s'>", type); + virBufferAsprintf(buf, " type='%s'", type); + if (loader->src && + loader->src->type < VIR_STORAGE_TYPE_LAST) { + if (!loader->oldStyleLoader) { + /* Format this in the new style, using the + * <source> sub-element */ + + if (virDomainStorageSourceFormat(&attrBuf, &childBuf, loader->src, + flags, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot format loader source")); + goto error; + } + + backing = virStorageTypeToString(loader->src->type); + virBufferAsprintf(buf, " backing='%s'>", backing); + virBufferAdjustIndent(buf, 2); + + if (virXMLFormatElement(buf, "source", &attrBuf, &childBuf) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot format loader source")); + goto error; + } + + virBufferAdjustIndent(buf, -2); + + } else { + /* Format this in the old-style, using absolute paths directly. */ + virBufferAsprintf(buf, ">%s", loader->src->path); + } + } else { + virBufferAddLit(buf, ">\n"); + } + + virBufferAddLit(buf, "</loader>\n"); - virBufferEscapeString(buf, "%s</loader>\n", loader->path); if (loader->nvram || loader->templt) { + ignore_value(virBufferContentAndReset(&attrBuf)); + ignore_value(virBufferContentAndReset(&childBuf)); + virBufferSetChildIndent(&childBuf, buf); + virBufferAddLit(buf, "<nvram"); - virBufferEscapeString(buf, " template='%s'", loader->templt); - if (loader->nvram) - virBufferEscapeString(buf, ">%s</nvram>\n", loader->nvram); - else - virBufferAddLit(buf, "/>\n"); + + if (loader->templt) + virBufferEscapeString(buf, " template='%s'", loader->templt); + + if (loader->nvram) { + backing = virStorageTypeToString(loader->nvram->type); + if (!loader->oldStyleNvram) { + + if (virDomainStorageSourceFormat(&attrBuf, &childBuf, + loader->nvram, flags, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot format NVRAM source")); + virBufferAddLit(buf, "></nvram>"); + goto error; + } + + virBufferEscapeString(buf, " backing='%s'>\n", backing); + virBufferAdjustIndent(buf, 2); + + if (virXMLFormatElement(buf, "source", &attrBuf, &childBuf) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot format NVRAM source")); + virBufferAddLit(buf, "</nvram>"); + goto error; + } + + virBufferAdjustIndent(buf, -2); + + } else { + /* old-style NVRAM declaration found */ + virBufferAsprintf(buf, ">%s", loader->nvram->path); + } + } else { + virBufferAddLit(buf, ">\n"); + } + + virBufferAddLit(buf, "</nvram>\n"); } + error: + virBufferFreeAndReset(&attrBuf); + virBufferFreeAndReset(&childBuf); + return; } static void @@ -26918,7 +27130,7 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAsprintf(buf, "<initgroup>%s</initgroup>\n", def->os.initgroup); if (def->os.loader) - virDomainLoaderDefFormat(buf, def->os.loader); + virDomainLoaderDefFormat(buf, def->os.loader, flags); virBufferEscapeString(buf, "<kernel>%s</kernel>\n", def->os.kernel); virBufferEscapeString(buf, "<initrd>%s</initrd>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a78fdee..fb44a90 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1856,15 +1856,22 @@ typedef enum { VIR_ENUM_DECL(virDomainLoader) +struct _virStorageSource; +typedef struct _virStorageSource *virStorageSourcePtr; + typedef struct _virDomainLoaderDef virDomainLoaderDef; typedef virDomainLoaderDef *virDomainLoaderDefPtr; struct _virDomainLoaderDef { - char *path; + virStorageSourcePtr src; int readonly; /* enum virTristateBool */ virDomainLoader type; int secure; /* enum virTristateBool */ - char *nvram; /* path to non-volatile RAM */ + virStorageSourcePtr nvram; /* path to non-voliatile RAM */ char *templt; /* user override of path to master nvram */ + bool oldStyleLoader; /* is this an old-style XML formatting, + * ie, absolute path is directly specified? */ + bool oldStyleNvram; /* is this an old-style XML formatting, + * ie, absolute path is directly specified? */ }; void virDomainLoaderDefFree(virDomainLoaderDefPtr loader); diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 546a4c8..5c16a48 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -607,16 +607,21 @@ qemuSetupMemoryCgroup(virDomainObjPtr vm) static int qemuSetupFirmwareCgroup(virDomainObjPtr vm) { + virStorageSourcePtr src = NULL; + if (!vm->def->os.loader) return 0; - if (vm->def->os.loader->path && - qemuSetupImagePathCgroup(vm, vm->def->os.loader->path, - vm->def->os.loader->readonly == VIR_TRISTATE_BOOL_YES) < 0) + src = vm->def->os.loader->src; + + if (src->type == VIR_STORAGE_TYPE_FILE && + qemuSetupImagePathCgroup(vm, src->path, + src->readonly == VIR_TRISTATE_BOOL_YES) < 0) return -1; if (vm->def->os.loader->nvram && - qemuSetupImagePathCgroup(vm, vm->def->os.loader->nvram, false) < 0) + vm->def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && + qemuSetupImagePathCgroup(vm, vm->def->os.loader->nvram->path, false) < 0) return -1; return 0; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9da2d60..ba5283f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9320,6 +9320,7 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, virDomainLoaderDefPtr loader = def->os.loader; virBuffer buf = VIR_BUFFER_INITIALIZER; int unit = 0; + char *source = NULL; if (!loader) return; @@ -9327,7 +9328,7 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, switch ((virDomainLoader) loader->type) { case VIR_DOMAIN_LOADER_TYPE_ROM: virCommandAddArg(cmd, "-bios"); - virCommandAddArg(cmd, loader->path); + virCommandAddArg(cmd, loader->src->path); break; case VIR_DOMAIN_LOADER_TYPE_PFLASH: @@ -9339,9 +9340,14 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, NULL); } + if (qemuGetDriveSourceString(loader->src, NULL, &source) < 0) + break; + virBufferAddLit(&buf, "file="); - virQEMUBuildBufferEscapeComma(&buf, loader->path); - virBufferAsprintf(&buf, ",if=pflash,format=raw,unit=%d", unit); + virQEMUBuildBufferEscapeComma(&buf, source); + VIR_FREE(source); + virBufferAsprintf(&buf, ",if=pflash,format=raw,unit=%d", + unit); unit++; if (loader->readonly) { @@ -9354,9 +9360,14 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, if (loader->nvram) { virBufferFreeAndReset(&buf); + if (qemuGetDriveSourceString(loader->nvram, NULL, &source) < 0) + break; + virBufferAddLit(&buf, "file="); - virQEMUBuildBufferEscapeComma(&buf, loader->nvram); - virBufferAsprintf(&buf, ",if=pflash,format=raw,unit=%d", unit); + virQEMUBuildBufferEscapeComma(&buf, source); + virBufferAsprintf(&buf, ",if=pflash,format=raw,unit=%d", + unit); + unit++; virCommandAddArg(cmd, "-drive"); virCommandAddArgBuffer(cmd, &buf); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index d3beee5..5b73a6e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3346,6 +3346,9 @@ qemuDomainDefPostParse(virDomainDefPtr def, * function shall not fail in that case. It will be re-run on VM startup * with the capabilities populated. */ virQEMUCapsPtr qemuCaps = parseOpaque; + virDomainLoaderDefPtr ldr = NULL; + char *nvramPath = NULL; + int ret = -1; if (def->os.bootloader || def->os.bootloaderArgs) { @@ -3360,13 +3363,20 @@ qemuDomainDefPostParse(virDomainDefPtr def, goto cleanup; } - if (def->os.loader && - def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_PFLASH && - def->os.loader->readonly == VIR_TRISTATE_SWITCH_ON && - !def->os.loader->nvram) { - if (virAsprintf(&def->os.loader->nvram, "%s/%s_VARS.fd", + ldr = def->os.loader; + if (ldr && + ldr->type == VIR_DOMAIN_LOADER_TYPE_PFLASH && + ldr->readonly == VIR_TRISTATE_SWITCH_ON && + !ldr->nvram) { + if (virAsprintf(&nvramPath, "%s/%s_VARS.fd", cfg->nvramDir, def->name) < 0) goto cleanup; + ldr->nvram = virStorageSourceNewFromBackingAbsolute(nvramPath); + if (!ldr->nvram) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to add NVRAM drive %s"), nvramPath); + goto cleanup; + } } if (qemuDomainDefAddDefaultDevices(def, qemuCaps) < 0) @@ -10547,19 +10557,22 @@ qemuDomainSetupLoader(virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED, VIR_DEBUG("Setting up loader"); - if (loader) { + if (loader && loader->src) { switch ((virDomainLoader) loader->type) { case VIR_DOMAIN_LOADER_TYPE_ROM: - if (qemuDomainCreateDevice(loader->path, data, false) < 0) + if (loader->src->type == VIR_STORAGE_TYPE_FILE && + qemuDomainCreateDevice(loader->src->path, data, false) < 0) goto cleanup; break; case VIR_DOMAIN_LOADER_TYPE_PFLASH: - if (qemuDomainCreateDevice(loader->path, data, false) < 0) + if (loader->src->type == VIR_STORAGE_TYPE_FILE && + qemuDomainCreateDevice(loader->src->path, data, false) < 0) goto cleanup; if (loader->nvram && - qemuDomainCreateDevice(loader->nvram, data, false) < 0) + loader->nvram->type == VIR_STORAGE_TYPE_FILE && + qemuDomainCreateDevice(loader->nvram->path, data, false) < 0) goto cleanup; break; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e61af23..a056bc4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7526,12 +7526,13 @@ qemuDomainUndefineFlags(virDomainPtr dom, if (vm->def->os.loader && vm->def->os.loader->nvram && - virFileExists(vm->def->os.loader->nvram)) { + vm->def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && + virFileExists(vm->def->os.loader->nvram->path)) { if ((flags & VIR_DOMAIN_UNDEFINE_NVRAM)) { - if (unlink(vm->def->os.loader->nvram) < 0) { + if (unlink(vm->def->os.loader->nvram->path) < 0) { virReportSystemError(errno, _("failed to remove nvram: %s"), - vm->def->os.loader->nvram); + vm->def->os.loader->nvram->path); goto endjob; } } else if (!(flags & VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)) { diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c index 351425f..9b1a86e 100644 --- a/src/qemu/qemu_parse_command.c +++ b/src/qemu/qemu_parse_command.c @@ -650,6 +650,7 @@ qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt, int idx = -1; int busid = -1; int unitid = -1; + bool is_firmware = false; if (qemuParseKeywords(val, &keywords, @@ -772,6 +773,9 @@ qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt, def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; } else if (STREQ(values[i], "xen")) { def->bus = VIR_DOMAIN_DISK_BUS_XEN; + } else if (STREQ(values[i], "pflash")) { + def->bus = VIR_DOMAIN_DISK_BUS_LAST; + is_firmware = true; } else if (STREQ(values[i], "sd")) { def->bus = VIR_DOMAIN_DISK_BUS_SD; } @@ -943,8 +947,25 @@ qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt, ignore_value(VIR_STRDUP(def->dst, "hda")); } - if (!def->dst) - goto error; + if (!def->dst) { + if (is_firmware && def->bus == VIR_DOMAIN_DISK_BUS_LAST) { + if (!dom->os.loader && (VIR_ALLOC(dom->os.loader) < 0)) + goto error; + if (def->src->readonly) { + /* Loader spec */ + dom->os.loader->src = def->src; + dom->os.loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH; + } else { + /* NVRAM Spec */ + if (!dom->os.loader->nvram && (VIR_ALLOC(dom->os.loader->nvram) < 0)) + goto error; + dom->os.loader->nvram = def->src; + } + } else { + goto error; + } + } + if (STREQ(def->dst, "xvda")) def->dst[3] = 'a' + idx; else @@ -2215,8 +2236,11 @@ qemuParseCommandLine(virCapsPtr caps, } else if (STREQ(arg, "-bios")) { WANT_VALUE(); if (VIR_ALLOC(def->os.loader) < 0 || - VIR_STRDUP(def->os.loader->path, val) < 0) + VIR_ALLOC(def->os.loader->src) < 0 || + VIR_STRDUP(def->os.loader->src->path, val) < 0) goto error; + def->os.loader->src->type = VIR_STORAGE_TYPE_FILE; + def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM; } else if (STREQ(arg, "-initrd")) { WANT_VALUE(); if (VIR_STRDUP(def->os.initrd, val) < 0) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5b73a61..5d406ef 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4187,25 +4187,53 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, const char *master_nvram_path; ssize_t r; - if (!loader || !loader->nvram || virFileExists(loader->nvram)) + /* This function is used to prepare a pristine local + * NVRAM file based on an existing template. + * The template could be explicitly specified in domain XML + * or inferred from absolute firmware path. + * + * Return early in the following cases: + * (1) loader or NVRAM is not specified. + * (2) loader is not of type 'pflash'. + * (3) NVRAM is already described as 'network-backed' + * (4) NVRAM is file-backed but file already exists. + * (5) Loader is network-backed, and hence impossible to + * infer firmware. + * (6) NVRAM is network-backed, + * hence not easy to customize. + */ + if (!loader || !loader->src || !loader->nvram || + loader->type != VIR_DOMAIN_LOADER_TYPE_PFLASH || + loader->src->type == VIR_STORAGE_TYPE_NETWORK || + loader->nvram->type == VIR_STORAGE_TYPE_NETWORK) + return 0; + + if (loader->nvram->type == VIR_STORAGE_TYPE_FILE && + virFileExists(loader->nvram->path)) return 0; master_nvram_path = loader->templt; - if (!loader->templt) { + /* Even if a template is not specified, we associate "known" EFI firmware + * to their NVRAM templates. + * Ofcourse this only applies to local firmware paths, as it is diffcult + * for libvirt to parse all network paths. + */ + if (!loader->templt && loader->src->type == VIR_STORAGE_TYPE_FILE) { size_t i; for (i = 0; i < cfg->nfirmwares; i++) { - if (STREQ(cfg->firmwares[i]->name, loader->path)) { + if (STREQ(cfg->firmwares[i]->name, loader->src->path)) { master_nvram_path = cfg->firmwares[i]->nvram; break; } } } - if (!master_nvram_path) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("unable to find any master var store for " - "loader: %s"), loader->path); - goto cleanup; + if (!master_nvram_path && loader->nvram) { + /* There is no template description, but an NVRAM spec + * has already been provided. + * Trust the client to have generated the right spec here + */ + return 0; } if ((srcFD = virFileOpenAs(master_nvram_path, O_RDONLY, @@ -4215,13 +4243,13 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, master_nvram_path); goto cleanup; } - if ((dstFD = virFileOpenAs(loader->nvram, + if ((dstFD = virFileOpenAs(loader->nvram->path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, cfg->user, cfg->group, 0)) < 0) { virReportSystemError(-dstFD, _("Failed to create file '%s'"), - loader->nvram); + loader->nvram->path); goto cleanup; } created = true; @@ -4239,7 +4267,7 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, if (safewrite(dstFD, buf, r) < 0) { virReportSystemError(errno, _("Unable to write to file '%s'"), - loader->nvram); + loader->nvram->path); goto cleanup; } } while (r); @@ -4253,7 +4281,7 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, if (VIR_CLOSE(dstFD) < 0) { virReportSystemError(errno, _("Unable to close file '%s'"), - loader->nvram); + loader->nvram->path); goto cleanup; } @@ -4263,7 +4291,7 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, * copy the file content. Roll back. */ if (ret < 0) { if (created) - unlink(loader->nvram); + unlink(loader->nvram->path); } VIR_FORCE_CLOSE(srcFD); diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 8938e2d..3febea6 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -1604,7 +1604,8 @@ virSecurityDACRestoreAllLabel(virSecurityManagerPtr mgr, } if (def->os.loader && def->os.loader->nvram && - virSecurityDACRestoreFileLabel(priv, def->os.loader->nvram) < 0) + def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && + virSecurityDACRestoreFileLabel(priv, def->os.loader->nvram->path) < 0) rc = -1; return rc; @@ -1732,8 +1733,9 @@ virSecurityDACSetAllLabel(virSecurityManagerPtr mgr, return -1; if (def->os.loader && def->os.loader->nvram && + def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && virSecurityDACSetOwnership(priv, NULL, - def->os.loader->nvram, user, group) < 0) + def->os.loader->nvram->path, user, group) < 0) return -1; if (def->os.kernel && diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 5f74ef7..bcda939 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -2459,7 +2459,8 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManagerPtr mgr, rc = -1; if (def->os.loader && def->os.loader->nvram && - virSecuritySELinuxRestoreFileLabel(mgr, def->os.loader->nvram) < 0) + def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && + virSecuritySELinuxRestoreFileLabel(mgr, def->os.loader->nvram->path) < 0) rc = -1; return rc; @@ -2851,8 +2852,9 @@ virSecuritySELinuxSetAllLabel(virSecurityManagerPtr mgr, /* This is different than kernel or initrd. The nvram store * is really a disk, qemu can read and write to it. */ if (def->os.loader && def->os.loader->nvram && + def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && secdef && secdef->imagelabel && - virSecuritySELinuxSetFilecon(mgr, def->os.loader->nvram, + virSecuritySELinuxSetFilecon(mgr, def->os.loader->nvram->path, secdef->imagelabel) < 0) return -1; diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index d0f9876..8217d67 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -1063,12 +1063,18 @@ get_files(vahControl * ctl) if (vah_add_file(&buf, ctl->def->os.slic_table, "r") != 0) goto cleanup; - if (ctl->def->os.loader && ctl->def->os.loader->path) - if (vah_add_file(&buf, ctl->def->os.loader->path, "rk") != 0) + if (ctl->def->os.loader && + ctl->def->os.loader->src && + ctl->def->os.loader->src->type == VIR_STORAGE_TYPE_FILE && + ctl->def->os.loader->src->path) + if (vah_add_file(&buf, ctl->def->os.loader->src->path, "rk") != 0) goto cleanup; - if (ctl->def->os.loader && ctl->def->os.loader->nvram) - if (vah_add_file(&buf, ctl->def->os.loader->nvram, "rwk") != 0) + if (ctl->def->os.loader && + ctl->def->os.loader->nvram && + ctl->def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE && + ctl->def->os.loader->nvram->path) + if (vah_add_file(&buf, ctl->def->os.loader->nvram->path, "rwk") != 0) goto cleanup; for (i = 0; i < ctl->def->ngraphics; i++) { diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index 72a24a3..60451a3 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -998,11 +998,16 @@ vboxSetBootDeviceOrder(virDomainDefPtr def, vboxDriverPtr data, VIR_DEBUG("def->os.initrd %s", def->os.initrd); VIR_DEBUG("def->os.cmdline %s", def->os.cmdline); VIR_DEBUG("def->os.root %s", def->os.root); - if (def->os.loader) { - VIR_DEBUG("def->os.loader->path %s", def->os.loader->path); + if (def->os.loader && + def->os.loader->src && + def->os.loader->src->type == VIR_STORAGE_TYPE_FILE) { + + VIR_DEBUG("def->os.loader->src->path %s", def->os.loader->src->path); VIR_DEBUG("def->os.loader->readonly %d", def->os.loader->readonly); VIR_DEBUG("def->os.loader->type %d", def->os.loader->type); - VIR_DEBUG("def->os.loader->nvram %s", def->os.loader->nvram); + if (def->os.loader->nvram && + def->os.loader->nvram->type == VIR_STORAGE_TYPE_FILE) + VIR_DEBUG("def->os.loader->nvram->path %s", def->os.loader->nvram->path); } VIR_DEBUG("def->os.bootloader %s", def->os.bootloader); VIR_DEBUG("def->os.bootloaderArgs %s", def->os.bootloaderArgs); diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index 42b305d..4070660 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1444,10 +1444,12 @@ xenapiDomainGetXMLDesc(virDomainPtr dom, unsigned int flags) char *value = NULL; defPtr->os.type = VIR_DOMAIN_OSTYPE_XEN; if (VIR_ALLOC(defPtr->os.loader) < 0 || - VIR_STRDUP(defPtr->os.loader->path, "pygrub") < 0) { + VIR_ALLOC(defPtr->os.loader->src) < 0 || + VIR_STRDUP(defPtr->os.loader->src->path, "pygrub") < 0) { VIR_FREE(boot_policy); goto error; } + defPtr->os.loader->src->type = VIR_STORAGE_TYPE_FILE; xen_vm_get_pv_kernel(session, &value, vm); if (STRNEQ(value, "")) { if (VIR_STRDUP(defPtr->os.kernel, value) < 0) { diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index e868c05..fd3165c 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -87,15 +87,17 @@ xenParseSxprOS(const struct sexpr *node, int hvm) { if (hvm) { - if (VIR_ALLOC(def->os.loader) < 0) + if ((VIR_ALLOC(def->os.loader) < 0) || + (VIR_ALLOC(def->os.loader->src) < 0)) goto error; - if (sexpr_node_copy(node, "domain/image/hvm/loader", &def->os.loader->path) < 0) + def->os.loader->src->type = VIR_STORAGE_TYPE_FILE; + if (sexpr_node_copy(node, "domain/image/hvm/loader", &def->os.loader->src->path) < 0) goto error; - if (def->os.loader->path == NULL) { - if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.loader->path) < 0) + if (def->os.loader->src->path == NULL) { + if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.loader->src->path) < 0) goto error; - if (def->os.loader->path == NULL) { + if (def->os.loader->src->path == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("domain information incomplete, missing HVM loader")); return -1; @@ -124,7 +126,8 @@ xenParseSxprOS(const struct sexpr *node, /* If HVM kenrel == loader, then old xend, so kill off kernel */ if (hvm && def->os.kernel && - STREQ(def->os.kernel, def->os.loader->path)) { + def->os.loader->src && + STREQ(def->os.kernel, def->os.loader->src->path)) { VIR_FREE(def->os.kernel); } /* Drop kernel argument that has no value */ @@ -2259,9 +2262,9 @@ xenFormatSxpr(virConnectPtr conn, virDomainDefPtr def) if (hvm) { char bootorder[VIR_DOMAIN_BOOT_LAST+1]; if (def->os.kernel) - virBufferEscapeSexpr(&buf, "(loader '%s')", def->os.loader->path); + virBufferEscapeSexpr(&buf, "(loader '%s')", def->os.loader->src->path); else - virBufferEscapeSexpr(&buf, "(kernel '%s')", def->os.loader->path); + virBufferEscapeSexpr(&buf, "(kernel '%s')", def->os.loader->src->path); virBufferAsprintf(&buf, "(vcpus %u)", virDomainDefGetVcpusMax(def)); if (virDomainDefHasVcpusOffline(def)) diff --git a/src/xenconfig/xen_xm.c b/src/xenconfig/xen_xm.c index 4becb40..408b7b8 100644 --- a/src/xenconfig/xen_xm.c +++ b/src/xenconfig/xen_xm.c @@ -47,8 +47,10 @@ xenParseXMOS(virConfPtr conf, virDomainDefPtr def) const char *boot; if (VIR_ALLOC(def->os.loader) < 0 || - xenConfigCopyString(conf, "kernel", &def->os.loader->path) < 0) + VIR_ALLOC(def->os.loader->src) < 0 || + xenConfigCopyString(conf, "kernel", &def->os.loader->src->path) < 0) return -1; + def->os.loader->src->type = VIR_STORAGE_TYPE_FILE; if (xenConfigGetString(conf, "boot", &boot, "c") < 0) return -1; @@ -484,9 +486,10 @@ xenFormatXMOS(virConfPtr conf, virDomainDefPtr def) if (xenConfigSetString(conf, "builder", "hvm") < 0) return -1; - if (def->os.loader && def->os.loader->path && - xenConfigSetString(conf, "kernel", def->os.loader->path) < 0) + if (def->os.loader && def->os.loader->src && + xenConfigSetString(conf, "kernel", def->os.loader->src->path) < 0) return -1; + def->os.loader->src->type = VIR_STORAGE_TYPE_FILE; for (i = 0; i < def->os.nBootDevs; i++) { switch (def->os.bootDevs[i]) { -- 1.8.1.2

On 05/21/2018 07:10 AM, Prerna Saxena wrote:
Augment definition to include virStorageSourcePtr that more comprehensively describes the nature of backing element. Also include flags for annotating if input XML definition is old-style or new-style.
2) Parse domain XML to generate virDomainLoaderDef & virDomainNvramDef.
This patch is used to interpret domain XML and store the Loader & Nvram's backing definitions as virStorageSource. It also identifies if input XML used old or new-style declaration. (This will later be used by the formatter).
3) Format the loader source appropriately.
If the initial XML used the old-style declaration as follows: <loader type='pflash'>/path/to/file</loader>
we format it as was read.
However, if it used new-style declaration: <loader type='pflash' backing='file'> <source file='path/to/file'/> </loader>
The formatter identifies that this is a new-style format and renders it likewise.
4) Plumb the loader source into generation of QEMU command line.
Given that nvram & loader elements can now be backed by a non-local source too, adjust all steps leading to generation of QEMU command line.
5) Fix the domain def inference logic to correctly account for network-backed pflash devices.
6) Bhyve: Fix command line generation to correctly pick up local loader path.
7) virt-aa-helper: Adjust references to loader & nvram elements to correctly parse the virStorageSource types.
8) Vbox: Adjust references to 'loader' and 'nvram' elements given that these are now represented by virStorageSourcePtr.
9)Xen: Adjust all references to loader & nvram elements given that they are now backed by virStorageSourcePtr --- src/bhyve/bhyve_command.c | 6 +- src/conf/domain_conf.c | 250 +++++++++++++++++++++++++++++++++++++--- src/conf/domain_conf.h | 11 +- src/qemu/qemu_cgroup.c | 13 ++- src/qemu/qemu_command.c | 21 +++- src/qemu/qemu_domain.c | 31 +++-- src/qemu/qemu_driver.c | 7 +- src/qemu/qemu_parse_command.c | 30 ++++- src/qemu/qemu_process.c | 54 ++++++--- src/security/security_dac.c | 6 +- src/security/security_selinux.c | 6 +- src/security/virt-aa-helper.c | 14 ++- src/vbox/vbox_common.c | 11 +- src/xenapi/xenapi_driver.c | 4 +- src/xenconfig/xen_sxpr.c | 19 +-- src/xenconfig/xen_xm.c | 9 +- 16 files changed, 409 insertions(+), 83 deletions(-)
The $SUBJ and commit message are just poorly formatted. But it pretty much shows that everything just got thrown into this one patch and you went from there. This series needs to progress a bit more "reasonably" to the desired goal. Consider this progression (with the following as patch 1 of the entire series): 1. Change path of loader to union: struct _virDomainLoaderDef { union { char *path; } loader; then compile/fix up references. 2. Create an accessor to loader.path - that way future consumers can just fetch the source path, e.g.: const char * virDomainLoaderDefGetLoaderPath(virDomainLoaderDefPtr loader) { return loader->loader.path; } Anything that accesses loader.path should change to use this via something like: const char *loaderPath; ... loaderPath = virDomainLoaderDefGetLoaderPath(xxx) ... Eventually this code will return either loader.path or loader.src->path since both FILE and NETWORK storage use src->path. 3. Add virStorageSourcePtr to the loader union and modify places in the code to use/read it. Update the domaincommon.rng, update formatdomain to describe usage of <source> for <loader>, add genericxml2xmltests for both "loader-source-file" and "loader-source-network" type formats for at least "type='rom'". You could add type='pflash' tests too... ... union { char *path; virStorageSourcePtr src; } loader; bool useLoaderSrc; ... The patch code to parse needs to be changed to follow more closely what virDomainDisk{BackingStore|DefMirror}Parse does... Alter ctxt locally to the passed @node (and restore at the end). It should also be able to use the union to do the magic, consider: loader->loader.path = (char *) xmlNodeGetContent(node); /* When not present, could return '' */ if (virStringIsEmpty(loader->loader.path)) VIR_FREE(loader->loader.path); /* See if we need to look for new style <source...> subelement */ if (!loader->loader.path) { xmlNodePtr source; if (!(source = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s" _("missing os loader source")); goto cleanup; } if (VIR_ALLOC(loader->loader.src) < 0) goto cleanup; if ((tmp = virXMLPropString(source, "file"))) loader->loader.src->type = VIR_STORAGE_TYPE_FILE; else if ((tmp = virXMLPropString(source, "protocol"))) loader->loader.src->type = VIR_STORAGE_TYPE_NETWORK; if (loader->loader.src->type == VIR_STORAGE_TYPE_NONE) { virReportError(VIR_ERR_XML_ERROR, _("unknown loader source type: '%s"), tmp); goto cleanup; } if (virDomainStorageSourceParse(source, ctxt, loader->loader.src, 0) < 0) goto cleanup; loader->useLoaderSrc = true; } Then do the similar set of changes for nvram... Although for this one - it's slightly trickier since <nvram> is optional... You'll probably also need to modify qemuDomainDefPostParse to not automagically generate an nvram.path if you're using <source>. Once the xml2xml portion is done, the next patch alters qemu_command, adds more tests, etc. Since you have generic xml2xml files, you probably could just create a link from the qemuxml2argvdata directory or create/use new files. Eventually it'd be nice for hte qemuxml2* code to be able to use the generic xml files, but that's outside this scope. BTW: I wouldn't bother with too many qemu_parse_command.c changes. Just do the minimum. That code is so far out of date it's going to take a solid effort to bring it back to today's options. In any case, there's a lot of changes to be made so it's really not worth going through each of the files in depth... I'll just point out the domain_conf.h changes. John [...]
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a78fdee..fb44a90 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1856,15 +1856,22 @@ typedef enum {
VIR_ENUM_DECL(virDomainLoader)
+struct _virStorageSource; +typedef struct _virStorageSource *virStorageSourcePtr; +
The above is not necessary.
typedef struct _virDomainLoaderDef virDomainLoaderDef; typedef virDomainLoaderDef *virDomainLoaderDefPtr; struct _virDomainLoaderDef { - char *path; + virStorageSourcePtr src;
Eventually: union { char *path; virStorageSourcePtr src; } loaderSource; bool useLoaderSrc;
int readonly; /* enum virTristateBool */ virDomainLoader type; int secure; /* enum virTristateBool */ - char *nvram; /* path to non-volatile RAM */ + virStorageSourcePtr nvram; /* path to non-voliatile RAM */
Eventually: union { char *nvram; /* path to non-volatile RAM */ virStorageSourcePtr src; } nvramSource; bool useNvramSrc;
char *templt; /* user override of path to master nvram */ + bool oldStyleLoader; /* is this an old-style XML formatting, + * ie, absolute path is directly specified? */ + bool oldStyleNvram; /* is this an old-style XML formatting, + * ie, absolute path is directly specified? */
The two are replaced by the use{Loader|Nvram}Src
};
You'll need to add API's for the fetch of the loader and nvram paths [...]

On Mon, Jun 4, 2018 at 6:24 PM, John Ferlan <jferlan@redhat.com> wrote:
Augment definition to include virStorageSourcePtr that more comprehensively describes the nature of backing element. Also include flags for annotating if input XML definition is old-style or new-style.
2) Parse domain XML to generate virDomainLoaderDef & virDomainNvramDef.
This patch is used to interpret domain XML and store the Loader & Nvram's backing definitions as virStorageSource. It also identifies if input XML used old or new-style declaration. (This will later be used by the formatter).
3) Format the loader source appropriately.
If the initial XML used the old-style declaration as follows: <loader type='pflash'>/path/to/file</loader>
we format it as was read.
However, if it used new-style declaration: <loader type='pflash' backing='file'> <source file='path/to/file'/> </loader>
The formatter identifies that this is a new-style format and renders it likewise.
4) Plumb the loader source into generation of QEMU command line.
Given that nvram & loader elements can now be backed by a non-local source too, adjust all steps leading to generation of QEMU command line.
5) Fix the domain def inference logic to correctly account for network-backed pflash devices.
6) Bhyve: Fix command line generation to correctly pick up local loader
On 05/21/2018 07:10 AM, Prerna Saxena wrote: path.
7) virt-aa-helper: Adjust references to loader & nvram elements to
correctly
parse the virStorageSource types.
8) Vbox: Adjust references to 'loader' and 'nvram' elements given that these are now represented by virStorageSourcePtr.
9)Xen: Adjust all references to loader & nvram elements given that they are now backed by virStorageSourcePtr --- src/bhyve/bhyve_command.c | 6 +- src/conf/domain_conf.c | 250 ++++++++++++++++++++++++++++++ +++++++--- src/conf/domain_conf.h | 11 +- src/qemu/qemu_cgroup.c | 13 ++- src/qemu/qemu_command.c | 21 +++- src/qemu/qemu_domain.c | 31 +++-- src/qemu/qemu_driver.c | 7 +- src/qemu/qemu_parse_command.c | 30 ++++- src/qemu/qemu_process.c | 54 ++++++--- src/security/security_dac.c | 6 +- src/security/security_selinux.c | 6 +- src/security/virt-aa-helper.c | 14 ++- src/vbox/vbox_common.c | 11 +- src/xenapi/xenapi_driver.c | 4 +- src/xenconfig/xen_sxpr.c | 19 +-- src/xenconfig/xen_xm.c | 9 +- 16 files changed, 409 insertions(+), 83 deletions(-)
The $SUBJ and commit message are just poorly formatted.
But it pretty much shows that everything just got thrown into this one patch and you went from there.
This series needs to progress a bit more "reasonably" to the desired goal. Consider this progression (with the following as patch 1 of the entire series):
1. Change path of loader to union:
struct _virDomainLoaderDef { union { char *path; } loader;
then compile/fix up references.
2. Create an accessor to loader.path - that way future consumers can just fetch the source path, e.g.:
const char * virDomainLoaderDefGetLoaderPath(virDomainLoaderDefPtr loader) { return loader->loader.path; }
Anything that accesses loader.path should change to use this via something like:
const char *loaderPath; ... loaderPath = virDomainLoaderDefGetLoaderPath(xxx) ...
Eventually this code will return either loader.path or loader.src->path since both FILE and NETWORK storage use src->path.
3. Add virStorageSourcePtr to the loader union and modify places in the code to use/read it. Update the domaincommon.rng, update formatdomain to describe usage of <source> for <loader>, add genericxml2xmltests for both "loader-source-file" and "loader-source-network" type formats for at least "type='rom'". You could add type='pflash' tests too...
... union { char *path; virStorageSourcePtr src; } loader; bool useLoaderSrc; ...
The patch code to parse needs to be changed to follow more closely what virDomainDisk{BackingStore|DefMirror}Parse does... Alter ctxt locally to the passed @node (and restore at the end). It should also be able to use the union to do the magic, consider:
loader->loader.path = (char *) xmlNodeGetContent(node);
/* When not present, could return '' */ if (virStringIsEmpty(loader->loader.path)) VIR_FREE(loader->loader.path);
/* See if we need to look for new style <source...> subelement */ if (!loader->loader.path) { xmlNodePtr source;
if (!(source = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s" _("missing os loader source")); goto cleanup; }
if (VIR_ALLOC(loader->loader.src) < 0) goto cleanup;
if ((tmp = virXMLPropString(source, "file"))) loader->loader.src->type = VIR_STORAGE_TYPE_FILE; else if ((tmp = virXMLPropString(source, "protocol"))) loader->loader.src->type = VIR_STORAGE_TYPE_NETWORK;
if (loader->loader.src->type == VIR_STORAGE_TYPE_NONE) { virReportError(VIR_ERR_XML_ERROR, _("unknown loader source type: '%s"), tmp); goto cleanup; }
if (virDomainStorageSourceParse(source, ctxt, loader->loader.src, 0) < 0) goto cleanup;
loader->useLoaderSrc = true; }
Then do the similar set of changes for nvram... Although for this one - it's slightly trickier since <nvram> is optional... You'll probably also need to modify qemuDomainDefPostParse to not automagically generate an nvram.path if you're using <source>.
Once the xml2xml portion is done, the next patch alters qemu_command, adds more tests, etc. Since you have generic xml2xml files, you probably could just create a link from the qemuxml2argvdata directory or create/use new files. Eventually it'd be nice for hte qemuxml2* code to be able to use the generic xml files, but that's outside this scope.
BTW: I wouldn't bother with too many qemu_parse_command.c changes. Just do the minimum. That code is so far out of date it's going to take a solid effort to bring it back to today's options.
In any case, there's a lot of changes to be made so it's really not worth going through each of the files in depth... I'll just point out the domain_conf.h changes.
Thanks John for the elaborate review. I will start in this direction and post the next series asap.

Signed-off-by: Prerna Saxena <saxenap.ltc@gmail.com> --- tests/qemuxml2argvdata/bios-nvram-network.args | 31 +++++++++++++++++++ tests/qemuxml2argvdata/bios-nvram-network.xml | 42 ++++++++++++++++++++++++++ tests/qemuxml2argvtest.c | 1 + 3 files changed, 74 insertions(+) create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.args create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.xml diff --git a/tests/qemuxml2argvdata/bios-nvram-network.args b/tests/qemuxml2argvdata/bios-nvram-network.args new file mode 100644 index 0000000..2f98fe6 --- /dev/null +++ b/tests/qemuxml2argvdata/bios-nvram-network.args @@ -0,0 +1,31 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-x86_64 \ +-name test-bios \ +-S \ +-machine pc,accel=tcg,usb=off,dump-guest-core=off \ +-drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,\ +readonly=on \ +-drive file=iscsi://example.org:6000/iqn.1992-01.com.example/0,if=pflash,\ +format=raw,unit=1 \ +-m 1024 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid 362d1fc1-df7d-193e-5c18-49a71bd1da66 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-test-bios/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot order=c,menu=on \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/bios-nvram-network.xml b/tests/qemuxml2argvdata/bios-nvram-network.xml new file mode 100644 index 0000000..d0b04ea --- /dev/null +++ b/tests/qemuxml2argvdata/bios-nvram-network.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>test-bios</name> + <uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>1048576</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <loader readonly='yes' type='pflash' backing='file'> + <source file='/usr/share/OVMF/OVMF_CODE.fd'/> + </loader> + <nvram backing='network'> + <source protocol='iscsi' name='iqn.1992-01.com.example/0'> + <host name='example.org' port='6000'/> + </source> + </nvram> + <boot dev='hd'/> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='tablet' bus='usb'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 78454ac..e49186c 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -853,6 +853,7 @@ mymain(void) QEMU_CAPS_DEVICE_ISA_SERIAL, QEMU_CAPS_SGA); DO_TEST("bios-nvram", NONE); + DO_TEST("bios-nvram-network", NONE); DO_TEST("bios-nvram-secure", QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, QEMU_CAPS_DEVICE_PCI_BRIDGE, -- 1.8.1.2

Signed-off-by: Prerna Saxena <saxenap.ltc@gmail.com> --- docs/formatdomain.html.in | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0d0fd3b..2ba2761 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -112,6 +112,20 @@ </os> ...</pre> + <p>Where loader/NVRAM can also be described as:</p> + +<pre> +... + <loader readonly='yes' secure='no' type='rom' backing='file'> + <source file='/usr/share/OVMF/OVMF_CODE.fd'/> + </loader> + <nvram backing='network' template='/usr/share/OVMF/OVMF_VARS.fd'> + <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/2'> + <host name='example.com' port='3260'/> + </source> + </nvram> +...</pre> + <dl> <dt><code>type</code></dt> <dd>The content of the <code>type</code> element specifies the @@ -143,7 +157,15 @@ <code>pflash</code>. Moreover, some firmwares may implement the Secure boot feature. Attribute <code>secure</code> can be used then to control it. - <span class="since">Since 2.1.0</span></dd> + <span class="since">Since 2.1.0</span><span class="since">Since 4.5.0</span> + Loader element can also be specified as a remote disk. The attribute + <code>backing</code> (accepted values are + <code>file</code> and <code>network</code>)can be used to represent + local absolute paths as well as network-backed disks. The details of + backing file may be specified as <a href="#elementsDiskSource">storage source</a> + Allowed backing type <code>file</code> replaces the old + specification and extends to all hypervisors that supported it, while + type <code>network</code> is only supported by QEMU.</dd> <dt><code>nvram</code></dt> <dd>Some UEFI firmwares may want to use a non-volatile memory to store some variables. In the host, this is represented as a file and the @@ -154,7 +176,15 @@ from the config file. Note, that for transient domains 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 be - persistent). <span class="since">Since 1.2.8</span></dd> + persistent). <span class="since">Since 1.2.8</span> + <span class="since">Since 4.5.0</span>, attribute + <code>backing</code>(accepted values <code>file</code> + and <code>network</code>)can be used to specify a + <a href='#elementsDiskSource'>storage source</a> + of type file or network. The value <code>file</code>describes + absolute NVRAM paths and extends the present specification + for all hypervisors that support this, while + <code>network</code> applies only to QEMU.</dd> <dt><code>boot</code></dt> <dd>The <code>dev</code> attribute takes one of the values "fd", "hd", "cdrom" or "network" and is used to specify the next boot device @@ -2726,7 +2756,7 @@ </dd> </dl> </dd> - <dt><code>source</code></dt> + <h5><a id="elementsDiskSource">Disk source</a></h5> <dd>Representation of the disk <code>source</code> depends on the disk <code>type</code> attribute value as follows: <dl> -- 1.8.1.2

On Mon, May 21, 2018 at 4:40 PM, Prerna Saxena <saxenap.ltc@gmail.com> wrote:
Libvirt domain XML allows only local filepaths to specify a loader element or its matching NVRAM. Given that VMs may themselves move across hypervisor hosts, it should be possible to allocate loaders/NVRAM disks on network storage for uninterrupted access.
This series extends the loader & NVRAM disk elements to be described as virStorageSource* elements, as discussed in : https://www.redhat.com/archives/libvir-list/2018-March/msg01721.html
Sample XML with new annotation:
<loader readonly='yes' type='pflash' backing='file'> <source file='/usr/share/OVMF/OVMF_CODE.fd'/> </loader> <nvram backing='network'> <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'> <host name='example.com' port='6000'/> </source> </nvram>
References: ---------- v0/ Proposal: https://www.redhat.com/archives/libvir-list/2018- March/msg01721.html.v1 v1: https://www.redhat.com/archives/libvir-list/2018-April/msg02024.html v2: https://www.redhat.com/archives/libvir-list/2018-May/msg00948.html
Changelog: --------- Changes since v2: - Consolidated patches with related data structures to avoid build breakage. - Passes make check & make syntax-check.
Prerna Saxena (4): Schema: Introduce XML schema for network-backed loader and nvram elements. Loader: Add a more elaborate definition. Test: Add a test snippet to evaluate command line generation for loader/nvram specified via virStorageSource Documentation: Add a blurb for the newly added XML snippets for loader and nvram.
docs/formatdomain.html.in | 36 +++- docs/schemas/domaincommon.rng | 108 +++++++++-- src/bhyve/bhyve_command.c | 6 +- src/conf/domain_conf.c | 250 +++++++++++++++++++++++-- src/conf/domain_conf.h | 11 +- src/qemu/qemu_cgroup.c | 13 +- src/qemu/qemu_command.c | 21 ++- src/qemu/qemu_domain.c | 31 ++- src/qemu/qemu_driver.c | 7 +- src/qemu/qemu_parse_command.c | 30 ++- src/qemu/qemu_process.c | 54 ++++-- src/security/security_dac.c | 6 +- src/security/security_selinux.c | 6 +- src/security/virt-aa-helper.c | 14 +- src/vbox/vbox_common.c | 11 +- src/xenapi/xenapi_driver.c | 4 +- src/xenconfig/xen_sxpr.c | 19 +- src/xenconfig/xen_xm.c | 9 +- tests/qemuxml2argvdata/bios-nvram-network.args | 31 +++ tests/qemuxml2argvdata/bios-nvram-network.xml | 42 +++++ tests/qemuxml2argvtest.c | 1 + 21 files changed, 606 insertions(+), 104 deletions(-) create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.args create mode 100644 tests/qemuxml2argvdata/bios-nvram-network.xml
--
Just FYI, I will be on vacation starting tomorrow until June 4. I will address all review comments as soon as I'm back. Regards, Prerna
participants (3)
-
John Ferlan
-
Prerna
-
Prerna Saxena