From a4c638f132f2b6ef40eac4dc7fada43ddc759da1 Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Tue, 23 Feb 2010 14:20:05 +0100 Subject: [PATCH] New version --- AUTHORS | 3 +- Makefile | 7 +- etc/config.i386 | 4 + etc/{lorax => }/config.noarch | 14 +- .../desktop/%gconf.xml => etc/ignore_errors | 0 etc/lorax/config.i386 | 8 - etc/lorax/config.i586 | 1 - etc/lorax/config.i686 | 1 - etc/lorax/config.ppc | 7 - etc/lorax/config.ppc64 | 10 - etc/lorax/config.s390 | 11 - etc/lorax/config.s390x | 1 - etc/lorax/config.x86_64 | 1 - etc/lorax/templates/initrd/includes/initrd | 246 ----- etc/lorax/templates/initrd/initrd.i386 | 11 - etc/lorax/templates/initrd/initrd.s390 | 51 - etc/lorax/templates/scrubs/scrubs.i386 | 103 --- etc/templates/initrd.ltmpl | 214 +++++ .../%gconf.xml => etc/templates/install.ltmpl | 0 setup.py | 48 +- src/bin/lorax | 120 ++- src/pylorax/__init__.py | 871 ++++++++---------- src/pylorax/base.py | 143 +++ src/pylorax/config.py | 243 +---- src/pylorax/constants.py | 83 ++ src/pylorax/{singleton.py => decorators.py} | 18 +- src/pylorax/efi.py | 236 ----- src/pylorax/images.py | 865 +++++++++++++++++ src/pylorax/install.py | 502 ---------- src/pylorax/insttree.py | 149 +++ src/pylorax/launcher.py | 224 ----- src/pylorax/lcs/__init__.py | 76 -- src/pylorax/lcs/actions/__init__.py | 49 - src/pylorax/lcs/actions/base.py | 55 -- src/pylorax/lcs/actions/file.py | 300 ------ src/pylorax/lcs/actions/ssh.py | 57 -- src/pylorax/ltmpl.py | 39 + src/pylorax/output.py | 156 ++-- src/pylorax/ramdisk.py | 522 ----------- src/pylorax/{utils.py => sysutils.py} | 328 +++---- src/pylorax/yumbase.py | 110 +++ usr/share/lorax/initrd/s390/etc/pam.d/login | 9 - usr/share/lorax/initrd/s390/etc/pam.d/remote | 9 - usr/share/lorax/initrd/s390/etc/pam.d/sshd | 9 - .../lorax/initrd/s390/etc/ssh/sshd_config | 17 - usr/share/lorax/initrd/s390x | 1 - .../.gconf/desktop/gnome/interface/%gconf.xml | 5 - .../lorax/installtree/noarch/etc/libuser.conf | 13 - .../installtree/noarch/etc/selinux/config | 3 - usr/share/lorax/outputdir/images/README | 9 - .../lorax/outputdir/images/pxeboot/README | 6 - 51 files changed, 2379 insertions(+), 3589 deletions(-) create mode 100644 etc/config.i386 rename etc/{lorax => }/config.noarch (52%) rename usr/share/lorax/installtree/noarch/.gconf/desktop/%gconf.xml => etc/ignore_errors (100%) delete mode 100644 etc/lorax/config.i386 delete mode 120000 etc/lorax/config.i586 delete mode 120000 etc/lorax/config.i686 delete mode 100644 etc/lorax/config.ppc delete mode 100644 etc/lorax/config.ppc64 delete mode 100644 etc/lorax/config.s390 delete mode 120000 etc/lorax/config.s390x delete mode 120000 etc/lorax/config.x86_64 delete mode 100644 etc/lorax/templates/initrd/includes/initrd delete mode 100644 etc/lorax/templates/initrd/initrd.i386 delete mode 100644 etc/lorax/templates/initrd/initrd.s390 delete mode 100644 etc/lorax/templates/scrubs/scrubs.i386 create mode 100644 etc/templates/initrd.ltmpl rename usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/%gconf.xml => etc/templates/install.ltmpl (100%) mode change 100755 => 100644 src/bin/lorax create mode 100644 src/pylorax/base.py create mode 100644 src/pylorax/constants.py rename src/pylorax/{singleton.py => decorators.py} (77%) delete mode 100644 src/pylorax/efi.py create mode 100644 src/pylorax/images.py delete mode 100644 src/pylorax/install.py create mode 100644 src/pylorax/insttree.py delete mode 100644 src/pylorax/launcher.py delete mode 100644 src/pylorax/lcs/__init__.py delete mode 100644 src/pylorax/lcs/actions/__init__.py delete mode 100644 src/pylorax/lcs/actions/base.py delete mode 100644 src/pylorax/lcs/actions/file.py delete mode 100644 src/pylorax/lcs/actions/ssh.py create mode 100644 src/pylorax/ltmpl.py delete mode 100644 src/pylorax/ramdisk.py rename src/pylorax/{utils.py => sysutils.py} (55%) create mode 100644 src/pylorax/yumbase.py delete mode 100644 usr/share/lorax/initrd/s390/etc/pam.d/login delete mode 100644 usr/share/lorax/initrd/s390/etc/pam.d/remote delete mode 100644 usr/share/lorax/initrd/s390/etc/pam.d/sshd delete mode 100644 usr/share/lorax/initrd/s390/etc/ssh/sshd_config delete mode 120000 usr/share/lorax/initrd/s390x delete mode 100644 usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/interface/%gconf.xml delete mode 100644 usr/share/lorax/installtree/noarch/etc/libuser.conf delete mode 100644 usr/share/lorax/installtree/noarch/etc/selinux/config delete mode 100644 usr/share/lorax/outputdir/images/README delete mode 100644 usr/share/lorax/outputdir/images/pxeboot/README diff --git a/AUTHORS b/AUTHORS index d88798fd..8c3defd5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,2 @@ -David Cantrell Martin Gracik - +David Cantrell diff --git a/Makefile b/Makefile index 8a20a852..53f4388f 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,11 @@ -# Makefile for lorax +PYTHON ?= /usr/bin/env python -PYTHON ?= /usr/bin/python -DESTDIR ?= / all: $(PYTHON) setup.py build install: - $(PYTHON) setup.py install --root $(DESTDIR) + $(PYTHON) setup.py install clean: -rm -rf build - -git clean -d -x -f diff --git a/etc/config.i386 b/etc/config.i386 new file mode 100644 index 00000000..4b33a229 --- /dev/null +++ b/etc/config.i386 @@ -0,0 +1,4 @@ +[lorax] + +packages: + kernel-PAE grub memtest86+ diff --git a/etc/lorax/config.noarch b/etc/config.noarch similarity index 52% rename from etc/lorax/config.noarch rename to etc/config.noarch index f57db2b2..9d17992c 100644 --- a/etc/lorax/config.noarch +++ b/etc/config.noarch @@ -1,15 +1,8 @@ [lorax] packages: - anaconda kernel *firmware* joe gnome-icon-theme fedora-icon-theme - xorg-x11-server-Xorg firstaidkit bzip2 busybox selinux-policy-targeted - python-imaging hal specspo xorg-x11-fonts-misc xorg-x11-drivers rhpxl - ntfs-3g ntfsprogs xfsprogs xfsdump reiserfs-utils gfs2-utils jfsutils - nfs-utils btrfs-progs mesa-dri-drivers dogtail rsh rsync prelink - smartmontools iscsi-initiator-utils samba-client mtr gtk-nodoka-engine - ftp openssh-clients gtk+ gdk-pixbuf madan-fonts lklug-fonts - xorg-x11-fonts-ethiopic un-core-fonts-dotum man yum-fedorakmod - /etc/gtk-2.0/gtkrc NetworkManager-gnome wget + anaconda kernel *firmware* man hal wget samba-client gfs2-utils nfs-utils + ntfs-3g modules: sunrpc lockd floppy cramfs loop edd pcspkr squashfs @@ -21,3 +14,6 @@ modules: dm-multipath dm-round-robin dm-crypt raid0 raid1 raid5 raid6 raid456 raid10 linear sha256_generic cbc xts lrw aes_generic crypto_blkcipher crc32c ecb arc4 yenta_socket i82365 tcic pcmcia =scsi =net =drm + +initrd_template = templates/initrd.ltmpl +install_template = templates/install.ltmpl diff --git a/usr/share/lorax/installtree/noarch/.gconf/desktop/%gconf.xml b/etc/ignore_errors similarity index 100% rename from usr/share/lorax/installtree/noarch/.gconf/desktop/%gconf.xml rename to etc/ignore_errors diff --git a/etc/lorax/config.i386 b/etc/lorax/config.i386 deleted file mode 100644 index 3783aada..00000000 --- a/etc/lorax/config.i386 +++ /dev/null @@ -1,8 +0,0 @@ -[lorax] - -packages: - grub gpart syslinux memtest86+ efibootmgr dmidecode pcmciautils - kernel-PAE - -initrd_template=templates/initrd/initrd.i386 -scrubs_template=templates/scrubs/scrubs.i386 diff --git a/etc/lorax/config.i586 b/etc/lorax/config.i586 deleted file mode 120000 index 744d7dfd..00000000 --- a/etc/lorax/config.i586 +++ /dev/null @@ -1 +0,0 @@ -config.i386 \ No newline at end of file diff --git a/etc/lorax/config.i686 b/etc/lorax/config.i686 deleted file mode 120000 index 744d7dfd..00000000 --- a/etc/lorax/config.i686 +++ /dev/null @@ -1 +0,0 @@ -config.i386 \ No newline at end of file diff --git a/etc/lorax/config.ppc b/etc/lorax/config.ppc deleted file mode 100644 index 9cb13f37..00000000 --- a/etc/lorax/config.ppc +++ /dev/null @@ -1,7 +0,0 @@ -[lorax] - -packages: - pcmciautils pdisk yaboot hfsutils fbset ppc64-utils - -initrd_template=templates/initrd/initrd.i386 -scrubs_template=templates/scrubs/scrubs.i386 diff --git a/etc/lorax/config.ppc64 b/etc/lorax/config.ppc64 deleted file mode 100644 index df5a2d09..00000000 --- a/etc/lorax/config.ppc64 +++ /dev/null @@ -1,10 +0,0 @@ -[lorax] - -packages: - pcmciautils pdisk yaboot hfsutils fbset ppc64-utils - -modules: - spufs viocd gpio_mdio - -initrd_template=templates/initrd/initrd.i386 -scrubs_template=templates/scrubs/scrubs.i386 diff --git a/etc/lorax/config.s390 b/etc/lorax/config.s390 deleted file mode 100644 index 56e359c3..00000000 --- a/etc/lorax/config.s390 +++ /dev/null @@ -1,11 +0,0 @@ -[lorax] - -packages: s390utils binutils libgcc tcp_wrappers sed net-tools openssh - openssh-server coreutils login initscripts portmap pam mount modutils - s390utils-cmsfs strace xorg-x11-xauth xorg-x11-libs - -modules: zfcp tape390 dasd_diag_mod dasd_eckd_mod dasd_fba_mod dasd_mod - ctc netiucv smsgiucv lcs qdio qeth ccwgroup crypto_api xfrm_nalgo - -initrd_template=templates/initrd/initrd.s390 -scrubs_template=templates/scrubs/scrubs.i386 diff --git a/etc/lorax/config.s390x b/etc/lorax/config.s390x deleted file mode 120000 index 5c0bf0e5..00000000 --- a/etc/lorax/config.s390x +++ /dev/null @@ -1 +0,0 @@ -config.s390 \ No newline at end of file diff --git a/etc/lorax/config.x86_64 b/etc/lorax/config.x86_64 deleted file mode 120000 index 744d7dfd..00000000 --- a/etc/lorax/config.x86_64 +++ /dev/null @@ -1 +0,0 @@ -config.i386 \ No newline at end of file diff --git a/etc/lorax/templates/initrd/includes/initrd b/etc/lorax/templates/initrd/includes/initrd deleted file mode 100644 index b4571f6b..00000000 --- a/etc/lorax/templates/initrd/includes/initrd +++ /dev/null @@ -1,246 +0,0 @@ -## create required directories -makedirs ${initrd}/lib -makedirs ${initrd}/modules -makedirs ${initrd}/firmware -symlink name ${initrd}/lib/modules target ../modules -symlink name ${initrd}/lib/firmware target ../firmware - -makedirs ${initrd}/sbin -makedirs ${initrd}/dev -makedirs ${initrd}/etc -makedirs ${initrd}/etc/udev/rules.d -makedirs ${initrd}/lib/udev/rules.d -makedirs ${initrd}/proc -makedirs ${initrd}/selinux -makedirs ${initrd}/sys -makedirs ${initrd}/etc/terminfo/{a,d,l,s,v,x,g} -makedirs ${initrd}/tmp -makedirs ${initrd}/usr/libexec -makedirs ${initrd}/usr/${libdir}/NetworkManager -makedirs ${initrd}/usr/share/dbus-1/system-services -makedirs ${initrd}/var/cache/hald -makedirs ${initrd}/var/lib/dbus -makedirs ${initrd}/var/lib/dhclient -makedirs ${initrd}/var/lock/rpm -makedirs ${initrd}/var/run -makedirs ${initrd}/var/run/dbus -makedirs ${initrd}/var/run/hald -makedirs ${initrd}/var/run/NetworkManager -makedirs ${initrd}/etc/dbus-1/system.d -makedirs ${initrd}/etc/modprobe.d -makedirs ${initrd}/etc/NetworkManager/dispatcher.d -makedirs ${initrd}/${libdir}/dbus-1 -makedirs ${initrd}/etc/sysconfig/network-scripts -makedirs ${initrd}/usr/share/polkit-1/actions -makedirs ${initrd}/var/lib/misc -makedirs ${initrd}/etc/hal/fdi -makedirs ${initrd}/usr/share/hal/fdi -makedirs ${initrd}/usr/share/hwdata -makedirs ${initrd}/etc/rc.d/init.d -makedirs ${initrd}/usr/sbin -makedirs ${initrd}/var/run/wpa_supplicant - -## set the buildarch -edit ${initrd}/etc/arch text "${arch}" - -## copy etc stuff -copy ${instroot} etc/passwd to ${initrd} etc -chmod ${initrd}/etc/passwd mode 0644 - -copy ${instroot} etc/group to ${initrd} etc -chmod ${initrd}/etc/group mode 0644 - -copy ${instroot} etc/nsswitch.conf to ${initrd} etc -chmod ${initrd}/etc/nsswitch.conf mode 0644 - -copy ${instroot} etc/hosts to ${initrd} etc -chmod ${initrd}/etc/hosts mode 0644 - -## copy mount/umount -copy ${instroot} bin/mount to ${initrd} sbin -copy ${instroot} bin/umount to ${initrd} sbin -copy ${instroot} sbin/mount.* to ${initrd} sbin -copy ${instroot} sbin/umount.* to ${initrd} sbin - -## copy udev -copy ${instroot} sbin/udevd to ${initrd} sbin -copy ${instroot} sbin/udevadm to ${initrd} sbin -symlink name ${initrd}/sbin/udevinfo target udevadm -symlink name ${initrd}/sbin/udevsettle target udevadm - -## udev rules -copy ${instroot} etc/udev/udev.conf to ${initrd} etc/udev -chmod ${initrd}/etc/udev/udev.conf mode 0644 - -copy ${instroot} lib/udev/* to ${initrd} lib/udev -##chmod ${initrd}/lib/udev/* mode 0644 - -remove ${initrd}/lib/udev/rules.d/*persistent* -remove ${initrd}/lib/udev/rules.d/*generator* - -copy ${instroot} etc/udev/rules.d/*.rules to ${initrd} etc/udev/rules.d -chmod ${initrd}/etc/udev/rules.d/*.rules mode 0644 - -## copy bash -copy ${instroot} bin/bash to ${initrd} sbin -symlink name ${initrd}/sbin/sh target bash - -copy ${instroot} sbin/consoletype to ${initrd} sbin -copy ${instroot} usr/bin/logger to ${initrd} sbin - -copy ${instroot} etc/rc.d/init.d/functions to ${initrd} etc/rc.d/init.d -copy ${instroot} etc/sysconfig/network-scripts/network-functions* to \ - ${initrd} etc/sysconfig/network-scripts - -symlink name ${initrd}/etc/init.d target /etc/rc.d/init.d - -## dhcp and dhcpv6 client daemons and support programs -copy ${instroot} sbin/dhclient to ${initrd} sbin -copy ${instroot} sbin/dhclient-script to ${initrd} sbin -copy ${instroot} sbin/arping to ${initrd} sbin -copy ${instroot} sbin/ifconfig to ${initrd} sbin -copy ${instroot} sbin/ip to ${initrd} sbin -copy ${instroot} bin/ipcalc to ${initrd} sbin -copy ${instroot} bin/hostname to ${initrd} sbin -copy ${instroot} sbin/ethtool to ${initrd} sbin -copy ${instroot} sbin/route to ${initrd} sbin -touch ${initrd}/etc/resolv.conf - -## hwdata -copy ${instroot} usr/share/hwdata/pci.ids to ${initrd} usr/share/hwdata -copy ${instroot} usr/share/hwdata/usb.ids to ${initrd} usr/share/hwdata - -## hal -copy ${instroot} usr/sbin/hald to ${initrd} sbin -copy ${instroot} usr/libexec/hald-runner to ${initrd} usr/libexec -copy ${instroot} usr/libexec/hald-generate-fdi-cache to ${initrd} usr/libexec -copy ${instroot} usr/libexec/hal*storage* to ${initrd} usr/libexec -touch ${initrd}/var/run/hald.acl-list -copy ${instroot} usr/share/hal/fdi/* to ${initrd} usr/share/hal/fdi -copy ${instroot} etc/hal/fdi/* to ${initrd} etc/hal/fdi -copy ${instroot} etc/dbus-1/system.d/hal.conf to ${initrd} etc/dbus-1/system.d - -## policykit -copy ${instroot} etc/polkit-1 to ${initrd} etc -copy ${instroot} \ - usr/share/dbus-1/system-services/org.freedesktop.PolicyKit1.service to \ - ${initrd} usr/share/dbus-1/system-services - -copy ${instroot} usr/share/polkit-1/actions/org.freedesktop.policykit.policy \ - to ${initrd} usr/share/polkit-1/actions - -## dbus -copy ${instroot} bin/dbus-uuidgen to ${initrd} sbin -copy ${instroot} bin/dbus-daemon to ${initrd} sbin -copy ${instroot} etc/dbus-1/system.conf to ${initrd} etc/dbus-1 -copy ${instroot} ${libdir}/dbus-1/dbus-daemon-launch-helper to \ - ${initrd} ${libdir}/dbus-1 - -chown ${initrd}/${libdir}/dbus-1/dbus-daemon-launch-helper user root group dbus -chmod ${initrd}/${libdir}/dbus-1/dbus-daemon-launch-helper mode 04750 - -## wpa_supplicant -copy ${instroot} usr/sbin/wpa_passphrase to ${initrd} usr/sbin -copy ${instroot} usr/sbin/wpa_supplicant to ${initrd} usr/sbin -copy ${instroot} etc/dbus-1/system.d/wpa_supplicant.conf to \ - ${initrd} etc/dbus-1/system.d - -copy ${instroot} etc/wpa_supplicant/wpa_supplicant.conf to \ - ${initrd} etc/wpa_supplicant - -copy ${instroot} \ - usr/share/dbus-1/system-services/fi.epitest.hostap.WPASupplicant.service \ - to ${initrd} usr/share/dbus-1/system-services - -## networkmanager -copy ${instroot} usr/sbin/NetworkManager to ${initrd} usr/sbin -copy ${instroot} etc/dbus-1/system.d/nm-*.conf to ${initrd} etc/dbus-1/system.d -copy ${instroot} etc/dbus-1/system.d/NetworkManager.conf to \ - ${initrd} etc/dbus-1/system.d -copy ${instroot} etc/NetworkManager/nm-system-settings.conf to \ - ${initrd} etc/NetworkManager -copy ${instroot} \ - usr/${libdir}/NetworkManager/libnm-settings-plugin-ifcfg-rh.so to \ - ${initrd} usr/${libdir}/NetworkManager -copy ${instroot} usr/libexec/nm-* to ${initrd} usr/libexec -##copy ${instroot} usr/share/dbus-1/system-services/org.freedesktop.NetworkManagerSystemSettings.service to ${initrd} usr/share/dbus-1/system-services -copy ${instroot} usr/share/dbus-1/system-services/org.freedesktop.nm_dispatcher.service to ${initrd} usr/share/dbus-1/system-services - -## modprobe -copy ${instroot} sbin/modprobe to ${initrd} sbin -copy ${instroot} sbin/insmod to ${initrd} sbin -copy ${instroot} sbin/rmmod to ${initrd} sbin - -## profile -edit ${initrd}/.profile text "PATH=/bin:/usr/bin:/usr/sbin:/mnt/sysimage/sbin:/mnt/sysimage/usr/sbin:/mnt/sysimage/bin:/mnt/sysimage/usr/bin\nexport PATH" - -## terminfos -copy ${instroot} usr/share/terminfo/a/ansi to ${initrd} etc/terminfo/a \ - nosymlinks -copy ${instroot} usr/share/terminfo/d/dumb to ${initrd} etc/terminfo/d \ - nosymlinks -copy ${instroot} usr/share/terminfo/l/linux to ${initrd} etc/terminfo/l \ - nosymlinks -copy ${instroot} usr/share/terminfo/s/screen to ${initrd} etc/terminfo/s \ - nosymlinks -copy ${instroot} usr/share/terminfo/v/vt100 to ${initrd} etc/terminfo/v \ - nosymlinks -copy ${instroot} usr/share/terminfo/v/vt100-nav to ${initrd} etc/terminfo/v \ - nosymlinks -copy ${instroot} usr/share/terminfo/v/vt102 to ${initrd} etc/terminfo/v \ - nosymlinks -copy ${instroot} usr/share/terminfo/x/xterm to ${initrd} etc/terminfo/x \ - nosymlinks -copy ${instroot} usr/share/terminfo/x/xterm-color to ${initrd} etc/terminfo/x \ - nosymlinks -copy ${instroot} usr/share/terminfo/g/gnome to ${initrd} etc/terminfo/g \ - nosymlinks -chmod ${initrd}/etc/terminfo/*/* mode 0644 - -## misc -copy ${instroot} bin/awk to ${initrd} sbin -copy ${instroot} bin/gawk to ${initrd} sbin -copy ${instroot} bin/egrep to ${initrd} sbin -copy ${instroot} bin/fgrep to ${initrd} sbin -copy ${instroot} bin/grep to ${initrd} sbin -copy ${instroot} bin/kill to ${initrd} sbin -copy ${instroot} bin/ln to ${initrd} sbin -copy ${instroot} usr/bin/readlink to ${initrd} sbin -copy ${instroot} bin/rm to ${initrd} sbin -copy ${instroot} bin/rmdir to ${initrd} sbin -copy ${instroot} bin/sed to ${initrd} sbin -copy ${instroot} bin/sleep to ${initrd} sbin -copy ${instroot} bin/touch to ${initrd} sbin - -symlink name ${initrd}/init target /sbin/init -symlink name ${initrd}/etc/mtab target /proc/mounts -symlink name ${initrd}/bin target sbin -symlink name ${initrd}/var/lib/xkb target ../../tmp - -## loader -copy ${instroot} usr/lib/anaconda-runtime/loader/loader to ${initrd} sbin -copy ${instroot} usr/lib/anaconda-runtime/loader/loader.tr to ${initrd} etc -chmod ${initrd}/etc/loader.tr mode 0644 - -## indirect dependencies -copy ${instroot} ${libdir}/ld-linux.so.2 to ${initrd} ${libdir} -copy ${instroot} ${libdir}/libcom_err.so.2 to ${initrd} ${libdir} -##copy ${instroot} ${libdir}/libdbus-glib-1.so.2 to ${initrd} ${libdir} -##copy ${instroot} ${libdir}/libfreebl3.so to ${initrd} ${libdir} -copy ${instroot} ${libdir}/libgcc_s.so.1 to ${initrd} ${libdir} -copy ${instroot} ${libdir}/libnss_dns.so.2 to ${initrd} ${libdir} -copy ${instroot} ${libdir}/libnss_files.so.2 to ${initrd} ${libdir} -##copy ${instroot} ${libdir}/libsoftokn3.so to ${initrd} ${libdir} -copy ${instroot} usr/${libdir}/libsqlite3.so.0 to ${initrd} usr/${libdir} - -## langtable -copy ${instroot} usr/lib/anaconda/lang-table to ${initrd} etc - - -## missing files -copy ${instroot} usr/sbin/dmidecode to ${initrd} bin -copy ${instroot} sbin/load_policy to ${initrd} bin -copy ${instroot} sbin/mdadm to ${initrd} bin -copy ${instroot} sbin/mdmon to ${initrd} bin -copy ${instroot} bin/mkdir to ${initrd} bin -copy ${instroot} usr/bin/wget to ${initrd} bin diff --git a/etc/lorax/templates/initrd/initrd.i386 b/etc/lorax/templates/initrd/initrd.i386 deleted file mode 100644 index 4f2c657a..00000000 --- a/etc/lorax/templates/initrd/initrd.i386 +++ /dev/null @@ -1,11 +0,0 @@ -<%include file="includes/initrd" /> - -## loader -copy ${instroot} usr/lib/anaconda-runtime/loader/init to ${initrd} sbin/init -symlink name ${initrd}/sbin/reboot target init -symlink name ${initrd}/sbin/halt target init -symlink name ${initrd}/sbin/poweroff target init - -## screenfont -copy ${instroot} usr/lib/anaconda-runtime/screenfont-${basearch}.gz to \ - ${initrd} etc/screenfont.gz diff --git a/etc/lorax/templates/initrd/initrd.s390 b/etc/lorax/templates/initrd/initrd.s390 deleted file mode 100644 index 1093bec8..00000000 --- a/etc/lorax/templates/initrd/initrd.s390 +++ /dev/null @@ -1,51 +0,0 @@ -<%include file="includes/initrd" /> - -## create required directories -makedirs ${initrd}/var/empty/sshd mode 0111 -makedirs ${initrd}/etc/security -makedirs ${initrd}/${libdir}/security - -## copy some files -copy ${instroot} usr/bin/xauth to ${initrd} sbin -copy ${instroot} usr/sbin/cmsfs* to ${initrd} sbin - -copy ${instroot} ${libdir}/libpam_misc.so.0.* to ${initrd} ${libdir}/libpam_misc.so.0 -copy ${instroot} ${libdir}/libwrap*.so* to ${initrd} ${libdir} - -symlink name ${initrd}/var/state/xkb target /tmp - -## loader -copy ${instroot} usr/lib/anaconda-runtime/loader/shutdown to ${initrd} sbin -copy ${instroot} usr/lib/anaconda-runtime/loader/linuxrc.s390 to \ - ${initrd} sbin/init -copy ${instroot} usr/lib/anaconda-runtime/loader/lsznet.raw to \ - ${initrd} sbin/lsznet -copy ${instroot} usr/lib/anaconda-runtime/loader/controlunits.sh to \ - ${initrd} sbin/controlunits -copy ${instroot} usr/sbin/dasdfmt to ${initrd} sbin - -## setup shell environment -edit ${initrd}/etc/protocols text "tcp\t6\tTCP\n" - -copy ${instroot} ${libdir}/security/pam_limits.so to \ - ${initrd} ${libdir}/security -copy ${instroot} ${libdir}/security/pam_env.so to ${initrd} ${libdir}/security -copy ${instroot} ${libdir}/security/pam_unix.so to ${initrd} ${libdir}/security -copy ${instroot} ${libdir}/security/pam_deny.so to ${initrd} ${libdir}/security - -copy ${instroot} etc/pam.d/other to ${initrd} etc/pam.d - -copy ${instroot} etc/security/limits.conf to ${initrd} etc/security -copy ${instroot} etc/security/pam_env.conf to ${initrd} etc/security - -## generate ssh keys -makedirs ${initrd}/etc/ssh mode 0700 -gensshkey ${initrd}/etc/ssh/ssh_host_key type rsa1 -gensshkey ${initrd}/etc/ssh/ssh_host_rsa_key type rsa -gensshkey ${initrd}/etc/ssh/ssh_host_dsa_key type dsa - -chmod ${initrd}/etc/ssh/sshd_config mode 0600 - -## copy in the binaries -copy ${instroot} bin/login to ${initrd} sbin/login -copy ${instroot} usr/sbin/sshd to ${initrd} sbin/sshd diff --git a/etc/lorax/templates/scrubs/scrubs.i386 b/etc/lorax/templates/scrubs/scrubs.i386 deleted file mode 100644 index 53aa6fd7..00000000 --- a/etc/lorax/templates/scrubs/scrubs.i386 +++ /dev/null @@ -1,103 +0,0 @@ -## remove unnecessary directories from / -remove ${instroot}/boot -remove ${instroot}/dev -remove ${instroot}/home -remove ${instroot}/media -remove ${instroot}/mnt -remove ${instroot}/opt -remove ${instroot}/root -remove ${instroot}/selinux -remove ${instroot}/srv -remove ${instroot}/sys -remove ${instroot}/tmp - -## remove directories from /usr -remove ${instroot}/usr/etc -remove ${instroot}/usr/games -remove ${instroot}/usr/include -remove ${instroot}/usr/kerberos -remove ${instroot}/usr/local -remove ${instroot}/usr/tmp - -## remove modules and firmware directories -##remove ${instroot}/lib/modules -##remove ${instroot}/lib/firmware - -## remove directories from /var -remove ${instroot}/var/db -remove ${instroot}/var/empty -remove ${instroot}/var/games -remove ${instroot}/var/local -remove ${instroot}/var/lock -remove ${instroot}/var/log -remove ${instroot}/var/mail -remove ${instroot}/var/nis -remove ${instroot}/var/opt -remove ${instroot}/var/preserve -remove ${instroot}/var/spool -remove ${instroot}/var/tmp -remove ${instroot}/var/yp - -## remove directories from /lib -remove ${instroot}/lib/i686 -remove ${instroot}/lib/kbd -remove ${instroot}/lib/rtkaio -remove ${instroot}/lib/security -remove ${instroot}/lib/tls -remove ${instroot}/lib/xtables - -## remove directories from /etc -remove ${instroot}/etc/ConsoleKit -remove ${instroot}/etc/X11 -remove ${instroot}/etc/alternatives -##remove ${instroot}/etc/asterisk -remove ${instroot}/etc/avahi -remove ${instroot}/etc/blkid -remove ${instroot}/etc/bonobo* -remove ${instroot}/etc/chkconfig* -remove ${instroot}/etc/cron.* -remove ${instroot}/etc/default -remove ${instroot}/etc/depmod.d -remove ${instroot}/etc/dirmngr -remove ${instroot}/etc/dnsmasq.d -remove ${instroot}/etc/event.d -remove ${instroot}/etc/firmware -remove ${instroot}/etc/firstaidkit -remove ${instroot}/etc/gconf -remove ${instroot}/etc/gcrypt -remove ${instroot}/etc/gnome-vfs* -remove ${instroot}/etc/gnupg -remove ${instroot}/etc/gtk -##remove ${instroot}/etc/hotplug -remove ${instroot}/etc/init.d -remove ${instroot}/etc/iproute2 -remove ${instroot}/etc/iscsi -remove ${instroot}/etc/kernel -remove ${instroot}/etc/ld.so.conf.d -remove ${instroot}/etc/logrotate.d -remove ${instroot}/etc/lvm -remove ${instroot}/etc/makedev.d -remove ${instroot}/etc/modprobe.d -remove ${instroot}/etc/netplug* -remove ${instroot}/etc/ntp -remove ${instroot}/etc/openldap -remove ${instroot}/etc/opt -remove ${instroot}/etc/pam.d -remove ${instroot}/etc/pki -remove ${instroot}/etc/pm -remove ${instroot}/etc/popt.d -remove ${instroot}/etc/ppp -remove ${instroot}/etc/prelink.conf.d -remove ${instroot}/etc/profile.d -remove ${instroot}/etc/rc?.d -remove ${instroot}/etc/rwtab.d -remove ${instroot}/etc/samba -remove ${instroot}/etc/sasl2 -remove ${instroot}/etc/security -remove ${instroot}/etc/setuptool.d -remove ${instroot}/etc/skel -remove ${instroot}/etc/ssh -remove ${instroot}/etc/statetab.d -remove ${instroot}/etc/terminfo -remove ${instroot}/etc/xdg -remove ${instroot}/etc/xinetd.d diff --git a/etc/templates/initrd.ltmpl b/etc/templates/initrd.ltmpl new file mode 100644 index 00000000..b4e11a8d --- /dev/null +++ b/etc/templates/initrd.ltmpl @@ -0,0 +1,214 @@ +## create modules and firmware symlinks +symlink "lib/modules" "modules" +symlink "lib/firmware" "firmware" + +## create required directories +makedirs "sbin" +symlink "sbin" "bin" +makedirs "dev" +makedirs "etc" +makedirs "lib" +makedirs "proc" +makedirs "selinux" +makedirs "sys" +makedirs "tmp" +makedirs "usr" +makedirs "var" + +## copy etc stuff +copy "etc/passwd" +copy "etc/group" +copy "etc/hosts" +copy "etc/nsswitch.conf" + +## copy mount/umount files +copy "bin/mount" +copy "bin/umount" +copy "sbin/mount.*" +copy "sbin/umount.*" + +## copy udev stuff +copy "sbin/udevd" +copy "sbin/udevadm" +symlink "udevadm" "sbin/udevinfo" +symlink "udevadm" "sbin/udevsettle" + +## copy udev conf and rules +copy "etc/udev/udev.conf" +copy "lib/udev/*" +remove "lib/udev/rules.d/*persistent*" +remove "lib/udev/rules.d/*generator*" +copy "etc/udev/rules.d/*.rules" + +## copy bash +copy "bin/bash" +symlink "bash" "bin/sh" + +## other +copy "sbin/consoletype" +copy "usr/bin/logger" "bin" + +## copy init functions +copy "etc/rc.d/init.d/functions" +copy "etc/sysconfig/network-scripts/network-functions*" + +## create init.d symlink +symlink "/etc/rc.d/init.d" "etc/init.d" + +## dhcp client daemons and support programs +copy "sbin/dhclient" +copy "sbin/dhclient-script" +copy "sbin/arping" +copy "sbin/ifconfig" +copy "sbin/ip" +copy "bin/ipcalc" +copy "bin/hostname" +copy "sbin/ethtool" +copy "sbin/route" +touch "etc/resolv.conf" + +## hwdata +copy "usr/share/hwdata/pci.ids" +copy "usr/share/hwdata/usb.ids" + +## hal +copy "usr/sbin/hald" "sbin" +copy "usr/libexec/hald-runner" +copy "usr/libexec/hald-generate-fdi-cache" +copy "usr/libexec/hal*storage*" +makedirs "var/run" +touch "var/run/hald.acl-list" +copy "usr/share/hal/fdi/*" +copy "etc/hal/fdi/*" +copy "etc/dbus-1/system.d/hal.conf" + +## policykit +copy "etc/polkit-1" +copy "usr/share/dbus-1/system-services/org.freedesktop.PolicyKit1.service" +copy "usr/share/polkit-1/actions/org.freedesktop.policykit.policy" + +## dbus +copy "bin/dbus-uuidgen" +copy "bin/dbus-daemon" +copy "etc/dbus-1/system.conf" +copy "${libdir}/dbus-1/dbus-daemon-launch-helper" + +## wpa_supplicant +copy "usr/sbin/wpa_passphrase" +copy "usr/sbin/wpa_supplicant" +copy "etc/dbus-1/system.d/wpa_supplicant.conf" +copy "etc/wpa_supplicant/wpa_supplicant.conf" +copy "usr/share/dbus-1/system-services/fi.epitest.hostap.WPASupplicant.service" + +## networkmanager +copy "usr/sbin/NetworkManager" +copy "etc/dbus-1/system.d/nm-*.conf" +copy "etc/dbus-1/system.d/NetworkManager.conf" +copy "etc/NetworkManager/nm-system-settings.conf" +copy "usr/${libdir}/NetworkManager/libnm-settings-plugin-ifcfg-rh.so" +copy "usr/libexec/nm-*" +copy "usr/share/dbus-1/system-services/org.freedesktop.nm_dispatcher.service" + +## modprobe +copy "sbin/modprobe" +copy "sbin/insmod" +copy "sbin/rmmod" + +## terminfos +copy "usr/share/terminfo/a/ansi" +copy "usr/share/terminfo/d/dumb" +copy "usr/share/terminfo/l/linux" +copy "usr/share/terminfo/s/screen" +copy "usr/share/terminfo/v/vt100" +copy "usr/share/terminfo/v/vt100-nav" +copy "usr/share/terminfo/v/vt102" +copy "usr/share/terminfo/x/xterm" +copy "usr/share/terminfo/x/xterm-color" +copy "usr/share/terminfo/g/gnome" + +## misc +copy "bin/awk" +copy "bin/egrep" +copy "bin/fgrep" +copy "bin/grep" +copy "bin/kill" +copy "bin/ln" +copy "usr/bin/readlink" "bin" +copy "bin/rm" +copy "bin/rmdir" +copy "bin/sed" +copy "bin/sleep" +copy "bin/touch" +copy "usr/sbin/dmidecode" "sbin" +copy "sbin/load_policy" +copy "sbin/mdadm" +copy "sbin/mdmon" +copy "bin/mkdir" +copy "usr/bin/wget" "bin" + +## misc symlinks +symlink "/sbin/init" "init" +symlink "/proc/mounts" "etc/mtab" +makedirs "var/lib" +symlink "../../tmp" "var/lib/xkb" + +## loader +copy "usr/lib/anaconda-runtime/loader/loader" "sbin" +copy "usr/lib/anaconda-runtime/loader/loader.tr" "etc" + +## indirect dependencies +copy "${libdir}/ld-linux.so.2" +copy "${libdir}/libcom_err.so.2" +copy "${libdir}/libgcc_s.so.1" +copy "${libdir}/libnss_dns.so.2" +copy "${libdir}/libnss_files.so.2" +copy "usr/${libdir}/libsqlite3.so.0" + +## langtable +copy "usr/lib/anaconda/lang-table" "etc" + +## arch bits +edit "etc/arch" "${buildarch}" + + +## architecture specific code +% if basearch in ("i386",): + copy "usr/lib/anaconda-runtime/loader/init" "sbin" + symlink "init" "sbin/reboot" + symlink "init" "sbin/halt" + symlink "init" "sbin/poweroff" + + ## screenfont + copy "usr/lib/anaconda-runtime/screenfont-${basearch}.gz" "etc" + rename "etc/screenfont-${basearch}.gz" "etc/screenfont.gz" +% endif + +% if basearch in ("s390", "s390x"): + makedirs "etc/ssh" + makedirs "etc/security" + makedirs "${libdir}/security" + makedirs "var/empty/sshd" + symlink "../../tmp" "var/state/xkb" + + copy "${libdir}/libpam_misc.so*" + copy "${libdir}/libwrap*.so*" + + copy "usr/lib/anaconda-runtime/loader/shutdown" "sbin" + copy "usr/lib/anaconda-runtime/loader/linuxrc.s390" "sbin" + rename "sbin/linuxrc.s390" "sbin/init" + copy "usr/lib/anaconda-runtime/loader/lsznet.raw" "sbin" + rename "sbin/lsznet.raw" "sbin/lsznet" + copy "usr/lib/anaconda-runtime/loader/controlunits.sh" "sbin" + rename "sbin/controlunits.sh" "sbin/controlunits" + + copy "${libdir}/security/pam*.so" + copy "etc/pam.d/other" + copy "etc/security/limits.conf" + copy "etc/security/pam_env.conf" + + copy "bin/login" + copy "usr/sbin/dasdfmt" + copy "usr/sbin/sshd" + copy "usr/bin/xauth" + copy "usr/sbin/cmsfs*" +% endif diff --git a/usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/%gconf.xml b/etc/templates/install.ltmpl similarity index 100% rename from usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/%gconf.xml rename to etc/templates/install.ltmpl diff --git a/setup.py b/setup.py index e1b2c22c..f34b8afe 100644 --- a/setup.py +++ b/setup.py @@ -1,32 +1,26 @@ +#! /usr/bin/env python + from distutils.core import setup -import glob -import os +from glob import glob -main_etc_files = [] -for comp in glob.glob(os.path.join(os.getcwd(), 'etc', '*')): - if os.path.isfile(comp): - main_etc_files.append(comp) -etc_data_files = [(os.path.join(os.path.sep, 'etc', 'lorax'), - main_etc_files)] +data_files = [("/etc/lorax", glob("etc/config.*")), + ("/etc/lorax", ["etc/ignore_errors"]), + ("/etc/lorax/templates", glob("etc/templates/*")) + ] -for comp in glob.glob(os.path.join(os.getcwd(), 'etc', '*')): - if os.path.isdir(comp): - sub_files = glob.glob(os.path.join(comp, '*')) - etc_path = os.path.join(os.path.sep, 'etc', 'lorax', os.path.basename(comp)) - etc_data_files.append((etc_path, sub_files)) -data_files = [(os.path.join(os.path.sep, 'usr', 'share', 'lorax'), - glob.glob(os.path.join('share', '*')))] + etc_data_files - -setup(name='lorax', - version='0.1', - description='Boot image build tool', - author='David Cantrell', - author_email='dcantrell@redhat.com', - license='GPLv2+', - package_dir = {'': 'src'}, - packages = ['pylorax'], - scripts = [os.path.join('src', 'bin', 'lorax')], - data_files = data_files - ) +setup(name="lorax", + version="0.1", + description="Lorax", + long_description="", + author="Martin Gracik", + author_email="mgracik@redhat.com", + url="http://", + download_url="http://", + license="GPLv2+", + packages=["pylorax"], + package_dir={"" : "src"}, + scripts=["src/bin/lorax"], + data_files=data_files + ) diff --git a/src/bin/lorax b/src/bin/lorax old mode 100755 new mode 100644 index 34d3c5e7..eaf2d511 --- a/src/bin/lorax +++ b/src/bin/lorax @@ -1,8 +1,7 @@ -#!/usr/bin/env python +#! /usr/bin/env python # # lorax -# lorax executable script # # Copyright (C) 2009 Red Hat, Inc. # @@ -22,9 +21,122 @@ # Red Hat Author(s): Martin Gracik # +from __future__ import print_function + import sys -import pylorax.launcher +import os +import tempfile +from optparse import OptionParser, OptionGroup + +import pylorax +import pylorax.config +from pylorax.yumbase import get_yum_base_object + + +def main(args): + version = "{0} 0.1".format(os.path.basename(args[0])) + usage = "%prog -p PRODUCT -v VERSION -r RELEASE -o OUTPUTDIR REPOSITORY" + + parser = OptionParser(usage=usage) + + # required arguments for image creation + required = OptionGroup(parser, "required arguments") + required.add_option("-p", "--product", help="product name", + metavar="STRING") + required.add_option("-v", "--version", help="version identifier", + metavar="STRING") + required.add_option("-r", "--release", help="release information", + metavar="STRING") + required.add_option("-o", "--outputdir", help="output directory", + metavar="PATHSPEC") + + # optional arguments + optional = OptionGroup(parser, "optional arguments") + optional.add_option("-m", "--mirrorlist", + help="mirrorlist repository (may be listed multiple times)", + metavar="REPOSITORY", action="append", default=[]) + optional.add_option("-t", "--variant", + help="variant name", metavar="STRING") + optional.add_option("-b", "--bugurl", + help="bug reporting URL for the product", metavar="URL", + default="your distribution provided bug reporting tool") + optional.add_option("-u", "--updates", + help="directory containing updates", metavar="PATHSPEC") + + # output settings + output = OptionGroup(parser, "output settings") + output.add_option("--no-colors", help="disable color output", + action="store_false", default=True, dest="colors") + output.add_option("--encoding", help="set encoding", + metavar="STRING", default="utf-8") + output.add_option("-d", "--debug", help="enable debugging messages", + action="store_true", default=False) + + # lorax settings + settings = OptionGroup(parser, "lorax settings") + settings.add_option("-c", "--cleanup", help="clean up on exit", + action="store_true", default=False) + + # add the option groups to the parser + parser.add_option_group(required) + parser.add_option_group(optional) + parser.add_option_group(output) + parser.add_option_group(settings) + + # add the show version option + parser.add_option("-V", help="show program's version number and exit", + action="store_true", default=False, dest="showver") + + # parse the arguments + opts, args = parser.parse_args() + repositories = args + + if opts.showver: + print(version) + sys.exit(0) + + # check for the required arguments + if not opts.product or not opts.version or not opts.release \ + or not opts.outputdir or not repositories: + parser.error("missing one or more required arguments") + + # create the temporary directory for lorax + tempdir = tempfile.mkdtemp(prefix="lorax.", dir=tempfile.gettempdir()) + + # create the yumbase object + installtree = os.path.join(tempdir, "installtree") + os.mkdir(installtree) + yumtempdir = os.path.join(tempdir, "yum") + os.mkdir(yumtempdir) + + yb = get_yum_base_object(installtree, repositories, opts.mirrorlist, + yumtempdir) + + if yb is None: + print("error: unable to create the yumbase object", file=sys.stderr) + shutil.rmtree(tempdir) + sys.exit(1) + + # configure lorax + config = pylorax.config.LoraxConfig() + config.colors = opts.colors + config.encoding = opts.encoding + config.debug = opts.debug + + # run lorax + params = {"installtree": installtree, + "outputdir": opts.outputdir, + "product": opts.product, + "version": opts.version, + "release": opts.release, + "workdir": tempdir, + "variant": opts.variant, + "bugurl": opts.bugurl, + "updatesdir": opts.updates} + + lorax = pylorax.Lorax(yb, **params) + lorax.run() if __name__ == "__main__": - pylorax.launcher.main(sys.argv) + main(sys.argv) diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index 46eb7459..c8e26919 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -1,6 +1,5 @@ # # __init__.py -# main lorax class # # Copyright (C) 2009 Red Hat, Inc. # @@ -17,308 +16,272 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Red Hat Author(s): David Cantrell -# Martin Gracik +# Red Hat Author(s): Martin Gracik +# David Cantrell # -__VERSION__ = "0.1" - import sys import os import ConfigParser -import re import glob import time import datetime import shutil -import commands import yum import yum.callbacks import yum.rpmtrans -import config +from base import BaseLoraxClass import output -import ramdisk -import efi -import install -import lcs + +import insttree +import images +from sysutils import * -ARCHS64 = ( "x86_64", - "s390x", - "sparc64" ) - -BASEARCH_MAP = { "i586" : "i386", - "i686" : "i386", - "sparc64" : "sparc" } - -EFIARCH_MAP = { "i386" : "IA32", - "i586" : "IA32", - "i686" : "IA32", - "x86_64" : "X64", - "ia64" : "IA64" } +# basearch efiarch 64bit +ARCHMAP = {"i386": ["i386", "IA32", False], + "i586": ["i386", "IA32", False], + "i686": ["i386", "IA32", False], + "x86_64": ["x86_64", "X64", True], + "ppc": ["ppc", "", False], + "ppc64": ["ppc", "", True], + "s390": ["s390", "", False], + "s390x": ["s390x", "", True], + "sparc": ["sparc", "", False], + "sparc64": ["sparc", "", True], + "ia64": ["ia64", "IA64", True]} LIB32 = "lib" LIB64 = "lib64" -class LoraxError(Exception): - pass +class Lorax(BaseLoraxClass): + def __init__(self, yb, installtree, outputdir, + product, version, release, + workdir="/tmp", variant="", bugurl="", updatesdir=None): -class Lorax(object): + BaseLoraxClass.__init__(self) - SETTINGS = ( "colors", - "encoding", - "debug", - "cleanup" ) + # XXX check if we have root privileges + assert os.geteuid() == self.const.ROOT_UID, "no root privileges" - REQ_PARAMS = ( "product", - "version", - "release", - "outputdir", - "tempdir", - "installtree" ) + # XXX check if we have a yumbase object + assert isinstance(yb, yum.YumBase), "not an yum base object" - OPT_PARAMS = ( "variant", - "bugurl", - "updates" ) - - def __init__(self, yb, *args, **kwargs): - # check if we have root privileges - if not os.geteuid() == 0: - raise LoraxError("no root privileges") - - # check the yumbase object - if not isinstance(yb, yum.YumBase): - raise LoraxError("not an yumbase object") - - # create the yum object + # setup yum and the install tree self.yum = YumHelper(yb) + self.installtree = insttree.InstallTree(yum=self.yum, + rootdir=installtree, + updatesdir=updatesdir) - # get the config object - self.conf = config.LoraxConfig.get() + # create the output directory + assert not os.path.isdir(outputdir), "output directory exists" + self.outputdir = outputdir + makedirs_(self.outputdir) - # get the settings first - for key in self.SETTINGS: - value = kwargs.get(key, None) - if value is not None: - setattr(self.conf, key, value) + # required parameters + self.product = product + self.version = version + self.release = release - # set up the output - self.output = output.Terminal.get() + # create the working director + self.workdir = workdir + makedirs_(self.workdir) + # optional parameters + self.variant = variant + self.bugurl = bugurl + + # setup the output output_level = output.INFO if self.conf.debug: output_level = output.DEBUG self.output.basic_config(colors=self.conf.colors, encoding=self.conf.encoding, - level=output_level) + output_level=output_level) - # check and set up the required parameters - for key in self.REQ_PARAMS: - value = kwargs.get(key, None) + ignore_errors = set() + if os.path.isfile(self.conf.ignore_errors): + with open(self.conf.ignore_errors, "r") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + ignore_errors.add(line) - if value is None: - raise LoraxError("missing required parameter '%s'" % key) - - setattr(self.conf, key, value) - - # set up the optional parameters - for key in self.OPT_PARAMS: - setattr(self.conf, key, kwargs.get(key, "")) - - # check if the required directories exist - if os.path.isdir(self.conf.outputdir): - raise LoraxError("output directory '%s' already exist" % \ - self.conf.outputdir) - - if not os.path.isdir(self.conf.tempdir): - raise LoraxError("temporary directory '%s' does not exist" % \ - self.conf.tempdir) - - if not os.path.isdir(self.conf.installtree): - raise LoraxError("install tree directory '%s' does not exist" % \ - self.conf.installtree) - - # get the paths - self.paths = config.LoraxPaths.get() + self.output.ignore = ignore_errors def run(self): - # set the target architecture - self.output.info(":: setting the build architecture") - self.conf.arch = self.get_arch() - self.conf.basearch = BASEARCH_MAP.get(self.conf.arch, self.conf.arch) - self.conf.efiarch = EFIARCH_MAP.get(self.conf.arch, "") + # check if we have all required commands + for cmd in self.cmd.values(): + if not os.path.isfile(cmd): + err = "required command {0} does not exist" + self.pwarning(err.format(cmd)) + + # set the build architecture + self.pinfo(":: setting the build architecture") + self.conf.buildarch = self.get_buildarch() + basearch, efiarch, is64 = ARCHMAP[self.conf.buildarch] + + self.conf.basearch = basearch + self.conf.efiarch = efiarch # set the libdir self.conf.libdir = LIB32 - if self.conf.arch in ARCHS64: + if is64: self.conf.libdir = LIB64 - # read the config files - self.output.info(":: reading the configuration files") - packages, modules, initrd_template, scrubs_template = self.get_config() + # read the configuration files + self.pinfo(":: reading the configuration files") + c = self.get_config() + packages, modules, initrd_template, install_template = c # add the branding - packages.add("%s-logos" % self.conf.product.lower()) - packages.add("%s-release" % self.conf.product.lower()) + packages.add("{0}-logos".format(self.product.lower())) + packages.add("{0}-release".format(self.product.lower())) - self.conf.packages = packages - self.conf.modules = modules - self.conf.initrd_template = initrd_template - self.conf.scrubs_template = scrubs_template + # prepare the install tree + self.pinfo(":: preparing the install tree") + self.installtree.install_packages(packages=packages) + self.installtree.run_ldconfig() + self.installtree.copy_updates() + self.installtree.fix_problems() - # install packages into the install tree - self.output.info(":: installing required packages") - for name in packages: - if not self.yum.install(name): - self.output.warning("no package %s found" % \ - self.output.format(name, type=output.BOLD)) + # check the anaconda runtime directory + anarun = os.path.join(self.installtree.rootdir, + self.const.ANACONDA_RUNTIME) - self.yum.process_transaction() - - # copy the updates - if self.conf.updates and os.path.isdir(self.conf.updates): - self.output.info(":: copying updates") - utils.scopy(src_root=self.conf.updates, src_path="*", - dst_root=self.conf.installtree, dst_path="") - - # get the anaconda runtime directory - if os.path.isdir(self.paths.ANACONDA_RUNTIME): - self.conf.anaconda_runtime = self.paths.ANACONDA_RUNTIME - self.conf.anaconda_boot = self.paths.ANACONDA_BOOT - else: - self.output.critical("no anaconda runtime directory found") + if not os.path.isdir(anarun): + self.pcritical("no anaconda runtime directory found") sys.exit(1) - # get list of the installed kernel files - kerneldir = self.paths.BOOTDIR - if self.conf.arch == "ia64": - kerneldir = self.paths.BOOTDIR_IA64 - - kernelfiles = glob.glob(os.path.join(kerneldir, "vmlinuz-*")) - - if not kernelfiles: - self.output.critical("no kernel image found") + # prepare the output directory + self.pinfo(":: preparing the output directory") + ok = self.prepare_outputdir() + if not ok: + self.pcritical("unable to prepare the output directory") sys.exit(1) - # create treeinfo, discinfo, and buildstamp + # write the treeinfo, discinfo and buildstamp + self.pinfo(":: creating the treeinfo, discinfo and buildstamp") self.conf.treeinfo = self.write_treeinfo() self.conf.discinfo = self.write_discinfo() self.conf.buildstamp = self.write_buildstamp() - # prepare the output directory - self.output.info(":: preparing the output directory") - ok = self.prepare_output_directory() + # create the initrd images for all kernels in install tree + initrd = images.InitRD(self.installtree, modules, initrd_template, + self.workdir) - if not ok: - self.output.critical("unable to prepare the output directory") - sys.exit(1) + for kernel, initrd in initrd.create(): + if kernel.is_pae: + kernelfile = "vmlinuz-PAE" + initrdfile = "initrd-PAE.img" - for kernelfile in kernelfiles: - kfilename = os.path.basename(kernelfile) - m = re.match(r"vmlinuz-(?P.*)", kfilename) - if m: - self.conf.kernelfile = kernelfile - self.conf.kernelver = m.group("ver") + # add images section to treeinfo + section = "images-xen" + data = {"kernel": "images/pxeboot/vmlinuz-PAE", + "initrd": "images/pxeboot/initrd-PAE.img"} + self.treeinfo_add_section(self.conf.treeinfo, section, data) + elif kernel.is_xen: + kernelfile = "vmlinuz-xen" + initrdfile = "initrd-xen.img" else: - continue + kernelfile = "vmlinuz" + initrdfile = "initrd.img" - self.output.info(":: creating initrd for '%s'" % kfilename) + # add images section to treeinfo + section = "images-{0}".format(self.conf.basearch) + data = {"kernel": "images/pxeboot/vmlinuz", + "initrd": "images/pxeboot/initrd.img"} + self.treeinfo_add_section(self.conf.treeinfo, section, data) - # create a temporary directory for the ramdisk tree - self.conf.ramdisktree = os.path.join(self.conf.tempdir, - "ramdisk-%s" % \ - self.conf.kernelver) + # copy the kernel and initrd image to the isolinux directory + kdst = os.path.join(self.conf.isodir, kernelfile) + idst = os.path.join(self.conf.isodir, initrdfile) + shutil.copy2(kernel.path, kdst) + shutil.copy2(initrd, idst) - utils.makedirs(self.conf.ramdisktree) + # create the efi images + if self.installtree.do_efi: + efi = images.EFI(self.installtree, kernel, initrd, + self.product, self.version, self.workdir) - initrd = ramdisk.Ramdisk() - kernel_path, initrd_path = initrd.create() + efiboot, efidisk = efi.create() - # copy the kernel and initrd images to the pxeboot directory - if kernel_path is not None: - shutil.copy2(kernel_path, self.conf.pxebootdir) - if kernel_path is not None: - shutil.copy2(initrd_path, self.conf.pxebootdir) + # copy the efi images to the images directory + shutil.copy2(efiboot, self.conf.imgdir) + shutil.copy2(efidisk, self.conf.imgdir) - # if this is a PAE kernel, skip the EFI part - if kernel_path.endswith("PAE"): - continue - - # copy the kernel and initrd images to the isolinux directory - shutil.copy2(kernel_path, self.conf.isolinuxdir) - shutil.copy2(initrd_path, self.conf.isolinuxdir) - - # create the efi images - self.output.info(":: creating efi images for '%s'" % kfilename) - - efiimages = efi.EFI() - eb, ed = efiimages.create(kernel=kernel_path, - initrd=initrd_path, - kernelpath="/images/pxeboot/vmlinuz", - initrdpath="/images/pxeboot/initrd.img") - - self.conf.efiboot = eb - self.conf.efidisk = ed - - # copy the efi images to the images directory - shutil.copy2(self.conf.efiboot, self.conf.imagesdir) - shutil.copy2(self.conf.efidisk, self.conf.imagesdir) + # copy the kernel and initrd image to the pxe directory + kdst = os.path.join(self.conf.pxedir, kernelfile) + idst = os.path.join(self.conf.pxedir, initrdfile) + shutil.copy2(kernel.path, kdst) + shutil.copy2(initrd, idst) # create the install image - self.output.info(":: creating the install image") - i = install.InstallImage() - installimg = i.create() + install = images.Install(self.installtree, install_template, + self.workdir) - if installimg is None: - self.output.critical("unable to create install image") + installimg = install.create() + if not installimg: + self.perror("unable to create the install image") sys.exit(1) - shutil.copy2(installimg, self.conf.imagesdir) + # add stage2 section to the treeinfo + section = "stage2" + data = {"mainimage": "images/install.img"} + self.treeinfo_add_section(self.conf.treeinfo, section, data) - # create the boot iso - if self.conf.arch in ("i386", "i586", "i686", "x86_64"): - self.output.info(":: creating the boot iso") - bootiso = self.create_boot_iso_x86() - elif self.conf.arch in ("ppc", "ppc64"): - self.output.info(":: creating the boot iso") - bootiso = self.create_boot_iso_ppc() + # move the install image to the images directory + shutil.move(installimg, self.conf.imgdir) - if bootiso is None: - self.output.critical("unable to create boot iso") - sys.exit(1) + # create the boot iso image + boot = images.Boot(self.product, self.workdir) + bootiso = boot.create() - shutil.copy2(bootiso, self.conf.imagesdir) + # add boot iso to the images section in treeinfo + section = "images-{0}".format(self.conf.basearch) + data = {"boot.iso": "images/boot.iso"} + self.treeinfo_add_section(self.conf.treeinfo, section, data) + + # move the boot iso to the images directory + shutil.move(bootiso, self.conf.imgdir) # copy the treeinfo and discinfo to the output directory - shutil.copy2(self.conf.treeinfo, self.conf.outputdir) - shutil.copy2(self.conf.discinfo, self.conf.outputdir) + shutil.copy2(self.conf.treeinfo, self.outputdir) + shutil.copy2(self.conf.discinfo, self.outputdir) # cleanup self.cleanup() - def get_arch(self): + def get_buildarch(self): # get the architecture of the anaconda package - installed, available = self.yum.search(self.paths.ANACONDA_PACKAGE) + installed, available = self.yum.search(self.const.ANACONDA_PACKAGE) + + if installed: + anaconda = installed[0] + if available: + anaconda = available[0] + try: - arch = available[0].arch + buildarch = anaconda.arch except: # fallback to the system architecture - arch = os.uname()[4] + self.pwarning("using system architecture") + buildarch = os.uname()[4] - return arch + return buildarch def get_config(self): generic = os.path.join(self.conf.confdir, "config.noarch") specific = os.path.join(self.conf.confdir, - "config.%s" % self.conf.arch) + "config.{0}".format(self.conf.basearch)) packages, modules = set(), set() - initrd_template, scrubs_template = None, None + initrd_template, install_template = None, None for f in (generic, specific): if not os.path.isfile(f): @@ -348,39 +311,168 @@ class Lorax(object): initrd_template = os.path.join(self.conf.confdir, initrd_template) - if c.has_option("lorax", "scrubs_template"): - scrubs_template = c.get("lorax", "scrubs_template") - scrubs_template = os.path.join(self.conf.confdir, - scrubs_template) + if c.has_option("lorax", "install_template"): + install_template = c.get("lorax", "install_template") + install_template = os.path.join(self.conf.confdir, + install_template) - return packages, modules, initrd_template, scrubs_template + return packages, modules, initrd_template, install_template + + def prepare_outputdir(self): + imgdir = os.path.join(self.outputdir, "images") + makedirs_(imgdir) + + # write the images/README file + text = """ +This directory contains image files that can be used to create media +capable of starting the {0} installation process. + +The boot.iso file is an ISO 9660 image of a bootable CD-ROM. It is useful +in cases where the CD-ROM installation method is not desired, but the +CD-ROM's boot speed would be an advantage. + +To use this image file, burn the file onto CD-R (or CD-RW) media as you +normally would. +""" + + text = text.format(self.product) + with open(os.path.join(imgdir, "README"), "w") as f: + f.write(text) + + pxedir = os.path.join(imgdir, "pxeboot") + makedirs_(pxedir) + + # write the images/pxeboot/README file + text = """ +The files in this directory are useful for booting a machine via PXE. + +The following files are available: +vmlinuz - the kernel used for the installer +initrd.img - an initrd with support for all install methods and + drivers supported for installation of {0} +""" + + text = text.format(self.product) + with open(os.path.join(pxedir, "README"), "w") as f: + f.write(text) + + efidir = os.path.join(self.outputdir, "EFI/BOOT") + makedirs_(efidir) + + isodir = os.path.join(self.outputdir, "isolinux") + makedirs_(isodir) + + self.conf.imgdir = imgdir + self.conf.pxedir = pxedir + self.conf.efidir = efidir + self.conf.isodir = isodir + + isolinuxbin = os.path.join(self.installtree.rootdir, + self.const.ISOLINUXBIN) + syslinuxcfg = os.path.join(self.installtree.rootdir, + self.const.SYSLINUXCFG) + + if not os.path.isfile(isolinuxbin): + self.perror("no isolinux binary found") + return False + + # copy the isolinux.bin file + shutil.copy2(isolinuxbin, self.conf.isodir) + + # copy the syslinux.cfg + isolinuxcfg = os.path.join(self.conf.isodir, "isolinux.cfg") + shutil.copy2(syslinuxcfg, isolinuxcfg) + + # set the product and version in isolinux.cfg + replace_(isolinuxcfg, r"@PRODUCT@", self.product) + replace_(isolinuxcfg, r"@VERSION@", self.version) + + # set up the label for finding stage2 with a hybrid iso + replace_(isolinuxcfg, r"initrd=initrd.img", + 'initrd=initrd.img stage2=hd:LABEL="{0}"'.format(self.product)) + + # copy the .msg files + msgfiles = os.path.join(self.const.ANACONDA_BOOTDIR, "*.msg") + msgfiles = os.path.join(self.installtree.rootdir, msgfiles) + for fname in glob.iglob(msgfiles): + shutil.copy2(fname, self.conf.isodir) + path = os.path.join(self.conf.isodir, os.path.basename(fname)) + replace_(path, r"@VERSION@", self.version) + + # copy the memtest + memtest = os.path.join(self.const.BOOTDIR, "memtest*") + memtest = os.path.join(self.installtree.rootdir, memtest) + for fname in glob.iglob(memtest): + shutil.copy2(fname, os.path.join(self.conf.isodir, "memtest")) + + text = """label memtest86 + menu label ^Memory test + kernel memtest + append - + +""" + + with open(isolinuxcfg, "a") as f: + f.write(text) + + break + + # copy the grub.conf + grubconf = os.path.join(self.installtree.rootdir, + self.const.ANACONDA_BOOTDIR, "grub.conf") + shutil.copy2(grubconf, isodir) + + # copy the splash files + vesasplash = os.path.join(self.installtree.rootdir, + self.const.VESASPLASH) + vesamenu = os.path.join(self.installtree.rootdir, + self.const.VESAMENU) + + splashtools = os.path.join(self.installtree.rootdir, + self.const.SPLASHTOOLS) + syslinuxsplash = os.path.join(self.installtree.rootdir, + self.const.SYSLINUXSPLASH) + splashlss = os.path.join(self.installtree.rootdir, + self.const.SPLASHLSS) + + if os.path.isfile(vesasplash): + shutil.copy2(vesasplash, os.path.join(isodir, "splash.jpg")) + shutil.copy2(vesamenu, isodir) + replace_(isolinuxcfg, r"default linux", "default vesamenu.c32") + replace_(isolinuxcfg, r"prompt 1", "#prompt 1") + else: + if os.path.isfile(splashtools): + cmd = "{0} {1} {2}".format(splashtools, + syslinuxsplash, + splashlss) + + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + if os.path.isfile(splashlss): + shutil.copy2(splashlss, isodir) + + return True def write_treeinfo(self, discnum=1, totaldiscs=1, packagedir=""): - outfile = os.path.join(self.conf.tempdir, ".treeinfo") - - variant = self.conf.variant - if variant is None: - variant = "" + outfile = os.path.join(self.workdir, ".treeinfo") c = ConfigParser.ConfigParser() + variant = self.variant + if variant is None: + variant = "" + section = "general" - data = { "timestamp" : time.time(), - "family" : self.conf.product, - "version" : self.conf.version, - "arch" : self.conf.basearch, - "variant" : variant, - "discnum" : discnum, - "totaldiscs" : totaldiscs, - "packagedir" : packagedir } - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - section = "images-%s" % self.conf.basearch - data = { "kernel" : "images/pxeboot/vmlinuz", - "initrd" : "images/pxeboot/initrd.img", - "boot.iso" : "images/boot.iso" } + data = {"timestamp": time.time(), + "family": self.product, + "version": self.version, + "variant": variant, + "arch": self.conf.basearch, + "discnum": discnum, + "totaldiscs": totaldiscs, + "packagedir": packagedir} c.add_section(section) map(lambda (key, value): c.set(section, key, value), data.items()) @@ -390,282 +482,47 @@ class Lorax(object): return outfile + def treeinfo_add_section(self, treeinfo, section, data): + c = ConfigParser.ConfigParser() + c.read(treeinfo) + + if not c.has_section(section): + c.add_section(section) + + map(lambda (key, value): c.set(section, key, value), data.items()) + + with open(treeinfo, "w") as f: + c.write(f) + def write_discinfo(self, discnum="ALL"): - outfile = os.path.join(self.conf.tempdir, ".discinfo") + outfile = os.path.join(self.workdir, ".discinfo") with open(outfile, "w") as f: - f.write("%f\n" % time.time()) - f.write("%s\n" % self.conf.release) - f.write("%s\n" % self.conf.basearch) - f.write("%s\n" % discnum) + f.write("{0:f}\n".format(time.time())) + f.write("{0}\n".format(self.release)) + f.write("{0}\n".format(self.conf.basearch)) + f.write("{0}\n".format(discnum)) return outfile def write_buildstamp(self): - outfile = os.path.join(self.conf.tempdir, ".buildstamp") + outfile = os.path.join(self.workdir, ".buildstamp") now = datetime.datetime.now() - uuid = "%s.%s" % (now.strftime("%Y%m%d%H%M"), self.conf.arch) + uuid = "{0}.{1}" + uuid = uuid.format(now.strftime("%Y%m%d%H%M"), self.conf.buildarch) with open(outfile, "w") as f: - f.write("%s\n" % uuid) - f.write("%s\n" % self.conf.product) - f.write("%s\n" % self.conf.version) - f.write("%s\n" % self.conf.bugurl) + f.write("{0}\n".format(uuid)) + f.write("{0}\n".format(self.product)) + f.write("{0}\n".format(self.version)) + f.write("{0}\n".format(self.bugurl)) return outfile - def prepare_output_directory(self): - # create the output directory - os.mkdir(self.conf.outputdir) - - # create the images directory - imagesdir = os.path.join(self.conf.outputdir, "images") - utils.mkdir(imagesdir) - self.conf.imagesdir = imagesdir - - # write the images/README file - src = os.path.join(self.paths.OUTPUTDIR_DATADIR, "images", "README") - dst = os.path.join(imagesdir, "README") - shutil.copy2(src, dst) - utils.replace(dst, r"@PRODUCT@", self.conf.product) - - # create the pxeboot directory - pxebootdir = os.path.join(imagesdir, "pxeboot") - utils.mkdir(pxebootdir) - self.conf.pxebootdir = pxebootdir - - # write the images/pxeboot/README file - src = os.path.join(self.paths.OUTPUTDIR_DATADIR, "images", "pxeboot", - "README") - dst = os.path.join(pxebootdir, "README") - shutil.copy2(src, dst) - utils.replace(dst, r"@PRODUCT@", self.conf.product) - - # create the efiboot directory - efibootdir = os.path.join(self.conf.outputdir, "EFI", "BOOT") - utils.makedirs(efibootdir) - self.conf.efibootdir = efibootdir - - # create the isolinux directory - isolinuxdir = os.path.join(self.conf.outputdir, "isolinux") - utils.mkdir(isolinuxdir) - self.conf.isolinuxdir = isolinuxdir - - syslinuxdir = self.paths.SYSLINUXDIR - isolinuxbin = self.paths.ISOLINUXBIN - - if not os.path.isfile(isolinuxbin): - self.output.error("no isolinux binary found") - return False - - # copy the isolinux.bin - shutil.copy2(isolinuxbin, isolinuxdir) - - # copy the syslinux.cfg to isolinux/isolinux.cfg - isolinuxcfg = os.path.join(isolinuxdir, "isolinux.cfg") - shutil.copy2(self.paths.SYSLINUXCFG, isolinuxcfg) - - # set the product and version in isolinux.cfg - utils.replace(isolinuxcfg, r"@PRODUCT@", self.conf.product) - utils.replace(isolinuxcfg, r"@VERSION@", self.conf.version) - - # set up the label for finding stage2 with a hybrid iso - utils.replace(isolinuxcfg, r"initrd=initrd.img", - 'initrd=initrd.img stage2=hd:LABEL="%s"' % \ - self.conf.product) - - # copy the grub.conf - dst = os.path.join(isolinuxdir, "grub.conf") - shutil.copy2(self.paths.GRUBCONF, dst) - - # copy the splash files - if os.path.isfile(self.paths.VESASPLASH): - shutil.copy2(self.paths.VESASPLASH, - os.path.join(isolinuxdir, "splash.jpg")) - - shutil.copy2(self.paths.VESAMENU, isolinuxdir) - - utils.replace(isolinuxcfg, r"default linux", "default vesamenu.c32") - utils.replace(isolinuxcfg, r"prompt 1", "#prompt 1") - - else: - if os.path.isfile(self.paths.SPLASHTOOLS): - cmd = "%s %s %s" % (self.paths.SPLASHTOOLS, - self.paths.SYSLINUXSPLASH, - self.paths.SPLASHLSS) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - if os.path.isfile(self.paths.SPLASHLSS): - shutil.copy2(self.paths.SPLASHLSS, isolinuxdir) - - # copy the .msg files - msgfiles = os.path.join(self.conf.anaconda_boot, "*.msg") - for fname in glob.glob(msgfiles): - shutil.copy2(fname, isolinuxdir) - utils.replace(os.path.join(isolinuxdir, os.path.basename(fname)), - r"@VERSION@", self.conf.version) - - # copy the memtest - memtest = os.path.join(self.paths.BOOTDIR, "memtest*") - for fname in glob.glob(memtest): - shutil.copy2(fname, os.path.join(isolinuxdir, "memtest")) - - text = """label memtest86 - menu label ^Memory test - kernel memtest - append - - -""" - - utils.edit(isolinuxcfg, append=True, text=text) - break - - return True - - def create_boot_iso_x86(self): - bootiso = os.path.join(self.conf.tempdir, "boot.iso") - - if os.path.exists(bootiso): - os.unlink(bootiso) - - if os.path.exists(self.conf.efiboot): - self.output.info(":: creating efi capable boot iso") - efiargs = "-eltorito-alt-boot -e images/efiboot.img -no-emul-boot" - efigraft = "EFI/BOOT=%s" % self.conf.efibootdir - else: - efiargs = "" - efigraft = "" - - #biosargs = "-b isolinux/isolinux.bin -c isolinux/boot.cat" \ - # " -no-emul-boot -boot-load-size 4 -boot-info-table" - - #cmd = "%s -v -o %s %s %s -R -J -V %s -T -graft-points" \ - # " isolinux=%s images=%s %s" % (self.paths.MKISOFS, bootiso, - # biosargs, efiargs, - # self.conf.product, - # self.conf.isolinuxdir, - # self.conf.imagesdir, efigraft) - - cmd = "%s -U -A %s -V %s -volset %s -J -joliet-long -r -v -T -o %s" \ - " -b isolinux/isolinux.bin -c isolinux/boot.cat" \ - " -no-emul-boot -boot-load-size 4 -boot-info-table" \ - " %s -graft-points isolinux=%s images=%s %s" \ - % (self.paths.MKISOFS, self.conf.product, self.conf.product, - self.conf.product, bootiso, efiargs, - self.conf.isolinuxdir, self.conf.imagesdir, efigraft) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return None - - if os.path.isfile(self.paths.ISOHYBRID): - cmd = "%s %s" % (self.paths.ISOHYBRID, bootiso) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - return bootiso - - def create_boot_iso_ppc(self): - bootiso = os.path.join(self.conf.tempdir, "boot.iso") - - if os.path.exists(bootiso): - os.unlink(bootiso) - - etcdir = os.path.join(self.conf.outputdir, "etc") - ppcdir = os.path.join(self.conf.outputdir, "ppc") - macdir = os.path.join(ppcdir, "mac") - chrpdir = os.path.join(ppcdir, "chrp") - - utils.makedirs(etcdir) - utils.makedirs(chrpdir) - - shutil.copy2(self.paths.BOOTINFO, ppcdir) - shutil.copy2(self.paths.EFIKA_FORTH, ppcdir) - - if os.path.isdir(macdir): - shutil.copy2(self.paths.OFBOOT, macdir) - shutil.copy2(self.paths.YABOOT, macdir) - - if os.path.isdir(chrpdir): - shutil.copy2(self.paths.YABOOT, chrpdir) - cmd = "%s %s" % (self.paths.ADD_NOTE, - os.path.join(chrpdir, "yaboot")) - - # IBM firmware can't handle boot scripts properly, - # so for biarch installs we use a yaboot.conf, - # which asks the user to select 32-bit or 64-bit kernel - yaboot32 = os.path.join(ppcdir, "ppc32", "yaboot.conf") - yaboot64 = os.path.join(ppcdir, "ppc64", "yaboot.conf") - if os.path.isfile(yaboot32) and os.path.isfile(yaboot64): - # both kernels exist, copy the biarch yaboot.conf into place - yaboot = os.path.join(etcdir, "yaboot.conf") - shutil.copy2(self.paths.BIARCH_YABOOT, yaboot) - utils.replace(yaboot, "%BITS%", "32") - utils.replace(yaboot, "%PRODUCT%", self.conf.product) - utils.replace(yaboot, "%VERSION%", self.conf.version) - else: - if os.path.isfile(yaboot32): - shutil.copy2(yaboot32, etcdir) - if os.path.isfile(yaboot64): - shutil.copy2(yaboot64, etcdir) - - isopath = os.path.join(self.conf.outputdir, "isopath") - utils.mkdir(isopath) - - utils.scopy(ppcdir, isopath) - utils.scopy(etcdir, isopath) - - netbootdir = os.path.join(self.conf.outputdir, "images", "netboot") - if os.path.isdir(netbootdir): - imagesdir = os.path.join(isopath, "images") - utils.mkdir(imagesdir) - - utils.scopy(netbootdir, imagesdir) - utils.remove(os.path.join(imagesdir, "ppc64.img")) - - ppc32img = os.path.join(netbootdir, "ppc32.img") - if os.path.isfile(pcp32img): - prepboot = "-prep-boot images/netboot/ppc32.img" - - isomacdir = os.path.join(isopath, "ppc", "mac") - if os.path.isdir(isomacdir): - macboot = "-hfs-volid %s -hfs-bless %s" % (self.conf.version, - isomacdir) - - installimg = os.path.join(self.conf.imagesdir, "install.img") - cmd = '%s -o %s -chrp-boot -U %s -part -hfs -T -r -l -J -A "%s %s"' \ - ' -sysid PPC -V "PBOOT" -volset %s -volset-size 1' \ - ' -volset-seqno 1 %s -map %s/mapping -magic %s/magic' \ - ' -no-desktop' \ - ' -allow-multidot -graft-points %s images/install.img=%s' % \ - (self.paths.MKISOFS, bootiso, prepboot, self.conf.product, - self.conf.version, self.conf.version, macboot, - self.paths.ANACONDA_BOOT, self.paths.ANACONDA_BOOT, - isopath, installimg) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - return None - - cmd = "%s %s" % (self.paths.IMPLANTISO, bootiso) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - return None - - utils.remove(isopath) - - return bootiso - def cleanup(self): - if self.conf.cleanup: - shutil.rmtree(self.conf.tempdir) + # TODO + pass class YumHelper(object): @@ -705,16 +562,26 @@ class RpmCallback(yum.rpmtrans.SimpleCliCallBack): def __init__(self): yum.rpmtrans.SimpleCliCallBack.__init__(self) - self.output = output.Terminal.get() + self.output = output.LoraxOutput() + + self.termwidth = 79 def event(self, package, action, te_current, te_total, - ts_current, ts_total): + ts_current, ts_total): - msg = "(%3d/%3d) [%3d%%] %s %s\r" % (ts_current, ts_total, - float(te_current) / float(te_total) * 100, - self.action[action], - self.output.format("%s" % package, type=output.BOLD)) + info = "({0:3d}/{1:3d}) [{2:3.0f}%] {3} " + info = info.format(ts_current, ts_total, + float(te_current) / float(te_total) * 100, + self.action[action]) + pkg = "{0}".format(package) + + infolen = len(info) + pkglen = len(pkg) + if (infolen + pkglen) > self.termwidth: + pkg = "{0}...".format(pkg[:self.termwidth-infolen-3]) + + msg = "{0}{1}\r".format(info, pkg) self.output.write(msg) if te_current == te_total: self.output.write("\n") diff --git a/src/pylorax/base.py b/src/pylorax/base.py new file mode 100644 index 00000000..b930843b --- /dev/null +++ b/src/pylorax/base.py @@ -0,0 +1,143 @@ +# +# base.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +from abc import ABCMeta, abstractmethod + +import sys +import os +import shlex +import shutil + +import config +import constants +import output +import ltmpl +from sysutils import * + + +class BaseLoraxClass(object): + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self): + self.conf = config.LoraxConfig() + self.const = constants.LoraxConstants() + self.cmd = constants.LoraxCommands() + self.output = output.LoraxOutput() + + def pcritical(self, msg, file=sys.stdout): + self.output.critical(msg, file) + + def perror(self, msg, file=sys.stdout): + self.output.error(msg, file) + + def pwarning(self, msg, file=sys.stdout): + self.output.warning(msg, file) + + def pinfo(self, msg, file=sys.stdout): + self.output.info(msg, file) + + def pdebug(self, msg, file=sys.stdout): + self.output.debug(msg, file) + + +class BaseImageClass(BaseLoraxClass): + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self): + BaseLoraxClass.__init__(self) + self.srctree, self.dsttree = None, None + + def parse_template(self, template_file, variables={}): + template = ltmpl.Template() + + for lineno, line in template.parse(template_file, variables): + fields = shlex.split(line) + func, args = fields[0], fields[1:] + + func = getattr(self, func, None) + + if not func: + err = "{0}:{1}: invalid command: {2}" + err = err.format(template_file, lineno, line) + self.perror(err) + else: + try: + msg = "{0}({1})".format(func.__name__, ", ".join(args)) + self.pdebug(msg) + func(*args) + except TypeError: + err = "{0}:{1}: invalid command syntax: {2}" + err = err.format(template_file, lineno, line) + self.perror(err) + + def makedirs(self, *dirs): + for dir in dirs: + dir = os.path.join(self.dsttree, dir) + makedirs_(dir) + + def remove(self, *fnames): + for fname in fnames: + fname = os.path.join(self.dsttree, fname) + remove_(fname) + + def symlink(self, link_target, link_name): + link_name = os.path.join(self.dsttree, link_name) + symlink_(link_target, link_name) + + def touch(self, fname): + fname = os.path.join(self.dsttree, fname) + touch_(fname) + + def chown(self): + # TODO + raise NotImplementedError + + def chmod(self): + # TODO + raise NotImplementedError + + def replace(self): + # TODO + raise NotImplementedError + + def copy(self, fname, target=None): + dstdir = os.path.dirname(fname) + if target: + dstdir = target + + if dstdir: + makedirs_(os.path.join(self.dsttree, dstdir)) + + dcopy_(fname, dstdir, self.srctree, self.dsttree) + + def rename(self, fname, target): + fname = os.path.join(self.dsttree, fname) + target = os.path.join(self.dsttree, target) + shutil.move(fname, target) + + def edit(self, fname, text): + fname = os.path.join(self.dsttree, fname) + with open(fname, "w") as f: + f.write(text) diff --git a/src/pylorax/config.py b/src/pylorax/config.py index c929c775..4c155ea9 100644 --- a/src/pylorax/config.py +++ b/src/pylorax/config.py @@ -1,6 +1,5 @@ # # config.py -# configuration classes for lorax # # Copyright (C) 2009 Red Hat, Inc. # @@ -20,244 +19,24 @@ # Red Hat Author(s): Martin Gracik # -import os - -import singleton +from decorators import singleton import output -class LoraxConfig(singleton.Singleton): +@singleton +class LoraxConfig(object): def __init__(self): - self.confdir = "/etc/lorax" - self.datadir = "/usr/share/lorax" - + # output settings self.colors = True self.encoding = "utf-8" self.debug = False - self.cleanup = False + + self.confdir = "/etc/lorax" + self.datadir = "/usr/share/lorax" + + self.ignore_errors = "/etc/lorax/ignore_errors" def __setattr__(self, attr, value): - output.Terminal.get().debug("[%s = %s]" % (attr, value)) - singleton.Singleton.__setattr__(self, attr, value) - - -class LoraxPaths(singleton.Singleton): - - def __init__(self): - self.datadir = LoraxConfig.get().datadir - self.installtree = LoraxConfig.get().installtree - - @property - def ANACONDA_PACKAGE(self): return "anaconda" - - @property - def INITRD_DATADIR(self): - return os.path.join(self.datadir, "initrd") - - @property - def INSTALLTREE_DATADIR(self): - return os.path.join(self.datadir, "installtree") - - @property - def OUTPUTDIR_DATADIR(self): - return os.path.join(self.datadir, "outputdir") - - @property - def BOOTDIR(self): - return os.path.join(self.installtree, "boot") - - @property - def BOOTDIR_IA64(self): - return os.path.join(self.BOOTDIR, "efi", "EFI", "redhat") - - @property - def ANACONDA_RUNTIME(self): - return os.path.join(self.installtree, "usr", "lib", "anaconda-runtime") - - @property - def ANACONDA_BOOT(self): - return os.path.join(self.ANACONDA_RUNTIME, "boot") - - @property - def SYSLINUXDIR(self): - return os.path.join(self.installtree, "usr", "share", "syslinux") - - @property - def ISOLINUXBIN(self): - return os.path.join(self.SYSLINUXDIR, "isolinux.bin") - - @property - def SYSLINUXCFG(self): - return os.path.join(self.ANACONDA_BOOT, "syslinux.cfg") - - @property - def GRUBCONF(self): - return os.path.join(self.ANACONDA_BOOT, "grub.conf") - - @property - def GRUBEFI(self): - return os.path.join(self.BOOTDIR, "efi", "EFI", "redhat", "grub.efi") - - @property - def VESASPLASH(self): - return os.path.join(self.ANACONDA_RUNTIME, "syslinux-vesa-splash.jpg") - - @property - def VESAMENU(self): - return os.path.join(self.SYSLINUXDIR, "vesamenu.c32") - - @property - def SPLASHTOOLS(self): - return os.path.join(self.ANACONDA_RUNTIME, "splashtools.sh") - - @property - def SPLASHLSS(self): - return os.path.join(self.ANACONDA_BOOT, "splash.lss") - - @property - def SYSLINUXSPLASH(self): - return os.path.join(self.ANACONDA_BOOT, "syslinux-splash.jpg") - - @property - def SPLASHXPM(self): - return os.path.join(self.BOOTDIR, "grub", "splash.xpm.gz") - - @property - def MODULES_DIR(self): - return os.path.join(self.installtree, "lib", "modules", - LoraxConfig.get().kernelver) - - @property - def MODULES_DEP(self): - return os.path.join(self.MODULES_DIR, "modules.dep") - - @property - def MODINFO(self): return "/sbin/modinfo" - - @property - def MODLIST(self): - return os.path.join(self.ANACONDA_RUNTIME, "modlist") - - @property - def DEPMOD(self): return "/sbin/depmod" - - @property - def GETKEYMAPS(self): - return os.path.join(self.ANACONDA_RUNTIME, "getkeymaps") - - @property - def LOCALEDEF(self): return "/usr/bin/localedef" - - @property - def GENINITRDSZ(self): - return os.path.join(self.ANACONDA_RUNTIME, "geninitrdsz") - - @property - def REDHAT_EXEC(self): - return os.path.join(self.ANACONDA_BOOT, "redhat.exec") - - @property - def GENERIC_PRM(self): - return os.path.join(self.ANACONDA_BOOT, "generic.prm") - - @property - def MKS390CD(self): - return os.path.join(self.ANACONDA_RUNTIME, "mk-s390-cdboot") - - @property - def MKSQUASHFS(self): return "/sbin/mksquashfs" - - @property - def MKCRAMFS(self): return "/sbin/mkfs.cramfs" - - @property - def MKISOFS(self): return "/usr/bin/mkisofs" - - @property - def MKDOSFS(self): return "/sbin/mkdosfs" - - @property - def ISOHYBRID(self): return "/usr/bin/isohybrid" - - @property - def SYSTEM_MAP(self): - return os.path.join(self.BOOTDIR, - "System.map-%s" % LoraxConfig.get().kernelver) - - @property - def KEYMAPS_OVERRIDE(self): - return os.path.join(self.ANACONDA_RUNTIME, - "keymaps-override-%s" % LoraxConfig.get().arch) - - @property - def LANGTABLE(self): - return os.path.join(self.installtree, "usr", "lib", "anaconda", - "lang-table") - - @property - def LOCALEPATH(self): - return os.path.join(self.installtree, "usr", "share", "locale") - - @property - def MANCONFIG(self): - return os.path.join(self.installtree, "etc", "man.config") - - @property - def FEDORAKMODCONF(self): - return os.path.join(self.installtree, "etc", "yum", "pluginconf.d", - "fedorakmod.conf") - - @property - def MKZIMAGE(self): - return os.path.join(self.installtree, "usr", "bin", "mkzimage") - - @property - def ZIMAGE_STUB(self): - return os.path.join(self.installtree, "usr", "share", "ppc64-utils", - "zImage.stub") - - @property - def ZIMAGE_LDS(self): - return os.path.join(self.installtree, "usr", "share", "ppc64-utils", - "zImage.lds") - - @property - def WRAPPER(self): - return os.path.join(self.installtree, "usr", "sbin", "wrapper") - - @property - def WRAPPER_A_DIR(self): - return os.path.join(self.installtree, "usr", "lib", "kernel-wrapper") - - @property - def WRAPPER_A(self): - return os.path.join(self.WRAPPER_A_DIR, "wrapper.a") - - @property - def BOOTINFO(self): - return os.path.join(self.ANACONDA_BOOT, "bootinfo.txt") - - @property - def EFIKA_FORTH(self): - return os.path.join(self.ANACONDA_BOOT, "efika.forth") - - @property - def OFBOOT(self): - return os.path.join(self.ANACONDA_BOOT, "ofboot.b") - - @property - def YABOOT(self): - return os.path.join(self.installtree, "usr", "lib", "yaboot", "yaboot") - - @property - def ADD_NOTE(self): - return os.path.join(self.installtree, "usr", "lib", "yaboot", "addnote") - - @property - def BIARCH_YABOOT(self): - return os.path.join(self.ANACONDA_BOOT, "yaboot.conf.3264") - - @property - def IMPLANTISO(self): - return "/usr/bin/implantisomd5" + output.LoraxOutput().debug("[set {0}={1}]".format(attr, value)) + object.__setattr__(self, attr, value) diff --git a/src/pylorax/constants.py b/src/pylorax/constants.py new file mode 100644 index 00000000..e17de83b --- /dev/null +++ b/src/pylorax/constants.py @@ -0,0 +1,83 @@ +# +# constants.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +from os.path import join as pjoin + + +class LoraxConstants(object): + + ROOT_UID = 0 + + ANACONDA_PACKAGE = "anaconda" + ANACONDA_RUNTIME = "usr/lib/anaconda-runtime" + ANACONDA_BOOTDIR = "usr/lib/anaconda-runtime/boot" + ANACONDA_LOADERDIR = "usr/lib/anaconda-runtime/loader" + + BOOTDIR = "boot" + BOOTDIR_IA64 = "boot/efi/EFI/redhat" + + EFIDIR = "boot/efi/EFI/redhat" + SPLASH = "boot/grub/splash.xpm.gz" + + VESASPLASH = pjoin(ANACONDA_RUNTIME, "syslinux-vesa-splash.jpg") + SYSLINUXSPLASH = pjoin(ANACONDA_BOOTDIR, "syslinux-splash.jpg") + SPLASHTOOLS = pjoin(ANACONDA_RUNTIME, "splashtools.sh") + SPLASHLSS = pjoin(ANACONDA_BOOTDIR, "splash.lss") + VESAMENU = "usr/share/syslinux/vesamenu.c32" + + MODDIR = "lib/modules" + FWDIR = "lib/firmware" + + MODDEPFILE = "modules.dep" + MODULEINFO = "module-info" + MODLIST = "usr/lib/anaconda-runtime/modlist" + + LOCALEDIR = "usr/lib/locale" + LOCALES = "usr/share/locale" + LANGTABLE = "usr/lib/anaconda/lang-table" + + ISOLINUXBIN = "usr/share/syslinux/isolinux.bin" + SYSLINUXCFG = "usr/lib/anaconda-runtime/boot/syslinux.cfg" + + LDSOCONF = "etc/ld.so.conf" + MANCONF = "etc/man.config" + + +class LoraxCommands(dict): + + def __init__(self): + self["MODINFO"] = "/sbin/modinfo" + self["DEPMOD"] = "/sbin/depmod" + self["LOCALEDEF"] = "/usr/bin/localedef" + self["MKDOSFS"] = "/sbin/mkdosfs" + self["MKSQUASHFS"] = "/sbin/mksquashfs" + self["MKISOFS"] = "/usr/bin/mkisofs" + self["ISOHYBRID"] = "/usr/bin/isohybrid" + self["LOSETUP"] = "/sbin/losetup" + self["DMSETUP"] = "/sbin/dmsetup" + self["AWK"] = "/usr/bin/awk" + self["MOUNT"] = "/bin/mount" + self["UMOUNT"] = "/bin/umount" + self["LDCONFIG"] = "/sbin/ldconfig" + self["PARTED"] = "/sbin/parted" + + def __getattr__(self, attr): + return self[attr] diff --git a/src/pylorax/singleton.py b/src/pylorax/decorators.py similarity index 77% rename from src/pylorax/singleton.py rename to src/pylorax/decorators.py index 056d448e..7a0412be 100644 --- a/src/pylorax/singleton.py +++ b/src/pylorax/decorators.py @@ -1,6 +1,5 @@ # -# singleton.py -# singleton base class +# decorators.py # # Copyright (C) 2009 Red Hat, Inc. # @@ -20,13 +19,12 @@ # Red Hat Author(s): Martin Gracik # -class Singleton(object): +def singleton(cls): + instances = {} - __instance = None + def get_instance(): + if cls not in instances: + instances[cls] = cls() + return instances[cls] - @classmethod - def get(cls): - if cls.__instance is None: - cls.__instance = cls() - - return cls.__instance + return get_instance diff --git a/src/pylorax/efi.py b/src/pylorax/efi.py deleted file mode 100644 index 3e94ed8b..00000000 --- a/src/pylorax/efi.py +++ /dev/null @@ -1,236 +0,0 @@ -# -# efi.py -# class for creating efi images -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - - -import sys -import os -import shutil -import tempfile -import commands - -import config -import output -import utils - - -class EFI(object): - - def __init__(self): - self.conf = config.LoraxConfig.get() - self.paths = config.LoraxPaths.get() - self.output = output.Terminal.get() - - def create_efiboot(self, kernel=None, initrd=None, - kernelpath=None, initrdpath=None): - - # create the temporary efi tree directory - efitreedir = tempfile.mkdtemp(prefix="efitree.", dir=self.conf.tempdir) - - # copy kernel and initrd files to efi tree directory - if kernel and initrd: - shutil.copy2(kernel, os.path.join(efitreedir, "vmlinuz")) - shutil.copy2(initrd, os.path.join(efitreedir, "initrd.img")) - efikernelpath = "/EFI/BOOT/vmlinuz" - efiinitrdpath = "/EFI/BOOT/initrd.img" - else: - efikernelpath = kernelpath - efiinitrdpath = initrdpath - - # copy conf files to efi tree directory - utils.scopy(src_root=self.conf.anaconda_boot, src_path="*.conf", - dst_root=efitreedir, dst_path="") - - # edit the grub.conf file - grubconf = os.path.join(efitreedir, "grub.conf") - utils.replace(grubconf, "@PRODUCT@", self.conf.product) - utils.replace(grubconf, "@VERSION@", self.conf.version) - utils.replace(grubconf, "@KERNELPATH@", efikernelpath) - utils.replace(grubconf, "@INITRDPATH@", efiinitrdpath) - utils.replace(grubconf, "@SPLASHPATH@", "/EFI/BOOT/splash.xpm.gz") - - # copy grub.efi - shutil.copy2(self.paths.GRUBEFI, efitreedir) - - # the first generation mactel machines get the bootloader name wrong - if self.conf.efiarch == "IA32": - src = os.path.join(efitreedir, "grub.efi") - dst = os.path.join(efitreedir, "BOOT.efi") - shutil.copy2(src, dst) - - src = os.path.join(efitreedir, "grub.conf") - dst = os.path.join(efitreedir, "BOOT.conf") - shutil.copy2(src, dst) - - src = os.path.join(efitreedir, "grub.efi") - dst = os.path.join(efitreedir, "BOOT%s.efi" % self.conf.efiarch) - shutil.move(src, dst) - - src = os.path.join(efitreedir, "grub.conf") - dst = os.path.join(efitreedir, "BOOT%s.conf" % self.conf.efiarch) - shutil.move(src, dst) - - # copy splash.xpm.gz - shutil.copy2(self.paths.SPLASHXPM, efitreedir) - - efiboot = os.path.join(self.conf.tempdir, "efiboot.img") - if os.path.isfile(efiboot): - os.unlink(efiboot) - - # calculate the size of the efitree directory - sizeinbytes = 0 - for root, dirs, files in os.walk(efitreedir): - for file in files: - filepath = os.path.join(root, file) - sizeinbytes += os.path.getsize(filepath) - - # mkdosfs needs the size in blocks of 1024 bytes - size = int(round(sizeinbytes / 1024.0)) - - # add 100 blocks for the filesystem overhead - size += 100 - - cmd = "%s -n ANACONDA -C %s %s > /dev/null" % (self.paths.MKDOSFS, - efiboot, size) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return None - - # mount the efiboot image - efibootdir = tempfile.mkdtemp(prefix="efiboot.", dir=self.conf.tempdir) - - cmd = "mount -o loop,shortname=winnt,umask=0777 -t vfat %s %s" % \ - (efiboot, efibootdir) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return None - - # copy the files to the efiboot image - dstdir = os.path.join(efibootdir, "EFI", "BOOT") - utils.makedirs(dstdir) - - utils.scopy(src_root=efitreedir, src_path="*", - dst_root=dstdir, dst_path="") - - # unmount the efiboot image - cmd = "umount %s" % efibootdir - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - pass - - # copy the conf files and splash.xpm.gz to the output directory - if not kernel and not initrd: - utils.scopy(src_root=efitreedir, src_path="*.conf", - dst_root=self.conf.efibootdir, dst_path="") - shutil.copy2(self.paths.SPLASHXPM, self.conf.efibootdir) - - return efiboot - - def create_efidisk(self, efiboot): - efidisk = os.path.join(self.conf.tempdir, "efidisk.img") - if os.path.isfile(efidisk): - os.unlink(efidisk) - - partsize = os.path.getsize(efiboot) - disksize = 17408 + partsize + 17408 - disksize = disksize + (disksize % 512) - - with open(efidisk, "wb") as f: - f.truncate(disksize) - - cmd = "losetup -v -f %s | awk '{print $4}'" % efidisk - err, loop = commands.getstatusoutput(cmd) - if err: - self.output.error(loop) - os.unlink(efidisk) - return None - - cmd = 'dmsetup create efiboot --table "0 %s linear %s 0"' % \ - (disksize / 512, loop) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - self.remove_loop_dev(loop) - os.unlink(efidisk) - return None - - cmd = "parted --script /dev/mapper/efiboot " \ - "mklabel gpt unit b mkpart '\"EFI System Partition\"' " \ - "fat32 17408 %s set 1 boot on" % (partsize + 17408) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - self.remove_dm_dev("efiboot") - self.remove_loop_dev(loop) - os.unlink(efidisk) - return None - - with open(efiboot, "rb") as f_from: - with open("/dev/mapper/efibootp1", "wb") as f_to: - efidata = f_from.read(1024) - while efidata: - f_to.write(efidata) - efidata = f_from.read(1024) - - self.remove_dm_dev("efibootp1") - self.remove_dm_dev("efiboot") - self.remove_loop_dev(loop) - - return efidisk - - def remove_loop_dev(self, dev): - cmd = "losetup -d %s" % dev - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - def remove_dm_dev(self, dev): - cmd = "dmsetup remove /dev/mapper/%s" % dev - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - def create(self, kernel=None, initrd=None, - kernelpath=None, initrdpath=None): - - # create the efiboot image - efiboot = self.create_efiboot(kernel, initrd) - if efiboot is None: - self.output.critical("unable to create the efiboot image") - sys.exit(1) - - # create the efidisk image - efidisk = self.create_efidisk(efiboot) - if efidisk is None: - self.output.critical("unable to create the efidisk image") - sys.exit(1) - - # create the efiboot image again - efiboot = self.create_efiboot(kernelpath=kernelpath, - initrdpath=initrdpath) - if efiboot is None: - self.output.critical("unable to create the efiboot image") - sys.exit(1) - - return efiboot, efidisk diff --git a/src/pylorax/images.py b/src/pylorax/images.py new file mode 100644 index 00000000..f97d1a97 --- /dev/null +++ b/src/pylorax/images.py @@ -0,0 +1,865 @@ +# +# images.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +import sys +import os +import re +import shutil +import gzip +import commands +import tempfile +import math +import glob +import fnmatch + +from base import BaseImageClass +from sysutils import * + + +class InitRD(BaseImageClass): + + def __init__(self, installtree, modules, template_file, workdir="/tmp"): + BaseImageClass.__init__(self) + + self.installtree = installtree + self.modules = modules + self.template_file = template_file + self.workdir = workdir + + self.srctree = self.installtree.rootdir + self.dsttree = None + + def create(self): + for kernel in self.installtree.kernels: + msg = ":: creating the initrd image for {0}" + msg = msg.format(kernel.filename) + self.pinfo(msg) + + # prepare the working environment + self.pinfo("preparing the work environment") + self.prepare(kernel) + + # get the kernel modules + self.pinfo("getting the kernel modules") + self.get_kernel_modules(kernel, self.modules) + + # get keymaps + self.pinfo("creating keymaps") + ok = self.get_keymaps() + if not ok: + self.perror("could not create keymaps") + continue + + # create locales + self.pinfo("creating locales") + ok = self.create_locales() + if not ok: + self.perror("could not create locales") + continue + + # parse the template file + self.pinfo("parsing the template") + variables = {"buildarch": self.conf.buildarch, + "basearch": self.conf.basearch, + "libdir": self.conf.libdir} + self.parse_template(self.template_file, variables) + + # create the initrd file + self.pinfo("compressing the initrd image file") + initrdfile = self.compress(kernel) + if not initrdfile: + self.perror("could not create the initrd image file") + continue + + yield (kernel, initrdfile) + + def prepare(self, kernel): + # create the initrd working directory + dir = os.path.join(self.workdir, "initrd-{0}".format(kernel.version)) + if os.path.isdir(dir): + shutil.rmtree(dir) + os.mkdir(dir) + + # set the destination tree + self.dsttree = dir + + # copy the buildstamp + shutil.copy2(self.conf.buildstamp, self.dsttree) + + # create the .profile + profile = os.path.join(self.dsttree, ".profile") + text = """PS1="[anaconda \u@\h \W]\\$ " +PATH=/bin:/usr/bin:/usr/sbin:/mnt/sysimage/sbin:/mnt/sysimage/usr/sbin:/mnt/sysimage/bin:/mnt/sysimage/usr/bin +export PS1 PATH + +""" + + with open(profile, "w") as f: + f.write(text) + + # XXX + def get_kernel_modules(self, kernel, modset): + moddir = os.path.join(self.const.MODDIR, kernel.version) + src_moddir = os.path.join(self.srctree, moddir) + dst_moddir = os.path.join(self.dsttree, moddir) + + # copy all modules to the initrd tree + os.makedirs(os.path.dirname(dst_moddir)) + shutil.copytree(src_moddir, dst_moddir) + + # expand modules + modules = set() + pattern = re.compile(r"\.ko$") + for name in modset: + if name.startswith("="): + group = name[1:] + if group in ("scsi", "ata"): + p = os.path.join(src_moddir, "modules.block") + elif group == "net": + p = os.path.join(src_moddir, "modules.networking") + else: + p = os.path.join(src_moddir, "modules.{0}".format(group)) + + if os.path.isfile(p): + with open(p, "r") as f: + for line in f: + module = pattern.sub("", line.strip()) + modules.add(module) + else: + modules.add(name) + + # resolve modules dependencies + moddep = os.path.join(src_moddir, self.const.MODDEPFILE) + with open(moddep, "r") as f: + lines = map(lambda line: line.strip(), f.readlines()) + + modpattern = re.compile(r"^.*/(?P.*)\.ko:(?P.*)$") + deppattern = re.compile(r"^.*/(?P.*)\.ko$") + unresolved = True + + while unresolved: + unresolved = False + for line in lines: + m = modpattern.match(line) + modname = m.group("name") + if modname in modules: + # add the dependencies + for dep in m.group("deps").split(): + m = deppattern.match(dep) + depname = m.group("name") + if depname not in modules: + unresolved = True + modules.add(depname) + + # remove not needed modules + for root, dirs, files in os.walk(dst_moddir): + for file in files: + path = os.path.join(root, file) + name, ext = os.path.splitext(file) + + if ext == ".ko": + if name not in modules: + os.unlink(path) + else: + # get the required firmware + cmd = "{0.MODINFO} -F firmware {1}" + cmd = cmd.format(self.cmd, path) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + continue + + for fw in stdout.split(): + fw = os.path.join(self.const.FWDIR, fw) + src = os.path.join(self.srctree, fw) + if not os.path.exists(src): + msg = "missing firmware {0}".format(fw) + self.pwarning(msg) + continue + + # copy the firmware + dst = os.path.join(self.dsttree, fw) + dir = os.path.dirname(dst) + makedirs_(dir) + shutil.copy2(src, dst) + + # copy additional firmware + fw = [("ipw2100", "ipw2100*"), + ("ipw2200", "ipw2200*"), + ("iwl3945", "iwlwifi-3945*"), + ("iwl4965", "iwlwifi-4965*"), + ("atmel", "atmel_*.bin"), + ("zd1211rw", "zd1211"), + ("qla2xxx", "ql*")] + + for module, fname in fw: + if module in modules: + scopy_(src_root=self.srctree, + src_path=os.path.join(self.const.FWDIR, fname), + dst_root=self.dsttree, + dst_path=self.const.FWDIR) + + # XXX + # remove empty directories + #empty_dirs = set() + #for root, dirs, files in os.walk(dst_moddir, topdown=False): + # if not dirs and not files: + # shutil.rmtree(root) + + # get the modules paths + modpaths = {} + for root, dirs, files in os.walk(dst_moddir): + for file in files: + modpaths[file] = os.path.join(root, file) + + # create the modules list + modlist = {} + for modtype, fname in {"scsi": "modules.block", + "eth": "modules.networking"}.items(): + + modlist[modtype] = {} + + fname = os.path.join(dst_moddir, fname) + with open(fname, "r") as f: + for line in f: + line = line.strip() + if not line: + continue + + modname, ext = os.path.splitext(line) + if (line not in modpaths or + modname in ("floppy", "libiscsi", "scsi_mod")): + continue + + cmd = "{0.MODINFO} -F description {1}" + cmd = cmd.format(self.cmd, modpaths[line]) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + desc = "" + else: + desc = stdout.split("\n")[0] + desc = desc.strip()[:65] + + if not desc: + desc = "{0} driver".format(modname) + + info = '{0}\n\t{1}\n\t"{2}"\n' + info = info.format(modname, modtype, desc) + modlist[modtype][modname] = info + + # write the source module-info + moduleinfo = os.path.join(self.workdir, self.const.MODULEINFO) + with open(moduleinfo, "w") as f: + f.write("Version 0\n") + for modtype, modules in modlist.items(): + for modname in sorted(modules.keys()): + f.write(modlist[modtype][modname]) + + # create the final module-info + dst = os.path.join(os.path.dirname(dst_moddir), self.const.MODULEINFO) + modlist = os.path.join(self.srctree, self.const.MODLIST) + cmd = "{0} --modinfo-file {1} --ignore-missing --modinfo {2} > {3}" + cmd = cmd.format(modlist, moduleinfo, " ".join(list(modules)), dst) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + + # remove the source module-info + #os.unlink(moduleinfo) + + # run depmod + systemmap = os.path.join(self.srctree, self.const.BOOTDIR, + "System.map-{0}".format(kernel.version)) + + cmd = "{0.DEPMOD} -a -F {1} -b {2} {3}" + cmd = cmd.format(self.cmd, systemmap, + self.srctree, kernel.version) + + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + + # compress modules + for root, dirs, files in os.walk(dst_moddir): + for file in filter(lambda f: f.endswith(".ko"), files): + path = os.path.join(root, file) + with open(path, "rb") as f: + data = f.read() + + gzipped = gzip.open("{0}.gz".format(path), "wb") + gzipped.write(data) + gzipped.close() + os.unlink(path) + + def get_keymaps(self): + override = "keymaps-override-{0}".format(self.conf.basearch) + override = os.path.join(self.srctree, self.const.ANACONDA_RUNTIME, + override) + + getkeymaps = os.path.join(self.srctree, self.const.ANACONDA_RUNTIME, + "getkeymaps") + + dst = os.path.join(self.dsttree, "etc/keymaps.gz") + dir = os.path.dirname(dst) + if not os.path.isdir(dir): + os.makedirs(dir) + + if os.path.isfile(override): + self.pinfo("using keymaps override") + shutil.copy2(override, dst) + else: + cmd = "{0} {1} {2} {3}" + cmd = cmd.format(getkeymaps, self.conf.basearch, dst, self.srctree) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return False + + return True + + def create_locales(self): + localedir = os.path.join(self.dsttree, self.const.LOCALEDIR) + os.makedirs(localedir) + + cmd = "{0.LOCALEDEF} -c -i en_US -f UTF-8 --prefix {1} en_US" + cmd = cmd.format(self.cmd, self.dsttree) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return False + + return True + + def compress(self, kernel): + filename = "initrd-{0}.img".format(kernel.version) + filepath = os.path.join(self.workdir, filename) + + # create the cpio archive + cpioarchive = "{0}.cpio".format(filepath) + + cwd = os.getcwd() + os.chdir(self.dsttree) + + cmd = "find . | cpio --quiet -c -o > {0}".format(cpioarchive) + err, stdout = commands.getstatusoutput(cmd) + + os.chdir(cwd) + + if err: + self.perror(stdout) + return None + + # create the gzip archive + with open(cpioarchive, "rb") as f: + cpiodata = f.read() + + gzipped = gzip.open(filepath, "wb") + gzipped.write(cpiodata) + gzipped.close() + + # remove the cpio archive + os.unlink(cpioarchive) + + # remove the initrd tree + #shutil.rmtree(self.dsttree) + + return filepath + + +class EFI(BaseImageClass): + + def __init__(self, installtree, kernel, initrd, product, version, + workdir="/tmp"): + + BaseImageClass.__init__(self) + + self.srctree = installtree.rootdir + self.dsttree = None + + self.kernel = kernel + self.initrd = initrd + self.product = product + self.version = version + + self.workdir = workdir + + def create(self): + msg = ":: creating the efi images for {0}" + self.pinfo(msg.format(self.kernel.filename)) + + # create efiboot image with kernel + self.pinfo("creating efiboot image with kernel") + efiboot_kernel = self.create_efiboot(with_kernel=True) + if efiboot_kernel is None: + self.perror("unable to create the efiboot image") + return None, None + + # create the efidisk image + self.pinfo("creating efidisk image") + efidisk = self.create_efidisk(efiboot_kernel) + if efidisk is None: + self.perror("unable to create the efidisk image") + return None, None + + # remove the efiboot image with kernel + os.unlink(efiboot_kernel) + + # create efiboot image without kernel + self.pinfo("creating efiboot image without kernel") + efiboot_nokernel = self.create_efiboot(with_kernel=False) + if efiboot_nokernel is None: + self.perror("unable to create the efiboot image") + return None, None + + return efiboot_nokernel, efidisk + + def create_efiboot(self, with_kernel=True): + # create the efi tree directory + efitree = tempfile.mkdtemp(prefix="efitree.", dir=self.workdir) + + efikernelpath = "/images/pxeboot/vmlinuz" + efiinitrdpath = "/images/pxeboot/initrd.img" + efisplashpath = "/EFI/BOOT/splash.xpm.gz" + + # copy kernel and initrd files to efi tree directory + if with_kernel: + kpath = self.kernel.path + shutil.copy2(kpath, os.path.join(efitree, "vmlinuz")) + shutil.copy2(self.initrd, os.path.join(efitree, "initrd.img")) + efikernelpath = "/EFI/BOOT/vmlinuz" + efiinitrdpath = "/EFI/BOOT/initrd.img" + + # copy conf files to efi tree directory + srcdir = os.path.join(self.srctree, self.const.ANACONDA_BOOTDIR) + scopy_(src_root=srcdir, src_path="*.conf", + dst_root=efitree, dst_path="") + + # edit the grub.conf file + grubconf = os.path.join(efitree, "grub.conf") + replace_(grubconf, "@PRODUCT@", self.product) + replace_(grubconf, "@VERSION@", self.version) + replace_(grubconf, "@KERNELPATH@", efikernelpath) + replace_(grubconf, "@INITRDPATH@", efiinitrdpath) + replace_(grubconf, "@SPLASHPATH@", efisplashpath) + + if self.conf.efiarch == "IA32": + shutil.copy2(grubconf, os.path.join(efitree, "BOOT.conf")) + + dst = os.path.join(efitree, "BOOT{0}.conf".format(self.conf.efiarch)) + shutil.move(grubconf, dst) + + # copy grub.efi + grubefi = os.path.join(self.srctree, self.const.EFIDIR, "grub.efi") + + if self.conf.efiarch == "IA32": + shutil.copy2(grubefi, os.path.join(efitree, "BOOT.efi")) + + dst = os.path.join(efitree, "BOOT{0}.efi".format(self.conf.efiarch)) + shutil.copy2(grubefi, dst) + + # copy splash.xpm.gz + splash = os.path.join(self.srctree, self.const.SPLASH) + shutil.copy2(splash, efitree) + + efiboot = os.path.join(self.workdir, "efiboot.img") + if os.path.isfile(efiboot): + os.unlink(efiboot) + + # calculate the size of the efi tree directory + fsoverhead = 100 * 1024 + sizeinbytes = fsoverhead + for root, dirs, files in os.walk(efitree): + for file in files: + filepath = os.path.join(root, file) + sizeinbytes += os.path.getsize(filepath) + + # mkdosfs needs the size in blocks of 1024 bytes + size = int(math.ceil(sizeinbytes / 1024.0)) + + cmd = "{0.MKDOSFS} -n ANACONDA -C {1} {2} > /dev/null" + cmd = cmd.format(self.cmd, efiboot, size) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return None + + # mount the efiboot image + efibootdir = tempfile.mkdtemp(prefix="efiboot.", dir=self.workdir) + + cmd = "mount -o loop,shortname=winnt,umask=0777 -t vfat {0} {1}" + cmd = cmd.format(efiboot, efibootdir) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return None + + # copy the files to the efiboot image + dstdir = os.path.join(efibootdir, "EFI/BOOT") + os.makedirs(dstdir) + + scopy_(src_root=efitree, src_path="*", + dst_root=dstdir, dst_path="") + + # unmount the efiboot image + cmd = "umount {0}".format(efibootdir) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + # remove the working directories + shutil.rmtree(efibootdir) + #shutil.rmtree(efitree) + + # XXX copy the conf files and splash.xpm.gz to the output directory + if not with_kernel: + scopy_(src_root=efitree, src_path="*.conf", + dst_root=self.conf.efidir, dst_path="") + shutil.copy2(splash, self.conf.efidir) + + return efiboot + + def create_efidisk(self, efiboot): + efidisk = os.path.join(self.workdir, "efidisk.img") + if os.path.isfile(efidisk): + os.unlink(efidisk) + + partsize = os.path.getsize(efiboot) + disksize = 17408 + partsize + 17408 + disksize = disksize + (disksize % 512) + + # create the efidisk file + with open(efidisk, "wb") as f: + f.truncate(disksize) + + # create the loop device + cmd = "{0.LOSETUP} -v -f {1} | {0.AWK} '{{print $4}}'" + cmd = cmd.format(self.cmd, efidisk) + err, loop = commands.getstatusoutput(cmd) + if err: + self.perror(loop) + os.unlink(efidisk) + return None + + # create the dm device + dmdev = "efiboot" + cmd = '{0.DMSETUP} create {1} --table "0 {2} linear {3} 0"' + cmd = cmd.format(self.cmd, dmdev, disksize / 512, loop) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(output) + self.remove_loop_dev(loop) + os.unlink(efidisk) + return None + + cmd = ("{0.PARTED} --script /dev/mapper/{1} " + "mklabel gpt unit b mkpart '\"EFI System Partition\"' " + "fat32 17408 {2} set 1 boot on") + cmd = cmd.format(self.cmd, dmdev, partsize + 17408) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + self.remove_dm_dev(dmdev) + self.remove_loop_dev(loop) + os.unlink(efidisk) + return None + + with open(efiboot, "rb") as f_from: + with open("/dev/mapper/{0}p1".format(dmdev), "wb") as f_to: + efidata = f_from.read(1024) + while efidata: + f_to.write(efidata) + efidata = f_from.read(1024) + + self.remove_dm_dev("{0}p1".format(dmdev)) + self.remove_dm_dev(dmdev) + self.remove_loop_dev(loop) + + return efidisk + + def remove_loop_dev(self, dev): + cmd = "{0.LOSETUP} -d {1}".format(self.cmd, dev) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + def remove_dm_dev(self, dev): + cmd = "{0.DMSETUP} remove /dev/mapper/{1}".format(self.cmd, dev) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + +class Install(BaseImageClass): + + def __init__(self, installtree, template_file, workdir="/tmp"): + BaseImageClass.__init__(self) + + self.srctree = installtree.rootdir + self.dsttree = installtree.rootdir + self.template_file = template_file + self.workdir = workdir + + def create(self, type="squashfs"): + self.pinfo(":: creating the install image") + + # copy the .buildstamp + shutil.copy2(self.conf.buildstamp, self.srctree) + + self.copy_stubs() + self.copy_bootloaders() + self.rename_repos() + self.move_anaconda_files() + self.create_modules_symlinks() + self.fix_man_pages() + self.remove_locales() + self.remove_unnecessary_files() + self.move_bins() + + # parse the template file + self.pinfo("parsing the template") + variables = {"buildarch": self.conf.buildarch, + "basearch": self.conf.basearch, + "libdir": self.conf.libdir} + self.parse_template(self.template_file, variables) + + installimg = os.path.join(self.workdir, "install.img") + if os.path.isfile(installimg): + os.unlink(installimg) + + if type == "squashfs": + self.pinfo("using squash filesystem") + cmd = "{0.MKSQUASHFS} {1} {2} -all-root -no-fragments -no-progress" + cmd = cmd.format(self.cmd, self.srctree, installimg) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return None + elif type == "cramfs": + # TODO + raise NotImplementedError + elif type == "ext2": + # TODO + raise NotImplementedError + + return installimg + + def copy_stubs(self): + stubs = ("raidstart", "raidstop", "losetup", "list-harddrives", + "loadkeys", "mknod", "syslogd") + + for stub in map(lambda s: "{0}-stub".format(s), stubs): + src = os.path.join(self.srctree, "usr/lib/anaconda", stub) + dst = os.path.join(self.srctree, "usr/bin", stub) + shutil.copy2(src, dst) + + def copy_bootloaders(self): + srcdir = os.path.join(self.srctree, self.const.BOOTDIR) + dstdir = os.path.join(self.srctree, self.const.ANACONDA_BOOTDIR) + + if self.conf.buildarch in ("i386", "i586", "i686", "x86_64"): + for f in glob.iglob(os.path.join(srcdir, "memtest*")): + shutil.copy2(f, dstdir) + + elif self.conf.buildarch in ("ppc", "ppc64"): + f = os.path.join(srcdir, "efika.forth") + shutil.copy2(f, dstdir) + + elif self.conf.buildarch in ("sparc", "sparc64"): + for f in glob.iglob(os.path.join(srcdir, "*.b")): + shutil.copy2(f, dstdir) + + elif self.conf.buildarch == "ia64": + srcdir = os.path.join(self.srctree, self.const.BOOTDIR_IA64) + for f in glob.iglob(os.path.join(srcdir, "*")): + shutil.copy2(dstdir) + + def rename_repos(self): + src = os.path.join(self.srctree, "etc/yum.repos.d") + dst = os.path.join(self.srctree, "etc/anaconda.repos.d") + shutil.move(src, dst) + + def move_anaconda_files(self): + # move anaconda executable + src = os.path.join(self.srctree, "usr/sbin/anaconda") + dst = os.path.join(self.srctree, "usr/bin/anaconda") + shutil.move(src, dst) + + # move anaconda libraries + srcdir = os.path.join(self.srctree, self.const.ANACONDA_RUNTIME) + dstdir = os.path.join(self.srctree, "usr", self.conf.libdir) + for f in glob.iglob(os.path.join(srcdir, "lib*")): + shutil.move(f, dstdir) + + def create_modules_symlinks(self): + mkdir_(os.path.join(self.srctree, "modules")) + mkdir_(os.path.join(self.srctree, "firmware")) + remove_(os.path.join(self.srctree, self.const.MODDIR)) + remove_(os.path.join(self.srctree, self.const.FWDIR)) + os.symlink("/modules", os.path.join(self.srctree, self.const.MODDIR)) + os.symlink("/firmware", os.path.join(self.srctree, self.const.FWDIR)) + + def fix_man_pages(self): + # fix up some links for man page related stuff + for file in ("nroff", "groff", "iconv", "geqn", "gtbl", "gpic", + "grefer"): + + target = os.path.join("/mnt/sysimage/usr/bin", file) + name = os.path.join(self.srctree, "usr/bin", file) + if not os.path.isfile(name): + os.symlink(target, name) + + # fix /etc/man.config to point into /mnt/sysimage + manconf = os.path.join(self.srctree, self.const.MANCONF) + replace_(manconf, r"^MANPATH\s+(\S+)", "MANPATH\t/mnt/sysimage\g<1>") + replace_(manconf, r"^MANPATH_MAP\s+(\S+)\s+(\S+)", + "MANPATH_MAP\t\g<1>\t/mnt/sysimage\g<2>") + + def remove_locales(self): + langtable = os.path.join(self.srctree, self.const.LANGTABLE) + localepath = os.path.join(self.srctree, self.const.LOCALES) + + if os.path.isfile(langtable): + keep = set() + + with open(langtable, "r") as f: + lines = f.readlines() + + for line in lines: + line = line.strip() + if not line or line.startswith("#"): + continue + + fields = line.split("\t") + + dir = os.path.join(localepath, fields[1]) + if os.path.isdir(dir): + keep.add(fields[1]) + + locale = fields[3].split(".")[0] + dir = os.path.join(localepath, locale) + if os.path.isdir(dir): + keep.add(locale) + + for locale in os.listdir(localepath): + if locale not in keep: + path = os.path.join(localepath, locale) + remove_(path) + + def remove_unnecessary_files(self): + for root, dirs, files in os.walk(self.srctree): + for file in files: + path = os.path.join(root, file) + + if (fnmatch.fnmatch(path, "*.a") and + not path.count("kernel-wrapper/wrapper.a")): + os.unlink(path) + + if (fnmatch.fnmatch(path, "lib*.la") and + not path.count("gtk-2.0")): + os.unlink(path) + + if fnmatch.fnmatch(path, "*.py"): + pyo, pyc = path + "o", path + "c" + if os.path.isfile(pyo): + os.unlink(pyo) + if os.path.isfile(pyc): + os.unlink(pyc) + + os.symlink("/dev/null", pyc) + + # remove libunicode-lite + remove_(os.path.join(self.srctree, "usr", self.conf.libdir, + "libunicode-lite*")) + + def move_bins(self): + # move bin to usr/bin + scopy_(src_root=self.srctree, src_path="bin/*", + dst_root=self.srctree, dst_path="usr/bin") + remove_(os.path.join(self.srctree, "bin")) + + # move sbin to /usr/sbin + scopy_(src_root=self.srctree, src_path="sbin/*", + dst_root=self.srctree, dst_path="usr/sbin") + remove_(os.path.join(self.srctree, "sbin")) + + # fix broken links + brokenlinks = [] + for dir in ("bin", "sbin"): + dir = os.path.join(self.srctree, "usr", dir) + for root, dnames, fnames in os.walk(dir): + for fname in fnames: + fname = os.path.join(root, fname) + if os.path.islink(fname) and not os.path.exists(fname): + brokenlinks.append(fname) + + pattern = re.compile(r"^\.\./\.\./(bin|sbin)/(.*)$") + for link in brokenlinks: + target = os.readlink(link) + newtarget = pattern.sub(r"../\g<1>/\g<2>", target) + if newtarget != target: + os.unlink(link) + os.symlink(newtarget, link) + + +class Boot(BaseImageClass): + + def __init__(self, product, workdir="/tmp"): + BaseImageClass.__init__(self) + self.product = product + self.workdir = workdir + + self.efiboot = os.path.join(self.conf.imgdir, "efiboot.img") + + def create(self): + self.pinfo(":: creating the boot iso image") + bootiso = os.path.join(self.workdir, "boot.iso") + if os.path.isfile(bootiso): + os.unlink(bootiso) + + if os.path.isfile(self.efiboot): + self.pinfo("creating efi capable boot iso") + efiargs = "-eltorito-alt-boot -e images/efiboot.img -no-emul-boot" + efigraft = "EFI/BOOT={0}".format(self.conf.efidir) + else: + efiargs = "" + efigraft = "" + + cmd = ("{0.MKISOFS} -U -A {1} -V {1} -volset {1} -J -joliet-long" + " -r -v -T -o {2} -b isolinux/isolinux.bin -c isolinux/boot.cat" + " -no-emul-boot -boot-load-size 4 -boot-info-table" + " {3} -graft-points isolinux={4} images={5} {6}") + cmd = cmd.format(self.cmd, self.product, bootiso, efiargs, + self.conf.isodir, self.conf.imgdir, efigraft) + + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return None + + if os.path.isfile(self.cmd.ISOHYBRID): + self.pinfo("creating hybrid boot iso") + cmd = "{0.ISOHYBRID} {1}".format(self.cmd, bootiso) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + return bootiso diff --git a/src/pylorax/install.py b/src/pylorax/install.py deleted file mode 100644 index d429d765..00000000 --- a/src/pylorax/install.py +++ /dev/null @@ -1,502 +0,0 @@ -# -# install.py -# class for creating install images -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): David Cantrell -# Martin Gracik -# - -import os -import shutil -import glob -import fnmatch -import re -import commands -import pwd -import grp -import stat - -import config -import output -import lcs -import utils - - -class InstallImage(object): - - def __init__(self): - self.conf = config.LoraxConfig.get() - self.paths = config.LoraxPaths.get() - self.output = output.Terminal.get() - - self.actions = self.get_actions_from_template() - - def get_actions_from_template(self): - variables = { "instroot" : self.conf.installtree } - - if self.conf.scrubs_template is not None: - template = lcs.TemplateParser(variables) - return template.get_actions(self.conf.scrubs_template) - - return [] - - def move_shared_files(self): - dirs = [os.path.join(self.paths.INSTALLTREE_DATADIR, "noarch"), - os.path.join(self.paths.INSTALLTREE_DATADIR, self.conf.arch)] - - self.output.info(":: copying the custom install tree files") - for dir in [dir for dir in dirs if os.path.isdir(dir)]: - utils.scopy(src_root=dir, src_path="*", - dst_root=self.conf.installtree, dst_path="") - - def process_actions(self): - for action in self.actions: - action.execute() - - # XXX why do we need this? - def copy_stubs(self): - for file in ("raidstart", "raidstop", "losetup", "list-harddrives", - "loadkeys", "mknod", "syslogd"): - - src = os.path.join(self.conf.installtree, "usr", "lib", "anaconda", - "%s-stub" % file) - dst = os.path.join(self.conf.installtree, "usr", "bin", file) - shutil.copy2(src, dst) - - # XXX i cannot find this in the repos for f12 - def configure_fedorakmod(self): - if os.path.isfile(self.paths.FEDORAKMODCONF): - utils.replace(self.paths.FEDORAKMODCONF, - r"installforallkernels = 0", - r"installforallkernels = 1") - - # XXX why do we need this? - def copy_bootloaders(self): - if self.conf.arch in ("i386", "i586", "x86_64"): - for f in glob.glob(os.path.join(self.paths.BOOTDIR, "memtest*")): - shutil.copy2(f, self.paths.ANACONDA_BOOT) - - elif self.conf.arch == "sparc": - for f in glob.glob(os.path.join(self.paths.BOOTDIR, "*.b")): - shutil.copy2(f, self.paths.ANACONDA_BOOT) - - elif self.conf.arch in ("ppc", "ppc64"): - f = os.path.join(self.paths.BOOTDIR, "efika.forth") - shutil.copy2(f, self.paths.ANACONDA_BOOT) - - # XXX alpha stuff should not be needed anymore - #elif self.conf.arch == "alpha": - # f = os.path.join(self.paths.BOOTDIR, "bootlx") - # shutil.copy2(f, self.paths.ANACONDA_BOOT) - - elif self.conf.arch == "ia64": - utils.scopy(src_root=self.paths.BOOTDIR_IA64, src_path="*", - dst_root=self.paths.ANACONDA_BOOT, dst_dir="") - - # XXX why do we need this? - def move_repos(self): - src = os.path.join(self.conf.installtree, "etc", "yum.repos.d") - dst = os.path.join(self.conf.installtree, "etc", "anaconda.repos.d") - shutil.move(src, dst) - - # XXX why do we need this? - def move_anaconda_files(self): - # move anaconda executable - src = os.path.join(self.conf.installtree, "usr", "sbin", "anaconda") - dst = os.path.join(self.conf.installtree, "usr", "bin", "anaconda") - shutil.move(src, dst) - - # move anaconda libraries - dstdir = os.path.join(self.conf.installtree, "usr", self.conf.libdir) - utils.scopy(src_root=self.paths.ANACONDA_RUNTIME, src_path="lib*", - dst_root=dstdir, dst_path="") - - utils.remove(os.path.join(self.paths.ANACONDA_RUNTIME, "lib*")) - - # XXX this saves 40 MB - def create_modules_symlinks(self): - utils.mkdir(os.path.join(self.conf.installtree, "modules")) - utils.mkdir(os.path.join(self.conf.installtree, "firmware")) - - utils.remove(os.path.join(self.conf.installtree, "lib", "modules")) - utils.remove(os.path.join(self.conf.installtree, "lib", "firmware")) - - utils.symlink("/modules", - os.path.join(self.conf.installtree, "lib", "modules")) - utils.symlink("/firmware", - os.path.join(self.conf.installtree, "lib", "firmware")) - - # XXX why do we need this? - def fix_man_pages(self): - # fix up some links for man page related stuff - for file in ("nroff", "groff", "iconv", "geqn", "gtbl", "gpic", - "grefer"): - - target = os.path.join("/mnt/sysimage/usr/bin", file) - name = os.path.join(self.conf.installtree, "usr", "bin", file) - - if not os.path.isfile(name): - utils.symlink(target, name) - - # fix /etc/man.config to point into /mnt/sysimage - utils.replace(self.paths.MANCONFIG, r"^MANPATH\s+(\S+)", - "MANPATH\t/mnt/sysimage\g<1>") - utils.replace(self.paths.MANCONFIG, r"^MANPATH_MAP\s+(\S+)\s+(\S+)", - "MANPATH_MAP\t\g<1>\t/mnt/sysimage\g<2>") - - # XXX this saves 2 MB - def remove_gtk_stuff(self): - pass -# # figure out the gtk+ theme to keep -# gtkrc = os.path.join(self.conf.installtree, "etc", "gtk-2.0", "gtkrc") -# -# gtk_theme_name = None -# gtk_engine = None -# gtk_icon_themes = [] -# -# if os.path.isfile(gtkrc): -# f = open(gtkrc, "r") -# lines = f.readlines() -# f.close() -# -# for line in lines: -# line = line.strip() -# if line.startswith("gtk-theme-name"): -# gtk_theme_name = line[line.find("=") + 1:] -# gtk_theme_name = gtk_theme_name.replace('"', "").strip() -# -# # find the engine for this theme -# gtkrc = os.path.join(self.conf.installtree, "usr", "share", -# "themes", gtk_theme_name, "gtk-2.0", "gtkrc") -# if os.path.isfile(gtkrc): -# f = open(gtkrc, "r") -# engine_lines = f.readlines() -# f.close() -# -# for engine_l in engine_lines: -# engine_l = engine_l.strip() -# if engine_l.find("engine") != -1: -# gtk_engine = engine_l[engine_l.find('"') + 1:] -# gtk_engine = gtk_engine.replace('"', "").strip() -# break -# -# elif line.startswith("gtk-icon-theme-name"): -# icon_theme = line[line.find("=") + 1:] -# icon_theme = icon_theme.replace('"', "").strip() -# gtk_icon_themes.append(icon_theme) -# -# # bring in all inherited themes -# while True: -# icon_theme_index = os.path.join(self.conf.installtree, -# "usr", "share", "icons", icon_theme, -# "index.theme") -# -# if os.path.isfile(icon_theme_index): -# inherits = False -# f = open(icon_theme_index, "r") -# icon_lines = f.readlines() -# f.close() -# -# for icon_l in icon_lines: -# icon_l = icon_l.strip() -# if icon_l.startswith("Inherits="): -# inherits = True -# icon_theme = icon_l[icon_l.find("=") + 1:] -# icon_theme = \ -# icon_theme.replace('"', "").strip() -# -# gtk_icon_themes.append(icon_theme) -# break -# -# if not inherits: -# break -# else: -# break -# -# # remove themes we don't need -# theme_path = os.path.join(self.conf.installtree, "usr", "share", -# "themes") -# -# if os.path.isdir(theme_path): -# for theme in filter(lambda theme: theme != gtk_theme_name, -# os.listdir(theme_path)): -# -# theme = os.path.join(theme_path, theme) -# shutil.rmtree(theme, ignore_errors=True) -# -# # remove icons we don't need -# icon_path = os.path.join(self.conf.installtree, "usr", "share", -# "icons") -# -# if os.path.isdir(icon_path): -# for icon in filter(lambda icon: icon not in gtk_icon_themes, -# os.listdir(icon_path)): -# -# icon = os.path.join(icon_path, icon) -# shutil.rmtree(icon, ignore_errors=True) -# -# # remove engines we don't need -# tmp_path = os.path.join(self.conf.installtree, "usr", -# self.conf.libdir, "gtk-2.0") -# -# if os.path.isdir(tmp_path): -# fnames = map(lambda fname: os.path.join(tmp_path, fname, -# "engines"), os.listdir(tmp_path)) -# -# dnames = filter(lambda fname: os.path.isdir(fname), fnames) -# for dir in dnames: -# engines = filter(lambda e: e.find(gtk_engine) == -1, -# os.listdir(dir)) -# for engine in engines: -# engine = os.path.join(dir, engine) -# os.unlink(engine) - - # XXX this saves 5 MB - def remove_locales(self): - if os.path.isfile(self.paths.LANGTABLE): - keep = set() - - with open(self.paths.LANGTABLE, "r") as f: - lines = f.readlines() - - for line in lines: - line = line.strip() - if not line or line.startswith("#"): - continue - - fields = line.split("\t") - - dir = os.path.join(self.paths.LOCALEPATH, fields[1]) - if os.path.isdir(dir): - keep.add(fields[1]) - - locale = fields[3].split(".")[0] - dir = os.path.join(self.paths.LOCALEPATH, locale) - if os.path.isdir(dir): - keep.add(locale) - - for locale in os.listdir(self.paths.LOCALEPATH): - if locale not in keep: - path = os.path.join(self.paths.LOCALEPATH, locale) - utils.remove(path) - - # XXX this saves 5 MB - def remove_unnecessary_files(self): - for root, dirs, files in os.walk(self.conf.installtree): - for file in files: - path = os.path.join(root, file) - - if fnmatch.fnmatch(path, "*.a"): - if path.find("kernel-wrapper/wrapper.a") == -1: - os.unlink(path) - - if fnmatch.fnmatch(path, "lib*.la"): - if path.find("usr/" + self.conf.libdir + "/gtk-2.0") == -1: - os.unlink(path) - - if fnmatch.fnmatch(path, "*.py"): - if os.path.isfile(path + "o"): - os.unlink(path + "o") - if os.path.isfile(path + "c"): - os.unlink(path + "c") - - utils.symlink("/dev/null", path + "c") - - # remove libunicode-lite - utils.remove(os.path.join(self.conf.installtree, "usr", - self.conf.libdir, "libunicode-lite*")) - - # XXX this saves 1 MB - def remove_python_stuff(self): - for fname in ("bsddb", "compiler", "curses", "distutils", "email", - "encodings", "hotshot", "idlelib", "test", - "doctest.py", "pydoc.py"): - - utils.remove(os.path.join(self.conf.installtree, "usr", - self.conf.libdir, "python?.?", fname)) - - # XXX the udev package should get fixed, - # but for now, we have to fix it ourselves, otherwise we get an error - def fix_udev_links(self): - # these links are broken by default (at least on i386) - for filename in ("udevcontrol", "udevsettle", "udevtrigger"): - filename = os.path.join(self.conf.installtree, "sbin", filename) - if os.path.islink(filename): - os.unlink(filename) - os.symlink("udevadm", filename) - - def move_bins(self): - # move bin to usr/bin - utils.scopy(src_root=self.conf.installtree, - src_path=os.path.join("bin", "*"), - dst_root=self.conf.installtree, - dst_path=os.path.join("usr", "bin")) - utils.remove(os.path.join(self.conf.installtree, "bin")) - - # move sbin to /usr/sbin - utils.scopy(src_root=self.conf.installtree, - src_path=os.path.join("sbin", "*"), - dst_root=self.conf.installtree, - dst_path=os.path.join("usr", "sbin")) - utils.remove(os.path.join(self.conf.installtree, "sbin")) - - # fix broken links - brokenlinks = [] - for dir in ("bin", "sbin"): - dir = os.path.join(self.conf.installtree, "usr", dir) - for root, dnames, fnames in os.walk(dir): - for fname in fnames: - fname = os.path.join(root, fname) - if os.path.islink(fname) and not os.path.exists(fname): - brokenlinks.append(fname) - - for link in brokenlinks: - target = os.readlink(link) - newtarget = re.sub(r"^\.\./\.\./(bin|sbin)/(.*)$", - r"../\g<1>/\g<2>", target) - - if newtarget != target: - os.unlink(link) - utils.symlink(newtarget, link) - - def create_ld_so_conf(self): - ldsoconf = os.path.join(self.conf.installtree, "etc", "ld.so.conf") - utils.touch(ldsoconf) - - procdir = os.path.join(self.conf.installtree, "proc") - if not os.path.isdir(procdir): - utils.mkdir(procdir) - - cmd = "mount -t proc proc %s" % procdir - err, output = commands.getstatusoutput(cmd) - - with open(ldsoconf, "w") as f: - f.write("/usr/kerberos/%s\n" % self.conf.libdir) - - cwd = os.getcwd() - os.chdir(self.conf.installtree) - - # XXX os.chroot does not support exiting from the root - cmd = "/usr/sbin/chroot %s /sbin/ldconfig" % self.conf.installtree - err, output = commands.getstatusoutput(cmd) - - os.chdir(cwd) - - cmd = "umount %s" % procdir - err, output = commands.getstatusoutput(cmd) - - os.unlink(ldsoconf) - - def change_tree_permissions(self): - root_uid = pwd.getpwnam("root")[2] - root_gid = grp.getgrnam("root")[2] - - for root, dirs, files in os.walk(self.conf.installtree): - os.chown(root, root_uid, root_gid) - os.chmod(root, 0755) - - for file in files: - # skip broken symlinks - if not os.path.exists(file): - continue - - path = os.path.join(root, file) - os.chown(path, root_uid, root_gid) - - mode = os.stat(path).st_mode - if (mode & stat.S_IXUSR) or (mode & stat.S_IXGRP) \ - or (mode & stat.S_IXOTH): - os.chmod(path, 0555) - else: - os.chmod(path, 0444) - - def prepare(self): - # copy the .buildstamp - shutil.copy2(self.conf.buildstamp, self.conf.installtree) - - self.move_shared_files() - self.process_actions() - - self.copy_stubs() - self.configure_fedorakmod() - self.copy_bootloaders() - self.move_repos() - self.move_anaconda_files() - self.create_modules_symlinks() - self.fix_man_pages() - self.remove_gtk_stuff() - self.remove_locales() - self.remove_unnecessary_files() - self.remove_python_stuff() - self.fix_udev_links() - self.move_bins() - - self.create_ld_so_conf() - self.change_tree_permissions() - - def create(self, type="squashfs"): - self.prepare() - - installimg = os.path.join(self.conf.tempdir, "install.img") - - if os.path.exists(installimg): - os.unlink(installimg) - - self.output.info(":: compressing the image file") - - if type == "squashfs": - if not os.path.exists(self.paths.MKSQUASHFS): - self.output.error("'%s' does not exist" % self.paths.MKSQUASHFS) - return None - - cmd = "%s %s %s -all-root -no-fragments -no-progress" % \ - (self.paths.MKSQUASHFS, self.conf.installtree, installimg) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return None - - elif type == "cramfs": - if not os.path.exists(self.paths.MKCRAMFS): - self.output.error("'%s' does not exist" % self.paths.MKCRAMFS) - return None - - crambs = "" - if self.conf.arch == "sparc64": - crambs = "--blocksize 8192" - elif self.conf.arch == "sparc": - crambs = "--blocksize 4096" - - cmd = "%s %s %s %s" % (self.paths.MKCRAMFS, crambs, - self.conf.installtree, installimg) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return None - - elif type == "ext2": - # TODO - raise NotImplementedError - - # edit the .treeinfo - text = "[stage2]\nmainimage = images/install.img\n" - utils.edit(self.conf.treeinfo, append=True, text=text) - - return installimg diff --git a/src/pylorax/insttree.py b/src/pylorax/insttree.py new file mode 100644 index 00000000..a83b8c3d --- /dev/null +++ b/src/pylorax/insttree.py @@ -0,0 +1,149 @@ +# +# insttree.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +import os +import re +import commands + +from base import BaseLoraxClass +from sysutils import * + + +class Kernel(object): + pass + + +class InstallTree(BaseLoraxClass): + + def __init__(self, yum, rootdir, updatesdir=None): + BaseLoraxClass.__init__(self) + self.yum = yum + self.rootdir = rootdir + self.updatesdir = updatesdir + + self.kpattern = re.compile(r"vmlinuz-(?P[-.0-9a-z]+?" + r"(?P(PAE)?)(?P(xen)?))$") + + def install_packages(self, packages): + for name in packages: + if not self.yum.install(name): + self.pwarning("no package {0} found".format(name)) + + self.yum.process_transaction() + self.run_ldconfig() + + def run_ldconfig(self): + ldsoconf = os.path.join(self.rootdir, self.const.LDSOCONF) + + # XXX + with open(ldsoconf, "w") as f: + f.write("/usr/kerberos/{0}\n".format(self.conf.libdir)) + + procdir = os.path.join(self.rootdir, "proc") + mkdir_(procdir) + + # mount proc + cmd = "{0.MOUNT} -t proc proc {1}".format(self.cmd, procdir) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.perror(stdout) + return + + cwd = os.getcwd() + + # chroot to the install tree directory, and run ldconfig + pid = os.fork() + if pid: + # parent + os.waitpid(pid, 0) + else: + # child + os.chroot(self.rootdir) + os.chdir("/") + + err, stdout = commands.getstatusoutput(self.cmd.LDCONFIG) + if err: + self.perror(stdout) + + os._exit(0) + + os.chdir(cwd) + + # umount proc + cmd = "{0.UMOUNT} {1}".format(self.cmd, procdir) + err, stdout = commands.getstatusoutput(cmd) + if err: + self.pwarning(stdout) + + # XXX + os.unlink(ldsoconf) + + def copy_updates(self): + if self.updatesdir and os.path.isdir(self.updatesdir): + scopy_(src_root=self.updatesdir, src_path="*", + dst_root=self.rootdir, dst_path="") + + @property + def kernels(self): + if self.conf.buildarch == "ia64": + kerneldir = self.const.BOOTDIR_IA64 + else: + kerneldir = self.const.BOOTDIR + + self.kerneldir = os.path.join(self.rootdir, kerneldir) + for filename in os.listdir(self.kerneldir): + m = self.kpattern.match(filename) + if m: + kernel = Kernel() + kernel.filename = filename + kernel.path = os.path.join(self.kerneldir, filename) + kernel.version = m.group("ver") + kernel.is_pae = bool(m.group("pae")) + kernel.is_xen = bool(m.group("xen")) + + yield kernel + + @property + def do_efi(self): + return os.path.isdir(os.path.join(self.rootdir, self.const.EFIDIR)) + + # XXX this should be just a temporary fix, + # should get fixed in the respective packages + def fix_problems(self): + # remove broken build and source links from the modules directory + for kernel in self.kernels: + moddir = os.path.join(self.rootdir, self.const.MODDIR, + kernel.version) + + build = os.path.join(moddir, "build") + if os.path.islink(build) and not os.path.exists(build): + os.unlink(build) + + source = os.path.join(moddir, "source") + if os.path.islink(source) and not os.path.exists(source): + os.unlink(source) + + # fix udev broken links + for fname in ("udevcontrol", "udevsettle", "udevtrigger"): + fname = os.path.join(self.rootdir, "sbin", fname) + if os.path.islink(fname): + os.unlink(fname) + os.symlink("udevadm", fname) diff --git a/src/pylorax/launcher.py b/src/pylorax/launcher.py deleted file mode 100644 index ac86b60d..00000000 --- a/src/pylorax/launcher.py +++ /dev/null @@ -1,224 +0,0 @@ -# -# launcher.py -# functions for the command line launcher -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - - -from __future__ import print_function - -import sys -import os -from optparse import OptionParser, OptionGroup -import tempfile -import shutil -import ConfigParser - -import yum - -import pylorax - - -def main(args): - version = "%s %s" % (os.path.basename(args[0]), pylorax.__VERSION__) - usage = "%prog -p PRODUCT -v VERSION -r RELEASE -o OUTPUTDIR REPOSITORY" - - parser = OptionParser(usage=usage) - - # required arguments for image creation - required = OptionGroup(parser, "required arguments") - required.add_option("-p", "--product", help="product name", - metavar="STRING") - required.add_option("-v", "--version", help="version identifier", - metavar="STRING") - required.add_option("-r", "--release", help="release information", - metavar="STRING") - required.add_option("-o", "--outputdir", help="output directory", - metavar="PATHSPEC") - - # optional arguments - optional = OptionGroup(parser, "optional arguments") - optional.add_option("-m", "--mirrorlist", - help="mirrorlist repository (may be listed multiple times)", - metavar="REPOSITORY", action="append", default=[]) - optional.add_option("-t", "--variant", - help="variant name", metavar="STRING") - optional.add_option("-b", "--bugurl", - help="bug reporting URL for the product", metavar="URL", - default="your distribution provided bug reporting tool") - optional.add_option("-u", "--updates", - help="directory containing updates", metavar="PATHSPEC") - - # output settings - output = OptionGroup(parser, "output settings") - output.add_option("--no-colors", help="disable color output", - action="store_false", default=True, dest="colors") - output.add_option("--encoding", help="set encoding", - metavar="STRING", default="utf-8") - output.add_option("-d", "--debug", help="enable debugging messages", - action="store_true", default=False) - - # lorax settings - settings = OptionGroup(parser, "lorax settings") - settings.add_option("-c", "--cleanup", help="clean up on exit", - action="store_true", default=False) - - # add the option groups to the parser - parser.add_option_group(required) - parser.add_option_group(optional) - parser.add_option_group(output) - parser.add_option_group(settings) - - # add the show version option - parser.add_option("-V", help="show program's version number and exit", - action="store_true", default=False, dest="showver") - - # parse the arguments - opts, args = parser.parse_args() - repositories = args - - if opts.showver: - print(version) - sys.exit(0) - - # check for the required arguments - if not opts.product or not opts.version or not opts.release \ - or not opts.outputdir or not repositories: - parser.error("missing one or more required arguments") - - # create the temporary directory for lorax - tempdir = tempfile.mkdtemp(prefix="lorax.", dir=tempfile.gettempdir()) - - # create the yumbase object - installtree = os.path.join(tempdir, "installtree") - os.mkdir(installtree) - - yumtempdir = os.path.join(tempdir, "yum") - os.mkdir(yumtempdir) - - yumconf = create_yumconf(repositories, opts.mirrorlist, yumtempdir) - yb = create_yumbase_object(yumconf, installtree) - - if yb is None: - print("error: unable to create the yumbase object", file=sys.stderr) - shutil.rmtree(tempdir) - sys.exit(1) - - # run lorax - params = { "product" : opts.product, - "version" : opts.version, - "release" : opts.release, - "outputdir" : opts.outputdir, - "tempdir" : tempdir, - "installtree" : installtree, - "colors" : opts.colors, - "encoding" : opts.encoding, - "debug" : opts.debug, - "cleanup" : opts.cleanup, - "variant" : opts.variant, - "bugurl" : opts.bugurl, - "updates" : opts.updates } - - lorax = pylorax.Lorax(yb, **params) - lorax.run() - - -def create_yumconf(repositories, mirrorlists=[], tempdir="/tmp/yum"): - - def sanitize_repo(repo): - if repo.startswith("/"): - return "file://%s" % repo - elif repo.startswith("http://") or repo.startswith("ftp://"): - return repo - else: - return None - - # sanitize the repositories - repositories = map(sanitize_repo, repositories) - - # remove invalid repositories - repositories = filter(bool, repositories) - - cachedir = os.path.join(tempdir, "cache") - if not os.path.isdir(cachedir): - os.mkdir(cachedir) - - yumconf = os.path.join(tempdir, "yum.conf") - c = ConfigParser.ConfigParser() - - # add the main section - section = "main" - data = { "cachedir" : cachedir, - "keepcache" : 0, - "gpgcheck" : 0, - "plugins" : 0, - "reposdir" : "", - "tsflags" : "nodocs" } - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # add the main repository - the first repository from list - section = "lorax-repo" - data = { "name" : "lorax repo", - "baseurl" : repositories[0], - "enabled" : 1 } - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # add the extra repositories - for n, extra in enumerate(repositories[1:], start=1): - section = "lorax-extra-repo-%d" % n - data = { "name" : "lorax extra repo %d" % n, - "baseurl" : extra, - "enabled" : 1 } - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # add the mirrorlists - for n, mirror in enumerate(mirrorlists, start=1): - section = "lorax-mirrorlist-%d" % n - data = { "name" : "lorax mirrorlist %d" % n, - "mirrorlist" : mirror, - "enabled" : 1 } - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # write the yumconf file - with open(yumconf, "w") as f: - c.write(f) - - return yumconf - - -def create_yumbase_object(yumconf, installroot="/"): - yb = yum.YumBase() - - yb.preconf.fn = yumconf - yb.preconf.root = installroot - yb._getConfig() - - yb._getRpmDB() - yb._getRepos() - yb._getSacks() - - return yb diff --git a/src/pylorax/lcs/__init__.py b/src/pylorax/lcs/__init__.py deleted file mode 100644 index dc699128..00000000 --- a/src/pylorax/lcs/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# __init__.py -# lorax control system classes -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -import sys -import os -import re - -from mako.template import Template as MakoTemplate -from mako.lookup import TemplateLookup as MakoTemplateLookup - -import actions - - -class TemplateParserError(Exception): - pass - - -class TemplateParser(object): - - def __init__(self, variables): - self.variables = variables - self.actions_map = actions.get_map() - - def get_actions(self, file): - template_actions = [] - - # we have to set the template lookup directories to ["/"], - # otherwise the relative and absolute includes don't work properly - lookup = MakoTemplateLookup(directories=["/"]) - template = MakoTemplate(filename=file, lookup=lookup) - s = template.render(**self.variables) - - # concatenate lines ending with "\" - #s = re.sub(r"\s*\\\s*\n", " ", s) - - for lineno, line in enumerate(s.splitlines(), start=1): - # remove multiple whitespaces - line = re.sub(r"\s+", " ", line.strip()) - if not line: - continue - - # get the command - command, line = line.split(None, 1) - if command not in self.actions_map: - raise TemplateParserError("%s: %d: invalid command" % \ - (file, lineno)) - - # create the action object - m = re.match(self.actions_map[command].REGEX, line) - if m: - new_action = self.actions_map[command](**m.groupdict()) - template_actions.append(new_action) - else: - raise TemplateParserError("%s: %d: invalid command format" % \ - (file, lineno)) - - return template_actions diff --git a/src/pylorax/lcs/actions/__init__.py b/src/pylorax/lcs/actions/__init__.py deleted file mode 100644 index 4a074693..00000000 --- a/src/pylorax/lcs/actions/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# __init__.py -# function for loading action modules -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -import sys -import os - - -def get_map(): - actions = {} - - root, actions_dir = os.path.split(os.path.dirname(__file__)) - - sys.path.insert(0, root) - - modules = set() - for filename in os.listdir(os.path.join(root, actions_dir)): - if filename.endswith(".py") and not filename == "__init__.py": - basename, extension = os.path.splitext(filename) - modules.add(os.path.join(actions_dir, basename).replace("/", ".")) - - for module in modules: - imported = __import__(module, globals(), locals(), [module], -1) - - commands = getattr(imported, "COMMANDS", {}) - for command, classname in commands.items(): - actions[command] = getattr(imported, classname) - - sys.path.pop(0) - - return actions diff --git a/src/pylorax/lcs/actions/base.py b/src/pylorax/lcs/actions/base.py deleted file mode 100644 index 5f627b1f..00000000 --- a/src/pylorax/lcs/actions/base.py +++ /dev/null @@ -1,55 +0,0 @@ -# -# base.py -# base lorax control system action class -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -class LCSAction(object): - """LCS Action base class. - - To create your own custom action, subclass this class, and override - the methods you need. - - A valid action has to have a REGEX class variable, which specifies - the format of the action command, so the needed parameters can be - properly extracted, and an execute() method, where all the work - should be done. This medhod will be called by Lorax. - - Don't forget to include a command : action map for your new action - in the COMMANDS dictionary in the beginning of your file. - Action classes which are not in the COMMANDS dictionary will not - be loaded by Lorax. - - """ - - # regular expression for extracting the parameters from the command - REGEX = r"" - - def __init__(self): - if self.__class__ is LCSAction: - raise TypeError("LCSAction is an abstract class, " \ - "cannot be used this way") - - self._attrs = {} - - def __str__(self): - return "%s: %s" % (self.__class__.__name__, self._attrs) - - def execute(self): - raise NotImplementedError("execute() method not implemented") diff --git a/src/pylorax/lcs/actions/file.py b/src/pylorax/lcs/actions/file.py deleted file mode 100644 index 16a63022..00000000 --- a/src/pylorax/lcs/actions/file.py +++ /dev/null @@ -1,300 +0,0 @@ -# -# file.py -# lcs file actions -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -import os - -from base import LCSAction -import pylorax.utils as utils - - -COMMANDS = { "remove" : "Remove", - "copy" : "Copy", - "symlink" : "SymLink", - "touch" : "Touch", - "mkdir" : "MkDir", - "makedirs" : "MakeDirs", - "chown" : "Chown", - "chmod" : "Chmod", - "edit" : "Edit", - "replace" : "Replace" } - - -class Remove(LCSAction): - - REGEX = r"^(?P.*?)$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["filename"] = kwargs.get("filename") - - def execute(self): - utils.remove(self.filename) - - @property - def filename(self): - return self._attrs["filename"] - - -# TODO add the ignore_errors flag -class Copy(LCSAction): - - REGEX = r"^(?P.*?)\s(?P.*?)\sto\s" \ - "(?P.*?)\s(?P.*?)" \ - "(\s(?Pnosymlinks))?$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["src_root"] = kwargs.get("src_root") - self._attrs["src_path"] = kwargs.get("src_path") - self._attrs["dst_root"] = kwargs.get("dst_root") - self._attrs["dst_path"] = kwargs.get("dst_path") - - nosymlinks = kwargs.get("nosymlinks") - if nosymlinks is not None: - self._attrs["symlinks"] = False - else: - self._attrs["symlinks"] = True - - def execute(self): - utils.dcopy(src_root=self.src_root, src_path=self.src_path, - dst_root=self.dst_root, dst_path=self.dst_path, - symlinks=self.symlinks) - - @property - def src(self): - path = os.path.join(self._attrs["src_root"], self._attrs["src_path"]) - return os.path.normpath(path) - - @property - def src_root(self): - return self._attrs["src_root"] - - @property - def src_path(self): - return self._attrs["src_path"] - - @property - def dst(self): - path = os.path.join(self._attrs["dst_root"], self._attrs["dst_path"]) - return os.path.normpath(path) - - @property - def dst_root(self): - return self._attrs["dst_root"] - - @property - def dst_path(self): - return self._attrs["dst_path"] - - @property - def symlinks(self): - return self._attrs["symlinks"] - - -class SymLink(LCSAction): - - REGEX = r"^name\s(?P.*?)\starget\s(?P.*?)$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["name"] = kwargs.get("name") - self._attrs["target"] = kwargs.get("target") - - def execute(self): - utils.symlink(link_name=self.name, link_target=self.target) - - @property - def name(self): - return self._attrs["name"] - - @property - def target(self): - return self._attrs["target"] - - -class Touch(LCSAction): - - REGEX = r"^(?P.*?)$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["filename"] = kwargs.get("filename") - - def execute(self): - utils.touch(self.filename) - - @property - def filename(self): - return self._attrs["filename"] - - -class MkDir(LCSAction): - - REGEX = r"^(?P.*?)(\smode\s(?P.*?))?$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["dir"] = kwargs.get("dir") - - mode = kwargs.get("mode") - if mode is not None: - self._attrs["mode"] = int(mode, 8) - else: - self._attrs["mode"] = None - - def execute(self): - utils.mkdir(self.dir, self.mode) - - @property - def dir(self): - return self._attrs["dir"] - - @property - def mode(self): - return self._attrs["mode"] - - -class MakeDirs(MkDir): - - def __init__(self, **kwargs): - MkDir.__init__(self, **kwargs) - - def execute(self): - utils.makedirs(self.dir, self.mode) - - -class Chown(LCSAction): - - REGEX = r"^(?P.*?)\suser\s(?P.*?)" \ - "\sgroup\s(?P.*?)(\s(?Precursive))?$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["filename"] = kwargs.get("filename") - self._attrs["user"] = kwargs.get("user") - self._attrs["group"] = kwargs.get("group") - - recursive = kwargs.get("recursive") - if recursive is not None: - self._attrs["recursive"] = True - else: - self._attrs["recursive"] = False - - def execute(self): - utils.chown(self.filename, self.user, self.group, self.recursive) - - @property - def filename(self): - return self._attrs["filename"] - - @property - def user(self): - return self._attrs["user"] - - @property - def group(self): - return self._attrs["group"] - - @property - def recursive(self): - return self._attrs["recursive"] - - -class Chmod(LCSAction): - - REGEX = r"^(?P.*?)\smode\s(?P[0-7]*?)" \ - "(\s(?Precursive))?$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["filename"] = kwargs.get("filename") - self._attrs["mode"] = int(kwargs.get("mode"), 8) - - recursive = kwargs.get("recursive") - if recursive is not None: - self._attrs["recursive"] = True - else: - self._attrs["recursive"] = False - - def execute(self): - utils.chmod(self.filename, self.mode, self.recursive) - - @property - def filename(self): - return self._attrs["filename"] - - @property - def mode(self): - return self._attrs["mode"] - - @property - def recursive(self): - return self._attrs["recursive"] - - -class Edit(Touch): - - REGEX = r'^(?P.*?)\stext\s"(?P.*?)"' \ - '(\s(?Pappend))?$' - - def __init__(self, **kwargs): - Touch.__init__(self, **kwargs) - self._attrs["text"] = kwargs.get("text") - - append = kwargs.get("append") - if append is not None: - self._attrs["append"] = True - else: - self._attrs["append"] = False - - def execute(self): - utils.edit(self.filename, self.text, self.append) - - @property - def text(self): - return self._attrs["text"] - - @property - def append(self): - return self._attrs["append"] - - -class Replace(Touch): - - REGEX = r'^(?P.*?)\sfind\s"(?P.*?)"' \ - '\sreplace\s"(?P.*?)"$' - - def __init__(self, **kwargs): - Touch.__init__(self, **kwargs) - self._attrs["find"] = kwargs.get("find") - self._attrs["replace"] = kwargs.get("replace") - - def execute(self): - utils.replace(self.filename, self.find, self.replace) - - @property - def find(self): - return self._attrs["find"] - - @property - def replace(self): - return self._attrs["replace"] diff --git a/src/pylorax/lcs/actions/ssh.py b/src/pylorax/lcs/actions/ssh.py deleted file mode 100644 index d4266b18..00000000 --- a/src/pylorax/lcs/actions/ssh.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# ssh.py -# lcs ssh actions -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -import commands - -from base import LCSAction -import pylorax.utils as utils - - -COMMANDS = { "gensshkey" : "GenerateSSHKey" } - - -class GenerateSSHKey(LCSAction): - - REGEX = r"^(?P.*?)\stype\s(?P.*?)$" - - def __init__(self, **kwargs): - LCSAction.__init__(self) - self._attrs["filename"] = kwargs.get("filename") - self._attrs["type"] = kwargs.get("type") - - def execute(self): - cmd = "/usr/bin/ssh-keygen -q -t %s -f %s -C '' -N ''" % \ - (self.type, self.filename) - - err, output = commands.getstatusoutput(cmd) - - if not err: - utils.chmod(self.filename, 0600) - utils.chmod(self.filename + ".pub", 0644) - - @property - def filename(self): - return self._attrs["filename"] - - @property - def type(self): - return self._attrs["type"] diff --git a/src/pylorax/ltmpl.py b/src/pylorax/ltmpl.py new file mode 100644 index 00000000..fab6d114 --- /dev/null +++ b/src/pylorax/ltmpl.py @@ -0,0 +1,39 @@ +# +# ltmpl.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +from mako.template import Template as MakoTemplate +from mako.lookup import TemplateLookup as MakoTemplateLookup + + +class Template(object): + + def parse(self, template_file, variables): + # we have to set the template lookup directories to ["/"], + # otherwise the file includes will not work properly + lookup = MakoTemplateLookup(directories=["/"]) + template = MakoTemplate(filename=template_file, lookup=lookup) + s = template.render(**variables) + + # enumerate, strip and remove empty lines + lines = enumerate(s.splitlines(), start=1) + lines = map(lambda (n, line): (n, line.strip()), lines) + lines = filter(lambda (n, line): line, lines) + return lines diff --git a/src/pylorax/output.py b/src/pylorax/output.py index f9469553..c2e6c391 100644 --- a/src/pylorax/output.py +++ b/src/pylorax/output.py @@ -1,6 +1,5 @@ # # output.py -# output control # # Copyright (C) 2009 Red Hat, Inc. # @@ -21,100 +20,127 @@ # import sys -import singleton +import re + +from decorators import singleton -### color codes -C_DEFAULT = "\x1b[39m" -C_RESET = "\x1b[0m" +# color codes +C_DEFAULT = "\x1b[39m" +C_RESET = "\x1b[0m" -C_BLACK = "\x1b[0;30m" -C_WHITE = "\x1b[1;37m" -C_RED = "\x1b[0;31m" -C_GREEN = "\x1b[0;32m" -C_BLUE = "\x1b[0;34m" -C_LIGHTRED = "\x1b[1;31m" -C_LIGHTGREEN = "\x1b[1;32m" -C_LIGHTBLUE = "\x1b[1;34m" +C_BLACK = "\x1b[0;30m" +C_WHITE = "\x1b[1;37m" +C_RED = "\x1b[0;31m" +C_GREEN = "\x1b[0;32m" +C_BLUE = "\x1b[0;34m" +C_LIGHTRED = "\x1b[1;31m" +C_LIGHTGREEN = "\x1b[1;32m" +C_LIGHTBLUE = "\x1b[1;34m" -C_BOLD = "\x1b[1m" -C_UNDERLINE = "\x1b[4m" - -### font types -BOLD = 0b01 -UNDERLINE = 0b10 - -### output levels -CRITICAL = 50 -ERROR = 40 -WARNING = 30 -INFO = 20 -DEBUG = 10 -NOTSET = 0 +C_BOLD = "\x1b[1m" +C_UNDERLINE = "\x1b[4m" -class Terminal(singleton.Singleton): +# format tags +TAGS = [(re.compile(r""), C_BOLD), + (re.compile(r""), C_UNDERLINE), + (re.compile(r""), C_RED), + (re.compile(r""), C_GREEN), + (re.compile(r""), C_BLUE), + (re.compile(r""), C_RESET)] + + +# output levels +CRITICAL = 50 +ERROR = 40 +WARNING = 30 +INFO = 20 +DEBUG = 10 +NOTSET = 0 + + +@singleton +class LoraxOutput(object): def __init__(self): - self.__colors = True - self.__encoding = "utf-8" - self.__output_level = INFO - self.__indent_level = 0 + self._colors = True + self._encoding = "utf-8" + self._output_level = INFO + self._indent_level = 0 - def basic_config(self, colors=None, encoding=None, level=None): + self._ignore_errors = set() + + def basic_config(self, colors=None, encoding=None, output_level=None): if colors is not None: - self.__colors = colors + self._colors = colors if encoding is not None: - self.__encoding = encoding + self._encoding = encoding - if level is not None: - self.__output_level = level + if output_level is not None: + self._output_level = output_level + + @property + def ignore(self): + return self._ignore_errors + + @ignore.setter + def ignore(self, errors): + self._ignore_errors = errors def indent(self): - self.__indent_level += 1 + self._indent_level += 1 def unindent(self): - if self.__indent_level > 0: - self.__indent_level -= 1 + if self._indent_level > 0: + self._indent_level -= 1 + + def write(self, s, file=sys.stdout): + if self._colors: + s = self.__format(s) + else: + s = self.__raw(s) - def write(self, s, color=C_RESET, type=None, file=sys.stdout): - s = self.format(s, color=color, type=type) file.write(s) file.flush() - def format(self, s, color=C_RESET, type=None): - s = s.encode(self.__encoding) - - if self.__colors: - if type is not None and (type & BOLD): - s = "%s%s" % (C_BOLD, s) - if type is not None and (type & UNDERLINE): - s = "%s%s" % (C_UNDERLINE, s) - s = "%s%s%s" % (color, s, C_RESET) - - return s - - def writeline(self, s, color=C_RESET, type=None, file=sys.stdout): - s = "%s%s" % (" " * self.__indent_level, s) - self.write(s + "\n", color=color, type=type, file=file) + def writeline(self, s, file=sys.stdout): + s = "{0}{1}\n".format(" " * self._indent_level, s) + self.write(s, file=file) def critical(self, s, file=sys.stdout): - if self.__output_level <= CRITICAL: - self.writeline("** critical: %s" % s, file=file) + s = "** critical: {0}".format(s) + if (self._output_level <= CRITICAL and + self.__raw(s) not in self.ignore): + self.writeline(s, file=file) def error(self, s, file=sys.stdout): - if self.__output_level <= ERROR: - self.writeline("** error: %s" % s, file=file) + s = "** error: {0}".format(s) + if (self._output_level <= ERROR and + self.__raw(s) not in self.ignore): + self.writeline(s, file=file) def warning(self, s, file=sys.stdout): - if self.__output_level <= WARNING: - self.writeline("** warning: %s" % s, file=file) + s = "** warning: {0}".format(s) + if (self._output_level <= WARNING and + self.__raw(s) not in self.ignore): + self.writeline(s, file=file) def info(self, s, file=sys.stdout): - if self.__output_level <= INFO: + if self._output_level <= INFO: self.writeline(s, file=file) def debug(self, s, file=sys.stdout): - if self.__output_level <= DEBUG: + if self._output_level <= DEBUG: self.writeline(s, file=file) + + def __raw(self, s): + for tag, ccode in TAGS: + s = tag.sub("", s) + return s + + def __format(self, s): + for tag, ccode in TAGS: + s = tag.sub(ccode, s) + return s diff --git a/src/pylorax/ramdisk.py b/src/pylorax/ramdisk.py deleted file mode 100644 index 26355a2a..00000000 --- a/src/pylorax/ramdisk.py +++ /dev/null @@ -1,522 +0,0 @@ -# -# ramdisk.py -# class for creating init ramdisk -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Red Hat Author(s): Martin Gracik -# - -import sys -import os -import glob -import shutil -import commands -import re -import gzip - -import config -import output -import lcs -import utils - - -class Ramdisk(object): - - def __init__(self): - # get the config, paths and output objects - self.conf = config.LoraxConfig.get() - self.paths = config.LoraxPaths.get() - self.output = output.Terminal.get() - - self.actions = self.get_actions_from_template() - - def get_actions_from_template(self): - variables = { "instroot" : self.conf.installtree, - "initrd" : self.conf.ramdisktree, - "libdir" : self.conf.libdir, - "arch" : self.conf.arch, - "basearch" : self.conf.basearch, - "confdir" : self.conf.confdir, - "datadir" : self.conf.datadir } - - if self.conf.initrd_template is not None: - template = lcs.TemplateParser(variables) - return template.get_actions(self.conf.initrd_template) - - return [] - - # XXX we have to do this, otherwise we get an error, when copying - def remove_modules_broken_links(self): - # remove build and source links from modules directories - build = os.path.join(self.paths.MODULES_DIR, "build") - if os.path.islink(build): - utils.remove(build) - - source = os.path.join(self.paths.MODULES_DIR, "source") - if os.path.islink(source): - utils.remove(source) - - def move_shared_files(self): - dirs = [os.path.join(self.paths.INITRD_DATADIR, "noarch"), - os.path.join(self.paths.INITRD_DATADIR, self.conf.arch)] - - self.output.info(":: copying the custom initrd files") - for dir in [dir for dir in dirs if os.path.isdir(dir)]: - utils.scopy(src_root=dir, src_path="*", - dst_root=self.conf.ramdisktree, dst_path="") - - def process_actions(self): - for action in self.actions: - self.output.debug(str(action)) - action.execute() - - def create_modinfo(self, moddir, target): - modules_map = {} - for root, dirs, files in os.walk(moddir): - for file in files: - modules_map[file] = os.path.join(root, file) - - modules = { "scsi_hostadapter" : "block", - "eth" : "networking" } - - blacklist = ( "floppy", - "scsi_mod", - "libiscsi" ) - - list = {} - for type, file_suffix in modules.items(): - list[type] = {} - - filename = os.path.join(moddir, "modules.%s" % file_suffix) - if not os.path.isfile(filename): - continue - - with open(filename, "r") as f: - for line in f: - line = line.strip() - - if line in modules_map: - modname, ext = os.path.splitext(line) - if modname in blacklist: - continue - - cmd = "%s -F description %s" % (self.paths.MODINFO, - modules_map[line]) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - desc = "" - else: - desc = output.split("\n")[0] - desc = desc.strip() - desc = desc[:65] - - if not desc: - desc = "%s driver" % modname - info = '%s\n\t%s\n\t"%s"\n' % (modname, type, desc) - list[type][modname] = info - - with open(target, "w") as f: - f.write("Version 0\n") - for type in list: - modlist = sorted(list[type].keys()) - for mod in modlist: - f.write("%s\n" % list[type][mod]) - - def get_kernel_modules(self): - src_moddir = self.paths.MODULES_DIR - dst_moddir = os.path.join(self.conf.ramdisktree, "lib", "modules", - self.conf.kernelver) - - # expand modules - modules = set() - - for name in self.conf.modules: - if name.startswith("="): - group = name[1:] - - if group in ("scsi", "ata"): - path = os.path.join(src_moddir, "modules.block") - elif group == "net": - path = os.path.join(src_moddir, "modules.networking") - else: - path = os.path.join(src_moddir, "modules.%s" % group) - - if os.path.isfile(path): - with open(path, "r") as f: - for line in f: - module = re.sub(r"\.ko$", "", line.strip()) - modules.add(module) - - else: - modules.add(name) - - # resolve modules dependencies - with open(self.paths.MODULES_DEP, "r") as f: - lines = map(lambda l: l.strip(), f.readlines()) - - modpattern = re.compile(r"^.*/(?P.*)\.ko:(?P.*)$") - deppattern = re.compile(r"^.*/(?P.*)\.ko$") - changed = True - - while changed: - for line in lines: - changed = False - - m = modpattern.match(line) - modname = m.group("name") - - if modname in modules: - # add the dependencies - for dep in m.group("deps").split(): - m = deppattern.match(dep) - depname = m.group("name") - - if depname not in modules: - changed = True - modules.add(depname) - - # copy all modules to the ramdisk tree - src_path = src_moddir.replace(self.conf.installtree, "", 1) - if src_path.startswith("/"): - src_path = src_path[1:] - - dst_path = "lib/modules" - - utils.scopy(src_root=self.conf.installtree, src_path=src_path, - dst_root=self.conf.ramdisktree, dst_path=dst_path) - - # remove not needed modules - for root, dirs, files in os.walk(dst_moddir): - for file in files: - full_path = os.path.join(root, file) - name, ext = os.path.splitext(file) - - if ext == ".ko": - if name not in modules: - utils.remove(full_path) - else: - # get the required firmware - cmd = "%s -F firmware %s" % (self.paths.MODINFO, - full_path) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - continue - - for fw in output.split(): - dst = os.path.join(self.conf.ramdisktree, - "lib", "firmware", fw) - - # create the destination directory - dir = os.path.dirname(dst) - if not os.path.isdir(dir): - utils.makedirs(dir) - - # copy the firmware - path = os.path.join("lib", "firmware", fw) - utils.scopy(src_root=self.conf.installtree, - src_path=path, - dst_root=self.conf.ramdisktree, - dst_path=path, - ignore_errors=True) - - # copy additional firmware - fw = [ ("ipw2100", "ipw2100*"), - ("ipw2200", "ipw2200*"), - ("iwl3945", "iwlwifi-3945*"), - ("iwl4965", "iwlwifi-4965*"), - ("atmel", "atmel_*.bin"), - ("zd1211rw", "zd1211"), - ("qla2xxx", "ql*") ] - - for module, file in fw: - if module in modules: - utils.scopy(src_root=self.conf.installtree, - src_path=os.path.join("lib", "firmware", file), - dst_root=self.conf.ramdisktree, - dst_path=os.path.join("lib", "firmware")) - - # compress modules - for root, dirs, files in os.walk(dst_moddir): - for file in files: - if not file.endswith(".ko"): - continue - - kopath = os.path.join(root, file) - with open(kopath, "rb") as f: - kodata = f.read() - - gzipped = gzip.open(kopath + ".gz", "wb") - gzipped.write(kodata) - gzipped.close() - - os.unlink(kopath) - - # create modinfo - modinfo = os.path.join(self.conf.tempdir, "module-info") - self.create_modinfo(src_moddir, modinfo) - - target = os.path.join(self.conf.ramdisktree, "lib", "modules", - "module-info") - - cmd = "%s --modinfo-file %s --ignore-missing --modinfo %s > %s" % \ - (self.paths.MODLIST, modinfo, " ".join(list(modules)), target) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - # run depmod - cmd = "%s -a -F %s -b %s %s" % ( - self.paths.DEPMOD, self.paths.SYSTEM_MAP, self.conf.installtree, - self.conf.kernelver) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - # remove leftovers - utils.remove(os.path.join(dst_moddir, "modules.*map")) - - def get_keymaps(self): - if os.path.isfile(self.paths.KEYMAPS_OVERRIDE): - dst = os.path.join(self.conf.ramdisktree, "etc", "keymaps.gz") - shutil.copy2(self.paths.KEYMAPS_OVERRIDE, dst) - else: - cmd = "%s %s %s %s" % ( - self.paths.GETKEYMAPS, self.conf.arch, - os.path.join(self.conf.ramdisktree, "etc", "keymaps.gz"), - self.conf.installtree) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return False - - return True - - def create_locales(self): - dir = os.path.join(self.conf.ramdisktree, "usr", "lib", "locale") - utils.makedirs(dir) - - cmd = "%s -c -i en_US -f UTF-8 --prefix %s en_US" % \ - (self.paths.LOCALEDEF, self.conf.ramdisktree) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.error(output) - return False - - return True - - def compress(self, filename): - self.output.info(":: compressing the image file") - cwd = os.getcwd() - os.chdir(self.conf.ramdisktree) - - # XXX python cpioarchive does not support writing - cpioarchive = filename + ".cpio" - - cmd = "find . | cpio --quiet -c -o > %s" % cpioarchive - err, output = commands.getstatusoutput(cmd) - if err: - return False - - os.chdir(cwd) - - with open(cpioarchive, "rb") as f: - cpiodata = f.read() - - gzipped = gzip.open(filename, "wb") - gzipped.write(cpiodata) - gzipped.close() - - os.unlink(cpioarchive) - - return True - - def prepare(self): - # copy the .buildstamp file - shutil.copy2(self.conf.buildstamp, self.conf.ramdisktree) - - self.remove_modules_broken_links() - self.move_shared_files() - self.process_actions() - self.get_kernel_modules() - self.get_keymaps() - self.create_locales() - - def create(self): - self.prepare() - - f = getattr(self, "create_%s" % self.conf.basearch, None) - if f: - return f() - - def create_i386(self): - initrd_filename = "initrd.img" - kernel_filename = "vmlinuz" - - if self.conf.kernelfile.endswith("PAE"): - initrd_filename = "initrd-PAE.img" - kernel_filename = "vmlinuz-PAE" - - text = """[images-xen] -kernel = images/pxeboot/vmlinuz-PAE -initrd = images/pxeboot/initrd-PAE.img - -""" - - utils.edit(self.conf.treeinfo, append=True, text=text) - - initrd_filename = os.path.join(self.conf.tempdir, initrd_filename) - self.compress(initrd_filename) - - kernel_filename = os.path.join(self.conf.tempdir, kernel_filename) - shutil.copy2(self.conf.kernelfile, kernel_filename) - - return kernel_filename, initrd_filename - - def create_x86_64(self): - return self.create_i386() - - def create_s390(self): - initrd_filename = os.path.join(self.conf.tempdir, "initrd.img") - self.compress(initrd_filename) - - kernel_filename = os.path.join(self.conf.tempdir, "kernel.img") - shutil.copy2(self.conf.kernelfile, kernel_filename) - - cmd = "%s %s %s" % (self.paths.GENINITRDSZ, - os.path.getsize(initrd_filename), - os.path.join(self.conf.imagesdir, "initrd.size")) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - for filename in (self.paths.REDHAT_EXEC, self.paths.GENERIC_PRM): - shutil.copy2(filename, self.conf.imagesdir) - shutil.copy2(filename, self.conf.outputdir) - - cmd = "%s -i %s -r %s -p %s -o %s" % ( - self.paths.MKS390CD, kernel_filename, initrd_filename, - self.paths.GENERIC_PRM, - os.path.join(self.conf.imagesdir, "cdboot.img")) - - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - text = """[images-%s] -kernel = images/kernel.img -initrd = images/initrd.img -initrd.size = images/initrd.size -generic.prm = images/generic.prm -generic.ins = generic.ins -cdboot.img = images/cdboot.img - -""" % self.conf.arch - - utils.edit(self.conf.treeinfo, append=True, text=text) - - return kernel_filename, initrd_filename - - def create_s390x(self): - return self.create_s390() - - # XXX this should be removed - def create_alpha(self): - raise NotImplementedError - - def create_ia64(self): - raise NotImplementedError - - def create_ppc(self): - if self.conf.arch == "ppc": - bits = "32" - elif self.conf.arch == "ppc64": - bits = "64" - - ppc_dir = os.path.join(self.conf.outputdir, "ppc", "ppc%s" % bits) - utils.makedirs(ppc_dir) - - if self.conf.arch == "ppc": - mac_dir = os.path.join(self.conf.outputdir, "ppc", "mac") - utils.makedirs(mac_dir) - - initrd_filename = os.path.join(ppc_dir, "ramdisk.image.gz") - self.compress(initrd_filename) - - kernel_filename = os.path.join(ppc_dir, "vmlinuz") - shutil.copy2(self.conf.kernelfile, kernel_filename) - - yaboot_src = os.path.join(self.paths.ANACONDA_BOOT, "yaboot.conf.in") - yaboot_dst = os.path.join(ppc_dir, "yaboot.conf") - shutil.copy2(yaboot_src, yaboot_dst) - - utils.replace(yaboot_dst, "%BITS%", bits) - utils.replace(yaboot_dst, "%PRODUCT%", self.conf.product) - utils.replace(yaboot_dst, "%VERSION%", self.conf.version) - - text = """[images-%s] -kernel = ppc/ppc%s/vmlinuz -initrd = ppc/ppc%s/ramdisk.image.gz - -""" % (self.conf.arch, bits, bits) - - utils.edit(self.conf.treeinfo, append=True, text=text) - - netboot_dir = os.path.join(self.conf.imagesdir, "netboot") - utils.makedirs(netboot_dir) - - ppc_img = os.path.join(netboot_dir, "ppc%s.img" % bits) - - if os.path.exists(self.paths.MKZIMAGE) and \ - os.path.exists(self.paths.ZIMAGE_STUB): - shutil.copy2(self.paths.ZIMAGE_LDS, ppc_dir) - - cmd = "%s %s no no %s %s %s" % \ - (self.paths.MKZIMAGE, kernel_filename, initrd_filename, - self.paths.ZIMAGE_STUB, ppc_img) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - utils.remove(os.path.join(ppc_dir, "zImage.lds")) - - elif os.path.exists(self.paths.WRAPPER) and \ - os.path.exists(self.paths.WRAPPER_A): - cmd = "%s -o %s -i %s -D %s %s" % \ - (self.paths.WRAPPER, ppc_img, initrd_filename, - self.paths.WRAPPER_A_DIR, kernel_filename) - err, output = commands.getstatusoutput(cmd) - if err: - self.output.warning(output) - - if os.path.exists(ppc_img): - text = "zimage = images/netboot/ppc%s.img" % bits - utils.edit(self.conf.treeinfo, append=True, text=text) - else: - utils.remove(netboot_dir) - - return None, None - - def create_ppc64(self): - return create_ppc() diff --git a/src/pylorax/utils.py b/src/pylorax/sysutils.py similarity index 55% rename from src/pylorax/utils.py rename to src/pylorax/sysutils.py index 8468580a..6e207def 100644 --- a/src/pylorax/utils.py +++ b/src/pylorax/sysutils.py @@ -1,6 +1,5 @@ # -# utils.py -# file utilities +# sysutils.py # # Copyright (C) 2009 Red Hat, Inc. # @@ -20,6 +19,10 @@ # Red Hat Author(s): Martin Gracik # +__all__ = ["mkdir_", "makedirs_", "remove_", "symlink_", "touch_", + "chown_", "chmod_", "replace_", "scopy_", "dcopy_"] + + import sys import os import shutil @@ -31,103 +34,52 @@ import grp import commands -def expand_path(path, globs=True): - l = [] - - m = re.match(r"(?P.*){(?P.*?)}(?P.*)", path) - if m: - for f in re.split(r"\s*,\s*", m.group("expand")): - l.extend(expand_path(m.group("prefix") + f + m.group("suffix"), - globs=globs)) - else: - # XXX are there any other chars in globs? - if globs and (path.find("*") != -1 or path.find("?") != -1): - l.extend(glob.glob(path)) - else: - l.append(path) - - return l +class SysUtilsError(Exception): + pass -def remove(file): - for fname in expand_path(file): +class SmartCopyError(SysUtilsError): + pass + + +class LinkerError(SysUtilsError): + pass + + +def mkdir_(dir): + if not os.path.isdir(dir): + os.mkdir(dir) + + +def makedirs_(dir): + if not os.path.isdir(dir): + os.makedirs(dir) + + +def remove_(path): + for fname in glob.iglob(path): if os.path.islink(fname) or os.path.isfile(fname): os.unlink(fname) else: shutil.rmtree(fname) -def __copy(src_path, dst_path, src_root="/", dst_root="/", symlinks=True, - ignore_errors=False, deps=False): - - # ensure that roots end with "/" - if not src_root.endswith("/"): - src_root = src_root + "/" - if not dst_root.endswith("/"): - dst_root = dst_root + "/" - - smartcopy = SmartCopy(src_root, dst_root, symlinks, ignore_errors) - - src = os.path.join(src_root, src_path) - for fname in expand_path(src): - fname = fname.replace(src_root, "", 1) - smartcopy.copy(fname, dst_path) - - if deps: - smartcopy.get_deps() - - smartcopy.process() - - -def scopy(src_path, dst_path, src_root="/", dst_root="/", symlinks=True, - ignore_errors=False): - - __copy(src_path, dst_path, src_root, dst_root, symlinks, - ignore_errors, deps=False) - - -def dcopy(src_path, dst_path, src_root="/", dst_root="/", symlinks=True, - ignore_errors=False): - - __copy(src_path, dst_path, src_root, dst_root, symlinks, - ignore_errors, deps=True) - - -def symlink(link_target, link_name): - if os.path.islink(link_name) or os.path.isfile(link_name): +def symlink_(link_target, link_name, force=True): + if force and (os.path.islink(link_name) or os.path.isfile(link_name)): os.unlink(link_name) os.symlink(link_target, link_name) -def touch(file): - if os.path.exists(file): - os.utime(file, None) +def touch_(fname): + if os.path.exists(fname): + os.utime(fname, None) else: - with open(file, "w") as f: + with open(fname, "w") as f: pass -def mkdir(dir, mode=None): - if mode is None: - mode = 0755 - - for d in expand_path(dir, globs=False): - if not os.path.isdir(d): - os.mkdir(d, mode) - - -def makedirs(dir, mode=None): - if mode is None: - mode = 0755 - - for d in expand_path(dir, globs=False): - if not os.path.isdir(d): - os.makedirs(d, mode) - - -def chown(file, user=None, group=None, recursive=False): - # if uid or gid is set to -1, it will not be changed +def chown_(path, user=None, group=None, recursive=False): uid = gid = -1 if user is not None: @@ -135,56 +87,88 @@ def chown(file, user=None, group=None, recursive=False): if group is not None: gid = grp.getgrnam(group)[2] - for fname in expand_path(file): + for fname in glob.iglob(path): os.chown(fname, uid, gid) if recursive and os.path.isdir(fname): for nested in os.listdir(fname): nested = os.path.join(fname, nested) - chown(nested, user, group, recursive) + chown_(nested, user, group, recursive) -def chmod(file, mode, recursive=False): - for fname in expand_path(file): +def chmod_(path, mode, recursive=False): + for fname in glob.iglob(path): os.chmod(fname, mode) if recursive and os.path.isdir(fname): for nested in os.listdir(fname): nested = os.path.join(fname, nested) - chmod(nested, mode, recursive) + chmod_(nested, mode, recursive) -def edit(file, text, append=False): - mode = "w" - if append: - mode = "a" - - with open(file, mode) as f: - f.write(text) - - -def replace(file, find, replace): - fin = fileinput.input(file, inplace=1) +def replace_(fname, find, replace): + fin = fileinput.input(fname, inplace=1) + pattern = re.compile(find) for line in fin: - line = re.sub(find, replace, line) + line = pattern.sub(replace, line) sys.stdout.write(line) fin.close() -class SmartCopyError(Exception): - pass +def scopy_(src_path, dst_path, src_root="/", dst_root="/", symlinks=True, + ignore_errors=False): + + __copy(src_path, dst_path, src_root, dst_root, + symlinks, deps=False, ignore_errors=ignore_errors) +def dcopy_(src_path, dst_path, src_root="/", dst_root="/", symlinks=True, + ignore_errors=False): + + __copy(src_path, dst_path, src_root, dst_root, + symlinks, deps=True, ignore_errors=ignore_errors) + + +def __copy(src_path, dst_path, src_root="/", dst_root="/", + symlinks=True, deps=False, ignore_errors=False): + + if not src_root.endswith("/"): + src_root += "/" + if not dst_root.endswith("/"): + dst_root += "/" + + smartcopy = SmartCopy(src_root, dst_root, symlinks, deps, ignore_errors) + + src = os.path.join(src_root, src_path) + pattern = re.compile(r"(\*|\?|\[.*?\])") + if pattern.search(src): + fnames = glob.glob(src) + else: + fnames = [src] + + if not fnames and not ignore_errors: + err_msg = "cannot stat '{0}': No such file or directory" + raise SysUtilsError(err_msg.format(src)) + + for fname in fnames: + fname = fname.replace(src_root, "", 1) + smartcopy.copy(fname, dst_path) + + smartcopy.process() + + +# XXX class SmartCopy(object): - def __init__(self, src_root="/", dst_root="/", symlinks=True, + def __init__(self, src_root="/", dst_root="/", symlinks=True, deps=False, ignore_errors=False): self.src_root = src_root self.dst_root = dst_root self.symlinks = symlinks + self.deps = deps self.ignore_errors = ignore_errors self.linker = Linker(src_root) @@ -201,14 +185,15 @@ class SmartCopy(object): src = os.path.normpath(os.path.join(self.src_root, src_path)) dst = os.path.normpath(os.path.join(self.dst_root, dst_path)) - # check if the source exists + # check if the source path exists if not os.path.exists(src): - err_msg = "cannot stat '%s': No such file or directory" % src + err_msg = "cannot stat '{0}': No such file or directory" + err_msg = err_msg.format(src) if not self.ignore_errors: raise SmartCopyError(err_msg) else: self.errors.append(err_msg) - return # EXIT + return if os.path.isfile(src): self.__copy_file(src_path, dst_path, src, dst) @@ -217,32 +202,23 @@ class SmartCopy(object): def __copy_file(self, src_path, dst_path, src, dst): # if destination is an existing directory, - # append the source filename to the destination path + # append the source filename to the destination if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) # check if the new destination is still an existing directory if os.path.isdir(dst): - - # do not overwrite a directory with a file - err_msg = "cannot overwrite directory '%s' " \ - "with non-directory" % dst + err_msg = "cannot overwrite directory '{0}' with non-directory" + err_msg = err_msg.format(dst) if not self.ignore_errors: raise SmartCopyError(err_msg) else: self.errors.append(err_msg) - return # EXIT + return if os.path.islink(src): - - if not self.symlinks: - real_src = os.path.realpath(src) - self.copyfiles.add((real_src, dst)) - else: - self.__copy_link(src_path, dst_path, src, dst) - + self.__copy_link(src_path, dst_path, src, dst) else: - self.copyfiles.add((src, dst)) def __copy_dir(self, src_path, dst_path, src, dst): @@ -250,60 +226,47 @@ class SmartCopy(object): dirname = os.path.basename(src) new_dst = os.path.join(dst, dirname) - # remove the trailing "/", - # to make sure, that we don't try to create "dir" and "dir/" + # remove the trailing "/" if new_dst.endswith("/"): new_dst = new_dst[:-1] if os.path.islink(src): - - if not self.symlinks: - real_src = os.path.realpath(src) - - if not os.path.exists(new_dst) and \ - new_dst not in self.makedirs: - self.makedirs.append(new_dst) - - for fname in os.listdir(real_src): - fname = os.path.join(real_src, fname) - self.copy(fname, new_dst) - - else: - self.__copy_link(src_path, dst_path, src, new_dst) - + self.__copy_link(src_path, dst_path, src, new_dst, dir=True) else: - - # create the destination directory, if it does not exist - if not os.path.exists(new_dst) and \ - new_dst not in self.makedirs: + # create the destination directory + if not os.path.isdir(new_dst) and new_dst not in self.makedirs: self.makedirs.append(new_dst) - elif os.path.isfile(new_dst): - err_msg = "cannot overwrite file '%s' with a directory" \ - % new_dst + err_msg = "cannot overwrite file '{0}' with directory" + err_msg = err_msg.format(new_dst) if not self.ignore_errors: raise SmartCopyError(err_msg) else: self.errors.append(err_msg) - return # EXIT + return new_dst_path = os.path.join(dst_path, dirname) try: fnames = os.listdir(src) except OSError as why: - err_msg = "cannot list directory '%s': %s'" % (src, why) + err_msg = "cannot list directory '{0}': {1}'" + err_msg = err_msg.format(src, why) if not self.ignore_errors: raise SmartCopyError(err_msg) else: self.errors.append(err_msg) - return # EXIT + return for fname in fnames: fname = os.path.join(src_path, fname) self.copy(fname, new_dst_path) - def __copy_link(self, src_path, dst_path, src, dst): + def __copy_link(self, src_path, dst_path, src, dst, dir=False): + if not self.symlinks: + # TODO + raise NotImplementedError + # read the link target link_target = os.readlink(src) @@ -320,10 +283,9 @@ class SmartCopy(object): if target_dst_dir.endswith("/"): target_dst_dir = target_dst_dir[:-1] - # create the destination directory, if it doesn't exist + # create the destination directory target_dst = os.path.join(self.dst_root, target_dst_dir) - if not os.path.exists(target_dst) and \ - target_dst not in self.makedirs: + if not os.path.isdir(target_dst) and target_dst not in self.makedirs: self.makedirs.append(target_dst) # copy the target along with the link @@ -332,7 +294,7 @@ class SmartCopy(object): # create the symlink named dst, pointing to link_target self.links.add((link_target, dst)) - def get_deps(self): + def __get_deps(self): deps = set() for src, dst in self.copyfiles: @@ -345,78 +307,84 @@ class SmartCopy(object): # create the destination directory dst_dir = os.path.join(self.dst_root, dst_path) - if not os.path.exists(dst_dir) and \ - dst_dir not in self.makedirs: + if not os.path.isdir(dst_dir) and dst_dir not in self.makedirs: self.makedirs.append(dst_dir) self.copy(src_path, dst_path) def process(self): - # create required directories - map(mkdir, self.makedirs) + if self.deps: + self.__get_deps() - # copy all the files + # create required directories + map(makedirs_, self.makedirs) # remove the dst, if it is a link to src for src, dst in self.copyfiles: if os.path.realpath(dst) == src: os.unlink(dst) + # copy all the files map(lambda (src, dst): shutil.copy2(src, dst), self.copyfiles) # create symlinks - map(lambda (target, name): symlink(target, name), self.links) - - -class LinkerError(Exception): - pass + map(lambda (target, name): symlink_(target, name), self.links) +# XXX class Linker(object): - LIBDIRS = ( "lib64", - "usr/lib64", - "lib", - "usr/lib" ) + LIBDIRS = ("lib64", + "usr/lib64", + "lib", + "usr/lib") + + LDDBIN = "/usr/bin/ldd" + FILEBIN = "/usr/bin/file" def __init__(self, root="/"): libdirs = map(lambda path: os.path.join(root, path), self.LIBDIRS) libdirs = ":".join(libdirs) ld_linux = None + pattern = re.compile(r"^RTLDLIST=(?P.*)$") - with open("/usr/bin/ldd", "r") as f: + with open(self.LDDBIN, "r") as f: for line in f: - m = re.match(r"^RTLDLIST=(?P.*)$", line.strip()) + m = pattern.match(line.strip()) if m: ld_linux = m.group("ld_linux") break if ld_linux is None: - raise LinkerError("unable to find the ld_linux executable") + raise LinkerError("cannot find the ld_linux executable") - self.lddcmd = "LD_LIBRARY_PATH=%s %s --list" % (libdirs, ld_linux) - self.pattern = re.compile(r"^[a-zA-Z0-9.-_/]*\s=>\s" \ - r"(?P[a-zA-Z0-9.-_/]*)" \ - r"\s\(0x[0-9a-f]*\)$") + self.lddcmd = "LD_LIBRARY_PATH={0} {1} --list" + self.lddcmd = self.lddcmd.format(libdirs, ld_linux) - def is_elf(self, file): - err, output = commands.getstatusoutput("file --brief %s" % file) + self.pattern = re.compile(r"^[-._/a-zA-Z0-9]+\s=>\s" + r"(?P[-._/a-zA-Z0-9]+)" + r"\s\(0x[0-9a-f]+\)$") + + def is_elf(self, fname): + cmd = "{0} --brief {1}".format(self.FILEBIN, fname) + err, stdout = commands.getstatusoutput(cmd) if err: - raise LinkerError("error getting the file type") + raise LinkerError(stdout) - if not output.startswith("ELF"): + if not stdout.count("ELF"): return False return True - def get_deps(self, file): - err, output = commands.getstatusoutput("%s %s" % (self.lddcmd, file)) + def get_deps(self, fname): + cmd = "{0} {1}".format(self.lddcmd, fname) + err, stdout = commands.getstatusoutput(cmd) if err: - raise LinkerError("error getting the file dependencies") + raise LinkerError(stdout) deps = set() - for line in output.splitlines(): + for line in stdout.splitlines(): m = self.pattern.match(line.strip()) if m: deps.add(m.group("lib")) diff --git a/src/pylorax/yumbase.py b/src/pylorax/yumbase.py new file mode 100644 index 00000000..ded8b616 --- /dev/null +++ b/src/pylorax/yumbase.py @@ -0,0 +1,110 @@ +# +# yumbase.py +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Red Hat Author(s): Martin Gracik +# + +import os +import ConfigParser + +import yum + + +def get_yum_base_object(installroot, repositories, mirrorlists=[], + tempdir="/tmp"): + + def sanitize_repo(repo): + if repo.startswith("/"): + return "file://{0}".format(repo) + elif repo.startswith("http://") or repo.startswith("ftp://"): + return repo + else: + return None + + # sanitize the repositories + repositories = map(sanitize_repo, repositories) + mirrorlists = map(sanitize_repo, mirrorlists) + + # remove invalid repositories + repositories = filter(bool, repositories) + mirrorlists = filter(bool, mirrorlists) + + cachedir = os.path.join(tempdir, "yum.cache") + if not os.path.isdir(cachedir): + os.mkdir(cachedir) + + yumconf = os.path.join(tempdir, "yum.conf") + c = ConfigParser.ConfigParser() + + # add the main section + section = "main" + data = {"cachedir": cachedir, + "keepcache": 0, + "gpgcheck": 0, + "plugins": 0, + "reposdir": "", + "tsflags": "nodocs"} + + c.add_section(section) + map(lambda (key, value): c.set(section, key, value), data.items()) + + # add the main repository - the first repository from list + section = "lorax-repo" + data = {"name": "lorax repo", + "baseurl": repositories[0], + "enabled": 1} + + c.add_section(section) + map(lambda (key, value): c.set(section, key, value), data.items()) + + # add the extra repositories + for n, extra in enumerate(repositories[1:], start=1): + section = "lorax-extra-repo-{0:d}".format(n) + data = {"name": "lorax extra repo {0:d}".format(n), + "baseurl": extra, + "enabled": 1} + + c.add_section(section) + map(lambda (key, value): c.set(section, key, value), data.items()) + + # add the mirrorlists + for n, mirror in enumerate(mirrorlists, start=1): + section = "lorax-mirrorlist-{0:d}".format(n) + data = {"name": "lorax mirrorlist {0:d}".format(n), + "mirrorlist": mirror, + "enabled": 1 } + + c.add_section(section) + map(lambda (key, value): c.set(section, key, value), data.items()) + + # write the yum configuration file + with open(yumconf, "w") as f: + c.write(f) + + # create the yum base object + yb = yum.YumBase() + + yb.preconf.fn = yumconf + yb.preconf.root = installroot + yb._getConfig() + + yb._getRpmDB() + yb._getRepos() + yb._getSacks() + + return yb diff --git a/usr/share/lorax/initrd/s390/etc/pam.d/login b/usr/share/lorax/initrd/s390/etc/pam.d/login deleted file mode 100644 index 5e8d5794..00000000 --- a/usr/share/lorax/initrd/s390/etc/pam.d/login +++ /dev/null @@ -1,9 +0,0 @@ -#%PAM-1.0 -auth required pam_env.so -auth sufficient pam_unix.so likeauth nullok -auth required pam_deny.so -account required pam_unix.so -password sufficient pam_unix.sp nullok use_authtok md5 shadow -password required pam_deny.so -session required pam_limits.so -session required pam_unix.so diff --git a/usr/share/lorax/initrd/s390/etc/pam.d/remote b/usr/share/lorax/initrd/s390/etc/pam.d/remote deleted file mode 100644 index 5e8d5794..00000000 --- a/usr/share/lorax/initrd/s390/etc/pam.d/remote +++ /dev/null @@ -1,9 +0,0 @@ -#%PAM-1.0 -auth required pam_env.so -auth sufficient pam_unix.so likeauth nullok -auth required pam_deny.so -account required pam_unix.so -password sufficient pam_unix.sp nullok use_authtok md5 shadow -password required pam_deny.so -session required pam_limits.so -session required pam_unix.so diff --git a/usr/share/lorax/initrd/s390/etc/pam.d/sshd b/usr/share/lorax/initrd/s390/etc/pam.d/sshd deleted file mode 100644 index 5e8d5794..00000000 --- a/usr/share/lorax/initrd/s390/etc/pam.d/sshd +++ /dev/null @@ -1,9 +0,0 @@ -#%PAM-1.0 -auth required pam_env.so -auth sufficient pam_unix.so likeauth nullok -auth required pam_deny.so -account required pam_unix.so -password sufficient pam_unix.sp nullok use_authtok md5 shadow -password required pam_deny.so -session required pam_limits.so -session required pam_unix.so diff --git a/usr/share/lorax/initrd/s390/etc/ssh/sshd_config b/usr/share/lorax/initrd/s390/etc/ssh/sshd_config deleted file mode 100644 index 0c0404cc..00000000 --- a/usr/share/lorax/initrd/s390/etc/ssh/sshd_config +++ /dev/null @@ -1,17 +0,0 @@ -Port 22 -HostKey /etc/ssh/ssh_host_key -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_dsa_key -PermitRootLogin yes -IgnoreRhosts yes -StrictModes yes -X11Forwarding yes -X11DisplayOffset 10 -PrintMotd yes -XAuthLocation /sbin/xauth -KeepAlive yes -SyslogFacility AUTHPRIV -RSAAuthentication yes -PasswordAuthentication yes -PermitEmptyPasswords yes -PermitUserEnvironment yes diff --git a/usr/share/lorax/initrd/s390x b/usr/share/lorax/initrd/s390x deleted file mode 120000 index ba766cae..00000000 --- a/usr/share/lorax/initrd/s390x +++ /dev/null @@ -1 +0,0 @@ -s390 \ No newline at end of file diff --git a/usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/interface/%gconf.xml b/usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/interface/%gconf.xml deleted file mode 100644 index 17f4a2bb..00000000 --- a/usr/share/lorax/installtree/noarch/.gconf/desktop/gnome/interface/%gconf.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/usr/share/lorax/installtree/noarch/etc/libuser.conf b/usr/share/lorax/installtree/noarch/etc/libuser.conf deleted file mode 100644 index 05302d64..00000000 --- a/usr/share/lorax/installtree/noarch/etc/libuser.conf +++ /dev/null @@ -1,13 +0,0 @@ -[defaults] -skeleton = /mnt/sysimage/etc/skel -mailspooldir = /mnt/sysimage/var/mail -crypt_style = md5 -modules = files shadow -create_modules = files shadow - -[files] -directory = /mnt/sysimage/etc - -[shadow] -directory = /mnt/sysimage/etc - diff --git a/usr/share/lorax/installtree/noarch/etc/selinux/config b/usr/share/lorax/installtree/noarch/etc/selinux/config deleted file mode 100644 index a68f8296..00000000 --- a/usr/share/lorax/installtree/noarch/etc/selinux/config +++ /dev/null @@ -1,3 +0,0 @@ -SELINUX=permissive -SELINUXTYPE=targeted - diff --git a/usr/share/lorax/outputdir/images/README b/usr/share/lorax/outputdir/images/README deleted file mode 100644 index 032d925f..00000000 --- a/usr/share/lorax/outputdir/images/README +++ /dev/null @@ -1,9 +0,0 @@ -This directory contains image files that can be used to create media -capable of starting the @PRODUCT@ installation process. - -The boot.iso file is an ISO 9660 image of a bootable CD-ROM. It is useful -in cases where the CD-ROM installation method is not desired, but the -CD-ROM's boot speed would be an advantage. - -To use this image file, burn the file onto CD-R (or CD-RW) media as you -normally would. diff --git a/usr/share/lorax/outputdir/images/pxeboot/README b/usr/share/lorax/outputdir/images/pxeboot/README deleted file mode 100644 index 72f6d2f4..00000000 --- a/usr/share/lorax/outputdir/images/pxeboot/README +++ /dev/null @@ -1,6 +0,0 @@ -The files in this directory are useful for booting a machine via PXE. - -The following files are available: -vmlinuz - the kernel used for the installer -initrd.img - an initrd with support for all install methods and - drivers supported for installation of @PRODUCT@