if mounting squashfs add ro mount option
[livecd.git] / imgcreate / live.py
blobcd022a53cff3ebdb9336d501da08d5523705b80d
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):
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)
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('filesystems+="' + self.__extra_filesystems() + ' "\n')
299 f.write('drivers+="' + self.__extra_drivers() + ' "\n')
300 f.write('add_dracutmodules+=" dmsquash-live "')
301 f.close()
303 def __create_iso(self, isodir):
304 iso = self._outdir + "/" + self.name + ".iso"
306 args = ["/usr/bin/mkisofs",
307 "-J", "-r",
308 "-hide-rr-moved", "-hide-joliet-trans-tbl",
309 "-V", self.fslabel,
310 "-o", iso]
312 args.extend(self._get_mkisofs_options(isodir))
313 if self._isofstype == "udf":
314 args.append("-allow-limited-size")
316 args.append(isodir)
318 if subprocess.call(args) != 0:
319 raise CreatorError("ISO creation failed!")
321 if os.path.exists("/usr/bin/isohybrid"):
322 if os.path.exists(isodir + "/isolinux/efiboot.img"):
323 subprocess.call(["/usr/bin/isohybrid", "-u", "-m", iso])
324 else:
325 subprocess.call(["/usr/bin/isohybrid", iso])
327 self.__implant_md5sum(iso)
329 def __implant_md5sum(self, iso):
330 """Implant an isomd5sum."""
331 if os.path.exists("/usr/bin/implantisomd5"):
332 implantisomd5 = "/usr/bin/implantisomd5"
333 elif os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"):
334 implantisomd5 = "/usr/lib/anaconda-runtime/implantisomd5"
335 else:
336 logging.warn("isomd5sum not installed; not setting up mediacheck")
337 return
339 subprocess.call([implantisomd5, iso])
341 def _stage_final_image(self):
342 try:
343 makedirs(self.__ensure_isodir() + "/LiveOS")
345 self._resparse()
347 if not self.skip_minimize:
348 create_image_minimizer(self.__isodir + "/LiveOS/osmin.img",
349 self._image, self.compress_type,
350 tmpdir = self.tmpdir)
352 if self.skip_compression:
353 shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
354 if os.stat(self.__isodir + "/LiveOS/ext3fs.img").st_size >= 4*1024*1024*1024:
355 self._isofstype = "udf"
356 logging.warn("Switching to UDF due to size of LiveOS/ext3fs.img")
357 else:
358 makedirs(os.path.join(os.path.dirname(self._image), "LiveOS"))
359 shutil.move(self._image,
360 os.path.join(os.path.dirname(self._image),
361 "LiveOS", "ext3fs.img"))
362 mksquashfs(os.path.dirname(self._image),
363 self.__isodir + "/LiveOS/squashfs.img",
364 self.compress_type)
365 if os.stat(self.__isodir + "/LiveOS/squashfs.img").st_size >= 4*1024*1024*1024:
366 self._isofstype = "udf"
367 logging.warn("Switching to UDF due to size of LiveOS/squashfs.img")
370 self.__create_iso(self.__isodir)
371 finally:
372 shutil.rmtree(self.__isodir, ignore_errors = True)
373 self.__isodir = None
375 class x86LiveImageCreator(LiveImageCreatorBase):
376 """ImageCreator for x86 machines"""
377 def _get_mkisofs_options(self, isodir):
378 options = [ "-b", "isolinux/isolinux.bin",
379 "-c", "isolinux/boot.cat",
380 "-no-emul-boot", "-boot-info-table",
381 "-boot-load-size", "4" ]
382 if os.path.exists(isodir + "/isolinux/efiboot.img"):
383 options.extend([ "-eltorito-alt-boot",
384 "-e", "isolinux/efiboot.img",
385 "-no-emul-boot",
386 "-eltorito-alt-boot",
387 "-e", "isolinux/macboot.img",
388 "-no-emul-boot"])
389 return options
391 def _get_required_packages(self):
392 return ["syslinux"] + LiveImageCreatorBase._get_required_packages(self)
394 def _get_isolinux_stanzas(self, isodir):
395 return ""
397 def __find_syslinux_menu(self):
398 for menu in ("vesamenu.c32", "menu.c32"):
399 for dir in ("/usr/lib/syslinux/", "/usr/share/syslinux/"):
400 if os.path.isfile(self._instroot + dir + menu):
401 return menu
403 raise CreatorError("syslinux not installed : "
404 "no suitable *menu.c32 found")
406 def __find_syslinux_mboot(self):
408 # We only need the mboot module if we have any xen hypervisors
410 if not glob.glob(self._instroot + "/boot/xen.gz*"):
411 return None
413 return "mboot.c32"
415 def __copy_syslinux_files(self, isodir, menu, mboot = None):
416 files = ["isolinux.bin", menu]
417 if mboot:
418 files += [mboot]
420 for f in files:
421 if os.path.exists(self._instroot + "/usr/lib/syslinux/" + f):
422 path = self._instroot + "/usr/lib/syslinux/" + f
423 elif os.path.exists(self._instroot + "/usr/share/syslinux/" + f):
424 path = self._instroot + "/usr/share/syslinux/" + f
425 if not os.path.isfile(path):
426 raise CreatorError("syslinux not installed : "
427 "%s not found" % path)
429 shutil.copy(path, isodir + "/isolinux/")
431 def __copy_syslinux_background(self, isodest):
432 background_path = self._instroot + \
433 "/usr/share/anaconda/boot/syslinux-vesa-splash.jpg"
435 if not os.path.exists(background_path):
436 # fallback to F13 location
437 background_path = self._instroot + \
438 "/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg"
440 if not os.path.exists(background_path):
441 return False
443 shutil.copyfile(background_path, isodest)
445 return True
447 def __copy_kernel_and_initramfs(self, isodir, version, index):
448 bootdir = self._instroot + "/boot"
450 shutil.copyfile(bootdir + "/vmlinuz-" + version,
451 isodir + "/isolinux/vmlinuz" + index)
453 isDracut = False
454 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
455 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
456 isodir + "/isolinux/initrd" + index + ".img")
457 isDracut = True
458 elif os.path.exists(bootdir + "/initrd-" + version + ".img"):
459 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
460 isodir + "/isolinux/initrd" + index + ".img")
461 elif not self.base_on:
462 logging.error("No initrd or initramfs found for %s" % (version,))
464 is_xen = False
465 if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
466 shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
467 isodir + "/isolinux/xen" + index + ".gz")
468 is_xen = True
470 return (is_xen, isDracut)
472 def __is_default_kernel(self, kernel, kernels):
473 if len(kernels) == 1:
474 return True
476 if kernel == self._default_kernel:
477 return True
479 if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
480 return True
482 return False
484 def __get_basic_syslinux_config(self, **args):
485 return """
486 default %(menu)s
487 timeout %(timeout)d
488 menu background %(background)s
489 menu autoboot Starting %(title)s in # second{,s}. Press any key to interrupt.
491 menu clear
492 menu title %(title)s
493 menu vshift 8
494 menu rows 18
495 menu margin 8
496 #menu hidden
497 menu helpmsgrow 15
498 menu tabmsgrow 13
500 menu color border * #00000000 #00000000 none
501 menu color sel 0 #ffffffff #00000000 none
502 menu color title 0 #ff7ba3d0 #00000000 none
503 menu color tabmsg 0 #ff3a6496 #00000000 none
504 menu color unsel 0 #84b8ffff #00000000 none
505 menu color hotsel 0 #84b8ffff #00000000 none
506 menu color hotkey 0 #ffffffff #00000000 none
507 menu color help 0 #ffffffff #00000000 none
508 menu color scrollbar 0 #ffffffff #ff355594 none
509 menu color timeout 0 #ffffffff #00000000 none
510 menu color timeout_msg 0 #ffffffff #00000000 none
511 menu color cmdmark 0 #84b8ffff #00000000 none
512 menu color cmdline 0 #ffffffff #00000000 none
514 menu tabmsg Press Tab for full configuration options on menu items.
515 menu separator
516 """ % args
518 def __get_image_stanza(self, is_xen, isDracut, **args):
519 if isDracut:
520 args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args
521 else:
522 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
524 if not is_xen:
525 template = """label %(short)s
526 menu label %(long)s
527 kernel vmlinuz%(index)s
528 append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
530 else:
531 template = """label %(short)s
532 menu label %(long)s
533 kernel mboot.c32
534 append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s --- initrd%(index)s.img
536 if args.get("help"):
537 template += """ text help
538 %(help)s
539 endtext
541 return template % args
543 def __get_image_stanzas(self, isodir):
544 versions = []
545 kernels = self._get_kernel_versions()
546 for kernel in kernels:
547 for version in kernels[kernel]:
548 versions.append(version)
550 kernel_options = self._get_kernel_options()
552 checkisomd5 = self._has_checkisomd5()
554 # Stanzas for insertion into the config template
555 linux = []
556 basic = []
557 check = []
559 index = "0"
560 for version in versions:
561 (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index)
562 if index == "0":
563 self._isDracut = isDracut
565 default = self.__is_default_kernel(kernel, kernels)
567 if default:
568 long = self.product
569 elif kernel.startswith("kernel-"):
570 long = "%s (%s)" % (self.product, kernel[7:])
571 else:
572 long = "%s (%s)" % (self.product, kernel)
574 # tell dracut not to ask for LUKS passwords or activate mdraid sets
575 if isDracut:
576 kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0"
577 else:
578 kern_opts = kernel_options
580 linux.append(self.__get_image_stanza(is_xen, isDracut,
581 fslabel = self.fslabel,
582 isofstype = "auto",
583 liveargs = kern_opts,
584 long = "^Start " + long,
585 short = "linux" + index,
586 extra = "",
587 help = "",
588 index = index))
590 if default:
591 linux[-1] += " menu default\n"
593 basic.append(self.__get_image_stanza(is_xen, isDracut,
594 fslabel = self.fslabel,
595 isofstype = "auto",
596 liveargs = kern_opts,
597 long = "Start " + long + " in ^basic graphics mode.",
598 short = "basic" + index,
599 extra = "xdriver=vesa nomodeset",
600 help = "Try this option out if you're having trouble starting.",
601 index = index))
603 if checkisomd5:
604 check.append(self.__get_image_stanza(is_xen, isDracut,
605 fslabel = self.fslabel,
606 isofstype = "auto",
607 liveargs = kern_opts,
608 long = "^Test this media & start " + long,
609 short = "check" + index,
610 extra = "rd.live.check",
611 help = "",
612 index = index))
613 else:
614 check.append(None)
616 index = str(int(index) + 1)
618 return (linux, basic, check)
620 def __get_memtest_stanza(self, isodir):
621 memtest = glob.glob(self._instroot + "/boot/memtest86*")
622 if not memtest:
623 return ""
625 shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
627 return """label memtest
628 menu label Run a ^memory test.
629 text help
630 If your system is having issues, an problem with your
631 system's memory may be the cause. Use this utility to
632 see if the memory is working correctly.
633 endtext
634 kernel memtest
637 def __get_local_stanza(self, isodir):
638 return """label local
639 menu label Boot from ^local drive
640 localboot 0xffff
643 def _configure_syslinux_bootloader(self, isodir):
644 """configure the boot loader"""
645 makedirs(isodir + "/isolinux")
647 menu = self.__find_syslinux_menu()
649 self.__copy_syslinux_files(isodir, menu,
650 self.__find_syslinux_mboot())
652 background = ""
653 if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
654 background = "splash.jpg"
656 cfg = self.__get_basic_syslinux_config(menu = menu,
657 background = background,
658 title = self.title,
659 timeout = self._timeout * 10)
660 cfg += "menu separator\n"
662 linux, basic, check = self.__get_image_stanzas(isodir)
663 # Add linux stanzas to main menu
664 for s in linux:
665 cfg += s
666 cfg += "menu separator\n"
668 cfg += """menu begin ^Troubleshooting
669 menu title Troubleshooting
671 # Add basic video and check to submenu
672 for b, c in zip(basic, check):
673 cfg += b
674 if c:
675 cfg += c
677 cfg += self.__get_memtest_stanza(isodir)
678 cfg += "menu separator\n"
680 cfg += self.__get_local_stanza(isodir)
681 cfg += self._get_isolinux_stanzas(isodir)
683 cfg += """menu separator
684 label returntomain
685 menu label Return to ^main menu.
686 menu exit
687 menu end
689 cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
690 cfgf.write(cfg)
691 cfgf.close()
693 def __copy_efi_files(self, isodir):
694 if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"):
695 return False
696 shutil.copy(self._instroot + "/boot/efi/EFI/redhat/grub.efi",
697 isodir + "/EFI/BOOT/grub.efi")
699 # Should exist, but if it doesn't we should fail
700 if os.path.exists(self._instroot + "/boot/grub/splash.xpm.gz"):
701 shutil.copy(self._instroot + "/boot/grub/splash.xpm.gz",
702 isodir + "/EFI/BOOT/splash.xpm.gz")
704 return True
706 def __get_basic_efi_config(self, **args):
707 return """
708 default=0
709 splashimage=/EFI/BOOT/splash.xpm.gz
710 timeout %(timeout)d
711 hiddenmenu
713 """ %args
715 def __get_efi_image_stanza(self, **args):
716 if self._isDracut:
717 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
718 else:
719 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
720 return """title %(long)s
721 findiso
722 kernel /isolinux/vmlinuz%(index)s root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s
723 initrd /isolinux/initrd%(index)s.img
724 """ %args
726 def __get_efi_image_stanzas(self, isodir, name):
727 # FIXME: this only supports one kernel right now...
729 kernel_options = self._get_kernel_options()
730 checkisomd5 = self._has_checkisomd5()
732 cfg = ""
734 for index in range(0, 9):
735 # we don't support xen kernels
736 if os.path.exists("%s/EFI/BOOT/xen%d.gz" %(isodir, index)):
737 continue
738 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
739 isofstype = "auto",
740 liveargs = kernel_options,
741 long = name,
742 extra = "", index = index)
743 if checkisomd5:
744 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
745 isofstype = "auto",
746 liveargs = kernel_options,
747 long = "Verify and Boot " + name,
748 extra = "rd.live.check",
749 index = index)
750 break
752 return cfg
754 def _configure_efi_bootloader(self, isodir):
755 """Set up the configuration for an EFI bootloader"""
756 makedirs(isodir + "/EFI/BOOT")
758 if not self.__copy_efi_files(isodir):
759 shutil.rmtree(isodir + "/EFI")
760 return
762 cfg = self.__get_basic_efi_config(name = self.name,
763 timeout = self._timeout)
764 cfg += self.__get_efi_image_stanzas(isodir, self.name)
766 cfgf = open(isodir + "/EFI/BOOT/grub.conf", "w")
767 cfgf.write(cfg)
768 cfgf.close()
770 # first gen mactel machines get the bootloader name wrong apparently
771 if rpmUtils.arch.getBaseArch() == "i386":
772 os.link(isodir + "/EFI/BOOT/grub.efi", isodir + "/EFI/BOOT/BOOT.efi")
773 os.link(isodir + "/EFI/BOOT/grub.conf", isodir + "/EFI/BOOT/BOOT.conf")
775 # for most things, we want them named boot$efiarch
776 efiarch = {"i386": "IA32", "x86_64": "X64"}
777 efiname = efiarch[rpmUtils.arch.getBaseArch()]
778 os.rename(isodir + "/EFI/BOOT/grub.efi", isodir + "/EFI/BOOT/BOOT%s.efi" %(efiname,))
779 os.link(isodir + "/EFI/BOOT/grub.conf", isodir + "/EFI/BOOT/BOOT%s.conf" %(efiname,))
782 def _configure_bootloader(self, isodir):
783 self._configure_syslinux_bootloader(isodir)
784 self._configure_efi_bootloader(isodir)
786 class ppcLiveImageCreator(LiveImageCreatorBase):
787 def _get_mkisofs_options(self, isodir):
788 return [ "-hfs", "-no-desktop", "-part",
789 "-map", isodir + "/ppc/mapping",
790 "-hfs-bless", isodir + "/ppc/mac",
791 "-hfs-volid", self.fslabel ]
793 def _get_required_packages(self):
794 return ["yaboot"] + \
795 LiveImageCreatorBase._get_required_packages(self)
797 def _get_excluded_packages(self):
798 # kind of hacky, but exclude memtest86+ on ppc so it can stay in cfg
799 return ["memtest86+"] + \
800 LiveImageCreatorBase._get_excluded_packages(self)
802 def __copy_boot_file(self, destdir, file):
803 for dir in ["/usr/share/ppc64-utils",
804 "/usr/lib/anaconda-runtime/boot"]:
805 path = self._instroot + dir + "/" + file
806 if not os.path.exists(path):
807 continue
809 makedirs(destdir)
810 shutil.copy(path, destdir)
811 return
813 raise CreatorError("Unable to find boot file " + file)
815 def __kernel_bits(self, kernel):
816 testpath = (self._instroot + "/lib/modules/" +
817 kernel + "/kernel/arch/powerpc/platforms")
819 if not os.path.exists(testpath):
820 return { "32" : True, "64" : False }
821 else:
822 return { "32" : False, "64" : True }
824 def __copy_kernel_and_initramfs(self, destdir, version):
825 isDracut = False
826 bootdir = self._instroot + "/boot"
828 makedirs(destdir)
830 shutil.copyfile(bootdir + "/vmlinuz-" + version,
831 destdir + "/vmlinuz")
833 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
834 shutil.copyfile(bootdir + "/initramfs-" + version + ".img",
835 destdir + "/initrd.img")
836 isDracut = True
837 else:
838 shutil.copyfile(bootdir + "/initrd-" + version + ".img",
839 destdir + "/initrd.img")
841 return isDracut
843 def __get_basic_yaboot_config(self, **args):
844 return """
845 init-message = "Welcome to %(name)s"
846 timeout=%(timeout)d
847 """ % args
849 def __get_image_stanza(self, **args):
850 if args["isDracut"]:
851 args["rootlabel"] = "live:LABEL=%(fslabel)s" % args
852 else:
853 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
854 return """
856 image=/ppc/ppc%(bit)s/vmlinuz
857 label=%(short)s
858 initrd=/ppc/ppc%(bit)s/initrd.img
859 read-only
860 append="root=%(rootlabel)s rootfstype=%(isofstype)s %(liveargs)s %(extra)s"
861 """ % args
864 def __write_yaboot_config(self, isodir, bit, isDracut = False):
865 cfg = self.__get_basic_yaboot_config(name = self.name,
866 timeout = self._timeout * 100)
868 kernel_options = self._get_kernel_options()
870 cfg += self.__get_image_stanza(fslabel = self.fslabel,
871 isofstype = "auto",
872 short = "linux",
873 long = "Run from image",
874 extra = "",
875 bit = bit,
876 liveargs = kernel_options,
877 isDracut = isDracut)
879 if self._has_checkisomd5():
880 cfg += self.__get_image_stanza(fslabel = self.fslabel,
881 isofstype = "auto",
882 short = "rd.live.check",
883 long = "Verify and run from image",
884 extra = "rd.live.check",
885 bit = bit,
886 liveargs = kernel_options,
887 isDracut = isDracut)
889 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
890 f.write(cfg)
891 f.close()
893 def __write_not_supported(self, isodir, bit):
894 makedirs(isodir + "/ppc/ppc" + bit)
896 message = "Sorry, this LiveCD does not support your hardware"
898 f = open(isodir + "/ppc/ppc" + bit + "/yaboot.conf", "w")
899 f.write('init-message = "' + message + '"')
900 f.close()
903 def __write_dualbits_yaboot_config(isodir, **args):
904 cfg = """
905 init-message = "\nWelcome to %(name)s!\nUse 'linux32' for 32-bit kernel.\n\n"
906 timeout=%(timeout)d
907 default=linux
909 image=/ppc/ppc64/vmlinuz
910 label=linux64
911 alias=linux
912 initrd=/ppc/ppc64/initrd.img
913 read-only
915 image=/ppc/ppc32/vmlinuz
916 label=linux32
917 initrd=/ppc/ppc32/initrd.img
918 read-only
919 """ % args
921 f = open(isodir + "/etc/yaboot.conf", "w")
922 f.write(cfg)
923 f.close()
925 def _configure_bootloader(self, isodir):
926 """configure the boot loader"""
927 havekernel = { 32: False, 64: False }
929 self.__copy_boot_file(isodir + "/ppc", "mapping")
930 self.__copy_boot_file(isodir + "/ppc", "bootinfo.txt")
931 self.__copy_boot_file(isodir + "/ppc/mac", "ofboot.b")
933 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
934 isodir + "/ppc/mac/yaboot")
936 makedirs(isodir + "/ppc/chrp")
937 shutil.copyfile(self._instroot + "/usr/lib/yaboot/yaboot",
938 isodir + "/ppc/chrp/yaboot")
940 subprocess.call(["/usr/sbin/addnote", isodir + "/ppc/chrp/yaboot"])
943 # FIXME: ppc should support multiple kernels too...
945 kernel = self._get_kernel_versions().values()[0][0]
947 kernel_bits = self.__kernel_bits(kernel)
949 for (bit, present) in kernel_bits.items():
950 if not present:
951 self.__write_not_supported(isodir, bit)
952 continue
954 isDracut = self.__copy_kernel_and_initramfs(isodir + "/ppc/ppc" + bit, kernel)
955 self.__write_yaboot_config(isodir, bit, isDracut)
957 makedirs(isodir + "/etc")
958 if kernel_bits["32"] and not kernel_bits["64"]:
959 shutil.copyfile(isodir + "/ppc/ppc32/yaboot.conf",
960 isodir + "/etc/yaboot.conf")
961 elif kernel_bits["64"] and not kernel_bits["32"]:
962 shutil.copyfile(isodir + "/ppc/ppc64/yaboot.conf",
963 isodir + "/etc/yaboot.conf")
964 else:
965 self.__write_dualbits_yaboot_config(isodir,
966 name = self.name,
967 timeout = self._timeout * 100)
970 # FIXME: build 'netboot' images with kernel+initrd, like mk-images.ppc
973 class ppc64LiveImageCreator(ppcLiveImageCreator):
974 def _get_excluded_packages(self):
975 # FIXME:
976 # while kernel.ppc and kernel.ppc64 co-exist,
977 # we can't have both
978 return ["kernel.ppc"] + \
979 ppcLiveImageCreator._get_excluded_packages(self)
981 arch = rpmUtils.arch.getBaseArch()
982 if arch in ("i386", "x86_64"):
983 LiveImageCreator = x86LiveImageCreator
984 elif arch in ("ppc",):
985 LiveImageCreator = ppcLiveImageCreator
986 elif arch in ("ppc64",):
987 LiveImageCreator = ppc64LiveImageCreator
988 elif arch.startswith('arm'):
989 LiveImageCreator = LiveImageCreatorBase
991 else:
992 raise CreatorError("Architecture not supported!")