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
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
67 class DebVersion(sqltypes.Text):
69 Support the debversion type
72 def get_col_spec(self):
75 sa_major_version = sqlalchemy.__version__[0:3]
76 if sa_major_version in ["0.5", "0.6"]:
77 from sqlalchemy.databases import postgres
78 postgres.ischema_names['debversion'] = DebVersion
80 raise Exception("dak only ported to SQLA versions 0.5 and 0.6. See daklib/dbconn.py")
82 ################################################################################
84 __all__ = ['IntegrityError', 'SQLAlchemyError']
86 ################################################################################
88 def session_wrapper(fn):
90 Wrapper around common ".., session=None):" handling. If the wrapped
91 function is called without passing 'session', we create a local one
92 and destroy it when the function ends.
94 Also attaches a commit_or_flush method to the session; if we created a
95 local session, this is a synonym for session.commit(), otherwise it is a
96 synonym for session.flush().
99 def wrapped(*args, **kwargs):
100 private_transaction = False
102 # Find the session object
103 session = kwargs.get('session')
106 if len(args) <= len(getargspec(fn)[0]) - 1:
107 # No session specified as last argument or in kwargs
108 private_transaction = True
109 session = kwargs['session'] = DBConn().session()
111 # Session is last argument in args
115 session = args[-1] = DBConn().session()
116 private_transaction = True
118 if private_transaction:
119 session.commit_or_flush = session.commit
121 session.commit_or_flush = session.flush
124 return fn(*args, **kwargs)
126 if private_transaction:
127 # We created a session; close it.
130 wrapped.__doc__ = fn.__doc__
131 wrapped.func_name = fn.func_name
135 __all__.append('session_wrapper')
137 ################################################################################
139 class Architecture(object):
140 def __init__(self, *args, **kwargs):
143 def __eq__(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
149 def __ne__(self, val):
150 if isinstance(val, str):
151 return (self.arch_string != val)
152 # This signals to use the normal comparison operator
153 return NotImplemented
156 return '<Architecture %s>' % self.arch_string
158 __all__.append('Architecture')
161 def get_architecture(architecture, session=None):
163 Returns database id for given C{architecture}.
165 @type architecture: string
166 @param architecture: The name of the architecture
168 @type session: Session
169 @param session: Optional SQLA session object (a temporary one will be
170 generated if not supplied)
173 @return: Architecture object for the given arch (None if not present)
176 q = session.query(Architecture).filter_by(arch_string=architecture)
180 except NoResultFound:
183 __all__.append('get_architecture')
186 def get_architecture_suites(architecture, session=None):
188 Returns list of Suite objects for given C{architecture} name
190 @type architecture: str
191 @param architecture: Architecture name to search for
193 @type session: Session
194 @param session: Optional SQL session object (a temporary one will be
195 generated if not supplied)
198 @return: list of Suite objects for the given name (may be empty)
201 q = session.query(Suite)
202 q = q.join(SuiteArchitecture)
203 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
209 __all__.append('get_architecture_suites')
211 ################################################################################
213 class Archive(object):
214 def __init__(self, *args, **kwargs):
218 return '<Archive %s>' % self.archive_name
220 __all__.append('Archive')
223 def get_archive(archive, session=None):
225 returns database id for given C{archive}.
227 @type archive: string
228 @param archive: the name of the arhive
230 @type session: Session
231 @param session: Optional SQLA session object (a temporary one will be
232 generated if not supplied)
235 @return: Archive object for the given name (None if not present)
238 archive = archive.lower()
240 q = session.query(Archive).filter_by(archive_name=archive)
244 except NoResultFound:
247 __all__.append('get_archive')
249 ################################################################################
251 class BinAssociation(object):
252 def __init__(self, *args, **kwargs):
256 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
258 __all__.append('BinAssociation')
260 ################################################################################
262 class BinContents(object):
263 def __init__(self, *args, **kwargs):
267 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
269 __all__.append('BinContents')
271 ################################################################################
273 class DBBinary(object):
274 def __init__(self, *args, **kwargs):
278 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
280 __all__.append('DBBinary')
283 def get_suites_binary_in(package, session=None):
285 Returns list of Suite objects which given C{package} name is in
288 @param package: DBBinary package name to search for
291 @return: list of Suite objects for the given package
294 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
296 __all__.append('get_suites_binary_in')
299 def get_binary_from_id(binary_id, session=None):
301 Returns DBBinary object for given C{id}
304 @param binary_id: Id of the required binary
306 @type session: Session
307 @param session: Optional SQLA session object (a temporary one will be
308 generated if not supplied)
311 @return: DBBinary object for the given binary (None if not present)
314 q = session.query(DBBinary).filter_by(binary_id=binary_id)
318 except NoResultFound:
321 __all__.append('get_binary_from_id')
324 def get_binaries_from_name(package, version=None, architecture=None, session=None):
326 Returns list of DBBinary objects for given C{package} name
329 @param package: DBBinary package name to search for
331 @type version: str or None
332 @param version: Version to search for (or None)
334 @type architecture: str, list or None
335 @param architecture: Architectures to limit to (or None if no limit)
337 @type session: Session
338 @param session: Optional SQL session object (a temporary one will be
339 generated if not supplied)
342 @return: list of DBBinary objects for the given name (may be empty)
345 q = session.query(DBBinary).filter_by(package=package)
347 if version is not None:
348 q = q.filter_by(version=version)
350 if architecture is not None:
351 if not isinstance(architecture, list):
352 architecture = [architecture]
353 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
359 __all__.append('get_binaries_from_name')
362 def get_binaries_from_source_id(source_id, session=None):
364 Returns list of DBBinary objects for given C{source_id}
367 @param source_id: source_id to search for
369 @type session: Session
370 @param session: Optional SQL session object (a temporary one will be
371 generated if not supplied)
374 @return: list of DBBinary objects for the given name (may be empty)
377 return session.query(DBBinary).filter_by(source_id=source_id).all()
379 __all__.append('get_binaries_from_source_id')
382 def get_binary_from_name_suite(package, suitename, session=None):
383 ### For dak examine-package
384 ### XXX: Doesn't use object API yet
386 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
387 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
388 WHERE b.package='%(package)s'
390 AND fi.location = l.id
391 AND l.component = c.id
394 AND su.suite_name %(suitename)s
395 ORDER BY b.version DESC"""
397 return session.execute(sql % {'package': package, 'suitename': suitename})
399 __all__.append('get_binary_from_name_suite')
402 def get_binary_components(package, suitename, arch, session=None):
403 # Check for packages that have moved from one component to another
404 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
405 WHERE b.package=:package AND s.suite_name=:suitename
406 AND (a.arch_string = :arch OR a.arch_string = 'all')
407 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
408 AND f.location = l.id
409 AND l.component = c.id
412 vals = {'package': package, 'suitename': suitename, 'arch': arch}
414 return session.execute(query, vals)
416 __all__.append('get_binary_components')
418 ################################################################################
420 class BinaryACL(object):
421 def __init__(self, *args, **kwargs):
425 return '<BinaryACL %s>' % self.binary_acl_id
427 __all__.append('BinaryACL')
429 ################################################################################
431 class BinaryACLMap(object):
432 def __init__(self, *args, **kwargs):
436 return '<BinaryACLMap %s>' % self.binary_acl_map_id
438 __all__.append('BinaryACLMap')
440 ################################################################################
445 ArchiveDir "%(archivepath)s";
446 OverrideDir "%(overridedir)s";
447 CacheDir "%(cachedir)s";
452 Packages::Compress ". bzip2 gzip";
453 Sources::Compress ". bzip2 gzip";
458 bindirectory "incoming"
463 BinOverride "override.sid.all3";
464 BinCacheDB "packages-accepted.db";
466 FileList "%(filelist)s";
469 Packages::Extensions ".deb .udeb";
472 bindirectory "incoming/"
475 BinOverride "override.sid.all3";
476 SrcOverride "override.sid.all3.src";
477 FileList "%(filelist)s";
481 class BuildQueue(object):
482 def __init__(self, *args, **kwargs):
486 return '<BuildQueue %s>' % self.queue_name
488 def write_metadata(self, starttime, force=False):
489 # Do we write out metafiles?
490 if not (force or self.generate_metadata):
493 session = DBConn().session().object_session(self)
495 fl_fd = fl_name = ac_fd = ac_name = None
497 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
498 startdir = os.getcwd()
501 # Grab files we want to include
502 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
503 # Write file list with newer files
504 (fl_fd, fl_name) = mkstemp()
506 os.write(fl_fd, '%s\n' % n.fullpath)
511 # Write minimal apt.conf
512 # TODO: Remove hardcoding from template
513 (ac_fd, ac_name) = mkstemp()
514 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
516 'cachedir': cnf["Dir::Cache"],
517 'overridedir': cnf["Dir::Override"],
521 # Run apt-ftparchive generate
522 os.chdir(os.path.dirname(ac_name))
523 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
525 # Run apt-ftparchive release
526 # TODO: Eww - fix this
527 bname = os.path.basename(self.path)
531 # We have to remove the Release file otherwise it'll be included in the
534 os.unlink(os.path.join(bname, 'Release'))
538 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))
540 # Crude hack with open and append, but this whole section is and should be redone.
541 if self.notautomatic:
542 release=open("Release", "a")
543 release.write("NotAutomatic: yes")
548 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
549 if cnf.has_key("Dinstall::SigningPubKeyring"):
550 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
552 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
554 # Move the files if we got this far
555 os.rename('Release', os.path.join(bname, 'Release'))
557 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
559 # Clean up any left behind files
586 def clean_and_update(self, starttime, Logger, dryrun=False):
587 """WARNING: This routine commits for you"""
588 session = DBConn().session().object_session(self)
590 if self.generate_metadata and not dryrun:
591 self.write_metadata(starttime)
593 # Grab files older than our execution time
594 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
600 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
602 Logger.log(["I: Removing %s from the queue" % o.fullpath])
603 os.unlink(o.fullpath)
606 # If it wasn't there, don't worry
607 if e.errno == ENOENT:
610 # TODO: Replace with proper logging call
611 Logger.log(["E: Could not remove %s" % o.fullpath])
618 for f in os.listdir(self.path):
619 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release') or f.startswith('advisory'):
623 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
624 except NoResultFound:
625 fp = os.path.join(self.path, f)
627 Logger.log(["I: Would remove unused link %s" % fp])
629 Logger.log(["I: Removing unused link %s" % fp])
633 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
635 def add_file_from_pool(self, poolfile):
636 """Copies a file into the pool. Assumes that the PoolFile object is
637 attached to the same SQLAlchemy session as the Queue object is.
639 The caller is responsible for committing after calling this function."""
640 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
642 # Check if we have a file of this name or this ID already
643 for f in self.queuefiles:
644 if f.fileid is not None and f.fileid == poolfile.file_id or \
645 f.poolfile.filename == poolfile_basename:
646 # In this case, update the BuildQueueFile entry so we
647 # don't remove it too early
648 f.lastused = datetime.now()
649 DBConn().session().object_session(poolfile).add(f)
652 # Prepare BuildQueueFile object
653 qf = BuildQueueFile()
654 qf.build_queue_id = self.queue_id
655 qf.lastused = datetime.now()
656 qf.filename = poolfile_basename
658 targetpath = poolfile.fullpath
659 queuepath = os.path.join(self.path, poolfile_basename)
663 # We need to copy instead of symlink
665 utils.copy(targetpath, queuepath)
666 # NULL in the fileid field implies a copy
669 os.symlink(targetpath, queuepath)
670 qf.fileid = poolfile.file_id
674 # Get the same session as the PoolFile is using and add the qf to it
675 DBConn().session().object_session(poolfile).add(qf)
680 __all__.append('BuildQueue')
683 def get_build_queue(queuename, session=None):
685 Returns BuildQueue object for given C{queue name}, creating it if it does not
688 @type queuename: string
689 @param queuename: The name of the queue
691 @type session: Session
692 @param session: Optional SQLA session object (a temporary one will be
693 generated if not supplied)
696 @return: BuildQueue object for the given queue
699 q = session.query(BuildQueue).filter_by(queue_name=queuename)
703 except NoResultFound:
706 __all__.append('get_build_queue')
708 ################################################################################
710 class BuildQueueFile(object):
711 def __init__(self, *args, **kwargs):
715 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
719 return os.path.join(self.buildqueue.path, self.filename)
722 __all__.append('BuildQueueFile')
724 ################################################################################
726 class ChangePendingBinary(object):
727 def __init__(self, *args, **kwargs):
731 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
733 __all__.append('ChangePendingBinary')
735 ################################################################################
737 class ChangePendingFile(object):
738 def __init__(self, *args, **kwargs):
742 return '<ChangePendingFile %s>' % self.change_pending_file_id
744 __all__.append('ChangePendingFile')
746 ################################################################################
748 class ChangePendingSource(object):
749 def __init__(self, *args, **kwargs):
753 return '<ChangePendingSource %s>' % self.change_pending_source_id
755 __all__.append('ChangePendingSource')
757 ################################################################################
759 class Component(object):
760 def __init__(self, *args, **kwargs):
763 def __eq__(self, val):
764 if isinstance(val, str):
765 return (self.component_name == val)
766 # This signals to use the normal comparison operator
767 return NotImplemented
769 def __ne__(self, val):
770 if isinstance(val, str):
771 return (self.component_name != val)
772 # This signals to use the normal comparison operator
773 return NotImplemented
776 return '<Component %s>' % self.component_name
779 __all__.append('Component')
782 def get_component(component, session=None):
784 Returns database id for given C{component}.
786 @type component: string
787 @param component: The name of the override type
790 @return: the database id for the given component
793 component = component.lower()
795 q = session.query(Component).filter_by(component_name=component)
799 except NoResultFound:
802 __all__.append('get_component')
804 ################################################################################
806 class DBConfig(object):
807 def __init__(self, *args, **kwargs):
811 return '<DBConfig %s>' % self.name
813 __all__.append('DBConfig')
815 ################################################################################
818 def get_or_set_contents_file_id(filename, session=None):
820 Returns database id for given filename.
822 If no matching file is found, a row is inserted.
824 @type filename: string
825 @param filename: The filename
826 @type session: SQLAlchemy
827 @param session: Optional SQL session object (a temporary one will be
828 generated if not supplied). If not passed, a commit will be performed at
829 the end of the function, otherwise the caller is responsible for commiting.
832 @return: the database id for the given component
835 q = session.query(ContentFilename).filter_by(filename=filename)
838 ret = q.one().cafilename_id
839 except NoResultFound:
840 cf = ContentFilename()
841 cf.filename = filename
843 session.commit_or_flush()
844 ret = cf.cafilename_id
848 __all__.append('get_or_set_contents_file_id')
851 def get_contents(suite, overridetype, section=None, session=None):
853 Returns contents for a suite / overridetype combination, limiting
854 to a section if not None.
857 @param suite: Suite object
859 @type overridetype: OverrideType
860 @param overridetype: OverrideType object
862 @type section: Section
863 @param section: Optional section object to limit results to
865 @type session: SQLAlchemy
866 @param session: Optional SQL session object (a temporary one will be
867 generated if not supplied)
870 @return: ResultsProxy object set up to return tuples of (filename, section,
874 # find me all of the contents for a given suite
875 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
879 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
880 JOIN content_file_names n ON (c.filename=n.id)
881 JOIN binaries b ON (b.id=c.binary_pkg)
882 JOIN override o ON (o.package=b.package)
883 JOIN section s ON (s.id=o.section)
884 WHERE o.suite = :suiteid AND o.type = :overridetypeid
885 AND b.type=:overridetypename"""
887 vals = {'suiteid': suite.suite_id,
888 'overridetypeid': overridetype.overridetype_id,
889 'overridetypename': overridetype.overridetype}
891 if section is not None:
892 contents_q += " AND s.id = :sectionid"
893 vals['sectionid'] = section.section_id
895 contents_q += " ORDER BY fn"
897 return session.execute(contents_q, vals)
899 __all__.append('get_contents')
901 ################################################################################
903 class ContentFilepath(object):
904 def __init__(self, *args, **kwargs):
908 return '<ContentFilepath %s>' % self.filepath
910 __all__.append('ContentFilepath')
913 def get_or_set_contents_path_id(filepath, session=None):
915 Returns database id for given path.
917 If no matching file is found, a row is inserted.
919 @type filepath: string
920 @param filepath: The filepath
922 @type session: SQLAlchemy
923 @param session: Optional SQL session object (a temporary one will be
924 generated if not supplied). If not passed, a commit will be performed at
925 the end of the function, otherwise the caller is responsible for commiting.
928 @return: the database id for the given path
931 q = session.query(ContentFilepath).filter_by(filepath=filepath)
934 ret = q.one().cafilepath_id
935 except NoResultFound:
936 cf = ContentFilepath()
937 cf.filepath = filepath
939 session.commit_or_flush()
940 ret = cf.cafilepath_id
944 __all__.append('get_or_set_contents_path_id')
946 ################################################################################
948 class ContentAssociation(object):
949 def __init__(self, *args, **kwargs):
953 return '<ContentAssociation %s>' % self.ca_id
955 __all__.append('ContentAssociation')
957 def insert_content_paths(binary_id, fullpaths, session=None):
959 Make sure given path is associated with given binary id
962 @param binary_id: the id of the binary
963 @type fullpaths: list
964 @param fullpaths: the list of paths of the file being associated with the binary
965 @type session: SQLAlchemy session
966 @param session: Optional SQLAlchemy session. If this is passed, the caller
967 is responsible for ensuring a transaction has begun and committing the
968 results or rolling back based on the result code. If not passed, a commit
969 will be performed at the end of the function, otherwise the caller is
970 responsible for commiting.
972 @return: True upon success
977 session = DBConn().session()
982 def generate_path_dicts():
983 for fullpath in fullpaths:
984 if fullpath.startswith( './' ):
985 fullpath = fullpath[2:]
987 yield {'filename':fullpath, 'id': binary_id }
989 for d in generate_path_dicts():
990 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
999 traceback.print_exc()
1001 # Only rollback if we set up the session ourself
1008 __all__.append('insert_content_paths')
1010 ################################################################################
1012 class DSCFile(object):
1013 def __init__(self, *args, **kwargs):
1017 return '<DSCFile %s>' % self.dscfile_id
1019 __all__.append('DSCFile')
1022 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1024 Returns a list of DSCFiles which may be empty
1026 @type dscfile_id: int (optional)
1027 @param dscfile_id: the dscfile_id of the DSCFiles to find
1029 @type source_id: int (optional)
1030 @param source_id: the source id related to the DSCFiles to find
1032 @type poolfile_id: int (optional)
1033 @param poolfile_id: the poolfile id related to the DSCFiles to find
1036 @return: Possibly empty list of DSCFiles
1039 q = session.query(DSCFile)
1041 if dscfile_id is not None:
1042 q = q.filter_by(dscfile_id=dscfile_id)
1044 if source_id is not None:
1045 q = q.filter_by(source_id=source_id)
1047 if poolfile_id is not None:
1048 q = q.filter_by(poolfile_id=poolfile_id)
1052 __all__.append('get_dscfiles')
1054 ################################################################################
1056 class PoolFile(object):
1057 def __init__(self, *args, **kwargs):
1061 return '<PoolFile %s>' % self.filename
1065 return os.path.join(self.location.path, self.filename)
1067 __all__.append('PoolFile')
1070 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1073 (ValidFileFound [boolean or None], PoolFile object or None)
1075 @type filename: string
1076 @param filename: the filename of the file to check against the DB
1079 @param filesize: the size of the file to check against the DB
1081 @type md5sum: string
1082 @param md5sum: the md5sum of the file to check against the DB
1084 @type location_id: int
1085 @param location_id: the id of the location to look in
1088 @return: Tuple of length 2.
1089 - If more than one file found with that name: (C{None}, C{None})
1090 - If valid pool file found: (C{True}, C{PoolFile object})
1091 - If valid pool file not found:
1092 - (C{False}, C{None}) if no file found
1093 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1096 q = session.query(PoolFile).filter_by(filename=filename)
1097 q = q.join(Location).filter_by(location_id=location_id)
1107 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1115 __all__.append('check_poolfile')
1118 def get_poolfile_by_id(file_id, session=None):
1120 Returns a PoolFile objects or None for the given id
1123 @param file_id: the id of the file to look for
1125 @rtype: PoolFile or None
1126 @return: either the PoolFile object or None
1129 q = session.query(PoolFile).filter_by(file_id=file_id)
1133 except NoResultFound:
1136 __all__.append('get_poolfile_by_id')
1140 def get_poolfile_by_name(filename, location_id=None, session=None):
1142 Returns an array of PoolFile objects for the given filename and
1143 (optionally) location_id
1145 @type filename: string
1146 @param filename: the filename of the file to check against the DB
1148 @type location_id: int
1149 @param location_id: the id of the location to look in (optional)
1152 @return: array of PoolFile objects
1155 q = session.query(PoolFile).filter_by(filename=filename)
1157 if location_id is not None:
1158 q = q.join(Location).filter_by(location_id=location_id)
1162 __all__.append('get_poolfile_by_name')
1165 def get_poolfile_like_name(filename, session=None):
1167 Returns an array of PoolFile objects which are like the given name
1169 @type filename: string
1170 @param filename: the filename of the file to check against the DB
1173 @return: array of PoolFile objects
1176 # TODO: There must be a way of properly using bind parameters with %FOO%
1177 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1181 __all__.append('get_poolfile_like_name')
1184 def add_poolfile(filename, datadict, location_id, session=None):
1186 Add a new file to the pool
1188 @type filename: string
1189 @param filename: filename
1191 @type datadict: dict
1192 @param datadict: dict with needed data
1194 @type location_id: int
1195 @param location_id: database id of the location
1198 @return: the PoolFile object created
1200 poolfile = PoolFile()
1201 poolfile.filename = filename
1202 poolfile.filesize = datadict["size"]
1203 poolfile.md5sum = datadict["md5sum"]
1204 poolfile.sha1sum = datadict["sha1sum"]
1205 poolfile.sha256sum = datadict["sha256sum"]
1206 poolfile.location_id = location_id
1208 session.add(poolfile)
1209 # Flush to get a file id (NB: This is not a commit)
1214 __all__.append('add_poolfile')
1216 ################################################################################
1218 class Fingerprint(object):
1219 def __init__(self, *args, **kwargs):
1223 return '<Fingerprint %s>' % self.fingerprint
1225 __all__.append('Fingerprint')
1228 def get_fingerprint(fpr, session=None):
1230 Returns Fingerprint object for given fpr.
1233 @param fpr: The fpr to find / add
1235 @type session: SQLAlchemy
1236 @param session: Optional SQL session object (a temporary one will be
1237 generated if not supplied).
1240 @return: the Fingerprint object for the given fpr or None
1243 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1247 except NoResultFound:
1252 __all__.append('get_fingerprint')
1255 def get_or_set_fingerprint(fpr, session=None):
1257 Returns Fingerprint object for given fpr.
1259 If no matching fpr is found, a row is inserted.
1262 @param fpr: The fpr to find / add
1264 @type session: SQLAlchemy
1265 @param session: Optional SQL session object (a temporary one will be
1266 generated if not supplied). If not passed, a commit will be performed at
1267 the end of the function, otherwise the caller is responsible for commiting.
1268 A flush will be performed either way.
1271 @return: the Fingerprint object for the given fpr
1274 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1278 except NoResultFound:
1279 fingerprint = Fingerprint()
1280 fingerprint.fingerprint = fpr
1281 session.add(fingerprint)
1282 session.commit_or_flush()
1287 __all__.append('get_or_set_fingerprint')
1289 ################################################################################
1291 # Helper routine for Keyring class
1292 def get_ldap_name(entry):
1294 for k in ["cn", "mn", "sn"]:
1296 if ret and ret[0] != "" and ret[0] != "-":
1298 return " ".join(name)
1300 ################################################################################
1302 class Keyring(object):
1303 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1304 " --with-colons --fingerprint --fingerprint"
1309 def __init__(self, *args, **kwargs):
1313 return '<Keyring %s>' % self.keyring_name
1315 def de_escape_gpg_str(self, txt):
1316 esclist = re.split(r'(\\x..)', txt)
1317 for x in range(1,len(esclist),2):
1318 esclist[x] = "%c" % (int(esclist[x][2:],16))
1319 return "".join(esclist)
1321 def parse_address(self, uid):
1322 """parses uid and returns a tuple of real name and email address"""
1324 (name, address) = email.Utils.parseaddr(uid)
1325 name = re.sub(r"\s*[(].*[)]", "", name)
1326 name = self.de_escape_gpg_str(name)
1329 return (name, address)
1331 def load_keys(self, keyring):
1332 if not self.keyring_id:
1333 raise Exception('Must be initialized with database information')
1335 k = os.popen(self.gpg_invocation % keyring, "r")
1339 for line in k.xreadlines():
1340 field = line.split(":")
1341 if field[0] == "pub":
1344 (name, addr) = self.parse_address(field[9])
1346 self.keys[key]["email"] = addr
1347 self.keys[key]["name"] = name
1348 self.keys[key]["fingerprints"] = []
1350 elif key and field[0] == "sub" and len(field) >= 12:
1351 signingkey = ("s" in field[11])
1352 elif key and field[0] == "uid":
1353 (name, addr) = self.parse_address(field[9])
1354 if "email" not in self.keys[key] and "@" in addr:
1355 self.keys[key]["email"] = addr
1356 self.keys[key]["name"] = name
1357 elif signingkey and field[0] == "fpr":
1358 self.keys[key]["fingerprints"].append(field[9])
1359 self.fpr_lookup[field[9]] = key
1361 def import_users_from_ldap(self, session):
1365 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1366 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1368 l = ldap.open(LDAPServer)
1369 l.simple_bind_s("","")
1370 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1371 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1372 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1374 ldap_fin_uid_id = {}
1381 uid = entry["uid"][0]
1382 name = get_ldap_name(entry)
1383 fingerprints = entry["keyFingerPrint"]
1385 for f in fingerprints:
1386 key = self.fpr_lookup.get(f, None)
1387 if key not in self.keys:
1389 self.keys[key]["uid"] = uid
1393 keyid = get_or_set_uid(uid, session).uid_id
1394 byuid[keyid] = (uid, name)
1395 byname[uid] = (keyid, name)
1397 return (byname, byuid)
1399 def generate_users_from_keyring(self, format, session):
1403 for x in self.keys.keys():
1404 if "email" not in self.keys[x]:
1406 self.keys[x]["uid"] = format % "invalid-uid"
1408 uid = format % self.keys[x]["email"]
1409 keyid = get_or_set_uid(uid, session).uid_id
1410 byuid[keyid] = (uid, self.keys[x]["name"])
1411 byname[uid] = (keyid, self.keys[x]["name"])
1412 self.keys[x]["uid"] = uid
1415 uid = format % "invalid-uid"
1416 keyid = get_or_set_uid(uid, session).uid_id
1417 byuid[keyid] = (uid, "ungeneratable user id")
1418 byname[uid] = (keyid, "ungeneratable user id")
1420 return (byname, byuid)
1422 __all__.append('Keyring')
1425 def get_keyring(keyring, session=None):
1427 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1428 If C{keyring} already has an entry, simply return the existing Keyring
1430 @type keyring: string
1431 @param keyring: the keyring name
1434 @return: the Keyring object for this keyring
1437 q = session.query(Keyring).filter_by(keyring_name=keyring)
1441 except NoResultFound:
1444 __all__.append('get_keyring')
1446 ################################################################################
1448 class KeyringACLMap(object):
1449 def __init__(self, *args, **kwargs):
1453 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1455 __all__.append('KeyringACLMap')
1457 ################################################################################
1459 class DBChange(object):
1460 def __init__(self, *args, **kwargs):
1464 return '<DBChange %s>' % self.changesname
1466 def clean_from_queue(self):
1467 session = DBConn().session().object_session(self)
1469 # Remove changes_pool_files entries
1472 # Remove changes_pending_files references
1475 # Clear out of queue
1476 self.in_queue = None
1477 self.approved_for_id = None
1479 __all__.append('DBChange')
1482 def get_dbchange(filename, session=None):
1484 returns DBChange object for given C{filename}.
1486 @type filename: string
1487 @param filename: the name of the file
1489 @type session: Session
1490 @param session: Optional SQLA session object (a temporary one will be
1491 generated if not supplied)
1494 @return: DBChange object for the given filename (C{None} if not present)
1497 q = session.query(DBChange).filter_by(changesname=filename)
1501 except NoResultFound:
1504 __all__.append('get_dbchange')
1506 ################################################################################
1508 class Location(object):
1509 def __init__(self, *args, **kwargs):
1513 return '<Location %s (%s)>' % (self.path, self.location_id)
1515 __all__.append('Location')
1518 def get_location(location, component=None, archive=None, session=None):
1520 Returns Location object for the given combination of location, component
1523 @type location: string
1524 @param location: the path of the location, e.g. I{/srv/ftp-master.debian.org/ftp/pool/}
1526 @type component: string
1527 @param component: the component name (if None, no restriction applied)
1529 @type archive: string
1530 @param archive: the archive name (if None, no restriction applied)
1532 @rtype: Location / None
1533 @return: Either a Location object or None if one can't be found
1536 q = session.query(Location).filter_by(path=location)
1538 if archive is not None:
1539 q = q.join(Archive).filter_by(archive_name=archive)
1541 if component is not None:
1542 q = q.join(Component).filter_by(component_name=component)
1546 except NoResultFound:
1549 __all__.append('get_location')
1551 ################################################################################
1553 class Maintainer(object):
1554 def __init__(self, *args, **kwargs):
1558 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1560 def get_split_maintainer(self):
1561 if not hasattr(self, 'name') or self.name is None:
1562 return ('', '', '', '')
1564 return fix_maintainer(self.name.strip())
1566 __all__.append('Maintainer')
1569 def get_or_set_maintainer(name, session=None):
1571 Returns Maintainer object for given maintainer name.
1573 If no matching maintainer name is found, a row is inserted.
1576 @param name: The maintainer name to add
1578 @type session: SQLAlchemy
1579 @param session: Optional SQL session object (a temporary one will be
1580 generated if not supplied). If not passed, a commit will be performed at
1581 the end of the function, otherwise the caller is responsible for commiting.
1582 A flush will be performed either way.
1585 @return: the Maintainer object for the given maintainer
1588 q = session.query(Maintainer).filter_by(name=name)
1591 except NoResultFound:
1592 maintainer = Maintainer()
1593 maintainer.name = name
1594 session.add(maintainer)
1595 session.commit_or_flush()
1600 __all__.append('get_or_set_maintainer')
1603 def get_maintainer(maintainer_id, session=None):
1605 Return the name of the maintainer behind C{maintainer_id} or None if that
1606 maintainer_id is invalid.
1608 @type maintainer_id: int
1609 @param maintainer_id: the id of the maintainer
1612 @return: the Maintainer with this C{maintainer_id}
1615 return session.query(Maintainer).get(maintainer_id)
1617 __all__.append('get_maintainer')
1619 ################################################################################
1621 class NewComment(object):
1622 def __init__(self, *args, **kwargs):
1626 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1628 __all__.append('NewComment')
1631 def has_new_comment(package, version, session=None):
1633 Returns true if the given combination of C{package}, C{version} has a comment.
1635 @type package: string
1636 @param package: name of the package
1638 @type version: string
1639 @param version: package version
1641 @type session: Session
1642 @param session: Optional SQLA session object (a temporary one will be
1643 generated if not supplied)
1649 q = session.query(NewComment)
1650 q = q.filter_by(package=package)
1651 q = q.filter_by(version=version)
1653 return bool(q.count() > 0)
1655 __all__.append('has_new_comment')
1658 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1660 Returns (possibly empty) list of NewComment objects for the given
1663 @type package: string (optional)
1664 @param package: name of the package
1666 @type version: string (optional)
1667 @param version: package version
1669 @type comment_id: int (optional)
1670 @param comment_id: An id of a comment
1672 @type session: Session
1673 @param session: Optional SQLA session object (a temporary one will be
1674 generated if not supplied)
1677 @return: A (possibly empty) list of NewComment objects will be returned
1680 q = session.query(NewComment)
1681 if package is not None: q = q.filter_by(package=package)
1682 if version is not None: q = q.filter_by(version=version)
1683 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1687 __all__.append('get_new_comments')
1689 ################################################################################
1691 class Override(object):
1692 def __init__(self, *args, **kwargs):
1696 return '<Override %s (%s)>' % (self.package, self.suite_id)
1698 __all__.append('Override')
1701 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1703 Returns Override object for the given parameters
1705 @type package: string
1706 @param package: The name of the package
1708 @type suite: string, list or None
1709 @param suite: The name of the suite (or suites if a list) to limit to. If
1710 None, don't limit. Defaults to None.
1712 @type component: string, list or None
1713 @param component: The name of the component (or components if a list) to
1714 limit to. If None, don't limit. Defaults to None.
1716 @type overridetype: string, list or None
1717 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1718 limit to. If None, don't limit. Defaults to None.
1720 @type session: Session
1721 @param session: Optional SQLA session object (a temporary one will be
1722 generated if not supplied)
1725 @return: A (possibly empty) list of Override objects will be returned
1728 q = session.query(Override)
1729 q = q.filter_by(package=package)
1731 if suite is not None:
1732 if not isinstance(suite, list): suite = [suite]
1733 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1735 if component is not None:
1736 if not isinstance(component, list): component = [component]
1737 q = q.join(Component).filter(Component.component_name.in_(component))
1739 if overridetype is not None:
1740 if not isinstance(overridetype, list): overridetype = [overridetype]
1741 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1745 __all__.append('get_override')
1748 ################################################################################
1750 class OverrideType(object):
1751 def __init__(self, *args, **kwargs):
1755 return '<OverrideType %s>' % self.overridetype
1757 __all__.append('OverrideType')
1760 def get_override_type(override_type, session=None):
1762 Returns OverrideType object for given C{override type}.
1764 @type override_type: string
1765 @param override_type: The name of the override type
1767 @type session: Session
1768 @param session: Optional SQLA session object (a temporary one will be
1769 generated if not supplied)
1772 @return: the database id for the given override type
1775 q = session.query(OverrideType).filter_by(overridetype=override_type)
1779 except NoResultFound:
1782 __all__.append('get_override_type')
1784 ################################################################################
1786 class DebContents(object):
1787 def __init__(self, *args, **kwargs):
1791 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1793 __all__.append('DebContents')
1796 class UdebContents(object):
1797 def __init__(self, *args, **kwargs):
1801 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1803 __all__.append('UdebContents')
1805 class PendingBinContents(object):
1806 def __init__(self, *args, **kwargs):
1810 return '<PendingBinContents %s>' % self.contents_id
1812 __all__.append('PendingBinContents')
1814 def insert_pending_content_paths(package,
1819 Make sure given paths are temporarily associated with given
1823 @param package: the package to associate with should have been read in from the binary control file
1824 @type fullpaths: list
1825 @param fullpaths: the list of paths of the file being associated with the binary
1826 @type session: SQLAlchemy session
1827 @param session: Optional SQLAlchemy session. If this is passed, the caller
1828 is responsible for ensuring a transaction has begun and committing the
1829 results or rolling back based on the result code. If not passed, a commit
1830 will be performed at the end of the function
1832 @return: True upon success, False if there is a problem
1835 privatetrans = False
1838 session = DBConn().session()
1842 arch = get_architecture(package['Architecture'], session)
1843 arch_id = arch.arch_id
1845 # Remove any already existing recorded files for this package
1846 q = session.query(PendingBinContents)
1847 q = q.filter_by(package=package['Package'])
1848 q = q.filter_by(version=package['Version'])
1849 q = q.filter_by(architecture=arch_id)
1852 for fullpath in fullpaths:
1854 if fullpath.startswith( "./" ):
1855 fullpath = fullpath[2:]
1857 pca = PendingBinContents()
1858 pca.package = package['Package']
1859 pca.version = package['Version']
1861 pca.architecture = arch_id
1864 pca.type = 8 # gross
1866 pca.type = 7 # also gross
1869 # Only commit if we set up the session ourself
1877 except Exception, e:
1878 traceback.print_exc()
1880 # Only rollback if we set up the session ourself
1887 __all__.append('insert_pending_content_paths')
1889 ################################################################################
1891 class PolicyQueue(object):
1892 def __init__(self, *args, **kwargs):
1896 return '<PolicyQueue %s>' % self.queue_name
1898 __all__.append('PolicyQueue')
1901 def get_policy_queue(queuename, session=None):
1903 Returns PolicyQueue object for given C{queue name}
1905 @type queuename: string
1906 @param queuename: The name of the queue
1908 @type session: Session
1909 @param session: Optional SQLA session object (a temporary one will be
1910 generated if not supplied)
1913 @return: PolicyQueue object for the given queue
1916 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1920 except NoResultFound:
1923 __all__.append('get_policy_queue')
1926 def get_policy_queue_from_path(pathname, session=None):
1928 Returns PolicyQueue object for given C{path name}
1930 @type queuename: string
1931 @param queuename: The path
1933 @type session: Session
1934 @param session: Optional SQLA session object (a temporary one will be
1935 generated if not supplied)
1938 @return: PolicyQueue object for the given queue
1941 q = session.query(PolicyQueue).filter_by(path=pathname)
1945 except NoResultFound:
1948 __all__.append('get_policy_queue_from_path')
1950 ################################################################################
1952 class Priority(object):
1953 def __init__(self, *args, **kwargs):
1956 def __eq__(self, val):
1957 if isinstance(val, str):
1958 return (self.priority == val)
1959 # This signals to use the normal comparison operator
1960 return NotImplemented
1962 def __ne__(self, val):
1963 if isinstance(val, str):
1964 return (self.priority != val)
1965 # This signals to use the normal comparison operator
1966 return NotImplemented
1969 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1971 __all__.append('Priority')
1974 def get_priority(priority, session=None):
1976 Returns Priority object for given C{priority name}.
1978 @type priority: string
1979 @param priority: The name of the priority
1981 @type session: Session
1982 @param session: Optional SQLA session object (a temporary one will be
1983 generated if not supplied)
1986 @return: Priority object for the given priority
1989 q = session.query(Priority).filter_by(priority=priority)
1993 except NoResultFound:
1996 __all__.append('get_priority')
1999 def get_priorities(session=None):
2001 Returns dictionary of priority names -> id mappings
2003 @type session: Session
2004 @param session: Optional SQL session object (a temporary one will be
2005 generated if not supplied)
2008 @return: dictionary of priority names -> id mappings
2012 q = session.query(Priority)
2014 ret[x.priority] = x.priority_id
2018 __all__.append('get_priorities')
2020 ################################################################################
2022 class Section(object):
2023 def __init__(self, *args, **kwargs):
2026 def __eq__(self, val):
2027 if isinstance(val, str):
2028 return (self.section == val)
2029 # This signals to use the normal comparison operator
2030 return NotImplemented
2032 def __ne__(self, val):
2033 if isinstance(val, str):
2034 return (self.section != val)
2035 # This signals to use the normal comparison operator
2036 return NotImplemented
2039 return '<Section %s>' % self.section
2041 __all__.append('Section')
2044 def get_section(section, session=None):
2046 Returns Section object for given C{section name}.
2048 @type section: string
2049 @param section: The name of the section
2051 @type session: Session
2052 @param session: Optional SQLA session object (a temporary one will be
2053 generated if not supplied)
2056 @return: Section object for the given section name
2059 q = session.query(Section).filter_by(section=section)
2063 except NoResultFound:
2066 __all__.append('get_section')
2069 def get_sections(session=None):
2071 Returns dictionary of section names -> id mappings
2073 @type session: Session
2074 @param session: Optional SQL session object (a temporary one will be
2075 generated if not supplied)
2078 @return: dictionary of section names -> id mappings
2082 q = session.query(Section)
2084 ret[x.section] = x.section_id
2088 __all__.append('get_sections')
2090 ################################################################################
2092 class DBSource(object):
2093 def __init__(self, *args, **kwargs):
2097 return '<DBSource %s (%s)>' % (self.source, self.version)
2099 __all__.append('DBSource')
2102 def source_exists(source, source_version, suites = ["any"], session=None):
2104 Ensure that source exists somewhere in the archive for the binary
2105 upload being processed.
2106 1. exact match => 1.0-3
2107 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2109 @type source: string
2110 @param source: source name
2112 @type source_version: string
2113 @param source_version: expected source version
2116 @param suites: list of suites to check in, default I{any}
2118 @type session: Session
2119 @param session: Optional SQLA session object (a temporary one will be
2120 generated if not supplied)
2123 @return: returns 1 if a source with expected version is found, otherwise 0
2130 for suite in suites:
2131 q = session.query(DBSource).filter_by(source=source)
2133 # source must exist in suite X, or in some other suite that's
2134 # mapped to X, recursively... silent-maps are counted too,
2135 # unreleased-maps aren't.
2136 maps = cnf.ValueList("SuiteMappings")[:]
2138 maps = [ m.split() for m in maps ]
2139 maps = [ (x[1], x[2]) for x in maps
2140 if x[0] == "map" or x[0] == "silent-map" ]
2143 if x[1] in s and x[0] not in s:
2146 q = q.join(SrcAssociation).join(Suite)
2147 q = q.filter(Suite.suite_name.in_(s))
2149 # Reduce the query results to a list of version numbers
2150 ql = [ j.version for j in q.all() ]
2153 if source_version in ql:
2157 from daklib.regexes import re_bin_only_nmu
2158 orig_source_version = re_bin_only_nmu.sub('', source_version)
2159 if orig_source_version in ql:
2162 # No source found so return not ok
2167 __all__.append('source_exists')
2170 def get_suites_source_in(source, session=None):
2172 Returns list of Suite objects which given C{source} name is in
2175 @param source: DBSource package name to search for
2178 @return: list of Suite objects for the given source
2181 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2183 __all__.append('get_suites_source_in')
2186 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2188 Returns list of DBSource objects for given C{source} name and other parameters
2191 @param source: DBSource package name to search for
2193 @type version: str or None
2194 @param version: DBSource version name to search for or None if not applicable
2196 @type dm_upload_allowed: bool
2197 @param dm_upload_allowed: If None, no effect. If True or False, only
2198 return packages with that dm_upload_allowed setting
2200 @type session: Session
2201 @param session: Optional SQL session object (a temporary one will be
2202 generated if not supplied)
2205 @return: list of DBSource objects for the given name (may be empty)
2208 q = session.query(DBSource).filter_by(source=source)
2210 if version is not None:
2211 q = q.filter_by(version=version)
2213 if dm_upload_allowed is not None:
2214 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2218 __all__.append('get_sources_from_name')
2221 def get_source_in_suite(source, suite, session=None):
2223 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2225 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2226 - B{suite} - a suite name, eg. I{unstable}
2228 @type source: string
2229 @param source: source package name
2232 @param suite: the suite name
2235 @return: the version for I{source} in I{suite}
2239 q = session.query(SrcAssociation)
2240 q = q.join('source').filter_by(source=source)
2241 q = q.join('suite').filter_by(suite_name=suite)
2244 return q.one().source
2245 except NoResultFound:
2248 __all__.append('get_source_in_suite')
2250 ################################################################################
2253 def add_dsc_to_db(u, filename, session=None):
2254 entry = u.pkg.files[filename]
2258 source.source = u.pkg.dsc["source"]
2259 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2260 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2261 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2262 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2263 source.install_date = datetime.now().date()
2265 dsc_component = entry["component"]
2266 dsc_location_id = entry["location id"]
2268 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2270 # Set up a new poolfile if necessary
2271 if not entry.has_key("files id") or not entry["files id"]:
2272 filename = entry["pool name"] + filename
2273 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2275 pfs.append(poolfile)
2276 entry["files id"] = poolfile.file_id
2278 source.poolfile_id = entry["files id"]
2282 for suite_name in u.pkg.changes["distribution"].keys():
2283 sa = SrcAssociation()
2284 sa.source_id = source.source_id
2285 sa.suite_id = get_suite(suite_name).suite_id
2290 # Add the source files to the DB (files and dsc_files)
2292 dscfile.source_id = source.source_id
2293 dscfile.poolfile_id = entry["files id"]
2294 session.add(dscfile)
2296 for dsc_file, dentry in u.pkg.dsc_files.items():
2298 df.source_id = source.source_id
2300 # If the .orig tarball is already in the pool, it's
2301 # files id is stored in dsc_files by check_dsc().
2302 files_id = dentry.get("files id", None)
2304 # Find the entry in the files hash
2305 # TODO: Bail out here properly
2307 for f, e in u.pkg.files.items():
2312 if files_id is None:
2313 filename = dfentry["pool name"] + dsc_file
2315 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2316 # FIXME: needs to check for -1/-2 and or handle exception
2317 if found and obj is not None:
2318 files_id = obj.file_id
2321 # If still not found, add it
2322 if files_id is None:
2323 # HACK: Force sha1sum etc into dentry
2324 dentry["sha1sum"] = dfentry["sha1sum"]
2325 dentry["sha256sum"] = dfentry["sha256sum"]
2326 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2327 pfs.append(poolfile)
2328 files_id = poolfile.file_id
2330 poolfile = get_poolfile_by_id(files_id, session)
2331 if poolfile is None:
2332 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2333 pfs.append(poolfile)
2335 df.poolfile_id = files_id
2340 # Add the src_uploaders to the DB
2341 uploader_ids = [source.maintainer_id]
2342 if u.pkg.dsc.has_key("uploaders"):
2343 for up in u.pkg.dsc["uploaders"].replace(">, ", ">\t").split("\t"):
2345 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2348 for up_id in uploader_ids:
2349 if added_ids.has_key(up_id):
2351 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2357 su.maintainer_id = up_id
2358 su.source_id = source.source_id
2363 return source, dsc_component, dsc_location_id, pfs
2365 __all__.append('add_dsc_to_db')
2368 def add_deb_to_db(u, filename, session=None):
2370 Contrary to what you might expect, this routine deals with both
2371 debs and udebs. That info is in 'dbtype', whilst 'type' is
2372 'deb' for both of them
2375 entry = u.pkg.files[filename]
2378 bin.package = entry["package"]
2379 bin.version = entry["version"]
2380 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2381 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2382 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2383 bin.binarytype = entry["dbtype"]
2386 filename = entry["pool name"] + filename
2387 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2388 if not entry.get("location id", None):
2389 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2391 if entry.get("files id", None):
2392 poolfile = get_poolfile_by_id(bin.poolfile_id)
2393 bin.poolfile_id = entry["files id"]
2395 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2396 bin.poolfile_id = entry["files id"] = poolfile.file_id
2399 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2400 if len(bin_sources) != 1:
2401 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2402 (bin.package, bin.version, entry["architecture"],
2403 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2405 bin.source_id = bin_sources[0].source_id
2407 # Add and flush object so it has an ID
2411 # Add BinAssociations
2412 for suite_name in u.pkg.changes["distribution"].keys():
2413 ba = BinAssociation()
2414 ba.binary_id = bin.binary_id
2415 ba.suite_id = get_suite(suite_name).suite_id
2420 # Deal with contents - disabled for now
2421 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2423 # print "REJECT\nCould not determine contents of package %s" % bin.package
2424 # session.rollback()
2425 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2429 __all__.append('add_deb_to_db')
2431 ################################################################################
2433 class SourceACL(object):
2434 def __init__(self, *args, **kwargs):
2438 return '<SourceACL %s>' % self.source_acl_id
2440 __all__.append('SourceACL')
2442 ################################################################################
2444 class SrcAssociation(object):
2445 def __init__(self, *args, **kwargs):
2449 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2451 __all__.append('SrcAssociation')
2453 ################################################################################
2455 class SrcFormat(object):
2456 def __init__(self, *args, **kwargs):
2460 return '<SrcFormat %s>' % (self.format_name)
2462 __all__.append('SrcFormat')
2464 ################################################################################
2466 class SrcUploader(object):
2467 def __init__(self, *args, **kwargs):
2471 return '<SrcUploader %s>' % self.uploader_id
2473 __all__.append('SrcUploader')
2475 ################################################################################
2477 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2478 ('SuiteID', 'suite_id'),
2479 ('Version', 'version'),
2480 ('Origin', 'origin'),
2482 ('Description', 'description'),
2483 ('Untouchable', 'untouchable'),
2484 ('Announce', 'announce'),
2485 ('Codename', 'codename'),
2486 ('OverrideCodename', 'overridecodename'),
2487 ('ValidTime', 'validtime'),
2488 ('Priority', 'priority'),
2489 ('NotAutomatic', 'notautomatic'),
2490 ('CopyChanges', 'copychanges'),
2491 ('OverrideSuite', 'overridesuite')]
2493 class Suite(object):
2494 def __init__(self, *args, **kwargs):
2498 return '<Suite %s>' % self.suite_name
2500 def __eq__(self, val):
2501 if isinstance(val, str):
2502 return (self.suite_name == val)
2503 # This signals to use the normal comparison operator
2504 return NotImplemented
2506 def __ne__(self, val):
2507 if isinstance(val, str):
2508 return (self.suite_name != val)
2509 # This signals to use the normal comparison operator
2510 return NotImplemented
2514 for disp, field in SUITE_FIELDS:
2515 val = getattr(self, field, None)
2517 ret.append("%s: %s" % (disp, val))
2519 return "\n".join(ret)
2521 __all__.append('Suite')
2524 def get_suite_architecture(suite, architecture, session=None):
2526 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2530 @param suite: Suite name to search for
2532 @type architecture: str
2533 @param architecture: Architecture name to search for
2535 @type session: Session
2536 @param session: Optional SQL session object (a temporary one will be
2537 generated if not supplied)
2539 @rtype: SuiteArchitecture
2540 @return: the SuiteArchitecture object or None
2543 q = session.query(SuiteArchitecture)
2544 q = q.join(Architecture).filter_by(arch_string=architecture)
2545 q = q.join(Suite).filter_by(suite_name=suite)
2549 except NoResultFound:
2552 __all__.append('get_suite_architecture')
2555 def get_suite(suite, session=None):
2557 Returns Suite object for given C{suite name}.
2560 @param suite: The name of the suite
2562 @type session: Session
2563 @param session: Optional SQLA session object (a temporary one will be
2564 generated if not supplied)
2567 @return: Suite object for the requested suite name (None if not present)
2570 q = session.query(Suite).filter_by(suite_name=suite)
2574 except NoResultFound:
2577 __all__.append('get_suite')
2579 ################################################################################
2581 class SuiteArchitecture(object):
2582 def __init__(self, *args, **kwargs):
2586 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2588 __all__.append('SuiteArchitecture')
2591 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2593 Returns list of Architecture objects for given C{suite} name
2596 @param suite: Suite name to search for
2598 @type skipsrc: boolean
2599 @param skipsrc: Whether to skip returning the 'source' architecture entry
2602 @type skipall: boolean
2603 @param skipall: Whether to skip returning the 'all' architecture entry
2606 @type session: Session
2607 @param session: Optional SQL session object (a temporary one will be
2608 generated if not supplied)
2611 @return: list of Architecture objects for the given name (may be empty)
2614 q = session.query(Architecture)
2615 q = q.join(SuiteArchitecture)
2616 q = q.join(Suite).filter_by(suite_name=suite)
2619 q = q.filter(Architecture.arch_string != 'source')
2622 q = q.filter(Architecture.arch_string != 'all')
2624 q = q.order_by('arch_string')
2628 __all__.append('get_suite_architectures')
2630 ################################################################################
2632 class SuiteSrcFormat(object):
2633 def __init__(self, *args, **kwargs):
2637 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2639 __all__.append('SuiteSrcFormat')
2642 def get_suite_src_formats(suite, session=None):
2644 Returns list of allowed SrcFormat for C{suite}.
2647 @param suite: Suite name to search for
2649 @type session: Session
2650 @param session: Optional SQL session object (a temporary one will be
2651 generated if not supplied)
2654 @return: the list of allowed source formats for I{suite}
2657 q = session.query(SrcFormat)
2658 q = q.join(SuiteSrcFormat)
2659 q = q.join(Suite).filter_by(suite_name=suite)
2660 q = q.order_by('format_name')
2664 __all__.append('get_suite_src_formats')
2666 ################################################################################
2669 def __init__(self, *args, **kwargs):
2672 def __eq__(self, val):
2673 if isinstance(val, str):
2674 return (self.uid == val)
2675 # This signals to use the normal comparison operator
2676 return NotImplemented
2678 def __ne__(self, val):
2679 if isinstance(val, str):
2680 return (self.uid != val)
2681 # This signals to use the normal comparison operator
2682 return NotImplemented
2685 return '<Uid %s (%s)>' % (self.uid, self.name)
2687 __all__.append('Uid')
2690 def get_or_set_uid(uidname, session=None):
2692 Returns uid object for given uidname.
2694 If no matching uidname is found, a row is inserted.
2696 @type uidname: string
2697 @param uidname: The uid to add
2699 @type session: SQLAlchemy
2700 @param session: Optional SQL session object (a temporary one will be
2701 generated if not supplied). If not passed, a commit will be performed at
2702 the end of the function, otherwise the caller is responsible for commiting.
2705 @return: the uid object for the given uidname
2708 q = session.query(Uid).filter_by(uid=uidname)
2712 except NoResultFound:
2716 session.commit_or_flush()
2721 __all__.append('get_or_set_uid')
2724 def get_uid_from_fingerprint(fpr, session=None):
2725 q = session.query(Uid)
2726 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2730 except NoResultFound:
2733 __all__.append('get_uid_from_fingerprint')
2735 ################################################################################
2737 class UploadBlock(object):
2738 def __init__(self, *args, **kwargs):
2742 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2744 __all__.append('UploadBlock')
2746 ################################################################################
2748 class DBConn(object):
2750 database module init.
2754 def __init__(self, *args, **kwargs):
2755 self.__dict__ = self.__shared_state
2757 if not getattr(self, 'initialised', False):
2758 self.initialised = True
2759 self.debug = kwargs.has_key('debug')
2762 def __setuptables(self):
2772 'build_queue_files',
2775 'changes_pending_binaries',
2776 'changes_pending_files',
2777 'changes_pending_files_map',
2778 'changes_pending_source',
2779 'changes_pending_source_files',
2780 'changes_pool_files',
2793 'pending_bin_contents',
2803 'suite_architectures',
2804 'suite_src_formats',
2805 'suite_build_queue_copy',
2811 for table_name in tables:
2812 table = Table(table_name, self.db_meta, autoload=True)
2813 setattr(self, 'tbl_%s' % table_name, table)
2815 def __setupmappers(self):
2816 mapper(Architecture, self.tbl_architecture,
2817 properties = dict(arch_id = self.tbl_architecture.c.id))
2819 mapper(Archive, self.tbl_archive,
2820 properties = dict(archive_id = self.tbl_archive.c.id,
2821 archive_name = self.tbl_archive.c.name))
2823 mapper(BinAssociation, self.tbl_bin_associations,
2824 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2825 suite_id = self.tbl_bin_associations.c.suite,
2826 suite = relation(Suite),
2827 binary_id = self.tbl_bin_associations.c.bin,
2828 binary = relation(DBBinary)))
2830 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2831 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2832 filename = self.tbl_pending_bin_contents.c.filename,
2833 package = self.tbl_pending_bin_contents.c.package,
2834 version = self.tbl_pending_bin_contents.c.version,
2835 arch = self.tbl_pending_bin_contents.c.arch,
2836 otype = self.tbl_pending_bin_contents.c.type))
2838 mapper(DebContents, self.tbl_deb_contents,
2839 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2840 package=self.tbl_deb_contents.c.package,
2841 suite=self.tbl_deb_contents.c.suite,
2842 arch=self.tbl_deb_contents.c.arch,
2843 section=self.tbl_deb_contents.c.section,
2844 filename=self.tbl_deb_contents.c.filename))
2846 mapper(UdebContents, self.tbl_udeb_contents,
2847 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2848 package=self.tbl_udeb_contents.c.package,
2849 suite=self.tbl_udeb_contents.c.suite,
2850 arch=self.tbl_udeb_contents.c.arch,
2851 section=self.tbl_udeb_contents.c.section,
2852 filename=self.tbl_udeb_contents.c.filename))
2854 mapper(BuildQueue, self.tbl_build_queue,
2855 properties = dict(queue_id = self.tbl_build_queue.c.id))
2857 mapper(BuildQueueFile, self.tbl_build_queue_files,
2858 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2859 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2861 mapper(DBBinary, self.tbl_binaries,
2862 properties = dict(binary_id = self.tbl_binaries.c.id,
2863 package = self.tbl_binaries.c.package,
2864 version = self.tbl_binaries.c.version,
2865 maintainer_id = self.tbl_binaries.c.maintainer,
2866 maintainer = relation(Maintainer),
2867 source_id = self.tbl_binaries.c.source,
2868 source = relation(DBSource),
2869 arch_id = self.tbl_binaries.c.architecture,
2870 architecture = relation(Architecture),
2871 poolfile_id = self.tbl_binaries.c.file,
2872 poolfile = relation(PoolFile),
2873 binarytype = self.tbl_binaries.c.type,
2874 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2875 fingerprint = relation(Fingerprint),
2876 install_date = self.tbl_binaries.c.install_date,
2877 binassociations = relation(BinAssociation,
2878 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2880 mapper(BinaryACL, self.tbl_binary_acl,
2881 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2883 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2884 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2885 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2886 architecture = relation(Architecture)))
2888 mapper(Component, self.tbl_component,
2889 properties = dict(component_id = self.tbl_component.c.id,
2890 component_name = self.tbl_component.c.name))
2892 mapper(DBConfig, self.tbl_config,
2893 properties = dict(config_id = self.tbl_config.c.id))
2895 mapper(DSCFile, self.tbl_dsc_files,
2896 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2897 source_id = self.tbl_dsc_files.c.source,
2898 source = relation(DBSource),
2899 poolfile_id = self.tbl_dsc_files.c.file,
2900 poolfile = relation(PoolFile)))
2902 mapper(PoolFile, self.tbl_files,
2903 properties = dict(file_id = self.tbl_files.c.id,
2904 filesize = self.tbl_files.c.size,
2905 location_id = self.tbl_files.c.location,
2906 location = relation(Location)))
2908 mapper(Fingerprint, self.tbl_fingerprint,
2909 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2910 uid_id = self.tbl_fingerprint.c.uid,
2911 uid = relation(Uid),
2912 keyring_id = self.tbl_fingerprint.c.keyring,
2913 keyring = relation(Keyring),
2914 source_acl = relation(SourceACL),
2915 binary_acl = relation(BinaryACL)))
2917 mapper(Keyring, self.tbl_keyrings,
2918 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2919 keyring_id = self.tbl_keyrings.c.id))
2921 mapper(DBChange, self.tbl_changes,
2922 properties = dict(change_id = self.tbl_changes.c.id,
2923 poolfiles = relation(PoolFile,
2924 secondary=self.tbl_changes_pool_files,
2925 backref="changeslinks"),
2926 seen = self.tbl_changes.c.seen,
2927 source = self.tbl_changes.c.source,
2928 binaries = self.tbl_changes.c.binaries,
2929 architecture = self.tbl_changes.c.architecture,
2930 distribution = self.tbl_changes.c.distribution,
2931 urgency = self.tbl_changes.c.urgency,
2932 maintainer = self.tbl_changes.c.maintainer,
2933 changedby = self.tbl_changes.c.changedby,
2934 date = self.tbl_changes.c.date,
2935 version = self.tbl_changes.c.version,
2936 files = relation(ChangePendingFile,
2937 secondary=self.tbl_changes_pending_files_map,
2938 backref="changesfile"),
2939 in_queue_id = self.tbl_changes.c.in_queue,
2940 in_queue = relation(PolicyQueue,
2941 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2942 approved_for_id = self.tbl_changes.c.approved_for))
2944 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2945 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2947 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2948 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
2949 filename = self.tbl_changes_pending_files.c.filename,
2950 size = self.tbl_changes_pending_files.c.size,
2951 md5sum = self.tbl_changes_pending_files.c.md5sum,
2952 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
2953 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
2955 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2956 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2957 change = relation(DBChange),
2958 maintainer = relation(Maintainer,
2959 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2960 changedby = relation(Maintainer,
2961 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2962 fingerprint = relation(Fingerprint),
2963 source_files = relation(ChangePendingFile,
2964 secondary=self.tbl_changes_pending_source_files,
2965 backref="pending_sources")))
2968 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2969 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2970 keyring = relation(Keyring, backref="keyring_acl_map"),
2971 architecture = relation(Architecture)))
2973 mapper(Location, self.tbl_location,
2974 properties = dict(location_id = self.tbl_location.c.id,
2975 component_id = self.tbl_location.c.component,
2976 component = relation(Component),
2977 archive_id = self.tbl_location.c.archive,
2978 archive = relation(Archive),
2979 archive_type = self.tbl_location.c.type))
2981 mapper(Maintainer, self.tbl_maintainer,
2982 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2984 mapper(NewComment, self.tbl_new_comments,
2985 properties = dict(comment_id = self.tbl_new_comments.c.id))
2987 mapper(Override, self.tbl_override,
2988 properties = dict(suite_id = self.tbl_override.c.suite,
2989 suite = relation(Suite),
2990 package = self.tbl_override.c.package,
2991 component_id = self.tbl_override.c.component,
2992 component = relation(Component),
2993 priority_id = self.tbl_override.c.priority,
2994 priority = relation(Priority),
2995 section_id = self.tbl_override.c.section,
2996 section = relation(Section),
2997 overridetype_id = self.tbl_override.c.type,
2998 overridetype = relation(OverrideType)))
3000 mapper(OverrideType, self.tbl_override_type,
3001 properties = dict(overridetype = self.tbl_override_type.c.type,
3002 overridetype_id = self.tbl_override_type.c.id))
3004 mapper(PolicyQueue, self.tbl_policy_queue,
3005 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
3007 mapper(Priority, self.tbl_priority,
3008 properties = dict(priority_id = self.tbl_priority.c.id))
3010 mapper(Section, self.tbl_section,
3011 properties = dict(section_id = self.tbl_section.c.id,
3012 section=self.tbl_section.c.section))
3014 mapper(DBSource, self.tbl_source,
3015 properties = dict(source_id = self.tbl_source.c.id,
3016 version = self.tbl_source.c.version,
3017 maintainer_id = self.tbl_source.c.maintainer,
3018 maintainer = relation(Maintainer,
3019 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3020 poolfile_id = self.tbl_source.c.file,
3021 poolfile = relation(PoolFile),
3022 fingerprint_id = self.tbl_source.c.sig_fpr,
3023 fingerprint = relation(Fingerprint),
3024 changedby_id = self.tbl_source.c.changedby,
3025 changedby = relation(Maintainer,
3026 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3027 srcfiles = relation(DSCFile,
3028 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3029 srcassociations = relation(SrcAssociation,
3030 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3031 srcuploaders = relation(SrcUploader)))
3033 mapper(SourceACL, self.tbl_source_acl,
3034 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3036 mapper(SrcAssociation, self.tbl_src_associations,
3037 properties = dict(sa_id = self.tbl_src_associations.c.id,
3038 suite_id = self.tbl_src_associations.c.suite,
3039 suite = relation(Suite),
3040 source_id = self.tbl_src_associations.c.source,
3041 source = relation(DBSource)))
3043 mapper(SrcFormat, self.tbl_src_format,
3044 properties = dict(src_format_id = self.tbl_src_format.c.id,
3045 format_name = self.tbl_src_format.c.format_name))
3047 mapper(SrcUploader, self.tbl_src_uploaders,
3048 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3049 source_id = self.tbl_src_uploaders.c.source,
3050 source = relation(DBSource,
3051 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3052 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3053 maintainer = relation(Maintainer,
3054 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3056 mapper(Suite, self.tbl_suite,
3057 properties = dict(suite_id = self.tbl_suite.c.id,
3058 policy_queue = relation(PolicyQueue),
3059 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3061 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3062 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3063 suite = relation(Suite, backref='suitearchitectures'),
3064 arch_id = self.tbl_suite_architectures.c.architecture,
3065 architecture = relation(Architecture)))
3067 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3068 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3069 suite = relation(Suite, backref='suitesrcformats'),
3070 src_format_id = self.tbl_suite_src_formats.c.src_format,
3071 src_format = relation(SrcFormat)))
3073 mapper(Uid, self.tbl_uid,
3074 properties = dict(uid_id = self.tbl_uid.c.id,
3075 fingerprint = relation(Fingerprint)))
3077 mapper(UploadBlock, self.tbl_upload_blocks,
3078 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3079 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3080 uid = relation(Uid, backref="uploadblocks")))
3082 ## Connection functions
3083 def __createconn(self):
3084 from config import Config
3088 connstr = "postgres://%s" % cnf["DB::Host"]
3089 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3090 connstr += ":%s" % cnf["DB::Port"]
3091 connstr += "/%s" % cnf["DB::Name"]
3094 connstr = "postgres:///%s" % cnf["DB::Name"]
3095 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3096 connstr += "?port=%s" % cnf["DB::Port"]
3098 self.db_pg = create_engine(connstr, echo=self.debug)
3099 self.db_meta = MetaData()
3100 self.db_meta.bind = self.db_pg
3101 self.db_smaker = sessionmaker(bind=self.db_pg,
3105 self.__setuptables()
3106 self.__setupmappers()
3109 return self.db_smaker()
3111 __all__.append('DBConn')