]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/transaction.py
2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
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. #
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. #
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/>. #
20 ###############################################################################
33 from constants
import *
36 PKG_DUMP_FORMAT
= " %-21s %-8s %-21s %-18s %6s "
38 # Import all actions directly.
41 class TransactionCheck(object):
42 def __init__(self
, pakfire
, transaction
):
43 self
.pakfire
= pakfire
44 self
.transaction
= transaction
46 # A place to store errors.
49 # Get a list of all installed files from the database.
50 self
.filelist
= self
.load_filelist()
53 def error_files(self
):
56 for name
, files
in self
.filelist
.items():
66 return not self
.error_files
68 def print_errors(self
):
69 for name
, files
in sorted(self
.error_files
.items()):
70 assert len(files
) >= 2
72 pkgs
= [f
.pkg
.friendly_name
for f
in files
]
76 _("file %s from %s conflicts with file from package %s") % \
77 (name
, pkgs
[0], pkgs
[1])
82 _("file %s from %s conflicts with files from %s") % \
83 (name
, pkgs
[0], i18n
.list(pkgs
[1:]))
86 def load_filelist(self
):
89 for file in self
.pakfire
.repos
.local
.filelist
:
90 filelist
[file.name
] = [file,]
94 def install(self
, pkg
):
95 for file in pkg
.filelist
:
99 if self
.filelist
.has_key(file.name
):
100 self
.filelist
[file.name
].append(file)
103 self
.filelist
[file.name
] = [file,]
105 def remove(self
, pkg
):
106 for file in pkg
.filelist
:
108 self
.filelist
[file.name
].remove(file)
109 except (KeyError, ValueError):
112 def update(self
, pkg
):
115 def cleanup(self
, pkg
):
119 class Transaction(object):
121 ActionInstall
.type : [ActionScriptPreIn
, ActionInstall
, ActionScriptPostIn
, ActionScriptPostTransIn
],
122 ActionReinstall
.type : [ActionScriptPreIn
, ActionReinstall
, ActionScriptPostIn
, ActionScriptPostTransIn
],
123 ActionRemove
.type : [ActionScriptPreUn
, ActionRemove
, ActionScriptPostUn
, ActionScriptPostTransUn
],
124 ActionUpdate
.type : [ActionScriptPreUp
, ActionUpdate
, ActionScriptPostUp
, ActionScriptPostTransUp
],
125 ActionCleanup
.type : [ActionCleanup
,],
126 ActionDowngrade
.type : [ActionScriptPreUp
, ActionDowngrade
, ActionScriptPostUp
, ActionScriptPostTransUp
],
127 ActionChange
.type : [ActionChange
,],
130 def __init__(self
, pakfire
):
131 self
.pakfire
= pakfire
134 self
.installsizechange
= 0
137 def from_solver(cls
, pakfire
, solver
, _transaction
):
138 # Create a new instance of our own transaction class.
139 transaction
= cls(pakfire
)
141 # Save installsizechange.
142 transaction
.installsizechange
= _transaction
.get_installsizechange()
144 # Get all steps that need to be done from the solver.
145 steps
= _transaction
.steps()
153 action_name
= step
.get_type()
154 pkg
= packages
.SolvPackage(pakfire
, step
.get_solvable())
157 classes
= transaction
.action_classes
[action_name
]
159 raise Exception, "Unknown action required: %s" % action_name
161 for action_cls
in classes
:
162 action
= action_cls(pakfire
, pkg
)
163 assert isinstance(action
, Action
), action
165 if isinstance(action
, ActionScriptPostTrans
):
166 actions_post
.append(action
)
168 actions
.append(action
)
170 transaction
.actions
+= actions
+ actions_post
176 return [a
.pkg
for a
in self
.actions
if isinstance(a
, ActionInstall
)]
179 def reinstalls(self
):
180 return [a
.pkg
for a
in self
.actions
if isinstance(a
, ActionReinstall
)]
184 return [a
.pkg
for a
in self
.actions
if isinstance(a
, ActionRemove
)]
188 return [a
.pkg
for a
in self
.actions
if isinstance(a
, ActionUpdate
)]
191 def downgrades(self
):
192 return [a
.pkg
for a
in self
.actions
if isinstance(a
, ActionDowngrade
)]
196 return sorted([a
for a
in self
.actions
if a
.needs_download
])
199 # Get all download actions as a list.
200 downloads
= [d
for d
in self
.downloads
]
203 # If there are no downloads, we can just stop here.
207 logging
.info(_("Downloading packages:"))
208 time_start
= time
.time()
210 # Calculate downloadsize.
212 for action
in downloads
:
213 download_size
+= action
.pkg
.size
216 for action
in downloads
:
218 action
.download(text
="(%d/%d): " % (i
, len(downloads
)))
220 # Write an empty line to the console when there have been any downloads.
221 width
, height
= util
.terminal_size()
224 logging
.info("-" * width
)
226 # Format and calculate download information.
227 time_stop
= time
.time()
228 download_time
= time_stop
- time_start
229 download_speed
= download_size
/ download_time
230 download_speed
= util
.format_speed(download_speed
)
231 download_size
= util
.format_size(download_size
)
232 download_time
= util
.format_time(download_time
)
234 line
= "%s | %5sB %s " % \
235 (download_speed
, download_size
, download_time
)
236 line
= " " * (width
- len(line
)) + line
240 def dump_pkg(self
, pkg
):
245 ret
.append(" %s" % name
)
248 ret
.append(PKG_DUMP_FORMAT
% (name
, pkg
.arch
, pkg
.friendly_version
,
249 pkg
.repo
.name
, util
.format_size(pkg
.size
)))
253 def dump_pkgs(self
, caption
, pkgs
):
258 for pkg
in sorted(pkgs
):
259 s
+= self
.dump_pkg(pkg
)
263 def dump(self
, logger
=None):
265 logger
= logging
.getLogger()
272 s
.append(PKG_DUMP_FORMAT
% (_("Package"), _("Arch"), _("Version"),
273 _("Repository"), _("Size")))
277 (_("Installing:"), self
.installs
),
278 (_("Reinstalling:"), self
.reinstalls
),
279 (_("Updating:"), self
.updates
),
280 (_("Downgrading:"), self
.downgrades
),
281 (_("Removing:"), self
.removes
),
284 for caption
, pkgs
in actions
:
285 s
+= self
.dump_pkgs(caption
, pkgs
)
287 s
.append(_("Transaction Summary"))
290 for caption
, pkgs
in actions
:
293 s
.append("%-20s %-4d %s" % (caption
, len(pkgs
),
294 _("package", "packages", len(pkgs
))))
296 # Calculate the size of all files that need to be downloaded this this
298 download_size
= sum([a
.pkg
.size
for a
in self
.downloads
])
300 s
.append(_("Total download size: %s") % util
.format_size(download_size
))
302 # Show the size that is consumed by the new packages.
303 if self
.installsizechange
> 0:
304 s
.append(_("Installed size: %s") % util
.format_size(self
.installsizechange
))
305 elif self
.installsizechange
< 0:
306 s
.append(_("Freed size: %s") % util
.format_size(self
.installsizechange
))
312 def cli_yesno(self
, logger
=None):
315 return util
.ask_user(_("Is this okay?"))
318 logging
.info(_("Running Transaction Test"))
320 # Initialize the check object.
321 check
= TransactionCheck(self
.pakfire
, self
)
323 for action
in self
.actions
:
326 except ActionError
, e
:
330 logging
.info(_("Transaction Test Succeeded"))
334 #raise TransactionCheckError, _("Transaction test was not successful")
337 # Download all packages.
340 # Run the transaction test
343 logging
.info(_("Running transaction"))
344 # Run all actions in order and catch all kinds of ActionError.
345 for action
in self
.actions
:
348 except ActionError
, e
:
349 logging
.error("Action finished with an error: %s - %s" % (action
, e
))