#!/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 3 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

""" casprint - fingerprinting utility for cas
"""
import os
import ConfigParser
import optparse
import sys
import urlparse

from cas.core import CoreBase
from cas.util import UtilBase, Logging
from cas.rpmutils import RPMBase
from subprocess import Popen, PIPE
from cas.cas_shutil import rmtree

if sys.version_info[:2] < (2,4):
    raise SystemExit("Python >= 2.4 required")

# Read in configuration
config = ConfigParser.ConfigParser()
config.read("/etc/cas.conf")
KERNELS = config.get("settings","kernels")
RPMS = config.get("settings","rpms")
RPMFILTER = config.get("settings","rpmFilter")
DEBUGS = config.get("settings","debugs")
DEBUGLEVEL = config.get("settings","debugLevel")
SERVERS = config.get("settings","servers")

class CasDatabaseHandler(object):
    def __init__(self, logger):
        self.casLog = logger
        self.util = UtilBase()
        if os.path.isfile(RPMS):
            self.rpmDB = self.util.load(RPMS)
        else:
            self.rpmDB = {}

    def rpmExist(self, rpm):
        """ checks existence of rpm in db
        """
        if self.rpmDB.has_key(rpm):
            return True
        return False

    def run(self):
        rpms = []
        # Uses emacs regex -- see `man find`
        cmd = ["find", "-L", KERNELS, "-iregex", RPMFILTER]
        pipe = Popen(cmd, stdout=PIPE, stderr=PIPE)
        # setup count for kernels found, mainly for console output
        count = 0
        # create list of rpms from `cmd`
        for line in pipe.stdout:
            rpms.append(line.strip())
            self.casLog.status("(found) %-5d kernel(s)" % (count,))
            count = count + 1
        # reset count for further informational messaging
        totalRpms = len(rpms)
        count = 0
        """ Build database out in the form of
        RPM - [( DebugKernel, Timestamp),
               ( DebugKernel2, Timestamp2)]
        """
        for x in rpms:
            if not self.rpmExist(x):
                self.rpmDB[x] = []
                count = count + 1
                # temporary storage path in form of DEBUGS/COUNT
                dst = os.path.join(DEBUGS, str(count))
                rpmTool = RPMBase()
                self.casLog.status("(extracting) [%d/%d] %-50s" % (count, totalRpms, 
                                                                   os.path.basename(x)))
                results = rpmTool.extract(x, dst)
                # Sort through extracted debug for each type
                # e.g. hugemem, PAE, smp, largesmp
                for item in results:
                    vmlinux = item.strip()
                    stamper = CoreBase()
                    debugKernel = os.path.normpath(vmlinux)
                    timestamp = stamper.timestamp(debugKernel)
                    # Build tuple of each debug type
                    self.rpmDB[x].append((debugKernel, timestamp))
                    self.util.save(self.rpmDB, RPMS)
                # Cleanup extracted debugs
                rmtree(dst)
        return
        
class CasServerHandler(object):
    def __init__(self, logger):
        self.casLog = logger
        self.util = UtilBase()
        
    def run(self):
        try:
            serverList = {}
            hostname_count = 0
            # Attempt to load func to automate detection of machine arch
            import func.overlord.client as fc
            parent_func = fc.Overlord("*")
            minions = parent_func.minions
            # TODO: add only servers that respond, purge the rest
            for i in minions:
                scheme, netloc, path, query, frag = urlparse.urlsplit(i)
                hostname, port = netloc.split(":")
                client = fc.Overlord(hostname)
                # Returns as {"hostname":['sts','stdout','stderr']}
                # what a pain
                client_dict = client.command.run("/bin/uname -m")
                # silly magic to get output from command module
                # since we are doing one host at a time we just pull the first
                # key from the dict and parse its return
                sts, arch, err = client_dict[client_dict.keys()[0]]
                if sts:
                    self.casLog.debug(err)
                # clean up arch string
                arch = arch.strip()
                if serverList.has_key(arch):
                    serverList[arch].append(hostname)
                else:
                    serverList[arch] = [hostname]
                hostname_count = hostname_count + 1
            self.util.save(serverList, SERVERS)
            self.casLog.info("Server database built with %d server(s) added." % (hostname_count,))
        except ImportError:
            self.casLog.debug("Please install func (http://fedorahosted.org/func) for " \
                   "an automated machine arch population.\n")
            sys.exit(1)
        return

class CasAdminApplication(object):
    def __init__(self, args):
        self.parse_options(args)
        self.casLog = Logging("/var/log","cas-admin")

    def parse_options(self, args):
        parser = optparse.OptionParser(usage="casprint [opts] args")
        parser.add_option("-b","--build", dest="buildDB",
                          help="Build CAS DB", action="store_true", default=False)
        parser.add_option("-s","--server", dest="server_init",
                          help="Build SERVER DB", action="store_true", default=False)
        (self.opts, args) = parser.parse_args()
        self.buildDB = self.opts.buildDB
        self.server_init = self.opts.server_init

    def run(self):
        """ Make sure necessary directories and configuration is setup
        prior to running the fingerprint
        """
        if os.getuid() is not 0:
            raise RuntimeError, "You must be root(0), instead you are id(%d)" % (os.getuid())
        if not os.path.isdir(os.path.dirname(RPMS)):
            os.makedirs(RPMS)
        if not os.path.isdir(DEBUGS):
            os.makedirs(DEBUGS)

        if self.buildDB:
            self.casLog.info("Starting CAS DB instance.")
            dbHandler = CasDatabaseHandler(self.casLog).run()
        elif self.server_init:
            self.casLog.info("Building CAS Server DB instance.")
            serverHandler = CasServerHandler(self.casLog).run()
        else:
            self.casLog.info("Missing options, please run with --help.")
            sys.exit(1)

if __name__=="__main__":
    app = CasAdminApplication(sys.argv[1:])
    sys.exit(app.run())

