]>
Commit | Line | Data |
---|---|---|
9137135a MT |
1 | #!/usr/bin/python |
2 | ||
f6e6ff79 MT |
3 | import datetime |
4 | import logging | |
9137135a MT |
5 | import os |
6 | import shutil | |
9137135a | 7 | |
f6e6ff79 MT |
8 | import pakfire |
9 | import pakfire.packages as packages | |
10 | ||
2c909128 MT |
11 | from . import arches |
12 | from . import base | |
13 | from . import database | |
14 | from . import misc | |
15 | from . import sources | |
9137135a | 16 | |
2c909128 | 17 | from .constants import * |
9137135a MT |
18 | |
19 | class Packages(base.Object): | |
f6e6ff79 | 20 | def get_all_names(self, public=None, user=None, states=None): |
3a0302c1 | 21 | query = "SELECT DISTINCT packages.name AS name, summary FROM packages \ |
f6e6ff79 MT |
22 | JOIN builds ON builds.pkg_id = packages.id \ |
23 | WHERE packages.type = 'source'" | |
24 | ||
25 | conditions = [] | |
26 | args = [] | |
27 | ||
28 | if public in (True, False): | |
29 | if public is True: | |
30 | public = "Y" | |
31 | elif public is False: | |
32 | public = "N" | |
33 | ||
34 | conditions.append("builds.public = %s") | |
35 | args.append(public) | |
36 | ||
37 | if user and not user.is_admin(): | |
38 | conditions.append("builds.owner_id = %s") | |
39 | args.append(user.id) | |
40 | ||
41 | if states: | |
42 | for state in states: | |
43 | conditions.append("builds.state = %s") | |
44 | args.append(state) | |
45 | ||
46 | if conditions: | |
47 | query += " AND (%s)" % " OR ".join(conditions) | |
48 | ||
49 | query += " ORDER BY packages.name" | |
50 | ||
51 | return [(n.name, n.summary) for n in self.db.query(query, *args)] | |
52 | ||
53 | def get_by_uuid(self, uuid): | |
1d2a49fe | 54 | pkg = self.db.get("SELECT * FROM packages WHERE uuid = %s LIMIT 1", uuid) |
f6e6ff79 MT |
55 | if not pkg: |
56 | return | |
57 | ||
1d2a49fe | 58 | return Package(self.pakfire, pkg.id, pkg) |
f6e6ff79 MT |
59 | |
60 | def search(self, pattern, limit=None): | |
61 | """ | |
62 | Searches for packages that do match the query. | |
63 | ||
64 | This function does not work for UUIDs or filenames. | |
65 | """ | |
ce7abd33 MT |
66 | query = "SELECT * FROM packages \ |
67 | WHERE type = %s AND ( \ | |
68 | name LIKE %s OR \ | |
69 | summary LIKE %s OR \ | |
70 | description LIKE %s \ | |
71 | ) \ | |
f6e6ff79 MT |
72 | GROUP BY name" |
73 | ||
ce7abd33 MT |
74 | pattern = "%%%s%%" % pattern |
75 | args = ("source", pattern, pattern, pattern) | |
76 | ||
77 | res = self.db.query(query, *args) | |
78 | ||
f6e6ff79 | 79 | pkgs = [] |
ce7abd33 MT |
80 | for row in res: |
81 | pkg = Package(self.pakfire, row.id, row) | |
f6e6ff79 MT |
82 | pkgs.append(pkg) |
83 | ||
84 | if limit and len(pkgs) >= limit: | |
85 | break | |
9137135a | 86 | |
f6e6ff79 | 87 | return pkgs |
9137135a | 88 | |
f6e6ff79 MT |
89 | def search_by_filename(self, filename, limit=None): |
90 | query = "SELECT filelists.* FROM filelists \ | |
91 | JOIN packages ON filelists.pkg_id = packages.id \ | |
92 | WHERE filelists.name = %s ORDER BY packages.build_time DESC" | |
93 | args = [filename,] | |
9137135a | 94 | |
f6e6ff79 MT |
95 | if limit: |
96 | query += " LIMIT %s" | |
97 | args.append(limit) | |
9137135a | 98 | |
f6e6ff79 MT |
99 | files = [] |
100 | for result in self.db.query(query, *args): | |
101 | pkg = Package(self.pakfire, result.pkg_id) | |
102 | files.append((pkg, result)) | |
9137135a | 103 | |
f6e6ff79 | 104 | return files |
9137135a | 105 | |
35c46db4 MT |
106 | def autocomplete(self, query, limit=8): |
107 | res = self.db.query("SELECT DISTINCT name FROM packages \ | |
108 | WHERE packages.name LIKE %s AND packages.type = %s \ | |
109 | ORDER BY packages.name LIMIT %s", "%%%s%%" % query, "source", limit) | |
110 | ||
111 | return [row.name for row in res] | |
112 | ||
9137135a | 113 | |
f6e6ff79 | 114 | class Package(base.Object): |
1d2a49fe | 115 | def __init__(self, pakfire, id, data=None): |
f6e6ff79 | 116 | base.Object.__init__(self, pakfire) |
9137135a | 117 | |
f6e6ff79 MT |
118 | # The ID of the package. |
119 | self.id = id | |
9137135a | 120 | |
f6e6ff79 | 121 | # Cache. |
1d2a49fe | 122 | self._data = data |
f6e6ff79 MT |
123 | self._deps = None |
124 | self._arch = None | |
125 | self._filelist = None | |
126 | self._job = None | |
127 | self._commit = None | |
128 | self._properties = None | |
129 | self._maintainer = None | |
9137135a | 130 | |
f6e6ff79 MT |
131 | def __repr__(self): |
132 | return "<%s %s>" % (self.__class__.__name__, self.friendly_name) | |
9137135a | 133 | |
f6e6ff79 MT |
134 | def __cmp__(self, other): |
135 | return pakfire.util.version_compare(self.pakfire, | |
136 | self.friendly_name, other.friendly_name) | |
9137135a | 137 | |
f6e6ff79 | 138 | @classmethod |
6c439abc | 139 | def open(cls, _pakfire, path): |
f6e6ff79 MT |
140 | # Just check if the file really does exist. |
141 | assert os.path.exists(path) | |
142 | ||
6c439abc MT |
143 | p = pakfire.PakfireServer() |
144 | file = packages.open(p, None, path) | |
f6e6ff79 MT |
145 | |
146 | # Get architecture from the database. | |
6c439abc | 147 | arch = _pakfire.arches.get_by_name(file.arch) |
f6e6ff79 MT |
148 | assert arch, "Unknown architecture: %s" % file.arch |
149 | ||
150 | hash_sha512 = misc.calc_hash(path, "sha512") | |
151 | assert hash_sha512 | |
152 | ||
153 | query = [ | |
154 | ("name", file.name), | |
155 | ("epoch", file.epoch), | |
156 | ("version", file.version), | |
157 | ("release", file.release), | |
158 | ("type", file.type), | |
159 | ("arch", arch.id), | |
160 | ||
161 | ("groups", " ".join(file.groups)), | |
162 | ("maintainer", file.maintainer), | |
163 | ("license", file.license), | |
164 | ("url", file.url), | |
165 | ("summary", file.summary), | |
166 | ("description", file.description), | |
7a59e9ba | 167 | ("size", file.inst_size), |
f6e6ff79 MT |
168 | ("uuid", file.uuid), |
169 | ||
170 | # Build information. | |
171 | ("build_id", file.build_id), | |
172 | ("build_host", file.build_host), | |
173 | ("build_time", datetime.datetime.utcfromtimestamp(file.build_time)), | |
174 | ||
175 | # File "metadata". | |
176 | ("path", path), | |
177 | ("filesize", os.path.getsize(path)), | |
178 | ("hash_sha512", hash_sha512), | |
179 | ] | |
180 | ||
181 | if file.type == "source": | |
182 | query.append(("supported_arches", file.supported_arches)) | |
183 | ||
184 | keys = [] | |
185 | vals = [] | |
186 | for key, val in query: | |
187 | keys.append("`%s`" % key) | |
188 | vals.append(val) | |
189 | ||
190 | _query = "INSERT INTO packages(%s)" % ", ".join(keys) | |
191 | _query += " VALUES(%s)" % ", ".join("%s" for v in vals) | |
192 | ||
193 | # Create package entry in the database. | |
6c439abc | 194 | id = _pakfire.db.execute(_query, *vals) |
f6e6ff79 MT |
195 | |
196 | # Dependency information. | |
197 | deps = [] | |
198 | for d in file.prerequires: | |
199 | deps.append((id, "prerequires", d)) | |
200 | ||
201 | for d in file.requires: | |
202 | deps.append((id, "requires", d)) | |
203 | ||
204 | for d in file.provides: | |
205 | deps.append((id, "provides", d)) | |
206 | ||
207 | for d in file.conflicts: | |
208 | deps.append((id, "conflicts", d)) | |
209 | ||
210 | for d in file.obsoletes: | |
211 | deps.append((id, "obsoletes", d)) | |
212 | ||
213 | if deps: | |
6c439abc | 214 | _pakfire.db.executemany("INSERT INTO packages_deps(pkg_id, type, what) \ |
f6e6ff79 MT |
215 | VALUES(%s, %s, %s)", deps) |
216 | ||
217 | # Add all files to filelists table. | |
218 | filelist = [] | |
219 | for f in file.filelist: | |
220 | if f.config: | |
221 | config = "Y" | |
222 | else: | |
223 | config = "N" | |
9137135a | 224 | |
f6e6ff79 MT |
225 | # Convert mtime to integer. |
226 | try: | |
227 | mtime = int(f.mtime) | |
228 | except ValueError: | |
229 | mtime = 0 | |
9137135a | 230 | |
f6e6ff79 MT |
231 | filelist.append((id, f.name, f.size, f.hash1, f.type, config, f.mode, |
232 | f.user, f.group, datetime.datetime.utcfromtimestamp(mtime), | |
233 | f.capabilities)) | |
9137135a | 234 | |
6c439abc | 235 | _pakfire.db.executemany("INSERT INTO filelists(pkg_id, name, size, hash_sha512, \ |
f6e6ff79 MT |
236 | type, config, mode, user, `group`, mtime, capabilities) \ |
237 | VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", filelist) | |
9137135a | 238 | |
f6e6ff79 | 239 | # Return the newly created object. |
6c439abc | 240 | return cls(_pakfire, id) |
9137135a | 241 | |
f6e6ff79 MT |
242 | def delete(self): |
243 | self.db.execute("INSERT INTO queue_delete(path) VALUES(%s)", self.path) | |
9137135a | 244 | |
f6e6ff79 MT |
245 | # Delete all files from the filelist. |
246 | self.db.execute("DELETE FROM filelists WHERE pkg_id = %s", self.id) | |
9137135a | 247 | |
f6e6ff79 MT |
248 | # Delete the package. |
249 | self.db.execute("DELETE FROM packages WHERE id = %s", self.id) | |
9137135a | 250 | |
f6e6ff79 MT |
251 | # Remove cached data. |
252 | self._data = {} | |
9137135a | 253 | |
f6e6ff79 MT |
254 | @property |
255 | def data(self): | |
256 | if self._data is None: | |
257 | self._data = \ | |
258 | self.db.get("SELECT * FROM packages WHERE id = %s", self.id) | |
259 | assert self._data, "Cannot fetch package %s: %s" % (self.id, self._data) | |
9137135a | 260 | |
f6e6ff79 | 261 | return self._data |
9137135a | 262 | |
f6e6ff79 MT |
263 | @property |
264 | def uuid(self): | |
265 | return self.data.uuid | |
9137135a MT |
266 | |
267 | @property | |
268 | def name(self): | |
f6e6ff79 | 269 | return self.data.name |
9137135a MT |
270 | |
271 | @property | |
272 | def epoch(self): | |
f6e6ff79 | 273 | return self.data.epoch |
9137135a MT |
274 | |
275 | @property | |
276 | def version(self): | |
f6e6ff79 | 277 | return self.data.version |
9137135a MT |
278 | |
279 | @property | |
280 | def release(self): | |
f6e6ff79 MT |
281 | return self.data.release |
282 | ||
283 | @property | |
284 | def arch(self): | |
285 | if self._arch is None: | |
286 | self._arch = self.pakfire.arches.get_by_id(self.data.arch) | |
287 | assert self._arch | |
288 | ||
289 | return self._arch | |
290 | ||
291 | @property | |
292 | def type(self): | |
293 | return self.data.type | |
9137135a MT |
294 | |
295 | @property | |
296 | def friendly_name(self): | |
f6e6ff79 | 297 | return "%s-%s.%s" % (self.name, self.friendly_version, self.arch.name) |
9137135a MT |
298 | |
299 | @property | |
300 | def friendly_version(self): | |
301 | s = "%s-%s" % (self.version, self.release) | |
302 | ||
303 | if self.epoch: | |
304 | s = "%d:%s" % (self.epoch, s) | |
305 | ||
306 | return s | |
307 | ||
308 | @property | |
f6e6ff79 MT |
309 | def groups(self): |
310 | return self.data.groups.split() | |
9137135a | 311 | |
f6e6ff79 MT |
312 | @property |
313 | def maintainer(self): | |
314 | if self._maintainer is None: | |
315 | self._maintainer = self.data.maintainer | |
9137135a | 316 | |
f6e6ff79 MT |
317 | # Search if there is a user account for this person. |
318 | user = self.pakfire.users.find_maintainer(self._maintainer) | |
319 | if user: | |
320 | self._maintainer = user | |
9137135a | 321 | |
f6e6ff79 | 322 | return self._maintainer |
9137135a MT |
323 | |
324 | @property | |
f6e6ff79 MT |
325 | def license(self): |
326 | return self.data.license | |
9137135a MT |
327 | |
328 | @property | |
f6e6ff79 MT |
329 | def url(self): |
330 | return self.data.url | |
9137135a MT |
331 | |
332 | @property | |
f6e6ff79 MT |
333 | def summary(self): |
334 | return self.data.summary | |
9137135a MT |
335 | |
336 | @property | |
f6e6ff79 MT |
337 | def description(self): |
338 | return self.data.description | |
9137135a MT |
339 | |
340 | @property | |
f6e6ff79 MT |
341 | def supported_arches(self): |
342 | return self.data.supported_arches | |
9137135a MT |
343 | |
344 | @property | |
f6e6ff79 MT |
345 | def size(self): |
346 | return self.data.size | |
9137135a | 347 | |
5669a87f MT |
348 | def has_deps(self): |
349 | """ | |
350 | Returns True if the package has got dependencies. | |
351 | ||
352 | Always filter out the uuid provides. | |
353 | """ | |
354 | return len(self.deps) > 1 | |
355 | ||
9137135a | 356 | @property |
f6e6ff79 MT |
357 | def deps(self): |
358 | if self._deps is None: | |
359 | query = self.db.query("SELECT type, what FROM packages_deps WHERE pkg_id = %s", self.id) | |
9137135a | 360 | |
f6e6ff79 MT |
361 | self._deps = [] |
362 | for row in query: | |
363 | self._deps.append((row.type, row.what)) | |
9137135a | 364 | |
f6e6ff79 | 365 | return self._deps |
9137135a MT |
366 | |
367 | @property | |
f6e6ff79 MT |
368 | def prerequires(self): |
369 | return [d[1] for d in self.deps if d[0] == "prerequires"] | |
9137135a MT |
370 | |
371 | @property | |
f6e6ff79 MT |
372 | def requires(self): |
373 | return [d[1] for d in self.deps if d[0] == "requires"] | |
9137135a MT |
374 | |
375 | @property | |
f6e6ff79 | 376 | def provides(self): |
5669a87f | 377 | return [d[1] for d in self.deps if d[0] == "provides" and not d[1].startswith("uuid(")] |
9137135a MT |
378 | |
379 | @property | |
f6e6ff79 MT |
380 | def conflicts(self): |
381 | return [d[1] for d in self.deps if d[0] == "conflicts"] | |
9137135a | 382 | |
f6e6ff79 MT |
383 | @property |
384 | def obsoletes(self): | |
385 | return [d[1] for d in self.deps if d[0] == "obsoletes"] | |
9137135a | 386 | |
5669a87f MT |
387 | @property |
388 | def suggests(self): | |
389 | return [d[1] for d in self.deps if d[0] == "suggests"] | |
390 | ||
391 | @property | |
392 | def recommends(self): | |
393 | return [d[1] for d in self.deps if d[0] == "recommends"] | |
394 | ||
f6e6ff79 MT |
395 | @property |
396 | def commit_id(self): | |
397 | return self.data.commit_id | |
9137135a | 398 | |
f6e6ff79 MT |
399 | def get_commit(self): |
400 | if not self.commit_id: | |
401 | return | |
9137135a | 402 | |
f6e6ff79 MT |
403 | if self._commit is None: |
404 | self._commit = sources.Commit(self.pakfire, self.commit_id) | |
9137135a | 405 | |
f6e6ff79 | 406 | return self._commit |
9137135a | 407 | |
f6e6ff79 MT |
408 | def set_commit(self, commit): |
409 | self.db.execute("UPDATE packages SET commit_id = %s WHERE id = %s", | |
410 | commit.id, self.id) | |
411 | self._commit = commit | |
9137135a | 412 | |
f6e6ff79 | 413 | commit = property(get_commit, set_commit) |
9137135a | 414 | |
f6e6ff79 MT |
415 | @property |
416 | def distro(self): | |
417 | if not self.commit: | |
418 | return | |
9137135a | 419 | |
f6e6ff79 | 420 | # XXX THIS CANNOT RETURN None |
9137135a | 421 | |
f6e6ff79 | 422 | return self.commit.distro |
9137135a | 423 | |
f6e6ff79 MT |
424 | @property |
425 | def build_id(self): | |
426 | return self.data.build_id | |
9137135a MT |
427 | |
428 | @property | |
f6e6ff79 MT |
429 | def build_host(self): |
430 | return self.data.build_host | |
9137135a | 431 | |
f6e6ff79 MT |
432 | @property |
433 | def build_time(self): | |
434 | return self.data.build_time | |
9137135a | 435 | |
f6e6ff79 MT |
436 | @property |
437 | def path(self): | |
438 | return self.data.path | |
9137135a | 439 | |
f6e6ff79 MT |
440 | @property |
441 | def hash_sha512(self): | |
442 | return self.data.hash_sha512 | |
9137135a | 443 | |
f6e6ff79 MT |
444 | @property |
445 | def filesize(self): | |
446 | return self.data.filesize | |
9137135a | 447 | |
f6e6ff79 MT |
448 | def move(self, target_dir): |
449 | # Create directory if it does not exist, yet. | |
450 | if not os.path.exists(target_dir): | |
451 | os.makedirs(target_dir) | |
9137135a | 452 | |
f6e6ff79 MT |
453 | # Make full path where to put the file. |
454 | target = os.path.join(target_dir, os.path.basename(self.path)) | |
9137135a | 455 | |
f6e6ff79 MT |
456 | # Copy the file to the target directory (keeping metadata). |
457 | shutil.move(self.path, target) | |
9137135a | 458 | |
f6e6ff79 MT |
459 | # Update file path in the database. |
460 | self.db.execute("UPDATE packages SET path = %s WHERE id = %s", | |
461 | os.path.relpath(target, PACKAGES_DIR), self.id) | |
462 | self._data["path"] = target | |
9137135a | 463 | |
f6e6ff79 MT |
464 | @property |
465 | def build(self): | |
80ee2515 MT |
466 | if self.job: |
467 | return self.job.build | |
468 | ||
f6e6ff79 MT |
469 | build = self.db.get("SELECT id FROM builds \ |
470 | WHERE type = 'release' AND pkg_id = %s", self.id) | |
9137135a | 471 | |
f6e6ff79 | 472 | if build: |
2c909128 | 473 | return self.pakfire.builds.get_by_id(build.id) |
9137135a | 474 | |
f6e6ff79 MT |
475 | @property |
476 | def job(self): | |
477 | if self._job is None: | |
478 | job = self.db.get("SELECT job_id AS id FROM jobs_packages \ | |
479 | WHERE pkg_id = %s", self.id) | |
9137135a | 480 | |
f6e6ff79 | 481 | if job: |
2c909128 | 482 | self._job = self.pakfire.jobs.get_by_id(job.id) |
9137135a | 483 | |
f6e6ff79 | 484 | return self._job |
9137135a MT |
485 | |
486 | @property | |
f6e6ff79 MT |
487 | def filelist(self): |
488 | if self._filelist is None: | |
01197c1d MT |
489 | self._filelist = [] |
490 | ||
491 | for f in self.db.query("SELECT * FROM filelists WHERE pkg_id = %s ORDER BY name", self.id): | |
492 | f = File(self.pakfire, f) | |
493 | self._filelist.append(f) | |
9137135a | 494 | |
f6e6ff79 | 495 | return self._filelist |
9137135a | 496 | |
f6e6ff79 | 497 | def get_file(self): |
01197c1d MT |
498 | path = os.path.join(PACKAGES_DIR, self.path) |
499 | ||
500 | if os.path.exists(path): | |
501 | return pakfire.packages.open(None, None, path) | |
9137135a | 502 | |
f6e6ff79 MT |
503 | ## properties |
504 | ||
505 | _default_properties = { | |
506 | "critical_path" : False, | |
507 | "priority" : 0, | |
508 | } | |
9137135a | 509 | |
f6e6ff79 MT |
510 | def update_property(self, key, value): |
511 | assert self._default_properties.has_key(key), "Unknown key: %s" % key | |
9137135a | 512 | |
f6e6ff79 | 513 | #print self.db.execute("UPDATE packages_properties SET |
9137135a MT |
514 | |
515 | @property | |
f6e6ff79 MT |
516 | def properties(self): |
517 | if self._properties is None: | |
518 | self._properties = \ | |
519 | self.db.get("SELECT * FROM packages_properties WHERE name = %s", self.name) | |
9137135a | 520 | |
f6e6ff79 MT |
521 | if not self._properties: |
522 | self._properties = database.Row(self._default_properties) | |
9137135a | 523 | |
f6e6ff79 | 524 | return self._properties |
9137135a MT |
525 | |
526 | @property | |
f6e6ff79 MT |
527 | def critical_path(self): |
528 | return self.properties.get("critical_path", "N") == "Y" | |
9137135a | 529 | |
9137135a | 530 | |
01197c1d MT |
531 | class File(base.Object): |
532 | def __init__(self, pakfire, data): | |
533 | base.Object.__init__(self, pakfire) | |
534 | ||
535 | self.data = data | |
536 | ||
537 | def __getattr__(self, attr): | |
538 | try: | |
539 | return self.data[attr] | |
540 | except KeyError: | |
541 | raise AttributeError, attr | |
542 | ||
543 | @property | |
544 | def downloadable(self): | |
545 | # All regular files are downloadable. | |
546 | return self.type == 0 | |
547 | ||
548 | @property | |
549 | def viewable(self): | |
5669a87f MT |
550 | # Empty files cannot be viewed. |
551 | if self.size == 0: | |
552 | return False | |
553 | ||
01197c1d MT |
554 | for ext in FILE_EXTENSIONS_VIEWABLE: |
555 | if self.name.endswith(ext): | |
556 | return True | |
557 | ||
558 | return False |