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 from config import Config
56 from textutils import fix_maintainer
58 ################################################################################
60 # Patch in support for the debversion field type so that it works during
63 class DebVersion(sqltypes.Text):
64 def get_col_spec(self):
67 sa_major_version = sqlalchemy.__version__[0:3]
68 if sa_major_version == "0.5":
69 from sqlalchemy.databases import postgres
70 postgres.ischema_names['debversion'] = DebVersion
72 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
74 ################################################################################
76 __all__ = ['IntegrityError', 'SQLAlchemyError']
78 ################################################################################
80 def session_wrapper(fn):
82 Wrapper around common ".., session=None):" handling. If the wrapped
83 function is called without passing 'session', we create a local one
84 and destroy it when the function ends.
86 Also attaches a commit_or_flush method to the session; if we created a
87 local session, this is a synonym for session.commit(), otherwise it is a
88 synonym for session.flush().
91 def wrapped(*args, **kwargs):
92 private_transaction = False
94 # Find the session object
95 session = kwargs.get('session')
98 if len(args) <= len(getargspec(fn)[0]) - 1:
99 # No session specified as last argument or in kwargs
100 private_transaction = True
101 session = kwargs['session'] = DBConn().session()
103 # Session is last argument in args
107 session = args[-1] = DBConn().session()
108 private_transaction = True
110 if private_transaction:
111 session.commit_or_flush = session.commit
113 session.commit_or_flush = session.flush
116 return fn(*args, **kwargs)
118 if private_transaction:
119 # We created a session; close it.
122 wrapped.__doc__ = fn.__doc__
123 wrapped.func_name = fn.func_name
127 __all__.append('session_wrapper')
129 ################################################################################
131 class Architecture(object):
132 def __init__(self, *args, **kwargs):
135 def __eq__(self, val):
136 if isinstance(val, str):
137 return (self.arch_string== val)
138 # This signals to use the normal comparison operator
139 return NotImplemented
141 def __ne__(self, val):
142 if isinstance(val, str):
143 return (self.arch_string != val)
144 # This signals to use the normal comparison operator
145 return NotImplemented
148 return '<Architecture %s>' % self.arch_string
150 __all__.append('Architecture')
153 def get_architecture(architecture, session=None):
155 Returns database id for given C{architecture}.
157 @type architecture: string
158 @param architecture: The name of the architecture
160 @type session: Session
161 @param session: Optional SQLA session object (a temporary one will be
162 generated if not supplied)
165 @return: Architecture object for the given arch (None if not present)
168 q = session.query(Architecture).filter_by(arch_string=architecture)
172 except NoResultFound:
175 __all__.append('get_architecture')
178 def get_architecture_suites(architecture, session=None):
180 Returns list of Suite objects for given C{architecture} name
183 @param source: Architecture name to search for
185 @type session: Session
186 @param session: Optional SQL session object (a temporary one will be
187 generated if not supplied)
190 @return: list of Suite objects for the given name (may be empty)
193 q = session.query(Suite)
194 q = q.join(SuiteArchitecture)
195 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
201 __all__.append('get_architecture_suites')
203 ################################################################################
205 class Archive(object):
206 def __init__(self, *args, **kwargs):
210 return '<Archive %s>' % self.archive_name
212 __all__.append('Archive')
215 def get_archive(archive, session=None):
217 returns database id for given C{archive}.
219 @type archive: string
220 @param archive: the name of the arhive
222 @type session: Session
223 @param session: Optional SQLA session object (a temporary one will be
224 generated if not supplied)
227 @return: Archive object for the given name (None if not present)
230 archive = archive.lower()
232 q = session.query(Archive).filter_by(archive_name=archive)
236 except NoResultFound:
239 __all__.append('get_archive')
241 ################################################################################
243 class BinAssociation(object):
244 def __init__(self, *args, **kwargs):
248 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
250 __all__.append('BinAssociation')
252 ################################################################################
254 class BinContents(object):
255 def __init__(self, *args, **kwargs):
259 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
261 __all__.append('BinContents')
263 ################################################################################
265 class DBBinary(object):
266 def __init__(self, *args, **kwargs):
270 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
272 __all__.append('DBBinary')
275 def get_suites_binary_in(package, session=None):
277 Returns list of Suite objects which given C{package} name is in
280 @param source: DBBinary package name to search for
283 @return: list of Suite objects for the given package
286 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
288 __all__.append('get_suites_binary_in')
291 def get_binary_from_id(binary_id, session=None):
293 Returns DBBinary object for given C{id}
296 @param binary_id: Id of the required binary
298 @type session: Session
299 @param session: Optional SQLA session object (a temporary one will be
300 generated if not supplied)
303 @return: DBBinary object for the given binary (None if not present)
306 q = session.query(DBBinary).filter_by(binary_id=binary_id)
310 except NoResultFound:
313 __all__.append('get_binary_from_id')
316 def get_binaries_from_name(package, version=None, architecture=None, session=None):
318 Returns list of DBBinary objects for given C{package} name
321 @param package: DBBinary package name to search for
323 @type version: str or None
324 @param version: Version to search for (or None)
326 @type package: str, list or None
327 @param package: Architectures to limit to (or None if no limit)
329 @type session: Session
330 @param session: Optional SQL session object (a temporary one will be
331 generated if not supplied)
334 @return: list of DBBinary objects for the given name (may be empty)
337 q = session.query(DBBinary).filter_by(package=package)
339 if version is not None:
340 q = q.filter_by(version=version)
342 if architecture is not None:
343 if not isinstance(architecture, list):
344 architecture = [architecture]
345 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
351 __all__.append('get_binaries_from_name')
354 def get_binaries_from_source_id(source_id, session=None):
356 Returns list of DBBinary objects for given C{source_id}
359 @param source_id: source_id to search for
361 @type session: Session
362 @param session: Optional SQL session object (a temporary one will be
363 generated if not supplied)
366 @return: list of DBBinary objects for the given name (may be empty)
369 return session.query(DBBinary).filter_by(source_id=source_id).all()
371 __all__.append('get_binaries_from_source_id')
374 def get_binary_from_name_suite(package, suitename, session=None):
375 ### For dak examine-package
376 ### XXX: Doesn't use object API yet
378 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
379 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
380 WHERE b.package=:package
382 AND fi.location = l.id
383 AND l.component = c.id
386 AND su.suite_name=:suitename
387 ORDER BY b.version DESC"""
389 return session.execute(sql, {'package': package, 'suitename': suitename})
391 __all__.append('get_binary_from_name_suite')
394 def get_binary_components(package, suitename, arch, session=None):
395 # Check for packages that have moved from one component to another
396 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
397 WHERE b.package=:package AND s.suite_name=:suitename
398 AND (a.arch_string = :arch OR a.arch_string = 'all')
399 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
400 AND f.location = l.id
401 AND l.component = c.id
404 vals = {'package': package, 'suitename': suitename, 'arch': arch}
406 return session.execute(query, vals)
408 __all__.append('get_binary_components')
410 ################################################################################
412 class BinaryACL(object):
413 def __init__(self, *args, **kwargs):
417 return '<BinaryACL %s>' % self.binary_acl_id
419 __all__.append('BinaryACL')
421 ################################################################################
423 class BinaryACLMap(object):
424 def __init__(self, *args, **kwargs):
428 return '<BinaryACLMap %s>' % self.binary_acl_map_id
430 __all__.append('BinaryACLMap')
432 ################################################################################
437 ArchiveDir "%(archivepath)s";
438 OverrideDir "/srv/ftp.debian.org/scripts/override/";
439 CacheDir "/srv/ftp.debian.org/database/";
444 Packages::Compress ". bzip2 gzip";
445 Sources::Compress ". bzip2 gzip";
450 bindirectory "incoming"
455 BinOverride "override.sid.all3";
456 BinCacheDB "packages-accepted.db";
458 FileList "%(filelist)s";
461 Packages::Extensions ".deb .udeb";
464 bindirectory "incoming/"
467 BinOverride "override.sid.all3";
468 SrcOverride "override.sid.all3.src";
469 FileList "%(filelist)s";
473 class BuildQueue(object):
474 def __init__(self, *args, **kwargs):
478 return '<BuildQueue %s>' % self.queue_name
480 def write_metadata(self, starttime, force=False):
481 # Do we write out metafiles?
482 if not (force or self.generate_metadata):
485 session = DBConn().session().object_session(self)
487 fl_fd = fl_name = ac_fd = ac_name = None
489 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
490 startdir = os.getcwd()
493 # Grab files we want to include
494 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
495 # Write file list with newer files
496 (fl_fd, fl_name) = mkstemp()
498 os.write(fl_fd, '%s\n' % n.fullpath)
501 # Write minimal apt.conf
502 # TODO: Remove hardcoding from template
503 (ac_fd, ac_name) = mkstemp()
504 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
505 'filelist': fl_name})
508 # Run apt-ftparchive generate
509 os.chdir(os.path.dirname(ac_name))
510 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
512 # Run apt-ftparchive release
513 # TODO: Eww - fix this
514 bname = os.path.basename(self.path)
517 os.system("""apt-ftparchive -qq -o APT::FTPArchive::Release::Origin="%s" -o APT::FTPArchive::Release::Label="%s" -o -o APT::FTPArchive::Release::Description="%s" -o APT::FTPArchive::Release::Architectures="${archs}" release %s > Release""", [self.origin, self.label, self.releasedescription, arches, bname])
522 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
523 if cnf.has_key("Dinstall::SigningPubKeyring"):
524 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
526 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
528 # Move the files if we got this far
529 os.rename('Release', os.path.join(bname, 'Release'))
531 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
533 # Clean up any left behind files
560 def clean_and_update(self, starttime, dryrun=False):
561 """WARNING: This routine commits for you"""
562 session = DBConn().session().object_session(self)
564 if self.generate_metadata:
565 self.write_metadata(starttime)
567 # Grab files older than our execution time
568 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
574 print "I: Would have removed %s from the queue" % o.fullpath
576 print "I: Removing %s from the queue" % o.fullpath
577 os.unlink(o.fullpath)
580 # If it wasn't there, don't worry
581 if e.errno == ENOENT:
584 # TODO: Replace with proper logging call
585 print "E: Could not remove %s" % o.fullpath
592 for f in os.listdir(self.path):
593 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
597 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
598 except NoResultFound:
599 fp = os.path.join(self.path, f)
601 print "I: Would remove unused link %s" % fp
603 print "I: Removing unused link %s" % fp
607 print "E: Failed to unlink unreferenced file %s" % r.fullpath
609 def add_file_from_pool(self, poolfile):
610 """Copies a file into the pool. Assumes that the PoolFile object is
611 attached to the same SQLAlchemy session as the Queue object is.
613 The caller is responsible for committing after calling this function."""
614 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
616 # Check if we have a file of this name or this ID already
617 for f in self.queuefiles:
618 if f.fileid is not None and f.fileid == poolfile.file_id or \
619 f.poolfile.filename == poolfile_basename:
620 # In this case, update the BuildQueueFile entry so we
621 # don't remove it too early
622 f.lastused = datetime.now()
623 DBConn().session().object_session(poolfile).add(f)
626 # Prepare BuildQueueFile object
627 qf = BuildQueueFile()
628 qf.build_queue_id = self.queue_id
629 qf.lastused = datetime.now()
630 qf.filename = poolfile_basename
632 targetpath = poolfile.fullpath
633 queuepath = os.path.join(self.path, poolfile_basename)
637 # We need to copy instead of symlink
639 utils.copy(targetpath, queuepath)
640 # NULL in the fileid field implies a copy
643 os.symlink(targetpath, queuepath)
644 qf.fileid = poolfile.file_id
648 # Get the same session as the PoolFile is using and add the qf to it
649 DBConn().session().object_session(poolfile).add(qf)
654 __all__.append('BuildQueue')
657 def get_build_queue(queuename, session=None):
659 Returns BuildQueue object for given C{queue name}, creating it if it does not
662 @type queuename: string
663 @param queuename: The name of the queue
665 @type session: Session
666 @param session: Optional SQLA session object (a temporary one will be
667 generated if not supplied)
670 @return: BuildQueue object for the given queue
673 q = session.query(BuildQueue).filter_by(queue_name=queuename)
677 except NoResultFound:
680 __all__.append('get_build_queue')
682 ################################################################################
684 class BuildQueueFile(object):
685 def __init__(self, *args, **kwargs):
689 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
693 return os.path.join(self.buildqueue.path, self.filename)
696 __all__.append('BuildQueueFile')
698 ################################################################################
700 class ChangePendingBinary(object):
701 def __init__(self, *args, **kwargs):
705 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
707 __all__.append('ChangePendingBinary')
709 ################################################################################
711 class ChangePendingFile(object):
712 def __init__(self, *args, **kwargs):
716 return '<ChangePendingFile %s>' % self.change_pending_file_id
718 __all__.append('ChangePendingFile')
720 ################################################################################
722 class ChangePendingSource(object):
723 def __init__(self, *args, **kwargs):
727 return '<ChangePendingSource %s>' % self.change_pending_source_id
729 __all__.append('ChangePendingSource')
731 ################################################################################
733 class Component(object):
734 def __init__(self, *args, **kwargs):
737 def __eq__(self, val):
738 if isinstance(val, str):
739 return (self.component_name == val)
740 # This signals to use the normal comparison operator
741 return NotImplemented
743 def __ne__(self, val):
744 if isinstance(val, str):
745 return (self.component_name != val)
746 # This signals to use the normal comparison operator
747 return NotImplemented
750 return '<Component %s>' % self.component_name
753 __all__.append('Component')
756 def get_component(component, session=None):
758 Returns database id for given C{component}.
760 @type component: string
761 @param component: The name of the override type
764 @return: the database id for the given component
767 component = component.lower()
769 q = session.query(Component).filter_by(component_name=component)
773 except NoResultFound:
776 __all__.append('get_component')
778 ################################################################################
780 class DBConfig(object):
781 def __init__(self, *args, **kwargs):
785 return '<DBConfig %s>' % self.name
787 __all__.append('DBConfig')
789 ################################################################################
792 def get_or_set_contents_file_id(filename, session=None):
794 Returns database id for given filename.
796 If no matching file is found, a row is inserted.
798 @type filename: string
799 @param filename: The filename
800 @type session: SQLAlchemy
801 @param session: Optional SQL session object (a temporary one will be
802 generated if not supplied). If not passed, a commit will be performed at
803 the end of the function, otherwise the caller is responsible for commiting.
806 @return: the database id for the given component
809 q = session.query(ContentFilename).filter_by(filename=filename)
812 ret = q.one().cafilename_id
813 except NoResultFound:
814 cf = ContentFilename()
815 cf.filename = filename
817 session.commit_or_flush()
818 ret = cf.cafilename_id
822 __all__.append('get_or_set_contents_file_id')
825 def get_contents(suite, overridetype, section=None, session=None):
827 Returns contents for a suite / overridetype combination, limiting
828 to a section if not None.
831 @param suite: Suite object
833 @type overridetype: OverrideType
834 @param overridetype: OverrideType object
836 @type section: Section
837 @param section: Optional section object to limit results to
839 @type session: SQLAlchemy
840 @param session: Optional SQL session object (a temporary one will be
841 generated if not supplied)
844 @return: ResultsProxy object set up to return tuples of (filename, section,
848 # find me all of the contents for a given suite
849 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
853 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
854 JOIN content_file_names n ON (c.filename=n.id)
855 JOIN binaries b ON (b.id=c.binary_pkg)
856 JOIN override o ON (o.package=b.package)
857 JOIN section s ON (s.id=o.section)
858 WHERE o.suite = :suiteid AND o.type = :overridetypeid
859 AND b.type=:overridetypename"""
861 vals = {'suiteid': suite.suite_id,
862 'overridetypeid': overridetype.overridetype_id,
863 'overridetypename': overridetype.overridetype}
865 if section is not None:
866 contents_q += " AND s.id = :sectionid"
867 vals['sectionid'] = section.section_id
869 contents_q += " ORDER BY fn"
871 return session.execute(contents_q, vals)
873 __all__.append('get_contents')
875 ################################################################################
877 class ContentFilepath(object):
878 def __init__(self, *args, **kwargs):
882 return '<ContentFilepath %s>' % self.filepath
884 __all__.append('ContentFilepath')
887 def get_or_set_contents_path_id(filepath, session=None):
889 Returns database id for given path.
891 If no matching file is found, a row is inserted.
893 @type filename: string
894 @param filename: The filepath
895 @type session: SQLAlchemy
896 @param session: Optional SQL session object (a temporary one will be
897 generated if not supplied). If not passed, a commit will be performed at
898 the end of the function, otherwise the caller is responsible for commiting.
901 @return: the database id for the given path
904 q = session.query(ContentFilepath).filter_by(filepath=filepath)
907 ret = q.one().cafilepath_id
908 except NoResultFound:
909 cf = ContentFilepath()
910 cf.filepath = filepath
912 session.commit_or_flush()
913 ret = cf.cafilepath_id
917 __all__.append('get_or_set_contents_path_id')
919 ################################################################################
921 class ContentAssociation(object):
922 def __init__(self, *args, **kwargs):
926 return '<ContentAssociation %s>' % self.ca_id
928 __all__.append('ContentAssociation')
930 def insert_content_paths(binary_id, fullpaths, session=None):
932 Make sure given path is associated with given binary id
935 @param binary_id: the id of the binary
936 @type fullpaths: list
937 @param fullpaths: the list of paths of the file being associated with the binary
938 @type session: SQLAlchemy session
939 @param session: Optional SQLAlchemy session. If this is passed, the caller
940 is responsible for ensuring a transaction has begun and committing the
941 results or rolling back based on the result code. If not passed, a commit
942 will be performed at the end of the function, otherwise the caller is
943 responsible for commiting.
945 @return: True upon success
950 session = DBConn().session()
956 for fullpath in fullpaths:
957 if fullpath.startswith( './' ):
958 fullpath = fullpath[2:]
960 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )", { 'filename': fullpath, 'id': binary_id} )
968 traceback.print_exc()
970 # Only rollback if we set up the session ourself
977 __all__.append('insert_content_paths')
979 ################################################################################
981 class DSCFile(object):
982 def __init__(self, *args, **kwargs):
986 return '<DSCFile %s>' % self.dscfile_id
988 __all__.append('DSCFile')
991 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
993 Returns a list of DSCFiles which may be empty
995 @type dscfile_id: int (optional)
996 @param dscfile_id: the dscfile_id of the DSCFiles to find
998 @type source_id: int (optional)
999 @param source_id: the source id related to the DSCFiles to find
1001 @type poolfile_id: int (optional)
1002 @param poolfile_id: the poolfile id related to the DSCFiles to find
1005 @return: Possibly empty list of DSCFiles
1008 q = session.query(DSCFile)
1010 if dscfile_id is not None:
1011 q = q.filter_by(dscfile_id=dscfile_id)
1013 if source_id is not None:
1014 q = q.filter_by(source_id=source_id)
1016 if poolfile_id is not None:
1017 q = q.filter_by(poolfile_id=poolfile_id)
1021 __all__.append('get_dscfiles')
1023 ################################################################################
1025 class PoolFile(object):
1026 def __init__(self, *args, **kwargs):
1030 return '<PoolFile %s>' % self.filename
1034 return os.path.join(self.location.path, self.filename)
1036 __all__.append('PoolFile')
1039 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1042 (ValidFileFound [boolean or None], PoolFile object or None)
1044 @type filename: string
1045 @param filename: the filename of the file to check against the DB
1048 @param filesize: the size of the file to check against the DB
1050 @type md5sum: string
1051 @param md5sum: the md5sum of the file to check against the DB
1053 @type location_id: int
1054 @param location_id: the id of the location to look in
1057 @return: Tuple of length 2.
1058 If more than one file found with that name:
1060 If valid pool file found: (True, PoolFile object)
1061 If valid pool file not found:
1062 (False, None) if no file found
1063 (False, PoolFile object) if file found with size/md5sum mismatch
1066 q = session.query(PoolFile).filter_by(filename=filename)
1067 q = q.join(Location).filter_by(location_id=location_id)
1077 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1085 __all__.append('check_poolfile')
1088 def get_poolfile_by_id(file_id, session=None):
1090 Returns a PoolFile objects or None for the given id
1093 @param file_id: the id of the file to look for
1095 @rtype: PoolFile or None
1096 @return: either the PoolFile object or None
1099 q = session.query(PoolFile).filter_by(file_id=file_id)
1103 except NoResultFound:
1106 __all__.append('get_poolfile_by_id')
1110 def get_poolfile_by_name(filename, location_id=None, session=None):
1112 Returns an array of PoolFile objects for the given filename and
1113 (optionally) location_id
1115 @type filename: string
1116 @param filename: the filename of the file to check against the DB
1118 @type location_id: int
1119 @param location_id: the id of the location to look in (optional)
1122 @return: array of PoolFile objects
1125 q = session.query(PoolFile).filter_by(filename=filename)
1127 if location_id is not None:
1128 q = q.join(Location).filter_by(location_id=location_id)
1132 __all__.append('get_poolfile_by_name')
1135 def get_poolfile_like_name(filename, session=None):
1137 Returns an array of PoolFile objects which are like the given name
1139 @type filename: string
1140 @param filename: the filename of the file to check against the DB
1143 @return: array of PoolFile objects
1146 # TODO: There must be a way of properly using bind parameters with %FOO%
1147 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
1151 __all__.append('get_poolfile_like_name')
1154 def add_poolfile(filename, datadict, location_id, session=None):
1156 Add a new file to the pool
1158 @type filename: string
1159 @param filename: filename
1161 @type datadict: dict
1162 @param datadict: dict with needed data
1164 @type location_id: int
1165 @param location_id: database id of the location
1168 @return: the PoolFile object created
1170 poolfile = PoolFile()
1171 poolfile.filename = filename
1172 poolfile.filesize = datadict["size"]
1173 poolfile.md5sum = datadict["md5sum"]
1174 poolfile.sha1sum = datadict["sha1sum"]
1175 poolfile.sha256sum = datadict["sha256sum"]
1176 poolfile.location_id = location_id
1178 session.add(poolfile)
1179 # Flush to get a file id (NB: This is not a commit)
1184 __all__.append('add_poolfile')
1186 ################################################################################
1188 class Fingerprint(object):
1189 def __init__(self, *args, **kwargs):
1193 return '<Fingerprint %s>' % self.fingerprint
1195 __all__.append('Fingerprint')
1198 def get_fingerprint(fpr, session=None):
1200 Returns Fingerprint object for given fpr.
1203 @param fpr: The fpr to find / add
1205 @type session: SQLAlchemy
1206 @param session: Optional SQL session object (a temporary one will be
1207 generated if not supplied).
1210 @return: the Fingerprint object for the given fpr or None
1213 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1217 except NoResultFound:
1222 __all__.append('get_fingerprint')
1225 def get_or_set_fingerprint(fpr, session=None):
1227 Returns Fingerprint object for given fpr.
1229 If no matching fpr is found, a row is inserted.
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). If not passed, a commit will be performed at
1237 the end of the function, otherwise the caller is responsible for commiting.
1238 A flush will be performed either way.
1241 @return: the Fingerprint object for the given fpr
1244 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1248 except NoResultFound:
1249 fingerprint = Fingerprint()
1250 fingerprint.fingerprint = fpr
1251 session.add(fingerprint)
1252 session.commit_or_flush()
1257 __all__.append('get_or_set_fingerprint')
1259 ################################################################################
1261 # Helper routine for Keyring class
1262 def get_ldap_name(entry):
1264 for k in ["cn", "mn", "sn"]:
1266 if ret and ret[0] != "" and ret[0] != "-":
1268 return " ".join(name)
1270 ################################################################################
1272 class Keyring(object):
1273 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1274 " --with-colons --fingerprint --fingerprint"
1279 def __init__(self, *args, **kwargs):
1283 return '<Keyring %s>' % self.keyring_name
1285 def de_escape_gpg_str(self, txt):
1286 esclist = re.split(r'(\\x..)', txt)
1287 for x in range(1,len(esclist),2):
1288 esclist[x] = "%c" % (int(esclist[x][2:],16))
1289 return "".join(esclist)
1291 def load_keys(self, keyring):
1294 if not self.keyring_id:
1295 raise Exception('Must be initialized with database information')
1297 k = os.popen(self.gpg_invocation % keyring, "r")
1301 for line in k.xreadlines():
1302 field = line.split(":")
1303 if field[0] == "pub":
1305 (name, addr) = email.Utils.parseaddr(field[9])
1306 name = re.sub(r"\s*[(].*[)]", "", name)
1307 if name == "" or addr == "" or "@" not in addr:
1309 addr = "invalid-uid"
1310 name = self.de_escape_gpg_str(name)
1311 self.keys[key] = {"email": addr}
1313 self.keys[key]["name"] = name
1314 self.keys[key]["aliases"] = [name]
1315 self.keys[key]["fingerprints"] = []
1317 elif key and field[0] == "sub" and len(field) >= 12:
1318 signingkey = ("s" in field[11])
1319 elif key and field[0] == "uid":
1320 (name, addr) = email.Utils.parseaddr(field[9])
1321 if name and name not in self.keys[key]["aliases"]:
1322 self.keys[key]["aliases"].append(name)
1323 elif signingkey and field[0] == "fpr":
1324 self.keys[key]["fingerprints"].append(field[9])
1325 self.fpr_lookup[field[9]] = key
1327 def import_users_from_ldap(self, session):
1331 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1332 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1334 l = ldap.open(LDAPServer)
1335 l.simple_bind_s("","")
1336 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1337 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1338 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1340 ldap_fin_uid_id = {}
1347 uid = entry["uid"][0]
1348 name = get_ldap_name(entry)
1349 fingerprints = entry["keyFingerPrint"]
1351 for f in fingerprints:
1352 key = self.fpr_lookup.get(f, None)
1353 if key not in self.keys:
1355 self.keys[key]["uid"] = uid
1359 keyid = get_or_set_uid(uid, session).uid_id
1360 byuid[keyid] = (uid, name)
1361 byname[uid] = (keyid, name)
1363 return (byname, byuid)
1365 def generate_users_from_keyring(self, format, session):
1369 for x in self.keys.keys():
1370 if self.keys[x]["email"] == "invalid-uid":
1372 self.keys[x]["uid"] = format % "invalid-uid"
1374 uid = format % self.keys[x]["email"]
1375 keyid = get_or_set_uid(uid, session).uid_id
1376 byuid[keyid] = (uid, self.keys[x]["name"])
1377 byname[uid] = (keyid, self.keys[x]["name"])
1378 self.keys[x]["uid"] = uid
1381 uid = format % "invalid-uid"
1382 keyid = get_or_set_uid(uid, session).uid_id
1383 byuid[keyid] = (uid, "ungeneratable user id")
1384 byname[uid] = (keyid, "ungeneratable user id")
1386 return (byname, byuid)
1388 __all__.append('Keyring')
1391 def get_keyring(keyring, session=None):
1393 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1394 If C{keyring} already has an entry, simply return the existing Keyring
1396 @type keyring: string
1397 @param keyring: the keyring name
1400 @return: the Keyring object for this keyring
1403 q = session.query(Keyring).filter_by(keyring_name=keyring)
1407 except NoResultFound:
1410 __all__.append('get_keyring')
1412 ################################################################################
1414 class KeyringACLMap(object):
1415 def __init__(self, *args, **kwargs):
1419 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1421 __all__.append('KeyringACLMap')
1423 ################################################################################
1425 class DBChange(object):
1426 def __init__(self, *args, **kwargs):
1430 return '<DBChange %s>' % self.changesname
1432 __all__.append('DBChange')
1435 def get_dbchange(filename, session=None):
1437 returns DBChange object for given C{filename}.
1439 @type archive: string
1440 @param archive: the name of the arhive
1442 @type session: Session
1443 @param session: Optional SQLA session object (a temporary one will be
1444 generated if not supplied)
1447 @return: Archive object for the given name (None if not present)
1450 q = session.query(DBChange).filter_by(changesname=filename)
1454 except NoResultFound:
1457 __all__.append('get_dbchange')
1459 ################################################################################
1461 class Location(object):
1462 def __init__(self, *args, **kwargs):
1466 return '<Location %s (%s)>' % (self.path, self.location_id)
1468 __all__.append('Location')
1471 def get_location(location, component=None, archive=None, session=None):
1473 Returns Location object for the given combination of location, component
1476 @type location: string
1477 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1479 @type component: string
1480 @param component: the component name (if None, no restriction applied)
1482 @type archive: string
1483 @param archive_id: the archive name (if None, no restriction applied)
1485 @rtype: Location / None
1486 @return: Either a Location object or None if one can't be found
1489 q = session.query(Location).filter_by(path=location)
1491 if archive is not None:
1492 q = q.join(Archive).filter_by(archive_name=archive)
1494 if component is not None:
1495 q = q.join(Component).filter_by(component_name=component)
1499 except NoResultFound:
1502 __all__.append('get_location')
1504 ################################################################################
1506 class Maintainer(object):
1507 def __init__(self, *args, **kwargs):
1511 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1513 def get_split_maintainer(self):
1514 if not hasattr(self, 'name') or self.name is None:
1515 return ('', '', '', '')
1517 return fix_maintainer(self.name.strip())
1519 __all__.append('Maintainer')
1522 def get_or_set_maintainer(name, session=None):
1524 Returns Maintainer object for given maintainer name.
1526 If no matching maintainer name is found, a row is inserted.
1529 @param name: The maintainer name to add
1531 @type session: SQLAlchemy
1532 @param session: Optional SQL session object (a temporary one will be
1533 generated if not supplied). If not passed, a commit will be performed at
1534 the end of the function, otherwise the caller is responsible for commiting.
1535 A flush will be performed either way.
1538 @return: the Maintainer object for the given maintainer
1541 q = session.query(Maintainer).filter_by(name=name)
1544 except NoResultFound:
1545 maintainer = Maintainer()
1546 maintainer.name = name
1547 session.add(maintainer)
1548 session.commit_or_flush()
1553 __all__.append('get_or_set_maintainer')
1556 def get_maintainer(maintainer_id, session=None):
1558 Return the name of the maintainer behind C{maintainer_id} or None if that
1559 maintainer_id is invalid.
1561 @type maintainer_id: int
1562 @param maintainer_id: the id of the maintainer
1565 @return: the Maintainer with this C{maintainer_id}
1568 return session.query(Maintainer).get(maintainer_id)
1570 __all__.append('get_maintainer')
1572 ################################################################################
1574 class NewComment(object):
1575 def __init__(self, *args, **kwargs):
1579 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1581 __all__.append('NewComment')
1584 def has_new_comment(package, version, session=None):
1586 Returns true if the given combination of C{package}, C{version} has a comment.
1588 @type package: string
1589 @param package: name of the package
1591 @type version: string
1592 @param version: package version
1594 @type session: Session
1595 @param session: Optional SQLA session object (a temporary one will be
1596 generated if not supplied)
1602 q = session.query(NewComment)
1603 q = q.filter_by(package=package)
1604 q = q.filter_by(version=version)
1606 return bool(q.count() > 0)
1608 __all__.append('has_new_comment')
1611 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1613 Returns (possibly empty) list of NewComment objects for the given
1616 @type package: string (optional)
1617 @param package: name of the package
1619 @type version: string (optional)
1620 @param version: package version
1622 @type comment_id: int (optional)
1623 @param comment_id: An id of a comment
1625 @type session: Session
1626 @param session: Optional SQLA session object (a temporary one will be
1627 generated if not supplied)
1630 @return: A (possibly empty) list of NewComment objects will be returned
1633 q = session.query(NewComment)
1634 if package is not None: q = q.filter_by(package=package)
1635 if version is not None: q = q.filter_by(version=version)
1636 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1640 __all__.append('get_new_comments')
1642 ################################################################################
1644 class Override(object):
1645 def __init__(self, *args, **kwargs):
1649 return '<Override %s (%s)>' % (self.package, self.suite_id)
1651 __all__.append('Override')
1654 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1656 Returns Override object for the given parameters
1658 @type package: string
1659 @param package: The name of the package
1661 @type suite: string, list or None
1662 @param suite: The name of the suite (or suites if a list) to limit to. If
1663 None, don't limit. Defaults to None.
1665 @type component: string, list or None
1666 @param component: The name of the component (or components if a list) to
1667 limit to. If None, don't limit. Defaults to None.
1669 @type overridetype: string, list or None
1670 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1671 limit to. If None, don't limit. Defaults to None.
1673 @type session: Session
1674 @param session: Optional SQLA session object (a temporary one will be
1675 generated if not supplied)
1678 @return: A (possibly empty) list of Override objects will be returned
1681 q = session.query(Override)
1682 q = q.filter_by(package=package)
1684 if suite is not None:
1685 if not isinstance(suite, list): suite = [suite]
1686 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1688 if component is not None:
1689 if not isinstance(component, list): component = [component]
1690 q = q.join(Component).filter(Component.component_name.in_(component))
1692 if overridetype is not None:
1693 if not isinstance(overridetype, list): overridetype = [overridetype]
1694 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1698 __all__.append('get_override')
1701 ################################################################################
1703 class OverrideType(object):
1704 def __init__(self, *args, **kwargs):
1708 return '<OverrideType %s>' % self.overridetype
1710 __all__.append('OverrideType')
1713 def get_override_type(override_type, session=None):
1715 Returns OverrideType object for given C{override type}.
1717 @type override_type: string
1718 @param override_type: The name of the override type
1720 @type session: Session
1721 @param session: Optional SQLA session object (a temporary one will be
1722 generated if not supplied)
1725 @return: the database id for the given override type
1728 q = session.query(OverrideType).filter_by(overridetype=override_type)
1732 except NoResultFound:
1735 __all__.append('get_override_type')
1737 ################################################################################
1739 class PendingContentAssociation(object):
1740 def __init__(self, *args, **kwargs):
1744 return '<PendingContentAssociation %s>' % self.pca_id
1746 __all__.append('PendingContentAssociation')
1748 def insert_pending_content_paths(package, fullpaths, session=None):
1750 Make sure given paths are temporarily associated with given
1754 @param package: the package to associate with should have been read in from the binary control file
1755 @type fullpaths: list
1756 @param fullpaths: the list of paths of the file being associated with the binary
1757 @type session: SQLAlchemy session
1758 @param session: Optional SQLAlchemy session. If this is passed, the caller
1759 is responsible for ensuring a transaction has begun and committing the
1760 results or rolling back based on the result code. If not passed, a commit
1761 will be performed at the end of the function
1763 @return: True upon success, False if there is a problem
1766 privatetrans = False
1769 session = DBConn().session()
1773 arch = get_architecture(package['Architecture'], session)
1774 arch_id = arch.arch_id
1776 # Remove any already existing recorded files for this package
1777 q = session.query(PendingContentAssociation)
1778 q = q.filter_by(package=package['Package'])
1779 q = q.filter_by(version=package['Version'])
1780 q = q.filter_by(architecture=arch_id)
1785 for fullpath in fullpaths:
1786 (path, filename) = os.path.split(fullpath)
1788 if path.startswith( "./" ):
1791 filepath_id = get_or_set_contents_path_id(path, session)
1792 filename_id = get_or_set_contents_file_id(filename, session)
1794 pathcache[fullpath] = (filepath_id, filename_id)
1796 for fullpath, dat in pathcache.items():
1797 pca = PendingContentAssociation()
1798 pca.package = package['Package']
1799 pca.version = package['Version']
1800 pca.filepath_id = dat[0]
1801 pca.filename_id = dat[1]
1802 pca.architecture = arch_id
1805 # Only commit if we set up the session ourself
1813 except Exception, e:
1814 traceback.print_exc()
1816 # Only rollback if we set up the session ourself
1823 __all__.append('insert_pending_content_paths')
1825 ################################################################################
1827 class PolicyQueue(object):
1828 def __init__(self, *args, **kwargs):
1832 return '<PolicyQueue %s>' % self.queue_name
1834 __all__.append('PolicyQueue')
1837 def get_policy_queue(queuename, session=None):
1839 Returns PolicyQueue object for given C{queue name}
1841 @type queuename: string
1842 @param queuename: The name of the queue
1844 @type session: Session
1845 @param session: Optional SQLA session object (a temporary one will be
1846 generated if not supplied)
1849 @return: PolicyQueue object for the given queue
1852 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1856 except NoResultFound:
1859 __all__.append('get_policy_queue')
1861 ################################################################################
1863 class Priority(object):
1864 def __init__(self, *args, **kwargs):
1867 def __eq__(self, val):
1868 if isinstance(val, str):
1869 return (self.priority == val)
1870 # This signals to use the normal comparison operator
1871 return NotImplemented
1873 def __ne__(self, val):
1874 if isinstance(val, str):
1875 return (self.priority != val)
1876 # This signals to use the normal comparison operator
1877 return NotImplemented
1880 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1882 __all__.append('Priority')
1885 def get_priority(priority, session=None):
1887 Returns Priority object for given C{priority name}.
1889 @type priority: string
1890 @param priority: The name of the priority
1892 @type session: Session
1893 @param session: Optional SQLA session object (a temporary one will be
1894 generated if not supplied)
1897 @return: Priority object for the given priority
1900 q = session.query(Priority).filter_by(priority=priority)
1904 except NoResultFound:
1907 __all__.append('get_priority')
1910 def get_priorities(session=None):
1912 Returns dictionary of priority names -> id mappings
1914 @type session: Session
1915 @param session: Optional SQL session object (a temporary one will be
1916 generated if not supplied)
1919 @return: dictionary of priority names -> id mappings
1923 q = session.query(Priority)
1925 ret[x.priority] = x.priority_id
1929 __all__.append('get_priorities')
1931 ################################################################################
1933 class Section(object):
1934 def __init__(self, *args, **kwargs):
1937 def __eq__(self, val):
1938 if isinstance(val, str):
1939 return (self.section == val)
1940 # This signals to use the normal comparison operator
1941 return NotImplemented
1943 def __ne__(self, val):
1944 if isinstance(val, str):
1945 return (self.section != val)
1946 # This signals to use the normal comparison operator
1947 return NotImplemented
1950 return '<Section %s>' % self.section
1952 __all__.append('Section')
1955 def get_section(section, session=None):
1957 Returns Section object for given C{section name}.
1959 @type section: string
1960 @param section: The name of the section
1962 @type session: Session
1963 @param session: Optional SQLA session object (a temporary one will be
1964 generated if not supplied)
1967 @return: Section object for the given section name
1970 q = session.query(Section).filter_by(section=section)
1974 except NoResultFound:
1977 __all__.append('get_section')
1980 def get_sections(session=None):
1982 Returns dictionary of section names -> id mappings
1984 @type session: Session
1985 @param session: Optional SQL session object (a temporary one will be
1986 generated if not supplied)
1989 @return: dictionary of section names -> id mappings
1993 q = session.query(Section)
1995 ret[x.section] = x.section_id
1999 __all__.append('get_sections')
2001 ################################################################################
2003 class DBSource(object):
2004 def __init__(self, *args, **kwargs):
2008 return '<DBSource %s (%s)>' % (self.source, self.version)
2010 __all__.append('DBSource')
2013 def source_exists(source, source_version, suites = ["any"], session=None):
2015 Ensure that source exists somewhere in the archive for the binary
2016 upload being processed.
2017 1. exact match => 1.0-3
2018 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2020 @type package: string
2021 @param package: package source name
2023 @type source_version: string
2024 @param source_version: expected source version
2027 @param suites: list of suites to check in, default I{any}
2029 @type session: Session
2030 @param session: Optional SQLA session object (a temporary one will be
2031 generated if not supplied)
2034 @return: returns 1 if a source with expected version is found, otherwise 0
2041 for suite in suites:
2042 q = session.query(DBSource).filter_by(source=source)
2044 # source must exist in suite X, or in some other suite that's
2045 # mapped to X, recursively... silent-maps are counted too,
2046 # unreleased-maps aren't.
2047 maps = cnf.ValueList("SuiteMappings")[:]
2049 maps = [ m.split() for m in maps ]
2050 maps = [ (x[1], x[2]) for x in maps
2051 if x[0] == "map" or x[0] == "silent-map" ]
2054 if x[1] in s and x[0] not in s:
2057 q = q.join(SrcAssociation).join(Suite)
2058 q = q.filter(Suite.suite_name.in_(s))
2060 # Reduce the query results to a list of version numbers
2061 ql = [ j.version for j in q.all() ]
2064 if source_version in ql:
2068 from daklib.regexes import re_bin_only_nmu
2069 orig_source_version = re_bin_only_nmu.sub('', source_version)
2070 if orig_source_version in ql:
2073 # No source found so return not ok
2078 __all__.append('source_exists')
2081 def get_suites_source_in(source, session=None):
2083 Returns list of Suite objects which given C{source} name is in
2086 @param source: DBSource package name to search for
2089 @return: list of Suite objects for the given source
2092 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2094 __all__.append('get_suites_source_in')
2097 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2099 Returns list of DBSource objects for given C{source} name and other parameters
2102 @param source: DBSource package name to search for
2104 @type source: str or None
2105 @param source: DBSource version name to search for or None if not applicable
2107 @type dm_upload_allowed: bool
2108 @param dm_upload_allowed: If None, no effect. If True or False, only
2109 return packages with that dm_upload_allowed setting
2111 @type session: Session
2112 @param session: Optional SQL session object (a temporary one will be
2113 generated if not supplied)
2116 @return: list of DBSource objects for the given name (may be empty)
2119 q = session.query(DBSource).filter_by(source=source)
2121 if version is not None:
2122 q = q.filter_by(version=version)
2124 if dm_upload_allowed is not None:
2125 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2129 __all__.append('get_sources_from_name')
2132 def get_source_in_suite(source, suite, session=None):
2134 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2136 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2137 - B{suite} - a suite name, eg. I{unstable}
2139 @type source: string
2140 @param source: source package name
2143 @param suite: the suite name
2146 @return: the version for I{source} in I{suite}
2150 q = session.query(SrcAssociation)
2151 q = q.join('source').filter_by(source=source)
2152 q = q.join('suite').filter_by(suite_name=suite)
2155 return q.one().source
2156 except NoResultFound:
2159 __all__.append('get_source_in_suite')
2161 ################################################################################
2164 def add_dsc_to_db(u, filename, session=None):
2165 entry = u.pkg.files[filename]
2169 source.source = u.pkg.dsc["source"]
2170 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2171 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2172 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2173 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2174 source.install_date = datetime.now().date()
2176 dsc_component = entry["component"]
2177 dsc_location_id = entry["location id"]
2179 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2181 # Set up a new poolfile if necessary
2182 if not entry.has_key("files id") or not entry["files id"]:
2183 filename = entry["pool name"] + filename
2184 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2186 pfs.append(poolfile)
2187 entry["files id"] = poolfile.file_id
2189 source.poolfile_id = entry["files id"]
2193 for suite_name in u.pkg.changes["distribution"].keys():
2194 sa = SrcAssociation()
2195 sa.source_id = source.source_id
2196 sa.suite_id = get_suite(suite_name).suite_id
2201 # Add the source files to the DB (files and dsc_files)
2203 dscfile.source_id = source.source_id
2204 dscfile.poolfile_id = entry["files id"]
2205 session.add(dscfile)
2207 for dsc_file, dentry in u.pkg.dsc_files.items():
2209 df.source_id = source.source_id
2211 # If the .orig tarball is already in the pool, it's
2212 # files id is stored in dsc_files by check_dsc().
2213 files_id = dentry.get("files id", None)
2215 # Find the entry in the files hash
2216 # TODO: Bail out here properly
2218 for f, e in u.pkg.files.items():
2223 if files_id is None:
2224 filename = dfentry["pool name"] + dsc_file
2226 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2227 # FIXME: needs to check for -1/-2 and or handle exception
2228 if found and obj is not None:
2229 files_id = obj.file_id
2232 # If still not found, add it
2233 if files_id is None:
2234 # HACK: Force sha1sum etc into dentry
2235 dentry["sha1sum"] = dfentry["sha1sum"]
2236 dentry["sha256sum"] = dfentry["sha256sum"]
2237 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2238 pfs.append(poolfile)
2239 files_id = poolfile.file_id
2241 poolfile = get_poolfile_by_id(files_id, session)
2242 if poolfile is None:
2243 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2244 pfs.append(poolfile)
2246 df.poolfile_id = files_id
2251 # Add the src_uploaders to the DB
2252 uploader_ids = [source.maintainer_id]
2253 if u.pkg.dsc.has_key("uploaders"):
2254 for up in u.pkg.dsc["uploaders"].split(","):
2256 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2259 for up in uploader_ids:
2260 if added_ids.has_key(up):
2261 utils.warn("Already saw uploader %s for source %s" % (up, source.source))
2267 su.maintainer_id = up
2268 su.source_id = source.source_id
2273 return dsc_component, dsc_location_id, pfs
2275 __all__.append('add_dsc_to_db')
2278 def add_deb_to_db(u, filename, session=None):
2280 Contrary to what you might expect, this routine deals with both
2281 debs and udebs. That info is in 'dbtype', whilst 'type' is
2282 'deb' for both of them
2285 entry = u.pkg.files[filename]
2288 bin.package = entry["package"]
2289 bin.version = entry["version"]
2290 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2291 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2292 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2293 bin.binarytype = entry["dbtype"]
2296 filename = entry["pool name"] + filename
2297 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2298 if not entry.get("location id", None):
2299 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2301 if entry.get("files id", None):
2302 poolfile = get_poolfile_by_id(bin.poolfile_id)
2303 bin.poolfile_id = entry["files id"]
2305 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2306 bin.poolfile_id = entry["files id"] = poolfile.file_id
2309 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2310 if len(bin_sources) != 1:
2311 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2312 (bin.package, bin.version, bin.architecture.arch_string,
2313 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2315 bin.source_id = bin_sources[0].source_id
2317 # Add and flush object so it has an ID
2321 # Add BinAssociations
2322 for suite_name in u.pkg.changes["distribution"].keys():
2323 ba = BinAssociation()
2324 ba.binary_id = bin.binary_id
2325 ba.suite_id = get_suite(suite_name).suite_id
2330 # Deal with contents - disabled for now
2331 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2333 # print "REJECT\nCould not determine contents of package %s" % bin.package
2334 # session.rollback()
2335 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2339 __all__.append('add_deb_to_db')
2341 ################################################################################
2343 class SourceACL(object):
2344 def __init__(self, *args, **kwargs):
2348 return '<SourceACL %s>' % self.source_acl_id
2350 __all__.append('SourceACL')
2352 ################################################################################
2354 class SrcAssociation(object):
2355 def __init__(self, *args, **kwargs):
2359 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2361 __all__.append('SrcAssociation')
2363 ################################################################################
2365 class SrcFormat(object):
2366 def __init__(self, *args, **kwargs):
2370 return '<SrcFormat %s>' % (self.format_name)
2372 __all__.append('SrcFormat')
2374 ################################################################################
2376 class SrcUploader(object):
2377 def __init__(self, *args, **kwargs):
2381 return '<SrcUploader %s>' % self.uploader_id
2383 __all__.append('SrcUploader')
2385 ################################################################################
2387 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2388 ('SuiteID', 'suite_id'),
2389 ('Version', 'version'),
2390 ('Origin', 'origin'),
2392 ('Description', 'description'),
2393 ('Untouchable', 'untouchable'),
2394 ('Announce', 'announce'),
2395 ('Codename', 'codename'),
2396 ('OverrideCodename', 'overridecodename'),
2397 ('ValidTime', 'validtime'),
2398 ('Priority', 'priority'),
2399 ('NotAutomatic', 'notautomatic'),
2400 ('CopyChanges', 'copychanges'),
2401 ('CopyDotDak', 'copydotdak'),
2402 ('CommentsDir', 'commentsdir'),
2403 ('OverrideSuite', 'overridesuite'),
2404 ('ChangelogBase', 'changelogbase')]
2407 class Suite(object):
2408 def __init__(self, *args, **kwargs):
2412 return '<Suite %s>' % self.suite_name
2414 def __eq__(self, val):
2415 if isinstance(val, str):
2416 return (self.suite_name == val)
2417 # This signals to use the normal comparison operator
2418 return NotImplemented
2420 def __ne__(self, val):
2421 if isinstance(val, str):
2422 return (self.suite_name != val)
2423 # This signals to use the normal comparison operator
2424 return NotImplemented
2428 for disp, field in SUITE_FIELDS:
2429 val = getattr(self, field, None)
2431 ret.append("%s: %s" % (disp, val))
2433 return "\n".join(ret)
2435 __all__.append('Suite')
2438 def get_suite_architecture(suite, architecture, session=None):
2440 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2444 @param suite: Suite name to search for
2446 @type architecture: str
2447 @param architecture: Architecture name to search for
2449 @type session: Session
2450 @param session: Optional SQL session object (a temporary one will be
2451 generated if not supplied)
2453 @rtype: SuiteArchitecture
2454 @return: the SuiteArchitecture object or None
2457 q = session.query(SuiteArchitecture)
2458 q = q.join(Architecture).filter_by(arch_string=architecture)
2459 q = q.join(Suite).filter_by(suite_name=suite)
2463 except NoResultFound:
2466 __all__.append('get_suite_architecture')
2469 def get_suite(suite, session=None):
2471 Returns Suite object for given C{suite name}.
2474 @param suite: The name of the suite
2476 @type session: Session
2477 @param session: Optional SQLA session object (a temporary one will be
2478 generated if not supplied)
2481 @return: Suite object for the requested suite name (None if not present)
2484 q = session.query(Suite).filter_by(suite_name=suite)
2488 except NoResultFound:
2491 __all__.append('get_suite')
2493 ################################################################################
2495 class SuiteArchitecture(object):
2496 def __init__(self, *args, **kwargs):
2500 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2502 __all__.append('SuiteArchitecture')
2505 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2507 Returns list of Architecture objects for given C{suite} name
2510 @param source: Suite name to search for
2512 @type skipsrc: boolean
2513 @param skipsrc: Whether to skip returning the 'source' architecture entry
2516 @type skipall: boolean
2517 @param skipall: Whether to skip returning the 'all' architecture entry
2520 @type session: Session
2521 @param session: Optional SQL session object (a temporary one will be
2522 generated if not supplied)
2525 @return: list of Architecture objects for the given name (may be empty)
2528 q = session.query(Architecture)
2529 q = q.join(SuiteArchitecture)
2530 q = q.join(Suite).filter_by(suite_name=suite)
2533 q = q.filter(Architecture.arch_string != 'source')
2536 q = q.filter(Architecture.arch_string != 'all')
2538 q = q.order_by('arch_string')
2542 __all__.append('get_suite_architectures')
2544 ################################################################################
2546 class SuiteSrcFormat(object):
2547 def __init__(self, *args, **kwargs):
2551 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2553 __all__.append('SuiteSrcFormat')
2556 def get_suite_src_formats(suite, session=None):
2558 Returns list of allowed SrcFormat for C{suite}.
2561 @param suite: Suite name to search for
2563 @type session: Session
2564 @param session: Optional SQL session object (a temporary one will be
2565 generated if not supplied)
2568 @return: the list of allowed source formats for I{suite}
2571 q = session.query(SrcFormat)
2572 q = q.join(SuiteSrcFormat)
2573 q = q.join(Suite).filter_by(suite_name=suite)
2574 q = q.order_by('format_name')
2578 __all__.append('get_suite_src_formats')
2580 ################################################################################
2583 def __init__(self, *args, **kwargs):
2586 def __eq__(self, val):
2587 if isinstance(val, str):
2588 return (self.uid == val)
2589 # This signals to use the normal comparison operator
2590 return NotImplemented
2592 def __ne__(self, val):
2593 if isinstance(val, str):
2594 return (self.uid != val)
2595 # This signals to use the normal comparison operator
2596 return NotImplemented
2599 return '<Uid %s (%s)>' % (self.uid, self.name)
2601 __all__.append('Uid')
2604 def add_database_user(uidname, session=None):
2606 Adds a database user
2608 @type uidname: string
2609 @param uidname: The uid of the user to add
2611 @type session: SQLAlchemy
2612 @param session: Optional SQL session object (a temporary one will be
2613 generated if not supplied). If not passed, a commit will be performed at
2614 the end of the function, otherwise the caller is responsible for commiting.
2617 @return: the uid object for the given uidname
2620 session.execute("CREATE USER :uid", {'uid': uidname})
2621 session.commit_or_flush()
2623 __all__.append('add_database_user')
2626 def get_or_set_uid(uidname, session=None):
2628 Returns uid object for given uidname.
2630 If no matching uidname is found, a row is inserted.
2632 @type uidname: string
2633 @param uidname: The uid to add
2635 @type session: SQLAlchemy
2636 @param session: Optional SQL session object (a temporary one will be
2637 generated if not supplied). If not passed, a commit will be performed at
2638 the end of the function, otherwise the caller is responsible for commiting.
2641 @return: the uid object for the given uidname
2644 q = session.query(Uid).filter_by(uid=uidname)
2648 except NoResultFound:
2652 session.commit_or_flush()
2657 __all__.append('get_or_set_uid')
2660 def get_uid_from_fingerprint(fpr, session=None):
2661 q = session.query(Uid)
2662 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2666 except NoResultFound:
2669 __all__.append('get_uid_from_fingerprint')
2671 ################################################################################
2673 class UploadBlock(object):
2674 def __init__(self, *args, **kwargs):
2678 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2680 __all__.append('UploadBlock')
2682 ################################################################################
2684 class DBConn(object):
2686 database module init.
2690 def __init__(self, *args, **kwargs):
2691 self.__dict__ = self.__shared_state
2693 if not getattr(self, 'initialised', False):
2694 self.initialised = True
2695 self.debug = kwargs.has_key('debug')
2698 def __setuptables(self):
2707 'build_queue_files',
2710 'content_associations',
2711 'content_file_names',
2712 'content_file_paths',
2713 'changes_pending_binaries',
2714 'changes_pending_files',
2715 'changes_pending_files_map',
2716 'changes_pending_source',
2717 'changes_pending_source_files',
2718 'changes_pool_files',
2730 'pending_content_associations',
2740 'suite_architectures',
2741 'suite_src_formats',
2742 'suite_build_queue_copy',
2747 for table_name in tables:
2748 table = Table(table_name, self.db_meta, autoload=True)
2749 setattr(self, 'tbl_%s' % table_name, table)
2751 def __setupmappers(self):
2752 mapper(Architecture, self.tbl_architecture,
2753 properties = dict(arch_id = self.tbl_architecture.c.id))
2755 mapper(Archive, self.tbl_archive,
2756 properties = dict(archive_id = self.tbl_archive.c.id,
2757 archive_name = self.tbl_archive.c.name))
2759 mapper(BinAssociation, self.tbl_bin_associations,
2760 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2761 suite_id = self.tbl_bin_associations.c.suite,
2762 suite = relation(Suite),
2763 binary_id = self.tbl_bin_associations.c.bin,
2764 binary = relation(DBBinary)))
2766 mapper(BuildQueue, self.tbl_build_queue,
2767 properties = dict(queue_id = self.tbl_build_queue.c.id))
2769 mapper(BuildQueueFile, self.tbl_build_queue_files,
2770 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2771 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2773 mapper(DBBinary, self.tbl_binaries,
2774 properties = dict(binary_id = self.tbl_binaries.c.id,
2775 package = self.tbl_binaries.c.package,
2776 version = self.tbl_binaries.c.version,
2777 maintainer_id = self.tbl_binaries.c.maintainer,
2778 maintainer = relation(Maintainer),
2779 source_id = self.tbl_binaries.c.source,
2780 source = relation(DBSource),
2781 arch_id = self.tbl_binaries.c.architecture,
2782 architecture = relation(Architecture),
2783 poolfile_id = self.tbl_binaries.c.file,
2784 poolfile = relation(PoolFile),
2785 binarytype = self.tbl_binaries.c.type,
2786 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2787 fingerprint = relation(Fingerprint),
2788 install_date = self.tbl_binaries.c.install_date,
2789 binassociations = relation(BinAssociation,
2790 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2792 mapper(BinaryACL, self.tbl_binary_acl,
2793 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2795 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2796 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2797 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2798 architecture = relation(Architecture)))
2800 mapper(Component, self.tbl_component,
2801 properties = dict(component_id = self.tbl_component.c.id,
2802 component_name = self.tbl_component.c.name))
2804 mapper(DBConfig, self.tbl_config,
2805 properties = dict(config_id = self.tbl_config.c.id))
2807 mapper(DSCFile, self.tbl_dsc_files,
2808 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2809 source_id = self.tbl_dsc_files.c.source,
2810 source = relation(DBSource),
2811 poolfile_id = self.tbl_dsc_files.c.file,
2812 poolfile = relation(PoolFile)))
2814 mapper(PoolFile, self.tbl_files,
2815 properties = dict(file_id = self.tbl_files.c.id,
2816 filesize = self.tbl_files.c.size,
2817 location_id = self.tbl_files.c.location,
2818 location = relation(Location)))
2820 mapper(Fingerprint, self.tbl_fingerprint,
2821 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2822 uid_id = self.tbl_fingerprint.c.uid,
2823 uid = relation(Uid),
2824 keyring_id = self.tbl_fingerprint.c.keyring,
2825 keyring = relation(Keyring),
2826 source_acl = relation(SourceACL),
2827 binary_acl = relation(BinaryACL)))
2829 mapper(Keyring, self.tbl_keyrings,
2830 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2831 keyring_id = self.tbl_keyrings.c.id))
2833 mapper(DBChange, self.tbl_changes,
2834 properties = dict(change_id = self.tbl_changes.c.id,
2835 poolfiles = relation(PoolFile,
2836 secondary=self.tbl_changes_pool_files,
2837 backref="changeslinks"),
2838 files = relation(ChangePendingFile,
2839 secondary=self.tbl_changes_pending_files_map,
2840 backref="changesfile"),
2841 in_queue_id = self.tbl_changes.c.in_queue,
2842 in_queue = relation(PolicyQueue,
2843 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2844 approved_for_id = self.tbl_changes.c.approved_for))
2846 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2847 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2849 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2850 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id))
2852 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2853 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2854 change = relation(DBChange),
2855 maintainer = relation(Maintainer,
2856 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2857 changedby = relation(Maintainer,
2858 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2859 fingerprint = relation(Fingerprint),
2860 source_files = relation(ChangePendingFile,
2861 secondary=self.tbl_changes_pending_source_files,
2862 backref="pending_sources")))
2863 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2864 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2865 keyring = relation(Keyring, backref="keyring_acl_map"),
2866 architecture = relation(Architecture)))
2868 mapper(Location, self.tbl_location,
2869 properties = dict(location_id = self.tbl_location.c.id,
2870 component_id = self.tbl_location.c.component,
2871 component = relation(Component),
2872 archive_id = self.tbl_location.c.archive,
2873 archive = relation(Archive),
2874 archive_type = self.tbl_location.c.type))
2876 mapper(Maintainer, self.tbl_maintainer,
2877 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2879 mapper(NewComment, self.tbl_new_comments,
2880 properties = dict(comment_id = self.tbl_new_comments.c.id))
2882 mapper(Override, self.tbl_override,
2883 properties = dict(suite_id = self.tbl_override.c.suite,
2884 suite = relation(Suite),
2885 component_id = self.tbl_override.c.component,
2886 component = relation(Component),
2887 priority_id = self.tbl_override.c.priority,
2888 priority = relation(Priority),
2889 section_id = self.tbl_override.c.section,
2890 section = relation(Section),
2891 overridetype_id = self.tbl_override.c.type,
2892 overridetype = relation(OverrideType)))
2894 mapper(OverrideType, self.tbl_override_type,
2895 properties = dict(overridetype = self.tbl_override_type.c.type,
2896 overridetype_id = self.tbl_override_type.c.id))
2898 mapper(PolicyQueue, self.tbl_policy_queue,
2899 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
2901 mapper(Priority, self.tbl_priority,
2902 properties = dict(priority_id = self.tbl_priority.c.id))
2904 mapper(Section, self.tbl_section,
2905 properties = dict(section_id = self.tbl_section.c.id))
2907 mapper(DBSource, self.tbl_source,
2908 properties = dict(source_id = self.tbl_source.c.id,
2909 version = self.tbl_source.c.version,
2910 maintainer_id = self.tbl_source.c.maintainer,
2911 maintainer = relation(Maintainer,
2912 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2913 poolfile_id = self.tbl_source.c.file,
2914 poolfile = relation(PoolFile),
2915 fingerprint_id = self.tbl_source.c.sig_fpr,
2916 fingerprint = relation(Fingerprint),
2917 changedby_id = self.tbl_source.c.changedby,
2918 changedby = relation(Maintainer,
2919 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2920 srcfiles = relation(DSCFile,
2921 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2922 srcassociations = relation(SrcAssociation,
2923 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2924 srcuploaders = relation(SrcUploader)))
2926 mapper(SourceACL, self.tbl_source_acl,
2927 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2929 mapper(SrcAssociation, self.tbl_src_associations,
2930 properties = dict(sa_id = self.tbl_src_associations.c.id,
2931 suite_id = self.tbl_src_associations.c.suite,
2932 suite = relation(Suite),
2933 source_id = self.tbl_src_associations.c.source,
2934 source = relation(DBSource)))
2936 mapper(SrcFormat, self.tbl_src_format,
2937 properties = dict(src_format_id = self.tbl_src_format.c.id,
2938 format_name = self.tbl_src_format.c.format_name))
2940 mapper(SrcUploader, self.tbl_src_uploaders,
2941 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2942 source_id = self.tbl_src_uploaders.c.source,
2943 source = relation(DBSource,
2944 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2945 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2946 maintainer = relation(Maintainer,
2947 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2949 mapper(Suite, self.tbl_suite,
2950 properties = dict(suite_id = self.tbl_suite.c.id,
2951 policy_queue = relation(PolicyQueue),
2952 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
2954 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2955 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2956 suite = relation(Suite, backref='suitearchitectures'),
2957 arch_id = self.tbl_suite_architectures.c.architecture,
2958 architecture = relation(Architecture)))
2960 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2961 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2962 suite = relation(Suite, backref='suitesrcformats'),
2963 src_format_id = self.tbl_suite_src_formats.c.src_format,
2964 src_format = relation(SrcFormat)))
2966 mapper(Uid, self.tbl_uid,
2967 properties = dict(uid_id = self.tbl_uid.c.id,
2968 fingerprint = relation(Fingerprint)))
2970 mapper(UploadBlock, self.tbl_upload_blocks,
2971 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2972 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2973 uid = relation(Uid, backref="uploadblocks")))
2975 ## Connection functions
2976 def __createconn(self):
2977 from config import Config
2981 connstr = "postgres://%s" % cnf["DB::Host"]
2982 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2983 connstr += ":%s" % cnf["DB::Port"]
2984 connstr += "/%s" % cnf["DB::Name"]
2987 connstr = "postgres:///%s" % cnf["DB::Name"]
2988 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2989 connstr += "?port=%s" % cnf["DB::Port"]
2991 self.db_pg = create_engine(connstr, echo=self.debug)
2992 self.db_meta = MetaData()
2993 self.db_meta.bind = self.db_pg
2994 self.db_smaker = sessionmaker(bind=self.db_pg,
2998 self.__setuptables()
2999 self.__setupmappers()
3002 return self.db_smaker()
3004 __all__.append('DBConn')