5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009, 2010 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from datetime import datetime, timedelta
41 from errno import ENOENT
42 from tempfile import mkstemp, mkdtemp
44 from inspect import getargspec
47 from sqlalchemy import create_engine, Table, MetaData
48 from sqlalchemy.orm import sessionmaker, mapper, relation
49 from sqlalchemy import types as sqltypes
51 # Don't remove this, we re-export the exceptions to scripts which import us
52 from sqlalchemy.exc import *
53 from sqlalchemy.orm.exc import NoResultFound
55 # Only import Config until Queue stuff is changed to store its config
57 from config import Config
58 from textutils import fix_maintainer
60 ################################################################################
62 # Patch in support for the debversion field type so that it works during
65 class DebVersion(sqltypes.Text):
67 Support the debversion type
70 def get_col_spec(self):
73 sa_major_version = sqlalchemy.__version__[0:3]
74 if sa_major_version == "0.5":
75 from sqlalchemy.databases import postgres
76 postgres.ischema_names['debversion'] = DebVersion
78 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
80 ################################################################################
82 __all__ = ['IntegrityError', 'SQLAlchemyError']
84 ################################################################################
86 def session_wrapper(fn):
88 Wrapper around common ".., session=None):" handling. If the wrapped
89 function is called without passing 'session', we create a local one
90 and destroy it when the function ends.
92 Also attaches a commit_or_flush method to the session; if we created a
93 local session, this is a synonym for session.commit(), otherwise it is a
94 synonym for session.flush().
97 def wrapped(*args, **kwargs):
98 private_transaction = False
100 # Find the session object
101 session = kwargs.get('session')
104 if len(args) <= len(getargspec(fn)[0]) - 1:
105 # No session specified as last argument or in kwargs
106 private_transaction = True
107 session = kwargs['session'] = DBConn().session()
109 # Session is last argument in args
113 session = args[-1] = DBConn().session()
114 private_transaction = True
116 if private_transaction:
117 session.commit_or_flush = session.commit
119 session.commit_or_flush = session.flush
122 return fn(*args, **kwargs)
124 if private_transaction:
125 # We created a session; close it.
128 wrapped.__doc__ = fn.__doc__
129 wrapped.func_name = fn.func_name
133 __all__.append('session_wrapper')
135 ################################################################################
137 class Architecture(object):
138 def __init__(self, *args, **kwargs):
141 def __eq__(self, val):
142 if isinstance(val, str):
143 return (self.arch_string== val)
144 # This signals to use the normal comparison operator
145 return NotImplemented
147 def __ne__(self, val):
148 if isinstance(val, str):
149 return (self.arch_string != val)
150 # This signals to use the normal comparison operator
151 return NotImplemented
154 return '<Architecture %s>' % self.arch_string
156 __all__.append('Architecture')
159 def get_architecture(architecture, session=None):
161 Returns database id for given C{architecture}.
163 @type architecture: string
164 @param architecture: The name of the architecture
166 @type session: Session
167 @param session: Optional SQLA session object (a temporary one will be
168 generated if not supplied)
171 @return: Architecture object for the given arch (None if not present)
174 q = session.query(Architecture).filter_by(arch_string=architecture)
178 except NoResultFound:
181 __all__.append('get_architecture')
184 def get_architecture_suites(architecture, session=None):
186 Returns list of Suite objects for given C{architecture} name
188 @type architecture: str
189 @param architecture: Architecture name to search for
191 @type session: Session
192 @param session: Optional SQL session object (a temporary one will be
193 generated if not supplied)
196 @return: list of Suite objects for the given name (may be empty)
199 q = session.query(Suite)
200 q = q.join(SuiteArchitecture)
201 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
207 __all__.append('get_architecture_suites')
209 ################################################################################
211 class Archive(object):
212 def __init__(self, *args, **kwargs):
216 return '<Archive %s>' % self.archive_name
218 __all__.append('Archive')
221 def get_archive(archive, session=None):
223 returns database id for given C{archive}.
225 @type archive: string
226 @param archive: the name of the arhive
228 @type session: Session
229 @param session: Optional SQLA session object (a temporary one will be
230 generated if not supplied)
233 @return: Archive object for the given name (None if not present)
236 archive = archive.lower()
238 q = session.query(Archive).filter_by(archive_name=archive)
242 except NoResultFound:
245 __all__.append('get_archive')
247 ################################################################################
249 class BinAssociation(object):
250 def __init__(self, *args, **kwargs):
254 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
256 __all__.append('BinAssociation')
258 ################################################################################
260 class BinContents(object):
261 def __init__(self, *args, **kwargs):
265 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
267 __all__.append('BinContents')
269 ################################################################################
271 class DBBinary(object):
272 def __init__(self, *args, **kwargs):
276 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
278 __all__.append('DBBinary')
281 def get_suites_binary_in(package, session=None):
283 Returns list of Suite objects which given C{package} name is in
286 @param package: DBBinary package name to search for
289 @return: list of Suite objects for the given package
292 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
294 __all__.append('get_suites_binary_in')
297 def get_binary_from_id(binary_id, session=None):
299 Returns DBBinary object for given C{id}
302 @param binary_id: Id of the required binary
304 @type session: Session
305 @param session: Optional SQLA session object (a temporary one will be
306 generated if not supplied)
309 @return: DBBinary object for the given binary (None if not present)
312 q = session.query(DBBinary).filter_by(binary_id=binary_id)
316 except NoResultFound:
319 __all__.append('get_binary_from_id')
322 def get_binaries_from_name(package, version=None, architecture=None, session=None):
324 Returns list of DBBinary objects for given C{package} name
327 @param package: DBBinary package name to search for
329 @type version: str or None
330 @param version: Version to search for (or None)
332 @type architecture: str, list or None
333 @param architecture: Architectures to limit to (or None if no limit)
335 @type session: Session
336 @param session: Optional SQL session object (a temporary one will be
337 generated if not supplied)
340 @return: list of DBBinary objects for the given name (may be empty)
343 q = session.query(DBBinary).filter_by(package=package)
345 if version is not None:
346 q = q.filter_by(version=version)
348 if architecture is not None:
349 if not isinstance(architecture, list):
350 architecture = [architecture]
351 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
357 __all__.append('get_binaries_from_name')
360 def get_binaries_from_source_id(source_id, session=None):
362 Returns list of DBBinary objects for given C{source_id}
365 @param source_id: source_id to search for
367 @type session: Session
368 @param session: Optional SQL session object (a temporary one will be
369 generated if not supplied)
372 @return: list of DBBinary objects for the given name (may be empty)
375 return session.query(DBBinary).filter_by(source_id=source_id).all()
377 __all__.append('get_binaries_from_source_id')
380 def get_binary_from_name_suite(package, suitename, session=None):
381 ### For dak examine-package
382 ### XXX: Doesn't use object API yet
384 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
385 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
386 WHERE b.package='%(package)s'
388 AND fi.location = l.id
389 AND l.component = c.id
392 AND su.suite_name %(suitename)s
393 ORDER BY b.version DESC"""
395 return session.execute(sql % {'package': package, 'suitename': suitename})
397 __all__.append('get_binary_from_name_suite')
400 def get_binary_components(package, suitename, arch, session=None):
401 # Check for packages that have moved from one component to another
402 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
403 WHERE b.package=:package AND s.suite_name=:suitename
404 AND (a.arch_string = :arch OR a.arch_string = 'all')
405 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
406 AND f.location = l.id
407 AND l.component = c.id
410 vals = {'package': package, 'suitename': suitename, 'arch': arch}
412 return session.execute(query, vals)
414 __all__.append('get_binary_components')
416 ################################################################################
418 class BinaryACL(object):
419 def __init__(self, *args, **kwargs):
423 return '<BinaryACL %s>' % self.binary_acl_id
425 __all__.append('BinaryACL')
427 ################################################################################
429 class BinaryACLMap(object):
430 def __init__(self, *args, **kwargs):
434 return '<BinaryACLMap %s>' % self.binary_acl_map_id
436 __all__.append('BinaryACLMap')
438 ################################################################################
443 ArchiveDir "%(archivepath)s";
444 OverrideDir "/srv/ftp-master.debian.org/scripts/override/";
445 CacheDir "/srv/ftp-master.debian.org/database/";
450 Packages::Compress ". bzip2 gzip";
451 Sources::Compress ". bzip2 gzip";
456 bindirectory "incoming"
461 BinOverride "override.sid.all3";
462 BinCacheDB "packages-accepted.db";
464 FileList "%(filelist)s";
467 Packages::Extensions ".deb .udeb";
470 bindirectory "incoming/"
473 BinOverride "override.sid.all3";
474 SrcOverride "override.sid.all3.src";
475 FileList "%(filelist)s";
479 class BuildQueue(object):
480 def __init__(self, *args, **kwargs):
484 return '<BuildQueue %s>' % self.queue_name
486 def write_metadata(self, starttime, force=False):
487 # Do we write out metafiles?
488 if not (force or self.generate_metadata):
491 session = DBConn().session().object_session(self)
493 fl_fd = fl_name = ac_fd = ac_name = None
495 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
496 startdir = os.getcwd()
499 # Grab files we want to include
500 newer = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) > starttime).all()
501 # Write file list with newer files
502 (fl_fd, fl_name) = mkstemp()
504 os.write(fl_fd, '%s\n' % n.fullpath)
507 # Write minimal apt.conf
508 # TODO: Remove hardcoding from template
509 (ac_fd, ac_name) = mkstemp()
510 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
511 'filelist': fl_name})
514 # Run apt-ftparchive generate
515 os.chdir(os.path.dirname(ac_name))
516 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(ac_name))
518 # Run apt-ftparchive release
519 # TODO: Eww - fix this
520 bname = os.path.basename(self.path)
524 # We have to remove the Release file otherwise it'll be included in the
527 os.unlink(os.path.join(bname, 'Release'))
531 os.system("""apt-ftparchive -qq -o APT::FTPArchive::Release::Origin="%s" -o APT::FTPArchive::Release::Label="%s" -o APT::FTPArchive::Release::Description="%s" -o APT::FTPArchive::Release::Architectures="%s" release %s > Release""" % (self.origin, self.label, self.releasedescription, arches, bname))
533 # Crude hack with open and append, but this whole section is and should be redone.
534 if self.notautomatic:
535 release=open("Release", "a")
536 release.write("NotAutomatic: yes")
542 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
543 if cnf.has_key("Dinstall::SigningPubKeyring"):
544 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
546 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
548 # Move the files if we got this far
549 os.rename('Release', os.path.join(bname, 'Release'))
551 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
553 # Clean up any left behind files
580 def clean_and_update(self, starttime, Logger, dryrun=False):
581 """WARNING: This routine commits for you"""
582 session = DBConn().session().object_session(self)
584 if self.generate_metadata and not dryrun:
585 self.write_metadata(starttime)
587 # Grab files older than our execution time
588 older = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter(BuildQueueFile.lastused + timedelta(seconds=self.stay_of_execution) <= starttime).all()
594 Logger.log(["I: Would have removed %s from the queue" % o.fullpath])
596 Logger.log(["I: Removing %s from the queue" % o.fullpath])
597 os.unlink(o.fullpath)
600 # If it wasn't there, don't worry
601 if e.errno == ENOENT:
604 # TODO: Replace with proper logging call
605 Logger.log(["E: Could not remove %s" % o.fullpath])
612 for f in os.listdir(self.path):
613 if f.startswith('Packages') or f.startswith('Source') or f.startswith('Release'):
617 r = session.query(BuildQueueFile).filter_by(build_queue_id = self.queue_id).filter_by(filename = f).one()
618 except NoResultFound:
619 fp = os.path.join(self.path, f)
621 Logger.log(["I: Would remove unused link %s" % fp])
623 Logger.log(["I: Removing unused link %s" % fp])
627 Logger.log(["E: Failed to unlink unreferenced file %s" % r.fullpath])
629 def add_file_from_pool(self, poolfile):
630 """Copies a file into the pool. Assumes that the PoolFile object is
631 attached to the same SQLAlchemy session as the Queue object is.
633 The caller is responsible for committing after calling this function."""
634 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
636 # Check if we have a file of this name or this ID already
637 for f in self.queuefiles:
638 if f.fileid is not None and f.fileid == poolfile.file_id or \
639 f.poolfile.filename == poolfile_basename:
640 # In this case, update the BuildQueueFile entry so we
641 # don't remove it too early
642 f.lastused = datetime.now()
643 DBConn().session().object_session(poolfile).add(f)
646 # Prepare BuildQueueFile object
647 qf = BuildQueueFile()
648 qf.build_queue_id = self.queue_id
649 qf.lastused = datetime.now()
650 qf.filename = poolfile_basename
652 targetpath = poolfile.fullpath
653 queuepath = os.path.join(self.path, poolfile_basename)
657 # We need to copy instead of symlink
659 utils.copy(targetpath, queuepath)
660 # NULL in the fileid field implies a copy
663 os.symlink(targetpath, queuepath)
664 qf.fileid = poolfile.file_id
668 # Get the same session as the PoolFile is using and add the qf to it
669 DBConn().session().object_session(poolfile).add(qf)
674 __all__.append('BuildQueue')
677 def get_build_queue(queuename, session=None):
679 Returns BuildQueue object for given C{queue name}, creating it if it does not
682 @type queuename: string
683 @param queuename: The name of the queue
685 @type session: Session
686 @param session: Optional SQLA session object (a temporary one will be
687 generated if not supplied)
690 @return: BuildQueue object for the given queue
693 q = session.query(BuildQueue).filter_by(queue_name=queuename)
697 except NoResultFound:
700 __all__.append('get_build_queue')
702 ################################################################################
704 class BuildQueueFile(object):
705 def __init__(self, *args, **kwargs):
709 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
713 return os.path.join(self.buildqueue.path, self.filename)
716 __all__.append('BuildQueueFile')
718 ################################################################################
720 class ChangePendingBinary(object):
721 def __init__(self, *args, **kwargs):
725 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
727 __all__.append('ChangePendingBinary')
729 ################################################################################
731 class ChangePendingFile(object):
732 def __init__(self, *args, **kwargs):
736 return '<ChangePendingFile %s>' % self.change_pending_file_id
738 __all__.append('ChangePendingFile')
740 ################################################################################
742 class ChangePendingSource(object):
743 def __init__(self, *args, **kwargs):
747 return '<ChangePendingSource %s>' % self.change_pending_source_id
749 __all__.append('ChangePendingSource')
751 ################################################################################
753 class Component(object):
754 def __init__(self, *args, **kwargs):
757 def __eq__(self, val):
758 if isinstance(val, str):
759 return (self.component_name == val)
760 # This signals to use the normal comparison operator
761 return NotImplemented
763 def __ne__(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
770 return '<Component %s>' % self.component_name
773 __all__.append('Component')
776 def get_component(component, session=None):
778 Returns database id for given C{component}.
780 @type component: string
781 @param component: The name of the override type
784 @return: the database id for the given component
787 component = component.lower()
789 q = session.query(Component).filter_by(component_name=component)
793 except NoResultFound:
796 __all__.append('get_component')
798 ################################################################################
800 class DBConfig(object):
801 def __init__(self, *args, **kwargs):
805 return '<DBConfig %s>' % self.name
807 __all__.append('DBConfig')
809 ################################################################################
812 def get_or_set_contents_file_id(filename, session=None):
814 Returns database id for given filename.
816 If no matching file is found, a row is inserted.
818 @type filename: string
819 @param filename: The filename
820 @type session: SQLAlchemy
821 @param session: Optional SQL session object (a temporary one will be
822 generated if not supplied). If not passed, a commit will be performed at
823 the end of the function, otherwise the caller is responsible for commiting.
826 @return: the database id for the given component
829 q = session.query(ContentFilename).filter_by(filename=filename)
832 ret = q.one().cafilename_id
833 except NoResultFound:
834 cf = ContentFilename()
835 cf.filename = filename
837 session.commit_or_flush()
838 ret = cf.cafilename_id
842 __all__.append('get_or_set_contents_file_id')
845 def get_contents(suite, overridetype, section=None, session=None):
847 Returns contents for a suite / overridetype combination, limiting
848 to a section if not None.
851 @param suite: Suite object
853 @type overridetype: OverrideType
854 @param overridetype: OverrideType object
856 @type section: Section
857 @param section: Optional section object to limit results to
859 @type session: SQLAlchemy
860 @param session: Optional SQL session object (a temporary one will be
861 generated if not supplied)
864 @return: ResultsProxy object set up to return tuples of (filename, section,
868 # find me all of the contents for a given suite
869 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
873 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
874 JOIN content_file_names n ON (c.filename=n.id)
875 JOIN binaries b ON (b.id=c.binary_pkg)
876 JOIN override o ON (o.package=b.package)
877 JOIN section s ON (s.id=o.section)
878 WHERE o.suite = :suiteid AND o.type = :overridetypeid
879 AND b.type=:overridetypename"""
881 vals = {'suiteid': suite.suite_id,
882 'overridetypeid': overridetype.overridetype_id,
883 'overridetypename': overridetype.overridetype}
885 if section is not None:
886 contents_q += " AND s.id = :sectionid"
887 vals['sectionid'] = section.section_id
889 contents_q += " ORDER BY fn"
891 return session.execute(contents_q, vals)
893 __all__.append('get_contents')
895 ################################################################################
897 class ContentFilepath(object):
898 def __init__(self, *args, **kwargs):
902 return '<ContentFilepath %s>' % self.filepath
904 __all__.append('ContentFilepath')
907 def get_or_set_contents_path_id(filepath, session=None):
909 Returns database id for given path.
911 If no matching file is found, a row is inserted.
913 @type filepath: string
914 @param filepath: The filepath
916 @type session: SQLAlchemy
917 @param session: Optional SQL session object (a temporary one will be
918 generated if not supplied). If not passed, a commit will be performed at
919 the end of the function, otherwise the caller is responsible for commiting.
922 @return: the database id for the given path
925 q = session.query(ContentFilepath).filter_by(filepath=filepath)
928 ret = q.one().cafilepath_id
929 except NoResultFound:
930 cf = ContentFilepath()
931 cf.filepath = filepath
933 session.commit_or_flush()
934 ret = cf.cafilepath_id
938 __all__.append('get_or_set_contents_path_id')
940 ################################################################################
942 class ContentAssociation(object):
943 def __init__(self, *args, **kwargs):
947 return '<ContentAssociation %s>' % self.ca_id
949 __all__.append('ContentAssociation')
951 def insert_content_paths(binary_id, fullpaths, session=None):
953 Make sure given path is associated with given binary id
956 @param binary_id: the id of the binary
957 @type fullpaths: list
958 @param fullpaths: the list of paths of the file being associated with the binary
959 @type session: SQLAlchemy session
960 @param session: Optional SQLAlchemy session. If this is passed, the caller
961 is responsible for ensuring a transaction has begun and committing the
962 results or rolling back based on the result code. If not passed, a commit
963 will be performed at the end of the function, otherwise the caller is
964 responsible for commiting.
966 @return: True upon success
971 session = DBConn().session()
976 def generate_path_dicts():
977 for fullpath in fullpaths:
978 if fullpath.startswith( './' ):
979 fullpath = fullpath[2:]
981 yield {'filename':fullpath, 'id': binary_id }
983 for d in generate_path_dicts():
984 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
993 traceback.print_exc()
995 # Only rollback if we set up the session ourself
1002 __all__.append('insert_content_paths')
1004 ################################################################################
1006 class DSCFile(object):
1007 def __init__(self, *args, **kwargs):
1011 return '<DSCFile %s>' % self.dscfile_id
1013 __all__.append('DSCFile')
1016 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
1018 Returns a list of DSCFiles which may be empty
1020 @type dscfile_id: int (optional)
1021 @param dscfile_id: the dscfile_id of the DSCFiles to find
1023 @type source_id: int (optional)
1024 @param source_id: the source id related to the DSCFiles to find
1026 @type poolfile_id: int (optional)
1027 @param poolfile_id: the poolfile id related to the DSCFiles to find
1030 @return: Possibly empty list of DSCFiles
1033 q = session.query(DSCFile)
1035 if dscfile_id is not None:
1036 q = q.filter_by(dscfile_id=dscfile_id)
1038 if source_id is not None:
1039 q = q.filter_by(source_id=source_id)
1041 if poolfile_id is not None:
1042 q = q.filter_by(poolfile_id=poolfile_id)
1046 __all__.append('get_dscfiles')
1048 ################################################################################
1050 class PoolFile(object):
1051 def __init__(self, *args, **kwargs):
1055 return '<PoolFile %s>' % self.filename
1059 return os.path.join(self.location.path, self.filename)
1061 __all__.append('PoolFile')
1064 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1067 (ValidFileFound [boolean or None], PoolFile object or None)
1069 @type filename: string
1070 @param filename: the filename of the file to check against the DB
1073 @param filesize: the size of the file to check against the DB
1075 @type md5sum: string
1076 @param md5sum: the md5sum of the file to check against the DB
1078 @type location_id: int
1079 @param location_id: the id of the location to look in
1082 @return: Tuple of length 2.
1083 - If more than one file found with that name: (C{None}, C{None})
1084 - If valid pool file found: (C{True}, C{PoolFile object})
1085 - If valid pool file not found:
1086 - (C{False}, C{None}) if no file found
1087 - (C{False}, C{PoolFile object}) if file found with size/md5sum mismatch
1090 q = session.query(PoolFile).filter_by(filename=filename)
1091 q = q.join(Location).filter_by(location_id=location_id)
1101 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1109 __all__.append('check_poolfile')
1112 def get_poolfile_by_id(file_id, session=None):
1114 Returns a PoolFile objects or None for the given id
1117 @param file_id: the id of the file to look for
1119 @rtype: PoolFile or None
1120 @return: either the PoolFile object or None
1123 q = session.query(PoolFile).filter_by(file_id=file_id)
1127 except NoResultFound:
1130 __all__.append('get_poolfile_by_id')
1134 def get_poolfile_by_name(filename, location_id=None, session=None):
1136 Returns an array of PoolFile objects for the given filename and
1137 (optionally) location_id
1139 @type filename: string
1140 @param filename: the filename of the file to check against the DB
1142 @type location_id: int
1143 @param location_id: the id of the location to look in (optional)
1146 @return: array of PoolFile objects
1149 q = session.query(PoolFile).filter_by(filename=filename)
1151 if location_id is not None:
1152 q = q.join(Location).filter_by(location_id=location_id)
1156 __all__.append('get_poolfile_by_name')
1159 def get_poolfile_like_name(filename, session=None):
1161 Returns an array of PoolFile objects which are like the given name
1163 @type filename: string
1164 @param filename: the filename of the file to check against the DB
1167 @return: array of PoolFile objects
1170 # TODO: There must be a way of properly using bind parameters with %FOO%
1171 q = session.query(PoolFile).filter(PoolFile.filename.like('%%/%s' % filename))
1175 __all__.append('get_poolfile_like_name')
1178 def add_poolfile(filename, datadict, location_id, session=None):
1180 Add a new file to the pool
1182 @type filename: string
1183 @param filename: filename
1185 @type datadict: dict
1186 @param datadict: dict with needed data
1188 @type location_id: int
1189 @param location_id: database id of the location
1192 @return: the PoolFile object created
1194 poolfile = PoolFile()
1195 poolfile.filename = filename
1196 poolfile.filesize = datadict["size"]
1197 poolfile.md5sum = datadict["md5sum"]
1198 poolfile.sha1sum = datadict["sha1sum"]
1199 poolfile.sha256sum = datadict["sha256sum"]
1200 poolfile.location_id = location_id
1202 session.add(poolfile)
1203 # Flush to get a file id (NB: This is not a commit)
1208 __all__.append('add_poolfile')
1210 ################################################################################
1212 class Fingerprint(object):
1213 def __init__(self, *args, **kwargs):
1217 return '<Fingerprint %s>' % self.fingerprint
1219 __all__.append('Fingerprint')
1222 def get_fingerprint(fpr, session=None):
1224 Returns Fingerprint object for given fpr.
1227 @param fpr: The fpr to find / add
1229 @type session: SQLAlchemy
1230 @param session: Optional SQL session object (a temporary one will be
1231 generated if not supplied).
1234 @return: the Fingerprint object for the given fpr or None
1237 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1241 except NoResultFound:
1246 __all__.append('get_fingerprint')
1249 def get_or_set_fingerprint(fpr, session=None):
1251 Returns Fingerprint object for given fpr.
1253 If no matching fpr is found, a row is inserted.
1256 @param fpr: The fpr to find / add
1258 @type session: SQLAlchemy
1259 @param session: Optional SQL session object (a temporary one will be
1260 generated if not supplied). If not passed, a commit will be performed at
1261 the end of the function, otherwise the caller is responsible for commiting.
1262 A flush will be performed either way.
1265 @return: the Fingerprint object for the given fpr
1268 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1272 except NoResultFound:
1273 fingerprint = Fingerprint()
1274 fingerprint.fingerprint = fpr
1275 session.add(fingerprint)
1276 session.commit_or_flush()
1281 __all__.append('get_or_set_fingerprint')
1283 ################################################################################
1285 # Helper routine for Keyring class
1286 def get_ldap_name(entry):
1288 for k in ["cn", "mn", "sn"]:
1290 if ret and ret[0] != "" and ret[0] != "-":
1292 return " ".join(name)
1294 ################################################################################
1296 class Keyring(object):
1297 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1298 " --with-colons --fingerprint --fingerprint"
1303 def __init__(self, *args, **kwargs):
1307 return '<Keyring %s>' % self.keyring_name
1309 def de_escape_gpg_str(self, txt):
1310 esclist = re.split(r'(\\x..)', txt)
1311 for x in range(1,len(esclist),2):
1312 esclist[x] = "%c" % (int(esclist[x][2:],16))
1313 return "".join(esclist)
1315 def load_keys(self, keyring):
1318 if not self.keyring_id:
1319 raise Exception('Must be initialized with database information')
1321 k = os.popen(self.gpg_invocation % keyring, "r")
1325 for line in k.xreadlines():
1326 field = line.split(":")
1327 if field[0] == "pub":
1329 (name, addr) = email.Utils.parseaddr(field[9])
1330 name = re.sub(r"\s*[(].*[)]", "", name)
1331 if name == "" or addr == "" or "@" not in addr:
1333 addr = "invalid-uid"
1334 name = self.de_escape_gpg_str(name)
1335 self.keys[key] = {"email": addr}
1337 self.keys[key]["name"] = name
1338 self.keys[key]["aliases"] = [name]
1339 self.keys[key]["fingerprints"] = []
1341 elif key and field[0] == "sub" and len(field) >= 12:
1342 signingkey = ("s" in field[11])
1343 elif key and field[0] == "uid":
1344 (name, addr) = email.Utils.parseaddr(field[9])
1345 if name and name not in self.keys[key]["aliases"]:
1346 self.keys[key]["aliases"].append(name)
1347 elif signingkey and field[0] == "fpr":
1348 self.keys[key]["fingerprints"].append(field[9])
1349 self.fpr_lookup[field[9]] = key
1351 def import_users_from_ldap(self, session):
1355 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1356 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1358 l = ldap.open(LDAPServer)
1359 l.simple_bind_s("","")
1360 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1361 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1362 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1364 ldap_fin_uid_id = {}
1371 uid = entry["uid"][0]
1372 name = get_ldap_name(entry)
1373 fingerprints = entry["keyFingerPrint"]
1375 for f in fingerprints:
1376 key = self.fpr_lookup.get(f, None)
1377 if key not in self.keys:
1379 self.keys[key]["uid"] = uid
1383 keyid = get_or_set_uid(uid, session).uid_id
1384 byuid[keyid] = (uid, name)
1385 byname[uid] = (keyid, name)
1387 return (byname, byuid)
1389 def generate_users_from_keyring(self, format, session):
1393 for x in self.keys.keys():
1394 if self.keys[x]["email"] == "invalid-uid":
1396 self.keys[x]["uid"] = format % "invalid-uid"
1398 uid = format % self.keys[x]["email"]
1399 keyid = get_or_set_uid(uid, session).uid_id
1400 byuid[keyid] = (uid, self.keys[x]["name"])
1401 byname[uid] = (keyid, self.keys[x]["name"])
1402 self.keys[x]["uid"] = uid
1405 uid = format % "invalid-uid"
1406 keyid = get_or_set_uid(uid, session).uid_id
1407 byuid[keyid] = (uid, "ungeneratable user id")
1408 byname[uid] = (keyid, "ungeneratable user id")
1410 return (byname, byuid)
1412 __all__.append('Keyring')
1415 def get_keyring(keyring, session=None):
1417 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1418 If C{keyring} already has an entry, simply return the existing Keyring
1420 @type keyring: string
1421 @param keyring: the keyring name
1424 @return: the Keyring object for this keyring
1427 q = session.query(Keyring).filter_by(keyring_name=keyring)
1431 except NoResultFound:
1434 __all__.append('get_keyring')
1436 ################################################################################
1438 class KeyringACLMap(object):
1439 def __init__(self, *args, **kwargs):
1443 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1445 __all__.append('KeyringACLMap')
1447 ################################################################################
1449 class DBChange(object):
1450 def __init__(self, *args, **kwargs):
1454 return '<DBChange %s>' % self.changesname
1456 def clean_from_queue(self):
1457 session = DBConn().session().object_session(self)
1459 # Remove changes_pool_files entries
1462 # Remove changes_pending_files references
1465 # Clear out of queue
1466 self.in_queue = None
1467 self.approved_for_id = None
1469 __all__.append('DBChange')
1472 def get_dbchange(filename, session=None):
1474 returns DBChange object for given C{filename}.
1476 @type filename: string
1477 @param filename: the name of the file
1479 @type session: Session
1480 @param session: Optional SQLA session object (a temporary one will be
1481 generated if not supplied)
1484 @return: DBChange object for the given filename (C{None} if not present)
1487 q = session.query(DBChange).filter_by(changesname=filename)
1491 except NoResultFound:
1494 __all__.append('get_dbchange')
1496 ################################################################################
1498 class Location(object):
1499 def __init__(self, *args, **kwargs):
1503 return '<Location %s (%s)>' % (self.path, self.location_id)
1505 __all__.append('Location')
1508 def get_location(location, component=None, archive=None, session=None):
1510 Returns Location object for the given combination of location, component
1513 @type location: string
1514 @param location: the path of the location, e.g. I{/srv/ftp-master.debian.org/ftp/pool/}
1516 @type component: string
1517 @param component: the component name (if None, no restriction applied)
1519 @type archive: string
1520 @param archive: the archive name (if None, no restriction applied)
1522 @rtype: Location / None
1523 @return: Either a Location object or None if one can't be found
1526 q = session.query(Location).filter_by(path=location)
1528 if archive is not None:
1529 q = q.join(Archive).filter_by(archive_name=archive)
1531 if component is not None:
1532 q = q.join(Component).filter_by(component_name=component)
1536 except NoResultFound:
1539 __all__.append('get_location')
1541 ################################################################################
1543 class Maintainer(object):
1544 def __init__(self, *args, **kwargs):
1548 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1550 def get_split_maintainer(self):
1551 if not hasattr(self, 'name') or self.name is None:
1552 return ('', '', '', '')
1554 return fix_maintainer(self.name.strip())
1556 __all__.append('Maintainer')
1559 def get_or_set_maintainer(name, session=None):
1561 Returns Maintainer object for given maintainer name.
1563 If no matching maintainer name is found, a row is inserted.
1566 @param name: The maintainer name to add
1568 @type session: SQLAlchemy
1569 @param session: Optional SQL session object (a temporary one will be
1570 generated if not supplied). If not passed, a commit will be performed at
1571 the end of the function, otherwise the caller is responsible for commiting.
1572 A flush will be performed either way.
1575 @return: the Maintainer object for the given maintainer
1578 q = session.query(Maintainer).filter_by(name=name)
1581 except NoResultFound:
1582 maintainer = Maintainer()
1583 maintainer.name = name
1584 session.add(maintainer)
1585 session.commit_or_flush()
1590 __all__.append('get_or_set_maintainer')
1593 def get_maintainer(maintainer_id, session=None):
1595 Return the name of the maintainer behind C{maintainer_id} or None if that
1596 maintainer_id is invalid.
1598 @type maintainer_id: int
1599 @param maintainer_id: the id of the maintainer
1602 @return: the Maintainer with this C{maintainer_id}
1605 return session.query(Maintainer).get(maintainer_id)
1607 __all__.append('get_maintainer')
1609 ################################################################################
1611 class NewComment(object):
1612 def __init__(self, *args, **kwargs):
1616 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1618 __all__.append('NewComment')
1621 def has_new_comment(package, version, session=None):
1623 Returns true if the given combination of C{package}, C{version} has a comment.
1625 @type package: string
1626 @param package: name of the package
1628 @type version: string
1629 @param version: package version
1631 @type session: Session
1632 @param session: Optional SQLA session object (a temporary one will be
1633 generated if not supplied)
1639 q = session.query(NewComment)
1640 q = q.filter_by(package=package)
1641 q = q.filter_by(version=version)
1643 return bool(q.count() > 0)
1645 __all__.append('has_new_comment')
1648 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1650 Returns (possibly empty) list of NewComment objects for the given
1653 @type package: string (optional)
1654 @param package: name of the package
1656 @type version: string (optional)
1657 @param version: package version
1659 @type comment_id: int (optional)
1660 @param comment_id: An id of a comment
1662 @type session: Session
1663 @param session: Optional SQLA session object (a temporary one will be
1664 generated if not supplied)
1667 @return: A (possibly empty) list of NewComment objects will be returned
1670 q = session.query(NewComment)
1671 if package is not None: q = q.filter_by(package=package)
1672 if version is not None: q = q.filter_by(version=version)
1673 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1677 __all__.append('get_new_comments')
1679 ################################################################################
1681 class Override(object):
1682 def __init__(self, *args, **kwargs):
1686 return '<Override %s (%s)>' % (self.package, self.suite_id)
1688 __all__.append('Override')
1691 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1693 Returns Override object for the given parameters
1695 @type package: string
1696 @param package: The name of the package
1698 @type suite: string, list or None
1699 @param suite: The name of the suite (or suites if a list) to limit to. If
1700 None, don't limit. Defaults to None.
1702 @type component: string, list or None
1703 @param component: The name of the component (or components if a list) to
1704 limit to. If None, don't limit. Defaults to None.
1706 @type overridetype: string, list or None
1707 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1708 limit to. If None, don't limit. Defaults to None.
1710 @type session: Session
1711 @param session: Optional SQLA session object (a temporary one will be
1712 generated if not supplied)
1715 @return: A (possibly empty) list of Override objects will be returned
1718 q = session.query(Override)
1719 q = q.filter_by(package=package)
1721 if suite is not None:
1722 if not isinstance(suite, list): suite = [suite]
1723 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1725 if component is not None:
1726 if not isinstance(component, list): component = [component]
1727 q = q.join(Component).filter(Component.component_name.in_(component))
1729 if overridetype is not None:
1730 if not isinstance(overridetype, list): overridetype = [overridetype]
1731 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1735 __all__.append('get_override')
1738 ################################################################################
1740 class OverrideType(object):
1741 def __init__(self, *args, **kwargs):
1745 return '<OverrideType %s>' % self.overridetype
1747 __all__.append('OverrideType')
1750 def get_override_type(override_type, session=None):
1752 Returns OverrideType object for given C{override type}.
1754 @type override_type: string
1755 @param override_type: The name of the override type
1757 @type session: Session
1758 @param session: Optional SQLA session object (a temporary one will be
1759 generated if not supplied)
1762 @return: the database id for the given override type
1765 q = session.query(OverrideType).filter_by(overridetype=override_type)
1769 except NoResultFound:
1772 __all__.append('get_override_type')
1774 ################################################################################
1776 class DebContents(object):
1777 def __init__(self, *args, **kwargs):
1781 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1783 __all__.append('DebContents')
1786 class UdebContents(object):
1787 def __init__(self, *args, **kwargs):
1791 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1793 __all__.append('UdebContents')
1795 class PendingBinContents(object):
1796 def __init__(self, *args, **kwargs):
1800 return '<PendingBinContents %s>' % self.contents_id
1802 __all__.append('PendingBinContents')
1804 def insert_pending_content_paths(package,
1809 Make sure given paths are temporarily associated with given
1813 @param package: the package to associate with should have been read in from the binary control file
1814 @type fullpaths: list
1815 @param fullpaths: the list of paths of the file being associated with the binary
1816 @type session: SQLAlchemy session
1817 @param session: Optional SQLAlchemy session. If this is passed, the caller
1818 is responsible for ensuring a transaction has begun and committing the
1819 results or rolling back based on the result code. If not passed, a commit
1820 will be performed at the end of the function
1822 @return: True upon success, False if there is a problem
1825 privatetrans = False
1828 session = DBConn().session()
1832 arch = get_architecture(package['Architecture'], session)
1833 arch_id = arch.arch_id
1835 # Remove any already existing recorded files for this package
1836 q = session.query(PendingBinContents)
1837 q = q.filter_by(package=package['Package'])
1838 q = q.filter_by(version=package['Version'])
1839 q = q.filter_by(architecture=arch_id)
1842 for fullpath in fullpaths:
1844 if fullpath.startswith( "./" ):
1845 fullpath = fullpath[2:]
1847 pca = PendingBinContents()
1848 pca.package = package['Package']
1849 pca.version = package['Version']
1851 pca.architecture = arch_id
1854 pca.type = 8 # gross
1856 pca.type = 7 # also gross
1859 # Only commit if we set up the session ourself
1867 except Exception, e:
1868 traceback.print_exc()
1870 # Only rollback if we set up the session ourself
1877 __all__.append('insert_pending_content_paths')
1879 ################################################################################
1881 class PolicyQueue(object):
1882 def __init__(self, *args, **kwargs):
1886 return '<PolicyQueue %s>' % self.queue_name
1888 __all__.append('PolicyQueue')
1891 def get_policy_queue(queuename, session=None):
1893 Returns PolicyQueue object for given C{queue name}
1895 @type queuename: string
1896 @param queuename: The name of the queue
1898 @type session: Session
1899 @param session: Optional SQLA session object (a temporary one will be
1900 generated if not supplied)
1903 @return: PolicyQueue object for the given queue
1906 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1910 except NoResultFound:
1913 __all__.append('get_policy_queue')
1915 ################################################################################
1917 class Priority(object):
1918 def __init__(self, *args, **kwargs):
1921 def __eq__(self, val):
1922 if isinstance(val, str):
1923 return (self.priority == val)
1924 # This signals to use the normal comparison operator
1925 return NotImplemented
1927 def __ne__(self, val):
1928 if isinstance(val, str):
1929 return (self.priority != val)
1930 # This signals to use the normal comparison operator
1931 return NotImplemented
1934 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1936 __all__.append('Priority')
1939 def get_priority(priority, session=None):
1941 Returns Priority object for given C{priority name}.
1943 @type priority: string
1944 @param priority: The name of the priority
1946 @type session: Session
1947 @param session: Optional SQLA session object (a temporary one will be
1948 generated if not supplied)
1951 @return: Priority object for the given priority
1954 q = session.query(Priority).filter_by(priority=priority)
1958 except NoResultFound:
1961 __all__.append('get_priority')
1964 def get_priorities(session=None):
1966 Returns dictionary of priority names -> id mappings
1968 @type session: Session
1969 @param session: Optional SQL session object (a temporary one will be
1970 generated if not supplied)
1973 @return: dictionary of priority names -> id mappings
1977 q = session.query(Priority)
1979 ret[x.priority] = x.priority_id
1983 __all__.append('get_priorities')
1985 ################################################################################
1987 class Section(object):
1988 def __init__(self, *args, **kwargs):
1991 def __eq__(self, val):
1992 if isinstance(val, str):
1993 return (self.section == val)
1994 # This signals to use the normal comparison operator
1995 return NotImplemented
1997 def __ne__(self, val):
1998 if isinstance(val, str):
1999 return (self.section != val)
2000 # This signals to use the normal comparison operator
2001 return NotImplemented
2004 return '<Section %s>' % self.section
2006 __all__.append('Section')
2009 def get_section(section, session=None):
2011 Returns Section object for given C{section name}.
2013 @type section: string
2014 @param section: The name of the section
2016 @type session: Session
2017 @param session: Optional SQLA session object (a temporary one will be
2018 generated if not supplied)
2021 @return: Section object for the given section name
2024 q = session.query(Section).filter_by(section=section)
2028 except NoResultFound:
2031 __all__.append('get_section')
2034 def get_sections(session=None):
2036 Returns dictionary of section names -> id mappings
2038 @type session: Session
2039 @param session: Optional SQL session object (a temporary one will be
2040 generated if not supplied)
2043 @return: dictionary of section names -> id mappings
2047 q = session.query(Section)
2049 ret[x.section] = x.section_id
2053 __all__.append('get_sections')
2055 ################################################################################
2057 class DBSource(object):
2058 def __init__(self, *args, **kwargs):
2062 return '<DBSource %s (%s)>' % (self.source, self.version)
2064 __all__.append('DBSource')
2067 def source_exists(source, source_version, suites = ["any"], session=None):
2069 Ensure that source exists somewhere in the archive for the binary
2070 upload being processed.
2071 1. exact match => 1.0-3
2072 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2074 @type source: string
2075 @param source: source name
2077 @type source_version: string
2078 @param source_version: expected source version
2081 @param suites: list of suites to check in, default I{any}
2083 @type session: Session
2084 @param session: Optional SQLA session object (a temporary one will be
2085 generated if not supplied)
2088 @return: returns 1 if a source with expected version is found, otherwise 0
2095 for suite in suites:
2096 q = session.query(DBSource).filter_by(source=source)
2098 # source must exist in suite X, or in some other suite that's
2099 # mapped to X, recursively... silent-maps are counted too,
2100 # unreleased-maps aren't.
2101 maps = cnf.ValueList("SuiteMappings")[:]
2103 maps = [ m.split() for m in maps ]
2104 maps = [ (x[1], x[2]) for x in maps
2105 if x[0] == "map" or x[0] == "silent-map" ]
2108 if x[1] in s and x[0] not in s:
2111 q = q.join(SrcAssociation).join(Suite)
2112 q = q.filter(Suite.suite_name.in_(s))
2114 # Reduce the query results to a list of version numbers
2115 ql = [ j.version for j in q.all() ]
2118 if source_version in ql:
2122 from daklib.regexes import re_bin_only_nmu
2123 orig_source_version = re_bin_only_nmu.sub('', source_version)
2124 if orig_source_version in ql:
2127 # No source found so return not ok
2132 __all__.append('source_exists')
2135 def get_suites_source_in(source, session=None):
2137 Returns list of Suite objects which given C{source} name is in
2140 @param source: DBSource package name to search for
2143 @return: list of Suite objects for the given source
2146 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2148 __all__.append('get_suites_source_in')
2151 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2153 Returns list of DBSource objects for given C{source} name and other parameters
2156 @param source: DBSource package name to search for
2158 @type version: str or None
2159 @param version: DBSource version name to search for or None if not applicable
2161 @type dm_upload_allowed: bool
2162 @param dm_upload_allowed: If None, no effect. If True or False, only
2163 return packages with that dm_upload_allowed setting
2165 @type session: Session
2166 @param session: Optional SQL session object (a temporary one will be
2167 generated if not supplied)
2170 @return: list of DBSource objects for the given name (may be empty)
2173 q = session.query(DBSource).filter_by(source=source)
2175 if version is not None:
2176 q = q.filter_by(version=version)
2178 if dm_upload_allowed is not None:
2179 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2183 __all__.append('get_sources_from_name')
2186 def get_source_in_suite(source, suite, session=None):
2188 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2190 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2191 - B{suite} - a suite name, eg. I{unstable}
2193 @type source: string
2194 @param source: source package name
2197 @param suite: the suite name
2200 @return: the version for I{source} in I{suite}
2204 q = session.query(SrcAssociation)
2205 q = q.join('source').filter_by(source=source)
2206 q = q.join('suite').filter_by(suite_name=suite)
2209 return q.one().source
2210 except NoResultFound:
2213 __all__.append('get_source_in_suite')
2215 ################################################################################
2218 def add_dsc_to_db(u, filename, session=None):
2219 entry = u.pkg.files[filename]
2223 source.source = u.pkg.dsc["source"]
2224 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2225 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2226 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2227 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2228 source.install_date = datetime.now().date()
2230 dsc_component = entry["component"]
2231 dsc_location_id = entry["location id"]
2233 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2235 # Set up a new poolfile if necessary
2236 if not entry.has_key("files id") or not entry["files id"]:
2237 filename = entry["pool name"] + filename
2238 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2240 pfs.append(poolfile)
2241 entry["files id"] = poolfile.file_id
2243 source.poolfile_id = entry["files id"]
2247 for suite_name in u.pkg.changes["distribution"].keys():
2248 sa = SrcAssociation()
2249 sa.source_id = source.source_id
2250 sa.suite_id = get_suite(suite_name).suite_id
2255 # Add the source files to the DB (files and dsc_files)
2257 dscfile.source_id = source.source_id
2258 dscfile.poolfile_id = entry["files id"]
2259 session.add(dscfile)
2261 for dsc_file, dentry in u.pkg.dsc_files.items():
2263 df.source_id = source.source_id
2265 # If the .orig tarball is already in the pool, it's
2266 # files id is stored in dsc_files by check_dsc().
2267 files_id = dentry.get("files id", None)
2269 # Find the entry in the files hash
2270 # TODO: Bail out here properly
2272 for f, e in u.pkg.files.items():
2277 if files_id is None:
2278 filename = dfentry["pool name"] + dsc_file
2280 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2281 # FIXME: needs to check for -1/-2 and or handle exception
2282 if found and obj is not None:
2283 files_id = obj.file_id
2286 # If still not found, add it
2287 if files_id is None:
2288 # HACK: Force sha1sum etc into dentry
2289 dentry["sha1sum"] = dfentry["sha1sum"]
2290 dentry["sha256sum"] = dfentry["sha256sum"]
2291 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2292 pfs.append(poolfile)
2293 files_id = poolfile.file_id
2295 poolfile = get_poolfile_by_id(files_id, session)
2296 if poolfile is None:
2297 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2298 pfs.append(poolfile)
2300 df.poolfile_id = files_id
2305 # Add the src_uploaders to the DB
2306 uploader_ids = [source.maintainer_id]
2307 if u.pkg.dsc.has_key("uploaders"):
2308 for up in u.pkg.dsc["uploaders"].replace(">, ", ">\t").split("\t"):
2310 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2313 for up_id in uploader_ids:
2314 if added_ids.has_key(up_id):
2316 utils.warn("Already saw uploader %s for source %s" % (up_id, source.source))
2322 su.maintainer_id = up_id
2323 su.source_id = source.source_id
2328 return source, dsc_component, dsc_location_id, pfs
2330 __all__.append('add_dsc_to_db')
2333 def add_deb_to_db(u, filename, session=None):
2335 Contrary to what you might expect, this routine deals with both
2336 debs and udebs. That info is in 'dbtype', whilst 'type' is
2337 'deb' for both of them
2340 entry = u.pkg.files[filename]
2343 bin.package = entry["package"]
2344 bin.version = entry["version"]
2345 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2346 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2347 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2348 bin.binarytype = entry["dbtype"]
2351 filename = entry["pool name"] + filename
2352 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2353 if not entry.get("location id", None):
2354 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2356 if entry.get("files id", None):
2357 poolfile = get_poolfile_by_id(bin.poolfile_id)
2358 bin.poolfile_id = entry["files id"]
2360 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2361 bin.poolfile_id = entry["files id"] = poolfile.file_id
2364 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2365 if len(bin_sources) != 1:
2366 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2367 (bin.package, bin.version, bin.architecture.arch_string,
2368 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2370 bin.source_id = bin_sources[0].source_id
2372 # Add and flush object so it has an ID
2376 # Add BinAssociations
2377 for suite_name in u.pkg.changes["distribution"].keys():
2378 ba = BinAssociation()
2379 ba.binary_id = bin.binary_id
2380 ba.suite_id = get_suite(suite_name).suite_id
2385 # Deal with contents - disabled for now
2386 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2388 # print "REJECT\nCould not determine contents of package %s" % bin.package
2389 # session.rollback()
2390 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2394 __all__.append('add_deb_to_db')
2396 ################################################################################
2398 class SourceACL(object):
2399 def __init__(self, *args, **kwargs):
2403 return '<SourceACL %s>' % self.source_acl_id
2405 __all__.append('SourceACL')
2407 ################################################################################
2409 class SrcAssociation(object):
2410 def __init__(self, *args, **kwargs):
2414 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2416 __all__.append('SrcAssociation')
2418 ################################################################################
2420 class SrcFormat(object):
2421 def __init__(self, *args, **kwargs):
2425 return '<SrcFormat %s>' % (self.format_name)
2427 __all__.append('SrcFormat')
2429 ################################################################################
2431 class SrcUploader(object):
2432 def __init__(self, *args, **kwargs):
2436 return '<SrcUploader %s>' % self.uploader_id
2438 __all__.append('SrcUploader')
2440 ################################################################################
2442 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2443 ('SuiteID', 'suite_id'),
2444 ('Version', 'version'),
2445 ('Origin', 'origin'),
2447 ('Description', 'description'),
2448 ('Untouchable', 'untouchable'),
2449 ('Announce', 'announce'),
2450 ('Codename', 'codename'),
2451 ('OverrideCodename', 'overridecodename'),
2452 ('ValidTime', 'validtime'),
2453 ('Priority', 'priority'),
2454 ('NotAutomatic', 'notautomatic'),
2455 ('CopyChanges', 'copychanges'),
2456 ('CopyDotDak', 'copydotdak'),
2457 ('CommentsDir', 'commentsdir'),
2458 ('OverrideSuite', 'overridesuite'),
2459 ('ChangelogBase', 'changelogbase')]
2465 ArchiveDir "%(archivepath)s";
2466 OverrideDir "/srv/ftp-master.debian.org/scripts/override/";
2467 CacheDir "/srv/ftp-master.debian.org/database/";
2472 Packages::Compress ". bzip2 gzip";
2473 Sources::Compress ". bzip2 gzip";
2474 Contents::Compress "gzip";
2476 MaxContentsChange 25000;
2482 Contents::Header "/srv/ftp-master.debian.org/dak/config/debian/Contents.top";
2488 apt_trees["testing"]="""
2489 tree "dists/testing"
2491 FakeDI "dists/unstable";
2492 FileList "/srv/ftp-master.debian.org/database/dists/testing_$(SECTION)_binary-$(ARCH).list";
2493 SourceFileList "/srv/ftp-master.debian.org/database/dists/testing_$(SECTION)_source.list";
2494 Sections "main contrib non-free";
2495 Architectures "%(arch)s";
2496 BinOverride "override.squeeze.$(SECTION)";
2497 ExtraOverride "override.squeeze.extra.$(SECTION)";
2498 SrcOverride "override.squeeze.$(SECTION).src";
2500 tree "dists/testing/main"
2502 FileList "/srv/ftp-master.debian.org/database/dists/testing_main_$(SECTION)_binary-$(ARCH).list";
2503 Sections "debian-installer";
2504 Architectures "%(arch)s";
2505 BinOverride "override.squeeze.main.$(SECTION)";
2506 SrcOverride "override.squeeze.main.src";
2507 BinCacheDB "packages-debian-installer-$(ARCH).db";
2508 Packages::Extensions ".udeb";
2509 Contents "$(DIST)/../Contents-udeb";
2512 tree "dists/testing/non-free"
2514 FileList "/srv/ftp-master.debian.org/database/dists/testing_non-free_$(SECTION)_binary-$(ARCH).list";
2515 Sections "debian-installer";
2516 Architectures "%(arch)s";
2517 BinOverride "override.squeeze.main.$(SECTION)";
2518 SrcOverride "override.squeeze.main.src";
2519 BinCacheDB "packages-debian-installer-$(ARCH).db";
2520 Packages::Extensions ".udeb";
2521 Contents "$(DIST)/../Contents-udeb-nf";
2525 apt_trees["unstable"]="""
2526 tree "dists/unstable"
2528 FileList "/srv/ftp-master.debian.org/database/dists/unstable_$(SECTION)_binary-$(ARCH).list";
2529 SourceFileList "/srv/ftp-master.debian.org/database/dists/unstable_$(SECTION)_source.list";
2530 Sections "main contrib non-free";
2531 Architectures "%(arch)s";
2532 BinOverride "override.sid.$(SECTION)";
2533 ExtraOverride "override.sid.extra.$(SECTION)";
2534 SrcOverride "override.sid.$(SECTION).src";
2536 tree "dists/unstable/main"
2538 FileList "/srv/ftp-master.debian.org/database/dists/unstable_main_$(SECTION)_binary-$(ARCH).list";
2539 Sections "debian-installer";
2540 Architectures "%(arch)s";
2541 BinOverride "override.sid.main.$(SECTION)";
2542 SrcOverride "override.sid.main.src";
2543 BinCacheDB "packages-debian-installer-$(ARCH).db";
2544 Packages::Extensions ".udeb";
2545 Contents "$(DIST)/../Contents-udeb";
2548 tree "dists/unstable/non-free"
2550 FileList "/srv/ftp-master.debian.org/database/dists/unstable_non-free_$(SECTION)_binary-$(ARCH).list";
2551 Sections "debian-installer";
2552 Architectures "%(arch)s";
2553 BinOverride "override.sid.main.$(SECTION)";
2554 SrcOverride "override.sid.main.src";
2555 BinCacheDB "packages-debian-installer-$(ARCH).db";
2556 Packages::Extensions ".udeb";
2557 Contents "$(DIST)/../Contents-udeb-nf";
2561 apt_trees["experimental"]="""
2562 tree "dists/experimental"
2564 FileList "/srv/ftp-master.debian.org/database/dists/experimental_$(SECTION)_binary-$(ARCH).list";
2565 SourceFileList "/srv/ftp-master.debian.org/database/dists/experimental_$(SECTION)_source.list";
2566 Sections "main contrib non-free";
2567 Architectures "%(arch)s";
2568 BinOverride "override.sid.$(SECTION)";
2569 SrcOverride "override.sid.$(SECTION).src";
2571 tree "dists/experimental/main"
2573 FileList "/srv/ftp-master.debian.org/database/dists/experimental_main_$(SECTION)_binary-$(ARCH).list";
2574 Sections "debian-installer";
2575 Architectures "%(arch)s";
2576 BinOverride "override.sid.main.$(SECTION)";
2577 SrcOverride "override.sid.main.src";
2578 BinCacheDB "packages-debian-installer-$(ARCH).db";
2579 Packages::Extensions ".udeb";
2580 Contents "$(DIST)/../Contents-udeb";
2583 tree "dists/experimental/non-free"
2585 FileList "/srv/ftp-master.debian.org/database/dists/experimental_non-free_$(SECTION)_binary-$(ARCH).list";
2586 Sections "debian-installer";
2587 Architectures "%(arch)s";
2588 BinOverride "override.sid.main.$(SECTION)";
2589 SrcOverride "override.sid.main.src";
2590 BinCacheDB "packages-debian-installer-$(ARCH).db";
2591 Packages::Extensions ".udeb";
2592 Contents "$(DIST)/../Contents-udeb-nf";
2596 apt_trees["testing-proposed-updates"]="""
2597 tree "dists/testing-proposed-updates"
2599 FileList "/srv/ftp-master.debian.org/database/dists/testing-proposed-updates_$(SECTION)_binary-$(ARCH).list";
2600 SourceFileList "/srv/ftp-master.debian.org/database/dists/testing-proposed-updates_$(SECTION)_source.list";
2601 Sections "main contrib non-free";
2602 Architectures "%(arch)s";
2603 BinOverride "override.squeeze.$(SECTION)";
2604 ExtraOverride "override.squeeze.extra.$(SECTION)";
2605 SrcOverride "override.squeeze.$(SECTION).src";
2608 tree "dists/testing-proposed-updates/main"
2610 FileList "/srv/ftp-master.debian.org/database/dists/testing-proposed-updates_main_$(SECTION)_binary-$(ARCH).list";
2611 Sections "debian-installer";
2612 Architectures "%(arch)s";
2613 BinOverride "override.squeeze.main.$(SECTION)";
2614 SrcOverride "override.squeeze.main.src";
2615 BinCacheDB "packages-debian-installer-$(ARCH).db";
2616 Packages::Extensions ".udeb";
2621 apt_trees["proposed-updates"]="""
2622 tree "dists/proposed-updates"
2624 FileList "/srv/ftp-master.debian.org/database/dists/proposed-updates_$(SECTION)_binary-$(ARCH).list";
2625 SourceFileList "/srv/ftp-master.debian.org/database/dists/proposed-updates_$(SECTION)_source.list";
2626 Sections "main contrib non-free";
2627 Architectures "amd64";
2628 BinOverride "override.lenny.$(SECTION)";
2629 ExtraOverride "override.lenny.extra.$(SECTION)";
2630 SrcOverride "override.lenny.$(SECTION).src";
2633 tree "dists/proposed-updates/main"
2635 FileList "/srv/ftp-master.debian.org/database/dists/proposed-updates_main_$(SECTION)_binary-$(ARCH).list";
2636 Sections "debian-installer";
2637 Architectures "%(arch)s";
2638 BinOverride "override.lenny.main.$(SECTION)";
2639 SrcOverride "override.lenny.main.src";
2640 BinCacheDB "packages-debian-installer-$(ARCH).db";
2641 Packages::Extensions ".udeb";
2646 class Suite(object):
2647 def __init__(self, *args, **kwargs):
2651 return '<Suite %s>' % self.suite_name
2653 def __eq__(self, val):
2654 if isinstance(val, str):
2655 return (self.suite_name == val)
2656 # This signals to use the normal comparison operator
2657 return NotImplemented
2659 def __ne__(self, val):
2660 if isinstance(val, str):
2661 return (self.suite_name != val)
2662 # This signals to use the normal comparison operator
2663 return NotImplemented
2667 for disp, field in SUITE_FIELDS:
2668 val = getattr(self, field, None)
2670 ret.append("%s: %s" % (disp, val))
2672 return "\n".join(ret)
2674 def generate_packages_sources(self, suite, arch):
2676 Generate Packages/Sources files with apt-ftparchive for the given suite/arch
2679 @param suite: Suite name
2682 @param arch: Architecture name
2686 startdir = os.getcwd()
2690 (ac_fd, ac_name) = mkstemp()
2691 os.write(ac_fd, DAILY_APT_CONF % {'archivepath': self.path})
2692 # here we want to generate the tree entries
2693 os.write(ac_fd, apt_trees[suite] % {'arch': arch})
2696 # Run apt-ftparchive generate
2697 os.chdir(os.path.dirname(ac_name))
2698 # We might want to add a -q or -qq here
2699 os.system('apt-ftparchive generate %s' % os.path.basename(ac_name))
2701 # Clean up any left behind files
2719 __all__.append('Suite')
2722 def get_suite_architecture(suite, architecture, session=None):
2724 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2728 @param suite: Suite name to search for
2730 @type architecture: str
2731 @param architecture: Architecture name to search for
2733 @type session: Session
2734 @param session: Optional SQL session object (a temporary one will be
2735 generated if not supplied)
2737 @rtype: SuiteArchitecture
2738 @return: the SuiteArchitecture object or None
2741 q = session.query(SuiteArchitecture)
2742 q = q.join(Architecture).filter_by(arch_string=architecture)
2743 q = q.join(Suite).filter_by(suite_name=suite)
2747 except NoResultFound:
2750 __all__.append('get_suite_architecture')
2753 def get_suite(suite, session=None):
2755 Returns Suite object for given C{suite name}.
2758 @param suite: The name of the suite
2760 @type session: Session
2761 @param session: Optional SQLA session object (a temporary one will be
2762 generated if not supplied)
2765 @return: Suite object for the requested suite name (None if not present)
2768 q = session.query(Suite).filter_by(suite_name=suite)
2772 except NoResultFound:
2775 __all__.append('get_suite')
2777 ################################################################################
2779 class SuiteArchitecture(object):
2780 def __init__(self, *args, **kwargs):
2784 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2786 __all__.append('SuiteArchitecture')
2789 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2791 Returns list of Architecture objects for given C{suite} name
2794 @param suite: Suite name to search for
2796 @type skipsrc: boolean
2797 @param skipsrc: Whether to skip returning the 'source' architecture entry
2800 @type skipall: boolean
2801 @param skipall: Whether to skip returning the 'all' architecture entry
2804 @type session: Session
2805 @param session: Optional SQL session object (a temporary one will be
2806 generated if not supplied)
2809 @return: list of Architecture objects for the given name (may be empty)
2812 q = session.query(Architecture)
2813 q = q.join(SuiteArchitecture)
2814 q = q.join(Suite).filter_by(suite_name=suite)
2817 q = q.filter(Architecture.arch_string != 'source')
2820 q = q.filter(Architecture.arch_string != 'all')
2822 q = q.order_by('arch_string')
2826 __all__.append('get_suite_architectures')
2828 ################################################################################
2830 class SuiteSrcFormat(object):
2831 def __init__(self, *args, **kwargs):
2835 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2837 __all__.append('SuiteSrcFormat')
2840 def get_suite_src_formats(suite, session=None):
2842 Returns list of allowed SrcFormat for C{suite}.
2845 @param suite: Suite name to search for
2847 @type session: Session
2848 @param session: Optional SQL session object (a temporary one will be
2849 generated if not supplied)
2852 @return: the list of allowed source formats for I{suite}
2855 q = session.query(SrcFormat)
2856 q = q.join(SuiteSrcFormat)
2857 q = q.join(Suite).filter_by(suite_name=suite)
2858 q = q.order_by('format_name')
2862 __all__.append('get_suite_src_formats')
2864 ################################################################################
2867 def __init__(self, *args, **kwargs):
2870 def __eq__(self, val):
2871 if isinstance(val, str):
2872 return (self.uid == val)
2873 # This signals to use the normal comparison operator
2874 return NotImplemented
2876 def __ne__(self, val):
2877 if isinstance(val, str):
2878 return (self.uid != val)
2879 # This signals to use the normal comparison operator
2880 return NotImplemented
2883 return '<Uid %s (%s)>' % (self.uid, self.name)
2885 __all__.append('Uid')
2888 def add_database_user(uidname, session=None):
2890 Adds a database user
2892 @type uidname: string
2893 @param uidname: The uid of the user to add
2895 @type session: SQLAlchemy
2896 @param session: Optional SQL session object (a temporary one will be
2897 generated if not supplied). If not passed, a commit will be performed at
2898 the end of the function, otherwise the caller is responsible for commiting.
2901 @return: the uid object for the given uidname
2904 session.execute("CREATE USER :uid", {'uid': uidname})
2905 session.commit_or_flush()
2907 __all__.append('add_database_user')
2910 def get_or_set_uid(uidname, session=None):
2912 Returns uid object for given uidname.
2914 If no matching uidname is found, a row is inserted.
2916 @type uidname: string
2917 @param uidname: The uid to add
2919 @type session: SQLAlchemy
2920 @param session: Optional SQL session object (a temporary one will be
2921 generated if not supplied). If not passed, a commit will be performed at
2922 the end of the function, otherwise the caller is responsible for commiting.
2925 @return: the uid object for the given uidname
2928 q = session.query(Uid).filter_by(uid=uidname)
2932 except NoResultFound:
2936 session.commit_or_flush()
2941 __all__.append('get_or_set_uid')
2944 def get_uid_from_fingerprint(fpr, session=None):
2945 q = session.query(Uid)
2946 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2950 except NoResultFound:
2953 __all__.append('get_uid_from_fingerprint')
2955 ################################################################################
2957 class UploadBlock(object):
2958 def __init__(self, *args, **kwargs):
2962 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2964 __all__.append('UploadBlock')
2966 ################################################################################
2968 class DBConn(object):
2970 database module init.
2974 def __init__(self, *args, **kwargs):
2975 self.__dict__ = self.__shared_state
2977 if not getattr(self, 'initialised', False):
2978 self.initialised = True
2979 self.debug = kwargs.has_key('debug')
2982 def __setuptables(self):
2992 'build_queue_files',
2995 'changes_pending_binaries',
2996 'changes_pending_files',
2997 'changes_pending_files_map',
2998 'changes_pending_source',
2999 'changes_pending_source_files',
3000 'changes_pool_files',
3013 'pending_bin_contents',
3023 'suite_architectures',
3024 'suite_src_formats',
3025 'suite_build_queue_copy',
3031 for table_name in tables:
3032 table = Table(table_name, self.db_meta, autoload=True)
3033 setattr(self, 'tbl_%s' % table_name, table)
3035 def __setupmappers(self):
3036 mapper(Architecture, self.tbl_architecture,
3037 properties = dict(arch_id = self.tbl_architecture.c.id))
3039 mapper(Archive, self.tbl_archive,
3040 properties = dict(archive_id = self.tbl_archive.c.id,
3041 archive_name = self.tbl_archive.c.name))
3043 mapper(BinAssociation, self.tbl_bin_associations,
3044 properties = dict(ba_id = self.tbl_bin_associations.c.id,
3045 suite_id = self.tbl_bin_associations.c.suite,
3046 suite = relation(Suite),
3047 binary_id = self.tbl_bin_associations.c.bin,
3048 binary = relation(DBBinary)))
3050 mapper(PendingBinContents, self.tbl_pending_bin_contents,
3051 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
3052 filename = self.tbl_pending_bin_contents.c.filename,
3053 package = self.tbl_pending_bin_contents.c.package,
3054 version = self.tbl_pending_bin_contents.c.version,
3055 arch = self.tbl_pending_bin_contents.c.arch,
3056 otype = self.tbl_pending_bin_contents.c.type))
3058 mapper(DebContents, self.tbl_deb_contents,
3059 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
3060 package=self.tbl_deb_contents.c.package,
3061 suite=self.tbl_deb_contents.c.suite,
3062 arch=self.tbl_deb_contents.c.arch,
3063 section=self.tbl_deb_contents.c.section,
3064 filename=self.tbl_deb_contents.c.filename))
3066 mapper(UdebContents, self.tbl_udeb_contents,
3067 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
3068 package=self.tbl_udeb_contents.c.package,
3069 suite=self.tbl_udeb_contents.c.suite,
3070 arch=self.tbl_udeb_contents.c.arch,
3071 section=self.tbl_udeb_contents.c.section,
3072 filename=self.tbl_udeb_contents.c.filename))
3074 mapper(BuildQueue, self.tbl_build_queue,
3075 properties = dict(queue_id = self.tbl_build_queue.c.id))
3077 mapper(BuildQueueFile, self.tbl_build_queue_files,
3078 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
3079 poolfile = relation(PoolFile, backref='buildqueueinstances')))
3081 mapper(DBBinary, self.tbl_binaries,
3082 properties = dict(binary_id = self.tbl_binaries.c.id,
3083 package = self.tbl_binaries.c.package,
3084 version = self.tbl_binaries.c.version,
3085 maintainer_id = self.tbl_binaries.c.maintainer,
3086 maintainer = relation(Maintainer),
3087 source_id = self.tbl_binaries.c.source,
3088 source = relation(DBSource),
3089 arch_id = self.tbl_binaries.c.architecture,
3090 architecture = relation(Architecture),
3091 poolfile_id = self.tbl_binaries.c.file,
3092 poolfile = relation(PoolFile),
3093 binarytype = self.tbl_binaries.c.type,
3094 fingerprint_id = self.tbl_binaries.c.sig_fpr,
3095 fingerprint = relation(Fingerprint),
3096 install_date = self.tbl_binaries.c.install_date,
3097 binassociations = relation(BinAssociation,
3098 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
3100 mapper(BinaryACL, self.tbl_binary_acl,
3101 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
3103 mapper(BinaryACLMap, self.tbl_binary_acl_map,
3104 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
3105 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
3106 architecture = relation(Architecture)))
3108 mapper(Component, self.tbl_component,
3109 properties = dict(component_id = self.tbl_component.c.id,
3110 component_name = self.tbl_component.c.name))
3112 mapper(DBConfig, self.tbl_config,
3113 properties = dict(config_id = self.tbl_config.c.id))
3115 mapper(DSCFile, self.tbl_dsc_files,
3116 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
3117 source_id = self.tbl_dsc_files.c.source,
3118 source = relation(DBSource),
3119 poolfile_id = self.tbl_dsc_files.c.file,
3120 poolfile = relation(PoolFile)))
3122 mapper(PoolFile, self.tbl_files,
3123 properties = dict(file_id = self.tbl_files.c.id,
3124 filesize = self.tbl_files.c.size,
3125 location_id = self.tbl_files.c.location,
3126 location = relation(Location)))
3128 mapper(Fingerprint, self.tbl_fingerprint,
3129 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
3130 uid_id = self.tbl_fingerprint.c.uid,
3131 uid = relation(Uid),
3132 keyring_id = self.tbl_fingerprint.c.keyring,
3133 keyring = relation(Keyring),
3134 source_acl = relation(SourceACL),
3135 binary_acl = relation(BinaryACL)))
3137 mapper(Keyring, self.tbl_keyrings,
3138 properties = dict(keyring_name = self.tbl_keyrings.c.name,
3139 keyring_id = self.tbl_keyrings.c.id))
3141 mapper(DBChange, self.tbl_changes,
3142 properties = dict(change_id = self.tbl_changes.c.id,
3143 poolfiles = relation(PoolFile,
3144 secondary=self.tbl_changes_pool_files,
3145 backref="changeslinks"),
3146 seen = self.tbl_changes.c.seen,
3147 source = self.tbl_changes.c.source,
3148 binaries = self.tbl_changes.c.binaries,
3149 architecture = self.tbl_changes.c.architecture,
3150 distribution = self.tbl_changes.c.distribution,
3151 urgency = self.tbl_changes.c.urgency,
3152 maintainer = self.tbl_changes.c.maintainer,
3153 changedby = self.tbl_changes.c.changedby,
3154 date = self.tbl_changes.c.date,
3155 version = self.tbl_changes.c.version,
3156 files = relation(ChangePendingFile,
3157 secondary=self.tbl_changes_pending_files_map,
3158 backref="changesfile"),
3159 in_queue_id = self.tbl_changes.c.in_queue,
3160 in_queue = relation(PolicyQueue,
3161 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
3162 approved_for_id = self.tbl_changes.c.approved_for))
3164 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
3165 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
3167 mapper(ChangePendingFile, self.tbl_changes_pending_files,
3168 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id,
3169 filename = self.tbl_changes_pending_files.c.filename,
3170 size = self.tbl_changes_pending_files.c.size,
3171 md5sum = self.tbl_changes_pending_files.c.md5sum,
3172 sha1sum = self.tbl_changes_pending_files.c.sha1sum,
3173 sha256sum = self.tbl_changes_pending_files.c.sha256sum))
3175 mapper(ChangePendingSource, self.tbl_changes_pending_source,
3176 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
3177 change = relation(DBChange),
3178 maintainer = relation(Maintainer,
3179 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
3180 changedby = relation(Maintainer,
3181 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
3182 fingerprint = relation(Fingerprint),
3183 source_files = relation(ChangePendingFile,
3184 secondary=self.tbl_changes_pending_source_files,
3185 backref="pending_sources")))
3188 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
3189 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
3190 keyring = relation(Keyring, backref="keyring_acl_map"),
3191 architecture = relation(Architecture)))
3193 mapper(Location, self.tbl_location,
3194 properties = dict(location_id = self.tbl_location.c.id,
3195 component_id = self.tbl_location.c.component,
3196 component = relation(Component),
3197 archive_id = self.tbl_location.c.archive,
3198 archive = relation(Archive),
3199 archive_type = self.tbl_location.c.type))
3201 mapper(Maintainer, self.tbl_maintainer,
3202 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
3204 mapper(NewComment, self.tbl_new_comments,
3205 properties = dict(comment_id = self.tbl_new_comments.c.id))
3207 mapper(Override, self.tbl_override,
3208 properties = dict(suite_id = self.tbl_override.c.suite,
3209 suite = relation(Suite),
3210 package = self.tbl_override.c.package,
3211 component_id = self.tbl_override.c.component,
3212 component = relation(Component),
3213 priority_id = self.tbl_override.c.priority,
3214 priority = relation(Priority),
3215 section_id = self.tbl_override.c.section,
3216 section = relation(Section),
3217 overridetype_id = self.tbl_override.c.type,
3218 overridetype = relation(OverrideType)))
3220 mapper(OverrideType, self.tbl_override_type,
3221 properties = dict(overridetype = self.tbl_override_type.c.type,
3222 overridetype_id = self.tbl_override_type.c.id))
3224 mapper(PolicyQueue, self.tbl_policy_queue,
3225 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
3227 mapper(Priority, self.tbl_priority,
3228 properties = dict(priority_id = self.tbl_priority.c.id))
3230 mapper(Section, self.tbl_section,
3231 properties = dict(section_id = self.tbl_section.c.id,
3232 section=self.tbl_section.c.section))
3234 mapper(DBSource, self.tbl_source,
3235 properties = dict(source_id = self.tbl_source.c.id,
3236 version = self.tbl_source.c.version,
3237 maintainer_id = self.tbl_source.c.maintainer,
3238 maintainer = relation(Maintainer,
3239 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
3240 poolfile_id = self.tbl_source.c.file,
3241 poolfile = relation(PoolFile),
3242 fingerprint_id = self.tbl_source.c.sig_fpr,
3243 fingerprint = relation(Fingerprint),
3244 changedby_id = self.tbl_source.c.changedby,
3245 changedby = relation(Maintainer,
3246 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
3247 srcfiles = relation(DSCFile,
3248 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
3249 srcassociations = relation(SrcAssociation,
3250 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
3251 srcuploaders = relation(SrcUploader)))
3253 mapper(SourceACL, self.tbl_source_acl,
3254 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
3256 mapper(SrcAssociation, self.tbl_src_associations,
3257 properties = dict(sa_id = self.tbl_src_associations.c.id,
3258 suite_id = self.tbl_src_associations.c.suite,
3259 suite = relation(Suite),
3260 source_id = self.tbl_src_associations.c.source,
3261 source = relation(DBSource)))
3263 mapper(SrcFormat, self.tbl_src_format,
3264 properties = dict(src_format_id = self.tbl_src_format.c.id,
3265 format_name = self.tbl_src_format.c.format_name))
3267 mapper(SrcUploader, self.tbl_src_uploaders,
3268 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
3269 source_id = self.tbl_src_uploaders.c.source,
3270 source = relation(DBSource,
3271 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
3272 maintainer_id = self.tbl_src_uploaders.c.maintainer,
3273 maintainer = relation(Maintainer,
3274 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
3276 mapper(Suite, self.tbl_suite,
3277 properties = dict(suite_id = self.tbl_suite.c.id,
3278 policy_queue = relation(PolicyQueue),
3279 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
3281 mapper(SuiteArchitecture, self.tbl_suite_architectures,
3282 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
3283 suite = relation(Suite, backref='suitearchitectures'),
3284 arch_id = self.tbl_suite_architectures.c.architecture,
3285 architecture = relation(Architecture)))
3287 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
3288 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
3289 suite = relation(Suite, backref='suitesrcformats'),
3290 src_format_id = self.tbl_suite_src_formats.c.src_format,
3291 src_format = relation(SrcFormat)))
3293 mapper(Uid, self.tbl_uid,
3294 properties = dict(uid_id = self.tbl_uid.c.id,
3295 fingerprint = relation(Fingerprint)))
3297 mapper(UploadBlock, self.tbl_upload_blocks,
3298 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
3299 fingerprint = relation(Fingerprint, backref="uploadblocks"),
3300 uid = relation(Uid, backref="uploadblocks")))
3302 ## Connection functions
3303 def __createconn(self):
3304 from config import Config
3308 connstr = "postgres://%s" % cnf["DB::Host"]
3309 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3310 connstr += ":%s" % cnf["DB::Port"]
3311 connstr += "/%s" % cnf["DB::Name"]
3314 connstr = "postgres:///%s" % cnf["DB::Name"]
3315 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
3316 connstr += "?port=%s" % cnf["DB::Port"]
3318 self.db_pg = create_engine(connstr, echo=self.debug)
3319 self.db_meta = MetaData()
3320 self.db_meta.bind = self.db_pg
3321 self.db_smaker = sessionmaker(bind=self.db_pg,
3325 self.__setuptables()
3326 self.__setupmappers()
3329 return self.db_smaker()
3331 __all__.append('DBConn')