]> git.ipfire.org Git - pakfire.git/blame - pakfire/repository/transaction.py
Add "remove" command to CLI to remove packages.
[pakfire.git] / pakfire / repository / transaction.py
CommitLineData
ae20b05f
MT
1#!/usr/bin/python
2
3import logging
4import os
5import progressbar
0c1a80b8 6import satsolver
ae20b05f
MT
7import sys
8
9import pakfire.packages as packages
10import pakfire.util as util
11
12from pakfire.i18n import _
13
14PKG_DUMP_FORMAT = " %-21s %-8s %-21s %-19s %5s "
15
16class ActionError(Exception):
17 pass
18
19
20class Action(object):
21 def __init__(self, pakfire, pkg, deps=None):
22 self.pakfire = pakfire
23 self.pkg = pkg
24 self.deps = deps or []
25
26 def __cmp__(self, other):
27 # XXX ugly
28 return cmp(self.__repr__(), other.__repr__())
29
30 def __repr__(self):
31 return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name)
32
33 def remove_dep(self, dep):
34 if not self.deps:
35 return
36
37 while dep in self.deps:
38 logging.debug("Removing dep %s from %s" % (dep, self))
39 self.deps.remove(dep)
40
41 def run(self):
42 raise NotImplementedError
43
44 @property
45 def local(self):
46 """
47 Reference to local repository (database).
48 """
49 return self.pakfire.repos.local
50
51
52class ActionCleanup(Action):
0c1a80b8 53 type = "ignore"
ae20b05f
MT
54
55 def run(self):
0c1a80b8 56 print "XXX Cleanup: %s" % self.pkg
ae20b05f
MT
57
58
59class ActionScript(Action):
60 def run(self):
61 pass # XXX TBD
62
63
64class ActionScriptPreIn(ActionScript):
65 pass
66
67
68class ActionScriptPostIn(ActionScript):
69 pass
70
71
72class ActionScriptPreUn(ActionScript):
73 pass
74
75
76class ActionScriptPostUn(ActionScript):
77 pass
78
79
80class ActionInstall(Action):
81 type = "install"
82
83 def extract(self, message, prefix=None):
84 logging.debug("Extracting package %s" % self.pkg.friendly_name)
85
86 if prefix is None:
87 prefix = self.pakfire.path
88
89 self.pkg.extract(message, prefix=prefix)
90
91 # Create package in the database
92 self.local.index.add_package(self.pkg)
93
94 def run(self):
95 self.extract(_("Installing: %s") % self.pkg.name)
96
97 self.pakfire.solver.add_package(self.pkg, "installed")
98
99
100class ActionUpdate(ActionInstall):
0c1a80b8 101 type = "upgrade"
ae20b05f
MT
102
103 def run(self):
104 self.extract(_("Updating: %s") % self.pkg.name)
105
106
107class ActionRemove(ActionCleanup):
a39fd08b 108 type = "erase"
ae20b05f
MT
109
110 def run(self):
111 files = self.pkg.filelist
112
113 if not files:
114 return
115
116 self.remove_files(_("Removing: %s") % self.pkg.name, files)
117
118
119class Transaction(object):
120 action_classes = [
121 ActionInstall,
122 ActionUpdate,
123 ActionRemove,
0c1a80b8 124 ActionCleanup,
ae20b05f
MT
125 ]
126
127 def __init__(self, pakfire):
128 self.pakfire = pakfire
129 self.actions = []
130
131 self.downloads = []
132
0c1a80b8
MT
133 self.installs = []
134 self.updates = []
135 self.removes = []
136
ae20b05f
MT
137 @classmethod
138 def from_solver(cls, pakfire, solver1, solver2):
139 # Grab the original transaction object from the solver.
140 _transaction = solver2.transaction()
141
142 # Order the objects in the transaction in that way we will run the
143 # installation.
144 _transaction.order()
145
146 # Create a new instance of our own transaction class.
147 transaction = cls(pakfire)
148
0c1a80b8
MT
149 # Copy all information.
150 transaction.installs = solver1.solvables2packages(solver2.installs())
151 transaction.updates = \
152 [p for p in solver1.solvables2packages(solver2.updates()) if not p.repo.name == "installed"]
153 transaction.removes = solver1.solvables2packages(solver2.removes())
154
ae20b05f 155 for step in _transaction.steps():
0c1a80b8 156 action = step.type_s(satsolver.TRANSACTION_MODE_ACTIVE)
ae20b05f
MT
157 pkg = solver1.id2pkg[step.solvable().id()]
158
0c1a80b8 159 if action in ("install", "upgrade") and not isinstance(pkg, packages.BinaryPackage):
ae20b05f
MT
160 transaction.downloads.append(pkg)
161
162 for action_cls in cls.action_classes:
163 if action_cls.type == action:
164 action = action_cls(pakfire, pkg)
165
166 if not isinstance(action, Action):
167 raise Exception, "Unknown action required: %s" % action
168
169 transaction.add_action(action)
170
171 return transaction
172
173 def download(self):
174 if not self.downloads:
175 return
176
177 i = 0
178 for pkg in self.downloads:
179 i += 1
180
181 # Actually download the package.
182 pkg_bin = pkg.download(text="(%2d/%02d): " % (i, len(self.downloads)))
183
184 # Replace the package in all actions where it matches.
185 actions = [a for a in self.actions if a.pkg == pkg]
186
187 for action in actions:
188 action.pkg = pkg_bin
189
190 # Reset packages to be downloaded.
191 self.downloads = []
192 print
193
ae20b05f
MT
194 def dump_pkg(self, pkg):
195 ret = []
196
197 name = pkg.name
198 if len(name) > 21:
199 ret.append(" %s" % name)
200 name = ""
201
202 ret.append(PKG_DUMP_FORMAT % (name, pkg.arch, pkg.friendly_version,
203 pkg.repo.name, util.format_size(pkg.size)))
204
205 return ret
206
207 def dump_pkgs(self, caption, pkgs):
208 if not pkgs:
209 return []
210
211 s = [caption,]
212 for pkg in sorted(pkgs):
213 s += self.dump_pkg(pkg)
214 s.append("")
215 return s
216
217 def dump(self, logger=None):
218 if not logger:
219 logger = logging.getLogger()
220
221 width = 80
222 line = "=" * width
223
224 s = []
225 s.append(line)
226 s.append(PKG_DUMP_FORMAT % (_("Package"), _("Arch"), _("Version"), _("Repository"), _("Size")))
227 s.append(line)
228
0c1a80b8
MT
229 s += self.dump_pkgs(_("Installing:"), self.installs)
230 s += self.dump_pkgs(_("Updating:"), self.updates)
231 s += self.dump_pkgs(_("Removing:"), self.removes)
ae20b05f
MT
232
233 s.append(_("Transaction Summary"))
234 s.append(line)
235
236 format = "%-20s %-4d %s"
237
238 if self.installs:
239 s.append(format % (_("Install"), len(self.installs), _("Package(s)")))
240
241 if self.updates:
242 s.append(format % (_("Updates"), len(self.updates), _("Package(s)")))
243
244 if self.removes:
245 s.append(format % (_("Remove"), len(self.removes), _("Package(s)")))
246
247 # Calculate the size of all files that need to be downloaded this this
248 # transaction.
249 download_size = sum([p.size for p in self.downloads])
250 if download_size:
251 s.append(_("Total download size: %s") % util.format_size(download_size))
252 s.append("")
253
254 for line in s:
255 logger.info(line)
256
257 def run_action(self, action):
258 try:
259 action.run()
260 except ActionError, e:
261 logging.error("Action finished with an error: %s - %s" % (action, e))
262
263 def add_action(self, action):
264 logging.debug("New action added: %s" % action)
265
266 self.actions.append(action)
267
268 def remove_action(self, action):
269 logging.debug("Removing action: %s" % action)
270
271 self.actions.remove(action)
272 for action in self.actions:
273 action.remove_dep(action)
274
275 def run(self):
276 # Download all packages.
277 self.download()
278
279 while True:
280 if not [a for a in self.actions]:
281 break
282
283 for action in self.actions:
284 if action.deps:
285 #logging.debug("Skipping %s which cannot be run now." % action)
286 continue
287
288 self.run_action(action)
289 self.remove_action(action)