#!/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 
#

from __future__ import print_function
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 c not in counts:
        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 as 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 ('AuthenticatedIdentity' not in factory_glidein_dict[glidename]['attrs']) or (factory_glidein_dict[glidename]['attrs']['AuthenticatedIdentity']!=factory_identity):
                print("Found an untrusted factory %s at %s; ignoring."%(glidename,factory_pool_node))
                if 'AuthenticatedIdentity' in factory_glidein_dict[glidename]['attrs']:
                    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 a[0] in glidein["attrs"]:
                        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])
