aboutsummaryrefslogtreecommitdiffstats
path: root/tools/misc
diff options
context:
space:
mode:
authorZhigang Wang <zhigang.x.wang@oracle.com>2012-03-01 18:54:37 +0000
committerZhigang Wang <zhigang.x.wang@oracle.com>2012-03-01 18:54:37 +0000
commitb129ff2b0427614fa1bdfbfc73a2e4737f3e71f3 (patch)
tree616ecf73a89918d3b7d713fede5ed949629487e0 /tools/misc
parent0c6cc72d62909f1fc7e876be4a0e63addb3425b0 (diff)
downloadxen-b129ff2b0427614fa1bdfbfc73a2e4737f3e71f3.tar.gz
xen-b129ff2b0427614fa1bdfbfc73a2e4737f3e71f3.tar.bz2
xen-b129ff2b0427614fa1bdfbfc73a2e4737f3e71f3.zip
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 <zhigang.x.wang@oracle.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
Diffstat (limited to 'tools/misc')
-rw-r--r--tools/misc/Makefile2
-rwxr-xr-xtools/misc/xenpvnetboot293
2 files changed, 294 insertions, 1 deletions
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()