At http://git.infradead.org/?p=users/dwmw2/livecd-ppc.git (git://git.infradead.org/~dwmw2/livecd-ppc.git) there's a set of patches which first introduces a little abstraction for the arch-specific configureBootloader() and createIso() functions, then implements the PPC version.
1. Allow mayflower path to be specified, to run from git tree 2, Remove syslinux from package lists; add it dynamically instead. 3. Move x86-specific functions (createIso, configureBootloader) into subclass 4. Only create isolinux/ directory in x86-specific configureBootloader() 5. Add PowerPC support, add pata_mpc52xx module to mayflower.conf
The full patch is as follows...
diff --git a/config/livecd-fedora-desktop.ks b/config/livecd-fedora-desktop.ks index 1ee6fba..780e201 100644 --- a/config/livecd-fedora-desktop.ks +++ b/config/livecd-fedora-desktop.ks @@ -23,7 +23,6 @@ services --enabled=NetworkManager,dhcdbd --disabled=network,sshd @dial-up @hardware-support @printing -syslinux kernel
scim* diff --git a/config/livecd-fedora-kde.ks b/config/livecd-fedora-kde.ks index 20413b1..9aeceed 100644 --- a/config/livecd-fedora-kde.ks +++ b/config/livecd-fedora-kde.ks @@ -23,7 +23,6 @@ kernel dejavu-lgc-fonts setroubleshoot smolt -syslinux system-config-display xorg-x11-drivers
diff --git a/config/livecd-fedora-minimal.ks b/config/livecd-fedora-minimal.ks index e8065b6..db52912 100644 --- a/config/livecd-fedora-minimal.ks +++ b/config/livecd-fedora-minimal.ks @@ -17,7 +17,6 @@ repo --name=a-extras-dev --baseurl=http://download.fedora.redhat.com/pub/fedora/ %packages bash kernel -syslinux passwd policycoreutils chkconfig diff --git a/config/livedvd-fedora-kde.ks b/config/livedvd-fedora-kde.ks index 1ed2f57..71aa122 100644 --- a/config/livedvd-fedora-kde.ks +++ b/config/livedvd-fedora-kde.ks @@ -23,7 +23,6 @@ kernel dejavu-lgc-fonts setroubleshoot smolt -syslinux system-config-display xorg-x11-drivers
diff --git a/creator/livecd-creator b/creator/livecd-creator index a526b07..755e659 100755 --- a/creator/livecd-creator +++ b/creator/livecd-creator @@ -27,6 +27,8 @@ import traceback import subprocess import shutil import yum +import rpmUtils.arch +import rhpl import pykickstart.parser import pykickstart.version
@@ -277,6 +279,178 @@ class LiveCDYum(yum.YumBase): cb.filelog = False return self.runTransaction(cb)
+class InstallationArchPPC: + def __init__(self): + # For now we need anaconda-runtime, for bits like ofboot.b and mapping files. + # We could generate those ourselves though... + self.bootldr_package = ("yaboot","anaconda-runtime") + + def createIso(self, target): + """write out the live CD ISO""" + subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(target.fs_label,), + "-hfs", "-hfs-bless", "%s/out/ppc/mac" %(target.build_dir), + "-hfs-volid", "%s" %(target.fs_label,), "-part", + "-map", "/usr/lib/anaconda-runtime/boot/mapping", + "-J", "-r", "-hide-rr-moved", "-no-desktop", + "-V", "%s" %(target.fs_label,), "%s/out" %(target.build_dir)]) + + def configureBootloader(self, target): + """configure the boot loader""" + + # Copy yaboot and ofboot.b in to mac directory + os.makedirs(target.build_dir + "/out/ppc/mac") + shutil.copyfile("%s/install_root/usr/lib/anaconda-runtime/boot/ofboot.b" %(target.build_dir), + "%s/out/ppc/mac/ofboot.b" %(target.build_dir,)) + shutil.copyfile("%s/install_root/usr/lib/yaboot/yaboot" %(target.build_dir), + "%s/out/ppc/mac/yaboot" %(target.build_dir,)) + + # Copy yaboot and ofboot.b in to mac directory + os.makedirs(target.build_dir + "/out/ppc/chrp") + shutil.copyfile("%s/install_root/usr/lib/anaconda-runtime/boot/bootinfo.txt" %(target.build_dir), + "%s/out/ppc/bootinfo.txt" %(target.build_dir,)) + shutil.copyfile("%s/install_root/usr/lib/yaboot/yaboot" %(target.build_dir), + "%s/out/ppc/chrp/yaboot" %(target.build_dir,)) + subprocess.call(["/usr/sbin/addnote", "%s/out/ppc/chrp/yaboot" %(target.build_dir,)]) + + os.makedirs(target.build_dir + "/out/ppc/ppc32") + shutil.copyfile("%s/install_root/boot/vmlinuz-%s" + %(target.build_dir, target.get_kernel_version()), + "%s/out/ppc/ppc32/vmlinuz" %(target.build_dir,)) + shutil.copyfile("%s/install_root/boot/livecd-initramfs.img" + %(target.build_dir,), + "%s/out/ppc/ppc32/initrd.img" %(target.build_dir,)) + os.unlink("%s/install_root/boot/livecd-initramfs.img" + %(target.build_dir,)) + + os.makedirs(target.build_dir + "/out/ppc/ppc64") + # FIXME: Need 64-bit kernel and initrd + + cfg32 = """ +init-message = "Welcome to the Fedora %(label)s LiveCD" +timeout=6000 + +""" %{"label": target.fs_label} + + stanzas = [("linux", "Run from image", ""), + ("runfromram", "Run from RAM - requires 1 GiB+", "live_ram")] + if os.path.exists("%s/install_root/usr/lib/anaconda-runtime/checkisomd5" %(target.build_dir,)): + stanzas.append( ("check", "Verify and run from image", "check") ) + + for (short, long, extra) in stanzas: + cfg32 += """ + +image=/ppc/ppc32/vmlinuz + label=%(short)s + initrd=/ppc/ppc32/initrd.img + read-only + append="ro quiet root=CDLABEL=%(label)s rootfstype=iso9660 %(extra)s" +""" %{"label": target.fs_label, + "short": short, "long": long, "extra": extra} + + cfgf = open("%s/out/ppc/ppc32/yaboot.conf" %(target.build_dir,), "w") + cfgf.write(cfg32) + cfgf.close() + os.makedirs(target.build_dir + "/out/etc") + cfgf = open("%s/out/etc/yaboot.conf" %(target.build_dir,), "w") + cfgf.write(cfg32) + cfgf.close() + + # FIXME. Obviously + cfg64 = """ +init-message = "Sorry, this LiveCD only supports 32-bit hardware" +""" + cfgf = open("%s/out/ppc/ppc64/yaboot.conf" %(target.build_dir,), "w") + cfgf.write(cfg64) + cfgf.close() + + # TODO: enable external entitity to partipate in adding boot entries + # build 'netboot' images with kernel+initrd, like mk-images.ppc + +class InstallationArchX86: + def __init__(self): + self.bootldr_package = ("syslinux",) + + def createIso(self, target): + """write out the live CD ISO""" + subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(target.fs_label,), + "-b", "isolinux/isolinux.bin", + "-c", "isolinux/boot.cat", + "-no-emul-boot", "-boot-load-size", "4", + "-boot-info-table", "-J", "-r", "-hide-rr-moved", + "-V", "%s" %(target.fs_label,), "%s/out" %(target.build_dir)]) + + def configureBootloader(self, target): + """configure the boot loader""" + + # set up boot loader + # + # TODO: + # - fix for archs not using grub + # - fix for non-i386 + # - error handling + # + os.makedirs(target.build_dir + "/out/isolinux") + + shutil.copyfile("%s/install_root/boot/vmlinuz-%s" + %(target.build_dir, target.get_kernel_version()), + "%s/out/isolinux/vmlinuz" %(target.build_dir,)) + + shutil.copyfile("%s/install_root/boot/livecd-initramfs.img" + %(target.build_dir,), + "%s/out/isolinux/initrd.img" %(target.build_dir,)) + os.unlink("%s/install_root/boot/livecd-initramfs.img" + %(target.build_dir,)) + + for p in ["isolinux.bin", "vesamenu.c32"]: + path = "%s/install_root/usr/lib/syslinux/%s" % (target.build_dir, p) + + if not os.path.isfile(path): + raise InstallationError("syslinux not installed : %s not found" % path) + + shutil.copy(path, "%s/out/isolinux/%s" % (target.build_dir, p)) + + if os.path.exists("%s/install_root/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" %(target.build_dir,)): + shutil.copy("%s/install_root/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" %(target.build_dir,), + "%s/out/isolinux/splash.jpg" %(target.build_dir,)) + have_background = "menu background splash.jpg" + else: + have_background = "" + + cfg = """ +default vesamenu.c32 +timeout 600 + +%(background)s +menu title Welcome to %(label)s! +menu color border 0 #ffffffff #00000000 +menu color sel 0 #ffffffff #ff000000 +menu color title 0 #ffffffff #00000000 +menu color tabmsg 0 #ffffffff #00000000 +menu color unsel 0 #ffffffff #00000000 +menu color hotsel 0 #ff000000 #ffffffff +menu color hotkey 0 #ffffffff #ff000000 +""" %{"label": target.fs_label, "background" : have_background} + + stanzas = [("linux", "Run from image", ""), + ("runfromram", "Run from RAM - requires 1 GiB+", "live_ram")] + if os.path.exists("%s/install_root/usr/lib/anaconda-runtime/checkisomd5" %(target.build_dir,)): + stanzas.append( ("check", "Verify and run from image", "check") ) + + for (short, long, extra) in stanzas: + cfg += """label %(short)s + menu label %(long)s + kernel vmlinuz + append initrd=initrd.img ro quiet root=CDLABEL=%(label)s rootfstype=iso9660 liveimg %(extra)s +""" %{"label": target.fs_label, "background" : have_background, + "short": short, "long": long, "extra": extra} + + cfgf = open("%s/out/isolinux/isolinux.cfg" %(target.build_dir,), "w") + cfgf.write(cfg) + cfgf.close() + + # TODO: enable external entitity to partipate in adding boot entries + + class InstallationTarget: def __init__(self, repos, packages, epackages, groups, fs_label, skip_compression, skip_prelink): self.ayum = None @@ -293,6 +467,14 @@ class InstallationTarget: self.bindmounts = [] self.ksparser = None
+ myarch = rhpl.getArch() + if myarch == "i386" or myarch == "x86_64": + self.arch = InstallationArchX86() + elif myarch == "ppc": + self.arch = InstallationArchPPC() + else: + raise InstallationError("Do not know how to build LiveCD for arch '%s'" % (myarch,)) + def parse(self, kscfg): ksversion = pykickstart.version.makeVersion() self.ksparser = pykickstart.parser.KickstartParser(ksversion) @@ -377,7 +559,6 @@ class InstallationTarget: except OSError, (err, msg): raise InstallationError("Failed create build directory in /var/tmp: %s" % msg)
- os.makedirs(self.build_dir + "/out/isolinux") os.makedirs(self.build_dir + "/out/sysroot") os.makedirs(self.build_dir + "/data/sysroot") os.makedirs(self.build_dir + "/install_root") @@ -748,12 +929,12 @@ class InstallationTarget:
return kernels[0]
- def createInitramfs(self): + def createInitramfs(self, mayflower_path): # Create initramfs - if not os.path.isfile("/usr/lib/livecd-creator/mayflower"): + if not os.path.isfile(mayflower_path): raise InstallationError("livecd-creator not correctly installed : "+ - "/usr/lib/livecd-creator/mayflower not found") - shutil.copy("/usr/lib/livecd-creator/mayflower", + mayflower_path+" not found") + shutil.copy(mayflower_path, "%s/install_root/sbin/mayflower" %(self.build_dir,)) # modules needed for booting (this is butt ugly and we need to retrieve this from elsewhere, e.g. the kernel) mayflowerconf = open(self.build_dir + "/install_root/etc/mayflower.conf", "w") @@ -761,7 +942,7 @@ class InstallationTarget: mayflowerconf.write('MODULES+="sata_mv sata_qstor sata_sis sata_uli "\n') mayflowerconf.write('MODULES+="sata_nv sata_sil24 sata_svw sata_via "\n') mayflowerconf.write('MODULES+="sata_promise sata_sil sata_sx4 sata_vsc "\n') - mayflowerconf.write('MODULES+="ata_generic pata_ali pata_amd pata_artop pata_atiixp pata_cmd64x pata_cs5520 pata_cs5530 pata_cs5535 pata_cypress pata_efar pata_hpt366 pata_hpt37x pata_hpt3x2n pata_hpt3x3 pata_isapnp pata_it821x pata_jmicron pata_marvell pata_mpiix pata_netcell pata_ns87410 pata_oldpiix pata_optidma pata_opti pata_pcmcia pata_pdc2027x pata_pdc202xx_old pata_qdi pata_serverworks pata_sil680 pata_sis pata_sl82c105 pata_triflex pata_via pdc_adma "\n') + mayflowerconf.write('MODULES+="ata_generic pata_ali pata_amd pata_artop pata_atiixp pata_cmd64x pata_cs5520 pata_cs5530 pata_cs5535 pata_cypress pata_efar pata_hpt366 pata_hpt37x pata_hpt3x2n pata_hpt3x3 pata_isapnp pata_it821x pata_jmicron pata_marvell pata_mpiix pata_netcell pata_ns87410 pata_oldpiix pata_optidma pata_opti pata_pcmcia pata_pdc2027x pata_pdc202xx_old pata_qdi pata_serverworks pata_sil680 pata_sis pata_sl82c105 pata_triflex pata_via pdc_adma pata_mpc52xx "\n') mayflowerconf.close()
subprocess.call(["/sbin/mayflower", "-f", "/boot/livecd-initramfs.img", @@ -789,76 +970,8 @@ class InstallationTarget: def launchShell(self): subprocess.call(["/bin/bash"], preexec_fn=self.run_in_root)
- def configureBootloader(self): - """configure the boot loader""" - - # set up boot loader - # - # TODO: - # - fix for archs not using grub - # - fix for non-i386 - # - error handling - # - shutil.copyfile("%s/install_root/boot/vmlinuz-%s" - %(self.build_dir, self.get_kernel_version()), - "%s/out/isolinux/vmlinuz" %(self.build_dir,)) - - shutil.copyfile("%s/install_root/boot/livecd-initramfs.img" - %(self.build_dir,), - "%s/out/isolinux/initrd.img" %(self.build_dir,)) - os.unlink("%s/install_root/boot/livecd-initramfs.img" - %(self.build_dir,)) - - for p in ["isolinux.bin", "vesamenu.c32"]: - path = "%s/install_root/usr/lib/syslinux/%s" % (self.build_dir, p) - - if not os.path.isfile(path): - raise InstallationError("syslinux not installed : %s not found" % path) - - shutil.copy(path, "%s/out/isolinux/%s" % (self.build_dir, p)) - - if os.path.exists("%s/install_root/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" %(self.build_dir,)): - shutil.copy("%s/install_root/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" %(self.build_dir,), - "%s/out/isolinux/splash.jpg" %(self.build_dir,)) - have_background = "menu background splash.jpg" - else: - have_background = "" - - cfg = """ -default vesamenu.c32 -timeout 600 - -%(background)s -menu title Welcome to %(label)s! -menu color border 0 #ffffffff #00000000 -menu color sel 0 #ffffffff #ff000000 -menu color title 0 #ffffffff #00000000 -menu color tabmsg 0 #ffffffff #00000000 -menu color unsel 0 #ffffffff #00000000 -menu color hotsel 0 #ff000000 #ffffffff -menu color hotkey 0 #ffffffff #ff000000 -""" %{"label": self.fs_label, "background" : have_background} - - stanzas = [("linux", "Run from image", ""), - ("runfromram", "Run from RAM - requires 1 GB+", "live_ram")] - if os.path.exists("%s/install_root/usr/lib/anaconda-runtime/checkisomd5" %(self.build_dir,)): - stanzas.append( ("check", "Verify and run from image", "check") ) - - for (short, long, extra) in stanzas: - cfg += """label %(short)s - menu label %(long)s - kernel vmlinuz - append initrd=initrd.img ro quiet root=CDLABEL=%(label)s rootfstype=iso9660 liveimg %(extra)s -""" %{"label": self.fs_label, "background" : have_background, - "short": short, "long": long, "extra": extra} - - cfgf = open("%s/out/isolinux/isolinux.cfg" %(self.build_dir,), "w") - cfgf.write(cfg) - cfgf.close()
- # TODO: enable external entitity to partipate in adding boot entries - - def install(self): + def install(self, mayflower_path): for (name, url) in self.repos: self.ayum.addRepository(name, url)
@@ -868,24 +981,8 @@ menu color hotkey 0 #ffffffff #ff000000 self.relabelSystem() if not self.skip_prelink: self.prelinkSystem() - self.createInitramfs() - self.configureBootloader() - - def createIso(self): - """write out the live CD ISO""" - subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(self.fs_label,), - "-b", "isolinux/isolinux.bin", - "-c", "isolinux/boot.cat", - "-no-emul-boot", "-boot-load-size", "4", - "-boot-info-table", "-J", "-r", "-hide-rr-moved", - "-V", "%s" %(self.fs_label,), "%s/out" %(self.build_dir)]) - - # implant an isomd5sum - if os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"): - subprocess.call(["/usr/lib/anaconda-runtime/implantisomd5", - "%s.iso" %(self.fs_label,)]) - else: - print >> sys.stderr, "anaconda-runtime not installed; not setting up mediacheck" + self.createInitramfs(mayflower_path) + self.arch.configureBootloader(self)
def createSquashFS(self): """create compressed squashfs file system""" @@ -901,7 +998,14 @@ menu color hotkey 0 #ffffffff #ff000000
def package(self): self.createSquashFS() - self.createIso() + self.arch.createIso(self) + # implant an isomd5sum + if os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"): + subprocess.call(["/usr/lib/anaconda-runtime/implantisomd5", + "%s.iso" %(self.fs_label,)]) + else: + print >> sys.stderr, "anaconda-runtime not installed; not setting up mediacheck" +
def usage(out): print >> out, """ @@ -911,9 +1015,9 @@ usage: livecd-creator [--help] [--package=<p1>] [--package=<p2> ...] [--exclude-package=<e1>] [--exclude-package=<e2> ...] [--base-on=<path-to-iso-file>] - [--fslabel=<label>] + [--fslabel=<label>] [--mayflower=<path-to-mayflower-script>] [--skip-compression] - [--uncompressed-size=<size-in-MB>] + [--uncompressed-size=<size-in-MiB>] [--shell]
--help : Print usage and exit @@ -923,9 +1027,10 @@ usage: livecd-creator [--help] --exclude-package : Exclude this package --base-on : Add packages to an existing live CD iso9660 image --fslabel : File system label (default: livecd-YYYYMMDD-HHMI) + --mayflower : Path to mayflower script --skip-compression : Don't compress the image --prelink : Prelink the image - --uncompressed-size : Size of uncompressed fs in MB (default: 4096) + --uncompressed-size : Size of uncompressed fs in MiB (default: 4096) --shell : Start a shell in the chroot for post-configuration
Examples: @@ -952,6 +1057,7 @@ class Options: self.fs_label = "livecd-" + time.strftime("%Y%m%d-%H%M") self.base_on = None self.kscfg = None + self.mayflower_path = "/usr/lib/livecd-creator/mayflower" self.skip_compression = False self.skip_prelink = True self.uncompressed_size = 4096 @@ -963,7 +1069,7 @@ def parse_options(args): ["help", "repo=", "base-on=", "package=", "exclude-package=", "fslabel=", "config=", "skip-compression", "uncompressed-size=", - "shell", "no-prelink", "prelink"]) + "mayflower=", "shell", "no-prelink", "prelink"]) except getopt.GetoptError, msg: raise Usage(msg)
@@ -992,6 +1098,11 @@ def parse_options(args): if not os.path.isfile(options.kscfg): raise Usage("Kickstart config '%s' does not exist" % options.kscfg) continue + if o in ("--mayflower"): + options.mayflower_path = a + if not os.path.isfile(options.mayflower_path): + raise Usage("Mayflower script '%s' does not exist" % options.mayflower_path) + continue if o in ("-r", "--repo"): (name, url) = a.split(",") for (n, u) in options.repos: @@ -1057,10 +1168,12 @@ def main():
try: target.parse(options.kscfg) + + target.packages.extend(target.arch.bootldr_package)
target.setup(options.uncompressed_size, options.base_on)
- target.install() + target.install(options.mayflower_path)
if options.give_shell: print "Launching shell. Exit to continue."
On Sun, 2007-04-29 at 14:31 +0100, David Woodhouse wrote:
At http://git.infradead.org/?p=users/dwmw2/livecd-ppc.git (git://git.infradead.org/~dwmw2/livecd-ppc.git) there's a set of patches which first introduces a little abstraction for the arch-specific configureBootloader() and createIso() functions, then implements the PPC version.
Cool -- there's definitely some abstraction needed, the question is more of where to put them. I think what might make sense is to get a few little things in the tree (like J5's fixes so that things work with FC6) and then branch off what we'll use for F7 and then continue to use HEAD so we can start doing more things like some of the refactoring from markmc as well as getting the architecture bits cleanly separated out.
I'll try to get to making progress on that this afternoon.
- Allow mayflower path to be specified, to run from git tree
I wonder if instead of specifying a path, it makes more sense to just notice that you're running from the git tree and use the mayflower script from there. The only problem is that sort of opens up to a certain class of attack. I'm not sure how important it really is to care about it, though.
2, Remove syslinux from package lists; add it dynamically instead.
The only problem with this is that it somewhat breaks the abstraction -- maybe some distro using livecd-tools needs the isolinux package for those bits. Does an error get raised with non-existent packages right now?
- Move x86-specific functions (createIso, configureBootloader) into subclass
- Only create isolinux/ directory in x86-specific configureBootloader()
- Add PowerPC support, add pata_mpc52xx module to mayflower.conf
This sounds pretty straight-forward and reasonable.
Jeremy
On Mon, 2007-04-30 at 13:33 -0400, Jeremy Katz wrote:
On Sun, 2007-04-29 at 14:31 +0100, David Woodhouse wrote:
At http://git.infradead.org/?p=users/dwmw2/livecd-ppc.git (git://git.infradead.org/~dwmw2/livecd-ppc.git) there's a set of patches which first introduces a little abstraction for the arch-specific configureBootloader() and createIso() functions, then implements the PPC version.
Cool -- there's definitely some abstraction needed, the question is more of where to put them. I think what might make sense is to get a few little things in the tree (like J5's fixes so that things work with FC6) and then branch off what we'll use for F7 and then continue to use HEAD so we can start doing more things like some of the refactoring from markmc as well as getting the architecture bits cleanly separated out.
I'll try to get to making progress on that this afternoon.
OK, thanks. There are a few details to be tidied up in the ppc bootloader support but it's basically there. The main thing missing is the 64-bit kernel.
- Allow mayflower path to be specified, to run from git tree
I wonder if instead of specifying a path, it makes more sense to just notice that you're running from the git tree and use the mayflower script from there. The only problem is that sort of opens up to a certain class of attack. I'm not sure how important it really is to care about it, though.
Yeah, I thought about that but I lack the wit for it -- what you see in the git tree is the most python I've ever done :)
2, Remove syslinux from package lists; add it dynamically instead.
The only problem with this is that it somewhat breaks the abstraction -- maybe some distro using livecd-tools needs the isolinux package for those bits. Does an error get raised with non-existent packages right now?
Yes. Although there is perhaps some logic in using --skip-broken.
On Mon, 2007-04-30 at 20:27 +0100, David Woodhouse wrote:
On Mon, 2007-04-30 at 13:33 -0400, Jeremy Katz wrote:
On Sun, 2007-04-29 at 14:31 +0100, David Woodhouse wrote:
2, Remove syslinux from package lists; add it dynamically instead.
The only problem with this is that it somewhat breaks the abstraction -- maybe some distro using livecd-tools needs the isolinux package for those bits. Does an error get raised with non-existent packages right now?
Yes. Although there is perhaps some logic in using --skip-broken.
--skip-broken doesn't really apply here; it falls back to keeping an installed version of a package if what you try to select doesn't upgrade cleanly.
We should just be doing the right thing as far as skipping non-existent packages if you're using %packages --ignoremissing, much like anaconda does
Jeremy
livecd@lists.fedoraproject.org