summaryrefslogtreecommitdiffstats
path: root/presto.py
blob: a31615e22cdffc21befb74e918cd51ae79dcd27c (plain)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# author: Jonathan Dieter <jdieter@gmail.com>
#
# heavily modified from yum-deltarpm.py created by
#         Lars Herrmann <herrmann@redhat.com>
# 
# 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 2005 Duke University

from yum.plugins import TYPE_INTERACTIVE
from yum import config

import os
import sys

sys.path.append("/usr/share/presto")
import deltarpm
from prestoRepo import PrestoRepository
from prestomdparser import PrestoMDParser
import prestoTransaction
import prestoLog

requires_api_version = '2.1'
LOG_FILE = "/var/log/presto.log"
plugin_type = (TYPE_INTERACTIVE,)

rpm_size = 0
drpm_size = 0
was_drpm = False

# Configuration stuff
def config_hook(conduit):    
    # Set up repository specific deltarpm url and mirrorlist
    config.RepoConf.deltaurl = config.UrlListOption()
    config.RepoConf.deltamirrorlist = config.UrlOption()
    
    # Add --disable-presto option
    parser = conduit.getOptParser()
    parser.add_option('', '--disablepresto', dest='disablepresto',
        action='store_true', default=False,
        help="disable Presto plugin and don't download any deltarpms")

# Set up Presto repositories
def postreposetup_hook(conduit):
    opts, commands = conduit.getCmdLine()
    if not opts.disablepresto:
        conduit.info(2, 'Setting up Presto')
        for active_repo in conduit.getRepos().listEnabled():
            p_repo = PrestoRepository(active_repo, conduit)
            p_repo.setup(conduit.getConf().cache)

        conduit.info(2, 'Reading Presto metadata in from local files')
        for active_repo in conduit.getRepos().listEnabled():
            xml = active_repo.p_repo.getPrestoXML()
            if active_repo.p_repo.enabled:
                xmldata = active_repo.p_repo.repoXML.getData('deltas')
                (ctype, csum) = xmldata.checksum
                parser = PrestoMDParser(xml)
                active_repo.p_repo.deltalist = parser.getDeltaList()
    else:
        conduit.info(5, '--disablepresto specified - Presto disabled')
             

def postresolve_hook(conduit):
    global rpm_size
    global drpm_size
    global was_drpm
    
    opts, commands = conduit.getCmdLine()
    if not opts.disablepresto:
        # Cycle through packages to see if there's a deltarpm available
        for newpkg in conduit.getTsInfo():
            if newpkg.ts_state != "e":
                (chosen_drpm, installed, local, drpm_enabled) = prestoTransaction.find_available_drpms(conduit, newpkg)

                # If a drpm was found, change certain package information so it reflects
                # the drpm, not the rpm.
                if chosen_drpm != None:
                    was_drpm = True
                    rpm_size += int(newpkg.po.simple['packagesize'])
                    drpm_size += int(chosen_drpm['size'])
                    newpkg.po.hasdrpm = True
                    newpkg.po.simple['basepath'] = chosen_drpm['baseurl']
                    newpkg.po.simple['realpackagesize'] = newpkg.po.simple['packagesize']
                    newpkg.po.simple['packagesize'] = chosen_drpm['size']
                    newpkg.po.simple['realrelativepath'] = newpkg.po.simple['relativepath']
                    newpkg.po.simple['relativepath'] = chosen_drpm['drpm_filename']
                    newpkg.po.reallocalpath = newpkg.po.localpath
                    newpkg.po.localpath = newpkg.po.repo.deltasdir + "/" + os.path.basename(chosen_drpm['drpm_filename'])
                    newpkg.po.to = newpkg
                    newpkg.realpkgtup = newpkg.pkgtup
                    newpkg.pkgtup = (newpkg.name + " *", newpkg.arch, newpkg.epoch, newpkg.version, newpkg.release)
                    for (csum_type, csum, csumid) in newpkg.po._checksums:
                        if csumid:
                            newpkg.po._realchecksum = (csum_type, csum, csumid)
                            newpkg.po._checksums.remove((csum_type, csum, csumid))
                            csum_type = chosen_drpm['checksum_type']
                            csum = chosen_drpm['checksum']
                            newpkg.po._checksums.append((csum_type, csum, csumid))

                    conduit.info(2, "Found deltarpm update for %s.%s %s:%s.%s" % (newpkg.name, newpkg.arch, newpkg.epoch, newpkg.version, newpkg.release))
                else:
                    newpkg.po.hasdrpm = False
                    if installed and drpm_enabled and not local:
                        try:
                            rpm_size += int(newpkg.po.simple['packagesize'])
                            drpm_size += int(newpkg.po.simple['packagesize'])
                        except:
                            pass
        return
                        
        # Free up memory used by deleting Presto repositories
        for active_repo in conduit.getRepos().listEnabled():
            if active_repo.p_repo.enabled:
                del active_repo.p_repo

def postdownload_hook(conduit):
    global was_drpm
    
    opts, commands = conduit.getCmdLine()
    if not opts.disablepresto and was_drpm:
        failure = False
        # Cycle through packages to see if we've downloaded a deltarpm
        conduit.info(2, "Rebuilding full packages from deltas")
        for pkg in conduit.getDownloadPackages():
            if pkg.hasdrpm and pkg.verifyLocalPkg():
                # Apply deltarpm and save where rpm would have been saved
                drpm = deltarpm.DeltaRpmWrapper(conduit)
                this_failure = False
                try:
                    drpm.apply(pkg.reallocalpath, pkg.localpath)
                except:
                    failure = True
                    this_failure = True
                if not this_failure:
                    drpm_path = pkg.localpath
                    
                    # Change package information to reflect original rpm information
                    pkg.to.pkgtup = pkg.to.realpkgtup
                    pkg.localpath = pkg.reallocalpath
                    pkg.simple['packagesize'] = pkg.simple['realpackagesize']
                    del pkg.simple['basepath']
                    for (csum_type, csum, csumid) in pkg._checksums:
                        if csumid:
                            pkg._checksums.remove((csum_type, csum, csumid))
                            pkg._checksums.append(pkg._realchecksum)
                    
                    # Check to see whether or not we should keep the drpms
                    # FIXME: Is there any way to see whether or not a Boolean option was not set?
                    if conduit.confBool('main', 'neverkeepdeltas'):
                        delete = True
                    elif conduit.confBool('main', 'keepdeltas'):
                        delete = False
                    elif conduit.getConf().keepcache != 0:
                        delete = False
                    else:
                        delete = True
                    
                    if delete:
                        os.unlink(drpm_path)

        if failure:
            raise PluginYumExit("Error rebuilding at least one deltarpm.  Please run report this error to\nhttps://hosted.fedoraproject.org/projects/presto/newticket.  To bypass this problem, run yum \nwith the --disablepresto option")

def posttrans_hook(conduit):
    global rpm_size
    global drpm_size
    global LOG_FILE
    
    if rpm_size > 0:
        prestoLog.log(conduit, LOG_FILE, rpm_size, drpm_size)
                    
        conduit.info(2, "Size of all updates downloaded from Presto-enabled repositories: %i bytes" % drpm_size)
        conduit.info(2, "Size updates would have been downloaded if Presto wasn't enabled: %i bytes" % rpm_size)
        conduit.info(2, "This is a savings of %i percent" % (100 - ((drpm_size * 100) / rpm_size)))