]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/buildservice/packages.py
jobs: Remove deps to type field
[people/jschlag/pbs.git] / src / buildservice / packages.py
CommitLineData
9137135a
MT
1#!/usr/bin/python
2
f6e6ff79
MT
3import datetime
4import logging
9137135a
MT
5import os
6import shutil
9137135a 7
f6e6ff79
MT
8import pakfire
9import pakfire.packages as packages
10
2c909128
MT
11from . import arches
12from . import base
13from . import database
14from . import misc
15from . import sources
9137135a 16
2c909128 17from .constants import *
9137135a
MT
18
19class 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 206class 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
526class 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