<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
<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>
<host name="hostname" port="7000"/>
</source>
<target dev="hdb" bus="ide"/>
+ <boot order='1'/>
</disk>
</devices>
...</pre>
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
<source bridge='xenbr0'/>
<mac address='00:16:3e:5d:c7:9e'/>
<script path='vif-bridge'/>
+ <boot order='1'/>
</interface>
</devices>
...</pre>
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>
#include "ignore-value.h"
#include "storage_file.h"
#include "files.h"
+#include "bitmap.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN
return ret;
}
+static int
+virDomainDeviceBootParseXML(xmlNodePtr node,
+ int *bootIndex,
+ virBitmapPtr bootMap)
+{
+ 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;
+ }
+
+ if (bootMap) {
+ bool set;
+ if (virBitmapGetBit(bootMap, boot - 1, &set) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("boot orders have to be contiguous and starting from 1"));
+ goto cleanup;
+ } else if (set) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("boot order %d used for more than one device"), boot);
+ goto cleanup;
+ }
+ ignore_value(virBitmapSetBit(bootMap, boot - 1));
+ }
+
+ *bootIndex = boot;
+ ret = 0;
+
+cleanup:
+ VIR_FREE(order);
+ return ret;
+}
+
static int
virDomainParseLegacyDeviceAddress(char *devaddr,
virDomainDevicePCIAddressPtr pci)
static virDomainDiskDefPtr
virDomainDiskDefParseXML(virCapsPtr caps,
xmlNodePtr node,
- int flags) {
+ virBitmapPtr bootMap,
+ int flags)
+{
virDomainDiskDefPtr def;
xmlNodePtr cur, host;
char *type = NULL;
} 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,
+ bootMap))
+ goto error;
}
}
cur = cur->next;
virDomainNetDefParseXML(virCapsPtr caps,
xmlNodePtr node,
xmlXPathContextPtr ctxt,
- int flags ATTRIBUTE_UNUSED) {
+ virBitmapPtr bootMap,
+ int flags ATTRIBUTE_UNUSED)
+{
virDomainNetDefPtr def;
xmlNodePtr cur;
char *macaddr = NULL;
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,
+ bootMap))
+ goto error;
}
}
cur = cur->next;
if (xmlStrEqual(node->name, BAD_CAST "disk")) {
dev->type = VIR_DOMAIN_DEVICE_DISK;
- if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, flags)))
+ if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node,
+ NULL, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "filesystem")) {
dev->type = VIR_DOMAIN_DEVICE_FS;
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
dev->type = VIR_DOMAIN_DEVICE_NET;
- if (!(dev->data.net = virDomainNetDefParseXML(caps, node, ctxt, flags)))
+ if (!(dev->data.net = virDomainNetDefParseXML(caps, node, ctxt,
+ NULL, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "input")) {
dev->type = VIR_DOMAIN_DEVICE_INPUT;
static int
virDomainDefParseBootXML(xmlXPathContextPtr ctxt,
- virDomainDefPtr def)
+ virDomainDefPtr def,
+ unsigned long *bootCount)
{
xmlNodePtr *nodes = NULL;
int i, n;
char *bootstr;
int ret = -1;
+ unsigned long deviceBoot;
+
+ if (virXPathULong("count(./devices/disk[boot]"
+ "|./devices/interface[boot])", ctxt, &deviceBoot) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot count boot devices"));
+ goto cleanup;
+ }
/* analysis of the boot devices */
if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) {
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");
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;
}
VIR_FREE(bootstr);
}
+ *bootCount = deviceBoot;
ret = 0;
cleanup:
virDomainDefPtr def;
unsigned long count;
bool uuid_generated = false;
+ virBitmapPtr bootMap = NULL;
+ unsigned long bootMapSize = 0;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
def->os.loader = virXPathString("string(./os/loader[1])", ctxt);
}
- if (STREQ(def->os.type, "hvm") &&
- virDomainDefParseBootXML(ctxt, def) < 0) {
- goto error;
+ if (STREQ(def->os.type, "hvm")) {
+ if (virDomainDefParseBootXML(ctxt, def, &bootMapSize) < 0)
+ goto error;
+ if (bootMapSize && !(bootMap = virBitmapAlloc(bootMapSize)))
+ goto no_memory;
}
def->emulator = virXPathString("string(./devices/emulator[1])", ctxt);
for (i = 0 ; i < n ; i++) {
virDomainDiskDefPtr disk = virDomainDiskDefParseXML(caps,
nodes[i],
+ bootMap,
flags);
if (!disk)
goto error;
virDomainNetDefPtr net = virDomainNetDefParseXML(caps,
nodes[i],
ctxt,
+ bootMap,
flags);
if (!net)
goto error;
if (virDomainDefAddImplicitControllers(def) < 0)
goto error;
+ virBitmapFree(bootMap);
+
return def;
no_memory:
error:
VIR_FREE(tmp);
VIR_FREE(nodes);
+ virBitmapFree(bootMap);
virDomainDefFree(def);
return NULL;
}
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)
virBufferVSprintf(buf, ">\n%s </filterref>\n", attrs);
VIR_FREE(attrs);
}
+ if (def->bootIndex)
+ virBufferVSprintf(buf, " <boot order='%d'/>\n", def->bootIndex);
if (def->tune.sndbuf_specified) {
virBufferAddLit(buf, " <tune>\n");