#!/usr/bin/python -tt

# 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) 2008 Red Hat. Written by skvidal@fedoraproject.org

import os
import subprocess
import sys
import yum
from yum import Errors
from rpmUtils import miscutils
import gzip
import rpm
from optparse import OptionParser

# maybe use YumQuiet?

class YumDebugDump(yum.YumBase):

    def __init__(self):
        self.file_version = '1'      
        yum.YumBase.__init__(self)
        self.parse_args()
    
    def parse_args(self):
        parser = OptionParser()
        parser.set_usage("yum-debug-dump [options]")
        parser.add_option("--norepos", action="store_true", default=False,
           help="do not attempt to dump the repository contents")
        self.opts, self.args = parser.parse_args()

    def dump_rpmdb(self):
        msg = "%%%%RPMDB\n"
        for po in self.rpmdb:
            msg += '  %s:%s-%s-%s.%s\n' % (po.epoch, po.name, po.ver,po.rel, po.arch)
        
        return msg

    def dump_repos(self):
        msg = "%%%%REPOS\n"
        for repo in self.repos.listEnabled():
            try:
                msg += '%%%s - %s\n' % (repo.id, repo.urls[0])
                msg += "  excludes: %s\n" % ",".join(repo.exclude)
                for po in self.pkgSack.returnPackages(repo.id):
                    msg += '  %s:%s-%s-%s.%s\n' % (po.epoch, po.name, po.ver,po.rel, po.arch)
            except Errors.RepoError, e:
                msg += "Error accessing repo %s: %s\n" % (repo, str(e))
                continue
        return msg

    def dump_system_info(self):
        msg = "%%%%SYSTEM INFO\n"
        msg += "  uname: %s, %s\n" % (os.uname()[2], os.uname()[4])
        msg += "  rpm ver: %s\n" % subprocess.Popen(["rpm", "--version"], stdout=subprocess.PIPE).communicate()[0].strip()
        msg += "  python ver: %s\n" % sys.version.replace('\n', '')
        return msg
        
    def dump_yum_config_info(self):
        msg = "%%%%YUM INFO\n"
        msg += "  arch: %s\n" % self.conf.yumvar['arch']
        msg += "  basearch: %s\n" % self.conf.yumvar['basearch']        
        msg += "  releasever: %s\n" % self.conf.yumvar['releasever']
        msg += "  yum ver: %s\n" % yum.__version__
        msg += "  enabled plugins: %s\n" % ",".join(self.plugins._plugins.keys())
        msg += "  global excludes: %s\n" % ",".join(self.conf.exclude)
        return msg
    
    def dump_rpm_problems(self):

        pkgs = {}
        for po in self.rpmdb.returnPackages():
            tup = po.pkgtup
            header= po.hdr
            requires = zip(
                header[rpm.RPMTAG_REQUIRENAME],
                header[rpm.RPMTAG_REQUIREFLAGS],
                header[rpm.RPMTAG_REQUIREVERSION],
                )
            pkgs[tup] = requires


        errors = []
        providers = {} # To speed depsolving, don't recheck deps that have 
                       # already been checked
        provsomething = {}
        for (pkg,reqs) in pkgs.items():
            for (req,flags,ver)  in reqs:
                if ver == '':
                    ver = None
                rflags = flags & 15
                if req.startswith('rpmlib'): continue # ignore rpmlib deps
                
                if not providers.has_key((req,rflags,ver)):
                    resolve_sack = self.rpmdb.whatProvides(req,rflags,ver)
                else:
                    resolve_sack = providers[(req,rflags,ver)]
                    
                if len(resolve_sack) < 1:
                    errors.append("Package %s requires %s" % (pkg[0],
                      miscutils.formatRequire(req,ver,rflags)))
                else:
                    for rpkg in resolve_sack:
                        # Skip packages that provide something for themselves
                        # as these can still be leaves
                        if rpkg != pkg:
                            provsomething[rpkg] = 1
                    # Store the resolve_sack so that we can re-use it if another
                    # package has the same requirement
                    providers[(req,rflags,ver)] = resolve_sack


        msg = "%%%%RPMDB PROBLEMS\n"
        for error in errors:
            msg += "%s\n" % error

        # possibly list all verify failures, too
        return msg


    
    def create_debug_file(self, fn=None):
        """create debug txt file and compress it, place it at yum_debug_dump.txt.gz
           unless fn is specified"""
        if not fn:
            fn = 'yum_debug_dump.txt.gz'
        
        if not fn.startswith('/'):
            fn = '%s/%s' % (os.getcwd(), fn)
        
        fo = gzip.GzipFile(fn, 'w')
        
        msg = "yum-debug-dump version %s\n" % self.file_version
        fo.write(msg)
        fo.write(self.dump_system_info())
        fo.write(self.dump_yum_config_info())
        fo.write(self.dump_rpm_problems())
        fo.write(self.dump_rpmdb())
        if not self.opts.norepos:
            fo.write(self.dump_repos())
        fo.close()
        return fn
        
def main():
    my = YumDebugDump()
    fn = my.create_debug_file()
    print "Output written to: %s" % fn
    
if __name__ == "__main__":
    main()


