2009-08-27 07:19:49 +00:00
|
|
|
#
|
|
|
|
# __init__.py
|
2010-01-12 11:45:54 +00:00
|
|
|
#
|
2015-05-06 17:38:57 +00:00
|
|
|
# Copyright (C) 2010-2015 Red Hat, Inc.
|
2010-01-12 11:45:54 +00:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
2010-02-23 13:20:05 +00:00
|
|
|
# Red Hat Author(s): Martin Gracik <mgracik@redhat.com>
|
|
|
|
# David Cantrell <dcantrell@redhat.com>
|
2011-05-09 23:06:25 +00:00
|
|
|
# Will Woods <wwoods@redhat.com>
|
2009-08-27 07:19:49 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
# set up logging
|
|
|
|
import logging
|
2011-01-19 14:37:44 +00:00
|
|
|
logger = logging.getLogger("pylorax")
|
2012-06-13 22:22:57 +00:00
|
|
|
logger.addHandler(logging.NullHandler())
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2015-05-27 17:36:40 +00:00
|
|
|
program_log = logging.getLogger("program")
|
|
|
|
|
2009-04-22 13:01:28 +00:00
|
|
|
import sys
|
2008-09-13 02:04:02 +00:00
|
|
|
import os
|
2015-05-06 17:38:57 +00:00
|
|
|
import configparser
|
2010-10-12 16:23:29 +00:00
|
|
|
import tempfile
|
2012-02-17 10:09:59 +00:00
|
|
|
import locale
|
2012-07-27 14:29:34 +00:00
|
|
|
from subprocess import CalledProcessError
|
2012-06-01 06:42:13 +00:00
|
|
|
import selinux
|
2016-01-30 01:08:52 +00:00
|
|
|
from glob import glob
|
2008-09-13 02:04:02 +00:00
|
|
|
|
2014-05-09 00:21:34 +00:00
|
|
|
from pylorax.base import BaseLoraxClass, DataHolder
|
|
|
|
import pylorax.output as output
|
2009-06-04 13:36:56 +00:00
|
|
|
|
2014-06-06 23:22:28 +00:00
|
|
|
import dnf
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2014-05-09 00:21:34 +00:00
|
|
|
from pylorax.sysutils import joinpaths, remove, linktree
|
2009-06-04 13:36:56 +00:00
|
|
|
|
2014-05-09 00:21:34 +00:00
|
|
|
from pylorax.treebuilder import RuntimeBuilder, TreeBuilder
|
|
|
|
from pylorax.buildstamp import BuildStamp
|
|
|
|
from pylorax.treeinfo import TreeInfo
|
|
|
|
from pylorax.discinfo import DiscInfo
|
|
|
|
from pylorax.executils import runcmd, runcmd_output
|
2010-11-23 10:14:25 +00:00
|
|
|
|
2017-08-08 22:14:15 +00:00
|
|
|
|
|
|
|
# get lorax version
|
|
|
|
try:
|
|
|
|
import pylorax.version
|
|
|
|
except ImportError:
|
|
|
|
vernum = "devel"
|
|
|
|
else:
|
|
|
|
vernum = pylorax.version.num
|
|
|
|
|
2014-02-13 16:38:20 +00:00
|
|
|
# List of drivers to remove on ppc64 arch to keep initrd < 32MiB
|
|
|
|
REMOVE_PPC64_DRIVERS = "floppy scsi_debug nouveau radeon cirrus mgag200"
|
|
|
|
REMOVE_PPC64_MODULES = "drm plymouth"
|
|
|
|
|
2011-05-10 21:40:26 +00:00
|
|
|
class ArchData(DataHolder):
|
2014-03-25 20:35:31 +00:00
|
|
|
lib64_arches = ("x86_64", "ppc64", "ppc64le", "s390x", "ia64", "aarch64")
|
2011-07-22 20:59:56 +00:00
|
|
|
bcj_arch = dict(i386="x86", x86_64="x86",
|
2014-09-02 17:58:48 +00:00
|
|
|
ppc="powerpc", ppc64="powerpc", ppc64le="powerpc",
|
2012-06-21 07:31:58 +00:00
|
|
|
arm="arm", armhfp="arm")
|
|
|
|
|
2011-04-27 20:25:35 +00:00
|
|
|
def __init__(self, buildarch):
|
2014-05-09 00:21:34 +00:00
|
|
|
super(ArchData, self).__init__()
|
2011-04-27 20:25:35 +00:00
|
|
|
self.buildarch = buildarch
|
2016-03-09 22:45:20 +00:00
|
|
|
self.basearch = dnf.rpm.basearch(buildarch)
|
2011-07-05 17:40:48 +00:00
|
|
|
self.libdir = "lib64" if self.basearch in self.lib64_arches else "lib"
|
2011-07-22 20:59:56 +00:00
|
|
|
self.bcj = self.bcj_arch.get(self.basearch)
|
2010-08-17 12:14:36 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
class Lorax(BaseLoraxClass):
|
2009-09-23 10:21:33 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
def __init__(self):
|
2010-02-23 13:20:05 +00:00
|
|
|
BaseLoraxClass.__init__(self)
|
2010-10-12 16:23:29 +00:00
|
|
|
self._configured = False
|
2014-05-09 00:21:34 +00:00
|
|
|
self.product = None
|
|
|
|
self.workdir = None
|
|
|
|
self.arch = None
|
|
|
|
self.conf = None
|
|
|
|
self.inroot = None
|
|
|
|
self.debug = False
|
|
|
|
self.outputdir = None
|
2016-01-30 01:08:52 +00:00
|
|
|
self._templatedir = None
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2012-02-17 10:09:59 +00:00
|
|
|
# set locale to C
|
|
|
|
locale.setlocale(locale.LC_ALL, 'C')
|
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
def configure(self, conf_file="/etc/lorax/lorax.conf"):
|
2015-05-06 17:38:57 +00:00
|
|
|
self.conf = configparser.SafeConfigParser()
|
2010-10-12 16:23:29 +00:00
|
|
|
|
|
|
|
# set defaults
|
|
|
|
self.conf.add_section("lorax")
|
|
|
|
self.conf.set("lorax", "debug", "1")
|
|
|
|
self.conf.set("lorax", "sharedir", "/usr/share/lorax")
|
2015-02-10 20:05:23 +00:00
|
|
|
self.conf.set("lorax", "logdir", "/var/log/lorax")
|
2010-10-12 16:23:29 +00:00
|
|
|
|
|
|
|
self.conf.add_section("output")
|
|
|
|
self.conf.set("output", "colors", "1")
|
|
|
|
self.conf.set("output", "encoding", "utf-8")
|
|
|
|
self.conf.set("output", "ignorelist", "/usr/share/lorax/ignorelist")
|
2009-09-23 10:21:33 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
self.conf.add_section("templates")
|
2010-10-27 09:23:47 +00:00
|
|
|
self.conf.set("templates", "ramdisk", "ramdisk.ltmpl")
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2011-04-21 15:06:47 +00:00
|
|
|
self.conf.add_section("compression")
|
|
|
|
self.conf.set("compression", "type", "xz")
|
2011-07-20 20:45:00 +00:00
|
|
|
self.conf.set("compression", "args", "")
|
|
|
|
self.conf.set("compression", "bcj", "on")
|
2011-04-21 15:06:47 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
# read the config file
|
|
|
|
if os.path.isfile(conf_file):
|
|
|
|
self.conf.read(conf_file)
|
|
|
|
|
|
|
|
# set up the output
|
2011-08-09 21:59:04 +00:00
|
|
|
self.debug = self.conf.getboolean("lorax", "debug")
|
|
|
|
output_level = output.DEBUG if self.debug else output.INFO
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2014-04-24 17:05:43 +00:00
|
|
|
if sys.stdout.isatty():
|
|
|
|
colors = self.conf.getboolean("output", "colors")
|
|
|
|
else:
|
|
|
|
colors = False
|
2010-10-12 16:23:29 +00:00
|
|
|
encoding = self.conf.get("output", "encoding")
|
|
|
|
|
|
|
|
self.output.basic_config(output_level=output_level,
|
|
|
|
colors=colors, encoding=encoding)
|
|
|
|
|
|
|
|
ignorelist = self.conf.get("output", "ignorelist")
|
|
|
|
if os.path.isfile(ignorelist):
|
|
|
|
with open(ignorelist, "r") as fobj:
|
|
|
|
for line in fobj:
|
|
|
|
line = line.strip()
|
|
|
|
if line and not line.startswith("#"):
|
|
|
|
self.output.ignore(line)
|
|
|
|
|
2010-12-02 11:59:08 +00:00
|
|
|
# cron does not have sbin in PATH,
|
2010-11-09 08:46:58 +00:00
|
|
|
# so we have to add it ourselves
|
|
|
|
os.environ["PATH"] = "{0}:/sbin:/usr/sbin".format(os.environ["PATH"])
|
|
|
|
|
2013-02-15 00:16:54 +00:00
|
|
|
# remove some environmental variables that can cause problems with package scripts
|
|
|
|
env_remove = ('DISPLAY', 'DBUS_SESSION_BUS_ADDRESS')
|
2015-05-06 17:38:57 +00:00
|
|
|
list(os.environ.pop(k) for k in env_remove if k in os.environ)
|
2013-02-15 00:16:54 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
self._configured = True
|
|
|
|
|
2016-01-30 01:08:52 +00:00
|
|
|
@property
|
|
|
|
def templatedir(self):
|
|
|
|
"""Find the template directory.
|
|
|
|
|
|
|
|
Pick the first directory under sharedir/templates.d/ if it exists.
|
|
|
|
Otherwise use the sharedir
|
|
|
|
"""
|
|
|
|
if not self._templatedir:
|
2016-02-11 18:50:46 +00:00
|
|
|
self._templatedir = find_templates(self.conf.get("lorax", "sharedir"))
|
|
|
|
logger.info("Using templatedir %s", self._templatedir)
|
2016-01-30 01:08:52 +00:00
|
|
|
return self._templatedir
|
|
|
|
|
2012-06-13 22:22:57 +00:00
|
|
|
def init_stream_logging(self):
|
|
|
|
sh = logging.StreamHandler()
|
|
|
|
sh.setLevel(logging.INFO)
|
|
|
|
logger.addHandler(sh)
|
|
|
|
|
2011-01-19 14:37:44 +00:00
|
|
|
def init_file_logging(self, logdir, logname="pylorax.log"):
|
|
|
|
fh = logging.FileHandler(filename=joinpaths(logdir, logname), mode="w")
|
|
|
|
fh.setLevel(logging.DEBUG)
|
|
|
|
logger.addHandler(fh)
|
|
|
|
|
2014-06-06 23:22:28 +00:00
|
|
|
def run(self, dbo, product, version, release, variant="", bugurl="",
|
2012-04-11 08:50:04 +00:00
|
|
|
isfinal=False, workdir=None, outputdir=None, buildarch=None, volid=None,
|
2014-05-02 01:34:54 +00:00
|
|
|
domacboot=True, doupgrade=True, remove_temp=False,
|
2016-11-04 19:25:33 +00:00
|
|
|
installpkgs=None, excludepkgs=None,
|
2016-08-22 23:17:35 +00:00
|
|
|
size=2,
|
2014-05-02 01:34:54 +00:00
|
|
|
add_templates=None,
|
Add ability for external templates to graft content into boot.iso
I originally added --add-template to support doing something similar
to pungi, which injects content into the system to be used by default.
However, this causes the content to be part of the squashfs, which
means PXE installations have to download significantly more data that
they may not need (if they actually want to pull the tree data from
the network, which is not an unusual case).
What I actually need is to be able to modify *both* the runtime image
and the arch-specific content. For the runtime, I need to change
/usr/share/anaconda/interactive-defaults.ks to point to the new
content. (Although, potentially we could patch Anaconda itself to
auto-detect an ostree repository configured in disk image, similar to
what it does for yum repositories)
For the arch-specfic image, I want to drop my content into the ISO
root.
So this patch adds --add-arch-template and --add-arch-template-var
in order to do the latter, while preserving the --add-template
to affect the runtime image.
Further, the templates will automatically graft in a directory named
"iso-graft/" from the working directory (if it exists).
(I suggest that external templates create a subdirectory named
"content" to avoid clashes with any future lorax work)
Thus, this will be used by the Atomic Host lorax templates to inject
content/repo, but could be used by e.g. pungi to add content/rpms as
well.
I tried to avoid code deduplication by creating a new template for the
product.img bits and this, but that broke because the parent boot.iso
code needs access to the `${imggraft}` variable. I think a real fix
here would involve turning the product.img, content/, *and* boot.iso
into a new template.
2015-03-17 21:26:21 +00:00
|
|
|
add_template_vars=None,
|
|
|
|
add_arch_templates=None,
|
2015-07-17 23:22:53 +00:00
|
|
|
add_arch_template_vars=None,
|
|
|
|
verify=True):
|
2010-10-12 16:23:29 +00:00
|
|
|
|
|
|
|
assert self._configured
|
|
|
|
|
2014-11-26 03:30:11 +00:00
|
|
|
installpkgs = installpkgs or []
|
2016-11-04 19:25:33 +00:00
|
|
|
excludepkgs = excludepkgs or []
|
2014-11-26 03:30:11 +00:00
|
|
|
|
2012-05-29 10:38:22 +00:00
|
|
|
if domacboot:
|
|
|
|
try:
|
2012-08-22 22:24:49 +00:00
|
|
|
runcmd(["rpm", "-q", "hfsplus-tools"])
|
2012-07-27 14:29:34 +00:00
|
|
|
except CalledProcessError:
|
2012-05-29 10:38:22 +00:00
|
|
|
logger.critical("you need to install hfsplus-tools to create mac images")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2011-01-19 14:37:44 +00:00
|
|
|
# set up work directory
|
|
|
|
self.workdir = workdir or tempfile.mkdtemp(prefix="pylorax.work.")
|
|
|
|
if not os.path.isdir(self.workdir):
|
|
|
|
os.makedirs(self.workdir)
|
|
|
|
|
|
|
|
# set up log directory
|
2015-02-10 20:05:23 +00:00
|
|
|
logdir = self.conf.get("lorax", "logdir")
|
2011-01-19 14:37:44 +00:00
|
|
|
if not os.path.isdir(logdir):
|
|
|
|
os.makedirs(logdir)
|
|
|
|
|
2012-06-13 22:22:57 +00:00
|
|
|
self.init_stream_logging()
|
2011-01-19 14:37:44 +00:00
|
|
|
self.init_file_logging(logdir)
|
2013-04-03 20:32:25 +00:00
|
|
|
|
2015-05-01 00:10:08 +00:00
|
|
|
logger.debug("version is %s", vernum)
|
|
|
|
logger.debug("using work directory %s", self.workdir)
|
|
|
|
logger.debug("using log directory %s", logdir)
|
2011-01-19 14:37:44 +00:00
|
|
|
|
|
|
|
# set up output directory
|
|
|
|
self.outputdir = outputdir or tempfile.mkdtemp(prefix="pylorax.out.")
|
|
|
|
if not os.path.isdir(self.outputdir):
|
|
|
|
os.makedirs(self.outputdir)
|
2015-05-01 00:10:08 +00:00
|
|
|
logger.debug("using output directory %s", self.outputdir)
|
2011-01-19 14:37:44 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
# do we have root privileges?
|
|
|
|
logger.info("checking for root privileges")
|
|
|
|
if not os.geteuid() == 0:
|
2010-08-17 12:14:36 +00:00
|
|
|
logger.critical("no root privileges")
|
|
|
|
sys.exit(1)
|
2009-09-23 10:21:33 +00:00
|
|
|
|
2012-05-29 09:01:18 +00:00
|
|
|
# is selinux disabled?
|
2012-06-04 08:54:01 +00:00
|
|
|
# With selinux in enforcing mode the rpcbind package required for
|
|
|
|
# dracut nfs module, which is in turn required by anaconda module,
|
|
|
|
# will not get installed, because it's preinstall scriptlet fails,
|
|
|
|
# resulting in an incomplete initial ramdisk image.
|
|
|
|
# The reason is that the scriptlet runs tools from the shadow-utils
|
|
|
|
# package in chroot, particularly groupadd and useradd to add the
|
|
|
|
# required rpc group and rpc user. This operation fails, because
|
|
|
|
# the selinux context on files in the chroot, that the shadow-utils
|
|
|
|
# tools need to access (/etc/group, /etc/passwd, /etc/shadow etc.),
|
|
|
|
# is wrong and selinux therefore disallows access to these files.
|
2012-05-29 09:01:18 +00:00
|
|
|
logger.info("checking the selinux mode")
|
2012-06-05 14:25:53 +00:00
|
|
|
if selinux.is_selinux_enabled() and selinux.security_getenforce():
|
2012-06-01 06:42:13 +00:00
|
|
|
logger.critical("selinux must be disabled or in Permissive mode")
|
|
|
|
sys.exit(1)
|
2012-05-29 09:01:18 +00:00
|
|
|
|
2014-06-06 23:22:28 +00:00
|
|
|
# do we have a proper dnf base object?
|
|
|
|
logger.info("checking dnf base object")
|
|
|
|
if not isinstance(dbo, dnf.Base):
|
|
|
|
logger.critical("no dnf base object")
|
2010-08-17 12:14:36 +00:00
|
|
|
sys.exit(1)
|
2014-06-06 23:22:28 +00:00
|
|
|
self.inroot = dbo.conf.installroot
|
2015-05-01 00:10:08 +00:00
|
|
|
logger.debug("using install root: %s", self.inroot)
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2012-01-05 10:26:59 +00:00
|
|
|
if not buildarch:
|
2014-06-06 23:22:28 +00:00
|
|
|
buildarch = get_buildarch(dbo)
|
2012-01-05 10:26:59 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
logger.info("setting up build architecture")
|
2012-01-05 10:26:59 +00:00
|
|
|
self.arch = ArchData(buildarch)
|
2011-04-27 20:25:35 +00:00
|
|
|
for attr in ('buildarch', 'basearch', 'libdir'):
|
|
|
|
logger.debug("self.arch.%s = %s", attr, getattr(self.arch,attr))
|
2010-08-17 12:14:36 +00:00
|
|
|
|
2010-10-12 16:23:29 +00:00
|
|
|
logger.info("setting up build parameters")
|
2015-05-18 15:23:27 +00:00
|
|
|
self.product = DataHolder(name=product, version=version, release=release,
|
|
|
|
variant=variant, bugurl=bugurl, isfinal=isfinal)
|
|
|
|
logger.debug("product data: %s", self.product)
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2012-02-17 09:53:18 +00:00
|
|
|
# NOTE: if you change isolabel, you need to change pungi to match, or
|
|
|
|
# the pungi images won't boot.
|
2015-05-18 15:23:27 +00:00
|
|
|
isolabel = volid or "%s-%s-%s" % (self.product.name, self.product.version, self.arch.basearch)
|
2012-02-17 09:53:18 +00:00
|
|
|
|
|
|
|
if len(isolabel) > 32:
|
|
|
|
logger.fatal("the volume id cannot be longer than 32 characters")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2014-06-06 23:22:28 +00:00
|
|
|
# NOTE: rb.root = dbo.conf.installroot (== self.inroot)
|
2011-06-22 23:22:33 +00:00
|
|
|
rb = RuntimeBuilder(product=self.product, arch=self.arch,
|
2016-01-30 01:08:52 +00:00
|
|
|
dbo=dbo, templatedir=self.templatedir,
|
2014-11-26 03:30:11 +00:00
|
|
|
installpkgs=installpkgs,
|
2016-11-04 19:25:33 +00:00
|
|
|
excludepkgs=excludepkgs,
|
2014-05-02 01:34:54 +00:00
|
|
|
add_templates=add_templates,
|
|
|
|
add_template_vars=add_template_vars)
|
2011-04-21 15:06:47 +00:00
|
|
|
|
2011-05-14 07:28:03 +00:00
|
|
|
logger.info("installing runtime packages")
|
|
|
|
rb.install()
|
2010-10-12 16:23:29 +00:00
|
|
|
|
2010-11-08 12:52:11 +00:00
|
|
|
# write .buildstamp
|
2011-04-28 18:10:31 +00:00
|
|
|
buildstamp = BuildStamp(self.product.name, self.product.version,
|
2011-09-21 18:34:27 +00:00
|
|
|
self.product.bugurl, self.product.isfinal, self.arch.buildarch)
|
2010-11-08 12:52:11 +00:00
|
|
|
|
2011-05-17 22:14:05 +00:00
|
|
|
buildstamp.write(joinpaths(self.inroot, ".buildstamp"))
|
2010-11-03 13:11:08 +00:00
|
|
|
|
2011-08-09 21:59:57 +00:00
|
|
|
if self.debug:
|
2011-08-09 22:18:12 +00:00
|
|
|
rb.writepkglists(joinpaths(logdir, "pkglists"))
|
|
|
|
rb.writepkgsizes(joinpaths(logdir, "original-pkgsizes.txt"))
|
2010-11-03 12:40:03 +00:00
|
|
|
|
2011-05-14 07:28:03 +00:00
|
|
|
logger.info("doing post-install configuration")
|
2011-08-08 23:03:53 +00:00
|
|
|
rb.postinstall()
|
2010-10-19 15:35:50 +00:00
|
|
|
|
2010-11-03 13:11:08 +00:00
|
|
|
# write .discinfo
|
2011-04-28 18:10:31 +00:00
|
|
|
discinfo = DiscInfo(self.product.release, self.arch.basearch)
|
|
|
|
discinfo.write(joinpaths(self.outputdir, ".discinfo"))
|
2011-03-07 16:15:27 +00:00
|
|
|
|
2011-05-09 23:06:25 +00:00
|
|
|
logger.info("backing up installroot")
|
|
|
|
installroot = joinpaths(self.workdir, "installroot")
|
2011-05-17 22:14:05 +00:00
|
|
|
linktree(self.inroot, installroot)
|
2010-11-08 12:52:11 +00:00
|
|
|
|
2011-07-01 19:42:47 +00:00
|
|
|
logger.info("generating kernel module metadata")
|
|
|
|
rb.generate_module_data()
|
|
|
|
|
2011-07-01 20:30:07 +00:00
|
|
|
logger.info("cleaning unneeded files")
|
|
|
|
rb.cleanup()
|
|
|
|
|
2015-07-17 23:22:53 +00:00
|
|
|
if verify:
|
|
|
|
logger.info("verifying the installroot")
|
|
|
|
if not rb.verify():
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
|
|
|
logger.info("Skipping verify")
|
|
|
|
|
2011-08-09 22:18:12 +00:00
|
|
|
if self.debug:
|
|
|
|
rb.writepkgsizes(joinpaths(logdir, "final-pkgsizes.txt"))
|
|
|
|
|
2011-04-28 20:41:24 +00:00
|
|
|
logger.info("creating the runtime image")
|
2011-06-24 21:26:01 +00:00
|
|
|
runtime = "images/install.img"
|
2011-07-20 20:45:00 +00:00
|
|
|
compression = self.conf.get("compression", "type")
|
2015-05-08 23:20:39 +00:00
|
|
|
compressargs = self.conf.get("compression", "args").split() # pylint: disable=no-member
|
2011-07-20 20:45:00 +00:00
|
|
|
if self.conf.getboolean("compression", "bcj"):
|
|
|
|
if self.arch.bcj:
|
|
|
|
compressargs += ["-Xbcj", self.arch.bcj]
|
|
|
|
else:
|
|
|
|
logger.info("no BCJ filter for arch %s", self.arch.basearch)
|
|
|
|
rb.create_runtime(joinpaths(installroot,runtime),
|
2013-11-19 01:17:41 +00:00
|
|
|
compression=compression, compressargs=compressargs,
|
|
|
|
size=size)
|
2014-06-19 21:43:56 +00:00
|
|
|
rb.finished()
|
2011-05-04 22:51:58 +00:00
|
|
|
|
|
|
|
logger.info("preparing to build output tree and boot images")
|
2011-06-22 23:22:33 +00:00
|
|
|
treebuilder = TreeBuilder(product=self.product, arch=self.arch,
|
|
|
|
inroot=installroot, outroot=self.outputdir,
|
2012-02-17 09:53:18 +00:00
|
|
|
runtime=runtime, isolabel=isolabel,
|
2012-12-18 13:31:45 +00:00
|
|
|
domacboot=domacboot, doupgrade=doupgrade,
|
2016-01-30 01:08:52 +00:00
|
|
|
templatedir=self.templatedir,
|
Add ability for external templates to graft content into boot.iso
I originally added --add-template to support doing something similar
to pungi, which injects content into the system to be used by default.
However, this causes the content to be part of the squashfs, which
means PXE installations have to download significantly more data that
they may not need (if they actually want to pull the tree data from
the network, which is not an unusual case).
What I actually need is to be able to modify *both* the runtime image
and the arch-specific content. For the runtime, I need to change
/usr/share/anaconda/interactive-defaults.ks to point to the new
content. (Although, potentially we could patch Anaconda itself to
auto-detect an ostree repository configured in disk image, similar to
what it does for yum repositories)
For the arch-specfic image, I want to drop my content into the ISO
root.
So this patch adds --add-arch-template and --add-arch-template-var
in order to do the latter, while preserving the --add-template
to affect the runtime image.
Further, the templates will automatically graft in a directory named
"iso-graft/" from the working directory (if it exists).
(I suggest that external templates create a subdirectory named
"content" to avoid clashes with any future lorax work)
Thus, this will be used by the Atomic Host lorax templates to inject
content/repo, but could be used by e.g. pungi to add content/rpms as
well.
I tried to avoid code deduplication by creating a new template for the
product.img bits and this, but that broke because the parent boot.iso
code needs access to the `${imggraft}` variable. I think a real fix
here would involve turning the product.img, content/, *and* boot.iso
into a new template.
2015-03-17 21:26:21 +00:00
|
|
|
add_templates=add_arch_templates,
|
|
|
|
add_template_vars=add_arch_template_vars,
|
|
|
|
workdir=self.workdir)
|
2011-05-04 22:51:58 +00:00
|
|
|
|
|
|
|
logger.info("rebuilding initramfs images")
|
2017-08-14 20:20:27 +00:00
|
|
|
dracut_args = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "--add", "fips"]
|
2015-11-23 17:33:42 +00:00
|
|
|
anaconda_args = dracut_args + ["--add", "anaconda pollcdrom qemu qemu-net"]
|
2014-02-13 16:38:20 +00:00
|
|
|
|
|
|
|
# ppc64 cannot boot an initrd > 32MiB so remove some drivers
|
2014-09-02 17:58:48 +00:00
|
|
|
if self.arch.basearch in ("ppc64", "ppc64le"):
|
2014-02-13 16:38:20 +00:00
|
|
|
dracut_args.extend(["--omit-drivers", REMOVE_PPC64_DRIVERS])
|
2012-11-13 06:33:16 +00:00
|
|
|
|
2014-09-11 20:39:16 +00:00
|
|
|
# Only omit dracut modules from the initrd so that they're kept for
|
|
|
|
# upgrade.img
|
|
|
|
anaconda_args.extend(["--omit", REMOVE_PPC64_MODULES])
|
|
|
|
|
2012-11-13 06:33:16 +00:00
|
|
|
treebuilder.rebuild_initrds(add_args=anaconda_args)
|
|
|
|
|
2011-05-04 22:51:58 +00:00
|
|
|
logger.info("populating output tree and building boot images")
|
2011-04-28 20:41:24 +00:00
|
|
|
treebuilder.build()
|
2011-05-04 22:51:58 +00:00
|
|
|
|
2011-05-11 18:07:46 +00:00
|
|
|
# write .treeinfo file and we're done
|
2011-05-04 22:51:58 +00:00
|
|
|
treeinfo = TreeInfo(self.product.name, self.product.version,
|
|
|
|
self.product.variant, self.arch.basearch)
|
2011-05-10 19:46:25 +00:00
|
|
|
for section, data in treebuilder.treeinfo_data.items():
|
2011-04-28 20:41:24 +00:00
|
|
|
treeinfo.add_section(section, data)
|
2011-04-28 18:10:31 +00:00
|
|
|
treeinfo.write(joinpaths(self.outputdir, ".treeinfo"))
|
2010-11-03 13:11:08 +00:00
|
|
|
|
2012-04-11 08:50:04 +00:00
|
|
|
# cleanup
|
|
|
|
if remove_temp:
|
|
|
|
remove(self.workdir)
|
|
|
|
|
|
|
|
|
2014-06-06 23:22:28 +00:00
|
|
|
def get_buildarch(dbo):
|
2011-05-14 07:28:03 +00:00
|
|
|
# get architecture of the available anaconda package
|
2011-05-26 18:09:46 +00:00
|
|
|
buildarch = None
|
2014-06-06 23:22:28 +00:00
|
|
|
q = dbo.sack.query()
|
|
|
|
a = q.available()
|
2017-05-30 17:52:08 +00:00
|
|
|
for anaconda in a.filter(name="anaconda-core"):
|
2011-05-26 18:09:46 +00:00
|
|
|
if anaconda.arch != "src":
|
|
|
|
buildarch = anaconda.arch
|
|
|
|
break
|
|
|
|
if not buildarch:
|
2017-05-30 17:52:08 +00:00
|
|
|
logger.critical("no anaconda-core package in the repository")
|
2012-01-05 10:26:59 +00:00
|
|
|
sys.exit(1)
|
2011-05-14 07:28:03 +00:00
|
|
|
|
|
|
|
return buildarch
|
2015-05-27 17:36:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
def setup_logging(logfile, theLogger):
|
|
|
|
"""
|
|
|
|
Setup the various logs
|
|
|
|
|
|
|
|
:param logfile: filename to write the log to
|
|
|
|
:type logfile: string
|
|
|
|
:param theLogger: top-level logger
|
|
|
|
:type theLogger: logging.Logger
|
|
|
|
"""
|
|
|
|
if not os.path.isdir(os.path.abspath(os.path.dirname(logfile))):
|
|
|
|
os.makedirs(os.path.abspath(os.path.dirname(logfile)))
|
|
|
|
|
|
|
|
# Setup logging to console and to logfile
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
theLogger.setLevel(logging.DEBUG)
|
|
|
|
|
|
|
|
sh = logging.StreamHandler()
|
|
|
|
sh.setLevel(logging.INFO)
|
|
|
|
fmt = logging.Formatter("%(asctime)s: %(message)s")
|
|
|
|
sh.setFormatter(fmt)
|
|
|
|
logger.addHandler(sh)
|
|
|
|
theLogger.addHandler(sh)
|
|
|
|
|
|
|
|
fh = logging.FileHandler(filename=logfile, mode="w")
|
|
|
|
fh.setLevel(logging.DEBUG)
|
|
|
|
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
|
|
fh.setFormatter(fmt)
|
|
|
|
logger.addHandler(fh)
|
|
|
|
theLogger.addHandler(fh)
|
|
|
|
|
|
|
|
# External program output log
|
|
|
|
program_log.setLevel(logging.DEBUG)
|
|
|
|
f = os.path.abspath(os.path.dirname(logfile))+"/program.log"
|
|
|
|
fh = logging.FileHandler(filename=f, mode="w")
|
|
|
|
fh.setLevel(logging.DEBUG)
|
|
|
|
program_log.addHandler(fh)
|
2016-02-11 18:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
def find_templates(templatedir="/usr/share/lorax"):
|
|
|
|
""" Find the templates to use.
|
|
|
|
|
|
|
|
:param str templatedir: Top directory to search for templates
|
|
|
|
:returns: Path to templates
|
|
|
|
:rtype: str
|
|
|
|
|
|
|
|
If there is a templates.d directory under templatedir the
|
|
|
|
lowest numbered directory entry is returned.
|
|
|
|
|
|
|
|
eg. /usr/share/lorax/templates.d/99-generic/
|
|
|
|
"""
|
|
|
|
if os.path.isdir(joinpaths(templatedir, "templates.d")):
|
|
|
|
try:
|
|
|
|
templatedir = sorted(glob(joinpaths(templatedir, "templates.d", "*")))[0]
|
|
|
|
except IndexError:
|
|
|
|
pass
|
|
|
|
return templatedir
|