[lorax 1/7] Switch lorax to use dnf instead of yum

Brian C. Lane bcl at redhat.com
Wed Feb 11 18:40:06 UTC 2015


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 ee3dfc2..387a33c 100644
--- a/lorax.spec
+++ b/lorax.spec
@@ -33,9 +33,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 8942d6c..44b180b 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 0000000..a220e62
--- /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 <http://www.gnu.org/licenses/>.
+#
+# Red Hat Author(s):  Martin Gracik <mgracik at redhat.com>
+#                     Brian C. Lane <bcl at redhat.com>
+#
+
+import logging
+logger = logging.getLogger("pylorax.yumhelper")
+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 99f26a5..f5d6f33 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 baad1ac..5a56233 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 79a3205..0000000
--- 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 <http://www.gnu.org/licenses/>.
-#
-# Red Hat Author(s):  Martin Gracik <mgracik at redhat.com>
-#
-
-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}<b>{1}</b>\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}<b>{1}</b>".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 9204f7b..bfc47e3 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()
-
-    # add the main section
-    section = "main"
-    data = {"cachedir": cachedir,
-            "keepcache": 0,
-            "gpgcheck": 0,
-            "plugins": 0,
-            "reposdir": "",
-            "tsflags": "nodocs"}
-
-    if proxy:
-        data["proxy"] = proxy
+    logdir = os.path.join(tempdir, "dnf.logs")
+    if not os.path.isdir(logdir):
+        os.mkdir(logdir)
 
-    if excludepkgs:
-        data["exclude"] = " ".join(excludepkgs)
+    dnfbase = dnf.Base()
+    conf = dnfbase.conf
+    conf.logdir = logdir
+    conf.cachedir = cachedir
 
-    c.add_section(section)
-    map(lambda (key, value): c.set(section, key, value), data.items())
+    # Turn off logging to the console
+    conf.debuglevel = 0
+    conf.errorlevel = 0
+    dnfbase.logging.setup_from_dnf_conf(conf)
 
-    # add the main repository - the first repository from list
-    section = "lorax-repo"
-    data = {"name": "lorax repo",
-            "baseurl": repositories[0],
-            "enabled": 1}
+    conf.releasever = releasever
+    conf.installroot = installroot
+    conf.prepend_installroot('persistdir')
+    conf.tsflags.append('nodocs')
 
-    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())
+    if proxy:
+        conf.proxy = proxy
+
+    # 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 }
-
-        c.add_section(section)
-        map(lambda (key, value): c.set(section, key, value), data.items())
-
-    # 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)
+    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
 
-    # 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)
+    dnfbase.fill_sack(load_system_repo=False)
+    dnfbase.read_comps()
 
-    return yb
+    return dnfbase
 
 
 if __name__ == "__main__":
-- 
2.1.0



More information about the anaconda-patches mailing list