5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from datetime import datetime, timedelta
41 from errno import ENOENT
42 from tempfile import mkstemp, mkdtemp
44 from inspect import getargspec
47 from sqlalchemy import create_engine, Table, MetaData
48 from sqlalchemy.orm import sessionmaker, mapper, relation
49 from sqlalchemy import types as sqltypes
51 # Don't remove this, we re-export the exceptions to scripts which import us
52 from sqlalchemy.exc import *
53 from sqlalchemy.orm.exc import NoResultFound
55 from config import Config
56 from textutils import fix_maintainer
58 ################################################################################
60 # Patch in support for the debversion field type so that it works during
63 class DebVersion(sqltypes.Text):
64 def get_col_spec(self):
67 sa_major_version = sqlalchemy.__version__[0:3]
68 if sa_major_version == "0.5":
69 from sqlalchemy.databases import postgres
70 postgres.ischema_names['debversion'] = DebVersion
72 raise Exception("dak isn't ported to SQLA versions != 0.5 yet. See daklib/dbconn.py")
74 ################################################################################
76 __all__ = ['IntegrityError', 'SQLAlchemyError']
78 ################################################################################
80 def session_wrapper(fn):
82 Wrapper around common ".., session=None):" handling. If the wrapped
83 function is called without passing 'session', we create a local one
84 and destroy it when the function ends.
86 Also attaches a commit_or_flush method to the session; if we created a
87 local session, this is a synonym for session.commit(), otherwise it is a
88 synonym for session.flush().
91 def wrapped(*args, **kwargs):
92 private_transaction = False
94 # Find the session object
95 session = kwargs.get('session')
98 if len(args) <= len(getargspec(fn)[0]) - 1:
99 # No session specified as last argument or in kwargs
100 private_transaction = True
101 session = kwargs['session'] = DBConn().session()
103 # Session is last argument in args
107 session = args[-1] = DBConn().session()
108 private_transaction = True
110 if private_transaction:
111 session.commit_or_flush = session.commit
113 session.commit_or_flush = session.flush
116 return fn(*args, **kwargs)
118 if private_transaction:
119 # We created a session; close it.
122 wrapped.__doc__ = fn.__doc__
123 wrapped.func_name = fn.func_name
127 __all__.append('session_wrapper')
129 ################################################################################
131 class Architecture(object):
132 def __init__(self, *args, **kwargs):
135 def __eq__(self, val):
136 if isinstance(val, str):
137 return (self.arch_string== val)
138 # This signals to use the normal comparison operator
139 return NotImplemented
141 def __ne__(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
148 return '<Architecture %s>' % self.arch_string
150 __all__.append('Architecture')
153 def get_architecture(architecture, session=None):
155 Returns database id for given C{architecture}.
157 @type architecture: string
158 @param architecture: The name of the architecture
160 @type session: Session
161 @param session: Optional SQLA session object (a temporary one will be
162 generated if not supplied)
165 @return: Architecture object for the given arch (None if not present)
168 q = session.query(Architecture).filter_by(arch_string=architecture)
172 except NoResultFound:
175 __all__.append('get_architecture')
178 def get_architecture_suites(architecture, session=None):
180 Returns list of Suite objects for given C{architecture} name
183 @param source: Architecture name to search for
185 @type session: Session
186 @param session: Optional SQL session object (a temporary one will be
187 generated if not supplied)
190 @return: list of Suite objects for the given name (may be empty)
193 q = session.query(Suite)
194 q = q.join(SuiteArchitecture)
195 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
201 __all__.append('get_architecture_suites')
203 ################################################################################
205 class Archive(object):
206 def __init__(self, *args, **kwargs):
210 return '<Archive %s>' % self.archive_name
212 __all__.append('Archive')
215 def get_archive(archive, session=None):
217 returns database id for given C{archive}.
219 @type archive: string
220 @param archive: the name of the arhive
222 @type session: Session
223 @param session: Optional SQLA session object (a temporary one will be
224 generated if not supplied)
227 @return: Archive object for the given name (None if not present)
230 archive = archive.lower()
232 q = session.query(Archive).filter_by(archive_name=archive)
236 except NoResultFound:
239 __all__.append('get_archive')
241 ################################################################################
243 class BinAssociation(object):
244 def __init__(self, *args, **kwargs):
248 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
250 __all__.append('BinAssociation')
252 ################################################################################
254 class BinContents(object):
255 def __init__(self, *args, **kwargs):
259 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
261 __all__.append('BinContents')
263 ################################################################################
265 class DBBinary(object):
266 def __init__(self, *args, **kwargs):
270 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
272 __all__.append('DBBinary')
275 def get_suites_binary_in(package, session=None):
277 Returns list of Suite objects which given C{package} name is in
280 @param source: DBBinary package name to search for
283 @return: list of Suite objects for the given package
286 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
288 __all__.append('get_suites_binary_in')
291 def get_binary_from_id(binary_id, session=None):
293 Returns DBBinary object for given C{id}
296 @param binary_id: Id of the required binary
298 @type session: Session
299 @param session: Optional SQLA session object (a temporary one will be
300 generated if not supplied)
303 @return: DBBinary object for the given binary (None if not present)
306 q = session.query(DBBinary).filter_by(binary_id=binary_id)
310 except NoResultFound:
313 __all__.append('get_binary_from_id')
316 def get_binaries_from_name(package, version=None, architecture=None, session=None):
318 Returns list of DBBinary objects for given C{package} name
321 @param package: DBBinary package name to search for
323 @type version: str or None
324 @param version: Version to search for (or None)
326 @type package: str, list or None
327 @param package: Architectures to limit to (or None if no limit)
329 @type session: Session
330 @param session: Optional SQL session object (a temporary one will be
331 generated if not supplied)
334 @return: list of DBBinary objects for the given name (may be empty)
337 q = session.query(DBBinary).filter_by(package=package)
339 if version is not None:
340 q = q.filter_by(version=version)
342 if architecture is not None:
343 if not isinstance(architecture, list):
344 architecture = [architecture]
345 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
351 __all__.append('get_binaries_from_name')
354 def get_binaries_from_source_id(source_id, session=None):
356 Returns list of DBBinary objects for given C{source_id}
359 @param source_id: source_id to search for
361 @type session: Session
362 @param session: Optional SQL session object (a temporary one will be
363 generated if not supplied)
366 @return: list of DBBinary objects for the given name (may be empty)
369 return session.query(DBBinary).filter_by(source_id=source_id).all()
371 __all__.append('get_binaries_from_source_id')
374 def get_binary_from_name_suite(package, suitename, session=None):
375 ### For dak examine-package
376 ### XXX: Doesn't use object API yet
378 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
379 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
380 WHERE b.package=:package
382 AND fi.location = l.id
383 AND l.component = c.id
386 AND su.suite_name=:suitename
387 ORDER BY b.version DESC"""
389 return session.execute(sql, {'package': package, 'suitename': suitename})
391 __all__.append('get_binary_from_name_suite')
394 def get_binary_components(package, suitename, arch, session=None):
395 # Check for packages that have moved from one component to another
396 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
397 WHERE b.package=:package AND s.suite_name=:suitename
398 AND (a.arch_string = :arch OR a.arch_string = 'all')
399 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
400 AND f.location = l.id
401 AND l.component = c.id
404 vals = {'package': package, 'suitename': suitename, 'arch': arch}
406 return session.execute(query, vals)
408 __all__.append('get_binary_components')
410 ################################################################################
412 class BinaryACL(object):
413 def __init__(self, *args, **kwargs):
417 return '<BinaryACL %s>' % self.binary_acl_id
419 __all__.append('BinaryACL')
421 ################################################################################
423 class BinaryACLMap(object):
424 def __init__(self, *args, **kwargs):
428 return '<BinaryACLMap %s>' % self.binary_acl_map_id
430 __all__.append('BinaryACLMap')
432 ################################################################################
437 ArchiveDir "%(archivepath)s";
438 OverrideDir "/srv/ftp.debian.org/scripts/override/";
439 CacheDir "/srv/ftp.debian.org/database/";
444 Packages::Compress ". bzip2 gzip";
445 Sources::Compress ". bzip2 gzip";
450 bindirectory "incoming"
455 BinOverride "override.sid.all3";
456 BinCacheDB "packages-accepted.db";
458 FileList "%(filelist)s";
461 Packages::Extensions ".deb .udeb";
464 bindirectory "incoming/"
467 BinOverride "override.sid.all3";
468 SrcOverride "override.sid.all3.src";
469 FileList "%(filelist)s";
473 class BuildQueue(object):
474 def __init__(self, *args, **kwargs):
478 return '<BuildQueue %s>' % self.queue_name
480 def write_metadata(self, ourtime, force=False):
481 # Do we write out metafiles?
482 if not (force or self.generate_metadata):
485 session = DBConn().session().object_session(self)
487 fl_fd = fl_name = ac_fd = ac_name = None
489 arches = " ".join([ a.arch_string for a in session.query(Architecture).all() if a.arch_string != 'source' ])
490 startdir = os.getcwd()
493 # Grab files we want to include
494 newer = session.query(BuildQueueFile).filter_by(build_queue_id = 1).filter(BuildQueueFile.lastused > ourtime).all()
496 # Write file list with newer files
497 (fl_fd, fl_name) = mkstemp()
499 os.write(fl_fd, '%s\n' % n.fullpath)
502 # Write minimal apt.conf
503 # TODO: Remove hardcoding from template
504 (ac_fd, ac_name) = mkstemp()
505 os.write(ac_fd, MINIMAL_APT_CONF % {'archivepath': self.path,
506 'filelist': fl_name})
509 # Run apt-ftparchive generate
510 os.chdir(os.path.dirname(fl_name))
511 os.system('apt-ftparchive -qq -o APT::FTPArchive::Contents=off generate %s' % os.path.basename(fl_name))
513 # Run apt-ftparchive release
514 # TODO: Eww - fix this
515 bname = os.path.basename(self.path)
518 os.system("""apt-ftparchive -qq -o APT::FTPArchive::Release::Origin="%s" -o APT::FTPArchive::Release::Label="%s" -o -o APT::FTPArchive::Release::Description="%s" -o APT::FTPArchive::Release::Architectures="${archs}" release %s > Release""", [self.origin, self.label, self.releasedescription, arches, bname])
523 keyring = "--secret-keyring \"%s\"" % cnf["Dinstall::SigningKeyring"]
524 if cnf.has_key("Dinstall::SigningPubKeyring"):
525 keyring += " --keyring \"%s\"" % cnf["Dinstall::SigningPubKeyring"]
527 os.system("gpg %s --no-options --batch --no-tty --armour --default-key %s --detach-sign -o Release.gpg Release""" % (keyring, self.signingkey))
529 # Move the files if we got this far
530 os.rename('Release', os.path.join(bname, 'Release'))
532 os.rename('Release.gpg', os.path.join(bname, 'Release.gpg'))
534 # Clean up any left behind files
561 def clean_and_update(self, starttime, dryrun=False):
562 """WARNING: This routine commits for you"""
563 session = DBConn().session().object_session(self)
565 ourtime = starttime + timedelta(seconds=self.stay_of_execution)
567 if self.generate_metadata:
568 self.write_metadata(ourtime)
570 # Grab files older than our execution time
571 older = session.query(BuildQueueFile).filter_by(build_queue_id = 1).filter(BuildQueueFile.lastused <= ourtime).all()
577 print "I: Would have removed %s from the queue"
579 os.unlink(o.fullpath)
582 # If it wasn't there, don't worry
583 if e.errno == ENOENT:
586 # TODO: Replace with proper logging call
587 print "E: Could not remove %s" % o.fullpath
595 def add_file_from_pool(self, poolfile):
596 """Copies a file into the pool. Assumes that the PoolFile object is
597 attached to the same SQLAlchemy session as the Queue object is.
599 The caller is responsible for committing after calling this function."""
600 poolfile_basename = poolfile.filename[poolfile.filename.rindex(os.sep)+1:]
602 # Check if we have a file of this name or this ID already
603 for f in self.queuefiles:
604 if f.fileid is not None and f.fileid == poolfile.file_id or \
605 f.poolfile.filename == poolfile_basename:
606 # In this case, update the BuildQueueFile entry so we
607 # don't remove it too early
608 f.lastused = datetime.now()
609 DBConn().session().object_session(poolfile).add(f)
612 # Prepare BuildQueueFile object
613 qf = BuildQueueFile()
614 qf.build_queue_id = self.queue_id
615 qf.lastused = datetime.now()
616 qf.filename = poolfile_basename
618 targetpath = poolfile.fullpath
619 queuepath = os.path.join(self.path, poolfile_basename)
623 # We need to copy instead of symlink
625 utils.copy(targetpath, queuepath)
626 # NULL in the fileid field implies a copy
629 os.symlink(targetpath, queuepath)
630 qf.fileid = poolfile.file_id
634 # Get the same session as the PoolFile is using and add the qf to it
635 DBConn().session().object_session(poolfile).add(qf)
640 __all__.append('BuildQueue')
643 def get_build_queue(queuename, session=None):
645 Returns BuildQueue object for given C{queue name}, creating it if it does not
648 @type queuename: string
649 @param queuename: The name of the queue
651 @type session: Session
652 @param session: Optional SQLA session object (a temporary one will be
653 generated if not supplied)
656 @return: BuildQueue object for the given queue
659 q = session.query(BuildQueue).filter_by(queue_name=queuename)
663 except NoResultFound:
666 __all__.append('get_build_queue')
668 ################################################################################
670 class BuildQueueFile(object):
671 def __init__(self, *args, **kwargs):
675 return '<BuildQueueFile %s (%s)>' % (self.filename, self.build_queue_id)
679 return os.path.join(self.buildqueue.path, self.filename)
682 __all__.append('BuildQueueFile')
684 ################################################################################
686 class ChangePendingBinary(object):
687 def __init__(self, *args, **kwargs):
691 return '<ChangePendingBinary %s>' % self.change_pending_binary_id
693 __all__.append('ChangePendingBinary')
695 ################################################################################
697 class ChangePendingFile(object):
698 def __init__(self, *args, **kwargs):
702 return '<ChangePendingFile %s>' % self.change_pending_file_id
704 __all__.append('ChangePendingFile')
706 ################################################################################
708 class ChangePendingSource(object):
709 def __init__(self, *args, **kwargs):
713 return '<ChangePendingSource %s>' % self.change_pending_source_id
715 __all__.append('ChangePendingSource')
717 ################################################################################
719 class Component(object):
720 def __init__(self, *args, **kwargs):
723 def __eq__(self, val):
724 if isinstance(val, str):
725 return (self.component_name == val)
726 # This signals to use the normal comparison operator
727 return NotImplemented
729 def __ne__(self, val):
730 if isinstance(val, str):
731 return (self.component_name != val)
732 # This signals to use the normal comparison operator
733 return NotImplemented
736 return '<Component %s>' % self.component_name
739 __all__.append('Component')
742 def get_component(component, session=None):
744 Returns database id for given C{component}.
746 @type component: string
747 @param component: The name of the override type
750 @return: the database id for the given component
753 component = component.lower()
755 q = session.query(Component).filter_by(component_name=component)
759 except NoResultFound:
762 __all__.append('get_component')
764 ################################################################################
766 class DBConfig(object):
767 def __init__(self, *args, **kwargs):
771 return '<DBConfig %s>' % self.name
773 __all__.append('DBConfig')
775 ################################################################################
778 def get_or_set_contents_file_id(filename, session=None):
780 Returns database id for given filename.
782 If no matching file is found, a row is inserted.
784 @type filename: string
785 @param filename: The filename
786 @type session: SQLAlchemy
787 @param session: Optional SQL session object (a temporary one will be
788 generated if not supplied). If not passed, a commit will be performed at
789 the end of the function, otherwise the caller is responsible for commiting.
792 @return: the database id for the given component
795 q = session.query(ContentFilename).filter_by(filename=filename)
798 ret = q.one().cafilename_id
799 except NoResultFound:
800 cf = ContentFilename()
801 cf.filename = filename
803 session.commit_or_flush()
804 ret = cf.cafilename_id
808 __all__.append('get_or_set_contents_file_id')
811 def get_contents(suite, overridetype, section=None, session=None):
813 Returns contents for a suite / overridetype combination, limiting
814 to a section if not None.
817 @param suite: Suite object
819 @type overridetype: OverrideType
820 @param overridetype: OverrideType object
822 @type section: Section
823 @param section: Optional section object to limit results to
825 @type session: SQLAlchemy
826 @param session: Optional SQL session object (a temporary one will be
827 generated if not supplied)
830 @return: ResultsProxy object set up to return tuples of (filename, section,
834 # find me all of the contents for a given suite
835 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
839 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
840 JOIN content_file_names n ON (c.filename=n.id)
841 JOIN binaries b ON (b.id=c.binary_pkg)
842 JOIN override o ON (o.package=b.package)
843 JOIN section s ON (s.id=o.section)
844 WHERE o.suite = :suiteid AND o.type = :overridetypeid
845 AND b.type=:overridetypename"""
847 vals = {'suiteid': suite.suite_id,
848 'overridetypeid': overridetype.overridetype_id,
849 'overridetypename': overridetype.overridetype}
851 if section is not None:
852 contents_q += " AND s.id = :sectionid"
853 vals['sectionid'] = section.section_id
855 contents_q += " ORDER BY fn"
857 return session.execute(contents_q, vals)
859 __all__.append('get_contents')
861 ################################################################################
863 class ContentFilepath(object):
864 def __init__(self, *args, **kwargs):
868 return '<ContentFilepath %s>' % self.filepath
870 __all__.append('ContentFilepath')
873 def get_or_set_contents_path_id(filepath, session=None):
875 Returns database id for given path.
877 If no matching file is found, a row is inserted.
879 @type filename: string
880 @param filename: The filepath
881 @type session: SQLAlchemy
882 @param session: Optional SQL session object (a temporary one will be
883 generated if not supplied). If not passed, a commit will be performed at
884 the end of the function, otherwise the caller is responsible for commiting.
887 @return: the database id for the given path
890 q = session.query(ContentFilepath).filter_by(filepath=filepath)
893 ret = q.one().cafilepath_id
894 except NoResultFound:
895 cf = ContentFilepath()
896 cf.filepath = filepath
898 session.commit_or_flush()
899 ret = cf.cafilepath_id
903 __all__.append('get_or_set_contents_path_id')
905 ################################################################################
907 class ContentAssociation(object):
908 def __init__(self, *args, **kwargs):
912 return '<ContentAssociation %s>' % self.ca_id
914 __all__.append('ContentAssociation')
916 def insert_content_paths(binary_id, fullpaths, session=None):
918 Make sure given path is associated with given binary id
921 @param binary_id: the id of the binary
922 @type fullpaths: list
923 @param fullpaths: the list of paths of the file being associated with the binary
924 @type session: SQLAlchemy session
925 @param session: Optional SQLAlchemy session. If this is passed, the caller
926 is responsible for ensuring a transaction has begun and committing the
927 results or rolling back based on the result code. If not passed, a commit
928 will be performed at the end of the function, otherwise the caller is
929 responsible for commiting.
931 @return: True upon success
936 session = DBConn().session()
942 for fullpath in fullpaths:
943 if fullpath.startswith( './' ):
944 fullpath = fullpath[2:]
946 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )", { 'filename': fullpath, 'id': binary_id} )
954 traceback.print_exc()
956 # Only rollback if we set up the session ourself
963 __all__.append('insert_content_paths')
965 ################################################################################
967 class DSCFile(object):
968 def __init__(self, *args, **kwargs):
972 return '<DSCFile %s>' % self.dscfile_id
974 __all__.append('DSCFile')
977 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
979 Returns a list of DSCFiles which may be empty
981 @type dscfile_id: int (optional)
982 @param dscfile_id: the dscfile_id of the DSCFiles to find
984 @type source_id: int (optional)
985 @param source_id: the source id related to the DSCFiles to find
987 @type poolfile_id: int (optional)
988 @param poolfile_id: the poolfile id related to the DSCFiles to find
991 @return: Possibly empty list of DSCFiles
994 q = session.query(DSCFile)
996 if dscfile_id is not None:
997 q = q.filter_by(dscfile_id=dscfile_id)
999 if source_id is not None:
1000 q = q.filter_by(source_id=source_id)
1002 if poolfile_id is not None:
1003 q = q.filter_by(poolfile_id=poolfile_id)
1007 __all__.append('get_dscfiles')
1009 ################################################################################
1011 class PoolFile(object):
1012 def __init__(self, *args, **kwargs):
1016 return '<PoolFile %s>' % self.filename
1020 return os.path.join(self.location.path, self.filename)
1022 __all__.append('PoolFile')
1025 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
1028 (ValidFileFound [boolean or None], PoolFile object or None)
1030 @type filename: string
1031 @param filename: the filename of the file to check against the DB
1034 @param filesize: the size of the file to check against the DB
1036 @type md5sum: string
1037 @param md5sum: the md5sum of the file to check against the DB
1039 @type location_id: int
1040 @param location_id: the id of the location to look in
1043 @return: Tuple of length 2.
1044 If more than one file found with that name:
1046 If valid pool file found: (True, PoolFile object)
1047 If valid pool file not found:
1048 (False, None) if no file found
1049 (False, PoolFile object) if file found with size/md5sum mismatch
1052 q = session.query(PoolFile).filter_by(filename=filename)
1053 q = q.join(Location).filter_by(location_id=location_id)
1063 if obj.md5sum != md5sum or obj.filesize != int(filesize):
1071 __all__.append('check_poolfile')
1074 def get_poolfile_by_id(file_id, session=None):
1076 Returns a PoolFile objects or None for the given id
1079 @param file_id: the id of the file to look for
1081 @rtype: PoolFile or None
1082 @return: either the PoolFile object or None
1085 q = session.query(PoolFile).filter_by(file_id=file_id)
1089 except NoResultFound:
1092 __all__.append('get_poolfile_by_id')
1096 def get_poolfile_by_name(filename, location_id=None, session=None):
1098 Returns an array of PoolFile objects for the given filename and
1099 (optionally) location_id
1101 @type filename: string
1102 @param filename: the filename of the file to check against the DB
1104 @type location_id: int
1105 @param location_id: the id of the location to look in (optional)
1108 @return: array of PoolFile objects
1111 q = session.query(PoolFile).filter_by(filename=filename)
1113 if location_id is not None:
1114 q = q.join(Location).filter_by(location_id=location_id)
1118 __all__.append('get_poolfile_by_name')
1121 def get_poolfile_like_name(filename, session=None):
1123 Returns an array of PoolFile objects which are like the given name
1125 @type filename: string
1126 @param filename: the filename of the file to check against the DB
1129 @return: array of PoolFile objects
1132 # TODO: There must be a way of properly using bind parameters with %FOO%
1133 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
1137 __all__.append('get_poolfile_like_name')
1140 def add_poolfile(filename, datadict, location_id, session=None):
1142 Add a new file to the pool
1144 @type filename: string
1145 @param filename: filename
1147 @type datadict: dict
1148 @param datadict: dict with needed data
1150 @type location_id: int
1151 @param location_id: database id of the location
1154 @return: the PoolFile object created
1156 poolfile = PoolFile()
1157 poolfile.filename = filename
1158 poolfile.filesize = datadict["size"]
1159 poolfile.md5sum = datadict["md5sum"]
1160 poolfile.sha1sum = datadict["sha1sum"]
1161 poolfile.sha256sum = datadict["sha256sum"]
1162 poolfile.location_id = location_id
1164 session.add(poolfile)
1165 # Flush to get a file id (NB: This is not a commit)
1170 __all__.append('add_poolfile')
1172 ################################################################################
1174 class Fingerprint(object):
1175 def __init__(self, *args, **kwargs):
1179 return '<Fingerprint %s>' % self.fingerprint
1181 __all__.append('Fingerprint')
1184 def get_fingerprint(fpr, session=None):
1186 Returns Fingerprint object for given fpr.
1189 @param fpr: The fpr to find / add
1191 @type session: SQLAlchemy
1192 @param session: Optional SQL session object (a temporary one will be
1193 generated if not supplied).
1196 @return: the Fingerprint object for the given fpr or None
1199 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1203 except NoResultFound:
1208 __all__.append('get_fingerprint')
1211 def get_or_set_fingerprint(fpr, session=None):
1213 Returns Fingerprint object for given fpr.
1215 If no matching fpr is found, a row is inserted.
1218 @param fpr: The fpr to find / add
1220 @type session: SQLAlchemy
1221 @param session: Optional SQL session object (a temporary one will be
1222 generated if not supplied). If not passed, a commit will be performed at
1223 the end of the function, otherwise the caller is responsible for commiting.
1224 A flush will be performed either way.
1227 @return: the Fingerprint object for the given fpr
1230 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
1234 except NoResultFound:
1235 fingerprint = Fingerprint()
1236 fingerprint.fingerprint = fpr
1237 session.add(fingerprint)
1238 session.commit_or_flush()
1243 __all__.append('get_or_set_fingerprint')
1245 ################################################################################
1247 # Helper routine for Keyring class
1248 def get_ldap_name(entry):
1250 for k in ["cn", "mn", "sn"]:
1252 if ret and ret[0] != "" and ret[0] != "-":
1254 return " ".join(name)
1256 ################################################################################
1258 class Keyring(object):
1259 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
1260 " --with-colons --fingerprint --fingerprint"
1265 def __init__(self, *args, **kwargs):
1269 return '<Keyring %s>' % self.keyring_name
1271 def de_escape_gpg_str(self, txt):
1272 esclist = re.split(r'(\\x..)', txt)
1273 for x in range(1,len(esclist),2):
1274 esclist[x] = "%c" % (int(esclist[x][2:],16))
1275 return "".join(esclist)
1277 def load_keys(self, keyring):
1280 if not self.keyring_id:
1281 raise Exception('Must be initialized with database information')
1283 k = os.popen(self.gpg_invocation % keyring, "r")
1287 for line in k.xreadlines():
1288 field = line.split(":")
1289 if field[0] == "pub":
1291 (name, addr) = email.Utils.parseaddr(field[9])
1292 name = re.sub(r"\s*[(].*[)]", "", name)
1293 if name == "" or addr == "" or "@" not in addr:
1295 addr = "invalid-uid"
1296 name = self.de_escape_gpg_str(name)
1297 self.keys[key] = {"email": addr}
1299 self.keys[key]["name"] = name
1300 self.keys[key]["aliases"] = [name]
1301 self.keys[key]["fingerprints"] = []
1303 elif key and field[0] == "sub" and len(field) >= 12:
1304 signingkey = ("s" in field[11])
1305 elif key and field[0] == "uid":
1306 (name, addr) = email.Utils.parseaddr(field[9])
1307 if name and name not in self.keys[key]["aliases"]:
1308 self.keys[key]["aliases"].append(name)
1309 elif signingkey and field[0] == "fpr":
1310 self.keys[key]["fingerprints"].append(field[9])
1311 self.fpr_lookup[field[9]] = key
1313 def import_users_from_ldap(self, session):
1317 LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
1318 LDAPServer = cnf["Import-LDAP-Fingerprints::LDAPServer"]
1320 l = ldap.open(LDAPServer)
1321 l.simple_bind_s("","")
1322 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
1323 "(&(keyfingerprint=*)(gidnumber=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
1324 ["uid", "keyfingerprint", "cn", "mn", "sn"])
1326 ldap_fin_uid_id = {}
1333 uid = entry["uid"][0]
1334 name = get_ldap_name(entry)
1335 fingerprints = entry["keyFingerPrint"]
1337 for f in fingerprints:
1338 key = self.fpr_lookup.get(f, None)
1339 if key not in self.keys:
1341 self.keys[key]["uid"] = uid
1345 keyid = get_or_set_uid(uid, session).uid_id
1346 byuid[keyid] = (uid, name)
1347 byname[uid] = (keyid, name)
1349 return (byname, byuid)
1351 def generate_users_from_keyring(self, format, session):
1355 for x in self.keys.keys():
1356 if self.keys[x]["email"] == "invalid-uid":
1358 self.keys[x]["uid"] = format % "invalid-uid"
1360 uid = format % self.keys[x]["email"]
1361 keyid = get_or_set_uid(uid, session).uid_id
1362 byuid[keyid] = (uid, self.keys[x]["name"])
1363 byname[uid] = (keyid, self.keys[x]["name"])
1364 self.keys[x]["uid"] = uid
1367 uid = format % "invalid-uid"
1368 keyid = get_or_set_uid(uid, session).uid_id
1369 byuid[keyid] = (uid, "ungeneratable user id")
1370 byname[uid] = (keyid, "ungeneratable user id")
1372 return (byname, byuid)
1374 __all__.append('Keyring')
1377 def get_keyring(keyring, session=None):
1379 If C{keyring} does not have an entry in the C{keyrings} table yet, return None
1380 If C{keyring} already has an entry, simply return the existing Keyring
1382 @type keyring: string
1383 @param keyring: the keyring name
1386 @return: the Keyring object for this keyring
1389 q = session.query(Keyring).filter_by(keyring_name=keyring)
1393 except NoResultFound:
1396 __all__.append('get_keyring')
1398 ################################################################################
1400 class KeyringACLMap(object):
1401 def __init__(self, *args, **kwargs):
1405 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
1407 __all__.append('KeyringACLMap')
1409 ################################################################################
1411 class DBChange(object):
1412 def __init__(self, *args, **kwargs):
1416 return '<DBChange %s>' % self.changesname
1418 __all__.append('DBChange')
1421 def get_dbchange(filename, session=None):
1423 returns DBChange object for given C{filename}.
1425 @type archive: string
1426 @param archive: the name of the arhive
1428 @type session: Session
1429 @param session: Optional SQLA session object (a temporary one will be
1430 generated if not supplied)
1433 @return: Archive object for the given name (None if not present)
1436 q = session.query(DBChange).filter_by(changesname=filename)
1440 except NoResultFound:
1443 __all__.append('get_dbchange')
1445 ################################################################################
1447 class Location(object):
1448 def __init__(self, *args, **kwargs):
1452 return '<Location %s (%s)>' % (self.path, self.location_id)
1454 __all__.append('Location')
1457 def get_location(location, component=None, archive=None, session=None):
1459 Returns Location object for the given combination of location, component
1462 @type location: string
1463 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1465 @type component: string
1466 @param component: the component name (if None, no restriction applied)
1468 @type archive: string
1469 @param archive_id: the archive name (if None, no restriction applied)
1471 @rtype: Location / None
1472 @return: Either a Location object or None if one can't be found
1475 q = session.query(Location).filter_by(path=location)
1477 if archive is not None:
1478 q = q.join(Archive).filter_by(archive_name=archive)
1480 if component is not None:
1481 q = q.join(Component).filter_by(component_name=component)
1485 except NoResultFound:
1488 __all__.append('get_location')
1490 ################################################################################
1492 class Maintainer(object):
1493 def __init__(self, *args, **kwargs):
1497 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1499 def get_split_maintainer(self):
1500 if not hasattr(self, 'name') or self.name is None:
1501 return ('', '', '', '')
1503 return fix_maintainer(self.name.strip())
1505 __all__.append('Maintainer')
1508 def get_or_set_maintainer(name, session=None):
1510 Returns Maintainer object for given maintainer name.
1512 If no matching maintainer name is found, a row is inserted.
1515 @param name: The maintainer name to add
1517 @type session: SQLAlchemy
1518 @param session: Optional SQL session object (a temporary one will be
1519 generated if not supplied). If not passed, a commit will be performed at
1520 the end of the function, otherwise the caller is responsible for commiting.
1521 A flush will be performed either way.
1524 @return: the Maintainer object for the given maintainer
1527 q = session.query(Maintainer).filter_by(name=name)
1530 except NoResultFound:
1531 maintainer = Maintainer()
1532 maintainer.name = name
1533 session.add(maintainer)
1534 session.commit_or_flush()
1539 __all__.append('get_or_set_maintainer')
1542 def get_maintainer(maintainer_id, session=None):
1544 Return the name of the maintainer behind C{maintainer_id} or None if that
1545 maintainer_id is invalid.
1547 @type maintainer_id: int
1548 @param maintainer_id: the id of the maintainer
1551 @return: the Maintainer with this C{maintainer_id}
1554 return session.query(Maintainer).get(maintainer_id)
1556 __all__.append('get_maintainer')
1558 ################################################################################
1560 class NewComment(object):
1561 def __init__(self, *args, **kwargs):
1565 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1567 __all__.append('NewComment')
1570 def has_new_comment(package, version, session=None):
1572 Returns true if the given combination of C{package}, C{version} has a comment.
1574 @type package: string
1575 @param package: name of the package
1577 @type version: string
1578 @param version: package version
1580 @type session: Session
1581 @param session: Optional SQLA session object (a temporary one will be
1582 generated if not supplied)
1588 q = session.query(NewComment)
1589 q = q.filter_by(package=package)
1590 q = q.filter_by(version=version)
1592 return bool(q.count() > 0)
1594 __all__.append('has_new_comment')
1597 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1599 Returns (possibly empty) list of NewComment objects for the given
1602 @type package: string (optional)
1603 @param package: name of the package
1605 @type version: string (optional)
1606 @param version: package version
1608 @type comment_id: int (optional)
1609 @param comment_id: An id of a comment
1611 @type session: Session
1612 @param session: Optional SQLA session object (a temporary one will be
1613 generated if not supplied)
1616 @return: A (possibly empty) list of NewComment objects will be returned
1619 q = session.query(NewComment)
1620 if package is not None: q = q.filter_by(package=package)
1621 if version is not None: q = q.filter_by(version=version)
1622 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1626 __all__.append('get_new_comments')
1628 ################################################################################
1630 class Override(object):
1631 def __init__(self, *args, **kwargs):
1635 return '<Override %s (%s)>' % (self.package, self.suite_id)
1637 __all__.append('Override')
1640 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1642 Returns Override object for the given parameters
1644 @type package: string
1645 @param package: The name of the package
1647 @type suite: string, list or None
1648 @param suite: The name of the suite (or suites if a list) to limit to. If
1649 None, don't limit. Defaults to None.
1651 @type component: string, list or None
1652 @param component: The name of the component (or components if a list) to
1653 limit to. If None, don't limit. Defaults to None.
1655 @type overridetype: string, list or None
1656 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1657 limit to. If None, don't limit. Defaults to None.
1659 @type session: Session
1660 @param session: Optional SQLA session object (a temporary one will be
1661 generated if not supplied)
1664 @return: A (possibly empty) list of Override objects will be returned
1667 q = session.query(Override)
1668 q = q.filter_by(package=package)
1670 if suite is not None:
1671 if not isinstance(suite, list): suite = [suite]
1672 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1674 if component is not None:
1675 if not isinstance(component, list): component = [component]
1676 q = q.join(Component).filter(Component.component_name.in_(component))
1678 if overridetype is not None:
1679 if not isinstance(overridetype, list): overridetype = [overridetype]
1680 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1684 __all__.append('get_override')
1687 ################################################################################
1689 class OverrideType(object):
1690 def __init__(self, *args, **kwargs):
1694 return '<OverrideType %s>' % self.overridetype
1696 __all__.append('OverrideType')
1699 def get_override_type(override_type, session=None):
1701 Returns OverrideType object for given C{override type}.
1703 @type override_type: string
1704 @param override_type: The name of the override type
1706 @type session: Session
1707 @param session: Optional SQLA session object (a temporary one will be
1708 generated if not supplied)
1711 @return: the database id for the given override type
1714 q = session.query(OverrideType).filter_by(overridetype=override_type)
1718 except NoResultFound:
1721 __all__.append('get_override_type')
1723 ################################################################################
1725 class PendingContentAssociation(object):
1726 def __init__(self, *args, **kwargs):
1730 return '<PendingContentAssociation %s>' % self.pca_id
1732 __all__.append('PendingContentAssociation')
1734 def insert_pending_content_paths(package, fullpaths, session=None):
1736 Make sure given paths are temporarily associated with given
1740 @param package: the package to associate with should have been read in from the binary control file
1741 @type fullpaths: list
1742 @param fullpaths: the list of paths of the file being associated with the binary
1743 @type session: SQLAlchemy session
1744 @param session: Optional SQLAlchemy session. If this is passed, the caller
1745 is responsible for ensuring a transaction has begun and committing the
1746 results or rolling back based on the result code. If not passed, a commit
1747 will be performed at the end of the function
1749 @return: True upon success, False if there is a problem
1752 privatetrans = False
1755 session = DBConn().session()
1759 arch = get_architecture(package['Architecture'], session)
1760 arch_id = arch.arch_id
1762 # Remove any already existing recorded files for this package
1763 q = session.query(PendingContentAssociation)
1764 q = q.filter_by(package=package['Package'])
1765 q = q.filter_by(version=package['Version'])
1766 q = q.filter_by(architecture=arch_id)
1771 for fullpath in fullpaths:
1772 (path, filename) = os.path.split(fullpath)
1774 if path.startswith( "./" ):
1777 filepath_id = get_or_set_contents_path_id(path, session)
1778 filename_id = get_or_set_contents_file_id(filename, session)
1780 pathcache[fullpath] = (filepath_id, filename_id)
1782 for fullpath, dat in pathcache.items():
1783 pca = PendingContentAssociation()
1784 pca.package = package['Package']
1785 pca.version = package['Version']
1786 pca.filepath_id = dat[0]
1787 pca.filename_id = dat[1]
1788 pca.architecture = arch_id
1791 # Only commit if we set up the session ourself
1799 except Exception, e:
1800 traceback.print_exc()
1802 # Only rollback if we set up the session ourself
1809 __all__.append('insert_pending_content_paths')
1811 ################################################################################
1813 class PolicyQueue(object):
1814 def __init__(self, *args, **kwargs):
1818 return '<PolicyQueue %s>' % self.queue_name
1820 __all__.append('PolicyQueue')
1823 def get_policy_queue(queuename, session=None):
1825 Returns PolicyQueue object for given C{queue name}
1827 @type queuename: string
1828 @param queuename: The name of the queue
1830 @type session: Session
1831 @param session: Optional SQLA session object (a temporary one will be
1832 generated if not supplied)
1835 @return: PolicyQueue object for the given queue
1838 q = session.query(PolicyQueue).filter_by(queue_name=queuename)
1842 except NoResultFound:
1845 __all__.append('get_policy_queue')
1847 ################################################################################
1849 class Priority(object):
1850 def __init__(self, *args, **kwargs):
1853 def __eq__(self, val):
1854 if isinstance(val, str):
1855 return (self.priority == val)
1856 # This signals to use the normal comparison operator
1857 return NotImplemented
1859 def __ne__(self, val):
1860 if isinstance(val, str):
1861 return (self.priority != val)
1862 # This signals to use the normal comparison operator
1863 return NotImplemented
1866 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1868 __all__.append('Priority')
1871 def get_priority(priority, session=None):
1873 Returns Priority object for given C{priority name}.
1875 @type priority: string
1876 @param priority: The name of the priority
1878 @type session: Session
1879 @param session: Optional SQLA session object (a temporary one will be
1880 generated if not supplied)
1883 @return: Priority object for the given priority
1886 q = session.query(Priority).filter_by(priority=priority)
1890 except NoResultFound:
1893 __all__.append('get_priority')
1896 def get_priorities(session=None):
1898 Returns dictionary of priority names -> id mappings
1900 @type session: Session
1901 @param session: Optional SQL session object (a temporary one will be
1902 generated if not supplied)
1905 @return: dictionary of priority names -> id mappings
1909 q = session.query(Priority)
1911 ret[x.priority] = x.priority_id
1915 __all__.append('get_priorities')
1917 ################################################################################
1919 class Section(object):
1920 def __init__(self, *args, **kwargs):
1923 def __eq__(self, val):
1924 if isinstance(val, str):
1925 return (self.section == val)
1926 # This signals to use the normal comparison operator
1927 return NotImplemented
1929 def __ne__(self, val):
1930 if isinstance(val, str):
1931 return (self.section != val)
1932 # This signals to use the normal comparison operator
1933 return NotImplemented
1936 return '<Section %s>' % self.section
1938 __all__.append('Section')
1941 def get_section(section, session=None):
1943 Returns Section object for given C{section name}.
1945 @type section: string
1946 @param section: The name of the section
1948 @type session: Session
1949 @param session: Optional SQLA session object (a temporary one will be
1950 generated if not supplied)
1953 @return: Section object for the given section name
1956 q = session.query(Section).filter_by(section=section)
1960 except NoResultFound:
1963 __all__.append('get_section')
1966 def get_sections(session=None):
1968 Returns dictionary of section names -> id mappings
1970 @type session: Session
1971 @param session: Optional SQL session object (a temporary one will be
1972 generated if not supplied)
1975 @return: dictionary of section names -> id mappings
1979 q = session.query(Section)
1981 ret[x.section] = x.section_id
1985 __all__.append('get_sections')
1987 ################################################################################
1989 class DBSource(object):
1990 def __init__(self, *args, **kwargs):
1994 return '<DBSource %s (%s)>' % (self.source, self.version)
1996 __all__.append('DBSource')
1999 def source_exists(source, source_version, suites = ["any"], session=None):
2001 Ensure that source exists somewhere in the archive for the binary
2002 upload being processed.
2003 1. exact match => 1.0-3
2004 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
2006 @type package: string
2007 @param package: package source name
2009 @type source_version: string
2010 @param source_version: expected source version
2013 @param suites: list of suites to check in, default I{any}
2015 @type session: Session
2016 @param session: Optional SQLA session object (a temporary one will be
2017 generated if not supplied)
2020 @return: returns 1 if a source with expected version is found, otherwise 0
2027 for suite in suites:
2028 q = session.query(DBSource).filter_by(source=source)
2030 # source must exist in suite X, or in some other suite that's
2031 # mapped to X, recursively... silent-maps are counted too,
2032 # unreleased-maps aren't.
2033 maps = cnf.ValueList("SuiteMappings")[:]
2035 maps = [ m.split() for m in maps ]
2036 maps = [ (x[1], x[2]) for x in maps
2037 if x[0] == "map" or x[0] == "silent-map" ]
2040 if x[1] in s and x[0] not in s:
2043 q = q.join(SrcAssociation).join(Suite)
2044 q = q.filter(Suite.suite_name.in_(s))
2046 # Reduce the query results to a list of version numbers
2047 ql = [ j.version for j in q.all() ]
2050 if source_version in ql:
2054 from daklib.regexes import re_bin_only_nmu
2055 orig_source_version = re_bin_only_nmu.sub('', source_version)
2056 if orig_source_version in ql:
2059 # No source found so return not ok
2064 __all__.append('source_exists')
2067 def get_suites_source_in(source, session=None):
2069 Returns list of Suite objects which given C{source} name is in
2072 @param source: DBSource package name to search for
2075 @return: list of Suite objects for the given source
2078 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
2080 __all__.append('get_suites_source_in')
2083 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
2085 Returns list of DBSource objects for given C{source} name and other parameters
2088 @param source: DBSource package name to search for
2090 @type source: str or None
2091 @param source: DBSource version name to search for or None if not applicable
2093 @type dm_upload_allowed: bool
2094 @param dm_upload_allowed: If None, no effect. If True or False, only
2095 return packages with that dm_upload_allowed setting
2097 @type session: Session
2098 @param session: Optional SQL session object (a temporary one will be
2099 generated if not supplied)
2102 @return: list of DBSource objects for the given name (may be empty)
2105 q = session.query(DBSource).filter_by(source=source)
2107 if version is not None:
2108 q = q.filter_by(version=version)
2110 if dm_upload_allowed is not None:
2111 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
2115 __all__.append('get_sources_from_name')
2118 def get_source_in_suite(source, suite, session=None):
2120 Returns list of DBSource objects for a combination of C{source} and C{suite}.
2122 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
2123 - B{suite} - a suite name, eg. I{unstable}
2125 @type source: string
2126 @param source: source package name
2129 @param suite: the suite name
2132 @return: the version for I{source} in I{suite}
2136 q = session.query(SrcAssociation)
2137 q = q.join('source').filter_by(source=source)
2138 q = q.join('suite').filter_by(suite_name=suite)
2141 return q.one().source
2142 except NoResultFound:
2145 __all__.append('get_source_in_suite')
2147 ################################################################################
2150 def add_dsc_to_db(u, filename, session=None):
2151 entry = u.pkg.files[filename]
2155 source.source = u.pkg.dsc["source"]
2156 source.version = u.pkg.dsc["version"] # NB: not files[file]["version"], that has no epoch
2157 source.maintainer_id = get_or_set_maintainer(u.pkg.dsc["maintainer"], session).maintainer_id
2158 source.changedby_id = get_or_set_maintainer(u.pkg.changes["changed-by"], session).maintainer_id
2159 source.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2160 source.install_date = datetime.now().date()
2162 dsc_component = entry["component"]
2163 dsc_location_id = entry["location id"]
2165 source.dm_upload_allowed = (u.pkg.dsc.get("dm-upload-allowed", '') == "yes")
2167 # Set up a new poolfile if necessary
2168 if not entry.has_key("files id") or not entry["files id"]:
2169 filename = entry["pool name"] + filename
2170 poolfile = add_poolfile(filename, entry, dsc_location_id, session)
2172 pfs.append(poolfile)
2173 entry["files id"] = poolfile.file_id
2175 source.poolfile_id = entry["files id"]
2179 for suite_name in u.pkg.changes["distribution"].keys():
2180 sa = SrcAssociation()
2181 sa.source_id = source.source_id
2182 sa.suite_id = get_suite(suite_name).suite_id
2187 # Add the source files to the DB (files and dsc_files)
2189 dscfile.source_id = source.source_id
2190 dscfile.poolfile_id = entry["files id"]
2191 session.add(dscfile)
2193 for dsc_file, dentry in u.pkg.dsc_files.items():
2195 df.source_id = source.source_id
2197 # If the .orig tarball is already in the pool, it's
2198 # files id is stored in dsc_files by check_dsc().
2199 files_id = dentry.get("files id", None)
2201 # Find the entry in the files hash
2202 # TODO: Bail out here properly
2204 for f, e in u.pkg.files.items():
2209 if files_id is None:
2210 filename = dfentry["pool name"] + dsc_file
2212 (found, obj) = check_poolfile(filename, dentry["size"], dentry["md5sum"], dsc_location_id)
2213 # FIXME: needs to check for -1/-2 and or handle exception
2214 if found and obj is not None:
2215 files_id = obj.file_id
2218 # If still not found, add it
2219 if files_id is None:
2220 # HACK: Force sha1sum etc into dentry
2221 dentry["sha1sum"] = dfentry["sha1sum"]
2222 dentry["sha256sum"] = dfentry["sha256sum"]
2223 poolfile = add_poolfile(filename, dentry, dsc_location_id, session)
2224 pfs.append(poolfile)
2225 files_id = poolfile.file_id
2227 poolfile = get_poolfile_by_id(files_id, session)
2228 if poolfile is None:
2229 utils.fubar("INTERNAL ERROR. Found no poolfile with id %d" % files_id)
2230 pfs.append(poolfile)
2232 df.poolfile_id = files_id
2237 # Add the src_uploaders to the DB
2238 uploader_ids = [source.maintainer_id]
2239 if u.pkg.dsc.has_key("uploaders"):
2240 for up in u.pkg.dsc["uploaders"].split(","):
2242 uploader_ids.append(get_or_set_maintainer(up, session).maintainer_id)
2245 for up in uploader_ids:
2246 if added_ids.has_key(up):
2247 utils.warn("Already saw uploader %s for source %s" % (up, source.source))
2253 su.maintainer_id = up
2254 su.source_id = source.source_id
2259 return dsc_component, dsc_location_id, pfs
2261 __all__.append('add_dsc_to_db')
2264 def add_deb_to_db(u, filename, session=None):
2266 Contrary to what you might expect, this routine deals with both
2267 debs and udebs. That info is in 'dbtype', whilst 'type' is
2268 'deb' for both of them
2271 entry = u.pkg.files[filename]
2274 bin.package = entry["package"]
2275 bin.version = entry["version"]
2276 bin.maintainer_id = get_or_set_maintainer(entry["maintainer"], session).maintainer_id
2277 bin.fingerprint_id = get_or_set_fingerprint(u.pkg.changes["fingerprint"], session).fingerprint_id
2278 bin.arch_id = get_architecture(entry["architecture"], session).arch_id
2279 bin.binarytype = entry["dbtype"]
2282 filename = entry["pool name"] + filename
2283 fullpath = os.path.join(cnf["Dir::Pool"], filename)
2284 if not entry.get("location id", None):
2285 entry["location id"] = get_location(cnf["Dir::Pool"], entry["component"], session=session).location_id
2287 if entry.get("files id", None):
2288 poolfile = get_poolfile_by_id(bin.poolfile_id)
2289 bin.poolfile_id = entry["files id"]
2291 poolfile = add_poolfile(filename, entry, entry["location id"], session)
2292 bin.poolfile_id = entry["files id"] = poolfile.file_id
2295 bin_sources = get_sources_from_name(entry["source package"], entry["source version"], session=session)
2296 if len(bin_sources) != 1:
2297 raise NoSourceFieldError, "Unable to find a unique source id for %s (%s), %s, file %s, type %s, signed by %s" % \
2298 (bin.package, bin.version, bin.architecture.arch_string,
2299 filename, bin.binarytype, u.pkg.changes["fingerprint"])
2301 bin.source_id = bin_sources[0].source_id
2303 # Add and flush object so it has an ID
2307 # Add BinAssociations
2308 for suite_name in u.pkg.changes["distribution"].keys():
2309 ba = BinAssociation()
2310 ba.binary_id = bin.binary_id
2311 ba.suite_id = get_suite(suite_name).suite_id
2316 # Deal with contents - disabled for now
2317 #contents = copy_temporary_contents(bin.package, bin.version, bin.architecture.arch_string, os.path.basename(filename), None, session)
2319 # print "REJECT\nCould not determine contents of package %s" % bin.package
2320 # session.rollback()
2321 # raise MissingContents, "No contents stored for package %s, and couldn't determine contents of %s" % (bin.package, filename)
2325 __all__.append('add_deb_to_db')
2327 ################################################################################
2329 class SourceACL(object):
2330 def __init__(self, *args, **kwargs):
2334 return '<SourceACL %s>' % self.source_acl_id
2336 __all__.append('SourceACL')
2338 ################################################################################
2340 class SrcAssociation(object):
2341 def __init__(self, *args, **kwargs):
2345 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2347 __all__.append('SrcAssociation')
2349 ################################################################################
2351 class SrcFormat(object):
2352 def __init__(self, *args, **kwargs):
2356 return '<SrcFormat %s>' % (self.format_name)
2358 __all__.append('SrcFormat')
2360 ################################################################################
2362 class SrcUploader(object):
2363 def __init__(self, *args, **kwargs):
2367 return '<SrcUploader %s>' % self.uploader_id
2369 __all__.append('SrcUploader')
2371 ################################################################################
2373 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2374 ('SuiteID', 'suite_id'),
2375 ('Version', 'version'),
2376 ('Origin', 'origin'),
2378 ('Description', 'description'),
2379 ('Untouchable', 'untouchable'),
2380 ('Announce', 'announce'),
2381 ('Codename', 'codename'),
2382 ('OverrideCodename', 'overridecodename'),
2383 ('ValidTime', 'validtime'),
2384 ('Priority', 'priority'),
2385 ('NotAutomatic', 'notautomatic'),
2386 ('CopyChanges', 'copychanges'),
2387 ('CopyDotDak', 'copydotdak'),
2388 ('CommentsDir', 'commentsdir'),
2389 ('OverrideSuite', 'overridesuite'),
2390 ('ChangelogBase', 'changelogbase')]
2393 class Suite(object):
2394 def __init__(self, *args, **kwargs):
2398 return '<Suite %s>' % self.suite_name
2400 def __eq__(self, val):
2401 if isinstance(val, str):
2402 return (self.suite_name == val)
2403 # This signals to use the normal comparison operator
2404 return NotImplemented
2406 def __ne__(self, val):
2407 if isinstance(val, str):
2408 return (self.suite_name != val)
2409 # This signals to use the normal comparison operator
2410 return NotImplemented
2414 for disp, field in SUITE_FIELDS:
2415 val = getattr(self, field, None)
2417 ret.append("%s: %s" % (disp, val))
2419 return "\n".join(ret)
2421 __all__.append('Suite')
2424 def get_suite_architecture(suite, architecture, session=None):
2426 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2430 @param suite: Suite name to search for
2432 @type architecture: str
2433 @param architecture: Architecture name to search for
2435 @type session: Session
2436 @param session: Optional SQL session object (a temporary one will be
2437 generated if not supplied)
2439 @rtype: SuiteArchitecture
2440 @return: the SuiteArchitecture object or None
2443 q = session.query(SuiteArchitecture)
2444 q = q.join(Architecture).filter_by(arch_string=architecture)
2445 q = q.join(Suite).filter_by(suite_name=suite)
2449 except NoResultFound:
2452 __all__.append('get_suite_architecture')
2455 def get_suite(suite, session=None):
2457 Returns Suite object for given C{suite name}.
2460 @param suite: The name of the suite
2462 @type session: Session
2463 @param session: Optional SQLA session object (a temporary one will be
2464 generated if not supplied)
2467 @return: Suite object for the requested suite name (None if not present)
2470 q = session.query(Suite).filter_by(suite_name=suite)
2474 except NoResultFound:
2477 __all__.append('get_suite')
2479 ################################################################################
2481 class SuiteArchitecture(object):
2482 def __init__(self, *args, **kwargs):
2486 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2488 __all__.append('SuiteArchitecture')
2491 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2493 Returns list of Architecture objects for given C{suite} name
2496 @param source: Suite name to search for
2498 @type skipsrc: boolean
2499 @param skipsrc: Whether to skip returning the 'source' architecture entry
2502 @type skipall: boolean
2503 @param skipall: Whether to skip returning the 'all' architecture entry
2506 @type session: Session
2507 @param session: Optional SQL session object (a temporary one will be
2508 generated if not supplied)
2511 @return: list of Architecture objects for the given name (may be empty)
2514 q = session.query(Architecture)
2515 q = q.join(SuiteArchitecture)
2516 q = q.join(Suite).filter_by(suite_name=suite)
2519 q = q.filter(Architecture.arch_string != 'source')
2522 q = q.filter(Architecture.arch_string != 'all')
2524 q = q.order_by('arch_string')
2528 __all__.append('get_suite_architectures')
2530 ################################################################################
2532 class SuiteSrcFormat(object):
2533 def __init__(self, *args, **kwargs):
2537 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2539 __all__.append('SuiteSrcFormat')
2542 def get_suite_src_formats(suite, session=None):
2544 Returns list of allowed SrcFormat for C{suite}.
2547 @param suite: Suite name to search for
2549 @type session: Session
2550 @param session: Optional SQL session object (a temporary one will be
2551 generated if not supplied)
2554 @return: the list of allowed source formats for I{suite}
2557 q = session.query(SrcFormat)
2558 q = q.join(SuiteSrcFormat)
2559 q = q.join(Suite).filter_by(suite_name=suite)
2560 q = q.order_by('format_name')
2564 __all__.append('get_suite_src_formats')
2566 ################################################################################
2569 def __init__(self, *args, **kwargs):
2572 def __eq__(self, val):
2573 if isinstance(val, str):
2574 return (self.uid == val)
2575 # This signals to use the normal comparison operator
2576 return NotImplemented
2578 def __ne__(self, val):
2579 if isinstance(val, str):
2580 return (self.uid != val)
2581 # This signals to use the normal comparison operator
2582 return NotImplemented
2585 return '<Uid %s (%s)>' % (self.uid, self.name)
2587 __all__.append('Uid')
2590 def add_database_user(uidname, session=None):
2592 Adds a database user
2594 @type uidname: string
2595 @param uidname: The uid of the user to add
2597 @type session: SQLAlchemy
2598 @param session: Optional SQL session object (a temporary one will be
2599 generated if not supplied). If not passed, a commit will be performed at
2600 the end of the function, otherwise the caller is responsible for commiting.
2603 @return: the uid object for the given uidname
2606 session.execute("CREATE USER :uid", {'uid': uidname})
2607 session.commit_or_flush()
2609 __all__.append('add_database_user')
2612 def get_or_set_uid(uidname, session=None):
2614 Returns uid object for given uidname.
2616 If no matching uidname is found, a row is inserted.
2618 @type uidname: string
2619 @param uidname: The uid to add
2621 @type session: SQLAlchemy
2622 @param session: Optional SQL session object (a temporary one will be
2623 generated if not supplied). If not passed, a commit will be performed at
2624 the end of the function, otherwise the caller is responsible for commiting.
2627 @return: the uid object for the given uidname
2630 q = session.query(Uid).filter_by(uid=uidname)
2634 except NoResultFound:
2638 session.commit_or_flush()
2643 __all__.append('get_or_set_uid')
2646 def get_uid_from_fingerprint(fpr, session=None):
2647 q = session.query(Uid)
2648 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2652 except NoResultFound:
2655 __all__.append('get_uid_from_fingerprint')
2657 ################################################################################
2659 class UploadBlock(object):
2660 def __init__(self, *args, **kwargs):
2664 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2666 __all__.append('UploadBlock')
2668 ################################################################################
2670 class DBConn(object):
2672 database module init.
2676 def __init__(self, *args, **kwargs):
2677 self.__dict__ = self.__shared_state
2679 if not getattr(self, 'initialised', False):
2680 self.initialised = True
2681 self.debug = kwargs.has_key('debug')
2684 def __setuptables(self):
2693 'build_queue_files',
2696 'content_associations',
2697 'content_file_names',
2698 'content_file_paths',
2699 'changes_pending_binaries',
2700 'changes_pending_files',
2701 'changes_pending_files_map',
2702 'changes_pending_source',
2703 'changes_pending_source_files',
2704 'changes_pool_files',
2716 'pending_content_associations',
2726 'suite_architectures',
2727 'suite_src_formats',
2728 'suite_build_queue_copy',
2733 for table_name in tables:
2734 table = Table(table_name, self.db_meta, autoload=True)
2735 setattr(self, 'tbl_%s' % table_name, table)
2737 def __setupmappers(self):
2738 mapper(Architecture, self.tbl_architecture,
2739 properties = dict(arch_id = self.tbl_architecture.c.id))
2741 mapper(Archive, self.tbl_archive,
2742 properties = dict(archive_id = self.tbl_archive.c.id,
2743 archive_name = self.tbl_archive.c.name))
2745 mapper(BinAssociation, self.tbl_bin_associations,
2746 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2747 suite_id = self.tbl_bin_associations.c.suite,
2748 suite = relation(Suite),
2749 binary_id = self.tbl_bin_associations.c.bin,
2750 binary = relation(DBBinary)))
2752 mapper(BuildQueue, self.tbl_build_queue,
2753 properties = dict(queue_id = self.tbl_build_queue.c.id))
2755 mapper(BuildQueueFile, self.tbl_build_queue_files,
2756 properties = dict(buildqueue = relation(BuildQueue, backref='queuefiles'),
2757 poolfile = relation(PoolFile, backref='buildqueueinstances')))
2759 mapper(DBBinary, self.tbl_binaries,
2760 properties = dict(binary_id = self.tbl_binaries.c.id,
2761 package = self.tbl_binaries.c.package,
2762 version = self.tbl_binaries.c.version,
2763 maintainer_id = self.tbl_binaries.c.maintainer,
2764 maintainer = relation(Maintainer),
2765 source_id = self.tbl_binaries.c.source,
2766 source = relation(DBSource),
2767 arch_id = self.tbl_binaries.c.architecture,
2768 architecture = relation(Architecture),
2769 poolfile_id = self.tbl_binaries.c.file,
2770 poolfile = relation(PoolFile),
2771 binarytype = self.tbl_binaries.c.type,
2772 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2773 fingerprint = relation(Fingerprint),
2774 install_date = self.tbl_binaries.c.install_date,
2775 binassociations = relation(BinAssociation,
2776 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2778 mapper(BinaryACL, self.tbl_binary_acl,
2779 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2781 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2782 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2783 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2784 architecture = relation(Architecture)))
2786 mapper(Component, self.tbl_component,
2787 properties = dict(component_id = self.tbl_component.c.id,
2788 component_name = self.tbl_component.c.name))
2790 mapper(DBConfig, self.tbl_config,
2791 properties = dict(config_id = self.tbl_config.c.id))
2793 mapper(DSCFile, self.tbl_dsc_files,
2794 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2795 source_id = self.tbl_dsc_files.c.source,
2796 source = relation(DBSource),
2797 poolfile_id = self.tbl_dsc_files.c.file,
2798 poolfile = relation(PoolFile)))
2800 mapper(PoolFile, self.tbl_files,
2801 properties = dict(file_id = self.tbl_files.c.id,
2802 filesize = self.tbl_files.c.size,
2803 location_id = self.tbl_files.c.location,
2804 location = relation(Location)))
2806 mapper(Fingerprint, self.tbl_fingerprint,
2807 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2808 uid_id = self.tbl_fingerprint.c.uid,
2809 uid = relation(Uid),
2810 keyring_id = self.tbl_fingerprint.c.keyring,
2811 keyring = relation(Keyring),
2812 source_acl = relation(SourceACL),
2813 binary_acl = relation(BinaryACL)))
2815 mapper(Keyring, self.tbl_keyrings,
2816 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2817 keyring_id = self.tbl_keyrings.c.id))
2819 mapper(DBChange, self.tbl_changes,
2820 properties = dict(change_id = self.tbl_changes.c.id,
2821 poolfiles = relation(PoolFile,
2822 secondary=self.tbl_changes_pool_files,
2823 backref="changeslinks"),
2824 files = relation(ChangePendingFile,
2825 secondary=self.tbl_changes_pending_files_map,
2826 backref="changesfile"),
2827 in_queue_id = self.tbl_changes.c.in_queue,
2828 in_queue = relation(PolicyQueue,
2829 primaryjoin=(self.tbl_changes.c.in_queue==self.tbl_policy_queue.c.id)),
2830 approved_for_id = self.tbl_changes.c.approved_for))
2832 mapper(ChangePendingBinary, self.tbl_changes_pending_binaries,
2833 properties = dict(change_pending_binary_id = self.tbl_changes_pending_binaries.c.id))
2835 mapper(ChangePendingFile, self.tbl_changes_pending_files,
2836 properties = dict(change_pending_file_id = self.tbl_changes_pending_files.c.id))
2838 mapper(ChangePendingSource, self.tbl_changes_pending_source,
2839 properties = dict(change_pending_source_id = self.tbl_changes_pending_source.c.id,
2840 change = relation(DBChange),
2841 maintainer = relation(Maintainer,
2842 primaryjoin=(self.tbl_changes_pending_source.c.maintainer_id==self.tbl_maintainer.c.id)),
2843 changedby = relation(Maintainer,
2844 primaryjoin=(self.tbl_changes_pending_source.c.changedby_id==self.tbl_maintainer.c.id)),
2845 fingerprint = relation(Fingerprint),
2846 source_files = relation(ChangePendingFile,
2847 secondary=self.tbl_changes_pending_source_files,
2848 backref="pending_sources")))
2849 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2850 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2851 keyring = relation(Keyring, backref="keyring_acl_map"),
2852 architecture = relation(Architecture)))
2854 mapper(Location, self.tbl_location,
2855 properties = dict(location_id = self.tbl_location.c.id,
2856 component_id = self.tbl_location.c.component,
2857 component = relation(Component),
2858 archive_id = self.tbl_location.c.archive,
2859 archive = relation(Archive),
2860 archive_type = self.tbl_location.c.type))
2862 mapper(Maintainer, self.tbl_maintainer,
2863 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2865 mapper(NewComment, self.tbl_new_comments,
2866 properties = dict(comment_id = self.tbl_new_comments.c.id))
2868 mapper(Override, self.tbl_override,
2869 properties = dict(suite_id = self.tbl_override.c.suite,
2870 suite = relation(Suite),
2871 component_id = self.tbl_override.c.component,
2872 component = relation(Component),
2873 priority_id = self.tbl_override.c.priority,
2874 priority = relation(Priority),
2875 section_id = self.tbl_override.c.section,
2876 section = relation(Section),
2877 overridetype_id = self.tbl_override.c.type,
2878 overridetype = relation(OverrideType)))
2880 mapper(OverrideType, self.tbl_override_type,
2881 properties = dict(overridetype = self.tbl_override_type.c.type,
2882 overridetype_id = self.tbl_override_type.c.id))
2884 mapper(PolicyQueue, self.tbl_policy_queue,
2885 properties = dict(policy_queue_id = self.tbl_policy_queue.c.id))
2887 mapper(Priority, self.tbl_priority,
2888 properties = dict(priority_id = self.tbl_priority.c.id))
2890 mapper(Section, self.tbl_section,
2891 properties = dict(section_id = self.tbl_section.c.id))
2893 mapper(DBSource, self.tbl_source,
2894 properties = dict(source_id = self.tbl_source.c.id,
2895 version = self.tbl_source.c.version,
2896 maintainer_id = self.tbl_source.c.maintainer,
2897 maintainer = relation(Maintainer,
2898 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2899 poolfile_id = self.tbl_source.c.file,
2900 poolfile = relation(PoolFile),
2901 fingerprint_id = self.tbl_source.c.sig_fpr,
2902 fingerprint = relation(Fingerprint),
2903 changedby_id = self.tbl_source.c.changedby,
2904 changedby = relation(Maintainer,
2905 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2906 srcfiles = relation(DSCFile,
2907 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2908 srcassociations = relation(SrcAssociation,
2909 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2910 srcuploaders = relation(SrcUploader)))
2912 mapper(SourceACL, self.tbl_source_acl,
2913 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2915 mapper(SrcAssociation, self.tbl_src_associations,
2916 properties = dict(sa_id = self.tbl_src_associations.c.id,
2917 suite_id = self.tbl_src_associations.c.suite,
2918 suite = relation(Suite),
2919 source_id = self.tbl_src_associations.c.source,
2920 source = relation(DBSource)))
2922 mapper(SrcFormat, self.tbl_src_format,
2923 properties = dict(src_format_id = self.tbl_src_format.c.id,
2924 format_name = self.tbl_src_format.c.format_name))
2926 mapper(SrcUploader, self.tbl_src_uploaders,
2927 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2928 source_id = self.tbl_src_uploaders.c.source,
2929 source = relation(DBSource,
2930 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2931 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2932 maintainer = relation(Maintainer,
2933 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2935 mapper(Suite, self.tbl_suite,
2936 properties = dict(suite_id = self.tbl_suite.c.id,
2937 policy_queue = relation(PolicyQueue),
2938 copy_queues = relation(BuildQueue, secondary=self.tbl_suite_build_queue_copy)))
2940 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2941 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2942 suite = relation(Suite, backref='suitearchitectures'),
2943 arch_id = self.tbl_suite_architectures.c.architecture,
2944 architecture = relation(Architecture)))
2946 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2947 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2948 suite = relation(Suite, backref='suitesrcformats'),
2949 src_format_id = self.tbl_suite_src_formats.c.src_format,
2950 src_format = relation(SrcFormat)))
2952 mapper(Uid, self.tbl_uid,
2953 properties = dict(uid_id = self.tbl_uid.c.id,
2954 fingerprint = relation(Fingerprint)))
2956 mapper(UploadBlock, self.tbl_upload_blocks,
2957 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2958 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2959 uid = relation(Uid, backref="uploadblocks")))
2961 ## Connection functions
2962 def __createconn(self):
2963 from config import Config
2967 connstr = "postgres://%s" % cnf["DB::Host"]
2968 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2969 connstr += ":%s" % cnf["DB::Port"]
2970 connstr += "/%s" % cnf["DB::Name"]
2973 connstr = "postgres:///%s" % cnf["DB::Name"]
2974 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2975 connstr += "?port=%s" % cnf["DB::Port"]
2977 self.db_pg = create_engine(connstr, echo=self.debug)
2978 self.db_meta = MetaData()
2979 self.db_meta.bind = self.db_pg
2980 self.db_smaker = sessionmaker(bind=self.db_pg,
2984 self.__setuptables()
2985 self.__setupmappers()
2988 return self.db_smaker()
2990 __all__.append('DBConn')