crazytime: replace installtree with runtimebuilder

This commit is contained in:
Will Woods 2011-05-14 03:28:03 -04:00
parent 46e18c3781
commit 905e05159d
5 changed files with 37 additions and 2094 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,5 @@
FIXME: FIXME:
- make TemplateRunner handle ramdisk.tmpl and installtree
- DONE: rewrite ramdisk.tmpl a bit
- DONE: install -> installpkg
- DONE: remove -> removepkg
- DONE: remove --path -> remove
- DONE: module takes multiple args
- DONE: rewrite installtree as template
- DONE: split installtree into postinstall/cleanup
- add TemplateRunner commands
- installpkg, removepkg, module
- also do_installpkg, do_removepkg
- or maybe run_transaction?
- handle [compression] conf keys (type, speed) - handle [compression] conf keys (type, speed)
- run prelink on runtime image
- chroot ${root} /etc/cron.daily/prelink
Good ideas: Good ideas:
@ -21,6 +7,8 @@ Good ideas:
- update imgutils to be able to partition images - update imgutils to be able to partition images
- via dmsetup stuff in anaconda.storage? - via dmsetup stuff in anaconda.storage?
- allow alternate runtime images via conf key - allow alternate runtime images via conf key
- run prelink on runtime image
- chroot ${root} /etc/cron.daily/prelink
Someday: Someday:

View File

@ -38,15 +38,13 @@ from base import BaseLoraxClass, DataHolder
import output import output
import yum import yum
import yumhelper
import ltmpl import ltmpl
import imgutils import imgutils
import constants import constants
from sysutils import * from sysutils import *
from treebuilder import TreeBuilder from treebuilder import RuntimeBuilder, TreeBuilder
from installtree import LoraxInstallTree
from buildstamp import BuildStamp from buildstamp import BuildStamp
from treeinfo import TreeInfo from treeinfo import TreeInfo
from discinfo import DiscInfo from discinfo import DiscInfo
@ -158,7 +156,7 @@ class Lorax(BaseLoraxClass):
# do we have all lorax required commands? # do we have all lorax required commands?
self.lcmds = constants.LoraxRequiredCommands() self.lcmds = constants.LoraxRequiredCommands()
# TODO: actually check for required commands # TODO: actually check for required commands (runcmd etc)
# do we have a proper yum base object? # do we have a proper yum base object?
logger.info("checking yum base object") logger.info("checking yum base object")
@ -166,49 +164,25 @@ class Lorax(BaseLoraxClass):
logger.critical("no yum base object") logger.critical("no yum base object")
sys.exit(1) sys.exit(1)
logger.info("setting up yum helper") logger.debug("using install root: {0}".format(ybo.installroot))
self.yum = yumhelper.LoraxYumHelper(ybo)
logger.debug("using install root: {0}".format(self.yum.installroot))
logger.info("setting up build architecture") logger.info("setting up build architecture")
self.arch = ArchData(self.get_buildarch()) self.arch = ArchData(get_buildarch(ybo))
for attr in ('buildarch', 'basearch', 'libdir'): for attr in ('buildarch', 'basearch', 'libdir'):
logger.debug("self.arch.%s = %s", attr, getattr(self.arch,attr)) logger.debug("self.arch.%s = %s", attr, getattr(self.arch,attr))
logger.info("setting up install tree")
self.installtree = LoraxInstallTree(self.yum, self.arch.libdir)
logger.info("setting up build parameters") logger.info("setting up build parameters")
product = DataHolder(name=product, version=version, release=release, product = DataHolder(name=product, version=version, release=release,
variant=variant, bugurl=bugurl, is_beta=is_beta) variant=variant, bugurl=bugurl, is_beta=is_beta)
self.product = product self.product = product
logger.debug("product data: %s" % product) logger.debug("product data: %s" % product)
logger.info("parsing the runtime template") templatedir = self.conf.get("lorax", "sharedir")
tfile = joinpaths(self.conf.get("lorax", "sharedir"), rb = RuntimeBuilder(product, arch, self.outputdir, ybo, templatedir)
self.conf.get("templates", "ramdisk"))
# TODO: normalize with arch templates: logger.info("installing runtime packages")
# tvars = dict(product=product, arch=arch) rb.yum.conf.skip_broken = self.conf.getboolean("yum", "skipbroken")
tvars = { "basearch": self.arch.basearch, rb.install()
"buildarch": self.arch.buildarch,
"libdir" : self.arch.libdir,
"product": self.product.name.lower() }
template = ltmpl.LoraxTemplate()
template.parse(tfile, tvars)
logger.info("creating tree directories")
for d in template.getdata("mkdir"):
os.makedirs(joinpaths(self.installtree.root, d))
# install packages
logger.info("getting list of required packages")
for package in template.getdata("install"):
self.installtree.yum.install(package)
skipbroken = self.conf.getboolean("yum", "skipbroken")
self.installtree.yum.process_transaction(skipbroken)
# write .buildstamp # write .buildstamp
buildstamp = BuildStamp(self.product.name, self.product.version, buildstamp = BuildStamp(self.product.name, self.product.version,
@ -219,55 +193,13 @@ class Lorax(BaseLoraxClass):
logger.debug("saving pkglists to %s", self.workdir) logger.debug("saving pkglists to %s", self.workdir)
dname = joinpaths(self.workdir, "pkglists") dname = joinpaths(self.workdir, "pkglists")
os.makedirs(dname) os.makedirs(dname)
for pkgname, pkgobj in self.installtree.yum.installed_packages.items(): for pkgobj in ybo.doPackageLists(pkgnarrow='installed').installed:
with open(joinpaths(dname, pkgname), "w") as fobj: with open(joinpaths(dname, pkgobj.name), "w") as fobj:
for fname in pkgobj.filelist: for fname in pkgobj.filelist:
fobj.write("{0}\n".format(fname)) fobj.write("{0}\n".format(fname))
logger.info("removing locales") logger.info("doing post-install configuration")
self.installtree.remove_locales() rb.postinstall()
logger.info("creating keymaps")
if self.arch.basearch not in ("s390", "s390x"):
self.installtree.create_keymaps(basearch=self.arch.basearch)
logger.info("creating screenfont")
self.installtree.create_screenfont(basearch=self.arch.basearch)
logger.info("moving stubs")
self.installtree.move_stubs()
logger.info("getting list of required modules")
# Need a list to pass to cleanup_kernel_modules, not a generator
modules = list(template.getdata("module"))
self.installtree.install_kernel_modules(modules)
logger.info("moving anaconda repos")
self.installtree.move_repos()
logger.info("creating depmod.conf")
self.installtree.create_depmod_conf()
# set up /sbin/init
if self.arch.basearch in ("s390", "s390x"):
self.installtree.setup_s390_init()
else:
self.installtree.setup_init()
# misc tree modifications
self.installtree.misc_tree_modifications()
# get config files
config_dir = joinpaths(self.conf.get("lorax", "sharedir"),
"config_files")
self.installtree.get_config_files(config_dir)
self.installtree.setup_sshd(config_dir)
if self.arch.basearch in ("s390", "s390x"):
self.installtree.generate_ssh_keys()
# get anaconda portions
self.installtree.get_anaconda_portions()
# write .discinfo # write .discinfo
discinfo = DiscInfo(self.product.release, self.arch.basearch) discinfo = DiscInfo(self.product.release, self.arch.basearch)
@ -277,12 +209,8 @@ class Lorax(BaseLoraxClass):
installroot = joinpaths(self.workdir, "installroot") installroot = joinpaths(self.workdir, "installroot")
linktree(self.installtree.root, installroot) linktree(self.installtree.root, installroot)
logger.info("getting list of not required packages") logger.info("cleaning unneeded files")
removepkgs = template.getdata("remove", mode="lines") rb.clean()
self.installtree.remove_packages(removepkgs)
logger.info("cleaning up python files")
self.installtree.cleanup_python_files()
logger.info("creating the runtime image") logger.info("creating the runtime image")
# TODO: different img styles / create_runtime implementations # TODO: different img styles / create_runtime implementations
@ -293,13 +221,13 @@ class Lorax(BaseLoraxClass):
logger.info("preparing to build output tree and boot images") logger.info("preparing to build output tree and boot images")
treebuilder = TreeBuilder(self.product, self.arch, treebuilder = TreeBuilder(self.product, self.arch,
installroot, self.outputdir, installroot, self.outputdir,
templatedir=self.conf.get("lorax", "sharedir")) templatedir)
# TODO: different image styles may do this part differently # TODO: different image styles may do this part differently
logger.info("rebuilding initramfs images") logger.info("rebuilding initramfs images")
treebuilder.rebuild_initrds(add_args=["--xz", "--add", "btrfs"]) treebuilder.rebuild_initrds(add_args=["--xz"])
# TODO: keep small initramfs for split initramfs/runtime media # TODO: keep small initramfs for split initramfs/runtime media?
logger.info("adding runtime to initrds") logger.info("adding runtime to initrds")
treebuilder.initrd_append(runtimedir) treebuilder.initrd_append(runtimedir)
@ -313,9 +241,9 @@ class Lorax(BaseLoraxClass):
treeinfo.add_section(section, data) treeinfo.add_section(section, data)
treeinfo.write(joinpaths(self.outputdir, ".treeinfo")) treeinfo.write(joinpaths(self.outputdir, ".treeinfo"))
def get_buildarch(self): def get_buildarch(ybo):
# get architecture of the available anaconda package # get architecture of the available anaconda package
_, available = self.yum.search("anaconda") available = ybo.doPackageLists(patterns=["anaconda"]).available
if available: if available:
anaconda = available.pop(0) anaconda = available.pop(0)

View File

@ -1,567 +0,0 @@
#
# installtree.py
#
# Copyright (C) 2010 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 <http://www.gnu.org/licenses/>.
#
# Red Hat Author(s): Martin Gracik <mgracik@redhat.com>
#
import logging
logger = logging.getLogger("pylorax.installtree")
import sys
import os
import shutil
import gzip
import re
import glob
import time
import subprocess
import operator
from base import BaseLoraxClass, DataHolder
import constants
from sysutils import *
class LoraxInstallTree(BaseLoraxClass):
def __init__(self, yum, libdir):
BaseLoraxClass.__init__(self)
self.yum = yum
self.root = self.yum.installroot
self.libdir = libdir
self.lcmds = constants.LoraxRequiredCommands()
def remove_locales(self):
chroot = lambda: os.chroot(self.root)
# get locales we need to keep
langtable = joinpaths(self.root, "usr/share/anaconda/lang-table")
if not os.path.exists(langtable):
logger.critical("could not find anaconda lang-table, exiting")
sys.exit(1)
# remove unneeded locales from /usr/share/locale
with open(langtable, "r") as fobj:
langs = fobj.readlines()
langs = map(lambda l: l.split()[1], langs)
localedir = joinpaths(self.root, "usr/share/locale")
for fname in os.listdir(localedir):
fpath = joinpaths(localedir, fname)
if os.path.isdir(fpath) and fname not in langs:
shutil.rmtree(fpath)
# move the lang-table to etc
shutil.move(langtable, joinpaths(self.root, "etc"))
def create_keymaps(self, basearch):
keymaps = joinpaths(self.root, "etc/keymaps.gz")
# look for override
override = "keymaps-override-{0}".format(basearch)
override = joinpaths(self.root, "usr/share/anaconda", override)
if os.path.isfile(override):
logger.debug("using keymaps override")
shutil.move(override, keymaps)
else:
# create keymaps
cmd = [joinpaths(self.root, "usr/libexec/anaconda", "getkeymaps"),
basearch, keymaps, self.root]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
proc.wait()
return True
def create_screenfont(self, basearch):
dst = joinpaths(self.root, "etc/screenfont.gz")
screenfont = "screenfont-{0}.gz".format(basearch)
screenfont = joinpaths(self.root, "usr/share/anaconda", screenfont)
if not os.path.isfile(screenfont):
return False
else:
shutil.move(screenfont, dst)
return True
def move_stubs(self):
stubs = ("list-harddrives", "loadkeys", "mknod",
"raidstart", "raidstop")
for stub in stubs:
src = joinpaths(self.root, "usr/share/anaconda",
"{0}-stub".format(stub))
dst = joinpaths(self.root, "usr/bin", stub)
if os.path.isfile(src):
shutil.move(src, dst)
# move restart-anaconda
src = joinpaths(self.root, "usr/share/anaconda", "restart-anaconda")
dst = joinpaths(self.root, "usr/bin")
shutil.move(src, dst)
# move sitecustomize.py
pythonpath = joinpaths(self.root, "usr", self.libdir, "python?.?")
for path in glob.glob(pythonpath):
src = joinpaths(path, "site-packages/pyanaconda/sitecustomize.py")
dst = joinpaths(path, "site-packages")
shutil.move(src, dst)
def remove_packages(self, remove):
rdb = {}
order = []
for item in remove:
package = None
pattern = None
if item[0] == "--path":
# remove files
package = None
pattern = item[1]
else:
# remove package
package = item[0]
try:
pattern = item[1]
except IndexError:
pattern = None
if package not in rdb:
rdb[package] = [pattern]
order.append(package)
elif pattern not in rdb[package]:
rdb[package].append(pattern)
for package in order:
pattern_list = rdb[package]
logger.debug("{0}\t{1}".format(package, pattern_list))
self.yum.remove(package, pattern_list)
def cleanup_python_files(self):
for root, _, fnames in os.walk(self.root):
for fname in fnames:
if fname.endswith(".py"):
path = joinpaths(root, fname, follow_symlinks=False)
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)
def move_modules(self):
shutil.move(joinpaths(self.root, "lib/modules"),
joinpaths(self.root, "modules"))
shutil.move(joinpaths(self.root, "lib/firmware"),
joinpaths(self.root, "firmware"))
os.symlink("../modules", joinpaths(self.root, "lib/modules"))
os.symlink("../firmware", joinpaths(self.root, "lib/firmware"))
def cleanup_kernel_modules(self, keepmodules, kernelver):
logger.info("cleaning up kernel modules for %s", kernelver)
moddir = joinpaths(self.root, "modules", kernelver)
fwdir = joinpaths(self.root, "firmware")
# expand required modules
modules = set()
pattern = re.compile(r"\.ko$")
for name in keepmodules:
if name.startswith("="):
group = name[1:]
if group in ("scsi", "ata"):
mpath = joinpaths(moddir, "modules.block")
elif group == "net":
mpath = joinpaths(moddir, "modules.networking")
else:
mpath = joinpaths(moddir, "modules.{0}".format(group))
if os.path.isfile(mpath):
with open(mpath, "r") as fobj:
for line in fobj:
module = pattern.sub("", line.strip())
modules.add(module)
else:
modules.add(name)
# resolve modules dependencies
moddep = joinpaths(moddir, "modules.dep")
with open(moddep, "r") as fobj:
lines = map(lambda line: line.strip(), fobj.readlines())
modpattern = re.compile(r"^.*/(?P<name>.*)\.ko:(?P<deps>.*)$")
deppattern = re.compile(r"^.*/(?P<name>.*)\.ko$")
unresolved = True
while unresolved:
unresolved = False
for line in lines:
match = modpattern.match(line)
modname = match.group("name")
if modname in modules:
# add the dependencies
for dep in match.group("deps").split():
match = deppattern.match(dep)
depname = match.group("name")
if depname not in modules:
unresolved = True
modules.add(depname)
# required firmware
firmware = set()
firmware.add("atmel_at76c504c-wpa.bin")
firmware.add("iwlwifi-3945-1.ucode")
firmware.add("iwlwifi-3945.ucode")
firmware.add("zd1211/zd1211_uph")
firmware.add("zd1211/zd1211_uphm")
firmware.add("zd1211/zd1211b_uph")
firmware.add("zd1211/zd1211b_uphm")
# remove not needed modules
for root, _, fnames in os.walk(moddir):
for fname in fnames:
path = os.path.join(root, fname)
name, ext = os.path.splitext(fname)
if ext == ".ko":
if name not in modules:
os.unlink(path)
logger.debug("removed module {0}".format(path))
else:
# get the required firmware
cmd = [self.lcmds.MODINFO, "-F", "firmware", path]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = proc.stdout.read()
firmware |= set(output.split())
# remove not needed firmware
firmware = map(lambda fw: joinpaths(fwdir, fw), list(firmware))
for root, _, fnames in os.walk(fwdir):
for fname in fnames:
path = joinpaths(root, fname)
if path not in firmware:
os.unlink(path)
logger.debug("removed firmware {0}".format(path))
# get the modules paths
modpaths = {}
for root, _, fnames in os.walk(moddir):
for fname in fnames:
modpaths[fname] = joinpaths(root, fname)
# create the modules list
modlist = {}
for modtype, fname in (("scsi", "modules.block"),
("eth", "modules.networking")):
fname = joinpaths(moddir, fname)
with open(fname, "r") as fobj:
lines = map(lambda l: l.strip(), fobj.readlines())
lines = filter(lambda l: l, lines)
for line in lines:
modname, ext = os.path.splitext(line)
if (line not in modpaths or
modname in ("floppy", "libiscsi", "scsi_mod")):
continue
cmd = [self.lcmds.MODINFO, "-F", "description", modpaths[line]]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = proc.stdout.read()
try:
desc = output.splitlines()[0]
desc = desc.strip()[:65]
except IndexError:
desc = "{0} driver".format(modname)
info = '{0}\n\t{1}\n\t"{2}"\n'
info = info.format(modname, modtype, desc)
modlist[modname] = info
# write the module-info
moduleinfo = joinpaths(os.path.dirname(moddir), "module-info")
with open(moduleinfo, "w") as fobj:
fobj.write("Version 0\n")
for modname in sorted(modlist.keys()):
fobj.write(modlist[modname])
def compress_modules(self, kernelver):
logger.debug("compressing modules for %s", kernelver)
moddir = joinpaths(self.root, "modules", kernelver)
for root, _, fnames in os.walk(moddir):
for fname in filter(lambda f: f.endswith(".ko"), fnames):
path = os.path.join(root, fname)
with open(path, "rb") as fobj:
data = fobj.read()
gzipped = gzip.open("{0}.gz".format(path), "wb")
gzipped.write(data)
gzipped.close()
os.unlink(path)
def run_depmod(self, kernelver):
logger.debug("running depmod for %s", kernelver)
systemmap = "System.map-{0}".format(kernelver)
systemmap = joinpaths(self.root, "boot", systemmap)
cmd = [self.lcmds.DEPMOD, "-a", "-F", systemmap, "-b", self.root,
kernelver]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
retcode = proc.wait()
if not retcode == 0:
logger.critical(proc.stdout.read())
sys.exit(1)
moddir = joinpaths(self.root, "modules", kernelver)
# remove *map files
mapfiles = joinpaths(moddir, "*map")
for fpath in glob.glob(mapfiles):
os.unlink(fpath)
# remove build and source symlinks
for fname in ["build", "source"]:
os.unlink(joinpaths(moddir, fname))
def move_repos(self):
src = joinpaths(self.root, "etc/yum.repos.d")
dst = joinpaths(self.root, "etc/anaconda.repos.d")
shutil.move(src, dst)
def create_depmod_conf(self):
text = "search updates built-in\n"
with open(joinpaths(self.root, "etc/depmod.d/dd.conf"), "w") as fobj:
fobj.write(text)
def setup_s390_init(self):
# copy shutdown
src = joinpaths(self.root, "usr", self.libdir, "anaconda/shutdown")
dst = joinpaths(self.root, "sbin", "shutdown")
os.unlink(dst)
shutil.copy2(src, dst)
# copy linuxrc.s390
src = joinpaths(self.root, "usr/share/anaconda/linuxrc.s390")
dst = joinpaths(self.root, "sbin", "init")
os.unlink(dst)
shutil.copy2(src, dst)
def setup_init(self):
# replace init with anaconda init
src = joinpaths(self.root, "usr", self.libdir, "anaconda", "init")
dst = joinpaths(self.root, "sbin", "init")
os.unlink(dst)
shutil.copy2(src, dst)
# init symlinks
target = "/sbin/init"
name = joinpaths(self.root, "init")
os.symlink(target, name)
for fname in ["halt", "poweroff", "reboot"]:
name = joinpaths(self.root, "sbin", fname)
os.unlink(name)
os.symlink("init", name)
for fname in ["runlevel", "shutdown", "telinit"]:
name = joinpaths(self.root, "sbin", fname)
os.unlink(name)
def misc_tree_modifications(self):
# create resolv.conf
touch(joinpaths(self.root, "etc", "resolv.conf"))
# create a basic /bin/login script that'll automatically start up
# bash as a login shell. This is needed because we can't pass bash
# arguments from the agetty command line, and there's not really a
# better way to autologin root.
with open(joinpaths(self.root, "bin/login"), "w") as fobj:
fobj.write("#!/bin/bash\n")
fobj.write("exec -l /bin/bash\n")
def get_config_files(self, src_dir):
# anaconda needs to change a couple of the default gconf entries
gconf = joinpaths(self.root, "etc", "gconf", "gconf.xml.defaults")
# 0 - path, 1 - entry type, 2 - value
gconf_settings = \
[("/apps/metacity/general/button_layout", "string", ":"),
("/apps/metacity/general/action_right_click_titlebar",
"string", "none"),
("/apps/metacity/general/num_workspaces", "int", "1"),
("/apps/metacity/window_keybindings/close", "string", "disabled"),
("/apps/metacity/global_keybindings/run_command_window_screenshot",
"string", "disabled"),
("/apps/metacity/global_keybindings/run_command_screenshot",
"string", "disabled"),
("/apps/metacity/global_keybindings/switch_to_workspace_down",
"string", "disabled"),
("/apps/metacity/global_keybindings/switch_to_workspace_left",
"string", "disabled"),
("/apps/metacity/global_keybindings/switch_to_workspace_right",
"string", "disabled"),
("/apps/metacity/global_keybindings/switch_to_workspace_up",
"string", "disabled"),
("/desktop/gnome/interface/accessibility", "bool", "true"),
("/desktop/gnome/interface/at-spi-corba", "bool", "true")]
for path, entry_type, value in gconf_settings:
cmd = [self.lcmds.GCONFTOOL, "--direct",
"--config-source=xml:readwrite:{0}".format(gconf),
"-s", "-t", entry_type, path, value]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
proc.wait()
# get rsyslog config
src = joinpaths(src_dir, "rsyslog.conf")
dst = joinpaths(self.root, "etc")
shutil.copy2(src, dst)
# get .bash_history
src = joinpaths(src_dir, ".bash_history")
dst = joinpaths(self.root, "root")
shutil.copy2(src, dst)
# get .profile
src = joinpaths(src_dir, ".profile")
dst = joinpaths(self.root, "root")
shutil.copy2(src, dst)
# get libuser.conf
src = joinpaths(src_dir, "libuser.conf")
dst = joinpaths(self.root, "etc")
shutil.copy2(src, dst)
# get selinux config
if os.path.exists(joinpaths(self.root, "etc/selinux/targeted")):
src = joinpaths(src_dir, "selinux.config")
dst = joinpaths(self.root, "etc/selinux", "config")
shutil.copy2(src, dst)
def setup_sshd(self, src_dir):
# get sshd config
src = joinpaths(src_dir, "sshd_config.anaconda")
dst = joinpaths(self.root, "etc", "ssh")
shutil.copy2(src, dst)
src = joinpaths(src_dir, "pam.sshd")
dst = joinpaths(self.root, "etc", "pam.d", "sshd")
shutil.copy2(src, dst)
dst = joinpaths(self.root, "etc", "pam.d", "login")
shutil.copy2(src, dst)
dst = joinpaths(self.root, "etc", "pam.d", "remote")
shutil.copy2(src, dst)
# enable root shell logins and
# 'install' account that starts anaconda on login
passwd = joinpaths(self.root, "etc", "passwd")
with open(passwd, "a") as fobj:
fobj.write("sshd:x:74:74:Privilege-separated "
"SSH:/var/empty/sshd:/sbin/nologin\n")
fobj.write("install:x:0:0:root:/root:/sbin/loader\n")
shadow = joinpaths(self.root, "etc", "shadow")
with open(shadow, "w") as fobj:
fobj.write("root::14438:0:99999:7:::\n")
fobj.write("install::14438:0:99999:7:::\n")
# change permissions
chmod_(shadow, 400)
def generate_ssh_keys(self):
logger.info("generating SSH1 RSA host key")
rsa1 = joinpaths(self.root, "etc/ssh/ssh_host_key")
cmd = [self.lcmds.SSHKEYGEN, "-q", "-t", "rsa1", "-f", rsa1,
"-C", "", "-N", ""]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.wait()
logger.info("generating SSH2 RSA host key")
rsa2 = joinpaths(self.root, "etc/ssh/ssh_host_rsa_key")
cmd = [self.lcmds.SSHKEYGEN, "-q", "-t", "rsa", "-f", rsa2,
"-C", "", "-N", ""]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.wait()
logger.info("generating SSH2 DSA host key")
dsa = joinpaths(self.root, "etc/ssh/ssh_host_dsa_key")
cmd = [self.lcmds.SSHKEYGEN, "-q", "-t", "dsa", "-f", dsa,
"-C", "", "-N", ""]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.wait()
# change key file permissions
for key in [rsa1, rsa2, dsa]:
chmod_(key, 0600)
chmod_(key + ".pub", 0644)
def get_anaconda_portions(self):
src = joinpaths(self.root, "usr", self.libdir, "anaconda", "loader")
dst = joinpaths(self.root, "sbin")
shutil.copy2(src, dst)
src = joinpaths(self.root, "usr/share/anaconda", "loader.tr")
dst = joinpaths(self.root, "etc")
shutil.move(src, dst)
src = joinpaths(self.root, "usr/libexec/anaconda", "auditd")
dst = joinpaths(self.root, "sbin")
shutil.copy2(src, dst)
def compress(self, outfile, type="xz", speed="9"):
chdir = lambda: os.chdir(self.root)
start = time.time()
find = subprocess.Popen([self.lcmds.FIND, "."], stdout=subprocess.PIPE,
preexec_fn=chdir)
cpio = subprocess.Popen([self.lcmds.CPIO,
"--quiet", "-H", "newc", "-o"],
stdin=find.stdout, stdout=subprocess.PIPE,
preexec_fn=chdir)
compressed = subprocess.Popen([type, "-%s" % speed], stdin=cpio.stdout,
stdout=open(outfile, "wb"))
logger.debug("compressing")
rc = compressed.wait()
elapsed = time.time() - start
return True, elapsed
def install_kernel_modules(self, keepmodules):
self.move_modules()
for kernel in os.listdir(joinpaths(self.root, "modules")):
self.cleanup_kernel_modules(keepmodules, kernel)
self.compress_modules(kernel)
self.run_depmod(kernel)

View File

@ -21,153 +21,12 @@
import logging import logging
logger = logging.getLogger("pylorax.yumhelper") logger = logging.getLogger("pylorax.yumhelper")
import sys, os, re
import sys import yum, yum.callbacks, yum.rpmtrans
import os
import fnmatch
import glob
import shutil
import re
import yum
import yum.callbacks
import yum.rpmtrans
import output import output
from sysutils import joinpaths
class LoraxYumHelper(object):
def __init__(self, ybo):
self.ybo = ybo
# create our own installroot, the pungi one may be poluted
installroot = joinpaths(self.ybo.conf.installroot, "installroot")
os.makedirs(installroot)
self.ybo.conf.installroot = installroot
self.installroot = self.ybo.conf.installroot
self.installed_packages = self.get_packages("installed")
def install(self, pattern):
try:
self.ybo.install(name=pattern)
except yum.Errors.InstallError:
try:
self.ybo.install(pattern=pattern)
except yum.Errors.InstallError as e:
msg = "cannot install {0}: {1}"
logger.error(msg.format(pattern, e))
return False
return True
def remove(self, package, pattern_list):
if package:
pkgobj = self.installed_packages.get(package)
if not pkgobj:
msg = "cannot erase {0}: Package not installed"
logger.error(msg.format(package))
return False
# match every file if no pattern specified
if None in pattern_list:
if len(pattern_list) > 1:
msg = "redundant patterns specified, " \
"removing whole package {0}"
logger.warning(msg.format(pkgobj.name))
pattern_list = ["*"]
logger.debug("erasing package {0}".format(pkgobj.name))
total = len(pkgobj.filelist)
newline = False
count = 0
for n, fname in enumerate(pkgobj.filelist, start=1):
msg = "[{0:3.0f}%] erasing <b>{1.ui_envra}</b>\r"
msg = msg.format(float(n) / float(total) * 100, pkgobj)
output.LoraxOutput().write(msg)
newline = True
for pattern in pattern_list:
if fnmatch.fnmatch(fname, pattern):
fullpath = joinpaths(self.installroot, fname)
if (os.path.islink(fullpath) or
os.path.isfile(fullpath)):
os.unlink(fullpath)
logger.debug("removed {0}".format(fullpath))
count += 1
if newline:
output.LoraxOutput().write("\n")
if not count:
msg = "no files matched patterns {0}"
logger.warning(msg.format(pattern_list))
else:
for pattern in pattern_list:
msg = "erasing files matching pattern {0}"
logger.info(msg.format(pattern))
fullpattern = joinpaths(self.installroot, pattern)
count = 0
for fname in glob.glob(fullpattern):
# if there are symlinks, we could already removed the file
if not os.path.exists(fname):
continue
if os.path.islink(fname) or os.path.isfile(fname):
os.unlink(fname)
else:
shutil.rmtree(fname)
logger.debug("removed {0}".format(fname))
count += 1
if not count:
msg = "no files matched pattern {0}"
logger.error(msg.format(pattern))
return True
def process_transaction(self, skip_broken=False):
# skip broken
self.ybo.conf.skip_broken = skip_broken
self.ybo.buildTransaction()
self.ybo.repos.setProgressBar(LoraxDownloadCallback())
try:
self.ybo.processTransaction(callback=LoraxTransactionCallback(),
rpmDisplay=LoraxRpmCallback())
except yum.Errors.YumRPMCheckError as e:
logger.error("yum transaction error: {0}".format(e))
sys.exit(1)
self.ybo.closeRpmDB()
self.installed_packages = self.get_packages("installed")
def search(self, pattern):
pl = self.ybo.doPackageLists(patterns=[pattern])
return pl.installed, pl.available
def get_packages(self, ptype="available"):
if ptype not in ("available", "installed"):
raise TypeError
pl = self.ybo.doPackageLists(pkgnarrow=ptype)
d = {}
for pkgobj in getattr(pl, ptype):
d[pkgobj.name] = pkgobj
return d
__all__ = ['LoraxDownloadCallback', 'LoraxTransactionCallback',
'LoraxRpmCallback']
class LoraxDownloadCallback(yum.callbacks.DownloadBaseCallback): class LoraxDownloadCallback(yum.callbacks.DownloadBaseCallback):