1#!/usr/bin/env python 2# 3# Copyright (C) 2010 Oracle. All rights reserved. 4# 5# This program is free software; you can redistribute it and/or modify it under 6# the terms of the GNU General Public License as published by the Free Software 7# Foundation, version 2. This program is distributed in the hope that it will be 8# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 10# Public License for more details. You should have received a copy of the GNU 11# General Public License along with this program; If not, see <http://www.gnu.org/licenses/>. 12 13import sys 14import os 15import stat 16import time 17import string 18import random 19import tempfile 20import commands 21import subprocess 22import urlgrabber 23from optparse import OptionParser 24 25 26XEN_PATHS = [ 27 ('images/xen/vmlinuz', 'images/xen/initrd.img'), # Fedora <= 10 and OL = 5 28 ('boot/i386/vmlinuz-xen', 'boot/i386/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 29 ('boot/x86_64/vmlinuz-xen', 'boot/x86_64/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 30 ('current/images/netboot/xen/vmlinuz', 'current/images/netboot/xen/initrd.gz'), # Debian 31 ('images/pxeboot/vmlinuz', 'images/pxeboot/initrd.img'), # Fedora >=10 and OL >= 6 32 ('isolinux/vmlinuz', 'isolinux/initrd.img'), # Fedora >= 10 and OL >= 6 33] 34 35 36def format_sxp(kernel, ramdisk, args): 37 s = 'linux (kernel %s)' % kernel 38 if ramdisk: 39 s += '(ramdisk %s)' % ramdisk 40 if args: 41 s += '(args "%s")' % args 42 return s 43 44 45def format_simple(kernel, ramdisk, args, sep): 46 s = ('kernel %s' % kernel) + sep 47 if ramdisk: 48 s += ('ramdisk %s' % ramdisk) + sep 49 if args: 50 s += ('args %s' % args) + sep 51 s += sep 52 return s 53 54 55def mount(dev, path, option=''): 56 if os.uname()[0] == 'SunOS': 57 mountcmd = '/usr/sbin/mount' 58 else: 59 mountcmd = '/bin/mount' 60 cmd = ' '.join([mountcmd, option, dev, path]) 61 (status, output) = commands.getstatusoutput(cmd) 62 if status != 0: 63 raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) 64 65 66def umount(path): 67 if os.uname()[0] == 'SunOS': 68 cmd = ['/usr/sbin/umount', path] 69 else: 70 cmd = ['/bin/umount', path] 71 subprocess.call(cmd) 72 73 74class Fetcher: 75 def __init__(self, location, tmpdir): 76 self.location = location 77 self.tmpdir = tmpdir 78 self.srcdir = location 79 80 def prepare(self): 81 if not os.path.exists(self.tmpdir): 82 os.makedirs(self.tmpdir, 0750) 83 84 def cleanup(self): 85 pass 86 87 def get_file(self, filename): 88 url = os.path.join(self.srcdir, filename) 89 suffix = ''.join(random.sample(string.ascii_letters, 6)) 90 local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) 91 try: 92 return urlgrabber.urlgrab(url, local_name, copy_local=1) 93 except Exception, err: 94 raise RuntimeError('Cannot get file %s: %s' % (url, err)) 95 96 97class MountedFetcher(Fetcher): 98 def prepare(self): 99 Fetcher.prepare(self) 100 self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) 101 if self.location.startswith('nfs:'): 102 mount(self.location[4:], self.srcdir, '-o ro') 103 else: 104 if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]): 105 option = '-o ro' 106 else: 107 option = '-o ro,loop' 108 if os.uname()[0] == 'SunOS': 109 option += ' -F hsfs' 110 mount(self.location, self.srcdir, option) 111 112 def cleanup(self): 113 umount(self.srcdir) 114 try: 115 os.rmdir(self.srcdir) 116 except: 117 pass 118 119 120class NFSISOFetcher(MountedFetcher): 121 def __init__(self, location, tmpdir): 122 self.nfsdir = None 123 MountedFetcher.__init__(self, location, tmpdir) 124 125 def prepare(self): 126 Fetcher.prepare(self) 127 self.nfsdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) 128 self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) 129 nfs = os.path.dirname(self.location[8:]) 130 iso = os.path.basename(self.location[8:]) 131 mount(nfs, self.nfsdir, '-o ro') 132 option = '-o ro,loop' 133 if os.uname()[0] == 'SunOS': 134 option += ' -F hsfs' 135 mount(os.path.join(self.nfsdir, iso), self.srcdir, option) 136 137 def cleanup(self): 138 MountedFetcher.cleanup(self) 139 time.sleep(1) 140 umount(self.nfsdir) 141 try: 142 os.rmdir(self.nfsdir) 143 except: 144 pass 145 146 147class TFTPFetcher(Fetcher): 148 def get_file(self, filename): 149 if '/' in self.location[7:]: 150 host = self.location[7:].split('/', 1)[0].replace(':', ' ') 151 basedir = self.location[7:].split('/', 1)[1] 152 else: 153 host = self.location[7:].replace(':', ' ') 154 basedir = '' 155 suffix = ''.join(random.sample(string.ascii_letters, 6)) 156 local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) 157 cmd = '/usr/bin/tftp %s -c get %s %s' % (host, os.path.join(basedir, filename), local_name) 158 (status, output) = commands.getstatusoutput(cmd) 159 if status != 0: 160 raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) 161 return local_name 162 163 164def main(): 165 usage = '''%prog [option] 166 167Get boot images from the given location and prepare for Xen to use. 168 169Supported locations: 170 171 - http://host/path 172 - https://host/path 173 - ftp://host/path 174 - file:///path 175 - tftp://host/path 176 - nfs:host:/path 177 - /path 178 - /path/file.iso 179 - /path/filesystem.img 180 - /dev/sda1 181 - nfs+iso:host:/path/file.iso 182 - nfs+iso:host:/path/filesystem.img''' 183 version = '%prog version 0.1' 184 parser = OptionParser(usage=usage, version=version) 185 parser.add_option('', '--location', 186 help='The base url for kernel and ramdisk files.') 187 parser.add_option('', '--kernel', 188 help='The kernel image file.') 189 parser.add_option('', '--ramdisk', 190 help='The initial ramdisk file.') 191 parser.add_option('', '--args', 192 help='Arguments pass to the kernel.') 193 parser.add_option('', '--output', 194 help='Redirect output to this file instead of stdout.') 195 parser.add_option('', '--output-directory', default='/var/run/libxl', 196 help='Output directory.') 197 parser.add_option('', '--output-format', default='sxp', 198 help='Output format: sxp, simple or simple0.') 199 parser.add_option('-q', '--quiet', action='store_true', 200 help='Be quiet.') 201 (opts, args) = parser.parse_args() 202 203 if not opts.location and not opts.kernel and not opts.ramdisk: 204 if not opts.quiet: 205 print >> sys.stderr, 'You should at least specify a location or kernel/ramdisk.' 206 parser.print_help(sys.stderr) 207 sys.exit(1) 208 209 if not opts.output or opts.output == '-': 210 fd = sys.stdout.fileno() 211 else: 212 fd = os.open(opts.output, os.O_WRONLY) 213 214 if opts.location: 215 location = opts.location 216 else: 217 location = '' 218 if (location == '' 219 or location.startswith('http://') or location.startswith('https://') 220 or location.startswith('ftp://') or location.startswith('file://') 221 or (os.path.exists(location) and os.path.isdir(location))): 222 fetcher = Fetcher(location, opts.output_directory) 223 elif location.startswith('nfs:') or (os.path.exists(location) and not os.path.isdir(location)): 224 fetcher = MountedFetcher(location, opts.output_directory) 225 elif location.startswith('nfs+iso:'): 226 fetcher = NFSISOFetcher(location, opts.output_directory) 227 elif location.startswith('tftp://'): 228 fetcher = TFTPFetcher(location, opts.output_directory) 229 else: 230 if not opts.quiet: 231 print >> sys.stderr, 'Unsupported location: %s' % location 232 sys.exit(1) 233 234 try: 235 fetcher.prepare() 236 except Exception, err: 237 if not opts.quiet: 238 print >> sys.stderr, str(err) 239 fetcher.cleanup() 240 sys.exit(1) 241 242 try: 243 kernel = None 244 if opts.kernel: 245 kernel = fetcher.get_file(opts.kernel) 246 else: 247 for (kernel_path, _) in XEN_PATHS: 248 try: 249 kernel = fetcher.get_file(kernel_path) 250 except Exception, err: 251 if not opts.quiet: 252 print >> sys.stderr, str(err) 253 continue 254 break 255 256 if not kernel: 257 if not opts.quiet: 258 print >> sys.stderr, 'Cannot get kernel from loacation: %s' % location 259 sys.exit(1) 260 261 ramdisk = None 262 if opts.ramdisk: 263 ramdisk = fetcher.get_file(opts.ramdisk) 264 else: 265 for (_, ramdisk_path) in XEN_PATHS: 266 try: 267 ramdisk = fetcher.get_file(ramdisk_path) 268 except Exception, err: 269 if not opts.quiet: 270 print >> sys.stderr, str(err) 271 continue 272 break 273 finally: 274 fetcher.cleanup() 275 276 if opts.output_format == 'sxp': 277 output = format_sxp(kernel, ramdisk, opts.args) 278 elif opts.output_format == 'simple': 279 output = format_simple(kernel, ramdisk, opts.args, '\n') 280 elif opts.output_format == 'simple0': 281 output = format_simple(kernel, ramdisk, opts.args, '\0') 282 else: 283 print >> sys.stderr, 'Unknown output format: %s' % opts.output_format 284 sys.exit(1) 285 286 sys.stdout.flush() 287 os.write(fd, output) 288 289 290if __name__ == '__main__': 291 main() 292