#!/usr/bin/python3
# -*- coding: utf-8 -*-

import getopt
import sys
import os
import json

def parse_meminfo():
    mem = {}
    fp = open("/proc/meminfo", 'r')
    line = fp.readline()
    while line:
        if line.find("MemTotal") > -1:
            mem["total"] = line.split()[1]
        elif line.find("MemFree") > -1:
            mem["free"] = line.split()[1]
        elif line.find("MemAvailable") > -1:
            mem["avil"] = line.split()[1]
        elif line.find("Active(anon):") > -1:
            mem["act_anon"] = line.split()[1]
        elif line.find("Inactive(anon)") > -1:
            mem["ina_anon"] = line.split()[1]
        elif line.find("Active(file)") > -1:
            mem["act_file"] = line.split()[1]
        elif line.find("Inactive(file)") > -1:
            mem["ina_file"] = line.split()[1]
        line = fp.readline()
    return mem

def parse_flag(flag):
    name = ""
    subname = ""
    if flag == "slab":
        return "slab","both un/reclaimable "
    flag_map = {}
    flag_map['c'] = "clean"
    flag_map['d'] = "dirty"
    flag_map['e'] = "evict"
    flag_map['u'] = "unevict"
    flag_map['i'] = "inactive"
    flag_map['a'] = "active"
    if flag[1] == 'f':
        name = "file"
    else:
        name = "anon"
    subname = flag_map[flag[3]]+" && "+flag_map[flag[0]]+" && "+flag_map[flag[2]]
    return name,subname

def parse(datas):
    swaps = 0
    files = 0
    slabs = 0
    detail = {}
    subdetail1 = []
    subdetail2 = []
    for key in datas:
        name,subname = parse_flag(key)
        subdetail2.append(name+"("+subname+")="+str(datas[key])+"MB")
        if name == "anon":
            swaps = swaps + datas[key]
        elif name == "file":
            files = files + datas[key]
        elif name == "slab":
            slabs = slabs + datas[key]
    subdetail1.append("anon="+str(swaps)+"MB")
    subdetail1.append("file="+str(files)+"MB")
    subdetail1.append("slab="+str(slabs)+"MB")
    #print("anon="+str(swaps)+"MB file="+str(files)+"MB slabs="+str(slabs))
    detail["detail1"] = subdetail1
    detail["detail2"] = subdetail2
    #print(detail)
    return detail

def cold_page(flag, num, total):
    coldMB = num >> 20
    if coldMB > 0:
        detailCold[flag] = coldMB
        #print(flag+" has "+str(coldMB)+" MB cold memory!")
    return total+coldMB

def read_idle_page(path):
    fp = open(path, 'r')
    line = fp.readline()
    totalColdMB = 0
    while line:
        if line[0] == '#':
            if len(line) < 3:
                line = fp.readline()
                continue
            if line[2]>='A' and line[2] <= 'z':
                linetail = line[2:]
                linearr = linetail.split(": ")
                if linearr[0].find("page_scans") > -1:
                    global _page_scans
                    _page_scans = int(linearr[1])
                    #print("_page_scans"+"="+_page_scans);
                elif linearr[0].find("slab_scans") > -1:
                    global _slab_scans
                    _slab_scans = int(linearr[1])
                    #print("_slab_scans"+"="+_slab_scans);
                elif linearr[0].find("scan_period") > -1:
                    global scan_period
                    scan_period = int(linearr[1])
                    #print("scan_period"+"="+scan_period)
                elif linearr[0].find("buckets") > -1:
                    _buckets = linearr[1].split(",")
                    #print(_buckets[0]+","+_buckets[-1])
                    global max_buck
                    max_buck = int(_buckets[-1])
        else:
            datas = line.split()
            totalColdMB = cold_page(datas[0], int(datas[4]), totalColdMB)
            #parse_flag(datas[0])
        line = fp.readline()
    fp.close()
    return totalColdMB


def main_proc(pattern):
    global detailCold
    global oldest
    global result
    global totalColdMB
    detailCold = {}
    result = {}
    summary = {}
    detail = {}
    meminfo = {}
    meminfo = parse_meminfo()
    totalMem = meminfo["total"]
    totalRes = read_idle_page("/sys/fs/cgroup/memory/memory.idle_page_stats")
    oldest = max_buck*scan_period
    scan_rounds = "normal_pages="+str(_page_scans)+"  slab_pages="+str(_slab_scans)+" (perRound="+str(scan_period)+" seconds)"
    summary["scanned_rounds"] = scan_rounds
    summary["reson"] = "There are "+str(totalRes)+"MB"+" memory wasn't touched more than "+str(oldest)+" seconds"
    #summary["conclusion"] =
    cold_proportion = 100*float(totalRes)/float(totalMem)
    #print("We have %f cold memory" % cold_proportion)
    if cold_proportion > 20:
        summary["cause"] = "存在较严重的内存瓶颈"
        summary["status"] = "Emergency"
        summary["suggestion"] = "建议安装coldpgs模块并使用idlemd工具进行优化"
    elif cold_proportion > 5:
        summary["cause"] = "存在内存瓶颈"
        summary["status"] = "Warn"
        summary["suggestion"] = "建议安装coldpgs模块并使用idlemd工具进行优化"
    else:
        summary["cause"] = "没有内存瓶颈"
        summary["status"] = "Normal"
        summary["suggestion"] = "N/A"
    #print(summary)
    detail = parse(detailCold)
    result["summary"] = summary
    result["detail"] = detail
    if pattern == 'j':
        print(result)
    elif pattern == 't':
        tmp = "Result:"+"["+summary["status"]+"] "+summary["reson"]+", "+summary["cause"]+"\n"
        tmp = tmp+"suggestion:"+summary["suggestion"]+"\n"
        tmp = tmp+"Detail:"
        dt1=detail["detail1"]
        dt2=detail["detail2"]
        for i in dt1:
            tmp = tmp+i+" "
        tmp = tmp + "\n       "
        for i in dt2:
            tmp = tmp+i+"\n       "
        tmp = tmp + "More details see: /sys/fs/cgroup/memory/memory.idle_page_stats"
        print(tmp)
    #print(json.dumps(result))

def usage():
    print ('coldmem: Detect if we have too many cold memory which can be reclaim')
    print ('Usage: coldmem <option> [<args>]')
    print ('  -h            help information')
    print ('  -j            print result with json pattern')
    print ('  -t            (default)print result with text pattern')
    print ('example:')
    print ('sysak idlepage -j')
    print ('sysak idlepage -t')
    return

opts,args = getopt.getopt(sys.argv[1:],'-h-j-t')

def main():
    for opt_name,opt_value in opts:
        if opt_name in ('-h'):
            usage()
            sys.exit()
        if opt_name in ('-j'):
            main_proc('j')
            sys.exit()
        if opt_name in ('-t'):
            main_proc('t')
            sys.exit()

    main_proc('t')

if __name__ == '__main__':
    main()
