]> git.ipfire.org Git - pakfire.git/blame - pakfire/transaction.py
Add copyright information to all files.
[pakfire.git] / pakfire / transaction.py
CommitLineData
ae20b05f 1#!/usr/bin/python
b792d887
MT
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
ae20b05f
MT
21
22import logging
23import os
24import progressbar
25import sys
0a9a2371 26import time
ae20b05f 27
196c5da0
MT
28import packages
29import satsolver
30import util
ae20b05f 31
1e80d5d7 32from constants import *
196c5da0 33from i18n import _
ae20b05f 34
022c792a 35PKG_DUMP_FORMAT = " %-21s %-8s %-21s %-18s %6s "
ae20b05f 36
1e80d5d7
MT
37# Import all actions directly.
38from actions import *
b3356c8c 39
ae20b05f 40class Transaction(object):
d767668b 41 action_classes = {
c600820f
MT
42 ActionInstall.type : [ActionScriptPreIn, ActionInstall, ActionScriptPostIn, ActionScriptPostTransIn],
43 ActionReinstall.type : [ActionScriptPreIn, ActionReinstall, ActionScriptPostIn, ActionScriptPostTransIn],
44 ActionRemove.type : [ActionScriptPreUn, ActionRemove, ActionScriptPostUn, ActionScriptPostTransUn],
45 ActionUpdate.type : [ActionScriptPreUp, ActionUpdate, ActionScriptPostUp, ActionScriptPostTransUp],
46 ActionCleanup.type : [ActionCleanup,],
47 ActionDowngrade.type : [ActionScriptPreUp, ActionDowngrade, ActionScriptPostUp, ActionScriptPostTransUp],
48 ActionChange.type : [ActionChange,],
d767668b 49 }
ae20b05f
MT
50
51 def __init__(self, pakfire):
52 self.pakfire = pakfire
53 self.actions = []
54
ee6715aa
MT
55 self.installsizechange = 0
56
ae20b05f 57 @classmethod
c605d735 58 def from_solver(cls, pakfire, solver, _transaction):
ae20b05f
MT
59 # Create a new instance of our own transaction class.
60 transaction = cls(pakfire)
61
ee6715aa
MT
62 # Save installsizechange.
63 transaction.installsizechange = _transaction.get_installsizechange()
64
c9a51ed9
MT
65 # Get all steps that need to be done from the solver.
66 steps = _transaction.steps()
67 if not steps:
68 return
69
d767668b
MT
70 actions = []
71 actions_post = []
72
c9a51ed9 73 for step in steps:
d767668b 74 action_name = step.get_type()
c605d735 75 pkg = packages.SolvPackage(pakfire, step.get_solvable())
ae20b05f 76
d767668b
MT
77 try:
78 classes = transaction.action_classes[action_name]
79 except KeyError:
80 raise Exception, "Unknown action required: %s" % action_name
81
82 for action_cls in classes:
83 action = action_cls(pakfire, pkg)
84 assert isinstance(action, Action), action
ae20b05f 85
d767668b
MT
86 if isinstance(action, ActionScriptPostTrans):
87 actions_post.append(action)
88 else:
89 actions.append(action)
ae20b05f 90
d767668b 91 transaction.actions += actions + actions_post
ae20b05f
MT
92
93 return transaction
94
d4c94aa5
MT
95 @property
96 def installs(self):
97 return [a.pkg for a in self.actions if isinstance(a, ActionInstall)]
ae20b05f 98
d4c94aa5
MT
99 @property
100 def reinstalls(self):
101 return [a.pkg for a in self.actions if isinstance(a, ActionReinstall)]
ae20b05f 102
d4c94aa5
MT
103 @property
104 def removes(self):
105 return [a.pkg for a in self.actions if isinstance(a, ActionRemove)]
ae20b05f 106
d4c94aa5
MT
107 @property
108 def updates(self):
109 return [a.pkg for a in self.actions if isinstance(a, ActionUpdate)]
ae20b05f 110
d4c94aa5
MT
111 @property
112 def downgrades(self):
113 return [a.pkg for a in self.actions if isinstance(a, ActionDowngrade)]
ae20b05f 114
d4c94aa5
MT
115 @property
116 def downloads(self):
117 return [a for a in self.actions if a.needs_download]
118
119 def download(self):
0a9a2371
MT
120 # Get all download actions as a list.
121 downloads = [d for d in self.downloads]
122 downloads.sort()
123
124 # If there are no downloads, we can just stop here.
125 if not downloads:
126 return
127
128 logging.info(_("Downloading packages:"))
129 time_start = time.time()
130
131 # Calculate downloadsize.
132 download_size = 0
133 for action in downloads:
134 download_size += action.pkg.size
d4c94aa5
MT
135
136 i = 0
0a9a2371 137 for action in downloads:
d4c94aa5 138 i += 1
0a9a2371
MT
139 action.download(text="(%d/%d): " % (i, len(downloads)))
140
141 # Write an empty line to the console when there have been any downloads.
142 width, height = util.terminal_size()
143
144 # Print a nice line.
145 logging.info("-" * width)
146
147 # Format and calculate download information.
148 time_stop = time.time()
149 download_time = time_stop - time_start
150 download_speed = download_size / download_time
151 download_speed = util.format_speed(download_speed)
152 download_size = util.format_size(download_size)
153 download_time = util.format_time(download_time)
154
8cb74f39 155 line = "%s | %5sB %s " % \
0a9a2371
MT
156 (download_speed, download_size, download_time)
157 line = " " * (width - len(line)) + line
158 logging.info(line)
159 logging.info("")
ae20b05f 160
ae20b05f
MT
161 def dump_pkg(self, pkg):
162 ret = []
163
164 name = pkg.name
165 if len(name) > 21:
166 ret.append(" %s" % name)
167 name = ""
168
169 ret.append(PKG_DUMP_FORMAT % (name, pkg.arch, pkg.friendly_version,
170 pkg.repo.name, util.format_size(pkg.size)))
171
172 return ret
173
174 def dump_pkgs(self, caption, pkgs):
175 if not pkgs:
176 return []
177
178 s = [caption,]
179 for pkg in sorted(pkgs):
180 s += self.dump_pkg(pkg)
181 s.append("")
182 return s
183
184 def dump(self, logger=None):
185 if not logger:
186 logger = logging.getLogger()
187
188 width = 80
189 line = "=" * width
190
d4c94aa5 191 s = [""]
ae20b05f 192 s.append(line)
c64002fd
MT
193 s.append(PKG_DUMP_FORMAT % (_("Package"), _("Arch"), _("Version"),
194 _("Repository"), _("Size")))
ae20b05f
MT
195 s.append(line)
196
d4c94aa5
MT
197 actions = (
198 (_("Installing:"), self.installs),
199 (_("Reinstalling:"), self.reinstalls),
200 (_("Updating:"), self.updates),
201 (_("Downgrading:"), self.downgrades),
202 (_("Removing:"), self.removes),
203 )
204
205 for caption, pkgs in actions:
206 s += self.dump_pkgs(caption, pkgs)
ae20b05f
MT
207
208 s.append(_("Transaction Summary"))
209 s.append(line)
210
d4c94aa5
MT
211 for caption, pkgs in actions:
212 if not len(pkgs):
213 continue
c64002fd
MT
214 s.append("%-20s %-4d %s" % (caption, len(pkgs),
215 _("package", "packages", len(pkgs))))
ae20b05f
MT
216
217 # Calculate the size of all files that need to be downloaded this this
218 # transaction.
d4c94aa5 219 download_size = sum([a.pkg.size for a in self.downloads])
ae20b05f
MT
220 if download_size:
221 s.append(_("Total download size: %s") % util.format_size(download_size))
ee6715aa
MT
222
223 # Show the size that is consumed by the new packages.
224 if self.installsizechange > 0:
225 s.append(_("Installed size: %s") % util.format_size(self.installsizechange))
226 elif self.installsizechange < 0:
227 s.append(_("Freed size: %s") % util.format_size(self.installsizechange))
ae20b05f
MT
228 s.append("")
229
230 for line in s:
231 logger.info(line)
232
c0fd807c
MT
233 def cli_yesno(self, logger=None):
234 self.dump(logger)
235
236 return util.ask_user(_("Is this okay?"))
237
ae20b05f
MT
238 def run(self):
239 # Download all packages.
240 self.download()
241
8cb74f39 242 logging.info(_("Running transaction"))
c64002fd
MT
243 # Run all actions in order and catch all kinds of ActionError.
244 for action in self.actions:
245 try:
246 action.run()
247 except ActionError, e:
248 logging.error("Action finished with an error: %s - %s" % (action, e))
6ee3d6b9
MT
249
250 logging.info("")