#!/usr/bin/python3
import argparse, json, logging, os, re
import common

def validate_environment():
    if not os.path.exists('/sys/fs/cgroup/cgroup.controllers'):
        common.fatal_error("cgroups is not v2 on this machine")
    if os.getuid() != 0:
        common.fatal_error("this program must be run as root")
    common.ls_ok()
    
def init_pgm():
    common.init_logging()
    parser = argparse.ArgumentParser(prog="lscgstats",
                                     description='LiteSpeed cgroups Statistics Program')
    parser.add_argument("uid", type=str, nargs='*', default=None, help="uid or user name for -user commands")
    parser.add_argument('-l', '--log', type=int, help='set logging level, 10=Debug, 20=Info, 30=Warning, 40=Error.  Default is Info')
    parser.add_argument('-q', '--quiet', action='store_true', help='turns off all logging and only outputs what is requested.')
    args = parser.parse_args()
    if not args.quiet or args.log != None:
        if args.log != None:
            logging.getLogger().setLevel(args.log)
        else:
            logging.getLogger().setLevel(logging.INFO)
        logging.debug("Entering lscgstats")

    validate_environment()
    if args.uid == None or len(args.uid) == 0:
        args.uid = slice_of()
    users = common.get_users(args.uid, True)
    return users

def read_stat(file, pos):
    try:
        f = open(file, 'r')
    except Exception as err:
        common.fatal_error('Error opening %s: %s' % (file, err))
    line = f.readline()
    pieces = line.split(' ')
    f.close()
    return pieces[pos]

def read_val(file):
    try:
        f = open(file, 'r')
    except Exception as err:
        common.fatal_error('Error opening %s: %s' % (file, err))
    line = f.readline()
    f.close()
    return line.rstrip()

def read_param_val(file, param):
    try:
        f = open(file, 'r')
    except Exception as err:
        common.fatal_error('Error opening %s: %s' % (file, err))
    for line in f:
        pieces = line.split(' ')
        if pieces[0] == param:
            f.close()
            return pieces[1].rstrip()
    f.close()
    common.fatal_error('Error %s not found in %s' % (param, file))

def user_to_file(user):
    return 'user-%s.slice' % user.pw_uid

def user_to_filename(user):
    if user == None:
        return '/sys/fs/cgroup/user.slice'
    return '/sys/fs/cgroup/user.slice/%s' % user_to_file(user)

def slice_of():
    names = os.listdir('/sys/fs/cgroup/user.slice')
    min_uid = common.get_min_uid()
    uids = []
    for name in names:
        if name.startswith('user-'):
            pos = name.find('.')
            if pos == -1:
                logging.debug('%s has the right starts with but no dot' % name)
                continue
            uid = name[5:pos]
        else:
            continue
        if int(uid) < min_uid:
            logging.debug('Skip uid %d, too low (%d)' % (int(uid), min_uid))
            continue
        logging.debug('Adding uid: %s' % uid)
        uids.append(uid)
    return uids

def scrape_cpu(user):
	return read_param_val(user_to_filename(user) + "/cpu.stat", 'usage_usec')

def scrape_io(user, block_devices):
    file = user_to_filename(user) + "/io.stat"
    try:
        f = open(file, 'r')
    except Exception as err:
        logging.debug('Error opening %s: %s' % (file, err))
        return '0', '0'
    bytes = 0
    ios = 0;
    for line in f:
        parm_vals = line.split(' ')
        maj_min = parm_vals[0].split(':')
        if int(maj_min[0]) in block_devices and int(maj_min[1]) in block_devices[int(maj_min[0])]:
            for parm_val in parm_vals:
                pieces = parm_val.split('=')
                if len(pieces) == 2:
                    if pieces[0] == 'rbytes' or pieces[0] == 'wbytes':
                        bytes = bytes + int(pieces[1])
                    elif pieces[0] == 'rios' or pieces[0] == 'wios':
                        ios = ios + int(pieces[1])
        else:
            logging.debug('Skip device: %s for io test for major/minor not usable' % parm_vals[0])
    f.close()
    return str(bytes), str(ios)

def scrape_mem(user):
    return read_val(user_to_filename(user) + "/memory.current")

def scrape_tasks(user):
    return read_val(user_to_filename(user) + "/pids.current")

def list_user(user, dict, block_devices):
    subdict = {}
    if user == None:
        subdict['name'] = '.'
    else:
        subdict['name'] = user.pw_name
    subdict['cpu'] = scrape_cpu(user)
    subdict['io'], subdict['iops'] = scrape_io(user, block_devices)
    subdict['mem'] = scrape_mem(user)
    subdict['tasks'] = scrape_tasks(user)
    if user == None:
        logging.debug("uid[.]: " + str(subdict))
        dict['.'] = subdict
    else:
        logging.debug("uid[" + str(user.pw_uid) + ']: ' + str(subdict))
        dict[user.pw_uid] = subdict
    
def command_list(users):
    dict = {}
    block_devices = common.get_devices()[1]
    list_user(None, dict, block_devices)
    for user in users:
        list_user(user, dict, block_devices)
    print(json.dumps(dict, indent=4))
    return 0

def do_pgm(users):
    logging.debug("Entering lscgstats")
    ret = command_list(users)
    logging.debug("Exiting lscgstats")
    return ret

def main():
    users = init_pgm()
    return do_pgm(users)
  
if __name__ == "__main__":
    main()
