5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009, 2010 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
41 from datetime import datetime, timedelta
42 from errno import ENOENT
43 from tempfile import mkstemp, mkdtemp
45 from inspect import getargspec
48 from sqlalchemy import create_engine, Table, MetaData, Column, Integer
49 from sqlalchemy.orm import sessionmaker, mapper, relation
50 from sqlalchemy import types as sqltypes
52 # Don't remove this, we re-export the exceptions to scripts which import us
53 from sqlalchemy.exc import *
54 from sqlalchemy.orm.exc import NoResultFound
56 # Only import Config until Queue stuff is changed to store its config
58 from config import Config
59 from textutils import fix_maintainer
60 from dak_exceptions import NoSourceFieldError
62 ################################################################################
64 # Patch in support for the debversion field type so that it works during
68 # that is for sqlalchemy 0.6
69 UserDefinedType = sqltypes.UserDefinedType
71 # this one for sqlalchemy 0.5
72 UserDefinedType = sqltypes.TypeEngine
74 class DebVersion(UserDefinedType):
75 def get_col_spec(self):
78 def bind_processor(self, dialect):
81 # ' = None' is needed for sqlalchemy 0.5:
82 def result_processor(self, dialect, coltype = None):
85 sa_major_version = sqlalchemy.__version__[0:3]
86 if sa_major_version in ["0.5", "0.6"]:
87 from sqlalchemy.databases import postgres
88 postgres.ischema_names['debversion'] = DebVersion
90 raise Exception("dak only ported to SQLA versions 0.5 and 0.6. See daklib/dbconn.py")
92 ################################################################################
94 __all__ = ['IntegrityError', 'SQLAlchemyError', 'DebVersion']
96 ################################################################################
98 def session_wrapper(fn):
100 Wrapper around common ".., session=None):" handling. If the wrapped
101 function is called without passing 'session', we create a local one
102 and destroy it when the function ends.
104 Also attaches a commit_or_flush method to the session; if we created a
105 local session, this is a synonym for session.commit(), otherwise it is a
106 synonym for session.flush().
109 def wrapped(*args, **kwargs):
110 private_transaction = False
112 # Find the session object
113 session = kwargs.get('session')
116 if len(args) <= len(getargspec(fn)[0]) - 1:
117 # No session specified as last argument or in kwargs
118 private_transaction = True
119 session = kwargs['session'] = DBConn().session()
121 # Session is last argument in args
125 session = args[-1] = DBConn().session()
126 private_transaction = True
128 if private_transaction:
129 session.commit_or_flush = session.commit
131 session.commit_or_flush = session.flush
134 return fn(*args, **kwargs)
136 if private_transaction:
137 # We created a session; close it.
140 wrapped.__doc__ = fn.__doc__
141 wrapped.func_name = fn.func_name
145 __all__.append('session_wrapper')
147 ################################################################################
149 class Architecture(object):
150 def __init__(self, arch_string = None, description = None):
151 self.arch_string = arch_string
152 self.description = description
154 def __eq__(self, val):
155 if isinstance(val, str):
156 return (self.arch_string== val)
157 # This signals to use the normal comparison operator
158 return NotImplemented
160 def __ne__(self, val):
161 if isinstance(val, str):
162 return (self.arch_string != val)
163 # This signals to use the normal comparison operator
164 return NotImplemented
167 return '<Architecture %s>' % self.arch_string
169 __all__.append('Architecture')
172 def get_architecture(architecture, session=None):
174 Returns database id for given C{architecture}.
176 @type architecture: string
177 @param architecture: The name of the architecture
179 @type session: Session
180 @param session: Optional SQLA session object (a temporary one will be
181 generated if not supplied)
184 @return: Architecture object for the given arch (None if not present)
187 q = session.query(Architecture).filter_by(arch_string=architecture)
191 except NoResultFound:
194 __all__.append('get_architecture')
197 def get_architecture_suites(architecture, session=None):
199 Returns list of Suite objects for given C{architecture} name
201 @type architecture: str
202 @param architecture: Architecture name to search for
204 @type session: Session
205 @param session: Optional SQL session object (a temporary one will be
206 generated if not supplied)
209 @return: list of Suite objects for the given name (may be empty)
212 q = session.query(Suite)
213 q = q.join(SuiteArchitecture)
214 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
220 __all__.append('get_architecture_suites')
222 ################################################################################
224 class Archive(object):
225 def __init__(self, *args, **kwargs):
229 return '<Archive %s>' % self.archive_name
231 __all__.append('Archive')
234 def get_archive(archive, session=None):
236 returns database id for given C{archive}.
238 @type archive: string
239 @param archive: the name of the arhive
241 @type session: Session
242 @param session: Optional SQLA session object (a temporary one will be
243 generated if not supplied)
246 @return: Archive object for the given name (None if not present)
249 archive = archive.lower()
251 q = session.query(Archive).filter_by(archive_name=archive)
255 except NoResultFound:
258 __all__.append('get_archive')
260 ################################################################################
262 class BinAssociation(object):
263 def __init__(self, *args, **kwargs):
267 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
269 __all__.append('BinAssociation')
271 ################################################################################
273 class BinContents(object):
274 def __init__(self, *args, **kwargs):
278 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
280 __all__.append('BinContents')
282 ################################################################################
284 class DBBinary(object):
285 def __init__(self, *args, **kwargs):
289 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
291 __all__.append('DBBinary')
294 def get_suites_binary_in(package, session=None):
296 Returns list of Suite objects which given C{package} name is in
299 @param package: DBBinary package name to search for
302 @return: list of Suite objects for the given package
305 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
307 __all__.append('get_suites_binary_in')
310 def get_binary_from_id(binary_id, session=None):
312 Returns DBBinary object for given C{id}
315 @param binary_id: Id of the required binary
317 @type session: Session
318 @param session: Optional SQLA session object (a temporary one will be
319 generated if not supplied)
322 @return: DBBinary object for the given binary (None if not present)
325 q = session.query(DBBinary).filter_by(binary_id=binary_id)
329 except NoResultFound:
332 __all__.append('get_binary_from_id')
335 def get_binaries_from_name(package, version=None, architecture=None, session=None):
337 Returns list of DBBinary objects for given C{package} name
340 @param package: DBBinary package name to search for
342 @type version: str or None
343 @param version: Version to search for (or None)
345 @type architecture: str, list or None
346 @param architecture: Architectures to limit to (or None if no limit)
348 @type session: Session
349 @param session: Optional SQL session object (a temporary one will be
350 generated if not supplied)
353 @return: list of DBBinary objects for the given name (may be empty)
356 q = session.query(DBBinary).filter_by(package=package)
358 if version is not None:
359 q = q.filter_by(version=version)
361 if architecture is not None:
362 if not isinstance(architecture, list):
363 architecture = [architecture]
364 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
370 __all__.append('get_binaries_from_name')
373 def get_binaries_from_source_id(source_id, session=None):
375 Returns list of DBBinary objects for given C{source_id}
378 @param source_id: source_id to search for
380 @type session: Session
381 @param session: Optional SQL session object (a temporary one will be
382 generated if not supplied)
385 @return: list of DBBinary objects for the given name (may be empty)
388 return session.query(DBBinary).filter_by(source_id=source_id).all()
390 __all__.append('get_binaries_from_source_id')
393 def get_binary_from_name_suite(package, suitename, session=None):
394 ### For dak examine-package
395 ### XXX: Doesn't use object API yet
397 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
398 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
399 WHERE b.package='%(package)s'
401 AND fi.location = l.id
402 AND l.component = c.id
405 AND su.suite_name %(suitename)s
406 ORDER BY b.version DESC"""
408 return session.execute(sql % {'package': package, 'suitename': suitename})
410 __all__.append('get_binary_from_name_suite')
413 def get_binary_components(package, suitename, arch, session=None):
414 # Check for packages that have moved from one component to another
415 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
416 WHERE b.package=:package AND s.suite_name=:suitename
417 AND (a.arch_string = :arch OR a.arch_string = 'all')
418 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
419 AND f.location = l.id
420 AND l.component = c.id
423 vals = {'package': package, 'suitename': suitename, 'arch': arch}
425 return session.execute(query, vals)
427 __all__.append('get_binary_components')
429 ################################################################################
431 class BinaryACL(object):
432 def __init__(self, *args, **kwargs):
436 return '<BinaryACL %s>' % self.binary_acl_id
438 __all__.append('BinaryACL')
440 ################################################################################
442 class BinaryACLMap(object):
443 def __init__(self, *args, **kwargs):
447 return '<BinaryACLMap %s>' % self.binary_acl_map_id
449 __all__.append('BinaryACLMap')
451 ################################################################################
456 ArchiveDir "%(archivepath)s";
457 OverrideDir "%(overridedir)s";
458 CacheDir "%(cachedir)s";
463 Packages::Compress ". bzip2 gzip";
464 Sources::Compress ". bzip2 gzip";
469 bindirectory "incoming"
474 BinOverride "override.sid.all3";
475 BinCacheDB "packages-accepted.db";
477 FileList "%(filelist)s";
480 Packages::Extensions ".deb .udeb";
483 bindirectory "incoming/"
486 BinOverride "override.sid.all3";
487 SrcOverride "override.sid.all3.src";
488 FileList "%(filelist)s";
492 class BuildQueue(object):
493 def __init__(self, *args, **kwargs):
497 return '<BuildQueue %s>' % self.queue_name
499 def write_metadata(self, starttime, force=False):
500 # Do we write out metafiles?
501 if not (force or self.generate_metadata):
504 session = DBConn().session().object_session(self)
506 fl_fd = fl_name = ac_fd = ac_name = None
508 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
509 startdir = os.getcwd()
512 # Grab files we want to include
513 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
514 # Write file list with newer files
515 (fl_fd, fl_name) = mkstemp()
517 os.write(fl_fd, '%s\n' % n.fullpath)
522 # Write minimal apt.conf
523 # TODO: Remove hardcoding from template
524 (ac_fd, ac_name) = mkstemp()
525 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
527 'cachedir': cnf["Dir::Cache"],
528 'overridedir': cnf["Dir::Override"],
532 # Run apt-ftparchive generate
533 os.chdir(os.path.dirname(ac_name))
534 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
536 # Run apt-ftparchive release
537 # TODO: Eww - fix this
538 bname = os.path.basename(self.path)
542 # We have to remove the Release file otherwise it'll be included in the
545 os.unlink(os.path.join(bname, 'Release'))
549 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))
551 # Crude hack with open and append, but this whole section is and should be redone.
552 if self.notautomatic:
553 release=open("Release", "a")
554 release.write("NotAutomatic: yes")
559 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
560 if cnf.has_key("Dinstall::SigningPubKeyring"):
561 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
563 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
565 # Move the files if we got this far
566 os.rename('Release', os.path.join(bname, 'Release'))
568 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
570 # Clean up any left behind files
597 def clean_and_update(self, starttime, Logger, dryrun=False):
598 """WARNING: This routine commits for you"""
599 session = DBConn().session().object_session(self)
601 if self.generate_metadata and not dryrun:
602 self.write_metadata(starttime)
604 # Grab files older than our execution time
605 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
611 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
613 Logger.log(["I: Removing %s from the queue" % o.fullpath])
614 os.unlink(o.fullpath)
617 # If it wasn't there, don't worry
618 if e.errno == ENOENT:
621 # TODO: Replace with proper logging call
622 Logger.log(["E: Could not remove %s" % o.fullpath])
629 for f in os.listdir(self.path):
630 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release') or f.startswith('advisory'):
634 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
635 except NoResultFound:
636 fp = os.path.join(self.path, f)
638 Logger.log(["I: Would remove unused link %s" % fp])
640 Logger.log(["I: Removing unused link %s" % fp])
644 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
646 def add_file_from_pool(self, poolfile):
647 """Copies a file into the pool. Assumes that the PoolFile object is
648 attached to the same SQLAlchemy session as the Queue object is.
650 The caller is responsible for committing after calling this function."""
651 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
653 # Check if we have a file of this name or this ID already
654 for f in self.queuefiles:
655 if f.fileid is not None and f.fileid == poolfile.file_id or \
656 f.poolfile.filename == poolfile_basename:
657 # In this case, update the BuildQueueFile entry so we
658 # don't remove it too early
659 f.lastused = datetime.now()
660 DBConn().session().object_session(poolfile).add(f)
663 # Prepare BuildQueueFile object
664 qf = BuildQueueFile()
665 qf.build_queue_id = self.queue_id
666 qf.lastused = datetime.now()
667 qf.filename = poolfile_basename
669 targetpath = poolfile.fullpath
670 queuepath = os.path.join(self.path, poolfile_basename)
674 # We need to copy instead of symlink
676 utils.copy(targetpath, queuepath)
677 # NULL in the fileid field implies a copy
680 os.symlink(targetpath, queuepath)
681 qf.fileid = poolfile.file_id
685 # Get the same session as the PoolFile is using and add the qf to it
686 DBConn().session().object_session(poolfile).add(qf)
691 __all__.append('BuildQueue')
694 def get_build_queue(queuename, session=None):
696 Returns BuildQueue object for given C{queue name}, creating it if it does not
699 @type queuename: string
700 @param queuename: The name of the queue
702 @type session: Session
703 @param session: Optional SQLA session object (a temporary one will be
704 generated if not supplied)
707 @return: BuildQueue object for the given queue
710 q = session.query(BuildQueue).filter_by(queue_name=queuename)
714 except NoResultFound:
717 __all__.append('get_build_queue')
719 ################################################################################
721 class BuildQueueFile(object):
722 def __init__(self, *args, **kwargs):
726 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
730 return os.path.join(self.buildqueue.path, self.filename)
733 __all__.append('BuildQueueFile')
735 ################################################################################
737 class ChangePendingBinary(object):
738 def __init__(self, *args, **kwargs):
742 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
744 __all__.append('ChangePendingBinary')
746 ################################################################################
748 class ChangePendingFile(object):
749 def __init__(self, *args, **kwargs):
753 return '<ChangePendingFile %s>' % self.change_pending_file_id
755 __all__.append('ChangePendingFile')
757 ################################################################################
759 class ChangePendingSource(object):
760 def __init__(self, *args, **kwargs):
764 return '<ChangePendingSource %s>' % self.change_pending_source_id
766 __all__.append('ChangePendingSource')
768 ################################################################################
770 class Component(object):
771 def __init__(self, *args, **kwargs):
774 def __eq__(self, val):
775 if isinstance(val, str):
776 return (self.component_name == val)
777 # This signals to use the normal comparison operator
778 return NotImplemented
780 def __ne__(self, val):
781 if isinstance(val, str):
782 return (self.component_name != val)
783 # This signals to use the normal comparison operator
784 return NotImplemented
787 return '<Component %s>' % self.component_name
790 __all__.append('Component')
793 def get_component(component, session=None):
795 Returns database id for given C{component}.
797 @type component: string
798 @param component: The name of the override type
801 @return: the database id for the given component
804 component = component.lower()
806 q = session.query(Component).filter_by(component_name=component)
810 except NoResultFound:
813 __all__.append('get_component')
815 ################################################################################
817 class DBConfig(object):
818 def __init__(self, *args, **kwargs):
822 return '<DBConfig %s>' % self.name
824 __all__.append('DBConfig')
826 ################################################################################
829 def get_or_set_contents_file_id(filename, session=None):
831 Returns database id for given filename.
833 If no matching file is found, a row is inserted.
835 @type filename: string
836 @param filename: The filename
837 @type session: SQLAlchemy
838 @param session: Optional SQL session object (a temporary one will be
839 generated if not supplied). If not passed, a commit will be performed at
840 the end of the function, otherwise the caller is responsible for commiting.
843 @return: the database id for the given component
846 q = session.query(ContentFilename).filter_by(filename=filename)
849 ret = q.one().cafilename_id
850 except NoResultFound:
851 cf = ContentFilename()
852 cf.filename = filename
854 session.commit_or_flush()
855 ret = cf.cafilename_id
859 __all__.append('get_or_set_contents_file_id')
862 def get_contents(suite, overridetype, section=None, session=None):
864 Returns contents for a suite / overridetype combination, limiting
865 to a section if not None.
868 @param suite: Suite object
870 @type overridetype: OverrideType
871 @param overridetype: OverrideType object
873 @type section: Section
874 @param section: Optional section object to limit results to
876 @type session: SQLAlchemy
877 @param session: Optional SQL session object (a temporary one will be
878 generated if not supplied)
881 @return: ResultsProxy object set up to return tuples of (filename, section,
885 # find me all of the contents for a given suite
886 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
890 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
891 JOIN content_file_names n ON (c.filename=n.id)
892 JOIN binaries b ON (b.id=c.binary_pkg)
893 JOIN override o ON (o.package=b.package)
894 JOIN section s ON (s.id=o.section)
895 WHERE o.suite = :suiteid AND o.type = :overridetypeid
896 AND b.type=:overridetypename"""
898 vals = {'suiteid': suite.suite_id,
899 'overridetypeid': overridetype.overridetype_id,
900 'overridetypename': overridetype.overridetype}
902 if section is not None:
903 contents_q += " AND s.id = :sectionid"
904 vals['sectionid'] = section.section_id
906 contents_q += " ORDER BY fn"
908 return session.execute(contents_q, vals)
910 __all__.append('get_contents')
912 ################################################################################
914 class ContentFilepath(object):
915 def __init__(self, *args, **kwargs):
919 return '<ContentFilepath %s>' % self.filepath
921 __all__.append('ContentFilepath')
924 def get_or_set_contents_path_id(filepath, session=None):
926 Returns database id for given path.
928 If no matching file is found, a row is inserted.
930 @type filepath: string
931 @param filepath: The filepath
933 @type session: SQLAlchemy
934 @param session: Optional SQL session object (a temporary one will be
935 generated if not supplied). If not passed, a commit will be performed at
936 the end of the function, otherwise the caller is responsible for commiting.
939 @return: the database id for the given path
942 q = session.query(ContentFilepath).filter_by(filepath=filepath)
945 ret = q.one().cafilepath_id
946 except NoResultFound:
947 cf = ContentFilepath()
948 cf.filepath = filepath
950 session.commit_or_flush()
951 ret = cf.cafilepath_id
955 __all__.append('get_or_set_contents_path_id')
957 ################################################################################
959 class ContentAssociation(object):
960 def __init__(self, *args, **kwargs):
964 return '<ContentAssociation %s>' % self.ca_id
966 __all__.append('ContentAssociation')
968 def insert_content_paths(binary_id, fullpaths, session=None):
970 Make sure given path is associated with given binary id
973 @param binary_id: the id of the binary
974 @type fullpaths: list
975 @param fullpaths: the list of paths of the file being associated with the binary
976 @type session: SQLAlchemy session
977 @param session: Optional SQLAlchemy session. If this is passed, the caller
978 is responsible for ensuring a transaction has begun and committing the
979 results or rolling back based on the result code. If not passed, a commit
980 will be performed at the end of the function, otherwise the caller is
981 responsible for commiting.
983 @return: True upon success
988 session = DBConn().session()
993 def generate_path_dicts():
994 for fullpath in fullpaths:
995 if fullpath.startswith( './' ):
996 fullpath = fullpath[2:]
998 yield {'filename':fullpath, 'id': binary_id }
1000 for d in generate_path_dicts():
1001 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
1010 traceback.print_exc()
1012 # Only rollback if we set up the session ourself
1019 __all__.append('insert_content_paths')
1021 ################################################################################
1023 class DSCFile(object):
1024 def __init__(self, *args, **kwargs):
1028 return '<DSCFile %s>' % self.dscfile_id
1030 __all__.append('DSCFile')
1033 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1035 Returns a list of DSCFiles which may be empty
1037 @type dscfile_id: int (optional)
1038 @param dscfile_id: the dscfile_id of the DSCFiles to find
1040 @type source_id: int (optional)
1041 @param source_id: the source id related to the DSCFiles to find
1043 @type poolfile_id: int (optional)
1044 @param poolfile_id: the poolfile id related to the DSCFiles to find
1047 @return: Possibly empty list of DSCFiles
1050 q = session.query(DSCFile)
1052 if dscfile_id is not None:
1053 q = q.filter_by(dscfile_id=dscfile_id)
1055 if source_id is not None:
1056 q = q.filter_by(source_id=source_id)
1058 if poolfile_id is not None:
1059 q = q.filter_by(poolfile_id=poolfile_id)
1063 __all__.append('get_dscfiles')
1065 ################################################################################
1067 class PoolFile(object):
1068 def __init__(self, *args, **kwargs):
1072 return '<PoolFile %s>' % self.filename
1076 return os.path.join(self.location.path, self.filename)
1078 __all__.append('PoolFile')
1081 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1084 (ValidFileFound [boolean or None], PoolFile object or None)
1086 @type filename: string
1087 @param filename: the filename of the file to check against the DB
1090 @param filesize: the size of the file to check against the DB
1092 @type md5sum: string
1093 @param md5sum: the md5sum of the file to check against the DB
1095 @type location_id: int
1096 @param location_id: the id of the location to look in
1099 @return: Tuple of length 2.
1100 - If more than one file found with that name: (C{None}, C{None})
1101 - If valid pool file found: (C{True}, C{PoolFile object})
1102 - If valid pool file not found:
1103 - (C{False}, C{None}) if no file found
1104 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1107 q = session.query(PoolFile).filter_by(filename=filename)
1108 q = q.join(Location).filter_by(location_id=location_id)
1118 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1126 __all__.append('check_poolfile')
1129 def get_poolfile_by_id(file_id, session=None):
1131 Returns a PoolFile objects or None for the given id
1134 @param file_id: the id of the file to look for
1136 @rtype: PoolFile or None
1137 @return: either the PoolFile object or None
1140 q = session.query(PoolFile).filter_by(file_id=file_id)
1144 except NoResultFound:
1147 __all__.append('get_poolfile_by_id')
1151 def get_poolfile_by_name(filename, location_id=None, session=None):
1153 Returns an array of PoolFile objects for the given filename and
1154 (optionally) location_id
1156 @type filename: string
1157 @param filename: the filename of the file to check against the DB
1159 @type location_id: int
1160 @param location_id: the id of the location to look in (optional)
1163 @return: array of PoolFile objects
1166 q = session.query(PoolFile).filter_by(filename=filename)
1168 if location_id is not None:
1169 q = q.join(Location).filter_by(location_id=location_id)
1173 __all__.append('get_poolfile_by_name')
1176 def get_poolfile_like_name(filename, session=None):
1178 Returns an array of PoolFile objects which are like the given name
1180 @type filename: string
1181 @param filename: the filename of the file to check against the DB
1184 @return: array of PoolFile objects
1187 # TODO: There must be a way of properly using bind parameters with %FOO%
1188 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1192 __all__.append('get_poolfile_like_name')
1195 def add_poolfile(filename, datadict, location_id, session=None):
1197 Add a new file to the pool
1199 @type filename: string
1200 @param filename: filename
1202 @type datadict: dict
1203 @param datadict: dict with needed data
1205 @type location_id: int
1206 @param location_id: database id of the location
1209 @return: the PoolFile object created
1211 poolfile = PoolFile()
1212 poolfile.filename = filename
1213 poolfile.filesize = datadict["size"]
1214 poolfile.md5sum = datadict["md5sum"]
1215 poolfile.sha1sum = datadict["sha1sum"]
1216 poolfile.sha256sum = datadict["sha256sum"]
1217 poolfile.location_id = location_id
1219 session.add(poolfile)
1220 # Flush to get a file id (NB: This is not a commit)
1225 __all__.append('add_poolfile')
1227 ################################################################################
1229 class Fingerprint(object):
1230 def __init__(self, fingerprint = None):
1231 self.fingerprint = fingerprint
1234 return '<Fingerprint %s>' % self.fingerprint
1236 __all__.append('Fingerprint')
1239 def get_fingerprint(fpr, session=None):
1241 Returns Fingerprint object for given fpr.
1244 @param fpr: The fpr to find / add
1246 @type session: SQLAlchemy
1247 @param session: Optional SQL session object (a temporary one will be
1248 generated if not supplied).
1251 @return: the Fingerprint object for the given fpr or None
1254 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1258 except NoResultFound:
1263 __all__.append('get_fingerprint')
1266 def get_or_set_fingerprint(fpr, session=None):
1268 Returns Fingerprint object for given fpr.
1270 If no matching fpr is found, a row is inserted.
1273 @param fpr: The fpr to find / add
1275 @type session: SQLAlchemy
1276 @param session: Optional SQL session object (a temporary one will be
1277 generated if not supplied). If not passed, a commit will be performed at
1278 the end of the function, otherwise the caller is responsible for commiting.
1279 A flush will be performed either way.
1282 @return: the Fingerprint object for the given fpr
1285 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1289 except NoResultFound:
1290 fingerprint = Fingerprint()
1291 fingerprint.fingerprint = fpr
1292 session.add(fingerprint)
1293 session.commit_or_flush()
1298 __all__.append('get_or_set_fingerprint')
1300 ################################################################################
1302 # Helper routine for Keyring class
1303 def get_ldap_name(entry):
1305 for k in ["cn", "mn", "sn"]:
1307 if ret and ret[0] != "" and ret[0] != "-":
1309 return " ".join(name)
1311 ################################################################################
1313 class Keyring(object):
1314 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1315 " --with-colons --fingerprint --fingerprint"
1320 def __init__(self, *args, **kwargs):
1324 return '<Keyring %s>' % self.keyring_name
1326 def de_escape_gpg_str(self, txt):
1327 esclist = re.split(r'(\\x..)', txt)
1328 for x in range(1,len(esclist),2):
1329 esclist[x] = "%c" % (int(esclist[x][2:],16))
1330 return "".join(esclist)
1332 def parse_address(self, uid):
1333 """parses uid and returns a tuple of real name and email address"""
1335 (name, address) = email.Utils.parseaddr(uid)
1336 name = re.sub(r"\s*[(].*[)]", "", name)
1337 name = self.de_escape_gpg_str(name)
1340 return (name, address)
1342 def load_keys(self, keyring):
1343 if not self.keyring_id:
1344 raise Exception('Must be initialized with database information')
1346 k = os.popen(self.gpg_invocation % keyring, "r")
1350 for line in k.xreadlines():
1351 field = line.split(":")
1352 if field[0] == "pub":
1355 (name, addr) = self.parse_address(field[9])
1357 self.keys[key]["email"] = addr
1358 self.keys[key]["name"] = name
1359 self.keys[key]["fingerprints"] = []
1361 elif key and field[0] == "sub" and len(field) >= 12:
1362 signingkey = ("s" in field[11])
1363 elif key and field[0] == "uid":
1364 (name, addr) = self.parse_address(field[9])
1365 if "email" not in self.keys[key] and "@" in addr:
1366 self.keys[key]["email"] = addr
1367 self.keys[key]["name"] = name
1368 elif signingkey and field[0] == "fpr":
1369 self.keys[key]["fingerprints"].append(field[9])
1370 self.fpr_lookup[field[9]] = key
1372 def import_users_from_ldap(self, session):
1376 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1377 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1379 l = ldap.open(LDAPServer)
1380 l.simple_bind_s("","")
1381 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1382 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1383 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1385 ldap_fin_uid_id = {}
1392 uid = entry["uid"][0]
1393 name = get_ldap_name(entry)
1394 fingerprints = entry["keyFingerPrint"]
1396 for f in fingerprints:
1397 key = self.fpr_lookup.get(f, None)
1398 if key not in self.keys:
1400 self.keys[key]["uid"] = uid
1404 keyid = get_or_set_uid(uid, session).uid_id
1405 byuid[keyid] = (uid, name)
1406 byname[uid] = (keyid, name)
1408 return (byname, byuid)
1410 def generate_users_from_keyring(self, format, session):
1414 for x in self.keys.keys():
1415 if "email" not in self.keys[x]:
1417 self.keys[x]["uid"] = format % "invalid-uid"
1419 uid = format % self.keys[x]["email"]
1420 keyid = get_or_set_uid(uid, session).uid_id
1421 byuid[keyid] = (uid, self.keys[x]["name"])
1422 byname[uid] = (keyid, self.keys[x]["name"])
1423 self.keys[x]["uid"] = uid
1426 uid = format % "invalid-uid"
1427 keyid = get_or_set_uid(uid, session).uid_id
1428 byuid[keyid] = (uid, "ungeneratable user id")
1429 byname[uid] = (keyid, "ungeneratable user id")
1431 return (byname, byuid)
1433 __all__.append('Keyring')
1436 def get_keyring(keyring, session=None):
1438 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1439 If C{keyring} already has an entry, simply return the existing Keyring
1441 @type keyring: string
1442 @param keyring: the keyring name
1445 @return: the Keyring object for this keyring
1448 q = session.query(Keyring).filter_by(keyring_name=keyring)
1452 except NoResultFound:
1455 __all__.append('get_keyring')
1457 ################################################################################
1459 class KeyringACLMap(object):
1460 def __init__(self, *args, **kwargs):
1464 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1466 __all__.append('KeyringACLMap')
1468 ################################################################################
1470 class DBChange(object):
1471 def __init__(self, *args, **kwargs):
1475 return '<DBChange %s>' % self.changesname
1477 def clean_from_queue(self):
1478 session = DBConn().session().object_session(self)
1480 # Remove changes_pool_files entries
1483 # Remove changes_pending_files references
1486 # Clear out of queue
1487 self.in_queue = None
1488 self.approved_for_id = None
1490 __all__.append('DBChange')
1493 def get_dbchange(filename, session=None):
1495 returns DBChange object for given C{filename}.
1497 @type filename: string
1498 @param filename: the name of the file
1500 @type session: Session
1501 @param session: Optional SQLA session object (a temporary one will be
1502 generated if not supplied)
1505 @return: DBChange object for the given filename (C{None} if not present)
1508 q = session.query(DBChange).filter_by(changesname=filename)
1512 except NoResultFound:
1515 __all__.append('get_dbchange')
1517 ################################################################################
1519 class Location(object):
1520 def __init__(self, *args, **kwargs):
1524 return '<Location %s (%s)>' % (self.path, self.location_id)
1526 __all__.append('Location')
1529 def get_location(location, component=None, archive=None, session=None):
1531 Returns Location object for the given combination of location, component
1534 @type location: string
1535 @param location: the path of the location, e.g. I{/srv/ftp-master.debian.org/ftp/pool/}
1537 @type component: string
1538 @param component: the component name (if None, no restriction applied)
1540 @type archive: string
1541 @param archive: the archive name (if None, no restriction applied)
1543 @rtype: Location / None
1544 @return: Either a Location object or None if one can't be found
1547 q = session.query(Location).filter_by(path=location)
1549 if archive is not None:
1550 q = q.join(Archive).filter_by(archive_name=archive)
1552 if component is not None:
1553 q = q.join(Component).filter_by(component_name=component)
1557 except NoResultFound:
1560 __all__.append('get_location')
1562 ################################################################################
1564 class Maintainer(object):
1565 def __init__(self, *args, **kwargs):
1569 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1571 def get_split_maintainer(self):
1572 if not hasattr(self, 'name') or self.name is None:
1573 return ('', '', '', '')
1575 return fix_maintainer(self.name.strip())
1577 __all__.append('Maintainer')
1580 def get_or_set_maintainer(name, session=None):
1582 Returns Maintainer object for given maintainer name.
1584 If no matching maintainer name is found, a row is inserted.
1587 @param name: The maintainer name to add
1589 @type session: SQLAlchemy
1590 @param session: Optional SQL session object (a temporary one will be
1591 generated if not supplied). If not passed, a commit will be performed at
1592 the end of the function, otherwise the caller is responsible for commiting.
1593 A flush will be performed either way.
1596 @return: the Maintainer object for the given maintainer
1599 q = session.query(Maintainer).filter_by(name=name)
1602 except NoResultFound:
1603 maintainer = Maintainer()
1604 maintainer.name = name
1605 session.add(maintainer)
1606 session.commit_or_flush()
1611 __all__.append('get_or_set_maintainer')
1614 def get_maintainer(maintainer_id, session=None):
1616 Return the name of the maintainer behind C{maintainer_id} or None if that
1617 maintainer_id is invalid.
1619 @type maintainer_id: int
1620 @param maintainer_id: the id of the maintainer
1623 @return: the Maintainer with this C{maintainer_id}
1626 return session.query(Maintainer).get(maintainer_id)
1628 __all__.append('get_maintainer')
1630 ################################################################################
1632 class NewComment(object):
1633 def __init__(self, *args, **kwargs):
1637 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1639 __all__.append('NewComment')
1642 def has_new_comment(package, version, session=None):
1644 Returns true if the given combination of C{package}, C{version} has a comment.
1646 @type package: string
1647 @param package: name of the package
1649 @type version: string
1650 @param version: package version
1652 @type session: Session
1653 @param session: Optional SQLA session object (a temporary one will be
1654 generated if not supplied)
1660 q = session.query(NewComment)
1661 q = q.filter_by(package=package)
1662 q = q.filter_by(version=version)
1664 return bool(q.count() > 0)
1666 __all__.append('has_new_comment')
1669 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1671 Returns (possibly empty) list of NewComment objects for the given
1674 @type package: string (optional)
1675 @param package: name of the package
1677 @type version: string (optional)
1678 @param version: package version
1680 @type comment_id: int (optional)
1681 @param comment_id: An id of a comment
1683 @type session: Session
1684 @param session: Optional SQLA session object (a temporary one will be
1685 generated if not supplied)
1688 @return: A (possibly empty) list of NewComment objects will be returned
1691 q = session.query(NewComment)
1692 if package is not None: q = q.filter_by(package=package)
1693 if version is not None: q = q.filter_by(version=version)
1694 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1698 __all__.append('get_new_comments')
1700 ################################################################################
1702 class Override(object):
1703 def __init__(self, *args, **kwargs):
1707 return '<Override %s (%s)>' % (self.package, self.suite_id)
1709 __all__.append('Override')
1712 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1714 Returns Override object for the given parameters
1716 @type package: string
1717 @param package: The name of the package
1719 @type suite: string, list or None
1720 @param suite: The name of the suite (or suites if a list) to limit to. If
1721 None, don't limit. Defaults to None.
1723 @type component: string, list or None
1724 @param component: The name of the component (or components if a list) to
1725 limit to. If None, don't limit. Defaults to None.
1727 @type overridetype: string, list or None
1728 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1729 limit to. If None, don't limit. Defaults to None.
1731 @type session: Session
1732 @param session: Optional SQLA session object (a temporary one will be
1733 generated if not supplied)
1736 @return: A (possibly empty) list of Override objects will be returned
1739 q = session.query(Override)
1740 q = q.filter_by(package=package)
1742 if suite is not None:
1743 if not isinstance(suite, list): suite = [suite]
1744 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1746 if component is not None:
1747 if not isinstance(component, list): component = [component]
1748 q = q.join(Component).filter(Component.component_name.in_(component))
1750 if overridetype is not None:
1751 if not isinstance(overridetype, list): overridetype = [overridetype]
1752 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1756 __all__.append('get_override')
1759 ################################################################################
1761 class OverrideType(object):
1762 def __init__(self, *args, **kwargs):
1766 return '<OverrideType %s>' % self.overridetype
1768 __all__.append('OverrideType')
1771 def get_override_type(override_type, session=None):
1773 Returns OverrideType object for given C{override type}.
1775 @type override_type: string
1776 @param override_type: The name of the override type
1778 @type session: Session
1779 @param session: Optional SQLA session object (a temporary one will be
1780 generated if not supplied)
1783 @return: the database id for the given override type
1786 q = session.query(OverrideType).filter_by(overridetype=override_type)
1790 except NoResultFound:
1793 __all__.append('get_override_type')
1795 ################################################################################
1797 class DebContents(object):
1798 def __init__(self, *args, **kwargs):
1802 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1804 __all__.append('DebContents')
1807 class UdebContents(object):
1808 def __init__(self, *args, **kwargs):
1812 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1814 __all__.append('UdebContents')
1816 class PendingBinContents(object):
1817 def __init__(self, *args, **kwargs):
1821 return '<PendingBinContents %s>' % self.contents_id
1823 __all__.append('PendingBinContents')
1825 def insert_pending_content_paths(package,
1830 Make sure given paths are temporarily associated with given
1834 @param package: the package to associate with should have been read in from the binary control file
1835 @type fullpaths: list
1836 @param fullpaths: the list of paths of the file being associated with the binary
1837 @type session: SQLAlchemy session
1838 @param session: Optional SQLAlchemy session. If this is passed, the caller
1839 is responsible for ensuring a transaction has begun and committing the
1840 results or rolling back based on the result code. If not passed, a commit
1841 will be performed at the end of the function
1843 @return: True upon success, False if there is a problem
1846 privatetrans = False
1849 session = DBConn().session()
1853 arch = get_architecture(package['Architecture'], session)
1854 arch_id = arch.arch_id
1856 # Remove any already existing recorded files for this package
1857 q = session.query(PendingBinContents)
1858 q = q.filter_by(package=package['Package'])
1859 q = q.filter_by(version=package['Version'])
1860 q = q.filter_by(architecture=arch_id)
1863 for fullpath in fullpaths:
1865 if fullpath.startswith( "./" ):
1866 fullpath = fullpath[2:]
1868 pca = PendingBinContents()
1869 pca.package = package['Package']
1870 pca.version = package['Version']
1872 pca.architecture = arch_id
1875 pca.type = 8 # gross
1877 pca.type = 7 # also gross
1880 # Only commit if we set up the session ourself
1888 except Exception, e:
1889 traceback.print_exc()
1891 # Only rollback if we set up the session ourself
1898 __all__.append('insert_pending_content_paths')
1900 ################################################################################
1902 class PolicyQueue(object):
1903 def __init__(self, *args, **kwargs):
1907 return '<PolicyQueue %s>' % self.queue_name
1909 __all__.append('PolicyQueue')
1912 def get_policy_queue(queuename, session=None):
1914 Returns PolicyQueue object for given C{queue name}
1916 @type queuename: string
1917 @param queuename: The name of the queue
1919 @type session: Session
1920 @param session: Optional SQLA session object (a temporary one will be
1921 generated if not supplied)
1924 @return: PolicyQueue object for the given queue
1927 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1931 except NoResultFound:
1934 __all__.append('get_policy_queue')
1937 def get_policy_queue_from_path(pathname, session=None):
1939 Returns PolicyQueue object for given C{path name}
1941 @type queuename: string
1942 @param queuename: The path
1944 @type session: Session
1945 @param session: Optional SQLA session object (a temporary one will be
1946 generated if not supplied)
1949 @return: PolicyQueue object for the given queue
1952 q = session.query(PolicyQueue).filter_by(path=pathname)
1956 except NoResultFound:
1959 __all__.append('get_policy_queue_from_path')
1961 ################################################################################
1963 class Priority(object):
1964 def __init__(self, *args, **kwargs):
1967 def __eq__(self, val):
1968 if isinstance(val, str):
1969 return (self.priority == val)
1970 # This signals to use the normal comparison operator
1971 return NotImplemented
1973 def __ne__(self, val):
1974 if isinstance(val, str):
1975 return (self.priority != val)
1976 # This signals to use the normal comparison operator
1977 return NotImplemented
1980 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1982 __all__.append('Priority')
1985 def get_priority(priority, session=None):
1987 Returns Priority object for given C{priority name}.
1989 @type priority: string
1990 @param priority: The name of the priority
1992 @type session: Session
1993 @param session: Optional SQLA session object (a temporary one will be
1994 generated if not supplied)
1997 @return: Priority object for the given priority
2000 q = session.query(Priority).filter_by(priority=priority)
2004 except NoResultFound:
2007 __all__.append('get_priority')
2010 def get_priorities(session=None):
2012 Returns dictionary of priority names -> id mappings
2014 @type session: Session
2015 @param session: Optional SQL session object (a temporary one will be
2016 generated if not supplied)
2019 @return: dictionary of priority names -> id mappings
2023 q = session.query(Priority)
2025 ret[x.priority] = x.priority_id
2029 __all__.append('get_priorities')
2031 ################################################################################
2033 class Section(object):
2034 def __init__(self, *args, **kwargs):
2037 def __eq__(self, val):
2038 if isinstance(val, str):
2039 return (self.section == val)
2040 # This signals to use the normal comparison operator
2041 return NotImplemented
2043 def __ne__(self, val):
2044 if isinstance(val, str):
2045 return (self.section != val)
2046 # This signals to use the normal comparison operator
2047 return NotImplemented
2050 return '<Section %s>' % self.section
2052 __all__.append('Section')
2055 def get_section(section, session=None):
2057 Returns Section object for given C{section name}.
2059 @type section: string
2060 @param section: The name of the section
2062 @type session: Session
2063 @param session: Optional SQLA session object (a temporary one will be
2064 generated if not supplied)
2067 @return: Section object for the given section name
2070 q = session.query(Section).filter_by(section=section)
2074 except NoResultFound:
2077 __all__.append('get_section')
2080 def get_sections(session=None):
2082 Returns dictionary of section names -> id mappings
2084 @type session: Session
2085 @param session: Optional SQL session object (a temporary one will be
2086 generated if not supplied)
2089 @return: dictionary of section names -> id mappings
2093 q = session.query(Section)
2095 ret[x.section] = x.section_id
2099 __all__.append('get_sections')
2101 ################################################################################
2103 class DBSource(object):
2104 def __init__(self, *args, **kwargs):
2108 return '<DBSource %s (%s)>' % (self.source, self.version)
2110 __all__.append('DBSource')
2113 def source_exists(source, source_version, suites = ["any"], session=None):
2115 Ensure that source exists somewhere in the archive for the binary
2116 upload being processed.
2117 1. exact match => 1.0-3
2118 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2120 @type source: string
2121 @param source: source name
2123 @type source_version: string
2124 @param source_version: expected source version
2127 @param suites: list of suites to check in, default I{any}
2129 @type session: Session
2130 @param session: Optional SQLA session object (a temporary one will be
2131 generated if not supplied)
2134 @return: returns 1 if a source with expected version is found, otherwise 0
2141 for suite in suites:
2142 q = session.query(DBSource).filter_by(source=source)
2144 # source must exist in suite X, or in some other suite that's
2145 # mapped to X, recursively... silent-maps are counted too,
2146 # unreleased-maps aren't.
2147 maps = cnf.ValueList("SuiteMappings")[:]
2149 maps = [ m.split() for m in maps ]
2150 maps = [ (x[1], x[2]) for x in maps
2151 if x[0] == "map" or x[0] == "silent-map" ]
2154 if x[1] in s and x[0] not in s:
2157 q = q.join(SrcAssociation).join(Suite)
2158 q = q.filter(Suite.suite_name.in_(s))
2160 # Reduce the query results to a list of version numbers
2161 ql = [ j.version for j in q.all() ]
2164 if source_version in ql:
2168 from daklib.regexes import re_bin_only_nmu
2169 orig_source_version = re_bin_only_nmu.sub('', source_version)
2170 if orig_source_version in ql:
2173 # No source found so return not ok
2178 __all__.append('source_exists')
2181 def get_suites_source_in(source, session=None):
2183 Returns list of Suite objects which given C{source} name is in
2186 @param source: DBSource package name to search for
2189 @return: list of Suite objects for the given source
2192 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2194 __all__.append('get_suites_source_in')
2197 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2199 Returns list of DBSource objects for given C{source} name and other parameters
2202 @param source: DBSource package name to search for
2204 @type version: str or None
2205 @param version: DBSource version name to search for or None if not applicable
2207 @type dm_upload_allowed: bool
2208 @param dm_upload_allowed: If None, no effect. If True or False, only
2209 return packages with that dm_upload_allowed setting
2211 @type session: Session
2212 @param session: Optional SQL session object (a temporary one will be
2213 generated if not supplied)
2216 @return: list of DBSource objects for the given name (may be empty)
2219 q = session.query(DBSource).filter_by(source=source)
2221 if version is not None:
2222 q = q.filter_by(version=version)
2224 if dm_upload_allowed is not None:
2225 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2229 __all__.append('get_sources_from_name')
2232 def get_source_in_suite(source, suite, session=None):
2234 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2236 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2237 - B{suite} - a suite name, eg. I{unstable}
2239 @type source: string
2240 @param source: source package name
2243 @param suite: the suite name
2246 @return: the version for I{source} in I{suite}
2250 q = session.query(SrcAssociation)
2251 q = q.join('source').filter_by(source=source)
2252 q = q.join('suite').filter_by(suite_name=suite)
2255 return q.one().source
2256 except NoResultFound:
2259 __all__.append('get_source_in_suite')
2261 ################################################################################
2264 def add_dsc_to_db(u, filename, session=None):
2265 entry = u.pkg.files[filename]
2269 source.source = u.pkg.dsc["source"]
2270 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2271 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2272 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2273 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2274 source.install_date = datetime.now().date()
2276 dsc_component = entry["component"]
2277 dsc_location_id = entry["location id"]
2279 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2281 # Set up a new poolfile if necessary
2282 if not entry.has_key("files id") or not entry["files id"]:
2283 filename = entry["pool name"] + filename
2284 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2286 pfs.append(poolfile)
2287 entry["files id"] = poolfile.file_id
2289 source.poolfile_id = entry["files id"]
2293 for suite_name in u.pkg.changes["distribution"].keys():
2294 sa = SrcAssociation()
2295 sa.source_id = source.source_id
2296 sa.suite_id = get_suite(suite_name).suite_id
2301 # Add the source files to the DB (files and dsc_files)
2303 dscfile.source_id = source.source_id
2304 dscfile.poolfile_id = entry["files id"]
2305 session.add(dscfile)
2307 for dsc_file, dentry in u.pkg.dsc_files.items():
2309 df.source_id = source.source_id
2311 # If the .orig tarball is already in the pool, it's
2312 # files id is stored in dsc_files by check_dsc().
2313 files_id = dentry.get("files id", None)
2315 # Find the entry in the files hash
2316 # TODO: Bail out here properly
2318 for f, e in u.pkg.files.items():
2323 if files_id is None:
2324 filename = dfentry["pool name"] + dsc_file
2326 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2327 # FIXME: needs to check for -1/-2 and or handle exception
2328 if found and obj is not None:
2329 files_id = obj.file_id
2332 # If still not found, add it
2333 if files_id is None:
2334 # HACK: Force sha1sum etc into dentry
2335 dentry["sha1sum"] = dfentry["sha1sum"]
2336 dentry["sha256sum"] = dfentry["sha256sum"]
2337 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2338 pfs.append(poolfile)
2339 files_id = poolfile.file_id
2341 poolfile = get_poolfile_by_id(files_id, session)
2342 if poolfile is None:
2343 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2344 pfs.append(poolfile)
2346 df.poolfile_id = files_id
2351 # Add the src_uploaders to the DB
2352 uploader_ids = [source.maintainer_id]
2353 if u.pkg.dsc.has_key("uploaders"):
2354 for up in u.pkg.dsc["uploaders"].replace(">, ", ">\t").split("\t"):
2356 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2359 for up_id in uploader_ids:
2360 if added_ids.has_key(up_id):
2362 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2368 su.maintainer_id = up_id
2369 su.source_id = source.source_id
2374 return source, dsc_component, dsc_location_id, pfs
2376 __all__.append('add_dsc_to_db')
2379 def add_deb_to_db(u, filename, session=None):
2381 Contrary to what you might expect, this routine deals with both
2382 debs and udebs. That info is in 'dbtype', whilst 'type' is
2383 'deb' for both of them
2386 entry = u.pkg.files[filename]
2389 bin.package = entry["package"]
2390 bin.version = entry["version"]
2391 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2392 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2393 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2394 bin.binarytype = entry["dbtype"]
2397 filename = entry["pool name"] + filename
2398 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2399 if not entry.get("location id", None):
2400 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2402 if entry.get("files id", None):
2403 poolfile = get_poolfile_by_id(bin.poolfile_id)
2404 bin.poolfile_id = entry["files id"]
2406 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2407 bin.poolfile_id = entry["files id"] = poolfile.file_id
2410 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2411 if len(bin_sources) != 1:
2412 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2413 (bin.package, bin.version, entry["architecture"],
2414 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2416 bin.source_id = bin_sources[0].source_id
2418 # Add and flush object so it has an ID
2422 # Add BinAssociations
2423 for suite_name in u.pkg.changes["distribution"].keys():
2424 ba = BinAssociation()
2425 ba.binary_id = bin.binary_id
2426 ba.suite_id = get_suite(suite_name).suite_id
2431 # Deal with contents - disabled for now
2432 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2434 # print "REJECT\nCould not determine contents of package %s" % bin.package
2435 # session.rollback()
2436 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2440 __all__.append('add_deb_to_db')
2442 ################################################################################
2444 class SourceACL(object):
2445 def __init__(self, *args, **kwargs):
2449 return '<SourceACL %s>' % self.source_acl_id
2451 __all__.append('SourceACL')
2453 ################################################################################
2455 class SrcAssociation(object):
2456 def __init__(self, *args, **kwargs):
2460 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2462 __all__.append('SrcAssociation')
2464 ################################################################################
2466 class SrcFormat(object):
2467 def __init__(self, *args, **kwargs):
2471 return '<SrcFormat %s>' % (self.format_name)
2473 __all__.append('SrcFormat')
2475 ################################################################################
2477 class SrcUploader(object):
2478 def __init__(self, *args, **kwargs):
2482 return '<SrcUploader %s>' % self.uploader_id
2484 __all__.append('SrcUploader')
2486 ################################################################################
2488 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2489 ('SuiteID', 'suite_id'),
2490 ('Version', 'version'),
2491 ('Origin', 'origin'),
2493 ('Description', 'description'),
2494 ('Untouchable', 'untouchable'),
2495 ('Announce', 'announce'),
2496 ('Codename', 'codename'),
2497 ('OverrideCodename', 'overridecodename'),
2498 ('ValidTime', 'validtime'),
2499 ('Priority', 'priority'),
2500 ('NotAutomatic', 'notautomatic'),
2501 ('CopyChanges', 'copychanges'),
2502 ('OverrideSuite', 'overridesuite')]
2504 class Suite(object):
2505 def __init__(self, *args, **kwargs):
2509 return '<Suite %s>' % self.suite_name
2511 def __eq__(self, val):
2512 if isinstance(val, str):
2513 return (self.suite_name == val)
2514 # This signals to use the normal comparison operator
2515 return NotImplemented
2517 def __ne__(self, val):
2518 if isinstance(val, str):
2519 return (self.suite_name != val)
2520 # This signals to use the normal comparison operator
2521 return NotImplemented
2525 for disp, field in SUITE_FIELDS:
2526 val = getattr(self, field, None)
2528 ret.append("%s: %s" % (disp, val))
2530 return "\n".join(ret)
2532 __all__.append('Suite')
2535 def get_suite_architecture(suite, architecture, session=None):
2537 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2541 @param suite: Suite name to search for
2543 @type architecture: str
2544 @param architecture: Architecture name to search for
2546 @type session: Session
2547 @param session: Optional SQL session object (a temporary one will be
2548 generated if not supplied)
2550 @rtype: SuiteArchitecture
2551 @return: the SuiteArchitecture object or None
2554 q = session.query(SuiteArchitecture)
2555 q = q.join(Architecture).filter_by(arch_string=architecture)
2556 q = q.join(Suite).filter_by(suite_name=suite)
2560 except NoResultFound:
2563 __all__.append('get_suite_architecture')
2566 def get_suite(suite, session=None):
2568 Returns Suite object for given C{suite name}.
2571 @param suite: The name of the suite
2573 @type session: Session
2574 @param session: Optional SQLA session object (a temporary one will be
2575 generated if not supplied)
2578 @return: Suite object for the requested suite name (None if not present)
2581 q = session.query(Suite).filter_by(suite_name=suite)
2585 except NoResultFound:
2588 __all__.append('get_suite')
2590 ################################################################################
2592 class SuiteArchitecture(object):
2593 def __init__(self, *args, **kwargs):
2597 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2599 __all__.append('SuiteArchitecture')
2602 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2604 Returns list of Architecture objects for given C{suite} name
2607 @param suite: Suite name to search for
2609 @type skipsrc: boolean
2610 @param skipsrc: Whether to skip returning the 'source' architecture entry
2613 @type skipall: boolean
2614 @param skipall: Whether to skip returning the 'all' architecture entry
2617 @type session: Session
2618 @param session: Optional SQL session object (a temporary one will be
2619 generated if not supplied)
2622 @return: list of Architecture objects for the given name (may be empty)
2625 q = session.query(Architecture)
2626 q = q.join(SuiteArchitecture)
2627 q = q.join(Suite).filter_by(suite_name=suite)
2630 q = q.filter(Architecture.arch_string != 'source')
2633 q = q.filter(Architecture.arch_string != 'all')
2635 q = q.order_by('arch_string')
2639 __all__.append('get_suite_architectures')
2641 ################################################################################
2643 class SuiteSrcFormat(object):
2644 def __init__(self, *args, **kwargs):
2648 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2650 __all__.append('SuiteSrcFormat')
2653 def get_suite_src_formats(suite, session=None):
2655 Returns list of allowed SrcFormat for C{suite}.
2658 @param suite: Suite name to search for
2660 @type session: Session
2661 @param session: Optional SQL session object (a temporary one will be
2662 generated if not supplied)
2665 @return: the list of allowed source formats for I{suite}
2668 q = session.query(SrcFormat)
2669 q = q.join(SuiteSrcFormat)
2670 q = q.join(Suite).filter_by(suite_name=suite)
2671 q = q.order_by('format_name')
2675 __all__.append('get_suite_src_formats')
2677 ################################################################################
2680 def __init__(self, uid = None, name = None):
2684 def __eq__(self, val):
2685 if isinstance(val, str):
2686 return (self.uid == val)
2687 # This signals to use the normal comparison operator
2688 return NotImplemented
2690 def __ne__(self, val):
2691 if isinstance(val, str):
2692 return (self.uid != val)
2693 # This signals to use the normal comparison operator
2694 return NotImplemented
2697 return '<Uid %s (%s)>' % (self.uid, self.name)
2699 __all__.append('Uid')
2702 def get_or_set_uid(uidname, session=None):
2704 Returns uid object for given uidname.
2706 If no matching uidname is found, a row is inserted.
2708 @type uidname: string
2709 @param uidname: The uid to add
2711 @type session: SQLAlchemy
2712 @param session: Optional SQL session object (a temporary one will be
2713 generated if not supplied). If not passed, a commit will be performed at
2714 the end of the function, otherwise the caller is responsible for commiting.
2717 @return: the uid object for the given uidname
2720 q = session.query(Uid).filter_by(uid=uidname)
2724 except NoResultFound:
2728 session.commit_or_flush()
2733 __all__.append('get_or_set_uid')
2736 def get_uid_from_fingerprint(fpr, session=None):
2737 q = session.query(Uid)
2738 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2742 except NoResultFound:
2745 __all__.append('get_uid_from_fingerprint')
2747 ################################################################################
2749 class UploadBlock(object):
2750 def __init__(self, *args, **kwargs):
2754 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2756 __all__.append('UploadBlock')
2758 ################################################################################
2760 class DBConn(object):
2762 database module init.
2766 def __init__(self, *args, **kwargs):
2767 self.__dict__ = self.__shared_state
2769 if not getattr(self, 'initialised', False):
2770 self.initialised = True
2771 self.debug = kwargs.has_key('debug')
2774 def __setuptables(self):
2775 tables_with_primary = (
2786 'changes_pending_binaries',
2787 'changes_pending_files',
2788 'changes_pending_source',
2798 'pending_bin_contents',
2810 # The following tables have primary keys but sqlalchemy
2811 # version 0.5 fails to reflect them correctly with database
2812 # versions before upgrade #41.
2814 #'build_queue_files',
2817 tables_no_primary = (
2819 'changes_pending_files_map',
2820 'changes_pending_source_files',
2821 'changes_pool_files',
2824 'suite_architectures',
2825 'suite_src_formats',
2826 'suite_build_queue_copy',
2828 # see the comment above
2830 'build_queue_files',
2834 'almost_obsolete_all_associations',
2835 'almost_obsolete_src_associations',
2836 'any_associations_source',
2837 'bin_assoc_by_arch',
2838 'bin_associations_binaries',
2839 'binaries_suite_arch',
2840 'binfiles_suite_component_arch',
2843 'newest_all_associations',
2844 'newest_any_associations',
2846 'newest_src_association',
2847 'obsolete_all_associations',
2848 'obsolete_any_associations',
2849 'obsolete_any_by_all_associations',
2850 'obsolete_src_associations',
2852 'src_associations_bin',
2853 'src_associations_src',
2854 'suite_arch_by_name',
2857 # Sqlalchemy version 0.5 fails to reflect the SERIAL type
2858 # correctly and that is why we have to use a workaround. It can
2859 # be removed as soon as we switch to version 0.6.
2860 for table_name in tables_with_primary:
2861 table = Table(table_name, self.db_meta, \
2862 Column('id', Integer, primary_key = True), \
2863 autoload=True, useexisting=True)
2864 setattr(self, 'tbl_%s' % table_name, table)
2866 for table_name in tables_no_primary:
2867 table = Table(table_name, self.db_meta, autoload=True)
2868 setattr(self, 'tbl_%s' % table_name, table)
2870 for view_name in views:
2871 view = Table(view_name, self.db_meta, autoload=True)
2872 setattr(self, 'view_%s' % view_name, view)
2874 def __setupmappers(self):
2875 mapper(Architecture, self.tbl_architecture,
2876 properties = dict(arch_id = self.tbl_architecture.c.id))
2878 mapper(Archive, self.tbl_archive,
2879 properties = dict(archive_id = self.tbl_archive.c.id,
2880 archive_name = self.tbl_archive.c.name))
2882 mapper(BinAssociation, self.tbl_bin_associations,
2883 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2884 suite_id = self.tbl_bin_associations.c.suite,
2885 suite = relation(Suite),
2886 binary_id = self.tbl_bin_associations.c.bin,
2887 binary = relation(DBBinary)))
2889 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2890 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2891 filename = self.tbl_pending_bin_contents.c.filename,
2892 package = self.tbl_pending_bin_contents.c.package,
2893 version = self.tbl_pending_bin_contents.c.version,
2894 arch = self.tbl_pending_bin_contents.c.arch,
2895 otype = self.tbl_pending_bin_contents.c.type))
2897 mapper(DebContents, self.tbl_deb_contents,
2898 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2899 package=self.tbl_deb_contents.c.package,
2900 suite=self.tbl_deb_contents.c.suite,
2901 arch=self.tbl_deb_contents.c.arch,
2902 section=self.tbl_deb_contents.c.section,
2903 filename=self.tbl_deb_contents.c.filename))
2905 mapper(UdebContents, self.tbl_udeb_contents,
2906 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2907 package=self.tbl_udeb_contents.c.package,
2908 suite=self.tbl_udeb_contents.c.suite,
2909 arch=self.tbl_udeb_contents.c.arch,
2910 section=self.tbl_udeb_contents.c.section,
2911 filename=self.tbl_udeb_contents.c.filename))
2913 mapper(BuildQueue, self.tbl_build_queue,
2914 properties = dict(queue_id = self.tbl_build_queue.c.id))
2916 mapper(BuildQueueFile, self.tbl_build_queue_files,
2917 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2918 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2920 mapper(DBBinary, self.tbl_binaries,
2921 properties = dict(binary_id = self.tbl_binaries.c.id,
2922 package = self.tbl_binaries.c.package,
2923 version = self.tbl_binaries.c.version,
2924 maintainer_id = self.tbl_binaries.c.maintainer,
2925 maintainer = relation(Maintainer),
2926 source_id = self.tbl_binaries.c.source,
2927 source = relation(DBSource),
2928 arch_id = self.tbl_binaries.c.architecture,
2929 architecture = relation(Architecture),
2930 poolfile_id = self.tbl_binaries.c.file,
2931 poolfile = relation(PoolFile),
2932 binarytype = self.tbl_binaries.c.type,
2933 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2934 fingerprint = relation(Fingerprint),
2935 install_date = self.tbl_binaries.c.install_date,
2936 binassociations = relation(BinAssociation,
2937 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2939 mapper(BinaryACL, self.tbl_binary_acl,
2940 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2942 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2943 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2944 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2945 architecture = relation(Architecture)))
2947 mapper(Component, self.tbl_component,
2948 properties = dict(component_id = self.tbl_component.c.id,
2949 component_name = self.tbl_component.c.name))
2951 mapper(DBConfig, self.tbl_config,
2952 properties = dict(config_id = self.tbl_config.c.id))
2954 mapper(DSCFile, self.tbl_dsc_files,
2955 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2956 source_id = self.tbl_dsc_files.c.source,
2957 source = relation(DBSource),
2958 poolfile_id = self.tbl_dsc_files.c.file,
2959 poolfile = relation(PoolFile)))
2961 mapper(PoolFile, self.tbl_files,
2962 properties = dict(file_id = self.tbl_files.c.id,
2963 filesize = self.tbl_files.c.size,
2964 location_id = self.tbl_files.c.location,
2965 location = relation(Location)))
2967 mapper(Fingerprint, self.tbl_fingerprint,
2968 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2969 uid_id = self.tbl_fingerprint.c.uid,
2970 uid = relation(Uid),
2971 keyring_id = self.tbl_fingerprint.c.keyring,
2972 keyring = relation(Keyring),
2973 source_acl = relation(SourceACL),
2974 binary_acl = relation(BinaryACL)))
2976 mapper(Keyring, self.tbl_keyrings,
2977 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2978 keyring_id = self.tbl_keyrings.c.id))
2980 mapper(DBChange, self.tbl_changes,
2981 properties = dict(change_id = self.tbl_changes.c.id,
2982 poolfiles = relation(PoolFile,
2983 secondary=self.tbl_changes_pool_files,
2984 backref="changeslinks"),
2985 seen = self.tbl_changes.c.seen,
2986 source = self.tbl_changes.c.source,
2987 binaries = self.tbl_changes.c.binaries,
2988 architecture = self.tbl_changes.c.architecture,
2989 distribution = self.tbl_changes.c.distribution,
2990 urgency = self.tbl_changes.c.urgency,
2991 maintainer = self.tbl_changes.c.maintainer,
2992 changedby = self.tbl_changes.c.changedby,
2993 date = self.tbl_changes.c.date,
2994 version = self.tbl_changes.c.version,
2995 files = relation(ChangePendingFile,
2996 secondary=self.tbl_changes_pending_files_map,
2997 backref="changesfile"),
2998 in_queue_id = self.tbl_changes.c.in_queue,
2999 in_queue = relation(PolicyQueue,
3000 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
3001 approved_for_id = self.tbl_changes.c.approved_for))
3003 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
3004 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
3006 mapper(ChangePendingFile, self.tbl_changes_pending_files,
3007 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
3008 filename = self.tbl_changes_pending_files.c.filename,
3009 size = self.tbl_changes_pending_files.c.size,
3010 md5sum = self.tbl_changes_pending_files.c.md5sum,
3011 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
3012 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
3014 mapper(ChangePendingSource, self.tbl_changes_pending_source,
3015 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
3016 change = relation(DBChange),
3017 maintainer = relation(Maintainer,
3018 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
3019 changedby = relation(Maintainer,
3020 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
3021 fingerprint = relation(Fingerprint),
3022 source_files = relation(ChangePendingFile,
3023 secondary=self.tbl_changes_pending_source_files,
3024 backref="pending_sources")))
3027 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
3028 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
3029 keyring = relation(Keyring, backref="keyring_acl_map"),
3030 architecture = relation(Architecture)))
3032 mapper(Location, self.tbl_location,
3033 properties = dict(location_id = self.tbl_location.c.id,
3034 component_id = self.tbl_location.c.component,
3035 component = relation(Component),
3036 archive_id = self.tbl_location.c.archive,
3037 archive = relation(Archive),
3038 archive_type = self.tbl_location.c.type))
3040 mapper(Maintainer, self.tbl_maintainer,
3041 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
3043 mapper(NewComment, self.tbl_new_comments,
3044 properties = dict(comment_id = self.tbl_new_comments.c.id))
3046 mapper(Override, self.tbl_override,
3047 properties = dict(suite_id = self.tbl_override.c.suite,
3048 suite = relation(Suite),
3049 package = self.tbl_override.c.package,
3050 component_id = self.tbl_override.c.component,
3051 component = relation(Component),
3052 priority_id = self.tbl_override.c.priority,
3053 priority = relation(Priority),
3054 section_id = self.tbl_override.c.section,
3055 section = relation(Section),
3056 overridetype_id = self.tbl_override.c.type,
3057 overridetype = relation(OverrideType)))
3059 mapper(OverrideType, self.tbl_override_type,
3060 properties = dict(overridetype = self.tbl_override_type.c.type,
3061 overridetype_id = self.tbl_override_type.c.id))
3063 mapper(PolicyQueue, self.tbl_policy_queue,
3064 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
3066 mapper(Priority, self.tbl_priority,
3067 properties = dict(priority_id = self.tbl_priority.c.id))
3069 mapper(Section, self.tbl_section,
3070 properties = dict(section_id = self.tbl_section.c.id,
3071 section=self.tbl_section.c.section))
3073 mapper(DBSource, self.tbl_source,
3074 properties = dict(source_id = self.tbl_source.c.id,
3075 version = self.tbl_source.c.version,
3076 maintainer_id = self.tbl_source.c.maintainer,
3077 maintainer = relation(Maintainer,
3078 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3079 poolfile_id = self.tbl_source.c.file,
3080 poolfile = relation(PoolFile),
3081 fingerprint_id = self.tbl_source.c.sig_fpr,
3082 fingerprint = relation(Fingerprint),
3083 changedby_id = self.tbl_source.c.changedby,
3084 changedby = relation(Maintainer,
3085 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3086 srcfiles = relation(DSCFile,
3087 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3088 srcassociations = relation(SrcAssociation,
3089 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3090 srcuploaders = relation(SrcUploader)))
3092 mapper(SourceACL, self.tbl_source_acl,
3093 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3095 mapper(SrcAssociation, self.tbl_src_associations,
3096 properties = dict(sa_id = self.tbl_src_associations.c.id,
3097 suite_id = self.tbl_src_associations.c.suite,
3098 suite = relation(Suite),
3099 source_id = self.tbl_src_associations.c.source,
3100 source = relation(DBSource)))
3102 mapper(SrcFormat, self.tbl_src_format,
3103 properties = dict(src_format_id = self.tbl_src_format.c.id,
3104 format_name = self.tbl_src_format.c.format_name))
3106 mapper(SrcUploader, self.tbl_src_uploaders,
3107 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3108 source_id = self.tbl_src_uploaders.c.source,
3109 source = relation(DBSource,
3110 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3111 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3112 maintainer = relation(Maintainer,
3113 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3115 mapper(Suite, self.tbl_suite,
3116 properties = dict(suite_id = self.tbl_suite.c.id,
3117 policy_queue = relation(PolicyQueue),
3118 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3120 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3121 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3122 suite = relation(Suite, backref='suitearchitectures'),
3123 arch_id = self.tbl_suite_architectures.c.architecture,
3124 architecture = relation(Architecture)))
3126 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3127 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3128 suite = relation(Suite, backref='suitesrcformats'),
3129 src_format_id = self.tbl_suite_src_formats.c.src_format,
3130 src_format = relation(SrcFormat)))
3132 mapper(Uid, self.tbl_uid,
3133 properties = dict(uid_id = self.tbl_uid.c.id,
3134 fingerprint = relation(Fingerprint)))
3136 mapper(UploadBlock, self.tbl_upload_blocks,
3137 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3138 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3139 uid = relation(Uid, backref="uploadblocks")))
3141 ## Connection functions
3142 def __createconn(self):
3143 from config import Config
3147 connstr = "postgres://%s" % cnf["DB::Host"]
3148 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3149 connstr += ":%s" % cnf["DB::Port"]
3150 connstr += "/%s" % cnf["DB::Name"]
3153 connstr = "postgres:///%s" % cnf["DB::Name"]
3154 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3155 connstr += "?port=%s" % cnf["DB::Port"]
3157 self.db_pg = create_engine(connstr, echo=self.debug)
3158 self.db_meta = MetaData()
3159 self.db_meta.bind = self.db_pg
3160 self.db_smaker = sessionmaker(bind=self.db_pg,
3164 self.__setuptables()
3165 self.__setupmappers()
3168 return self.db_smaker()
3170 __all__.append('DBConn')