From b129ff2b0427614fa1bdfbfc73a2e4737f3e71f3 Mon Sep 17 00:00:00 2001 From: Zhigang Wang Date: Thu, 1 Mar 2012 18:54:37 +0000 Subject: add new bootloader xenpvnetboot for pv guest `xenpvnetboot' supports getting boot images from following locations: - http://host/path - https://host/path - ftp://host/path - file:///path - tftp://host/path - nfs:host:/path - /path - /path/file.iso - /path/filesystem.img - /dev/sda1 - nfs+iso:host:/path/file.iso - nfs+iso:host:/path/filesystem.img To use it, make `xenpvnetboot' as bootloader for PV guest:: bootloader = '/usr/bin/xenpvnetboot' To get boot images from various locations, set the right bootloader arguments, e.g.: bootloarder_args = ['--location=http://192.168.0.1/fedora/x86_64'] bootloarder_args = ['--location=ftp://192.168.0.1/fedora/x86_64'] bootloarder_args = ['--location=file:///fedora/x86_64'] bootloarder_args = ['--location=tftp://192.168.0.1/fedora/x86_64'] bootloarder_args = ['--location=/fedora/x86_64'] bootloarder_args = ['--location=/fedora/Fedora-16-x86_64-DVD.iso'] bootloarder_args = ['--location=nfs:192.168.0.1:/fedora/x86_64'] bootloarder_args = ['--location=nfs+iso:192.168.0.1:/fedora/Fedora-16-x86_64-DVD.iso'] You can use `kernel' and `ramdisk' to specify the relative path of boot kernel and ramdisk. `xenpvnetboot' will join them with the location to find the boot kernel and ramdisk, e.g.: kernel = 'images/pxeboot/vmlinuz' ramdisk = 'images/pxeboot/initrd.img' bootloarder_args = ['--location=http://192.168.0.1/fedora/x86_64'] kernel = 'fedora/x86_64/images/pxeboot/vmlinuz' ramdisk = 'fedora/x86_64/images/pxeboot/initrd.img' bootloarder_args = ['--location=http://192.168.0.1/'] You can also omit the `--location' option and specify the full URL for `kernel' and `ramdisk' directly, e.g.: kernel = 'http://192.168.0.1/fedora/x86_64/images/pxeboot/vmlinuz' ramdisk = 'http://192.168.0.1/fedora/x86_64/images/pxeboot/initrd.img' If only `--location' is specified and `kernel' and `ramdisk' are not specified, `xenpvnetboot' will search the following places for boot images from the location:: ('images/xen/vmlinuz', 'images/xen/initrd.img'), # Fedora <= 10 and OL = 5 ('boot/i386/vmlinuz-xen', 'boot/i386/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 ('boot/x86_64/vmlinuz-xen', 'boot/x86_64/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 ('current/images/netboot/xen/vmlinuz', 'current/images/netboot/xen/initrd.gz'), # Debian ('images/pxeboot/vmlinuz', 'images/pxeboot/initrd.img'), # Fedora >=10 and OL >= 6 ('isolinux/vmlinuz', 'isolinux/initrd.img'), # Fedora >= 10 and OL >= 6 `xenpvnetboot' requires python module `urlgrabber' http://urlgrabber.baseurl.org/. Signed-off-by: Zhigang Wang Acked-by: Ian Jackson Committed-by: Ian Jackson --- tools/misc/Makefile | 2 +- tools/misc/xenpvnetboot | 293 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+), 1 deletion(-) create mode 100755 tools/misc/xenpvnetboot (limited to 'tools/misc') diff --git a/tools/misc/Makefile b/tools/misc/Makefile index 2c691a4329..af380b0a65 100644 --- a/tools/misc/Makefile +++ b/tools/misc/Makefile @@ -18,7 +18,7 @@ SUBDIRS-$(CONFIG_LOMOUNT) += lomount SUBDIRS-$(CONFIG_MINITERM) += miniterm SUBDIRS := $(SUBDIRS-y) -INSTALL_BIN-y := xencons +INSTALL_BIN-y := xencons xenpvnetboot INSTALL_BIN-$(CONFIG_X86) += xen-detect INSTALL_BIN := $(INSTALL_BIN-y) diff --git a/tools/misc/xenpvnetboot b/tools/misc/xenpvnetboot new file mode 100755 index 0000000000..98413f06cf --- /dev/null +++ b/tools/misc/xenpvnetboot @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# +# Copyright (C) 2010 Oracle. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2. This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. 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., 59 Temple Place - Suite 330, Boston, MA 021110-1307, +# USA. + +import sys +import os +import stat +import time +import string +import random +import tempfile +import commands +import subprocess +import urlgrabber +from optparse import OptionParser + + +XEN_PATHS = [ + ('images/xen/vmlinuz', 'images/xen/initrd.img'), # Fedora <= 10 and OL = 5 + ('boot/i386/vmlinuz-xen', 'boot/i386/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 + ('boot/x86_64/vmlinuz-xen', 'boot/x86_64/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 + ('current/images/netboot/xen/vmlinuz', 'current/images/netboot/xen/initrd.gz'), # Debian + ('images/pxeboot/vmlinuz', 'images/pxeboot/initrd.img'), # Fedora >=10 and OL >= 6 + ('isolinux/vmlinuz', 'isolinux/initrd.img'), # Fedora >= 10 and OL >= 6 +] + + +def format_sxp(kernel, ramdisk, args): + s = 'linux (kernel %s)' % kernel + if ramdisk: + s += '(ramdisk %s)' % ramdisk + if args: + s += '(args "%s")' % args + return s + + +def format_simple(kernel, ramdisk, args, sep): + s = ('kernel %s' % kernel) + sep + if ramdisk: + s += ('ramdisk %s' % ramdisk) + sep + if args: + s += ('args %s' % args) + sep + s += sep + return s + + +def mount(dev, path, option=''): + if os.uname()[0] == 'SunOS': + mountcmd = '/usr/sbin/mount' + else: + mountcmd = '/bin/mount' + cmd = ' '.join([mountcmd, option, dev, path]) + (status, output) = commands.getstatusoutput(cmd) + if status != 0: + raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) + + +def umount(path): + if os.uname()[0] == 'SunOS': + cmd = ['/usr/sbin/umount', path] + else: + cmd = ['/bin/umount', path] + subprocess.call(cmd) + + +class Fetcher: + def __init__(self, location, tmpdir): + self.location = location + self.tmpdir = tmpdir + self.srcdir = location + + def prepare(self): + if not os.path.exists(self.tmpdir): + os.makedirs(self.tmpdir, 0750) + + def cleanup(self): + pass + + def get_file(self, filename): + url = os.path.join(self.srcdir, filename) + suffix = ''.join(random.sample(string.ascii_letters, 6)) + local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) + try: + return urlgrabber.urlgrab(url, local_name, copy_local=1) + except Exception, err: + raise RuntimeError('Cannot get file %s: %s' % (url, err)) + + +class MountedFetcher(Fetcher): + def prepare(self): + Fetcher.prepare(self) + self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) + if self.location.startswith('nfs:'): + mount(self.location[4:], self.srcdir, '-o ro') + else: + if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]): + option = '-o ro' + else: + option = '-o ro,loop' + if os.uname()[0] == 'SunOS': + option += ' -F hsfs' + mount(self.location, self.srcdir, option) + + def cleanup(self): + umount(self.srcdir) + try: + os.rmdir(self.srcdir) + except: + pass + + +class NFSISOFetcher(MountedFetcher): + def __init__(self, location, tmpdir): + self.nfsdir = None + MountedFetcher.__init__(self, location, tmpdir) + + def prepare(self): + Fetcher.prepare(self) + self.nfsdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) + self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) + nfs = os.path.dirname(self.location[8:]) + iso = os.path.basename(self.location[8:]) + mount(nfs, self.nfsdir, '-o ro') + option = '-o ro,loop' + if os.uname()[0] == 'SunOS': + option += ' -F hsfs' + mount(os.path.join(self.nfsdir, iso), self.srcdir, option) + + def cleanup(self): + MountedFetcher.cleanup(self) + time.sleep(1) + umount(self.nfsdir) + try: + os.rmdir(self.nfsdir) + except: + pass + + +class TFTPFetcher(Fetcher): + def get_file(self, filename): + if '/' in self.location[7:]: + host = self.location[7:].split('/', 1)[0].replace(':', ' ') + basedir = self.location[7:].split('/', 1)[1] + else: + host = self.location[7:].replace(':', ' ') + basedir = '' + suffix = ''.join(random.sample(string.ascii_letters, 6)) + local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) + cmd = '/usr/bin/tftp %s -c get %s %s' % (host, os.path.join(basedir, filename), local_name) + (status, output) = commands.getstatusoutput(cmd) + if status != 0: + raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) + return local_name + + +def main(): + usage = '''%prog [option] + +Get boot images from the given location and prepare for Xen to use. + +Supported locations: + + - http://host/path + - https://host/path + - ftp://host/path + - file:///path + - tftp://host/path + - nfs:host:/path + - /path + - /path/file.iso + - /path/filesystem.img + - /dev/sda1 + - nfs+iso:host:/path/file.iso + - nfs+iso:host:/path/filesystem.img''' + version = '%prog version 0.1' + parser = OptionParser(usage=usage, version=version) + parser.add_option('', '--location', + help='The base url for kernel and ramdisk files.') + parser.add_option('', '--kernel', + help='The kernel image file.') + parser.add_option('', '--ramdisk', + help='The initial ramdisk file.') + parser.add_option('', '--args', + help='Arguments pass to the kernel.') + parser.add_option('', '--output', + help='Redirect output to this file instead of stdout.') + parser.add_option('', '--output-directory', default='/var/run/libxl', + help='Output directory.') + parser.add_option('', '--output-format', default='sxp', + help='Output format: sxp, simple or simple0.') + parser.add_option('-q', '--quiet', action='store_true', + help='Be quiet.') + (opts, args) = parser.parse_args() + + if not opts.location and not opts.kernel and not opts.ramdisk: + if not opts.quiet: + print >> sys.stderr, 'You should at least specify a location or kernel/ramdisk.' + parser.print_help(sys.stderr) + sys.exit(1) + + if not opts.output or opts.output == '-': + fd = sys.stdout.fileno() + else: + fd = os.open(opts.output, os.O_WRONLY) + + if opts.location: + location = opts.location + else: + location = '' + if (location == '' + or location.startswith('http://') or location.startswith('https://') + or location.startswith('ftp://') or location.startswith('file://') + or (os.path.exists(location) and os.path.isdir(location))): + fetcher = Fetcher(location, opts.output_directory) + elif location.startswith('nfs:') or (os.path.exists(location) and not os.path.isdir(location)): + fetcher = MountedFetcher(location, opts.output_directory) + elif location.startswith('nfs+iso:'): + fetcher = NFSISOFetcher(location, opts.output_directory) + elif location.startswith('tftp://'): + fetcher = TFTPFetcher(location, opts.output_directory) + else: + if not opts.quiet: + print >> sys.stderr, 'Unsupported location: %s' % location + sys.exit(1) + + try: + fetcher.prepare() + except Exception, err: + if not opts.quiet: + print >> sys.stderr, str(err) + fetcher.cleanup() + sys.exit(1) + + try: + kernel = None + if opts.kernel: + kernel = fetcher.get_file(opts.kernel) + else: + for (kernel_path, _) in XEN_PATHS: + try: + kernel = fetcher.get_file(kernel_path) + except Exception, err: + if not opts.quiet: + print >> sys.stderr, str(err) + continue + break + + if not kernel: + if not opts.quiet: + print >> sys.stderr, 'Cannot get kernel from loacation: %s' % location + sys.exit(1) + + ramdisk = None + if opts.ramdisk: + ramdisk = fetcher.get_file(opts.ramdisk) + else: + for (_, ramdisk_path) in XEN_PATHS: + try: + ramdisk = fetcher.get_file(ramdisk_path) + except Exception, err: + if not opts.quiet: + print >> sys.stderr, str(err) + continue + break + finally: + fetcher.cleanup() + + if opts.output_format == 'sxp': + output = format_sxp(kernel, ramdisk, opts.args) + elif opts.output_format == 'simple': + output = format_simple(kernel, ramdisk, opts.args, '\n') + elif opts.output_format == 'simple0': + output = format_simple(kernel, ramdisk, opts.args, '\0') + else: + print >> sys.stderr, 'Unknown output format: %s' % opts.output_format + sys.exit(1) + + sys.stdout.flush() + os.write(fd, output) + + +if __name__ == '__main__': + main() -- cgit v1.2.3