Remove grub 0.97 splash
[livecd.git] / imgcreate / live.py
blob189b86912be182e86c67e05ba654fded2f75f1ec
2 # live.py : LiveImageCreator class for creating Live CD images
4 # Copyright 2007-2012, 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 sys
20 import os
21 import os.path
22 import glob
23 import shutil
24 import subprocess
25 import logging
26 import re
28 from imgcreate.errors import *
29 from imgcreate.fs import *
30 from imgcreate.creator import *
32 class LiveImageCreatorBase(LoopImageCreator):
33 """A base class for LiveCD image creators.
35 This class serves as a base class for the architecture-specific LiveCD
36 image creator subclass, LiveImageCreator.
38 LiveImageCreator creates a bootable ISO containing the system image,
39 bootloader, bootloader configuration, kernel and initramfs.
41 """
43 def __init__(self, ks, name, fslabel=None, releasever=None, tmpdir="/tmp",
44 title="Linux", product="Linux", useplugins=False, cacheonly=False,
45 docleanup=True):
46 """Initialise a LiveImageCreator instance.
48 This method takes the same arguments as LoopImageCreator.__init__().
50 """
51 LoopImageCreator.__init__(self, ks, name,
52 fslabel=fslabel,
53 releasever=releasever,
54 tmpdir=tmpdir,
55 useplugins=useplugins,
56 cacheonly=cacheonly,
57 docleanup=docleanup)
59 self.compress_type = "xz"
60 """mksquashfs compressor to use."""
62 self.skip_compression = False
63 """Controls whether to use squashfs to compress the image."""
65 self.skip_minimize = False
66 """Controls whether an image minimizing snapshot should be created.
68 This snapshot can be used when copying the system image from the ISO in
69 order to minimize the amount of data that needs to be copied; simply,
70 it makes it possible to create a version of the image's filesystem with
71 no spare space.
73 """
75 self._timeout = kickstart.get_timeout(self.ks, 10)
76 """The bootloader timeout from kickstart."""
78 self._default_kernel = kickstart.get_default_kernel(self.ks, "kernel")
79 """The default kernel type from kickstart."""
81 self.__isodir = None
83 self.__modules = ["=ata", "sym53c8xx", "aic7xxx", "=usb", "=firewire",
84 "=mmc", "=pcmcia", "mptsas", "udf", "virtio_blk",
85 "virtio_pci"]
86 self.__modules.extend(kickstart.get_modules(self.ks))
88 self._isofstype = "iso9660"
89 self.base_on = False
91 self.title = title
92 self.product = product
95 # Hooks for subclasses
97 def _configure_bootloader(self, isodir):
98 """Create the architecture specific booloader configuration.
100 This is the hook where subclasses must create the booloader
101 configuration in order to allow a bootable ISO to be built.
103 isodir -- the directory where the contents of the ISO are to be staged
106 raise CreatorError("Bootloader configuration is arch-specific, "
107 "but not implemented for this arch!")
109 def _get_kernel_options(self):
110 """Return a kernel options string for bootloader configuration.
112 This is the hook where subclasses may specify a set of kernel options
113 which should be included in the images bootloader configuration.
115 A sensible default implementation is provided.
118 r = kickstart.get_kernel_args(self.ks)
119 if os.path.exists(self._instroot + "/usr/bin/rhgb"):
120 r += " rhgb"
121 if os.path.exists(self._instroot + "/usr/bin/plymouth"):
122 r += " rhgb"
123 return r
125 def _get_mkisofs_options(self, isodir):
126 """Return the architecture specific mkisosfs options.
128 This is the hook where subclasses may specify additional arguments to
129 mkisofs, e.g. to enable a bootable ISO to be built.
131 By default, an empty list is returned.
134 return []
137 # Helpers for subclasses
139 def _has_checkisomd5(self):
140 """Check whether checkisomd5 is available in the install root."""
141 def exists(instroot, path):
142 return os.path.exists(instroot + path)
144 if (exists(self._instroot, "/usr/lib/anaconda-runtime/checkisomd5") or
145 exists(self._instroot, "/usr/bin/checkisomd5")):
146 return True
148 return False
151 # Actual implementation
153 def _base_on(self, base_on):
154 """helper function to extract ext3 file system from a live CD ISO"""
155 isoloop = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
157 try:
158 isoloop.mount()
159 except MountError, e:
160 raise CreatorError("Failed to loopback mount '%s' : %s" %
161 (base_on, e))
163 # Copy the initrd%d.img and xen%d.gz files over to /isolinux
164 # This is because the originals in /boot are removed when the
165 # original .iso was created.
166 src = isoloop.mountdir + "/isolinux/"
167 dest = self.__ensure_isodir() + "/isolinux/"
168 makedirs(dest)
169 pattern = re.compile(r"(initrd\d+\.img|xen\d+\.gz)")
170 files = [f for f in os.listdir(src) if pattern.search(f)
171 and os.path.isfile(src+f)]
172 for f in files:
173 shutil.copyfile(src+f, dest+f)
175 # legacy LiveOS filesystem layout support, remove for F9 or F10
176 if os.path.exists(isoloop.mountdir + "/squashfs.img"):
177 squashimg = isoloop.mountdir + "/squashfs.img"
178 else:
179 squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
181 squashloop = DiskMount(LoopbackDisk(squashimg, 0), self._mkdtemp(), "squashfs")
183 # 'self.compress_type = None' will force reading it from base_on.
184 if self.compress_type is None:
185 self.compress_type = squashfs_compression_type(squashimg)
186 if self.compress_type == 'undetermined':
187 # Default to 'gzip' for compatibility with older versions.
188 self.compress_type = 'gzip'
189 try:
190 if not squashloop.disk.exists():
191 raise CreatorError("'%s' is not a valid live CD ISO : "
192 "squashfs.img doesn't exist" % base_on)
194 try:
195 squashloop.mount()
196 except MountError, e:
197 raise CreatorError("Failed to loopback mount squashfs.img "
198 "from '%s' : %s" % (base_on, e))
200 # legacy LiveOS filesystem layout support, remove for F9 or F10
201 if os.path.exists(squashloop.mountdir + "/os.img"):
202 os_image = squashloop.mountdir + "/os.img"
203 else:
204 os_image = squashloop.mountdir + "/LiveOS/ext3fs.img"
206 if not os.path.exists(os_image):
207 raise CreatorError("'%s' is not a valid live CD ISO : neither "
208 "LiveOS/ext3fs.img nor os.img exist" %
209 base_on)
211 try:
212 shutil.copyfile(os_image, self._image)
213 except IOError, e:
214 raise CreatorError("Failed to copy base live image to %s for modification: %s" %(self._image, e))
215 finally:
216 squashloop.cleanup()
217 isoloop.cleanup()
219 def _mount_instroot(self, base_on = None):
220 self.base_on = True
221 LoopImageCreator._mount_instroot(self, base_on)
222 self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd")
223 self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
225 def _unmount_instroot(self):
226 self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd")
227 self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
228 LoopImageCreator._unmount_instroot(self)
230 def __ensure_isodir(self):
231 if self.__isodir is None:
232 self.__isodir = self._mkdtemp("iso-")
233 return self.__isodir
235 def _generate_efiboot(self, isodir):
236 """Generate EFI boot images."""
237 if not glob.glob(self._instroot+"/boot/efi/EFI/*/shim.efi"):
238 logging.error("Missing shim.efi, skipping efiboot.img creation.")
239 return
241 # XXX-BCL: does this need --label?
242 subprocess.call(["mkefiboot", isodir + "/EFI/BOOT",
243 isodir + "/isolinux/efiboot.img"])
244 subprocess.call(["mkefiboot", "-a", isodir + "/EFI/BOOT",
245 isodir + "/isolinux/macboot.img", "-l", self.product,
246 "-n", "/usr/share/pixmaps/bootloader/fedora-media.vol",
247 "-i", "/usr/share/pixmaps/bootloader/fedora.icns",
248 "-p", self.product])
250 def _create_bootconfig(self):
251 """Configure the image so that it's bootable."""
252 self._configure_bootloader(self.__ensure_isodir())
253 self._generate_efiboot(self.__ensure_isodir())
255 def _get_post_scripts_env(self, in_chroot):
256 env = LoopImageCreator._get_post_scripts_env(self, in_chroot)
258 if not in_chroot:
259 env["LIVE_ROOT"] = self.__ensure_isodir()
261 return env
263 def __extra_filesystems(self):
264 return "vfat msdos ";
266 def __extra_drivers(self):
267 retval = "sr_mod sd_mod ide-cd cdrom "
268 for module in self.__modules:
269 if module == "=usb":
270 retval = retval + "ehci_hcd uhci_hcd ohci_hcd "
271 retval = retval + "usb_storage usbhid "
272 elif module == "=firewire":
273 retval = retval + "firewire-sbp2 firewire-ohci "
274 retval = retval + "sbp2 ohci1394 ieee1394 "
275 elif module == "=mmc":
276 retval = retval + "mmc_block sdhci sdhci-pci "
277 elif module == "=pcmcia":
278 retval = retval + "pata_pcmcia "
279 else:
280 retval = retval + module + " "
281 return retval
283 def __restore_file(self,path):
284 try:
285 os.unlink(path)
286 except:
287 pass
288 if os.path.exists(path + '.rpmnew'):
289 os.rename(path + '.rpmnew', path)
291 def __write_initrd_conf(self, path):
292 if not os.path.exists(os.path.dirname(path)):
293 makedirs(os.path.dirname(path))
294 f = open(path, "a")
295 f.write('LIVEOS="yes"\n')
296 f.write('PROBE="no"\n')
297 f.write('MODULES+="' + self.__extra_filesystems() + '"\n')
298 f.write('MODULES+="' + self.__extra_drivers() + '"\n')
299 f.close()
301 def __write_dracut_conf(self, path):
302 if not os.path.exists(os.path.dirname(path)):
303 makedirs(os.path.dirname(path))
304 f = open(path, "a")
305 f.write('filesystems+="' + self.__extra_filesystems() + ' "\n')
306 f.write('drivers+="' + self.__extra_drivers() + ' "\n')
307 f.write('add_dracutmodules+=" dmsquash-live pollcdrom "')
308 f.close()
310 def __create_iso(self, isodir):
311 iso = self._outdir + "/" + self.name + ".iso"
313 args = ["/usr/bin/mkisofs",
314 "-J", "-r",
315 "-hide-rr-moved", "-hide-joliet-trans-tbl",
316 "-V", self.fslabel,
317 "-o", iso]
319 args.extend(self._get_mkisofs_options(isodir))
320 if self._isofstype == "udf":
321 args.append("-allow-limited-size")
323 args.append(isodir)
325 if subprocess.call(args) != 0:
326 raise CreatorError("ISO creation failed!")
328 if os.path.exists("/usr/bin/isohybrid"):
329 if os.path.exists(isodir + "/isolinux/efiboot.img"):
330 subprocess.call(["/usr/bin/isohybrid", "-u", "-m", iso])
331 else:
332 subprocess.call(["/usr/bin/isohybrid", iso])
334 self.__implant_md5sum(iso)
336 def __implant_md5sum(self, iso):
337 """Implant an isomd5sum."""
338 if os.path.exists("/usr/bin/implantisomd5"):
339 implantisomd5 = "/usr/bin/implantisomd5"
340 elif os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"):
341 implantisomd5 = "/usr/lib/anaconda-runtime/implantisomd5"
342 else:
343 logging.warn("isomd5sum not installed; not setting up mediacheck")
344 return
346 subprocess.call([implantisomd5, iso])
348 def _stage_final_image(self):
349 try:
350 makedirs(self.__ensure_isodir() + "/LiveOS")
352 self._resparse()
354 if not self.skip_minimize:
355 create_image_minimizer(self.__isodir + "/LiveOS/osmin.img",
356 self._image, self.compress_type,
357 tmpdir = self.tmpdir)
359 if self.skip_compression:
360 shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
361 if os.stat(self.__isodir + "/LiveOS/ext3fs.img").st_size >= 4*1024*1024*1024:
362 self._isofstype = "udf"
363 logging.warn("Switching to UDF due to size of LiveOS/ext3fs.img")
364 else:
365 makedirs(os.path.join(os.path.dirname(self._image), "LiveOS"))
366 shutil.move(self._image,
367 os.path.join(os.path.dirname(self._image),
368 "LiveOS", "ext3fs.img"))
369 mksquashfs(os.path.dirname(self._image),
370 self.__isodir + "/LiveOS/squashfs.img",
371 self.compress_type)
372 if os.stat(self.__isodir + "/LiveOS/squashfs.img").st_size >= 4*1024*1024*1024:
373 self._isofstype = "udf"
374 logging.warn("Switching to UDF due to size of LiveOS/squashfs.img")
377 self.__create_iso(self.__isodir)
378 finally:
379 shutil.rmtree(self.__isodir, ignore_errors = True)
380 self.__isodir = None
382 class x86LiveImageCreator(LiveImageCreatorBase):
383 """ImageCreator for x86 machines"""
384 def __init__(self, *args, **kwargs):
385 LiveImageCreatorBase.__init__(self, *args, **kwargs)
386 self._efiarch = None
388 def _get_mkisofs_options(self, isodir):
389 options = [ "-b", "isolinux/isolinux.bin",
390 "-c", "isolinux/boot.cat",
391 "-no-emul-boot", "-boot-info-table",
392 "-boot-load-size", "4" ]
393 if os.path.exists(isodir + "/isolinux/efiboot.img"):
394 options.extend([ "-eltorito-alt-boot",
395 "-e", "isolinux/efiboot.img",
396 "-no-emul-boot",
397 "-eltorito-alt-boot",
398 "-e", "isolinux/macboot.img",
399 "-no-emul-boot"])
400 return options
402 def _get_required_packages(self):
403 return ["syslinux"] \
404 + LiveImageCreatorBase._get_required_packages(self)
406 def _get_isolinux_stanzas(self, isodir):
407 return ""
409 def __find_syslinux_menu(self):
410 for menu in ("vesamenu.c32", "menu.c32"):
411 for dir in ("/usr/lib/syslinux/", "/usr/share/syslinux/"):
412 if os.path.isfile(self._instroot + dir + menu):
413 return menu
415 raise CreatorError("syslinux not installed : "
416 "no suitable *menu.c32 found")
418 def __find_syslinux_mboot(self):
420 # We only need the mboot module if we have any xen hypervisors
422 if not glob.glob(self._instroot + "/boot/xen.gz*"):
423 return None
425 return "mboot.c32"
427 def __copy_syslinux_files(self, isodir, menu, mboot = None):
428 files = ["isolinux.bin", menu]
429 if mboot:
430 files += [mboot]
432 for f in files:
433 if os.path.exists(self._instroot + "/usr/lib/syslinux/" + f):
434 path = self._instroot + "/usr/lib/syslinux/" + f
435 elif os.path.exists(self._instroot + "/usr/share/syslinux/" + f):
436 path = self._instroot + "/usr/share/syslinux/" + f
437 if not os.path.isfile(path):
438 raise CreatorError("syslinux not installed : "
439 "%s not found" % path)
441 shutil.copy(path, isodir + "/isolinux/")
443 def __copy_syslinux_background(self, isodest):
444 background_path = self._instroot + \
445 "/usr/share/anaconda/boot/syslinux-vesa-splash.jpg"
447 if not os.path.exists(background_path):
448 # fallback to F13 location
449 background_path = self._instroot + \
450 "/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg"
452 if not os.path.exists(background_path):
453 return False
455 shutil.copyfile(background_path, isodest)
457 return True
459 def __copy_kernel_and_initramfs(self, isodir, version, index):
460 bootdir = self._instroot + "/boot"
462 shutil.copyfile(bootdir + "/vmlinuz-" + version,
463 isodir + "/isolinux/vmlinuz" + index)
465 isDracut = False
466 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
467 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
468 isodir + "/isolinux/initrd" + index + ".img")
469 isDracut = True
470 elif os.path.exists(bootdir + "/initrd-" + version + ".img"):
471 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
472 isodir + "/isolinux/initrd" + index + ".img")
473 elif not self.base_on:
474 logging.error("No initrd or initramfs found for %s" % (version,))
476 is_xen = False
477 if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
478 shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
479 isodir + "/isolinux/xen" + index + ".gz")
480 is_xen = True
482 return (is_xen, isDracut)
484 def __is_default_kernel(self, kernel, kernels):
485 if len(kernels) == 1:
486 return True
488 if kernel == self._default_kernel:
489 return True
491 if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
492 return True
494 return False
496 def __get_basic_syslinux_config(self, **args):
497 return """
498 default %(menu)s
499 timeout %(timeout)d
500 menu background %(background)s
501 menu autoboot Starting %(title)s in # second{,s}. Press any key to interrupt.
503 menu clear
504 menu title %(title)s
505 menu vshift 8
506 menu rows 18
507 menu margin 8
508 #menu hidden
509 menu helpmsgrow 15
510 menu tabmsgrow 13
512 menu color border * #00000000 #00000000 none
513 menu color sel 0 #ffffffff #00000000 none
514 menu color title 0 #ff7ba3d0 #00000000 none
515 menu color tabmsg 0 #ff3a6496 #00000000 none
516 menu color unsel 0 #84b8ffff #00000000 none
517 menu color hotsel 0 #84b8ffff #00000000 none
518 menu color hotkey 0 #ffffffff #00000000 none
519 menu color help 0 #ffffffff #00000000 none
520 menu color scrollbar 0 #ffffffff #ff355594 none
521 menu color timeout 0 #ffffffff #00000000 none
522 menu color timeout_msg 0 #ffffffff #00000000 none
523 menu color cmdmark 0 #84b8ffff #00000000 none
524 menu color cmdline 0 #ffffffff #00000000 none
526 menu tabmsg Press Tab for full configuration options on menu items.
527 menu separator
528 """ % args
530 def __get_image_stanza(self, is_xen, isDracut, **args):
531 if isDracut:
532 args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args
533 else:
534 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
536 if not is_xen:
537 template = """label %(short)s
538 menu label %(long)s
539 kernel vmlinuz%(index)s
540 append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
542 else:
543 template = """label %(short)s
544 menu label %(long)s
545 kernel mboot.c32
546 append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s --- initrd%(index)s.img
548 if args.get("help"):
549 template += """ text help
550 %(help)s
551 endtext
553 return template % args
555 def __get_image_stanzas(self, isodir):
556 versions = []
557 kernels = self._get_kernel_versions()
558 for kernel in kernels:
559 for version in kernels[kernel]:
560 versions.append(version)
562 kernel_options = self._get_kernel_options()
564 checkisomd5 = self._has_checkisomd5()
566 # Stanzas for insertion into the config template
567 linux = []
568 basic = []
569 check = []
571 index = "0"
572 for version in versions:
573 (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index)
574 if index == "0":
575 self._isDracut = isDracut
577 default = self.__is_default_kernel(kernel, kernels)
579 if default:
580 long = self.product
581 elif kernel.startswith("kernel-"):
582 long = "%s (%s)" % (self.product, kernel[7:])
583 else:
584 long = "%s (%s)" % (self.product, kernel)
586 # tell dracut not to ask for LUKS passwords or activate mdraid sets
587 if isDracut:
588 kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0"
589 else:
590 kern_opts = kernel_options
592 linux.append(self.__get_image_stanza(is_xen, isDracut,
593 fslabel = self.fslabel,
594 isofstype = "auto",
595 liveargs = kern_opts,
596 long = "^Start " + long,
597 short = "linux" + index,
598 extra = "",
599 help = "",
600 index = index))
602 if default:
603 linux[-1] += " menu default\n"
605 basic.append(self.__get_image_stanza(is_xen, isDracut,
606 fslabel = self.fslabel,
607 isofstype = "auto",
608 liveargs = kern_opts,
609 long = "Start " + long + " in ^basic graphics mode.",
610 short = "basic" + index,
611 extra = "xdriver=vesa nomodeset",
612 help = "Try this option out if you're having trouble starting.",
613 index = index))
615 if checkisomd5:
616 check.append(self.__get_image_stanza(is_xen, isDracut,
617 fslabel = self.fslabel,
618 isofstype = "auto",
619 liveargs = kern_opts,
620 long = "^Test this media & start " + long,
621 short = "check" + index,
622 extra = "rd.live.check",
623 help = "",
624 index = index))
625 else:
626 check.append(None)
628 index = str(int(index) + 1)
630 return (linux, basic, check)
632 def __get_memtest_stanza(self, isodir):
633 memtest = glob.glob(self._instroot + "/boot/memtest86*")
634 if not memtest:
635 return ""
637 shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
639 return """label memtest
640 menu label Run a ^memory test.
641 text help
642 If your system is having issues, an problem with your
643 system's memory may be the cause. Use this utility to
644 see if the memory is working correctly.
645 endtext
646 kernel memtest
649 def __get_local_stanza(self, isodir):
650 return """label local
651 menu label Boot from ^local drive
652 localboot 0xffff
655 def _configure_syslinux_bootloader(self, isodir):
656 """configure the boot loader"""
657 makedirs(isodir + "/isolinux")
659 menu = self.__find_syslinux_menu()
661 self.__copy_syslinux_files(isodir, menu,
662 self.__find_syslinux_mboot())
664 background = ""
665 if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
666 background = "splash.jpg"
668 cfg = self.__get_basic_syslinux_config(menu = menu,
669 background = background,
670 title = self.title,
671 timeout = self._timeout * 10)
672 cfg += "menu separator\n"
674 linux, basic, check = self.__get_image_stanzas(isodir)
675 # Add linux stanzas to main menu
676 for s in linux:
677 cfg += s
678 cfg += "menu separator\n"
680 cfg += """menu begin ^Troubleshooting
681 menu title Troubleshooting
683 # Add basic video and check to submenu
684 for b, c in zip(basic, check):
685 cfg += b
686 if c:
687 cfg += c
689 cfg += self.__get_memtest_stanza(isodir)
690 cfg += "menu separator\n"
692 cfg += self.__get_local_stanza(isodir)
693 cfg += self._get_isolinux_stanzas(isodir)
695 cfg += """menu separator
696 label returntomain
697 menu label Return to ^main menu.
698 menu exit
699 menu end
701 cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
702 cfgf.write(cfg)
703 cfgf.close()
705 @property
706 def efiarch(self):
707 if not self._efiarch:
708 # for most things, we want them named boot$efiarch
709 efiarch = {"i386": "IA32", "x86_64": "X64"}
710 self._efiarch = efiarch[rpmUtils.arch.getBaseArch()]
711 return self._efiarch
713 def __copy_efi_files(self, isodir):
714 """ Copy the efi files into /EFI/BOOT/
715 If any of them are missing, return False.
716 requires:
717 shim.efi
718 gcdx64.efi
719 fonts/unicode.pf2
721 fail = False
722 missing = []
723 files = [("/boot/efi/EFI/*/shim.efi", "/EFI/BOOT/BOOT%s.efi" % (self.efiarch,)),
724 ("/boot/efi/EFI/*/gcdx64.efi", "/EFI/BOOT/grubx64.efi"),
725 ("/boot/efi/EFI/*/fonts/unicode.pf2", "/EFI/BOOT/fonts/"),
727 makedirs(isodir+"/EFI/BOOT/fonts/")
728 for src, dest in files:
729 src_glob = glob.glob(self._instroot+src)
730 if not src_glob:
731 missing.append("Missing EFI file (%s)" % (src,))
732 fail = True
733 else:
734 shutil.copy(src_glob[0], isodir+dest)
735 map(logging.error, missing)
736 return fail
738 def __get_basic_efi_config(self, **args):
739 return """
740 set default="0"
742 function load_video {
743 insmod efi_gop
744 insmod efi_uga
745 insmod video_bochs
746 insmod video_cirrus
747 insmod all_video
750 load_video
751 set gfxpayload=keep
752 insmod gzio
753 insmod part_gpt
754 insmod ext2
756 set timeout=%(timeout)d
757 ### END /etc/grub.d/00_header ###
759 search --no-floppy --set=root -l '%(isolabel)s'
761 ### BEGIN /etc/grub.d/10_linux ###
762 """ %args
764 def __get_efi_image_stanza(self, **args):
765 if self._isDracut:
766 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
767 else:
768 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
769 return """menuentry '%(long)s' --class fedora --class gnu-linux --class gnu --class os {
770 linuxefi /isolinux/vmlinuz%(index)s root=%(rootlabel)s %(liveargs)s %(extra)s
771 initrdefi /isolinux/initrd%(index)s.img
773 """ %args
775 def __get_efi_image_stanzas(self, isodir, name):
776 # FIXME: this only supports one kernel right now...
778 kernel_options = self._get_kernel_options()
779 checkisomd5 = self._has_checkisomd5()
781 cfg = ""
783 for index in range(0, 9):
784 # we don't support xen kernels
785 if os.path.exists("%s/EFI/BOOT/xen%d.gz" %(isodir, index)):
786 continue
787 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
788 liveargs = kernel_options,
789 long = name,
790 extra = "", index = index)
791 if checkisomd5:
792 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
793 liveargs = kernel_options,
794 long = "Verify and Boot " + name,
795 extra = "rd.live.check",
796 index = index)
797 break
799 return cfg
801 def _configure_efi_bootloader(self, isodir):
802 """Set up the configuration for an EFI bootloader"""
803 if self.__copy_efi_files(isodir):
804 shutil.rmtree(isodir + "/EFI")
805 logging.warn("Failed to copy EFI files, no EFI Support will be included.")
806 return
808 cfg = self.__get_basic_efi_config(isolabel = self.fslabel,
809 timeout = self._timeout)
810 cfg += self.__get_efi_image_stanzas(isodir, self.name)
812 cfgf = open(isodir + "/EFI/BOOT/grub.cfg", "w")
813 cfgf.write(cfg)
814 cfgf.close()
816 # first gen mactel machines get the bootloader name wrong apparently
817 if rpmUtils.arch.getBaseArch() == "i386":
818 os.link(isodir + "/EFI/BOOT/BOOT%s.efi" % (self.efiarch),
819 isodir + "/EFI/BOOT/BOOT.efi")
822 def _configure_bootloader(self, isodir):
823 self._configure_syslinux_bootloader(isodir)
824 self._configure_efi_bootloader(isodir)
826 class ppcLiveImageCreator(LiveImageCreatorBase):
827 def _get_mkisofs_options(self, isodir):
828 return [ "-hfs", "-no-desktop", "-part",
829 "-map", isodir + "/ppc/mapping",
830 "-hfs-bless", isodir + "/ppc/mac",
831 "-hfs-volid", self.fslabel ]
833 def _get_required_packages(self):
834 return ["yaboot"] + \
835 LiveImageCreatorBase._get_required_packages(self)
837 def _get_excluded_packages(self):
838 # kind of hacky, but exclude memtest86+ on ppc so it can stay in cfg
839 return ["memtest86+"] + \
840 LiveImageCreatorBase._get_excluded_packages(self)
842 def __copy_boot_file(self, destdir, file):
843 for dir in ["/usr/share/ppc64-utils",
844 "/usr/lib/anaconda-runtime/boot"]:
845 path = self._instroot + dir + "/" + file
846 if not os.path.exists(path):
847 continue
849 makedirs(destdir)
850 shutil.copy(path, destdir)
851 return
853 raise CreatorError("Unable to find boot file " + file)
855 def __kernel_bits(self, kernel):
856 testpath = (self._instroot + "/lib/modules/" +
857 kernel + "/kernel/arch/powerpc/platforms")
859 if not os.path.exists(testpath):
860 return { "32" : True, "64" : False }
861 else:
862 return { "32" : False, "64" : True }
864 def __copy_kernel_and_initramfs(self, destdir, version):
865 isDracut = False
866 bootdir = self._instroot + "/boot"
868 makedirs(destdir)
870 shutil.copyfile(bootdir + "/vmlinuz-" + version,
871 destdir + "/vmlinuz")
873 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
874 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
875 destdir + "/initrd.img")
876 isDracut = True
877 else:
878 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
879 destdir + "/initrd.img")
881 return isDracut
883 def __get_basic_yaboot_config(self, **args):
884 return """
885 init-message = "Welcome to %(name)s"
886 timeout=%(timeout)d
887 """ % args
889 def __get_image_stanza(self, **args):
890 if args["isDracut"]:
891 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
892 else:
893 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
894 return """
896 image=/ppc/ppc%(bit)s/vmlinuz
897 label=%(short)s
898 initrd=/ppc/ppc%(bit)s/initrd.img
899 read-only
900 append="root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s"
901 """ % args
904 def __write_yaboot_config(self, isodir, bit, isDracut = False):
905 cfg = self.__get_basic_yaboot_config(name = self.name,
906 timeout = self._timeout * 100)
908 kernel_options = self._get_kernel_options()
910 cfg += self.__get_image_stanza(fslabel = self.fslabel,
911 isofstype = "auto",
912 short = "linux",
913 long = "Run from image",
914 extra = "",
915 bit = bit,
916 liveargs = kernel_options,
917 isDracut = isDracut)
919 if self._has_checkisomd5():
920 cfg += self.__get_image_stanza(fslabel = self.fslabel,
921 isofstype = "auto",
922 short = "rd.live.check",
923 long = "Verify and run from image",
924 extra = "rd.live.check",
925 bit = bit,
926 liveargs = kernel_options,
927 isDracut = isDracut)
929 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
930 f.write(cfg)
931 f.close()
933 def __write_not_supported(self, isodir, bit):
934 makedirs(isodir + "/ppc/ppc" + bit)
936 message = "Sorry, this LiveCD does not support your hardware"
938 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
939 f.write('init-message = "' + message + '"')
940 f.close()
943 def __write_dualbits_yaboot_config(isodir, **args):
944 cfg = """
945 init-message = "\nWelcome to %(name)s!\nUse 'linux32' for 32-bit kernel.\n\n"
946 timeout=%(timeout)d
947 default=linux
949 image=/ppc/ppc64/vmlinuz
950 label=linux64
951 alias=linux
952 initrd=/ppc/ppc64/initrd.img
953 read-only
955 image=/ppc/ppc32/vmlinuz
956 label=linux32
957 initrd=/ppc/ppc32/initrd.img
958 read-only
959 """ % args
961 f = open(isodir + "/etc/yaboot.conf", "w")
962 f.write(cfg)
963 f.close()
965 def _configure_bootloader(self, isodir):
966 """configure the boot loader"""
967 havekernel = { 32: False, 64: False }
969 self.__copy_boot_file(isodir + "/ppc", "mapping")
970 self.__copy_boot_file(isodir + "/ppc", "bootinfo.txt")
971 self.__copy_boot_file(isodir + "/ppc/mac", "ofboot.b")
973 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
974 isodir + "/ppc/mac/yaboot")
976 makedirs(isodir + "/ppc/chrp")
977 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
978 isodir + "/ppc/chrp/yaboot")
980 subprocess.call(["/usr/sbin/addnote", isodir + "/ppc/chrp/yaboot"])
983 # FIXME: ppc should support multiple kernels too...
985 kernel = self._get_kernel_versions().values()[0][0]
987 kernel_bits = self.__kernel_bits(kernel)
989 for (bit, present) in kernel_bits.items():
990 if not present:
991 self.__write_not_supported(isodir, bit)
992 continue
994 isDracut = self.__copy_kernel_and_initramfs(isodir + "/ppc/ppc" + bit, kernel)
995 self.__write_yaboot_config(isodir, bit, isDracut)
997 makedirs(isodir + "/etc")
998 if kernel_bits["32"] and not kernel_bits["64"]:
999 shutil.copyfile(isodir + "/ppc/ppc32/yaboot.conf",
1000 isodir + "/etc/yaboot.conf")
1001 elif kernel_bits["64"] and not kernel_bits["32"]:
1002 shutil.copyfile(isodir + "/ppc/ppc64/yaboot.conf",
1003 isodir + "/etc/yaboot.conf")
1004 else:
1005 self.__write_dualbits_yaboot_config(isodir,
1006 name = self.name,
1007 timeout = self._timeout * 100)
1010 # FIXME: build 'netboot' images with kernel+initrd, like mk-images.ppc
1013 class ppc64LiveImageCreator(ppcLiveImageCreator):
1014 def _get_excluded_packages(self):
1015 # FIXME:
1016 # while kernel.ppc and kernel.ppc64 co-exist,
1017 # we can't have both
1018 return ["kernel.ppc"] + \
1019 ppcLiveImageCreator._get_excluded_packages(self)
1021 arch = rpmUtils.arch.getBaseArch()
1022 if arch in ("i386", "x86_64"):
1023 LiveImageCreator = x86LiveImageCreator
1024 elif arch in ("ppc",):
1025 LiveImageCreator = ppcLiveImageCreator
1026 elif arch in ("ppc64",):
1027 LiveImageCreator = ppc64LiveImageCreator
1028 elif arch.startswith('arm'):
1029 LiveImageCreator = LiveImageCreatorBase
1031 else:
1032 raise CreatorError("Architecture not supported!")