]> git.ipfire.org Git - people/jschlag/pbs.git/blob - src/buildservice/packages.py
Use relative imports everywhere
[people/jschlag/pbs.git] / src / buildservice / packages.py
1 #!/usr/bin/python
2
3 import datetime
4 import logging
5 import os
6 import shutil
7
8 import pakfire
9 import pakfire.packages as packages
10
11 from . import arches
12 from . import base
13 from . import database
14 from . import misc
15 from . import sources
16
17 from .constants import *
18
19 class Packages(base.Object):
20 def get_all_names(self, public=None, user=None, states=None):
21 query = "SELECT DISTINCT name, summary FROM packages \
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):
54 pkg = self.db.get("SELECT * FROM packages WHERE uuid = %s LIMIT 1", uuid)
55 if not pkg:
56 return
57
58 return Package(self.pakfire, pkg.id, pkg)
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 """
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 ) \
72 GROUP BY name"
73
74 pattern = "%%%s%%" % pattern
75 args = ("source", pattern, pattern, pattern)
76
77 res = self.db.query(query, *args)
78
79 pkgs = []
80 for row in res:
81 pkg = Package(self.pakfire, row.id, row)
82 pkgs.append(pkg)
83
84 if limit and len(pkgs) >= limit:
85 break
86
87 return pkgs
88
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,]
94
95 if limit:
96 query += " LIMIT %s"
97 args.append(limit)
98
99 files = []
100 for result in self.db.query(query, *args):
101 pkg = Package(self.pakfire, result.pkg_id)
102 files.append((pkg, result))
103
104 return files
105
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
113
114 class Package(base.Object):
115 def __init__(self, pakfire, id, data=None):
116 base.Object.__init__(self, pakfire)
117
118 # The ID of the package.
119 self.id = id
120
121 # Cache.
122 self._data = data
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
130
131 def __repr__(self):
132 return "<%s %s>" % (self.__class__.__name__, self.friendly_name)
133
134 def __cmp__(self, other):
135 return pakfire.util.version_compare(self.pakfire,
136 self.friendly_name, other.friendly_name)
137
138 @classmethod
139 def open(cls, _pakfire, path):
140 # Just check if the file really does exist.
141 assert os.path.exists(path)
142
143 p = pakfire.PakfireServer()
144 file = packages.open(p, None, path)
145
146 # Get architecture from the database.
147 arch = _pakfire.arches.get_by_name(file.arch)
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),
167 ("size", file.inst_size),
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.
194 id = _pakfire.db.execute(_query, *vals)
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:
214 _pakfire.db.executemany("INSERT INTO packages_deps(pkg_id, type, what) \
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"
224
225 # Convert mtime to integer.
226 try:
227 mtime = int(f.mtime)
228 except ValueError:
229 mtime = 0
230
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))
234
235 _pakfire.db.executemany("INSERT INTO filelists(pkg_id, name, size, hash_sha512, \
236 type, config, mode, user, `group`, mtime, capabilities) \
237 VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", filelist)
238
239 # Return the newly created object.
240 return cls(_pakfire, id)
241
242 def delete(self):
243 self.db.execute("INSERT INTO queue_delete(path) VALUES(%s)", self.path)
244
245 # Delete all files from the filelist.
246 self.db.execute("DELETE FROM filelists WHERE pkg_id = %s", self.id)
247
248 # Delete the package.
249 self.db.execute("DELETE FROM packages WHERE id = %s", self.id)
250
251 # Remove cached data.
252 self._data = {}
253
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)
260
261 return self._data
262
263 @property
264 def uuid(self):
265 return self.data.uuid
266
267 @property
268 def name(self):
269 return self.data.name
270
271 @property
272 def epoch(self):
273 return self.data.epoch
274
275 @property
276 def version(self):
277 return self.data.version
278
279 @property
280 def release(self):
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
294
295 @property
296 def friendly_name(self):
297 return "%s-%s.%s" % (self.name, self.friendly_version, self.arch.name)
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
309 def groups(self):
310 return self.data.groups.split()
311
312 @property
313 def maintainer(self):
314 if self._maintainer is None:
315 self._maintainer = self.data.maintainer
316
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
321
322 return self._maintainer
323
324 @property
325 def license(self):
326 return self.data.license
327
328 @property
329 def url(self):
330 return self.data.url
331
332 @property
333 def summary(self):
334 return self.data.summary
335
336 @property
337 def description(self):
338 return self.data.description
339
340 @property
341 def supported_arches(self):
342 return self.data.supported_arches
343
344 @property
345 def size(self):
346 return self.data.size
347
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
356 @property
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)
360
361 self._deps = []
362 for row in query:
363 self._deps.append((row.type, row.what))
364
365 return self._deps
366
367 @property
368 def prerequires(self):
369 return [d[1] for d in self.deps if d[0] == "prerequires"]
370
371 @property
372 def requires(self):
373 return [d[1] for d in self.deps if d[0] == "requires"]
374
375 @property
376 def provides(self):
377 return [d[1] for d in self.deps if d[0] == "provides" and not d[1].startswith("uuid(")]
378
379 @property
380 def conflicts(self):
381 return [d[1] for d in self.deps if d[0] == "conflicts"]
382
383 @property
384 def obsoletes(self):
385 return [d[1] for d in self.deps if d[0] == "obsoletes"]
386
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
395 @property
396 def commit_id(self):
397 return self.data.commit_id
398
399 def get_commit(self):
400 if not self.commit_id:
401 return
402
403 if self._commit is None:
404 self._commit = sources.Commit(self.pakfire, self.commit_id)
405
406 return self._commit
407
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
412
413 commit = property(get_commit, set_commit)
414
415 @property
416 def distro(self):
417 if not self.commit:
418 return
419
420 # XXX THIS CANNOT RETURN None
421
422 return self.commit.distro
423
424 @property
425 def build_id(self):
426 return self.data.build_id
427
428 @property
429 def build_host(self):
430 return self.data.build_host
431
432 @property
433 def build_time(self):
434 return self.data.build_time
435
436 @property
437 def path(self):
438 return self.data.path
439
440 @property
441 def hash_sha512(self):
442 return self.data.hash_sha512
443
444 @property
445 def filesize(self):
446 return self.data.filesize
447
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)
452
453 # Make full path where to put the file.
454 target = os.path.join(target_dir, os.path.basename(self.path))
455
456 # Copy the file to the target directory (keeping metadata).
457 shutil.move(self.path, target)
458
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
463
464 @property
465 def build(self):
466 if self.job:
467 return self.job.build
468
469 build = self.db.get("SELECT id FROM builds \
470 WHERE type = 'release' AND pkg_id = %s", self.id)
471
472 if build:
473 return self.pakfire.builds.get_by_id(build.id)
474
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)
480
481 if job:
482 self._job = self.pakfire.jobs.get_by_id(job.id)
483
484 return self._job
485
486 @property
487 def filelist(self):
488 if self._filelist is None:
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)
494
495 return self._filelist
496
497 def get_file(self):
498 path = os.path.join(PACKAGES_DIR, self.path)
499
500 if os.path.exists(path):
501 return pakfire.packages.open(None, None, path)
502
503 ## properties
504
505 _default_properties = {
506 "critical_path" : False,
507 "priority" : 0,
508 }
509
510 def update_property(self, key, value):
511 assert self._default_properties.has_key(key), "Unknown key: %s" % key
512
513 #print self.db.execute("UPDATE packages_properties SET
514
515 @property
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)
520
521 if not self._properties:
522 self._properties = database.Row(self._default_properties)
523
524 return self._properties
525
526 @property
527 def critical_path(self):
528 return self.properties.get("critical_path", "N") == "Y"
529
530
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):
550 # Empty files cannot be viewed.
551 if self.size == 0:
552 return False
553
554 for ext in FILE_EXTENSIONS_VIEWABLE:
555 if self.name.endswith(ext):
556 return True
557
558 return False