liveimage-mount: add missing import
[livecd.git] / imgcreate / live.py
blob4fe1ea43fc822e1da04d000a56b00cad1139b16c
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", "virtio_scsi", "virtio_net", "virtio_mmio",
86 "virtio_balloon", "virtio-rng"]
87 self.__modules.extend(kickstart.get_modules(self.ks))
89 self._isofstype = "iso9660"
90 self.base_on = False
92 self.title = title
93 self.product = product
96 # Hooks for subclasses
98 def _configure_bootloader(self, isodir):
99 """Create the architecture specific booloader configuration.
101 This is the hook where subclasses must create the booloader
102 configuration in order to allow a bootable ISO to be built.
104 isodir -- the directory where the contents of the ISO are to be staged
107 raise CreatorError("Bootloader configuration is arch-specific, "
108 "but not implemented for this arch!")
110 def _get_kernel_options(self):
111 """Return a kernel options string for bootloader configuration.
113 This is the hook where subclasses may specify a set of kernel options
114 which should be included in the images bootloader configuration.
116 A sensible default implementation is provided.
119 r = kickstart.get_kernel_args(self.ks)
120 if os.path.exists(self._instroot + "/usr/bin/rhgb"):
121 r += " rhgb"
122 if os.path.exists(self._instroot + "/usr/bin/plymouth"):
123 r += " rhgb"
124 return r
126 def _get_mkisofs_options(self, isodir):
127 """Return the architecture specific mkisosfs options.
129 This is the hook where subclasses may specify additional arguments to
130 mkisofs, e.g. to enable a bootable ISO to be built.
132 By default, an empty list is returned.
135 return []
138 # Helpers for subclasses
140 def _has_checkisomd5(self):
141 """Check whether checkisomd5 is available in the install root."""
142 def exists(instroot, path):
143 return os.path.exists(instroot + path)
145 if (exists(self._instroot, "/usr/lib/anaconda-runtime/checkisomd5") or
146 exists(self._instroot, "/usr/bin/checkisomd5")):
147 return True
149 return False
152 # Actual implementation
154 def _base_on(self, base_on):
155 """helper function to extract ext3 file system from a live CD ISO"""
156 isoloop = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
158 try:
159 isoloop.mount()
160 except MountError, e:
161 raise CreatorError("Failed to loopback mount '%s' : %s" %
162 (base_on, e))
164 # Copy the initrd%d.img and xen%d.gz files over to /isolinux
165 # This is because the originals in /boot are removed when the
166 # original .iso was created.
167 src = isoloop.mountdir + "/isolinux/"
168 dest = self.__ensure_isodir() + "/isolinux/"
169 makedirs(dest)
170 pattern = re.compile(r"(initrd\d+\.img|xen\d+\.gz)")
171 files = [f for f in os.listdir(src) if pattern.search(f)
172 and os.path.isfile(src+f)]
173 for f in files:
174 shutil.copyfile(src+f, dest+f)
176 # legacy LiveOS filesystem layout support, remove for F9 or F10
177 if os.path.exists(isoloop.mountdir + "/squashfs.img"):
178 squashimg = isoloop.mountdir + "/squashfs.img"
179 else:
180 squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
182 squashloop = DiskMount(LoopbackDisk(squashimg, 0), self._mkdtemp(), "squashfs")
184 # 'self.compress_type = None' will force reading it from base_on.
185 if self.compress_type is None:
186 self.compress_type = squashfs_compression_type(squashimg)
187 if self.compress_type == 'undetermined':
188 # Default to 'gzip' for compatibility with older versions.
189 self.compress_type = 'gzip'
190 try:
191 if not squashloop.disk.exists():
192 raise CreatorError("'%s' is not a valid live CD ISO : "
193 "squashfs.img doesn't exist" % base_on)
195 try:
196 squashloop.mount()
197 except MountError, e:
198 raise CreatorError("Failed to loopback mount squashfs.img "
199 "from '%s' : %s" % (base_on, e))
201 # legacy LiveOS filesystem layout support, remove for F9 or F10
202 if os.path.exists(squashloop.mountdir + "/os.img"):
203 os_image = squashloop.mountdir + "/os.img"
204 else:
205 os_image = squashloop.mountdir + "/LiveOS/ext3fs.img"
207 if not os.path.exists(os_image):
208 raise CreatorError("'%s' is not a valid live CD ISO : neither "
209 "LiveOS/ext3fs.img nor os.img exist" %
210 base_on)
212 try:
213 shutil.copyfile(os_image, self._image)
214 except IOError, e:
215 raise CreatorError("Failed to copy base live image to %s for modification: %s" %(self._image, e))
216 finally:
217 squashloop.cleanup()
218 isoloop.cleanup()
220 def _mount_instroot(self, base_on = None):
221 self.base_on = True
222 LoopImageCreator._mount_instroot(self, base_on)
223 self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd")
224 self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
226 def _unmount_instroot(self):
227 self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd")
228 self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
229 LoopImageCreator._unmount_instroot(self)
231 def __ensure_isodir(self):
232 if self.__isodir is None:
233 self.__isodir = self._mkdtemp("iso-")
234 return self.__isodir
236 def _generate_efiboot(self, isodir):
237 """Generate EFI boot images."""
238 if not glob.glob(self._instroot+"/boot/efi/EFI/*/shim.efi"):
239 logging.error("Missing shim.efi, skipping efiboot.img creation.")
240 return
242 # XXX-BCL: does this need --label?
243 subprocess.call(["mkefiboot", isodir + "/EFI/BOOT",
244 isodir + "/isolinux/efiboot.img"])
245 subprocess.call(["mkefiboot", "-a", isodir + "/EFI/BOOT",
246 isodir + "/isolinux/macboot.img", "-l", self.product,
247 "-n", "/usr/share/pixmaps/bootloader/fedora-media.vol",
248 "-i", "/usr/share/pixmaps/bootloader/fedora.icns",
249 "-p", self.product])
251 def _create_bootconfig(self):
252 """Configure the image so that it's bootable."""
253 self._configure_bootloader(self.__ensure_isodir())
254 self._generate_efiboot(self.__ensure_isodir())
256 def _get_post_scripts_env(self, in_chroot):
257 env = LoopImageCreator._get_post_scripts_env(self, in_chroot)
259 if not in_chroot:
260 env["LIVE_ROOT"] = self.__ensure_isodir()
262 return env
264 def __extra_filesystems(self):
265 return "vfat msdos isofs ";
267 def __extra_drivers(self):
268 retval = "sr_mod sd_mod ide-cd cdrom "
269 for module in self.__modules:
270 if module == "=usb":
271 retval = retval + "ehci_hcd uhci_hcd ohci_hcd "
272 retval = retval + "usb_storage usbhid "
273 elif module == "=firewire":
274 retval = retval + "firewire-sbp2 firewire-ohci "
275 retval = retval + "sbp2 ohci1394 ieee1394 "
276 elif module == "=mmc":
277 retval = retval + "mmc_block sdhci sdhci-pci "
278 elif module == "=pcmcia":
279 retval = retval + "pata_pcmcia "
280 else:
281 retval = retval + module + " "
282 return retval
284 def __restore_file(self,path):
285 try:
286 os.unlink(path)
287 except:
288 pass
289 if os.path.exists(path + '.rpmnew'):
290 os.rename(path + '.rpmnew', path)
292 def __write_initrd_conf(self, path):
293 if not os.path.exists(os.path.dirname(path)):
294 makedirs(os.path.dirname(path))
295 f = open(path, "a")
296 f.write('LIVEOS="yes"\n')
297 f.write('PROBE="no"\n')
298 f.write('MODULES+="' + self.__extra_filesystems() + '"\n')
299 f.write('MODULES+="' + self.__extra_drivers() + '"\n')
300 f.close()
302 def __write_dracut_conf(self, path):
303 if not os.path.exists(os.path.dirname(path)):
304 makedirs(os.path.dirname(path))
305 f = open(path, "a")
306 f.write('filesystems+="' + self.__extra_filesystems() + ' "\n')
307 f.write('drivers+="' + self.__extra_drivers() + ' "\n')
308 f.write('add_dracutmodules+=" dmsquash-live pollcdrom "\n')
309 f.write('hostonly="no"\n')
310 f.write('dracut_rescue_image="no"\n')
311 f.close()
313 def __create_iso(self, isodir):
314 iso = self._outdir + "/" + self.name + ".iso"
316 args = ["/usr/bin/mkisofs",
317 "-J", "-r",
318 "-hide-rr-moved", "-hide-joliet-trans-tbl",
319 "-V", self.fslabel,
320 "-o", iso]
322 args.extend(self._get_mkisofs_options(isodir))
323 if self._isofstype == "udf":
324 args.append("-allow-limited-size")
326 args.append(isodir)
328 if subprocess.call(args) != 0:
329 raise CreatorError("ISO creation failed!")
331 if os.path.exists("/usr/bin/isohybrid"):
332 if os.path.exists(isodir + "/isolinux/efiboot.img"):
333 subprocess.call(["/usr/bin/isohybrid", "-u", "-m", iso])
334 else:
335 subprocess.call(["/usr/bin/isohybrid", iso])
337 self.__implant_md5sum(iso)
339 def __implant_md5sum(self, iso):
340 """Implant an isomd5sum."""
341 if os.path.exists("/usr/bin/implantisomd5"):
342 implantisomd5 = "/usr/bin/implantisomd5"
343 elif os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"):
344 implantisomd5 = "/usr/lib/anaconda-runtime/implantisomd5"
345 else:
346 logging.warn("isomd5sum not installed; not setting up mediacheck")
347 return
349 subprocess.call([implantisomd5, iso])
351 def _stage_final_image(self):
352 try:
353 makedirs(self.__ensure_isodir() + "/LiveOS")
355 self._resparse()
357 if not self.skip_minimize:
358 create_image_minimizer(self.__isodir + "/LiveOS/osmin.img",
359 self._image, self.compress_type,
360 tmpdir = self.tmpdir)
362 if self.skip_compression:
363 shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
364 if os.stat(self.__isodir + "/LiveOS/ext3fs.img").st_size >= 4*1024*1024*1024:
365 self._isofstype = "udf"
366 logging.warn("Switching to UDF due to size of LiveOS/ext3fs.img")
367 else:
368 makedirs(os.path.join(os.path.dirname(self._image), "LiveOS"))
369 shutil.move(self._image,
370 os.path.join(os.path.dirname(self._image),
371 "LiveOS", "ext3fs.img"))
372 mksquashfs(os.path.dirname(self._image),
373 self.__isodir + "/LiveOS/squashfs.img",
374 self.compress_type)
375 if os.stat(self.__isodir + "/LiveOS/squashfs.img").st_size >= 4*1024*1024*1024:
376 self._isofstype = "udf"
377 logging.warn("Switching to UDF due to size of LiveOS/squashfs.img")
380 self.__create_iso(self.__isodir)
381 finally:
382 shutil.rmtree(self.__isodir, ignore_errors = True)
383 self.__isodir = None
385 class x86LiveImageCreator(LiveImageCreatorBase):
386 """ImageCreator for x86 machines"""
387 def __init__(self, *args, **kwargs):
388 LiveImageCreatorBase.__init__(self, *args, **kwargs)
389 self._efiarch = None
391 def _get_mkisofs_options(self, isodir):
392 options = [ "-b", "isolinux/isolinux.bin",
393 "-c", "isolinux/boot.cat",
394 "-no-emul-boot", "-boot-info-table",
395 "-boot-load-size", "4" ]
396 if os.path.exists(isodir + "/isolinux/efiboot.img"):
397 options.extend([ "-eltorito-alt-boot",
398 "-e", "isolinux/efiboot.img",
399 "-no-emul-boot",
400 "-eltorito-alt-boot",
401 "-e", "isolinux/macboot.img",
402 "-no-emul-boot"])
403 return options
405 def _get_required_packages(self):
406 return ["syslinux"] \
407 + LiveImageCreatorBase._get_required_packages(self)
409 def _get_isolinux_stanzas(self, isodir):
410 return ""
412 def __find_syslinux_menu(self):
413 for menu in ("vesamenu.c32", "menu.c32"):
414 for dir in ("/usr/lib/syslinux/", "/usr/share/syslinux/"):
415 if os.path.isfile(self._instroot + dir + menu):
416 return menu
418 raise CreatorError("syslinux not installed : "
419 "no suitable *menu.c32 found")
421 def __find_syslinux_mboot(self):
423 # We only need the mboot module if we have any xen hypervisors
425 if not glob.glob(self._instroot + "/boot/xen.gz*"):
426 return None
428 return "mboot.c32"
430 def __copy_syslinux_files(self, isodir, menu, mboot = None):
431 files = ["isolinux.bin", menu]
432 if mboot:
433 files += [mboot]
435 for f in files:
436 if os.path.exists(self._instroot + "/usr/lib/syslinux/" + f):
437 path = self._instroot + "/usr/lib/syslinux/" + f
438 elif os.path.exists(self._instroot + "/usr/share/syslinux/" + f):
439 path = self._instroot + "/usr/share/syslinux/" + f
440 if not os.path.isfile(path):
441 raise CreatorError("syslinux not installed : "
442 "%s not found" % path)
444 shutil.copy(path, isodir + "/isolinux/")
446 def __copy_syslinux_background(self, isodest):
447 background_path = self._instroot + \
448 "/usr/share/anaconda/boot/syslinux-vesa-splash.jpg"
450 if not os.path.exists(background_path):
451 # fallback to F13 location
452 background_path = self._instroot + \
453 "/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg"
455 if not os.path.exists(background_path):
456 return False
458 shutil.copyfile(background_path, isodest)
460 return True
462 def __copy_kernel_and_initramfs(self, isodir, version, index):
463 bootdir = self._instroot + "/boot"
465 shutil.copyfile(bootdir + "/vmlinuz-" + version,
466 isodir + "/isolinux/vmlinuz" + index)
468 isDracut = False
469 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
470 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
471 isodir + "/isolinux/initrd" + index + ".img")
472 isDracut = True
473 elif os.path.exists(bootdir + "/initrd-" + version + ".img"):
474 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
475 isodir + "/isolinux/initrd" + index + ".img")
476 elif not self.base_on:
477 logging.error("No initrd or initramfs found for %s" % (version,))
479 is_xen = False
480 if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
481 shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
482 isodir + "/isolinux/xen" + index + ".gz")
483 is_xen = True
485 return (is_xen, isDracut)
487 def __is_default_kernel(self, kernel, kernels):
488 if len(kernels) == 1:
489 return True
491 if kernel == self._default_kernel:
492 return True
494 if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
495 return True
497 return False
499 def __get_basic_syslinux_config(self, **args):
500 return """
501 default %(menu)s
502 timeout %(timeout)d
503 menu background %(background)s
504 menu autoboot Starting %(title)s in # second{,s}. Press any key to interrupt.
506 menu clear
507 menu title %(title)s
508 menu vshift 8
509 menu rows 18
510 menu margin 8
511 #menu hidden
512 menu helpmsgrow 15
513 menu tabmsgrow 13
515 menu color border * #00000000 #00000000 none
516 menu color sel 0 #ffffffff #00000000 none
517 menu color title 0 #ff7ba3d0 #00000000 none
518 menu color tabmsg 0 #ff3a6496 #00000000 none
519 menu color unsel 0 #84b8ffff #00000000 none
520 menu color hotsel 0 #84b8ffff #00000000 none
521 menu color hotkey 0 #ffffffff #00000000 none
522 menu color help 0 #ffffffff #00000000 none
523 menu color scrollbar 0 #ffffffff #ff355594 none
524 menu color timeout 0 #ffffffff #00000000 none
525 menu color timeout_msg 0 #ffffffff #00000000 none
526 menu color cmdmark 0 #84b8ffff #00000000 none
527 menu color cmdline 0 #ffffffff #00000000 none
529 menu tabmsg Press Tab for full configuration options on menu items.
530 menu separator
531 """ % args
533 def __get_image_stanza(self, is_xen, isDracut, **args):
534 if isDracut:
535 args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args
536 else:
537 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
539 if not is_xen:
540 template = """label %(short)s
541 menu label %(long)s
542 kernel vmlinuz%(index)s
543 append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
545 else:
546 template = """label %(short)s
547 menu label %(long)s
548 kernel mboot.c32
549 append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s --- initrd%(index)s.img
551 if args.get("help"):
552 template += """ text help
553 %(help)s
554 endtext
556 return template % args
558 def __get_image_stanzas(self, isodir):
559 kernels = self._get_kernel_versions()
560 kernel_options = self._get_kernel_options()
561 checkisomd5 = self._has_checkisomd5()
563 # Stanzas for insertion into the config template
564 linux = []
565 basic = []
566 check = []
568 index = "0"
569 for kernel, version in ((k,v) for k in kernels for v in kernels[k]):
570 (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index)
571 if index == "0":
572 self._isDracut = isDracut
574 default = self.__is_default_kernel(kernel, kernels)
576 if default:
577 long = self.product
578 elif kernel.startswith("kernel-"):
579 long = "%s (%s)" % (self.product, kernel[7:])
580 else:
581 long = "%s (%s)" % (self.product, kernel)
583 # tell dracut not to ask for LUKS passwords or activate mdraid sets
584 if isDracut:
585 kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0"
586 else:
587 kern_opts = kernel_options
589 linux.append(self.__get_image_stanza(is_xen, isDracut,
590 fslabel = self.fslabel,
591 isofstype = "auto",
592 liveargs = kern_opts,
593 long = "^Start " + long,
594 short = "linux" + index,
595 extra = "",
596 help = "",
597 index = index))
599 if default:
600 linux[-1] += " menu default\n"
602 basic.append(self.__get_image_stanza(is_xen, isDracut,
603 fslabel = self.fslabel,
604 isofstype = "auto",
605 liveargs = kern_opts,
606 long = "Start " + long + " in ^basic graphics mode.",
607 short = "basic" + index,
608 extra = "xdriver=vesa nomodeset",
609 help = "Try this option out if you're having trouble starting.",
610 index = index))
612 if checkisomd5:
613 check.append(self.__get_image_stanza(is_xen, isDracut,
614 fslabel = self.fslabel,
615 isofstype = "auto",
616 liveargs = kern_opts,
617 long = "^Test this media & start " + long,
618 short = "check" + index,
619 extra = "rd.live.check",
620 help = "",
621 index = index))
622 else:
623 check.append(None)
625 index = str(int(index) + 1)
627 return (linux, basic, check)
629 def __get_memtest_stanza(self, isodir):
630 memtest = glob.glob(self._instroot + "/boot/memtest86*")
631 if not memtest:
632 return ""
634 shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
636 return """label memtest
637 menu label Run a ^memory test.
638 text help
639 If your system is having issues, an problem with your
640 system's memory may be the cause. Use this utility to
641 see if the memory is working correctly.
642 endtext
643 kernel memtest
646 def __get_local_stanza(self, isodir):
647 return """label local
648 menu label Boot from ^local drive
649 localboot 0xffff
652 def _configure_syslinux_bootloader(self, isodir):
653 """configure the boot loader"""
654 makedirs(isodir + "/isolinux")
656 menu = self.__find_syslinux_menu()
658 self.__copy_syslinux_files(isodir, menu,
659 self.__find_syslinux_mboot())
661 background = ""
662 if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
663 background = "splash.jpg"
665 cfg = self.__get_basic_syslinux_config(menu = menu,
666 background = background,
667 title = self.title,
668 timeout = self._timeout * 10)
669 cfg += "menu separator\n"
671 linux, basic, check = self.__get_image_stanzas(isodir)
672 # Add linux stanzas to main menu
673 for s in linux:
674 cfg += s
675 cfg += "menu separator\n"
677 cfg += """menu begin ^Troubleshooting
678 menu title Troubleshooting
680 # Add basic video and check to submenu
681 for b, c in zip(basic, check):
682 cfg += b
683 if c:
684 cfg += c
686 cfg += self.__get_memtest_stanza(isodir)
687 cfg += "menu separator\n"
689 cfg += self.__get_local_stanza(isodir)
690 cfg += self._get_isolinux_stanzas(isodir)
692 cfg += """menu separator
693 label returntomain
694 menu label Return to ^main menu.
695 menu exit
696 menu end
698 cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
699 cfgf.write(cfg)
700 cfgf.close()
702 @property
703 def efiarch(self):
704 if not self._efiarch:
705 # for most things, we want them named boot$efiarch
706 efiarch = {"i386": "IA32", "x86_64": "X64"}
707 self._efiarch = efiarch[rpmUtils.arch.getBaseArch()]
708 return self._efiarch
710 def __copy_efi_files(self, isodir):
711 """ Copy the efi files into /EFI/BOOT/
712 If any of them are missing, return False.
713 requires:
714 shim.efi
715 gcdx64.efi
716 fonts/unicode.pf2
718 fail = False
719 missing = []
720 files = [("/boot/efi/EFI/*/shim.efi", "/EFI/BOOT/BOOT%s.efi" % (self.efiarch,)),
721 ("/boot/efi/EFI/*/gcdx64.efi", "/EFI/BOOT/grubx64.efi"),
722 ("/boot/efi/EFI/*/fonts/unicode.pf2", "/EFI/BOOT/fonts/"),
724 makedirs(isodir+"/EFI/BOOT/fonts/")
725 for src, dest in files:
726 src_glob = glob.glob(self._instroot+src)
727 if not src_glob:
728 missing.append("Missing EFI file (%s)" % (src,))
729 fail = True
730 else:
731 shutil.copy(src_glob[0], isodir+dest)
732 map(logging.error, missing)
733 return fail
735 def __get_basic_efi_config(self, **args):
736 return """
737 set default="0"
739 function load_video {
740 insmod efi_gop
741 insmod efi_uga
742 insmod video_bochs
743 insmod video_cirrus
744 insmod all_video
747 load_video
748 set gfxpayload=keep
749 insmod gzio
750 insmod part_gpt
751 insmod ext2
753 set timeout=%(timeout)d
754 ### END /etc/grub.d/00_header ###
756 search --no-floppy --set=root -l '%(isolabel)s'
758 ### BEGIN /etc/grub.d/10_linux ###
759 """ %args
761 def __get_efi_image_stanza(self, **args):
762 if self._isDracut:
763 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
764 else:
765 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
766 return """menuentry '%(long)s' --class fedora --class gnu-linux --class gnu --class os {
767 linuxefi /isolinux/vmlinuz%(index)s root=%(rootlabel)s %(liveargs)s %(extra)s
768 initrdefi /isolinux/initrd%(index)s.img
770 """ %args
772 def __get_efi_image_stanzas(self, isodir, name):
773 # FIXME: this only supports one kernel right now...
775 kernel_options = self._get_kernel_options()
776 checkisomd5 = self._has_checkisomd5()
778 cfg = ""
780 for index in range(0, 9):
781 # we don't support xen kernels
782 if os.path.exists("%s/EFI/BOOT/xen%d.gz" %(isodir, index)):
783 continue
784 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
785 liveargs = kernel_options,
786 long = name,
787 extra = "", index = index)
788 if checkisomd5:
789 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
790 liveargs = kernel_options,
791 long = "Verify and Boot " + name,
792 extra = "rd.live.check",
793 index = index)
794 break
796 return cfg
798 def _configure_efi_bootloader(self, isodir):
799 """Set up the configuration for an EFI bootloader"""
800 if self.__copy_efi_files(isodir):
801 shutil.rmtree(isodir + "/EFI")
802 logging.warn("Failed to copy EFI files, no EFI Support will be included.")
803 return
805 cfg = self.__get_basic_efi_config(isolabel = self.fslabel,
806 timeout = self._timeout)
807 cfg += self.__get_efi_image_stanzas(isodir, self.name)
809 cfgf = open(isodir + "/EFI/BOOT/grub.cfg", "w")
810 cfgf.write(cfg)
811 cfgf.close()
813 # first gen mactel machines get the bootloader name wrong apparently
814 if rpmUtils.arch.getBaseArch() == "i386":
815 os.link(isodir + "/EFI/BOOT/BOOT%s.efi" % (self.efiarch),
816 isodir + "/EFI/BOOT/BOOT.efi")
819 def _configure_bootloader(self, isodir):
820 self._configure_syslinux_bootloader(isodir)
821 self._configure_efi_bootloader(isodir)
823 class ppcLiveImageCreator(LiveImageCreatorBase):
824 def _get_mkisofs_options(self, isodir):
825 return [ "-hfs", "-no-desktop", "-part",
826 "-map", isodir + "/ppc/mapping",
827 "-hfs-bless", isodir + "/ppc/mac",
828 "-hfs-volid", self.fslabel ]
830 def _get_required_packages(self):
831 return ["yaboot"] + \
832 LiveImageCreatorBase._get_required_packages(self)
834 def _get_excluded_packages(self):
835 # kind of hacky, but exclude memtest86+ on ppc so it can stay in cfg
836 return ["memtest86+"] + \
837 LiveImageCreatorBase._get_excluded_packages(self)
839 def __copy_boot_file(self, destdir, file):
840 for dir in ["/usr/share/ppc64-utils",
841 "/usr/lib/anaconda-runtime/boot"]:
842 path = self._instroot + dir + "/" + file
843 if not os.path.exists(path):
844 continue
846 makedirs(destdir)
847 shutil.copy(path, destdir)
848 return
850 raise CreatorError("Unable to find boot file " + file)
852 def __kernel_bits(self, kernel):
853 testpath = (self._instroot + "/lib/modules/" +
854 kernel + "/kernel/arch/powerpc/platforms")
856 if not os.path.exists(testpath):
857 return { "32" : True, "64" : False }
858 else:
859 return { "32" : False, "64" : True }
861 def __copy_kernel_and_initramfs(self, destdir, version):
862 isDracut = False
863 bootdir = self._instroot + "/boot"
865 makedirs(destdir)
867 shutil.copyfile(bootdir + "/vmlinuz-" + version,
868 destdir + "/vmlinuz")
870 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
871 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
872 destdir + "/initrd.img")
873 isDracut = True
874 else:
875 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
876 destdir + "/initrd.img")
878 return isDracut
880 def __get_basic_yaboot_config(self, **args):
881 return """
882 init-message = "Welcome to %(name)s"
883 timeout=%(timeout)d
884 """ % args
886 def __get_image_stanza(self, **args):
887 if args["isDracut"]:
888 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
889 else:
890 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
891 return """
893 image=/ppc/ppc%(bit)s/vmlinuz
894 label=%(short)s
895 initrd=/ppc/ppc%(bit)s/initrd.img
896 read-only
897 append="root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s"
898 """ % args
901 def __write_yaboot_config(self, isodir, bit, isDracut = False):
902 cfg = self.__get_basic_yaboot_config(name = self.name,
903 timeout = self._timeout * 100)
905 kernel_options = self._get_kernel_options()
907 cfg += self.__get_image_stanza(fslabel = self.fslabel,
908 isofstype = "auto",
909 short = "linux",
910 long = "Run from image",
911 extra = "",
912 bit = bit,
913 liveargs = kernel_options,
914 isDracut = isDracut)
916 if self._has_checkisomd5():
917 cfg += self.__get_image_stanza(fslabel = self.fslabel,
918 isofstype = "auto",
919 short = "rd.live.check",
920 long = "Verify and run from image",
921 extra = "rd.live.check",
922 bit = bit,
923 liveargs = kernel_options,
924 isDracut = isDracut)
926 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
927 f.write(cfg)
928 f.close()
930 def __write_not_supported(self, isodir, bit):
931 makedirs(isodir + "/ppc/ppc" + bit)
933 message = "Sorry, this LiveCD does not support your hardware"
935 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
936 f.write('init-message = "' + message + '"')
937 f.close()
940 def __write_dualbits_yaboot_config(isodir, **args):
941 cfg = """
942 init-message = "\nWelcome to %(name)s!\nUse 'linux32' for 32-bit kernel.\n\n"
943 timeout=%(timeout)d
944 default=linux
946 image=/ppc/ppc64/vmlinuz
947 label=linux64
948 alias=linux
949 initrd=/ppc/ppc64/initrd.img
950 read-only
952 image=/ppc/ppc32/vmlinuz
953 label=linux32
954 initrd=/ppc/ppc32/initrd.img
955 read-only
956 """ % args
958 f = open(isodir + "/etc/yaboot.conf", "w")
959 f.write(cfg)
960 f.close()
962 def _configure_bootloader(self, isodir):
963 """configure the boot loader"""
964 havekernel = { 32: False, 64: False }
966 self.__copy_boot_file(isodir + "/ppc", "mapping")
967 self.__copy_boot_file(isodir + "/ppc", "bootinfo.txt")
968 self.__copy_boot_file(isodir + "/ppc/mac", "ofboot.b")
970 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
971 isodir + "/ppc/mac/yaboot")
973 makedirs(isodir + "/ppc/chrp")
974 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
975 isodir + "/ppc/chrp/yaboot")
977 subprocess.call(["/usr/sbin/addnote", isodir + "/ppc/chrp/yaboot"])
980 # FIXME: ppc should support multiple kernels too...
982 kernel = self._get_kernel_versions().values()[0][0]
984 kernel_bits = self.__kernel_bits(kernel)
986 for (bit, present) in kernel_bits.items():
987 if not present:
988 self.__write_not_supported(isodir, bit)
989 continue
991 isDracut = self.__copy_kernel_and_initramfs(isodir + "/ppc/ppc" + bit, kernel)
992 self.__write_yaboot_config(isodir, bit, isDracut)
994 makedirs(isodir + "/etc")
995 if kernel_bits["32"] and not kernel_bits["64"]:
996 shutil.copyfile(isodir + "/ppc/ppc32/yaboot.conf",
997 isodir + "/etc/yaboot.conf")
998 elif kernel_bits["64"] and not kernel_bits["32"]:
999 shutil.copyfile(isodir + "/ppc/ppc64/yaboot.conf",
1000 isodir + "/etc/yaboot.conf")
1001 else:
1002 self.__write_dualbits_yaboot_config(isodir,
1003 name = self.name,
1004 timeout = self._timeout * 100)
1007 # FIXME: build 'netboot' images with kernel+initrd, like mk-images.ppc
1010 class ppc64LiveImageCreator(ppcLiveImageCreator):
1011 def _get_excluded_packages(self):
1012 # FIXME:
1013 # while kernel.ppc and kernel.ppc64 co-exist,
1014 # we can't have both
1015 return ["kernel.ppc"] + \
1016 ppcLiveImageCreator._get_excluded_packages(self)
1018 arch = rpmUtils.arch.getBaseArch()
1019 if arch in ("i386", "x86_64"):
1020 LiveImageCreator = x86LiveImageCreator
1021 elif arch in ("ppc",):
1022 LiveImageCreator = ppcLiveImageCreator
1023 elif arch in ("ppc64",):
1024 LiveImageCreator = ppc64LiveImageCreator
1025 elif arch.startswith('arm'):
1026 LiveImageCreator = LiveImageCreatorBase
1028 else:
1029 raise CreatorError("Architecture not supported!")