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
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
61 ################################################################################
63 # Patch in support for the debversion field type so that it works during
66 class DebVersion(sqltypes.Text):
68 Support the debversion type
71 def get_col_spec(self):
74 sa_major_version = sqlalchemy.__version__[0:3]
75 if sa_major_version == "0.5":
76 from sqlalchemy.databases import postgres
77 postgres.ischema_names['debversion'] = DebVersion
79 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
81 ################################################################################
83 __all__ = ['IntegrityError', 'SQLAlchemyError']
85 ################################################################################
87 def session_wrapper(fn):
89 Wrapper around common ".., session=None):" handling. If the wrapped
90 function is called without passing 'session', we create a local one
91 and destroy it when the function ends.
93 Also attaches a commit_or_flush method to the session; if we created a
94 local session, this is a synonym for session.commit(), otherwise it is a
95 synonym for session.flush().
98 def wrapped(*args, **kwargs):
99 private_transaction = False
101 # Find the session object
102 session = kwargs.get('session')
105 if len(args) <= len(getargspec(fn)[0]) - 1:
106 # No session specified as last argument or in kwargs
107 private_transaction = True
108 session = kwargs['session'] = DBConn().session()
110 # Session is last argument in args
114 session = args[-1] = DBConn().session()
115 private_transaction = True
117 if private_transaction:
118 session.commit_or_flush = session.commit
120 session.commit_or_flush = session.flush
123 return fn(*args, **kwargs)
125 if private_transaction:
126 # We created a session; close it.
129 wrapped.__doc__ = fn.__doc__
130 wrapped.func_name = fn.func_name
134 __all__.append('session_wrapper')
136 ################################################################################
138 class Architecture(object):
139 def __init__(self, *args, **kwargs):
142 def __eq__(self, val):
143 if isinstance(val, str):
144 return (self.arch_string== val)
145 # This signals to use the normal comparison operator
146 return NotImplemented
148 def __ne__(self, val):
149 if isinstance(val, str):
150 return (self.arch_string != val)
151 # This signals to use the normal comparison operator
152 return NotImplemented
155 return '<Architecture %s>' % self.arch_string
157 __all__.append('Architecture')
160 def get_architecture(architecture, session=None):
162 Returns database id for given C{architecture}.
164 @type architecture: string
165 @param architecture: The name of the architecture
167 @type session: Session
168 @param session: Optional SQLA session object (a temporary one will be
169 generated if not supplied)
172 @return: Architecture object for the given arch (None if not present)
175 q = session.query(Architecture).filter_by(arch_string=architecture)
179 except NoResultFound:
182 __all__.append('get_architecture')
185 def get_architecture_suites(architecture, session=None):
187 Returns list of Suite objects for given C{architecture} name
189 @type architecture: str
190 @param architecture: Architecture name to search for
192 @type session: Session
193 @param session: Optional SQL session object (a temporary one will be
194 generated if not supplied)
197 @return: list of Suite objects for the given name (may be empty)
200 q = session.query(Suite)
201 q = q.join(SuiteArchitecture)
202 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
208 __all__.append('get_architecture_suites')
210 ################################################################################
212 class Archive(object):
213 def __init__(self, *args, **kwargs):
217 return '<Archive %s>' % self.archive_name
219 __all__.append('Archive')
222 def get_archive(archive, session=None):
224 returns database id for given C{archive}.
226 @type archive: string
227 @param archive: the name of the arhive
229 @type session: Session
230 @param session: Optional SQLA session object (a temporary one will be
231 generated if not supplied)
234 @return: Archive object for the given name (None if not present)
237 archive = archive.lower()
239 q = session.query(Archive).filter_by(archive_name=archive)
243 except NoResultFound:
246 __all__.append('get_archive')
248 ################################################################################
250 class BinAssociation(object):
251 def __init__(self, *args, **kwargs):
255 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
257 __all__.append('BinAssociation')
259 ################################################################################
261 class BinContents(object):
262 def __init__(self, *args, **kwargs):
266 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
268 __all__.append('BinContents')
270 ################################################################################
272 class DBBinary(object):
273 def __init__(self, *args, **kwargs):
277 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
279 __all__.append('DBBinary')
282 def get_suites_binary_in(package, session=None):
284 Returns list of Suite objects which given C{package} name is in
287 @param package: DBBinary package name to search for
290 @return: list of Suite objects for the given package
293 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
295 __all__.append('get_suites_binary_in')
298 def get_binary_from_id(binary_id, session=None):
300 Returns DBBinary object for given C{id}
303 @param binary_id: Id of the required binary
305 @type session: Session
306 @param session: Optional SQLA session object (a temporary one will be
307 generated if not supplied)
310 @return: DBBinary object for the given binary (None if not present)
313 q = session.query(DBBinary).filter_by(binary_id=binary_id)
317 except NoResultFound:
320 __all__.append('get_binary_from_id')
323 def get_binaries_from_name(package, version=None, architecture=None, session=None):
325 Returns list of DBBinary objects for given C{package} name
328 @param package: DBBinary package name to search for
330 @type version: str or None
331 @param version: Version to search for (or None)
333 @type architecture: str, list or None
334 @param architecture: Architectures to limit to (or None if no limit)
336 @type session: Session
337 @param session: Optional SQL session object (a temporary one will be
338 generated if not supplied)
341 @return: list of DBBinary objects for the given name (may be empty)
344 q = session.query(DBBinary).filter_by(package=package)
346 if version is not None:
347 q = q.filter_by(version=version)
349 if architecture is not None:
350 if not isinstance(architecture, list):
351 architecture = [architecture]
352 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
358 __all__.append('get_binaries_from_name')
361 def get_binaries_from_source_id(source_id, session=None):
363 Returns list of DBBinary objects for given C{source_id}
366 @param source_id: source_id to search for
368 @type session: Session
369 @param session: Optional SQL session object (a temporary one will be
370 generated if not supplied)
373 @return: list of DBBinary objects for the given name (may be empty)
376 return session.query(DBBinary).filter_by(source_id=source_id).all()
378 __all__.append('get_binaries_from_source_id')
381 def get_binary_from_name_suite(package, suitename, session=None):
382 ### For dak examine-package
383 ### XXX: Doesn't use object API yet
385 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
386 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
387 WHERE b.package='%(package)s'
389 AND fi.location = l.id
390 AND l.component = c.id
393 AND su.suite_name %(suitename)s
394 ORDER BY b.version DESC"""
396 return session.execute(sql % {'package': package, 'suitename': suitename})
398 __all__.append('get_binary_from_name_suite')
401 def get_binary_components(package, suitename, arch, session=None):
402 # Check for packages that have moved from one component to another
403 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
404 WHERE b.package=:package AND s.suite_name=:suitename
405 AND (a.arch_string = :arch OR a.arch_string = 'all')
406 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
407 AND f.location = l.id
408 AND l.component = c.id
411 vals = {'package': package, 'suitename': suitename, 'arch': arch}
413 return session.execute(query, vals)
415 __all__.append('get_binary_components')
417 ################################################################################
419 class BinaryACL(object):
420 def __init__(self, *args, **kwargs):
424 return '<BinaryACL %s>' % self.binary_acl_id
426 __all__.append('BinaryACL')
428 ################################################################################
430 class BinaryACLMap(object):
431 def __init__(self, *args, **kwargs):
435 return '<BinaryACLMap %s>' % self.binary_acl_map_id
437 __all__.append('BinaryACLMap')
439 ################################################################################
444 ArchiveDir "%(archivepath)s";
445 OverrideDir "%(overridedir)s";
446 CacheDir "%(cachedir)s";
451 Packages::Compress ". bzip2 gzip";
452 Sources::Compress ". bzip2 gzip";
457 bindirectory "incoming"
462 BinOverride "override.sid.all3";
463 BinCacheDB "packages-accepted.db";
465 FileList "%(filelist)s";
468 Packages::Extensions ".deb .udeb";
471 bindirectory "incoming/"
474 BinOverride "override.sid.all3";
475 SrcOverride "override.sid.all3.src";
476 FileList "%(filelist)s";
480 class BuildQueue(object):
481 def __init__(self, *args, **kwargs):
485 return '<BuildQueue %s>' % self.queue_name
487 def write_metadata(self, starttime, force=False):
488 # Do we write out metafiles?
489 if not (force or self.generate_metadata):
492 session = DBConn().session().object_session(self)
494 fl_fd = fl_name = ac_fd = ac_name = None
496 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
497 startdir = os.getcwd()
500 # Grab files we want to include
501 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
502 # Write file list with newer files
503 (fl_fd, fl_name) = mkstemp()
505 os.write(fl_fd, '%s\n' % n.fullpath)
510 # Write minimal apt.conf
511 # TODO: Remove hardcoding from template
512 (ac_fd, ac_name) = mkstemp()
513 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
515 'cachedir': cnf["Dir::Cache"],
516 'overridedir': cnf["Dir::Override"],
520 # Run apt-ftparchive generate
521 os.chdir(os.path.dirname(ac_name))
522 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
524 # Run apt-ftparchive release
525 # TODO: Eww - fix this
526 bname = os.path.basename(self.path)
530 # We have to remove the Release file otherwise it'll be included in the
533 os.unlink(os.path.join(bname, 'Release'))
537 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))
539 # Crude hack with open and append, but this whole section is and should be redone.
540 if self.notautomatic:
541 release=open("Release", "a")
542 release.write("NotAutomatic: yes")
547 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
548 if cnf.has_key("Dinstall::SigningPubKeyring"):
549 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
551 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
553 # Move the files if we got this far
554 os.rename('Release', os.path.join(bname, 'Release'))
556 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
558 # Clean up any left behind files
585 def clean_and_update(self, starttime, Logger, dryrun=False):
586 """WARNING: This routine commits for you"""
587 session = DBConn().session().object_session(self)
589 if self.generate_metadata and not dryrun:
590 self.write_metadata(starttime)
592 # Grab files older than our execution time
593 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
599 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
601 Logger.log(["I: Removing %s from the queue" % o.fullpath])
602 os.unlink(o.fullpath)
605 # If it wasn't there, don't worry
606 if e.errno == ENOENT:
609 # TODO: Replace with proper logging call
610 Logger.log(["E: Could not remove %s" % o.fullpath])
617 for f in os.listdir(self.path):
618 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
622 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
623 except NoResultFound:
624 fp = os.path.join(self.path, f)
626 Logger.log(["I: Would remove unused link %s" % fp])
628 Logger.log(["I: Removing unused link %s" % fp])
632 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
634 def add_file_from_pool(self, poolfile):
635 """Copies a file into the pool. Assumes that the PoolFile object is
636 attached to the same SQLAlchemy session as the Queue object is.
638 The caller is responsible for committing after calling this function."""
639 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
641 # Check if we have a file of this name or this ID already
642 for f in self.queuefiles:
643 if f.fileid is not None and f.fileid == poolfile.file_id or \
644 f.poolfile.filename == poolfile_basename:
645 # In this case, update the BuildQueueFile entry so we
646 # don't remove it too early
647 f.lastused = datetime.now()
648 DBConn().session().object_session(poolfile).add(f)
651 # Prepare BuildQueueFile object
652 qf = BuildQueueFile()
653 qf.build_queue_id = self.queue_id
654 qf.lastused = datetime.now()
655 qf.filename = poolfile_basename
657 targetpath = poolfile.fullpath
658 queuepath = os.path.join(self.path, poolfile_basename)
662 # We need to copy instead of symlink
664 utils.copy(targetpath, queuepath)
665 # NULL in the fileid field implies a copy
668 os.symlink(targetpath, queuepath)
669 qf.fileid = poolfile.file_id
673 # Get the same session as the PoolFile is using and add the qf to it
674 DBConn().session().object_session(poolfile).add(qf)
679 __all__.append('BuildQueue')
682 def get_build_queue(queuename, session=None):
684 Returns BuildQueue object for given C{queue name}, creating it if it does not
687 @type queuename: string
688 @param queuename: The name of the queue
690 @type session: Session
691 @param session: Optional SQLA session object (a temporary one will be
692 generated if not supplied)
695 @return: BuildQueue object for the given queue
698 q = session.query(BuildQueue).filter_by(queue_name=queuename)
702 except NoResultFound:
705 __all__.append('get_build_queue')
707 ################################################################################
709 class BuildQueueFile(object):
710 def __init__(self, *args, **kwargs):
714 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
718 return os.path.join(self.buildqueue.path, self.filename)
721 __all__.append('BuildQueueFile')
723 ################################################################################
725 class ChangePendingBinary(object):
726 def __init__(self, *args, **kwargs):
730 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
732 __all__.append('ChangePendingBinary')
734 ################################################################################
736 class ChangePendingFile(object):
737 def __init__(self, *args, **kwargs):
741 return '<ChangePendingFile %s>' % self.change_pending_file_id
743 __all__.append('ChangePendingFile')
745 ################################################################################
747 class ChangePendingSource(object):
748 def __init__(self, *args, **kwargs):
752 return '<ChangePendingSource %s>' % self.change_pending_source_id
754 __all__.append('ChangePendingSource')
756 ################################################################################
758 class Component(object):
759 def __init__(self, *args, **kwargs):
762 def __eq__(self, val):
763 if isinstance(val, str):
764 return (self.component_name == val)
765 # This signals to use the normal comparison operator
766 return NotImplemented
768 def __ne__(self, val):
769 if isinstance(val, str):
770 return (self.component_name != val)
771 # This signals to use the normal comparison operator
772 return NotImplemented
775 return '<Component %s>' % self.component_name
778 __all__.append('Component')
781 def get_component(component, session=None):
783 Returns database id for given C{component}.
785 @type component: string
786 @param component: The name of the override type
789 @return: the database id for the given component
792 component = component.lower()
794 q = session.query(Component).filter_by(component_name=component)
798 except NoResultFound:
801 __all__.append('get_component')
803 ################################################################################
805 class DBConfig(object):
806 def __init__(self, *args, **kwargs):
810 return '<DBConfig %s>' % self.name
812 __all__.append('DBConfig')
814 ################################################################################
817 def get_or_set_contents_file_id(filename, session=None):
819 Returns database id for given filename.
821 If no matching file is found, a row is inserted.
823 @type filename: string
824 @param filename: The filename
825 @type session: SQLAlchemy
826 @param session: Optional SQL session object (a temporary one will be
827 generated if not supplied). If not passed, a commit will be performed at
828 the end of the function, otherwise the caller is responsible for commiting.
831 @return: the database id for the given component
834 q = session.query(ContentFilename).filter_by(filename=filename)
837 ret = q.one().cafilename_id
838 except NoResultFound:
839 cf = ContentFilename()
840 cf.filename = filename
842 session.commit_or_flush()
843 ret = cf.cafilename_id
847 __all__.append('get_or_set_contents_file_id')
850 def get_contents(suite, overridetype, section=None, session=None):
852 Returns contents for a suite / overridetype combination, limiting
853 to a section if not None.
856 @param suite: Suite object
858 @type overridetype: OverrideType
859 @param overridetype: OverrideType object
861 @type section: Section
862 @param section: Optional section object to limit results to
864 @type session: SQLAlchemy
865 @param session: Optional SQL session object (a temporary one will be
866 generated if not supplied)
869 @return: ResultsProxy object set up to return tuples of (filename, section,
873 # find me all of the contents for a given suite
874 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
878 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
879 JOIN content_file_names n ON (c.filename=n.id)
880 JOIN binaries b ON (b.id=c.binary_pkg)
881 JOIN override o ON (o.package=b.package)
882 JOIN section s ON (s.id=o.section)
883 WHERE o.suite = :suiteid AND o.type = :overridetypeid
884 AND b.type=:overridetypename"""
886 vals = {'suiteid': suite.suite_id,
887 'overridetypeid': overridetype.overridetype_id,
888 'overridetypename': overridetype.overridetype}
890 if section is not None:
891 contents_q += " AND s.id = :sectionid"
892 vals['sectionid'] = section.section_id
894 contents_q += " ORDER BY fn"
896 return session.execute(contents_q, vals)
898 __all__.append('get_contents')
900 ################################################################################
902 class ContentFilepath(object):
903 def __init__(self, *args, **kwargs):
907 return '<ContentFilepath %s>' % self.filepath
909 __all__.append('ContentFilepath')
912 def get_or_set_contents_path_id(filepath, session=None):
914 Returns database id for given path.
916 If no matching file is found, a row is inserted.
918 @type filepath: string
919 @param filepath: The filepath
921 @type session: SQLAlchemy
922 @param session: Optional SQL session object (a temporary one will be
923 generated if not supplied). If not passed, a commit will be performed at
924 the end of the function, otherwise the caller is responsible for commiting.
927 @return: the database id for the given path
930 q = session.query(ContentFilepath).filter_by(filepath=filepath)
933 ret = q.one().cafilepath_id
934 except NoResultFound:
935 cf = ContentFilepath()
936 cf.filepath = filepath
938 session.commit_or_flush()
939 ret = cf.cafilepath_id
943 __all__.append('get_or_set_contents_path_id')
945 ################################################################################
947 class ContentAssociation(object):
948 def __init__(self, *args, **kwargs):
952 return '<ContentAssociation %s>' % self.ca_id
954 __all__.append('ContentAssociation')
956 def insert_content_paths(binary_id, fullpaths, session=None):
958 Make sure given path is associated with given binary id
961 @param binary_id: the id of the binary
962 @type fullpaths: list
963 @param fullpaths: the list of paths of the file being associated with the binary
964 @type session: SQLAlchemy session
965 @param session: Optional SQLAlchemy session. If this is passed, the caller
966 is responsible for ensuring a transaction has begun and committing the
967 results or rolling back based on the result code. If not passed, a commit
968 will be performed at the end of the function, otherwise the caller is
969 responsible for commiting.
971 @return: True upon success
976 session = DBConn().session()
981 def generate_path_dicts():
982 for fullpath in fullpaths:
983 if fullpath.startswith( './' ):
984 fullpath = fullpath[2:]
986 yield {'filename':fullpath, 'id': binary_id }
988 for d in generate_path_dicts():
989 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
998 traceback.print_exc()
1000 # Only rollback if we set up the session ourself
1007 __all__.append('insert_content_paths')
1009 ################################################################################
1011 class DSCFile(object):
1012 def __init__(self, *args, **kwargs):
1016 return '<DSCFile %s>' % self.dscfile_id
1018 __all__.append('DSCFile')
1021 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1023 Returns a list of DSCFiles which may be empty
1025 @type dscfile_id: int (optional)
1026 @param dscfile_id: the dscfile_id of the DSCFiles to find
1028 @type source_id: int (optional)
1029 @param source_id: the source id related to the DSCFiles to find
1031 @type poolfile_id: int (optional)
1032 @param poolfile_id: the poolfile id related to the DSCFiles to find
1035 @return: Possibly empty list of DSCFiles
1038 q = session.query(DSCFile)
1040 if dscfile_id is not None:
1041 q = q.filter_by(dscfile_id=dscfile_id)
1043 if source_id is not None:
1044 q = q.filter_by(source_id=source_id)
1046 if poolfile_id is not None:
1047 q = q.filter_by(poolfile_id=poolfile_id)
1051 __all__.append('get_dscfiles')
1053 ################################################################################
1055 class PoolFile(object):
1056 def __init__(self, *args, **kwargs):
1060 return '<PoolFile %s>' % self.filename
1064 return os.path.join(self.location.path, self.filename)
1066 __all__.append('PoolFile')
1069 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1072 (ValidFileFound [boolean or None], PoolFile object or None)
1074 @type filename: string
1075 @param filename: the filename of the file to check against the DB
1078 @param filesize: the size of the file to check against the DB
1080 @type md5sum: string
1081 @param md5sum: the md5sum of the file to check against the DB
1083 @type location_id: int
1084 @param location_id: the id of the location to look in
1087 @return: Tuple of length 2.
1088 - If more than one file found with that name: (C{None}, C{None})
1089 - If valid pool file found: (C{True}, C{PoolFile object})
1090 - If valid pool file not found:
1091 - (C{False}, C{None}) if no file found
1092 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1095 q = session.query(PoolFile).filter_by(filename=filename)
1096 q = q.join(Location).filter_by(location_id=location_id)
1106 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1114 __all__.append('check_poolfile')
1117 def get_poolfile_by_id(file_id, session=None):
1119 Returns a PoolFile objects or None for the given id
1122 @param file_id: the id of the file to look for
1124 @rtype: PoolFile or None
1125 @return: either the PoolFile object or None
1128 q = session.query(PoolFile).filter_by(file_id=file_id)
1132 except NoResultFound:
1135 __all__.append('get_poolfile_by_id')
1139 def get_poolfile_by_name(filename, location_id=None, session=None):
1141 Returns an array of PoolFile objects for the given filename and
1142 (optionally) location_id
1144 @type filename: string
1145 @param filename: the filename of the file to check against the DB
1147 @type location_id: int
1148 @param location_id: the id of the location to look in (optional)
1151 @return: array of PoolFile objects
1154 q = session.query(PoolFile).filter_by(filename=filename)
1156 if location_id is not None:
1157 q = q.join(Location).filter_by(location_id=location_id)
1161 __all__.append('get_poolfile_by_name')
1164 def get_poolfile_like_name(filename, session=None):
1166 Returns an array of PoolFile objects which are like the given name
1168 @type filename: string
1169 @param filename: the filename of the file to check against the DB
1172 @return: array of PoolFile objects
1175 # TODO: There must be a way of properly using bind parameters with %FOO%
1176 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1180 __all__.append('get_poolfile_like_name')
1183 def add_poolfile(filename, datadict, location_id, session=None):
1185 Add a new file to the pool
1187 @type filename: string
1188 @param filename: filename
1190 @type datadict: dict
1191 @param datadict: dict with needed data
1193 @type location_id: int
1194 @param location_id: database id of the location
1197 @return: the PoolFile object created
1199 poolfile = PoolFile()
1200 poolfile.filename = filename
1201 poolfile.filesize = datadict["size"]
1202 poolfile.md5sum = datadict["md5sum"]
1203 poolfile.sha1sum = datadict["sha1sum"]
1204 poolfile.sha256sum = datadict["sha256sum"]
1205 poolfile.location_id = location_id
1207 session.add(poolfile)
1208 # Flush to get a file id (NB: This is not a commit)
1213 __all__.append('add_poolfile')
1215 ################################################################################
1217 class Fingerprint(object):
1218 def __init__(self, *args, **kwargs):
1222 return '<Fingerprint %s>' % self.fingerprint
1224 __all__.append('Fingerprint')
1227 def get_fingerprint(fpr, session=None):
1229 Returns Fingerprint object for given fpr.
1232 @param fpr: The fpr to find / add
1234 @type session: SQLAlchemy
1235 @param session: Optional SQL session object (a temporary one will be
1236 generated if not supplied).
1239 @return: the Fingerprint object for the given fpr or None
1242 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1246 except NoResultFound:
1251 __all__.append('get_fingerprint')
1254 def get_or_set_fingerprint(fpr, session=None):
1256 Returns Fingerprint object for given fpr.
1258 If no matching fpr is found, a row is inserted.
1261 @param fpr: The fpr to find / add
1263 @type session: SQLAlchemy
1264 @param session: Optional SQL session object (a temporary one will be
1265 generated if not supplied). If not passed, a commit will be performed at
1266 the end of the function, otherwise the caller is responsible for commiting.
1267 A flush will be performed either way.
1270 @return: the Fingerprint object for the given fpr
1273 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1277 except NoResultFound:
1278 fingerprint = Fingerprint()
1279 fingerprint.fingerprint = fpr
1280 session.add(fingerprint)
1281 session.commit_or_flush()
1286 __all__.append('get_or_set_fingerprint')
1288 ################################################################################
1290 # Helper routine for Keyring class
1291 def get_ldap_name(entry):
1293 for k in ["cn", "mn", "sn"]:
1295 if ret and ret[0] != "" and ret[0] != "-":
1297 return " ".join(name)
1299 ################################################################################
1301 class Keyring(object):
1302 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1303 " --with-colons --fingerprint --fingerprint"
1308 def __init__(self, *args, **kwargs):
1312 return '<Keyring %s>' % self.keyring_name
1314 def de_escape_gpg_str(self, txt):
1315 esclist = re.split(r'(\\x..)', txt)
1316 for x in range(1,len(esclist),2):
1317 esclist[x] = "%c" % (int(esclist[x][2:],16))
1318 return "".join(esclist)
1320 def load_keys(self, keyring):
1323 if not self.keyring_id:
1324 raise Exception('Must be initialized with database information')
1326 k = os.popen(self.gpg_invocation % keyring, "r")
1330 for line in k.xreadlines():
1331 field = line.split(":")
1332 if field[0] == "pub":
1334 (name, addr) = email.Utils.parseaddr(field[9])
1335 name = re.sub(r"\s*[(].*[)]", "", name)
1336 if name == "" or addr == "" or "@" not in addr:
1338 addr = "invalid-uid"
1339 name = self.de_escape_gpg_str(name)
1340 self.keys[key] = {"email": addr}
1342 self.keys[key]["name"] = name
1343 self.keys[key]["aliases"] = [name]
1344 self.keys[key]["fingerprints"] = []
1346 elif key and field[0] == "sub" and len(field) >= 12:
1347 signingkey = ("s" in field[11])
1348 elif key and field[0] == "uid":
1349 (name, addr) = email.Utils.parseaddr(field[9])
1350 if name and name not in self.keys[key]["aliases"]:
1351 self.keys[key]["aliases"].append(name)
1352 elif signingkey and field[0] == "fpr":
1353 self.keys[key]["fingerprints"].append(field[9])
1354 self.fpr_lookup[field[9]] = key
1356 def import_users_from_ldap(self, session):
1360 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1361 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1363 l = ldap.open(LDAPServer)
1364 l.simple_bind_s("","")
1365 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1366 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1367 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1369 ldap_fin_uid_id = {}
1376 uid = entry["uid"][0]
1377 name = get_ldap_name(entry)
1378 fingerprints = entry["keyFingerPrint"]
1380 for f in fingerprints:
1381 key = self.fpr_lookup.get(f, None)
1382 if key not in self.keys:
1384 self.keys[key]["uid"] = uid
1388 keyid = get_or_set_uid(uid, session).uid_id
1389 byuid[keyid] = (uid, name)
1390 byname[uid] = (keyid, name)
1392 return (byname, byuid)
1394 def generate_users_from_keyring(self, format, session):
1398 for x in self.keys.keys():
1399 if self.keys[x]["email"] == "invalid-uid":
1401 self.keys[x]["uid"] = format % "invalid-uid"
1403 uid = format % self.keys[x]["email"]
1404 keyid = get_or_set_uid(uid, session).uid_id
1405 byuid[keyid] = (uid, self.keys[x]["name"])
1406 byname[uid] = (keyid, self.keys[x]["name"])
1407 self.keys[x]["uid"] = uid
1410 uid = format % "invalid-uid"
1411 keyid = get_or_set_uid(uid, session).uid_id
1412 byuid[keyid] = (uid, "ungeneratable user id")
1413 byname[uid] = (keyid, "ungeneratable user id")
1415 return (byname, byuid)
1417 __all__.append('Keyring')
1420 def get_keyring(keyring, session=None):
1422 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1423 If C{keyring} already has an entry, simply return the existing Keyring
1425 @type keyring: string
1426 @param keyring: the keyring name
1429 @return: the Keyring object for this keyring
1432 q = session.query(Keyring).filter_by(keyring_name=keyring)
1436 except NoResultFound:
1439 __all__.append('get_keyring')
1441 ################################################################################
1443 class KeyringACLMap(object):
1444 def __init__(self, *args, **kwargs):
1448 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1450 __all__.append('KeyringACLMap')
1452 ################################################################################
1454 class DBChange(object):
1455 def __init__(self, *args, **kwargs):
1459 return '<DBChange %s>' % self.changesname
1461 def clean_from_queue(self):
1462 session = DBConn().session().object_session(self)
1464 # Remove changes_pool_files entries
1467 # Remove changes_pending_files references
1470 # Clear out of queue
1471 self.in_queue = None
1472 self.approved_for_id = None
1474 __all__.append('DBChange')
1477 def get_dbchange(filename, session=None):
1479 returns DBChange object for given C{filename}.
1481 @type filename: string
1482 @param filename: the name of the file
1484 @type session: Session
1485 @param session: Optional SQLA session object (a temporary one will be
1486 generated if not supplied)
1489 @return: DBChange object for the given filename (C{None} if not present)
1492 q = session.query(DBChange).filter_by(changesname=filename)
1496 except NoResultFound:
1499 __all__.append('get_dbchange')
1501 ################################################################################
1503 class Location(object):
1504 def __init__(self, *args, **kwargs):
1508 return '<Location %s (%s)>' % (self.path, self.location_id)
1510 __all__.append('Location')
1513 def get_location(location, component=None, archive=None, session=None):
1515 Returns Location object for the given combination of location, component
1518 @type location: string
1519 @param location: the path of the location, e.g. I{/srv/ftp-master.debian.org/ftp/pool/}
1521 @type component: string
1522 @param component: the component name (if None, no restriction applied)
1524 @type archive: string
1525 @param archive: the archive name (if None, no restriction applied)
1527 @rtype: Location / None
1528 @return: Either a Location object or None if one can't be found
1531 q = session.query(Location).filter_by(path=location)
1533 if archive is not None:
1534 q = q.join(Archive).filter_by(archive_name=archive)
1536 if component is not None:
1537 q = q.join(Component).filter_by(component_name=component)
1541 except NoResultFound:
1544 __all__.append('get_location')
1546 ################################################################################
1548 class Maintainer(object):
1549 def __init__(self, *args, **kwargs):
1553 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1555 def get_split_maintainer(self):
1556 if not hasattr(self, 'name') or self.name is None:
1557 return ('', '', '', '')
1559 return fix_maintainer(self.name.strip())
1561 __all__.append('Maintainer')
1564 def get_or_set_maintainer(name, session=None):
1566 Returns Maintainer object for given maintainer name.
1568 If no matching maintainer name is found, a row is inserted.
1571 @param name: The maintainer name to add
1573 @type session: SQLAlchemy
1574 @param session: Optional SQL session object (a temporary one will be
1575 generated if not supplied). If not passed, a commit will be performed at
1576 the end of the function, otherwise the caller is responsible for commiting.
1577 A flush will be performed either way.
1580 @return: the Maintainer object for the given maintainer
1583 q = session.query(Maintainer).filter_by(name=name)
1586 except NoResultFound:
1587 maintainer = Maintainer()
1588 maintainer.name = name
1589 session.add(maintainer)
1590 session.commit_or_flush()
1595 __all__.append('get_or_set_maintainer')
1598 def get_maintainer(maintainer_id, session=None):
1600 Return the name of the maintainer behind C{maintainer_id} or None if that
1601 maintainer_id is invalid.
1603 @type maintainer_id: int
1604 @param maintainer_id: the id of the maintainer
1607 @return: the Maintainer with this C{maintainer_id}
1610 return session.query(Maintainer).get(maintainer_id)
1612 __all__.append('get_maintainer')
1614 ################################################################################
1616 class NewComment(object):
1617 def __init__(self, *args, **kwargs):
1621 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1623 __all__.append('NewComment')
1626 def has_new_comment(package, version, session=None):
1628 Returns true if the given combination of C{package}, C{version} has a comment.
1630 @type package: string
1631 @param package: name of the package
1633 @type version: string
1634 @param version: package version
1636 @type session: Session
1637 @param session: Optional SQLA session object (a temporary one will be
1638 generated if not supplied)
1644 q = session.query(NewComment)
1645 q = q.filter_by(package=package)
1646 q = q.filter_by(version=version)
1648 return bool(q.count() > 0)
1650 __all__.append('has_new_comment')
1653 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1655 Returns (possibly empty) list of NewComment objects for the given
1658 @type package: string (optional)
1659 @param package: name of the package
1661 @type version: string (optional)
1662 @param version: package version
1664 @type comment_id: int (optional)
1665 @param comment_id: An id of a comment
1667 @type session: Session
1668 @param session: Optional SQLA session object (a temporary one will be
1669 generated if not supplied)
1672 @return: A (possibly empty) list of NewComment objects will be returned
1675 q = session.query(NewComment)
1676 if package is not None: q = q.filter_by(package=package)
1677 if version is not None: q = q.filter_by(version=version)
1678 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1682 __all__.append('get_new_comments')
1684 ################################################################################
1686 class Override(object):
1687 def __init__(self, *args, **kwargs):
1691 return '<Override %s (%s)>' % (self.package, self.suite_id)
1693 __all__.append('Override')
1696 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1698 Returns Override object for the given parameters
1700 @type package: string
1701 @param package: The name of the package
1703 @type suite: string, list or None
1704 @param suite: The name of the suite (or suites if a list) to limit to. If
1705 None, don't limit. Defaults to None.
1707 @type component: string, list or None
1708 @param component: The name of the component (or components if a list) to
1709 limit to. If None, don't limit. Defaults to None.
1711 @type overridetype: string, list or None
1712 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1713 limit to. If None, don't limit. Defaults to None.
1715 @type session: Session
1716 @param session: Optional SQLA session object (a temporary one will be
1717 generated if not supplied)
1720 @return: A (possibly empty) list of Override objects will be returned
1723 q = session.query(Override)
1724 q = q.filter_by(package=package)
1726 if suite is not None:
1727 if not isinstance(suite, list): suite = [suite]
1728 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1730 if component is not None:
1731 if not isinstance(component, list): component = [component]
1732 q = q.join(Component).filter(Component.component_name.in_(component))
1734 if overridetype is not None:
1735 if not isinstance(overridetype, list): overridetype = [overridetype]
1736 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1740 __all__.append('get_override')
1743 ################################################################################
1745 class OverrideType(object):
1746 def __init__(self, *args, **kwargs):
1750 return '<OverrideType %s>' % self.overridetype
1752 __all__.append('OverrideType')
1755 def get_override_type(override_type, session=None):
1757 Returns OverrideType object for given C{override type}.
1759 @type override_type: string
1760 @param override_type: The name of the override type
1762 @type session: Session
1763 @param session: Optional SQLA session object (a temporary one will be
1764 generated if not supplied)
1767 @return: the database id for the given override type
1770 q = session.query(OverrideType).filter_by(overridetype=override_type)
1774 except NoResultFound:
1777 __all__.append('get_override_type')
1779 ################################################################################
1781 class DebContents(object):
1782 def __init__(self, *args, **kwargs):
1786 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1788 __all__.append('DebContents')
1791 class UdebContents(object):
1792 def __init__(self, *args, **kwargs):
1796 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1798 __all__.append('UdebContents')
1800 class PendingBinContents(object):
1801 def __init__(self, *args, **kwargs):
1805 return '<PendingBinContents %s>' % self.contents_id
1807 __all__.append('PendingBinContents')
1809 def insert_pending_content_paths(package,
1814 Make sure given paths are temporarily associated with given
1818 @param package: the package to associate with should have been read in from the binary control file
1819 @type fullpaths: list
1820 @param fullpaths: the list of paths of the file being associated with the binary
1821 @type session: SQLAlchemy session
1822 @param session: Optional SQLAlchemy session. If this is passed, the caller
1823 is responsible for ensuring a transaction has begun and committing the
1824 results or rolling back based on the result code. If not passed, a commit
1825 will be performed at the end of the function
1827 @return: True upon success, False if there is a problem
1830 privatetrans = False
1833 session = DBConn().session()
1837 arch = get_architecture(package['Architecture'], session)
1838 arch_id = arch.arch_id
1840 # Remove any already existing recorded files for this package
1841 q = session.query(PendingBinContents)
1842 q = q.filter_by(package=package['Package'])
1843 q = q.filter_by(version=package['Version'])
1844 q = q.filter_by(architecture=arch_id)
1847 for fullpath in fullpaths:
1849 if fullpath.startswith( "./" ):
1850 fullpath = fullpath[2:]
1852 pca = PendingBinContents()
1853 pca.package = package['Package']
1854 pca.version = package['Version']
1856 pca.architecture = arch_id
1859 pca.type = 8 # gross
1861 pca.type = 7 # also gross
1864 # Only commit if we set up the session ourself
1872 except Exception, e:
1873 traceback.print_exc()
1875 # Only rollback if we set up the session ourself
1882 __all__.append('insert_pending_content_paths')
1884 ################################################################################
1886 class PolicyQueue(object):
1887 def __init__(self, *args, **kwargs):
1891 return '<PolicyQueue %s>' % self.queue_name
1893 __all__.append('PolicyQueue')
1896 def get_policy_queue(queuename, session=None):
1898 Returns PolicyQueue object for given C{queue name}
1900 @type queuename: string
1901 @param queuename: The name of the queue
1903 @type session: Session
1904 @param session: Optional SQLA session object (a temporary one will be
1905 generated if not supplied)
1908 @return: PolicyQueue object for the given queue
1911 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1915 except NoResultFound:
1918 __all__.append('get_policy_queue')
1921 def get_policy_queue_from_path(pathname, session=None):
1923 Returns PolicyQueue object for given C{path name}
1925 @type queuename: string
1926 @param queuename: The path
1928 @type session: Session
1929 @param session: Optional SQLA session object (a temporary one will be
1930 generated if not supplied)
1933 @return: PolicyQueue object for the given queue
1936 q = session.query(PolicyQueue).filter_by(path=pathname)
1940 except NoResultFound:
1943 __all__.append('get_policy_queue_from_path')
1945 ################################################################################
1947 class Priority(object):
1948 def __init__(self, *args, **kwargs):
1951 def __eq__(self, val):
1952 if isinstance(val, str):
1953 return (self.priority == val)
1954 # This signals to use the normal comparison operator
1955 return NotImplemented
1957 def __ne__(self, val):
1958 if isinstance(val, str):
1959 return (self.priority != val)
1960 # This signals to use the normal comparison operator
1961 return NotImplemented
1964 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1966 __all__.append('Priority')
1969 def get_priority(priority, session=None):
1971 Returns Priority object for given C{priority name}.
1973 @type priority: string
1974 @param priority: The name of the priority
1976 @type session: Session
1977 @param session: Optional SQLA session object (a temporary one will be
1978 generated if not supplied)
1981 @return: Priority object for the given priority
1984 q = session.query(Priority).filter_by(priority=priority)
1988 except NoResultFound:
1991 __all__.append('get_priority')
1994 def get_priorities(session=None):
1996 Returns dictionary of priority names -> id mappings
1998 @type session: Session
1999 @param session: Optional SQL session object (a temporary one will be
2000 generated if not supplied)
2003 @return: dictionary of priority names -> id mappings
2007 q = session.query(Priority)
2009 ret[x.priority] = x.priority_id
2013 __all__.append('get_priorities')
2015 ################################################################################
2017 class Section(object):
2018 def __init__(self, *args, **kwargs):
2021 def __eq__(self, val):
2022 if isinstance(val, str):
2023 return (self.section == val)
2024 # This signals to use the normal comparison operator
2025 return NotImplemented
2027 def __ne__(self, val):
2028 if isinstance(val, str):
2029 return (self.section != val)
2030 # This signals to use the normal comparison operator
2031 return NotImplemented
2034 return '<Section %s>' % self.section
2036 __all__.append('Section')
2039 def get_section(section, session=None):
2041 Returns Section object for given C{section name}.
2043 @type section: string
2044 @param section: The name of the section
2046 @type session: Session
2047 @param session: Optional SQLA session object (a temporary one will be
2048 generated if not supplied)
2051 @return: Section object for the given section name
2054 q = session.query(Section).filter_by(section=section)
2058 except NoResultFound:
2061 __all__.append('get_section')
2064 def get_sections(session=None):
2066 Returns dictionary of section names -> id mappings
2068 @type session: Session
2069 @param session: Optional SQL session object (a temporary one will be
2070 generated if not supplied)
2073 @return: dictionary of section names -> id mappings
2077 q = session.query(Section)
2079 ret[x.section] = x.section_id
2083 __all__.append('get_sections')
2085 ################################################################################
2087 class DBSource(object):
2088 def __init__(self, *args, **kwargs):
2092 return '<DBSource %s (%s)>' % (self.source, self.version)
2094 __all__.append('DBSource')
2097 def source_exists(source, source_version, suites = ["any"], session=None):
2099 Ensure that source exists somewhere in the archive for the binary
2100 upload being processed.
2101 1. exact match => 1.0-3
2102 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2104 @type source: string
2105 @param source: source name
2107 @type source_version: string
2108 @param source_version: expected source version
2111 @param suites: list of suites to check in, default I{any}
2113 @type session: Session
2114 @param session: Optional SQLA session object (a temporary one will be
2115 generated if not supplied)
2118 @return: returns 1 if a source with expected version is found, otherwise 0
2125 for suite in suites:
2126 q = session.query(DBSource).filter_by(source=source)
2128 # source must exist in suite X, or in some other suite that's
2129 # mapped to X, recursively... silent-maps are counted too,
2130 # unreleased-maps aren't.
2131 maps = cnf.ValueList("SuiteMappings")[:]
2133 maps = [ m.split() for m in maps ]
2134 maps = [ (x[1], x[2]) for x in maps
2135 if x[0] == "map" or x[0] == "silent-map" ]
2138 if x[1] in s and x[0] not in s:
2141 q = q.join(SrcAssociation).join(Suite)
2142 q = q.filter(Suite.suite_name.in_(s))
2144 # Reduce the query results to a list of version numbers
2145 ql = [ j.version for j in q.all() ]
2148 if source_version in ql:
2152 from daklib.regexes import re_bin_only_nmu
2153 orig_source_version = re_bin_only_nmu.sub('', source_version)
2154 if orig_source_version in ql:
2157 # No source found so return not ok
2162 __all__.append('source_exists')
2165 def get_suites_source_in(source, session=None):
2167 Returns list of Suite objects which given C{source} name is in
2170 @param source: DBSource package name to search for
2173 @return: list of Suite objects for the given source
2176 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2178 __all__.append('get_suites_source_in')
2181 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2183 Returns list of DBSource objects for given C{source} name and other parameters
2186 @param source: DBSource package name to search for
2188 @type version: str or None
2189 @param version: DBSource version name to search for or None if not applicable
2191 @type dm_upload_allowed: bool
2192 @param dm_upload_allowed: If None, no effect. If True or False, only
2193 return packages with that dm_upload_allowed setting
2195 @type session: Session
2196 @param session: Optional SQL session object (a temporary one will be
2197 generated if not supplied)
2200 @return: list of DBSource objects for the given name (may be empty)
2203 q = session.query(DBSource).filter_by(source=source)
2205 if version is not None:
2206 q = q.filter_by(version=version)
2208 if dm_upload_allowed is not None:
2209 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2213 __all__.append('get_sources_from_name')
2216 def get_source_in_suite(source, suite, session=None):
2218 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2220 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2221 - B{suite} - a suite name, eg. I{unstable}
2223 @type source: string
2224 @param source: source package name
2227 @param suite: the suite name
2230 @return: the version for I{source} in I{suite}
2234 q = session.query(SrcAssociation)
2235 q = q.join('source').filter_by(source=source)
2236 q = q.join('suite').filter_by(suite_name=suite)
2239 return q.one().source
2240 except NoResultFound:
2243 __all__.append('get_source_in_suite')
2245 ################################################################################
2248 def add_dsc_to_db(u, filename, session=None):
2249 entry = u.pkg.files[filename]
2253 source.source = u.pkg.dsc["source"]
2254 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2255 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2256 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2257 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2258 source.install_date = datetime.now().date()
2260 dsc_component = entry["component"]
2261 dsc_location_id = entry["location id"]
2263 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2265 # Set up a new poolfile if necessary
2266 if not entry.has_key("files id") or not entry["files id"]:
2267 filename = entry["pool name"] + filename
2268 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2270 pfs.append(poolfile)
2271 entry["files id"] = poolfile.file_id
2273 source.poolfile_id = entry["files id"]
2277 for suite_name in u.pkg.changes["distribution"].keys():
2278 sa = SrcAssociation()
2279 sa.source_id = source.source_id
2280 sa.suite_id = get_suite(suite_name).suite_id
2285 # Add the source files to the DB (files and dsc_files)
2287 dscfile.source_id = source.source_id
2288 dscfile.poolfile_id = entry["files id"]
2289 session.add(dscfile)
2291 for dsc_file, dentry in u.pkg.dsc_files.items():
2293 df.source_id = source.source_id
2295 # If the .orig tarball is already in the pool, it's
2296 # files id is stored in dsc_files by check_dsc().
2297 files_id = dentry.get("files id", None)
2299 # Find the entry in the files hash
2300 # TODO: Bail out here properly
2302 for f, e in u.pkg.files.items():
2307 if files_id is None:
2308 filename = dfentry["pool name"] + dsc_file
2310 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2311 # FIXME: needs to check for -1/-2 and or handle exception
2312 if found and obj is not None:
2313 files_id = obj.file_id
2316 # If still not found, add it
2317 if files_id is None:
2318 # HACK: Force sha1sum etc into dentry
2319 dentry["sha1sum"] = dfentry["sha1sum"]
2320 dentry["sha256sum"] = dfentry["sha256sum"]
2321 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2322 pfs.append(poolfile)
2323 files_id = poolfile.file_id
2325 poolfile = get_poolfile_by_id(files_id, session)
2326 if poolfile is None:
2327 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2328 pfs.append(poolfile)
2330 df.poolfile_id = files_id
2335 # Add the src_uploaders to the DB
2336 uploader_ids = [source.maintainer_id]
2337 if u.pkg.dsc.has_key("uploaders"):
2338 for up in u.pkg.dsc["uploaders"].replace(">, ", ">\t").split("\t"):
2340 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2343 for up_id in uploader_ids:
2344 if added_ids.has_key(up_id):
2346 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2352 su.maintainer_id = up_id
2353 su.source_id = source.source_id
2358 return source, dsc_component, dsc_location_id, pfs
2360 __all__.append('add_dsc_to_db')
2363 def add_deb_to_db(u, filename, session=None):
2365 Contrary to what you might expect, this routine deals with both
2366 debs and udebs. That info is in 'dbtype', whilst 'type' is
2367 'deb' for both of them
2370 entry = u.pkg.files[filename]
2373 bin.package = entry["package"]
2374 bin.version = entry["version"]
2375 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2376 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2377 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2378 bin.binarytype = entry["dbtype"]
2381 filename = entry["pool name"] + filename
2382 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2383 if not entry.get("location id", None):
2384 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2386 if entry.get("files id", None):
2387 poolfile = get_poolfile_by_id(bin.poolfile_id)
2388 bin.poolfile_id = entry["files id"]
2390 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2391 bin.poolfile_id = entry["files id"] = poolfile.file_id
2394 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2395 if len(bin_sources) != 1:
2396 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2397 (bin.package, bin.version, bin.architecture.arch_string,
2398 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2400 bin.source_id = bin_sources[0].source_id
2402 # Add and flush object so it has an ID
2406 # Add BinAssociations
2407 for suite_name in u.pkg.changes["distribution"].keys():
2408 ba = BinAssociation()
2409 ba.binary_id = bin.binary_id
2410 ba.suite_id = get_suite(suite_name).suite_id
2415 # Deal with contents - disabled for now
2416 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2418 # print "REJECT\nCould not determine contents of package %s" % bin.package
2419 # session.rollback()
2420 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2424 __all__.append('add_deb_to_db')
2426 ################################################################################
2428 class SourceACL(object):
2429 def __init__(self, *args, **kwargs):
2433 return '<SourceACL %s>' % self.source_acl_id
2435 __all__.append('SourceACL')
2437 ################################################################################
2439 class SrcAssociation(object):
2440 def __init__(self, *args, **kwargs):
2444 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2446 __all__.append('SrcAssociation')
2448 ################################################################################
2450 class SrcFormat(object):
2451 def __init__(self, *args, **kwargs):
2455 return '<SrcFormat %s>' % (self.format_name)
2457 __all__.append('SrcFormat')
2459 ################################################################################
2461 class SrcUploader(object):
2462 def __init__(self, *args, **kwargs):
2466 return '<SrcUploader %s>' % self.uploader_id
2468 __all__.append('SrcUploader')
2470 ################################################################################
2472 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2473 ('SuiteID', 'suite_id'),
2474 ('Version', 'version'),
2475 ('Origin', 'origin'),
2477 ('Description', 'description'),
2478 ('Untouchable', 'untouchable'),
2479 ('Announce', 'announce'),
2480 ('Codename', 'codename'),
2481 ('OverrideCodename', 'overridecodename'),
2482 ('ValidTime', 'validtime'),
2483 ('Priority', 'priority'),
2484 ('NotAutomatic', 'notautomatic'),
2485 ('CopyChanges', 'copychanges'),
2486 ('CopyDotDak', 'copydotdak'),
2487 ('CommentsDir', 'commentsdir'),
2488 ('OverrideSuite', 'overridesuite'),
2489 ('ChangelogBase', 'changelogbase')]
2491 class Suite(object):
2492 def __init__(self, *args, **kwargs):
2496 return '<Suite %s>' % self.suite_name
2498 def __eq__(self, val):
2499 if isinstance(val, str):
2500 return (self.suite_name == val)
2501 # This signals to use the normal comparison operator
2502 return NotImplemented
2504 def __ne__(self, val):
2505 if isinstance(val, str):
2506 return (self.suite_name != val)
2507 # This signals to use the normal comparison operator
2508 return NotImplemented
2512 for disp, field in SUITE_FIELDS:
2513 val = getattr(self, field, None)
2515 ret.append("%s: %s" % (disp, val))
2517 return "\n".join(ret)
2519 __all__.append('Suite')
2522 def get_suite_architecture(suite, architecture, session=None):
2524 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2528 @param suite: Suite name to search for
2530 @type architecture: str
2531 @param architecture: Architecture name to search for
2533 @type session: Session
2534 @param session: Optional SQL session object (a temporary one will be
2535 generated if not supplied)
2537 @rtype: SuiteArchitecture
2538 @return: the SuiteArchitecture object or None
2541 q = session.query(SuiteArchitecture)
2542 q = q.join(Architecture).filter_by(arch_string=architecture)
2543 q = q.join(Suite).filter_by(suite_name=suite)
2547 except NoResultFound:
2550 __all__.append('get_suite_architecture')
2553 def get_suite(suite, session=None):
2555 Returns Suite object for given C{suite name}.
2558 @param suite: The name of the suite
2560 @type session: Session
2561 @param session: Optional SQLA session object (a temporary one will be
2562 generated if not supplied)
2565 @return: Suite object for the requested suite name (None if not present)
2568 q = session.query(Suite).filter_by(suite_name=suite)
2572 except NoResultFound:
2575 __all__.append('get_suite')
2577 ################################################################################
2579 class SuiteArchitecture(object):
2580 def __init__(self, *args, **kwargs):
2584 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2586 __all__.append('SuiteArchitecture')
2589 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2591 Returns list of Architecture objects for given C{suite} name
2594 @param suite: Suite name to search for
2596 @type skipsrc: boolean
2597 @param skipsrc: Whether to skip returning the 'source' architecture entry
2600 @type skipall: boolean
2601 @param skipall: Whether to skip returning the 'all' architecture entry
2604 @type session: Session
2605 @param session: Optional SQL session object (a temporary one will be
2606 generated if not supplied)
2609 @return: list of Architecture objects for the given name (may be empty)
2612 q = session.query(Architecture)
2613 q = q.join(SuiteArchitecture)
2614 q = q.join(Suite).filter_by(suite_name=suite)
2617 q = q.filter(Architecture.arch_string != 'source')
2620 q = q.filter(Architecture.arch_string != 'all')
2622 q = q.order_by('arch_string')
2626 __all__.append('get_suite_architectures')
2628 ################################################################################
2630 class SuiteSrcFormat(object):
2631 def __init__(self, *args, **kwargs):
2635 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2637 __all__.append('SuiteSrcFormat')
2640 def get_suite_src_formats(suite, session=None):
2642 Returns list of allowed SrcFormat for C{suite}.
2645 @param suite: Suite name to search for
2647 @type session: Session
2648 @param session: Optional SQL session object (a temporary one will be
2649 generated if not supplied)
2652 @return: the list of allowed source formats for I{suite}
2655 q = session.query(SrcFormat)
2656 q = q.join(SuiteSrcFormat)
2657 q = q.join(Suite).filter_by(suite_name=suite)
2658 q = q.order_by('format_name')
2662 __all__.append('get_suite_src_formats')
2664 ################################################################################
2667 def __init__(self, *args, **kwargs):
2670 def __eq__(self, val):
2671 if isinstance(val, str):
2672 return (self.uid == val)
2673 # This signals to use the normal comparison operator
2674 return NotImplemented
2676 def __ne__(self, val):
2677 if isinstance(val, str):
2678 return (self.uid != val)
2679 # This signals to use the normal comparison operator
2680 return NotImplemented
2683 return '<Uid %s (%s)>' % (self.uid, self.name)
2685 __all__.append('Uid')
2688 def get_or_set_uid(uidname, session=None):
2690 Returns uid object for given uidname.
2692 If no matching uidname is found, a row is inserted.
2694 @type uidname: string
2695 @param uidname: The uid to add
2697 @type session: SQLAlchemy
2698 @param session: Optional SQL session object (a temporary one will be
2699 generated if not supplied). If not passed, a commit will be performed at
2700 the end of the function, otherwise the caller is responsible for commiting.
2703 @return: the uid object for the given uidname
2706 q = session.query(Uid).filter_by(uid=uidname)
2710 except NoResultFound:
2714 session.commit_or_flush()
2719 __all__.append('get_or_set_uid')
2722 def get_uid_from_fingerprint(fpr, session=None):
2723 q = session.query(Uid)
2724 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2728 except NoResultFound:
2731 __all__.append('get_uid_from_fingerprint')
2733 ################################################################################
2735 class UploadBlock(object):
2736 def __init__(self, *args, **kwargs):
2740 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2742 __all__.append('UploadBlock')
2744 ################################################################################
2746 class DBConn(object):
2748 database module init.
2752 def __init__(self, *args, **kwargs):
2753 self.__dict__ = self.__shared_state
2755 if not getattr(self, 'initialised', False):
2756 self.initialised = True
2757 self.debug = kwargs.has_key('debug')
2760 def __setuptables(self):
2770 'build_queue_files',
2773 'changes_pending_binaries',
2774 'changes_pending_files',
2775 'changes_pending_files_map',
2776 'changes_pending_source',
2777 'changes_pending_source_files',
2778 'changes_pool_files',
2791 'pending_bin_contents',
2801 'suite_architectures',
2802 'suite_src_formats',
2803 'suite_build_queue_copy',
2809 for table_name in tables:
2810 table = Table(table_name, self.db_meta, autoload=True)
2811 setattr(self, 'tbl_%s' % table_name, table)
2813 def __setupmappers(self):
2814 mapper(Architecture, self.tbl_architecture,
2815 properties = dict(arch_id = self.tbl_architecture.c.id))
2817 mapper(Archive, self.tbl_archive,
2818 properties = dict(archive_id = self.tbl_archive.c.id,
2819 archive_name = self.tbl_archive.c.name))
2821 mapper(BinAssociation, self.tbl_bin_associations,
2822 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2823 suite_id = self.tbl_bin_associations.c.suite,
2824 suite = relation(Suite),
2825 binary_id = self.tbl_bin_associations.c.bin,
2826 binary = relation(DBBinary)))
2828 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2829 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2830 filename = self.tbl_pending_bin_contents.c.filename,
2831 package = self.tbl_pending_bin_contents.c.package,
2832 version = self.tbl_pending_bin_contents.c.version,
2833 arch = self.tbl_pending_bin_contents.c.arch,
2834 otype = self.tbl_pending_bin_contents.c.type))
2836 mapper(DebContents, self.tbl_deb_contents,
2837 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2838 package=self.tbl_deb_contents.c.package,
2839 suite=self.tbl_deb_contents.c.suite,
2840 arch=self.tbl_deb_contents.c.arch,
2841 section=self.tbl_deb_contents.c.section,
2842 filename=self.tbl_deb_contents.c.filename))
2844 mapper(UdebContents, self.tbl_udeb_contents,
2845 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2846 package=self.tbl_udeb_contents.c.package,
2847 suite=self.tbl_udeb_contents.c.suite,
2848 arch=self.tbl_udeb_contents.c.arch,
2849 section=self.tbl_udeb_contents.c.section,
2850 filename=self.tbl_udeb_contents.c.filename))
2852 mapper(BuildQueue, self.tbl_build_queue,
2853 properties = dict(queue_id = self.tbl_build_queue.c.id))
2855 mapper(BuildQueueFile, self.tbl_build_queue_files,
2856 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2857 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2859 mapper(DBBinary, self.tbl_binaries,
2860 properties = dict(binary_id = self.tbl_binaries.c.id,
2861 package = self.tbl_binaries.c.package,
2862 version = self.tbl_binaries.c.version,
2863 maintainer_id = self.tbl_binaries.c.maintainer,
2864 maintainer = relation(Maintainer),
2865 source_id = self.tbl_binaries.c.source,
2866 source = relation(DBSource),
2867 arch_id = self.tbl_binaries.c.architecture,
2868 architecture = relation(Architecture),
2869 poolfile_id = self.tbl_binaries.c.file,
2870 poolfile = relation(PoolFile),
2871 binarytype = self.tbl_binaries.c.type,
2872 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2873 fingerprint = relation(Fingerprint),
2874 install_date = self.tbl_binaries.c.install_date,
2875 binassociations = relation(BinAssociation,
2876 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2878 mapper(BinaryACL, self.tbl_binary_acl,
2879 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2881 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2882 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2883 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2884 architecture = relation(Architecture)))
2886 mapper(Component, self.tbl_component,
2887 properties = dict(component_id = self.tbl_component.c.id,
2888 component_name = self.tbl_component.c.name))
2890 mapper(DBConfig, self.tbl_config,
2891 properties = dict(config_id = self.tbl_config.c.id))
2893 mapper(DSCFile, self.tbl_dsc_files,
2894 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2895 source_id = self.tbl_dsc_files.c.source,
2896 source = relation(DBSource),
2897 poolfile_id = self.tbl_dsc_files.c.file,
2898 poolfile = relation(PoolFile)))
2900 mapper(PoolFile, self.tbl_files,
2901 properties = dict(file_id = self.tbl_files.c.id,
2902 filesize = self.tbl_files.c.size,
2903 location_id = self.tbl_files.c.location,
2904 location = relation(Location)))
2906 mapper(Fingerprint, self.tbl_fingerprint,
2907 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2908 uid_id = self.tbl_fingerprint.c.uid,
2909 uid = relation(Uid),
2910 keyring_id = self.tbl_fingerprint.c.keyring,
2911 keyring = relation(Keyring),
2912 source_acl = relation(SourceACL),
2913 binary_acl = relation(BinaryACL)))
2915 mapper(Keyring, self.tbl_keyrings,
2916 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2917 keyring_id = self.tbl_keyrings.c.id))
2919 mapper(DBChange, self.tbl_changes,
2920 properties = dict(change_id = self.tbl_changes.c.id,
2921 poolfiles = relation(PoolFile,
2922 secondary=self.tbl_changes_pool_files,
2923 backref="changeslinks"),
2924 seen = self.tbl_changes.c.seen,
2925 source = self.tbl_changes.c.source,
2926 binaries = self.tbl_changes.c.binaries,
2927 architecture = self.tbl_changes.c.architecture,
2928 distribution = self.tbl_changes.c.distribution,
2929 urgency = self.tbl_changes.c.urgency,
2930 maintainer = self.tbl_changes.c.maintainer,
2931 changedby = self.tbl_changes.c.changedby,
2932 date = self.tbl_changes.c.date,
2933 version = self.tbl_changes.c.version,
2934 files = relation(ChangePendingFile,
2935 secondary=self.tbl_changes_pending_files_map,
2936 backref="changesfile"),
2937 in_queue_id = self.tbl_changes.c.in_queue,
2938 in_queue = relation(PolicyQueue,
2939 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2940 approved_for_id = self.tbl_changes.c.approved_for))
2942 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2943 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2945 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2946 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
2947 filename = self.tbl_changes_pending_files.c.filename,
2948 size = self.tbl_changes_pending_files.c.size,
2949 md5sum = self.tbl_changes_pending_files.c.md5sum,
2950 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
2951 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
2953 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2954 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2955 change = relation(DBChange),
2956 maintainer = relation(Maintainer,
2957 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2958 changedby = relation(Maintainer,
2959 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2960 fingerprint = relation(Fingerprint),
2961 source_files = relation(ChangePendingFile,
2962 secondary=self.tbl_changes_pending_source_files,
2963 backref="pending_sources")))
2966 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2967 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2968 keyring = relation(Keyring, backref="keyring_acl_map"),
2969 architecture = relation(Architecture)))
2971 mapper(Location, self.tbl_location,
2972 properties = dict(location_id = self.tbl_location.c.id,
2973 component_id = self.tbl_location.c.component,
2974 component = relation(Component),
2975 archive_id = self.tbl_location.c.archive,
2976 archive = relation(Archive),
2977 archive_type = self.tbl_location.c.type))
2979 mapper(Maintainer, self.tbl_maintainer,
2980 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2982 mapper(NewComment, self.tbl_new_comments,
2983 properties = dict(comment_id = self.tbl_new_comments.c.id))
2985 mapper(Override, self.tbl_override,
2986 properties = dict(suite_id = self.tbl_override.c.suite,
2987 suite = relation(Suite),
2988 package = self.tbl_override.c.package,
2989 component_id = self.tbl_override.c.component,
2990 component = relation(Component),
2991 priority_id = self.tbl_override.c.priority,
2992 priority = relation(Priority),
2993 section_id = self.tbl_override.c.section,
2994 section = relation(Section),
2995 overridetype_id = self.tbl_override.c.type,
2996 overridetype = relation(OverrideType)))
2998 mapper(OverrideType, self.tbl_override_type,
2999 properties = dict(overridetype = self.tbl_override_type.c.type,
3000 overridetype_id = self.tbl_override_type.c.id))
3002 mapper(PolicyQueue, self.tbl_policy_queue,
3003 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
3005 mapper(Priority, self.tbl_priority,
3006 properties = dict(priority_id = self.tbl_priority.c.id))
3008 mapper(Section, self.tbl_section,
3009 properties = dict(section_id = self.tbl_section.c.id,
3010 section=self.tbl_section.c.section))
3012 mapper(DBSource, self.tbl_source,
3013 properties = dict(source_id = self.tbl_source.c.id,
3014 version = self.tbl_source.c.version,
3015 maintainer_id = self.tbl_source.c.maintainer,
3016 maintainer = relation(Maintainer,
3017 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3018 poolfile_id = self.tbl_source.c.file,
3019 poolfile = relation(PoolFile),
3020 fingerprint_id = self.tbl_source.c.sig_fpr,
3021 fingerprint = relation(Fingerprint),
3022 changedby_id = self.tbl_source.c.changedby,
3023 changedby = relation(Maintainer,
3024 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3025 srcfiles = relation(DSCFile,
3026 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3027 srcassociations = relation(SrcAssociation,
3028 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3029 srcuploaders = relation(SrcUploader)))
3031 mapper(SourceACL, self.tbl_source_acl,
3032 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3034 mapper(SrcAssociation, self.tbl_src_associations,
3035 properties = dict(sa_id = self.tbl_src_associations.c.id,
3036 suite_id = self.tbl_src_associations.c.suite,
3037 suite = relation(Suite),
3038 source_id = self.tbl_src_associations.c.source,
3039 source = relation(DBSource)))
3041 mapper(SrcFormat, self.tbl_src_format,
3042 properties = dict(src_format_id = self.tbl_src_format.c.id,
3043 format_name = self.tbl_src_format.c.format_name))
3045 mapper(SrcUploader, self.tbl_src_uploaders,
3046 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3047 source_id = self.tbl_src_uploaders.c.source,
3048 source = relation(DBSource,
3049 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3050 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3051 maintainer = relation(Maintainer,
3052 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3054 mapper(Suite, self.tbl_suite,
3055 properties = dict(suite_id = self.tbl_suite.c.id,
3056 policy_queue = relation(PolicyQueue),
3057 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3059 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3060 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3061 suite = relation(Suite, backref='suitearchitectures'),
3062 arch_id = self.tbl_suite_architectures.c.architecture,
3063 architecture = relation(Architecture)))
3065 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3066 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3067 suite = relation(Suite, backref='suitesrcformats'),
3068 src_format_id = self.tbl_suite_src_formats.c.src_format,
3069 src_format = relation(SrcFormat)))
3071 mapper(Uid, self.tbl_uid,
3072 properties = dict(uid_id = self.tbl_uid.c.id,
3073 fingerprint = relation(Fingerprint)))
3075 mapper(UploadBlock, self.tbl_upload_blocks,
3076 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3077 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3078 uid = relation(Uid, backref="uploadblocks")))
3080 ## Connection functions
3081 def __createconn(self):
3082 from config import Config
3086 connstr = "postgres://%s" % cnf["DB::Host"]
3087 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3088 connstr += ":%s" % cnf["DB::Port"]
3089 connstr += "/%s" % cnf["DB::Name"]
3092 connstr = "postgres:///%s" % cnf["DB::Name"]
3093 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3094 connstr += "?port=%s" % cnf["DB::Port"]
3096 self.db_pg = create_engine(connstr, echo=self.debug)
3097 self.db_meta = MetaData()
3098 self.db_meta.bind = self.db_pg
3099 self.db_smaker = sessionmaker(bind=self.db_pg,
3103 self.__setuptables()
3104 self.__setupmappers()
3107 return self.db_smaker()
3109 __all__.append('DBConn')