5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
42 from inspect import getargspec
45 from sqlalchemy import create_engine, Table, MetaData
46 from sqlalchemy.orm import sessionmaker, mapper, relation
47 from sqlalchemy import types as sqltypes
49 # Don't remove this, we re-export the exceptions to scripts which import us
50 from sqlalchemy.exc import *
51 from sqlalchemy.orm.exc import NoResultFound
53 # Only import Config until Queue stuff is changed to store its config
55 from config import Config
56 from singleton import Singleton
57 from textutils import fix_maintainer
59 ################################################################################
61 # Patch in support for the debversion field type so that it works during
64 class DebVersion(sqltypes.Text):
65 def get_col_spec(self):
68 sa_major_version = sqlalchemy.__version__[0:3]
69 if sa_major_version == "0.5":
70 from sqlalchemy.databases import postgres
71 postgres.ischema_names['debversion'] = DebVersion
73 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
75 ################################################################################
77 __all__ = ['IntegrityError', 'SQLAlchemyError']
79 ################################################################################
81 def session_wrapper(fn):
83 Wrapper around common ".., session=None):" handling. If the wrapped
84 function is called without passing 'session', we create a local one
85 and destroy it when the function ends.
87 Also attaches a commit_or_flush method to the session; if we created a
88 local session, this is a synonym for session.commit(), otherwise it is a
89 synonym for session.flush().
92 def wrapped(*args, **kwargs):
93 private_transaction = False
95 # Find the session object
96 session = kwargs.get('session')
99 if len(args) <= len(getargspec(fn)[0]) - 1:
100 # No session specified as last argument or in kwargs
101 private_transaction = True
102 session = kwargs['session'] = DBConn().session()
104 # Session is last argument in args
108 session = args[-1] = DBConn().session()
109 private_transaction = True
111 if private_transaction:
112 session.commit_or_flush = session.commit
114 session.commit_or_flush = session.flush
117 return fn(*args, **kwargs)
119 if private_transaction:
120 # We created a session; close it.
123 wrapped.__doc__ = fn.__doc__
124 wrapped.func_name = fn.func_name
128 ################################################################################
130 class Architecture(object):
131 def __init__(self, *args, **kwargs):
134 def __eq__(self, val):
135 if isinstance(val, str):
136 return (self.arch_string== val)
137 # This signals to use the normal comparison operator
138 return NotImplemented
140 def __ne__(self, val):
141 if isinstance(val, str):
142 return (self.arch_string != val)
143 # This signals to use the normal comparison operator
144 return NotImplemented
147 return '<Architecture %s>' % self.arch_string
149 __all__.append('Architecture')
152 def get_architecture(architecture, session=None):
154 Returns database id for given C{architecture}.
156 @type architecture: string
157 @param architecture: The name of the architecture
159 @type session: Session
160 @param session: Optional SQLA session object (a temporary one will be
161 generated if not supplied)
164 @return: Architecture object for the given arch (None if not present)
167 q = session.query(Architecture).filter_by(arch_string=architecture)
171 except NoResultFound:
174 __all__.append('get_architecture')
177 def get_architecture_suites(architecture, session=None):
179 Returns list of Suite objects for given C{architecture} name
182 @param source: Architecture name to search for
184 @type session: Session
185 @param session: Optional SQL session object (a temporary one will be
186 generated if not supplied)
189 @return: list of Suite objects for the given name (may be empty)
192 q = session.query(Suite)
193 q = q.join(SuiteArchitecture)
194 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
200 __all__.append('get_architecture_suites')
202 ################################################################################
204 class Archive(object):
205 def __init__(self, *args, **kwargs):
209 return '<Archive %s>' % self.archive_name
211 __all__.append('Archive')
214 def get_archive(archive, session=None):
216 returns database id for given C{archive}.
218 @type archive: string
219 @param archive: the name of the arhive
221 @type session: Session
222 @param session: Optional SQLA session object (a temporary one will be
223 generated if not supplied)
226 @return: Archive object for the given name (None if not present)
229 archive = archive.lower()
231 q = session.query(Archive).filter_by(archive_name=archive)
235 except NoResultFound:
238 __all__.append('get_archive')
240 ################################################################################
242 class BinAssociation(object):
243 def __init__(self, *args, **kwargs):
247 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
249 __all__.append('BinAssociation')
251 ################################################################################
253 class BinContents(object):
254 def __init__(self, *args, **kwargs):
258 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
260 __all__.append('BinContents')
262 ################################################################################
264 class DBBinary(object):
265 def __init__(self, *args, **kwargs):
269 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
271 __all__.append('DBBinary')
274 def get_suites_binary_in(package, session=None):
276 Returns list of Suite objects which given C{package} name is in
279 @param source: DBBinary package name to search for
282 @return: list of Suite objects for the given package
285 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
287 __all__.append('get_suites_binary_in')
290 def get_binary_from_id(binary_id, session=None):
292 Returns DBBinary object for given C{id}
295 @param binary_id: Id of the required binary
297 @type session: Session
298 @param session: Optional SQLA session object (a temporary one will be
299 generated if not supplied)
302 @return: DBBinary object for the given binary (None if not present)
305 q = session.query(DBBinary).filter_by(binary_id=binary_id)
309 except NoResultFound:
312 __all__.append('get_binary_from_id')
315 def get_binaries_from_name(package, version=None, architecture=None, session=None):
317 Returns list of DBBinary objects for given C{package} name
320 @param package: DBBinary package name to search for
322 @type version: str or None
323 @param version: Version to search for (or None)
325 @type package: str, list or None
326 @param package: Architectures to limit to (or None if no limit)
328 @type session: Session
329 @param session: Optional SQL session object (a temporary one will be
330 generated if not supplied)
333 @return: list of DBBinary objects for the given name (may be empty)
336 q = session.query(DBBinary).filter_by(package=package)
338 if version is not None:
339 q = q.filter_by(version=version)
341 if architecture is not None:
342 if not isinstance(architecture, list):
343 architecture = [architecture]
344 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
350 __all__.append('get_binaries_from_name')
353 def get_binaries_from_source_id(source_id, session=None):
355 Returns list of DBBinary objects for given C{source_id}
358 @param source_id: source_id to search for
360 @type session: Session
361 @param session: Optional SQL session object (a temporary one will be
362 generated if not supplied)
365 @return: list of DBBinary objects for the given name (may be empty)
368 return session.query(DBBinary).filter_by(source_id=source_id).all()
370 __all__.append('get_binaries_from_source_id')
373 def get_binary_from_name_suite(package, suitename, session=None):
374 ### For dak examine-package
375 ### XXX: Doesn't use object API yet
377 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
378 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
379 WHERE b.package=:package
381 AND fi.location = l.id
382 AND l.component = c.id
385 AND su.suite_name=:suitename
386 ORDER BY b.version DESC"""
388 return session.execute(sql, {'package': package, 'suitename': suitename})
390 __all__.append('get_binary_from_name_suite')
393 def get_binary_components(package, suitename, arch, session=None):
394 # Check for packages that have moved from one component to another
395 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
396 WHERE b.package=:package AND s.suite_name=:suitename
397 AND (a.arch_string = :arch OR a.arch_string = 'all')
398 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
399 AND f.location = l.id
400 AND l.component = c.id
403 vals = {'package': package, 'suitename': suitename, 'arch': arch}
405 return session.execute(query, vals)
407 __all__.append('get_binary_components')
409 ################################################################################
411 class BinaryACL(object):
412 def __init__(self, *args, **kwargs):
416 return '<BinaryACL %s>' % self.binary_acl_id
418 __all__.append('BinaryACL')
420 ################################################################################
422 class BinaryACLMap(object):
423 def __init__(self, *args, **kwargs):
427 return '<BinaryACLMap %s>' % self.binary_acl_map_id
429 __all__.append('BinaryACLMap')
431 ################################################################################
433 class Component(object):
434 def __init__(self, *args, **kwargs):
437 def __eq__(self, val):
438 if isinstance(val, str):
439 return (self.component_name == val)
440 # This signals to use the normal comparison operator
441 return NotImplemented
443 def __ne__(self, val):
444 if isinstance(val, str):
445 return (self.component_name != val)
446 # This signals to use the normal comparison operator
447 return NotImplemented
450 return '<Component %s>' % self.component_name
453 __all__.append('Component')
456 def get_component(component, session=None):
458 Returns database id for given C{component}.
460 @type component: string
461 @param component: The name of the override type
464 @return: the database id for the given component
467 component = component.lower()
469 q = session.query(Component).filter_by(component_name=component)
473 except NoResultFound:
476 __all__.append('get_component')
478 ################################################################################
480 class DBConfig(object):
481 def __init__(self, *args, **kwargs):
485 return '<DBConfig %s>' % self.name
487 __all__.append('DBConfig')
489 ################################################################################
492 def get_or_set_contents_file_id(filename, session=None):
494 Returns database id for given filename.
496 If no matching file is found, a row is inserted.
498 @type filename: string
499 @param filename: The filename
500 @type session: SQLAlchemy
501 @param session: Optional SQL session object (a temporary one will be
502 generated if not supplied). If not passed, a commit will be performed at
503 the end of the function, otherwise the caller is responsible for commiting.
506 @return: the database id for the given component
509 q = session.query(ContentFilename).filter_by(filename=filename)
512 ret = q.one().cafilename_id
513 except NoResultFound:
514 cf = ContentFilename()
515 cf.filename = filename
517 session.commit_or_flush()
518 ret = cf.cafilename_id
522 __all__.append('get_or_set_contents_file_id')
525 def get_contents(suite, overridetype, section=None, session=None):
527 Returns contents for a suite / overridetype combination, limiting
528 to a section if not None.
531 @param suite: Suite object
533 @type overridetype: OverrideType
534 @param overridetype: OverrideType object
536 @type section: Section
537 @param section: Optional section object to limit results to
539 @type session: SQLAlchemy
540 @param session: Optional SQL session object (a temporary one will be
541 generated if not supplied)
544 @return: ResultsProxy object set up to return tuples of (filename, section,
548 # find me all of the contents for a given suite
549 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
553 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
554 JOIN content_file_names n ON (c.filename=n.id)
555 JOIN binaries b ON (b.id=c.binary_pkg)
556 JOIN override o ON (o.package=b.package)
557 JOIN section s ON (s.id=o.section)
558 WHERE o.suite = :suiteid AND o.type = :overridetypeid
559 AND b.type=:overridetypename"""
561 vals = {'suiteid': suite.suite_id,
562 'overridetypeid': overridetype.overridetype_id,
563 'overridetypename': overridetype.overridetype}
565 if section is not None:
566 contents_q += " AND s.id = :sectionid"
567 vals['sectionid'] = section.section_id
569 contents_q += " ORDER BY fn"
571 return session.execute(contents_q, vals)
573 __all__.append('get_contents')
575 ################################################################################
577 class ContentFilepath(object):
578 def __init__(self, *args, **kwargs):
582 return '<ContentFilepath %s>' % self.filepath
584 __all__.append('ContentFilepath')
587 def get_or_set_contents_path_id(filepath, session=None):
589 Returns database id for given path.
591 If no matching file is found, a row is inserted.
593 @type filename: string
594 @param filename: The filepath
595 @type session: SQLAlchemy
596 @param session: Optional SQL session object (a temporary one will be
597 generated if not supplied). If not passed, a commit will be performed at
598 the end of the function, otherwise the caller is responsible for commiting.
601 @return: the database id for the given path
604 q = session.query(ContentFilepath).filter_by(filepath=filepath)
607 ret = q.one().cafilepath_id
608 except NoResultFound:
609 cf = ContentFilepath()
610 cf.filepath = filepath
612 session.commit_or_flush()
613 ret = cf.cafilepath_id
617 __all__.append('get_or_set_contents_path_id')
619 ################################################################################
621 class ContentAssociation(object):
622 def __init__(self, *args, **kwargs):
626 return '<ContentAssociation %s>' % self.ca_id
628 __all__.append('ContentAssociation')
630 def insert_content_paths(binary_id, fullpaths, session=None):
632 Make sure given path is associated with given binary id
635 @param binary_id: the id of the binary
636 @type fullpaths: list
637 @param fullpaths: the list of paths of the file being associated with the binary
638 @type session: SQLAlchemy session
639 @param session: Optional SQLAlchemy session. If this is passed, the caller
640 is responsible for ensuring a transaction has begun and committing the
641 results or rolling back based on the result code. If not passed, a commit
642 will be performed at the end of the function, otherwise the caller is
643 responsible for commiting.
645 @return: True upon success
650 session = DBConn().session()
657 def generate_path_dicts():
658 for fullpath in fullpaths:
659 if fullpath.startswith( './' ):
660 fullpath = fullpath[2:]
662 yield {'fulename':fullpath, 'id': binary_id }
664 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
665 generate_path_dicts() )
673 traceback.print_exc()
675 # Only rollback if we set up the session ourself
682 __all__.append('insert_content_paths')
684 ################################################################################
686 class DSCFile(object):
687 def __init__(self, *args, **kwargs):
691 return '<DSCFile %s>' % self.dscfile_id
693 __all__.append('DSCFile')
696 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
698 Returns a list of DSCFiles which may be empty
700 @type dscfile_id: int (optional)
701 @param dscfile_id: the dscfile_id of the DSCFiles to find
703 @type source_id: int (optional)
704 @param source_id: the source id related to the DSCFiles to find
706 @type poolfile_id: int (optional)
707 @param poolfile_id: the poolfile id related to the DSCFiles to find
710 @return: Possibly empty list of DSCFiles
713 q = session.query(DSCFile)
715 if dscfile_id is not None:
716 q = q.filter_by(dscfile_id=dscfile_id)
718 if source_id is not None:
719 q = q.filter_by(source_id=source_id)
721 if poolfile_id is not None:
722 q = q.filter_by(poolfile_id=poolfile_id)
726 __all__.append('get_dscfiles')
728 ################################################################################
730 class PoolFile(object):
731 def __init__(self, *args, **kwargs):
735 return '<PoolFile %s>' % self.filename
739 return os.path.join(self.location.path, self.filename)
741 __all__.append('PoolFile')
744 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
747 (ValidFileFound [boolean or None], PoolFile object or None)
749 @type filename: string
750 @param filename: the filename of the file to check against the DB
753 @param filesize: the size of the file to check against the DB
756 @param md5sum: the md5sum of the file to check against the DB
758 @type location_id: int
759 @param location_id: the id of the location to look in
762 @return: Tuple of length 2.
763 If more than one file found with that name:
765 If valid pool file found: (True, PoolFile object)
766 If valid pool file not found:
767 (False, None) if no file found
768 (False, PoolFile object) if file found with size/md5sum mismatch
771 q = session.query(PoolFile).filter_by(filename=filename)
772 q = q.join(Location).filter_by(location_id=location_id)
782 if obj.md5sum != md5sum or obj.filesize != int(filesize):
790 __all__.append('check_poolfile')
793 def get_poolfile_by_id(file_id, session=None):
795 Returns a PoolFile objects or None for the given id
798 @param file_id: the id of the file to look for
800 @rtype: PoolFile or None
801 @return: either the PoolFile object or None
804 q = session.query(PoolFile).filter_by(file_id=file_id)
808 except NoResultFound:
811 __all__.append('get_poolfile_by_id')
815 def get_poolfile_by_name(filename, location_id=None, session=None):
817 Returns an array of PoolFile objects for the given filename and
818 (optionally) location_id
820 @type filename: string
821 @param filename: the filename of the file to check against the DB
823 @type location_id: int
824 @param location_id: the id of the location to look in (optional)
827 @return: array of PoolFile objects
830 q = session.query(PoolFile).filter_by(filename=filename)
832 if location_id is not None:
833 q = q.join(Location).filter_by(location_id=location_id)
837 __all__.append('get_poolfile_by_name')
840 def get_poolfile_like_name(filename, session=None):
842 Returns an array of PoolFile objects which are like the given name
844 @type filename: string
845 @param filename: the filename of the file to check against the DB
848 @return: array of PoolFile objects
851 # TODO: There must be a way of properly using bind parameters with %FOO%
852 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
856 __all__.append('get_poolfile_like_name')
858 ################################################################################
860 class Fingerprint(object):
861 def __init__(self, *args, **kwargs):
865 return '<Fingerprint %s>' % self.fingerprint
867 __all__.append('Fingerprint')
870 def get_fingerprint(fpr, session=None):
872 Returns Fingerprint object for given fpr.
875 @param fpr: The fpr to find / add
877 @type session: SQLAlchemy
878 @param session: Optional SQL session object (a temporary one will be
879 generated if not supplied).
882 @return: the Fingerprint object for the given fpr or None
885 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
889 except NoResultFound:
894 __all__.append('get_fingerprint')
897 def get_or_set_fingerprint(fpr, session=None):
899 Returns Fingerprint object for given fpr.
901 If no matching fpr is found, a row is inserted.
904 @param fpr: The fpr to find / add
906 @type session: SQLAlchemy
907 @param session: Optional SQL session object (a temporary one will be
908 generated if not supplied). If not passed, a commit will be performed at
909 the end of the function, otherwise the caller is responsible for commiting.
910 A flush will be performed either way.
913 @return: the Fingerprint object for the given fpr
916 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
920 except NoResultFound:
921 fingerprint = Fingerprint()
922 fingerprint.fingerprint = fpr
923 session.add(fingerprint)
924 session.commit_or_flush()
929 __all__.append('get_or_set_fingerprint')
931 ################################################################################
933 # Helper routine for Keyring class
934 def get_ldap_name(entry):
936 for k in ["cn", "mn", "sn"]:
938 if ret and ret[0] != "" and ret[0] != "-":
940 return " ".join(name)
942 ################################################################################
944 class Keyring(object):
945 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
946 " --with-colons --fingerprint --fingerprint"
951 def __init__(self, *args, **kwargs):
955 return '<Keyring %s>' % self.keyring_name
957 def de_escape_gpg_str(self, txt):
958 esclist = re.split(r'(\\x..)', txt)
959 for x in range(1,len(esclist),2):
960 esclist[x] = "%c" % (int(esclist[x][2:],16))
961 return "".join(esclist)
963 def load_keys(self, keyring):
966 if not self.keyring_id:
967 raise Exception('Must be initialized with database information')
969 k = os.popen(self.gpg_invocation % keyring, "r")
973 for line in k.xreadlines():
974 field = line.split(":")
975 if field[0] == "pub":
977 (name, addr) = email.Utils.parseaddr(field[9])
978 name = re.sub(r"\s*[(].*[)]", "", name)
979 if name == "" or addr == "" or "@" not in addr:
982 name = self.de_escape_gpg_str(name)
983 self.keys[key] = {"email": addr}
985 self.keys[key]["name"] = name
986 self.keys[key]["aliases"] = [name]
987 self.keys[key]["fingerprints"] = []
989 elif key and field[0] == "sub" and len(field) >= 12:
990 signingkey = ("s" in field[11])
991 elif key and field[0] == "uid":
992 (name, addr) = email.Utils.parseaddr(field[9])
993 if name and name not in self.keys[key]["aliases"]:
994 self.keys[key]["aliases"].append(name)
995 elif signingkey and field[0] == "fpr":
996 self.keys[key]["fingerprints"].append(field[9])
997 self.fpr_lookup[field[9]] = key
999 def import_users_from_ldap(self, session):
1003 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1004 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1006 l = ldap.open(LDAPServer)
1007 l.simple_bind_s("","")
1008 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1009 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1010 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1012 ldap_fin_uid_id = {}
1019 uid = entry["uid"][0]
1020 name = get_ldap_name(entry)
1021 fingerprints = entry["keyFingerPrint"]
1023 for f in fingerprints:
1024 key = self.fpr_lookup.get(f, None)
1025 if key not in self.keys:
1027 self.keys[key]["uid"] = uid
1031 keyid = get_or_set_uid(uid, session).uid_id
1032 byuid[keyid] = (uid, name)
1033 byname[uid] = (keyid, name)
1035 return (byname, byuid)
1037 def generate_users_from_keyring(self, format, session):
1041 for x in self.keys.keys():
1042 if self.keys[x]["email"] == "invalid-uid":
1044 self.keys[x]["uid"] = format % "invalid-uid"
1046 uid = format % self.keys[x]["email"]
1047 keyid = get_or_set_uid(uid, session).uid_id
1048 byuid[keyid] = (uid, self.keys[x]["name"])
1049 byname[uid] = (keyid, self.keys[x]["name"])
1050 self.keys[x]["uid"] = uid
1053 uid = format % "invalid-uid"
1054 keyid = get_or_set_uid(uid, session).uid_id
1055 byuid[keyid] = (uid, "ungeneratable user id")
1056 byname[uid] = (keyid, "ungeneratable user id")
1058 return (byname, byuid)
1060 __all__.append('Keyring')
1063 def get_keyring(keyring, session=None):
1065 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1066 If C{keyring} already has an entry, simply return the existing Keyring
1068 @type keyring: string
1069 @param keyring: the keyring name
1072 @return: the Keyring object for this keyring
1075 q = session.query(Keyring).filter_by(keyring_name=keyring)
1079 except NoResultFound:
1082 __all__.append('get_keyring')
1084 ################################################################################
1086 class KeyringACLMap(object):
1087 def __init__(self, *args, **kwargs):
1091 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1093 __all__.append('KeyringACLMap')
1095 ################################################################################
1097 class KnownChange(object):
1098 def __init__(self, *args, **kwargs):
1102 return '<KnownChange %s>' % self.changesname
1104 __all__.append('KnownChange')
1107 def get_knownchange(filename, session=None):
1109 returns knownchange object for given C{filename}.
1111 @type archive: string
1112 @param archive: the name of the arhive
1114 @type session: Session
1115 @param session: Optional SQLA session object (a temporary one will be
1116 generated if not supplied)
1119 @return: Archive object for the given name (None if not present)
1122 q = session.query(KnownChange).filter_by(changesname=filename)
1126 except NoResultFound:
1129 __all__.append('get_knownchange')
1131 ################################################################################
1133 class KnownChangePendingFile(object):
1134 def __init__(self, *args, **kwargs):
1138 return '<KnownChangePendingFile %s>' % self.known_change_pending_file_id
1140 __all__.append('KnownChangePendingFile')
1142 ################################################################################
1144 class Location(object):
1145 def __init__(self, *args, **kwargs):
1149 return '<Location %s (%s)>' % (self.path, self.location_id)
1151 __all__.append('Location')
1154 def get_location(location, component=None, archive=None, session=None):
1156 Returns Location object for the given combination of location, component
1159 @type location: string
1160 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1162 @type component: string
1163 @param component: the component name (if None, no restriction applied)
1165 @type archive: string
1166 @param archive_id: the archive name (if None, no restriction applied)
1168 @rtype: Location / None
1169 @return: Either a Location object or None if one can't be found
1172 q = session.query(Location).filter_by(path=location)
1174 if archive is not None:
1175 q = q.join(Archive).filter_by(archive_name=archive)
1177 if component is not None:
1178 q = q.join(Component).filter_by(component_name=component)
1182 except NoResultFound:
1185 __all__.append('get_location')
1187 ################################################################################
1189 class Maintainer(object):
1190 def __init__(self, *args, **kwargs):
1194 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1196 def get_split_maintainer(self):
1197 if not hasattr(self, 'name') or self.name is None:
1198 return ('', '', '', '')
1200 return fix_maintainer(self.name.strip())
1202 __all__.append('Maintainer')
1205 def get_or_set_maintainer(name, session=None):
1207 Returns Maintainer object for given maintainer name.
1209 If no matching maintainer name is found, a row is inserted.
1212 @param name: The maintainer name to add
1214 @type session: SQLAlchemy
1215 @param session: Optional SQL session object (a temporary one will be
1216 generated if not supplied). If not passed, a commit will be performed at
1217 the end of the function, otherwise the caller is responsible for commiting.
1218 A flush will be performed either way.
1221 @return: the Maintainer object for the given maintainer
1224 q = session.query(Maintainer).filter_by(name=name)
1227 except NoResultFound:
1228 maintainer = Maintainer()
1229 maintainer.name = name
1230 session.add(maintainer)
1231 session.commit_or_flush()
1236 __all__.append('get_or_set_maintainer')
1239 def get_maintainer(maintainer_id, session=None):
1241 Return the name of the maintainer behind C{maintainer_id} or None if that
1242 maintainer_id is invalid.
1244 @type maintainer_id: int
1245 @param maintainer_id: the id of the maintainer
1248 @return: the Maintainer with this C{maintainer_id}
1251 return session.query(Maintainer).get(maintainer_id)
1253 __all__.append('get_maintainer')
1255 ################################################################################
1257 class NewComment(object):
1258 def __init__(self, *args, **kwargs):
1262 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1264 __all__.append('NewComment')
1267 def has_new_comment(package, version, session=None):
1269 Returns true if the given combination of C{package}, C{version} has a comment.
1271 @type package: string
1272 @param package: name of the package
1274 @type version: string
1275 @param version: package version
1277 @type session: Session
1278 @param session: Optional SQLA session object (a temporary one will be
1279 generated if not supplied)
1285 q = session.query(NewComment)
1286 q = q.filter_by(package=package)
1287 q = q.filter_by(version=version)
1289 return bool(q.count() > 0)
1291 __all__.append('has_new_comment')
1294 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1296 Returns (possibly empty) list of NewComment objects for the given
1299 @type package: string (optional)
1300 @param package: name of the package
1302 @type version: string (optional)
1303 @param version: package version
1305 @type comment_id: int (optional)
1306 @param comment_id: An id of a comment
1308 @type session: Session
1309 @param session: Optional SQLA session object (a temporary one will be
1310 generated if not supplied)
1313 @return: A (possibly empty) list of NewComment objects will be returned
1316 q = session.query(NewComment)
1317 if package is not None: q = q.filter_by(package=package)
1318 if version is not None: q = q.filter_by(version=version)
1319 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1323 __all__.append('get_new_comments')
1325 ################################################################################
1327 class Override(object):
1328 def __init__(self, *args, **kwargs):
1332 return '<Override %s (%s)>' % (self.package, self.suite_id)
1334 __all__.append('Override')
1337 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1339 Returns Override object for the given parameters
1341 @type package: string
1342 @param package: The name of the package
1344 @type suite: string, list or None
1345 @param suite: The name of the suite (or suites if a list) to limit to. If
1346 None, don't limit. Defaults to None.
1348 @type component: string, list or None
1349 @param component: The name of the component (or components if a list) to
1350 limit to. If None, don't limit. Defaults to None.
1352 @type overridetype: string, list or None
1353 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1354 limit to. If None, don't limit. Defaults to None.
1356 @type session: Session
1357 @param session: Optional SQLA session object (a temporary one will be
1358 generated if not supplied)
1361 @return: A (possibly empty) list of Override objects will be returned
1364 q = session.query(Override)
1365 q = q.filter_by(package=package)
1367 if suite is not None:
1368 if not isinstance(suite, list): suite = [suite]
1369 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1371 if component is not None:
1372 if not isinstance(component, list): component = [component]
1373 q = q.join(Component).filter(Component.component_name.in_(component))
1375 if overridetype is not None:
1376 if not isinstance(overridetype, list): overridetype = [overridetype]
1377 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1381 __all__.append('get_override')
1384 ################################################################################
1386 class OverrideType(object):
1387 def __init__(self, *args, **kwargs):
1391 return '<OverrideType %s>' % self.overridetype
1393 __all__.append('OverrideType')
1396 def get_override_type(override_type, session=None):
1398 Returns OverrideType object for given C{override type}.
1400 @type override_type: string
1401 @param override_type: The name of the override type
1403 @type session: Session
1404 @param session: Optional SQLA session object (a temporary one will be
1405 generated if not supplied)
1408 @return: the database id for the given override type
1411 q = session.query(OverrideType).filter_by(overridetype=override_type)
1415 except NoResultFound:
1418 __all__.append('get_override_type')
1420 ################################################################################
1422 class DebContents(object):
1423 def __init__(self, *args, **kwargs):
1427 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1429 __all__.append('DebContents')
1432 class UdebContents(object):
1433 def __init__(self, *args, **kwargs):
1437 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1439 __all__.append('UdebContents')
1441 class PendingBinContents(object):
1442 def __init__(self, *args, **kwargs):
1446 return '<PendingBinContents %s>' % self.contents_id
1448 __all__.append('PendingBinContents')
1450 def insert_pending_content_paths(package,
1455 Make sure given paths are temporarily associated with given
1459 @param package: the package to associate with should have been read in from the binary control file
1460 @type fullpaths: list
1461 @param fullpaths: the list of paths of the file being associated with the binary
1462 @type session: SQLAlchemy session
1463 @param session: Optional SQLAlchemy session. If this is passed, the caller
1464 is responsible for ensuring a transaction has begun and committing the
1465 results or rolling back based on the result code. If not passed, a commit
1466 will be performed at the end of the function
1468 @return: True upon success, False if there is a problem
1471 privatetrans = False
1474 session = DBConn().session()
1478 arch = get_architecture(package['Architecture'], session)
1479 arch_id = arch.arch_id
1481 # Remove any already existing recorded files for this package
1482 q = session.query(PendingBinContents)
1483 q = q.filter_by(package=package['Package'])
1484 q = q.filter_by(version=package['Version'])
1485 q = q.filter_by(architecture=arch_id)
1488 for fullpath in fullpaths:
1490 if fullpath.startswith( "./" ):
1491 fullpath = fullpath[2:]
1493 pca = PendingBinContents()
1494 pca.package = package['Package']
1495 pca.version = package['Version']
1497 pca.architecture = arch_id
1500 pca.type = 8 # gross
1502 pca.type = 7 # also gross
1505 # Only commit if we set up the session ourself
1513 except Exception, e:
1514 traceback.print_exc()
1516 # Only rollback if we set up the session ourself
1523 __all__.append('insert_pending_content_paths')
1525 ################################################################################
1527 class Priority(object):
1528 def __init__(self, *args, **kwargs):
1531 def __eq__(self, val):
1532 if isinstance(val, str):
1533 return (self.priority == val)
1534 # This signals to use the normal comparison operator
1535 return NotImplemented
1537 def __ne__(self, val):
1538 if isinstance(val, str):
1539 return (self.priority != val)
1540 # This signals to use the normal comparison operator
1541 return NotImplemented
1544 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1546 __all__.append('Priority')
1549 def get_priority(priority, session=None):
1551 Returns Priority object for given C{priority name}.
1553 @type priority: string
1554 @param priority: The name of the priority
1556 @type session: Session
1557 @param session: Optional SQLA session object (a temporary one will be
1558 generated if not supplied)
1561 @return: Priority object for the given priority
1564 q = session.query(Priority).filter_by(priority=priority)
1568 except NoResultFound:
1571 __all__.append('get_priority')
1574 def get_priorities(session=None):
1576 Returns dictionary of priority names -> id mappings
1578 @type session: Session
1579 @param session: Optional SQL session object (a temporary one will be
1580 generated if not supplied)
1583 @return: dictionary of priority names -> id mappings
1587 q = session.query(Priority)
1589 ret[x.priority] = x.priority_id
1593 __all__.append('get_priorities')
1595 ################################################################################
1597 class Queue(object):
1598 def __init__(self, *args, **kwargs):
1602 return '<Queue %s>' % self.queue_name
1604 def add_file_from_pool(self, poolfile):
1605 """Copies a file into the pool. Assumes that the PoolFile object is
1606 attached to the same SQLAlchemy session as the Queue object is.
1608 The caller is responsible for committing after calling this function."""
1609 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
1611 # Check if we have a file of this name or this ID already
1612 for f in self.queuefiles:
1613 if f.fileid is not None and f.fileid == poolfile.file_id or \
1614 f.poolfile.filename == poolfile_basename:
1615 # In this case, update the QueueFile entry so we
1616 # don't remove it too early
1617 f.lastused = datetime.now()
1618 DBConn().session().object_session(pf).add(f)
1621 # Prepare QueueFile object
1623 qf.queue_id = self.queue_id
1624 qf.lastused = datetime.now()
1627 targetpath = qf.fullpath
1628 queuepath = os.path.join(self.path, poolfile_basename)
1631 if self.copy_pool_files:
1632 # We need to copy instead of symlink
1634 utils.copy(targetfile, queuepath)
1635 # NULL in the fileid field implies a copy
1638 os.symlink(targetfile, queuepath)
1639 qf.fileid = poolfile.file_id
1643 # Get the same session as the PoolFile is using and add the qf to it
1644 DBConn().session().object_session(poolfile).add(qf)
1649 __all__.append('Queue')
1652 def get_queue(queuename, session=None):
1654 Returns Queue object for given C{queue name}, creating it if it does not
1657 @type queuename: string
1658 @param queuename: The name of the queue
1660 @type session: Session
1661 @param session: Optional SQLA session object (a temporary one will be
1662 generated if not supplied)
1665 @return: Queue object for the given queue
1668 q = session.query(Queue).filter_by(queue_name=queuename)
1672 except NoResultFound:
1675 __all__.append('get_queue')
1677 ################################################################################
1679 class QueueFile(object):
1680 def __init__(self, *args, **kwargs):
1684 return '<QueueFile %s (%s)>' % (self.filename, self.queue_id)
1686 __all__.append('QueueFile')
1688 ################################################################################
1690 class Section(object):
1691 def __init__(self, *args, **kwargs):
1694 def __eq__(self, val):
1695 if isinstance(val, str):
1696 return (self.section == val)
1697 # This signals to use the normal comparison operator
1698 return NotImplemented
1700 def __ne__(self, val):
1701 if isinstance(val, str):
1702 return (self.section != val)
1703 # This signals to use the normal comparison operator
1704 return NotImplemented
1707 return '<Section %s>' % self.section
1709 __all__.append('Section')
1712 def get_section(section, session=None):
1714 Returns Section object for given C{section name}.
1716 @type section: string
1717 @param section: The name of the section
1719 @type session: Session
1720 @param session: Optional SQLA session object (a temporary one will be
1721 generated if not supplied)
1724 @return: Section object for the given section name
1727 q = session.query(Section).filter_by(section=section)
1731 except NoResultFound:
1734 __all__.append('get_section')
1737 def get_sections(session=None):
1739 Returns dictionary of section names -> id mappings
1741 @type session: Session
1742 @param session: Optional SQL session object (a temporary one will be
1743 generated if not supplied)
1746 @return: dictionary of section names -> id mappings
1750 q = session.query(Section)
1752 ret[x.section] = x.section_id
1756 __all__.append('get_sections')
1758 ################################################################################
1760 class DBSource(object):
1761 def __init__(self, *args, **kwargs):
1765 return '<DBSource %s (%s)>' % (self.source, self.version)
1767 __all__.append('DBSource')
1770 def source_exists(source, source_version, suites = ["any"], session=None):
1772 Ensure that source exists somewhere in the archive for the binary
1773 upload being processed.
1774 1. exact match => 1.0-3
1775 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1777 @type package: string
1778 @param package: package source name
1780 @type source_version: string
1781 @param source_version: expected source version
1784 @param suites: list of suites to check in, default I{any}
1786 @type session: Session
1787 @param session: Optional SQLA session object (a temporary one will be
1788 generated if not supplied)
1791 @return: returns 1 if a source with expected version is found, otherwise 0
1798 for suite in suites:
1799 q = session.query(DBSource).filter_by(source=source)
1801 # source must exist in suite X, or in some other suite that's
1802 # mapped to X, recursively... silent-maps are counted too,
1803 # unreleased-maps aren't.
1804 maps = cnf.ValueList("SuiteMappings")[:]
1806 maps = [ m.split() for m in maps ]
1807 maps = [ (x[1], x[2]) for x in maps
1808 if x[0] == "map" or x[0] == "silent-map" ]
1811 if x[1] in s and x[0] not in s:
1814 q = q.join(SrcAssociation).join(Suite)
1815 q = q.filter(Suite.suite_name.in_(s))
1817 # Reduce the query results to a list of version numbers
1818 ql = [ j.version for j in q.all() ]
1821 if source_version in ql:
1825 from daklib.regexes import re_bin_only_nmu
1826 orig_source_version = re_bin_only_nmu.sub('', source_version)
1827 if orig_source_version in ql:
1830 # No source found so return not ok
1835 __all__.append('source_exists')
1838 def get_suites_source_in(source, session=None):
1840 Returns list of Suite objects which given C{source} name is in
1843 @param source: DBSource package name to search for
1846 @return: list of Suite objects for the given source
1849 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1851 __all__.append('get_suites_source_in')
1854 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1856 Returns list of DBSource objects for given C{source} name and other parameters
1859 @param source: DBSource package name to search for
1861 @type source: str or None
1862 @param source: DBSource version name to search for or None if not applicable
1864 @type dm_upload_allowed: bool
1865 @param dm_upload_allowed: If None, no effect. If True or False, only
1866 return packages with that dm_upload_allowed setting
1868 @type session: Session
1869 @param session: Optional SQL session object (a temporary one will be
1870 generated if not supplied)
1873 @return: list of DBSource objects for the given name (may be empty)
1876 q = session.query(DBSource).filter_by(source=source)
1878 if version is not None:
1879 q = q.filter_by(version=version)
1881 if dm_upload_allowed is not None:
1882 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1886 __all__.append('get_sources_from_name')
1889 def get_source_in_suite(source, suite, session=None):
1891 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1893 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1894 - B{suite} - a suite name, eg. I{unstable}
1896 @type source: string
1897 @param source: source package name
1900 @param suite: the suite name
1903 @return: the version for I{source} in I{suite}
1907 q = session.query(SrcAssociation)
1908 q = q.join('source').filter_by(source=source)
1909 q = q.join('suite').filter_by(suite_name=suite)
1912 return q.one().source
1913 except NoResultFound:
1916 __all__.append('get_source_in_suite')
1918 ################################################################################
1920 class SourceACL(object):
1921 def __init__(self, *args, **kwargs):
1925 return '<SourceACL %s>' % self.source_acl_id
1927 __all__.append('SourceACL')
1929 ################################################################################
1931 class SrcAssociation(object):
1932 def __init__(self, *args, **kwargs):
1936 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1938 __all__.append('SrcAssociation')
1940 ################################################################################
1942 class SrcFormat(object):
1943 def __init__(self, *args, **kwargs):
1947 return '<SrcFormat %s>' % (self.format_name)
1949 __all__.append('SrcFormat')
1951 ################################################################################
1953 class SrcUploader(object):
1954 def __init__(self, *args, **kwargs):
1958 return '<SrcUploader %s>' % self.uploader_id
1960 __all__.append('SrcUploader')
1962 ################################################################################
1964 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1965 ('SuiteID', 'suite_id'),
1966 ('Version', 'version'),
1967 ('Origin', 'origin'),
1969 ('Description', 'description'),
1970 ('Untouchable', 'untouchable'),
1971 ('Announce', 'announce'),
1972 ('Codename', 'codename'),
1973 ('OverrideCodename', 'overridecodename'),
1974 ('ValidTime', 'validtime'),
1975 ('Priority', 'priority'),
1976 ('NotAutomatic', 'notautomatic'),
1977 ('CopyChanges', 'copychanges'),
1978 ('CopyDotDak', 'copydotdak'),
1979 ('CommentsDir', 'commentsdir'),
1980 ('OverrideSuite', 'overridesuite'),
1981 ('ChangelogBase', 'changelogbase')]
1984 class Suite(object):
1985 def __init__(self, *args, **kwargs):
1989 return '<Suite %s>' % self.suite_name
1991 def __eq__(self, val):
1992 if isinstance(val, str):
1993 return (self.suite_name == val)
1994 # This signals to use the normal comparison operator
1995 return NotImplemented
1997 def __ne__(self, val):
1998 if isinstance(val, str):
1999 return (self.suite_name != val)
2000 # This signals to use the normal comparison operator
2001 return NotImplemented
2005 for disp, field in SUITE_FIELDS:
2006 val = getattr(self, field, None)
2008 ret.append("%s: %s" % (disp, val))
2010 return "\n".join(ret)
2012 __all__.append('Suite')
2015 def get_suite_architecture(suite, architecture, session=None):
2017 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2021 @param suite: Suite name to search for
2023 @type architecture: str
2024 @param architecture: Architecture name to search for
2026 @type session: Session
2027 @param session: Optional SQL session object (a temporary one will be
2028 generated if not supplied)
2030 @rtype: SuiteArchitecture
2031 @return: the SuiteArchitecture object or None
2034 q = session.query(SuiteArchitecture)
2035 q = q.join(Architecture).filter_by(arch_string=architecture)
2036 q = q.join(Suite).filter_by(suite_name=suite)
2040 except NoResultFound:
2043 __all__.append('get_suite_architecture')
2046 def get_suite(suite, session=None):
2048 Returns Suite object for given C{suite name}.
2051 @param suite: The name of the suite
2053 @type session: Session
2054 @param session: Optional SQLA session object (a temporary one will be
2055 generated if not supplied)
2058 @return: Suite object for the requested suite name (None if not present)
2061 q = session.query(Suite).filter_by(suite_name=suite)
2065 except NoResultFound:
2068 __all__.append('get_suite')
2070 ################################################################################
2072 class SuiteArchitecture(object):
2073 def __init__(self, *args, **kwargs):
2077 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2079 __all__.append('SuiteArchitecture')
2082 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2084 Returns list of Architecture objects for given C{suite} name
2087 @param source: Suite name to search for
2089 @type skipsrc: boolean
2090 @param skipsrc: Whether to skip returning the 'source' architecture entry
2093 @type skipall: boolean
2094 @param skipall: Whether to skip returning the 'all' architecture entry
2097 @type session: Session
2098 @param session: Optional SQL session object (a temporary one will be
2099 generated if not supplied)
2102 @return: list of Architecture objects for the given name (may be empty)
2105 q = session.query(Architecture)
2106 q = q.join(SuiteArchitecture)
2107 q = q.join(Suite).filter_by(suite_name=suite)
2110 q = q.filter(Architecture.arch_string != 'source')
2113 q = q.filter(Architecture.arch_string != 'all')
2115 q = q.order_by('arch_string')
2119 __all__.append('get_suite_architectures')
2121 ################################################################################
2123 class SuiteSrcFormat(object):
2124 def __init__(self, *args, **kwargs):
2128 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2130 __all__.append('SuiteSrcFormat')
2133 def get_suite_src_formats(suite, session=None):
2135 Returns list of allowed SrcFormat for C{suite}.
2138 @param suite: Suite name to search for
2140 @type session: Session
2141 @param session: Optional SQL session object (a temporary one will be
2142 generated if not supplied)
2145 @return: the list of allowed source formats for I{suite}
2148 q = session.query(SrcFormat)
2149 q = q.join(SuiteSrcFormat)
2150 q = q.join(Suite).filter_by(suite_name=suite)
2151 q = q.order_by('format_name')
2155 __all__.append('get_suite_src_formats')
2157 ################################################################################
2160 def __init__(self, *args, **kwargs):
2163 def __eq__(self, val):
2164 if isinstance(val, str):
2165 return (self.uid == val)
2166 # This signals to use the normal comparison operator
2167 return NotImplemented
2169 def __ne__(self, val):
2170 if isinstance(val, str):
2171 return (self.uid != val)
2172 # This signals to use the normal comparison operator
2173 return NotImplemented
2176 return '<Uid %s (%s)>' % (self.uid, self.name)
2178 __all__.append('Uid')
2181 def add_database_user(uidname, session=None):
2183 Adds a database user
2185 @type uidname: string
2186 @param uidname: The uid of the user to add
2188 @type session: SQLAlchemy
2189 @param session: Optional SQL session object (a temporary one will be
2190 generated if not supplied). If not passed, a commit will be performed at
2191 the end of the function, otherwise the caller is responsible for commiting.
2194 @return: the uid object for the given uidname
2197 session.execute("CREATE USER :uid", {'uid': uidname})
2198 session.commit_or_flush()
2200 __all__.append('add_database_user')
2203 def get_or_set_uid(uidname, session=None):
2205 Returns uid object for given uidname.
2207 If no matching uidname is found, a row is inserted.
2209 @type uidname: string
2210 @param uidname: The uid to add
2212 @type session: SQLAlchemy
2213 @param session: Optional SQL session object (a temporary one will be
2214 generated if not supplied). If not passed, a commit will be performed at
2215 the end of the function, otherwise the caller is responsible for commiting.
2218 @return: the uid object for the given uidname
2221 q = session.query(Uid).filter_by(uid=uidname)
2225 except NoResultFound:
2229 session.commit_or_flush()
2234 __all__.append('get_or_set_uid')
2237 def get_uid_from_fingerprint(fpr, session=None):
2238 q = session.query(Uid)
2239 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2243 except NoResultFound:
2246 __all__.append('get_uid_from_fingerprint')
2248 ################################################################################
2250 class UploadBlock(object):
2251 def __init__(self, *args, **kwargs):
2255 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2257 __all__.append('UploadBlock')
2259 ################################################################################
2261 class DBConn(Singleton):
2263 database module init.
2265 def __init__(self, *args, **kwargs):
2266 super(DBConn, self).__init__(*args, **kwargs)
2268 def _startup(self, *args, **kwargs):
2270 if kwargs.has_key('debug'):
2274 def __setuptables(self):
2275 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2276 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2277 self.tbl_bin_contents = Table('bin_contents', self.db_meta, autoload=True)
2278 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2279 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2280 self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2281 self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2282 self.tbl_component = Table('component', self.db_meta, autoload=True)
2283 self.tbl_config = Table('config', self.db_meta, autoload=True)
2284 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2285 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2286 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2287 self.tbl_changes_pending_files = Table('changes_pending_files', self.db_meta, autoload=True)
2288 self.tbl_changes_pool_files = Table('changes_pool_files', self.db_meta, autoload=True)
2289 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2290 self.tbl_deb_contents = Table('deb_contents', self.db_meta, autoload=True)
2291 self.tbl_files = Table('files', self.db_meta, autoload=True)
2292 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2293 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2294 self.tbl_known_changes = Table('known_changes', self.db_meta, autoload=True)
2295 self.tbl_keyring_acl_map = Table('keyring_acl_map', self.db_meta, autoload=True)
2296 self.tbl_location = Table('location', self.db_meta, autoload=True)
2297 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2298 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2299 self.tbl_override = Table('override', self.db_meta, autoload=True)
2300 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2301 self.tbl_pending_bin_contents = Table('pending_bin_contents', self.db_meta, autoload=True)
2302 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2303 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2304 self.tbl_queue_files = Table('queue_files', self.db_meta, autoload=True)
2305 self.tbl_section = Table('section', self.db_meta, autoload=True)
2306 self.tbl_source = Table('source', self.db_meta, autoload=True)
2307 self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2308 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2309 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2310 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2311 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2312 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2313 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2314 self.tbl_suite_queue_copy = Table('suite_queue_copy', self.db_meta, autoload=True)
2315 self.tbl_udeb_contents = Table('udeb_contents', self.db_meta, autoload=True)
2316 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2317 self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2319 def __setupmappers(self):
2320 mapper(Architecture, self.tbl_architecture,
2321 properties = dict(arch_id = self.tbl_architecture.c.id))
2323 mapper(Archive, self.tbl_archive,
2324 properties = dict(archive_id = self.tbl_archive.c.id,
2325 archive_name = self.tbl_archive.c.name))
2327 mapper(BinAssociation, self.tbl_bin_associations,
2328 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2329 suite_id = self.tbl_bin_associations.c.suite,
2330 suite = relation(Suite),
2331 binary_id = self.tbl_bin_associations.c.bin,
2332 binary = relation(DBBinary)))
2334 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2335 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2336 filename = self.tbl_pending_bin_contents.c.filename,
2337 package = self.tbl_pending_bin_contents.c.package,
2338 version = self.tbl_pending_bin_contents.c.version,
2339 arch = self.tbl_pending_bin_contents.c.arch,
2340 otype = self.tbl_pending_bin_contents.c.type))
2342 mapper(DebContents, self.tbl_deb_contents,
2343 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2344 package=self.tbl_deb_contents.c.package,
2345 component=self.tbl_deb_contents.c.component,
2346 arch=self.tbl_deb_contents.c.arch,
2347 section=self.tbl_deb_contents.c.section,
2348 filename=self.tbl_deb_contents.c.filename))
2350 mapper(UdebContents, self.tbl_udeb_contents,
2351 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2352 package=self.tbl_udeb_contents.c.package,
2353 component=self.tbl_udeb_contents.c.component,
2354 arch=self.tbl_udeb_contents.c.arch,
2355 section=self.tbl_udeb_contents.c.section,
2356 filename=self.tbl_udeb_contents.c.filename))
2358 mapper(DBBinary, self.tbl_binaries,
2359 properties = dict(binary_id = self.tbl_binaries.c.id,
2360 package = self.tbl_binaries.c.package,
2361 version = self.tbl_binaries.c.version,
2362 maintainer_id = self.tbl_binaries.c.maintainer,
2363 maintainer = relation(Maintainer),
2364 source_id = self.tbl_binaries.c.source,
2365 source = relation(DBSource),
2366 arch_id = self.tbl_binaries.c.architecture,
2367 architecture = relation(Architecture),
2368 poolfile_id = self.tbl_binaries.c.file,
2369 poolfile = relation(PoolFile),
2370 binarytype = self.tbl_binaries.c.type,
2371 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2372 fingerprint = relation(Fingerprint),
2373 install_date = self.tbl_binaries.c.install_date,
2374 binassociations = relation(BinAssociation,
2375 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2377 mapper(BinaryACL, self.tbl_binary_acl,
2378 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2380 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2381 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2382 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2383 architecture = relation(Architecture)))
2385 mapper(Component, self.tbl_component,
2386 properties = dict(component_id = self.tbl_component.c.id,
2387 component_name = self.tbl_component.c.name))
2389 mapper(DBConfig, self.tbl_config,
2390 properties = dict(config_id = self.tbl_config.c.id))
2392 mapper(DSCFile, self.tbl_dsc_files,
2393 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2394 source_id = self.tbl_dsc_files.c.source,
2395 source = relation(DBSource),
2396 poolfile_id = self.tbl_dsc_files.c.file,
2397 poolfile = relation(PoolFile)))
2399 mapper(PoolFile, self.tbl_files,
2400 properties = dict(file_id = self.tbl_files.c.id,
2401 filesize = self.tbl_files.c.size,
2402 location_id = self.tbl_files.c.location,
2403 location = relation(Location)))
2405 mapper(Fingerprint, self.tbl_fingerprint,
2406 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2407 uid_id = self.tbl_fingerprint.c.uid,
2408 uid = relation(Uid),
2409 keyring_id = self.tbl_fingerprint.c.keyring,
2410 keyring = relation(Keyring),
2411 source_acl = relation(SourceACL),
2412 binary_acl = relation(BinaryACL)))
2414 mapper(Keyring, self.tbl_keyrings,
2415 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2416 keyring_id = self.tbl_keyrings.c.id))
2418 mapper(KnownChange, self.tbl_known_changes,
2419 properties = dict(known_change_id = self.tbl_known_changes.c.id,
2420 poolfiles = relation(PoolFile,
2421 secondary=self.tbl_changes_pool_files,
2422 backref="changeslinks"),
2423 files = relation(KnownChangePendingFile, backref="changesfile")))
2425 mapper(KnownChangePendingFile, self.tbl_changes_pending_files,
2426 properties = dict(known_change_pending_file_id = self.tbl_changes_pending_files.c.id))
2428 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2429 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2430 keyring = relation(Keyring, backref="keyring_acl_map"),
2431 architecture = relation(Architecture)))
2433 mapper(Location, self.tbl_location,
2434 properties = dict(location_id = self.tbl_location.c.id,
2435 component_id = self.tbl_location.c.component,
2436 component = relation(Component),
2437 archive_id = self.tbl_location.c.archive,
2438 archive = relation(Archive),
2439 archive_type = self.tbl_location.c.type))
2441 mapper(Maintainer, self.tbl_maintainer,
2442 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2444 mapper(NewComment, self.tbl_new_comments,
2445 properties = dict(comment_id = self.tbl_new_comments.c.id))
2447 mapper(Override, self.tbl_override,
2448 properties = dict(suite_id = self.tbl_override.c.suite,
2449 suite = relation(Suite),
2450 package = self.tbl_override.c.package,
2451 component_id = self.tbl_override.c.component,
2452 component = relation(Component),
2453 priority_id = self.tbl_override.c.priority,
2454 priority = relation(Priority),
2455 section_id = self.tbl_override.c.section,
2456 section = relation(Section),
2457 overridetype_id = self.tbl_override.c.type,
2458 overridetype = relation(OverrideType)))
2460 mapper(OverrideType, self.tbl_override_type,
2461 properties = dict(overridetype = self.tbl_override_type.c.type,
2462 overridetype_id = self.tbl_override_type.c.id))
2464 mapper(Priority, self.tbl_priority,
2465 properties = dict(priority_id = self.tbl_priority.c.id))
2467 mapper(Queue, self.tbl_queue,
2468 properties = dict(queue_id = self.tbl_queue.c.id))
2470 mapper(QueueFile, self.tbl_queue_files,
2471 properties = dict(queue = relation(Queue, backref='queuefiles'),
2472 poolfile = relation(PoolFile, backref='queueinstances')))
2474 mapper(Section, self.tbl_section,
2475 properties = dict(section_id = self.tbl_section.c.id,
2476 section=self.tbl_section.c.section))
2478 mapper(DBSource, self.tbl_source,
2479 properties = dict(source_id = self.tbl_source.c.id,
2480 version = self.tbl_source.c.version,
2481 maintainer_id = self.tbl_source.c.maintainer,
2482 maintainer = relation(Maintainer,
2483 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2484 poolfile_id = self.tbl_source.c.file,
2485 poolfile = relation(PoolFile),
2486 fingerprint_id = self.tbl_source.c.sig_fpr,
2487 fingerprint = relation(Fingerprint),
2488 changedby_id = self.tbl_source.c.changedby,
2489 changedby = relation(Maintainer,
2490 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2491 srcfiles = relation(DSCFile,
2492 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2493 srcassociations = relation(SrcAssociation,
2494 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2495 srcuploaders = relation(SrcUploader)))
2497 mapper(SourceACL, self.tbl_source_acl,
2498 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2500 mapper(SrcAssociation, self.tbl_src_associations,
2501 properties = dict(sa_id = self.tbl_src_associations.c.id,
2502 suite_id = self.tbl_src_associations.c.suite,
2503 suite = relation(Suite),
2504 source_id = self.tbl_src_associations.c.source,
2505 source = relation(DBSource)))
2507 mapper(SrcFormat, self.tbl_src_format,
2508 properties = dict(src_format_id = self.tbl_src_format.c.id,
2509 format_name = self.tbl_src_format.c.format_name))
2511 mapper(SrcUploader, self.tbl_src_uploaders,
2512 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2513 source_id = self.tbl_src_uploaders.c.source,
2514 source = relation(DBSource,
2515 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2516 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2517 maintainer = relation(Maintainer,
2518 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2520 mapper(Suite, self.tbl_suite,
2521 properties = dict(suite_id = self.tbl_suite.c.id,
2522 policy_queue = relation(Queue),
2523 copy_queues = relation(Queue, secondary=self.tbl_suite_queue_copy)))
2525 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2526 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2527 suite = relation(Suite, backref='suitearchitectures'),
2528 arch_id = self.tbl_suite_architectures.c.architecture,
2529 architecture = relation(Architecture)))
2531 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2532 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2533 suite = relation(Suite, backref='suitesrcformats'),
2534 src_format_id = self.tbl_suite_src_formats.c.src_format,
2535 src_format = relation(SrcFormat)))
2537 mapper(Uid, self.tbl_uid,
2538 properties = dict(uid_id = self.tbl_uid.c.id,
2539 fingerprint = relation(Fingerprint)))
2541 mapper(UploadBlock, self.tbl_upload_blocks,
2542 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2543 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2544 uid = relation(Uid, backref="uploadblocks")))
2546 ## Connection functions
2547 def __createconn(self):
2548 from config import Config
2552 connstr = "postgres://%s" % cnf["DB::Host"]
2553 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2554 connstr += ":%s" % cnf["DB::Port"]
2555 connstr += "/%s" % cnf["DB::Name"]
2558 connstr = "postgres:///%s" % cnf["DB::Name"]
2559 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2560 connstr += "?port=%s" % cnf["DB::Port"]
2562 self.db_pg = create_engine(connstr, echo=self.debug)
2563 self.db_meta = MetaData()
2564 self.db_meta.bind = self.db_pg
2565 self.db_smaker = sessionmaker(bind=self.db_pg,
2569 self.__setuptables()
2570 self.__setupmappers()
2573 return self.db_smaker()
2575 __all__.append('DBConn')