lorax/src/pylorax/install.py
Martin Gracik b672d9936e New version
Added support for Mako templates.
Changed the Lorax class to require an yumbase object as a parameter.
Changed a lot of unnecessary system calls to native python code.
Moved most of the file paths into a separate object.
The output and config objects are now singletons.
Also done a lot of code cleanup.
2009-12-15 15:26:01 +01:00

480 lines
17 KiB
Python

#
# install.py
#
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)
def configure_fedorakmod(self):
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):
# 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