Added a VBD.bootable flag.
authorEwan Mellor <ewan@xensource.com>
Sun, 28 Jan 2007 17:11:40 +0000 (17:11 +0000)
committerEwan Mellor <ewan@xensource.com>
Sun, 28 Jan 2007 17:11:40 +0000 (17:11 +0000)
Signed-off-by: Ewan Mellor <ewan@xensource.com>
docs/xen-api/xenapi-datamodel.tex
tools/libxen/include/xen_vbd.h
tools/libxen/src/xen_vbd.c
tools/python/xen/xend/XendAPI.py
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/XendDomainInfo.py

index 75f164562347d986d363972ca8269a6ac6d6d131..644489850c3ef5db17978ae4c4067102e5e8b44e 100644 (file)
@@ -1089,7 +1089,10 @@ control domain to some other bootloader.  The other fields, PV/kernel,
 PV/ramdisk, PV/args and PV/bootloader\_args will be passed to the
 bootloader unmodified, and interpretation of those fields is then specific
 to the bootloader itself, including the possibility that the bootloader
-will ignore some or all of those given values.
+will ignore some or all of those given values. Finally the paths of all
+bootable disks are added to the bootloader commandline (a disk is bootable
+if its VBD has the bootable flag set). There may be zero, one or many
+bootable disks; the bootloader decides which disk (if any) to boot from.
 
 If the bootloader is pygrub, then the menu.lst is parsed if present in the
 guest's filesystem, otherwise the specified kernel and ramdisk are used, or
@@ -8665,7 +8668,8 @@ $\mathit{RO}_\mathit{run}$ &  {\tt uuid} & string & unique identifier/object ref
 $\mathit{RW}$ &  {\tt VM} & VM ref & the virtual machine \\
 $\mathit{RW}$ &  {\tt VDI} & VDI ref & the virtual disk \\
 $\mathit{RW}$ &  {\tt device} & string & device seen by the guest e.g. hda1 \\
-$\mathit{RW}$ &  {\tt mode} & vbd\_mode & the mode the disk should be mounted with \\
+$\mathit{RW}$ &  {\tt bootable} & bool & true if this VBD is bootable \\
+$\mathit{RW}$ &  {\tt mode} & vbd\_mode & the mode the VBD should be mounted with \\
 $\mathit{RW}$ &  {\tt type} & vbd\_type & how the VBD will appear to the guest (e.g. disk or CD) \\
 $\mathit{RW}$ &  {\tt driver} & driver\_type & the style of driver \\
 $\mathit{RO}_\mathit{run}$ &  {\tt io/read\_kbs} & float & Read bandwidth (KiB/s) \\
@@ -8934,6 +8938,72 @@ void
 
 
 
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_bootable}
+
+{\bf Overview:} 
+Get the bootable field of the given VBD.
+
+ \noindent {\bf Signature:} 
+\begin{verbatim} bool get_bootable (session_id s, VBD ref self)\end{verbatim}
+
+
+\noindent{\bf Arguments:}
+
+\vspace{0.3cm}
+\begin{tabular}{|c|c|p{7cm}|}
+ \hline
+{\bf type} & {\bf name} & {\bf description} \\ \hline
+{\tt VBD ref } & self & reference to the object \\ \hline 
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:} 
+{\tt 
+bool
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~set\_bootable}
+
+{\bf Overview:} 
+Set the bootable field of the given VBD.
+
+ \noindent {\bf Signature:} 
+\begin{verbatim} void set_bootable (session_id s, VBD ref self, bool value)\end{verbatim}
+
+
+\noindent{\bf Arguments:}
+
+\vspace{0.3cm}
+\begin{tabular}{|c|c|p{7cm}|}
+ \hline
+{\bf type} & {\bf name} & {\bf description} \\ \hline
+{\tt VBD ref } & self & reference to the object \\ \hline 
+
+{\tt bool } & value & New value to set \\ \hline 
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:} 
+{\tt 
+void
+}
+
+
+
 \vspace{0.3cm}
 \vspace{0.3cm}
 \vspace{0.3cm}
index 2b9d8b05eb3a6faea94ef9cc1fcf3b096c40c1e7..d3cb1a88767900cb1d48d3ee71503f6fbfb0fb3c 100644 (file)
@@ -70,6 +70,7 @@ typedef struct xen_vbd_record
     struct xen_vdi_record_opt *vdi;
     char *device;
     char *image;
+    bool bootable;
     enum xen_vbd_mode mode;
     enum xen_driver_type driver;
     double io_read_kbs;
@@ -211,6 +212,13 @@ extern bool
 xen_vbd_get_device(xen_session *session, char **result, xen_vbd vbd);
 
 
+/**
+ * Get the bootable field of the given VBD.
+ */
+extern bool
+xen_vbd_get_bootable(xen_session *session, bool *result, xen_vbd vbd);
+
+
 /**
  * Get the mode field of the given VBD.
  */
@@ -260,6 +268,13 @@ extern bool
 xen_vbd_set_device(xen_session *session, xen_vbd vbd, char *device);
 
 
+/**
+ * Set the bootable field of the given VBD.
+ */
+extern bool
+xen_vbd_set_bootable(xen_session *session, xen_vbd vbd, bool bootable);
+
+
 /**
  * Set the mode field of the given VBD.
  */
index c49ecf236a779e3cebe22c60732d10adbb6e3492..3bd8ba560c9cecb7a4f79f32b4e1a8f1425c4a6a 100644 (file)
@@ -55,6 +55,9 @@ static const struct_member xen_vbd_record_struct_members[] =
         { .key = "image",
           .type = &abstract_type_string,
           .offset = offsetof(xen_vbd_record, image) },
+        { .key = "bootable",
+          .type = &abstract_type_bool,
+          .offset = offsetof(xen_vbd_record, bootable) },
         { .key = "mode",
           .type = &xen_vbd_mode_abstract_type_,
           .offset = offsetof(xen_vbd_record, mode) },
@@ -217,6 +220,22 @@ xen_vbd_get_device(xen_session *session, char **result, xen_vbd vbd)
 }
 
 
+bool
+xen_vbd_get_bootable(xen_session *session, bool *result, xen_vbd vbd)
+{
+    abstract_value param_values[] =
+        {
+            { .type = &abstract_type_string,
+              .u.string_val = vbd }
+        };
+
+    abstract_type result_type = abstract_type_bool;
+
+    XEN_CALL_("VBD.get_bootable");
+    return session->ok;
+}
+
+
 bool
 xen_vbd_get_mode(xen_session *session, enum xen_vbd_mode *result, xen_vbd vbd)
 {
@@ -331,6 +350,22 @@ xen_vbd_set_device(xen_session *session, xen_vbd vbd, char *device)
 }
 
 
+bool
+xen_vbd_set_bootable(xen_session *session, xen_vbd vbd, bool bootable)
+{
+    abstract_value param_values[] =
+        {
+            { .type = &abstract_type_string,
+              .u.string_val = vbd },
+            { .type = &abstract_type_bool,
+              .u.bool_val = bootable }
+        };
+
+    xen_call_(session, "VBD.set_bootable", param_values, 2, NULL, NULL);
+    return session->ok;
+}
+
+
 bool
 xen_vbd_set_mode(xen_session *session, xen_vbd vbd, enum xen_vbd_mode mode)
 {
index 60f50330c46284c2abb1c74da37bd142a79f1db2..26702819436428edec0c47bb0e7c6033488f2b73 100644 (file)
@@ -1348,6 +1348,7 @@ class XendAPI(object):
     VBD_attr_rw = ['VM',
                    'VDI',
                    'device',
+                   'bootable',
                    'mode',
                    'type',                   
                    'driver']
@@ -1429,6 +1430,10 @@ class XendAPI(object):
         xendom = XendDomain.instance()
         return xen_api_success(xendom.get_dev_property_by_uuid('vbd', vbd_ref,
                                                                'device'))
+    def VBD_get_bootable(self, session, vbd_ref):
+        xendom = XendDomain.instance()
+        return xen_api_success(xendom.get_dev_property_by_uuid('vbd', vbd_ref,
+                                                               'bootable'))
     def VBD_get_mode(self, session, vbd_ref):
         xendom = XendDomain.instance()
         return xen_api_success(xendom.get_dev_property_by_uuid('vbd', vbd_ref,
@@ -1454,6 +1459,14 @@ class XendAPI(object):
         return xen_api_success(xendom.get_dev_property_by_uuid('vbd', vbd_ref,
                                                               'io_read_kbs'))
     
+    def VBD_set_bootable(self, session, vbd_ref, bootable):
+        bootable = bool(bootable)
+        xd = XendDomain.instance()
+        vm = xd.get_vm_with_dev_uuid('vbd', vbd_ref)
+        vm.set_dev_property('vbd', vbd_ref, 'bootable', bootable)
+        xd.managed_config_save(vm)
+        return xen_api_success_void()
+
     def VBD_get_all(self, session):
         xendom = XendDomain.instance()
         vbds = [d.get_vbds() for d in XendDomain.instance().list('all')]
index 62b462f577857352cbdee1711a9ef82e599ac05f..9a31b1150a4c5c68217cb39c3b41d602d63206de 100644 (file)
@@ -28,7 +28,7 @@ from xen.xend.PrettyPrint import prettyprintstring
 from xen.xend.XendConstants import DOM_STATE_HALTED
 
 log = logging.getLogger("xend.XendConfig")
-log.setLevel(logging.WARN)
+log.setLevel(logging.DEBUG)
 
 
 """
@@ -878,6 +878,13 @@ class XendConfig(dict):
                         controller = domain.getDeviceController(cls)
                         configs = controller.configurations()
                         for config in configs:
+                            if sxp.name(config) in ('vbd', 'tap'):
+                                # The bootable flag is never written to the
+                                # store as part of the device config.
+                                uuid = sxp.child_value(sxpr, 'uuid')
+                                sxpr.append(
+                                    'bootable', 
+                                    self['devices'][dev_uuid]['bootable'])
                             sxpr.append(['device', config])
 
                         found = True
@@ -948,6 +955,7 @@ class XendConfig(dict):
                     pass
 
             if dev_type == 'vbd':
+                dev_info['bootable'] = False
                 if dev_info.get('dev', '').startswith('ioemu:'):
                     dev_info['driver'] = 'ioemu'
                 else:
@@ -964,13 +972,21 @@ class XendConfig(dict):
                 if param not in target:
                     target[param] = []
                 if dev_uuid not in target[param]:
+                    if dev_type == 'vbd' and not target[param]:
+                        # Compat hack -- this is the first disk, so mark it
+                        # bootable.
+                        dev_info['bootable'] = True
                     target[param].append(dev_uuid)
-            elif dev_type in ('tap',):
+            elif dev_type == 'tap':
                 if 'vbd_refs' not in target:
                     target['vbd_refs'] = []
                 if dev_uuid not in target['vbd_refs']:
+                    if not target['vbd_refs']:
+                        # Compat hack -- this is the first disk, so mark it
+                        # bootable.
+                        dev_info['bootable'] = True
                     target['vbd_refs'].append(dev_uuid)
-            elif dev_type in ('console',):
+            elif dev_type == 'console':
                 if 'console_refs' not in target:
                     target['console_refs'] = []
                 if dev_uuid not in target['console_refs']:
@@ -1009,6 +1025,7 @@ class XendConfig(dict):
                 dev_info['uname'] = cfg_xenapi.get('image', '')
                 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
                                              old_vbd_type)
+                dev_info['bootable'] = cfg_xenapi.get('bootable', False)
                 dev_info['driver'] = cfg_xenapi.get('driver')
                 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
                     
index a0c6c6ffa724ce7072e4d00df5039cb1108d5990..cc0280ac97632a924f6f65b65708eb1509e88aeb 100644 (file)
@@ -1653,48 +1653,45 @@ class XendDomainInfo:
                 blexec = osdep.pygrub_path
 
             blcfg = None
-            for (devtype, devinfo) in self.info.all_devices_sxpr():
-                if not devtype or not devinfo or devtype not in ('vbd', 'tap'):
-                    continue
-                disk = None
-                for param in devinfo:
-                    if param[0] == 'uname':
-                        disk = param[1]
-                        break
-
-                if disk is None:
-                    continue
-                fn = blkdev_uname_to_file(disk)
-                mounted = devtype == 'tap' and not os.stat(fn).st_rdev
-                if mounted:
-                    # This is a file, not a device.  pygrub can cope with a
-                    # file if it's raw, but if it's QCOW or other such formats
-                    # used through blktap, then we need to mount it first.
+            disks = [x for x in self.info['vbd_refs']
+                     if self.info['devices'][x][1]['bootable']]
 
-                    log.info("Mounting %s on %s." %
-                             (fn, BOOTLOADER_LOOPBACK_DEVICE))
+            if not disks:
+                msg = "Had a bootloader specified, but no disks are bootable"
+                log.error(msg)
+                raise VmError(msg)
 
-                    vbd = {
-                        'mode': 'RO',
-                        'device': BOOTLOADER_LOOPBACK_DEVICE,
-                        }
+            disk = disks[0]
 
-                    from xen.xend import XendDomain
-                    dom0 = XendDomain.instance().privilegedDomain()
-                    dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
-                    fn = BOOTLOADER_LOOPBACK_DEVICE
+            fn = blkdev_uname_to_file(disk)
+            mounted = devtype == 'tap' and not os.stat(fn).st_rdev
+            if mounted:
+                # This is a file, not a device.  pygrub can cope with a
+                # file if it's raw, but if it's QCOW or other such formats
+                # used through blktap, then we need to mount it first.
 
-                try:
-                    blcfg = bootloader(blexec, fn, self, False,
-                                       bootloader_args, kernel, ramdisk, args)
-                finally:
-                    if mounted:
-                        log.info("Unmounting %s from %s." %
-                                 (fn, BOOTLOADER_LOOPBACK_DEVICE))
+                log.info("Mounting %s on %s." %
+                         (fn, BOOTLOADER_LOOPBACK_DEVICE))
 
-                        dom0.destroyDevice('tap', '/dev/xvdp')
+                vbd = {
+                    'mode': 'RO',
+                    'device': BOOTLOADER_LOOPBACK_DEVICE,
+                    }
 
-                break
+                from xen.xend import XendDomain
+                dom0 = XendDomain.instance().privilegedDomain()
+                dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
+                fn = BOOTLOADER_LOOPBACK_DEVICE
+
+            try:
+                blcfg = bootloader(blexec, fn, self, False,
+                                   bootloader_args, kernel, ramdisk, args)
+            finally:
+                if mounted:
+                    log.info("Unmounting %s from %s." %
+                             (fn, BOOTLOADER_LOOPBACK_DEVICE))
+
+                    dom0.destroyDevice('tap', '/dev/xvdp')
 
             if blcfg is None:
                 msg = "Had a bootloader specified, but can't find disk"
@@ -2137,6 +2134,9 @@ class XendDomainInfo:
         except KeyError:
             raise XendError('Invalid property for device: %s' % field)
 
+    def set_dev_property(self, dev_class, dev_uuid, field, value):
+        self.info['devices'][dev_uuid][1][field] = value
+
     def get_vcpus_util(self):
         vcpu_util = {}
         xennode = XendNode.instance()