|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# Copyright (C) 2015 Red Hat, Inc.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit Service |
27f74b |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit Service |
27f74b |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit Service |
27f74b |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit Service |
27f74b |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit Service |
27f74b |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit Service |
27f74b |
# Public License for more details. You should have received a copy of the
|
|
Packit Service |
27f74b |
# GNU General Public License along with this program; if not, write to the
|
|
Packit Service |
27f74b |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
27f74b |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit Service |
27f74b |
# source code or documentation are not subject to the GNU General Public
|
|
Packit Service |
27f74b |
# License and may only be used or replicated with the express permission of
|
|
Packit Service |
27f74b |
# Red Hat, Inc.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
from __future__ import absolute_import
|
|
Packit Service |
27f74b |
from __future__ import unicode_literals
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
from dnf.i18n import ucd
|
|
Packit Service |
27f74b |
from dnfpluginscore import _, logger
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
import dnf
|
|
Packit Service |
27f74b |
import dnf.cli
|
|
Packit Service |
27f74b |
import gzip
|
|
Packit Service |
27f74b |
import hawkey
|
|
Packit Service |
27f74b |
import os
|
|
Packit Service |
27f74b |
import rpm
|
|
Packit Service |
27f74b |
import sys
|
|
Packit Service |
27f74b |
import time
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
DEBUG_VERSION = "dnf-debug-dump version 1\n"
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
class Debug(dnf.Plugin):
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
name = 'debug'
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def __init__(self, base, cli):
|
|
Packit Service |
27f74b |
super(Debug, self).__init__(base, cli)
|
|
Packit Service |
27f74b |
self.base = base
|
|
Packit Service |
27f74b |
self.cli = cli
|
|
Packit Service |
27f74b |
if self.cli is not None:
|
|
Packit Service |
27f74b |
self.cli.register_command(DebugDumpCommand)
|
|
Packit Service |
27f74b |
self.cli.register_command(DebugRestoreCommand)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
class DebugDumpCommand(dnf.cli.Command):
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
aliases = ("debug-dump",)
|
|
Packit Service |
27f74b |
summary = _("dump information about installed rpm packages to file")
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def __init__(self, cli):
|
|
Packit Service |
27f74b |
super(DebugDumpCommand, self).__init__(cli)
|
|
Packit Service |
27f74b |
self.dump_file = None
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def configure(self):
|
|
Packit Service |
27f74b |
self.cli.demands.sack_activation = True
|
|
Packit Service |
27f74b |
self.cli.demands.available_repos = True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
@staticmethod
|
|
Packit Service |
27f74b |
def set_argparser(parser):
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"--norepos", action="store_true", default=False,
|
|
Packit Service |
27f74b |
help=_("do not attempt to dump the repository contents."))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"filename", nargs="?",
|
|
Packit Service |
27f74b |
help=_("optional name of dump file"))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def run(self):
|
|
Packit Service |
27f74b |
"""create debug txt file and compress it, if no filename specified
|
|
Packit Service |
27f74b |
use dnf_debug_dump-<timestamp>.txt.gz by default"""
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
filename = self.opts.filename
|
|
Packit Service |
27f74b |
if not filename:
|
|
Packit Service |
27f74b |
now = time.strftime("%Y-%m-%d_%T", time.localtime(time.time()))
|
|
Packit Service |
27f74b |
filename = "dnf_debug_dump-%s-%s.txt.gz" % (os.uname()[1], now)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
filename = os.path.abspath(filename)
|
|
Packit Service |
27f74b |
if filename.endswith(".gz"):
|
|
Packit Service |
27f74b |
self.dump_file = gzip.GzipFile(filename, "w")
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
self.dump_file = open(filename, "w")
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
self.write(DEBUG_VERSION)
|
|
Packit Service |
27f74b |
self.dump_system_info()
|
|
Packit Service |
27f74b |
self.dump_dnf_config_info()
|
|
Packit Service |
27f74b |
self.dump_rpm_problems()
|
|
Packit Service |
27f74b |
self.dump_packages(not self.opts.norepos)
|
|
Packit Service |
27f74b |
self.dump_rpmdb_versions()
|
|
Packit Service |
27f74b |
self.dump_file.close()
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
print(_("Output written to: %s") % filename)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def write(self, msg):
|
|
Packit Service |
27f74b |
if dnf.pycomp.PY3 and isinstance(self.dump_file, gzip.GzipFile):
|
|
Packit Service |
27f74b |
msg = bytes(msg, "utf8")
|
|
Packit Service |
27f74b |
dnf.pycomp.write_to_file(self.dump_file, msg)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def dump_system_info(self):
|
|
Packit Service |
27f74b |
self.write("%%%%SYSTEM INFO\n")
|
|
Packit Service |
27f74b |
uname = os.uname()
|
|
Packit Service |
27f74b |
self.write(" uname: %s, %s\n" % (uname[2], uname[4]))
|
|
Packit Service |
27f74b |
self.write(" rpm ver: %s\n" % rpm.__version__)
|
|
Packit Service |
27f74b |
self.write(" python ver: %s\n" % sys.version.replace("\n", ""))
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def dump_dnf_config_info(self):
|
|
Packit Service |
27f74b |
var = self.base.conf.substitutions
|
|
Packit Service |
27f74b |
plugins = ",".join([p.name for p in self.base._plugins.plugins])
|
|
Packit Service |
27f74b |
self.write("%%%%DNF INFO\n")
|
|
Packit Service |
27f74b |
self.write(" arch: %s\n" % var["arch"])
|
|
Packit Service |
27f74b |
self.write(" basearch: %s\n" % var["basearch"])
|
|
Packit Service |
27f74b |
self.write(" releasever: %s\n" % var["releasever"])
|
|
Packit Service |
27f74b |
self.write(" dnf ver: %s\n" % dnf.const.VERSION)
|
|
Packit Service |
27f74b |
self.write(" enabled plugins: %s\n" % plugins)
|
|
Packit Service |
27f74b |
self.write(" global excludes: %s\n" % ",".join(self.base.conf.excludepkgs))
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def dump_rpm_problems(self):
|
|
Packit Service |
27f74b |
self.write("%%%%RPMDB PROBLEMS\n")
|
|
Packit Service |
27f74b |
(missing, conflicts) = rpm_problems(self.base)
|
|
Packit Service |
27f74b |
self.write("".join(["Package %s requires %s\n" % (ucd(pkg), ucd(req))
|
|
Packit Service |
27f74b |
for (req, pkg) in missing]))
|
|
Packit Service |
27f74b |
self.write("".join(["Package %s conflicts with %s\n" % (ucd(pkg),
|
|
Packit Service |
27f74b |
ucd(conf))
|
|
Packit Service |
27f74b |
for (conf, pkg) in conflicts]))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def dump_packages(self, load_repos):
|
|
Packit Service |
27f74b |
q = self.base.sack.query()
|
|
Packit Service |
27f74b |
# packages from rpmdb
|
|
Packit Service |
27f74b |
self.write("%%%%RPMDB\n")
|
|
Packit Service |
27f74b |
for p in sorted(q.installed()):
|
|
Packit Service |
27f74b |
self.write(" %s\n" % pkgspec(p))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if not load_repos:
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
self.write("%%%%REPOS\n")
|
|
Packit Service |
27f74b |
available = q.available()
|
|
Packit Service |
27f74b |
for repo in sorted(self.base.repos.iter_enabled(), key=lambda x: x.id):
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
url = None
|
|
Packit Service |
27f74b |
if repo.metalink is not None:
|
|
Packit Service |
27f74b |
url = repo.metalink
|
|
Packit Service |
27f74b |
elif repo.mirrorlist is not None:
|
|
Packit Service |
27f74b |
url = repo.mirrorlist
|
|
Packit Service |
27f74b |
elif len(repo.baseurl) > 0:
|
|
Packit Service |
27f74b |
url = repo.baseurl[0]
|
|
Packit Service |
27f74b |
self.write("%%%s - %s\n" % (repo.id, url))
|
|
Packit Service |
27f74b |
self.write(" excludes: %s\n" % ",".join(repo.excludepkgs))
|
|
Packit Service |
27f74b |
for po in sorted(available.filter(reponame=repo.id)):
|
|
Packit Service |
27f74b |
self.write(" %s\n" % pkgspec(po))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
except dnf.exceptions.Error as e:
|
|
Packit Service |
27f74b |
self.write("Error accessing repo %s: %s\n" % (repo, str(e)))
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def dump_rpmdb_versions(self):
|
|
Packit Service |
27f74b |
self.write("%%%%RPMDB VERSIONS\n")
|
|
Packit Service |
27f74b |
version = self.base.sack._rpmdb_version()
|
|
Packit Service |
27f74b |
self.write(" all: %s\n" % version)
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
class DebugRestoreCommand(dnf.cli.Command):
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
aliases = ("debug-restore",)
|
|
Packit Service |
27f74b |
summary = _("restore packages recorded in debug-dump file")
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def configure(self):
|
|
Packit Service |
27f74b |
self.cli.demands.sack_activation = True
|
|
Packit Service |
27f74b |
self.cli.demands.available_repos = True
|
|
Packit Service |
27f74b |
self.cli.demands.root_user = True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
@staticmethod
|
|
Packit Service |
27f74b |
def set_argparser(parser):
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"--output", action="store_true",
|
|
Packit Service |
27f74b |
help=_("output commands that would be run to stdout."))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"--install-latest", action="store_true",
|
|
Packit Service |
27f74b |
help=_("Install the latest version of recorded packages."))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"--ignore-arch", action="store_true",
|
|
Packit Service |
27f74b |
help=_("Ignore architecture and install missing packages matching "
|
|
Packit Service |
27f74b |
"the name, epoch, version and release."))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"--filter-types", metavar="[install, remove, replace]",
|
|
Packit Service |
27f74b |
default="install, remove, replace",
|
|
Packit Service |
27f74b |
help=_("limit to specified type"))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
"filename", nargs=1, help=_("name of dump file"))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def run(self):
|
|
Packit Service |
27f74b |
"""Execute the command action here."""
|
|
Packit Service |
27f74b |
if self.opts.filter_types:
|
|
Packit Service |
27f74b |
self.opts.filter_types = set(
|
|
Packit Service |
27f74b |
self.opts.filter_types.replace(",", " ").split())
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
installed = self.base.sack.query().installed()
|
|
Packit Service |
27f74b |
dump_pkgs = self.read_dump_file(self.opts.filename[0])
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
self.process_installed(installed, dump_pkgs, self.opts)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
self.process_dump(dump_pkgs, self.opts)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if not self.opts.output:
|
|
Packit Service |
27f74b |
self.base.resolve()
|
|
Packit Service |
27f74b |
self.base.do_transaction()
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def process_installed(self, installed, dump_pkgs, opts):
|
|
Packit Service |
27f74b |
for pkg in sorted(installed):
|
|
Packit Service |
27f74b |
filtered = False
|
|
Packit Service |
27f74b |
spec = pkgspec(pkg)
|
|
Packit Service |
27f74b |
action, dn, da, de, dv, dr = dump_pkgs.get((pkg.name, pkg.arch),
|
|
Packit Service |
27f74b |
[None, None, None,
|
|
Packit Service |
27f74b |
None, None, None])
|
|
Packit Service |
27f74b |
dump_naevr = (dn, da, de, dv, dr)
|
|
Packit Service |
27f74b |
if pkg.pkgtup == dump_naevr:
|
|
Packit Service |
27f74b |
# package unchanged
|
|
Packit Service |
27f74b |
del dump_pkgs[(pkg.name, pkg.arch)]
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
if action == "install":
|
|
Packit Service |
27f74b |
# already have some version
|
|
Packit Service |
27f74b |
dump_pkgs[(pkg.name, pkg.arch)][0] = "replace"
|
|
Packit Service |
27f74b |
if "replace" not in opts.filter_types:
|
|
Packit Service |
27f74b |
filtered = True
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
if "remove" not in opts.filter_types:
|
|
Packit Service |
27f74b |
filtered = True
|
|
Packit Service |
27f74b |
if not filtered:
|
|
Packit Service |
27f74b |
if opts.output:
|
|
Packit Service |
27f74b |
print("remove %s" % spec)
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
self.base.package_remove(pkg)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def process_dump(self, dump_pkgs, opts):
|
|
Packit Service |
27f74b |
for (action, n, a, e, v, r) in sorted(dump_pkgs.values()):
|
|
Packit Service |
27f74b |
filtered = False
|
|
Packit Service |
27f74b |
if opts.ignore_arch:
|
|
Packit Service |
27f74b |
arch = ""
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
arch = "." + a
|
|
Packit Service |
27f74b |
if opts.install_latest and action == "install":
|
|
Packit Service |
27f74b |
pkg_spec = "%s%s" % (n, arch)
|
|
Packit Service |
27f74b |
if "install" not in opts.filter_types:
|
|
Packit Service |
27f74b |
filtered = True
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
pkg_spec = pkgtup2spec(n, arch, e, v, r)
|
|
Packit Service |
27f74b |
if (action == "replace" and
|
|
Packit Service |
27f74b |
"replace" not in opts.filter_types):
|
|
Packit Service |
27f74b |
filtered = True
|
|
Packit Service |
27f74b |
if not filtered:
|
|
Packit Service |
27f74b |
if opts.output:
|
|
Packit Service |
27f74b |
print("install %s" % pkg_spec)
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
self.base.install(pkg_spec)
|
|
Packit Service |
27f74b |
except dnf.exceptions.MarkingError:
|
|
Packit Service |
27f74b |
logger.error(_("Package %s is not available"), pkg_spec)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
@staticmethod
|
|
Packit Service |
27f74b |
def read_dump_file(filename):
|
|
Packit Service |
27f74b |
if filename.endswith(".gz"):
|
|
Packit Service |
27f74b |
fobj = gzip.GzipFile(filename)
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
fobj = open(filename)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if ucd(fobj.readline()) != DEBUG_VERSION:
|
|
Packit Service |
27f74b |
logger.error(_("Bad dnf debug file: %s"), filename)
|
|
Packit Service |
27f74b |
raise dnf.exceptions.Error
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
skip = True
|
|
Packit Service |
27f74b |
pkgs = {}
|
|
Packit Service |
27f74b |
for line in fobj:
|
|
Packit Service |
27f74b |
line = ucd(line)
|
|
Packit Service |
27f74b |
if skip:
|
|
Packit Service |
27f74b |
if line == "%%%%RPMDB\n":
|
|
Packit Service |
27f74b |
skip = False
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if not line or line[0] != " ":
|
|
Packit Service |
27f74b |
break
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
pkg_spec = line.strip()
|
|
Packit Service |
27f74b |
nevra = hawkey.split_nevra(pkg_spec)
|
|
Packit Service |
27f74b |
pkgs[(nevra.name, nevra.arch)] = ["install", ucd(nevra.name),
|
|
Packit Service |
27f74b |
ucd(nevra.arch),
|
|
Packit Service |
27f74b |
ucd(nevra.epoch),
|
|
Packit Service |
27f74b |
ucd(nevra.version),
|
|
Packit Service |
27f74b |
ucd(nevra.release)]
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
return pkgs
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def rpm_problems(base):
|
|
Packit Service |
27f74b |
rpmdb = dnf.sack._rpmdb_sack(base)
|
|
Packit Service |
27f74b |
allpkgs = rpmdb.query().installed()
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
requires = set()
|
|
Packit Service |
27f74b |
conflicts = set()
|
|
Packit Service |
27f74b |
for pkg in allpkgs:
|
|
Packit Service |
27f74b |
requires.update([(req, pkg) for req in pkg.requires
|
|
Packit Service |
27f74b |
if not str(req) == "solvable:prereqmarker"
|
|
Packit Service |
27f74b |
and not str(req).startswith("rpmlib(")])
|
|
Packit Service |
27f74b |
conflicts.update([(conf, pkg) for conf in pkg.conflicts])
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
missing_requires = [(req, pkg) for (req, pkg) in requires
|
|
Packit Service |
27f74b |
if not allpkgs.filter(provides=req)]
|
|
Packit Service |
27f74b |
existing_conflicts = [(conf, pkg) for (conf, pkg) in conflicts
|
|
Packit Service |
27f74b |
if allpkgs.filter(provides=conf)]
|
|
Packit Service |
27f74b |
return missing_requires, existing_conflicts
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def pkgspec(pkg):
|
|
Packit Service |
27f74b |
return pkgtup2spec(pkg.name, pkg.arch, pkg.epoch, pkg.version, pkg.release)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def pkgtup2spec(name, arch, epoch, version, release):
|
|
Packit Service |
27f74b |
a = "" if not arch else ".%s" % arch
|
|
Packit Service |
27f74b |
e = "" if epoch in (None, "") else "%s:" % epoch
|
|
Packit Service |
27f74b |
return "%s-%s%s-%s%s" % (name, e, version, release, a)
|