123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514 |
- """ DB access class
- @contact: Debian FTPMaster <ftpmaster@debian.org>
- @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
- @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
- @copyright: 2009, 2010 Joerg Jaspert <joerg@debian.org>
- @copyright: 2009 Mike O'Connor <stew@debian.org>
- @license: GNU General Public License version 2 or later
- """
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- ################################################################################
- # < mhy> I need a funny comment
- # < sgran> two peanuts were walking down a dark street
- # < sgran> one was a-salted
- # * mhy looks up the definition of "funny"
- ################################################################################
- import apt_pkg
- from daklib.gpg import GpgException
- import functools
- import inspect
- import os
- from os.path import normpath
- import re
- import six
- import subprocess
- import warnings
- from debian.debfile import Deb822
- from tarfile import TarFile
- import sqlalchemy
- from sqlalchemy import create_engine, Table, desc
- from sqlalchemy.orm import sessionmaker, mapper, relation, object_session, \
- backref, object_mapper
- import sqlalchemy.types
- from sqlalchemy.orm.collections import attribute_mapped_collection
- from sqlalchemy.ext.associationproxy import association_proxy
- # Don't remove this, we re-export the exceptions to scripts which import us
- from sqlalchemy.exc import *
- from sqlalchemy.orm.exc import NoResultFound
- from .aptversion import AptVersion
- # Only import Config until Queue stuff is changed to store its config
- # in the database
- from .config import Config
- from .textutils import fix_maintainer, force_to_utf8
- # suppress some deprecation warnings in squeeze related to sqlalchemy
- warnings.filterwarnings('ignore',
- "Predicate of partial index .* ignored during reflection",
- SAWarning)
- from .database.base import Base
- ################################################################################
- # Patch in support for the debversion field type so that it works during
- # reflection
- class DebVersion(sqlalchemy.types.UserDefinedType):
- def get_col_spec(self):
- return "DEBVERSION"
- def bind_processor(self, dialect):
- return None
- def result_processor(self, dialect, coltype):
- return None
- from sqlalchemy.databases import postgresql
- postgresql.ischema_names['debversion'] = DebVersion
- ################################################################################
- __all__ = ['IntegrityError', 'SQLAlchemyError', 'DebVersion']
- ################################################################################
- def session_wrapper(fn):
- """
- Wrapper around common ".., session=None):" handling. If the wrapped
- function is called without passing 'session', we create a local one
- and destroy it when the function ends.
- Also attaches a commit_or_flush method to the session; if we created a
- local session, this is a synonym for session.commit(), otherwise it is a
- synonym for session.flush().
- """
- def wrapped(*args, **kwargs):
- private_transaction = False
- # Find the session object
- session = kwargs.get('session')
- if session is None:
- if len(args) < len(inspect.getfullargspec(fn).args):
- # No session specified as last argument or in kwargs
- private_transaction = True
- session = kwargs['session'] = DBConn().session()
- else:
- # Session is last argument in args
- session = args[-1]
- if session is None:
- args = list(args)
- session = args[-1] = DBConn().session()
- private_transaction = True
- if private_transaction:
- session.commit_or_flush = session.commit
- else:
- session.commit_or_flush = session.flush
- try:
- return fn(*args, **kwargs)
- finally:
- if private_transaction:
- # We created a session; close it.
- session.close()
- wrapped.__doc__ = fn.__doc__
- wrapped.__name__ = fn.__name__
- return wrapped
- __all__.append('session_wrapper')
- ################################################################################
- class ORMObject(object):
- """
- ORMObject is a base class for all ORM classes mapped by SQLalchemy. All
- derived classes must implement the properties() method.
- """
- def properties(self):
- '''
- This method should be implemented by all derived classes and returns a
- list of the important properties. The properties 'created' and
- 'modified' will be added automatically. A suffix '_count' should be
- added to properties that are lists or query objects. The most important
- property name should be returned as the first element in the list
- because it is used by repr().
- '''
- return []
- def classname(self):
- '''
- Returns the name of the class.
- '''
- return type(self).__name__
- def __repr__(self):
- '''
- Returns a short string representation of the object using the first
- element from the properties() method.
- '''
- primary_property = self.properties()[0]
- value = getattr(self, primary_property)
- return '<%s %s>' % (self.classname(), str(value))
- def __str__(self):
- '''
- Returns a human readable form of the object using the properties()
- method.
- '''
- return '<%s(...)>' % (self.classname())
- @classmethod
- @session_wrapper
- def get(cls, primary_key, session=None):
- '''
- This is a support function that allows getting an object by its primary
- key.
- Architecture.get(3[, session])
- instead of the more verbose
- session.query(Architecture).get(3)
- '''
- return session.query(cls).get(primary_key)
- def session(self):
- '''
- Returns the current session that is associated with the object. May
- return None is object is in detached state.
- '''
- return object_session(self)
- def clone(self, session=None):
- """
- Clones the current object in a new session and returns the new clone. A
- fresh session is created if the optional session parameter is not
- provided. The function will fail if a session is provided and has
- unflushed changes.
- RATIONALE: SQLAlchemy's session is not thread safe. This method clones
- an existing object to allow several threads to work with their own
- instances of an ORMObject.
- WARNING: Only persistent (committed) objects can be cloned. Changes
- made to the original object that are not committed yet will get lost.
- The session of the new object will always be rolled back to avoid
- resource leaks.
- """
- if self.session() is None:
- raise RuntimeError(
- 'Method clone() failed for detached object:\n%s' % self)
- self.session().flush()
- mapper = object_mapper(self)
- primary_key = mapper.primary_key_from_instance(self)
- object_class = self.__class__
- if session is None:
- session = DBConn().session()
- elif len(session.new) + len(session.dirty) + len(session.deleted) > 0:
- raise RuntimeError(
- 'Method clone() failed due to unflushed changes in session.')
- new_object = session.query(object_class).get(primary_key)
- session.rollback()
- if new_object is None:
- raise RuntimeError(
- 'Method clone() failed for non-persistent object:\n%s' % self)
- return new_object
- __all__.append('ORMObject')
- ################################################################################
- class ACL(ORMObject):
- def __repr__(self):
- return "<ACL {0}>".format(self.name)
- __all__.append('ACL')
- class ACLPerSource(ORMObject):
- def __repr__(self):
- return "<ACLPerSource acl={0} fingerprint={1} source={2} reason={3}>".format(self.acl.name, self.fingerprint.fingerprint, self.source, self.reason)
- __all__.append('ACLPerSource')
- ################################################################################
- from .database.architecture import Architecture
- __all__.append('Architecture')
- @session_wrapper
- def get_architecture(architecture, session=None):
- """
- Returns database id for given C{architecture}.
- @type architecture: string
- @param architecture: The name of the architecture
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: Architecture
- @return: Architecture object for the given arch (None if not present)
- """
- q = session.query(Architecture).filter_by(arch_string=architecture)
- return q.one_or_none()
- __all__.append('get_architecture')
- ################################################################################
- class Archive(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<Archive %s>' % self.archive_name
- __all__.append('Archive')
- @session_wrapper
- def get_archive(archive, session=None):
- """
- returns database id for given C{archive}.
- @type archive: string
- @param archive: the name of the arhive
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: Archive
- @return: Archive object for the given name (None if not present)
- """
- archive = archive.lower()
- q = session.query(Archive).filter_by(archive_name=archive)
- return q.one_or_none()
- __all__.append('get_archive')
- ################################################################################
- class ArchiveFile(object):
- def __init__(self, archive=None, component=None, file=None):
- self.archive = archive
- self.component = component
- self.file = file
- @property
- def path(self):
- return os.path.join(self.archive.path, 'pool', self.component.component_name, self.file.filename)
- __all__.append('ArchiveFile')
- ################################################################################
- class BinContents(ORMObject):
- def __init__(self, file=None, binary=None):
- self.file = file
- self.binary = binary
- def properties(self):
- return ['file', 'binary']
- __all__.append('BinContents')
- ################################################################################
- class DBBinary(ORMObject):
- def __init__(self, package=None, source=None, version=None,
- maintainer=None, architecture=None, poolfile=None,
- binarytype='deb', fingerprint=None):
- self.package = package
- self.source = source
- self.version = version
- self.maintainer = maintainer
- self.architecture = architecture
- self.poolfile = poolfile
- self.binarytype = binarytype
- self.fingerprint = fingerprint
- @property
- def pkid(self):
- return self.binary_id
- @property
- def name(self):
- return self.package
- @property
- def arch_string(self):
- return "%s" % self.architecture
- def properties(self):
- return ['package', 'version', 'maintainer', 'source', 'architecture',
- 'poolfile', 'binarytype', 'fingerprint', 'install_date',
- 'suites_count', 'binary_id', 'contents_count', 'extra_sources']
- metadata = association_proxy('key', 'value')
- def scan_contents(self):
- '''
- Yields the contents of the package. Only regular files are yielded and
- the path names are normalized after converting them from either utf-8
- or iso8859-1 encoding. It yields the string ' <EMPTY PACKAGE>' if the
- package does not contain any regular file.
- '''
- fullpath = self.poolfile.fullpath
- dpkg_cmd = ('dpkg-deb', '--fsys-tarfile', fullpath)
- dpkg = subprocess.Popen(dpkg_cmd, stdout=subprocess.PIPE)
- tar = TarFile.open(fileobj=dpkg.stdout, mode='r|')
- for member in tar.getmembers():
- if not member.isdir():
- name = normpath(member.name)
- name = force_to_utf8(name)
- yield name
- tar.close()
- dpkg.stdout.close()
- dpkg.wait()
- def read_control(self):
- '''
- Reads the control information from a binary.
- @rtype: text
- @return: stanza text of the control section.
- '''
- from . import utils
- fullpath = self.poolfile.fullpath
- return utils.deb_extract_control(fullpath)
- def read_control_fields(self):
- '''
- Reads the control information from a binary and return
- as a dictionary.
- @rtype: dict
- @return: fields of the control section as a dictionary.
- '''
- stanza = self.read_control()
- return apt_pkg.TagSection(stanza)
- @property
- def proxy(self):
- session = object_session(self)
- query = session.query(BinaryMetadata).filter_by(binary=self)
- return MetadataProxy(session, query)
- __all__.append('DBBinary')
- @session_wrapper
- def get_suites_binary_in(package, session=None):
- """
- Returns list of Suite objects which given C{package} name is in
- @type package: str
- @param package: DBBinary package name to search for
- @rtype: list
- @return: list of Suite objects for the given package
- """
- return session.query(Suite).filter(Suite.binaries.any(DBBinary.package == package)).all()
- __all__.append('get_suites_binary_in')
- @session_wrapper
- def get_component_by_package_suite(package, suite_list, arch_list=None, session=None):
- '''
- Returns the component name of the newest binary package in suite_list or
- None if no package is found. The result can be optionally filtered by a list
- of architecture names.
- @type package: str
- @param package: DBBinary package name to search for
- @type suite_list: list of str
- @param suite_list: list of suite_name items
- @type arch_list: list of str
- @param arch_list: optional list of arch_string items that defaults to []
- @rtype: str or NoneType
- @return: name of component or None
- '''
- q = session.query(DBBinary).filter_by(package=package). \
- join(DBBinary.suites).filter(Suite.suite_name.in_(suite_list))
- if arch_list:
- q = q.join(DBBinary.architecture). \
- filter(Architecture.arch_string.in_(arch_list))
- binary = q.order_by(desc(DBBinary.version)).first()
- if binary is None:
- return None
- else:
- return binary.poolfile.component.component_name
- __all__.append('get_component_by_package_suite')
- ################################################################################
- class BuildQueue(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<BuildQueue %s>' % self.queue_name
- __all__.append('BuildQueue')
- ################################################################################
- class Component(ORMObject):
- def __init__(self, component_name=None):
- self.component_name = component_name
- def __eq__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.component_name == val)
- # This signals to use the normal comparison operator
- return NotImplemented
- def __ne__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.component_name != val)
- # This signals to use the normal comparison operator
- return NotImplemented
- __hash__ = ORMObject.__hash__
- def properties(self):
- return ['component_name', 'component_id', 'description',
- 'meets_dfsg', 'overrides_count']
- __all__.append('Component')
- @session_wrapper
- def get_component(component, session=None):
- """
- Returns database id for given C{component}.
- @type component: string
- @param component: The name of the override type
- @rtype: int
- @return: the database id for the given component
- """
- component = component.lower()
- q = session.query(Component).filter_by(component_name=component)
- return q.one_or_none()
- __all__.append('get_component')
- def get_mapped_component_name(component_name):
- cnf = Config()
- for m in cnf.value_list("ComponentMappings"):
- (src, dst) = m.split()
- if component_name == src:
- component_name = dst
- return component_name
- __all__.append('get_mapped_component_name')
- @session_wrapper
- def get_mapped_component(component_name, session=None):
- """get component after mappings
- Evaluate component mappings from ComponentMappings in dak.conf for the
- given component name.
- @todo: ansgar wants to get rid of this. It's currently only used for
- the security archive
- @type component_name: str
- @param component_name: component name
- @param session: database session
- @rtype: L{daklib.dbconn.Component} or C{None}
- @return: component after applying maps or C{None}
- """
- component_name = get_mapped_component_name(component_name)
- component = session.query(Component).filter_by(component_name=component_name).first()
- return component
- __all__.append('get_mapped_component')
- @session_wrapper
- def get_component_names(session=None):
- """
- Returns list of strings of component names.
- @rtype: list
- @return: list of strings of component names
- """
- return [x.component_name for x in session.query(Component).all()]
- __all__.append('get_component_names')
- ################################################################################
- class DBConfig(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<DBConfig %s>' % self.name
- __all__.append('DBConfig')
- ################################################################################
- class DSCFile(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<DSCFile %s>' % self.dscfile_id
- __all__.append('DSCFile')
- @session_wrapper
- def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
- """
- Returns a list of DSCFiles which may be empty
- @type dscfile_id: int (optional)
- @param dscfile_id: the dscfile_id of the DSCFiles to find
- @type source_id: int (optional)
- @param source_id: the source id related to the DSCFiles to find
- @type poolfile_id: int (optional)
- @param poolfile_id: the poolfile id related to the DSCFiles to find
- @rtype: list
- @return: Possibly empty list of DSCFiles
- """
- q = session.query(DSCFile)
- if dscfile_id is not None:
- q = q.filter_by(dscfile_id=dscfile_id)
- if source_id is not None:
- q = q.filter_by(source_id=source_id)
- if poolfile_id is not None:
- q = q.filter_by(poolfile_id=poolfile_id)
- return q.all()
- __all__.append('get_dscfiles')
- ################################################################################
- class ExternalOverride(ORMObject):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<ExternalOverride %s = %s: %s>' % (self.package, self.key, self.value)
- __all__.append('ExternalOverride')
- ################################################################################
- class PoolFile(ORMObject):
- def __init__(self, filename=None, filesize=-1,
- md5sum=None):
- self.filename = filename
- self.filesize = filesize
- self.md5sum = md5sum
- @property
- def fullpath(self):
- session = DBConn().session().object_session(self)
- af = session.query(ArchiveFile).join(Archive) \
- .filter(ArchiveFile.file == self) \
- .order_by(Archive.tainted.desc()).first()
- return af.path
- @property
- def component(self):
- session = DBConn().session().object_session(self)
- component_id = session.query(ArchiveFile.component_id).filter(ArchiveFile.file == self) \
- .group_by(ArchiveFile.component_id).one()
- return session.query(Component).get(component_id)
- @property
- def basename(self):
- return os.path.basename(self.filename)
- def properties(self):
- return ['filename', 'file_id', 'filesize', 'md5sum', 'sha1sum',
- 'sha256sum', 'source', 'binary', 'last_used']
- __all__.append('PoolFile')
- ################################################################################
- class Fingerprint(ORMObject):
- def __init__(self, fingerprint=None):
- self.fingerprint = fingerprint
- def properties(self):
- return ['fingerprint', 'fingerprint_id', 'keyring', 'uid',
- 'binary_reject']
- __all__.append('Fingerprint')
- @session_wrapper
- def get_fingerprint(fpr, session=None):
- """
- Returns Fingerprint object for given fpr.
- @type fpr: string
- @param fpr: The fpr to find / add
- @type session: SQLAlchemy
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied).
- @rtype: Fingerprint
- @return: the Fingerprint object for the given fpr or None
- """
- q = session.query(Fingerprint).filter_by(fingerprint=fpr)
- return q.one_or_none()
- __all__.append('get_fingerprint')
- @session_wrapper
- def get_or_set_fingerprint(fpr, session=None):
- """
- Returns Fingerprint object for given fpr.
- If no matching fpr is found, a row is inserted.
- @type fpr: string
- @param fpr: The fpr to find / add
- @type session: SQLAlchemy
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied). If not passed, a commit will be performed at
- the end of the function, otherwise the caller is responsible for commiting.
- A flush will be performed either way.
- @rtype: Fingerprint
- @return: the Fingerprint object for the given fpr
- """
- q = session.query(Fingerprint).filter_by(fingerprint=fpr)
- try:
- ret = q.one()
- except NoResultFound:
- fingerprint = Fingerprint()
- fingerprint.fingerprint = fpr
- session.add(fingerprint)
- session.commit_or_flush()
- ret = fingerprint
- return ret
- __all__.append('get_or_set_fingerprint')
- ################################################################################
- # Helper routine for Keyring class
- def get_ldap_name(entry):
- name = []
- for k in ["cn", "mn", "sn"]:
- ret = entry.get(k)
- if not ret:
- continue
- value = six.ensure_str(ret[0])
- if value and value[0] != "-":
- name.append(value)
- return " ".join(name)
- ################################################################################
- class Keyring(object):
- keys = {}
- fpr_lookup = {}
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<Keyring %s>' % self.keyring_name
- def de_escape_gpg_str(self, txt):
- esclist = re.split(r'(\\x..)', txt)
- for x in range(1, len(esclist), 2):
- esclist[x] = "%c" % (int(esclist[x][2:], 16))
- return "".join(esclist)
- def parse_address(self, uid):
- """parses uid and returns a tuple of real name and email address"""
- import email.utils
- (name, address) = email.utils.parseaddr(uid)
- name = re.sub(r"\s*[(].*[)]", "", name)
- name = self.de_escape_gpg_str(name)
- if name == "":
- name = uid
- return (name, address)
- def load_keys(self, keyring):
- if not self.keyring_id:
- raise Exception('Must be initialized with database information')
- cmd = ["gpg", "--no-default-keyring", "--keyring", keyring,
- "--with-colons", "--fingerprint", "--fingerprint"]
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- key = None
- need_fingerprint = False
- for line_raw in p.stdout:
- try:
- line = six.ensure_str(line_raw)
- except UnicodeDecodeError:
- # Some old UIDs might not use UTF-8 encoding. We assume they
- # use latin1.
- line = six.ensure_str(line_raw, encoding='latin1')
- field = line.split(":")
- if field[0] == "pub":
- key = field[4]
- self.keys[key] = {}
- (name, addr) = self.parse_address(field[9])
- if "@" in addr:
- self.keys[key]["email"] = addr
- self.keys[key]["name"] = name
- need_fingerprint = True
- elif key and field[0] == "uid":
- (name, addr) = self.parse_address(field[9])
- if "email" not in self.keys[key] and "@" in addr:
- self.keys[key]["email"] = addr
- self.keys[key]["name"] = name
- elif need_fingerprint and field[0] == "fpr":
- self.keys[key]["fingerprints"] = [field[9]]
- self.fpr_lookup[field[9]] = key
- need_fingerprint = False
- (out, err) = p.communicate()
- r = p.returncode
- if r != 0:
- raise GpgException("command failed: %s\nstdout: %s\nstderr: %s\n" % (cmd, out, err))
- def import_users_from_ldap(self, session):
- from .utils import open_ldap_connection
- import ldap
- l = open_ldap_connection()
- cnf = Config()
- LDAPDn = cnf["Import-LDAP-Fingerprints::LDAPDn"]
- Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
- "(&(keyfingerprint=*)(supplementaryGid=%s))" % (cnf["Import-Users-From-Passwd::ValidGID"]),
- ["uid", "keyfingerprint", "cn", "mn", "sn"])
- byuid = {}
- byname = {}
- for i in Attrs:
- entry = i[1]
- uid = six.ensure_str(entry["uid"][0])
- name = get_ldap_name(entry)
- fingerprints = entry["keyFingerPrint"]
- keyid = None
- for f in fingerprints:
- f = six.ensure_str(f)
- key = self.fpr_lookup.get(f, None)
- if key not in self.keys:
- continue
- self.keys[key]["uid"] = uid
- if keyid is not None:
- continue
- keyid = get_or_set_uid(uid, session).uid_id
- byuid[keyid] = (uid, name)
- byname[uid] = (keyid, name)
- return (byname, byuid)
- def generate_users_from_keyring(self, format, session):
- byuid = {}
- byname = {}
- any_invalid = False
- for x in list(self.keys.keys()):
- if "email" not in self.keys[x]:
- any_invalid = True
- self.keys[x]["uid"] = format % "invalid-uid"
- else:
- uid = format % self.keys[x]["email"]
- keyid = get_or_set_uid(uid, session).uid_id
- byuid[keyid] = (uid, self.keys[x]["name"])
- byname[uid] = (keyid, self.keys[x]["name"])
- self.keys[x]["uid"] = uid
- if any_invalid:
- uid = format % "invalid-uid"
- keyid = get_or_set_uid(uid, session).uid_id
- byuid[keyid] = (uid, "ungeneratable user id")
- byname[uid] = (keyid, "ungeneratable user id")
- return (byname, byuid)
- __all__.append('Keyring')
- @session_wrapper
- def get_keyring(keyring, session=None):
- """
- If C{keyring} does not have an entry in the C{keyrings} table yet, return None
- If C{keyring} already has an entry, simply return the existing Keyring
- @type keyring: string
- @param keyring: the keyring name
- @rtype: Keyring
- @return: the Keyring object for this keyring
- """
- q = session.query(Keyring).filter_by(keyring_name=keyring)
- return q.one_or_none()
- __all__.append('get_keyring')
- @session_wrapper
- def get_active_keyring_paths(session=None):
- """
- @rtype: list
- @return: list of active keyring paths
- """
- return [x.keyring_name for x in session.query(Keyring).filter(Keyring.active == True).order_by(desc(Keyring.priority)).all()] # noqa:E712
- __all__.append('get_active_keyring_paths')
- ################################################################################
- class DBChange(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<DBChange %s>' % self.changesname
- __all__.append('DBChange')
- @session_wrapper
- def get_dbchange(filename, session=None):
- """
- returns DBChange object for given C{filename}.
- @type filename: string
- @param filename: the name of the file
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: DBChange
- @return: DBChange object for the given filename (C{None} if not present)
- """
- q = session.query(DBChange).filter_by(changesname=filename)
- return q.one_or_none()
- __all__.append('get_dbchange')
- ################################################################################
- class Maintainer(ORMObject):
- def __init__(self, name=None):
- self.name = name
- def properties(self):
- return ['name', 'maintainer_id']
- def get_split_maintainer(self):
- if not hasattr(self, 'name') or self.name is None:
- return ('', '', '', '')
- return fix_maintainer(self.name.strip())
- __all__.append('Maintainer')
- @session_wrapper
- def get_or_set_maintainer(name, session=None):
- """
- Returns Maintainer object for given maintainer name.
- If no matching maintainer name is found, a row is inserted.
- @type name: string
- @param name: The maintainer name to add
- @type session: SQLAlchemy
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied). If not passed, a commit will be performed at
- the end of the function, otherwise the caller is responsible for commiting.
- A flush will be performed either way.
- @rtype: Maintainer
- @return: the Maintainer object for the given maintainer
- """
- q = session.query(Maintainer).filter_by(name=name)
- try:
- ret = q.one()
- except NoResultFound:
- maintainer = Maintainer()
- maintainer.name = name
- session.add(maintainer)
- session.commit_or_flush()
- ret = maintainer
- return ret
- __all__.append('get_or_set_maintainer')
- @session_wrapper
- def get_maintainer(maintainer_id, session=None):
- """
- Return the name of the maintainer behind C{maintainer_id} or None if that
- maintainer_id is invalid.
- @type maintainer_id: int
- @param maintainer_id: the id of the maintainer
- @rtype: Maintainer
- @return: the Maintainer with this C{maintainer_id}
- """
- return session.query(Maintainer).get(maintainer_id)
- __all__.append('get_maintainer')
- ################################################################################
- class NewComment(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
- __all__.append('NewComment')
- @session_wrapper
- def has_new_comment(policy_queue, package, version, session=None):
- """
- Returns true if the given combination of C{package}, C{version} has a comment.
- @type package: string
- @param package: name of the package
- @type version: string
- @param version: package version
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: boolean
- @return: true/false
- """
- q = session.query(NewComment).filter_by(policy_queue=policy_queue)
- q = q.filter_by(package=package)
- q = q.filter_by(version=version)
- return bool(q.count() > 0)
- __all__.append('has_new_comment')
- @session_wrapper
- def get_new_comments(policy_queue, package=None, version=None, comment_id=None, session=None):
- """
- Returns (possibly empty) list of NewComment objects for the given
- parameters
- @type package: string (optional)
- @param package: name of the package
- @type version: string (optional)
- @param version: package version
- @type comment_id: int (optional)
- @param comment_id: An id of a comment
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: list
- @return: A (possibly empty) list of NewComment objects will be returned
- """
- q = session.query(NewComment).filter_by(policy_queue=policy_queue)
- if package is not None:
- q = q.filter_by(package=package)
- if version is not None:
- q = q.filter_by(version=version)
- if comment_id is not None:
- q = q.filter_by(comment_id=comment_id)
- return q.all()
- __all__.append('get_new_comments')
- ################################################################################
- class Override(ORMObject):
- def __init__(self, package=None, suite=None, component=None, overridetype=None,
- section=None, priority=None):
- self.package = package
- self.suite = suite
- self.component = component
- self.overridetype = overridetype
- self.section = section
- self.priority = priority
- def properties(self):
- return ['package', 'suite', 'component', 'overridetype', 'section',
- 'priority']
- __all__.append('Override')
- @session_wrapper
- def get_override(package, suite=None, component=None, overridetype=None, session=None):
- """
- Returns Override object for the given parameters
- @type package: string
- @param package: The name of the package
- @type suite: string, list or None
- @param suite: The name of the suite (or suites if a list) to limit to. If
- None, don't limit. Defaults to None.
- @type component: string, list or None
- @param component: The name of the component (or components if a list) to
- limit to. If None, don't limit. Defaults to None.
- @type overridetype: string, list or None
- @param overridetype: The name of the overridetype (or overridetypes if a list) to
- limit to. If None, don't limit. Defaults to None.
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: list
- @return: A (possibly empty) list of Override objects will be returned
- """
- q = session.query(Override)
- q = q.filter_by(package=package)
- if suite is not None:
- if not isinstance(suite, list):
- suite = [suite]
- q = q.join(Suite).filter(Suite.suite_name.in_(suite))
- if component is not None:
- if not isinstance(component, list):
- component = [component]
- q = q.join(Component).filter(Component.component_name.in_(component))
- if overridetype is not None:
- if not isinstance(overridetype, list):
- overridetype = [overridetype]
- q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
- return q.all()
- __all__.append('get_override')
- ################################################################################
- class OverrideType(ORMObject):
- def __init__(self, overridetype=None):
- self.overridetype = overridetype
- def properties(self):
- return ['overridetype', 'overridetype_id', 'overrides_count']
- __all__.append('OverrideType')
- @session_wrapper
- def get_override_type(override_type, session=None):
- """
- Returns OverrideType object for given C{override type}.
- @type override_type: string
- @param override_type: The name of the override type
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: int
- @return: the database id for the given override type
- """
- q = session.query(OverrideType).filter_by(overridetype=override_type)
- return q.one_or_none()
- __all__.append('get_override_type')
- ################################################################################
- class PolicyQueue(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<PolicyQueue %s>' % self.queue_name
- __all__.append('PolicyQueue')
- @session_wrapper
- def get_policy_queue(queuename, session=None):
- """
- Returns PolicyQueue object for given C{queue name}
- @type queuename: string
- @param queuename: The name of the queue
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: PolicyQueue
- @return: PolicyQueue object for the given queue
- """
- q = session.query(PolicyQueue).filter_by(queue_name=queuename)
- return q.one_or_none()
- __all__.append('get_policy_queue')
- ################################################################################
- @functools.total_ordering
- class PolicyQueueUpload(object):
- def _key(self):
- return (
- self.changes.source,
- AptVersion(self.changes.version),
- self.source is None,
- self.changes.changesname
- )
- def __eq__(self, other):
- return self._key() == other._key()
- def __lt__(self, other):
- return self._key() < other._key()
- __all__.append('PolicyQueueUpload')
- ################################################################################
- class PolicyQueueByhandFile(object):
- pass
- __all__.append('PolicyQueueByhandFile')
- ################################################################################
- class Priority(ORMObject):
- def __init__(self, priority=None, level=None):
- self.priority = priority
- self.level = level
- def properties(self):
- return ['priority', 'priority_id', 'level', 'overrides_count']
- def __eq__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.priority == val)
- # This signals to use the normal comparison operator
- return NotImplemented
- def __ne__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.priority != val)
- # This signals to use the normal comparison operator
- return NotImplemented
- __hash__ = ORMObject.__hash__
- __all__.append('Priority')
- @session_wrapper
- def get_priority(priority, session=None):
- """
- Returns Priority object for given C{priority name}.
- @type priority: string
- @param priority: The name of the priority
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: Priority
- @return: Priority object for the given priority
- """
- q = session.query(Priority).filter_by(priority=priority)
- return q.one_or_none()
- __all__.append('get_priority')
- @session_wrapper
- def get_priorities(session=None):
- """
- Returns dictionary of priority names -> id mappings
- @type session: Session
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied)
- @rtype: dictionary
- @return: dictionary of priority names -> id mappings
- """
- ret = {}
- q = session.query(Priority)
- for x in q.all():
- ret[x.priority] = x.priority_id
- return ret
- __all__.append('get_priorities')
- ################################################################################
- from .database.section import Section
- __all__.append('Section')
- @session_wrapper
- def get_section(section, session=None):
- """
- Returns Section object for given C{section name}.
- @type section: string
- @param section: The name of the section
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: Section
- @return: Section object for the given section name
- """
- q = session.query(Section).filter_by(section=section)
- return q.one_or_none()
- __all__.append('get_section')
- @session_wrapper
- def get_sections(session=None):
- """
- Returns dictionary of section names -> id mappings
- @type session: Session
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied)
- @rtype: dictionary
- @return: dictionary of section names -> id mappings
- """
- ret = {}
- q = session.query(Section)
- for x in q.all():
- ret[x.section] = x.section_id
- return ret
- __all__.append('get_sections')
- ################################################################################
- class SignatureHistory(ORMObject):
- @classmethod
- def from_signed_file(cls, signed_file):
- """signature history entry from signed file
- @type signed_file: L{daklib.gpg.SignedFile}
- @param signed_file: signed file
- @rtype: L{SignatureHistory}
- """
- self = cls()
- self.fingerprint = signed_file.primary_fingerprint
- self.signature_timestamp = signed_file.signature_timestamp
- self.contents_sha1 = signed_file.contents_sha1()
- return self
- def query(self, session):
- return session.query(SignatureHistory).filter_by(fingerprint=self.fingerprint, signature_timestamp=self.signature_timestamp, contents_sha1=self.contents_sha1).first()
- __all__.append('SignatureHistory')
- ################################################################################
- class SrcContents(ORMObject):
- def __init__(self, file=None, source=None):
- self.file = file
- self.source = source
- def properties(self):
- return ['file', 'source']
- __all__.append('SrcContents')
- ################################################################################
- class DBSource(ORMObject):
- def __init__(self, source=None, version=None, maintainer=None,
- changedby=None, poolfile=None, install_date=None, fingerprint=None):
- self.source = source
- self.version = version
- self.maintainer = maintainer
- self.changedby = changedby
- self.poolfile = poolfile
- self.install_date = install_date
- self.fingerprint = fingerprint
- @property
- def pkid(self):
- return self.source_id
- @property
- def name(self):
- return self.source
- @property
- def arch_string(self):
- return 'source'
- def properties(self):
- return ['source', 'source_id', 'maintainer', 'changedby',
- 'fingerprint', 'poolfile', 'version', 'suites_count',
- 'install_date', 'binaries_count', 'uploaders_count']
- def read_control_fields(self):
- '''
- Reads the control information from a dsc
- @rtype: tuple
- @return: fields is the dsc information in a dictionary form
- '''
- with open(self.poolfile.fullpath, 'r') as fd:
- fields = Deb822(fd)
- return fields
- metadata = association_proxy('key', 'value')
- def scan_contents(self):
- '''
- Returns a set of names for non directories. The path names are
- normalized after converting them from either utf-8 or iso8859-1
- encoding.
- '''
- fullpath = self.poolfile.fullpath
- from daklib.contents import UnpackedSource
- unpacked = UnpackedSource(fullpath)
- fileset = set()
- for name in unpacked.get_all_filenames():
- name = force_to_utf8(name)
- fileset.add(name)
- return fileset
- @property
- def proxy(self):
- session = object_session(self)
- query = session.query(SourceMetadata).filter_by(source=self)
- return MetadataProxy(session, query)
- __all__.append('DBSource')
- @session_wrapper
- def get_suites_source_in(source, session=None):
- """
- Returns list of Suite objects which given C{source} name is in
- @type source: str
- @param source: DBSource package name to search for
- @rtype: list
- @return: list of Suite objects for the given source
- """
- return session.query(Suite).filter(Suite.sources.any(source=source)).all()
- __all__.append('get_suites_source_in')
- # FIXME: This function fails badly if it finds more than 1 source package and
- # its implementation is trivial enough to be inlined.
- @session_wrapper
- def get_source_in_suite(source, suite_name, session=None):
- """
- Returns a DBSource object for a combination of C{source} and C{suite_name}.
- - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
- - B{suite_name} - a suite name, eg. I{unstable}
- @type source: string
- @param source: source package name
- @type suite_name: string
- @param suite_name: the suite name
- @rtype: string
- @return: the version for I{source} in I{suite}
- """
- suite = get_suite(suite_name, session)
- if suite is None:
- return None
- return suite.get_sources(source).one_or_none()
- __all__.append('get_source_in_suite')
- @session_wrapper
- def import_metadata_into_db(obj, session=None):
- """
- This routine works on either DBBinary or DBSource objects and imports
- their metadata into the database
- """
- fields = obj.read_control_fields()
- for k in fields.keys():
- try:
- # Try raw ASCII
- val = str(fields[k])
- except UnicodeEncodeError:
- # Fall back to UTF-8
- try:
- val = fields[k].encode('utf-8')
- except UnicodeEncodeError:
- # Finally try iso8859-1
- val = fields[k].encode('iso8859-1')
- # Otherwise we allow the exception to percolate up and we cause
- # a reject as someone is playing silly buggers
- obj.metadata[get_or_set_metadatakey(k, session)] = val
- session.commit_or_flush()
- __all__.append('import_metadata_into_db')
- ################################################################################
- class SrcFormat(object):
- def __init__(self, *args, **kwargs):
- pass
- def __repr__(self):
- return '<SrcFormat %s>' % (self.format_name)
- __all__.append('SrcFormat')
- ################################################################################
- SUITE_FIELDS = [('SuiteName', 'suite_name'),
- ('SuiteID', 'suite_id'),
- ('Version', 'version'),
- ('Origin', 'origin'),
- ('Label', 'label'),
- ('Description', 'description'),
- ('Untouchable', 'untouchable'),
- ('Announce', 'announce'),
- ('Codename', 'codename'),
- ('OverrideCodename', 'overridecodename'),
- ('ValidTime', 'validtime'),
- ('Priority', 'priority'),
- ('NotAutomatic', 'notautomatic'),
- ('CopyChanges', 'copychanges'),
- ('OverrideSuite', 'overridesuite')]
- # Why the heck don't we have any UNIQUE constraints in table suite?
- # TODO: Add UNIQUE constraints for appropriate columns.
- class Suite(ORMObject):
- def __init__(self, suite_name=None, version=None):
- self.suite_name = suite_name
- self.version = version
- def properties(self):
- return ['suite_name', 'version', 'sources_count', 'binaries_count',
- 'overrides_count']
- def __eq__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.suite_name == val)
- # This signals to use the normal comparison operator
- return NotImplemented
- def __ne__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.suite_name != val)
- # This signals to use the normal comparison operator
- return NotImplemented
- __hash__ = ORMObject.__hash__
- def details(self):
- ret = []
- for disp, field in SUITE_FIELDS:
- val = getattr(self, field, None)
- if val is not None:
- ret.append("%s: %s" % (disp, val))
- return "\n".join(ret)
- def get_architectures(self, skipsrc=False, skipall=False):
- """
- Returns list of Architecture objects
- @type skipsrc: boolean
- @param skipsrc: Whether to skip returning the 'source' architecture entry
- (Default False)
- @type skipall: boolean
- @param skipall: Whether to skip returning the 'all' architecture entry
- (Default False)
- @rtype: list
- @return: list of Architecture objects for the given name (may be empty)
- """
- q = object_session(self).query(Architecture).with_parent(self)
- if skipsrc:
- q = q.filter(Architecture.arch_string != 'source')
- if skipall:
- q = q.filter(Architecture.arch_string != 'all')
- return q.order_by(Architecture.arch_string).all()
- def get_sources(self, source):
- """
- Returns a query object representing DBSource that is part of C{suite}.
- - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
- @type source: string
- @param source: source package name
- @rtype: sqlalchemy.orm.query.Query
- @return: a query of DBSource
- """
- session = object_session(self)
- return session.query(DBSource).filter_by(source=source). \
- with_parent(self)
- def get_overridesuite(self):
- if self.overridesuite is None:
- return self
- else:
- return object_session(self).query(Suite).filter_by(suite_name=self.overridesuite).one()
- def update_last_changed(self):
- self.last_changed = sqlalchemy.func.now()
- @property
- def path(self):
- return os.path.join(self.archive.path, 'dists', self.suite_name)
- @property
- def release_suite_output(self):
- if self.release_suite is not None:
- return self.release_suite
- return self.suite_name
- __all__.append('Suite')
- @session_wrapper
- def get_suite(suite, session=None):
- """
- Returns Suite object for given C{suite name}.
- @type suite: string
- @param suite: The name of the suite
- @type session: Session
- @param session: Optional SQLA session object (a temporary one will be
- generated if not supplied)
- @rtype: Suite
- @return: Suite object for the requested suite name (None if not present)
- """
- # Start by looking for the dak internal name
- q = session.query(Suite).filter_by(suite_name=suite)
- try:
- return q.one()
- except NoResultFound:
- pass
- # Now try codename
- q = session.query(Suite).filter_by(codename=suite)
- try:
- return q.one()
- except NoResultFound:
- pass
- # Finally give release_suite a try
- q = session.query(Suite).filter_by(release_suite=suite)
- return q.one_or_none()
- __all__.append('get_suite')
- ################################################################################
- @session_wrapper
- def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
- """
- Returns list of Architecture objects for given C{suite} name. The list is
- empty if suite does not exist.
- @type suite: str
- @param suite: Suite name to search for
- @type skipsrc: boolean
- @param skipsrc: Whether to skip returning the 'source' architecture entry
- (Default False)
- @type skipall: boolean
- @param skipall: Whether to skip returning the 'all' architecture entry
- (Default False)
- @type session: Session
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied)
- @rtype: list
- @return: list of Architecture objects for the given name (may be empty)
- """
- try:
- return get_suite(suite, session).get_architectures(skipsrc, skipall)
- except AttributeError:
- return []
- __all__.append('get_suite_architectures')
- ################################################################################
- class Uid(ORMObject):
- def __init__(self, uid=None, name=None):
- self.uid = uid
- self.name = name
- def __eq__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.uid == val)
- # This signals to use the normal comparison operator
- return NotImplemented
- def __ne__(self, val):
- if isinstance(val, str):
- warnings.warn("comparison with a `str` is deprecated", DeprecationWarning, stacklevel=2)
- return (self.uid != val)
- # This signals to use the normal comparison operator
- return NotImplemented
- __hash__ = ORMObject.__hash__
- def properties(self):
- return ['uid', 'name', 'fingerprint']
- __all__.append('Uid')
- @session_wrapper
- def get_or_set_uid(uidname, session=None):
- """
- Returns uid object for given uidname.
- If no matching uidname is found, a row is inserted.
- @type uidname: string
- @param uidname: The uid to add
- @type session: SQLAlchemy
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied). If not passed, a commit will be performed at
- the end of the function, otherwise the caller is responsible for commiting.
- @rtype: Uid
- @return: the uid object for the given uidname
- """
- q = session.query(Uid).filter_by(uid=uidname)
- try:
- ret = q.one()
- except NoResultFound:
- uid = Uid()
- uid.uid = uidname
- session.add(uid)
- session.commit_or_flush()
- ret = uid
- return ret
- __all__.append('get_or_set_uid')
- @session_wrapper
- def get_uid_from_fingerprint(fpr, session=None):
- q = session.query(Uid)
- q = q.join(Fingerprint).filter_by(fingerprint=fpr)
- return q.one_or_none()
- __all__.append('get_uid_from_fingerprint')
- ################################################################################
- class MetadataKey(ORMObject):
- def __init__(self, key=None):
- self.key = key
- def properties(self):
- return ['key']
- __all__.append('MetadataKey')
- @session_wrapper
- def get_or_set_metadatakey(keyname, session=None):
- """
- Returns MetadataKey object for given uidname.
- If no matching keyname is found, a row is inserted.
- @type keyname: string
- @param keyname: The keyname to add
- @type session: SQLAlchemy
- @param session: Optional SQL session object (a temporary one will be
- generated if not supplied). If not passed, a commit will be performed at
- the end of the function, otherwise the caller is responsible for commiting.
- @rtype: MetadataKey
- @return: the metadatakey object for the given keyname
- """
- q = session.query(MetadataKey).filter_by(key=keyname)
- try:
- ret = q.one()
- except NoResultFound:
- ret = MetadataKey(keyname)
- session.add(ret)
- session.commit_or_flush()
- return ret
- __all__.append('get_or_set_metadatakey')
- ################################################################################
- class BinaryMetadata(ORMObject):
- def __init__(self, key=None, value=None, binary=None):
- self.key = key
- self.value = value
- if binary is not None:
- self.binary = binary
- def properties(self):
- return ['binary', 'key', 'value']
- __all__.append('BinaryMetadata')
- ################################################################################
- class SourceMetadata(ORMObject):
- def __init__(self, key=None, value=None, source=None):
- self.key = key
- self.value = value
- if source is not None:
- self.source = source
- def properties(self):
- return ['source', 'key', 'value']
- __all__.append('SourceMetadata')
- ################################################################################
- class MetadataProxy(object):
- def __init__(self, session, query):
- self.session = session
- self.query = query
- def _get(self, key):
- metadata_key = self.session.query(MetadataKey).filter_by(key=key).first()
- if metadata_key is None:
- return None
- metadata = self.query.filter_by(key=metadata_key).first()
- return metadata
- def __contains__(self, key):
- if self._get(key) is not None:
- return True
- return False
- def __getitem__(self, key):
- metadata = self._get(key)
- if metadata is None:
- raise KeyError
- return metadata.value
- def get(self, key, default=None):
- try:
- return self[key]
- except KeyError:
- return default
- ################################################################################
- class VersionCheck(ORMObject):
- def __init__(self, *args, **kwargs):
- pass
- def properties(self):
- return ['check']
- __all__.append('VersionCheck')
- @session_wrapper
- def get_version_checks(suite_name, check=None, session=None):
- suite = get_suite(suite_name, session)
- if not suite:
- # Make sure that what we return is iterable so that list comprehensions
- # involving this don't cause a traceback
- return []
- q = session.query(VersionCheck).filter_by(suite=suite)
- if check:
- q = q.filter_by(check=check)
- return q.all()
- __all__.append('get_version_checks')
- ################################################################################
- class DBConn(object):
- """
- database module init.
- """
- __shared_state = {}
- db_meta = None
- tbl_architecture = Architecture.__table__
- tables = (
- 'acl',
- 'acl_architecture_map',
- 'acl_fingerprint_map',
- 'acl_per_source',
- 'archive',
- 'bin_associations',
- 'bin_contents',
- 'binaries',
- 'binaries_metadata',
- 'build_queue',
- 'changelogs_text',
- 'changes',
- 'component',
- 'component_suite',
- 'config',
- 'dsc_files',
- 'external_files',
- 'external_overrides',
- 'external_signature_requests',
- 'extra_src_references',
- 'files',
- 'files_archive_map',
- 'fingerprint',
- 'hashfile',
- 'keyrings',
- 'maintainer',
- 'metadata_keys',
- 'new_comments',
- # TODO: the maintainer column in table override should be removed.
- 'override',
- 'override_type',
- 'policy_queue',
- 'policy_queue_upload',
- 'policy_queue_upload_binaries_map',
- 'policy_queue_byhand_file',
- 'priority',
- 'signature_history',
- 'source',
- 'source_metadata',
- 'src_associations',
- 'src_contents',
- 'src_format',
- 'src_uploaders',
- 'suite',
- 'suite_acl_map',
- 'suite_architectures',
- 'suite_build_queue_copy',
- 'suite_permission',
- 'suite_src_formats',
- 'uid',
- 'version_check',
- )
- views = (
- 'bin_associations_binaries',
- 'changelogs',
- 'newest_source',
- 'newest_src_association',
- 'package_list',
- 'source_suite',
- 'src_associations_src',
- )
- def __init__(self, *args, **kwargs):
- self.__dict__ = self.__shared_state
- if not getattr(self, 'initialised', False):
- self.initialised = True
- self.debug = 'debug' in kwargs
- self.__createconn()
- def __setuptables(self):
- for table_name in self.tables:
- table = Table(table_name, self.db_meta,
- autoload=True, extend_existing=True)
- setattr(self, 'tbl_%s' % table_name, table)
- for view_name in self.views:
- view = Table(view_name, self.db_meta, autoload=True)
- setattr(self, 'view_%s' % view_name, view)
- def __setupmappers(self):
- mapper(ACL, self.tbl_acl,
- properties=dict(
- architectures=relation(Architecture, secondary=self.tbl_acl_architecture_map, collection_class=set),
- fingerprints=relation(Fingerprint, secondary=self.tbl_acl_fingerprint_map, collection_class=set),
- match_keyring=relation(Keyring, primaryjoin=(self.tbl_acl.c.match_keyring_id == self.tbl_keyrings.c.id)),
- per_source=relation(ACLPerSource, collection_class=set),
- ))
- mapper(ACLPerSource, self.tbl_acl_per_source,
- properties=dict(
- acl=relation(ACL),
- fingerprint=relation(Fingerprint, primaryjoin=(self.tbl_acl_per_source.c.fingerprint_id == self.tbl_fingerprint.c.id)),
- created_by=relation(Fingerprint, primaryjoin=(self.tbl_acl_per_source.c.created_by_id == self.tbl_fingerprint.c.id)),
- ))
- mapper(Archive, self.tbl_archive,
- properties=dict(archive_id=self.tbl_archive.c.id,
- archive_name=self.tbl_archive.c.name))
- mapper(ArchiveFile, self.tbl_files_archive_map,
- properties=dict(archive=relation(Archive, backref='files'),
- component=relation(Component),
- file=relation(PoolFile, backref='archives')))
- mapper(BuildQueue, self.tbl_build_queue,
- properties=dict(queue_id=self.tbl_build_queue.c.id,
- suite=relation(Suite, primaryjoin=(self.tbl_build_queue.c.suite_id == self.tbl_suite.c.id))))
- mapper(DBBinary, self.tbl_binaries,
- properties=dict(binary_id=self.tbl_binaries.c.id,
- package=self.tbl_binaries.c.package,
- version=self.tbl_binaries.c.version,
- maintainer_id=self.tbl_binaries.c.maintainer,
- maintainer=relation(Maintainer),
- source_id=self.tbl_binaries.c.source,
- source=relation(DBSource, backref='binaries'),
- arch_id=self.tbl_binaries.c.architecture,
- architecture=relation(Architecture),
- poolfile_id=self.tbl_binaries.c.file,
- poolfile=relation(PoolFile),
- binarytype=self.tbl_binaries.c.type,
- fingerprint_id=self.tbl_binaries.c.sig_fpr,
- fingerprint=relation(Fingerprint),
- install_date=self.tbl_binaries.c.install_date,
- suites=relation(Suite, secondary=self.tbl_bin_associations,
- backref=backref('binaries', lazy='dynamic')),
- extra_sources=relation(DBSource, secondary=self.tbl_extra_src_references,
- backref=backref('extra_binary_references', lazy='dynamic')),
- key=relation(BinaryMetadata, cascade='all',
- collection_class=attribute_mapped_collection('key'))),
- )
- mapper(Component, self.tbl_component,
- properties=dict(component_id=self.tbl_component.c.id,
- component_name=self.tbl_component.c.name),
- )
- mapper(DBConfig, self.tbl_config,
- properties=dict(config_id=self.tbl_config.c.id))
- mapper(DSCFile, self.tbl_dsc_files,
- properties=dict(dscfile_id=self.tbl_dsc_files.c.id,
- source_id=self.tbl_dsc_files.c.source,
- source=relation(DBSource),
- poolfile_id=self.tbl_dsc_files.c.file,
- poolfile=relation(PoolFile)))
- mapper(ExternalOverride, self.tbl_external_overrides,
- properties=dict(
- suite_id=self.tbl_external_overrides.c.suite,
- suite=relation(Suite),
- component_id=self.tbl_external_overrides.c.component,
- component=relation(Component)))
- mapper(PoolFile, self.tbl_files,
- properties=dict(file_id=self.tbl_files.c.id,
- filesize=self.tbl_files.c.size),
- )
- mapper(Fingerprint, self.tbl_fingerprint,
- properties=dict(fingerprint_id=self.tbl_fingerprint.c.id,
- uid_id=self.tbl_fingerprint.c.uid,
- uid=relation(Uid),
- keyring_id=self.tbl_fingerprint.c.keyring,
- keyring=relation(Keyring),
- acl=relation(ACL)),
- )
- mapper(Keyring, self.tbl_keyrings,
- properties=dict(keyring_name=self.tbl_keyrings.c.name,
- keyring_id=self.tbl_keyrings.c.id,
- acl=relation(ACL, primaryjoin=(self.tbl_keyrings.c.acl_id == self.tbl_acl.c.id)))),
- mapper(DBChange, self.tbl_changes,
- properties=dict(change_id=self.tbl_changes.c.id,
- seen=self.tbl_changes.c.seen,
- source=self.tbl_changes.c.source,
- binaries=self.tbl_changes.c.binaries,
- architecture=self.tbl_changes.c.architecture,
- distribution=self.tbl_changes.c.distribution,
- urgency=self.tbl_changes.c.urgency,
- maintainer=self.tbl_changes.c.maintainer,
- changedby=self.tbl_changes.c.changedby,
- date=self.tbl_changes.c.date,
- version=self.tbl_changes.c.version))
- mapper(Maintainer, self.tbl_maintainer,
- properties=dict(maintainer_id=self.tbl_maintainer.c.id,
- maintains_sources=relation(DBSource, backref='maintainer',
- primaryjoin=(self.tbl_maintainer.c.id == self.tbl_source.c.maintainer)),
- changed_sources=relation(DBSource, backref='changedby',
- primaryjoin=(self.tbl_maintainer.c.id == self.tbl_source.c.changedby))),
- )
- mapper(NewComment, self.tbl_new_comments,
- properties=dict(comment_id=self.tbl_new_comments.c.id,
- policy_queue=relation(PolicyQueue)))
- mapper(Override, self.tbl_override,
- properties=dict(suite_id=self.tbl_override.c.suite,
- suite=relation(Suite,
- backref=backref('overrides', lazy='dynamic')),
- package=self.tbl_override.c.package,
- component_id=self.tbl_override.c.component,
- component=relation(Component,
- backref=backref('overrides', lazy='dynamic')),
- priority_id=self.tbl_override.c.priority,
- priority=relation(Priority,
- backref=backref('overrides', lazy='dynamic')),
- section_id=self.tbl_override.c.section,
- section=relation(Section,
- backref=backref('overrides', lazy='dynamic')),
- overridetype_id=self.tbl_override.c.type,
- overridetype=relation(OverrideType,
- backref=backref('overrides', lazy='dynamic'))))
- mapper(OverrideType, self.tbl_override_type,
- properties=dict(overridetype=self.tbl_override_type.c.type,
- overridetype_id=self.tbl_override_type.c.id))
- mapper(PolicyQueue, self.tbl_policy_queue,
- properties=dict(policy_queue_id=self.tbl_policy_queue.c.id,
- suite=relation(Suite, primaryjoin=(self.tbl_policy_queue.c.suite_id == self.tbl_suite.c.id))))
- mapper(PolicyQueueUpload, self.tbl_policy_queue_upload,
- properties=dict(
- changes=relation(DBChange),
- policy_queue=relation(PolicyQueue, backref='uploads'),
- target_suite=relation(Suite),
- source=relation(DBSource),
- binaries=relation(DBBinary, secondary=self.tbl_policy_queue_upload_binaries_map),
- ))
- mapper(PolicyQueueByhandFile, self.tbl_policy_queue_byhand_file,
- properties=dict(
- upload=relation(PolicyQueueUpload, backref='byhand'),
- )
- )
- mapper(Priority, self.tbl_priority,
- properties=dict(priority_id=self.tbl_priority.c.id))
- mapper(SignatureHistory, self.tbl_signature_history)
- mapper(DBSource, self.tbl_source,
- properties=dict(source_id=self.tbl_source.c.id,
- version=self.tbl_source.c.version,
- maintainer_id=self.tbl_source.c.maintainer,
- poolfile_id=self.tbl_source.c.file,
- poolfile=relation(PoolFile),
- fingerprint_id=self.tbl_source.c.sig_fpr,
- fingerprint=relation(Fingerprint),
- changedby_id=self.tbl_source.c.changedby,
- srcfiles=relation(DSCFile,
- primaryjoin=(self.tbl_source.c.id == self.tbl_dsc_files.c.source)),
- suites=relation(Suite, secondary=self.tbl_src_associations,
- backref=backref('sources', lazy='dynamic')),
- uploaders=relation(Maintainer,
- secondary=self.tbl_src_uploaders),
- key=relation(SourceMetadata, cascade='all',
- collection_class=attribute_mapped_collection('key'))),
- )
- mapper(SrcFormat, self.tbl_src_format,
- properties=dict(src_format_id=self.tbl_src_format.c.id,
- format_name=self.tbl_src_format.c.format_name))
- mapper(Suite, self.tbl_suite,
- properties=dict(suite_id=self.tbl_suite.c.id,
- policy_queue=relation(PolicyQueue, primaryjoin=(self.tbl_suite.c.policy_queue_id == self.tbl_policy_queue.c.id)),
- new_queue=relation(PolicyQueue, primaryjoin=(self.tbl_suite.c.new_queue_id == self.tbl_policy_queue.c.id)),
- debug_suite=relation(Suite, remote_side=[self.tbl_suite.c.id]),
- copy_queues=relation(BuildQueue,
- secondary=self.tbl_suite_build_queue_copy),
- srcformats=relation(SrcFormat, secondary=self.tbl_suite_src_formats,
- backref=backref('suites', lazy='dynamic')),
- archive=relation(Archive, backref='suites'),
- acls=relation(ACL, secondary=self.tbl_suite_acl_map, collection_class=set),
- components=relation(Component, secondary=self.tbl_component_suite,
- order_by=self.tbl_component.c.ordering,
- backref=backref('suites')),
- architectures=relation(Architecture, secondary=self.tbl_suite_architectures,
- backref=backref('suites'))),
- )
- mapper(Uid, self.tbl_uid,
- properties=dict(uid_id=self.tbl_uid.c.id,
- fingerprint=relation(Fingerprint)),
- )
- mapper(BinContents, self.tbl_bin_contents,
- properties=dict(
- binary=relation(DBBinary,
- backref=backref('contents', lazy='dynamic', cascade='all')),
- file=self.tbl_bin_contents.c.file))
- mapper(SrcContents, self.tbl_src_contents,
- properties=dict(
- source=relation(DBSource,
- backref=backref('contents', lazy='dynamic', cascade='all')),
- file=self.tbl_src_contents.c.file))
- mapper(MetadataKey, self.tbl_metadata_keys,
- properties=dict(
- key_id=self.tbl_metadata_keys.c.key_id,
- key=self.tbl_metadata_keys.c.key))
- mapper(BinaryMetadata, self.tbl_binaries_metadata,
- properties=dict(
- binary_id=self.tbl_binaries_metadata.c.bin_id,
- binary=relation(DBBinary),
- key_id=self.tbl_binaries_metadata.c.key_id,
- key=relation(MetadataKey),
- value=self.tbl_binaries_metadata.c.value))
- mapper(SourceMetadata, self.tbl_source_metadata,
- properties=dict(
- source_id=self.tbl_source_metadata.c.src_id,
- source=relation(DBSource),
- key_id=self.tbl_source_metadata.c.key_id,
- key=relation(MetadataKey),
- value=self.tbl_source_metadata.c.value))
- mapper(VersionCheck, self.tbl_version_check,
- properties=dict(
- suite_id=self.tbl_version_check.c.suite,
- suite=relation(Suite, primaryjoin=self.tbl_version_check.c.suite == self.tbl_suite.c.id),
- reference_id=self.tbl_version_check.c.reference,
- reference=relation(Suite, primaryjoin=self.tbl_version_check.c.reference == self.tbl_suite.c.id, lazy='joined')))
- ## Connection functions
- def __createconn(self):
- from .config import Config
- cnf = Config()
- if "DB::Service" in cnf:
- connstr = "postgresql://service=%s" % cnf["DB::Service"]
- elif "DB::Host" in cnf:
- # TCP/IP
- connstr = "postgresql://%s" % cnf["DB::Host"]
- if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
- connstr += ":%s" % cnf["DB::Port"]
- connstr += "/%s" % cnf["DB::Name"]
- else:
- # Unix Socket
- connstr = "postgresql:///%s" % cnf["DB::Name"]
- if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
- connstr += "?port=%s" % cnf["DB::Port"]
- engine_args = {'echo': self.debug}
- if 'DB::PoolSize' in cnf:
- engine_args['pool_size'] = int(cnf['DB::PoolSize'])
- if 'DB::MaxOverflow' in cnf:
- engine_args['max_overflow'] = int(cnf['DB::MaxOverflow'])
- # we don't support non-utf-8 connections
- engine_args['client_encoding'] = 'utf-8'
- # Monkey patch a new dialect in in order to support service= syntax
- import sqlalchemy.dialects.postgresql
- from sqlalchemy.dialects.postgresql.psycopg2 import PGDialect_psycopg2
- class PGDialect_psycopg2_dak(PGDialect_psycopg2):
- def create_connect_args(self, url):
- if str(url).startswith('postgresql://service='):
- # Eww
- servicename = str(url)[21:]
- return (['service=%s' % servicename], {})
- else:
- return PGDialect_psycopg2.create_connect_args(self, url)
- sqlalchemy.dialects.postgresql.base.dialect = PGDialect_psycopg2_dak
- try:
- self.db_pg = create_engine(connstr, **engine_args)
- self.db_smaker = sessionmaker(bind=self.db_pg,
- autoflush=True,
- autocommit=False)
- if self.db_meta is None:
- self.__class__.db_meta = Base.metadata
- self.__class__.db_meta.bind = self.db_pg
- self.__setuptables()
- self.__setupmappers()
- except OperationalError as e:
- from . import utils
- utils.fubar("Cannot connect to database (%s)" % str(e))
- self.pid = os.getpid()
- def session(self, work_mem=0):
- '''
- Returns a new session object. If a work_mem parameter is provided a new
- transaction is started and the work_mem parameter is set for this
- transaction. The work_mem parameter is measured in MB. A default value
- will be used if the parameter is not set.
- '''
- # reinitialize DBConn in new processes
- if self.pid != os.getpid():
- self.__createconn()
- session = self.db_smaker()
- if work_mem > 0:
- session.execute("SET LOCAL work_mem TO '%d MB'" % work_mem)
- return session
- __all__.append('DBConn')
|