From: Michael Tremer Date: Mon, 15 Jan 2018 17:02:36 +0000 (+0100) Subject: python: Drop old transaction code X-Git-Tag: 0.9.28~1285^2~1186 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d304969c898760d7c9cd27496dec55217cf40c92;p=pakfire.git python: Drop old transaction code Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 8d2967534..01fb04c6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -133,7 +133,6 @@ pakfire_PYTHON = \ src/pakfire/progressbar.py \ src/pakfire/shell.py \ src/pakfire/system.py \ - src/pakfire/transaction.py \ src/pakfire/util.py pakfiredir = $(pythondir)/pakfire diff --git a/src/pakfire/base.py b/src/pakfire/base.py index 6bea1cc9f..b5aa828ac 100644 --- a/src/pakfire/base.py +++ b/src/pakfire/base.py @@ -28,7 +28,6 @@ from . import distro from . import filelist from . import packages from . import repository -from . import transaction from . import util import logging diff --git a/src/pakfire/cli.py b/src/pakfire/cli.py index c894df90f..bb2e42eec 100644 --- a/src/pakfire/cli.py +++ b/src/pakfire/cli.py @@ -37,7 +37,6 @@ from . import config from . import daemon from . import packages from . import repository -from . import transaction from . import ui from . import util diff --git a/src/pakfire/transaction.py b/src/pakfire/transaction.py deleted file mode 100644 index 0bf867a90..000000000 --- a/src/pakfire/transaction.py +++ /dev/null @@ -1,634 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Pakfire - The IPFire package management system # -# Copyright (C) 2011 Pakfire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -import os -import sys -import time - -from . import _pakfire -from . import i18n -from . import packages -from . import progressbar -from . import system -from . import util - -import logging -log = logging.getLogger("pakfire") - -from .constants import * -from .i18n import _ - -# Import all actions directly. -from .actions import * - -class TransactionCheck(object): - def __init__(self, pakfire, transaction): - self.pakfire = pakfire - self.transaction = transaction - - # A place to store errors. - self.errors = [] - - # Get a list of all installed files from the database. - self.filelist = self.load_filelist() - - # Get information about the mounted filesystems. - self.mountpoints = system.Mountpoints(self.pakfire.path) - - @property - def error_files(self): - ret = [] - - for name, count in list(self.filelist.items()): - if count > 1: - ret.append(name) - - return sorted(ret) - - def provides_file(self, name): - return [] # XXX TODO - - @property - def successful(self): - if self.error_files: - return False - - # Check if all mountpoints have enough space left. - for mp in self.mountpoints: - if mp.space_left < 0: - return False - - return True - - def print_errors(self, logger=None): - if logger is None: - logger = logging.getLogger("pakfire") - - for file in self.error_files: - pkgs = self.provides_file(file) - - if len(pkgs) == 2: - logger.critical( - _("file %(name)s from %(pkg1)s conflicts with file from package %(pkg2)s") % \ - { "name" : file, "pkg1" : pkgs[0], "pkg2" : pkgs[1] } - ) - - elif len(pkgs) >= 3: - logger.critical( - _("file %(name)s from %(pkg)s conflicts with files from %(pkgs)s") % \ - { "name" : file, "pkg" : pkgs[0], "pkgs" : i18n.list(pkgs[1:])} - ) - - else: - logger.critical( - _("file %(name)s causes the transaction test to fail for an unknown reason") % \ - { "name" : file } - ) - - for mp in self.mountpoints: - if mp.space_left >= 0: - continue - - logger.critical(_("There is not enough space left on %(name)s. Need at least %(size)s to perform transaction.") \ - % { "name" : mp.path, "size" : util.format_size(mp.space_needed) }) - - def load_filelist(self): - filelist = {} - - for file in self.pakfire.repos.local.filelist: - filelist[file] = 1 - - return filelist - - def install(self, pkg): - for file in pkg.filelist: - if file.is_dir(): - continue - - try: - self.filelist[file.name] += 1 - except KeyError: - self.filelist[file.name] = 1 - - # Add all filesize data to mountpoints. - self.mountpoints.add_pkg(pkg) - - def remove(self, pkg): - for file in pkg.filelist: - if file.is_dir(): - continue - - try: - self.filelist[file.name] -= 1 - except KeyError: - pass - - # Remove all filesize data from mountpoints. - self.mountpoints.rem_pkg(pkg) - - def update(self, pkg): - self.install(pkg) - - def cleanup(self, pkg): - self.remove(pkg) - - -class Step(object): - def __init__(self, pakfire, type, pkg): - self.pakfire = pakfire - - self.type = type - self.pkg = pkg - - @classmethod - def from_step(cls, pakfire, step): - pkg = packages.SolvPackage(pakfire, step.get_solvable()) - - return cls(pakfire, step.get_type(), pkg) - - def __repr__(self): - return "<%s %s %s>" % (self.__class__.__name__, self.type, self.pkg) - - def get_binary_pkg(self): - if not hasattr(self, "__binary_pkg"): - if self.type in (ActionCleanup.type, ActionRemove.type): - self.__binary_pkg = self.pkg.get_from_db() - assert self.__binary_pkg - else: - self.__binary_pkg = self.pkg.get_from_cache() - - return self.__binary_pkg - - def set_binary_pkg(self, pkg): - self.__binary_pkg = pkg - - binary_pkg = property(get_binary_pkg, set_binary_pkg) - - @property - def needs_download(self): - """ - Returns True if the package file needs to be downloaded. - """ - if not self.type in (ActionInstall.type, ActionReinstall.type, - ActionUpdate.type, ActionDowngrade.type): - return False - - # If no binary version of the package has been found, - # we don't need to download anything - return not self.binary_pkg - - def create_actions(self): - actions = [] - - for action_cls in Transaction.action_classes.get(self.type, []): - action = action_cls(self.pakfire, self.pkg, self.binary_pkg) - actions.append(action) - - return actions - - -class Transaction(object): - action_classes = { - ActionInstall.type : [ - ActionScriptPreTransIn, - ActionScriptPreIn, - ActionInstall, - ActionScriptPostIn, - ActionScriptPostTransIn, - ], - ActionReinstall.type : [ - ActionScriptPreTransIn, - ActionScriptPreIn, - ActionReinstall, - ActionScriptPostIn, - ActionScriptPostTransIn, - ], - ActionRemove.type : [ - ActionScriptPreTransUn, - ActionScriptPreUn, - ActionRemove, - ActionScriptPostUn, - ActionScriptPostTransUn, - ], - ActionUpdate.type : [ - ActionScriptPreTransUp, - ActionScriptPreUp, - ActionUpdate, - ActionScriptPostUp, - ActionScriptPostTransUp, - ], - ActionCleanup.type : [ - ActionCleanup, - ], - ActionDowngrade.type : [ - ActionScriptPreTransUp, - ActionScriptPreUp, - ActionDowngrade, - ActionScriptPostUp, - ActionScriptPostTransUp, - ], - } - - def __init__(self, pakfire): - self.pakfire = pakfire - - self._steps = [] - self.installsizechange = 0 - - def __bool__(self): - if self.steps: - return True - - return False - - @classmethod - def from_solver(cls, pakfire, solver): - # Create a new instance of our own transaction class. - transaction = cls(pakfire) - - # Get transaction data from the solver. - _transaction = _pakfire.Transaction(solver.solver) - - # Save installsizechange. - transaction.installsizechange = _transaction.get_installsizechange() - - # Get all steps that need to be done from the solver. - for step in _transaction.steps(): - step = Step.from_step(pakfire, step) - transaction.add_step(step) - - return transaction - - @property - def local(self): - # Shortcut to local repository. - return self.pakfire.repos.local - - @property - def steps(self): - return self._steps - - def add_step(self, step): - """ - Adds a new step to this transaction. - """ - assert isinstance(step, Step), step - - self._steps.append(step) - - def get_steps_by_type(self, type): - return [s for s in self.steps if s.type == type] - - @property - def installs(self): - return self.get_steps_by_type(ActionInstall.type) - - @property - def reinstalls(self): - return self.get_steps_by_type(ActionReinstall.type) - - @property - def removes(self): - return self.get_steps_by_type(ActionRemove.type) - - @property - def updates(self): - return self.get_steps_by_type(ActionUpdate.type) - - @property - def downgrades(self): - return self.get_steps_by_type(ActionDowngrade.type) - - def get_downloads(self): - """ - Returns a list of all steps that need - to a download. - """ - return [s for s in self.steps if s.needs_download] - - @property - def download_size(self): - """ - Returns the amount of bytes that need to be downloaded. - """ - if not hasattr(self, "__download_size"): - self.__download_size = sum((s.pkg.size for s in self.get_downloads())) - - return self.__download_size - - def download(self, logger=None): - if logger is None: - logger = logging.getLogger("pakfire") - - downloads = self.get_downloads() - - # If there are no downloads, we can just stop here. - if not downloads: - return - - # Get free space of the download location. - path = os.path.realpath(REPO_CACHE_DIR) - while not os.path.ismount(path): - path = os.path.dirname(path) - path_stat = os.statvfs(path) - - if self.download_size >= path_stat.f_bavail * path_stat.f_bsize: - raise DownloadError(_("Not enough space to download %s of packages.") \ - % util.format_size(self.download_size)) - - logger.info(_("Downloading packages:")) - time_start = time.time() - - counter = 0 - counter_downloads = len(downloads) - for step in downloads: - counter += 1 - - # Download the package file. - step.binary_pkg = step.pkg.download( - text="(%d/%d): " % (counter, counter_downloads), - logger=logger) - - # Write an empty line to the console when there have been any downloads. - width, height = util.terminal_size() - - # Print a nice line. - logger.info("-" * width) - - # Format and calculate download information. - time_stop = time.time() - download_time = time_stop - time_start - download_speed = self.download_size / download_time - download_speed = util.format_speed(download_speed) - download_size = util.format_size(self.download_size) - download_time = util.format_time(download_time) - - line = "%s | %5sB %s " % \ - (download_speed, self.download_size, download_time) - line = " " * (width - len(line)) + line - logger.info(line) - logger.info("") - - def print_section(self, caption, steps, format_string): - section = [caption,] - - pkgs = [s.pkg for s in steps] - for pkg in sorted(pkgs): - line = format_string % { - "arch" : pkg.arch, - "name" : pkg.name, - "repo" : pkg.repo.name, - "size" : util.format_size(pkg.size), - "version" : pkg.friendly_version, - } - section.append(line) - - section.append("") - - return section - - def dump(self, logger=None): - if logger is None: - logger = logging.getLogger("pakfire") - - if not self.steps: - logger.info(_("Nothing to do")) - return - - # Prepare some string formatting stuff. - # XXX this needs to adapt to the terminal size - format_string = " %(name)-21s %(arch)-8s %(version)-21s %(repo)-18s %(size)6s " - - # Prepare the headline. - headline = format_string % { - "arch" : _("Arch"), - "name" : _("Package"), - "repo" : _("Repository"), - "size" : _("Size"), - "version" : _("Version"), - } - - # As long, as we can't use the actual terminal width, we use the - # length of the headline. - terminal_width = len(headline) - - # Create a separator line. - sep_line = "=" * terminal_width - - # Create the header. - s = [sep_line, headline, sep_line,] - - steps = ( - (_("Installing:"), self.installs), - (_("Reinstalling:"), self.reinstalls), - (_("Updating:"), self.updates), - (_("Downgrading:"), self.downgrades), - (_("Removing:"), self.removes), - ) - - for caption, _steps in steps: - if not _steps: - continue - - s += self.print_section(caption, _steps, format_string) - - # Append the transaction summary - s.append(_("Transaction Summary")) - s.append(sep_line) - - for caption, _steps in steps: - if not _steps: - continue - - s.append("%-20s %-4d %s" % (caption, len(_steps), - _("package", "packages", len(_steps)))) - - # Calculate the size of all files that need to be downloaded this this - # transaction. - if self.download_size: - s.append(_("Total download size: %s") % util.format_size(self.download_size)) - - # Show the size that is consumed by the new packages. - if self.installsizechange > 0: - s.append(_("Installed size: %s") % util.format_size(self.installsizechange)) - elif self.installsizechange < 0: - s.append(_("Freed size: %s") % util.format_size(-self.installsizechange)) - s.append("") - - for line in s: - logger.info(line) - - def cli_yesno(self): - # Empty transactions are always denied. - if not self.steps: - return False - - return util.ask_user(_("Is this okay?")) - - def check(self, actions, logger=None): - if logger is None: - logger = logging.getLogger("pakfire") - - logger.info(_("Running Transaction Test")) - - # Initialize the check object. - check = TransactionCheck(self.pakfire, self) - - for action in actions: - try: - action.check(check) - except ActionError as e: - raise - - if check.successful: - logger.info(_("Transaction Test Succeeded")) - return - - # In case of an unsuccessful transaction test, we print the error - # and raise TransactionCheckError. - check.print_errors(logger=logger) - - raise TransactionCheckError(_("Transaction test was not successful")) - - def verify_signatures(self, mode=None, logger=None): - """ - Check the downloaded files for valid signatures. - """ - if not logger: - logger = log.getLogger("pakfire") - - if mode is None: - mode = self.pakfire.config.get("signatures", "mode", "strict") - - # If this disabled, we do nothing. - if mode == "disabled": - return - - # Search for steps we need to process. - steps = [] - for step in self.steps: - if not step.binary_pkg: - continue - - steps.append(step) - - # Make a nice progressbar. - p = progressbar.ProgressBar(len(steps)) - p.add(_("Verifying signatures...")) - p.add(progressbar.WidgetBar()) - p.add(progressbar.WidgetPercentage()) - - # Collect all errors. - errors = [] - - try: - p.start() - - # Do the verification for every action. - i = 0 - for step in steps: - # Update the progressbar. - if p: - i += 1 - p.update(i) - - try: - step.pkg.verify() - - except SignatureError as e: - errors.append("%s" % e) - finally: - if p: p.finish() - - # If no errors were found everything is fine. - if not errors: - logger.info("") - return - - # Raise a SignatureError in strict mode. - if mode == "strict": - raise SignatureError("\n".join(errors)) - - elif mode == "permissive": - logger.warning(_("Found %s signature error(s)!") % len(errors)) - for error in errors: - logger.warning(" %s" % error) - logger.warning("") - - logger.warning(_("Going on because we are running in permissive mode.")) - logger.warning(_("This is dangerous!")) - logger.warning("") - - def create_actions(self): - """ - Create actions from steps. - """ - actions = [] - actions_pre = [] - actions_post = [] - - for step in self.steps: - for action in step.create_actions(): - if isinstance(action, ActionScriptPreTrans): - actions_pre.append(action) - elif isinstance(action, ActionScriptPostTrans): - actions_post.append(action) - else: - actions.append(action) - - return actions_pre + actions + actions_post - - def run(self, logger=None, signatures_mode=None): - if logger is None: - logger = logging.getLogger("pakfire") - - # Download all packages. - # (don't add logger here because I do not want to see downloads - # in the build logs on the build service) - self.download() - - # Verify signatures. - #self.verify_signatures(mode=signatures_mode, logger=logger) - - # Create actions. - actions = self.create_actions() - - # Run the transaction test - self.check(actions, logger=logger) - - logger.info(_("Running transaction")) - # Run all actions in order and catch all kinds of ActionError. - for action in actions: - try: - action.run() - - except ActionError as e: - logger.error("Action finished with an error: %s - %s" % (action, e)) - #except Exception, e: - # logger.error(_("An unforeseen error occoured: %s") % e) - - logger.info("") - - # Commit repository metadata. - self.local.commit() - - # Call sync to make sure all buffers are written to disk. - _pakfire.sync()