]>
Commit | Line | Data |
---|---|---|
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 | |
22 | import logging | |
23 | import os | |
24 | import progressbar | |
25 | import sys | |
0a9a2371 | 26 | import time |
ae20b05f | 27 | |
196c5da0 MT |
28 | import packages |
29 | import satsolver | |
30 | import util | |
ae20b05f | 31 | |
1e80d5d7 | 32 | from constants import * |
196c5da0 | 33 | from i18n import _ |
ae20b05f | 34 | |
022c792a | 35 | PKG_DUMP_FORMAT = " %-21s %-8s %-21s %-18s %6s " |
ae20b05f | 36 | |
1e80d5d7 MT |
37 | # Import all actions directly. |
38 | from actions import * | |
b3356c8c | 39 | |
ae20b05f | 40 | class 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("") |