[libvirt] [PATCH 0/5] Explicit boot device ordering

Currently, boot order can be specified per device class but there is no way to specify exact disk/NIC device to boot from. This patchset fixes that. There were two options suggested for how this should be modeled in domain XML: 1) <os> <boot target='net1'/> <boot target='net0'/> <boot target='hdc'/> <boot target='hdb'/> <os> Where target attributes would match /domain/devices/*/target@dev 2) Adding <boot order='n'/> elements into appropriate device elements. In addition to the two options Rich suggested a more compact variant of option 1. Per former discussion this patchset implements the second option. Jiri Denemark (5): conf: Move boot parsing into a separate function Introduce per-device boot element qemu: Refactor qemuCapsParsePCIDeviceStrs using virCommand qemu: Support per-device boot ordering tests: Add tests for per-device boot elements docs/formatcaps.html.in | 1 + docs/formatdomain.html.in | 41 +++++- docs/schemas/domain.rng | 20 +++- src/conf/capabilities.c | 3 +- src/conf/domain_conf.c | 148 ++++++++++++++------ src/conf/domain_conf.h | 2 + src/qemu/qemu_capabilities.c | 82 +++++++---- src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 15 ++- src/qemu/qemu_command.h | 6 +- src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 8 +- .../qemuxml2argvdata/qemuxml2argv-boot-order.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml | 44 ++++++ tests/qemuxml2argvtest.c | 2 + tests/qemuxml2xmltest.c | 1 + 16 files changed, 291 insertions(+), 86 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-boot-order.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml -- 1.7.4.rc1

--- src/conf/domain_conf.c | 99 ++++++++++++++++++++++++++++-------------------- 1 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b4df38c..a2584b8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4582,6 +4582,61 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def, return retemu; } +static int +virDomainDefParseBootXML(xmlXPathContextPtr ctxt, + virDomainDefPtr def) +{ + xmlNodePtr *nodes = NULL; + int i, n; + char *bootstr; + int ret = -1; + + /* analysis of the boot devices */ + if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract boot device")); + goto cleanup; + } + + for (i = 0 ; i < n && i < VIR_DOMAIN_BOOT_LAST ; i++) { + int val; + char *dev = virXMLPropString(nodes[i], "dev"); + if (!dev) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing boot device")); + goto cleanup; + } + if ((val = virDomainBootTypeFromString(dev)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown boot device '%s'"), + dev); + VIR_FREE(dev); + goto cleanup; + } + VIR_FREE(dev); + def->os.bootDevs[def->os.nBootDevs++] = val; + } + if (def->os.nBootDevs == 0) { + def->os.nBootDevs = 1; + def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; + } + + bootstr = virXPathString("string(./os/bootmenu[1]/@enable)", ctxt); + if (bootstr) { + if (STREQ(bootstr, "yes")) + def->os.bootmenu = VIR_DOMAIN_BOOT_MENU_ENABLED; + else + def->os.bootmenu = VIR_DOMAIN_BOOT_MENU_DISABLED; + VIR_FREE(bootstr); + } + + ret = 0; + +cleanup: + VIR_FREE(nodes); + return ret; +} + static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, xmlDocPtr xml, xmlNodePtr root, @@ -4911,47 +4966,9 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, def->os.loader = virXPathString("string(./os/loader[1])", ctxt); } - if (STREQ(def->os.type, "hvm")) { - char *bootstr; - - /* analysis of the boot devices */ - if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot extract boot device")); - goto error; - } - for (i = 0 ; i < n && i < VIR_DOMAIN_BOOT_LAST ; i++) { - int val; - char *dev = virXMLPropString(nodes[i], "dev"); - if (!dev) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("missing boot device")); - goto error; - } - if ((val = virDomainBootTypeFromString(dev)) < 0) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown boot device '%s'"), - dev); - VIR_FREE(dev); - goto error; - } - VIR_FREE(dev); - def->os.bootDevs[def->os.nBootDevs++] = val; - } - if (def->os.nBootDevs == 0) { - def->os.nBootDevs = 1; - def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; - } - VIR_FREE(nodes); - - bootstr = virXPathString("string(./os/bootmenu[1]/@enable)", ctxt); - if (bootstr) { - if (STREQ(bootstr, "yes")) - def->os.bootmenu = VIR_DOMAIN_BOOT_MENU_ENABLED; - else - def->os.bootmenu = VIR_DOMAIN_BOOT_MENU_DISABLED; - VIR_FREE(bootstr); - } + if (STREQ(def->os.type, "hvm") && + virDomainDefParseBootXML(ctxt, def) < 0) { + goto error; } def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); -- 1.7.4.rc1

Currently, boot order can be specified per device class but there is no way to specify exact disk/NIC device to boot from. This patch adds <boot order='N'/> element which can be used inside <disk/> and <interface/>. This is incompatible with the older os/boot element. Since not all hypervisors support per-device boot specification, new deviceboot flag is included in capabilities XML for hypervisors which understand the new boot element. Presence of the flag allows (but doesn't require) users to use the new style boot order specification. --- docs/formatcaps.html.in | 1 + docs/formatdomain.html.in | 41 ++++++++++++++++++++++++++++++++++- docs/schemas/domain.rng | 20 +++++++++++++++- src/conf/capabilities.c | 3 +- src/conf/domain_conf.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 2 + 6 files changed, 112 insertions(+), 6 deletions(-) diff --git a/docs/formatcaps.html.in b/docs/formatcaps.html.in index dcbf35a..a4297ce 100644 --- a/docs/formatcaps.html.in +++ b/docs/formatcaps.html.in @@ -55,6 +55,7 @@ BIOS you will see</p> </arch> <features> <cpuselection/> + <deviceboot/> </features> </guest></span> ... diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 99068ad..8a8deab 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -103,8 +103,11 @@ <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 to consider. The <code>boot</code> element can be repeated multiple - times to setup a priority list of boot devices to try in turn. - <span class="since">Since 0.1.3</span> + times to setup a priority list of boot devices to try in turn. The + <code>boot</code> element cannot be used if per-device boot elements + are used (see <a href="#elementsDisks">disks</a> and + <a href="#elementsNICS">network interfaces</a> sections below. + <span class="since">Since 0.1.3, per-device boot since 0.8.8</span> </dd> <dt><code>bootmenu</code></dt> <dd> Whether or not to enable an interactive boot menu prompt on guest @@ -620,6 +623,7 @@ <driver name="tap" type="aio" cache="default"/> <source file='/var/lib/xen/images/fv0'/> <target dev='hda' bus='ide'/> + <boot order='2'/> <encryption type='...'> ... </encryption> @@ -635,6 +639,7 @@ <host name="hostname" port="7000"/> </source> <target dev="hdb" bus="ide"/> + <boot order='1'/> </disk> </devices> ...</pre> @@ -687,6 +692,14 @@ controls the cache mechanism, possible values are "default", "none", "writethrough" and "writeback". <span class="since">Since 0.1.8</span> </dd> + <dt><code>boot</code></dt> + <dd>Specifies that the disk is bootable. The <code>order</code> + attribute determines the order in which devices will be tried during + boot sequence. The per-device <code>boot</code> elements cannot be + used together with general boot elements in + <a href="#elementsOSBIOS">BIOS bootloader</a> section. + <span class="since">Since 0.8.8</span> + </dd> <dt><code>encryption</code></dt> <dd>If present, specifies how the volume is encrypted. See the <a href="formatstorageencryption.html">Storage Encryption</a> page @@ -808,6 +821,7 @@ <source bridge='xenbr0'/> <mac address='00:16:3e:5d:c7:9e'/> <script path='vif-bridge'/> + <boot order='1'/> </interface> </devices> ...</pre> @@ -1085,6 +1099,29 @@ qemu-kvm -net nic,model=? /dev/null ignored. </p> + <h5><a name="elementsNICSBoot">Specifying boot order</a></h5> + +<pre> + ... + <devices> + <interface type='network'> + <source network='default'/> + <target dev='vnet1'/> + <b><boot order='1'/></b> + </interface> + </devices> + ...</pre> + + <p> + For hypervisors which support this, you can set exact NIC which should + be used for network boot. The <code>order</code> attribute determines + the order in which devices will be tried during boot sequence. The + per-device <code>boot</code> elements cannot be used together with + general boot elements in + <a href="#elementsOSBIOS">BIOS bootloader</a> section. + <span class="since">Since 0.8.8</span> + </p> + <h4><a name="elementsInput">Input devices</a></h4> <p> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index a524e4b..a8a0073 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -121,9 +121,9 @@ </optional> <choice> <ref name="osbootkernel"/> - <oneOrMore> + <zeroOrMore> <ref name="osbootdev"/> - </oneOrMore> + </zeroOrMore> </choice> <optional> <element name="bootmenu"> @@ -526,6 +526,9 @@ </optional> <ref name="target"/> <optional> + <ref name="deviceBoot"/> + </optional> + <optional> <element name="readonly"> <empty/> </element> @@ -963,6 +966,7 @@ - the IP address bound to the interface - the name of the script used to set up the binding - the target device used + - boot order --> <define name="interface-options"> <interleave> @@ -1012,6 +1016,9 @@ <ref name="filterref-node-attributes"/> </element> </optional> + <optional> + <ref name="deviceBoot"/> + </optional> </interleave> </define> <define name="virtualPortProfile"> @@ -1980,6 +1987,15 @@ </optional> </define> + <define name="deviceBoot"> + <element name="boot"> + <attribute name="order"> + <ref name="positiveInteger"/> + </attribute> + <empty/> + </element> + </define> + <!-- Optional hypervisor extensions in their own namespace: QEmu diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 99d5a56..cb9113c 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -782,7 +782,8 @@ virCapabilitiesFormatXML(virCapsPtr caps) if (STREQ(caps->guests[i]->features[j]->name, "pae") || STREQ(caps->guests[i]->features[j]->name, "nonpae") || STREQ(caps->guests[i]->features[j]->name, "ia64_be") || - STREQ(caps->guests[i]->features[j]->name, "cpuselection")) { + STREQ(caps->guests[i]->features[j]->name, "cpuselection") || + STREQ(caps->guests[i]->features[j]->name, "deviceboot")) { virBufferVSprintf(&xml, " <%s/>\n", caps->guests[i]->features[j]->name); } else { diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a2584b8..3a1aee8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1510,6 +1510,35 @@ cleanup: } static int +virDomainDeviceBootParseXML(xmlNodePtr node, + int *bootIndex) +{ + char *order; + int boot; + int ret = -1; + + order = virXMLPropString(node, "order"); + if (!order) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing boot order attribute")); + goto cleanup; + } else if (virStrToLong_i(order, NULL, 10, &boot) < 0 || + boot <= 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("incorrect boot order '%s', expecting positive integer"), + order); + goto cleanup; + } + + *bootIndex = boot; + ret = 0; + +cleanup: + VIR_FREE(order); + return ret; +} + +static int virDomainParseLegacyDeviceAddress(char *devaddr, virDomainDevicePCIAddressPtr pci) { @@ -1740,6 +1769,9 @@ virDomainDiskDefParseXML(virCapsPtr caps, } else if ((serial == NULL) && (xmlStrEqual(cur->name, BAD_CAST "serial"))) { serial = (char *)xmlNodeGetContent(cur); + } else if (xmlStrEqual(cur->name, BAD_CAST "boot")) { + if (virDomainDeviceBootParseXML(cur, &def->bootIndex)) + goto error; } } cur = cur->next; @@ -2379,6 +2411,9 @@ virDomainNetDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "state")) { /* Legacy back-compat. Don't add any more attributes here */ devaddr = virXMLPropString(cur, "devaddr"); + } else if (xmlStrEqual(cur->name, BAD_CAST "boot")) { + if (virDomainDeviceBootParseXML(cur, &def->bootIndex)) + goto error; } } cur = cur->next; @@ -4590,6 +4625,9 @@ virDomainDefParseBootXML(xmlXPathContextPtr ctxt, int i, n; char *bootstr; int ret = -1; + bool deviceBoot; + + deviceBoot = virXPathBoolean("boolean(./devices/*/boot)", ctxt) > 0; /* analysis of the boot devices */ if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) { @@ -4598,6 +4636,13 @@ virDomainDefParseBootXML(xmlXPathContextPtr ctxt, goto cleanup; } + if (n > 0 && deviceBoot) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("per-device boot elements cannot be used" + " together with os/boot elements")); + goto cleanup; + } + for (i = 0 ; i < n && i < VIR_DOMAIN_BOOT_LAST ; i++) { int val; char *dev = virXMLPropString(nodes[i], "dev"); @@ -4616,7 +4661,7 @@ virDomainDefParseBootXML(xmlXPathContextPtr ctxt, VIR_FREE(dev); def->os.bootDevs[def->os.nBootDevs++] = val; } - if (def->os.nBootDevs == 0) { + if (def->os.nBootDevs == 0 && !deviceBoot) { def->os.nBootDevs = 1; def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; } @@ -6067,6 +6112,8 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferVSprintf(buf, " <target dev='%s' bus='%s'/>\n", def->dst, bus); + if (def->bootIndex) + virBufferVSprintf(buf, " <boot order='%d'/>\n", def->bootIndex); if (def->readonly) virBufferAddLit(buf, " <readonly/>\n"); if (def->shared) @@ -6306,6 +6353,8 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferVSprintf(buf, ">\n%s </filterref>\n", attrs); VIR_FREE(attrs); } + if (def->bootIndex) + virBufferVSprintf(buf, " <boot order='%d'/>\n", def->bootIndex); if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) return -1; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a459a22..77460da 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -197,6 +197,7 @@ struct _virDomainDiskDef { char *serial; int cachemode; int error_policy; + int bootIndex; unsigned int readonly : 1; unsigned int shared : 1; virDomainDeviceInfo info; @@ -338,6 +339,7 @@ struct _virDomainNetDef { } direct; } data; char *ifname; + int bootIndex; virDomainDeviceInfo info; char *filter; virNWFilterHashTablePtr filterparams; -- 1.7.4.rc1

--- src/qemu/qemu_capabilities.c | 58 +++++++++++++++++++---------------------- 1 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 3d10b42..0fb6ec9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1087,48 +1087,44 @@ fail: return -1; } + +static char * +qemuCapsExtractDeviceProps(const char *qemu, + const char *device) +{ + virCommandPtr cmd = virCommandNew(qemu); + char *output = NULL; + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "%s,?", device); + virCommandAddEnvPassCommon(cmd); + virCommandClearCaps(cmd); + virCommandSetErrorBuffer(cmd, &output); + + if (virCommandRun(cmd, NULL) < 0) { + VIR_ERROR(_("Unable to read %s %s device output"), qemu, device); + VIR_FREE(output); + } + + virCommandFree(cmd); + + return output; +} + + static void qemuCapsParsePCIDeviceStrs(const char *qemu, unsigned long long *flags) { - const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - pid_t child; - int status; - int newstderr = -1; + char *pciassign = qemuCapsExtractDeviceProps(qemu, "pci-assign"); - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0) + if (!pciassign) return; - char *pciassign = NULL; - enum { MAX_PCI_OUTPUT_SIZE = 1024*4 }; - int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign); - if (len < 0) { - virReportSystemError(errno, - _("Unable to read %s pci-assign device output"), - qemu); - goto cleanup; - } - if (strstr(pciassign, "pci-assign.configfd")) *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD; -cleanup: VIR_FREE(pciassign); - VIR_FORCE_CLOSE(newstderr); -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - } - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } } int qemuCapsExtractVersionInfo(const char *qemu, -- 1.7.4.rc1

On 01/13/2011 06:04 AM, Jiri Denemark wrote:
--- src/qemu/qemu_capabilities.c | 58 +++++++++++++++++++---------------------- 1 files changed, 27 insertions(+), 31 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 3d10b42..0fb6ec9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1087,48 +1087,44 @@ fail: return -1; }
+ +static char * +qemuCapsExtractDeviceProps(const char *qemu, + const char *device) +{ + virCommandPtr cmd = virCommandNew(qemu); + char *output = NULL; + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "%s,?", device);
Phooey - I have a patch in my smartcard series that conflicts with this. But I like mine better - I added testsuite coverage of device parsing, and have only 1 qemu run for all devices rather than 1 run per device. I guess I'd better clean it up and post right away, to compare our two approaches. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

+ +static char * +qemuCapsExtractDeviceProps(const char *qemu, + const char *device) +{ + virCommandPtr cmd = virCommandNew(qemu); + char *output = NULL; + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "%s,?", device);
Phooey - I have a patch in my smartcard series that conflicts with this. But I like mine better - I added testsuite coverage of device parsing, and have only 1 qemu run for all devices rather than 1 run per device. I guess I'd better clean it up and post right away, to compare our two approaches.
I went the minimal-effort way since it doesn't matter much now when we only check for two device types. And the better approach could have been done as another patch in the future. But since you already have it, it'd be better to push that first. You can also take just the most important part of it and do the rest in a separate patch later to minimize it's time consumption now. Jirka

Support for this is included in qemu and seabios from git. --- src/qemu/qemu_capabilities.c | 28 +++++++++++++++++++++++++++- src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 15 +++++++++++---- src/qemu/qemu_command.h | 6 ++++-- src/qemu/qemu_driver.c | 2 ++ src/qemu/qemu_hotplug.c | 8 ++++---- 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 0fb6ec9..6558d6e 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -486,6 +486,7 @@ qemuCapsInitGuest(virCapsPtr caps, int nmachines = 0; struct stat st; unsigned int ncpus; + unsigned long long qemuCmdFlags; int ret = -1; /* Check for existance of base emulator, or alternate base @@ -601,6 +602,11 @@ qemuCapsInitGuest(virCapsPtr caps, !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) goto error; + if (qemuCapsExtractVersionInfo(binary, NULL, &qemuCmdFlags) < 0 || + ((qemuCmdFlags & QEMUD_CMD_FLAG_BOOTINDEX) && + !virCapabilitiesAddGuestFeature(guest, "deviceboot", 1, 0))) + goto error; + if (hvm) { if (virCapabilitiesAddGuestDomain(guest, "qemu", @@ -1127,6 +1133,24 @@ qemuCapsParsePCIDeviceStrs(const char *qemu, VIR_FREE(pciassign); } +static void +qemuCapsParseBlockDeviceStrs(const char *qemu, + unsigned long long *flags) +{ + char *block = qemuCapsExtractDeviceProps(qemu, "virtio-blk-pci"); + + if (!block) + return; + + if (strstr(block, "virtio-blk-pci.bootindex")) { + VIR_DEBUG0("je tam"); + *flags |= QEMUD_CMD_FLAG_BOOTINDEX; + } + + VIR_FREE(block); +} + + int qemuCapsExtractVersionInfo(const char *qemu, unsigned int *retversion, unsigned long long *retflags) @@ -1170,8 +1194,10 @@ int qemuCapsExtractVersionInfo(const char *qemu, &version, &is_kvm, &kvm_version) == -1) goto cleanup2; - if (flags & QEMUD_CMD_FLAG_DEVICE) + if (flags & QEMUD_CMD_FLAG_DEVICE) { qemuCapsParsePCIDeviceStrs(qemu, &flags); + qemuCapsParseBlockDeviceStrs(qemu, &flags); + } if (retversion) *retversion = version; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index ee648f0..f0ffbc8 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -83,6 +83,7 @@ enum qemuCapsFlags { QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ QEMUD_CMD_FLAG_VGA_NONE = (1LL << 47), /* The 'none' arg for '-vga' */ QEMUD_CMD_FLAG_MIGRATE_QEMU_FD = (1LL << 48), /* -incoming fd:n */ + QEMUD_CMD_FLAG_BOOTINDEX = (1LL << 49), /* -device bootindex property */ }; virCapsPtr qemuCapsInit(virCapsPtr old_caps); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 86c5bb5..c19ef36 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1308,7 +1308,8 @@ error: char * -qemuBuildDriveDevStr(virDomainDiskDefPtr disk) +qemuBuildDriveDevStr(virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags) { virBuffer opt = VIR_BUFFER_INITIALIZER; const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); @@ -1348,6 +1349,8 @@ qemuBuildDriveDevStr(virDomainDiskDefPtr disk) } virBufferVSprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); virBufferVSprintf(&opt, ",id=%s", disk->info.alias); + if (disk->bootIndex && (qemuCmdFlags & QEMUD_CMD_FLAG_BOOTINDEX)) + virBufferVSprintf(&opt, ",bootindex=%d", disk->bootIndex); if (virBufferError(&opt)) { virReportOOMError(); @@ -1504,7 +1507,9 @@ qemuBuildNicStr(virDomainNetDefPtr net, char * -qemuBuildNicDevStr(virDomainNetDefPtr net, int vlan) +qemuBuildNicDevStr(virDomainNetDefPtr net, + int vlan, + unsigned long long qemuCmdFlags) { virBuffer buf = VIR_BUFFER_INITIALIZER; const char *nic; @@ -1529,6 +1534,8 @@ qemuBuildNicDevStr(virDomainNetDefPtr net, int vlan) net->mac[4], net->mac[5]); if (qemuBuildDeviceAddressStr(&buf, &net->info) < 0) goto error; + if (net->bootIndex && (qemuCmdFlags & QEMUD_CMD_FLAG_BOOTINDEX)) + virBufferVSprintf(&buf, ",bootindex=%d", net->bootIndex); if (virBufferError(&buf)) { virReportOOMError(); @@ -3085,7 +3092,7 @@ qemuBuildCommandLine(virConnectPtr conn, } else { virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildDriveDevStr(disk))) + if (!(optstr = qemuBuildDriveDevStr(disk, qemuCmdFlags))) goto error; virCommandAddArg(cmd, optstr); VIR_FREE(optstr); @@ -3306,7 +3313,7 @@ qemuBuildCommandLine(virConnectPtr conn, } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { virCommandAddArg(cmd, "-device"); - if (!(nic = qemuBuildNicDevStr(net, vlan))) + if (!(nic = qemuBuildNicDevStr(net, vlan, qemuCmdFlags))) goto error; virCommandAddArg(cmd, nic); VIR_FREE(nic); diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 4c42a10..9e9d5f5 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -62,7 +62,8 @@ char * qemuBuildNicStr(virDomainNetDefPtr net, /* Current, best practice */ char * qemuBuildNicDevStr(virDomainNetDefPtr net, - int vlan); + int vlan, + unsigned long long qemuCmdFlags); char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, unsigned long long qemuCmdFlags); @@ -75,7 +76,8 @@ char *qemuBuildFSStr(virDomainFSDefPtr fs, unsigned long long qemuCmdFlags); /* Current, best practice */ -char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk); +char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags); char * qemuBuildFSDevStr(virDomainFSDefPtr fs); /* Current, best practice */ char * qemuBuildControllerDevStr(virDomainControllerDefPtr def); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9eb9cd5..cfb56bc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6054,6 +6054,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, */ for (i = 0 ; i < def->nnets ; i++) { virDomainNetDefPtr net = def->nets[i]; + int bootIndex = net->bootIndex; if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { VIR_FREE(net->data.network.name); @@ -6076,6 +6077,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, net->data.ethernet.script = script; net->data.ethernet.ipaddr = ipaddr; } + net->bootIndex = bootIndex; } for (i = 0 ; i < def->ngraphics ; i++) { if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1dc036c..3b4a673 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -169,7 +169,7 @@ int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver, if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) goto error; - if (!(devstr = qemuBuildDriveDevStr(disk))) + if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags))) goto error; } @@ -380,7 +380,7 @@ int qemuDomainAttachSCSIDisk(struct qemud_driver *driver, if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) goto error; - if (!(devstr = qemuBuildDriveDevStr(disk))) + if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags))) goto error; } @@ -493,7 +493,7 @@ int qemuDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, goto error; if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) goto error; - if (!(devstr = qemuBuildDriveDevStr(disk))) + if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags))) goto error; } @@ -675,7 +675,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (!(nicstr = qemuBuildNicDevStr(net, vlan))) + if (!(nicstr = qemuBuildNicDevStr(net, vlan, qemuCmdFlags))) goto try_remove; } else { if (!(nicstr = qemuBuildNicStr(net, NULL, vlan))) -- 1.7.4.rc1

--- .../qemuxml2argvdata/qemuxml2argv-boot-order.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml | 44 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 2 + tests/qemuxml2xmltest.c | 1 + 4 files changed, 48 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-boot-order.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml diff --git a/tests/qemuxml2argvdata/qemuxml2argv-boot-order.args b/tests/qemuxml2argvdata/qemuxml2argv-boot-order.args new file mode 100644 index 0000000..7b4a071 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-boot-order.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -drive file=/dev/HostVG/QEMUGuest1,if=none,id=drive-ide0-0-0 -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 -drive file=sheepdog:example.org:6000:image,if=none,id=drive-virtio-disk0 -device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=3 -drive file=/root/boot.iso,if=none,media=cdrom,id=drive-ide0-1-0 -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0,bootindex=1 -device virtio-net-pci,vlan=0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x2,bootindex=2 -net user,vlan=0,name=hostnet0 -usb -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml b/tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml new file mode 100644 index 0000000..3a272de --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-boot-order.xml @@ -0,0 +1,44 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <disk type='network' device='disk'> + <driver name='qemu' type='raw'/> + <source protocol='sheepdog' name='image'> + <host name='example.org' port='6000'/> + </source> + <target dev='vda' bus='virtio'/> + <boot order='3'/> + </disk> + <disk type='file' device='cdrom'> + <source file='/root/boot.iso'/> + <target dev='hdc' bus='ide'/> + <boot order='1'/> + <readonly/> + <address type='drive' controller='0' bus='1' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <model type='virtio'/> + <boot order='2'/> + </interface> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 6760f67..a0e4481 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -252,6 +252,8 @@ mymain(int argc, char **argv) DO_TEST("boot-floppy", 0, false); DO_TEST("boot-multi", QEMUD_CMD_FLAG_BOOT_MENU, false); DO_TEST("boot-menu-disable", QEMUD_CMD_FLAG_BOOT_MENU, false); + DO_TEST("boot-order", QEMUD_CMD_FLAG_BOOTINDEX | + QEMUD_CMD_FLAG_DRIVE | QEMUD_CMD_FLAG_DEVICE, false); DO_TEST("bootloader", QEMUD_CMD_FLAG_DOMID, true); DO_TEST("clock-utc", 0, false); DO_TEST("clock-localtime", 0, false); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 326a1f1..ab82d36 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -127,6 +127,7 @@ mymain(int argc, char **argv) DO_TEST("boot-floppy"); DO_TEST("boot-multi"); DO_TEST("boot-menu-disable"); + DO_TEST("boot-order"); DO_TEST("bootloader"); DO_TEST("clock-utc"); DO_TEST("clock-localtime"); -- 1.7.4.rc1
participants (2)
-
Eric Blake
-
Jiri Denemark