#coding:utf-8

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2022 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 os

import clcommon
import lvectllib
from clcommon.clconfpars import load_fast
from cllimits.clquota_lib import ClQuotaLib
from clcommon.cpapi import resellers, is_panel_feature_supported, Feature
from clcommon.lib import MySQLGovernor


PKG_DIR = "/var/cpanel/packages"
LVE_CONFIG = "/etc/container/ve.cfg"
QUOTA_CONFIG = "/etc/container/cl-quotas.dat"
MYSQLGOV_PACKAGES_CONFIG = "/etc/container/governor_package_limit.json"

LIMITS_NAMES = (
    ('inodes_soft', 'INODES soft'),
    ('inodes_hard', 'INODES hard'),
)
LVE_LIMITS_NAMES = (
    ('ncpu', 'NCPU'),
    ('cpu', 'SPEED'),
    ('io', 'IO'),
    ('mem', 'Vmem'),
    ('pmem', 'Pmem'),
    ('nproc', 'NPROC'),
    ('iops', 'IOPS'),
    ('ep', 'Max Entry Procs'),
)
GOVERNOR_LIMITS_NAMES = (
    ('mysql_cpu', 'MySQL CPU'),
    ('mysql_io', 'MySQL IO'),
)


def is_reseller_package(package):
    reseller_list = resellers()
    for reseller in reseller_list:
        if package.startswith(reseller + '_'):
            return True
    return False


def check_data(data, name, def_value):
    if data is None:
        if name == "CPU":
            def_value = f"{def_value // 100}%"  # convert kernel version speed limit to human readable format
        data = def_value
    return data


def _get_limit_defaults():
    limit_defaults = {'inodes_soft': 0, 'inodes_hard': 0}
    if is_panel_feature_supported(Feature.LVE):
        limit_defaults |= lvectllib.ve_defaults.copy()
    if is_panel_feature_supported(Feature.GOVERNOR):
        limit_defaults |= {'mysql_cpu': 0, 'mysql_io': 0}
    return limit_defaults


def _get_limit_names():
    limit_names = LIMITS_NAMES
    if is_panel_feature_supported(Feature.LVE):
        limit_names += LVE_LIMITS_NAMES
    if is_panel_feature_supported(Feature.GOVERNOR):
        limit_names += GOVERNOR_LIMITS_NAMES
    return limit_names


def _handle_limit(limit_name, limit_value, message, limit_defaults):
    if limit_name == 'cpu':
        limit_value = clcommon.validate_cpu(limit_value)
        limit_value = check_data(limit_value, message, f"{limit_defaults[limit_name] // 100}%")
    elif limit_name in ('pmem', 'mem', 'vmem'):
        limit_value = clcommon.memory_to_page(limit_value, 0)
        limit_value = check_data(limit_value, message, limit_defaults[limit_name])
    elif limit_name in ('inodes_soft', 'inodes_hard', 'mysql_io'):
        limit_value = clcommon.validate_int(limit_value, min_val=-1)
        limit_value = check_data(limit_value, message, limit_defaults[limit_name])
    elif limit_name == 'mysql_cpu':
        limit_value = limit_value.replace('%', '')
        limit_value = clcommon.validate_int(limit_value, min_val=-1)
        limit_value = check_data(limit_value, message, limit_defaults[limit_name])
    else:
        limit_value = clcommon.validate_int(limit_value)
        limit_value = check_data(limit_value, message, limit_defaults[limit_name])
    return limit_value


def _categorize_limits(limit_name, limit_value, lve_data, governor_limits, quota_data):
    if limit_name in ('ncpu', 'cpu', 'io', 'mem', 'pmem', 'nproc', 'iops', 'ep'):
        lve_data[limit_name] = limit_value
    elif limit_name in ('mysql_cpu', 'mysql_io'):
        governor_limits[limit_name] = limit_value
    else:
        quota_data[limit_name] = limit_value


def _update_lve_limits(lve_data):
    if is_panel_feature_supported(Feature.LVE):
        lvectllib.package_set(lve_data)


def _update_quota_data(package, quota_data):
    if quota_data:
        ClQuotaLib().set_package_limits_independently(package, quota_data)
        os.utime(LVE_CONFIG)
    elif os.path.exists(QUOTA_CONFIG):
        os.utime(QUOTA_CONFIG)


def _update_governor_limits(package, governor_limits):
    if not is_panel_feature_supported(Feature.GOVERNOR):
        return

    governor_lib = MySQLGovernor()
    if not governor_lib.is_governor_present():
        return

    governor_lib.set_package_limits(
        package,
        int(governor_limits['mysql_cpu']),
        int(governor_limits['mysql_io']),
    )
    if os.path.exists(MYSQLGOV_PACKAGES_CONFIG):
        os.utime(MYSQLGOV_PACKAGES_CONFIG)


def save_package(package):
    """
    Save cpanel package info to LVE config and cl-quotas.dat
    """
    package_path = os.path.join(PKG_DIR, package)
    if not os.path.isfile(package_path):
        return None

    if is_reseller_package(package):
        return None  # we skip all resellers packages

    package_params = load_fast(package_path)
    extention_list = package_params.get('_PACKAGE_EXTENSIONS', '').split()
    if "lve" not in extention_list:   # skip packages without lve extention
        return None

    lve_data = {'ve_id': package, 'save': True}
    quota_data = {}
    governor_limits = {'mysql_cpu': None, 'mysql_io': None}

    limit_names = _get_limit_names()
    limit_defaults = _get_limit_defaults()

    for limit_name, message in limit_names:
        limit_value = package_params.get(f'lve_{limit_name}', '')
        if limit_name in governor_limits and limit_value == 'DEFAULT':
            governor_limits[limit_name] = 0
        if limit_value == 'DEFAULT':
            continue
        limit_value = _handle_limit(limit_name, limit_value, message, limit_defaults)
        _categorize_limits(limit_name, limit_value, lve_data, governor_limits, quota_data)

    _update_lve_limits(lve_data)
    _update_quota_data(package, quota_data)
    _update_governor_limits(package, governor_limits)

    return True
