5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009, 2010 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
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):
67 Support the debversion type
70 def get_col_spec(self):
73 sa_major_version = sqlalchemy.__version__[0:3]
74 if sa_major_version == "0.5":
75 from sqlalchemy.databases import postgres
76 postgres.ischema_names['debversion'] = DebVersion
78 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
80 ################################################################################
82 __all__ = ['IntegrityError', 'SQLAlchemyError']
84 ################################################################################
86 def session_wrapper(fn):
88 Wrapper around common ".., session=None):" handling. If the wrapped
89 function is called without passing 'session', we create a local one
90 and destroy it when the function ends.
92 Also attaches a commit_or_flush method to the session; if we created a
93 local session, this is a synonym for session.commit(), otherwise it is a
94 synonym for session.flush().
97 def wrapped(*args, **kwargs):
98 private_transaction = False
100 # Find the session object
101 session = kwargs.get('session')
104 if len(args) <= len(getargspec(fn)[0]) - 1:
105 # No session specified as last argument or in kwargs
106 private_transaction = True
107 session = kwargs['session'] = DBConn().session()
109 # Session is last argument in args
113 session = args[-1] = DBConn().session()
114 private_transaction = True
116 if private_transaction:
117 session.commit_or_flush = session.commit
119 session.commit_or_flush = session.flush
122 return fn(*args, **kwargs)
124 if private_transaction:
125 # We created a session; close it.
128 wrapped.__doc__ = fn.__doc__
129 wrapped.func_name = fn.func_name
133 __all__.append('session_wrapper')
135 ################################################################################
137 class Architecture(object):
138 def __init__(self, *args, **kwargs):
141 def __eq__(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
147 def __ne__(self, val):
148 if isinstance(val, str):
149 return (self.arch_string != val)
150 # This signals to use the normal comparison operator
151 return NotImplemented
154 return '<Architecture %s>' % self.arch_string
156 __all__.append('Architecture')
159 def get_architecture(architecture, session=None):
161 Returns database id for given C{architecture}.
163 @type architecture: string
164 @param architecture: The name of the architecture
166 @type session: Session
167 @param session: Optional SQLA session object (a temporary one will be
168 generated if not supplied)
171 @return: Architecture object for the given arch (None if not present)
174 q = session.query(Architecture).filter_by(arch_string=architecture)
178 except NoResultFound:
181 __all__.append('get_architecture')
184 def get_architecture_suites(architecture, session=None):
186 Returns list of Suite objects for given C{architecture} name
188 @type architecture: str
189 @param architecture: Architecture name to search for
191 @type session: Session
192 @param session: Optional SQL session object (a temporary one will be
193 generated if not supplied)
196 @return: list of Suite objects for the given name (may be empty)
199 q = session.query(Suite)
200 q = q.join(SuiteArchitecture)
201 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
207 __all__.append('get_architecture_suites')
209 ################################################################################
211 class Archive(object):
212 def __init__(self, *args, **kwargs):
216 return '<Archive %s>' % self.archive_name
218 __all__.append('Archive')
221 def get_archive(archive, session=None):
223 returns database id for given C{archive}.
225 @type archive: string
226 @param archive: the name of the arhive
228 @type session: Session
229 @param session: Optional SQLA session object (a temporary one will be
230 generated if not supplied)
233 @return: Archive object for the given name (None if not present)
236 archive = archive.lower()
238 q = session.query(Archive).filter_by(archive_name=archive)
242 except NoResultFound:
245 __all__.append('get_archive')
247 ################################################################################
249 class BinAssociation(object):
250 def __init__(self, *args, **kwargs):
254 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
256 __all__.append('BinAssociation')
258 ################################################################################
260 class BinContents(object):
261 def __init__(self, *args, **kwargs):
265 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
267 __all__.append('BinContents')
269 ################################################################################
271 class DBBinary(object):
272 def __init__(self, *args, **kwargs):
276 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
278 __all__.append('DBBinary')
281 def get_suites_binary_in(package, session=None):
283 Returns list of Suite objects which given C{package} name is in
286 @param package: DBBinary package name to search for
289 @return: list of Suite objects for the given package
292 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
294 __all__.append('get_suites_binary_in')
297 def get_binary_from_id(binary_id, session=None):
299 Returns DBBinary object for given C{id}
302 @param binary_id: Id of the required binary
304 @type session: Session
305 @param session: Optional SQLA session object (a temporary one will be
306 generated if not supplied)
309 @return: DBBinary object for the given binary (None if not present)
312 q = session.query(DBBinary).filter_by(binary_id=binary_id)
316 except NoResultFound:
319 __all__.append('get_binary_from_id')
322 def get_binaries_from_name(package, version=None, architecture=None, session=None):
324 Returns list of DBBinary objects for given C{package} name
327 @param package: DBBinary package name to search for
329 @type version: str or None
330 @param version: Version to search for (or None)
332 @type architecture: str, list or None
333 @param architecture: Architectures to limit to (or None if no limit)
335 @type session: Session
336 @param session: Optional SQL session object (a temporary one will be
337 generated if not supplied)
340 @return: list of DBBinary objects for the given name (may be empty)
343 q = session.query(DBBinary).filter_by(package=package)
345 if version is not None:
346 q = q.filter_by(version=version)
348 if architecture is not None:
349 if not isinstance(architecture, list):
350 architecture = [architecture]
351 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
357 __all__.append('get_binaries_from_name')
360 def get_binaries_from_source_id(source_id, session=None):
362 Returns list of DBBinary objects for given C{source_id}
365 @param source_id: source_id to search for
367 @type session: Session
368 @param session: Optional SQL session object (a temporary one will be
369 generated if not supplied)
372 @return: list of DBBinary objects for the given name (may be empty)
375 return session.query(DBBinary).filter_by(source_id=source_id).all()
377 __all__.append('get_binaries_from_source_id')
380 def get_binary_from_name_suite(package, suitename, session=None):
381 ### For dak examine-package
382 ### XXX: Doesn't use object API yet
384 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
385 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
386 WHERE b.package='%(package)s'
388 AND fi.location = l.id
389 AND l.component = c.id
392 AND su.suite_name %(suitename)s
393 ORDER BY b.version DESC"""
395 return session.execute(sql % {'package': package, 'suitename': suitename})
397 __all__.append('get_binary_from_name_suite')
400 def get_binary_components(package, suitename, arch, session=None):
401 # Check for packages that have moved from one component to another
402 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
403 WHERE b.package=:package AND s.suite_name=:suitename
404 AND (a.arch_string = :arch OR a.arch_string = 'all')
405 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
406 AND f.location = l.id
407 AND l.component = c.id
410 vals = {'package': package, 'suitename': suitename, 'arch': arch}
412 return session.execute(query, vals)
414 __all__.append('get_binary_components')
416 ################################################################################
418 class BinaryACL(object):
419 def __init__(self, *args, **kwargs):
423 return '<BinaryACL %s>' % self.binary_acl_id
425 __all__.append('BinaryACL')
427 ################################################################################
429 class BinaryACLMap(object):
430 def __init__(self, *args, **kwargs):
434 return '<BinaryACLMap %s>' % self.binary_acl_map_id
436 __all__.append('BinaryACLMap')
438 ################################################################################
443 ArchiveDir "%(archivepath)s";
444 OverrideDir "/srv/ftp.debian.org/scripts/override/";
445 CacheDir "/srv/ftp.debian.org/database/";
450 Packages::Compress ". bzip2 gzip";
451 Sources::Compress ". bzip2 gzip";
456 bindirectory "incoming"
461 BinOverride "override.sid.all3";
462 BinCacheDB "packages-accepted.db";
464 FileList "%(filelist)s";
467 Packages::Extensions ".deb .udeb";
470 bindirectory "incoming/"
473 BinOverride "override.sid.all3";
474 SrcOverride "override.sid.all3.src";
475 FileList "%(filelist)s";
479 class BuildQueue(object):
480 def __init__(self, *args, **kwargs):
484 return '<BuildQueue %s>' % self.queue_name
486 def write_metadata(self, starttime, force=False):
487 # Do we write out metafiles?
488 if not (force or self.generate_metadata):
491 session = DBConn().session().object_session(self)
493 fl_fd = fl_name = ac_fd = ac_name = None
495 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
496 startdir = os.getcwd()
499 # Grab files we want to include
500 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
501 # Write file list with newer files
502 (fl_fd, fl_name) = mkstemp()
504 os.write(fl_fd, '%s\n' % n.fullpath)
507 # Write minimal apt.conf
508 # TODO: Remove hardcoding from template
509 (ac_fd, ac_name) = mkstemp()
510 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
511 'filelist': fl_name})
514 # Run apt-ftparchive generate
515 os.chdir(os.path.dirname(ac_name))
516 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
518 # Run apt-ftparchive release
519 # TODO: Eww - fix this
520 bname = os.path.basename(self.path)
524 # We have to remove the Release file otherwise it'll be included in the
527 os.unlink(os.path.join(bname, 'Release'))
531 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))
536 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
537 if cnf.has_key("Dinstall::SigningPubKeyring"):
538 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
540 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
542 # Move the files if we got this far
543 os.rename('Release', os.path.join(bname, 'Release'))
545 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
547 # Clean up any left behind files
574 def clean_and_update(self, starttime, Logger, dryrun=False):
575 """WARNING: This routine commits for you"""
576 session = DBConn().session().object_session(self)
578 if self.generate_metadata and not dryrun:
579 self.write_metadata(starttime)
581 # Grab files older than our execution time
582 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
588 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
590 Logger.log(["I: Removing %s from the queue" % o.fullpath])
591 os.unlink(o.fullpath)
594 # If it wasn't there, don't worry
595 if e.errno == ENOENT:
598 # TODO: Replace with proper logging call
599 Logger.log(["E: Could not remove %s" % o.fullpath])
606 for f in os.listdir(self.path):
607 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
611 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
612 except NoResultFound:
613 fp = os.path.join(self.path, f)
615 Logger.log(["I: Would remove unused link %s" % fp])
617 Logger.log(["I: Removing unused link %s" % fp])
621 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
623 def add_file_from_pool(self, poolfile):
624 """Copies a file into the pool. Assumes that the PoolFile object is
625 attached to the same SQLAlchemy session as the Queue object is.
627 The caller is responsible for committing after calling this function."""
628 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
630 # Check if we have a file of this name or this ID already
631 for f in self.queuefiles:
632 if f.fileid is not None and f.fileid == poolfile.file_id or \
633 f.poolfile.filename == poolfile_basename:
634 # In this case, update the BuildQueueFile entry so we
635 # don't remove it too early
636 f.lastused = datetime.now()
637 DBConn().session().object_session(poolfile).add(f)
640 # Prepare BuildQueueFile object
641 qf = BuildQueueFile()
642 qf.build_queue_id = self.queue_id
643 qf.lastused = datetime.now()
644 qf.filename = poolfile_basename
646 targetpath = poolfile.fullpath
647 queuepath = os.path.join(self.path, poolfile_basename)
651 # We need to copy instead of symlink
653 utils.copy(targetpath, queuepath)
654 # NULL in the fileid field implies a copy
657 os.symlink(targetpath, queuepath)
658 qf.fileid = poolfile.file_id
662 # Get the same session as the PoolFile is using and add the qf to it
663 DBConn().session().object_session(poolfile).add(qf)
668 __all__.append('BuildQueue')
671 def get_build_queue(queuename, session=None):
673 Returns BuildQueue object for given C{queue name}, creating it if it does not
676 @type queuename: string
677 @param queuename: The name of the queue
679 @type session: Session
680 @param session: Optional SQLA session object (a temporary one will be
681 generated if not supplied)
684 @return: BuildQueue object for the given queue
687 q = session.query(BuildQueue).filter_by(queue_name=queuename)
691 except NoResultFound:
694 __all__.append('get_build_queue')
696 ################################################################################
698 class BuildQueueFile(object):
699 def __init__(self, *args, **kwargs):
703 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
707 return os.path.join(self.buildqueue.path, self.filename)
710 __all__.append('BuildQueueFile')
712 ################################################################################
714 class ChangePendingBinary(object):
715 def __init__(self, *args, **kwargs):
719 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
721 __all__.append('ChangePendingBinary')
723 ################################################################################
725 class ChangePendingFile(object):
726 def __init__(self, *args, **kwargs):
730 return '<ChangePendingFile %s>' % self.change_pending_file_id
732 __all__.append('ChangePendingFile')
734 ################################################################################
736 class ChangePendingSource(object):
737 def __init__(self, *args, **kwargs):
741 return '<ChangePendingSource %s>' % self.change_pending_source_id
743 __all__.append('ChangePendingSource')
745 ################################################################################
747 class Component(object):
748 def __init__(self, *args, **kwargs):
751 def __eq__(self, val):
752 if isinstance(val, str):
753 return (self.component_name == val)
754 # This signals to use the normal comparison operator
755 return NotImplemented
757 def __ne__(self, val):
758 if isinstance(val, str):
759 return (self.component_name != val)
760 # This signals to use the normal comparison operator
761 return NotImplemented
764 return '<Component %s>' % self.component_name
767 __all__.append('Component')
770 def get_component(component, session=None):
772 Returns database id for given C{component}.
774 @type component: string
775 @param component: The name of the override type
778 @return: the database id for the given component
781 component = component.lower()
783 q = session.query(Component).filter_by(component_name=component)
787 except NoResultFound:
790 __all__.append('get_component')
792 ################################################################################
794 class DBConfig(object):
795 def __init__(self, *args, **kwargs):
799 return '<DBConfig %s>' % self.name
801 __all__.append('DBConfig')
803 ################################################################################
806 def get_or_set_contents_file_id(filename, session=None):
808 Returns database id for given filename.
810 If no matching file is found, a row is inserted.
812 @type filename: string
813 @param filename: The filename
814 @type session: SQLAlchemy
815 @param session: Optional SQL session object (a temporary one will be
816 generated if not supplied). If not passed, a commit will be performed at
817 the end of the function, otherwise the caller is responsible for commiting.
820 @return: the database id for the given component
823 q = session.query(ContentFilename).filter_by(filename=filename)
826 ret = q.one().cafilename_id
827 except NoResultFound:
828 cf = ContentFilename()
829 cf.filename = filename
831 session.commit_or_flush()
832 ret = cf.cafilename_id
836 __all__.append('get_or_set_contents_file_id')
839 def get_contents(suite, overridetype, section=None, session=None):
841 Returns contents for a suite / overridetype combination, limiting
842 to a section if not None.
845 @param suite: Suite object
847 @type overridetype: OverrideType
848 @param overridetype: OverrideType object
850 @type section: Section
851 @param section: Optional section object to limit results to
853 @type session: SQLAlchemy
854 @param session: Optional SQL session object (a temporary one will be
855 generated if not supplied)
858 @return: ResultsProxy object set up to return tuples of (filename, section,
862 # find me all of the contents for a given suite
863 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
867 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
868 JOIN content_file_names n ON (c.filename=n.id)
869 JOIN binaries b ON (b.id=c.binary_pkg)
870 JOIN override o ON (o.package=b.package)
871 JOIN section s ON (s.id=o.section)
872 WHERE o.suite = :suiteid AND o.type = :overridetypeid
873 AND b.type=:overridetypename"""
875 vals = {'suiteid': suite.suite_id,
876 'overridetypeid': overridetype.overridetype_id,
877 'overridetypename': overridetype.overridetype}
879 if section is not None:
880 contents_q += " AND s.id = :sectionid"
881 vals['sectionid'] = section.section_id
883 contents_q += " ORDER BY fn"
885 return session.execute(contents_q, vals)
887 __all__.append('get_contents')
889 ################################################################################
891 class ContentFilepath(object):
892 def __init__(self, *args, **kwargs):
896 return '<ContentFilepath %s>' % self.filepath
898 __all__.append('ContentFilepath')
901 def get_or_set_contents_path_id(filepath, session=None):
903 Returns database id for given path.
905 If no matching file is found, a row is inserted.
907 @type filepath: string
908 @param filepath: The filepath
910 @type session: SQLAlchemy
911 @param session: Optional SQL session object (a temporary one will be
912 generated if not supplied). If not passed, a commit will be performed at
913 the end of the function, otherwise the caller is responsible for commiting.
916 @return: the database id for the given path
919 q = session.query(ContentFilepath).filter_by(filepath=filepath)
922 ret = q.one().cafilepath_id
923 except NoResultFound:
924 cf = ContentFilepath()
925 cf.filepath = filepath
927 session.commit_or_flush()
928 ret = cf.cafilepath_id
932 __all__.append('get_or_set_contents_path_id')
934 ################################################################################
936 class ContentAssociation(object):
937 def __init__(self, *args, **kwargs):
941 return '<ContentAssociation %s>' % self.ca_id
943 __all__.append('ContentAssociation')
945 def insert_content_paths(binary_id, fullpaths, session=None):
947 Make sure given path is associated with given binary id
950 @param binary_id: the id of the binary
951 @type fullpaths: list
952 @param fullpaths: the list of paths of the file being associated with the binary
953 @type session: SQLAlchemy session
954 @param session: Optional SQLAlchemy session. If this is passed, the caller
955 is responsible for ensuring a transaction has begun and committing the
956 results or rolling back based on the result code. If not passed, a commit
957 will be performed at the end of the function, otherwise the caller is
958 responsible for commiting.
960 @return: True upon success
965 session = DBConn().session()
970 def generate_path_dicts():
971 for fullpath in fullpaths:
972 if fullpath.startswith( './' ):
973 fullpath = fullpath[2:]
975 yield {'filename':fullpath, 'id': binary_id }
977 for d in generate_path_dicts():
978 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
987 traceback.print_exc()
989 # Only rollback if we set up the session ourself
996 __all__.append('insert_content_paths')
998 ################################################################################
1000 class DSCFile(object):
1001 def __init__(self, *args, **kwargs):
1005 return '<DSCFile %s>' % self.dscfile_id
1007 __all__.append('DSCFile')
1010 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1012 Returns a list of DSCFiles which may be empty
1014 @type dscfile_id: int (optional)
1015 @param dscfile_id: the dscfile_id of the DSCFiles to find
1017 @type source_id: int (optional)
1018 @param source_id: the source id related to the DSCFiles to find
1020 @type poolfile_id: int (optional)
1021 @param poolfile_id: the poolfile id related to the DSCFiles to find
1024 @return: Possibly empty list of DSCFiles
1027 q = session.query(DSCFile)
1029 if dscfile_id is not None:
1030 q = q.filter_by(dscfile_id=dscfile_id)
1032 if source_id is not None:
1033 q = q.filter_by(source_id=source_id)
1035 if poolfile_id is not None:
1036 q = q.filter_by(poolfile_id=poolfile_id)
1040 __all__.append('get_dscfiles')
1042 ################################################################################
1044 class PoolFile(object):
1045 def __init__(self, *args, **kwargs):
1049 return '<PoolFile %s>' % self.filename
1053 return os.path.join(self.location.path, self.filename)
1055 __all__.append('PoolFile')
1058 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1061 (ValidFileFound [boolean or None], PoolFile object or None)
1063 @type filename: string
1064 @param filename: the filename of the file to check against the DB
1067 @param filesize: the size of the file to check against the DB
1069 @type md5sum: string
1070 @param md5sum: the md5sum of the file to check against the DB
1072 @type location_id: int
1073 @param location_id: the id of the location to look in
1076 @return: Tuple of length 2.
1077 - If more than one file found with that name: (C{None}, C{None})
1078 - If valid pool file found: (C{True}, C{PoolFile object})
1079 - If valid pool file not found:
1080 - (C{False}, C{None}) if no file found
1081 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1084 q = session.query(PoolFile).filter_by(filename=filename)
1085 q = q.join(Location).filter_by(location_id=location_id)
1095 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1103 __all__.append('check_poolfile')
1106 def get_poolfile_by_id(file_id, session=None):
1108 Returns a PoolFile objects or None for the given id
1111 @param file_id: the id of the file to look for
1113 @rtype: PoolFile or None
1114 @return: either the PoolFile object or None
1117 q = session.query(PoolFile).filter_by(file_id=file_id)
1121 except NoResultFound:
1124 __all__.append('get_poolfile_by_id')
1128 def get_poolfile_by_name(filename, location_id=None, session=None):
1130 Returns an array of PoolFile objects for the given filename and
1131 (optionally) location_id
1133 @type filename: string
1134 @param filename: the filename of the file to check against the DB
1136 @type location_id: int
1137 @param location_id: the id of the location to look in (optional)
1140 @return: array of PoolFile objects
1143 q = session.query(PoolFile).filter_by(filename=filename)
1145 if location_id is not None:
1146 q = q.join(Location).filter_by(location_id=location_id)
1150 __all__.append('get_poolfile_by_name')
1153 def get_poolfile_like_name(filename, session=None):
1155 Returns an array of PoolFile objects which are like the given name
1157 @type filename: string
1158 @param filename: the filename of the file to check against the DB
1161 @return: array of PoolFile objects
1164 # TODO: There must be a way of properly using bind parameters with %FOO%
1165 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1169 __all__.append('get_poolfile_like_name')
1172 def add_poolfile(filename, datadict, location_id, session=None):
1174 Add a new file to the pool
1176 @type filename: string
1177 @param filename: filename
1179 @type datadict: dict
1180 @param datadict: dict with needed data
1182 @type location_id: int
1183 @param location_id: database id of the location
1186 @return: the PoolFile object created
1188 poolfile = PoolFile()
1189 poolfile.filename = filename
1190 poolfile.filesize = datadict["size"]
1191 poolfile.md5sum = datadict["md5sum"]
1192 poolfile.sha1sum = datadict["sha1sum"]
1193 poolfile.sha256sum = datadict["sha256sum"]
1194 poolfile.location_id = location_id
1196 session.add(poolfile)
1197 # Flush to get a file id (NB: This is not a commit)
1202 __all__.append('add_poolfile')
1204 ################################################################################
1206 class Fingerprint(object):
1207 def __init__(self, *args, **kwargs):
1211 return '<Fingerprint %s>' % self.fingerprint
1213 __all__.append('Fingerprint')
1216 def get_fingerprint(fpr, session=None):
1218 Returns Fingerprint object for given fpr.
1221 @param fpr: The fpr to find / add
1223 @type session: SQLAlchemy
1224 @param session: Optional SQL session object (a temporary one will be
1225 generated if not supplied).
1228 @return: the Fingerprint object for the given fpr or None
1231 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1235 except NoResultFound:
1240 __all__.append('get_fingerprint')
1243 def get_or_set_fingerprint(fpr, session=None):
1245 Returns Fingerprint object for given fpr.
1247 If no matching fpr is found, a row is inserted.
1250 @param fpr: The fpr to find / add
1252 @type session: SQLAlchemy
1253 @param session: Optional SQL session object (a temporary one will be
1254 generated if not supplied). If not passed, a commit will be performed at
1255 the end of the function, otherwise the caller is responsible for commiting.
1256 A flush will be performed either way.
1259 @return: the Fingerprint object for the given fpr
1262 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1266 except NoResultFound:
1267 fingerprint = Fingerprint()
1268 fingerprint.fingerprint = fpr
1269 session.add(fingerprint)
1270 session.commit_or_flush()
1275 __all__.append('get_or_set_fingerprint')
1277 ################################################################################
1279 # Helper routine for Keyring class
1280 def get_ldap_name(entry):
1282 for k in ["cn", "mn", "sn"]:
1284 if ret and ret[0] != "" and ret[0] != "-":
1286 return " ".join(name)
1288 ################################################################################
1290 class Keyring(object):
1291 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1292 " --with-colons --fingerprint --fingerprint"
1297 def __init__(self, *args, **kwargs):
1301 return '<Keyring %s>' % self.keyring_name
1303 def de_escape_gpg_str(self, txt):
1304 esclist = re.split(r'(\\x..)', txt)
1305 for x in range(1,len(esclist),2):
1306 esclist[x] = "%c" % (int(esclist[x][2:],16))
1307 return "".join(esclist)
1309 def load_keys(self, keyring):
1312 if not self.keyring_id:
1313 raise Exception('Must be initialized with database information')
1315 k = os.popen(self.gpg_invocation % keyring, "r")
1319 for line in k.xreadlines():
1320 field = line.split(":")
1321 if field[0] == "pub":
1323 (name, addr) = email.Utils.parseaddr(field[9])
1324 name = re.sub(r"\s*[(].*[)]", "", name)
1325 if name == "" or addr == "" or "@" not in addr:
1327 addr = "invalid-uid"
1328 name = self.de_escape_gpg_str(name)
1329 self.keys[key] = {"email": addr}
1331 self.keys[key]["name"] = name
1332 self.keys[key]["aliases"] = [name]
1333 self.keys[key]["fingerprints"] = []
1335 elif key and field[0] == "sub" and len(field) >= 12:
1336 signingkey = ("s" in field[11])
1337 elif key and field[0] == "uid":
1338 (name, addr) = email.Utils.parseaddr(field[9])
1339 if name and name not in self.keys[key]["aliases"]:
1340 self.keys[key]["aliases"].append(name)
1341 elif signingkey and field[0] == "fpr":
1342 self.keys[key]["fingerprints"].append(field[9])
1343 self.fpr_lookup[field[9]] = key
1345 def import_users_from_ldap(self, session):
1349 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1350 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1352 l = ldap.open(LDAPServer)
1353 l.simple_bind_s("","")
1354 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1355 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1356 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1358 ldap_fin_uid_id = {}
1365 uid = entry["uid"][0]
1366 name = get_ldap_name(entry)
1367 fingerprints = entry["keyFingerPrint"]
1369 for f in fingerprints:
1370 key = self.fpr_lookup.get(f, None)
1371 if key not in self.keys:
1373 self.keys[key]["uid"] = uid
1377 keyid = get_or_set_uid(uid, session).uid_id
1378 byuid[keyid] = (uid, name)
1379 byname[uid] = (keyid, name)
1381 return (byname, byuid)
1383 def generate_users_from_keyring(self, format, session):
1387 for x in self.keys.keys():
1388 if self.keys[x]["email"] == "invalid-uid":
1390 self.keys[x]["uid"] = format % "invalid-uid"
1392 uid = format % self.keys[x]["email"]
1393 keyid = get_or_set_uid(uid, session).uid_id
1394 byuid[keyid] = (uid, self.keys[x]["name"])
1395 byname[uid] = (keyid, self.keys[x]["name"])
1396 self.keys[x]["uid"] = uid
1399 uid = format % "invalid-uid"
1400 keyid = get_or_set_uid(uid, session).uid_id
1401 byuid[keyid] = (uid, "ungeneratable user id")
1402 byname[uid] = (keyid, "ungeneratable user id")
1404 return (byname, byuid)
1406 __all__.append('Keyring')
1409 def get_keyring(keyring, session=None):
1411 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1412 If C{keyring} already has an entry, simply return the existing Keyring
1414 @type keyring: string
1415 @param keyring: the keyring name
1418 @return: the Keyring object for this keyring
1421 q = session.query(Keyring).filter_by(keyring_name=keyring)
1425 except NoResultFound:
1428 __all__.append('get_keyring')
1430 ################################################################################
1432 class KeyringACLMap(object):
1433 def __init__(self, *args, **kwargs):
1437 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1439 __all__.append('KeyringACLMap')
1441 ################################################################################
1443 class DBChange(object):
1444 def __init__(self, *args, **kwargs):
1448 return '<DBChange %s>' % self.changesname
1450 def clean_from_queue(self):
1451 session = DBConn().session().object_session(self)
1453 # Remove changes_pool_files entries
1456 # Remove changes_pending_files references
1459 # Clear out of queue
1460 self.in_queue = None
1461 self.approved_for_id = None
1463 __all__.append('DBChange')
1466 def get_dbchange(filename, session=None):
1468 returns DBChange object for given C{filename}.
1470 @type filename: string
1471 @param filename: the name of the file
1473 @type session: Session
1474 @param session: Optional SQLA session object (a temporary one will be
1475 generated if not supplied)
1478 @return: DBChange object for the given filename (C{None} if not present)
1481 q = session.query(DBChange).filter_by(changesname=filename)
1485 except NoResultFound:
1488 __all__.append('get_dbchange')
1490 ################################################################################
1492 class Location(object):
1493 def __init__(self, *args, **kwargs):
1497 return '<Location %s (%s)>' % (self.path, self.location_id)
1499 __all__.append('Location')
1502 def get_location(location, component=None, archive=None, session=None):
1504 Returns Location object for the given combination of location, component
1507 @type location: string
1508 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1510 @type component: string
1511 @param component: the component name (if None, no restriction applied)
1513 @type archive: string
1514 @param archive: the archive name (if None, no restriction applied)
1516 @rtype: Location / None
1517 @return: Either a Location object or None if one can't be found
1520 q = session.query(Location).filter_by(path=location)
1522 if archive is not None:
1523 q = q.join(Archive).filter_by(archive_name=archive)
1525 if component is not None:
1526 q = q.join(Component).filter_by(component_name=component)
1530 except NoResultFound:
1533 __all__.append('get_location')
1535 ################################################################################
1537 class Maintainer(object):
1538 def __init__(self, *args, **kwargs):
1542 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1544 def get_split_maintainer(self):
1545 if not hasattr(self, 'name') or self.name is None:
1546 return ('', '', '', '')
1548 return fix_maintainer(self.name.strip())
1550 __all__.append('Maintainer')
1553 def get_or_set_maintainer(name, session=None):
1555 Returns Maintainer object for given maintainer name.
1557 If no matching maintainer name is found, a row is inserted.
1560 @param name: The maintainer name to add
1562 @type session: SQLAlchemy
1563 @param session: Optional SQL session object (a temporary one will be
1564 generated if not supplied). If not passed, a commit will be performed at
1565 the end of the function, otherwise the caller is responsible for commiting.
1566 A flush will be performed either way.
1569 @return: the Maintainer object for the given maintainer
1572 q = session.query(Maintainer).filter_by(name=name)
1575 except NoResultFound:
1576 maintainer = Maintainer()
1577 maintainer.name = name
1578 session.add(maintainer)
1579 session.commit_or_flush()
1584 __all__.append('get_or_set_maintainer')
1587 def get_maintainer(maintainer_id, session=None):
1589 Return the name of the maintainer behind C{maintainer_id} or None if that
1590 maintainer_id is invalid.
1592 @type maintainer_id: int
1593 @param maintainer_id: the id of the maintainer
1596 @return: the Maintainer with this C{maintainer_id}
1599 return session.query(Maintainer).get(maintainer_id)
1601 __all__.append('get_maintainer')
1603 ################################################################################
1605 class NewComment(object):
1606 def __init__(self, *args, **kwargs):
1610 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1612 __all__.append('NewComment')
1615 def has_new_comment(package, version, session=None):
1617 Returns true if the given combination of C{package}, C{version} has a comment.
1619 @type package: string
1620 @param package: name of the package
1622 @type version: string
1623 @param version: package version
1625 @type session: Session
1626 @param session: Optional SQLA session object (a temporary one will be
1627 generated if not supplied)
1633 q = session.query(NewComment)
1634 q = q.filter_by(package=package)
1635 q = q.filter_by(version=version)
1637 return bool(q.count() > 0)
1639 __all__.append('has_new_comment')
1642 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1644 Returns (possibly empty) list of NewComment objects for the given
1647 @type package: string (optional)
1648 @param package: name of the package
1650 @type version: string (optional)
1651 @param version: package version
1653 @type comment_id: int (optional)
1654 @param comment_id: An id of a comment
1656 @type session: Session
1657 @param session: Optional SQLA session object (a temporary one will be
1658 generated if not supplied)
1661 @return: A (possibly empty) list of NewComment objects will be returned
1664 q = session.query(NewComment)
1665 if package is not None: q = q.filter_by(package=package)
1666 if version is not None: q = q.filter_by(version=version)
1667 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1671 __all__.append('get_new_comments')
1673 ################################################################################
1675 class Override(object):
1676 def __init__(self, *args, **kwargs):
1680 return '<Override %s (%s)>' % (self.package, self.suite_id)
1682 __all__.append('Override')
1685 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1687 Returns Override object for the given parameters
1689 @type package: string
1690 @param package: The name of the package
1692 @type suite: string, list or None
1693 @param suite: The name of the suite (or suites if a list) to limit to. If
1694 None, don't limit. Defaults to None.
1696 @type component: string, list or None
1697 @param component: The name of the component (or components if a list) to
1698 limit to. If None, don't limit. Defaults to None.
1700 @type overridetype: string, list or None
1701 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1702 limit to. If None, don't limit. Defaults to None.
1704 @type session: Session
1705 @param session: Optional SQLA session object (a temporary one will be
1706 generated if not supplied)
1709 @return: A (possibly empty) list of Override objects will be returned
1712 q = session.query(Override)
1713 q = q.filter_by(package=package)
1715 if suite is not None:
1716 if not isinstance(suite, list): suite = [suite]
1717 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1719 if component is not None:
1720 if not isinstance(component, list): component = [component]
1721 q = q.join(Component).filter(Component.component_name.in_(component))
1723 if overridetype is not None:
1724 if not isinstance(overridetype, list): overridetype = [overridetype]
1725 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1729 __all__.append('get_override')
1732 ################################################################################
1734 class OverrideType(object):
1735 def __init__(self, *args, **kwargs):
1739 return '<OverrideType %s>' % self.overridetype
1741 __all__.append('OverrideType')
1744 def get_override_type(override_type, session=None):
1746 Returns OverrideType object for given C{override type}.
1748 @type override_type: string
1749 @param override_type: The name of the override type
1751 @type session: Session
1752 @param session: Optional SQLA session object (a temporary one will be
1753 generated if not supplied)
1756 @return: the database id for the given override type
1759 q = session.query(OverrideType).filter_by(overridetype=override_type)
1763 except NoResultFound:
1766 __all__.append('get_override_type')
1768 ################################################################################
1770 class DebContents(object):
1771 def __init__(self, *args, **kwargs):
1775 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1777 __all__.append('DebContents')
1780 class UdebContents(object):
1781 def __init__(self, *args, **kwargs):
1785 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1787 __all__.append('UdebContents')
1789 class PendingBinContents(object):
1790 def __init__(self, *args, **kwargs):
1794 return '<PendingBinContents %s>' % self.contents_id
1796 __all__.append('PendingBinContents')
1798 def insert_pending_content_paths(package,
1803 Make sure given paths are temporarily associated with given
1807 @param package: the package to associate with should have been read in from the binary control file
1808 @type fullpaths: list
1809 @param fullpaths: the list of paths of the file being associated with the binary
1810 @type session: SQLAlchemy session
1811 @param session: Optional SQLAlchemy session. If this is passed, the caller
1812 is responsible for ensuring a transaction has begun and committing the
1813 results or rolling back based on the result code. If not passed, a commit
1814 will be performed at the end of the function
1816 @return: True upon success, False if there is a problem
1819 privatetrans = False
1822 session = DBConn().session()
1826 arch = get_architecture(package['Architecture'], session)
1827 arch_id = arch.arch_id
1829 # Remove any already existing recorded files for this package
1830 q = session.query(PendingBinContents)
1831 q = q.filter_by(package=package['Package'])
1832 q = q.filter_by(version=package['Version'])
1833 q = q.filter_by(architecture=arch_id)
1836 for fullpath in fullpaths:
1838 if fullpath.startswith( "./" ):
1839 fullpath = fullpath[2:]
1841 pca = PendingBinContents()
1842 pca.package = package['Package']
1843 pca.version = package['Version']
1845 pca.architecture = arch_id
1848 pca.type = 8 # gross
1850 pca.type = 7 # also gross
1853 # Only commit if we set up the session ourself
1861 except Exception, e:
1862 traceback.print_exc()
1864 # Only rollback if we set up the session ourself
1871 __all__.append('insert_pending_content_paths')
1873 ################################################################################
1875 class PolicyQueue(object):
1876 def __init__(self, *args, **kwargs):
1880 return '<PolicyQueue %s>' % self.queue_name
1882 __all__.append('PolicyQueue')
1885 def get_policy_queue(queuename, session=None):
1887 Returns PolicyQueue object for given C{queue name}
1889 @type queuename: string
1890 @param queuename: The name of the queue
1892 @type session: Session
1893 @param session: Optional SQLA session object (a temporary one will be
1894 generated if not supplied)
1897 @return: PolicyQueue object for the given queue
1900 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1904 except NoResultFound:
1907 __all__.append('get_policy_queue')
1909 ################################################################################
1911 class Priority(object):
1912 def __init__(self, *args, **kwargs):
1915 def __eq__(self, val):
1916 if isinstance(val, str):
1917 return (self.priority == val)
1918 # This signals to use the normal comparison operator
1919 return NotImplemented
1921 def __ne__(self, val):
1922 if isinstance(val, str):
1923 return (self.priority != val)
1924 # This signals to use the normal comparison operator
1925 return NotImplemented
1928 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1930 __all__.append('Priority')
1933 def get_priority(priority, session=None):
1935 Returns Priority object for given C{priority name}.
1937 @type priority: string
1938 @param priority: The name of the priority
1940 @type session: Session
1941 @param session: Optional SQLA session object (a temporary one will be
1942 generated if not supplied)
1945 @return: Priority object for the given priority
1948 q = session.query(Priority).filter_by(priority=priority)
1952 except NoResultFound:
1955 __all__.append('get_priority')
1958 def get_priorities(session=None):
1960 Returns dictionary of priority names -> id mappings
1962 @type session: Session
1963 @param session: Optional SQL session object (a temporary one will be
1964 generated if not supplied)
1967 @return: dictionary of priority names -> id mappings
1971 q = session.query(Priority)
1973 ret[x.priority] = x.priority_id
1977 __all__.append('get_priorities')
1979 ################################################################################
1981 class Section(object):
1982 def __init__(self, *args, **kwargs):
1985 def __eq__(self, val):
1986 if isinstance(val, str):
1987 return (self.section == val)
1988 # This signals to use the normal comparison operator
1989 return NotImplemented
1991 def __ne__(self, val):
1992 if isinstance(val, str):
1993 return (self.section != val)
1994 # This signals to use the normal comparison operator
1995 return NotImplemented
1998 return '<Section %s>' % self.section
2000 __all__.append('Section')
2003 def get_section(section, session=None):
2005 Returns Section object for given C{section name}.
2007 @type section: string
2008 @param section: The name of the section
2010 @type session: Session
2011 @param session: Optional SQLA session object (a temporary one will be
2012 generated if not supplied)
2015 @return: Section object for the given section name
2018 q = session.query(Section).filter_by(section=section)
2022 except NoResultFound:
2025 __all__.append('get_section')
2028 def get_sections(session=None):
2030 Returns dictionary of section names -> id mappings
2032 @type session: Session
2033 @param session: Optional SQL session object (a temporary one will be
2034 generated if not supplied)
2037 @return: dictionary of section names -> id mappings
2041 q = session.query(Section)
2043 ret[x.section] = x.section_id
2047 __all__.append('get_sections')
2049 ################################################################################
2051 class DBSource(object):
2052 def __init__(self, *args, **kwargs):
2056 return '<DBSource %s (%s)>' % (self.source, self.version)
2058 __all__.append('DBSource')
2061 def source_exists(source, source_version, suites = ["any"], session=None):
2063 Ensure that source exists somewhere in the archive for the binary
2064 upload being processed.
2065 1. exact match => 1.0-3
2066 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2068 @type source: string
2069 @param source: source name
2071 @type source_version: string
2072 @param source_version: expected source version
2075 @param suites: list of suites to check in, default I{any}
2077 @type session: Session
2078 @param session: Optional SQLA session object (a temporary one will be
2079 generated if not supplied)
2082 @return: returns 1 if a source with expected version is found, otherwise 0
2089 for suite in suites:
2090 q = session.query(DBSource).filter_by(source=source)
2092 # source must exist in suite X, or in some other suite that's
2093 # mapped to X, recursively... silent-maps are counted too,
2094 # unreleased-maps aren't.
2095 maps = cnf.ValueList("SuiteMappings")[:]
2097 maps = [ m.split() for m in maps ]
2098 maps = [ (x[1], x[2]) for x in maps
2099 if x[0] == "map" or x[0] == "silent-map" ]
2102 if x[1] in s and x[0] not in s:
2105 q = q.join(SrcAssociation).join(Suite)
2106 q = q.filter(Suite.suite_name.in_(s))
2108 # Reduce the query results to a list of version numbers
2109 ql = [ j.version for j in q.all() ]
2112 if source_version in ql:
2116 from daklib.regexes import re_bin_only_nmu
2117 orig_source_version = re_bin_only_nmu.sub('', source_version)
2118 if orig_source_version in ql:
2121 # No source found so return not ok
2126 __all__.append('source_exists')
2129 def get_suites_source_in(source, session=None):
2131 Returns list of Suite objects which given C{source} name is in
2134 @param source: DBSource package name to search for
2137 @return: list of Suite objects for the given source
2140 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2142 __all__.append('get_suites_source_in')
2145 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2147 Returns list of DBSource objects for given C{source} name and other parameters
2150 @param source: DBSource package name to search for
2152 @type version: str or None
2153 @param version: DBSource version name to search for or None if not applicable
2155 @type dm_upload_allowed: bool
2156 @param dm_upload_allowed: If None, no effect. If True or False, only
2157 return packages with that dm_upload_allowed setting
2159 @type session: Session
2160 @param session: Optional SQL session object (a temporary one will be
2161 generated if not supplied)
2164 @return: list of DBSource objects for the given name (may be empty)
2167 q = session.query(DBSource).filter_by(source=source)
2169 if version is not None:
2170 q = q.filter_by(version=version)
2172 if dm_upload_allowed is not None:
2173 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2177 __all__.append('get_sources_from_name')
2180 def get_source_in_suite(source, suite, session=None):
2182 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2184 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2185 - B{suite} - a suite name, eg. I{unstable}
2187 @type source: string
2188 @param source: source package name
2191 @param suite: the suite name
2194 @return: the version for I{source} in I{suite}
2198 q = session.query(SrcAssociation)
2199 q = q.join('source').filter_by(source=source)
2200 q = q.join('suite').filter_by(suite_name=suite)
2203 return q.one().source
2204 except NoResultFound:
2207 __all__.append('get_source_in_suite')
2209 ################################################################################
2212 def add_dsc_to_db(u, filename, session=None):
2213 entry = u.pkg.files[filename]
2217 source.source = u.pkg.dsc["source"]
2218 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2219 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2220 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2221 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2222 source.install_date = datetime.now().date()
2224 dsc_component = entry["component"]
2225 dsc_location_id = entry["location id"]
2227 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2229 # Set up a new poolfile if necessary
2230 if not entry.has_key("files id") or not entry["files id"]:
2231 filename = entry["pool name"] + filename
2232 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2234 pfs.append(poolfile)
2235 entry["files id"] = poolfile.file_id
2237 source.poolfile_id = entry["files id"]
2241 for suite_name in u.pkg.changes["distribution"].keys():
2242 sa = SrcAssociation()
2243 sa.source_id = source.source_id
2244 sa.suite_id = get_suite(suite_name).suite_id
2249 # Add the source files to the DB (files and dsc_files)
2251 dscfile.source_id = source.source_id
2252 dscfile.poolfile_id = entry["files id"]
2253 session.add(dscfile)
2255 for dsc_file, dentry in u.pkg.dsc_files.items():
2257 df.source_id = source.source_id
2259 # If the .orig tarball is already in the pool, it's
2260 # files id is stored in dsc_files by check_dsc().
2261 files_id = dentry.get("files id", None)
2263 # Find the entry in the files hash
2264 # TODO: Bail out here properly
2266 for f, e in u.pkg.files.items():
2271 if files_id is None:
2272 filename = dfentry["pool name"] + dsc_file
2274 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2275 # FIXME: needs to check for -1/-2 and or handle exception
2276 if found and obj is not None:
2277 files_id = obj.file_id
2280 # If still not found, add it
2281 if files_id is None:
2282 # HACK: Force sha1sum etc into dentry
2283 dentry["sha1sum"] = dfentry["sha1sum"]
2284 dentry["sha256sum"] = dfentry["sha256sum"]
2285 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2286 pfs.append(poolfile)
2287 files_id = poolfile.file_id
2289 poolfile = get_poolfile_by_id(files_id, session)
2290 if poolfile is None:
2291 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2292 pfs.append(poolfile)
2294 df.poolfile_id = files_id
2299 # Add the src_uploaders to the DB
2300 uploader_ids = [source.maintainer_id]
2301 if u.pkg.dsc.has_key("uploaders"):
2302 for up in u.pkg.dsc["uploaders"].split(","):
2304 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2307 for up_id in uploader_ids:
2308 if added_ids.has_key(up_id):
2310 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2316 su.maintainer_id = up_id
2317 su.source_id = source.source_id
2322 return source, dsc_component, dsc_location_id, pfs
2324 __all__.append('add_dsc_to_db')
2327 def add_deb_to_db(u, filename, session=None):
2329 Contrary to what you might expect, this routine deals with both
2330 debs and udebs. That info is in 'dbtype', whilst 'type' is
2331 'deb' for both of them
2334 entry = u.pkg.files[filename]
2337 bin.package = entry["package"]
2338 bin.version = entry["version"]
2339 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2340 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2341 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2342 bin.binarytype = entry["dbtype"]
2345 filename = entry["pool name"] + filename
2346 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2347 if not entry.get("location id", None):
2348 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2350 if entry.get("files id", None):
2351 poolfile = get_poolfile_by_id(bin.poolfile_id)
2352 bin.poolfile_id = entry["files id"]
2354 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2355 bin.poolfile_id = entry["files id"] = poolfile.file_id
2358 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2359 if len(bin_sources) != 1:
2360 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2361 (bin.package, bin.version, bin.architecture.arch_string,
2362 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2364 bin.source_id = bin_sources[0].source_id
2366 # Add and flush object so it has an ID
2370 # Add BinAssociations
2371 for suite_name in u.pkg.changes["distribution"].keys():
2372 ba = BinAssociation()
2373 ba.binary_id = bin.binary_id
2374 ba.suite_id = get_suite(suite_name).suite_id
2379 # Deal with contents - disabled for now
2380 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2382 # print "REJECT\nCould not determine contents of package %s" % bin.package
2383 # session.rollback()
2384 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2388 __all__.append('add_deb_to_db')
2390 ################################################################################
2392 class SourceACL(object):
2393 def __init__(self, *args, **kwargs):
2397 return '<SourceACL %s>' % self.source_acl_id
2399 __all__.append('SourceACL')
2401 ################################################################################
2403 class SrcAssociation(object):
2404 def __init__(self, *args, **kwargs):
2408 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2410 __all__.append('SrcAssociation')
2412 ################################################################################
2414 class SrcFormat(object):
2415 def __init__(self, *args, **kwargs):
2419 return '<SrcFormat %s>' % (self.format_name)
2421 __all__.append('SrcFormat')
2423 ################################################################################
2425 class SrcUploader(object):
2426 def __init__(self, *args, **kwargs):
2430 return '<SrcUploader %s>' % self.uploader_id
2432 __all__.append('SrcUploader')
2434 ################################################################################
2436 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2437 ('SuiteID', 'suite_id'),
2438 ('Version', 'version'),
2439 ('Origin', 'origin'),
2441 ('Description', 'description'),
2442 ('Untouchable', 'untouchable'),
2443 ('Announce', 'announce'),
2444 ('Codename', 'codename'),
2445 ('OverrideCodename', 'overridecodename'),
2446 ('ValidTime', 'validtime'),
2447 ('Priority', 'priority'),
2448 ('NotAutomatic', 'notautomatic'),
2449 ('CopyChanges', 'copychanges'),
2450 ('CopyDotDak', 'copydotdak'),
2451 ('CommentsDir', 'commentsdir'),
2452 ('OverrideSuite', 'overridesuite'),
2453 ('ChangelogBase', 'changelogbase')]
2456 class Suite(object):
2457 def __init__(self, *args, **kwargs):
2461 return '<Suite %s>' % self.suite_name
2463 def __eq__(self, val):
2464 if isinstance(val, str):
2465 return (self.suite_name == val)
2466 # This signals to use the normal comparison operator
2467 return NotImplemented
2469 def __ne__(self, val):
2470 if isinstance(val, str):
2471 return (self.suite_name != val)
2472 # This signals to use the normal comparison operator
2473 return NotImplemented
2477 for disp, field in SUITE_FIELDS:
2478 val = getattr(self, field, None)
2480 ret.append("%s: %s" % (disp, val))
2482 return "\n".join(ret)
2484 __all__.append('Suite')
2487 def get_suite_architecture(suite, architecture, session=None):
2489 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2493 @param suite: Suite name to search for
2495 @type architecture: str
2496 @param architecture: Architecture name to search for
2498 @type session: Session
2499 @param session: Optional SQL session object (a temporary one will be
2500 generated if not supplied)
2502 @rtype: SuiteArchitecture
2503 @return: the SuiteArchitecture object or None
2506 q = session.query(SuiteArchitecture)
2507 q = q.join(Architecture).filter_by(arch_string=architecture)
2508 q = q.join(Suite).filter_by(suite_name=suite)
2512 except NoResultFound:
2515 __all__.append('get_suite_architecture')
2518 def get_suite(suite, session=None):
2520 Returns Suite object for given C{suite name}.
2523 @param suite: The name of the suite
2525 @type session: Session
2526 @param session: Optional SQLA session object (a temporary one will be
2527 generated if not supplied)
2530 @return: Suite object for the requested suite name (None if not present)
2533 q = session.query(Suite).filter_by(suite_name=suite)
2537 except NoResultFound:
2540 __all__.append('get_suite')
2542 ################################################################################
2544 class SuiteArchitecture(object):
2545 def __init__(self, *args, **kwargs):
2549 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2551 __all__.append('SuiteArchitecture')
2554 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2556 Returns list of Architecture objects for given C{suite} name
2559 @param suite: Suite name to search for
2561 @type skipsrc: boolean
2562 @param skipsrc: Whether to skip returning the 'source' architecture entry
2565 @type skipall: boolean
2566 @param skipall: Whether to skip returning the 'all' architecture entry
2569 @type session: Session
2570 @param session: Optional SQL session object (a temporary one will be
2571 generated if not supplied)
2574 @return: list of Architecture objects for the given name (may be empty)
2577 q = session.query(Architecture)
2578 q = q.join(SuiteArchitecture)
2579 q = q.join(Suite).filter_by(suite_name=suite)
2582 q = q.filter(Architecture.arch_string != 'source')
2585 q = q.filter(Architecture.arch_string != 'all')
2587 q = q.order_by('arch_string')
2591 __all__.append('get_suite_architectures')
2593 ################################################################################
2595 class SuiteSrcFormat(object):
2596 def __init__(self, *args, **kwargs):
2600 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2602 __all__.append('SuiteSrcFormat')
2605 def get_suite_src_formats(suite, session=None):
2607 Returns list of allowed SrcFormat for C{suite}.
2610 @param suite: Suite name to search for
2612 @type session: Session
2613 @param session: Optional SQL session object (a temporary one will be
2614 generated if not supplied)
2617 @return: the list of allowed source formats for I{suite}
2620 q = session.query(SrcFormat)
2621 q = q.join(SuiteSrcFormat)
2622 q = q.join(Suite).filter_by(suite_name=suite)
2623 q = q.order_by('format_name')
2627 __all__.append('get_suite_src_formats')
2629 ################################################################################
2632 def __init__(self, *args, **kwargs):
2635 def __eq__(self, val):
2636 if isinstance(val, str):
2637 return (self.uid == val)
2638 # This signals to use the normal comparison operator
2639 return NotImplemented
2641 def __ne__(self, val):
2642 if isinstance(val, str):
2643 return (self.uid != val)
2644 # This signals to use the normal comparison operator
2645 return NotImplemented
2648 return '<Uid %s (%s)>' % (self.uid, self.name)
2650 __all__.append('Uid')
2653 def add_database_user(uidname, session=None):
2655 Adds a database user
2657 @type uidname: string
2658 @param uidname: The uid of the user to add
2660 @type session: SQLAlchemy
2661 @param session: Optional SQL session object (a temporary one will be
2662 generated if not supplied). If not passed, a commit will be performed at
2663 the end of the function, otherwise the caller is responsible for commiting.
2666 @return: the uid object for the given uidname
2669 session.execute("CREATE USER :uid", {'uid': uidname})
2670 session.commit_or_flush()
2672 __all__.append('add_database_user')
2675 def get_or_set_uid(uidname, session=None):
2677 Returns uid object for given uidname.
2679 If no matching uidname is found, a row is inserted.
2681 @type uidname: string
2682 @param uidname: The uid to add
2684 @type session: SQLAlchemy
2685 @param session: Optional SQL session object (a temporary one will be
2686 generated if not supplied). If not passed, a commit will be performed at
2687 the end of the function, otherwise the caller is responsible for commiting.
2690 @return: the uid object for the given uidname
2693 q = session.query(Uid).filter_by(uid=uidname)
2697 except NoResultFound:
2701 session.commit_or_flush()
2706 __all__.append('get_or_set_uid')
2709 def get_uid_from_fingerprint(fpr, session=None):
2710 q = session.query(Uid)
2711 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2715 except NoResultFound:
2718 __all__.append('get_uid_from_fingerprint')
2720 ################################################################################
2722 class UploadBlock(object):
2723 def __init__(self, *args, **kwargs):
2727 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2729 __all__.append('UploadBlock')
2731 ################################################################################
2733 class DBConn(object):
2735 database module init.
2739 def __init__(self, *args, **kwargs):
2740 self.__dict__ = self.__shared_state
2742 if not getattr(self, 'initialised', False):
2743 self.initialised = True
2744 self.debug = kwargs.has_key('debug')
2747 def __setuptables(self):
2757 'build_queue_files',
2760 'changes_pending_binaries',
2761 'changes_pending_files',
2762 'changes_pending_files_map',
2763 'changes_pending_source',
2764 'changes_pending_source_files',
2765 'changes_pool_files',
2778 'pending_bin_contents',
2788 'suite_architectures',
2789 'suite_src_formats',
2790 'suite_build_queue_copy',
2796 for table_name in tables:
2797 table = Table(table_name, self.db_meta, autoload=True)
2798 setattr(self, 'tbl_%s' % table_name, table)
2800 def __setupmappers(self):
2801 mapper(Architecture, self.tbl_architecture,
2802 properties = dict(arch_id = self.tbl_architecture.c.id))
2804 mapper(Archive, self.tbl_archive,
2805 properties = dict(archive_id = self.tbl_archive.c.id,
2806 archive_name = self.tbl_archive.c.name))
2808 mapper(BinAssociation, self.tbl_bin_associations,
2809 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2810 suite_id = self.tbl_bin_associations.c.suite,
2811 suite = relation(Suite),
2812 binary_id = self.tbl_bin_associations.c.bin,
2813 binary = relation(DBBinary)))
2815 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2816 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2817 filename = self.tbl_pending_bin_contents.c.filename,
2818 package = self.tbl_pending_bin_contents.c.package,
2819 version = self.tbl_pending_bin_contents.c.version,
2820 arch = self.tbl_pending_bin_contents.c.arch,
2821 otype = self.tbl_pending_bin_contents.c.type))
2823 mapper(DebContents, self.tbl_deb_contents,
2824 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2825 package=self.tbl_deb_contents.c.package,
2826 suite=self.tbl_deb_contents.c.suite,
2827 arch=self.tbl_deb_contents.c.arch,
2828 section=self.tbl_deb_contents.c.section,
2829 filename=self.tbl_deb_contents.c.filename))
2831 mapper(UdebContents, self.tbl_udeb_contents,
2832 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2833 package=self.tbl_udeb_contents.c.package,
2834 suite=self.tbl_udeb_contents.c.suite,
2835 arch=self.tbl_udeb_contents.c.arch,
2836 section=self.tbl_udeb_contents.c.section,
2837 filename=self.tbl_udeb_contents.c.filename))
2839 mapper(BuildQueue, self.tbl_build_queue,
2840 properties = dict(queue_id = self.tbl_build_queue.c.id))
2842 mapper(BuildQueueFile, self.tbl_build_queue_files,
2843 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2844 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2846 mapper(DBBinary, self.tbl_binaries,
2847 properties = dict(binary_id = self.tbl_binaries.c.id,
2848 package = self.tbl_binaries.c.package,
2849 version = self.tbl_binaries.c.version,
2850 maintainer_id = self.tbl_binaries.c.maintainer,
2851 maintainer = relation(Maintainer),
2852 source_id = self.tbl_binaries.c.source,
2853 source = relation(DBSource),
2854 arch_id = self.tbl_binaries.c.architecture,
2855 architecture = relation(Architecture),
2856 poolfile_id = self.tbl_binaries.c.file,
2857 poolfile = relation(PoolFile),
2858 binarytype = self.tbl_binaries.c.type,
2859 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2860 fingerprint = relation(Fingerprint),
2861 install_date = self.tbl_binaries.c.install_date,
2862 binassociations = relation(BinAssociation,
2863 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2865 mapper(BinaryACL, self.tbl_binary_acl,
2866 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2868 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2869 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2870 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2871 architecture = relation(Architecture)))
2873 mapper(Component, self.tbl_component,
2874 properties = dict(component_id = self.tbl_component.c.id,
2875 component_name = self.tbl_component.c.name))
2877 mapper(DBConfig, self.tbl_config,
2878 properties = dict(config_id = self.tbl_config.c.id))
2880 mapper(DSCFile, self.tbl_dsc_files,
2881 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2882 source_id = self.tbl_dsc_files.c.source,
2883 source = relation(DBSource),
2884 poolfile_id = self.tbl_dsc_files.c.file,
2885 poolfile = relation(PoolFile)))
2887 mapper(PoolFile, self.tbl_files,
2888 properties = dict(file_id = self.tbl_files.c.id,
2889 filesize = self.tbl_files.c.size,
2890 location_id = self.tbl_files.c.location,
2891 location = relation(Location)))
2893 mapper(Fingerprint, self.tbl_fingerprint,
2894 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2895 uid_id = self.tbl_fingerprint.c.uid,
2896 uid = relation(Uid),
2897 keyring_id = self.tbl_fingerprint.c.keyring,
2898 keyring = relation(Keyring),
2899 source_acl = relation(SourceACL),
2900 binary_acl = relation(BinaryACL)))
2902 mapper(Keyring, self.tbl_keyrings,
2903 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2904 keyring_id = self.tbl_keyrings.c.id))
2906 mapper(DBChange, self.tbl_changes,
2907 properties = dict(change_id = self.tbl_changes.c.id,
2908 poolfiles = relation(PoolFile,
2909 secondary=self.tbl_changes_pool_files,
2910 backref="changeslinks"),
2911 seen = self.tbl_changes.c.seen,
2912 source = self.tbl_changes.c.source,
2913 binaries = self.tbl_changes.c.binaries,
2914 architecture = self.tbl_changes.c.architecture,
2915 distribution = self.tbl_changes.c.distribution,
2916 urgency = self.tbl_changes.c.urgency,
2917 maintainer = self.tbl_changes.c.maintainer,
2918 changedby = self.tbl_changes.c.changedby,
2919 date = self.tbl_changes.c.date,
2920 version = self.tbl_changes.c.version,
2921 files = relation(ChangePendingFile,
2922 secondary=self.tbl_changes_pending_files_map,
2923 backref="changesfile"),
2924 in_queue_id = self.tbl_changes.c.in_queue,
2925 in_queue = relation(PolicyQueue,
2926 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2927 approved_for_id = self.tbl_changes.c.approved_for))
2929 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2930 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2932 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2933 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
2934 filename = self.tbl_changes_pending_files.c.filename,
2935 size = self.tbl_changes_pending_files.c.size,
2936 md5sum = self.tbl_changes_pending_files.c.md5sum,
2937 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
2938 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
2940 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2941 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2942 change = relation(DBChange),
2943 maintainer = relation(Maintainer,
2944 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2945 changedby = relation(Maintainer,
2946 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2947 fingerprint = relation(Fingerprint),
2948 source_files = relation(ChangePendingFile,
2949 secondary=self.tbl_changes_pending_source_files,
2950 backref="pending_sources")))
2953 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2954 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2955 keyring = relation(Keyring, backref="keyring_acl_map"),
2956 architecture = relation(Architecture)))
2958 mapper(Location, self.tbl_location,
2959 properties = dict(location_id = self.tbl_location.c.id,
2960 component_id = self.tbl_location.c.component,
2961 component = relation(Component),
2962 archive_id = self.tbl_location.c.archive,
2963 archive = relation(Archive),
2964 archive_type = self.tbl_location.c.type))
2966 mapper(Maintainer, self.tbl_maintainer,
2967 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2969 mapper(NewComment, self.tbl_new_comments,
2970 properties = dict(comment_id = self.tbl_new_comments.c.id))
2972 mapper(Override, self.tbl_override,
2973 properties = dict(suite_id = self.tbl_override.c.suite,
2974 suite = relation(Suite),
2975 package = self.tbl_override.c.package,
2976 component_id = self.tbl_override.c.component,
2977 component = relation(Component),
2978 priority_id = self.tbl_override.c.priority,
2979 priority = relation(Priority),
2980 section_id = self.tbl_override.c.section,
2981 section = relation(Section),
2982 overridetype_id = self.tbl_override.c.type,
2983 overridetype = relation(OverrideType)))
2985 mapper(OverrideType, self.tbl_override_type,
2986 properties = dict(overridetype = self.tbl_override_type.c.type,
2987 overridetype_id = self.tbl_override_type.c.id))
2989 mapper(PolicyQueue, self.tbl_policy_queue,
2990 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
2992 mapper(Priority, self.tbl_priority,
2993 properties = dict(priority_id = self.tbl_priority.c.id))
2995 mapper(Section, self.tbl_section,
2996 properties = dict(section_id = self.tbl_section.c.id,
2997 section=self.tbl_section.c.section))
2999 mapper(DBSource, self.tbl_source,
3000 properties = dict(source_id = self.tbl_source.c.id,
3001 version = self.tbl_source.c.version,
3002 maintainer_id = self.tbl_source.c.maintainer,
3003 maintainer = relation(Maintainer,
3004 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3005 poolfile_id = self.tbl_source.c.file,
3006 poolfile = relation(PoolFile),
3007 fingerprint_id = self.tbl_source.c.sig_fpr,
3008 fingerprint = relation(Fingerprint),
3009 changedby_id = self.tbl_source.c.changedby,
3010 changedby = relation(Maintainer,
3011 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3012 srcfiles = relation(DSCFile,
3013 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3014 srcassociations = relation(SrcAssociation,
3015 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3016 srcuploaders = relation(SrcUploader)))
3018 mapper(SourceACL, self.tbl_source_acl,
3019 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3021 mapper(SrcAssociation, self.tbl_src_associations,
3022 properties = dict(sa_id = self.tbl_src_associations.c.id,
3023 suite_id = self.tbl_src_associations.c.suite,
3024 suite = relation(Suite),
3025 source_id = self.tbl_src_associations.c.source,
3026 source = relation(DBSource)))
3028 mapper(SrcFormat, self.tbl_src_format,
3029 properties = dict(src_format_id = self.tbl_src_format.c.id,
3030 format_name = self.tbl_src_format.c.format_name))
3032 mapper(SrcUploader, self.tbl_src_uploaders,
3033 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3034 source_id = self.tbl_src_uploaders.c.source,
3035 source = relation(DBSource,
3036 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3037 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3038 maintainer = relation(Maintainer,
3039 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3041 mapper(Suite, self.tbl_suite,
3042 properties = dict(suite_id = self.tbl_suite.c.id,
3043 policy_queue = relation(PolicyQueue),
3044 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3046 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3047 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3048 suite = relation(Suite, backref='suitearchitectures'),
3049 arch_id = self.tbl_suite_architectures.c.architecture,
3050 architecture = relation(Architecture)))
3052 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3053 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3054 suite = relation(Suite, backref='suitesrcformats'),
3055 src_format_id = self.tbl_suite_src_formats.c.src_format,
3056 src_format = relation(SrcFormat)))
3058 mapper(Uid, self.tbl_uid,
3059 properties = dict(uid_id = self.tbl_uid.c.id,
3060 fingerprint = relation(Fingerprint)))
3062 mapper(UploadBlock, self.tbl_upload_blocks,
3063 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3064 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3065 uid = relation(Uid, backref="uploadblocks")))
3067 ## Connection functions
3068 def __createconn(self):
3069 from config import Config
3073 connstr = "postgres://%s" % cnf["DB::Host"]
3074 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3075 connstr += ":%s" % cnf["DB::Port"]
3076 connstr += "/%s" % cnf["DB::Name"]
3079 connstr = "postgres:///%s" % cnf["DB::Name"]
3080 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3081 connstr += "?port=%s" % cnf["DB::Port"]
3083 self.db_pg = create_engine(connstr, echo=self.debug)
3084 self.db_meta = MetaData()
3085 self.db_meta.bind = self.db_pg
3086 self.db_smaker = sessionmaker(bind=self.db_pg,
3090 self.__setuptables()
3091 self.__setupmappers()
3094 return self.db_smaker()
3096 __all__.append('DBConn')