|
Packit Service |
27f74b |
# Copyright (C) 2019 Red Hat, Inc.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# Licensed under the GNU Lesser General Public License Version 2.1
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# This library is free software; you can redistribute it and/or
|
|
Packit Service |
27f74b |
# modify it under the terms of the GNU Lesser General Public
|
|
Packit Service |
27f74b |
# License as published by the Free Software Foundation; either
|
|
Packit Service |
27f74b |
# version 2.1 of the License, or (at your option) any later version.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# This library is distributed in the hope that it will be useful,
|
|
Packit Service |
27f74b |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
27f74b |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
27f74b |
# Lesser General Public License for more details.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# You should have received a copy of the GNU Lesser General Public
|
|
Packit Service |
27f74b |
# License along with this library; if not, write to the Free Software
|
|
Packit Service |
27f74b |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
"""
|
|
Packit Service |
27f74b |
The plugin allows to define actions to be executed upon completing an RPM transaction. Each action
|
|
Packit Service |
27f74b |
may define a (glob-like) filtering rule on the package NEVRA or package files, as well as whether
|
|
Packit Service |
27f74b |
the package was installed or removed. Actions are defined in action files.
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
The action can start any shell command. Commands can contain variables. The same command (after
|
|
Packit Service |
27f74b |
variables substitution) is executed only once per transaction. The order of execution
|
|
Packit Service |
27f74b |
of the commands may differ from the order of the packages in the transaction.
|
|
Packit Service |
27f74b |
"""
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
from dnfpluginscore import _, logger
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
import libdnf.conf
|
|
Packit Service |
27f74b |
import dnf
|
|
Packit Service |
27f74b |
import dnf.transaction
|
|
Packit Service |
27f74b |
import glob
|
|
Packit Service |
27f74b |
import os
|
|
Packit Service |
27f74b |
import subprocess
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
class PostTransactionActions(dnf.Plugin):
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
name = "post-transaction-actions"
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def __init__(self, base, cli):
|
|
Packit Service |
27f74b |
super(PostTransactionActions, self).__init__(base, cli)
|
|
Packit Service |
27f74b |
self.actiondir = "/etc/dnf/plugins/post-transaction-actions.d/"
|
|
Packit Service |
27f74b |
self.base = base
|
|
Packit Service |
27f74b |
self.logger = logger
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def config(self):
|
|
Packit Service |
27f74b |
conf = self.read_config(self.base.conf)
|
|
Packit Service |
27f74b |
if conf.has_section("main"):
|
|
Packit Service |
27f74b |
if conf.has_option("main", "actiondir"):
|
|
Packit Service |
27f74b |
self.actiondir = conf.get("main", "actiondir")
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def _parse_actions(self):
|
|
Packit Service |
27f74b |
"""Parses *.action files from self.actiondir path.
|
|
Packit Service |
27f74b |
Parsed actions are stored in a list of tuples."""
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
action_file_list = []
|
|
Packit Service |
27f74b |
if os.access(self.actiondir, os.R_OK):
|
|
Packit Service |
27f74b |
action_file_list.extend(glob.glob(self.actiondir + "*.action"))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
action_tuples = [] # (action key, action_state, shell command)
|
|
Packit Service |
27f74b |
for file_name in action_file_list:
|
|
Packit Service |
27f74b |
for line in open(file_name).readlines():
|
|
Packit Service |
27f74b |
line = line.strip()
|
|
Packit Service |
27f74b |
if line and line[0] != "#":
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
(action_key, action_state, action_command) = line.split(':', 2)
|
|
Packit Service |
27f74b |
except ValueError as e:
|
|
Packit Service |
27f74b |
self.logger.error(_('Bad Action Line "%s": %s') % (line, e))
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
action_tuples.append((action_key, action_state, action_command))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
return action_tuples
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def _replace_vars(self, ts_item, command):
|
|
Packit Service |
27f74b |
"""Replaces variables in the command.
|
|
Packit Service |
27f74b |
Variables: ${name}, ${arch}, ${ver}, ${rel}, ${epoch}, ${repoid}, ${state}
|
|
Packit Service |
27f74b |
or $name, $arch, $ver, $rel, $epoch, $repoid, $state"""
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
action_dict = {dnf.transaction.PKG_DOWNGRADE: "downgrade",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_DOWNGRADED: "downgraded",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_INSTALL: "install",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_OBSOLETE: "obsolete",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_OBSOLETED: "obsoleted",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_REINSTALL: "reinstall",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_REINSTALLED: "reinstalled",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_REMOVE: "remove",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_UPGRADE: "upgrade",
|
|
Packit Service |
27f74b |
dnf.transaction.PKG_UPGRADED: "upgraded"}
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
action = action_dict.get(ts_item.action, "unknown - %s" % ts_item.action)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
vardict = {"name": ts_item.name,
|
|
Packit Service |
27f74b |
"arch": ts_item.arch,
|
|
Packit Service |
27f74b |
"ver": ts_item.version,
|
|
Packit Service |
27f74b |
"rel": ts_item.release,
|
|
Packit Service |
27f74b |
"epoch": str(ts_item.epoch),
|
|
Packit Service |
27f74b |
"repoid": ts_item.from_repo,
|
|
Packit Service |
27f74b |
"state": action}
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
result = libdnf.conf.ConfigParser_substitute(command, vardict)
|
|
Packit Service |
27f74b |
return result
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def transaction(self):
|
|
Packit Service |
27f74b |
action_tuples = self._parse_actions()
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
in_ts_items = []
|
|
Packit Service |
27f74b |
out_ts_items = []
|
|
Packit Service |
27f74b |
all_ts_items = []
|
|
Packit Service |
27f74b |
for ts_item in self.base.transaction:
|
|
Packit Service |
27f74b |
if ts_item.action in dnf.transaction.FORWARD_ACTIONS:
|
|
Packit Service |
27f74b |
in_ts_items.append(ts_item)
|
|
Packit Service |
27f74b |
elif ts_item.action in dnf.transaction.BACKWARD_ACTIONS:
|
|
Packit Service |
27f74b |
out_ts_items.append(ts_item)
|
|
Packit Service |
27f74b |
all_ts_items.append(ts_item)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
commands_to_run = set()
|
|
Packit Service |
27f74b |
for (a_key, a_state, a_command) in action_tuples:
|
|
Packit Service |
27f74b |
if a_state == "in":
|
|
Packit Service |
27f74b |
ts_items = in_ts_items
|
|
Packit Service |
27f74b |
elif a_state == "out":
|
|
Packit Service |
27f74b |
ts_items = out_ts_items
|
|
Packit Service |
27f74b |
elif a_state == "any":
|
|
Packit Service |
27f74b |
ts_items = all_ts_items
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
# unsupported state, skip it
|
|
Packit Service |
27f74b |
self.logger.error(_("Bad Transaction State: %s") % a_state)
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
subj = dnf.subject.Subject(a_key)
|
|
Packit Service |
27f74b |
pkgs = [tsi.pkg for tsi in ts_items]
|
|
Packit Service |
27f74b |
query = self.base.sack.query().filterm(pkg=pkgs)
|
|
Packit Service |
27f74b |
query = subj.get_best_query(self.base.sack, with_nevra=True, with_provides=False,
|
|
Packit Service |
27f74b |
with_filenames=True, query=query)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
for pkg in query:
|
|
Packit Service |
27f74b |
ts_item_list = [tsi for tsi in ts_items
|
|
Packit Service |
27f74b |
if tsi.pkg == pkg and tsi.pkg.reponame == pkg.reponame]
|
|
Packit Service |
27f74b |
ts_item = ts_item_list[0]
|
|
Packit Service |
27f74b |
command = self._replace_vars(ts_item, a_command)
|
|
Packit Service |
27f74b |
commands_to_run.add(command)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
for command in commands_to_run:
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
|
|
Packit Service |
27f74b |
stderr=subprocess.PIPE, close_fds=True,
|
|
Packit Service |
27f74b |
universal_newlines=True)
|
|
Packit Service |
27f74b |
stdout, stderr = proc.communicate()
|
|
Packit Service |
27f74b |
if stdout:
|
|
Packit Service |
27f74b |
self.logger.info(_('post-transaction-actions: %s') % (stdout))
|
|
Packit Service |
27f74b |
if stderr:
|
|
Packit Service |
27f74b |
self.logger.error(_('post-transaction-actions: %s') % (stderr))
|
|
Packit Service |
27f74b |
except Exception as e:
|
|
Packit Service |
27f74b |
self.logger.error(_('post-transaction-actions: Bad Command "%s": %s') %
|
|
Packit Service |
27f74b |
(command, e))
|