#!/usr/bin/env python
#
# Project:
#   glideinWMS
#
# File Version: 
#
# Description:
#   This tool analyzes the matches between jobs and factory entries
#
# Arguments:
#   $1 = work dir
#   $2 = group_name
#   $3 = job search pattern (e.g. '234\.3')
#
# Author:
#   Igor Sfiligoi 
#

import signal
import sys,os,os.path,copy
import re
import fcntl
import traceback
import time,string
sys.path.append(os.path.join(sys.path[0],".."))
sys.path.append(os.path.join(sys.path[0],"../../lib"))

from glideinwms.frontend import glideinFrontendConfig
from glideinwms.frontend import glideinFrontendInterface
from glideinwms.frontend import glideinFrontendLib
from glideinwms.lib import exprParser
from glideinwms.frontend.glideinFrontendLib import getGlideinCpusNum

# shortest first
def mycmp(a,b):
    r=cmp(len(a),len(b))
    if r==0:
        r=cmp(a,b)
    return r

def print_match(job,glidein,match_ast,counts,level,countid):
    prefix=""
    for l in range(level):
        prefix+="  "
    
    match_obj=exprParser.compile(match_ast)
    res=eval(match_obj)
    c="%s.%s"%(countid,res)
    if not counts.has_key(c):
        counts[c]=[0,exprParser.unparse(match_ast)]
    counts[c][0]+=1
    print "%s%-10s %s"%(prefix,res,exprParser.unparse(match_ast))

    if isinstance(match_ast,exprParser.Or):
        #print 
        print "  %s%-5s due to OR of"%(prefix,res)
        i=0
        for n in match_ast.nodes:
            r=print_match(job,glidein,n,counts,level+2,c+".OR%i"%i)
            i+=1
            if r==True:
                break # partial eval, exit at first True
    elif isinstance(match_ast,exprParser.And):
        #print 
        print "  %s%-5s due to AND of"%(prefix,res)
        i=0
        for n in match_ast.nodes:
            r=print_match(job,glidein,n,counts,level+2,c+".AND%i"%i)
            i+=1
            if r==False:
                break # partial eval, exit at first False
    elif isinstance(match_ast,exprParser.Not):
        #print 
        print "  %s%-5s due to NOT of"%(prefix,res)
        print_match(job,glidein,match_ast.expr,counts,level+2,c+".NOT")
            
    return res


############################################################
def do_work(elementDescript,paramsDescript,signatureDescript,job_filter):
    job_filter_obj=re.compile(job_filter)
    # query condor

    glidein_dict={}
    factory_constraint=elementDescript.merged_data['FactoryQueryExpr']
    factory_pools=elementDescript.merged_data['FactoryCollectors']
    for factory_pool in factory_pools:
        factory_pool_node=factory_pool[0]
        factory_identity=factory_pool[1]
        my_identity_at_factory_pool=factory_pool[2]
        try:
            full_constraint='(%s) && ((PubKeyType=?="RSA") && (GlideinAllowx509_Proxy=!=False))' % factory_constraint
            factory_glidein_dict=glideinFrontendInterface.findGlideins(factory_pool_node,None,signatureDescript.signature_type,full_constraint)
        except RuntimeError,e:
            if factory_pool_node is not None:
                print "Failed to talk to factory_pool %s: %s"%(factory_pool_node, e)
            else:
                print "Failed to talk to factory_pool: %s"%e
            # failed to talk, like empty... maybe the next factory will have something
            factory_glidein_dict={}

        for glidename in factory_glidein_dict.keys():
            if (not factory_glidein_dict[glidename]['attrs'].has_key('AuthenticatedIdentity')) or (factory_glidein_dict[glidename]['attrs']['AuthenticatedIdentity']!=factory_identity):
                print "Found an untrusted factory %s at %s; ignoring."%(glidename,factory_pool_node)
                if factory_glidein_dict[glidename]['attrs'].has_key('AuthenticatedIdentity'):
                    print "Found an untrusted factory %s at %s; identity mismatch '%s'!='%s'"%(glidename,factory_pool_node,factory_glidein_dict[glidename]['attrs']['AuthenticatedIdentity'],factory_identity)
            else:
                glidein_dict[(factory_pool_node,glidename,my_identity_at_factory_pool)]=factory_glidein_dict[glidename]

    


    ## schedd
    condorq_format_list=elementDescript.merged_data['JobMatchAttrs']
    condorq_dict=glideinFrontendLib.getCondorQ(elementDescript.merged_data['JobSchedds'],
                                               elementDescript.merged_data['JobQueryExpr'],
                                               condorq_format_list)


    # Match
    match_ast=exprParser.parse(elementDescript.merged_data['MatchExpr'])
    jobs=[]
    sk=condorq_dict.keys()
    sk.sort()
    for schedd in sk:
        condorq=condorq_dict[schedd]
        condorq_data=condorq.fetchStored()
        jk=condorq_data.keys()
        jk.sort()
        for jid in jk:
            job=condorq_data[jid]
            t="%s#%i.%i"%(schedd,jid[0],jid[1])

            if job_filter_obj.search(t) is None:
                continue #no matching
            
            print
            print "-=-=-=-=-=-=-=-=-=-"
            print "Job %s"%t

            match_counts={}
            for glidename in glidein_dict:
                glidein=glidein_dict[glidename]

                print "-------------"
                print "%s vs %s"%(t, glidename[1])
                print "job:     %s"%job
                glideattr={}
                for a in elementDescript.merged_data.get('FactoryMatchAttrs', []):
                    if glidein["attrs"].has_key(a[0]):
                        glideattr[a[0]]=glidein["attrs"][a[0]]
                print "glidein: %s"%glideattr
                print "="

                print_match(job,glidein,match_ast,match_counts,1,t)

                pass

            print
            print "======================"
            print "Summary for %s"%t
            print "job:     %s"%job
            ks=match_counts.keys()
            ks.sort(mycmp)
            for k in ks:
                print "%-80s %10i %s"%(k, match_counts[k][0],match_counts[k][1])
            pass
        pass

    
    return


############################################################
def main(work_dir, group_name,job_filter):
    startup_time=time.time()

    elementDescript=glideinFrontendConfig.ElementMergedDescript(work_dir,group_name)

    paramsDescript=glideinFrontendConfig.ParamsDescript(work_dir,group_name)
    signatureDescript=glideinFrontendConfig.GroupSignatureDescript(work_dir,group_name)

    # set the condor configuration and GSI setup globally, so I don't need to worry about it later on
    os.environ['CONDOR_CONFIG']=elementDescript.frontend_data['CondorConfig']
    os.environ['_CONDOR_CERTIFICATE_MAPFILE']=elementDescript.element_data['MapFile']
    os.environ['X509_USER_PROXY']=elementDescript.frontend_data['ClassAdProxy']

    try:
        do_work(elementDescript,paramsDescript,signatureDescript,job_filter)
    except KeyboardInterrupt:
        print "Received signal...exit"
    except:
        tb = traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],
                                        sys.exc_info()[2])
        print "Exception occurred: %s" % tb
        
############################################################
#
# S T A R T U P
#
############################################################

if __name__ == '__main__':
    main(sys.argv[1],sys.argv[2],sys.argv[3])
 
