crazytime: replace installtree with runtimebuilder
This commit is contained in:
parent
46e18c3781
commit
905e05159d
1265
share/ramdisk.ltmpl
1265
share/ramdisk.ltmpl
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,5 @@
|
||||
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)
|
||||
- run prelink on runtime image
|
||||
- chroot ${root} /etc/cron.daily/prelink
|
||||
|
||||
|
||||
Good ideas:
|
||||
@ -21,6 +7,8 @@ Good ideas:
|
||||
- update imgutils to be able to partition images
|
||||
- via dmsetup stuff in anaconda.storage?
|
||||
- allow alternate runtime images via conf key
|
||||
- run prelink on runtime image
|
||||
- chroot ${root} /etc/cron.daily/prelink
|
||||
|
||||
|
||||
Someday:
|
||||
|
@ -38,15 +38,13 @@ from base import BaseLoraxClass, DataHolder
|
||||
import output
|
||||
|
||||
import yum
|
||||
import yumhelper
|
||||
import ltmpl
|
||||
|
||||
import imgutils
|
||||
import constants
|
||||
from sysutils import *
|
||||
|
||||
from treebuilder import TreeBuilder
|
||||
from installtree import LoraxInstallTree
|
||||
from treebuilder import RuntimeBuilder, TreeBuilder
|
||||
from buildstamp import BuildStamp
|
||||
from treeinfo import TreeInfo
|
||||
from discinfo import DiscInfo
|
||||
@ -158,7 +156,7 @@ class Lorax(BaseLoraxClass):
|
||||
|
||||
# do we have all lorax required commands?
|
||||
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?
|
||||
logger.info("checking yum base object")
|
||||
@ -166,49 +164,25 @@ class Lorax(BaseLoraxClass):
|
||||
logger.critical("no yum base object")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("setting up yum helper")
|
||||
self.yum = yumhelper.LoraxYumHelper(ybo)
|
||||
logger.debug("using install root: {0}".format(self.yum.installroot))
|
||||
logger.debug("using install root: {0}".format(ybo.installroot))
|
||||
|
||||
logger.info("setting up build architecture")
|
||||
self.arch = ArchData(self.get_buildarch())
|
||||
self.arch = ArchData(get_buildarch(ybo))
|
||||
for attr in ('buildarch', 'basearch', 'libdir'):
|
||||
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")
|
||||
product = DataHolder(name=product, version=version, release=release,
|
||||
variant=variant, bugurl=bugurl, is_beta=is_beta)
|
||||
self.product = product
|
||||
logger.debug("product data: %s" % product)
|
||||
|
||||
logger.info("parsing the runtime template")
|
||||
tfile = joinpaths(self.conf.get("lorax", "sharedir"),
|
||||
self.conf.get("templates", "ramdisk"))
|
||||
templatedir = self.conf.get("lorax", "sharedir")
|
||||
rb = RuntimeBuilder(product, arch, self.outputdir, ybo, templatedir)
|
||||
|
||||
# TODO: normalize with arch templates:
|
||||
# tvars = dict(product=product, arch=arch)
|
||||
tvars = { "basearch": self.arch.basearch,
|
||||
"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)
|
||||
logger.info("installing runtime packages")
|
||||
rb.yum.conf.skip_broken = self.conf.getboolean("yum", "skipbroken")
|
||||
rb.install()
|
||||
|
||||
# write .buildstamp
|
||||
buildstamp = BuildStamp(self.product.name, self.product.version,
|
||||
@ -219,55 +193,13 @@ class Lorax(BaseLoraxClass):
|
||||
logger.debug("saving pkglists to %s", self.workdir)
|
||||
dname = joinpaths(self.workdir, "pkglists")
|
||||
os.makedirs(dname)
|
||||
for pkgname, pkgobj in self.installtree.yum.installed_packages.items():
|
||||
with open(joinpaths(dname, pkgname), "w") as fobj:
|
||||
for pkgobj in ybo.doPackageLists(pkgnarrow='installed').installed:
|
||||
with open(joinpaths(dname, pkgobj.name), "w") as fobj:
|
||||
for fname in pkgobj.filelist:
|
||||
fobj.write("{0}\n".format(fname))
|
||||
|
||||
logger.info("removing locales")
|
||||
self.installtree.remove_locales()
|
||||
|
||||
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()
|
||||
logger.info("doing post-install configuration")
|
||||
rb.postinstall()
|
||||
|
||||
# write .discinfo
|
||||
discinfo = DiscInfo(self.product.release, self.arch.basearch)
|
||||
@ -277,12 +209,8 @@ class Lorax(BaseLoraxClass):
|
||||
installroot = joinpaths(self.workdir, "installroot")
|
||||
linktree(self.installtree.root, installroot)
|
||||
|
||||
logger.info("getting list of not required packages")
|
||||
removepkgs = template.getdata("remove", mode="lines")
|
||||
self.installtree.remove_packages(removepkgs)
|
||||
|
||||
logger.info("cleaning up python files")
|
||||
self.installtree.cleanup_python_files()
|
||||
logger.info("cleaning unneeded files")
|
||||
rb.clean()
|
||||
|
||||
logger.info("creating the runtime image")
|
||||
# TODO: different img styles / create_runtime implementations
|
||||
@ -293,13 +221,13 @@ class Lorax(BaseLoraxClass):
|
||||
logger.info("preparing to build output tree and boot images")
|
||||
treebuilder = TreeBuilder(self.product, self.arch,
|
||||
installroot, self.outputdir,
|
||||
templatedir=self.conf.get("lorax", "sharedir"))
|
||||
templatedir)
|
||||
|
||||
# TODO: different image styles may do this part differently
|
||||
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")
|
||||
treebuilder.initrd_append(runtimedir)
|
||||
|
||||
@ -313,9 +241,9 @@ class Lorax(BaseLoraxClass):
|
||||
treeinfo.add_section(section, data)
|
||||
treeinfo.write(joinpaths(self.outputdir, ".treeinfo"))
|
||||
|
||||
def get_buildarch(self):
|
||||
def get_buildarch(ybo):
|
||||
# get architecture of the available anaconda package
|
||||
_, available = self.yum.search("anaconda")
|
||||
available = ybo.doPackageLists(patterns=["anaconda"]).available
|
||||
|
||||
if available:
|
||||
anaconda = available.pop(0)
|
||||
|
@ -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)
|
@ -21,153 +21,12 @@
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger("pylorax.yumhelper")
|
||||
|
||||
import sys
|
||||
import os
|
||||
import fnmatch
|
||||
import glob
|
||||
import shutil
|
||||
import re
|
||||
|
||||
import yum
|
||||
import yum.callbacks
|
||||
import yum.rpmtrans
|
||||
|
||||
import sys, os, re
|
||||
import yum, yum.callbacks, yum.rpmtrans
|
||||
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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user