5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from datetime import datetime, timedelta
41 from errno import ENOENT
42 from tempfile import mkstemp, mkdtemp
44 from inspect import getargspec
47 from sqlalchemy import create_engine, Table, MetaData
48 from sqlalchemy.orm import sessionmaker, mapper, relation
49 from sqlalchemy import types as sqltypes
51 # Don't remove this, we re-export the exceptions to scripts which import us
52 from sqlalchemy.exc import *
53 from sqlalchemy.orm.exc import NoResultFound
55 # Only import Config until Queue stuff is changed to store its config
57 from config import Config
58 from textutils import fix_maintainer
60 ################################################################################
62 # Patch in support for the debversion field type so that it works during
65 class DebVersion(sqltypes.Text):
66 def get_col_spec(self):
69 sa_major_version = sqlalchemy.__version__[0:3]
70 if sa_major_version == "0.5":
71 from sqlalchemy.databases import postgres
72 postgres.ischema_names['debversion'] = DebVersion
74 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
76 ################################################################################
78 __all__ = ['IntegrityError', 'SQLAlchemyError']
80 ################################################################################
82 def session_wrapper(fn):
84 Wrapper around common ".., session=None):" handling. If the wrapped
85 function is called without passing 'session', we create a local one
86 and destroy it when the function ends.
88 Also attaches a commit_or_flush method to the session; if we created a
89 local session, this is a synonym for session.commit(), otherwise it is a
90 synonym for session.flush().
93 def wrapped(*args, **kwargs):
94 private_transaction = False
96 # Find the session object
97 session = kwargs.get('session')
100 if len(args) <= len(getargspec(fn)[0]) - 1:
101 # No session specified as last argument or in kwargs
102 private_transaction = True
103 session = kwargs['session'] = DBConn().session()
105 # Session is last argument in args
109 session = args[-1] = DBConn().session()
110 private_transaction = True
112 if private_transaction:
113 session.commit_or_flush = session.commit
115 session.commit_or_flush = session.flush
118 return fn(*args, **kwargs)
120 if private_transaction:
121 # We created a session; close it.
124 wrapped.__doc__ = fn.__doc__
125 wrapped.func_name = fn.func_name
129 __all__.append('session_wrapper')
131 ################################################################################
133 class Architecture(object):
134 def __init__(self, *args, **kwargs):
137 def __eq__(self, val):
138 if isinstance(val, str):
139 return (self.arch_string== val)
140 # This signals to use the normal comparison operator
141 return NotImplemented
143 def __ne__(self, val):
144 if isinstance(val, str):
145 return (self.arch_string != val)
146 # This signals to use the normal comparison operator
147 return NotImplemented
150 return '<Architecture %s>' % self.arch_string
152 __all__.append('Architecture')
155 def get_architecture(architecture, session=None):
157 Returns database id for given C{architecture}.
159 @type architecture: string
160 @param architecture: The name of the architecture
162 @type session: Session
163 @param session: Optional SQLA session object (a temporary one will be
164 generated if not supplied)
167 @return: Architecture object for the given arch (None if not present)
170 q = session.query(Architecture).filter_by(arch_string=architecture)
174 except NoResultFound:
177 __all__.append('get_architecture')
180 def get_architecture_suites(architecture, session=None):
182 Returns list of Suite objects for given C{architecture} name
185 @param source: Architecture name to search for
187 @type session: Session
188 @param session: Optional SQL session object (a temporary one will be
189 generated if not supplied)
192 @return: list of Suite objects for the given name (may be empty)
195 q = session.query(Suite)
196 q = q.join(SuiteArchitecture)
197 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
203 __all__.append('get_architecture_suites')
205 ################################################################################
207 class Archive(object):
208 def __init__(self, *args, **kwargs):
212 return '<Archive %s>' % self.archive_name
214 __all__.append('Archive')
217 def get_archive(archive, session=None):
219 returns database id for given C{archive}.
221 @type archive: string
222 @param archive: the name of the arhive
224 @type session: Session
225 @param session: Optional SQLA session object (a temporary one will be
226 generated if not supplied)
229 @return: Archive object for the given name (None if not present)
232 archive = archive.lower()
234 q = session.query(Archive).filter_by(archive_name=archive)
238 except NoResultFound:
241 __all__.append('get_archive')
243 ################################################################################
245 class BinAssociation(object):
246 def __init__(self, *args, **kwargs):
250 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
252 __all__.append('BinAssociation')
254 ################################################################################
256 class BinContents(object):
257 def __init__(self, *args, **kwargs):
261 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
263 __all__.append('BinContents')
265 ################################################################################
267 class DBBinary(object):
268 def __init__(self, *args, **kwargs):
272 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
274 __all__.append('DBBinary')
277 def get_suites_binary_in(package, session=None):
279 Returns list of Suite objects which given C{package} name is in
282 @param source: DBBinary package name to search for
285 @return: list of Suite objects for the given package
288 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
290 __all__.append('get_suites_binary_in')
293 def get_binary_from_id(binary_id, session=None):
295 Returns DBBinary object for given C{id}
298 @param binary_id: Id of the required binary
300 @type session: Session
301 @param session: Optional SQLA session object (a temporary one will be
302 generated if not supplied)
305 @return: DBBinary object for the given binary (None if not present)
308 q = session.query(DBBinary).filter_by(binary_id=binary_id)
312 except NoResultFound:
315 __all__.append('get_binary_from_id')
318 def get_binaries_from_name(package, version=None, architecture=None, session=None):
320 Returns list of DBBinary objects for given C{package} name
323 @param package: DBBinary package name to search for
325 @type version: str or None
326 @param version: Version to search for (or None)
328 @type package: str, list or None
329 @param package: Architectures to limit to (or None if no limit)
331 @type session: Session
332 @param session: Optional SQL session object (a temporary one will be
333 generated if not supplied)
336 @return: list of DBBinary objects for the given name (may be empty)
339 q = session.query(DBBinary).filter_by(package=package)
341 if version is not None:
342 q = q.filter_by(version=version)
344 if architecture is not None:
345 if not isinstance(architecture, list):
346 architecture = [architecture]
347 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
353 __all__.append('get_binaries_from_name')
356 def get_binaries_from_source_id(source_id, session=None):
358 Returns list of DBBinary objects for given C{source_id}
361 @param source_id: source_id to search for
363 @type session: Session
364 @param session: Optional SQL session object (a temporary one will be
365 generated if not supplied)
368 @return: list of DBBinary objects for the given name (may be empty)
371 return session.query(DBBinary).filter_by(source_id=source_id).all()
373 __all__.append('get_binaries_from_source_id')
376 def get_binary_from_name_suite(package, suitename, session=None):
377 ### For dak examine-package
378 ### XXX: Doesn't use object API yet
380 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
381 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
382 WHERE b.package='%(package)s'
384 AND fi.location = l.id
385 AND l.component = c.id
388 AND su.suite_name %(suitename)s
389 ORDER BY b.version DESC"""
391 return session.execute(sql % {'package': package, 'suitename': suitename})
393 __all__.append('get_binary_from_name_suite')
396 def get_binary_components(package, suitename, arch, session=None):
397 # Check for packages that have moved from one component to another
398 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
399 WHERE b.package=:package AND s.suite_name=:suitename
400 AND (a.arch_string = :arch OR a.arch_string = 'all')
401 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
402 AND f.location = l.id
403 AND l.component = c.id
406 vals = {'package': package, 'suitename': suitename, 'arch': arch}
408 return session.execute(query, vals)
410 __all__.append('get_binary_components')
412 ################################################################################
414 class BinaryACL(object):
415 def __init__(self, *args, **kwargs):
419 return '<BinaryACL %s>' % self.binary_acl_id
421 __all__.append('BinaryACL')
423 ################################################################################
425 class BinaryACLMap(object):
426 def __init__(self, *args, **kwargs):
430 return '<BinaryACLMap %s>' % self.binary_acl_map_id
432 __all__.append('BinaryACLMap')
434 ################################################################################
439 ArchiveDir "%(archivepath)s";
440 OverrideDir "/srv/ftp.debian.org/scripts/override/";
441 CacheDir "/srv/ftp.debian.org/database/";
446 Packages::Compress ". bzip2 gzip";
447 Sources::Compress ". bzip2 gzip";
452 bindirectory "incoming"
457 BinOverride "override.sid.all3";
458 BinCacheDB "packages-accepted.db";
460 FileList "%(filelist)s";
463 Packages::Extensions ".deb .udeb";
466 bindirectory "incoming/"
469 BinOverride "override.sid.all3";
470 SrcOverride "override.sid.all3.src";
471 FileList "%(filelist)s";
475 class BuildQueue(object):
476 def __init__(self, *args, **kwargs):
480 return '<BuildQueue %s>' % self.queue_name
482 def write_metadata(self, starttime, force=False):
483 # Do we write out metafiles?
484 if not (force or self.generate_metadata):
487 session = DBConn().session().object_session(self)
489 fl_fd = fl_name = ac_fd = ac_name = None
491 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
492 startdir = os.getcwd()
495 # Grab files we want to include
496 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
497 # Write file list with newer files
498 (fl_fd, fl_name) = mkstemp()
500 os.write(fl_fd, '%s\n' % n.fullpath)
503 # Write minimal apt.conf
504 # TODO: Remove hardcoding from template
505 (ac_fd, ac_name) = mkstemp()
506 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
507 'filelist': fl_name})
510 # Run apt-ftparchive generate
511 os.chdir(os.path.dirname(ac_name))
512 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
514 # Run apt-ftparchive release
515 # TODO: Eww - fix this
516 bname = os.path.basename(self.path)
520 # We have to remove the Release file otherwise it'll be included in the
523 os.unlink(os.path.join(bname, 'Release'))
527 os.system("""apt-ftparchive -qq -o APT::FTPArchive::Release::Origin="%s" -o APT::FTPArchive::Release::Label="%s" -o APT::FTPArchive::Release::Description="%s" -o APT::FTPArchive::Release::Architectures="%s" release %s > Release""" % (self.origin, self.label, self.releasedescription, arches, bname))
532 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
533 if cnf.has_key("Dinstall::SigningPubKeyring"):
534 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
536 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
538 # Move the files if we got this far
539 os.rename('Release', os.path.join(bname, 'Release'))
541 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
543 # Clean up any left behind files
570 def clean_and_update(self, starttime, Logger, dryrun=False):
571 """WARNING: This routine commits for you"""
572 session = DBConn().session().object_session(self)
574 if self.generate_metadata and not dryrun:
575 self.write_metadata(starttime)
577 # Grab files older than our execution time
578 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
584 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
586 Logger.log(["I: Removing %s from the queue" % o.fullpath])
587 os.unlink(o.fullpath)
590 # If it wasn't there, don't worry
591 if e.errno == ENOENT:
594 # TODO: Replace with proper logging call
595 Logger.log(["E: Could not remove %s" % o.fullpath])
602 for f in os.listdir(self.path):
603 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
607 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
608 except NoResultFound:
609 fp = os.path.join(self.path, f)
611 Logger.log(["I: Would remove unused link %s" % fp])
613 Logger.log(["I: Removing unused link %s" % fp])
617 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
619 def add_file_from_pool(self, poolfile):
620 """Copies a file into the pool. Assumes that the PoolFile object is
621 attached to the same SQLAlchemy session as the Queue object is.
623 The caller is responsible for committing after calling this function."""
624 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
626 # Check if we have a file of this name or this ID already
627 for f in self.queuefiles:
628 if f.fileid is not None and f.fileid == poolfile.file_id or \
629 f.poolfile.filename == poolfile_basename:
630 # In this case, update the BuildQueueFile entry so we
631 # don't remove it too early
632 f.lastused = datetime.now()
633 DBConn().session().object_session(poolfile).add(f)
636 # Prepare BuildQueueFile object
637 qf = BuildQueueFile()
638 qf.build_queue_id = self.queue_id
639 qf.lastused = datetime.now()
640 qf.filename = poolfile_basename
642 targetpath = poolfile.fullpath
643 queuepath = os.path.join(self.path, poolfile_basename)
647 # We need to copy instead of symlink
649 utils.copy(targetpath, queuepath)
650 # NULL in the fileid field implies a copy
653 os.symlink(targetpath, queuepath)
654 qf.fileid = poolfile.file_id
658 # Get the same session as the PoolFile is using and add the qf to it
659 DBConn().session().object_session(poolfile).add(qf)
664 __all__.append('BuildQueue')
667 def get_build_queue(queuename, session=None):
669 Returns BuildQueue object for given C{queue name}, creating it if it does not
672 @type queuename: string
673 @param queuename: The name of the queue
675 @type session: Session
676 @param session: Optional SQLA session object (a temporary one will be
677 generated if not supplied)
680 @return: BuildQueue object for the given queue
683 q = session.query(BuildQueue).filter_by(queue_name=queuename)
687 except NoResultFound:
690 __all__.append('get_build_queue')
692 ################################################################################
694 class BuildQueueFile(object):
695 def __init__(self, *args, **kwargs):
699 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
703 return os.path.join(self.buildqueue.path, self.filename)
706 __all__.append('BuildQueueFile')
708 ################################################################################
710 class ChangePendingBinary(object):
711 def __init__(self, *args, **kwargs):
715 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
717 __all__.append('ChangePendingBinary')
719 ################################################################################
721 class ChangePendingFile(object):
722 def __init__(self, *args, **kwargs):
726 return '<ChangePendingFile %s>' % self.change_pending_file_id
728 __all__.append('ChangePendingFile')
730 ################################################################################
732 class ChangePendingSource(object):
733 def __init__(self, *args, **kwargs):
737 return '<ChangePendingSource %s>' % self.change_pending_source_id
739 __all__.append('ChangePendingSource')
741 ################################################################################
743 class Component(object):
744 def __init__(self, *args, **kwargs):
747 def __eq__(self, val):
748 if isinstance(val, str):
749 return (self.component_name == val)
750 # This signals to use the normal comparison operator
751 return NotImplemented
753 def __ne__(self, val):
754 if isinstance(val, str):
755 return (self.component_name != val)
756 # This signals to use the normal comparison operator
757 return NotImplemented
760 return '<Component %s>' % self.component_name
763 __all__.append('Component')
766 def get_component(component, session=None):
768 Returns database id for given C{component}.
770 @type component: string
771 @param component: The name of the override type
774 @return: the database id for the given component
777 component = component.lower()
779 q = session.query(Component).filter_by(component_name=component)
783 except NoResultFound:
786 __all__.append('get_component')
788 ################################################################################
790 class DBConfig(object):
791 def __init__(self, *args, **kwargs):
795 return '<DBConfig %s>' % self.name
797 __all__.append('DBConfig')
799 ################################################################################
802 def get_or_set_contents_file_id(filename, session=None):
804 Returns database id for given filename.
806 If no matching file is found, a row is inserted.
808 @type filename: string
809 @param filename: The filename
810 @type session: SQLAlchemy
811 @param session: Optional SQL session object (a temporary one will be
812 generated if not supplied). If not passed, a commit will be performed at
813 the end of the function, otherwise the caller is responsible for commiting.
816 @return: the database id for the given component
819 q = session.query(ContentFilename).filter_by(filename=filename)
822 ret = q.one().cafilename_id
823 except NoResultFound:
824 cf = ContentFilename()
825 cf.filename = filename
827 session.commit_or_flush()
828 ret = cf.cafilename_id
832 __all__.append('get_or_set_contents_file_id')
835 def get_contents(suite, overridetype, section=None, session=None):
837 Returns contents for a suite / overridetype combination, limiting
838 to a section if not None.
841 @param suite: Suite object
843 @type overridetype: OverrideType
844 @param overridetype: OverrideType object
846 @type section: Section
847 @param section: Optional section object to limit results to
849 @type session: SQLAlchemy
850 @param session: Optional SQL session object (a temporary one will be
851 generated if not supplied)
854 @return: ResultsProxy object set up to return tuples of (filename, section,
858 # find me all of the contents for a given suite
859 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
863 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
864 JOIN content_file_names n ON (c.filename=n.id)
865 JOIN binaries b ON (b.id=c.binary_pkg)
866 JOIN override o ON (o.package=b.package)
867 JOIN section s ON (s.id=o.section)
868 WHERE o.suite = :suiteid AND o.type = :overridetypeid
869 AND b.type=:overridetypename"""
871 vals = {'suiteid': suite.suite_id,
872 'overridetypeid': overridetype.overridetype_id,
873 'overridetypename': overridetype.overridetype}
875 if section is not None:
876 contents_q += " AND s.id = :sectionid"
877 vals['sectionid'] = section.section_id
879 contents_q += " ORDER BY fn"
881 return session.execute(contents_q, vals)
883 __all__.append('get_contents')
885 ################################################################################
887 class ContentFilepath(object):
888 def __init__(self, *args, **kwargs):
892 return '<ContentFilepath %s>' % self.filepath
894 __all__.append('ContentFilepath')
897 def get_or_set_contents_path_id(filepath, session=None):
899 Returns database id for given path.
901 If no matching file is found, a row is inserted.
903 @type filename: string
904 @param filename: The filepath
905 @type session: SQLAlchemy
906 @param session: Optional SQL session object (a temporary one will be
907 generated if not supplied). If not passed, a commit will be performed at
908 the end of the function, otherwise the caller is responsible for commiting.
911 @return: the database id for the given path
914 q = session.query(ContentFilepath).filter_by(filepath=filepath)
917 ret = q.one().cafilepath_id
918 except NoResultFound:
919 cf = ContentFilepath()
920 cf.filepath = filepath
922 session.commit_or_flush()
923 ret = cf.cafilepath_id
927 __all__.append('get_or_set_contents_path_id')
929 ################################################################################
931 class ContentAssociation(object):
932 def __init__(self, *args, **kwargs):
936 return '<ContentAssociation %s>' % self.ca_id
938 __all__.append('ContentAssociation')
940 def insert_content_paths(binary_id, fullpaths, session=None):
942 Make sure given path is associated with given binary id
945 @param binary_id: the id of the binary
946 @type fullpaths: list
947 @param fullpaths: the list of paths of the file being associated with the binary
948 @type session: SQLAlchemy session
949 @param session: Optional SQLAlchemy session. If this is passed, the caller
950 is responsible for ensuring a transaction has begun and committing the
951 results or rolling back based on the result code. If not passed, a commit
952 will be performed at the end of the function, otherwise the caller is
953 responsible for commiting.
955 @return: True upon success
960 session = DBConn().session()
967 def generate_path_dicts():
968 for fullpath in fullpaths:
969 if fullpath.startswith( './' ):
970 fullpath = fullpath[2:]
972 yield {'fulename':fullpath, 'id': binary_id }
974 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
975 generate_path_dicts() )
983 traceback.print_exc()
985 # Only rollback if we set up the session ourself
992 __all__.append('insert_content_paths')
994 ################################################################################
996 class DSCFile(object):
997 def __init__(self, *args, **kwargs):
1001 return '<DSCFile %s>' % self.dscfile_id
1003 __all__.append('DSCFile')
1006 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1008 Returns a list of DSCFiles which may be empty
1010 @type dscfile_id: int (optional)
1011 @param dscfile_id: the dscfile_id of the DSCFiles to find
1013 @type source_id: int (optional)
1014 @param source_id: the source id related to the DSCFiles to find
1016 @type poolfile_id: int (optional)
1017 @param poolfile_id: the poolfile id related to the DSCFiles to find
1020 @return: Possibly empty list of DSCFiles
1023 q = session.query(DSCFile)
1025 if dscfile_id is not None:
1026 q = q.filter_by(dscfile_id=dscfile_id)
1028 if source_id is not None:
1029 q = q.filter_by(source_id=source_id)
1031 if poolfile_id is not None:
1032 q = q.filter_by(poolfile_id=poolfile_id)
1036 __all__.append('get_dscfiles')
1038 ################################################################################
1040 class PoolFile(object):
1041 def __init__(self, *args, **kwargs):
1045 return '<PoolFile %s>' % self.filename
1049 return os.path.join(self.location.path, self.filename)
1051 __all__.append('PoolFile')
1054 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1057 (ValidFileFound [boolean or None], PoolFile object or None)
1059 @type filename: string
1060 @param filename: the filename of the file to check against the DB
1063 @param filesize: the size of the file to check against the DB
1065 @type md5sum: string
1066 @param md5sum: the md5sum of the file to check against the DB
1068 @type location_id: int
1069 @param location_id: the id of the location to look in
1072 @return: Tuple of length 2.
1073 If more than one file found with that name:
1075 If valid pool file found: (True, PoolFile object)
1076 If valid pool file not found:
1077 (False, None) if no file found
1078 (False, PoolFile object) if file found with size/md5sum mismatch
1081 q = session.query(PoolFile).filter_by(filename=filename)
1082 q = q.join(Location).filter_by(location_id=location_id)
1092 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1100 __all__.append('check_poolfile')
1103 def get_poolfile_by_id(file_id, session=None):
1105 Returns a PoolFile objects or None for the given id
1108 @param file_id: the id of the file to look for
1110 @rtype: PoolFile or None
1111 @return: either the PoolFile object or None
1114 q = session.query(PoolFile).filter_by(file_id=file_id)
1118 except NoResultFound:
1121 __all__.append('get_poolfile_by_id')
1125 def get_poolfile_by_name(filename, location_id=None, session=None):
1127 Returns an array of PoolFile objects for the given filename and
1128 (optionally) location_id
1130 @type filename: string
1131 @param filename: the filename of the file to check against the DB
1133 @type location_id: int
1134 @param location_id: the id of the location to look in (optional)
1137 @return: array of PoolFile objects
1140 q = session.query(PoolFile).filter_by(filename=filename)
1142 if location_id is not None:
1143 q = q.join(Location).filter_by(location_id=location_id)
1147 __all__.append('get_poolfile_by_name')
1150 def get_poolfile_like_name(filename, session=None):
1152 Returns an array of PoolFile objects which are like the given name
1154 @type filename: string
1155 @param filename: the filename of the file to check against the DB
1158 @return: array of PoolFile objects
1161 # TODO: There must be a way of properly using bind parameters with %FOO%
1162 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1166 __all__.append('get_poolfile_like_name')
1169 def add_poolfile(filename, datadict, location_id, session=None):
1171 Add a new file to the pool
1173 @type filename: string
1174 @param filename: filename
1176 @type datadict: dict
1177 @param datadict: dict with needed data
1179 @type location_id: int
1180 @param location_id: database id of the location
1183 @return: the PoolFile object created
1185 poolfile = PoolFile()
1186 poolfile.filename = filename
1187 poolfile.filesize = datadict["size"]
1188 poolfile.md5sum = datadict["md5sum"]
1189 poolfile.sha1sum = datadict["sha1sum"]
1190 poolfile.sha256sum = datadict["sha256sum"]
1191 poolfile.location_id = location_id
1193 session.add(poolfile)
1194 # Flush to get a file id (NB: This is not a commit)
1199 __all__.append('add_poolfile')
1201 ################################################################################
1203 class Fingerprint(object):
1204 def __init__(self, *args, **kwargs):
1208 return '<Fingerprint %s>' % self.fingerprint
1210 __all__.append('Fingerprint')
1213 def get_fingerprint(fpr, session=None):
1215 Returns Fingerprint object for given fpr.
1218 @param fpr: The fpr to find / add
1220 @type session: SQLAlchemy
1221 @param session: Optional SQL session object (a temporary one will be
1222 generated if not supplied).
1225 @return: the Fingerprint object for the given fpr or None
1228 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1232 except NoResultFound:
1237 __all__.append('get_fingerprint')
1240 def get_or_set_fingerprint(fpr, session=None):
1242 Returns Fingerprint object for given fpr.
1244 If no matching fpr is found, a row is inserted.
1247 @param fpr: The fpr to find / add
1249 @type session: SQLAlchemy
1250 @param session: Optional SQL session object (a temporary one will be
1251 generated if not supplied). If not passed, a commit will be performed at
1252 the end of the function, otherwise the caller is responsible for commiting.
1253 A flush will be performed either way.
1256 @return: the Fingerprint object for the given fpr
1259 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1263 except NoResultFound:
1264 fingerprint = Fingerprint()
1265 fingerprint.fingerprint = fpr
1266 session.add(fingerprint)
1267 session.commit_or_flush()
1272 __all__.append('get_or_set_fingerprint')
1274 ################################################################################
1276 # Helper routine for Keyring class
1277 def get_ldap_name(entry):
1279 for k in ["cn", "mn", "sn"]:
1281 if ret and ret[0] != "" and ret[0] != "-":
1283 return " ".join(name)
1285 ################################################################################
1287 class Keyring(object):
1288 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1289 " --with-colons --fingerprint --fingerprint"
1294 def __init__(self, *args, **kwargs):
1298 return '<Keyring %s>' % self.keyring_name
1300 def de_escape_gpg_str(self, txt):
1301 esclist = re.split(r'(\\x..)', txt)
1302 for x in range(1,len(esclist),2):
1303 esclist[x] = "%c" % (int(esclist[x][2:],16))
1304 return "".join(esclist)
1306 def load_keys(self, keyring):
1309 if not self.keyring_id:
1310 raise Exception('Must be initialized with database information')
1312 k = os.popen(self.gpg_invocation % keyring, "r")
1316 for line in k.xreadlines():
1317 field = line.split(":")
1318 if field[0] == "pub":
1320 (name, addr) = email.Utils.parseaddr(field[9])
1321 name = re.sub(r"\s*[(].*[)]", "", name)
1322 if name == "" or addr == "" or "@" not in addr:
1324 addr = "invalid-uid"
1325 name = self.de_escape_gpg_str(name)
1326 self.keys[key] = {"email": addr}
1328 self.keys[key]["name"] = name
1329 self.keys[key]["aliases"] = [name]
1330 self.keys[key]["fingerprints"] = []
1332 elif key and field[0] == "sub" and len(field) >= 12:
1333 signingkey = ("s" in field[11])
1334 elif key and field[0] == "uid":
1335 (name, addr) = email.Utils.parseaddr(field[9])
1336 if name and name not in self.keys[key]["aliases"]:
1337 self.keys[key]["aliases"].append(name)
1338 elif signingkey and field[0] == "fpr":
1339 self.keys[key]["fingerprints"].append(field[9])
1340 self.fpr_lookup[field[9]] = key
1342 def import_users_from_ldap(self, session):
1346 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1347 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1349 l = ldap.open(LDAPServer)
1350 l.simple_bind_s("","")
1351 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1352 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1353 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1355 ldap_fin_uid_id = {}
1362 uid = entry["uid"][0]
1363 name = get_ldap_name(entry)
1364 fingerprints = entry["keyFingerPrint"]
1366 for f in fingerprints:
1367 key = self.fpr_lookup.get(f, None)
1368 if key not in self.keys:
1370 self.keys[key]["uid"] = uid
1374 keyid = get_or_set_uid(uid, session).uid_id
1375 byuid[keyid] = (uid, name)
1376 byname[uid] = (keyid, name)
1378 return (byname, byuid)
1380 def generate_users_from_keyring(self, format, session):
1384 for x in self.keys.keys():
1385 if self.keys[x]["email"] == "invalid-uid":
1387 self.keys[x]["uid"] = format % "invalid-uid"
1389 uid = format % self.keys[x]["email"]
1390 keyid = get_or_set_uid(uid, session).uid_id
1391 byuid[keyid] = (uid, self.keys[x]["name"])
1392 byname[uid] = (keyid, self.keys[x]["name"])
1393 self.keys[x]["uid"] = uid
1396 uid = format % "invalid-uid"
1397 keyid = get_or_set_uid(uid, session).uid_id
1398 byuid[keyid] = (uid, "ungeneratable user id")
1399 byname[uid] = (keyid, "ungeneratable user id")
1401 return (byname, byuid)
1403 __all__.append('Keyring')
1406 def get_keyring(keyring, session=None):
1408 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1409 If C{keyring} already has an entry, simply return the existing Keyring
1411 @type keyring: string
1412 @param keyring: the keyring name
1415 @return: the Keyring object for this keyring
1418 q = session.query(Keyring).filter_by(keyring_name=keyring)
1422 except NoResultFound:
1425 __all__.append('get_keyring')
1427 ################################################################################
1429 class KeyringACLMap(object):
1430 def __init__(self, *args, **kwargs):
1434 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1436 __all__.append('KeyringACLMap')
1438 ################################################################################
1440 class DBChange(object):
1441 def __init__(self, *args, **kwargs):
1445 return '<DBChange %s>' % self.changesname
1447 def clean_from_queue(self):
1448 session = DBConn().session().object_session(self)
1450 # Remove changes_pool_files entries
1453 # Remove changes_pending_files references
1456 # Clear out of queue
1457 self.in_queue = None
1458 self.approved_for_id = None
1460 __all__.append('DBChange')
1463 def get_dbchange(filename, session=None):
1465 returns DBChange object for given C{filename}.
1467 @type archive: string
1468 @param archive: the name of the arhive
1470 @type session: Session
1471 @param session: Optional SQLA session object (a temporary one will be
1472 generated if not supplied)
1475 @return: Archive object for the given name (None if not present)
1478 q = session.query(DBChange).filter_by(changesname=filename)
1482 except NoResultFound:
1485 __all__.append('get_dbchange')
1487 ################################################################################
1489 class Location(object):
1490 def __init__(self, *args, **kwargs):
1494 return '<Location %s (%s)>' % (self.path, self.location_id)
1496 __all__.append('Location')
1499 def get_location(location, component=None, archive=None, session=None):
1501 Returns Location object for the given combination of location, component
1504 @type location: string
1505 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1507 @type component: string
1508 @param component: the component name (if None, no restriction applied)
1510 @type archive: string
1511 @param archive_id: the archive name (if None, no restriction applied)
1513 @rtype: Location / None
1514 @return: Either a Location object or None if one can't be found
1517 q = session.query(Location).filter_by(path=location)
1519 if archive is not None:
1520 q = q.join(Archive).filter_by(archive_name=archive)
1522 if component is not None:
1523 q = q.join(Component).filter_by(component_name=component)
1527 except NoResultFound:
1530 __all__.append('get_location')
1532 ################################################################################
1534 class Maintainer(object):
1535 def __init__(self, *args, **kwargs):
1539 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1541 def get_split_maintainer(self):
1542 if not hasattr(self, 'name') or self.name is None:
1543 return ('', '', '', '')
1545 return fix_maintainer(self.name.strip())
1547 __all__.append('Maintainer')
1550 def get_or_set_maintainer(name, session=None):
1552 Returns Maintainer object for given maintainer name.
1554 If no matching maintainer name is found, a row is inserted.
1557 @param name: The maintainer name to add
1559 @type session: SQLAlchemy
1560 @param session: Optional SQL session object (a temporary one will be
1561 generated if not supplied). If not passed, a commit will be performed at
1562 the end of the function, otherwise the caller is responsible for commiting.
1563 A flush will be performed either way.
1566 @return: the Maintainer object for the given maintainer
1569 q = session.query(Maintainer).filter_by(name=name)
1572 except NoResultFound:
1573 maintainer = Maintainer()
1574 maintainer.name = name
1575 session.add(maintainer)
1576 session.commit_or_flush()
1581 __all__.append('get_or_set_maintainer')
1584 def get_maintainer(maintainer_id, session=None):
1586 Return the name of the maintainer behind C{maintainer_id} or None if that
1587 maintainer_id is invalid.
1589 @type maintainer_id: int
1590 @param maintainer_id: the id of the maintainer
1593 @return: the Maintainer with this C{maintainer_id}
1596 return session.query(Maintainer).get(maintainer_id)
1598 __all__.append('get_maintainer')
1600 ################################################################################
1602 class NewComment(object):
1603 def __init__(self, *args, **kwargs):
1607 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1609 __all__.append('NewComment')
1612 def has_new_comment(package, version, session=None):
1614 Returns true if the given combination of C{package}, C{version} has a comment.
1616 @type package: string
1617 @param package: name of the package
1619 @type version: string
1620 @param version: package version
1622 @type session: Session
1623 @param session: Optional SQLA session object (a temporary one will be
1624 generated if not supplied)
1630 q = session.query(NewComment)
1631 q = q.filter_by(package=package)
1632 q = q.filter_by(version=version)
1634 return bool(q.count() > 0)
1636 __all__.append('has_new_comment')
1639 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1641 Returns (possibly empty) list of NewComment objects for the given
1644 @type package: string (optional)
1645 @param package: name of the package
1647 @type version: string (optional)
1648 @param version: package version
1650 @type comment_id: int (optional)
1651 @param comment_id: An id of a comment
1653 @type session: Session
1654 @param session: Optional SQLA session object (a temporary one will be
1655 generated if not supplied)
1658 @return: A (possibly empty) list of NewComment objects will be returned
1661 q = session.query(NewComment)
1662 if package is not None: q = q.filter_by(package=package)
1663 if version is not None: q = q.filter_by(version=version)
1664 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1668 __all__.append('get_new_comments')
1670 ################################################################################
1672 class Override(object):
1673 def __init__(self, *args, **kwargs):
1677 return '<Override %s (%s)>' % (self.package, self.suite_id)
1679 __all__.append('Override')
1682 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1684 Returns Override object for the given parameters
1686 @type package: string
1687 @param package: The name of the package
1689 @type suite: string, list or None
1690 @param suite: The name of the suite (or suites if a list) to limit to. If
1691 None, don't limit. Defaults to None.
1693 @type component: string, list or None
1694 @param component: The name of the component (or components if a list) to
1695 limit to. If None, don't limit. Defaults to None.
1697 @type overridetype: string, list or None
1698 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1699 limit to. If None, don't limit. Defaults to None.
1701 @type session: Session
1702 @param session: Optional SQLA session object (a temporary one will be
1703 generated if not supplied)
1706 @return: A (possibly empty) list of Override objects will be returned
1709 q = session.query(Override)
1710 q = q.filter_by(package=package)
1712 if suite is not None:
1713 if not isinstance(suite, list): suite = [suite]
1714 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1716 if component is not None:
1717 if not isinstance(component, list): component = [component]
1718 q = q.join(Component).filter(Component.component_name.in_(component))
1720 if overridetype is not None:
1721 if not isinstance(overridetype, list): overridetype = [overridetype]
1722 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1726 __all__.append('get_override')
1729 ################################################################################
1731 class OverrideType(object):
1732 def __init__(self, *args, **kwargs):
1736 return '<OverrideType %s>' % self.overridetype
1738 __all__.append('OverrideType')
1741 def get_override_type(override_type, session=None):
1743 Returns OverrideType object for given C{override type}.
1745 @type override_type: string
1746 @param override_type: The name of the override type
1748 @type session: Session
1749 @param session: Optional SQLA session object (a temporary one will be
1750 generated if not supplied)
1753 @return: the database id for the given override type
1756 q = session.query(OverrideType).filter_by(overridetype=override_type)
1760 except NoResultFound:
1763 __all__.append('get_override_type')
1765 ################################################################################
1767 class DebContents(object):
1768 def __init__(self, *args, **kwargs):
1772 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1774 __all__.append('DebContents')
1777 class UdebContents(object):
1778 def __init__(self, *args, **kwargs):
1782 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1784 __all__.append('UdebContents')
1786 class PendingBinContents(object):
1787 def __init__(self, *args, **kwargs):
1791 return '<PendingBinContents %s>' % self.contents_id
1793 __all__.append('PendingBinContents')
1795 def insert_pending_content_paths(package,
1800 Make sure given paths are temporarily associated with given
1804 @param package: the package to associate with should have been read in from the binary control file
1805 @type fullpaths: list
1806 @param fullpaths: the list of paths of the file being associated with the binary
1807 @type session: SQLAlchemy session
1808 @param session: Optional SQLAlchemy session. If this is passed, the caller
1809 is responsible for ensuring a transaction has begun and committing the
1810 results or rolling back based on the result code. If not passed, a commit
1811 will be performed at the end of the function
1813 @return: True upon success, False if there is a problem
1816 privatetrans = False
1819 session = DBConn().session()
1823 arch = get_architecture(package['Architecture'], session)
1824 arch_id = arch.arch_id
1826 # Remove any already existing recorded files for this package
1827 q = session.query(PendingBinContents)
1828 q = q.filter_by(package=package['Package'])
1829 q = q.filter_by(version=package['Version'])
1830 q = q.filter_by(architecture=arch_id)
1833 for fullpath in fullpaths:
1835 if fullpath.startswith( "./" ):
1836 fullpath = fullpath[2:]
1838 pca = PendingBinContents()
1839 pca.package = package['Package']
1840 pca.version = package['Version']
1842 pca.architecture = arch_id
1845 pca.type = 8 # gross
1847 pca.type = 7 # also gross
1850 # Only commit if we set up the session ourself
1858 except Exception, e:
1859 traceback.print_exc()
1861 # Only rollback if we set up the session ourself
1868 __all__.append('insert_pending_content_paths')
1870 ################################################################################
1872 class PolicyQueue(object):
1873 def __init__(self, *args, **kwargs):
1877 return '<PolicyQueue %s>' % self.queue_name
1879 __all__.append('PolicyQueue')
1882 def get_policy_queue(queuename, session=None):
1884 Returns PolicyQueue object for given C{queue name}
1886 @type queuename: string
1887 @param queuename: The name of the queue
1889 @type session: Session
1890 @param session: Optional SQLA session object (a temporary one will be
1891 generated if not supplied)
1894 @return: PolicyQueue object for the given queue
1897 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1901 except NoResultFound:
1904 __all__.append('get_policy_queue')
1906 ################################################################################
1908 class Priority(object):
1909 def __init__(self, *args, **kwargs):
1912 def __eq__(self, val):
1913 if isinstance(val, str):
1914 return (self.priority == val)
1915 # This signals to use the normal comparison operator
1916 return NotImplemented
1918 def __ne__(self, val):
1919 if isinstance(val, str):
1920 return (self.priority != val)
1921 # This signals to use the normal comparison operator
1922 return NotImplemented
1925 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1927 __all__.append('Priority')
1930 def get_priority(priority, session=None):
1932 Returns Priority object for given C{priority name}.
1934 @type priority: string
1935 @param priority: The name of the priority
1937 @type session: Session
1938 @param session: Optional SQLA session object (a temporary one will be
1939 generated if not supplied)
1942 @return: Priority object for the given priority
1945 q = session.query(Priority).filter_by(priority=priority)
1949 except NoResultFound:
1952 __all__.append('get_priority')
1955 def get_priorities(session=None):
1957 Returns dictionary of priority names -> id mappings
1959 @type session: Session
1960 @param session: Optional SQL session object (a temporary one will be
1961 generated if not supplied)
1964 @return: dictionary of priority names -> id mappings
1968 q = session.query(Priority)
1970 ret[x.priority] = x.priority_id
1974 __all__.append('get_priorities')
1976 ################################################################################
1978 class Section(object):
1979 def __init__(self, *args, **kwargs):
1982 def __eq__(self, val):
1983 if isinstance(val, str):
1984 return (self.section == val)
1985 # This signals to use the normal comparison operator
1986 return NotImplemented
1988 def __ne__(self, val):
1989 if isinstance(val, str):
1990 return (self.section != val)
1991 # This signals to use the normal comparison operator
1992 return NotImplemented
1995 return '<Section %s>' % self.section
1997 __all__.append('Section')
2000 def get_section(section, session=None):
2002 Returns Section object for given C{section name}.
2004 @type section: string
2005 @param section: The name of the section
2007 @type session: Session
2008 @param session: Optional SQLA session object (a temporary one will be
2009 generated if not supplied)
2012 @return: Section object for the given section name
2015 q = session.query(Section).filter_by(section=section)
2019 except NoResultFound:
2022 __all__.append('get_section')
2025 def get_sections(session=None):
2027 Returns dictionary of section names -> id mappings
2029 @type session: Session
2030 @param session: Optional SQL session object (a temporary one will be
2031 generated if not supplied)
2034 @return: dictionary of section names -> id mappings
2038 q = session.query(Section)
2040 ret[x.section] = x.section_id
2044 __all__.append('get_sections')
2046 ################################################################################
2048 class DBSource(object):
2049 def __init__(self, *args, **kwargs):
2053 return '<DBSource %s (%s)>' % (self.source, self.version)
2055 __all__.append('DBSource')
2058 def source_exists(source, source_version, suites = ["any"], session=None):
2060 Ensure that source exists somewhere in the archive for the binary
2061 upload being processed.
2062 1. exact match => 1.0-3
2063 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2065 @type package: string
2066 @param package: package source name
2068 @type source_version: string
2069 @param source_version: expected source version
2072 @param suites: list of suites to check in, default I{any}
2074 @type session: Session
2075 @param session: Optional SQLA session object (a temporary one will be
2076 generated if not supplied)
2079 @return: returns 1 if a source with expected version is found, otherwise 0
2086 for suite in suites:
2087 q = session.query(DBSource).filter_by(source=source)
2089 # source must exist in suite X, or in some other suite that's
2090 # mapped to X, recursively... silent-maps are counted too,
2091 # unreleased-maps aren't.
2092 maps = cnf.ValueList("SuiteMappings")[:]
2094 maps = [ m.split() for m in maps ]
2095 maps = [ (x[1], x[2]) for x in maps
2096 if x[0] == "map" or x[0] == "silent-map" ]
2099 if x[1] in s and x[0] not in s:
2102 q = q.join(SrcAssociation).join(Suite)
2103 q = q.filter(Suite.suite_name.in_(s))
2105 # Reduce the query results to a list of version numbers
2106 ql = [ j.version for j in q.all() ]
2109 if source_version in ql:
2113 from daklib.regexes import re_bin_only_nmu
2114 orig_source_version = re_bin_only_nmu.sub('', source_version)
2115 if orig_source_version in ql:
2118 # No source found so return not ok
2123 __all__.append('source_exists')
2126 def get_suites_source_in(source, session=None):
2128 Returns list of Suite objects which given C{source} name is in
2131 @param source: DBSource package name to search for
2134 @return: list of Suite objects for the given source
2137 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2139 __all__.append('get_suites_source_in')
2142 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2144 Returns list of DBSource objects for given C{source} name and other parameters
2147 @param source: DBSource package name to search for
2149 @type source: str or None
2150 @param source: DBSource version name to search for or None if not applicable
2152 @type dm_upload_allowed: bool
2153 @param dm_upload_allowed: If None, no effect. If True or False, only
2154 return packages with that dm_upload_allowed setting
2156 @type session: Session
2157 @param session: Optional SQL session object (a temporary one will be
2158 generated if not supplied)
2161 @return: list of DBSource objects for the given name (may be empty)
2164 q = session.query(DBSource).filter_by(source=source)
2166 if version is not None:
2167 q = q.filter_by(version=version)
2169 if dm_upload_allowed is not None:
2170 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2174 __all__.append('get_sources_from_name')
2177 def get_source_in_suite(source, suite, session=None):
2179 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2181 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2182 - B{suite} - a suite name, eg. I{unstable}
2184 @type source: string
2185 @param source: source package name
2188 @param suite: the suite name
2191 @return: the version for I{source} in I{suite}
2195 q = session.query(SrcAssociation)
2196 q = q.join('source').filter_by(source=source)
2197 q = q.join('suite').filter_by(suite_name=suite)
2200 return q.one().source
2201 except NoResultFound:
2204 __all__.append('get_source_in_suite')
2206 ################################################################################
2209 def add_dsc_to_db(u, filename, session=None):
2210 entry = u.pkg.files[filename]
2214 source.source = u.pkg.dsc["source"]
2215 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2216 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2217 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2218 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2219 source.install_date = datetime.now().date()
2221 dsc_component = entry["component"]
2222 dsc_location_id = entry["location id"]
2224 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2226 # Set up a new poolfile if necessary
2227 if not entry.has_key("files id") or not entry["files id"]:
2228 filename = entry["pool name"] + filename
2229 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2231 pfs.append(poolfile)
2232 entry["files id"] = poolfile.file_id
2234 source.poolfile_id = entry["files id"]
2238 for suite_name in u.pkg.changes["distribution"].keys():
2239 sa = SrcAssociation()
2240 sa.source_id = source.source_id
2241 sa.suite_id = get_suite(suite_name).suite_id
2246 # Add the source files to the DB (files and dsc_files)
2248 dscfile.source_id = source.source_id
2249 dscfile.poolfile_id = entry["files id"]
2250 session.add(dscfile)
2252 for dsc_file, dentry in u.pkg.dsc_files.items():
2254 df.source_id = source.source_id
2256 # If the .orig tarball is already in the pool, it's
2257 # files id is stored in dsc_files by check_dsc().
2258 files_id = dentry.get("files id", None)
2260 # Find the entry in the files hash
2261 # TODO: Bail out here properly
2263 for f, e in u.pkg.files.items():
2268 if files_id is None:
2269 filename = dfentry["pool name"] + dsc_file
2271 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2272 # FIXME: needs to check for -1/-2 and or handle exception
2273 if found and obj is not None:
2274 files_id = obj.file_id
2277 # If still not found, add it
2278 if files_id is None:
2279 # HACK: Force sha1sum etc into dentry
2280 dentry["sha1sum"] = dfentry["sha1sum"]
2281 dentry["sha256sum"] = dfentry["sha256sum"]
2282 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2283 pfs.append(poolfile)
2284 files_id = poolfile.file_id
2286 poolfile = get_poolfile_by_id(files_id, session)
2287 if poolfile is None:
2288 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2289 pfs.append(poolfile)
2291 df.poolfile_id = files_id
2296 # Add the src_uploaders to the DB
2297 uploader_ids = [source.maintainer_id]
2298 if u.pkg.dsc.has_key("uploaders"):
2299 for up in u.pkg.dsc["uploaders"].split(","):
2301 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2304 for up in uploader_ids:
2305 if added_ids.has_key(up):
2306 utils.warn("Already saw uploader %s for source %s" % (up, source.source))
2312 su.maintainer_id = up
2313 su.source_id = source.source_id
2318 return source, dsc_component, dsc_location_id, pfs
2320 __all__.append('add_dsc_to_db')
2323 def add_deb_to_db(u, filename, session=None):
2325 Contrary to what you might expect, this routine deals with both
2326 debs and udebs. That info is in 'dbtype', whilst 'type' is
2327 'deb' for both of them
2330 entry = u.pkg.files[filename]
2333 bin.package = entry["package"]
2334 bin.version = entry["version"]
2335 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2336 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2337 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2338 bin.binarytype = entry["dbtype"]
2341 filename = entry["pool name"] + filename
2342 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2343 if not entry.get("location id", None):
2344 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2346 if entry.get("files id", None):
2347 poolfile = get_poolfile_by_id(bin.poolfile_id)
2348 bin.poolfile_id = entry["files id"]
2350 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2351 bin.poolfile_id = entry["files id"] = poolfile.file_id
2354 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2355 if len(bin_sources) != 1:
2356 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2357 (bin.package, bin.version, bin.architecture.arch_string,
2358 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2360 bin.source_id = bin_sources[0].source_id
2362 # Add and flush object so it has an ID
2366 # Add BinAssociations
2367 for suite_name in u.pkg.changes["distribution"].keys():
2368 ba = BinAssociation()
2369 ba.binary_id = bin.binary_id
2370 ba.suite_id = get_suite(suite_name).suite_id
2375 # Deal with contents - disabled for now
2376 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2378 # print "REJECT\nCould not determine contents of package %s" % bin.package
2379 # session.rollback()
2380 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2384 __all__.append('add_deb_to_db')
2386 ################################################################################
2388 class SourceACL(object):
2389 def __init__(self, *args, **kwargs):
2393 return '<SourceACL %s>' % self.source_acl_id
2395 __all__.append('SourceACL')
2397 ################################################################################
2399 class SrcAssociation(object):
2400 def __init__(self, *args, **kwargs):
2404 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2406 __all__.append('SrcAssociation')
2408 ################################################################################
2410 class SrcFormat(object):
2411 def __init__(self, *args, **kwargs):
2415 return '<SrcFormat %s>' % (self.format_name)
2417 __all__.append('SrcFormat')
2419 ################################################################################
2421 class SrcUploader(object):
2422 def __init__(self, *args, **kwargs):
2426 return '<SrcUploader %s>' % self.uploader_id
2428 __all__.append('SrcUploader')
2430 ################################################################################
2432 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2433 ('SuiteID', 'suite_id'),
2434 ('Version', 'version'),
2435 ('Origin', 'origin'),
2437 ('Description', 'description'),
2438 ('Untouchable', 'untouchable'),
2439 ('Announce', 'announce'),
2440 ('Codename', 'codename'),
2441 ('OverrideCodename', 'overridecodename'),
2442 ('ValidTime', 'validtime'),
2443 ('Priority', 'priority'),
2444 ('NotAutomatic', 'notautomatic'),
2445 ('CopyChanges', 'copychanges'),
2446 ('CopyDotDak', 'copydotdak'),
2447 ('CommentsDir', 'commentsdir'),
2448 ('OverrideSuite', 'overridesuite'),
2449 ('ChangelogBase', 'changelogbase')]
2452 class Suite(object):
2453 def __init__(self, *args, **kwargs):
2457 return '<Suite %s>' % self.suite_name
2459 def __eq__(self, val):
2460 if isinstance(val, str):
2461 return (self.suite_name == val)
2462 # This signals to use the normal comparison operator
2463 return NotImplemented
2465 def __ne__(self, val):
2466 if isinstance(val, str):
2467 return (self.suite_name != val)
2468 # This signals to use the normal comparison operator
2469 return NotImplemented
2473 for disp, field in SUITE_FIELDS:
2474 val = getattr(self, field, None)
2476 ret.append("%s: %s" % (disp, val))
2478 return "\n".join(ret)
2480 __all__.append('Suite')
2483 def get_suite_architecture(suite, architecture, session=None):
2485 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2489 @param suite: Suite name to search for
2491 @type architecture: str
2492 @param architecture: Architecture name to search for
2494 @type session: Session
2495 @param session: Optional SQL session object (a temporary one will be
2496 generated if not supplied)
2498 @rtype: SuiteArchitecture
2499 @return: the SuiteArchitecture object or None
2502 q = session.query(SuiteArchitecture)
2503 q = q.join(Architecture).filter_by(arch_string=architecture)
2504 q = q.join(Suite).filter_by(suite_name=suite)
2508 except NoResultFound:
2511 __all__.append('get_suite_architecture')
2514 def get_suite(suite, session=None):
2516 Returns Suite object for given C{suite name}.
2519 @param suite: The name of the suite
2521 @type session: Session
2522 @param session: Optional SQLA session object (a temporary one will be
2523 generated if not supplied)
2526 @return: Suite object for the requested suite name (None if not present)
2529 q = session.query(Suite).filter_by(suite_name=suite)
2533 except NoResultFound:
2536 __all__.append('get_suite')
2538 ################################################################################
2540 class SuiteArchitecture(object):
2541 def __init__(self, *args, **kwargs):
2545 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2547 __all__.append('SuiteArchitecture')
2550 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2552 Returns list of Architecture objects for given C{suite} name
2555 @param source: Suite name to search for
2557 @type skipsrc: boolean
2558 @param skipsrc: Whether to skip returning the 'source' architecture entry
2561 @type skipall: boolean
2562 @param skipall: Whether to skip returning the 'all' architecture entry
2565 @type session: Session
2566 @param session: Optional SQL session object (a temporary one will be
2567 generated if not supplied)
2570 @return: list of Architecture objects for the given name (may be empty)
2573 q = session.query(Architecture)
2574 q = q.join(SuiteArchitecture)
2575 q = q.join(Suite).filter_by(suite_name=suite)
2578 q = q.filter(Architecture.arch_string != 'source')
2581 q = q.filter(Architecture.arch_string != 'all')
2583 q = q.order_by('arch_string')
2587 __all__.append('get_suite_architectures')
2589 ################################################################################
2591 class SuiteSrcFormat(object):
2592 def __init__(self, *args, **kwargs):
2596 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2598 __all__.append('SuiteSrcFormat')
2601 def get_suite_src_formats(suite, session=None):
2603 Returns list of allowed SrcFormat for C{suite}.
2606 @param suite: Suite name to search for
2608 @type session: Session
2609 @param session: Optional SQL session object (a temporary one will be
2610 generated if not supplied)
2613 @return: the list of allowed source formats for I{suite}
2616 q = session.query(SrcFormat)
2617 q = q.join(SuiteSrcFormat)
2618 q = q.join(Suite).filter_by(suite_name=suite)
2619 q = q.order_by('format_name')
2623 __all__.append('get_suite_src_formats')
2625 ################################################################################
2628 def __init__(self, *args, **kwargs):
2631 def __eq__(self, val):
2632 if isinstance(val, str):
2633 return (self.uid == val)
2634 # This signals to use the normal comparison operator
2635 return NotImplemented
2637 def __ne__(self, val):
2638 if isinstance(val, str):
2639 return (self.uid != val)
2640 # This signals to use the normal comparison operator
2641 return NotImplemented
2644 return '<Uid %s (%s)>' % (self.uid, self.name)
2646 __all__.append('Uid')
2649 def add_database_user(uidname, session=None):
2651 Adds a database user
2653 @type uidname: string
2654 @param uidname: The uid of the user to add
2656 @type session: SQLAlchemy
2657 @param session: Optional SQL session object (a temporary one will be
2658 generated if not supplied). If not passed, a commit will be performed at
2659 the end of the function, otherwise the caller is responsible for commiting.
2662 @return: the uid object for the given uidname
2665 session.execute("CREATE USER :uid", {'uid': uidname})
2666 session.commit_or_flush()
2668 __all__.append('add_database_user')
2671 def get_or_set_uid(uidname, session=None):
2673 Returns uid object for given uidname.
2675 If no matching uidname is found, a row is inserted.
2677 @type uidname: string
2678 @param uidname: The uid to add
2680 @type session: SQLAlchemy
2681 @param session: Optional SQL session object (a temporary one will be
2682 generated if not supplied). If not passed, a commit will be performed at
2683 the end of the function, otherwise the caller is responsible for commiting.
2686 @return: the uid object for the given uidname
2689 q = session.query(Uid).filter_by(uid=uidname)
2693 except NoResultFound:
2697 session.commit_or_flush()
2702 __all__.append('get_or_set_uid')
2705 def get_uid_from_fingerprint(fpr, session=None):
2706 q = session.query(Uid)
2707 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2711 except NoResultFound:
2714 __all__.append('get_uid_from_fingerprint')
2716 ################################################################################
2718 class UploadBlock(object):
2719 def __init__(self, *args, **kwargs):
2723 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2725 __all__.append('UploadBlock')
2727 ################################################################################
2729 class DBConn(object):
2731 database module init.
2735 def __init__(self, *args, **kwargs):
2736 self.__dict__ = self.__shared_state
2738 if not getattr(self, 'initialised', False):
2739 self.initialised = True
2740 self.debug = kwargs.has_key('debug')
2743 def __setuptables(self):
2753 'build_queue_files',
2756 'changes_pending_binaries',
2757 'changes_pending_files',
2758 'changes_pending_files_map',
2759 'changes_pending_source',
2760 'changes_pending_source_files',
2761 'changes_pool_files',
2774 'pending_bin_contents',
2784 'suite_architectures',
2785 'suite_src_formats',
2786 'suite_build_queue_copy',
2792 for table_name in tables:
2793 table = Table(table_name, self.db_meta, autoload=True)
2794 setattr(self, 'tbl_%s' % table_name, table)
2796 def __setupmappers(self):
2797 mapper(Architecture, self.tbl_architecture,
2798 properties = dict(arch_id = self.tbl_architecture.c.id))
2800 mapper(Archive, self.tbl_archive,
2801 properties = dict(archive_id = self.tbl_archive.c.id,
2802 archive_name = self.tbl_archive.c.name))
2804 mapper(BinAssociation, self.tbl_bin_associations,
2805 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2806 suite_id = self.tbl_bin_associations.c.suite,
2807 suite = relation(Suite),
2808 binary_id = self.tbl_bin_associations.c.bin,
2809 binary = relation(DBBinary)))
2811 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2812 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2813 filename = self.tbl_pending_bin_contents.c.filename,
2814 package = self.tbl_pending_bin_contents.c.package,
2815 version = self.tbl_pending_bin_contents.c.version,
2816 arch = self.tbl_pending_bin_contents.c.arch,
2817 otype = self.tbl_pending_bin_contents.c.type))
2819 mapper(DebContents, self.tbl_deb_contents,
2820 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2821 package=self.tbl_deb_contents.c.package,
2822 component=self.tbl_deb_contents.c.component,
2823 arch=self.tbl_deb_contents.c.arch,
2824 section=self.tbl_deb_contents.c.section,
2825 filename=self.tbl_deb_contents.c.filename))
2827 mapper(UdebContents, self.tbl_udeb_contents,
2828 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2829 package=self.tbl_udeb_contents.c.package,
2830 component=self.tbl_udeb_contents.c.component,
2831 arch=self.tbl_udeb_contents.c.arch,
2832 section=self.tbl_udeb_contents.c.section,
2833 filename=self.tbl_udeb_contents.c.filename))
2835 mapper(DBBinary, self.tbl_binaries,
2836 properties = dict(binary_id = self.tbl_binaries.c.id,
2837 package = self.tbl_binaries.c.package,
2838 version = self.tbl_binaries.c.version,
2839 maintainer_id = self.tbl_binaries.c.maintainer,
2840 maintainer = relation(Maintainer),
2841 source_id = self.tbl_binaries.c.source,
2842 source = relation(DBSource),
2843 arch_id = self.tbl_binaries.c.architecture,
2844 architecture = relation(Architecture),
2845 poolfile_id = self.tbl_binaries.c.file,
2846 poolfile = relation(PoolFile),
2847 binarytype = self.tbl_binaries.c.type,
2848 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2849 fingerprint = relation(Fingerprint),
2850 install_date = self.tbl_binaries.c.install_date,
2851 binassociations = relation(BinAssociation,
2852 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2854 mapper(BinaryACL, self.tbl_binary_acl,
2855 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2857 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2858 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2859 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2860 architecture = relation(Architecture)))
2862 mapper(Component, self.tbl_component,
2863 properties = dict(component_id = self.tbl_component.c.id,
2864 component_name = self.tbl_component.c.name))
2866 mapper(DBConfig, self.tbl_config,
2867 properties = dict(config_id = self.tbl_config.c.id))
2869 mapper(DSCFile, self.tbl_dsc_files,
2870 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2871 source_id = self.tbl_dsc_files.c.source,
2872 source = relation(DBSource),
2873 poolfile_id = self.tbl_dsc_files.c.file,
2874 poolfile = relation(PoolFile)))
2876 mapper(PoolFile, self.tbl_files,
2877 properties = dict(file_id = self.tbl_files.c.id,
2878 filesize = self.tbl_files.c.size,
2879 location_id = self.tbl_files.c.location,
2880 location = relation(Location)))
2882 mapper(Fingerprint, self.tbl_fingerprint,
2883 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2884 uid_id = self.tbl_fingerprint.c.uid,
2885 uid = relation(Uid),
2886 keyring_id = self.tbl_fingerprint.c.keyring,
2887 keyring = relation(Keyring),
2888 source_acl = relation(SourceACL),
2889 binary_acl = relation(BinaryACL)))
2891 mapper(Keyring, self.tbl_keyrings,
2892 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2893 keyring_id = self.tbl_keyrings.c.id))
2895 mapper(DBChange, self.tbl_changes,
2896 properties = dict(change_id = self.tbl_changes.c.id,
2897 poolfiles = relation(PoolFile,
2898 secondary=self.tbl_changes_pool_files,
2899 backref="changeslinks"),
2900 seen = self.tbl_changes.c.seen,
2901 source = self.tbl_changes.c.source,
2902 binaries = self.tbl_changes.c.binaries,
2903 architecture = self.tbl_changes.c.architecture,
2904 distribution = self.tbl_changes.c.distribution,
2905 urgency = self.tbl_changes.c.urgency,
2906 maintainer = self.tbl_changes.c.maintainer,
2907 changedby = self.tbl_changes.c.changedby,
2908 date = self.tbl_changes.c.date,
2909 version = self.tbl_changes.c.version,
2910 files = relation(ChangePendingFile,
2911 secondary=self.tbl_changes_pending_files_map,
2912 backref="changesfile"),
2913 in_queue_id = self.tbl_changes.c.in_queue,
2914 in_queue = relation(PolicyQueue,
2915 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2916 approved_for_id = self.tbl_changes.c.approved_for))
2918 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2919 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2921 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2922 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
2923 filename = self.tbl_changes_pending_files.c.filename,
2924 size = self.tbl_changes_pending_files.c.size,
2925 md5sum = self.tbl_changes_pending_files.c.md5sum,
2926 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
2927 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
2929 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2930 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2931 change = relation(DBChange),
2932 maintainer = relation(Maintainer,
2933 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2934 changedby = relation(Maintainer,
2935 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2936 fingerprint = relation(Fingerprint),
2937 source_files = relation(ChangePendingFile,
2938 secondary=self.tbl_changes_pending_source_files,
2939 backref="pending_sources")))
2940 files = relation(KnownChangePendingFile, backref="changesfile")))
2942 mapper(KnownChangePendingFile, self.tbl_changes_pending_files,
2943 properties = dict(known_change_pending_file_id = self.tbl_changes_pending_files.c.id))
2945 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2946 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2947 keyring = relation(Keyring, backref="keyring_acl_map"),
2948 architecture = relation(Architecture)))
2950 mapper(Location, self.tbl_location,
2951 properties = dict(location_id = self.tbl_location.c.id,
2952 component_id = self.tbl_location.c.component,
2953 component = relation(Component),
2954 archive_id = self.tbl_location.c.archive,
2955 archive = relation(Archive),
2956 archive_type = self.tbl_location.c.type))
2958 mapper(Maintainer, self.tbl_maintainer,
2959 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2961 mapper(NewComment, self.tbl_new_comments,
2962 properties = dict(comment_id = self.tbl_new_comments.c.id))
2964 mapper(Override, self.tbl_override,
2965 properties = dict(suite_id = self.tbl_override.c.suite,
2966 suite = relation(Suite),
2967 package = self.tbl_override.c.package,
2968 component_id = self.tbl_override.c.component,
2969 component = relation(Component),
2970 priority_id = self.tbl_override.c.priority,
2971 priority = relation(Priority),
2972 section_id = self.tbl_override.c.section,
2973 section = relation(Section),
2974 overridetype_id = self.tbl_override.c.type,
2975 overridetype = relation(OverrideType)))
2977 mapper(OverrideType, self.tbl_override_type,
2978 properties = dict(overridetype = self.tbl_override_type.c.type,
2979 overridetype_id = self.tbl_override_type.c.id))
2981 mapper(PolicyQueue, self.tbl_policy_queue,
2982 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
2984 mapper(Priority, self.tbl_priority,
2985 properties = dict(priority_id = self.tbl_priority.c.id))
2987 mapper(Section, self.tbl_section,
2988 properties = dict(section_id = self.tbl_section.c.id,
2989 section=self.tbl_section.c.section))
2991 mapper(DBSource, self.tbl_source,
2992 properties = dict(source_id = self.tbl_source.c.id,
2993 version = self.tbl_source.c.version,
2994 maintainer_id = self.tbl_source.c.maintainer,
2995 maintainer = relation(Maintainer,
2996 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2997 poolfile_id = self.tbl_source.c.file,
2998 poolfile = relation(PoolFile),
2999 fingerprint_id = self.tbl_source.c.sig_fpr,
3000 fingerprint = relation(Fingerprint),
3001 changedby_id = self.tbl_source.c.changedby,
3002 changedby = relation(Maintainer,
3003 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3004 srcfiles = relation(DSCFile,
3005 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3006 srcassociations = relation(SrcAssociation,
3007 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3008 srcuploaders = relation(SrcUploader)))
3010 mapper(SourceACL, self.tbl_source_acl,
3011 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3013 mapper(SrcAssociation, self.tbl_src_associations,
3014 properties = dict(sa_id = self.tbl_src_associations.c.id,
3015 suite_id = self.tbl_src_associations.c.suite,
3016 suite = relation(Suite),
3017 source_id = self.tbl_src_associations.c.source,
3018 source = relation(DBSource)))
3020 mapper(SrcFormat, self.tbl_src_format,
3021 properties = dict(src_format_id = self.tbl_src_format.c.id,
3022 format_name = self.tbl_src_format.c.format_name))
3024 mapper(SrcUploader, self.tbl_src_uploaders,
3025 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3026 source_id = self.tbl_src_uploaders.c.source,
3027 source = relation(DBSource,
3028 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3029 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3030 maintainer = relation(Maintainer,
3031 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3033 mapper(Suite, self.tbl_suite,
3034 properties = dict(suite_id = self.tbl_suite.c.id,
3035 policy_queue = relation(PolicyQueue),
3036 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3038 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3039 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3040 suite = relation(Suite, backref='suitearchitectures'),
3041 arch_id = self.tbl_suite_architectures.c.architecture,
3042 architecture = relation(Architecture)))
3044 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3045 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3046 suite = relation(Suite, backref='suitesrcformats'),
3047 src_format_id = self.tbl_suite_src_formats.c.src_format,
3048 src_format = relation(SrcFormat)))
3050 mapper(Uid, self.tbl_uid,
3051 properties = dict(uid_id = self.tbl_uid.c.id,
3052 fingerprint = relation(Fingerprint)))
3054 mapper(UploadBlock, self.tbl_upload_blocks,
3055 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3056 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3057 uid = relation(Uid, backref="uploadblocks")))
3059 ## Connection functions
3060 def __createconn(self):
3061 from config import Config
3065 connstr = "postgres://%s" % cnf["DB::Host"]
3066 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3067 connstr += ":%s" % cnf["DB::Port"]
3068 connstr += "/%s" % cnf["DB::Name"]
3071 connstr = "postgres:///%s" % cnf["DB::Name"]
3072 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3073 connstr += "?port=%s" % cnf["DB::Port"]
3075 self.db_pg = create_engine(connstr, echo=self.debug)
3076 self.db_meta = MetaData()
3077 self.db_meta.bind = self.db_pg
3078 self.db_smaker = sessionmaker(bind=self.db_pg,
3082 self.__setuptables()
3083 self.__setupmappers()
3086 return self.db_smaker()
3088 __all__.append('DBConn')