]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/actions.py
Add transaction test for duplicate files.
[pakfire.git] / python / pakfire / actions.py
1 #!/usr/bin/python
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 ###############################################################################
21
22 import logging
23 import os
24
25 import chroot
26 import packages
27 import util
28
29 from constants import *
30 from i18n import _
31
32 class Action(object):
33 def __init__(self, pakfire, pkg):
34 self.pakfire = pakfire
35 self.pkg_solv = self.pkg = pkg
36
37 # Try to get the binary version of the package from the cache if
38 # any.
39 binary_package = self.pkg.get_from_cache()
40 if binary_package:
41 self.pkg = binary_package
42
43 self.init()
44
45 def __cmp__(self, other):
46 return cmp(self.pkg, other.pkg)
47
48 def __repr__(self):
49 return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name)
50
51 def init(self):
52 # A function to run additional initialization.
53 pass
54
55 def check(self, filelist):
56 # This is just a dummy test that does nothing at all.
57 return filelist
58
59 @property
60 def needs_download(self):
61 return self.type in ("install", "reinstall", "upgrade", "downgrade",) \
62 and not isinstance(self.pkg, packages.BinaryPackage)
63
64 def download(self, text):
65 if not self.needs_download:
66 return
67
68 self.pkg = self.pkg.download(text)
69
70 def run(self):
71 raise NotImplementedError
72
73 @property
74 def local(self):
75 """
76 Reference to local repository.
77 """
78 return self.pakfire.repos.local
79
80
81 class ActionScript(Action):
82 type = "script"
83 script_action = None
84
85 def init(self):
86 # Load the scriplet.
87 self.scriptlet = self.pkg.get_scriptlet(self.script_action)
88
89 @property
90 def interpreter(self):
91 """
92 Get the interpreter of this scriptlet.
93 """
94 return util.scriptlet_interpreter(self.scriptlet)
95
96 @property
97 def args(self):
98 return []
99
100 def run(self):
101 # Exit immediately, if the scriptlet is empty.
102 if not self.scriptlet:
103 return
104
105 # Actually run the scriplet.
106 logging.debug("Running scriptlet %s" % self)
107
108 # Check if the interpreter does exist and is executable.
109 if self.interpreter:
110 interpreter = "%s/%s" % (self.pakfire.path, self.interpreter)
111 if not os.path.exists(interpreter):
112 raise ActionError, _("Cannot run scriptlet because no interpreter is available: %s" \
113 % self.interpreter)
114
115 if not os.access(interpreter, os.X_OK):
116 raise ActionError, _("Cannot run scriptlet because the interpreter is not executable: %s" \
117 % self.interpreter)
118
119 # Create a name for the temporary script file.
120 script_file_chroot = os.path.join("/", LOCAL_TMP_PATH,
121 "scriptlet_%s" % util.random_string(10))
122 script_file = os.path.join(self.pakfire.path, script_file_chroot[1:])
123 assert script_file.startswith("%s/" % self.pakfire.path)
124
125 # Create script directory, if it does not exist.
126 script_dir = os.path.dirname(script_file)
127 if not os.path.exists(script_dir):
128 os.makedirs(script_dir)
129
130 # Write the scriptlet to a file that we can execute it.
131 try:
132 f = open(script_file, "wb")
133 f.write(self.scriptlet)
134 f.close()
135
136 # The file is only accessable by root.
137 os.chmod(script_file, 700)
138 except:
139 # Remove the file if an error occurs.
140 try:
141 os.unlink(script_file)
142 except OSError:
143 pass
144
145 # XXX catch errors and return a beautiful message to the user
146 raise
147
148 # Generate the script command.
149 command = [script_file_chroot] + self.args
150
151 # If we are running in /, we do not need to chroot there.
152 chroot_path = None
153 if not self.pakfire.path == "/":
154 chroot_path = self.pakfire.path
155
156 try:
157 ret = chroot.do(command, cwd="/tmp",
158 chrootPath=chroot_path,
159 personality=self.pakfire.distro.personality,
160 shell=False,
161 timeout=SCRIPTLET_TIMEOUT,
162 logger=logging.getLogger())
163
164 except Error, e:
165 raise ActionError, _("The scriptlet returned an error:\n%s" % e)
166
167 except commandTimeoutExpired:
168 raise ActionError, _("The scriptlet ran more than %s seconds and was killed." \
169 % SCRIPTLET_TIMEOUT)
170
171 finally:
172 # Remove the script file.
173 try:
174 os.unlink(script_file)
175 except OSError:
176 logging.debug("Could not remove scriptlet file: %s" % script_file)
177
178
179 class ActionScriptPreIn(ActionScript):
180 script_action = "prein"
181
182
183 class ActionScriptPostIn(ActionScript):
184 script_action = "postin"
185
186
187 class ActionScriptPreUn(ActionScript):
188 script_action = "preun"
189
190
191 class ActionScriptPostUn(ActionScript):
192 script_action = "postun"
193
194
195 class ActionScriptPreUp(ActionScript):
196 script_action = "preup"
197
198
199 class ActionScriptPostUp(ActionScript):
200 script_action = "postup"
201
202
203 class ActionScriptPostTrans(ActionScript):
204 pass
205
206
207 class ActionScriptPostTransIn(ActionScriptPostTrans):
208 script_action = "posttransin"
209
210
211 class ActionScriptPostTransUn(ActionScriptPostTrans):
212 script_action = "posttransun"
213
214
215 class ActionScriptPostTransUp(ActionScriptPostTrans):
216 script_action = "posttransup"
217
218
219 class ActionInstall(Action):
220 type = "install"
221
222 def check(self, check):
223 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
224
225 # Check if this package can be installed.
226 check.install(self.pkg)
227
228 def run(self):
229 # Add package to the database.
230 self.local.add_package(self.pkg)
231
232 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
233
234
235 class ActionUpdate(Action):
236 type = "upgrade"
237
238 def check(self, check):
239 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
240
241 # Check if this package can be updated.
242 check.update(self.pkg)
243
244 def run(self):
245 # Add new package to the database.
246 self.local.add_package(self.pkg)
247
248 self.pkg.extract(_("Updating"), prefix=self.pakfire.path)
249
250
251 class ActionRemove(Action):
252 type = "erase"
253
254 def __init__(self, *args, **kwargs):
255 Action.__init__(self, *args, **kwargs)
256
257 # XXX This is ugly, but works for the moment.
258 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
259 assert self.pkg
260
261 def check(self, check):
262 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
263
264 # Check if this package can be removed.
265 check.remove(self.pkg)
266
267 def run(self):
268 self.pkg.cleanup(_("Removing"), prefix=self.pakfire.path)
269
270 # Remove package from the database.
271 self.local.rem_package(self.pkg)
272
273
274 class ActionCleanup(Action):
275 type = "ignore"
276
277 def __init__(self, *args, **kwargs):
278 Action.__init__(self, *args, **kwargs)
279
280 # XXX This is ugly, but works for the moment.
281 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
282 assert self.pkg
283
284 def check(self, check):
285 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
286
287 # Check if this package can be removed.
288 check.cleanup(self.pkg)
289
290 def run(self):
291 # Cleaning up leftover files and stuff.
292 self.pkg.cleanup(_("Cleanup"), prefix=self.pakfire.path)
293
294 # Remove package from the database.
295 self.local.rem_package(self.pkg)
296
297
298 class ActionReinstall(Action):
299 type = "reinstall"
300
301 def check(self, check):
302 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
303
304 # Check if this package can be reinstalled.
305 check.remove(self.pkg)
306 check.install(self.pkg)
307
308 def run(self):
309 # Remove package from the database and add it afterwards.
310 # Sounds weird, but fixes broken entries in the database.
311 self.local.rem_package(self.pkg)
312 self.local.add_package(self.pkg)
313
314 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
315
316
317 class ActionDowngrade(Action):
318 type = "downgrade"
319
320 def check(self, check):
321 logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
322
323 # Check if this package can be downgraded.
324 check.install(self.pkg)
325
326 def run(self):
327 # Add new package to database.
328 self.local.add_package(self.pkg)
329
330 self.pkg.extract(_("Downgrading"), prefix=self.pakfire.path)
331
332
333 class ActionChange(Action):
334 type = "change"
335
336 # XXX still need to find out what this should be doing
337
338 def run(self):
339 print "XXX Change: %s" % self.pkg