lorax/src/sbin/lorax
Marek Marczykowski-Górecki 27e611629f Add --squashfs-only option to drop inner rootfs.img layer
Make runtime directly into squashfs image. This reduces largely
unreproducible ext4 layer, but requires anaconda's dracut module
modification to properly mount the image.
2018-10-29 13:16:38 -07:00

316 lines
11 KiB
Python
Executable File

#!/usr/bin/python3
#
# lorax
#
# Copyright (C) 2009-2015 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
log = logging.getLogger("lorax")
dnf_log = logging.getLogger("dnf")
import sys
import os
import tempfile
import shutil
import dnf
import dnf.logging
import librepo
import pylorax
from pylorax import DRACUT_DEFAULT, DEFAULT_PLATFORM_ID
from pylorax.cmdline import lorax_parser
from pylorax.sysutils import flatconfig
import selinux
def setup_logging(opts):
pylorax.setup_logging(opts.logfile, log)
# dnf logging
dnf_log.setLevel(dnf.logging.DDEBUG)
logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/dnf.log"
fh = logging.FileHandler(filename=logfile, mode="w")
fh.setLevel(logging.NOTSET)
dnf_log.addHandler(fh)
# Setup librepo logging
logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/dnf.librepo.log"
librepo.log_set_file(logfile)
def main():
parser = lorax_parser(DRACUT_DEFAULT)
opts = parser.parse_args()
log.info("Lorax v%s", pylorax.vernum)
if not opts.source and not opts.repos:
parser.error("--source, --repo, or both are required.")
if not opts.force and os.path.exists(opts.outputdir):
parser.error("output directory %s should not exist." % opts.outputdir)
if not os.path.exists(os.path.dirname(opts.logfile)):
os.makedirs(os.path.dirname(opts.logfile))
if opts.sharedir and not os.path.isdir(opts.sharedir):
parser.error("sharedir %s doesn't exist." % opts.sharedir)
if opts.config and not os.path.exists(opts.config):
parser.error("config file %s doesn't exist." % opts.config)
setup_logging(opts)
log.info("checking the selinux mode")
if selinux.is_selinux_enabled() and selinux.security_getenforce():
log.critical("selinux must be disabled or in Permissive mode")
sys.exit(1)
if not opts.workdir:
tempfile.tempdir = opts.tmp
# create the temporary directory for lorax
tempdir = tempfile.mkdtemp(prefix="lorax.", dir=tempfile.gettempdir())
else:
tempdir = opts.workdir
if not os.path.exists(tempdir):
os.makedirs(tempdir)
installtree = os.path.join(tempdir, "installtree")
if not os.path.exists(installtree):
os.mkdir(installtree)
dnftempdir = os.path.join(tempdir, "dnf")
if not os.path.exists(dnftempdir):
os.mkdir(dnftempdir)
dnfbase = get_dnf_base_object(installtree, opts.source, opts.mirrorlist, opts.repos,
opts.enablerepos, opts.disablerepos,
dnftempdir, opts.proxy, opts.version, opts.cachedir,
os.path.dirname(opts.logfile), not opts.noverifyssl,
opts.dnfplugins)
if dnfbase is None:
print("error: unable to create the dnf base object", file=sys.stderr)
if not opts.workdir:
shutil.rmtree(tempdir)
sys.exit(1)
parsed_add_template_vars = {}
for kv in opts.add_template_vars:
k, t, v = kv.partition('=')
if t == '':
raise ValueError("Missing '=' for key=value in " % kv)
parsed_add_template_vars[k] = v
parsed_add_arch_template_vars = {}
for kv in opts.add_arch_template_vars:
k, t, v = kv.partition('=')
if t == '':
raise ValueError("Missing '=' for key=value in " % kv)
parsed_add_arch_template_vars[k] = v
if 'SOURCE_DATE_EPOCH' in os.environ:
log.info("Using SOURCE_DATE_EPOCH=%s as the current time.", os.environ["SOURCE_DATE_EPOCH"])
# run lorax
lorax = pylorax.Lorax()
lorax.configure(conf_file=opts.config)
lorax.conf.set("lorax", "logdir", os.path.dirname(opts.logfile))
# Override the config file's template sharedir
if opts.sharedir:
lorax.conf.set("lorax", "sharedir", opts.sharedir)
lorax.run(dnfbase, opts.product, opts.version, opts.release,
opts.variant, opts.bugurl, opts.isfinal,
workdir=tempdir, outputdir=opts.outputdir, buildarch=opts.buildarch,
volid=opts.volid, domacboot=opts.domacboot, doupgrade=opts.doupgrade,
installpkgs=opts.installpkgs, excludepkgs=opts.excludepkgs,
size=opts.rootfs_size,
add_templates=opts.add_templates,
add_template_vars=parsed_add_template_vars,
add_arch_templates=opts.add_arch_templates,
add_arch_template_vars=parsed_add_arch_template_vars,
remove_temp=True, verify=opts.verify,
user_dracut_args=opts.dracut_args,
squashfs_only=opts.squashfs_only)
def get_dnf_base_object(installroot, sources, mirrorlists=None, repos=None,
enablerepos=None, disablerepos=None,
tempdir="/var/tmp", proxy=None, releasever="29",
cachedir=None, logdir=None, sslverify=True, dnfplugins=None):
""" Create a dnf Base object and setup the repositories and installroot
:param string installroot: Full path to the installroot
:param list sources: List of source repo urls to use for the installation
:param list enablerepos: List of repo names to enable
:param list disablerepos: List of repo names to disable
:param list mirrorlist: List of mirrors to use
:param string tempdir: Path of temporary directory
:param string proxy: http proxy to use when fetching packages
:param string releasever: Release version to pass to dnf
:param string cachedir: Directory to use for caching packages
:param bool noverifyssl: Set to True to ignore the CA of ssl certs. eg. use self-signed ssl for https repos.
If tempdir is not set /var/tmp is used.
If cachedir is None a dnf.cache directory is created inside tmpdir
"""
def sanitize_repo(repo):
"""Convert bare paths to file:/// URIs, and silently reject protocols unhandled by yum"""
if repo.startswith("/"):
return "file://{0}".format(repo)
elif any(repo.startswith(p) for p in ('http://', 'https://', 'ftp://', 'file://')):
return repo
else:
return None
mirrorlists = mirrorlists or []
# sanitize the repositories
sources = list(sanitize_repo(r) for r in sources)
mirrorlists = list(sanitize_repo(r) for r in mirrorlists)
# remove invalid repositories
sources = list(r for r in sources if r)
mirrorlists = list(r for r in mirrorlists if r)
if not cachedir:
cachedir = os.path.join(tempdir, "dnf.cache")
if not os.path.isdir(cachedir):
os.mkdir(cachedir)
if not logdir:
logdir = os.path.join(tempdir, "dnf.logs")
if not os.path.isdir(logdir):
os.mkdir(logdir)
dnfbase = dnf.Base()
# Enable DNF pluings
# NOTE: These come from the HOST system's environment
if dnfplugins:
if dnfplugins[0] == "*":
# Enable them all
dnfbase.init_plugins()
else:
# Only enable the listed plugins
dnfbase.init_plugins(disabled_glob=["*"], enable_plugins=dnfplugins)
conf = dnfbase.conf
conf.logdir = logdir
conf.cachedir = cachedir
conf.install_weak_deps = False
conf.releasever = releasever
conf.installroot = installroot
conf.prepend_installroot('persistdir')
# this is a weird 'AppendOption' thing that, when you set it,
# actually appends. Doing this adds 'nodocs' to the existing list
# of values, over in libdnf, it does not replace the existing values.
conf.tsflags = ['nodocs']
if proxy:
conf.proxy = proxy
if sslverify == False:
conf.sslverify = False
# DNF 3.2 needs to have module_platform_id set, otherwise depsolve won't work correctly
if not os.path.exists("/etc/os-release"):
log.warning("/etc/os-release is missing, cannot determine platform id, falling back to %s", DEFAULT_PLATFORM_ID)
platform_id = DEFAULT_PLATFORM_ID
else:
os_release = flatconfig("/etc/os-release")
platform_id = os_release.get("PLATFORM_ID", DEFAULT_PLATFORM_ID)
log.info("Using %s for module_platform_id", platform_id)
conf.module_platform_id = platform_id
# Add .repo files
if repos:
reposdir = os.path.join(tempdir, "dnf.repos")
if not os.path.isdir(reposdir):
os.mkdir(reposdir)
for r in repos:
shutil.copy2(r, reposdir)
conf.reposdir = [reposdir]
dnfbase.read_all_repos()
# add the sources
for i, r in enumerate(sources):
if "SRPM" in r or "srpm" in r:
log.info("Skipping source repo: %s", r)
continue
repo_name = "lorax-repo-%d" % i
repo = dnf.repo.Repo(repo_name, conf)
repo.baseurl = [r]
if proxy:
repo.proxy = proxy
repo.enable()
dnfbase.repos.add(repo)
log.info("Added '%s': %s", repo_name, r)
log.info("Fetching metadata...")
try:
repo.load()
except dnf.exceptions.RepoError as e:
log.error("Error fetching metadata for %s: %s", repo_name, e)
return None
# add the mirrorlists
for i, r in enumerate(mirrorlists):
if "SRPM" in r or "srpm" in r:
log.info("Skipping source repo: %s", r)
continue
repo_name = "lorax-mirrorlist-%d" % i
repo = dnf.repo.Repo(repo_name, conf)
repo.mirrorlist = r
if proxy:
repo.proxy = proxy
repo.enable()
dnfbase.repos.add(repo)
log.info("Added '%s': %s", repo_name, r)
log.info("Fetching metadata...")
try:
repo.load()
except dnf.exceptions.RepoError as e:
log.error("Error fetching metadata for %s: %s", repo_name, e)
return None
# Enable repos listed on the cmdline
for r in enablerepos:
repolist = dnfbase.repos.get_matching(r)
if not repolist:
log.warning("%s is an unknown repo, not enabling it", r)
else:
repolist.enable()
log.info("Enabled repo %s", r)
# Disable repos listed on the cmdline
for r in disablerepos:
repolist = dnfbase.repos.get_matching(r)
if not repolist:
log.warning("%s is an unknown repo, not disabling it", r)
else:
repolist.disable()
log.info("Disabled repo %s", r)
dnfbase.fill_sack(load_system_repo=False)
dnfbase.read_comps()
return dnfbase
if __name__ == "__main__":
main()