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