#!/opt/cloudlinux/venv/bin/python3 -sbb
# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import json
import sys
import os
import errno

from cldetectlib import get_param_from_file
from clcommon.utils import exec_utility, get_file_lines
from clcommon.ui_config import UIConfig

LVE_INFO = '/var/lve/info'
PERCENTS_STATS_MODE_FLAG = '/opt/cloudlinux/flags/enabled-flags.d/percentage-user-stats-mode.flag'

# Format of line in /var/lve/info (formed by lvestats-server):
# (id, mep, lep, cpu_usage, lcpu, mem_usage, lmem,
# mem_fault, mep_fault, lmemphy, memphy, memphy_fault,
# lnproc, nproc, nproc_fault, lcpuw, ioread, iowrite)

ID_INDEX     = 0
EP_INDEX     = 1
LEP_INDEX    = 2
CPU_INDEX    = 3
LCPU_INDEX   = 4
MEM_INDEX    = 5
LMEM_INDEX   = 6
MEMF_INDEX   = 7
MEPF_INDEX   = 8
LPMEM_INDEX  = 9
PMEM_INDEX   = 10
PMEMF_INDEX  = 11
LNPROC_INDEX = 12
NPROC_INDEX  = 13
NPROCF_INDEX = 14
LCPUW_INDEX  = 15
IO_INDEX     = 16
LIO_INDEX    = 17
LIOPS_INDEX  = 18
IOPS_INDEX   = 19

MEMORY_PAGE_SIZE = 4096
BYTES_IN_KILO = 1024

LIMITS_LIST = [
                ('lcpu', 'cpu'),
                ('lep', 'ep'),
                ('lmem','mem'),
                ('lmemphy', 'memphy'),
                ('liops', 'iops'),
                ('lio','io'),
                ('lnproc', 'nproc'),
                ('linodes', 'inodes'),
              ]

template_dict = {
    'cpu' : {
            'id' : 'lvecpu',
            'description' : 'CPU Usage',
            'formatter' : None,
            },
    'ep': {
            'id':'lveep',
            'description' : 'Entry Processes',
            'formatter' : None,
            },
    'mem': {
            'id':'lvemem',
            'description' : 'Virtual Memory Usage',
            'formatter' : 'format_bytes',
            },
    'memphy': {
            'id':'lvememphy',
            'description' : 'Physical Memory Usage',
            'formatter' : 'format_bytes',
            },

    'nproc': {
            'id':'lvenproc',
            'description' : 'Number of Processes',
            'formatter' : None,
            },
    'io': {
            'id':'lveio',
            'description' : 'I/O Usage',
            'formatter' : 'format_bytes_per_second',
            },

    'iops': {
            'id':'lveiops',
            'description' : 'IOPS',
            'formatter' : None,
            },
    'inodes': {
            'id':'lveinodes',
            'description' : 'Inodes',
            'formatter' : None,
            },
}


def is_normalize_user_cpu():
    # default TRUE
    CONFIG = '/etc/sysconfig/cloudlinux'
    norm_cpu = get_param_from_file(CONFIG, 'normalized_user_cpu', '=')
    if norm_cpu.upper() in ['N','NO','FALSE']:
        return False
    return True


def convert_cpu(kernel_cpu_limit, kernel_cpu_usage):
    """
    Normilize cpu if needed
    """
    if is_normalize_user_cpu():
        return (100, int(float(kernel_cpu_usage) / kernel_cpu_limit * 100))
    else:
        return (int(float(kernel_cpu_limit) / 100), int(float(kernel_cpu_usage) / 100))

def convert_to_percent(limit, usage):
    """
    Convert any limit/usage to percent
    """
    percent = int((usage / limit) * 100)
    return percent


def get_lve_limits_for_uid(uid, show_inode=False):
    """
    Parse LVE_INFO and return line by ID
    param:: uid - int
    param:: show_inode - bool
    return:: dict or None
    """
    data = None
    lines = get_file_lines(LVE_INFO)
    if show_inode:
        quota = get_quota_info(uid)
    else:
        quota = None
    for line in lines:
        line = line.strip()
        line_parts = line.split(',')
        if int(line_parts[ID_INDEX]) == uid:
            data =  {
                    'ep'        : int(line_parts[EP_INDEX]),
                    'lep'       : int(line_parts[LEP_INDEX]),
                    'cpu'       : int(line_parts[CPU_INDEX]),
                    'lcpu'      : int(line_parts[LCPU_INDEX]),
                    'mem'       : int(line_parts[MEM_INDEX]) * MEMORY_PAGE_SIZE, #convert page to bytes
                    'lmem'      : int(line_parts[LMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'memphy'    : int(line_parts[PMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'lmemphy'   : int(line_parts[LPMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'nproc'     : int(line_parts[NPROC_INDEX]),
                    'lnproc'    : int(line_parts[LNPROC_INDEX]),
                    'io'        : int(line_parts[IO_INDEX]) * BYTES_IN_KILO,
                    'lio'       : int(line_parts[LIO_INDEX]) * BYTES_IN_KILO,
                    'iops'      : int(line_parts[IOPS_INDEX]),
                    'liops'     : int(line_parts[LIOPS_INDEX]),
                    }
            break
        if int(line_parts[ID_INDEX]) == 0:
            data_default =  {
                    'ep'        : int(line_parts[EP_INDEX]),
                    'lep'       : int(line_parts[LEP_INDEX]),
                    'cpu'       : int(line_parts[CPU_INDEX]),
                    'lcpu'      : int(line_parts[LCPU_INDEX]),
                    'mem'       : int(line_parts[MEM_INDEX]) * MEMORY_PAGE_SIZE, #convert page to bytes
                    'lmem'      : int(line_parts[LMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'memphy'    : int(line_parts[PMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'lmemphy'   : int(line_parts[LPMEM_INDEX]) * MEMORY_PAGE_SIZE,
                    'nproc'     : int(line_parts[NPROC_INDEX]),
                    'lnproc'    : int(line_parts[LNPROC_INDEX]),
                    'io'        : int(line_parts[IO_INDEX]) * BYTES_IN_KILO,
                    'lio'       : int(line_parts[LIO_INDEX]) * BYTES_IN_KILO,
                    'iops'      : int(line_parts[IOPS_INDEX]),
                    'liops'     : int(line_parts[LIOPS_INDEX]),
                    }
    # data can be None if all limits are default and no usage
    # use default limits as lve limits
    if data is None:
        data = data_default
    if quota is not None and show_inode:
        data['inodes'] = int(quota[1])
        data['linodes'] = int(quota[2])
    return data


def get_quota_info(lve_id):
    """
    Return qouta line by id
    return list [id, usage, hard, soft] or None
    """
    try:
        (r, std_out) = exec_utility('/usr/bin/cl-quota', ['--user-id=%s' % lve_id, '--csv'])
    except OSError:
        return None
    if r == 0:
        try:
            parts = std_out.split('\n', 1)
            limits = parts[1].strip().split(',')
            return limits
        except IndexError:
            return None
    else:
        return None

def build_stats_hash(limits_dict):
    return_list = []
    for limit, usage in LIMITS_LIST:
        try:
            if limits_dict[limit] == 0: #skip unlimited limit
                continue
            perl_hash = {
                'id': template_dict[usage]['id'],
                'description': template_dict[usage]['description'],
                'maximum' : limits_dict[limit],
                'usage' : limits_dict[usage]
            }
            if usage == 'cpu':
                l, u = convert_cpu(limits_dict[limit], limits_dict[usage])
                perl_hash['maximum'] = l
                perl_hash['usage'] = u
            if template_dict[usage]['formatter'] is not None:
                perl_hash['formatter'] = template_dict[usage]['formatter']
            if os.path.isfile(PERCENTS_STATS_MODE_FLAG):
                # Convert to Percentage if appropriate flag exist and remove formatters
                perl_hash['maximum'] = 100
                perl_hash['usage'] = convert_to_percent(limits_dict[limit], limits_dict[usage])
                perl_hash.pop('formatter', None)
            return_list.append(perl_hash)
        except KeyError: #skip missing limits
            pass
    return return_list


def get_config():
    """ Return pair show_stats and show_inode based on cpanel.config"""

    def parse_line(line):
        return bool(int(line.split('=')[1].strip()))

    ui_config = UIConfig().get_config()
    lve_hideuserstat = ui_config.get('uiSettings', {}).get('hideLVEUserStat')
    lve_showinodeusage = ui_config.get('inodeLimits', {}).get('showUserInodesUsage')
    file_usage = False
    CPANEL_CONFIG = '/var/cpanel/cpanel.config'
    try:
        cfg = get_file_lines(CPANEL_CONFIG)
        for line in cfg:
            if line.startswith('file_usage'):
                file_usage = parse_line(line)
    except IOError as e:
    # LVEMAN-1412. Use default value if config file has wrong permissions
        if e.errno != errno.EACCES:
            raise
    show_stats = not lve_hideuserstat
    show_inode = not file_usage and lve_showinodeusage
    return show_stats, show_inode


if __name__ == "__main__":
    try:
        lve_id = int(sys.argv[1])
        show_stats, show_inode = get_config()
        limits = None
        if show_stats:
            limits = get_lve_limits_for_uid(lve_id, show_inode)
        if limits is not None:
            print(json.dumps(build_stats_hash(limits)))
        else:
            print(json.dumps([]))
    except IndexError:
        print(json.dumps([]))

