tmprepo.py 7.21 KB
Newer Older
James Antill's avatar
James Antill committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#!/usr/bin/python

# 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 Library 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.
#
# Copyright Red Hat Inc. 2007, 2008
#
# Author: James Antill <james@fedoraproject.com>
# Examples:
#
# yum --tmprepo=http://example.com/foo/bar.repo ...

import yum
import types
from yum.plugins import TYPE_INTERACTIVE
import logging # for commands
from yum import logginglevels

import logging
import urlgrabber.grabber
import tempfile
33 34
import os
import shutil
35
import time
James Antill's avatar
James Antill committed
36 37 38 39

requires_api_version = '2.5'
plugin_type = (TYPE_INTERACTIVE,)

40
def make_validate(log, pkgs_gpgcheck, repo_gpgcheck):
James Antill's avatar
James Antill committed
41
    def tvalidate(repo):
42
        if pkgs_gpgcheck or repo_gpgcheck:
43 44

            # Don't ever allow them to set gpgcheck='false'
45
            if pkgs_gpgcheck and not repo.gpgcheck:
James Antill's avatar
James Antill committed
46 47
                log.warn("Repo %s tried to set gpgcheck=false" % repo)
                return False
48

49 50
            if repo_gpgcheck and not repo.repo_gpgcheck:
                log.warn("Repo %s tried to set repo_gpgcheck=false" % repo)
51
                return False
52

James Antill's avatar
James Antill committed
53 54 55 56 57 58 59 60 61 62 63
            # Don't allow them to set gpgkey=anything
            for key in repo.gpgkey:
                if not key.startswith('file:/'):
                    log.warn("Repo %s tried to set gpgkey to %s" %
                             (repo, repo.gpgkey))
                    return False

        return True

    return tvalidate

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
dnames = []
class AutoCleanupDir:
    """
    Given a directory ... let it exist until "the end", and then clean it up.
    """

    def __init__(self, dname):
        self.dname = dname
        # Want it to live until python shutdown
        dnames.append(self)

    # Can't use __del__ as python doesn't dtrt. on exit
    def cleanup(self):
        shutil.rmtree(self.dname, ignore_errors=True)

def close_hook(conduit):
    global dnames
    for dname in dnames:
        dname.cleanup()
    dnames = []

85
def add_dir_repo(base, trepo, cleanup):
86 87 88 89
    # Let people do it via. local directories ... we don't want this for
    # HTTP because people _should_ just generate .repo files, but for
    # local CDs of pkgs etc. we'll be nice.
    trepo_path = trepo[len("file:"):]
90 91 92 93 94
    trepo_data = tempfile.mkdtemp()
    if cleanup:
        AutoCleanupDir(trepo_data)
    else:
        os.chmod(trepo_data, 0755)
95
    trepo_name = os.path.basename(os.path.dirname(trepo_path))
96 97
    tmp_fname  = "%s/tmp-%s.repo" % (trepo_data, trepo_name)
    repoid     = "T-%4.4s-%x" % (trepo_name, int(time.time()))
98
    open(tmp_fname, "wb").write("""\
99 100
[%(repoid)s]
name=Tmp. repo. for %(path)s
101 102
baseurl=file:%(dname)s
enabled=1
103 104
gpgcheck=true
repo_gpgcheck=false
105 106 107 108
metadata_expire=0
#  Make cost smaller, as we know it's "local" ... if this isn't good just create
# your own .repo file. ... then you won't need to createrepo each run either.
cost=500
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
""" % {'basename' : trepo_name,
       'path'     : trepo_path,
       'repoid'   : repoid,
       'dname'    : trepo_data})
    if cleanup:
        print "Creating tmp. repodata for:", trepo_path
    else:
        print "Creating saved repodata for:", trepo_path
        print "    * Result is saved here :", tmp_fname
        
    os.spawnlp(os.P_WAIT, "createrepo",
               "createrepo", "--database", "--baseurl", trepo,
               "--outputdir", trepo_data, trepo_path)
    AutoCleanupDir("%s/%s" % (base.conf.cachedir, repoid))
    return tmp_fname

125
def add_repos(base, log, tmp_repos, tvalidate, tlocvalidate, cleanup_dir_temp):
James Antill's avatar
James Antill committed
126 127 128
    """ Add temporary repos to yum. """
    # Don't use self._splitArg()? ... or require URLs without commas?
    for trepo in tmp_repos:
129 130
        if trepo.startswith("~/"):
            trepo = "%s%s" % (os.environ['HOME'], trepo[1:])
131 132
        if trepo.startswith("/"):
            trepo = "file:%s" % trepo
133 134 135
        validate = tvalidate
        if trepo.startswith("file:"):
            validate = tlocvalidate
136 137 138 139
        if trepo.startswith("file:") and trepo.endswith("/"):
            if not os.path.isdir(trepo[len("file:"):]):
                log.warn("Failed to find directory " + trepo[len("file:"):])
                continue
140
            fname = add_dir_repo(base, trepo, cleanup_dir_temp)
141 142
        else:
            grab = urlgrabber.grabber.URLGrabber()
143 144 145
            # Need to keep alive until fname is used
            gc_keep = tempfile.NamedTemporaryFile()
            fname = gc_keep.name
146
            try:
147
                fname = grab.urlgrab(trepo, fname)
148 149 150
            except urlgrabber.grabber.URLGrabError, e:
                log.warn("Failed to retrieve " + trepo)
                continue
James Antill's avatar
James Antill committed
151

152
        base.getReposFromConfigFile(fname, validate=validate)
James Antill's avatar
James Antill committed
153 154 155 156 157
        added = True

    # Just do it all again...
    base.setupProgressCallbacks()

158 159 160 161
rpgpgcheck = True # Remote 
rrgpgcheck = True # Remote 
lpgpgcheck = True
lrgpgcheck = False
James Antill's avatar
James Antill committed
162 163 164 165 166
def config_hook(conduit):
    '''
    Yum Plugin Config Hook: 
    Add the --tmprepo option.
    '''
167 168 169 170
    global rpgpgcheck
    global rrgpgcheck
    global lpgpgcheck
    global lrgpgcheck
171
    global def_tmp_repos_cleanup
James Antill's avatar
James Antill committed
172 173 174 175 176 177 178 179 180 181
    
    parser = conduit.getOptParser()
    if not parser:
        return

    parser.values.tmp_repos = []
    parser.add_option("--tmprepo", action='append',
                      type='string', dest='tmp_repos', default=[],
                      help="enable one or more repositories from URLs",
                      metavar='[url]')
182 183 184
    parser.add_option("--tmprepo-keep-created", action='store_true',
                      dest='tmp_repos_cleanup', default=False,
                      help="keep created direcotry based tmp. repos.")
185 186
    #  We default to repository for actual repo files, because that's the most
    # secure, but packages for local dirs./files
187 188 189 190 191 192
    rpgpgcheck = conduit.confBool('main', 'pkgs_gpgcheck', default=True)
    rrgpgcheck = conduit.confBool('main', 'repo_gpgcheck', default=True)
    lpgpgcheck = conduit.confBool('main', 'pkgs_local_gpgcheck',
                                  default=rpgpgcheck)
    lrgpgcheck = conduit.confBool('main', 'repo_local_gpgcheck',
                                  default=False)
193
    def_tmp_repos_cleanup = conduit.confBool('main', 'cleanup', default=False)
James Antill's avatar
James Antill committed
194

195
_tmprepo_done = False
James Antill's avatar
James Antill committed
196 197 198 199 200
def prereposetup_hook(conduit):
    '''
    Process the tmp repos from --tmprepos.
    '''

201 202 203 204 205
    # Stupid group commands doing the explicit setup stuff...
    global _tmprepo_done
    if _tmprepo_done: return
    _tmprepo_done = True

James Antill's avatar
James Antill committed
206
    opts, args = conduit.getCmdLine()
James Antill's avatar
James Antill committed
207 208
    if not opts.tmp_repos:
        return
209

James Antill's avatar
James Antill committed
210 211
    log = logging.getLogger("yum.verbose.main")
    add_repos(conduit._base, log, opts.tmp_repos,
212 213
              make_validate(log, rpgpgcheck, rrgpgcheck),
              make_validate(log, lpgpgcheck, lrgpgcheck),
214
              not (opts.tmp_repos_cleanup or def_tmp_repos_cleanup))