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()
656 for fullpath in fullpaths:
657 if fullpath.startswith( './' ):
658 fullpath = fullpath[2:]
660 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )", { 'filename': fullpath, 'id': binary_id} )
668 traceback.print_exc()
670 # Only rollback if we set up the session ourself
677 __all__.append('insert_content_paths')
679 ################################################################################
681 class DSCFile(object):
682 def __init__(self, *args, **kwargs):
686 return '<DSCFile %s>' % self.dscfile_id
688 __all__.append('DSCFile')
691 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
693 Returns a list of DSCFiles which may be empty
695 @type dscfile_id: int (optional)
696 @param dscfile_id: the dscfile_id of the DSCFiles to find
698 @type source_id: int (optional)
699 @param source_id: the source id related to the DSCFiles to find
701 @type poolfile_id: int (optional)
702 @param poolfile_id: the poolfile id related to the DSCFiles to find
705 @return: Possibly empty list of DSCFiles
708 q = session.query(DSCFile)
710 if dscfile_id is not None:
711 q = q.filter_by(dscfile_id=dscfile_id)
713 if source_id is not None:
714 q = q.filter_by(source_id=source_id)
716 if poolfile_id is not None:
717 q = q.filter_by(poolfile_id=poolfile_id)
721 __all__.append('get_dscfiles')
723 ################################################################################
725 class PoolFile(object):
726 def __init__(self, *args, **kwargs):
730 return '<PoolFile %s>' % self.filename
734 return os.path.join(self.location.path, self.filename)
736 __all__.append('PoolFile')
739 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
742 (ValidFileFound [boolean or None], PoolFile object or None)
744 @type filename: string
745 @param filename: the filename of the file to check against the DB
748 @param filesize: the size of the file to check against the DB
751 @param md5sum: the md5sum of the file to check against the DB
753 @type location_id: int
754 @param location_id: the id of the location to look in
757 @return: Tuple of length 2.
758 If more than one file found with that name:
760 If valid pool file found: (True, PoolFile object)
761 If valid pool file not found:
762 (False, None) if no file found
763 (False, PoolFile object) if file found with size/md5sum mismatch
766 q = session.query(PoolFile).filter_by(filename=filename)
767 q = q.join(Location).filter_by(location_id=location_id)
777 if obj.md5sum != md5sum or obj.filesize != int(filesize):
785 __all__.append('check_poolfile')
788 def get_poolfile_by_id(file_id, session=None):
790 Returns a PoolFile objects or None for the given id
793 @param file_id: the id of the file to look for
795 @rtype: PoolFile or None
796 @return: either the PoolFile object or None
799 q = session.query(PoolFile).filter_by(file_id=file_id)
803 except NoResultFound:
806 __all__.append('get_poolfile_by_id')
810 def get_poolfile_by_name(filename, location_id=None, session=None):
812 Returns an array of PoolFile objects for the given filename and
813 (optionally) location_id
815 @type filename: string
816 @param filename: the filename of the file to check against the DB
818 @type location_id: int
819 @param location_id: the id of the location to look in (optional)
822 @return: array of PoolFile objects
825 q = session.query(PoolFile).filter_by(filename=filename)
827 if location_id is not None:
828 q = q.join(Location).filter_by(location_id=location_id)
832 __all__.append('get_poolfile_by_name')
835 def get_poolfile_like_name(filename, session=None):
837 Returns an array of PoolFile objects which are like the given name
839 @type filename: string
840 @param filename: the filename of the file to check against the DB
843 @return: array of PoolFile objects
846 # TODO: There must be a way of properly using bind parameters with %FOO%
847 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
851 __all__.append('get_poolfile_like_name')
853 ################################################################################
855 class Fingerprint(object):
856 def __init__(self, *args, **kwargs):
860 return '<Fingerprint %s>' % self.fingerprint
862 __all__.append('Fingerprint')
865 def get_fingerprint(fpr, session=None):
867 Returns Fingerprint object for given fpr.
870 @param fpr: The fpr to find / add
872 @type session: SQLAlchemy
873 @param session: Optional SQL session object (a temporary one will be
874 generated if not supplied).
877 @return: the Fingerprint object for the given fpr or None
880 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
884 except NoResultFound:
889 __all__.append('get_fingerprint')
892 def get_or_set_fingerprint(fpr, session=None):
894 Returns Fingerprint object for given fpr.
896 If no matching fpr is found, a row is inserted.
899 @param fpr: The fpr to find / add
901 @type session: SQLAlchemy
902 @param session: Optional SQL session object (a temporary one will be
903 generated if not supplied). If not passed, a commit will be performed at
904 the end of the function, otherwise the caller is responsible for commiting.
905 A flush will be performed either way.
908 @return: the Fingerprint object for the given fpr
911 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
915 except NoResultFound:
916 fingerprint = Fingerprint()
917 fingerprint.fingerprint = fpr
918 session.add(fingerprint)
919 session.commit_or_flush()
924 __all__.append('get_or_set_fingerprint')
926 ################################################################################
928 # Helper routine for Keyring class
929 def get_ldap_name(entry):
931 for k in ["cn", "mn", "sn"]:
933 if ret and ret[0] != "" and ret[0] != "-":
935 return " ".join(name)
937 ################################################################################
939 class Keyring(object):
940 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
941 " --with-colons --fingerprint --fingerprint"
946 def __init__(self, *args, **kwargs):
950 return '<Keyring %s>' % self.keyring_name
952 def de_escape_gpg_str(self, txt):
953 esclist = re.split(r'(\\x..)', txt)
954 for x in range(1,len(esclist),2):
955 esclist[x] = "%c" % (int(esclist[x][2:],16))
956 return "".join(esclist)
958 def load_keys(self, keyring):
961 if not self.keyring_id:
962 raise Exception('Must be initialized with database information')
964 k = os.popen(self.gpg_invocation % keyring, "r")
968 for line in k.xreadlines():
969 field = line.split(":")
970 if field[0] == "pub":
972 (name, addr) = email.Utils.parseaddr(field[9])
973 name = re.sub(r"\s*[(].*[)]", "", name)
974 if name == "" or addr == "" or "@" not in addr:
977 name = self.de_escape_gpg_str(name)
978 self.keys[key] = {"email": addr}
980 self.keys[key]["name"] = name
981 self.keys[key]["aliases"] = [name]
982 self.keys[key]["fingerprints"] = []
984 elif key and field[0] == "sub" and len(field) >= 12:
985 signingkey = ("s" in field[11])
986 elif key and field[0] == "uid":
987 (name, addr) = email.Utils.parseaddr(field[9])
988 if name and name not in self.keys[key]["aliases"]:
989 self.keys[key]["aliases"].append(name)
990 elif signingkey and field[0] == "fpr":
991 self.keys[key]["fingerprints"].append(field[9])
992 self.fpr_lookup[field[9]] = key
994 def import_users_from_ldap(self, session):
998 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
999 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1001 l = ldap.open(LDAPServer)
1002 l.simple_bind_s("","")
1003 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1004 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1005 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1007 ldap_fin_uid_id = {}
1014 uid = entry["uid"][0]
1015 name = get_ldap_name(entry)
1016 fingerprints = entry["keyFingerPrint"]
1018 for f in fingerprints:
1019 key = self.fpr_lookup.get(f, None)
1020 if key not in self.keys:
1022 self.keys[key]["uid"] = uid
1026 keyid = get_or_set_uid(uid, session).uid_id
1027 byuid[keyid] = (uid, name)
1028 byname[uid] = (keyid, name)
1030 return (byname, byuid)
1032 def generate_users_from_keyring(self, format, session):
1036 for x in self.keys.keys():
1037 if self.keys[x]["email"] == "invalid-uid":
1039 self.keys[x]["uid"] = format % "invalid-uid"
1041 uid = format % self.keys[x]["email"]
1042 keyid = get_or_set_uid(uid, session).uid_id
1043 byuid[keyid] = (uid, self.keys[x]["name"])
1044 byname[uid] = (keyid, self.keys[x]["name"])
1045 self.keys[x]["uid"] = uid
1048 uid = format % "invalid-uid"
1049 keyid = get_or_set_uid(uid, session).uid_id
1050 byuid[keyid] = (uid, "ungeneratable user id")
1051 byname[uid] = (keyid, "ungeneratable user id")
1053 return (byname, byuid)
1055 __all__.append('Keyring')
1058 def get_keyring(keyring, session=None):
1060 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1061 If C{keyring} already has an entry, simply return the existing Keyring
1063 @type keyring: string
1064 @param keyring: the keyring name
1067 @return: the Keyring object for this keyring
1070 q = session.query(Keyring).filter_by(keyring_name=keyring)
1074 except NoResultFound:
1077 __all__.append('get_keyring')
1079 ################################################################################
1081 class KeyringACLMap(object):
1082 def __init__(self, *args, **kwargs):
1086 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1088 __all__.append('KeyringACLMap')
1090 ################################################################################
1092 class KnownChange(object):
1093 def __init__(self, *args, **kwargs):
1097 return '<KnownChange %s>' % self.changesname
1099 __all__.append('KnownChange')
1102 def get_knownchange(filename, session=None):
1104 returns knownchange object for given C{filename}.
1106 @type archive: string
1107 @param archive: the name of the arhive
1109 @type session: Session
1110 @param session: Optional SQLA session object (a temporary one will be
1111 generated if not supplied)
1114 @return: Archive object for the given name (None if not present)
1117 q = session.query(KnownChange).filter_by(changesname=filename)
1121 except NoResultFound:
1124 __all__.append('get_knownchange')
1126 ################################################################################
1128 class KnownChangePendingFile(object):
1129 def __init__(self, *args, **kwargs):
1133 return '<KnownChangePendingFile %s>' % self.known_change_pending_file_id
1135 __all__.append('KnownChangePendingFile')
1137 ################################################################################
1139 class Location(object):
1140 def __init__(self, *args, **kwargs):
1144 return '<Location %s (%s)>' % (self.path, self.location_id)
1146 __all__.append('Location')
1149 def get_location(location, component=None, archive=None, session=None):
1151 Returns Location object for the given combination of location, component
1154 @type location: string
1155 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1157 @type component: string
1158 @param component: the component name (if None, no restriction applied)
1160 @type archive: string
1161 @param archive_id: the archive name (if None, no restriction applied)
1163 @rtype: Location / None
1164 @return: Either a Location object or None if one can't be found
1167 q = session.query(Location).filter_by(path=location)
1169 if archive is not None:
1170 q = q.join(Archive).filter_by(archive_name=archive)
1172 if component is not None:
1173 q = q.join(Component).filter_by(component_name=component)
1177 except NoResultFound:
1180 __all__.append('get_location')
1182 ################################################################################
1184 class Maintainer(object):
1185 def __init__(self, *args, **kwargs):
1189 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1191 def get_split_maintainer(self):
1192 if not hasattr(self, 'name') or self.name is None:
1193 return ('', '', '', '')
1195 return fix_maintainer(self.name.strip())
1197 __all__.append('Maintainer')
1200 def get_or_set_maintainer(name, session=None):
1202 Returns Maintainer object for given maintainer name.
1204 If no matching maintainer name is found, a row is inserted.
1207 @param name: The maintainer name to add
1209 @type session: SQLAlchemy
1210 @param session: Optional SQL session object (a temporary one will be
1211 generated if not supplied). If not passed, a commit will be performed at
1212 the end of the function, otherwise the caller is responsible for commiting.
1213 A flush will be performed either way.
1216 @return: the Maintainer object for the given maintainer
1219 q = session.query(Maintainer).filter_by(name=name)
1222 except NoResultFound:
1223 maintainer = Maintainer()
1224 maintainer.name = name
1225 session.add(maintainer)
1226 session.commit_or_flush()
1231 __all__.append('get_or_set_maintainer')
1234 def get_maintainer(maintainer_id, session=None):
1236 Return the name of the maintainer behind C{maintainer_id} or None if that
1237 maintainer_id is invalid.
1239 @type maintainer_id: int
1240 @param maintainer_id: the id of the maintainer
1243 @return: the Maintainer with this C{maintainer_id}
1246 return session.query(Maintainer).get(maintainer_id)
1248 __all__.append('get_maintainer')
1250 ################################################################################
1252 class NewComment(object):
1253 def __init__(self, *args, **kwargs):
1257 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1259 __all__.append('NewComment')
1262 def has_new_comment(package, version, session=None):
1264 Returns true if the given combination of C{package}, C{version} has a comment.
1266 @type package: string
1267 @param package: name of the package
1269 @type version: string
1270 @param version: package version
1272 @type session: Session
1273 @param session: Optional SQLA session object (a temporary one will be
1274 generated if not supplied)
1280 q = session.query(NewComment)
1281 q = q.filter_by(package=package)
1282 q = q.filter_by(version=version)
1284 return bool(q.count() > 0)
1286 __all__.append('has_new_comment')
1289 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1291 Returns (possibly empty) list of NewComment objects for the given
1294 @type package: string (optional)
1295 @param package: name of the package
1297 @type version: string (optional)
1298 @param version: package version
1300 @type comment_id: int (optional)
1301 @param comment_id: An id of a comment
1303 @type session: Session
1304 @param session: Optional SQLA session object (a temporary one will be
1305 generated if not supplied)
1308 @return: A (possibly empty) list of NewComment objects will be returned
1311 q = session.query(NewComment)
1312 if package is not None: q = q.filter_by(package=package)
1313 if version is not None: q = q.filter_by(version=version)
1314 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1318 __all__.append('get_new_comments')
1320 ################################################################################
1322 class Override(object):
1323 def __init__(self, *args, **kwargs):
1327 return '<Override %s (%s)>' % (self.package, self.suite_id)
1329 __all__.append('Override')
1332 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1334 Returns Override object for the given parameters
1336 @type package: string
1337 @param package: The name of the package
1339 @type suite: string, list or None
1340 @param suite: The name of the suite (or suites if a list) to limit to. If
1341 None, don't limit. Defaults to None.
1343 @type component: string, list or None
1344 @param component: The name of the component (or components if a list) to
1345 limit to. If None, don't limit. Defaults to None.
1347 @type overridetype: string, list or None
1348 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1349 limit to. If None, don't limit. Defaults to None.
1351 @type session: Session
1352 @param session: Optional SQLA session object (a temporary one will be
1353 generated if not supplied)
1356 @return: A (possibly empty) list of Override objects will be returned
1359 q = session.query(Override)
1360 q = q.filter_by(package=package)
1362 if suite is not None:
1363 if not isinstance(suite, list): suite = [suite]
1364 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1366 if component is not None:
1367 if not isinstance(component, list): component = [component]
1368 q = q.join(Component).filter(Component.component_name.in_(component))
1370 if overridetype is not None:
1371 if not isinstance(overridetype, list): overridetype = [overridetype]
1372 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1376 __all__.append('get_override')
1379 ################################################################################
1381 class OverrideType(object):
1382 def __init__(self, *args, **kwargs):
1386 return '<OverrideType %s>' % self.overridetype
1388 __all__.append('OverrideType')
1391 def get_override_type(override_type, session=None):
1393 Returns OverrideType object for given C{override type}.
1395 @type override_type: string
1396 @param override_type: The name of the override type
1398 @type session: Session
1399 @param session: Optional SQLA session object (a temporary one will be
1400 generated if not supplied)
1403 @return: the database id for the given override type
1406 q = session.query(OverrideType).filter_by(overridetype=override_type)
1410 except NoResultFound:
1413 __all__.append('get_override_type')
1415 ################################################################################
1417 class PendingContentAssociation(object):
1418 def __init__(self, *args, **kwargs):
1422 return '<PendingContentAssociation %s>' % self.pca_id
1424 __all__.append('PendingContentAssociation')
1426 def insert_pending_content_paths(package, fullpaths, session=None):
1428 Make sure given paths are temporarily associated with given
1432 @param package: the package to associate with should have been read in from the binary control file
1433 @type fullpaths: list
1434 @param fullpaths: the list of paths of the file being associated with the binary
1435 @type session: SQLAlchemy session
1436 @param session: Optional SQLAlchemy session. If this is passed, the caller
1437 is responsible for ensuring a transaction has begun and committing the
1438 results or rolling back based on the result code. If not passed, a commit
1439 will be performed at the end of the function
1441 @return: True upon success, False if there is a problem
1444 privatetrans = False
1447 session = DBConn().session()
1451 arch = get_architecture(package['Architecture'], session)
1452 arch_id = arch.arch_id
1454 # Remove any already existing recorded files for this package
1455 q = session.query(PendingContentAssociation)
1456 q = q.filter_by(package=package['Package'])
1457 q = q.filter_by(version=package['Version'])
1458 q = q.filter_by(architecture=arch_id)
1463 for fullpath in fullpaths:
1464 (path, filename) = os.path.split(fullpath)
1466 if path.startswith( "./" ):
1469 filepath_id = get_or_set_contents_path_id(path, session)
1470 filename_id = get_or_set_contents_file_id(filename, session)
1472 pathcache[fullpath] = (filepath_id, filename_id)
1474 for fullpath, dat in pathcache.items():
1475 pca = PendingContentAssociation()
1476 pca.package = package['Package']
1477 pca.version = package['Version']
1478 pca.filepath_id = dat[0]
1479 pca.filename_id = dat[1]
1480 pca.architecture = arch_id
1483 # Only commit if we set up the session ourself
1491 except Exception, e:
1492 traceback.print_exc()
1494 # Only rollback if we set up the session ourself
1501 __all__.append('insert_pending_content_paths')
1503 ################################################################################
1505 class Priority(object):
1506 def __init__(self, *args, **kwargs):
1509 def __eq__(self, val):
1510 if isinstance(val, str):
1511 return (self.priority == val)
1512 # This signals to use the normal comparison operator
1513 return NotImplemented
1515 def __ne__(self, val):
1516 if isinstance(val, str):
1517 return (self.priority != val)
1518 # This signals to use the normal comparison operator
1519 return NotImplemented
1522 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1524 __all__.append('Priority')
1527 def get_priority(priority, session=None):
1529 Returns Priority object for given C{priority name}.
1531 @type priority: string
1532 @param priority: The name of the priority
1534 @type session: Session
1535 @param session: Optional SQLA session object (a temporary one will be
1536 generated if not supplied)
1539 @return: Priority object for the given priority
1542 q = session.query(Priority).filter_by(priority=priority)
1546 except NoResultFound:
1549 __all__.append('get_priority')
1552 def get_priorities(session=None):
1554 Returns dictionary of priority names -> id mappings
1556 @type session: Session
1557 @param session: Optional SQL session object (a temporary one will be
1558 generated if not supplied)
1561 @return: dictionary of priority names -> id mappings
1565 q = session.query(Priority)
1567 ret[x.priority] = x.priority_id
1571 __all__.append('get_priorities')
1573 ################################################################################
1575 class Queue(object):
1576 def __init__(self, *args, **kwargs):
1580 return '<Queue %s>' % self.queue_name
1582 def add_file_from_pool(self, poolfile):
1583 """Copies a file into the pool. Assumes that the PoolFile object is
1584 attached to the same SQLAlchemy session as the Queue object is.
1586 The caller is responsible for committing after calling this function."""
1587 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
1589 # Check if we have a file of this name or this ID already
1590 for f in self.queuefiles:
1591 if f.fileid is not None and f.fileid == poolfile.file_id or \
1592 f.poolfile.filename == poolfile_basename:
1593 # In this case, update the QueueFile entry so we
1594 # don't remove it too early
1595 f.lastused = datetime.now()
1596 DBConn().session().object_session(pf).add(f)
1599 # Prepare QueueFile object
1601 qf.queue_id = self.queue_id
1602 qf.lastused = datetime.now()
1605 targetpath = qf.fullpath
1606 queuepath = os.path.join(self.path, poolfile_basename)
1609 if self.copy_pool_files:
1610 # We need to copy instead of symlink
1612 utils.copy(targetfile, queuepath)
1613 # NULL in the fileid field implies a copy
1616 os.symlink(targetfile, queuepath)
1617 qf.fileid = poolfile.file_id
1621 # Get the same session as the PoolFile is using and add the qf to it
1622 DBConn().session().object_session(poolfile).add(qf)
1627 __all__.append('Queue')
1630 def get_queue(queuename, session=None):
1632 Returns Queue object for given C{queue name}, creating it if it does not
1635 @type queuename: string
1636 @param queuename: The name of the queue
1638 @type session: Session
1639 @param session: Optional SQLA session object (a temporary one will be
1640 generated if not supplied)
1643 @return: Queue object for the given queue
1646 q = session.query(Queue).filter_by(queue_name=queuename)
1650 except NoResultFound:
1653 __all__.append('get_queue')
1655 ################################################################################
1657 class QueueFile(object):
1658 def __init__(self, *args, **kwargs):
1662 return '<QueueFile %s (%s)>' % (self.filename, self.queue_id)
1664 __all__.append('QueueFile')
1666 ################################################################################
1668 class Section(object):
1669 def __init__(self, *args, **kwargs):
1672 def __eq__(self, val):
1673 if isinstance(val, str):
1674 return (self.section == val)
1675 # This signals to use the normal comparison operator
1676 return NotImplemented
1678 def __ne__(self, val):
1679 if isinstance(val, str):
1680 return (self.section != val)
1681 # This signals to use the normal comparison operator
1682 return NotImplemented
1685 return '<Section %s>' % self.section
1687 __all__.append('Section')
1690 def get_section(section, session=None):
1692 Returns Section object for given C{section name}.
1694 @type section: string
1695 @param section: The name of the section
1697 @type session: Session
1698 @param session: Optional SQLA session object (a temporary one will be
1699 generated if not supplied)
1702 @return: Section object for the given section name
1705 q = session.query(Section).filter_by(section=section)
1709 except NoResultFound:
1712 __all__.append('get_section')
1715 def get_sections(session=None):
1717 Returns dictionary of section names -> id mappings
1719 @type session: Session
1720 @param session: Optional SQL session object (a temporary one will be
1721 generated if not supplied)
1724 @return: dictionary of section names -> id mappings
1728 q = session.query(Section)
1730 ret[x.section] = x.section_id
1734 __all__.append('get_sections')
1736 ################################################################################
1738 class DBSource(object):
1739 def __init__(self, *args, **kwargs):
1743 return '<DBSource %s (%s)>' % (self.source, self.version)
1745 __all__.append('DBSource')
1748 def source_exists(source, source_version, suites = ["any"], session=None):
1750 Ensure that source exists somewhere in the archive for the binary
1751 upload being processed.
1752 1. exact match => 1.0-3
1753 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1755 @type package: string
1756 @param package: package source name
1758 @type source_version: string
1759 @param source_version: expected source version
1762 @param suites: list of suites to check in, default I{any}
1764 @type session: Session
1765 @param session: Optional SQLA session object (a temporary one will be
1766 generated if not supplied)
1769 @return: returns 1 if a source with expected version is found, otherwise 0
1776 for suite in suites:
1777 q = session.query(DBSource).filter_by(source=source)
1779 # source must exist in suite X, or in some other suite that's
1780 # mapped to X, recursively... silent-maps are counted too,
1781 # unreleased-maps aren't.
1782 maps = cnf.ValueList("SuiteMappings")[:]
1784 maps = [ m.split() for m in maps ]
1785 maps = [ (x[1], x[2]) for x in maps
1786 if x[0] == "map" or x[0] == "silent-map" ]
1789 if x[1] in s and x[0] not in s:
1792 q = q.join(SrcAssociation).join(Suite)
1793 q = q.filter(Suite.suite_name.in_(s))
1795 # Reduce the query results to a list of version numbers
1796 ql = [ j.version for j in q.all() ]
1799 if source_version in ql:
1803 from daklib.regexes import re_bin_only_nmu
1804 orig_source_version = re_bin_only_nmu.sub('', source_version)
1805 if orig_source_version in ql:
1808 # No source found so return not ok
1813 __all__.append('source_exists')
1816 def get_suites_source_in(source, session=None):
1818 Returns list of Suite objects which given C{source} name is in
1821 @param source: DBSource package name to search for
1824 @return: list of Suite objects for the given source
1827 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1829 __all__.append('get_suites_source_in')
1832 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1834 Returns list of DBSource objects for given C{source} name and other parameters
1837 @param source: DBSource package name to search for
1839 @type source: str or None
1840 @param source: DBSource version name to search for or None if not applicable
1842 @type dm_upload_allowed: bool
1843 @param dm_upload_allowed: If None, no effect. If True or False, only
1844 return packages with that dm_upload_allowed setting
1846 @type session: Session
1847 @param session: Optional SQL session object (a temporary one will be
1848 generated if not supplied)
1851 @return: list of DBSource objects for the given name (may be empty)
1854 q = session.query(DBSource).filter_by(source=source)
1856 if version is not None:
1857 q = q.filter_by(version=version)
1859 if dm_upload_allowed is not None:
1860 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1864 __all__.append('get_sources_from_name')
1867 def get_source_in_suite(source, suite, session=None):
1869 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1871 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1872 - B{suite} - a suite name, eg. I{unstable}
1874 @type source: string
1875 @param source: source package name
1878 @param suite: the suite name
1881 @return: the version for I{source} in I{suite}
1885 q = session.query(SrcAssociation)
1886 q = q.join('source').filter_by(source=source)
1887 q = q.join('suite').filter_by(suite_name=suite)
1890 return q.one().source
1891 except NoResultFound:
1894 __all__.append('get_source_in_suite')
1896 ################################################################################
1898 class SourceACL(object):
1899 def __init__(self, *args, **kwargs):
1903 return '<SourceACL %s>' % self.source_acl_id
1905 __all__.append('SourceACL')
1907 ################################################################################
1909 class SrcAssociation(object):
1910 def __init__(self, *args, **kwargs):
1914 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1916 __all__.append('SrcAssociation')
1918 ################################################################################
1920 class SrcFormat(object):
1921 def __init__(self, *args, **kwargs):
1925 return '<SrcFormat %s>' % (self.format_name)
1927 __all__.append('SrcFormat')
1929 ################################################################################
1931 class SrcUploader(object):
1932 def __init__(self, *args, **kwargs):
1936 return '<SrcUploader %s>' % self.uploader_id
1938 __all__.append('SrcUploader')
1940 ################################################################################
1942 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1943 ('SuiteID', 'suite_id'),
1944 ('Version', 'version'),
1945 ('Origin', 'origin'),
1947 ('Description', 'description'),
1948 ('Untouchable', 'untouchable'),
1949 ('Announce', 'announce'),
1950 ('Codename', 'codename'),
1951 ('OverrideCodename', 'overridecodename'),
1952 ('ValidTime', 'validtime'),
1953 ('Priority', 'priority'),
1954 ('NotAutomatic', 'notautomatic'),
1955 ('CopyChanges', 'copychanges'),
1956 ('CopyDotDak', 'copydotdak'),
1957 ('CommentsDir', 'commentsdir'),
1958 ('OverrideSuite', 'overridesuite'),
1959 ('ChangelogBase', 'changelogbase')]
1962 class Suite(object):
1963 def __init__(self, *args, **kwargs):
1967 return '<Suite %s>' % self.suite_name
1969 def __eq__(self, val):
1970 if isinstance(val, str):
1971 return (self.suite_name == val)
1972 # This signals to use the normal comparison operator
1973 return NotImplemented
1975 def __ne__(self, val):
1976 if isinstance(val, str):
1977 return (self.suite_name != val)
1978 # This signals to use the normal comparison operator
1979 return NotImplemented
1983 for disp, field in SUITE_FIELDS:
1984 val = getattr(self, field, None)
1986 ret.append("%s: %s" % (disp, val))
1988 return "\n".join(ret)
1990 __all__.append('Suite')
1993 def get_suite_architecture(suite, architecture, session=None):
1995 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1999 @param suite: Suite name to search for
2001 @type architecture: str
2002 @param architecture: Architecture name to search for
2004 @type session: Session
2005 @param session: Optional SQL session object (a temporary one will be
2006 generated if not supplied)
2008 @rtype: SuiteArchitecture
2009 @return: the SuiteArchitecture object or None
2012 q = session.query(SuiteArchitecture)
2013 q = q.join(Architecture).filter_by(arch_string=architecture)
2014 q = q.join(Suite).filter_by(suite_name=suite)
2018 except NoResultFound:
2021 __all__.append('get_suite_architecture')
2024 def get_suite(suite, session=None):
2026 Returns Suite object for given C{suite name}.
2029 @param suite: The name of the suite
2031 @type session: Session
2032 @param session: Optional SQLA session object (a temporary one will be
2033 generated if not supplied)
2036 @return: Suite object for the requested suite name (None if not present)
2039 q = session.query(Suite).filter_by(suite_name=suite)
2043 except NoResultFound:
2046 __all__.append('get_suite')
2048 ################################################################################
2050 class SuiteArchitecture(object):
2051 def __init__(self, *args, **kwargs):
2055 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2057 __all__.append('SuiteArchitecture')
2060 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2062 Returns list of Architecture objects for given C{suite} name
2065 @param source: Suite name to search for
2067 @type skipsrc: boolean
2068 @param skipsrc: Whether to skip returning the 'source' architecture entry
2071 @type skipall: boolean
2072 @param skipall: Whether to skip returning the 'all' architecture entry
2075 @type session: Session
2076 @param session: Optional SQL session object (a temporary one will be
2077 generated if not supplied)
2080 @return: list of Architecture objects for the given name (may be empty)
2083 q = session.query(Architecture)
2084 q = q.join(SuiteArchitecture)
2085 q = q.join(Suite).filter_by(suite_name=suite)
2088 q = q.filter(Architecture.arch_string != 'source')
2091 q = q.filter(Architecture.arch_string != 'all')
2093 q = q.order_by('arch_string')
2097 __all__.append('get_suite_architectures')
2099 ################################################################################
2101 class SuiteSrcFormat(object):
2102 def __init__(self, *args, **kwargs):
2106 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2108 __all__.append('SuiteSrcFormat')
2111 def get_suite_src_formats(suite, session=None):
2113 Returns list of allowed SrcFormat for C{suite}.
2116 @param suite: Suite name to search for
2118 @type session: Session
2119 @param session: Optional SQL session object (a temporary one will be
2120 generated if not supplied)
2123 @return: the list of allowed source formats for I{suite}
2126 q = session.query(SrcFormat)
2127 q = q.join(SuiteSrcFormat)
2128 q = q.join(Suite).filter_by(suite_name=suite)
2129 q = q.order_by('format_name')
2133 __all__.append('get_suite_src_formats')
2135 ################################################################################
2138 def __init__(self, *args, **kwargs):
2141 def __eq__(self, val):
2142 if isinstance(val, str):
2143 return (self.uid == val)
2144 # This signals to use the normal comparison operator
2145 return NotImplemented
2147 def __ne__(self, val):
2148 if isinstance(val, str):
2149 return (self.uid != val)
2150 # This signals to use the normal comparison operator
2151 return NotImplemented
2154 return '<Uid %s (%s)>' % (self.uid, self.name)
2156 __all__.append('Uid')
2159 def add_database_user(uidname, session=None):
2161 Adds a database user
2163 @type uidname: string
2164 @param uidname: The uid of the user to add
2166 @type session: SQLAlchemy
2167 @param session: Optional SQL session object (a temporary one will be
2168 generated if not supplied). If not passed, a commit will be performed at
2169 the end of the function, otherwise the caller is responsible for commiting.
2172 @return: the uid object for the given uidname
2175 session.execute("CREATE USER :uid", {'uid': uidname})
2176 session.commit_or_flush()
2178 __all__.append('add_database_user')
2181 def get_or_set_uid(uidname, session=None):
2183 Returns uid object for given uidname.
2185 If no matching uidname is found, a row is inserted.
2187 @type uidname: string
2188 @param uidname: The uid to add
2190 @type session: SQLAlchemy
2191 @param session: Optional SQL session object (a temporary one will be
2192 generated if not supplied). If not passed, a commit will be performed at
2193 the end of the function, otherwise the caller is responsible for commiting.
2196 @return: the uid object for the given uidname
2199 q = session.query(Uid).filter_by(uid=uidname)
2203 except NoResultFound:
2207 session.commit_or_flush()
2212 __all__.append('get_or_set_uid')
2215 def get_uid_from_fingerprint(fpr, session=None):
2216 q = session.query(Uid)
2217 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2221 except NoResultFound:
2224 __all__.append('get_uid_from_fingerprint')
2226 ################################################################################
2228 class UploadBlock(object):
2229 def __init__(self, *args, **kwargs):
2233 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2235 __all__.append('UploadBlock')
2237 ################################################################################
2239 class DBConn(Singleton):
2241 database module init.
2243 def __init__(self, *args, **kwargs):
2244 super(DBConn, self).__init__(*args, **kwargs)
2246 def _startup(self, *args, **kwargs):
2248 if kwargs.has_key('debug'):
2252 def __setuptables(self):
2253 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2254 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2255 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2256 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2257 self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2258 self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2259 self.tbl_component = Table('component', self.db_meta, autoload=True)
2260 self.tbl_config = Table('config', self.db_meta, autoload=True)
2261 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2262 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2263 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2264 self.tbl_changes_pending_files = Table('changes_pending_files', self.db_meta, autoload=True)
2265 self.tbl_changes_pool_files = Table('changes_pool_files', self.db_meta, autoload=True)
2266 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2267 self.tbl_files = Table('files', self.db_meta, autoload=True)
2268 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2269 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2270 self.tbl_known_changes = Table('known_changes', self.db_meta, autoload=True)
2271 self.tbl_keyring_acl_map = Table('keyring_acl_map', self.db_meta, autoload=True)
2272 self.tbl_location = Table('location', self.db_meta, autoload=True)
2273 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2274 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2275 self.tbl_override = Table('override', self.db_meta, autoload=True)
2276 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2277 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2278 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2279 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2280 self.tbl_queue_files = Table('queue_files', self.db_meta, autoload=True)
2281 self.tbl_section = Table('section', self.db_meta, autoload=True)
2282 self.tbl_source = Table('source', self.db_meta, autoload=True)
2283 self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2284 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2285 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2286 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2287 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2288 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2289 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2290 self.tbl_suite_queue_copy = Table('suite_queue_copy', self.db_meta, autoload=True)
2291 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2292 self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2294 def __setupmappers(self):
2295 mapper(Architecture, self.tbl_architecture,
2296 properties = dict(arch_id = self.tbl_architecture.c.id))
2298 mapper(Archive, self.tbl_archive,
2299 properties = dict(archive_id = self.tbl_archive.c.id,
2300 archive_name = self.tbl_archive.c.name))
2302 mapper(BinAssociation, self.tbl_bin_associations,
2303 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2304 suite_id = self.tbl_bin_associations.c.suite,
2305 suite = relation(Suite),
2306 binary_id = self.tbl_bin_associations.c.bin,
2307 binary = relation(DBBinary)))
2310 mapper(DBBinary, self.tbl_binaries,
2311 properties = dict(binary_id = self.tbl_binaries.c.id,
2312 package = self.tbl_binaries.c.package,
2313 version = self.tbl_binaries.c.version,
2314 maintainer_id = self.tbl_binaries.c.maintainer,
2315 maintainer = relation(Maintainer),
2316 source_id = self.tbl_binaries.c.source,
2317 source = relation(DBSource),
2318 arch_id = self.tbl_binaries.c.architecture,
2319 architecture = relation(Architecture),
2320 poolfile_id = self.tbl_binaries.c.file,
2321 poolfile = relation(PoolFile),
2322 binarytype = self.tbl_binaries.c.type,
2323 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2324 fingerprint = relation(Fingerprint),
2325 install_date = self.tbl_binaries.c.install_date,
2326 binassociations = relation(BinAssociation,
2327 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2329 mapper(BinaryACL, self.tbl_binary_acl,
2330 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2332 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2333 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2334 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2335 architecture = relation(Architecture)))
2337 mapper(Component, self.tbl_component,
2338 properties = dict(component_id = self.tbl_component.c.id,
2339 component_name = self.tbl_component.c.name))
2341 mapper(DBConfig, self.tbl_config,
2342 properties = dict(config_id = self.tbl_config.c.id))
2344 mapper(DSCFile, self.tbl_dsc_files,
2345 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2346 source_id = self.tbl_dsc_files.c.source,
2347 source = relation(DBSource),
2348 poolfile_id = self.tbl_dsc_files.c.file,
2349 poolfile = relation(PoolFile)))
2351 mapper(PoolFile, self.tbl_files,
2352 properties = dict(file_id = self.tbl_files.c.id,
2353 filesize = self.tbl_files.c.size,
2354 location_id = self.tbl_files.c.location,
2355 location = relation(Location)))
2357 mapper(Fingerprint, self.tbl_fingerprint,
2358 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2359 uid_id = self.tbl_fingerprint.c.uid,
2360 uid = relation(Uid),
2361 keyring_id = self.tbl_fingerprint.c.keyring,
2362 keyring = relation(Keyring),
2363 source_acl = relation(SourceACL),
2364 binary_acl = relation(BinaryACL)))
2366 mapper(Keyring, self.tbl_keyrings,
2367 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2368 keyring_id = self.tbl_keyrings.c.id))
2370 mapper(KnownChange, self.tbl_known_changes,
2371 properties = dict(known_change_id = self.tbl_known_changes.c.id,
2372 poolfiles = relation(PoolFile,
2373 secondary=self.tbl_changes_pool_files,
2374 backref="changeslinks"),
2375 files = relation(KnownChangePendingFile, backref="changesfile")))
2377 mapper(KnownChangePendingFile, self.tbl_changes_pending_files,
2378 properties = dict(known_change_pending_file_id = self.tbl_changes_pending_files.id))
2380 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2381 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2382 keyring = relation(Keyring, backref="keyring_acl_map"),
2383 architecture = relation(Architecture)))
2385 mapper(Location, self.tbl_location,
2386 properties = dict(location_id = self.tbl_location.c.id,
2387 component_id = self.tbl_location.c.component,
2388 component = relation(Component),
2389 archive_id = self.tbl_location.c.archive,
2390 archive = relation(Archive),
2391 archive_type = self.tbl_location.c.type))
2393 mapper(Maintainer, self.tbl_maintainer,
2394 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2396 mapper(NewComment, self.tbl_new_comments,
2397 properties = dict(comment_id = self.tbl_new_comments.c.id))
2399 mapper(Override, self.tbl_override,
2400 properties = dict(suite_id = self.tbl_override.c.suite,
2401 suite = relation(Suite),
2402 component_id = self.tbl_override.c.component,
2403 component = relation(Component),
2404 priority_id = self.tbl_override.c.priority,
2405 priority = relation(Priority),
2406 section_id = self.tbl_override.c.section,
2407 section = relation(Section),
2408 overridetype_id = self.tbl_override.c.type,
2409 overridetype = relation(OverrideType)))
2411 mapper(OverrideType, self.tbl_override_type,
2412 properties = dict(overridetype = self.tbl_override_type.c.type,
2413 overridetype_id = self.tbl_override_type.c.id))
2415 mapper(Priority, self.tbl_priority,
2416 properties = dict(priority_id = self.tbl_priority.c.id))
2418 mapper(Queue, self.tbl_queue,
2419 properties = dict(queue_id = self.tbl_queue.c.id))
2421 mapper(QueueFile, self.tbl_queue_files,
2422 properties = dict(queue = relation(Queue, backref='queuefiles'),
2423 poolfile = relation(PoolFile, backref='queueinstances')))
2425 mapper(Section, self.tbl_section,
2426 properties = dict(section_id = self.tbl_section.c.id))
2428 mapper(DBSource, self.tbl_source,
2429 properties = dict(source_id = self.tbl_source.c.id,
2430 version = self.tbl_source.c.version,
2431 maintainer_id = self.tbl_source.c.maintainer,
2432 maintainer = relation(Maintainer,
2433 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2434 poolfile_id = self.tbl_source.c.file,
2435 poolfile = relation(PoolFile),
2436 fingerprint_id = self.tbl_source.c.sig_fpr,
2437 fingerprint = relation(Fingerprint),
2438 changedby_id = self.tbl_source.c.changedby,
2439 changedby = relation(Maintainer,
2440 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2441 srcfiles = relation(DSCFile,
2442 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2443 srcassociations = relation(SrcAssociation,
2444 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2445 srcuploaders = relation(SrcUploader)))
2447 mapper(SourceACL, self.tbl_source_acl,
2448 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2450 mapper(SrcAssociation, self.tbl_src_associations,
2451 properties = dict(sa_id = self.tbl_src_associations.c.id,
2452 suite_id = self.tbl_src_associations.c.suite,
2453 suite = relation(Suite),
2454 source_id = self.tbl_src_associations.c.source,
2455 source = relation(DBSource)))
2457 mapper(SrcFormat, self.tbl_src_format,
2458 properties = dict(src_format_id = self.tbl_src_format.c.id,
2459 format_name = self.tbl_src_format.c.format_name))
2461 mapper(SrcUploader, self.tbl_src_uploaders,
2462 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2463 source_id = self.tbl_src_uploaders.c.source,
2464 source = relation(DBSource,
2465 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2466 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2467 maintainer = relation(Maintainer,
2468 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2470 mapper(Suite, self.tbl_suite,
2471 properties = dict(suite_id = self.tbl_suite.c.id,
2472 policy_queue = relation(Queue),
2473 copy_queues = relation(Queue, secondary=self.tbl_suite_queue_copy)))
2475 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2476 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2477 suite = relation(Suite, backref='suitearchitectures'),
2478 arch_id = self.tbl_suite_architectures.c.architecture,
2479 architecture = relation(Architecture)))
2481 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2482 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2483 suite = relation(Suite, backref='suitesrcformats'),
2484 src_format_id = self.tbl_suite_src_formats.c.src_format,
2485 src_format = relation(SrcFormat)))
2487 mapper(Uid, self.tbl_uid,
2488 properties = dict(uid_id = self.tbl_uid.c.id,
2489 fingerprint = relation(Fingerprint)))
2491 mapper(UploadBlock, self.tbl_upload_blocks,
2492 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2493 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2494 uid = relation(Uid, backref="uploadblocks")))
2496 ## Connection functions
2497 def __createconn(self):
2498 from config import Config
2502 connstr = "postgres://%s" % cnf["DB::Host"]
2503 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2504 connstr += ":%s" % cnf["DB::Port"]
2505 connstr += "/%s" % cnf["DB::Name"]
2508 connstr = "postgres:///%s" % cnf["DB::Name"]
2509 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2510 connstr += "?port=%s" % cnf["DB::Port"]
2512 self.db_pg = create_engine(connstr, echo=self.debug)
2513 self.db_meta = MetaData()
2514 self.db_meta.bind = self.db_pg
2515 self.db_smaker = sessionmaker(bind=self.db_pg,
2519 self.__setuptables()
2520 self.__setupmappers()
2523 return self.db_smaker()
2525 __all__.append('DBConn')