From 431ca6cea47d1721ac2982fa947e7a9aa388a9ca Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Fri, 6 Jun 2014 16:22:28 -0700 Subject: [PATCH] Switch lorax to use dnf instead of yum pylorax users will need to change to using dnf and pass a dnf.Base() object as the dbo argument instead of a yum object as the yum or ybo argument. See the lorax script for an example of how to do this. The lorax cmdline argument --excludepkgs has been removed since dnf doesn't appear to have any way to support it and packages should be controlled using templates anyway. --- lorax.spec | 2 +- src/pylorax/__init__.py | 30 ++++--- src/pylorax/dnfhelper.py | 107 ++++++++++++++++++++++++ src/pylorax/ltmpl.py | 80 +++++++++++++----- src/pylorax/treebuilder.py | 24 +++--- src/pylorax/yumhelper.py | 127 ----------------------------- src/sbin/lorax | 161 +++++++++++++++++-------------------- 7 files changed, 274 insertions(+), 257 deletions(-) create mode 100644 src/pylorax/dnfhelper.py delete mode 100644 src/pylorax/yumhelper.py diff --git a/lorax.spec b/lorax.spec index 526f12ea..209b7101 100644 --- a/lorax.spec +++ b/lorax.spec @@ -37,9 +37,9 @@ Requires: squashfs-tools >= 4.2 Requires: util-linux Requires: xz Requires: pigz -Requires: yum Requires: pykickstart Requires: dracut >= 030 +Requires: dnf %if 0%{?fedora} # Fedora specific deps diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index 8942d6c2..44b180bc 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -36,7 +36,7 @@ import selinux from pylorax.base import BaseLoraxClass, DataHolder import pylorax.output as output -import yum +import dnf from pylorax.sysutils import joinpaths, remove, linktree from rpmUtils.arch import getBaseArch @@ -96,9 +96,6 @@ class Lorax(BaseLoraxClass): self.conf.add_section("templates") self.conf.set("templates", "ramdisk", "ramdisk.ltmpl") - self.conf.add_section("yum") - self.conf.set("yum", "skipbroken", "0") - self.conf.add_section("compression") self.conf.set("compression", "type", "xz") self.conf.set("compression", "args", "") @@ -149,7 +146,7 @@ class Lorax(BaseLoraxClass): fh.setLevel(logging.DEBUG) logger.addHandler(fh) - def run(self, ybo, product, version, release, variant="", bugurl="", + def run(self, dbo, product, version, release, variant="", bugurl="", isfinal=False, workdir=None, outputdir=None, buildarch=None, volid=None, domacboot=True, doupgrade=True, remove_temp=False, installpkgs=None, @@ -221,16 +218,16 @@ class Lorax(BaseLoraxClass): logger.critical("selinux must be disabled or in Permissive mode") sys.exit(1) - # do we have a proper yum base object? - logger.info("checking yum base object") - if not isinstance(ybo, yum.YumBase): - logger.critical("no yum base object") + # 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") sys.exit(1) - self.inroot = ybo.conf.installroot + self.inroot = dbo.conf.installroot logger.debug("using install root: {0}".format(self.inroot)) if not buildarch: - buildarch = get_buildarch(ybo) + buildarch = get_buildarch(dbo) logger.info("setting up build architecture") self.arch = ArchData(buildarch) @@ -253,15 +250,14 @@ class Lorax(BaseLoraxClass): sys.exit(1) templatedir = self.conf.get("lorax", "sharedir") - # NOTE: rb.root = ybo.conf.installroot (== self.inroot) + # NOTE: rb.root = dbo.conf.installroot (== self.inroot) rb = RuntimeBuilder(product=self.product, arch=self.arch, - yum=ybo, templatedir=templatedir, + dbo=dbo, templatedir=templatedir, installpkgs=installpkgs, add_templates=add_templates, add_template_vars=add_template_vars) logger.info("installing runtime packages") - rb.yum.conf.skip_broken = self.conf.getboolean("yum", "skipbroken") rb.install() # write .buildstamp @@ -358,10 +354,12 @@ class Lorax(BaseLoraxClass): remove(self.workdir) -def get_buildarch(ybo): +def get_buildarch(dbo): # get architecture of the available anaconda package buildarch = None - for anaconda in ybo.doPackageLists(patterns=["anaconda"]).available: + q = dbo.sack.query() + a = q.available() + for anaconda in a.filter(name="anaconda"): if anaconda.arch != "src": buildarch = anaconda.arch break diff --git a/src/pylorax/dnfhelper.py b/src/pylorax/dnfhelper.py new file mode 100644 index 00000000..8bf82302 --- /dev/null +++ b/src/pylorax/dnfhelper.py @@ -0,0 +1,107 @@ +# +# dnfhelper.py +# +# Copyright (C) 2010-2014 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 . +# +# Red Hat Author(s): Martin Gracik +# Brian C. Lane +# + +import logging +logger = logging.getLogger("pylorax.dnfhelper") +import dnf +import collections +import time +import pylorax.output as output + +__all__ = ['LoraxDownloadCallback', 'LoraxRpmCallback'] + +def _paced(fn): + """Execute `fn` no more often then every 2 seconds.""" + def paced_fn(self, *args): + now = time.time() + if now - self.last_time < 2: + return + self.last_time = now + return fn(self, *args) + return paced_fn + + +class LoraxDownloadCallback(dnf.callback.DownloadProgress): + def __init__(self): + self.downloads = collections.defaultdict(int) + self.last_time = time.time() + self.total_files = 0 + self.total_size = 0 + + self.pkgno = 0 + self.total = 0 + + self.output = output.LoraxOutput() + + @_paced + def _update(self): + msg = "Downloading %(pkgno)s / %(total_files)s RPMs, " \ + "%(downloaded)s / %(total_size)s (%(percent)d%%) done.\n" + downloaded = sum(self.downloads.values()) + vals = { + 'downloaded' : downloaded, + 'percent' : int(100 * downloaded/self.total_size), + 'pkgno' : self.pkgno, + 'total_files' : self.total_files, + 'total_size' : self.total_size + } + self.output.write(msg % vals) + + def end(self, payload, status, err_msg): + nevra = str(payload) + if status is dnf.callback.STATUS_OK: + self.downloads[nevra] = payload.download_size + self.pkgno += 1 + self._update() + return + logger.critical("Failed to download '%s': %d - %s", nevra, status, err_msg) + + def progress(self, payload, done): + nevra = str(payload) + self.downloads[nevra] = done + self._update() + + def start(self, total_files, total_size): + self.total_files = total_files + self.total_size = total_size + + +class LoraxRpmCallback(dnf.callback.LoggingTransactionDisplay): + def __init__(self, queue): + super(LoraxRpmCallback, self).__init__() + self._queue = queue + self._last_ts = None + self.cnt = 0 + + def event(self, package, action, te_current, te_total, ts_current, ts_total): + if action == self.PKG_INSTALL and te_current == 0: + # do not report same package twice + if self._last_ts == ts_current: + return + self._last_ts = ts_current + + msg = '(%d/%d) %s.%s' % \ + (ts_current, ts_total, package.name, package.arch) + self.cnt += 1 + self._queue.put(('install', msg)) + elif action == self.TRANS_POST: + self._queue.put(('post', None)) diff --git a/src/pylorax/ltmpl.py b/src/pylorax/ltmpl.py index 99f26a57..f5d6f335 100644 --- a/src/pylorax/ltmpl.py +++ b/src/pylorax/ltmpl.py @@ -28,15 +28,18 @@ from os.path import basename, isdir from subprocess import CalledProcessError from pylorax.sysutils import joinpaths, cpfile, mvfile, replace, remove -from pylorax.yumhelper import LoraxDownloadCallback, LoraxTransactionCallback, LoraxRpmCallback +from pylorax.dnfhelper import LoraxDownloadCallback, LoraxRpmCallback from pylorax.base import DataHolder from pylorax.executils import runcmd, runcmd_output from pylorax.imgutils import mkcpio +import pylorax.output as output from mako.lookup import TemplateLookup from mako.exceptions import text_error_template import sys, traceback import struct +import dnf +import multiprocessing class LoraxTemplate(object): def __init__(self, directories=None): @@ -108,7 +111,7 @@ class LoraxTemplateRunner(object): This class parses and executes Lorax templates. Sample usage: # install a bunch of packages - runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, yum=yum_obj) + runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj) runner.run("install-packages.ltmpl") # modify a runtime dir @@ -145,11 +148,11 @@ class LoraxTemplateRunner(object): * Commands should raise exceptions for errors - don't use sys.exit() ''' - def __init__(self, inroot, outroot, yum=None, fatalerrors=True, + def __init__(self, inroot, outroot, dbo=None, fatalerrors=True, templatedir=None, defaults=None): self.inroot = inroot self.outroot = outroot - self.yum = yum + self.dbo = dbo self.fatalerrors = fatalerrors self.templatedir = templatedir or "/usr/share/lorax" self.templatefile = None @@ -166,8 +169,8 @@ class LoraxTemplateRunner(object): return joinpaths(self.inroot, path) def _filelist(self, *pkgs): - pkglist = self.yum.doPackageLists(pkgnarrow="installed", patterns=pkgs) - return set([f for pkg in pkglist.installed for f in pkg.filelist+pkg.ghostlist]) + pkglist = self.dbo.doPackageLists(pkgnarrow="installed", patterns=pkgs) + return set([f for pkg in pkglist for f in pkg.files]) def _getsize(self, *files): return sum(os.path.getsize(self._out(f)) for f in files if os.path.isfile(self._out(f))) @@ -447,9 +450,9 @@ class LoraxTemplateRunner(object): cmd = cmd[1:] try: - output = runcmd_output(cmd, cwd=cwd) - if output: - logger.debug('command output:\n%s', output) + stdout = runcmd_output(cmd, cwd=cwd) + if stdout: + logger.debug('command output:\n%s', stdout) logger.debug("command finished successfully") except CalledProcessError as e: if e.output: @@ -471,10 +474,10 @@ class LoraxTemplateRunner(object): for p in pkgs: try: - self.yum.install(pattern=p) + self.dbo.install(p) except Exception as e: # pylint: disable=broad-except # FIXME: save exception and re-raise after the loop finishes - logger.error("installpkg %s failed: %s",p,str(e)) + logger.error("installpkg %s failed: %s", p, str(e)) if required: raise @@ -501,19 +504,60 @@ class LoraxTemplateRunner(object): Actually install all the packages requested by previous 'installpkg' commands. ''' - self.yum.buildTransaction() - dl_callback = LoraxDownloadCallback() - self.yum.repos.setProgressBar(dl_callback) - self.yum.processTransaction(callback=LoraxTransactionCallback(dl_callback), - rpmDisplay=LoraxRpmCallback()) + + def do_transaction(base, queue): + try: + display = LoraxRpmCallback(queue) + base.do_transaction(display=display) + except BaseException as e: + logger.error("The transaction process has ended abruptly: %s", e) + queue.put(('quit', str(e))) + + self.dbo.reset() + try: + logger.info("Checking dependencies") + self.dbo.resolve() + except dnf.exceptions.DepsolveError as e: + logger.error("Dependency check failed: %s", e) + raise + logger.info("%d packages selected", len(self.dbo.transaction)) + if len(self.dbo.transaction) == 0: + raise Exception("No packages in transaction") + + pkgs_to_download = self.dbo.transaction.install_set + logger.info("Downloading packages") + progress = LoraxDownloadCallback() + try: + self.dbo.download_packages(pkgs_to_download, progress) + except dnf.exceptions.DownloadError as e: + logger.error("Failed to download the following packages: %s", e) + raise + + logger.info("Preparing transaction from installation source") + queue = multiprocessing.Queue() + msgout = output.LoraxOutput() + process = multiprocessing.Process(target=do_transaction, args=(self.dbo, queue)) + process.start() + (token, msg) = queue.get() + while token not in ('post', 'quit'): + if token == 'install': + logging.info("%s", msg) + msgout.writeline(msg) + (token, msg) = queue.get() + if token == 'quit': + logger.error("Transaction failed.") + raise Exception("Transaction failed") + + logger.info("Performing post-installation setup tasks") + process.join() # verify if all packages that were supposed to be installed, # are really installed - errs = [t.po for t in self.yum.tsInfo if not self.yum.rpmdb.contains(po=t.po)] + errs = [t.po for t in self.dbo.tsInfo if not self.dbo.rpmdb.contains(po=t.po)] for po in errs: logger.error("package '%s' was not installed", po) - self.yum.closeRpmDB() + self.dbo.close() def removefrom(self, pkg, *globs): ''' diff --git a/src/pylorax/treebuilder.py b/src/pylorax/treebuilder.py index baad1ac5..5a56233d 100644 --- a/src/pylorax/treebuilder.py +++ b/src/pylorax/treebuilder.py @@ -67,19 +67,19 @@ def generate_module_info(moddir, outfile=None): class RuntimeBuilder(object): '''Builds the anaconda runtime image.''' - def __init__(self, product, arch, yum, templatedir=None, + def __init__(self, product, arch, dbo, templatedir=None, installpkgs=None, add_templates=None, add_template_vars=None): - root = yum.conf.installroot + root = dbo.conf.installroot # use a copy of product so we can modify it locally product = product.copy() product.name = product.name.lower() - self.vars = DataHolder(arch=arch, product=product, yum=yum, root=root, + self.vars = DataHolder(arch=arch, product=product, dbo=dbo, root=root, basearch=arch.basearch, libdir=arch.libdir) - self.yum = yum + self.dbo = dbo self._runner = LoraxTemplateRunner(inroot=root, outroot=root, - yum=yum, templatedir=templatedir) + dbo=dbo, templatedir=templatedir) self.add_templates = add_templates or [] self.add_template_vars = add_template_vars or {} self._installpkgs = installpkgs or [] @@ -87,7 +87,9 @@ class RuntimeBuilder(object): def _install_branding(self): release = None - for pkg in self.yum.whatProvides('/etc/system-release', None, None): + q = self.dbo.sack.query() + a = q.available() + for pkg in a.filter(provides='/etc/system-release'): if pkg.name.startswith('generic'): continue else: @@ -119,9 +121,10 @@ class RuntimeBuilder(object): '''debugging data: write out lists of package contents''' if not os.path.isdir(pkglistdir): os.makedirs(pkglistdir) - for pkgobj in self.yum.doPackageLists(pkgnarrow='installed').installed: + q = self.dbo.sack.query() + for pkgobj in q.installed(): with open(joinpaths(pkglistdir, pkgobj.name), "w") as fobj: - for fname in pkgobj.filelist + pkgobj.dirlist: + for fname in pkgobj.files: fobj.write("{0}\n".format(fname)) def postinstall(self): @@ -143,8 +146,9 @@ class RuntimeBuilder(object): '''debugging data: write a big list of pkg sizes''' fobj = open(pkgsizefile, "w") getsize = lambda f: os.lstat(f).st_size if os.path.exists(f) else 0 - for p in sorted(self.yum.doPackageLists(pkgnarrow='installed').installed): - pkgsize = sum(getsize(joinpaths(self.vars.root,f)) for f in p.filelist) + q = self.dbo.sack.query() + for p in sorted(q.installed()): + pkgsize = sum(getsize(joinpaths(self.vars.root,f)) for f in p.files) fobj.write("{0.name}.{0.arch}: {1}\n".format(p, pkgsize)) def generate_module_data(self): diff --git a/src/pylorax/yumhelper.py b/src/pylorax/yumhelper.py deleted file mode 100644 index 79a32053..00000000 --- a/src/pylorax/yumhelper.py +++ /dev/null @@ -1,127 +0,0 @@ -# -# yumhelper.py -# -# Copyright (C) 2010-2014 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 . -# -# Red Hat Author(s): Martin Gracik -# - -import logging -logger = logging.getLogger("pylorax.yumhelper") -import sys -import yum, yum.callbacks, yum.rpmtrans -import pylorax.output as output - -__all__ = ['LoraxDownloadCallback', 'LoraxTransactionCallback', - 'LoraxRpmCallback'] - -class LoraxDownloadCallback(yum.callbacks.DownloadBaseCallback): - def __init__(self): - yum.callbacks.DownloadBaseCallback.__init__(self) - - self.pkgno = 0 - self.total = 0 - - self.output = output.LoraxOutput() - - - def updateProgress(self, name, frac, fread, ftime): - """ - Update the progress bar - @param name: filename - @param frac: progress fraction (0 -> 1) - @param fread: formated string containing BytesRead - @param ftime: formated string containing remaining or elapsed time - """ - # Only update when it is finished downloading - if frac < 1: - return - - self.pkgno += 1 - info = "({0:3d}/{1:3d}) " - info = info.format(self.pkgno, self.total) - - infolen, pkglen = len(info), len(name) - if (infolen + pkglen) > self.output.width: - name = "{0}...".format(name[:self.output.width-infolen-3]) - - msg = "{0}{1}\n".format(info, name) - self.output.write(msg) - - -class LoraxTransactionCallback(object): - - def __init__(self, dl_callback): - self.output = output.LoraxOutput() - - self.dl_callback = dl_callback - - def event(self, state, data=None): - if state == yum.callbacks.PT_DOWNLOAD: - self.output.write("downloading packages\n") - elif state == yum.callbacks.PT_DOWNLOAD_PKGS: - # Initialize the total number of packages being downloaded - self.dl_callback.total = len(data) - elif state == yum.callbacks.PT_GPGCHECK: - self.output.write("checking package signatures\n") - elif state == yum.callbacks.PT_TEST_TRANS: - self.output.write("running test transaction\n") - elif state == yum.callbacks.PT_TRANSACTION: - self.output.write("running transaction\n") - - -class LoraxRpmCallback(yum.rpmtrans.RPMBaseCallback): - - def __init__(self): - yum.rpmtrans.RPMBaseCallback.__init__(self) - self.output = output.LoraxOutput() - - def event(self, package, action, te_current, te_total, - ts_current, ts_total): - - action_str = self.action[action].encode("utf-8") - info = "({0:3d}/{1:3d}) [{2:3.0f}%] {3} " - info = info.format(ts_current, ts_total, - float(te_current) / float(te_total) * 100, - action_str.lower()) - - pkg = "{0}".format(package) - - infolen, pkglen = len(info), len(pkg) - if (infolen + pkglen) > self.output.width: - pkg = "{0}...".format(pkg[:self.output.width-infolen-3]) - - msg = "{0}{1}".format(info, pkg) - - # When not outputting to a tty we only want to print it once at the end - if sys.stdout.isatty(): - self.output.write(msg + "\r") - if te_current == te_total: - self.output.write("\n") - elif te_current == te_total: - self.output.write(msg + "\n") - - - def filelog(self, package, action): - if self.fileaction.get(action) == "Installed": - logger.debug("{0} installed successfully".format(package)) - - def errorlog(self, msg): - logger.warning("RPM transaction error: %s", msg) - - def scriptout(self, package, msgs): - if msgs: - logger.info("%s scriptlet output:\n%s", package, msgs) diff --git a/src/sbin/lorax b/src/sbin/lorax index ce25a87b..cfa66d1a 100755 --- a/src/sbin/lorax +++ b/src/sbin/lorax @@ -25,20 +25,16 @@ import logging log = logging.getLogger("lorax") program_log = logging.getLogger("program") pylorax_log = logging.getLogger("pylorax") -yum_log = logging.getLogger("yum") +dnf_log = logging.getLogger("dnf") import sys import os import tempfile from optparse import OptionParser, OptionGroup -import ConfigParser import shutil -import yum -# This is a bit of a hack to short circuit yum's internal logging -# handler setup. We already set one up so we don't need it to run. -yum.logginglevels._added_handlers = True +import dnf import pylorax def setup_logging(opts): @@ -67,12 +63,12 @@ def setup_logging(opts): fh.setLevel(logging.DEBUG) program_log.addHandler(fh) - # yum logging - yum_log.setLevel(logging.DEBUG) - logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/yum.log" + # dnf logging + dnf_log.setLevel(logging.DEBUG) + logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/dnf.log" fh = logging.FileHandler(filename=logfile, mode="w") fh.setLevel(logging.DEBUG) - yum_log.addHandler(fh) + dnf_log.addHandler(fh) def main(args): @@ -116,9 +112,6 @@ def main(args): help="config file", metavar="STRING") optional.add_option("--proxy", default=None, help="repo proxy url:port", metavar="STRING") - optional.add_option("-e", "--excludepkgs", default=[], - action="append", metavar="STRING", - help="package glob to exclude (may be listed multiple times)") optional.add_option("-i", "--installpkgs", default=[], action="append", metavar="STRING", help="package glob to install before runtime-install.tmpl runs. (may be listed multiple times)") @@ -180,17 +173,16 @@ def main(args): # create the temporary directory for lorax tempdir = tempfile.mkdtemp(prefix="lorax.", dir=tempfile.gettempdir()) - # create the yumbase object installtree = os.path.join(tempdir, "installtree") os.mkdir(installtree) - yumtempdir = os.path.join(tempdir, "yum") - os.mkdir(yumtempdir) + dnftempdir = os.path.join(tempdir, "dnf") + os.mkdir(dnftempdir) - yb = get_yum_base_object(installtree, opts.source, opts.mirrorlist, - yumtempdir, opts.proxy, opts.excludepkgs) + dnfbase = get_dnf_base_object(installtree, opts.source, opts.mirrorlist, + dnftempdir, opts.proxy, opts.version) - if yb is None: - print("error: unable to create the yumbase object", file=sys.stderr) + if dnfbase is None: + print("error: unable to create the dnf base object", file=sys.stderr) shutil.rmtree(tempdir) sys.exit(1) @@ -204,7 +196,7 @@ def main(args): # run lorax lorax = pylorax.Lorax() lorax.configure(conf_file=opts.config) - lorax.run(yb, opts.product, opts.version, opts.release, + lorax.run(dnfbase, opts.product, opts.version, opts.release, opts.variant, opts.bugurl, opts.isfinal, workdir=tempdir, outputdir=outputdir, buildarch=opts.buildarch, volid=opts.volid, domacboot=opts.domacboot, doupgrade=opts.doupgrade, @@ -214,9 +206,17 @@ def main(args): remove_temp=True) -def get_yum_base_object(installroot, repositories, mirrorlists=None, - tempdir="/var/tmp", proxy=None, excludepkgs=None): +def get_dnf_base_object(installroot, repositories, mirrorlists=None, + tempdir="/var/tmp", proxy=None, releasever="21"): + """ Create a dnf Base object and setup the repositories and installroot + :param string installroot: Full path to the installroot + :param list repositories: List of repositories to use for the installation + :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 + """ def sanitize_repo(repo): """Convert bare paths to file:/// URIs, and silently reject protocols unhandled by yum""" if repo.startswith("/"): @@ -227,7 +227,6 @@ def get_yum_base_object(installroot, repositories, mirrorlists=None, return None mirrorlists = mirrorlists or [] - excludepkgs = excludepkgs or [] # sanitize the repositories repositories = map(sanitize_repo, repositories) @@ -237,78 +236,70 @@ def get_yum_base_object(installroot, repositories, mirrorlists=None, repositories = filter(bool, repositories) mirrorlists = filter(bool, mirrorlists) - cachedir = os.path.join(tempdir, "yum.cache") + cachedir = os.path.join(tempdir, "dnf.cache") if not os.path.isdir(cachedir): os.mkdir(cachedir) - yumconf = os.path.join(tempdir, "yum.conf") - c = ConfigParser.ConfigParser() + logdir = os.path.join(tempdir, "dnf.logs") + if not os.path.isdir(logdir): + os.mkdir(logdir) - # add the main section - section = "main" - data = {"cachedir": cachedir, - "keepcache": 0, - "gpgcheck": 0, - "plugins": 0, - "reposdir": "", - "tsflags": "nodocs"} + dnfbase = dnf.Base() + conf = dnfbase.conf + conf.logdir = logdir + conf.cachedir = cachedir + + # Turn off logging to the console + conf.debuglevel = 0 + conf.errorlevel = 0 + dnfbase.logging.setup_from_dnf_conf(conf) + + conf.releasever = releasever + conf.installroot = installroot + conf.prepend_installroot('persistdir') + conf.tsflags.append('nodocs') if proxy: - data["proxy"] = proxy + conf.proxy = proxy - if excludepkgs: - data["exclude"] = " ".join(excludepkgs) - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # add the main repository - the first repository from list - section = "lorax-repo" - data = {"name": "lorax repo", - "baseurl": repositories[0], - "enabled": 1} - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) - - # add the extra repositories - for n, extra in enumerate(repositories[1:], start=1): - section = "lorax-extra-repo-{0:d}".format(n) - data = {"name": "lorax extra repo {0:d}".format(n), - "baseurl": extra, - "enabled": 1} - - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) + # add the repositories + for i, r in enumerate(repositories): + repo_name = "lorax-repo-%d" % i + repo = dnf.repo.Repo(repo_name, cachedir) + 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 n, mirror in enumerate(mirrorlists, start=1): - section = "lorax-mirrorlist-{0:d}".format(n) - data = {"name": "lorax mirrorlist {0:d}".format(n), - "mirrorlist": mirror, - "enabled": 1 } + for i, r in enumerate(mirrorlists): + repo_name = "lorax-mirrorlist-%d" % i + repo = dnf.repo.Repo(repo_name, cachedir) + 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 - c.add_section(section) - map(lambda (key, value): c.set(section, key, value), data.items()) + dnfbase.fill_sack(load_system_repo=False) + dnfbase.read_comps() - # write the yum configuration file - with open(yumconf, "w") as f: - c.write(f) - - # create the yum base object - yb = yum.YumBase() - - yb.preconf.fn = yumconf - yb.preconf.root = installroot - #yb.repos.setCacheDir(cachedir) - - # Turn on as much yum logging as we can - yb.preconf.debuglevel = 6 - yb.preconf.errorlevel = 6 - yb.logger.setLevel(logging.DEBUG) - yb.verbose_logger.setLevel(logging.DEBUG) - - return yb + return dnfbase if __name__ == "__main__":