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, 2010 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 ################################################################################
41 from datetime import datetime, timedelta
42 from errno import ENOENT
43 from tempfile import mkstemp, mkdtemp
45 from inspect import getargspec
48 from sqlalchemy import create_engine, Table, MetaData, Column, Integer
49 from sqlalchemy.orm import sessionmaker, mapper, relation
50 from sqlalchemy import types as sqltypes
52 # Don't remove this, we re-export the exceptions to scripts which import us
53 from sqlalchemy.exc import *
54 from sqlalchemy.orm.exc import NoResultFound
56 # Only import Config until Queue stuff is changed to store its config
58 from config import Config
59 from textutils import fix_maintainer
60 from dak_exceptions import NoSourceFieldError
62 ################################################################################
64 # Patch in support for the debversion field type so that it works during
68 # that is for sqlalchemy 0.6
69 UserDefinedType = sqltypes.UserDefinedType
71 # this one for sqlalchemy 0.5
72 UserDefinedType = sqltypes.TypeEngine
74 class DebVersion(UserDefinedType):
75 def get_col_spec(self):
78 def bind_processor(self, dialect):
81 def result_processor(self, dialect):
84 sa_major_version = sqlalchemy.__version__[0:3]
85 if sa_major_version in ["0.5", "0.6"]:
86 from sqlalchemy.databases import postgres
87 postgres.ischema_names['debversion'] = DebVersion
89 raise Exception("dak only ported to SQLA versions 0.5 and 0.6. See daklib/dbconn.py")
91 ################################################################################
93 __all__ = ['IntegrityError', 'SQLAlchemyError', 'DebVersion']
95 ################################################################################
97 def session_wrapper(fn):
99 Wrapper around common ".., session=None):" handling. If the wrapped
100 function is called without passing 'session', we create a local one
101 and destroy it when the function ends.
103 Also attaches a commit_or_flush method to the session; if we created a
104 local session, this is a synonym for session.commit(), otherwise it is a
105 synonym for session.flush().
108 def wrapped(*args, **kwargs):
109 private_transaction = False
111 # Find the session object
112 session = kwargs.get('session')
115 if len(args) <= len(getargspec(fn)[0]) - 1:
116 # No session specified as last argument or in kwargs
117 private_transaction = True
118 session = kwargs['session'] = DBConn().session()
120 # Session is last argument in args
124 session = args[-1] = DBConn().session()
125 private_transaction = True
127 if private_transaction:
128 session.commit_or_flush = session.commit
130 session.commit_or_flush = session.flush
133 return fn(*args, **kwargs)
135 if private_transaction:
136 # We created a session; close it.
139 wrapped.__doc__ = fn.__doc__
140 wrapped.func_name = fn.func_name
144 __all__.append('session_wrapper')
146 ################################################################################
148 class Architecture(object):
149 def __init__(self, *args, **kwargs):
152 def __eq__(self, val):
153 if isinstance(val, str):
154 return (self.arch_string== val)
155 # This signals to use the normal comparison operator
156 return NotImplemented
158 def __ne__(self, val):
159 if isinstance(val, str):
160 return (self.arch_string != val)
161 # This signals to use the normal comparison operator
162 return NotImplemented
165 return '<Architecture %s>' % self.arch_string
167 __all__.append('Architecture')
170 def get_architecture(architecture, session=None):
172 Returns database id for given C{architecture}.
174 @type architecture: string
175 @param architecture: The name of the architecture
177 @type session: Session
178 @param session: Optional SQLA session object (a temporary one will be
179 generated if not supplied)
182 @return: Architecture object for the given arch (None if not present)
185 q = session.query(Architecture).filter_by(arch_string=architecture)
189 except NoResultFound:
192 __all__.append('get_architecture')
195 def get_architecture_suites(architecture, session=None):
197 Returns list of Suite objects for given C{architecture} name
199 @type architecture: str
200 @param architecture: Architecture name to search for
202 @type session: Session
203 @param session: Optional SQL session object (a temporary one will be
204 generated if not supplied)
207 @return: list of Suite objects for the given name (may be empty)
210 q = session.query(Suite)
211 q = q.join(SuiteArchitecture)
212 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
218 __all__.append('get_architecture_suites')
220 ################################################################################
222 class Archive(object):
223 def __init__(self, *args, **kwargs):
227 return '<Archive %s>' % self.archive_name
229 __all__.append('Archive')
232 def get_archive(archive, session=None):
234 returns database id for given C{archive}.
236 @type archive: string
237 @param archive: the name of the arhive
239 @type session: Session
240 @param session: Optional SQLA session object (a temporary one will be
241 generated if not supplied)
244 @return: Archive object for the given name (None if not present)
247 archive = archive.lower()
249 q = session.query(Archive).filter_by(archive_name=archive)
253 except NoResultFound:
256 __all__.append('get_archive')
258 ################################################################################
260 class BinAssociation(object):
261 def __init__(self, *args, **kwargs):
265 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
267 __all__.append('BinAssociation')
269 ################################################################################
271 class BinContents(object):
272 def __init__(self, *args, **kwargs):
276 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
278 __all__.append('BinContents')
280 ################################################################################
282 class DBBinary(object):
283 def __init__(self, *args, **kwargs):
287 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
289 __all__.append('DBBinary')
292 def get_suites_binary_in(package, session=None):
294 Returns list of Suite objects which given C{package} name is in
297 @param package: DBBinary package name to search for
300 @return: list of Suite objects for the given package
303 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
305 __all__.append('get_suites_binary_in')
308 def get_binary_from_id(binary_id, session=None):
310 Returns DBBinary object for given C{id}
313 @param binary_id: Id of the required binary
315 @type session: Session
316 @param session: Optional SQLA session object (a temporary one will be
317 generated if not supplied)
320 @return: DBBinary object for the given binary (None if not present)
323 q = session.query(DBBinary).filter_by(binary_id=binary_id)
327 except NoResultFound:
330 __all__.append('get_binary_from_id')
333 def get_binaries_from_name(package, version=None, architecture=None, session=None):
335 Returns list of DBBinary objects for given C{package} name
338 @param package: DBBinary package name to search for
340 @type version: str or None
341 @param version: Version to search for (or None)
343 @type architecture: str, list or None
344 @param architecture: Architectures to limit to (or None if no limit)
346 @type session: Session
347 @param session: Optional SQL session object (a temporary one will be
348 generated if not supplied)
351 @return: list of DBBinary objects for the given name (may be empty)
354 q = session.query(DBBinary).filter_by(package=package)
356 if version is not None:
357 q = q.filter_by(version=version)
359 if architecture is not None:
360 if not isinstance(architecture, list):
361 architecture = [architecture]
362 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
368 __all__.append('get_binaries_from_name')
371 def get_binaries_from_source_id(source_id, session=None):
373 Returns list of DBBinary objects for given C{source_id}
376 @param source_id: source_id to search for
378 @type session: Session
379 @param session: Optional SQL session object (a temporary one will be
380 generated if not supplied)
383 @return: list of DBBinary objects for the given name (may be empty)
386 return session.query(DBBinary).filter_by(source_id=source_id).all()
388 __all__.append('get_binaries_from_source_id')
391 def get_binary_from_name_suite(package, suitename, session=None):
392 ### For dak examine-package
393 ### XXX: Doesn't use object API yet
395 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
396 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
397 WHERE b.package='%(package)s'
399 AND fi.location = l.id
400 AND l.component = c.id
403 AND su.suite_name %(suitename)s
404 ORDER BY b.version DESC"""
406 return session.execute(sql % {'package': package, 'suitename': suitename})
408 __all__.append('get_binary_from_name_suite')
411 def get_binary_components(package, suitename, arch, session=None):
412 # Check for packages that have moved from one component to another
413 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
414 WHERE b.package=:package AND s.suite_name=:suitename
415 AND (a.arch_string = :arch OR a.arch_string = 'all')
416 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
417 AND f.location = l.id
418 AND l.component = c.id
421 vals = {'package': package, 'suitename': suitename, 'arch': arch}
423 return session.execute(query, vals)
425 __all__.append('get_binary_components')
427 ################################################################################
429 class BinaryACL(object):
430 def __init__(self, *args, **kwargs):
434 return '<BinaryACL %s>' % self.binary_acl_id
436 __all__.append('BinaryACL')
438 ################################################################################
440 class BinaryACLMap(object):
441 def __init__(self, *args, **kwargs):
445 return '<BinaryACLMap %s>' % self.binary_acl_map_id
447 __all__.append('BinaryACLMap')
449 ################################################################################
454 ArchiveDir "%(archivepath)s";
455 OverrideDir "%(overridedir)s";
456 CacheDir "%(cachedir)s";
461 Packages::Compress ". bzip2 gzip";
462 Sources::Compress ". bzip2 gzip";
467 bindirectory "incoming"
472 BinOverride "override.sid.all3";
473 BinCacheDB "packages-accepted.db";
475 FileList "%(filelist)s";
478 Packages::Extensions ".deb .udeb";
481 bindirectory "incoming/"
484 BinOverride "override.sid.all3";
485 SrcOverride "override.sid.all3.src";
486 FileList "%(filelist)s";
490 class BuildQueue(object):
491 def __init__(self, *args, **kwargs):
495 return '<BuildQueue %s>' % self.queue_name
497 def write_metadata(self, starttime, force=False):
498 # Do we write out metafiles?
499 if not (force or self.generate_metadata):
502 session = DBConn().session().object_session(self)
504 fl_fd = fl_name = ac_fd = ac_name = None
506 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
507 startdir = os.getcwd()
510 # Grab files we want to include
511 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
512 # Write file list with newer files
513 (fl_fd, fl_name) = mkstemp()
515 os.write(fl_fd, '%s\n' % n.fullpath)
520 # Write minimal apt.conf
521 # TODO: Remove hardcoding from template
522 (ac_fd, ac_name) = mkstemp()
523 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
525 'cachedir': cnf["Dir::Cache"],
526 'overridedir': cnf["Dir::Override"],
530 # Run apt-ftparchive generate
531 os.chdir(os.path.dirname(ac_name))
532 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
534 # Run apt-ftparchive release
535 # TODO: Eww - fix this
536 bname = os.path.basename(self.path)
540 # We have to remove the Release file otherwise it'll be included in the
543 os.unlink(os.path.join(bname, 'Release'))
547 os.system("""apt-ftparchive -qq -o APT::FTPArchive::Release::Origin="%s" -o APT::FTPArchive::Release::Label="%s" -o APT::FTPArchive::Release::Description="%s" -o APT::FTPArchive::Release::Architectures="%s" release %s > Release""" % (self.origin, self.label, self.releasedescription, arches, bname))
549 # Crude hack with open and append, but this whole section is and should be redone.
550 if self.notautomatic:
551 release=open("Release", "a")
552 release.write("NotAutomatic: yes")
557 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
558 if cnf.has_key("Dinstall::SigningPubKeyring"):
559 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
561 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
563 # Move the files if we got this far
564 os.rename('Release', os.path.join(bname, 'Release'))
566 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
568 # Clean up any left behind files
595 def clean_and_update(self, starttime, Logger, dryrun=False):
596 """WARNING: This routine commits for you"""
597 session = DBConn().session().object_session(self)
599 if self.generate_metadata and not dryrun:
600 self.write_metadata(starttime)
602 # Grab files older than our execution time
603 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
609 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
611 Logger.log(["I: Removing %s from the queue" % o.fullpath])
612 os.unlink(o.fullpath)
615 # If it wasn't there, don't worry
616 if e.errno == ENOENT:
619 # TODO: Replace with proper logging call
620 Logger.log(["E: Could not remove %s" % o.fullpath])
627 for f in os.listdir(self.path):
628 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release') or f.startswith('advisory'):
632 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
633 except NoResultFound:
634 fp = os.path.join(self.path, f)
636 Logger.log(["I: Would remove unused link %s" % fp])
638 Logger.log(["I: Removing unused link %s" % fp])
642 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
644 def add_file_from_pool(self, poolfile):
645 """Copies a file into the pool. Assumes that the PoolFile object is
646 attached to the same SQLAlchemy session as the Queue object is.
648 The caller is responsible for committing after calling this function."""
649 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
651 # Check if we have a file of this name or this ID already
652 for f in self.queuefiles:
653 if f.fileid is not None and f.fileid == poolfile.file_id or \
654 f.poolfile.filename == poolfile_basename:
655 # In this case, update the BuildQueueFile entry so we
656 # don't remove it too early
657 f.lastused = datetime.now()
658 DBConn().session().object_session(poolfile).add(f)
661 # Prepare BuildQueueFile object
662 qf = BuildQueueFile()
663 qf.build_queue_id = self.queue_id
664 qf.lastused = datetime.now()
665 qf.filename = poolfile_basename
667 targetpath = poolfile.fullpath
668 queuepath = os.path.join(self.path, poolfile_basename)
672 # We need to copy instead of symlink
674 utils.copy(targetpath, queuepath)
675 # NULL in the fileid field implies a copy
678 os.symlink(targetpath, queuepath)
679 qf.fileid = poolfile.file_id
683 # Get the same session as the PoolFile is using and add the qf to it
684 DBConn().session().object_session(poolfile).add(qf)
689 __all__.append('BuildQueue')
692 def get_build_queue(queuename, session=None):
694 Returns BuildQueue object for given C{queue name}, creating it if it does not
697 @type queuename: string
698 @param queuename: The name of the queue
700 @type session: Session
701 @param session: Optional SQLA session object (a temporary one will be
702 generated if not supplied)
705 @return: BuildQueue object for the given queue
708 q = session.query(BuildQueue).filter_by(queue_name=queuename)
712 except NoResultFound:
715 __all__.append('get_build_queue')
717 ################################################################################
719 class BuildQueueFile(object):
720 def __init__(self, *args, **kwargs):
724 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
728 return os.path.join(self.buildqueue.path, self.filename)
731 __all__.append('BuildQueueFile')
733 ################################################################################
735 class ChangePendingBinary(object):
736 def __init__(self, *args, **kwargs):
740 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
742 __all__.append('ChangePendingBinary')
744 ################################################################################
746 class ChangePendingFile(object):
747 def __init__(self, *args, **kwargs):
751 return '<ChangePendingFile %s>' % self.change_pending_file_id
753 __all__.append('ChangePendingFile')
755 ################################################################################
757 class ChangePendingSource(object):
758 def __init__(self, *args, **kwargs):
762 return '<ChangePendingSource %s>' % self.change_pending_source_id
764 __all__.append('ChangePendingSource')
766 ################################################################################
768 class Component(object):
769 def __init__(self, *args, **kwargs):
772 def __eq__(self, val):
773 if isinstance(val, str):
774 return (self.component_name == val)
775 # This signals to use the normal comparison operator
776 return NotImplemented
778 def __ne__(self, val):
779 if isinstance(val, str):
780 return (self.component_name != val)
781 # This signals to use the normal comparison operator
782 return NotImplemented
785 return '<Component %s>' % self.component_name
788 __all__.append('Component')
791 def get_component(component, session=None):
793 Returns database id for given C{component}.
795 @type component: string
796 @param component: The name of the override type
799 @return: the database id for the given component
802 component = component.lower()
804 q = session.query(Component).filter_by(component_name=component)
808 except NoResultFound:
811 __all__.append('get_component')
813 ################################################################################
815 class DBConfig(object):
816 def __init__(self, *args, **kwargs):
820 return '<DBConfig %s>' % self.name
822 __all__.append('DBConfig')
824 ################################################################################
827 def get_or_set_contents_file_id(filename, session=None):
829 Returns database id for given filename.
831 If no matching file is found, a row is inserted.
833 @type filename: string
834 @param filename: The filename
835 @type session: SQLAlchemy
836 @param session: Optional SQL session object (a temporary one will be
837 generated if not supplied). If not passed, a commit will be performed at
838 the end of the function, otherwise the caller is responsible for commiting.
841 @return: the database id for the given component
844 q = session.query(ContentFilename).filter_by(filename=filename)
847 ret = q.one().cafilename_id
848 except NoResultFound:
849 cf = ContentFilename()
850 cf.filename = filename
852 session.commit_or_flush()
853 ret = cf.cafilename_id
857 __all__.append('get_or_set_contents_file_id')
860 def get_contents(suite, overridetype, section=None, session=None):
862 Returns contents for a suite / overridetype combination, limiting
863 to a section if not None.
866 @param suite: Suite object
868 @type overridetype: OverrideType
869 @param overridetype: OverrideType object
871 @type section: Section
872 @param section: Optional section object to limit results to
874 @type session: SQLAlchemy
875 @param session: Optional SQL session object (a temporary one will be
876 generated if not supplied)
879 @return: ResultsProxy object set up to return tuples of (filename, section,
883 # find me all of the contents for a given suite
884 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
888 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
889 JOIN content_file_names n ON (c.filename=n.id)
890 JOIN binaries b ON (b.id=c.binary_pkg)
891 JOIN override o ON (o.package=b.package)
892 JOIN section s ON (s.id=o.section)
893 WHERE o.suite = :suiteid AND o.type = :overridetypeid
894 AND b.type=:overridetypename"""
896 vals = {'suiteid': suite.suite_id,
897 'overridetypeid': overridetype.overridetype_id,
898 'overridetypename': overridetype.overridetype}
900 if section is not None:
901 contents_q += " AND s.id = :sectionid"
902 vals['sectionid'] = section.section_id
904 contents_q += " ORDER BY fn"
906 return session.execute(contents_q, vals)
908 __all__.append('get_contents')
910 ################################################################################
912 class ContentFilepath(object):
913 def __init__(self, *args, **kwargs):
917 return '<ContentFilepath %s>' % self.filepath
919 __all__.append('ContentFilepath')
922 def get_or_set_contents_path_id(filepath, session=None):
924 Returns database id for given path.
926 If no matching file is found, a row is inserted.
928 @type filepath: string
929 @param filepath: The filepath
931 @type session: SQLAlchemy
932 @param session: Optional SQL session object (a temporary one will be
933 generated if not supplied). If not passed, a commit will be performed at
934 the end of the function, otherwise the caller is responsible for commiting.
937 @return: the database id for the given path
940 q = session.query(ContentFilepath).filter_by(filepath=filepath)
943 ret = q.one().cafilepath_id
944 except NoResultFound:
945 cf = ContentFilepath()
946 cf.filepath = filepath
948 session.commit_or_flush()
949 ret = cf.cafilepath_id
953 __all__.append('get_or_set_contents_path_id')
955 ################################################################################
957 class ContentAssociation(object):
958 def __init__(self, *args, **kwargs):
962 return '<ContentAssociation %s>' % self.ca_id
964 __all__.append('ContentAssociation')
966 def insert_content_paths(binary_id, fullpaths, session=None):
968 Make sure given path is associated with given binary id
971 @param binary_id: the id of the binary
972 @type fullpaths: list
973 @param fullpaths: the list of paths of the file being associated with the binary
974 @type session: SQLAlchemy session
975 @param session: Optional SQLAlchemy session. If this is passed, the caller
976 is responsible for ensuring a transaction has begun and committing the
977 results or rolling back based on the result code. If not passed, a commit
978 will be performed at the end of the function, otherwise the caller is
979 responsible for commiting.
981 @return: True upon success
986 session = DBConn().session()
991 def generate_path_dicts():
992 for fullpath in fullpaths:
993 if fullpath.startswith( './' ):
994 fullpath = fullpath[2:]
996 yield {'filename':fullpath, 'id': binary_id }
998 for d in generate_path_dicts():
999 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
1008 traceback.print_exc()
1010 # Only rollback if we set up the session ourself
1017 __all__.append('insert_content_paths')
1019 ################################################################################
1021 class DSCFile(object):
1022 def __init__(self, *args, **kwargs):
1026 return '<DSCFile %s>' % self.dscfile_id
1028 __all__.append('DSCFile')
1031 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1033 Returns a list of DSCFiles which may be empty
1035 @type dscfile_id: int (optional)
1036 @param dscfile_id: the dscfile_id of the DSCFiles to find
1038 @type source_id: int (optional)
1039 @param source_id: the source id related to the DSCFiles to find
1041 @type poolfile_id: int (optional)
1042 @param poolfile_id: the poolfile id related to the DSCFiles to find
1045 @return: Possibly empty list of DSCFiles
1048 q = session.query(DSCFile)
1050 if dscfile_id is not None:
1051 q = q.filter_by(dscfile_id=dscfile_id)
1053 if source_id is not None:
1054 q = q.filter_by(source_id=source_id)
1056 if poolfile_id is not None:
1057 q = q.filter_by(poolfile_id=poolfile_id)
1061 __all__.append('get_dscfiles')
1063 ################################################################################
1065 class PoolFile(object):
1066 def __init__(self, *args, **kwargs):
1070 return '<PoolFile %s>' % self.filename
1074 return os.path.join(self.location.path, self.filename)
1076 __all__.append('PoolFile')
1079 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1082 (ValidFileFound [boolean or None], PoolFile object or None)
1084 @type filename: string
1085 @param filename: the filename of the file to check against the DB
1088 @param filesize: the size of the file to check against the DB
1090 @type md5sum: string
1091 @param md5sum: the md5sum of the file to check against the DB
1093 @type location_id: int
1094 @param location_id: the id of the location to look in
1097 @return: Tuple of length 2.
1098 - If more than one file found with that name: (C{None}, C{None})
1099 - If valid pool file found: (C{True}, C{PoolFile object})
1100 - If valid pool file not found:
1101 - (C{False}, C{None}) if no file found
1102 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1105 q = session.query(PoolFile).filter_by(filename=filename)
1106 q = q.join(Location).filter_by(location_id=location_id)
1116 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1124 __all__.append('check_poolfile')
1127 def get_poolfile_by_id(file_id, session=None):
1129 Returns a PoolFile objects or None for the given id
1132 @param file_id: the id of the file to look for
1134 @rtype: PoolFile or None
1135 @return: either the PoolFile object or None
1138 q = session.query(PoolFile).filter_by(file_id=file_id)
1142 except NoResultFound:
1145 __all__.append('get_poolfile_by_id')
1149 def get_poolfile_by_name(filename, location_id=None, session=None):
1151 Returns an array of PoolFile objects for the given filename and
1152 (optionally) location_id
1154 @type filename: string
1155 @param filename: the filename of the file to check against the DB
1157 @type location_id: int
1158 @param location_id: the id of the location to look in (optional)
1161 @return: array of PoolFile objects
1164 q = session.query(PoolFile).filter_by(filename=filename)
1166 if location_id is not None:
1167 q = q.join(Location).filter_by(location_id=location_id)
1171 __all__.append('get_poolfile_by_name')
1174 def get_poolfile_like_name(filename, session=None):
1176 Returns an array of PoolFile objects which are like the given name
1178 @type filename: string
1179 @param filename: the filename of the file to check against the DB
1182 @return: array of PoolFile objects
1185 # TODO: There must be a way of properly using bind parameters with %FOO%
1186 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1190 __all__.append('get_poolfile_like_name')
1193 def add_poolfile(filename, datadict, location_id, session=None):
1195 Add a new file to the pool
1197 @type filename: string
1198 @param filename: filename
1200 @type datadict: dict
1201 @param datadict: dict with needed data
1203 @type location_id: int
1204 @param location_id: database id of the location
1207 @return: the PoolFile object created
1209 poolfile = PoolFile()
1210 poolfile.filename = filename
1211 poolfile.filesize = datadict["size"]
1212 poolfile.md5sum = datadict["md5sum"]
1213 poolfile.sha1sum = datadict["sha1sum"]
1214 poolfile.sha256sum = datadict["sha256sum"]
1215 poolfile.location_id = location_id
1217 session.add(poolfile)
1218 # Flush to get a file id (NB: This is not a commit)
1223 __all__.append('add_poolfile')
1225 ################################################################################
1227 class Fingerprint(object):
1228 def __init__(self, *args, **kwargs):
1232 return '<Fingerprint %s>' % self.fingerprint
1234 __all__.append('Fingerprint')
1237 def get_fingerprint(fpr, session=None):
1239 Returns Fingerprint object for given fpr.
1242 @param fpr: The fpr to find / add
1244 @type session: SQLAlchemy
1245 @param session: Optional SQL session object (a temporary one will be
1246 generated if not supplied).
1249 @return: the Fingerprint object for the given fpr or None
1252 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1256 except NoResultFound:
1261 __all__.append('get_fingerprint')
1264 def get_or_set_fingerprint(fpr, session=None):
1266 Returns Fingerprint object for given fpr.
1268 If no matching fpr is found, a row is inserted.
1271 @param fpr: The fpr to find / add
1273 @type session: SQLAlchemy
1274 @param session: Optional SQL session object (a temporary one will be
1275 generated if not supplied). If not passed, a commit will be performed at
1276 the end of the function, otherwise the caller is responsible for commiting.
1277 A flush will be performed either way.
1280 @return: the Fingerprint object for the given fpr
1283 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1287 except NoResultFound:
1288 fingerprint = Fingerprint()
1289 fingerprint.fingerprint = fpr
1290 session.add(fingerprint)
1291 session.commit_or_flush()
1296 __all__.append('get_or_set_fingerprint')
1298 ################################################################################
1300 # Helper routine for Keyring class
1301 def get_ldap_name(entry):
1303 for k in ["cn", "mn", "sn"]:
1305 if ret and ret[0] != "" and ret[0] != "-":
1307 return " ".join(name)
1309 ################################################################################
1311 class Keyring(object):
1312 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1313 " --with-colons --fingerprint --fingerprint"
1318 def __init__(self, *args, **kwargs):
1322 return '<Keyring %s>' % self.keyring_name
1324 def de_escape_gpg_str(self, txt):
1325 esclist = re.split(r'(\\x..)', txt)
1326 for x in range(1,len(esclist),2):
1327 esclist[x] = "%c" % (int(esclist[x][2:],16))
1328 return "".join(esclist)
1330 def parse_address(self, uid):
1331 """parses uid and returns a tuple of real name and email address"""
1333 (name, address) = email.Utils.parseaddr(uid)
1334 name = re.sub(r"\s*[(].*[)]", "", name)
1335 name = self.de_escape_gpg_str(name)
1338 return (name, address)
1340 def load_keys(self, keyring):
1341 if not self.keyring_id:
1342 raise Exception('Must be initialized with database information')
1344 k = os.popen(self.gpg_invocation % keyring, "r")
1348 for line in k.xreadlines():
1349 field = line.split(":")
1350 if field[0] == "pub":
1353 (name, addr) = self.parse_address(field[9])
1355 self.keys[key]["email"] = addr
1356 self.keys[key]["name"] = name
1357 self.keys[key]["fingerprints"] = []
1359 elif key and field[0] == "sub" and len(field) >= 12:
1360 signingkey = ("s" in field[11])
1361 elif key and field[0] == "uid":
1362 (name, addr) = self.parse_address(field[9])
1363 if "email" not in self.keys[key] and "@" in addr:
1364 self.keys[key]["email"] = addr
1365 self.keys[key]["name"] = name
1366 elif signingkey and field[0] == "fpr":
1367 self.keys[key]["fingerprints"].append(field[9])
1368 self.fpr_lookup[field[9]] = key
1370 def import_users_from_ldap(self, session):
1374 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1375 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1377 l = ldap.open(LDAPServer)
1378 l.simple_bind_s("","")
1379 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1380 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1381 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1383 ldap_fin_uid_id = {}
1390 uid = entry["uid"][0]
1391 name = get_ldap_name(entry)
1392 fingerprints = entry["keyFingerPrint"]
1394 for f in fingerprints:
1395 key = self.fpr_lookup.get(f, None)
1396 if key not in self.keys:
1398 self.keys[key]["uid"] = uid
1402 keyid = get_or_set_uid(uid, session).uid_id
1403 byuid[keyid] = (uid, name)
1404 byname[uid] = (keyid, name)
1406 return (byname, byuid)
1408 def generate_users_from_keyring(self, format, session):
1412 for x in self.keys.keys():
1413 if "email" not in self.keys[x]:
1415 self.keys[x]["uid"] = format % "invalid-uid"
1417 uid = format % self.keys[x]["email"]
1418 keyid = get_or_set_uid(uid, session).uid_id
1419 byuid[keyid] = (uid, self.keys[x]["name"])
1420 byname[uid] = (keyid, self.keys[x]["name"])
1421 self.keys[x]["uid"] = uid
1424 uid = format % "invalid-uid"
1425 keyid = get_or_set_uid(uid, session).uid_id
1426 byuid[keyid] = (uid, "ungeneratable user id")
1427 byname[uid] = (keyid, "ungeneratable user id")
1429 return (byname, byuid)
1431 __all__.append('Keyring')
1434 def get_keyring(keyring, session=None):
1436 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1437 If C{keyring} already has an entry, simply return the existing Keyring
1439 @type keyring: string
1440 @param keyring: the keyring name
1443 @return: the Keyring object for this keyring
1446 q = session.query(Keyring).filter_by(keyring_name=keyring)
1450 except NoResultFound:
1453 __all__.append('get_keyring')
1455 ################################################################################
1457 class KeyringACLMap(object):
1458 def __init__(self, *args, **kwargs):
1462 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1464 __all__.append('KeyringACLMap')
1466 ################################################################################
1468 class DBChange(object):
1469 def __init__(self, *args, **kwargs):
1473 return '<DBChange %s>' % self.changesname
1475 def clean_from_queue(self):
1476 session = DBConn().session().object_session(self)
1478 # Remove changes_pool_files entries
1481 # Remove changes_pending_files references
1484 # Clear out of queue
1485 self.in_queue = None
1486 self.approved_for_id = None
1488 __all__.append('DBChange')
1491 def get_dbchange(filename, session=None):
1493 returns DBChange object for given C{filename}.
1495 @type filename: string
1496 @param filename: the name of the file
1498 @type session: Session
1499 @param session: Optional SQLA session object (a temporary one will be
1500 generated if not supplied)
1503 @return: DBChange object for the given filename (C{None} if not present)
1506 q = session.query(DBChange).filter_by(changesname=filename)
1510 except NoResultFound:
1513 __all__.append('get_dbchange')
1515 ################################################################################
1517 class Location(object):
1518 def __init__(self, *args, **kwargs):
1522 return '<Location %s (%s)>' % (self.path, self.location_id)
1524 __all__.append('Location')
1527 def get_location(location, component=None, archive=None, session=None):
1529 Returns Location object for the given combination of location, component
1532 @type location: string
1533 @param location: the path of the location, e.g. I{/srv/ftp-master.debian.org/ftp/pool/}
1535 @type component: string
1536 @param component: the component name (if None, no restriction applied)
1538 @type archive: string
1539 @param archive: the archive name (if None, no restriction applied)
1541 @rtype: Location / None
1542 @return: Either a Location object or None if one can't be found
1545 q = session.query(Location).filter_by(path=location)
1547 if archive is not None:
1548 q = q.join(Archive).filter_by(archive_name=archive)
1550 if component is not None:
1551 q = q.join(Component).filter_by(component_name=component)
1555 except NoResultFound:
1558 __all__.append('get_location')
1560 ################################################################################
1562 class Maintainer(object):
1563 def __init__(self, *args, **kwargs):
1567 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1569 def get_split_maintainer(self):
1570 if not hasattr(self, 'name') or self.name is None:
1571 return ('', '', '', '')
1573 return fix_maintainer(self.name.strip())
1575 __all__.append('Maintainer')
1578 def get_or_set_maintainer(name, session=None):
1580 Returns Maintainer object for given maintainer name.
1582 If no matching maintainer name is found, a row is inserted.
1585 @param name: The maintainer name to add
1587 @type session: SQLAlchemy
1588 @param session: Optional SQL session object (a temporary one will be
1589 generated if not supplied). If not passed, a commit will be performed at
1590 the end of the function, otherwise the caller is responsible for commiting.
1591 A flush will be performed either way.
1594 @return: the Maintainer object for the given maintainer
1597 q = session.query(Maintainer).filter_by(name=name)
1600 except NoResultFound:
1601 maintainer = Maintainer()
1602 maintainer.name = name
1603 session.add(maintainer)
1604 session.commit_or_flush()
1609 __all__.append('get_or_set_maintainer')
1612 def get_maintainer(maintainer_id, session=None):
1614 Return the name of the maintainer behind C{maintainer_id} or None if that
1615 maintainer_id is invalid.
1617 @type maintainer_id: int
1618 @param maintainer_id: the id of the maintainer
1621 @return: the Maintainer with this C{maintainer_id}
1624 return session.query(Maintainer).get(maintainer_id)
1626 __all__.append('get_maintainer')
1628 ################################################################################
1630 class NewComment(object):
1631 def __init__(self, *args, **kwargs):
1635 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1637 __all__.append('NewComment')
1640 def has_new_comment(package, version, session=None):
1642 Returns true if the given combination of C{package}, C{version} has a comment.
1644 @type package: string
1645 @param package: name of the package
1647 @type version: string
1648 @param version: package version
1650 @type session: Session
1651 @param session: Optional SQLA session object (a temporary one will be
1652 generated if not supplied)
1658 q = session.query(NewComment)
1659 q = q.filter_by(package=package)
1660 q = q.filter_by(version=version)
1662 return bool(q.count() > 0)
1664 __all__.append('has_new_comment')
1667 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1669 Returns (possibly empty) list of NewComment objects for the given
1672 @type package: string (optional)
1673 @param package: name of the package
1675 @type version: string (optional)
1676 @param version: package version
1678 @type comment_id: int (optional)
1679 @param comment_id: An id of a comment
1681 @type session: Session
1682 @param session: Optional SQLA session object (a temporary one will be
1683 generated if not supplied)
1686 @return: A (possibly empty) list of NewComment objects will be returned
1689 q = session.query(NewComment)
1690 if package is not None: q = q.filter_by(package=package)
1691 if version is not None: q = q.filter_by(version=version)
1692 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1696 __all__.append('get_new_comments')
1698 ################################################################################
1700 class Override(object):
1701 def __init__(self, *args, **kwargs):
1705 return '<Override %s (%s)>' % (self.package, self.suite_id)
1707 __all__.append('Override')
1710 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1712 Returns Override object for the given parameters
1714 @type package: string
1715 @param package: The name of the package
1717 @type suite: string, list or None
1718 @param suite: The name of the suite (or suites if a list) to limit to. If
1719 None, don't limit. Defaults to None.
1721 @type component: string, list or None
1722 @param component: The name of the component (or components if a list) to
1723 limit to. If None, don't limit. Defaults to None.
1725 @type overridetype: string, list or None
1726 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1727 limit to. If None, don't limit. Defaults to None.
1729 @type session: Session
1730 @param session: Optional SQLA session object (a temporary one will be
1731 generated if not supplied)
1734 @return: A (possibly empty) list of Override objects will be returned
1737 q = session.query(Override)
1738 q = q.filter_by(package=package)
1740 if suite is not None:
1741 if not isinstance(suite, list): suite = [suite]
1742 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1744 if component is not None:
1745 if not isinstance(component, list): component = [component]
1746 q = q.join(Component).filter(Component.component_name.in_(component))
1748 if overridetype is not None:
1749 if not isinstance(overridetype, list): overridetype = [overridetype]
1750 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1754 __all__.append('get_override')
1757 ################################################################################
1759 class OverrideType(object):
1760 def __init__(self, *args, **kwargs):
1764 return '<OverrideType %s>' % self.overridetype
1766 __all__.append('OverrideType')
1769 def get_override_type(override_type, session=None):
1771 Returns OverrideType object for given C{override type}.
1773 @type override_type: string
1774 @param override_type: The name of the override type
1776 @type session: Session
1777 @param session: Optional SQLA session object (a temporary one will be
1778 generated if not supplied)
1781 @return: the database id for the given override type
1784 q = session.query(OverrideType).filter_by(overridetype=override_type)
1788 except NoResultFound:
1791 __all__.append('get_override_type')
1793 ################################################################################
1795 class DebContents(object):
1796 def __init__(self, *args, **kwargs):
1800 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1802 __all__.append('DebContents')
1805 class UdebContents(object):
1806 def __init__(self, *args, **kwargs):
1810 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1812 __all__.append('UdebContents')
1814 class PendingBinContents(object):
1815 def __init__(self, *args, **kwargs):
1819 return '<PendingBinContents %s>' % self.contents_id
1821 __all__.append('PendingBinContents')
1823 def insert_pending_content_paths(package,
1828 Make sure given paths are temporarily associated with given
1832 @param package: the package to associate with should have been read in from the binary control file
1833 @type fullpaths: list
1834 @param fullpaths: the list of paths of the file being associated with the binary
1835 @type session: SQLAlchemy session
1836 @param session: Optional SQLAlchemy session. If this is passed, the caller
1837 is responsible for ensuring a transaction has begun and committing the
1838 results or rolling back based on the result code. If not passed, a commit
1839 will be performed at the end of the function
1841 @return: True upon success, False if there is a problem
1844 privatetrans = False
1847 session = DBConn().session()
1851 arch = get_architecture(package['Architecture'], session)
1852 arch_id = arch.arch_id
1854 # Remove any already existing recorded files for this package
1855 q = session.query(PendingBinContents)
1856 q = q.filter_by(package=package['Package'])
1857 q = q.filter_by(version=package['Version'])
1858 q = q.filter_by(architecture=arch_id)
1861 for fullpath in fullpaths:
1863 if fullpath.startswith( "./" ):
1864 fullpath = fullpath[2:]
1866 pca = PendingBinContents()
1867 pca.package = package['Package']
1868 pca.version = package['Version']
1870 pca.architecture = arch_id
1873 pca.type = 8 # gross
1875 pca.type = 7 # also gross
1878 # Only commit if we set up the session ourself
1886 except Exception, e:
1887 traceback.print_exc()
1889 # Only rollback if we set up the session ourself
1896 __all__.append('insert_pending_content_paths')
1898 ################################################################################
1900 class PolicyQueue(object):
1901 def __init__(self, *args, **kwargs):
1905 return '<PolicyQueue %s>' % self.queue_name
1907 __all__.append('PolicyQueue')
1910 def get_policy_queue(queuename, session=None):
1912 Returns PolicyQueue object for given C{queue name}
1914 @type queuename: string
1915 @param queuename: The name of the queue
1917 @type session: Session
1918 @param session: Optional SQLA session object (a temporary one will be
1919 generated if not supplied)
1922 @return: PolicyQueue object for the given queue
1925 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1929 except NoResultFound:
1932 __all__.append('get_policy_queue')
1935 def get_policy_queue_from_path(pathname, session=None):
1937 Returns PolicyQueue object for given C{path name}
1939 @type queuename: string
1940 @param queuename: The path
1942 @type session: Session
1943 @param session: Optional SQLA session object (a temporary one will be
1944 generated if not supplied)
1947 @return: PolicyQueue object for the given queue
1950 q = session.query(PolicyQueue).filter_by(path=pathname)
1954 except NoResultFound:
1957 __all__.append('get_policy_queue_from_path')
1959 ################################################################################
1961 class Priority(object):
1962 def __init__(self, *args, **kwargs):
1965 def __eq__(self, val):
1966 if isinstance(val, str):
1967 return (self.priority == val)
1968 # This signals to use the normal comparison operator
1969 return NotImplemented
1971 def __ne__(self, val):
1972 if isinstance(val, str):
1973 return (self.priority != val)
1974 # This signals to use the normal comparison operator
1975 return NotImplemented
1978 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1980 __all__.append('Priority')
1983 def get_priority(priority, session=None):
1985 Returns Priority object for given C{priority name}.
1987 @type priority: string
1988 @param priority: The name of the priority
1990 @type session: Session
1991 @param session: Optional SQLA session object (a temporary one will be
1992 generated if not supplied)
1995 @return: Priority object for the given priority
1998 q = session.query(Priority).filter_by(priority=priority)
2002 except NoResultFound:
2005 __all__.append('get_priority')
2008 def get_priorities(session=None):
2010 Returns dictionary of priority names -> id mappings
2012 @type session: Session
2013 @param session: Optional SQL session object (a temporary one will be
2014 generated if not supplied)
2017 @return: dictionary of priority names -> id mappings
2021 q = session.query(Priority)
2023 ret[x.priority] = x.priority_id
2027 __all__.append('get_priorities')
2029 ################################################################################
2031 class Section(object):
2032 def __init__(self, *args, **kwargs):
2035 def __eq__(self, val):
2036 if isinstance(val, str):
2037 return (self.section == val)
2038 # This signals to use the normal comparison operator
2039 return NotImplemented
2041 def __ne__(self, val):
2042 if isinstance(val, str):
2043 return (self.section != val)
2044 # This signals to use the normal comparison operator
2045 return NotImplemented
2048 return '<Section %s>' % self.section
2050 __all__.append('Section')
2053 def get_section(section, session=None):
2055 Returns Section object for given C{section name}.
2057 @type section: string
2058 @param section: The name of the section
2060 @type session: Session
2061 @param session: Optional SQLA session object (a temporary one will be
2062 generated if not supplied)
2065 @return: Section object for the given section name
2068 q = session.query(Section).filter_by(section=section)
2072 except NoResultFound:
2075 __all__.append('get_section')
2078 def get_sections(session=None):
2080 Returns dictionary of section names -> id mappings
2082 @type session: Session
2083 @param session: Optional SQL session object (a temporary one will be
2084 generated if not supplied)
2087 @return: dictionary of section names -> id mappings
2091 q = session.query(Section)
2093 ret[x.section] = x.section_id
2097 __all__.append('get_sections')
2099 ################################################################################
2101 class DBSource(object):
2102 def __init__(self, *args, **kwargs):
2106 return '<DBSource %s (%s)>' % (self.source, self.version)
2108 __all__.append('DBSource')
2111 def source_exists(source, source_version, suites = ["any"], session=None):
2113 Ensure that source exists somewhere in the archive for the binary
2114 upload being processed.
2115 1. exact match => 1.0-3
2116 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2118 @type source: string
2119 @param source: source name
2121 @type source_version: string
2122 @param source_version: expected source version
2125 @param suites: list of suites to check in, default I{any}
2127 @type session: Session
2128 @param session: Optional SQLA session object (a temporary one will be
2129 generated if not supplied)
2132 @return: returns 1 if a source with expected version is found, otherwise 0
2139 for suite in suites:
2140 q = session.query(DBSource).filter_by(source=source)
2142 # source must exist in suite X, or in some other suite that's
2143 # mapped to X, recursively... silent-maps are counted too,
2144 # unreleased-maps aren't.
2145 maps = cnf.ValueList("SuiteMappings")[:]
2147 maps = [ m.split() for m in maps ]
2148 maps = [ (x[1], x[2]) for x in maps
2149 if x[0] == "map" or x[0] == "silent-map" ]
2152 if x[1] in s and x[0] not in s:
2155 q = q.join(SrcAssociation).join(Suite)
2156 q = q.filter(Suite.suite_name.in_(s))
2158 # Reduce the query results to a list of version numbers
2159 ql = [ j.version for j in q.all() ]
2162 if source_version in ql:
2166 from daklib.regexes import re_bin_only_nmu
2167 orig_source_version = re_bin_only_nmu.sub('', source_version)
2168 if orig_source_version in ql:
2171 # No source found so return not ok
2176 __all__.append('source_exists')
2179 def get_suites_source_in(source, session=None):
2181 Returns list of Suite objects which given C{source} name is in
2184 @param source: DBSource package name to search for
2187 @return: list of Suite objects for the given source
2190 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2192 __all__.append('get_suites_source_in')
2195 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2197 Returns list of DBSource objects for given C{source} name and other parameters
2200 @param source: DBSource package name to search for
2202 @type version: str or None
2203 @param version: DBSource version name to search for or None if not applicable
2205 @type dm_upload_allowed: bool
2206 @param dm_upload_allowed: If None, no effect. If True or False, only
2207 return packages with that dm_upload_allowed setting
2209 @type session: Session
2210 @param session: Optional SQL session object (a temporary one will be
2211 generated if not supplied)
2214 @return: list of DBSource objects for the given name (may be empty)
2217 q = session.query(DBSource).filter_by(source=source)
2219 if version is not None:
2220 q = q.filter_by(version=version)
2222 if dm_upload_allowed is not None:
2223 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2227 __all__.append('get_sources_from_name')
2230 def get_source_in_suite(source, suite, session=None):
2232 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2234 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2235 - B{suite} - a suite name, eg. I{unstable}
2237 @type source: string
2238 @param source: source package name
2241 @param suite: the suite name
2244 @return: the version for I{source} in I{suite}
2248 q = session.query(SrcAssociation)
2249 q = q.join('source').filter_by(source=source)
2250 q = q.join('suite').filter_by(suite_name=suite)
2253 return q.one().source
2254 except NoResultFound:
2257 __all__.append('get_source_in_suite')
2259 ################################################################################
2262 def add_dsc_to_db(u, filename, session=None):
2263 entry = u.pkg.files[filename]
2267 source.source = u.pkg.dsc["source"]
2268 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2269 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2270 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2271 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2272 source.install_date = datetime.now().date()
2274 dsc_component = entry["component"]
2275 dsc_location_id = entry["location id"]
2277 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2279 # Set up a new poolfile if necessary
2280 if not entry.has_key("files id") or not entry["files id"]:
2281 filename = entry["pool name"] + filename
2282 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2284 pfs.append(poolfile)
2285 entry["files id"] = poolfile.file_id
2287 source.poolfile_id = entry["files id"]
2291 for suite_name in u.pkg.changes["distribution"].keys():
2292 sa = SrcAssociation()
2293 sa.source_id = source.source_id
2294 sa.suite_id = get_suite(suite_name).suite_id
2299 # Add the source files to the DB (files and dsc_files)
2301 dscfile.source_id = source.source_id
2302 dscfile.poolfile_id = entry["files id"]
2303 session.add(dscfile)
2305 for dsc_file, dentry in u.pkg.dsc_files.items():
2307 df.source_id = source.source_id
2309 # If the .orig tarball is already in the pool, it's
2310 # files id is stored in dsc_files by check_dsc().
2311 files_id = dentry.get("files id", None)
2313 # Find the entry in the files hash
2314 # TODO: Bail out here properly
2316 for f, e in u.pkg.files.items():
2321 if files_id is None:
2322 filename = dfentry["pool name"] + dsc_file
2324 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2325 # FIXME: needs to check for -1/-2 and or handle exception
2326 if found and obj is not None:
2327 files_id = obj.file_id
2330 # If still not found, add it
2331 if files_id is None:
2332 # HACK: Force sha1sum etc into dentry
2333 dentry["sha1sum"] = dfentry["sha1sum"]
2334 dentry["sha256sum"] = dfentry["sha256sum"]
2335 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2336 pfs.append(poolfile)
2337 files_id = poolfile.file_id
2339 poolfile = get_poolfile_by_id(files_id, session)
2340 if poolfile is None:
2341 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2342 pfs.append(poolfile)
2344 df.poolfile_id = files_id
2349 # Add the src_uploaders to the DB
2350 uploader_ids = [source.maintainer_id]
2351 if u.pkg.dsc.has_key("uploaders"):
2352 for up in u.pkg.dsc["uploaders"].replace(">, ", ">\t").split("\t"):
2354 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2357 for up_id in uploader_ids:
2358 if added_ids.has_key(up_id):
2360 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2366 su.maintainer_id = up_id
2367 su.source_id = source.source_id
2372 return source, dsc_component, dsc_location_id, pfs
2374 __all__.append('add_dsc_to_db')
2377 def add_deb_to_db(u, filename, session=None):
2379 Contrary to what you might expect, this routine deals with both
2380 debs and udebs. That info is in 'dbtype', whilst 'type' is
2381 'deb' for both of them
2384 entry = u.pkg.files[filename]
2387 bin.package = entry["package"]
2388 bin.version = entry["version"]
2389 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2390 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2391 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2392 bin.binarytype = entry["dbtype"]
2395 filename = entry["pool name"] + filename
2396 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2397 if not entry.get("location id", None):
2398 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2400 if entry.get("files id", None):
2401 poolfile = get_poolfile_by_id(bin.poolfile_id)
2402 bin.poolfile_id = entry["files id"]
2404 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2405 bin.poolfile_id = entry["files id"] = poolfile.file_id
2408 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2409 if len(bin_sources) != 1:
2410 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2411 (bin.package, bin.version, entry["architecture"],
2412 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2414 bin.source_id = bin_sources[0].source_id
2416 # Add and flush object so it has an ID
2420 # Add BinAssociations
2421 for suite_name in u.pkg.changes["distribution"].keys():
2422 ba = BinAssociation()
2423 ba.binary_id = bin.binary_id
2424 ba.suite_id = get_suite(suite_name).suite_id
2429 # Deal with contents - disabled for now
2430 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2432 # print "REJECT\nCould not determine contents of package %s" % bin.package
2433 # session.rollback()
2434 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2438 __all__.append('add_deb_to_db')
2440 ################################################################################
2442 class SourceACL(object):
2443 def __init__(self, *args, **kwargs):
2447 return '<SourceACL %s>' % self.source_acl_id
2449 __all__.append('SourceACL')
2451 ################################################################################
2453 class SrcAssociation(object):
2454 def __init__(self, *args, **kwargs):
2458 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2460 __all__.append('SrcAssociation')
2462 ################################################################################
2464 class SrcFormat(object):
2465 def __init__(self, *args, **kwargs):
2469 return '<SrcFormat %s>' % (self.format_name)
2471 __all__.append('SrcFormat')
2473 ################################################################################
2475 class SrcUploader(object):
2476 def __init__(self, *args, **kwargs):
2480 return '<SrcUploader %s>' % self.uploader_id
2482 __all__.append('SrcUploader')
2484 ################################################################################
2486 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2487 ('SuiteID', 'suite_id'),
2488 ('Version', 'version'),
2489 ('Origin', 'origin'),
2491 ('Description', 'description'),
2492 ('Untouchable', 'untouchable'),
2493 ('Announce', 'announce'),
2494 ('Codename', 'codename'),
2495 ('OverrideCodename', 'overridecodename'),
2496 ('ValidTime', 'validtime'),
2497 ('Priority', 'priority'),
2498 ('NotAutomatic', 'notautomatic'),
2499 ('CopyChanges', 'copychanges'),
2500 ('OverrideSuite', 'overridesuite')]
2502 class Suite(object):
2503 def __init__(self, *args, **kwargs):
2507 return '<Suite %s>' % self.suite_name
2509 def __eq__(self, val):
2510 if isinstance(val, str):
2511 return (self.suite_name == val)
2512 # This signals to use the normal comparison operator
2513 return NotImplemented
2515 def __ne__(self, val):
2516 if isinstance(val, str):
2517 return (self.suite_name != val)
2518 # This signals to use the normal comparison operator
2519 return NotImplemented
2523 for disp, field in SUITE_FIELDS:
2524 val = getattr(self, field, None)
2526 ret.append("%s: %s" % (disp, val))
2528 return "\n".join(ret)
2530 __all__.append('Suite')
2533 def get_suite_architecture(suite, architecture, session=None):
2535 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2539 @param suite: Suite name to search for
2541 @type architecture: str
2542 @param architecture: Architecture name to search for
2544 @type session: Session
2545 @param session: Optional SQL session object (a temporary one will be
2546 generated if not supplied)
2548 @rtype: SuiteArchitecture
2549 @return: the SuiteArchitecture object or None
2552 q = session.query(SuiteArchitecture)
2553 q = q.join(Architecture).filter_by(arch_string=architecture)
2554 q = q.join(Suite).filter_by(suite_name=suite)
2558 except NoResultFound:
2561 __all__.append('get_suite_architecture')
2564 def get_suite(suite, session=None):
2566 Returns Suite object for given C{suite name}.
2569 @param suite: The name of the suite
2571 @type session: Session
2572 @param session: Optional SQLA session object (a temporary one will be
2573 generated if not supplied)
2576 @return: Suite object for the requested suite name (None if not present)
2579 q = session.query(Suite).filter_by(suite_name=suite)
2583 except NoResultFound:
2586 __all__.append('get_suite')
2588 ################################################################################
2590 class SuiteArchitecture(object):
2591 def __init__(self, *args, **kwargs):
2595 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2597 __all__.append('SuiteArchitecture')
2600 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2602 Returns list of Architecture objects for given C{suite} name
2605 @param suite: Suite name to search for
2607 @type skipsrc: boolean
2608 @param skipsrc: Whether to skip returning the 'source' architecture entry
2611 @type skipall: boolean
2612 @param skipall: Whether to skip returning the 'all' architecture entry
2615 @type session: Session
2616 @param session: Optional SQL session object (a temporary one will be
2617 generated if not supplied)
2620 @return: list of Architecture objects for the given name (may be empty)
2623 q = session.query(Architecture)
2624 q = q.join(SuiteArchitecture)
2625 q = q.join(Suite).filter_by(suite_name=suite)
2628 q = q.filter(Architecture.arch_string != 'source')
2631 q = q.filter(Architecture.arch_string != 'all')
2633 q = q.order_by('arch_string')
2637 __all__.append('get_suite_architectures')
2639 ################################################################################
2641 class SuiteSrcFormat(object):
2642 def __init__(self, *args, **kwargs):
2646 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2648 __all__.append('SuiteSrcFormat')
2651 def get_suite_src_formats(suite, session=None):
2653 Returns list of allowed SrcFormat for C{suite}.
2656 @param suite: Suite name to search for
2658 @type session: Session
2659 @param session: Optional SQL session object (a temporary one will be
2660 generated if not supplied)
2663 @return: the list of allowed source formats for I{suite}
2666 q = session.query(SrcFormat)
2667 q = q.join(SuiteSrcFormat)
2668 q = q.join(Suite).filter_by(suite_name=suite)
2669 q = q.order_by('format_name')
2673 __all__.append('get_suite_src_formats')
2675 ################################################################################
2678 def __init__(self, *args, **kwargs):
2681 def __eq__(self, val):
2682 if isinstance(val, str):
2683 return (self.uid == val)
2684 # This signals to use the normal comparison operator
2685 return NotImplemented
2687 def __ne__(self, val):
2688 if isinstance(val, str):
2689 return (self.uid != val)
2690 # This signals to use the normal comparison operator
2691 return NotImplemented
2694 return '<Uid %s (%s)>' % (self.uid, self.name)
2696 __all__.append('Uid')
2699 def get_or_set_uid(uidname, session=None):
2701 Returns uid object for given uidname.
2703 If no matching uidname is found, a row is inserted.
2705 @type uidname: string
2706 @param uidname: The uid to add
2708 @type session: SQLAlchemy
2709 @param session: Optional SQL session object (a temporary one will be
2710 generated if not supplied). If not passed, a commit will be performed at
2711 the end of the function, otherwise the caller is responsible for commiting.
2714 @return: the uid object for the given uidname
2717 q = session.query(Uid).filter_by(uid=uidname)
2721 except NoResultFound:
2725 session.commit_or_flush()
2730 __all__.append('get_or_set_uid')
2733 def get_uid_from_fingerprint(fpr, session=None):
2734 q = session.query(Uid)
2735 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2739 except NoResultFound:
2742 __all__.append('get_uid_from_fingerprint')
2744 ################################################################################
2746 class UploadBlock(object):
2747 def __init__(self, *args, **kwargs):
2751 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2753 __all__.append('UploadBlock')
2755 ################################################################################
2757 class DBConn(object):
2759 database module init.
2763 def __init__(self, *args, **kwargs):
2764 self.__dict__ = self.__shared_state
2766 if not getattr(self, 'initialised', False):
2767 self.initialised = True
2768 self.debug = kwargs.has_key('debug')
2771 def __setuptables(self):
2772 tables_with_primary = (
2783 'changes_pending_binaries',
2784 'changes_pending_files',
2785 'changes_pending_source',
2795 'pending_bin_contents',
2807 # The following tables have primary keys but sqlalchemy
2808 # version 0.5 fails to reflect them correctly with database
2809 # versions before upgrade #41.
2811 #'build_queue_files',
2814 tables_no_primary = (
2816 'changes_pending_files_map',
2817 'changes_pending_source_files',
2818 'changes_pool_files',
2821 'suite_architectures',
2822 'suite_src_formats',
2823 'suite_build_queue_copy',
2825 # see the comment above
2827 'build_queue_files',
2831 'almost_obsolete_all_associations',
2832 'almost_obsolete_src_associations',
2833 'any_associations_source',
2834 'bin_assoc_by_arch',
2835 'bin_associations_binaries',
2836 'binaries_suite_arch',
2837 'binfiles_suite_component_arch',
2840 'newest_all_associations',
2841 'newest_any_associations',
2843 'newest_src_association',
2844 'obsolete_all_associations',
2845 'obsolete_any_associations',
2846 'obsolete_any_by_all_associations',
2847 'obsolete_src_associations',
2849 'src_associations_bin',
2850 'src_associations_src',
2851 'suite_arch_by_name',
2854 # Sqlalchemy version 0.5 fails to reflect the SERIAL type
2855 # correctly and that is why we have to use a workaround. It can
2856 # be removed as soon as we switch to version 0.6.
2857 for table_name in tables_with_primary:
2858 table = Table(table_name, self.db_meta, \
2859 Column('id', Integer, primary_key = True), \
2860 autoload=True, useexisting=True)
2861 setattr(self, 'tbl_%s' % table_name, table)
2863 for table_name in tables_no_primary:
2864 table = Table(table_name, self.db_meta, autoload=True)
2865 setattr(self, 'tbl_%s' % table_name, table)
2867 for view_name in views:
2868 view = Table(view_name, self.db_meta, autoload=True)
2869 setattr(self, 'view_%s' % view_name, view)
2871 def __setupmappers(self):
2872 mapper(Architecture, self.tbl_architecture,
2873 properties = dict(arch_id = self.tbl_architecture.c.id))
2875 mapper(Archive, self.tbl_archive,
2876 properties = dict(archive_id = self.tbl_archive.c.id,
2877 archive_name = self.tbl_archive.c.name))
2879 mapper(BinAssociation, self.tbl_bin_associations,
2880 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2881 suite_id = self.tbl_bin_associations.c.suite,
2882 suite = relation(Suite),
2883 binary_id = self.tbl_bin_associations.c.bin,
2884 binary = relation(DBBinary)))
2886 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2887 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2888 filename = self.tbl_pending_bin_contents.c.filename,
2889 package = self.tbl_pending_bin_contents.c.package,
2890 version = self.tbl_pending_bin_contents.c.version,
2891 arch = self.tbl_pending_bin_contents.c.arch,
2892 otype = self.tbl_pending_bin_contents.c.type))
2894 mapper(DebContents, self.tbl_deb_contents,
2895 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2896 package=self.tbl_deb_contents.c.package,
2897 suite=self.tbl_deb_contents.c.suite,
2898 arch=self.tbl_deb_contents.c.arch,
2899 section=self.tbl_deb_contents.c.section,
2900 filename=self.tbl_deb_contents.c.filename))
2902 mapper(UdebContents, self.tbl_udeb_contents,
2903 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2904 package=self.tbl_udeb_contents.c.package,
2905 suite=self.tbl_udeb_contents.c.suite,
2906 arch=self.tbl_udeb_contents.c.arch,
2907 section=self.tbl_udeb_contents.c.section,
2908 filename=self.tbl_udeb_contents.c.filename))
2910 mapper(BuildQueue, self.tbl_build_queue,
2911 properties = dict(queue_id = self.tbl_build_queue.c.id))
2913 mapper(BuildQueueFile, self.tbl_build_queue_files,
2914 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2915 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2917 mapper(DBBinary, self.tbl_binaries,
2918 properties = dict(binary_id = self.tbl_binaries.c.id,
2919 package = self.tbl_binaries.c.package,
2920 version = self.tbl_binaries.c.version,
2921 maintainer_id = self.tbl_binaries.c.maintainer,
2922 maintainer = relation(Maintainer),
2923 source_id = self.tbl_binaries.c.source,
2924 source = relation(DBSource),
2925 arch_id = self.tbl_binaries.c.architecture,
2926 architecture = relation(Architecture),
2927 poolfile_id = self.tbl_binaries.c.file,
2928 poolfile = relation(PoolFile),
2929 binarytype = self.tbl_binaries.c.type,
2930 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2931 fingerprint = relation(Fingerprint),
2932 install_date = self.tbl_binaries.c.install_date,
2933 binassociations = relation(BinAssociation,
2934 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2936 mapper(BinaryACL, self.tbl_binary_acl,
2937 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2939 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2940 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2941 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2942 architecture = relation(Architecture)))
2944 mapper(Component, self.tbl_component,
2945 properties = dict(component_id = self.tbl_component.c.id,
2946 component_name = self.tbl_component.c.name))
2948 mapper(DBConfig, self.tbl_config,
2949 properties = dict(config_id = self.tbl_config.c.id))
2951 mapper(DSCFile, self.tbl_dsc_files,
2952 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2953 source_id = self.tbl_dsc_files.c.source,
2954 source = relation(DBSource),
2955 poolfile_id = self.tbl_dsc_files.c.file,
2956 poolfile = relation(PoolFile)))
2958 mapper(PoolFile, self.tbl_files,
2959 properties = dict(file_id = self.tbl_files.c.id,
2960 filesize = self.tbl_files.c.size,
2961 location_id = self.tbl_files.c.location,
2962 location = relation(Location)))
2964 mapper(Fingerprint, self.tbl_fingerprint,
2965 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2966 uid_id = self.tbl_fingerprint.c.uid,
2967 uid = relation(Uid),
2968 keyring_id = self.tbl_fingerprint.c.keyring,
2969 keyring = relation(Keyring),
2970 source_acl = relation(SourceACL),
2971 binary_acl = relation(BinaryACL)))
2973 mapper(Keyring, self.tbl_keyrings,
2974 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2975 keyring_id = self.tbl_keyrings.c.id))
2977 mapper(DBChange, self.tbl_changes,
2978 properties = dict(change_id = self.tbl_changes.c.id,
2979 poolfiles = relation(PoolFile,
2980 secondary=self.tbl_changes_pool_files,
2981 backref="changeslinks"),
2982 seen = self.tbl_changes.c.seen,
2983 source = self.tbl_changes.c.source,
2984 binaries = self.tbl_changes.c.binaries,
2985 architecture = self.tbl_changes.c.architecture,
2986 distribution = self.tbl_changes.c.distribution,
2987 urgency = self.tbl_changes.c.urgency,
2988 maintainer = self.tbl_changes.c.maintainer,
2989 changedby = self.tbl_changes.c.changedby,
2990 date = self.tbl_changes.c.date,
2991 version = self.tbl_changes.c.version,
2992 files = relation(ChangePendingFile,
2993 secondary=self.tbl_changes_pending_files_map,
2994 backref="changesfile"),
2995 in_queue_id = self.tbl_changes.c.in_queue,
2996 in_queue = relation(PolicyQueue,
2997 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2998 approved_for_id = self.tbl_changes.c.approved_for))
3000 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
3001 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
3003 mapper(ChangePendingFile, self.tbl_changes_pending_files,
3004 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
3005 filename = self.tbl_changes_pending_files.c.filename,
3006 size = self.tbl_changes_pending_files.c.size,
3007 md5sum = self.tbl_changes_pending_files.c.md5sum,
3008 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
3009 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
3011 mapper(ChangePendingSource, self.tbl_changes_pending_source,
3012 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
3013 change = relation(DBChange),
3014 maintainer = relation(Maintainer,
3015 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
3016 changedby = relation(Maintainer,
3017 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
3018 fingerprint = relation(Fingerprint),
3019 source_files = relation(ChangePendingFile,
3020 secondary=self.tbl_changes_pending_source_files,
3021 backref="pending_sources")))
3024 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
3025 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
3026 keyring = relation(Keyring, backref="keyring_acl_map"),
3027 architecture = relation(Architecture)))
3029 mapper(Location, self.tbl_location,
3030 properties = dict(location_id = self.tbl_location.c.id,
3031 component_id = self.tbl_location.c.component,
3032 component = relation(Component),
3033 archive_id = self.tbl_location.c.archive,
3034 archive = relation(Archive),
3035 archive_type = self.tbl_location.c.type))
3037 mapper(Maintainer, self.tbl_maintainer,
3038 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
3040 mapper(NewComment, self.tbl_new_comments,
3041 properties = dict(comment_id = self.tbl_new_comments.c.id))
3043 mapper(Override, self.tbl_override,
3044 properties = dict(suite_id = self.tbl_override.c.suite,
3045 suite = relation(Suite),
3046 package = self.tbl_override.c.package,
3047 component_id = self.tbl_override.c.component,
3048 component = relation(Component),
3049 priority_id = self.tbl_override.c.priority,
3050 priority = relation(Priority),
3051 section_id = self.tbl_override.c.section,
3052 section = relation(Section),
3053 overridetype_id = self.tbl_override.c.type,
3054 overridetype = relation(OverrideType)))
3056 mapper(OverrideType, self.tbl_override_type,
3057 properties = dict(overridetype = self.tbl_override_type.c.type,
3058 overridetype_id = self.tbl_override_type.c.id))
3060 mapper(PolicyQueue, self.tbl_policy_queue,
3061 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
3063 mapper(Priority, self.tbl_priority,
3064 properties = dict(priority_id = self.tbl_priority.c.id))
3066 mapper(Section, self.tbl_section,
3067 properties = dict(section_id = self.tbl_section.c.id,
3068 section=self.tbl_section.c.section))
3070 mapper(DBSource, self.tbl_source,
3071 properties = dict(source_id = self.tbl_source.c.id,
3072 version = self.tbl_source.c.version,
3073 maintainer_id = self.tbl_source.c.maintainer,
3074 maintainer = relation(Maintainer,
3075 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3076 poolfile_id = self.tbl_source.c.file,
3077 poolfile = relation(PoolFile),
3078 fingerprint_id = self.tbl_source.c.sig_fpr,
3079 fingerprint = relation(Fingerprint),
3080 changedby_id = self.tbl_source.c.changedby,
3081 changedby = relation(Maintainer,
3082 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3083 srcfiles = relation(DSCFile,
3084 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3085 srcassociations = relation(SrcAssociation,
3086 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3087 srcuploaders = relation(SrcUploader)))
3089 mapper(SourceACL, self.tbl_source_acl,
3090 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3092 mapper(SrcAssociation, self.tbl_src_associations,
3093 properties = dict(sa_id = self.tbl_src_associations.c.id,
3094 suite_id = self.tbl_src_associations.c.suite,
3095 suite = relation(Suite),
3096 source_id = self.tbl_src_associations.c.source,
3097 source = relation(DBSource)))
3099 mapper(SrcFormat, self.tbl_src_format,
3100 properties = dict(src_format_id = self.tbl_src_format.c.id,
3101 format_name = self.tbl_src_format.c.format_name))
3103 mapper(SrcUploader, self.tbl_src_uploaders,
3104 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3105 source_id = self.tbl_src_uploaders.c.source,
3106 source = relation(DBSource,
3107 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3108 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3109 maintainer = relation(Maintainer,
3110 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3112 mapper(Suite, self.tbl_suite,
3113 properties = dict(suite_id = self.tbl_suite.c.id,
3114 policy_queue = relation(PolicyQueue),
3115 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3117 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3118 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3119 suite = relation(Suite, backref='suitearchitectures'),
3120 arch_id = self.tbl_suite_architectures.c.architecture,
3121 architecture = relation(Architecture)))
3123 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3124 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3125 suite = relation(Suite, backref='suitesrcformats'),
3126 src_format_id = self.tbl_suite_src_formats.c.src_format,
3127 src_format = relation(SrcFormat)))
3129 mapper(Uid, self.tbl_uid,
3130 properties = dict(uid_id = self.tbl_uid.c.id,
3131 fingerprint = relation(Fingerprint)))
3133 mapper(UploadBlock, self.tbl_upload_blocks,
3134 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3135 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3136 uid = relation(Uid, backref="uploadblocks")))
3138 ## Connection functions
3139 def __createconn(self):
3140 from config import Config
3144 connstr = "postgres://%s" % cnf["DB::Host"]
3145 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3146 connstr += ":%s" % cnf["DB::Port"]
3147 connstr += "/%s" % cnf["DB::Name"]
3150 connstr = "postgres:///%s" % cnf["DB::Name"]
3151 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3152 connstr += "?port=%s" % cnf["DB::Port"]
3154 self.db_pg = create_engine(connstr, echo=self.debug)
3155 self.db_meta = MetaData()
3156 self.db_meta.bind = self.db_pg
3157 self.db_smaker = sessionmaker(bind=self.db_pg,
3161 self.__setuptables()
3162 self.__setupmappers()
3165 return self.db_smaker()
3167 __all__.append('DBConn')