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