#!/usr/bin/env python3
# ----------------------------------------------------------------------------
# process_status -- collect process status info
#
# January 2022, Anand Kumar, Akshatha
#
# Copyright (c) 2022-2023 by cisco Systems, Inc.
# All rights reserved.
# ----------------------------------------------------------------------------

import sys
import optparse
import subprocess
import re
import os
import signal
core_found = False
platform_prefix = "/opt/cisco/calvados/bin/vrfch.sh CTRL_VRF"
import json
from datetime import datetime
ONE_HOUR=3600
DATE="date '+%Y-%m-%d %T'"
SUCCESS = "success"
FAILURE = "failure"
FAILURE_STR = FAILURE+"$"

def handler(signum, frame):
    print(FAILURE_STR)
    sys.exit(0)

def get_vm_time(vm_type):
    if vm_type == "xr":
        cmd = DATE
    else:
        cmd = '/pkg/bin/install_exec_sysadmin \"source /opt/cisco/calvados/bin/install-functions.sh ; \
                %s %s \"' %(platform_prefix, DATE)
    status, output = subprocess.getstatusoutput(cmd)
    return(output)

def get_corec_time(tim):
     
     core_timestamp = 0
     val = ""
     num_date = ""  

     num_date = re.search("Core dump time: [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{6}", tim)
     if not num_date:
         num_date = re.search("Core Dump time: [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{6}", tim)
     if(num_date):
         ti = num_date.group(0).split(":", 1)[-1].strip().split()
         y, m, d = ti[0].split("-")
         h, mm, sec = ti[1].split(":")
         s, mi = sec.split(".")
         core_time = datetime(int(y), int(m), int(d), int(h), int(mm), int(s), int(mi)) 
         core_timestamp = (core_time - datetime(1970, 1, 1)).total_seconds()
     else:
         val = re.search("Core Dump time: (.+)", tim)
         if not val:
             val = re.search("Core dump time: (.+)", tim)
         val = val.group(1)  
         date_time_obj = datetime.strptime(val, '%a %b %d %H:%M:%S %Y')
         hms = str(date_time_obj.time())
         h, m ,s = hms.split(":")
         ymd = str(date_time_obj.date())
         y, mo, d = ymd.split("-")
         mi = 0
         core_time = datetime(int(y), int(mo), int(d), int(h), int(m), int(s), int(mi))  
         core_timestamp = (core_time - datetime(1970, 1, 1)).total_seconds()

     return core_timestamp

def get_current_time(vmtype):

    curr_time = get_vm_time(vmtype)
    ymd, hms = curr_time.split()
    y, m, d = ymd.split("-")
    h, mm, s = hms.split(":")
    mi = 000000
    ctime = datetime(int(y), int(m), int(d), int(h), int(mm), int(s), int(mi))
    ctimestamp = (ctime - datetime(1970, 1, 1)).total_seconds()         

    return ctimestamp

def parse_core_dump(output, vmtype, metric):
    process_dict = {}
    val = "" 

    value = output.split("Backtrace for Thread")
    for i in range(len(value)):
        if "Core was generated by" in value[i]:
            loca = ""
            process_name = ""
            sig = ""
            try :
                tim = re.search("Core dump time: (.+)", value[i])
                if not tim:
                    tim = re.search("Core Dump time: (.+)", value[i])
                if(tim):
                    core_timestamp = get_corec_time(value[i])
                    ctimestamp = get_current_time(vmtype)

                    time_diff = ctimestamp - core_timestamp
                    if time_diff < ONE_HOUR:
                        val = re.search("node: node(.+)", value[i]) or re.search("Location : (.+)", value[i])
                        if val:
                            loca = val.group(1).replace("/", "_")
                        val = re.search("Core for pid = (.+)", value[i])
                        if not val:
                            val = re.search("Core for Pid = (.+)", value[i])
                        if val:
                            val = val.group(1).split("(")
                            process_name = val[-1].replace(")", "")
                        val = re.search("Program terminated with signal .*", value[i])
                        if val:
                            sig = val.group(0)
                        if (process_dict.get(process_name) is None):
                            metric['process_state_metric']['metric_info'][process_name] = {}
                            metric['process_state_metric']['metric_info'][process_name]['loc'] = loca
                            metric['process_state_metric']['metric_info'][process_name]['vm_type'] = vmtype
                            metric['process_state_metric']['metric_info'][process_name]['signal'] = sig
                            process_dict[process_name] = 1;
            except:
                pass

def extract_process_status_data(data):
    data = data.split("},")
    str = ""

    for i in range(len(data)):
        val = data[i].replace("{", "").replace('"', "").replace("}", "").replace("'", "").split(":")
        str += val[0] + ":"
        str += val[2].split(",")[0] + ":"
        substr = val[3].split(",")
        if len(substr) > 1:
            str += substr[0] + "," + substr[1] + ":" + val[4] + "\n"
        else:
            str += substr[0] + ":" + val[4] + "\n"

    return(str)

def is_exr_platform():
    path = "/pkg/bin/sdr_instmgr"
    if(os.path.exists(path)) : 
        return True
    else:
        return False

def func_show_context():
    global platform_prefix
    output = ""
    health_state = ""
    health_msg = ""
    metric_name = "process-status"
    metric = {}
    metric_info = "{}"  
    metric['process_state_metric'] = {}
    metric['process_state_metric']['metric_info'] = {}

    vm_list = []

    if is_exr_platform() == False :
        vm_list = ["xr"]
    else:
        vm_list = ["xr", "admin"]

    for vm_type in vm_list:
        if vm_type == "xr":
            cmd = 'corehelper_context "-c" "0xb" "-n" "all"'
        else:
            cmd = "/pkg/bin/install_exec_sysadmin \"source /opt/cisco/calvados/bin/install-functions.sh ;\
                   %s /opt/cisco/calvados/bin/context  \"" %(platform_prefix)
        status, output = subprocess.getstatusoutput(cmd)
        if status:
            print(FAILURE_STR)
            sys.exit(0) 
        parse_core_dump(output, vm_type, metric)

    if metric['process_state_metric']['metric_info']:
        health_state = 'Warning'
        health_msg = 'Process aborted or terminated'
    else:
        health_state = 'Normal'
        health_msg = 'No process aborted or terminated'

    last_update = datetime.now().strftime('%-d %b %H:%M:%S.%f')

    if(metric['process_state_metric']['metric_info']):
        metric_info = extract_process_status_data(str(metric['process_state_metric']['metric_info']))
           
    output = SUCCESS+"$"+metric_name+"$"+ health_state+"$"+health_msg+"$"+last_update+"$"+metric_info+"$"
    print(output)

if __name__ == '__main__':
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(7)     
    func_show_context()
