#!/usr/bin/python3 # # Output the package set that would be installed if the packages on the cmdline were installed # import os import sys import dnf import argparse import shutil import tempfile def setup_argparse(): parser = argparse.ArgumentParser(description="Output packages DNF has selected") # required arguments for image creation required = parser.add_argument_group("required arguments") required.add_argument("-r", "--release", help="release information", required=True, metavar="STRING") required.add_argument("-s", "--source", help="source repository (may be listed multiple times)", metavar="REPOSITORY", action="append", default=[], required=True) parser.add_argument("-t", dest="pkg_file", help="Read packages from a Mako template file") parser.add_argument("-f", "--find", help="Find the package that pulls in this other package") parser.add_argument("--skip-broken", help="Skip broken packages. This is the DNF default.", action="store_true", default=False) parser.add_argument("--tempdir", help="Directory to store temporary DNF files") parser.add_argument("--proxy", help="Proxy URL to use for DNF") parser.add_argument("packages", help="Packages to Install", metavar="STRING", nargs='*', default=[]) return parser def get_dbo(tempdir, repositories, releasever, best, proxy=None): """ Create a dnf Base object and setup the repositories and installroot :param list repositories: List of repositories to use for the installation :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("/"): return "file://{0}".format(repo) elif any(repo.startswith(p) for p in ('http://', 'https://', 'ftp://', 'file://')): return repo else: return None # sanitize the repository urls, split out .repo files repo_urls = filter(None, [sanitize_repo(r) for r in repositories if not r.endswith(".repo")]) repo_files = list(r for r in repositories if r.endswith(".repo") and os.path.isfile(r)) cachedir = os.path.join(tempdir, "dnf.cache") if not os.path.isdir(cachedir): os.mkdir(cachedir) logdir = os.path.join(tempdir, "dnf.logs") if not os.path.isdir(logdir): os.mkdir(logdir) installroot = os.path.join(tempdir, "installroot") if not os.path.isdir(installroot): os.mkdir(installroot) dnfbase = dnf.Base() conf = dnfbase.conf print("Use highest NVR package: %s" % best) conf.best = best # setup dirs. conf.logdir = logdir conf.cachedir = cachedir # Turn off logging to the console conf.debuglevel = 10 conf.errorlevel = 0 conf.debug_solver = True conf.releasever = releasever conf.installroot = installroot conf.prepend_installroot('persistdir') conf.tsflags.append('nodocs') if proxy: conf.proxy = proxy # Add .repo files if repo_files: reposdir = os.path.join(tempdir, "dnf.repos") if not os.path.isdir(reposdir): os.mkdir(reposdir) for r in repo_files: shutil.copy2(r, reposdir) conf.reposdir = [reposdir] dnfbase.read_all_repos() # add the repositories for i, r in enumerate(repo_urls): if "SRPM" in r or "srpm" in r: print("Skipping source repo: %s" % r) continue repo_name = "lorax-repo-%d" % i repo = dnf.repo.Repo(repo_name, conf) repo.baseurl = [r] repo.skip_if_unavailable = False repo.enable() dnfbase.repos.add(repo) print("Added '%s': %s" % (repo_name, r)) print("Fetching metadata...") try: repo.load() except dnf.exceptions.RepoError as e: print("Error fetching metadata for %s: %s" % (repo_name, e)) return None dnfbase.fill_sack(load_system_repo=False) dnfbase.read_comps() return dnfbase def mako_installpkg(pkg_file): """ Read a Mako template file and return a list of all the packages listed in the installpkg lines. """ packages = [] with open(pkg_file, "r") as f: for line in f.readlines(): if line.startswith("installpkg"): packages += line.split()[1:] return packages def find_package_parent(pkg_name, dbo, packages): """ Brute force discovery of what make a specific pkg_name get pulled into the transaction. """ package_set = [] for p in packages: package_set += [p] dbo.reset() for pkg in package_set: try: dbo.install(pkg) except Exception as e: print("Failed to install %s\n%s" % (pkg, e)) try: dbo.resolve() except dnf.exceptions.DepsolveError as e: print("Dependency check failed: %s" % e) raise if len(dbo.transaction) == 0: raise Exception("No packages in transaction") # Print what DNF picked. for pkg in dbo.transaction.install_set: if pkg_name in pkg.pkgtup[0]: print("FOUND IT! %s pulled in %s" % (p, pkg_name)) print(package_set) return if __name__ == "__main__": parser = setup_argparse() opts = parser.parse_args() tempdir = opts.tempdir or tempfile.mkdtemp(prefix="test-dnf.") print("Using tempdir: %s" % tempdir) dbo = get_dbo(tempdir, opts.source, opts.release, not opts.skip_broken, opts.proxy) packages = opts.packages if opts.pkg_file: packages += mako_installpkg(opts.pkg_file) if len(packages) == 0: print("Pass packages on cmdline or via Mako template using -f") sys.exit(1) # Find what pulls in a specific package if opts.find: find_package_parent(opts.find, dbo, packages) sys.exit(0) # info about the packages q = dbo.sack.query() available = q.available() for pkg in packages: answer = available.filter(name=pkg) for pkg in answer: print(pkg) # Print all the packages DNF picks for pkg in packages: print("Adding %s to the transaction" % pkg) try: dbo.install(pkg) except Exception as e: print("Failed to install %s\n%s" % (pkg, e)) try: print("Checking dependencies") dbo.resolve() except dnf.exceptions.DepsolveError as e: print("Dependency check failed: %s" % e) raise print("%d packages selected" % len(dbo.transaction)) if len(dbo.transaction) == 0: raise Exception("No packages in transaction") # Print what DNF picked. for pkg in dbo.transaction.install_set: print("%s - %s" % (pkg.repoid, pkg.pkgtup))