switch to using rd.live.image instead of liveimg
[livecd.git] / imgcreate / live.py
blob88c70349a4ecb91c758cab3b57fbb75459f23aef
2 # live.py : LiveImageCreator class for creating Live CD images
4 # Copyright 2007, Red Hat Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Library General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 import os
20 import os.path
21 import glob
22 import shutil
23 import subprocess
24 import logging
25 import re
27 from imgcreate.errors import *
28 from imgcreate.fs import *
29 from imgcreate.creator import *
31 class LiveImageCreatorBase(LoopImageCreator):
32 """A base class for LiveCD image creators.
34 This class serves as a base class for the architecture-specific LiveCD
35 image creator subclass, LiveImageCreator.
37 LiveImageCreator creates a bootable ISO containing the system image,
38 bootloader, bootloader configuration, kernel and initramfs.
40 """
42 def __init__(self, ks, name, fslabel=None, releasever=None, tmpdir="/tmp",
43 title="Linux", product="Linux", useplugins=False, cacheonly=False):
44 """Initialise a LiveImageCreator instance.
46 This method takes the same arguments as LoopImageCreator.__init__().
48 """
49 LoopImageCreator.__init__(self, ks, name,
50 fslabel=fslabel,
51 releasever=releasever,
52 tmpdir=tmpdir,
53 useplugins=useplugins, cacheonly=cacheonly)
55 self.compress_type = "xz"
56 """mksquashfs compressor to use."""
58 self.skip_compression = False
59 """Controls whether to use squashfs to compress the image."""
61 self.skip_minimize = False
62 """Controls whether an image minimizing snapshot should be created.
64 This snapshot can be used when copying the system image from the ISO in
65 order to minimize the amount of data that needs to be copied; simply,
66 it makes it possible to create a version of the image's filesystem with
67 no spare space.
69 """
71 self._timeout = kickstart.get_timeout(self.ks, 10)
72 """The bootloader timeout from kickstart."""
74 self._default_kernel = kickstart.get_default_kernel(self.ks, "kernel")
75 """The default kernel type from kickstart."""
77 self.__isodir = None
79 self.__modules = ["=ata", "sym53c8xx", "aic7xxx", "=usb", "=firewire",
80 "=mmc", "=pcmcia", "mptsas", "udf", "virtio_blk",
81 "virtio_pci"]
82 self.__modules.extend(kickstart.get_modules(self.ks))
84 self._isofstype = "iso9660"
85 self.base_on = False
87 self.title = title
88 self.product = product
91 # Hooks for subclasses
93 def _configure_bootloader(self, isodir):
94 """Create the architecture specific booloader configuration.
96 This is the hook where subclasses must create the booloader
97 configuration in order to allow a bootable ISO to be built.
99 isodir -- the directory where the contents of the ISO are to be staged
102 raise CreatorError("Bootloader configuration is arch-specific, "
103 "but not implemented for this arch!")
105 def _get_kernel_options(self):
106 """Return a kernel options string for bootloader configuration.
108 This is the hook where subclasses may specify a set of kernel options
109 which should be included in the images bootloader configuration.
111 A sensible default implementation is provided.
114 r = kickstart.get_kernel_args(self.ks)
115 if os.path.exists(self._instroot + "/usr/bin/rhgb"):
116 r += " rhgb"
117 if os.path.exists(self._instroot + "/usr/bin/plymouth"):
118 r += " rhgb"
119 return r
121 def _get_mkisofs_options(self, isodir):
122 """Return the architecture specific mkisosfs options.
124 This is the hook where subclasses may specify additional arguments to
125 mkisofs, e.g. to enable a bootable ISO to be built.
127 By default, an empty list is returned.
130 return []
133 # Helpers for subclasses
135 def _has_checkisomd5(self):
136 """Check whether checkisomd5 is available in the install root."""
137 def exists(instroot, path):
138 return os.path.exists(instroot + path)
140 if (exists(self._instroot, "/usr/lib/anaconda-runtime/checkisomd5") or
141 exists(self._instroot, "/usr/bin/checkisomd5")):
142 return True
144 return False
147 # Actual implementation
149 def _base_on(self, base_on):
150 """helper function to extract ext3 file system from a live CD ISO"""
151 isoloop = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
153 try:
154 isoloop.mount()
155 except MountError, e:
156 raise CreatorError("Failed to loopback mount '%s' : %s" %
157 (base_on, e))
159 # Copy the initrd%d.img and xen%d.gz files over to /isolinux
160 # This is because the originals in /boot are removed when the
161 # original .iso was created.
162 src = isoloop.mountdir + "/isolinux/"
163 dest = self.__ensure_isodir() + "/isolinux/"
164 makedirs(dest)
165 pattern = re.compile(r"(initrd\d+\.img|xen\d+\.gz)")
166 files = [f for f in os.listdir(src) if pattern.search(f)
167 and os.path.isfile(src+f)]
168 for f in files:
169 shutil.copyfile(src+f, dest+f)
171 # legacy LiveOS filesystem layout support, remove for F9 or F10
172 if os.path.exists(isoloop.mountdir + "/squashfs.img"):
173 squashimg = isoloop.mountdir + "/squashfs.img"
174 else:
175 squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
177 squashloop = DiskMount(LoopbackDisk(squashimg, 0), self._mkdtemp(), "squashfs")
179 # 'self.compress_type = None' will force reading it from base_on.
180 if self.compress_type is None:
181 self.compress_type = squashfs_compression_type(squashimg)
182 if self.compress_type == 'undetermined':
183 # Default to 'gzip' for compatibility with older versions.
184 self.compress_type = 'gzip'
185 try:
186 if not squashloop.disk.exists():
187 raise CreatorError("'%s' is not a valid live CD ISO : "
188 "squashfs.img doesn't exist" % base_on)
190 try:
191 squashloop.mount()
192 except MountError, e:
193 raise CreatorError("Failed to loopback mount squashfs.img "
194 "from '%s' : %s" % (base_on, e))
196 # legacy LiveOS filesystem layout support, remove for F9 or F10
197 if os.path.exists(squashloop.mountdir + "/os.img"):
198 os_image = squashloop.mountdir + "/os.img"
199 else:
200 os_image = squashloop.mountdir + "/LiveOS/ext3fs.img"
202 if not os.path.exists(os_image):
203 raise CreatorError("'%s' is not a valid live CD ISO : neither "
204 "LiveOS/ext3fs.img nor os.img exist" %
205 base_on)
207 try:
208 shutil.copyfile(os_image, self._image)
209 except IOError, e:
210 raise CreatorError("Failed to copy base live image to %s for modification: %s" %(self._image, e))
211 finally:
212 squashloop.cleanup()
213 isoloop.cleanup()
215 def _mount_instroot(self, base_on = None):
216 self.base_on = True
217 LoopImageCreator._mount_instroot(self, base_on)
218 self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd")
219 self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
221 def _unmount_instroot(self):
222 self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd")
223 self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
224 LoopImageCreator._unmount_instroot(self)
226 def __ensure_isodir(self):
227 if self.__isodir is None:
228 self.__isodir = self._mkdtemp("iso-")
229 return self.__isodir
231 def _generate_efiboot(self, isodir):
232 """Generate EFI boot images."""
233 if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"):
234 return False
235 subprocess.call(["mkefiboot", isodir + "/EFI/BOOT",
236 isodir + "/isolinux/efiboot.img"])
237 subprocess.call(["mkefiboot", "-a", isodir + "/EFI/BOOT",
238 isodir + "/isolinux/macboot.img", "-l", self.product,
239 "-n", "/usr/share/pixmaps/bootloader/fedora-media.vol",
240 "-i", "/usr/share/pixmaps/bootloader/fedora.icns",
241 "-p", self.product])
243 def _create_bootconfig(self):
244 """Configure the image so that it's bootable."""
245 self._configure_bootloader(self.__ensure_isodir())
246 self._generate_efiboot(self.__ensure_isodir())
248 def _get_post_scripts_env(self, in_chroot):
249 env = LoopImageCreator._get_post_scripts_env(self, in_chroot)
251 if not in_chroot:
252 env["LIVE_ROOT"] = self.__ensure_isodir()
254 return env
256 def __extra_filesystems(self):
257 return "squashfs ext4 ext3 ext2 vfat msdos ";
259 def __extra_drivers(self):
260 retval = "sr_mod sd_mod ide-cd cdrom "
261 for module in self.__modules:
262 if module == "=usb":
263 retval = retval + "ehci_hcd uhci_hcd ohci_hcd "
264 retval = retval + "usb_storage usbhid "
265 elif module == "=firewire":
266 retval = retval + "firewire-sbp2 firewire-ohci "
267 retval = retval + "sbp2 ohci1394 ieee1394 "
268 elif module == "=mmc":
269 retval = retval + "mmc_block sdhci sdhci-pci "
270 elif module == "=pcmcia":
271 retval = retval + "pata_pcmcia "
272 else:
273 retval = retval + module + " "
274 return retval
276 def __restore_file(self,path):
277 try:
278 os.unlink(path)
279 except:
280 pass
281 if os.path.exists(path + '.rpmnew'):
282 os.rename(path + '.rpmnew', path)
284 def __write_initrd_conf(self, path):
285 if not os.path.exists(os.path.dirname(path)):
286 makedirs(os.path.dirname(path))
287 f = open(path, "a")
288 f.write('LIVEOS="yes"\n')
289 f.write('PROBE="no"\n')
290 f.write('MODULES+="' + self.__extra_filesystems() + '"\n')
291 f.write('MODULES+="' + self.__extra_drivers() + '"\n')
292 f.close()
294 def __write_dracut_conf(self, path):
295 if not os.path.exists(os.path.dirname(path)):
296 makedirs(os.path.dirname(path))
297 f = open(path, "a")
298 f.write('drivers+="' + self.__extra_drivers() + ' "\n')
299 f.write('add_dracutmodules+=" dmsquash-live "')
300 f.close()
302 def __create_iso(self, isodir):
303 iso = self._outdir + "/" + self.name + ".iso"
305 args = ["/usr/bin/mkisofs",
306 "-J", "-r",
307 "-hide-rr-moved", "-hide-joliet-trans-tbl",
308 "-V", self.fslabel,
309 "-o", iso]
311 args.extend(self._get_mkisofs_options(isodir))
312 if self._isofstype == "udf":
313 args.append("-allow-limited-size")
315 args.append(isodir)
317 if subprocess.call(args) != 0:
318 raise CreatorError("ISO creation failed!")
320 if os.path.exists("/usr/bin/isohybrid"):
321 if os.path.exists(isodir + "/isolinux/efiboot.img"):
322 subprocess.call(["/usr/bin/isohybrid", "-u", "-m", iso])
323 else:
324 subprocess.call(["/usr/bin/isohybrid", iso])
326 self.__implant_md5sum(iso)
328 def __implant_md5sum(self, iso):
329 """Implant an isomd5sum."""
330 if os.path.exists("/usr/bin/implantisomd5"):
331 implantisomd5 = "/usr/bin/implantisomd5"
332 elif os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"):
333 implantisomd5 = "/usr/lib/anaconda-runtime/implantisomd5"
334 else:
335 logging.warn("isomd5sum not installed; not setting up mediacheck")
336 return
338 subprocess.call([implantisomd5, iso])
340 def _stage_final_image(self):
341 try:
342 makedirs(self.__ensure_isodir() + "/LiveOS")
344 self._resparse()
346 if not self.skip_minimize:
347 create_image_minimizer(self.__isodir + "/LiveOS/osmin.img",
348 self._image, self.compress_type,
349 tmpdir = self.tmpdir)
351 if self.skip_compression:
352 shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
353 if os.stat(self.__isodir + "/LiveOS/ext3fs.img").st_size >= 4*1024*1024*1024:
354 self._isofstype = "udf"
355 logging.warn("Switching to UDF due to size of LiveOS/ext3fs.img")
356 else:
357 makedirs(os.path.join(os.path.dirname(self._image), "LiveOS"))
358 shutil.move(self._image,
359 os.path.join(os.path.dirname(self._image),
360 "LiveOS", "ext3fs.img"))
361 mksquashfs(os.path.dirname(self._image),
362 self.__isodir + "/LiveOS/squashfs.img",
363 self.compress_type)
364 if os.stat(self.__isodir + "/LiveOS/squashfs.img").st_size >= 4*1024*1024*1024:
365 self._isofstype = "udf"
366 logging.warn("Switching to UDF due to size of LiveOS/squashfs.img")
369 self.__create_iso(self.__isodir)
370 finally:
371 shutil.rmtree(self.__isodir, ignore_errors = True)
372 self.__isodir = None
374 class x86LiveImageCreator(LiveImageCreatorBase):
375 """ImageCreator for x86 machines"""
376 def _get_mkisofs_options(self, isodir):
377 options = [ "-b", "isolinux/isolinux.bin",
378 "-c", "isolinux/boot.cat",
379 "-no-emul-boot", "-boot-info-table",
380 "-boot-load-size", "4" ]
381 if os.path.exists(isodir + "/isolinux/efiboot.img"):
382 options.extend([ "-eltorito-alt-boot",
383 "-e", "isolinux/efiboot.img",
384 "-no-emul-boot",
385 "-eltorito-alt-boot",
386 "-e", "isolinux/macboot.img",
387 "-no-emul-boot"])
388 return options
390 def _get_required_packages(self):
391 return ["syslinux"] + LiveImageCreatorBase._get_required_packages(self)
393 def _get_isolinux_stanzas(self, isodir):
394 return ""
396 def __find_syslinux_menu(self):
397 for menu in ("vesamenu.c32", "menu.c32"):
398 for dir in ("/usr/lib/syslinux/", "/usr/share/syslinux/"):
399 if os.path.isfile(self._instroot + dir + menu):
400 return menu
402 raise CreatorError("syslinux not installed : "
403 "no suitable *menu.c32 found")
405 def __find_syslinux_mboot(self):
407 # We only need the mboot module if we have any xen hypervisors
409 if not glob.glob(self._instroot + "/boot/xen.gz*"):
410 return None
412 return "mboot.c32"
414 def __copy_syslinux_files(self, isodir, menu, mboot = None):
415 files = ["isolinux.bin", menu]
416 if mboot:
417 files += [mboot]
419 for f in files:
420 if os.path.exists(self._instroot + "/usr/lib/syslinux/" + f):
421 path = self._instroot + "/usr/lib/syslinux/" + f
422 elif os.path.exists(self._instroot + "/usr/share/syslinux/" + f):
423 path = self._instroot + "/usr/share/syslinux/" + f
424 if not os.path.isfile(path):
425 raise CreatorError("syslinux not installed : "
426 "%s not found" % path)
428 shutil.copy(path, isodir + "/isolinux/")
430 def __copy_syslinux_background(self, isodest):
431 background_path = self._instroot + \
432 "/usr/share/anaconda/boot/syslinux-vesa-splash.jpg"
434 if not os.path.exists(background_path):
435 # fallback to F13 location
436 background_path = self._instroot + \
437 "/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg"
439 if not os.path.exists(background_path):
440 return False
442 shutil.copyfile(background_path, isodest)
444 return True
446 def __copy_kernel_and_initramfs(self, isodir, version, index):
447 bootdir = self._instroot + "/boot"
449 shutil.copyfile(bootdir + "/vmlinuz-" + version,
450 isodir + "/isolinux/vmlinuz" + index)
452 isDracut = False
453 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
454 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
455 isodir + "/isolinux/initrd" + index + ".img")
456 isDracut = True
457 elif os.path.exists(bootdir + "/initrd-" + version + ".img"):
458 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
459 isodir + "/isolinux/initrd" + index + ".img")
460 elif not self.base_on:
461 logging.error("No initrd or initramfs found for %s" % (version,))
463 is_xen = False
464 if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
465 shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
466 isodir + "/isolinux/xen" + index + ".gz")
467 is_xen = True
469 return (is_xen, isDracut)
471 def __is_default_kernel(self, kernel, kernels):
472 if len(kernels) == 1:
473 return True
475 if kernel == self._default_kernel:
476 return True
478 if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
479 return True
481 return False
483 def __get_basic_syslinux_config(self, **args):
484 return """
485 default %(menu)s
486 timeout %(timeout)d
487 menu background %(background)s
488 menu autoboot Starting %(title)s in # second{,s}. Press any key to interrupt.
490 menu clear
491 menu title %(title)s
492 menu vshift 8
493 menu rows 18
494 menu margin 8
495 #menu hidden
496 menu helpmsgrow 15
497 menu tabmsgrow 13
499 menu color border * #00000000 #00000000 none
500 menu color sel 0 #ffffffff #00000000 none
501 menu color title 0 #ff7ba3d0 #00000000 none
502 menu color tabmsg 0 #ff3a6496 #00000000 none
503 menu color unsel 0 #84b8ffff #00000000 none
504 menu color hotsel 0 #84b8ffff #00000000 none
505 menu color hotkey 0 #ffffffff #00000000 none
506 menu color help 0 #ffffffff #00000000 none
507 menu color scrollbar 0 #ffffffff #ff355594 none
508 menu color timeout 0 #ffffffff #00000000 none
509 menu color timeout_msg 0 #ffffffff #00000000 none
510 menu color cmdmark 0 #84b8ffff #00000000 none
511 menu color cmdline 0 #ffffffff #00000000 none
513 menu tabmsg Press Tab for full configuration options on menu items.
514 menu separator
515 """ % args
517 def __get_image_stanza(self, is_xen, isDracut, **args):
518 if isDracut:
519 args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args
520 else:
521 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
523 if not is_xen:
524 template = """label %(short)s
525 menu label %(long)s
526 kernel vmlinuz%(index)s
527 append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
529 else:
530 template = """label %(short)s
531 menu label %(long)s
532 kernel mboot.c32
533 append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s --- initrd%(index)s.img
535 if args.get("help"):
536 template += """ text help
537 %(help)s
538 endtext
540 return template % args
542 def __get_image_stanzas(self, isodir):
543 versions = []
544 kernels = self._get_kernel_versions()
545 for kernel in kernels:
546 for version in kernels[kernel]:
547 versions.append(version)
549 kernel_options = self._get_kernel_options()
551 checkisomd5 = self._has_checkisomd5()
553 # Stanzas for insertion into the config template
554 linux = []
555 basic = []
556 check = []
558 index = "0"
559 for version in versions:
560 (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index)
561 if index == "0":
562 self._isDracut = isDracut
564 default = self.__is_default_kernel(kernel, kernels)
566 if default:
567 long = self.product
568 elif kernel.startswith("kernel-"):
569 long = "%s (%s)" % (self.product, kernel[7:])
570 else:
571 long = "%s (%s)" % (self.product, kernel)
573 # tell dracut not to ask for LUKS passwords or activate mdraid sets
574 if isDracut:
575 kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0"
576 else:
577 kern_opts = kernel_options
579 linux.append(self.__get_image_stanza(is_xen, isDracut,
580 fslabel = self.fslabel,
581 isofstype = "auto",
582 liveargs = kern_opts,
583 long = "^Start " + long,
584 short = "linux" + index,
585 extra = "",
586 help = "",
587 index = index))
589 if default:
590 linux[-1] += " menu default\n"
592 basic.append(self.__get_image_stanza(is_xen, isDracut,
593 fslabel = self.fslabel,
594 isofstype = "auto",
595 liveargs = kern_opts,
596 long = "Start " + long + " in ^basic graphics mode.",
597 short = "basic" + index,
598 extra = "xdriver=vesa nomodeset",
599 help = "Try this option out if you're having trouble starting.",
600 index = index))
602 if checkisomd5:
603 check.append(self.__get_image_stanza(is_xen, isDracut,
604 fslabel = self.fslabel,
605 isofstype = "auto",
606 liveargs = kern_opts,
607 long = "^Test this media & start " + long,
608 short = "check" + index,
609 extra = "rd.live.check",
610 help = "",
611 index = index))
612 else:
613 check.append(None)
615 index = str(int(index) + 1)
617 return (linux, basic, check)
619 def __get_memtest_stanza(self, isodir):
620 memtest = glob.glob(self._instroot + "/boot/memtest86*")
621 if not memtest:
622 return ""
624 shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
626 return """label memtest
627 menu label Run a ^memory test.
628 text help
629 If your system is having issues, an problem with your
630 system's memory may be the cause. Use this utility to
631 see if the memory is working correctly.
632 endtext
633 kernel memtest
636 def __get_local_stanza(self, isodir):
637 return """label local
638 menu label Boot from ^local drive
639 localboot 0xffff
642 def _configure_syslinux_bootloader(self, isodir):
643 """configure the boot loader"""
644 makedirs(isodir + "/isolinux")
646 menu = self.__find_syslinux_menu()
648 self.__copy_syslinux_files(isodir, menu,
649 self.__find_syslinux_mboot())
651 background = ""
652 if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
653 background = "splash.jpg"
655 cfg = self.__get_basic_syslinux_config(menu = menu,
656 background = background,
657 title = self.title,
658 timeout = self._timeout * 10)
659 cfg += "menu separator\n"
661 linux, basic, check = self.__get_image_stanzas(isodir)
662 # Add linux stanzas to main menu
663 for s in linux:
664 cfg += s
665 cfg += "menu separator\n"
667 cfg += """menu begin ^Troubleshooting
668 menu title Troubleshooting
670 # Add basic video and check to submenu
671 for b, c in zip(basic, check):
672 cfg += b
673 if c:
674 cfg += c
676 cfg += self.__get_memtest_stanza(isodir)
677 cfg += "menu separator\n"
679 cfg += self.__get_local_stanza(isodir)
680 cfg += self._get_isolinux_stanzas(isodir)
682 cfg += """menu separator
683 label returntomain
684 menu label Return to ^main menu.
685 menu exit
686 menu end
688 cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
689 cfgf.write(cfg)
690 cfgf.close()
692 def __copy_efi_files(self, isodir):
693 if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"):
694 return False
695 shutil.copy(self._instroot + "/boot/efi/EFI/redhat/grub.efi",
696 isodir + "/EFI/BOOT/grub.efi")
698 # Should exist, but if it doesn't we should fail
699 if os.path.exists(self._instroot + "/boot/grub/splash.xpm.gz"):
700 shutil.copy(self._instroot + "/boot/grub/splash.xpm.gz",
701 isodir + "/EFI/BOOT/splash.xpm.gz")
703 return True
705 def __get_basic_efi_config(self, **args):
706 return """
707 default=0
708 splashimage=/EFI/BOOT/splash.xpm.gz
709 timeout %(timeout)d
710 hiddenmenu
712 """ %args
714 def __get_efi_image_stanza(self, **args):
715 if self._isDracut:
716 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
717 else:
718 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
719 return """title %(long)s
720 findiso
721 kernel /isolinux/vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
722 initrd /isolinux/initrd%(index)s.img
723 """ %args
725 def __get_efi_image_stanzas(self, isodir, name):
726 # FIXME: this only supports one kernel right now...
728 kernel_options = self._get_kernel_options()
729 checkisomd5 = self._has_checkisomd5()
731 cfg = ""
733 for index in range(0, 9):
734 # we don't support xen kernels
735 if os.path.exists("%s/EFI/BOOT/xen%d.gz" %(isodir, index)):
736 continue
737 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
738 isofstype = "auto",
739 liveargs = kernel_options,
740 long = name,
741 extra = "", index = index)
742 if checkisomd5:
743 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
744 isofstype = "auto",
745 liveargs = kernel_options,
746 long = "Verify and Boot " + name,
747 extra = "rd.live.check",
748 index = index)
749 break
751 return cfg
753 def _configure_efi_bootloader(self, isodir):
754 """Set up the configuration for an EFI bootloader"""
755 makedirs(isodir + "/EFI/BOOT")
757 if not self.__copy_efi_files(isodir):
758 shutil.rmtree(isodir + "/EFI")
759 return
761 cfg = self.__get_basic_efi_config(name = self.name,
762 timeout = self._timeout)
763 cfg += self.__get_efi_image_stanzas(isodir, self.name)
765 cfgf = open(isodir + "/EFI/BOOT/grub.conf", "w")
766 cfgf.write(cfg)
767 cfgf.close()
769 # first gen mactel machines get the bootloader name wrong apparently
770 if rpmUtils.arch.getBaseArch() == "i386":
771 os.link(isodir + "/EFI/BOOT/grub.efi", isodir + "/EFI/BOOT/BOOT.efi")
772 os.link(isodir + "/EFI/BOOT/grub.conf", isodir + "/EFI/BOOT/BOOT.conf")
774 # for most things, we want them named boot$efiarch
775 efiarch = {"i386": "IA32", "x86_64": "X64"}
776 efiname = efiarch[rpmUtils.arch.getBaseArch()]
777 os.rename(isodir + "/EFI/BOOT/grub.efi", isodir + "/EFI/BOOT/BOOT%s.efi" %(efiname,))
778 os.link(isodir + "/EFI/BOOT/grub.conf", isodir + "/EFI/BOOT/BOOT%s.conf" %(efiname,))
781 def _configure_bootloader(self, isodir):
782 self._configure_syslinux_bootloader(isodir)
783 self._configure_efi_bootloader(isodir)
785 class ppcLiveImageCreator(LiveImageCreatorBase):
786 def _get_mkisofs_options(self, isodir):
787 return [ "-hfs", "-no-desktop", "-part",
788 "-map", isodir + "/ppc/mapping",
789 "-hfs-bless", isodir + "/ppc/mac",
790 "-hfs-volid", self.fslabel ]
792 def _get_required_packages(self):
793 return ["yaboot"] + \
794 LiveImageCreatorBase._get_required_packages(self)
796 def _get_excluded_packages(self):
797 # kind of hacky, but exclude memtest86+ on ppc so it can stay in cfg
798 return ["memtest86+"] + \
799 LiveImageCreatorBase._get_excluded_packages(self)
801 def __copy_boot_file(self, destdir, file):
802 for dir in ["/usr/share/ppc64-utils",
803 "/usr/lib/anaconda-runtime/boot"]:
804 path = self._instroot + dir + "/" + file
805 if not os.path.exists(path):
806 continue
808 makedirs(destdir)
809 shutil.copy(path, destdir)
810 return
812 raise CreatorError("Unable to find boot file " + file)
814 def __kernel_bits(self, kernel):
815 testpath = (self._instroot + "/lib/modules/" +
816 kernel + "/kernel/arch/powerpc/platforms")
818 if not os.path.exists(testpath):
819 return { "32" : True, "64" : False }
820 else:
821 return { "32" : False, "64" : True }
823 def __copy_kernel_and_initramfs(self, destdir, version):
824 isDracut = False
825 bootdir = self._instroot + "/boot"
827 makedirs(destdir)
829 shutil.copyfile(bootdir + "/vmlinuz-" + version,
830 destdir + "/vmlinuz")
832 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
833 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
834 destdir + "/initrd.img")
835 isDracut = True
836 else:
837 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
838 destdir + "/initrd.img")
840 return isDracut
842 def __get_basic_yaboot_config(self, **args):
843 return """
844 init-message = "Welcome to %(name)s"
845 timeout=%(timeout)d
846 """ % args
848 def __get_image_stanza(self, **args):
849 if args["isDracut"]:
850 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
851 else:
852 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
853 return """
855 image=/ppc/ppc%(bit)s/vmlinuz
856 label=%(short)s
857 initrd=/ppc/ppc%(bit)s/initrd.img
858 read-only
859 append="root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s"
860 """ % args
863 def __write_yaboot_config(self, isodir, bit, isDracut = False):
864 cfg = self.__get_basic_yaboot_config(name = self.name,
865 timeout = self._timeout * 100)
867 kernel_options = self._get_kernel_options()
869 cfg += self.__get_image_stanza(fslabel = self.fslabel,
870 isofstype = "auto",
871 short = "linux",
872 long = "Run from image",
873 extra = "",
874 bit = bit,
875 liveargs = kernel_options,
876 isDracut = isDracut)
878 if self._has_checkisomd5():
879 cfg += self.__get_image_stanza(fslabel = self.fslabel,
880 isofstype = "auto",
881 short = "rd.live.check",
882 long = "Verify and run from image",
883 extra = "rd.live.check",
884 bit = bit,
885 liveargs = kernel_options,
886 isDracut = isDracut)
888 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
889 f.write(cfg)
890 f.close()
892 def __write_not_supported(self, isodir, bit):
893 makedirs(isodir + "/ppc/ppc" + bit)
895 message = "Sorry, this LiveCD does not support your hardware"
897 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
898 f.write('init-message = "' + message + '"')
899 f.close()
902 def __write_dualbits_yaboot_config(isodir, **args):
903 cfg = """
904 init-message = "\nWelcome to %(name)s!\nUse 'linux32' for 32-bit kernel.\n\n"
905 timeout=%(timeout)d
906 default=linux
908 image=/ppc/ppc64/vmlinuz
909 label=linux64
910 alias=linux
911 initrd=/ppc/ppc64/initrd.img
912 read-only
914 image=/ppc/ppc32/vmlinuz
915 label=linux32
916 initrd=/ppc/ppc32/initrd.img
917 read-only
918 """ % args
920 f = open(isodir + "/etc/yaboot.conf", "w")
921 f.write(cfg)
922 f.close()
924 def _configure_bootloader(self, isodir):
925 """configure the boot loader"""
926 havekernel = { 32: False, 64: False }
928 self.__copy_boot_file(isodir + "/ppc", "mapping")
929 self.__copy_boot_file(isodir + "/ppc", "bootinfo.txt")
930 self.__copy_boot_file(isodir + "/ppc/mac", "ofboot.b")
932 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
933 isodir + "/ppc/mac/yaboot")
935 makedirs(isodir + "/ppc/chrp")
936 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
937 isodir + "/ppc/chrp/yaboot")
939 subprocess.call(["/usr/sbin/addnote", isodir + "/ppc/chrp/yaboot"])
942 # FIXME: ppc should support multiple kernels too...
944 kernel = self._get_kernel_versions().values()[0][0]
946 kernel_bits = self.__kernel_bits(kernel)
948 for (bit, present) in kernel_bits.items():
949 if not present:
950 self.__write_not_supported(isodir, bit)
951 continue
953 isDracut = self.__copy_kernel_and_initramfs(isodir + "/ppc/ppc" + bit, kernel)
954 self.__write_yaboot_config(isodir, bit, isDracut)
956 makedirs(isodir + "/etc")
957 if kernel_bits["32"] and not kernel_bits["64"]:
958 shutil.copyfile(isodir + "/ppc/ppc32/yaboot.conf",
959 isodir + "/etc/yaboot.conf")
960 elif kernel_bits["64"] and not kernel_bits["32"]:
961 shutil.copyfile(isodir + "/ppc/ppc64/yaboot.conf",
962 isodir + "/etc/yaboot.conf")
963 else:
964 self.__write_dualbits_yaboot_config(isodir,
965 name = self.name,
966 timeout = self._timeout * 100)
969 # FIXME: build 'netboot' images with kernel+initrd, like mk-images.ppc
972 class ppc64LiveImageCreator(ppcLiveImageCreator):
973 def _get_excluded_packages(self):
974 # FIXME:
975 # while kernel.ppc and kernel.ppc64 co-exist,
976 # we can't have both
977 return ["kernel.ppc"] + \
978 ppcLiveImageCreator._get_excluded_packages(self)
980 arch = rpmUtils.arch.getBaseArch()
981 if arch in ("i386", "x86_64"):
982 LiveImageCreator = x86LiveImageCreator
983 elif arch in ("ppc",):
984 LiveImageCreator = ppcLiveImageCreator
985 elif arch in ("ppc64",):
986 LiveImageCreator = ppc64LiveImageCreator
987 elif arch.startswith('arm'):
988 LiveImageCreator = LiveImageCreatorBase
990 else:
991 raise CreatorError("Architecture not supported!")