tools: use dnf filtering in lorax-template helper
The pylorax implementation of the template running code supports globbing, as well as `--exclude` and `--optional` commands. These are handled independently for each `installpkg` command, so that requesting the installation of firmware packages in one command together with an exclude of `*alsa` does indeed only exclude any alsa firmware packages but not any other alsa packages. The previous version of this script would just build an global list for excludes which has a drastically different result because a global exclude of "*alsa" would result in a global exclusion of all matching packages and probably a dependency error. Therefore, add support for dnf based filtering of packages on a per `installpkg` command bases, very much like pylorax does.
This commit is contained in:
parent
961ce3077f
commit
01319b55a9
1 changed files with 112 additions and 31 deletions
|
|
@ -8,56 +8,137 @@ in form of a JSON array.
|
|||
"""
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import dnf
|
||||
import dnf.conf
|
||||
import dnf.conf.read
|
||||
|
||||
from osbuild.util.lorax import render_template
|
||||
import osbuild.util.osrelease as ostrelease
|
||||
|
||||
|
||||
def main():
|
||||
class DepSolver:
|
||||
def __init__(self, arch, relver, dirs):
|
||||
self.base = dnf.Base()
|
||||
self.arch = arch
|
||||
self.basearch = dnf.rpm.basearch(arch)
|
||||
conf = self.base.conf
|
||||
conf.config_file_path = "/dev/null"
|
||||
conf.persistdir = dirs["persistdir"]
|
||||
conf.cachedir = dirs["cachedir"]
|
||||
conf.substitutions["arch"] = arch
|
||||
conf.substitutions["basearch"] = self.basearch
|
||||
conf.substitutions["releasever"] = relver
|
||||
conf.reposdir = [dirs["repodir"]]
|
||||
self.repos = self.read_repos()
|
||||
|
||||
def read_repos(self):
|
||||
conf = self.base.conf
|
||||
reader = dnf.conf.read.RepoReader(conf, {})
|
||||
return {r.id: r for r in reader}
|
||||
|
||||
def reset(self):
|
||||
base = self.base
|
||||
base.reset(goal=True, repos=True, sack=True)
|
||||
|
||||
for repo in self.repos.values():
|
||||
base.repos.add(repo)
|
||||
|
||||
base.fill_sack(load_system_repo=False)
|
||||
|
||||
def filter(self, pkg):
|
||||
sack = self.base.sack
|
||||
return dnf.subject.Subject(pkg).get_best_query(sack).filter(latest=True)
|
||||
|
||||
def install(self, packages, excludes=None, optional=False):
|
||||
def included(pkg):
|
||||
for exclude in excludes or []:
|
||||
if fnmatch.fnmatch(pkg.name, exclude):
|
||||
return False
|
||||
return True
|
||||
|
||||
result = []
|
||||
|
||||
for p in packages:
|
||||
pkgs = self.filter(p)
|
||||
if not pkgs:
|
||||
if optional:
|
||||
continue
|
||||
raise dnf.exceptions.PackageNotFoundError("no package matched", p)
|
||||
|
||||
result.extend(map(lambda p: p.name, filter(included, pkgs)))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def list_packages(text, solver):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--basearch", help="Set the `basearch` variable", default="x86_64")
|
||||
parser.add_argument("--product", help="Set the `product` variable", default="fedora")
|
||||
parser.add_argument("FILE", help="The template to process")
|
||||
args = parser.parse_args()
|
||||
|
||||
variables = {
|
||||
"basearch": args.basearch,
|
||||
"product": args.product
|
||||
}
|
||||
|
||||
txt = render_template(args.FILE, variables)
|
||||
|
||||
packages = []
|
||||
optional = []
|
||||
excludes = []
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--optional", action="append")
|
||||
parser.add_argument("--optional", action="store_true", default=False)
|
||||
parser.add_argument("--except", dest="excludes", action="append")
|
||||
parser.add_argument("packages", help="The template to process", nargs="*")
|
||||
|
||||
for line in txt:
|
||||
packages = []
|
||||
for line in text:
|
||||
cmd, args = line[0], parser.parse_args(line[1:])
|
||||
|
||||
if cmd != "installpkg":
|
||||
print(f"{cmd} ignored", file=sys.stderr)
|
||||
continue
|
||||
|
||||
if args.optional:
|
||||
optional += args.optional
|
||||
if args.excludes:
|
||||
excludes += args.excludes
|
||||
if args.packages:
|
||||
packages += args.packages
|
||||
pkgs = solver.install(args.packages, None, args.optional)
|
||||
packages += pkgs
|
||||
|
||||
data = {
|
||||
"packages": packages,
|
||||
"optional": optional,
|
||||
"except": excludes
|
||||
return packages
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--basearch", help="Set the `basearch` variable", default="x86_64")
|
||||
parser.add_argument("--product", help="Set the `product` variable", default="fedora")
|
||||
parser.add_argument("--dnf-cache", metavar="PATH", type=os.path.abspath, default=None,
|
||||
help="Path to DNF cache-directory to use")
|
||||
parser.add_argument("--repo-dir", metavar="PATH", type=os.path.abspath,
|
||||
default="/etc/yum.repos.d",
|
||||
help="Path to DNF repositories directory")
|
||||
parser.add_argument("--os-version", metavar="PATH", default=None,
|
||||
help="OS version to use for dnf")
|
||||
parser.add_argument("FILE", help="The template to process")
|
||||
args = parser.parse_args()
|
||||
|
||||
variables = {
|
||||
"basearch": args.basearch,
|
||||
"product": args.product
|
||||
}
|
||||
|
||||
json.dump(data, sys.stdout, indent=2)
|
||||
txt = render_template(args.FILE, variables)
|
||||
|
||||
packages = []
|
||||
|
||||
os_version = args.os_version
|
||||
if not os_version:
|
||||
release = ostrelease.parse_files(*ostrelease.DEFAULT_PATHS)
|
||||
os_version = release["VERSION_ID"]
|
||||
|
||||
with tempfile.TemporaryDirectory(dir="/var/tmp") as tmp:
|
||||
persistdir = os.path.join(tmp, "dnf-persist")
|
||||
cachedir = args.dnf_cache or os.path.join(tmp, "dnf-cache")
|
||||
dirs = {
|
||||
"persistdir": persistdir,
|
||||
"cachedir": cachedir,
|
||||
"repodir": args.repo_dir
|
||||
}
|
||||
|
||||
solver = DepSolver(args.basearch, os_version, dirs)
|
||||
solver.reset()
|
||||
|
||||
packages = list_packages(txt, solver)
|
||||
|
||||
json.dump(packages, sys.stdout, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue