#!/usr/bin/python -tt # -*- coding: utf-8 -*- # # Copyright © 2009 Red Hat, Inc. # # This copyrighted material is made available to anyone wishing to use, modify, # copy, or redistribute it subject to the terms and conditions of the GNU # General Public License v.2, or (at your option) any later version. This # program is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY expressed or implied, including the implied warranties 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., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. Any Red Hat trademarks that are incorporated in the source # code or documentation are not subject to the GNU General Public License and # may only be used or replicated with the express permission of Red Hat, Inc. # # Author(s): Ionuț Arțăriși # Martin Bacovsky # ''' Import PackageBuilds into the packagedb Import PackageBuild(rpm) related information from all the `active` repos available in the pkgdb. ''' __requires__ = 'fedora_packagedb' # Pylint Errors # :E1101: Ignore this anytime we use the session. SQLAlchemy monkeypatches # extra functionality into here that isn't apparent to pylint import os import sys CONFDIR = '/etc' PKGDBDIR = os.path.join('/usr/share', 'fedora-packagedb') sys.path.append(PKGDBDIR) import pkg_resources import logging import argparse from fnmatch import fnmatch from sqlalchemy.sql import and_ from sqlalchemy.exceptions import SADeprecationWarning from turbogears import config, update_config from turbogears.database import session log = logging.getLogger('pkgdb-sync-yum') # suppress sqlalchemy warnings # FIXME: update the code to avoid warnings import warnings warnings.filterwarnings('ignore', '.*', SADeprecationWarning) def setup_parser(): # create the argument parser parser = argparse.ArgumentParser( description='Import packagebuilds and applications into Fedora PkgDB.') parser.add_argument('--config', '-c', help='Fedora PkgDB config file') subparsers = parser.add_subparsers(title='Import commands', help='for more help run "%(prog)s --help"') # import local command parser_importlocal = subparsers.add_parser('importlocal', help='import local rpm files. WARNING: Make sure you are choosing the right repo. Make sure the files exist on the mirrors (links from the web ui will not work). Make sure the imported files are most fresh builds (we assume that the last imported is most fresh version).') parser_importlocal.add_argument('--repo', '-r', metavar='shortname', required=True, help='repository in which the rpm(s) belongs to. For list of valid repos run with showrepos command') parser_importlocal.add_argument('--force', action='store_true', help='reimport existing builds') parser_importlocal.add_argument('files', nargs='+', metavar='file', help='rpm file to be imported into pkgdb') parser_importlocal.set_defaults(func='import_local') # update command parser_update = subparsers.add_parser('update', help='import builds from all active repos. Script goes through builds backwards. It skips already imported builds.') parser_update.add_argument('--repo', '-r', metavar='shortname', default='*', help='go just through this repo. For list of valid repos run with showrepos command') parser_update.add_argument('--cachedir', help='directory where the yum cache will be created during the import. If not set, we are looking for sync-yum.cachedir option in config file. If not found use "/var/tmp" as default. Make sure cachedir is writeable.') parser_update.add_argument('--verbose', action='store_true', help="set verbose output") parser_update.add_argument('--quiet', action='store_true', help="print just error messages") parser_update.add_argument('--force', action='store_true', help="reimport existing builds. When this option is selected we DON'T skip to next repo on hitting already imported build") parser_update.add_argument('--quick', default=False, action='store_true', help='with this option the script skips to next repo on first already imported build occurence') parser_update.add_argument('--profile', metavar='dir', help='directory where data collected from profiler should be stored. Output is in kcachegrind format.') parser_update.add_argument('--profile-skip', type=int, default=0, help='how many files should be processed before profiling data are taken') parser_update.add_argument('package', nargs='?', help='update just these packages. Wildcards are allowed') parser_update.set_defaults(func='update') # showrepos command parser_showrepos = subparsers.add_parser('showrepos', help='show available active/inactive repos') parser_showrepos.add_argument('--inactive', default=False, action='store_true', help='show inactive repos instead of the active ones') parser_showrepos.set_defaults(func='show_repos') # clean command parser_clean = subparsers.add_parser('clean', help='Clean up pkgdb database. It removes all builds from inactive repos. For list of inactive repos run "%(prog)s showrepos --inactive". This command also removes all builds that are no longer in active repos.') parser_clean.add_argument('--cachedir', help='directory where the yum cache will be created. If not set, we are looking for sync-yum.cachedir option in config file. If not found use "/var/tmp" as default. Make sure cachedir is writeable.') parser_clean.set_defaults(func='clean') return parser def setup_framework(args): #configure turbogears framework if args.config: update_config(configfile=args.config, modulename='pkgdb.config') elif os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'setup.py')): update_config(configfile='pkgdb.cfg', modulename='pkgdb.config') else: update_config(configfile=os.path.join(CONFDIR,'pkgdb.cfg'), modulename='pkgdb.config') config.update({'pkgdb.basedir': PKGDBDIR}) fas_url = config.get('fas.url', 'https://admin.fedoraproject.org/accounts/') username = config.get('fas.username', 'admin') password = config.get('fas.password', 'admin') if args.verbose: log.setLevel(logging.INFO) if args.quiet: log.setLevel(logging.ERROR) def show_repos(args): """Print list of active repos """ from pkgdb.model import Repo #pylint:disable-msg=E1101 repos = session.query(Repo).filter_by(active=(not args.inactive)).all() #pylint:enable-msg=E1101 print 'List of active repositories:' for r in repos: print '%-20s:%-60s' % (r.shortname, r.name) def import_local(args): """Import local rpm files into pkgdb Turns local rpms into YumLocalPackage and imports them the usual way. This is usefull for testing or initial import. WARNING: be carefull to associate the builds with the right repo. :arg args: set of commandline arguments """ from pkgdb.model import Repo from yum.packages import YumLocalPackage from rpmUtils.transaction import initReadOnlyTransaction from pkgdb.lib.packagebuild import PackageBuildImporter, RPM from pkgdb.lib.packagebuild import PkgImportAlreadyExists, PkgImportError # prepare rpm transaction for rpm parser ts = initReadOnlyTransaction() # find repo try: #pylint:disable-msg=E1101 repo = session.query(Repo).filter_by(shortname=args.repo).one() #pylint:enable-msg=E1101 except: log.error("Repo (%s) does not exist" % args.repo) exit(1) if not repo.active: log.error("Repo (%s) is not active" % args.repo) exit(1) # prepare importer importer = PackageBuildImporter(repo, force=args.force) for f in args.files: session.begin() #pylint:disable-msg=E1101 log.info("PackageBuild: %s" % f) pkg = YumLocalPackage(ts=ts, filename=f) try: importer.process(RPM(pkg, yumrepo=None)) except PkgImportAlreadyExists, e: rpm.close() log.info("%s: Already imported - skipping" % pkg.name) session.rollback() #pylint:disable-msg=E1101 continue except PkgImportError, e: rpm.close() log.warning("%s: Error during import (%s)- skipping" % (pkg.name, e)) session.rollback() #pylint:disable-msg=E1101 continue session.commit() #pylint:disable-msg=E1101 rpm.close() session.begin() #pylint:disable-msg=E1101 importer.close(prune=False) # do not remove builds records that are not on mirrors session.commit() #pylint:disable-msg=E1101 log.info('Done.') def _get_cachedir(cachedir): if not cachedir: cachedir = config.get('sync-yum.cachedir', '/var/tmp') log.info('CacheDir: %s' % cachedir) return cachedir def update(args): """Import local rpm files into pkgdb Turns local rpms into YumLocalPackage and imports them the usual way. This is usefull for testing or initial import. WARNING: be carefull to associate the builds with the right repo. :arg args: set of commandline arguments """ from pkgdb.model import Repo from datetime import datetime from pkgdb.lib.dt_utils import fancy_delta from pkgdb.lib.packagebuild import PackageBuildImporter, RPM from pkgdb.lib.packagebuild import PkgImportAlreadyExists, PkgImportError started = datetime.now() cachedir = _get_cachedir(args.cachedir) profile_skip = args.profile_skip total_packages = 0 # find repo #pylint:disable-msg=E1101 repos = session.query(Repo)\ .filter(and_( Repo.active==True, Repo.shortname.like(args.repo.replace('*','%'))))\ .all() #pylint:disable-msg=E1101 if len(repos)==0: log.error("Repo '%s' not found!" % args.repo) exit(1) for repo in repos: log.info("Repo: %s" % repo.name) # prepare importer try: importer = PackageBuildImporter(repo, force=args.force, cachedir=cachedir) except Exception, e: log.error('Failed to setup import for repo %s! (%s)' % (repo.name, e)) continue try: pkgbuilds = importer.yumrepo.sack.returnNewestByName() except Exception, e: log.error('Failed to read repo %s! (%s)' % (repo.name, e)) continue total = len(pkgbuilds) counter = 1 for pkg in pkgbuilds: if args.package and not fnmatch(pkg.name, args.package): continue session.begin() #pylint:disable-msg=E1101 log.info(" (%s/%s)%s" % (counter, total, pkg)) counter += 1 rpm = RPM(pkg, yumrepo=importer.yumrepo) if args.profile: if profile_skip == 0: def import_rpm(r): importer.process(r) session.flush() from pkgdb.lib.profiling import Profiler p = Profiler(directory=args.profile) # collect profiling data and die p.profileit(import_rpm, rpm) sys.exit(0) else: profile_skip -= 1; try: try: importer.process(rpm) except PkgImportAlreadyExists, e: log.info("%s: Already imported - skipping" % pkg.name) session.rollback() #pylint:disable-msg=E1101 if args.quick: break else: continue except PkgImportError, e: log.warning("%s: Error during import (%s)- skipping" % (pkg.name, e)) session.rollback() #pylint:disable-msg=E1101 continue finally: rpm.close() session.commit() #pylint:disable-msg=E1101 session.begin() #pylint:disable-msg=E1101 importer.close() session.commit() #pylint:disable-msg=E1101 total_packages += counter-1 finished = datetime.now() run_delta = finished - started run_sec = run_delta.days*24*3600 + run_delta.seconds + run_delta.microseconds/1000000 log.info('Processed %i packages in %s. That make avg %.1f packages per second.' % (total_packages, fancy_delta(started, now=finished, precision=None, verbose=False, short=True, gran='millisecond'), 1.0*total_packages/run_sec)) log.info('Done.') def clean(args): """Clean up databse. Remove all builds from inactive repos. Remove all builds that are no longer in active repos. """ from pkgdb.model import Repo from pkgdb.lib.packagebuild import PackageBuildImporter cachedir = _get_cachedir(args.cachedir) #pylint:disable-msg=E1101 repos = session.query(Repo).all() #pylint:enable-msg=E1101 for repo in repos: log.info("Cleaning: %s" % repo.name) # prepare importer try: importer = PackageBuildImporter(repo, cachedir=cachedir) except Exception, e: log.error('Failed to setup import for repo %s! (%s)' % (repo.name, e)) continue session.begin() #pylint:disable-msg=E1101 if repo.active: importer.prune_builds() else: importer.delete_all_builds() importer.close(prune=False) session.commit() #pylint:disable-msg=E1101 log.info('Done.') if __name__ == "__main__": parser = setup_parser() args = parser.parse_args() setup_framework(args) # command handling locals().get(args.func)(args)