# This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2005/04/14 12:16:27-04:00 katzj@xxxxxxxxxxxxxx # Minor fixes based on feedback from Mike Wray # # tools/python/xen/xm/create.py # 2005/04/14 12:16:25-04:00 katzj@xxxxxxxxxxxxxx +2 -3 # Use method in blkif to do uname -> file conversion # # tools/python/xen/xend/server/blkif.py # 2005/04/14 12:16:25-04:00 katzj@xxxxxxxxxxxxxx +9 -0 # Add method to convert from a uname to a real file # # tools/python/xen/xend/XendDomainInfo.py # 2005/04/14 12:16:25-04:00 katzj@xxxxxxxxxxxxxx +6 -8 # Make lack of a disk a fatal error. Use the method in blkif # for going from a disk uname to a filename # # tools/python/xen/xend/XendBootloader.py # 2005/04/14 12:16:25-04:00 katzj@xxxxxxxxxxxxxx +14 -4 # Improve error handling. Log errors and raise an error on a problem # instead of just printing to stderr and exiting # # ChangeSet # 2005/04/13 23:01:42-04:00 katzj@xxxxxxxxxxxxxx # Add framework for running a bootloader from within xend and xm. To # specify the executable to run, set in your domain config like # bootloader="/usr/bin/pygrub" # # The bootloader will get executed on both domain creation and domain # reboot to get the default kernel/initrd specified in the bootloader # config for the domain. If you auto-connect the console on domain # creation, then your bootloader will be run in an interactive mode. # # BitKeeper/etc/logging_ok # 2005/04/13 23:01:42-04:00 katzj@xxxxxxxxxxxxxx +1 -0 # Logging to logging@xxxxxxxxxxxxxxx accepted # # tools/python/xen/xend/XendBootloader.py # 2005/04/13 23:01:37-04:00 katzj@xxxxxxxxxxxxxx +79 -0 # # tools/python/xen/xm/create.py # 2005/04/13 23:01:37-04:00 katzj@xxxxxxxxxxxxxx +29 -2 # Run bootloader on domain creation. If console is being connected, # then run in an interactive mode, else just go with the default. # # tools/python/xen/xend/XendDomainInfo.py # 2005/04/13 23:01:37-04:00 katzj@xxxxxxxxxxxxxx +41 -1 # Add running of a boot loader from xend on domain reboot. Runs in # an uninteractive mode to get the default kernel/initrd for the # domain. Also removes any temporary kernels created by the # bootloader. # # tools/python/xen/xend/XendBootloader.py # 2005/04/13 23:01:37-04:00 katzj@xxxxxxxxxxxxxx +0 -0 # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/python/xen/xend/XendBootloader.py # diff -Nru a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/tools/python/xen/xend/XendBootloader.py 2005-04-14 12:58:19 -04:00 @@ -0,0 +1,89 @@ +# +# XendBootloader.py - Framework to run a boot loader for picking the kernel +# +# Copyright 2005 Red Hat, Inc. +# Jeremy Katz +# +# This software may be freely redistributed under the terms of the GNU +# general public license. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys, select +import sxp + +from XendLogging import log +from XendError import VmError + +BL_FIFO = "/var/lib/xen/xenbl" + +def bootloader(blexec, disk, quiet = 0, vcpus = None): + """Run the boot loader executable on the given disk and return a + config image. + @param blexec Binary to use as the boot loader + @param disk Disk to run the boot loader on.""" + + if not os.access(blexec, os.X_OK): + msg = "Bootloader isn't executable" + log.error(msg) + raise VmError, msg + if not os.access(disk, os.R_OK): + msg = "Disk isn't accessible" + log.error(msg) + raise VmError, msg + + os.mkfifo(BL_FIFO, 0600) + + child = os.fork() + if (not child): + args = [ blexec, "-o", BL_FIFO ] + if quiet: + args.append("-q") + args.append(disk) + + try: + os.execvp(args[0], args) + except OSError, e: + print e + pass + os._exit(1) + + while 1: + try: + r = os.open(BL_FIFO, os.O_RDONLY) + except OSError, e: + if e.errno == 4: + continue + break + ret = "" + while 1: + select.select([r], [], []) + s = os.read(r, 1024) + ret = ret + s + if len(s) == 0: + break + + (pid, status) = os.waitpid(child, 0) + os.close(r) + os.unlink(BL_FIFO) + + if len(ret) == 0: + msg = "Boot loader didn't return any data!" + log.error(msg) + raise VmError, msg + + pin = sxp.Parser() + pin.input(ret) + pin.input_eof() + + config_image = pin.val + if vcpus and sxp.child_value(config_image, "vcpus") is None: + config_image.append(['vcpus', vcpus]) + + config = [['image', pin.val]] + config.append(['bootloader', blexec]) + return config + diff -Nru a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py 2005-04-14 12:58:19 -04:00 +++ b/tools/python/xen/xend/XendDomainInfo.py 2005-04-14 12:58:19 -04:00 @@ -20,7 +20,8 @@ import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() import xen.util.ip from xen.util.ip import _readline, _readlines -from xen.xend.server import channel +from xen.xend.server import channel, blkif +from xen.xend.XendBootloader import bootloader import sxp @@ -323,6 +324,7 @@ self.image_handler = None self.is_vmx = 0 self.vcpus = 1 + self.bootloader = None def setdom(self, dom): """Set the domain id. @@ -458,6 +460,7 @@ self.find_image_handler() self.init_domain() + self.configure_bootloader() self.configure_console() self.configure_backends() self.construct_image() @@ -795,7 +798,7 @@ ramdisk = ramdisk, flags = flags) else: - log.warning('building dom with %d vcpus', self.vcpus) + log.warning('building dom with %d vcpus' %(self.vcpus,)) err = buildfn(dom = dom, image = kernel, control_evtchn = self.console.getRemotePort(), @@ -803,6 +806,14 @@ ramdisk = ramdisk, flags = flags, vcpus = self.vcpus) + + if self.bootloader: + try: + if kernel: os.unlink(kernel) + if ramdisk: os.unlink(ramdisk) + except OSError, e: + log.warning('unable to unlink kernel/ramdisk: %s' %(e,)) + if err != 0: raise VmError('Building domain failed: type=%s dom=%d err=%d' % (ostype, dom, err)) @@ -961,6 +972,13 @@ maxmem = self.memory xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024) + def configure_bootloader(self): + """Configure boot loader. + """ + bl = sxp.child_value(self.config, "bootloader") + if bl is not None: + self.bootloader = bl + def configure_console(self): """Configure the vm console port. """ @@ -1034,6 +1052,24 @@ try: self.restart_check() self.restart_state = STATE_RESTART_BOOTING + # if we're restarting with a bootloader, we need to run it + if self.bootloader: + # FIXME: this assumes the disk is the first device and + # that we're booting from the first disk + blcfg = None + # FIXME: this assumes that we want to use the first disk + dev = sxp.child_value(self.config, "device") + if dev: + disk = sxp.child_value(dev, "uname") + fn = blkif.blkdev_uname_to_file(disk) + blcfg = bootloader(self.bootloader, fn, 1, self.vcpus) + if blcfg is None: + msg = "Had a bootloader specified, but can't find disk" + log.error(msg) + raise VmError(msg) + else: + s = "(vm %s)" %(sxp.to_string(blcfg[0]),) + self.config = sxp.merge(sxp.from_string(s), self.config) d = self.construct(self.config) finally: self.restart_state = None @@ -1163,6 +1199,7 @@ if args: cmdline += " " + args ramdisk = sxp.child_value(image, "ramdisk", '') + log.debug("creating linux domain with cmdline: %s" %(cmdline,)) vm.create_domain("linux", kernel, ramdisk, cmdline) return vm @@ -1377,6 +1414,7 @@ add_config_handler('device', vm_field_ignore) add_config_handler('backend', vm_field_ignore) add_config_handler('vcpus', vm_field_ignore) +add_config_handler('bootloader', vm_field_ignore) # Register other config handlers. add_config_handler('maxmem', vm_field_maxmem) diff -Nru a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py --- a/tools/python/xen/xend/server/blkif.py 2005-04-14 12:58:19 -04:00 +++ b/tools/python/xen/xend/server/blkif.py 2005-04-14 12:58:19 -04:00 @@ -96,6 +96,15 @@ 'type' : 'Disk' } return val +def blkdev_uname_to_file(uname): + """Take a blkdev uname and return the corresponding filename.""" + fn = None + if uname.find(":") != -1: + (typ, fn) = uname.split(":") + if typ == "phy" and not fn.startswith("/dev/"): + fn = "/dev/%s" %(fn,) + return fn + class BlkifBackendController(controller.BackendController): """ Handler for the 'back-end' channel to a block device driver domain. """ diff -Nru a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py 2005-04-14 12:58:19 -04:00 +++ b/tools/python/xen/xm/create.py 2005-04-14 12:58:19 -04:00 @@ -10,6 +10,8 @@ from xen.xend import sxp from xen.xend import PrettyPrint from xen.xend.XendClient import server, XendError +from xen.xend.XendBootloader import bootloader +from xen.xend.server import blkif from xen.util import console_client @@ -94,6 +96,10 @@ fn=set_value, default=None, use="Domain name. Must be unique.") +gopts.var('bootloader', val='FILE', + fn=set_value, default=None, + use="Path to bootloader.") + gopts.var('kernel', val='FILE', fn=set_value, default=None, use="Path to kernel image.") @@ -375,6 +381,21 @@ config_devs.append(['device_model', device_model]) config_devs.append(['device_config', device_config]) +def run_bootloader(config, vals): + if not os.access(vals.bootloader, os.X_OK): + print >> sys.stderr, "Bootloader isn't executable" + sys.exit(1) + if len(vals.disk) < 1: + print >> sys.stderr, "No disks configured and boot loader requested" + sys.exit(1) + (uname, dev, mode, backend) = vals.disk[0] + file = blkif.blkdev_uname_to_file(uname) + + blcfg = bootloader(vals.bootloader, file, + not vals.console_autoconnect, vals.vcpus) + + config.extend(blcfg) + def make_config(vals): """Create the domain configuration. """ @@ -396,8 +417,11 @@ config.append(['restart', vals.restart]) if vals.console: config.append(['console', vals.console]) - - configure_image(config, vals) + + if vals.bootloader: + run_bootloader(config, vals) + else: + configure_image(config, vals) config_devs = [] configure_disks(config_devs, vals) configure_pci(config_devs, vals) @@ -405,6 +429,7 @@ configure_usb(config_devs, vals) configure_vmx(config_devs, vals) config += config_devs + return config def preprocess_disk(opts, vals): @@ -588,6 +613,7 @@ if not opts.getopt('name') and opts.getopt('defconfig'): opts.setopt('name', os.path.basename(opts.getopt('defconfig'))) config = make_config(opts.vals) + if opts.vals.dryrun: PrettyPrint.prettyprint(config) else: