repomanage.py 6.88 KB
Newer Older
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
#!/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.

# (c) Copyright Seth Vidal 2004

# need hdropen, dir traversing, version comparison, and getopt (eventually)

# this should take a dir, traverse it - build a dict of foo[(name, arch)] = [/path/to/file/that/is/highest, /path/to/equalfile]

import os
import sys
import rpm
import fnmatch
import string
28
import rpmUtils
29
from yum import misc
30

31 32
from optparse import OptionParser

33 34 35 36 37 38 39 40 41 42 43 44 45

def errorprint(stuff):
    print >> sys.stderr, stuff
    
    
def getFileList(path, ext, filelist):
    """Return all files in path matching ext, store them in filelist, recurse dirs
       return list object"""
    
    extlen = len(ext)
    try:
        dir_list = os.listdir(path)
    except OSError, e:
46
        errorprint('Error accessing directory %s, %s' % (path, str(e)))
47
        return []
48 49 50 51 52 53
        
    for d in dir_list:
        if os.path.isdir(path + '/' + d):
            filelist = getFileList(path + '/' + d, ext, filelist)
        else:
            if string.lower(d[-extlen:]) == '%s' % (ext):
54 55
                newpath = os.path.normpath(path + '/' + d)
                filelist.append(newpath)
56 57 58 59 60 61 62
                    
    return filelist


def trimRpms(rpms, excludeGlobs):
    # print 'Pre-Trim Len: %d' % len(rpms)
    badrpms = []
63
    for fn in rpms:
64
        for glob in excludeGlobs:
65 66 67 68 69 70 71
            if fnmatch.fnmatch(fn, glob):
                # print 'excluded: %s' % fn
                if fn not in badrpms:
                    badrpms.append(fn)
    for fn in badrpms:
        if fn in rpms:
            rpms.remove(fn)            
72 73 74 75 76
    # print 'Post-Trim Len: %d' % len(rpms)
    return rpms


def parseargs(args):
77 78 79 80 81 82
    usage = """
    repomanage: manage a directory of rpm packages. returns lists of newest 
                or oldest packages in a directory for easy piping to xargs
                or similar programs.
    repomanage [--old] [--new] path.
    """
83
    parser = OptionParser(usage=usage)
84
    
85 86 87 88 89 90 91 92 93 94 95 96
    # new is only used to make sure that the user is not trying to get both 
    # new and old, after this old and not old will be used. 
    # (default = not old = new)
    parser.add_option("-o", "--old", default=False, action="store_true",
      help='print the older packages')
    parser.add_option("-n", "--new", default=False, action="store_true",
      help='print the newest packages')
    parser.add_option("-s", "--space", default=False, action="store_true",
      help='space separated output, not newline')
    parser.add_option("-k", "--keep", default=1, dest='keep', action="store",
      help='newest N packages to keep - defaults to 1')
    parser.add_option("-c", "--nocheck", default=0, action="store_true", 
97
      help='do not check package payload signatures/digests')
98
    
99 100 101 102 103
    (opts, args)= parser.parse_args()
    
    
    if opts.new and opts.old:
        errorprint('\nPass either --old or --new, not both!\n')
104
        print parser.format_help()
105 106 107
        sys.exit(1)
        
    if len(args) > 1:
108
        errorprint('Error: Only one directory allowed per run.')
109
        print parser.format_help()
110
        sys.exit(1)
111 112
        
    if len(args) < 1:
113
        errorprint('Error: Must specify a directory to index.')
114
        print parser.format_help()
115
        sys.exit(1)
116 117
        
    return (opts, args)
118 119


120
def main(args):
121 122 123 124 125
    
    (options, args) = parseargs(args)
    mydir = args[0]

    
126 127 128
    rpmList = []
    rpmList = getFileList(mydir, '.rpm', rpmList)
    verfile = {}
129 130
    pkgdict = {} # hold all of them - put them in (n,a) = [(e,v,r),(e1,v1,r1)]
    
131
    keepnum = int(options.keep)*(-1) # the number of items to keep
132 133 134 135 136
    
    if len(rpmList) == 0:
        errorprint('No files to process')
        sys.exit(1)
    
137

138
    ts = rpm.TransactionSet()
139
    if options.nocheck:
140 141 142
        ts.setVSFlags(~(rpm._RPMVSF_NOPAYLOAD))
    else:
        ts.setVSFlags(~(rpm.RPMVSF_NOMD5|rpm.RPMVSF_NEEDPAYLOAD))
143 144
    for pkg in rpmList:
        try:
145 146
            hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
        except rpmUtils.RpmUtilsError, e:
147
            msg = "Error opening pkg %s: %s" % (pkg, str(e))
148
            errorprint(msg)
149 150
            continue
        
151
        pkgtuple = rpmUtils.miscutils.pkgTupleFromHeader(hdr)
152 153 154
        (n,a,e,v,r) = pkgtuple
        del hdr
        
155
        if (n,a) not in pkgdict:
156 157 158
            pkgdict[(n,a)] = []
        pkgdict[(n,a)].append((e,v,r))
        
159
        if pkgtuple not in verfile:
160 161 162
            verfile[pkgtuple] = []
        verfile[pkgtuple].append(pkg)
        
163 164 165 166
    for natup in pkgdict.keys():
        evrlist = pkgdict[natup]
        if len(evrlist) > 1:
            evrlist = misc.unique(evrlist)
167
            evrlist.sort(rpmUtils.miscutils.compareEVR)
168
            pkgdict[natup] = evrlist
169
                
170
    del ts
171

172 173 174
    # now we have our dicts - we can return whatever by iterating over them
    
    outputpackages = []
175 176 177
    
    #if new
    if not options.old:
178 179
        for (n,a) in pkgdict.keys():
            evrlist = pkgdict[(n,a)]
180
            
181 182 183 184
            if len(evrlist) < abs(keepnum):
                newevrs = evrlist
            else:
                newevrs = evrlist[keepnum:]
185
            
186 187 188
            for (e,v,r) in newevrs:
                for pkg in verfile[(n,a,e,v,r)]:
                    outputpackages.append(pkg)
189
   
190
    if options.old:
191 192
        for (n,a) in pkgdict.keys():
            evrlist = pkgdict[(n,a)]
193
            
194 195 196 197 198
            if len(evrlist) < abs(keepnum):
                continue
 
            oldevrs = evrlist[:keepnum]
            for (e,v,r) in oldevrs:
199 200 201 202 203
                for pkg in verfile[(n,a,e,v,r)]:
                    outputpackages.append(pkg)
    
    outputpackages.sort()
    for pkg in outputpackages:
204
        if options.space:
205 206 207 208 209 210 211 212 213 214 215
            print '%s' % pkg,
        else:
            print pkg
        
    
def usage():
    print """
      repomanage [--old] [--new] path
      -o --old - print the older packages
      -n --new - print the newest packages
      -s --space - space separated output, not newline
216
      -k --keep - newest N packages to keep - defaults to 1
217
      -c --nocheck - do not check package payload signatures/digests
218 219 220 221 222 223 224 225 226 227 228
      -h --help - duh
    By default it will output the full path to the newest packages in the path.
        """
        

if __name__ == "__main__":
    if len(sys.argv) < 1:
        usage()
        sys.exit(1)
    else:
        main(sys.argv[1:])