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 dad268d..3afea8d 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
@@ -625,6 +628,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>
@@ -640,6 +644,7 @@
<host name="hostname" port="7000"/>
</source>
<target dev="hdb" bus="ide"/>
+ <boot order='1'/>
</disk>
</devices>
...</pre>
@@ -692,6 +697,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
@@ -813,6 +826,7 @@
<source bridge='xenbr0'/>
<mac address='00:16:3e:5d:c7:9e'/>
<script path='vif-bridge'/>
+ <boot order='1'/>
</interface>
</devices>
...</pre>
@@ -1090,6 +1104,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 6de85fd..8e2027f 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">
@@ -1985,6 +1992,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 7c7a6fa..ec098f6 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1511,6 +1511,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)
{
@@ -1741,6 +1770,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;
@@ -2380,6 +2412,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;
@@ -4591,6 +4626,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) {
@@ -4599,6 +4637,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");
@@ -4617,7 +4662,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;
}
@@ -6068,6 +6113,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)
@@ -6307,6 +6354,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 6a8ec64..c5dc4bd 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