]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Introduce per-device boot element
authorJiri Denemark <jdenemar@redhat.com>
Wed, 12 Jan 2011 14:19:34 +0000 (15:19 +0100)
committerJiri Denemark <jdenemar@redhat.com>
Mon, 17 Jan 2011 16:08:13 +0000 (17:08 +0100)
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
docs/formatdomain.html.in
docs/schemas/domain.rng
src/conf/capabilities.c
src/conf/domain_conf.c
src/conf/domain_conf.h

index dcbf35a9dcad230d1ae3aa5dfe671f02ac22ca92..a4297cefa1ce1e32e53dc4c7f7e7a5efda14768e 100644 (file)
@@ -55,6 +55,7 @@ BIOS you will see</p>
     &lt;/arch&gt;
     &lt;features&gt;
       &lt;cpuselection/&gt;
+      &lt;deviceboot/&gt;
     &lt;/features&gt;
   &lt;/guest&gt;</span>
   ...
index dad268d0b3cb7720bccab3fa3eec56b956c4f7de..3afea8deaee85ece90b59ec3ebf96f2a3abd4392 100644 (file)
       <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
       &lt;driver name="tap" type="aio" cache="default"/&gt;
       &lt;source file='/var/lib/xen/images/fv0'/&gt;
       &lt;target dev='hda' bus='ide'/&gt;
+      &lt;boot order='2'/&gt;
       &lt;encryption type='...'&gt;
         ...
       &lt;/encryption&gt;
         &lt;host name="hostname" port="7000"/&gt;
       &lt;/source&gt;
       &lt;target dev="hdb" bus="ide"/&gt;
+      &lt;boot order='1'/&gt;
     &lt;/disk&gt;
   &lt;/devices&gt;
   ...</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
       &lt;source bridge='xenbr0'/&gt;
       &lt;mac address='00:16:3e:5d:c7:9e'/&gt;
       &lt;script path='vif-bridge'/&gt;
+      &lt;boot order='1'/&gt;
     &lt;/interface&gt;
   &lt;/devices&gt;
   ...</pre>
@@ -1090,6 +1104,29 @@ qemu-kvm -net nic,model=? /dev/null
         ignored.
     </p>
 
+    <h5><a name="elementsNICSBoot">Specifying boot order</a></h5>
+
+<pre>
+  ...
+  &lt;devices&gt;
+    &lt;interface type='network'&gt;
+      &lt;source network='default'/&gt;
+      &lt;target dev='vnet1'/&gt;
+      <b>&lt;boot order='1'/&gt;</b>
+    &lt;/interface&gt;
+  &lt;/devices&gt;
+  ...</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>
index a79ca6a0153b87263325b00a07e79f0be603a16a..9b977b5d8cb7a040a5ba8835f72d71f91539b444 100644 (file)
         </optional>
         <choice>
           <ref name="osbootkernel"/>
-          <oneOrMore>
+          <zeroOrMore>
             <ref name="osbootdev"/>
-          </oneOrMore>
+          </zeroOrMore>
         </choice>
         <optional>
           <element name="bootmenu">
       <ref name="driver"/>
     </optional>
     <ref name="target"/>
+    <optional>
+      <ref name="deviceBoot"/>
+    </optional>
     <optional>
       <element name="readonly">
         <empty/>
         - 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>
           </optional>
         </element>
       </optional>
+      <optional>
+        <ref name="deviceBoot"/>
+      </optional>
     </interleave>
   </define>
   <define name="virtualPortProfile">
     </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
index 99d5a564ed2902f0fb5c04e5f805cdb6725d535d..cb9113ca1e31a32c10b0b9754cdaf88d605983dc 100644 (file)
@@ -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 {
index d57af222932f934e75d505186d17591c7d3a59d1..645767e9a24141fabb4e705552c09b20e153c403 100644 (file)
@@ -46,6 +46,7 @@
 #include "ignore-value.h"
 #include "storage_file.h"
 #include "files.h"
+#include "bitmap.h"
 
 #define VIR_FROM_THIS VIR_FROM_DOMAIN
 
@@ -1529,6 +1530,50 @@ cleanup:
     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)
@@ -1614,7 +1659,9 @@ virDomainDiskDefAssignAddress(virCapsPtr caps, virDomainDiskDefPtr def)
 static virDomainDiskDefPtr
 virDomainDiskDefParseXML(virCapsPtr caps,
                          xmlNodePtr node,
-                         int flags) {
+                         virBitmapPtr bootMap,
+                         int flags)
+{
     virDomainDiskDefPtr def;
     xmlNodePtr cur, host;
     char *type = NULL;
@@ -1760,6 +1807,10 @@ 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,
+                                                bootMap))
+                    goto error;
             }
         }
         cur = cur->next;
@@ -2297,7 +2348,9 @@ static virDomainNetDefPtr
 virDomainNetDefParseXML(virCapsPtr caps,
                         xmlNodePtr node,
                         xmlXPathContextPtr ctxt,
-                        int flags ATTRIBUTE_UNUSED) {
+                        virBitmapPtr bootMap,
+                        int flags ATTRIBUTE_UNUSED)
+{
     virDomainNetDefPtr def;
     xmlNodePtr cur;
     char *macaddr = NULL;
@@ -2407,6 +2460,10 @@ 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,
+                                                bootMap))
+                    goto error;
             }
         }
         cur = cur->next;
@@ -4394,7 +4451,8 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
 
     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;
@@ -4402,7 +4460,8 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
             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;
@@ -4673,12 +4732,21 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def,
 
 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) {
@@ -4687,6 +4755,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");
@@ -4705,7 +4780,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;
     }
@@ -4719,6 +4794,7 @@ virDomainDefParseBootXML(xmlXPathContextPtr ctxt,
         VIR_FREE(bootstr);
     }
 
+    *bootCount = deviceBoot;
     ret = 0;
 
 cleanup:
@@ -4739,6 +4815,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
     virDomainDefPtr def;
     unsigned long count;
     bool uuid_generated = false;
+    virBitmapPtr bootMap = NULL;
+    unsigned long bootMapSize = 0;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
@@ -5055,9 +5133,11 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         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);
@@ -5078,6 +5158,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
     for (i = 0 ; i < n ; i++) {
         virDomainDiskDefPtr disk = virDomainDiskDefParseXML(caps,
                                                             nodes[i],
+                                                            bootMap,
                                                             flags);
         if (!disk)
             goto error;
@@ -5134,6 +5215,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         virDomainNetDefPtr net = virDomainNetDefParseXML(caps,
                                                          nodes[i],
                                                          ctxt,
+                                                         bootMap,
                                                          flags);
         if (!net)
             goto error;
@@ -5533,6 +5615,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
     if (virDomainDefAddImplicitControllers(def) < 0)
         goto error;
 
+    virBitmapFree(bootMap);
+
     return def;
 
 no_memory:
@@ -5542,6 +5626,7 @@ no_memory:
  error:
     VIR_FREE(tmp);
     VIR_FREE(nodes);
+    virBitmapFree(bootMap);
     virDomainDefFree(def);
     return NULL;
 }
@@ -6157,6 +6242,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)
@@ -6401,6 +6488,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 (def->tune.sndbuf_specified) {
         virBufferAddLit(buf,   "      <tune>\n");
index d4c8e872e6b4ca72d097cbececc616eae9bc0967..cf7bdc0c08d22cbdf3a7bdc182113bfe644b2430 100644 (file)
@@ -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;
@@ -351,6 +352,7 @@ struct _virDomainNetDef {
         unsigned long sndbuf;
     } tune;
     char *ifname;
+    int bootIndex;
     virDomainDeviceInfo info;
     char *filter;
     virNWFilterHashTablePtr filterparams;