5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from datetime import datetime, timedelta
41 from errno import ENOENT
42 from tempfile import mkstemp, mkdtemp
44 from inspect import getargspec
47 from sqlalchemy import create_engine, Table, MetaData
48 from sqlalchemy.orm import sessionmaker, mapper, relation
49 from sqlalchemy import types as sqltypes
51 # Don't remove this, we re-export the exceptions to scripts which import us
52 from sqlalchemy.exc import *
53 from sqlalchemy.orm.exc import NoResultFound
55 # Only import Config until Queue stuff is changed to store its config
57 from config import Config
58 from textutils import fix_maintainer
60 ################################################################################
62 # Patch in support for the debversion field type so that it works during
65 class DebVersion(sqltypes.Text):
66 def get_col_spec(self):
69 sa_major_version = sqlalchemy.__version__[0:3]
70 if sa_major_version == "0.5":
71 from sqlalchemy.databases import postgres
72 postgres.ischema_names['debversion'] = DebVersion
74 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
76 ################################################################################
78 __all__ = ['IntegrityError', 'SQLAlchemyError']
80 ################################################################################
82 def session_wrapper(fn):
84 Wrapper around common ".., session=None):" handling. If the wrapped
85 function is called without passing 'session', we create a local one
86 and destroy it when the function ends.
88 Also attaches a commit_or_flush method to the session; if we created a
89 local session, this is a synonym for session.commit(), otherwise it is a
90 synonym for session.flush().
93 def wrapped(*args, **kwargs):
94 private_transaction = False
96 # Find the session object
97 session = kwargs.get('session')
100 if len(args) <= len(getargspec(fn)[0]) - 1:
101 # No session specified as last argument or in kwargs
102 private_transaction = True
103 session = kwargs['session'] = DBConn().session()
105 # Session is last argument in args
109 session = args[-1] = DBConn().session()
110 private_transaction = True
112 if private_transaction:
113 session.commit_or_flush = session.commit
115 session.commit_or_flush = session.flush
118 return fn(*args, **kwargs)
120 if private_transaction:
121 # We created a session; close it.
124 wrapped.__doc__ = fn.__doc__
125 wrapped.func_name = fn.func_name
129 __all__.append('session_wrapper')
131 ################################################################################
133 class Architecture(object):
134 def __init__(self, *args, **kwargs):
137 def __eq__(self, val):
138 if isinstance(val, str):
139 return (self.arch_string== val)
140 # This signals to use the normal comparison operator
141 return NotImplemented
143 def __ne__(self, val):
144 if isinstance(val, str):
145 return (self.arch_string != val)
146 # This signals to use the normal comparison operator
147 return NotImplemented
150 return '<Architecture %s>' % self.arch_string
152 __all__.append('Architecture')
155 def get_architecture(architecture, session=None):
157 Returns database id for given C{architecture}.
159 @type architecture: string
160 @param architecture: The name of the architecture
162 @type session: Session
163 @param session: Optional SQLA session object (a temporary one will be
164 generated if not supplied)
167 @return: Architecture object for the given arch (None if not present)
170 q = session.query(Architecture).filter_by(arch_string=architecture)
174 except NoResultFound:
177 __all__.append('get_architecture')
180 def get_architecture_suites(architecture, session=None):
182 Returns list of Suite objects for given C{architecture} name
185 @param source: Architecture name to search for
187 @type session: Session
188 @param session: Optional SQL session object (a temporary one will be
189 generated if not supplied)
192 @return: list of Suite objects for the given name (may be empty)
195 q = session.query(Suite)
196 q = q.join(SuiteArchitecture)
197 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
203 __all__.append('get_architecture_suites')
205 ################################################################################
207 class Archive(object):
208 def __init__(self, *args, **kwargs):
212 return '<Archive %s>' % self.archive_name
214 __all__.append('Archive')
217 def get_archive(archive, session=None):
219 returns database id for given C{archive}.
221 @type archive: string
222 @param archive: the name of the arhive
224 @type session: Session
225 @param session: Optional SQLA session object (a temporary one will be
226 generated if not supplied)
229 @return: Archive object for the given name (None if not present)
232 archive = archive.lower()
234 q = session.query(Archive).filter_by(archive_name=archive)
238 except NoResultFound:
241 __all__.append('get_archive')
243 ################################################################################
245 class BinAssociation(object):
246 def __init__(self, *args, **kwargs):
250 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
252 __all__.append('BinAssociation')
254 ################################################################################
256 class BinContents(object):
257 def __init__(self, *args, **kwargs):
261 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
263 __all__.append('BinContents')
265 ################################################################################
267 class DBBinary(object):
268 def __init__(self, *args, **kwargs):
272 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
274 __all__.append('DBBinary')
277 def get_suites_binary_in(package, session=None):
279 Returns list of Suite objects which given C{package} name is in
282 @param source: DBBinary package name to search for
285 @return: list of Suite objects for the given package
288 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
290 __all__.append('get_suites_binary_in')
293 def get_binary_from_id(binary_id, session=None):
295 Returns DBBinary object for given C{id}
298 @param binary_id: Id of the required binary
300 @type session: Session
301 @param session: Optional SQLA session object (a temporary one will be
302 generated if not supplied)
305 @return: DBBinary object for the given binary (None if not present)
308 q = session.query(DBBinary).filter_by(binary_id=binary_id)
312 except NoResultFound:
315 __all__.append('get_binary_from_id')
318 def get_binaries_from_name(package, version=None, architecture=None, session=None):
320 Returns list of DBBinary objects for given C{package} name
323 @param package: DBBinary package name to search for
325 @type version: str or None
326 @param version: Version to search for (or None)
328 @type package: str, list or None
329 @param package: Architectures to limit to (or None if no limit)
331 @type session: Session
332 @param session: Optional SQL session object (a temporary one will be
333 generated if not supplied)
336 @return: list of DBBinary objects for the given name (may be empty)
339 q = session.query(DBBinary).filter_by(package=package)
341 if version is not None:
342 q = q.filter_by(version=version)
344 if architecture is not None:
345 if not isinstance(architecture, list):
346 architecture = [architecture]
347 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
353 __all__.append('get_binaries_from_name')
356 def get_binaries_from_source_id(source_id, session=None):
358 Returns list of DBBinary objects for given C{source_id}
361 @param source_id: source_id to search for
363 @type session: Session
364 @param session: Optional SQL session object (a temporary one will be
365 generated if not supplied)
368 @return: list of DBBinary objects for the given name (may be empty)
371 return session.query(DBBinary).filter_by(source_id=source_id).all()
373 __all__.append('get_binaries_from_source_id')
376 def get_binary_from_name_suite(package, suitename, session=None):
377 ### For dak examine-package
378 ### XXX: Doesn't use object API yet
380 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
381 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
382 WHERE b.package=:package
384 AND fi.location = l.id
385 AND l.component = c.id
388 AND su.suite_name=:suitename
389 ORDER BY b.version DESC"""
391 return session.execute(sql, {'package': package, 'suitename': suitename})
393 __all__.append('get_binary_from_name_suite')
396 def get_binary_components(package, suitename, arch, session=None):
397 # Check for packages that have moved from one component to another
398 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
399 WHERE b.package=:package AND s.suite_name=:suitename
400 AND (a.arch_string = :arch OR a.arch_string = 'all')
401 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
402 AND f.location = l.id
403 AND l.component = c.id
406 vals = {'package': package, 'suitename': suitename, 'arch': arch}
408 return session.execute(query, vals)
410 __all__.append('get_binary_components')
412 ################################################################################
414 class BinaryACL(object):
415 def __init__(self, *args, **kwargs):
419 return '<BinaryACL %s>' % self.binary_acl_id
421 __all__.append('BinaryACL')
423 ################################################################################
425 class BinaryACLMap(object):
426 def __init__(self, *args, **kwargs):
430 return '<BinaryACLMap %s>' % self.binary_acl_map_id
432 __all__.append('BinaryACLMap')
434 ################################################################################
439 ArchiveDir "%(archivepath)s";
440 OverrideDir "/srv/ftp.debian.org/scripts/override/";
441 CacheDir "/srv/ftp.debian.org/database/";
446 Packages::Compress ". bzip2 gzip";
447 Sources::Compress ". bzip2 gzip";
452 bindirectory "incoming"
457 BinOverride "override.sid.all3";
458 BinCacheDB "packages-accepted.db";
460 FileList "%(filelist)s";
463 Packages::Extensions ".deb .udeb";
466 bindirectory "incoming/"
469 BinOverride "override.sid.all3";
470 SrcOverride "override.sid.all3.src";
471 FileList "%(filelist)s";
475 class BuildQueue(object):
476 def __init__(self, *args, **kwargs):
480 return '<BuildQueue %s>' % self.queue_name
482 def write_metadata(self, starttime, force=False):
483 # Do we write out metafiles?
484 if not (force or self.generate_metadata):
487 session = DBConn().session().object_session(self)
489 fl_fd = fl_name = ac_fd = ac_name = None
491 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
492 startdir = os.getcwd()
495 # Grab files we want to include
496 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
497 # Write file list with newer files
498 (fl_fd, fl_name) = mkstemp()
500 os.write(fl_fd, '%s\n' % n.fullpath)
503 # Write minimal apt.conf
504 # TODO: Remove hardcoding from template
505 (ac_fd, ac_name) = mkstemp()
506 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
507 'filelist': fl_name})
510 # Run apt-ftparchive generate
511 os.chdir(os.path.dirname(ac_name))
512 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
514 # Run apt-ftparchive release
515 # TODO: Eww - fix this
516 bname = os.path.basename(self.path)
520 # We have to remove the Release file otherwise it'll be included in the
523 os.unlink(os.path.join(bname, 'Release'))
527 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))
532 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
533 if cnf.has_key("Dinstall::SigningPubKeyring"):
534 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
536 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
538 # Move the files if we got this far
539 os.rename('Release', os.path.join(bname, 'Release'))
541 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
543 # Clean up any left behind files
570 def clean_and_update(self, starttime, Logger, dryrun=False):
571 """WARNING: This routine commits for you"""
572 session = DBConn().session().object_session(self)
574 if self.generate_metadata and not dryrun:
575 self.write_metadata(starttime)
577 # Grab files older than our execution time
578 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
584 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
586 Logger.log(["I: Removing %s from the queue" % o.fullpath])
587 os.unlink(o.fullpath)
590 # If it wasn't there, don't worry
591 if e.errno == ENOENT:
594 # TODO: Replace with proper logging call
595 Logger.log(["E: Could not remove %s" % o.fullpath])
602 for f in os.listdir(self.path):
603 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
607 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
608 except NoResultFound:
609 fp = os.path.join(self.path, f)
611 Logger.log(["I: Would remove unused link %s" % fp])
613 Logger.log(["I: Removing unused link %s" % fp])
617 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
619 def add_file_from_pool(self, poolfile):
620 """Copies a file into the pool. Assumes that the PoolFile object is
621 attached to the same SQLAlchemy session as the Queue object is.
623 The caller is responsible for committing after calling this function."""
624 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
626 # Check if we have a file of this name or this ID already
627 for f in self.queuefiles:
628 if f.fileid is not None and f.fileid == poolfile.file_id or \
629 f.poolfile.filename == poolfile_basename:
630 # In this case, update the BuildQueueFile entry so we
631 # don't remove it too early
632 f.lastused = datetime.now()
633 DBConn().session().object_session(poolfile).add(f)
636 # Prepare BuildQueueFile object
637 qf = BuildQueueFile()
638 qf.build_queue_id = self.queue_id
639 qf.lastused = datetime.now()
640 qf.filename = poolfile_basename
642 targetpath = poolfile.fullpath
643 queuepath = os.path.join(self.path, poolfile_basename)
647 # We need to copy instead of symlink
649 utils.copy(targetpath, queuepath)
650 # NULL in the fileid field implies a copy
653 os.symlink(targetpath, queuepath)
654 qf.fileid = poolfile.file_id
658 # Get the same session as the PoolFile is using and add the qf to it
659 DBConn().session().object_session(poolfile).add(qf)
664 __all__.append('BuildQueue')
667 def get_build_queue(queuename, session=None):
669 Returns BuildQueue object for given C{queue name}, creating it if it does not
672 @type queuename: string
673 @param queuename: The name of the queue
675 @type session: Session
676 @param session: Optional SQLA session object (a temporary one will be
677 generated if not supplied)
680 @return: BuildQueue object for the given queue
683 q = session.query(BuildQueue).filter_by(queue_name=queuename)
687 except NoResultFound:
690 __all__.append('get_build_queue')
692 ################################################################################
694 class BuildQueueFile(object):
695 def __init__(self, *args, **kwargs):
699 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
703 return os.path.join(self.buildqueue.path, self.filename)
706 __all__.append('BuildQueueFile')
708 ################################################################################
710 class ChangePendingBinary(object):
711 def __init__(self, *args, **kwargs):
715 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
717 __all__.append('ChangePendingBinary')
719 ################################################################################
721 class ChangePendingFile(object):
722 def __init__(self, *args, **kwargs):
726 return '<ChangePendingFile %s>' % self.change_pending_file_id
728 __all__.append('ChangePendingFile')
730 ################################################################################
732 class ChangePendingSource(object):
733 def __init__(self, *args, **kwargs):
737 return '<ChangePendingSource %s>' % self.change_pending_source_id
739 __all__.append('ChangePendingSource')
741 ################################################################################
743 class Component(object):
744 def __init__(self, *args, **kwargs):
747 def __eq__(self, val):
748 if isinstance(val, str):
749 return (self.component_name == val)
750 # This signals to use the normal comparison operator
751 return NotImplemented
753 def __ne__(self, val):
754 if isinstance(val, str):
755 return (self.component_name != val)
756 # This signals to use the normal comparison operator
757 return NotImplemented
760 return '<Component %s>' % self.component_name
763 __all__.append('Component')
766 def get_component(component, session=None):
768 Returns database id for given C{component}.
770 @type component: string
771 @param component: The name of the override type
774 @return: the database id for the given component
777 component = component.lower()
779 q = session.query(Component).filter_by(component_name=component)
783 except NoResultFound:
786 __all__.append('get_component')
788 ################################################################################
790 class DBConfig(object):
791 def __init__(self, *args, **kwargs):
795 return '<DBConfig %s>' % self.name
797 __all__.append('DBConfig')
799 ################################################################################
802 def get_or_set_contents_file_id(filename, session=None):
804 Returns database id for given filename.
806 If no matching file is found, a row is inserted.
808 @type filename: string
809 @param filename: The filename
810 @type session: SQLAlchemy
811 @param session: Optional SQL session object (a temporary one will be
812 generated if not supplied). If not passed, a commit will be performed at
813 the end of the function, otherwise the caller is responsible for commiting.
816 @return: the database id for the given component
819 q = session.query(ContentFilename).filter_by(filename=filename)
822 ret = q.one().cafilename_id
823 except NoResultFound:
824 cf = ContentFilename()
825 cf.filename = filename
827 session.commit_or_flush()
828 ret = cf.cafilename_id
832 __all__.append('get_or_set_contents_file_id')
835 def get_contents(suite, overridetype, section=None, session=None):
837 Returns contents for a suite / overridetype combination, limiting
838 to a section if not None.
841 @param suite: Suite object
843 @type overridetype: OverrideType
844 @param overridetype: OverrideType object
846 @type section: Section
847 @param section: Optional section object to limit results to
849 @type session: SQLAlchemy
850 @param session: Optional SQL session object (a temporary one will be
851 generated if not supplied)
854 @return: ResultsProxy object set up to return tuples of (filename, section,
858 # find me all of the contents for a given suite
859 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
863 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
864 JOIN content_file_names n ON (c.filename=n.id)
865 JOIN binaries b ON (b.id=c.binary_pkg)
866 JOIN override o ON (o.package=b.package)
867 JOIN section s ON (s.id=o.section)
868 WHERE o.suite = :suiteid AND o.type = :overridetypeid
869 AND b.type=:overridetypename"""
871 vals = {'suiteid': suite.suite_id,
872 'overridetypeid': overridetype.overridetype_id,
873 'overridetypename': overridetype.overridetype}
875 if section is not None:
876 contents_q += " AND s.id = :sectionid"
877 vals['sectionid'] = section.section_id
879 contents_q += " ORDER BY fn"
881 return session.execute(contents_q, vals)
883 __all__.append('get_contents')
885 ################################################################################
887 class ContentFilepath(object):
888 def __init__(self, *args, **kwargs):
892 return '<ContentFilepath %s>' % self.filepath
894 __all__.append('ContentFilepath')
897 def get_or_set_contents_path_id(filepath, session=None):
899 Returns database id for given path.
901 If no matching file is found, a row is inserted.
903 @type filename: string
904 @param filename: The filepath
905 @type session: SQLAlchemy
906 @param session: Optional SQL session object (a temporary one will be
907 generated if not supplied). If not passed, a commit will be performed at
908 the end of the function, otherwise the caller is responsible for commiting.
911 @return: the database id for the given path
914 q = session.query(ContentFilepath).filter_by(filepath=filepath)
917 ret = q.one().cafilepath_id
918 except NoResultFound:
919 cf = ContentFilepath()
920 cf.filepath = filepath
922 session.commit_or_flush()
923 ret = cf.cafilepath_id
927 __all__.append('get_or_set_contents_path_id')
929 ################################################################################
931 class ContentAssociation(object):
932 def __init__(self, *args, **kwargs):
936 return '<ContentAssociation %s>' % self.ca_id
938 __all__.append('ContentAssociation')
940 def insert_content_paths(binary_id, fullpaths, session=None):
942 Make sure given path is associated with given binary id
945 @param binary_id: the id of the binary
946 @type fullpaths: list
947 @param fullpaths: the list of paths of the file being associated with the binary
948 @type session: SQLAlchemy session
949 @param session: Optional SQLAlchemy session. If this is passed, the caller
950 is responsible for ensuring a transaction has begun and committing the
951 results or rolling back based on the result code. If not passed, a commit
952 will be performed at the end of the function, otherwise the caller is
953 responsible for commiting.
955 @return: True upon success
960 session = DBConn().session()
967 def generate_path_dicts():
968 for fullpath in fullpaths:
969 if fullpath.startswith( './' ):
970 fullpath = fullpath[2:]
972 yield {'fulename':fullpath, 'id': binary_id }
974 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
975 generate_path_dicts() )
983 traceback.print_exc()
985 # Only rollback if we set up the session ourself
992 __all__.append('insert_content_paths')
994 ################################################################################
996 class DSCFile(object):
997 def __init__(self, *args, **kwargs):
1001 return '<DSCFile %s>' % self.dscfile_id
1003 __all__.append('DSCFile')
1006 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1008 Returns a list of DSCFiles which may be empty
1010 @type dscfile_id: int (optional)
1011 @param dscfile_id: the dscfile_id of the DSCFiles to find
1013 @type source_id: int (optional)
1014 @param source_id: the source id related to the DSCFiles to find
1016 @type poolfile_id: int (optional)
1017 @param poolfile_id: the poolfile id related to the DSCFiles to find
1020 @return: Possibly empty list of DSCFiles
1023 q = session.query(DSCFile)
1025 if dscfile_id is not None:
1026 q = q.filter_by(dscfile_id=dscfile_id)
1028 if source_id is not None:
1029 q = q.filter_by(source_id=source_id)
1031 if poolfile_id is not None:
1032 q = q.filter_by(poolfile_id=poolfile_id)
1036 __all__.append('get_dscfiles')
1038 ################################################################################
1040 class PoolFile(object):
1041 def __init__(self, *args, **kwargs):
1045 return '<PoolFile %s>' % self.filename
1049 return os.path.join(self.location.path, self.filename)
1051 __all__.append('PoolFile')
1054 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1057 (ValidFileFound [boolean or None], PoolFile object or None)
1059 @type filename: string
1060 @param filename: the filename of the file to check against the DB
1063 @param filesize: the size of the file to check against the DB
1065 @type md5sum: string
1066 @param md5sum: the md5sum of the file to check against the DB
1068 @type location_id: int
1069 @param location_id: the id of the location to look in
1072 @return: Tuple of length 2.
1073 If more than one file found with that name:
1075 If valid pool file found: (True, PoolFile object)
1076 If valid pool file not found:
1077 (False, None) if no file found
1078 (False, PoolFile object) if file found with size/md5sum mismatch
1081 q = session.query(PoolFile).filter_by(filename=filename)
1082 q = q.join(Location).filter_by(location_id=location_id)
1092 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1100 __all__.append('check_poolfile')
1103 def get_poolfile_by_id(file_id, session=None):
1105 Returns a PoolFile objects or None for the given id
1108 @param file_id: the id of the file to look for
1110 @rtype: PoolFile or None
1111 @return: either the PoolFile object or None
1114 q = session.query(PoolFile).filter_by(file_id=file_id)
1118 except NoResultFound:
1121 __all__.append('get_poolfile_by_id')
1125 def get_poolfile_by_name(filename, location_id=None, session=None):
1127 Returns an array of PoolFile objects for the given filename and
1128 (optionally) location_id
1130 @type filename: string
1131 @param filename: the filename of the file to check against the DB
1133 @type location_id: int
1134 @param location_id: the id of the location to look in (optional)
1137 @return: array of PoolFile objects
1140 q = session.query(PoolFile).filter_by(filename=filename)
1142 if location_id is not None:
1143 q = q.join(Location).filter_by(location_id=location_id)
1147 __all__.append('get_poolfile_by_name')
1150 def get_poolfile_like_name(filename, session=None):
1152 Returns an array of PoolFile objects which are like the given name
1154 @type filename: string
1155 @param filename: the filename of the file to check against the DB
1158 @return: array of PoolFile objects
1161 # TODO: There must be a way of properly using bind parameters with %FOO%
1162 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1166 __all__.append('get_poolfile_like_name')
1169 def add_poolfile(filename, datadict, location_id, session=None):
1171 Add a new file to the pool
1173 @type filename: string
1174 @param filename: filename
1176 @type datadict: dict
1177 @param datadict: dict with needed data
1179 @type location_id: int
1180 @param location_id: database id of the location
1183 @return: the PoolFile object created
1185 poolfile = PoolFile()
1186 poolfile.filename = filename
1187 poolfile.filesize = datadict["size"]
1188 poolfile.md5sum = datadict["md5sum"]
1189 poolfile.sha1sum = datadict["sha1sum"]
1190 poolfile.sha256sum = datadict["sha256sum"]
1191 poolfile.location_id = location_id
1193 session.add(poolfile)
1194 # Flush to get a file id (NB: This is not a commit)
1199 __all__.append('add_poolfile')
1201 ################################################################################
1203 class Fingerprint(object):
1204 def __init__(self, *args, **kwargs):
1208 return '<Fingerprint %s>' % self.fingerprint
1210 __all__.append('Fingerprint')
1213 def get_fingerprint(fpr, session=None):
1215 Returns Fingerprint object for given fpr.
1218 @param fpr: The fpr to find / add
1220 @type session: SQLAlchemy
1221 @param session: Optional SQL session object (a temporary one will be
1222 generated if not supplied).
1225 @return: the Fingerprint object for the given fpr or None
1228 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1232 except NoResultFound:
1237 __all__.append('get_fingerprint')
1240 def get_or_set_fingerprint(fpr, session=None):
1242 Returns Fingerprint object for given fpr.
1244 If no matching fpr is found, a row is inserted.
1247 @param fpr: The fpr to find / add
1249 @type session: SQLAlchemy
1250 @param session: Optional SQL session object (a temporary one will be
1251 generated if not supplied). If not passed, a commit will be performed at
1252 the end of the function, otherwise the caller is responsible for commiting.
1253 A flush will be performed either way.
1256 @return: the Fingerprint object for the given fpr
1259 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1263 except NoResultFound:
1264 fingerprint = Fingerprint()
1265 fingerprint.fingerprint = fpr
1266 session.add(fingerprint)
1267 session.commit_or_flush()
1272 __all__.append('get_or_set_fingerprint')
1274 ################################################################################
1276 # Helper routine for Keyring class
1277 def get_ldap_name(entry):
1279 for k in ["cn", "mn", "sn"]:
1281 if ret and ret[0] != "" and ret[0] != "-":
1283 return " ".join(name)
1285 ################################################################################
1287 class Keyring(object):
1288 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1289 " --with-colons --fingerprint --fingerprint"
1294 def __init__(self, *args, **kwargs):
1298 return '<Keyring %s>' % self.keyring_name
1300 def de_escape_gpg_str(self, txt):
1301 esclist = re.split(r'(\\x..)', txt)
1302 for x in range(1,len(esclist),2):
1303 esclist[x] = "%c" % (int(esclist[x][2:],16))
1304 return "".join(esclist)
1306 def load_keys(self, keyring):
1309 if not self.keyring_id:
1310 raise Exception('Must be initialized with database information')
1312 k = os.popen(self.gpg_invocation % keyring, "r")
1316 for line in k.xreadlines():
1317 field = line.split(":")
1318 if field[0] == "pub":
1320 (name, addr) = email.Utils.parseaddr(field[9])
1321 name = re.sub(r"\s*[(].*[)]", "", name)
1322 if name == "" or addr == "" or "@" not in addr:
1324 addr = "invalid-uid"
1325 name = self.de_escape_gpg_str(name)
1326 self.keys[key] = {"email": addr}
1328 self.keys[key]["name"] = name
1329 self.keys[key]["aliases"] = [name]
1330 self.keys[key]["fingerprints"] = []
1332 elif key and field[0] == "sub" and len(field) >= 12:
1333 signingkey = ("s" in field[11])
1334 elif key and field[0] == "uid":
1335 (name, addr) = email.Utils.parseaddr(field[9])
1336 if name and name not in self.keys[key]["aliases"]:
1337 self.keys[key]["aliases"].append(name)
1338 elif signingkey and field[0] == "fpr":
1339 self.keys[key]["fingerprints"].append(field[9])
1340 self.fpr_lookup[field[9]] = key
1342 def import_users_from_ldap(self, session):
1346 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1347 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1349 l = ldap.open(LDAPServer)
1350 l.simple_bind_s("","")
1351 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1352 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1353 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1355 ldap_fin_uid_id = {}
1362 uid = entry["uid"][0]
1363 name = get_ldap_name(entry)
1364 fingerprints = entry["keyFingerPrint"]
1366 for f in fingerprints:
1367 key = self.fpr_lookup.get(f, None)
1368 if key not in self.keys:
1370 self.keys[key]["uid"] = uid
1374 keyid = get_or_set_uid(uid, session).uid_id
1375 byuid[keyid] = (uid, name)
1376 byname[uid] = (keyid, name)
1378 return (byname, byuid)
1380 def generate_users_from_keyring(self, format, session):
1384 for x in self.keys.keys():
1385 if self.keys[x]["email"] == "invalid-uid":
1387 self.keys[x]["uid"] = format % "invalid-uid"
1389 uid = format % self.keys[x]["email"]
1390 keyid = get_or_set_uid(uid, session).uid_id
1391 byuid[keyid] = (uid, self.keys[x]["name"])
1392 byname[uid] = (keyid, self.keys[x]["name"])
1393 self.keys[x]["uid"] = uid
1396 uid = format % "invalid-uid"
1397 keyid = get_or_set_uid(uid, session).uid_id
1398 byuid[keyid] = (uid, "ungeneratable user id")
1399 byname[uid] = (keyid, "ungeneratable user id")
1401 return (byname, byuid)
1403 __all__.append('Keyring')
1406 def get_keyring(keyring, session=None):
1408 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1409 If C{keyring} already has an entry, simply return the existing Keyring
1411 @type keyring: string
1412 @param keyring: the keyring name
1415 @return: the Keyring object for this keyring
1418 q = session.query(Keyring).filter_by(keyring_name=keyring)
1422 except NoResultFound:
1425 __all__.append('get_keyring')
1427 ################################################################################
1429 class KeyringACLMap(object):
1430 def __init__(self, *args, **kwargs):
1434 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1436 __all__.append('KeyringACLMap')
1438 ################################################################################
1440 class DBChange(object):
1441 def __init__(self, *args, **kwargs):
1445 return '<DBChange %s>' % self.changesname
1447 __all__.append('DBChange')
1450 def get_dbchange(filename, session=None):
1452 returns DBChange object for given C{filename}.
1454 @type archive: string
1455 @param archive: the name of the arhive
1457 @type session: Session
1458 @param session: Optional SQLA session object (a temporary one will be
1459 generated if not supplied)
1462 @return: Archive object for the given name (None if not present)
1465 q = session.query(DBChange).filter_by(changesname=filename)
1469 except NoResultFound:
1472 __all__.append('get_dbchange')
1474 ################################################################################
1476 class Location(object):
1477 def __init__(self, *args, **kwargs):
1481 return '<Location %s (%s)>' % (self.path, self.location_id)
1483 __all__.append('Location')
1486 def get_location(location, component=None, archive=None, session=None):
1488 Returns Location object for the given combination of location, component
1491 @type location: string
1492 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1494 @type component: string
1495 @param component: the component name (if None, no restriction applied)
1497 @type archive: string
1498 @param archive_id: the archive name (if None, no restriction applied)
1500 @rtype: Location / None
1501 @return: Either a Location object or None if one can't be found
1504 q = session.query(Location).filter_by(path=location)
1506 if archive is not None:
1507 q = q.join(Archive).filter_by(archive_name=archive)
1509 if component is not None:
1510 q = q.join(Component).filter_by(component_name=component)
1514 except NoResultFound:
1517 __all__.append('get_location')
1519 ################################################################################
1521 class Maintainer(object):
1522 def __init__(self, *args, **kwargs):
1526 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1528 def get_split_maintainer(self):
1529 if not hasattr(self, 'name') or self.name is None:
1530 return ('', '', '', '')
1532 return fix_maintainer(self.name.strip())
1534 __all__.append('Maintainer')
1537 def get_or_set_maintainer(name, session=None):
1539 Returns Maintainer object for given maintainer name.
1541 If no matching maintainer name is found, a row is inserted.
1544 @param name: The maintainer name to add
1546 @type session: SQLAlchemy
1547 @param session: Optional SQL session object (a temporary one will be
1548 generated if not supplied). If not passed, a commit will be performed at
1549 the end of the function, otherwise the caller is responsible for commiting.
1550 A flush will be performed either way.
1553 @return: the Maintainer object for the given maintainer
1556 q = session.query(Maintainer).filter_by(name=name)
1559 except NoResultFound:
1560 maintainer = Maintainer()
1561 maintainer.name = name
1562 session.add(maintainer)
1563 session.commit_or_flush()
1568 __all__.append('get_or_set_maintainer')
1571 def get_maintainer(maintainer_id, session=None):
1573 Return the name of the maintainer behind C{maintainer_id} or None if that
1574 maintainer_id is invalid.
1576 @type maintainer_id: int
1577 @param maintainer_id: the id of the maintainer
1580 @return: the Maintainer with this C{maintainer_id}
1583 return session.query(Maintainer).get(maintainer_id)
1585 __all__.append('get_maintainer')
1587 ################################################################################
1589 class NewComment(object):
1590 def __init__(self, *args, **kwargs):
1594 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1596 __all__.append('NewComment')
1599 def has_new_comment(package, version, session=None):
1601 Returns true if the given combination of C{package}, C{version} has a comment.
1603 @type package: string
1604 @param package: name of the package
1606 @type version: string
1607 @param version: package version
1609 @type session: Session
1610 @param session: Optional SQLA session object (a temporary one will be
1611 generated if not supplied)
1617 q = session.query(NewComment)
1618 q = q.filter_by(package=package)
1619 q = q.filter_by(version=version)
1621 return bool(q.count() > 0)
1623 __all__.append('has_new_comment')
1626 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1628 Returns (possibly empty) list of NewComment objects for the given
1631 @type package: string (optional)
1632 @param package: name of the package
1634 @type version: string (optional)
1635 @param version: package version
1637 @type comment_id: int (optional)
1638 @param comment_id: An id of a comment
1640 @type session: Session
1641 @param session: Optional SQLA session object (a temporary one will be
1642 generated if not supplied)
1645 @return: A (possibly empty) list of NewComment objects will be returned
1648 q = session.query(NewComment)
1649 if package is not None: q = q.filter_by(package=package)
1650 if version is not None: q = q.filter_by(version=version)
1651 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1655 __all__.append('get_new_comments')
1657 ################################################################################
1659 class Override(object):
1660 def __init__(self, *args, **kwargs):
1664 return '<Override %s (%s)>' % (self.package, self.suite_id)
1666 __all__.append('Override')
1669 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1671 Returns Override object for the given parameters
1673 @type package: string
1674 @param package: The name of the package
1676 @type suite: string, list or None
1677 @param suite: The name of the suite (or suites if a list) to limit to. If
1678 None, don't limit. Defaults to None.
1680 @type component: string, list or None
1681 @param component: The name of the component (or components if a list) to
1682 limit to. If None, don't limit. Defaults to None.
1684 @type overridetype: string, list or None
1685 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1686 limit to. If None, don't limit. Defaults to None.
1688 @type session: Session
1689 @param session: Optional SQLA session object (a temporary one will be
1690 generated if not supplied)
1693 @return: A (possibly empty) list of Override objects will be returned
1696 q = session.query(Override)
1697 q = q.filter_by(package=package)
1699 if suite is not None:
1700 if not isinstance(suite, list): suite = [suite]
1701 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1703 if component is not None:
1704 if not isinstance(component, list): component = [component]
1705 q = q.join(Component).filter(Component.component_name.in_(component))
1707 if overridetype is not None:
1708 if not isinstance(overridetype, list): overridetype = [overridetype]
1709 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1713 __all__.append('get_override')
1716 ################################################################################
1718 class OverrideType(object):
1719 def __init__(self, *args, **kwargs):
1723 return '<OverrideType %s>' % self.overridetype
1725 __all__.append('OverrideType')
1728 def get_override_type(override_type, session=None):
1730 Returns OverrideType object for given C{override type}.
1732 @type override_type: string
1733 @param override_type: The name of the override type
1735 @type session: Session
1736 @param session: Optional SQLA session object (a temporary one will be
1737 generated if not supplied)
1740 @return: the database id for the given override type
1743 q = session.query(OverrideType).filter_by(overridetype=override_type)
1747 except NoResultFound:
1750 __all__.append('get_override_type')
1752 ################################################################################
1754 class DebContents(object):
1755 def __init__(self, *args, **kwargs):
1759 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1761 __all__.append('DebContents')
1764 class UdebContents(object):
1765 def __init__(self, *args, **kwargs):
1769 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1771 __all__.append('UdebContents')
1773 class PendingBinContents(object):
1774 def __init__(self, *args, **kwargs):
1778 return '<PendingBinContents %s>' % self.contents_id
1780 __all__.append('PendingBinContents')
1782 def insert_pending_content_paths(package,
1787 Make sure given paths are temporarily associated with given
1791 @param package: the package to associate with should have been read in from the binary control file
1792 @type fullpaths: list
1793 @param fullpaths: the list of paths of the file being associated with the binary
1794 @type session: SQLAlchemy session
1795 @param session: Optional SQLAlchemy session. If this is passed, the caller
1796 is responsible for ensuring a transaction has begun and committing the
1797 results or rolling back based on the result code. If not passed, a commit
1798 will be performed at the end of the function
1800 @return: True upon success, False if there is a problem
1803 privatetrans = False
1806 session = DBConn().session()
1810 arch = get_architecture(package['Architecture'], session)
1811 arch_id = arch.arch_id
1813 # Remove any already existing recorded files for this package
1814 q = session.query(PendingBinContents)
1815 q = q.filter_by(package=package['Package'])
1816 q = q.filter_by(version=package['Version'])
1817 q = q.filter_by(architecture=arch_id)
1820 for fullpath in fullpaths:
1822 if fullpath.startswith( "./" ):
1823 fullpath = fullpath[2:]
1825 pca = PendingBinContents()
1826 pca.package = package['Package']
1827 pca.version = package['Version']
1829 pca.architecture = arch_id
1832 pca.type = 8 # gross
1834 pca.type = 7 # also gross
1837 # Only commit if we set up the session ourself
1845 except Exception, e:
1846 traceback.print_exc()
1848 # Only rollback if we set up the session ourself
1855 __all__.append('insert_pending_content_paths')
1857 ################################################################################
1859 class PolicyQueue(object):
1860 def __init__(self, *args, **kwargs):
1864 return '<PolicyQueue %s>' % self.queue_name
1866 __all__.append('PolicyQueue')
1869 def get_policy_queue(queuename, session=None):
1871 Returns PolicyQueue object for given C{queue name}
1873 @type queuename: string
1874 @param queuename: The name of the queue
1876 @type session: Session
1877 @param session: Optional SQLA session object (a temporary one will be
1878 generated if not supplied)
1881 @return: PolicyQueue object for the given queue
1884 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1888 except NoResultFound:
1891 __all__.append('get_policy_queue')
1893 ################################################################################
1895 class Priority(object):
1896 def __init__(self, *args, **kwargs):
1899 def __eq__(self, val):
1900 if isinstance(val, str):
1901 return (self.priority == val)
1902 # This signals to use the normal comparison operator
1903 return NotImplemented
1905 def __ne__(self, val):
1906 if isinstance(val, str):
1907 return (self.priority != val)
1908 # This signals to use the normal comparison operator
1909 return NotImplemented
1912 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1914 __all__.append('Priority')
1917 def get_priority(priority, session=None):
1919 Returns Priority object for given C{priority name}.
1921 @type priority: string
1922 @param priority: The name of the priority
1924 @type session: Session
1925 @param session: Optional SQLA session object (a temporary one will be
1926 generated if not supplied)
1929 @return: Priority object for the given priority
1932 q = session.query(Priority).filter_by(priority=priority)
1936 except NoResultFound:
1939 __all__.append('get_priority')
1942 def get_priorities(session=None):
1944 Returns dictionary of priority names -> id mappings
1946 @type session: Session
1947 @param session: Optional SQL session object (a temporary one will be
1948 generated if not supplied)
1951 @return: dictionary of priority names -> id mappings
1955 q = session.query(Priority)
1957 ret[x.priority] = x.priority_id
1961 __all__.append('get_priorities')
1963 ################################################################################
1965 class Section(object):
1966 def __init__(self, *args, **kwargs):
1969 def __eq__(self, val):
1970 if isinstance(val, str):
1971 return (self.section == val)
1972 # This signals to use the normal comparison operator
1973 return NotImplemented
1975 def __ne__(self, val):
1976 if isinstance(val, str):
1977 return (self.section != val)
1978 # This signals to use the normal comparison operator
1979 return NotImplemented
1982 return '<Section %s>' % self.section
1984 __all__.append('Section')
1987 def get_section(section, session=None):
1989 Returns Section object for given C{section name}.
1991 @type section: string
1992 @param section: The name of the section
1994 @type session: Session
1995 @param session: Optional SQLA session object (a temporary one will be
1996 generated if not supplied)
1999 @return: Section object for the given section name
2002 q = session.query(Section).filter_by(section=section)
2006 except NoResultFound:
2009 __all__.append('get_section')
2012 def get_sections(session=None):
2014 Returns dictionary of section names -> id mappings
2016 @type session: Session
2017 @param session: Optional SQL session object (a temporary one will be
2018 generated if not supplied)
2021 @return: dictionary of section names -> id mappings
2025 q = session.query(Section)
2027 ret[x.section] = x.section_id
2031 __all__.append('get_sections')
2033 ################################################################################
2035 class DBSource(object):
2036 def __init__(self, *args, **kwargs):
2040 return '<DBSource %s (%s)>' % (self.source, self.version)
2042 __all__.append('DBSource')
2045 def source_exists(source, source_version, suites = ["any"], session=None):
2047 Ensure that source exists somewhere in the archive for the binary
2048 upload being processed.
2049 1. exact match => 1.0-3
2050 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2052 @type package: string
2053 @param package: package source name
2055 @type source_version: string
2056 @param source_version: expected source version
2059 @param suites: list of suites to check in, default I{any}
2061 @type session: Session
2062 @param session: Optional SQLA session object (a temporary one will be
2063 generated if not supplied)
2066 @return: returns 1 if a source with expected version is found, otherwise 0
2073 for suite in suites:
2074 q = session.query(DBSource).filter_by(source=source)
2076 # source must exist in suite X, or in some other suite that's
2077 # mapped to X, recursively... silent-maps are counted too,
2078 # unreleased-maps aren't.
2079 maps = cnf.ValueList("SuiteMappings")[:]
2081 maps = [ m.split() for m in maps ]
2082 maps = [ (x[1], x[2]) for x in maps
2083 if x[0] == "map" or x[0] == "silent-map" ]
2086 if x[1] in s and x[0] not in s:
2089 q = q.join(SrcAssociation).join(Suite)
2090 q = q.filter(Suite.suite_name.in_(s))
2092 # Reduce the query results to a list of version numbers
2093 ql = [ j.version for j in q.all() ]
2096 if source_version in ql:
2100 from daklib.regexes import re_bin_only_nmu
2101 orig_source_version = re_bin_only_nmu.sub('', source_version)
2102 if orig_source_version in ql:
2105 # No source found so return not ok
2110 __all__.append('source_exists')
2113 def get_suites_source_in(source, session=None):
2115 Returns list of Suite objects which given C{source} name is in
2118 @param source: DBSource package name to search for
2121 @return: list of Suite objects for the given source
2124 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2126 __all__.append('get_suites_source_in')
2129 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2131 Returns list of DBSource objects for given C{source} name and other parameters
2134 @param source: DBSource package name to search for
2136 @type source: str or None
2137 @param source: DBSource version name to search for or None if not applicable
2139 @type dm_upload_allowed: bool
2140 @param dm_upload_allowed: If None, no effect. If True or False, only
2141 return packages with that dm_upload_allowed setting
2143 @type session: Session
2144 @param session: Optional SQL session object (a temporary one will be
2145 generated if not supplied)
2148 @return: list of DBSource objects for the given name (may be empty)
2151 q = session.query(DBSource).filter_by(source=source)
2153 if version is not None:
2154 q = q.filter_by(version=version)
2156 if dm_upload_allowed is not None:
2157 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2161 __all__.append('get_sources_from_name')
2164 def get_source_in_suite(source, suite, session=None):
2166 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2168 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2169 - B{suite} - a suite name, eg. I{unstable}
2171 @type source: string
2172 @param source: source package name
2175 @param suite: the suite name
2178 @return: the version for I{source} in I{suite}
2182 q = session.query(SrcAssociation)
2183 q = q.join('source').filter_by(source=source)
2184 q = q.join('suite').filter_by(suite_name=suite)
2187 return q.one().source
2188 except NoResultFound:
2191 __all__.append('get_source_in_suite')
2193 ################################################################################
2196 def add_dsc_to_db(u, filename, session=None):
2197 entry = u.pkg.files[filename]
2201 source.source = u.pkg.dsc["source"]
2202 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2203 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2204 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2205 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2206 source.install_date = datetime.now().date()
2208 dsc_component = entry["component"]
2209 dsc_location_id = entry["location id"]
2211 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2213 # Set up a new poolfile if necessary
2214 if not entry.has_key("files id") or not entry["files id"]:
2215 filename = entry["pool name"] + filename
2216 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2218 pfs.append(poolfile)
2219 entry["files id"] = poolfile.file_id
2221 source.poolfile_id = entry["files id"]
2225 for suite_name in u.pkg.changes["distribution"].keys():
2226 sa = SrcAssociation()
2227 sa.source_id = source.source_id
2228 sa.suite_id = get_suite(suite_name).suite_id
2233 # Add the source files to the DB (files and dsc_files)
2235 dscfile.source_id = source.source_id
2236 dscfile.poolfile_id = entry["files id"]
2237 session.add(dscfile)
2239 for dsc_file, dentry in u.pkg.dsc_files.items():
2241 df.source_id = source.source_id
2243 # If the .orig tarball is already in the pool, it's
2244 # files id is stored in dsc_files by check_dsc().
2245 files_id = dentry.get("files id", None)
2247 # Find the entry in the files hash
2248 # TODO: Bail out here properly
2250 for f, e in u.pkg.files.items():
2255 if files_id is None:
2256 filename = dfentry["pool name"] + dsc_file
2258 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2259 # FIXME: needs to check for -1/-2 and or handle exception
2260 if found and obj is not None:
2261 files_id = obj.file_id
2264 # If still not found, add it
2265 if files_id is None:
2266 # HACK: Force sha1sum etc into dentry
2267 dentry["sha1sum"] = dfentry["sha1sum"]
2268 dentry["sha256sum"] = dfentry["sha256sum"]
2269 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2270 pfs.append(poolfile)
2271 files_id = poolfile.file_id
2273 poolfile = get_poolfile_by_id(files_id, session)
2274 if poolfile is None:
2275 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2276 pfs.append(poolfile)
2278 df.poolfile_id = files_id
2283 # Add the src_uploaders to the DB
2284 uploader_ids = [source.maintainer_id]
2285 if u.pkg.dsc.has_key("uploaders"):
2286 for up in u.pkg.dsc["uploaders"].split(","):
2288 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2291 for up in uploader_ids:
2292 if added_ids.has_key(up):
2293 utils.warn("Already saw uploader %s for source %s" % (up, source.source))
2299 su.maintainer_id = up
2300 su.source_id = source.source_id
2305 return dsc_component, dsc_location_id, pfs
2307 __all__.append('add_dsc_to_db')
2310 def add_deb_to_db(u, filename, session=None):
2312 Contrary to what you might expect, this routine deals with both
2313 debs and udebs. That info is in 'dbtype', whilst 'type' is
2314 'deb' for both of them
2317 entry = u.pkg.files[filename]
2320 bin.package = entry["package"]
2321 bin.version = entry["version"]
2322 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2323 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2324 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2325 bin.binarytype = entry["dbtype"]
2328 filename = entry["pool name"] + filename
2329 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2330 if not entry.get("location id", None):
2331 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2333 if entry.get("files id", None):
2334 poolfile = get_poolfile_by_id(bin.poolfile_id)
2335 bin.poolfile_id = entry["files id"]
2337 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2338 bin.poolfile_id = entry["files id"] = poolfile.file_id
2341 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2342 if len(bin_sources) != 1:
2343 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2344 (bin.package, bin.version, bin.architecture.arch_string,
2345 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2347 bin.source_id = bin_sources[0].source_id
2349 # Add and flush object so it has an ID
2353 # Add BinAssociations
2354 for suite_name in u.pkg.changes["distribution"].keys():
2355 ba = BinAssociation()
2356 ba.binary_id = bin.binary_id
2357 ba.suite_id = get_suite(suite_name).suite_id
2362 # Deal with contents - disabled for now
2363 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2365 # print "REJECT\nCould not determine contents of package %s" % bin.package
2366 # session.rollback()
2367 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2371 __all__.append('add_deb_to_db')
2373 ################################################################################
2375 class SourceACL(object):
2376 def __init__(self, *args, **kwargs):
2380 return '<SourceACL %s>' % self.source_acl_id
2382 __all__.append('SourceACL')
2384 ################################################################################
2386 class SrcAssociation(object):
2387 def __init__(self, *args, **kwargs):
2391 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2393 __all__.append('SrcAssociation')
2395 ################################################################################
2397 class SrcFormat(object):
2398 def __init__(self, *args, **kwargs):
2402 return '<SrcFormat %s>' % (self.format_name)
2404 __all__.append('SrcFormat')
2406 ################################################################################
2408 class SrcUploader(object):
2409 def __init__(self, *args, **kwargs):
2413 return '<SrcUploader %s>' % self.uploader_id
2415 __all__.append('SrcUploader')
2417 ################################################################################
2419 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2420 ('SuiteID', 'suite_id'),
2421 ('Version', 'version'),
2422 ('Origin', 'origin'),
2424 ('Description', 'description'),
2425 ('Untouchable', 'untouchable'),
2426 ('Announce', 'announce'),
2427 ('Codename', 'codename'),
2428 ('OverrideCodename', 'overridecodename'),
2429 ('ValidTime', 'validtime'),
2430 ('Priority', 'priority'),
2431 ('NotAutomatic', 'notautomatic'),
2432 ('CopyChanges', 'copychanges'),
2433 ('CopyDotDak', 'copydotdak'),
2434 ('CommentsDir', 'commentsdir'),
2435 ('OverrideSuite', 'overridesuite'),
2436 ('ChangelogBase', 'changelogbase')]
2439 class Suite(object):
2440 def __init__(self, *args, **kwargs):
2444 return '<Suite %s>' % self.suite_name
2446 def __eq__(self, val):
2447 if isinstance(val, str):
2448 return (self.suite_name == val)
2449 # This signals to use the normal comparison operator
2450 return NotImplemented
2452 def __ne__(self, val):
2453 if isinstance(val, str):
2454 return (self.suite_name != val)
2455 # This signals to use the normal comparison operator
2456 return NotImplemented
2460 for disp, field in SUITE_FIELDS:
2461 val = getattr(self, field, None)
2463 ret.append("%s: %s" % (disp, val))
2465 return "\n".join(ret)
2467 __all__.append('Suite')
2470 def get_suite_architecture(suite, architecture, session=None):
2472 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2476 @param suite: Suite name to search for
2478 @type architecture: str
2479 @param architecture: Architecture name to search for
2481 @type session: Session
2482 @param session: Optional SQL session object (a temporary one will be
2483 generated if not supplied)
2485 @rtype: SuiteArchitecture
2486 @return: the SuiteArchitecture object or None
2489 q = session.query(SuiteArchitecture)
2490 q = q.join(Architecture).filter_by(arch_string=architecture)
2491 q = q.join(Suite).filter_by(suite_name=suite)
2495 except NoResultFound:
2498 __all__.append('get_suite_architecture')
2501 def get_suite(suite, session=None):
2503 Returns Suite object for given C{suite name}.
2506 @param suite: The name of the suite
2508 @type session: Session
2509 @param session: Optional SQLA session object (a temporary one will be
2510 generated if not supplied)
2513 @return: Suite object for the requested suite name (None if not present)
2516 q = session.query(Suite).filter_by(suite_name=suite)
2520 except NoResultFound:
2523 __all__.append('get_suite')
2525 ################################################################################
2527 class SuiteArchitecture(object):
2528 def __init__(self, *args, **kwargs):
2532 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2534 __all__.append('SuiteArchitecture')
2537 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2539 Returns list of Architecture objects for given C{suite} name
2542 @param source: Suite name to search for
2544 @type skipsrc: boolean
2545 @param skipsrc: Whether to skip returning the 'source' architecture entry
2548 @type skipall: boolean
2549 @param skipall: Whether to skip returning the 'all' architecture entry
2552 @type session: Session
2553 @param session: Optional SQL session object (a temporary one will be
2554 generated if not supplied)
2557 @return: list of Architecture objects for the given name (may be empty)
2560 q = session.query(Architecture)
2561 q = q.join(SuiteArchitecture)
2562 q = q.join(Suite).filter_by(suite_name=suite)
2565 q = q.filter(Architecture.arch_string != 'source')
2568 q = q.filter(Architecture.arch_string != 'all')
2570 q = q.order_by('arch_string')
2574 __all__.append('get_suite_architectures')
2576 ################################################################################
2578 class SuiteSrcFormat(object):
2579 def __init__(self, *args, **kwargs):
2583 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2585 __all__.append('SuiteSrcFormat')
2588 def get_suite_src_formats(suite, session=None):
2590 Returns list of allowed SrcFormat for C{suite}.
2593 @param suite: Suite name to search for
2595 @type session: Session
2596 @param session: Optional SQL session object (a temporary one will be
2597 generated if not supplied)
2600 @return: the list of allowed source formats for I{suite}
2603 q = session.query(SrcFormat)
2604 q = q.join(SuiteSrcFormat)
2605 q = q.join(Suite).filter_by(suite_name=suite)
2606 q = q.order_by('format_name')
2610 __all__.append('get_suite_src_formats')
2612 ################################################################################
2615 def __init__(self, *args, **kwargs):
2618 def __eq__(self, val):
2619 if isinstance(val, str):
2620 return (self.uid == val)
2621 # This signals to use the normal comparison operator
2622 return NotImplemented
2624 def __ne__(self, val):
2625 if isinstance(val, str):
2626 return (self.uid != val)
2627 # This signals to use the normal comparison operator
2628 return NotImplemented
2631 return '<Uid %s (%s)>' % (self.uid, self.name)
2633 __all__.append('Uid')
2636 def add_database_user(uidname, session=None):
2638 Adds a database user
2640 @type uidname: string
2641 @param uidname: The uid of the user to add
2643 @type session: SQLAlchemy
2644 @param session: Optional SQL session object (a temporary one will be
2645 generated if not supplied). If not passed, a commit will be performed at
2646 the end of the function, otherwise the caller is responsible for commiting.
2649 @return: the uid object for the given uidname
2652 session.execute("CREATE USER :uid", {'uid': uidname})
2653 session.commit_or_flush()
2655 __all__.append('add_database_user')
2658 def get_or_set_uid(uidname, session=None):
2660 Returns uid object for given uidname.
2662 If no matching uidname is found, a row is inserted.
2664 @type uidname: string
2665 @param uidname: The uid to add
2667 @type session: SQLAlchemy
2668 @param session: Optional SQL session object (a temporary one will be
2669 generated if not supplied). If not passed, a commit will be performed at
2670 the end of the function, otherwise the caller is responsible for commiting.
2673 @return: the uid object for the given uidname
2676 q = session.query(Uid).filter_by(uid=uidname)
2680 except NoResultFound:
2684 session.commit_or_flush()
2689 __all__.append('get_or_set_uid')
2692 def get_uid_from_fingerprint(fpr, session=None):
2693 q = session.query(Uid)
2694 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2698 except NoResultFound:
2701 __all__.append('get_uid_from_fingerprint')
2703 ################################################################################
2705 class UploadBlock(object):
2706 def __init__(self, *args, **kwargs):
2710 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2712 __all__.append('UploadBlock')
2714 ################################################################################
2716 class DBConn(object):
2718 database module init.
2722 def __init__(self, *args, **kwargs):
2723 self.__dict__ = self.__shared_state
2725 if not getattr(self, 'initialised', False):
2726 self.initialised = True
2727 self.debug = kwargs.has_key('debug')
2730 def __setuptables(self):
2740 'build_queue_files',
2743 'changes_pending_binaries',
2744 'changes_pending_files',
2745 'changes_pending_files_map',
2746 'changes_pending_source',
2747 'changes_pending_source_files',
2748 'changes_pool_files',
2761 'pending_bin_contents',
2771 'suite_architectures',
2772 'suite_src_formats',
2773 'suite_build_queue_copy',
2779 for table_name in tables:
2780 table = Table(table_name, self.db_meta, autoload=True)
2781 setattr(self, 'tbl_%s' % table_name, table)
2783 def __setupmappers(self):
2784 mapper(Architecture, self.tbl_architecture,
2785 properties = dict(arch_id = self.tbl_architecture.c.id))
2787 mapper(Archive, self.tbl_archive,
2788 properties = dict(archive_id = self.tbl_archive.c.id,
2789 archive_name = self.tbl_archive.c.name))
2791 mapper(BinAssociation, self.tbl_bin_associations,
2792 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2793 suite_id = self.tbl_bin_associations.c.suite,
2794 suite = relation(Suite),
2795 binary_id = self.tbl_bin_associations.c.bin,
2796 binary = relation(DBBinary)))
2798 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2799 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2800 filename = self.tbl_pending_bin_contents.c.filename,
2801 package = self.tbl_pending_bin_contents.c.package,
2802 version = self.tbl_pending_bin_contents.c.version,
2803 arch = self.tbl_pending_bin_contents.c.arch,
2804 otype = self.tbl_pending_bin_contents.c.type))
2806 mapper(DebContents, self.tbl_deb_contents,
2807 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2808 package=self.tbl_deb_contents.c.package,
2809 component=self.tbl_deb_contents.c.component,
2810 arch=self.tbl_deb_contents.c.arch,
2811 section=self.tbl_deb_contents.c.section,
2812 filename=self.tbl_deb_contents.c.filename))
2814 mapper(UdebContents, self.tbl_udeb_contents,
2815 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2816 package=self.tbl_udeb_contents.c.package,
2817 component=self.tbl_udeb_contents.c.component,
2818 arch=self.tbl_udeb_contents.c.arch,
2819 section=self.tbl_udeb_contents.c.section,
2820 filename=self.tbl_udeb_contents.c.filename))
2822 mapper(DBBinary, self.tbl_binaries,
2823 properties = dict(binary_id = self.tbl_binaries.c.id,
2824 package = self.tbl_binaries.c.package,
2825 version = self.tbl_binaries.c.version,
2826 maintainer_id = self.tbl_binaries.c.maintainer,
2827 maintainer = relation(Maintainer),
2828 source_id = self.tbl_binaries.c.source,
2829 source = relation(DBSource),
2830 arch_id = self.tbl_binaries.c.architecture,
2831 architecture = relation(Architecture),
2832 poolfile_id = self.tbl_binaries.c.file,
2833 poolfile = relation(PoolFile),
2834 binarytype = self.tbl_binaries.c.type,
2835 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2836 fingerprint = relation(Fingerprint),
2837 install_date = self.tbl_binaries.c.install_date,
2838 binassociations = relation(BinAssociation,
2839 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2841 mapper(BinaryACL, self.tbl_binary_acl,
2842 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2844 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2845 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2846 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2847 architecture = relation(Architecture)))
2849 mapper(Component, self.tbl_component,
2850 properties = dict(component_id = self.tbl_component.c.id,
2851 component_name = self.tbl_component.c.name))
2853 mapper(DBConfig, self.tbl_config,
2854 properties = dict(config_id = self.tbl_config.c.id))
2856 mapper(DSCFile, self.tbl_dsc_files,
2857 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2858 source_id = self.tbl_dsc_files.c.source,
2859 source = relation(DBSource),
2860 poolfile_id = self.tbl_dsc_files.c.file,
2861 poolfile = relation(PoolFile)))
2863 mapper(PoolFile, self.tbl_files,
2864 properties = dict(file_id = self.tbl_files.c.id,
2865 filesize = self.tbl_files.c.size,
2866 location_id = self.tbl_files.c.location,
2867 location = relation(Location)))
2869 mapper(Fingerprint, self.tbl_fingerprint,
2870 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2871 uid_id = self.tbl_fingerprint.c.uid,
2872 uid = relation(Uid),
2873 keyring_id = self.tbl_fingerprint.c.keyring,
2874 keyring = relation(Keyring),
2875 source_acl = relation(SourceACL),
2876 binary_acl = relation(BinaryACL)))
2878 mapper(Keyring, self.tbl_keyrings,
2879 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2880 keyring_id = self.tbl_keyrings.c.id))
2882 mapper(DBChange, self.tbl_changes,
2883 properties = dict(change_id = self.tbl_changes.c.id,
2884 poolfiles = relation(PoolFile,
2885 secondary=self.tbl_changes_pool_files,
2886 backref="changeslinks"),
2887 files = relation(ChangePendingFile,
2888 secondary=self.tbl_changes_pending_files_map,
2889 backref="changesfile"),
2890 in_queue_id = self.tbl_changes.c.in_queue,
2891 in_queue = relation(PolicyQueue,
2892 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2893 approved_for_id = self.tbl_changes.c.approved_for))
2895 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2896 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2898 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2899 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id))
2901 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2902 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2903 change = relation(DBChange),
2904 maintainer = relation(Maintainer,
2905 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2906 changedby = relation(Maintainer,
2907 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2908 fingerprint = relation(Fingerprint),
2909 source_files = relation(ChangePendingFile,
2910 secondary=self.tbl_changes_pending_source_files,
2911 backref="pending_sources")))
2912 files = relation(KnownChangePendingFile, backref="changesfile")))
2914 mapper(KnownChangePendingFile, self.tbl_changes_pending_files,
2915 properties = dict(known_change_pending_file_id = self.tbl_changes_pending_files.c.id))
2917 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2918 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2919 keyring = relation(Keyring, backref="keyring_acl_map"),
2920 architecture = relation(Architecture)))
2922 mapper(Location, self.tbl_location,
2923 properties = dict(location_id = self.tbl_location.c.id,
2924 component_id = self.tbl_location.c.component,
2925 component = relation(Component),
2926 archive_id = self.tbl_location.c.archive,
2927 archive = relation(Archive),
2928 archive_type = self.tbl_location.c.type))
2930 mapper(Maintainer, self.tbl_maintainer,
2931 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2933 mapper(NewComment, self.tbl_new_comments,
2934 properties = dict(comment_id = self.tbl_new_comments.c.id))
2936 mapper(Override, self.tbl_override,
2937 properties = dict(suite_id = self.tbl_override.c.suite,
2938 suite = relation(Suite),
2939 package = self.tbl_override.c.package,
2940 component_id = self.tbl_override.c.component,
2941 component = relation(Component),
2942 priority_id = self.tbl_override.c.priority,
2943 priority = relation(Priority),
2944 section_id = self.tbl_override.c.section,
2945 section = relation(Section),
2946 overridetype_id = self.tbl_override.c.type,
2947 overridetype = relation(OverrideType)))
2949 mapper(OverrideType, self.tbl_override_type,
2950 properties = dict(overridetype = self.tbl_override_type.c.type,
2951 overridetype_id = self.tbl_override_type.c.id))
2953 mapper(PolicyQueue, self.tbl_policy_queue,
2954 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
2956 mapper(Priority, self.tbl_priority,
2957 properties = dict(priority_id = self.tbl_priority.c.id))
2959 mapper(Section, self.tbl_section,
2960 properties = dict(section_id = self.tbl_section.c.id,
2961 section=self.tbl_section.c.section))
2963 mapper(DBSource, self.tbl_source,
2964 properties = dict(source_id = self.tbl_source.c.id,
2965 version = self.tbl_source.c.version,
2966 maintainer_id = self.tbl_source.c.maintainer,
2967 maintainer = relation(Maintainer,
2968 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2969 poolfile_id = self.tbl_source.c.file,
2970 poolfile = relation(PoolFile),
2971 fingerprint_id = self.tbl_source.c.sig_fpr,
2972 fingerprint = relation(Fingerprint),
2973 changedby_id = self.tbl_source.c.changedby,
2974 changedby = relation(Maintainer,
2975 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2976 srcfiles = relation(DSCFile,
2977 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2978 srcassociations = relation(SrcAssociation,
2979 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2980 srcuploaders = relation(SrcUploader)))
2982 mapper(SourceACL, self.tbl_source_acl,
2983 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2985 mapper(SrcAssociation, self.tbl_src_associations,
2986 properties = dict(sa_id = self.tbl_src_associations.c.id,
2987 suite_id = self.tbl_src_associations.c.suite,
2988 suite = relation(Suite),
2989 source_id = self.tbl_src_associations.c.source,
2990 source = relation(DBSource)))
2992 mapper(SrcFormat, self.tbl_src_format,
2993 properties = dict(src_format_id = self.tbl_src_format.c.id,
2994 format_name = self.tbl_src_format.c.format_name))
2996 mapper(SrcUploader, self.tbl_src_uploaders,
2997 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2998 source_id = self.tbl_src_uploaders.c.source,
2999 source = relation(DBSource,
3000 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3001 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3002 maintainer = relation(Maintainer,
3003 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3005 mapper(Suite, self.tbl_suite,
3006 properties = dict(suite_id = self.tbl_suite.c.id,
3007 policy_queue = relation(PolicyQueue),
3008 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3010 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3011 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3012 suite = relation(Suite, backref='suitearchitectures'),
3013 arch_id = self.tbl_suite_architectures.c.architecture,
3014 architecture = relation(Architecture)))
3016 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3017 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3018 suite = relation(Suite, backref='suitesrcformats'),
3019 src_format_id = self.tbl_suite_src_formats.c.src_format,
3020 src_format = relation(SrcFormat)))
3022 mapper(Uid, self.tbl_uid,
3023 properties = dict(uid_id = self.tbl_uid.c.id,
3024 fingerprint = relation(Fingerprint)))
3026 mapper(UploadBlock, self.tbl_upload_blocks,
3027 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3028 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3029 uid = relation(Uid, backref="uploadblocks")))
3031 ## Connection functions
3032 def __createconn(self):
3033 from config import Config
3037 connstr = "postgres://%s" % cnf["DB::Host"]
3038 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3039 connstr += ":%s" % cnf["DB::Port"]
3040 connstr += "/%s" % cnf["DB::Name"]
3043 connstr = "postgres:///%s" % cnf["DB::Name"]
3044 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3045 connstr += "?port=%s" % cnf["DB::Port"]
3047 self.db_pg = create_engine(connstr, echo=self.debug)
3048 self.db_meta = MetaData()
3049 self.db_meta.bind = self.db_pg
3050 self.db_smaker = sessionmaker(bind=self.db_pg,
3054 self.__setuptables()
3055 self.__setupmappers()
3058 return self.db_smaker()
3060 __all__.append('DBConn')