|
Packit Service |
21c75c |
# Copyright (C) 2020 Red Hat, Inc.
|
|
Packit Service |
21c75c |
#
|
|
Packit Service |
21c75c |
# This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
21c75c |
# it under the terms of the GNU General Public License as published by
|
|
Packit Service |
21c75c |
# the Free Software Foundation; either version 2 of the License, or
|
|
Packit Service |
21c75c |
# (at your option) any later version.
|
|
Packit Service |
21c75c |
#
|
|
Packit Service |
21c75c |
# This program is distributed in the hope that it will be useful,
|
|
Packit Service |
21c75c |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
21c75c |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
21c75c |
# GNU Library General Public License for more details.
|
|
Packit Service |
21c75c |
#
|
|
Packit Service |
21c75c |
# You should have received a copy of the GNU General Public License
|
|
Packit Service |
21c75c |
# along with this program; if not, write to the Free Software
|
|
Packit Service |
21c75c |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
from __future__ import absolute_import
|
|
Packit Service |
21c75c |
from __future__ import print_function
|
|
Packit Service |
21c75c |
from __future__ import unicode_literals
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
import libdnf
|
|
Packit Service |
21c75c |
import hawkey
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
from dnf.i18n import _
|
|
Packit Service |
21c75c |
import dnf.exceptions
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
import json
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
VERSION_MAJOR = 0
|
|
Packit Service |
21c75c |
VERSION_MINOR = 0
|
|
Packit Service |
21c75c |
VERSION = "%s.%s" % (VERSION_MAJOR, VERSION_MINOR)
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
The version of the stored transaction.
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
MAJOR version denotes backwards incompatible changes (old dnf won't work with
|
|
Packit Service |
21c75c |
new transaction JSON).
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
MINOR version denotes extending the format without breaking backwards
|
|
Packit Service |
21c75c |
compatibility (old dnf can work with new transaction JSON). Forwards
|
|
Packit Service |
21c75c |
compatibility needs to be handled by being able to process the old format as
|
|
Packit Service |
21c75c |
well as the new one.
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class TransactionError(dnf.exceptions.Error):
|
|
Packit Service |
21c75c |
def __init__(self, msg):
|
|
Packit Service |
21c75c |
super(TransactionError, self).__init__(msg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class TransactionFileError(dnf.exceptions.Error):
|
|
Packit Service |
21c75c |
def __init__(self, filename, errors):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
:param filename: The name of the transaction file being replayed
|
|
Packit Service |
21c75c |
:param errors: a list of error classes or a string with an error description
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if isinstance(errors, (list, tuple)):
|
|
Packit Service |
21c75c |
if len(errors) > 1:
|
|
Packit Service |
21c75c |
msg = _('Errors in "{filename}":').format(filename=filename)
|
|
Packit Service |
21c75c |
for error in errors:
|
|
Packit Service |
21c75c |
msg += "\n " + str(error)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
super(TransactionFileError, self).__init__(msg)
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
errors = str(errors[0])
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
msg = _('Error in "{filename}": {error}').format(filename=filename, error=errors)
|
|
Packit Service |
21c75c |
super(TransactionFileError, self).__init__(msg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class IncompatibleTransactionVersionError(TransactionFileError):
|
|
Packit Service |
21c75c |
def __init__(self, filename, msg):
|
|
Packit Service |
21c75c |
super(IncompatibleTransactionVersionError, self).__init__(filename, msg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _check_version(version, filename):
|
|
Packit Service |
21c75c |
major, minor = version.split('.')
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
major = int(major)
|
|
Packit Service |
21c75c |
except ValueError as e:
|
|
Packit Service |
21c75c |
raise TransactionFileError(
|
|
Packit Service |
21c75c |
filename,
|
|
Packit Service |
21c75c |
_('Invalid major version "{major}", number expected.').format(major=major)
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
int(minor) # minor is unused, just check it's a number
|
|
Packit Service |
21c75c |
except ValueError as e:
|
|
Packit Service |
21c75c |
raise TransactionFileError(
|
|
Packit Service |
21c75c |
filename,
|
|
Packit Service |
21c75c |
_('Invalid minor version "{minor}", number expected.').format(minor=minor)
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if major != VERSION_MAJOR:
|
|
Packit Service |
21c75c |
raise IncompatibleTransactionVersionError(
|
|
Packit Service |
21c75c |
filename,
|
|
Packit Service |
21c75c |
_('Incompatible major version "{major}", supported major version is "{major_supp}".')
|
|
Packit Service |
21c75c |
.format(major=major, major_supp=VERSION_MAJOR)
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def serialize_transaction(transaction):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
Serializes a transaction to a data structure that is equivalent to the stored JSON format.
|
|
Packit Service |
21c75c |
:param transaction: the transaction to serialize (an instance of dnf.db.history.TransactionWrapper)
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
data = {
|
|
Packit Service |
21c75c |
"version": VERSION,
|
|
Packit Service |
21c75c |
}
|
|
Packit Service |
21c75c |
rpms = []
|
|
Packit Service |
21c75c |
groups = []
|
|
Packit Service |
21c75c |
environments = []
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for tsi in transaction.packages():
|
|
Packit Service |
21c75c |
if tsi.is_package():
|
|
Packit Service |
21c75c |
rpms.append({
|
|
Packit Service |
21c75c |
"action": tsi.action_name,
|
|
Packit Service |
21c75c |
"nevra": tsi.nevra,
|
|
Packit Service |
21c75c |
"reason": libdnf.transaction.TransactionItemReasonToString(tsi.reason),
|
|
Packit Service |
21c75c |
"repo_id": tsi.from_repo
|
|
Packit Service |
21c75c |
})
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
elif tsi.is_group():
|
|
Packit Service |
21c75c |
group = tsi.get_group()
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
group_data = {
|
|
Packit Service |
21c75c |
"action": tsi.action_name,
|
|
Packit Service |
21c75c |
"id": group.getGroupId(),
|
|
Packit Service |
21c75c |
"packages": [],
|
|
Packit Service |
21c75c |
"package_types": libdnf.transaction.compsPackageTypeToString(group.getPackageTypes())
|
|
Packit Service |
21c75c |
}
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for pkg in group.getPackages():
|
|
Packit Service |
21c75c |
group_data["packages"].append({
|
|
Packit Service |
21c75c |
"name": pkg.getName(),
|
|
Packit Service |
21c75c |
"installed": pkg.getInstalled(),
|
|
Packit Service |
21c75c |
"package_type": libdnf.transaction.compsPackageTypeToString(pkg.getPackageType())
|
|
Packit Service |
21c75c |
})
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
groups.append(group_data)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
elif tsi.is_environment():
|
|
Packit Service |
21c75c |
env = tsi.get_environment()
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
env_data = {
|
|
Packit Service |
21c75c |
"action": tsi.action_name,
|
|
Packit Service |
21c75c |
"id": env.getEnvironmentId(),
|
|
Packit Service |
21c75c |
"groups": [],
|
|
Packit Service |
21c75c |
"package_types": libdnf.transaction.compsPackageTypeToString(env.getPackageTypes())
|
|
Packit Service |
21c75c |
}
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for grp in env.getGroups():
|
|
Packit Service |
21c75c |
env_data["groups"].append({
|
|
Packit Service |
21c75c |
"id": grp.getGroupId(),
|
|
Packit Service |
21c75c |
"installed": grp.getInstalled(),
|
|
Packit Service |
21c75c |
"group_type": libdnf.transaction.compsPackageTypeToString(grp.getGroupType())
|
|
Packit Service |
21c75c |
})
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
environments.append(env_data)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if rpms:
|
|
Packit Service |
21c75c |
data["rpms"] = rpms
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if groups:
|
|
Packit Service |
21c75c |
data["groups"] = groups
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if environments:
|
|
Packit Service |
21c75c |
data["environments"] = environments
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return data
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class TransactionReplay(object):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
A class that encapsulates replaying a transaction. The transaction data are
|
|
Packit Service |
21c75c |
loaded and stored when the class is initialized. The transaction is run by
|
|
Packit Service |
21c75c |
calling the `run()` method, after the transaction is created (but before it is
|
|
Packit Service |
21c75c |
performed), the `post_transaction()` method needs to be called to verify no
|
|
Packit Service |
21c75c |
extra packages were pulled in and also to fix the reasons.
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __init__(
|
|
Packit Service |
21c75c |
self,
|
|
Packit Service |
21c75c |
base,
|
|
Packit Service |
21c75c |
fn,
|
|
Packit Service |
21c75c |
ignore_extras=False,
|
|
Packit Service |
21c75c |
ignore_installed=False,
|
|
Packit Service |
21c75c |
skip_unavailable=False
|
|
Packit Service |
21c75c |
):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
:param base: the dnf base
|
|
Packit Service |
21c75c |
:param fn: the filename to load the transaction from
|
|
Packit Service |
21c75c |
:param ignore_extras: whether to ignore extra package pulled into the transaction
|
|
Packit Service |
21c75c |
:param ignore_installed: whether to ignore installed versions of packages
|
|
Packit Service |
21c75c |
:param skip_unavailable: whether to skip transaction packages that aren't available
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._base = base
|
|
Packit Service |
21c75c |
self._filename = fn
|
|
Packit Service |
21c75c |
self._ignore_installed = ignore_installed
|
|
Packit Service |
21c75c |
self._ignore_extras = ignore_extras
|
|
Packit Service |
21c75c |
self._skip_unavailable = skip_unavailable
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not self._base.conf.strict:
|
|
Packit Service |
21c75c |
self._skip_unavailable = True
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._nevra_cache = set()
|
|
Packit Service |
21c75c |
self._nevra_reason_cache = {}
|
|
Packit Service |
21c75c |
self._warnings = []
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
with open(fn, "r") as f:
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
self._replay_data = json.load(f)
|
|
Packit Service |
21c75c |
except json.decoder.JSONDecodeError as e:
|
|
Packit Service |
21c75c |
raise TransactionFileError(fn, str(e) + ".")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
self._verify_toplevel_json(self._replay_data)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._rpms = self._replay_data.get("rpms", [])
|
|
Packit Service |
21c75c |
self._assert_type(self._rpms, list, "rpms", "array")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._groups = self._replay_data.get("groups", [])
|
|
Packit Service |
21c75c |
self._assert_type(self._groups, list, "groups", "array")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._environments = self._replay_data.get("environments", [])
|
|
Packit Service |
21c75c |
self._assert_type(self._environments, list, "environments", "array")
|
|
Packit Service |
21c75c |
except TransactionError as e:
|
|
Packit Service |
21c75c |
raise TransactionFileError(fn, e)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _raise_or_warn(self, warn_only, msg):
|
|
Packit Service |
21c75c |
if warn_only:
|
|
Packit Service |
21c75c |
self._warnings.append(msg)
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
raise TransactionError(msg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _assert_type(self, value, t, id, expected):
|
|
Packit Service |
21c75c |
if not isinstance(value, t):
|
|
Packit Service |
21c75c |
raise TransactionError(_('Unexpected type of "{id}", {exp} expected.').format(id=id, exp=expected))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _verify_toplevel_json(self, replay_data):
|
|
Packit Service |
21c75c |
fn = self._filename
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if "version" not in replay_data:
|
|
Packit Service |
21c75c |
raise TransactionFileError(fn, _('Missing key "{key}".'.format(key="version")))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
self._assert_type(replay_data["version"], str, "version", "string")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
_check_version(replay_data["version"], fn)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _replay_pkg_action(self, pkg_data):
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
action = pkg_data["action"]
|
|
Packit Service |
21c75c |
nevra = pkg_data["nevra"]
|
|
Packit Service |
21c75c |
reason = libdnf.transaction.StringToTransactionItemReason(pkg_data["reason"])
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Missing object key "{key}" in an rpm.').format(key=e.args[0])
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
except IndexError as e:
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Unexpected value of package reason "{reason}" for rpm nevra "{nevra}".')
|
|
Packit Service |
21c75c |
.format(reason=pkg_data["reason"], nevra=nevra)
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
subj = hawkey.Subject(nevra)
|
|
Packit Service |
21c75c |
parsed_nevras = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if len(parsed_nevras) != 1:
|
|
Packit Service |
21c75c |
raise TransactionError(_('Cannot parse NEVRA for package "{nevra}".').format(nevra=nevra))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
parsed_nevra = parsed_nevras[0]
|
|
Packit Service |
21c75c |
na = "%s.%s" % (parsed_nevra.name, parsed_nevra.arch)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
query_na = self._base.sack.query().filter(name=parsed_nevra.name, arch=parsed_nevra.arch)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
epoch = parsed_nevra.epoch if parsed_nevra.epoch is not None else 0
|
|
Packit Service |
21c75c |
query = query_na.filter(epoch=epoch, version=parsed_nevra.version, release=parsed_nevra.release)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not query:
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._skip_unavailable, _('Cannot find rpm nevra "{nevra}".').format(nevra=nevra))
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# a cache to check no extra packages were pulled into the transaction
|
|
Packit Service |
21c75c |
if action != "Reason Change":
|
|
Packit Service |
21c75c |
self._nevra_cache.add(nevra)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# store reasons for forward actions and "Removed", the rest of the
|
|
Packit Service |
21c75c |
# actions reasons should stay as they were determined by the transaction
|
|
Packit Service |
21c75c |
if action in ("Install", "Upgrade", "Downgrade", "Reinstall", "Removed"):
|
|
Packit Service |
21c75c |
self._nevra_reason_cache[nevra] = reason
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if action in ("Install", "Upgrade", "Downgrade"):
|
|
Packit Service |
21c75c |
if action == "Install" and query_na.installed() and not self._base._get_installonly_query(query_na):
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._ignore_installed,
|
|
Packit Service |
21c75c |
_('Package "{na}" is already installed for action "{action}".').format(na=na, action=action))
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
sltr = dnf.selector.Selector(self._base.sack).set(pkg=query)
|
|
Packit Service |
21c75c |
self._base.goal.install(select=sltr, optional=not self._base.conf.strict)
|
|
Packit Service |
21c75c |
elif action == "Reinstall":
|
|
Packit Service |
21c75c |
query = query.available()
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not query:
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._skip_unavailable,
|
|
Packit Service |
21c75c |
_('Package nevra "{nevra}" not available in repositories for action "{action}".')
|
|
Packit Service |
21c75c |
.format(nevra=nevra, action=action))
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
sltr = dnf.selector.Selector(self._base.sack).set(pkg=query)
|
|
Packit Service |
21c75c |
self._base.goal.install(select=sltr, optional=not self._base.conf.strict)
|
|
Packit Service |
21c75c |
elif action in ("Upgraded", "Downgraded", "Reinstalled", "Removed", "Obsoleted"):
|
|
Packit Service |
21c75c |
query = query.installed()
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not query:
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._ignore_installed,
|
|
Packit Service |
21c75c |
_('Package nevra "{nevra}" not installed for action "{action}".').format(nevra=nevra, action=action))
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# erasing the original version (the reverse part of an action like
|
|
Packit Service |
21c75c |
# e.g. upgrade) is more robust, but we can't do it if
|
|
Packit Service |
21c75c |
# skip_unavailable is True, because if the forward part of the
|
|
Packit Service |
21c75c |
# action is skipped, we would simply remove the package here
|
|
Packit Service |
21c75c |
if not self._skip_unavailable or action == "Removed":
|
|
Packit Service |
21c75c |
for pkg in query:
|
|
Packit Service |
21c75c |
self._base.goal.erase(pkg, clean_deps=False)
|
|
Packit Service |
21c75c |
elif action == "Reason Change":
|
|
Packit Service |
21c75c |
self._base.history.set_reason(query[0], reason)
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Unexpected value of package action "{action}" for rpm nevra "{nevra}".')
|
|
Packit Service |
21c75c |
.format(action=action, nevra=nevra)
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _create_swdb_group(self, group_id, pkg_types, pkgs):
|
|
Packit Service |
21c75c |
comps_group = self._base.comps._group_by_id(group_id)
|
|
Packit Service |
21c75c |
if not comps_group:
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._skip_unavailable, _("Group id '%s' is not available.") % group_id)
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_group = self._base.history.group.new(group_id, comps_group.name, comps_group.ui_name, pkg_types)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
for pkg in pkgs:
|
|
Packit Service |
21c75c |
name = pkg["name"]
|
|
Packit Service |
21c75c |
self._assert_type(name, str, "groups.packages.name", "string")
|
|
Packit Service |
21c75c |
installed = pkg["installed"]
|
|
Packit Service |
21c75c |
self._assert_type(installed, bool, "groups.packages.installed", "boolean")
|
|
Packit Service |
21c75c |
package_type = pkg["package_type"]
|
|
Packit Service |
21c75c |
self._assert_type(package_type, str, "groups.packages.package_type", "string")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
swdb_group.addPackage(name, installed, libdnf.transaction.stringToCompsPackageType(package_type))
|
|
Packit Service |
21c75c |
except libdnf.error.Error as e:
|
|
Packit Service |
21c75c |
raise TransactionError(str(e))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Missing object key "{key}" in groups.packages.').format(key=e.args[0])
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return swdb_group
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_group_install(self, group_id, pkg_types, pkgs):
|
|
Packit Service |
21c75c |
swdb_group = self._create_swdb_group(group_id, pkg_types, pkgs)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_group is not None:
|
|
Packit Service |
21c75c |
self._base.history.group.install(swdb_group)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_group_upgrade(self, group_id, pkg_types, pkgs):
|
|
Packit Service |
21c75c |
if not self._base.history.group.get(group_id):
|
|
Packit Service |
21c75c |
self._raise_or_warn( self._ignore_installed, _("Group id '%s' is not installed.") % group_id)
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_group = self._create_swdb_group(group_id, pkg_types, pkgs)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_group is not None:
|
|
Packit Service |
21c75c |
self._base.history.group.upgrade(swdb_group)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_group_remove(self, group_id, pkg_types, pkgs):
|
|
Packit Service |
21c75c |
if not self._base.history.group.get(group_id):
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._ignore_installed, _("Group id '%s' is not installed.") % group_id)
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_group = self._create_swdb_group(group_id, pkg_types, pkgs)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_group is not None:
|
|
Packit Service |
21c75c |
self._base.history.group.remove(swdb_group)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _create_swdb_environment(self, env_id, pkg_types, groups):
|
|
Packit Service |
21c75c |
comps_env = self._base.comps._environment_by_id(env_id)
|
|
Packit Service |
21c75c |
if not comps_env:
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._skip_unavailable, _("Environment id '%s' is not available.") % env_id)
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_env = self._base.history.env.new(env_id, comps_env.name, comps_env.ui_name, pkg_types)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
for grp in groups:
|
|
Packit Service |
21c75c |
id = grp["id"]
|
|
Packit Service |
21c75c |
self._assert_type(id, str, "environments.groups.id", "string")
|
|
Packit Service |
21c75c |
installed = grp["installed"]
|
|
Packit Service |
21c75c |
self._assert_type(installed, bool, "environments.groups.installed", "boolean")
|
|
Packit Service |
21c75c |
group_type = grp["group_type"]
|
|
Packit Service |
21c75c |
self._assert_type(group_type, str, "environments.groups.group_type", "string")
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
group_type = libdnf.transaction.stringToCompsPackageType(group_type)
|
|
Packit Service |
21c75c |
except libdnf.error.Error as e:
|
|
Packit Service |
21c75c |
raise TransactionError(str(e))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if group_type not in (
|
|
Packit Service |
21c75c |
libdnf.transaction.CompsPackageType_MANDATORY,
|
|
Packit Service |
21c75c |
libdnf.transaction.CompsPackageType_OPTIONAL
|
|
Packit Service |
21c75c |
):
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Invalid value "{group_type}" of environments.groups.group_type, '
|
|
Packit Service |
21c75c |
'only "mandatory" or "optional" is supported.'
|
|
Packit Service |
21c75c |
).format(group_type=grp["group_type"])
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_env.addGroup(id, installed, group_type)
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
raise TransactionError(
|
|
Packit Service |
21c75c |
_('Missing object key "{key}" in environments.groups.').format(key=e.args[0])
|
|
Packit Service |
21c75c |
)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return swdb_env
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_environment_install(self, env_id, pkg_types, groups):
|
|
Packit Service |
21c75c |
swdb_env = self._create_swdb_environment(env_id, pkg_types, groups)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_env is not None:
|
|
Packit Service |
21c75c |
self._base.history.env.install(swdb_env)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_environment_upgrade(self, env_id, pkg_types, groups):
|
|
Packit Service |
21c75c |
if not self._base.history.env.get(env_id):
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._ignore_installed,_("Environment id '%s' is not installed.") % env_id)
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_env = self._create_swdb_environment(env_id, pkg_types, groups)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_env is not None:
|
|
Packit Service |
21c75c |
self._base.history.env.upgrade(swdb_env)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _swdb_environment_remove(self, env_id, pkg_types, groups):
|
|
Packit Service |
21c75c |
if not self._base.history.env.get(env_id):
|
|
Packit Service |
21c75c |
self._raise_or_warn(self._ignore_installed, _("Environment id '%s' is not installed.") % env_id)
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
swdb_env = self._create_swdb_environment(env_id, pkg_types, groups)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if swdb_env is not None:
|
|
Packit Service |
21c75c |
self._base.history.env.remove(swdb_env)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def get_data(self):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
:returns: the loaded data of the transaction
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return self._replay_data
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def get_warnings(self):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
:returns: an array of warnings gathered during the transaction replay
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return self._warnings
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def run(self):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
Replays the transaction.
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
fn = self._filename
|
|
Packit Service |
21c75c |
errors = []
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for pkg_data in self._rpms:
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
self._replay_pkg_action(pkg_data)
|
|
Packit Service |
21c75c |
except TransactionError as e:
|
|
Packit Service |
21c75c |
errors.append(e)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for group_data in self._groups:
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
action = group_data["action"]
|
|
Packit Service |
21c75c |
group_id = group_data["id"]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
pkg_types = libdnf.transaction.stringToCompsPackageType(group_data["package_types"])
|
|
Packit Service |
21c75c |
except libdnf.error.Error as e:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(str(e)))
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if action == "Install":
|
|
Packit Service |
21c75c |
self._swdb_group_install(group_id, pkg_types, group_data["packages"])
|
|
Packit Service |
21c75c |
elif action == "Upgrade":
|
|
Packit Service |
21c75c |
self._swdb_group_upgrade(group_id, pkg_types, group_data["packages"])
|
|
Packit Service |
21c75c |
elif action == "Removed":
|
|
Packit Service |
21c75c |
self._swdb_group_remove(group_id, pkg_types, group_data["packages"])
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(
|
|
Packit Service |
21c75c |
_('Unexpected value of group action "{action}" for group "{group}".')
|
|
Packit Service |
21c75c |
.format(action=action, group=group_id)
|
|
Packit Service |
21c75c |
))
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(
|
|
Packit Service |
21c75c |
_('Missing object key "{key}" in a group.').format(key=e.args[0])
|
|
Packit Service |
21c75c |
))
|
|
Packit Service |
21c75c |
except TransactionError as e:
|
|
Packit Service |
21c75c |
errors.append(e)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for env_data in self._environments:
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
action = env_data["action"]
|
|
Packit Service |
21c75c |
env_id = env_data["id"]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
pkg_types = libdnf.transaction.stringToCompsPackageType(env_data["package_types"])
|
|
Packit Service |
21c75c |
except libdnf.error.Error as e:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(str(e)))
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if action == "Install":
|
|
Packit Service |
21c75c |
self._swdb_environment_install(env_id, pkg_types, env_data["groups"])
|
|
Packit Service |
21c75c |
elif action == "Upgrade":
|
|
Packit Service |
21c75c |
self._swdb_environment_upgrade(env_id, pkg_types, env_data["groups"])
|
|
Packit Service |
21c75c |
elif action == "Removed":
|
|
Packit Service |
21c75c |
self._swdb_environment_remove(env_id, pkg_types, env_data["groups"])
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(
|
|
Packit Service |
21c75c |
_('Unexpected value of environment action "{action}" for environment "{env}".')
|
|
Packit Service |
21c75c |
.format(action=action, env=env_id)
|
|
Packit Service |
21c75c |
))
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(
|
|
Packit Service |
21c75c |
_('Missing object key "{key}" in an environment.').format(key=e.args[0])
|
|
Packit Service |
21c75c |
))
|
|
Packit Service |
21c75c |
except TransactionError as e:
|
|
Packit Service |
21c75c |
errors.append(e)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if errors:
|
|
Packit Service |
21c75c |
raise TransactionFileError(fn, errors)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def post_transaction(self):
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
Sets reasons in the transaction history to values from the stored transaction.
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
Also serves to check whether additional packages were pulled in by the
|
|
Packit Service |
21c75c |
transaction, which results in an error (unless ignore_extras is True).
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not self._base.transaction:
|
|
Packit Service |
21c75c |
return
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
errors = []
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
for tsi in self._base.transaction:
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
pkg = tsi.pkg
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
# the transaction item has no package, happens for action == "Reason Change"
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
nevra = str(pkg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if nevra not in self._nevra_cache:
|
|
Packit Service |
21c75c |
# if ignore_installed is True, we don't want to check for
|
|
Packit Service |
21c75c |
# Upgraded/Downgraded/Reinstalled extras in the transaction,
|
|
Packit Service |
21c75c |
# basically those may be installed and we are ignoring them
|
|
Packit Service |
21c75c |
if not self._ignore_installed or not tsi.action in (
|
|
Packit Service |
21c75c |
libdnf.transaction.TransactionItemAction_UPGRADED,
|
|
Packit Service |
21c75c |
libdnf.transaction.TransactionItemAction_DOWNGRADED,
|
|
Packit Service |
21c75c |
libdnf.transaction.TransactionItemAction_REINSTALLED
|
|
Packit Service |
21c75c |
):
|
|
Packit Service |
21c75c |
msg = _('Package nevra "{nevra}", which is not present in the transaction file, was pulled '
|
|
Packit Service |
21c75c |
'into the transaction.'
|
|
Packit Service |
21c75c |
).format(nevra=nevra)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if not self._ignore_extras:
|
|
Packit Service |
21c75c |
errors.append(TransactionError(msg))
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
self._warnings.append(msg)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
replay_reason = self._nevra_reason_cache[nevra]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if tsi.action in (
|
|
Packit Service |
21c75c |
libdnf.transaction.TransactionItemAction_INSTALL,
|
|
Packit Service |
21c75c |
libdnf.transaction.TransactionItemAction_REMOVE
|
|
Packit Service |
21c75c |
) or libdnf.transaction.TransactionItemReasonCompare(replay_reason, tsi.reason) > 0:
|
|
Packit Service |
21c75c |
tsi.reason = replay_reason
|
|
Packit Service |
21c75c |
except KeyError as e:
|
|
Packit Service |
21c75c |
# if the pkg nevra wasn't found, we don't want to change the reason
|
|
Packit Service |
21c75c |
pass
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if errors:
|
|
Packit Service |
21c75c |
raise TransactionFileError(self._filename, errors)
|