#!/opt/alt/python27/bin/python2.7

#
# Copyright CloudLinux Zug GmbH 2010-2016 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT
#


import getopt
import sys
import pwd
import syslog

from clcommon.utils import is_root_or_exit
is_root_or_exit()   # check here or imports will fail

import clcommon
from clcommon.clproc import NoSuchLvp, LIMIT_LVP_ID
from clcommon.cpapi.cpapiexceptions import CPAPIException
import lveapi
import lvestat as LVEStat

import lvectllib as lve_commands
from lvectllib import _page_to_memory_or_bytes
import cldetectlib as detect
from lve_utils import PKG_VERSION


DO_NOT_EXIT = 999

PRINT_FORMATS = {True: {
    8: '{"ID":"%s","SPEED":"%s","CPU":"%s","PMEM":"%s","VMEM":"%s","EP":"%s","NPROC":"%s","IO":"%s","IOPS":"%s"}',
    6: '{"ID":"%s","SPEED":"%s","CPU":"%s","PMEM":"%s","VMEM":"%s","EP":"%s","NPROC":"%s","IO":"%s"}',
    4: '{"ID":"%s","SPEED":"%s","CPU":"%s","VMEM":"%s","EP":"%s","IO":"%s"}'}, False: {
    8: "%8s%8s%8s%8s%8s%8s%8s%8s",
    6: "%8s%8s%8s%8s%8s%8s%8s",
    4: "%8s%8s%8s%8s%8s"}}
PRINT_HEADERS = {8: ('ID', 'SPEED', 'PMEM', 'VMEM', 'EP', 'NPROC', 'IO', 'IOPS'),
                 6: ('ID', 'SPEED', 'PMEM', 'VMEM', 'EP', 'NPROC', 'IO'),
                 4: ('ID', 'SPEED', 'VMEM', 'EP', 'IO')}


def print_error(message, print_usage_flag=False):
    if lve_commands.JSON:
        lve_commands.json_format(
            lve_commands.MULTI_FORMAT,
            ['ERROR', message]
        )
    else:
        err_message = "error: %s" % message
        sys.stderr.write("%s\n" % err_message)
        if print_usage_flag:
            print_usage()


def print_error_and_exit(message, exit_code=-1, print_usage_flag=False):
    print_error(message, print_usage_flag)
    if exit_code != DO_NOT_EXIT:
        sys.exit(exit_code)

# GET VERSION from /proc/lve/list
lve_ver = clcommon.get_lve_version()
if lve_ver[0] is None:
    print_error_and_exit(lve_ver[1],1)

LVE_VERSION = lve_ver[0]

LVE_VERSION = max(key for key in PRINT_HEADERS.keys() if key <= LVE_VERSION) # use max supported version

try:
    lve_commands.init(LVE_VERSION)
except Exception, e:
    print_error_and_exit(e, 1)

# Check if JSON output required or old format without iops

command_prompt = sys.argv[:]
for i, el in enumerate(sys.argv):
    if el in ('json', '--json'):
        lve_commands.set_json(True)
        lve_commands.set_bytes('--bytes' in sys.argv)
        command_prompt.pop(i)
        if '--bytes' in sys.argv:
            command_prompt.pop(command_prompt.index('--bytes'))
        continue
    if el in ('no-iops', '--no-iops'):   #check no-iops key in command prompt and drop LVE_VERSION to 6 if this key founded and LVE_VERSION is 8
        if LVE_VERSION == 8:
            lve_commands.NOIOPS = True   # STUPID HACK. Remove with iops release
        command_prompt.pop(i)
        continue

syslog.openlog('lve-utils')


def print_usage():
    print 'lvectl version ' + PKG_VERSION
    print 'usage: lvectl command [veid] [options]'
    print 'commands:'
    print 'apply                            apply config settings to specified LVE'
    print 'apply all                        apply config settings to all the LVEs'
    print 'apply-many                       to apply LVE limits to multiple distinct LVEs (uids of users are read from stdin)'
    print 'set                              set parameters for a LVE and/or create a LVE'
    print 'set-reseller                     create a LVP (resellers) container move LVE there'
    print 'set-reseller-default             set default limits for users inside LVP'
    print 'sync-map                         load/update reseller:user map for correct support resellers limits'
    print 'set-user                         set parameters for a LVE and/or create a LVE using username instead of ID'
    print 'list                             list loaded LVEs'
    print 'list-reseller                    list loaded LVPs,  --with-name for display reselers name'
    print 'list-user                        list loaded LVEs, display username instead of user id'
    print 'limits                           show limits for loaded LVEs'
    print 'all-user-list                    show limits for loaded LVEs with packages info'
    print 'delete                           delete LVE and set configuration for that LVE to defaults'
    print 'remove-reseller                  delete LVP container and record in config, move LVE containers to host container'
    print 'delete-user                      delete LVE and set configuration for that user to defaults'
    print 'destroy                          destroy LVE (configuration file remains unchanged)'
    print 'destroy all                      destroy all LVE (configuration file remains unchanged)'
    print 'destroy-many                     to destroy LVE limits to multiple distinct LVEs (uids of users are read from stdin)'
    print 'package-set                      set LVE parameters for a package'
    print 'package-list                     list LVE parameters for packages'
    print 'reseller-package-list            list LVE parameters for resellers packages'
    print 'all-package-list                 list LVE parameters for all packages (whether it is resseler package or not )'
    print 'package-delete                   delete LVE parameters for a package'
    print 'paneluserslimits [reseller]      show current user\'s limits for control panel. Reseller name is optional parameter'
    print 'paneluserlimits                  show current limits for one user-id of user in control panel'

    print 'limit                            limit PID into specified LVE. Parameters PID LVE_ID'
    print 'release                          release PID from LVE. Parameters PID'
    print 'set-binary                       add binary to be run inside LVE on execution'
    print 'del-binary                       remove binary from being run inside LVE on execution'
    print 'list-binaries                    list all binaries to be run inside LVE on execution'
    print 'load-binaries                    load binaries (used on startup) from config file'
    print 'reload-binaries                  re-load list of binaries from config file'
    print 'help (-h)                        show this message'
    print 'version (-v)                     version number'
    print 'lve-version                      lve version number'
    print 'options:'
    print '--cpu=N                          limit CPU usage; (deprecated. Use --speed)'
    print '--speed=N%                       limit CPU usage in percentage; 100% is one core '
    print '--speed=Nmhz\ghz                 limit CPU usage in mhz\ghz'
    print '--io=N                           define io limits (KB/s)'
    if LVE_VERSION > 4:
        print '--nproc=N                        limit number of processes'
        print '--pmem=N                         limit physical memory usage for aplications inside LVE'
    if LVE_VERSION > 6:
        print '--iops=N                         limit io operations per second'
    print '--mem=N                          mem alias for vmem (deprecated)'
    print '--vmem=N                         limit virtual memory for applications inside LVE'
    print '--maxEntryProcs=N                limit number of entry processes'
    print '--save                           save configuration settings (use with set) (deprecated)'
    print '--save-all-parameters            save all parameters even if they match with defaults settings'
    print '--json                           returns result of command json formatted'
    print '--bytes                          show all limits in bytes. Works only with --json'
    print '--unlimited                      set all limits to unlimited'
    print '--reseller                       specify reseller name who owns the package/uid; use with package-set/package-delete/set command'
    print '--skip-custom                    skip calling custom script inside lvectl'
    print """--default=A,B,C                  set limits to default value (will delete lve first)
                                 available limits are either from %s
                                 or 'all' to reset all limits""" % ("cpu, io, mem/vmem, ep/maxEntryProcs, nproc, pmem" if LVE_VERSION > 4
                                                 else "cpu, io, mem/vmem, ep/maxEntryProcs, nproc, pmem, iops" if  LVE_VERSION > 6
                                                 else "cpu, io, mem/vmem, ep/maxEntryProcs")
    print '--save-username                  save username in the config file. This parameter is used in conjunction with set-user'
    print ''


def validate_options(options):
    opts = {'ncpu', 'speed', 'cpu', 'io', 'mem', 'vmem', 'ep', 'maxEntryProcs'}
    if LVE_VERSION > 4:
        opts.update(['nproc', 'pmem'])
    if LVE_VERSION > 6:
        opts.update(['iops'])
    if len(options) == 1 and "all" in options:
        options = opts
    # check all options are valid
    if len(set(options) - opts) > 0:
        print_error_and_exit(
            "%s are not available options.\n\navailable options are:\n%s" % (
                ', '.join(set(options) - opts),
                '\n'.join(opts))
        )
    # map options to set_data keys
    opts_map = {'ncpu': 'ncpu', 'speed': 'cpu', 'cpu': 'cpu', 'io': 'io',
                'mem': 'mem', 'vmem': 'mem', 'ep': 'ep', 'maxEntryProcs': 'ep',
                'nproc': 'nproc', 'pmem': 'pmem', 'iops': 'iops'}
    return set([opts_map[k] for k in options])


def invalid_option(o, a):
    print 'Invalid value for option %s: %s' % (o, a)
    print_usage()
    sys.exit(1)


def id_to_name(ve_id, display_user):
    if ve_id == 0:
        return 'default'
    elif ve_id == LIMIT_LVP_ID:
        return 'limit'
    if display_user:
        try:
            return pwd.getpwuid(ve_id).pw_name
        except KeyError:
            pass
    return str(ve_id)


def get_line_data(line, stats, display_user, with_reseller_name=False, prefix=''):
    ID = id_to_name(line, display_user)
    if with_reseller_name and ID.isdigit():  # for show reseller name with id
        ID += ':' + (lve_commands.lve.map.get_reseller_name(int(ID)) or 'N/A')  # N/A if can't find lvp_id reseller name
    ID = prefix + ID
    data = ()

    stats_line = stats[line]  # type: lvestat.LVEStat
    if lve_commands.JSON:
        if lve_commands.NOIOPS and LVE_VERSION == 8:
            data = (ID, stats_line.cpu, lve_commands.speed_to_old_cpu(stats_line.cpu), _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io)
        elif LVE_VERSION == 8:
            data = (ID, stats_line.cpu, lve_commands.speed_to_old_cpu(stats_line.cpu), _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io, stats_line.liops)
        elif LVE_VERSION == 6:
            data = (ID, stats_line.cpu, lve_commands.speed_to_old_cpu(stats_line.cpu), _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io)
        elif LVE_VERSION == 4:
            data = (ID, stats_line.cpu, lve_commands.speed_to_old_cpu(stats_line.cpu), _page_to_memory_or_bytes(stats_line.lmem),
                    stats_line.lep, stats_line.io)
    else:
        if lve_commands.NOIOPS and LVE_VERSION == 8:
            data = (ID, stats_line.cpu, _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io)
        elif LVE_VERSION == 8:
            data = (ID, stats_line.cpu, _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io, stats_line.liops)
        elif LVE_VERSION == 6:
            data = (ID, stats_line.cpu, _page_to_memory_or_bytes(stats_line.lmemphy),
                    _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep, stats_line.lnproc, stats_line.io)
        elif LVE_VERSION == 4:
            data = (ID, stats_line.cpu, _page_to_memory_or_bytes(stats_line.lmem), stats_line.lep,
                    stats_line.io)

    return data


def limits_print(ve_id, display_user=False, lvp=False, with_reseller_name=False):
    if lvp:
        if ve_id == 'all':
            stats = parseLVPList()
        else:
            try:
                stats = parseLVEList(ve_id)
            except IOError:  # if /proc/lve/resellers/lvp{ve_id}/list no present
                stats = dict()
    else:
        stats = dict()
        if lve_commands.lve.reseller_limit_supported():
            for lvp_id_ in parseLVPList():  # scan all /proc/lve/resellers/lvp{ID}/list
                stats.update(parseLVEList(lvp_id_, ignore_id=LIMIT_LVP_ID))
        stats.update(parseLVEList())

    if lve_commands.NOIOPS and LVE_VERSION == 8:
        out_format = PRINT_FORMATS[lve_commands.JSON][6]
    else:
        out_format = PRINT_FORMATS[lve_commands.JSON][LVE_VERSION]

    if lve_commands.JSON:
        result = '{"data":['
        separator = ","
    else:
        if lve_commands.NOIOPS and LVE_VERSION == 8:
            result = out_format % PRINT_HEADERS[6] + "\n"
        else:
            result = out_format % PRINT_HEADERS[LVE_VERSION] + "\n"
        separator = "\n"
    if ve_id == 'all' or lvp:
        result += separator.join((out_format % get_line_data(line, stats, display_user, with_reseller_name=with_reseller_name) for line in stats))
    else:
        for line in stats:
            if line == ve_id:
                result += out_format % get_line_data(line, stats, display_user)
    if lve_commands.JSON:
        result += ']}'
    else:
        result += separator
    try:
        print result
    except IOError:
        pass


def _parse_lve_lines(lines, id_=None, ignore_id=None):
    stats = {}
    for line in lines:
        stat = LVEStat.LVEStat(line, lve_ver[0])
        if id_ is not None and stat.id != id_:  # filter by id if need
            continue
        if ignore_id is not None and stat.id == ignore_id:
            continue
        if ( lve_ver < 8 ):
            cpu_data = lve_commands.CPUINFO_DATA
            stat.cpu = stat.cpu * cpu_data[0]  # cpu_data[0] - Num_core
        elif ( lve_ver >= 8 ):
            stat.cpu = stat.cpu / 100
        stats[stat.id] = stat
    return stats


def parseLVEList(lvp_id=0, id_=None, ignore_id=None):
    with open(lve_commands.lve.proc.get_list_path(lvp_id=lvp_id)) as lines:
        lines.readline()
        return _parse_lve_lines(lines, id_=id_, ignore_id=ignore_id)


def parseLVPList():
    # Remove from LVE all resellers, wich absent in panel
    lve_commands.remove_absent_resellers()
    stats = {}
    for lvp_id in lve_commands.lve.proc.lvp_id_list():
        try:
            stats[lvp_id] = parseLVEList(lvp_id=lvp_id, id_=LIMIT_LVP_ID)[LIMIT_LVP_ID]
        except KeyError:
            pass
    return stats


def delete_lve(a, lvp_id=0):
    if not str(a).isdigit():
        print_error_and_exit("LVE id must be number")
    if delete_lve_int(int(a)):
        return
    print_error_and_exit("Invalid LVE id")


def send_json_ok():
    if lve_commands.JSON:
        lve_commands.json_format(lve_commands.SINGLE_FORMAT, ['OK'])


def delete_lve_int(lve_id):
    if lve_id > 0:
        lve_commands.lve_delete(lve_id)
        send_json_ok()
        return True


def delete_lve_by_name(a):
    try:
        if delete_lve_int(pwd.getpwnam(a).pw_uid):
            return
    except KeyError:
        pass
    print_error_and_exit("invalid username given")


def convert_to_int_or_exit(val, error_message, exit_code=-1):
    try:
        return int(val)
    except ValueError:
        print_error_and_exit(error_message, exit_code)


def parse_int_param(el, set_data, param_name, min_val, set_limits, max_val=sys.maxint, alias=None):
    if not alias:
        alias = param_name
    try:
        set_data[param_name] = convert_to_int_or_exit(el.split('--' + alias + '=')[1],
                                                      'Non integer %s limit provided' % alias)
        if max_val >= set_data[param_name] >= min_val:
            set_limits.add(param_name)
            return True
        else:
            print_error_and_exit("%s limit is out of range" % alias)
    except IndexError:
        pass
    return False


def parse_set_data(args, set_data):
    args.pop(0)
    args.pop(0)
    args2 = []
    if len(args) > 0 and not args[0].startswith("--"):
        raise Exception("Wrong args")
    skip_next = False
    args_length = len(args)
    for idx, el in enumerate(args):
        # used current value as value for previous key
        if skip_next:
            skip_next = False
            continue
        if el.startswith("--"):
            # --key=value format
            if el.find("=") > 0:
                args2.append(el)
            elif idx < (args_length - 1):
                # next element is key again
                if (args[idx + 1]).startswith("--"):
                    args2.append(el)
                # either value
                else:
                    args2.append(el + "=" + args[idx + 1])
                    skip_next = True
            # no more elements, so it is key
            elif idx == args_length - 1:
                args2.append(el)
    args = args2
    set_limits = set([])
    set_options = [set(['default']), set(['unlimited']),
                   set(['cpu', 'speed', 'ncpu', 'vmem', 'mem', 'pmem', 'io',
                        'maxEntryProcs', 'ep', 'nproc', 'iops'])]
    for el in args:
        parsed = False
        if el.startswith('--'):
            if el.startswith('--cpu'):
                print 'Warning: --cpu option had been deprecated, use --speed instead'
                if 'speed' in set_limits:
                    print_error_and_exit('Error: used both cpu and speed limit.')
                parsed = parsed or parse_int_param(el, set_data, 'cpu', 1, set_limits, max_val=100)
            if el.startswith('--speed'):
                if 'cpu' in set_limits:
                    print_error_and_exit('Error: used both cpu and speed limit.')
                speed = el.split('--speed=')[1]
                speedlimit = lve_commands.convert_to_kernel_format(speed,True)
                if speedlimit is None:
                    print_error_and_exit('Speed limit error',1,1)
                    parsed = False
                else:
                    parsed = True
                set_data['cpu'] = speed
                set_limits.add('speed')
            parsed = parsed or parse_int_param(el, set_data, 'ncpu', 0, set_limits)
            if el.startswith('--mem') or el.startswith('--vmem'):
                set_data['mem'] = clcommon.memory_to_page(el.split('=')[1])
                if set_data['mem'] == None:
                   print_error_and_exit("Invalid vmem value")
                parsed = parsed or True
                set_limits.add('mem')
            parsed = parsed or parse_int_param(el, set_data, 'io', 0, set_limits)
            parsed = parsed or parse_int_param(el, set_data, 'ep', 0, set_limits)
            parsed = parsed or parse_int_param(el, set_data, 'ep', 0, set_limits, alias='maxEntryProcs')
            parsed = parsed or parse_int_param(el, set_data, 'nproc', 0, set_limits)
            if el.startswith('--pmem'):
                set_data['pmem'] = clcommon.memory_to_page(el.split('=')[1])
                if set_data['pmem'] == None:
                    print_error_and_exit("Invalid pmem value")
                parsed = parsed or True
                set_limits.add('pmem')
            parsed = parsed or parse_int_param(el, set_data, 'iops', 0, set_limits)
            if el == '--save':
                print_error('--save option had been deprecated')
                parsed = True
            if el == '--save-all-parameters':
                set_data['save'] = True
                parsed = True
            if el == '--unlimited':
                set_data['cpu'] = 100
                set_data['ncpu'] = 0
                set_data['mem'] = 0
                set_data['io'] = 0
                set_data['ep'] = 0
                set_data['pmem'] = 0
                set_data['nproc'] = 0
                set_data['iops'] = 0
                set_limits.add('unlimited')
                parsed = True
            if el.startswith('--default='):
                if set_data['ve_id'] == 0:
                    print_error_and_exit("Can not reset default ve to default")
                opts_to_default = set([p for p in el.split('=')[1].split(',') if p != ''])
                set_data['set-default'] = validate_options(opts_to_default)
                set_limits.add('default')
                parsed = True
            if el.startswith('--reseller='):
                reseller_name = el.split("=")[1]
                set_data['reseller_name'] = reseller_name
                parsed = True
            # LU-400
            if el == '--skip-custom':
                parsed = True
        else:
            print_error_and_exit("wrong arguments for command: set", print_usage_flag=True)
        if not parsed:
            print_error_and_exit("unrecognized option or bad limit given %s" % el)
        if sum(bool(set_limits.intersection(s)) for s in set_options) > 1:
            print_error_and_exit("can not combine options: %s" % ', '.join(set_limits))


def pars_reseller(id_):
    try:
        lvp_id = int(id_)
        if lvp_id <= 0:
            print_error_and_exit('Incorect user id {}'.format(id_))
        lve_commands.get_XML_cfg()
        reseller_name = lve_commands.lve.map.get_reseller_name(lvp_id)
    except ValueError:
        reseller_name = id_
        lve_commands.get_XML_cfg()
        lvp_id = lve_commands.lve.map.get_reseller_id(id_)
    if reseller_name is None:
        print_error_and_exit('User {} is not reseller'.format(id_))
    return lvp_id, reseller_name


def process_lve_set(args, o):
    # variable lvp_id using for identificate operations with lve top container;
    # None - operations with default lve container;
    # list[lve_id1, lve_id2, ..., lveid_n] - operations with lve top container included simple lve containers id
    lvp_id = 0
    # args example set limitt to uid:
    #  args:  ['set', '1023', '--reseller=res', '--iops=2222']
    #   o == 'set'
    if len(args) == 1:
        print_error_and_exit('No LVE ID provided')
    else:
        set_data = {}
        if o in ('set', ):
            if args[1] == 'default':
                set_data['ve_id'] = 0
            else:
                set_data['ve_id'] = convert_to_int_or_exit(args[1], "LVE ID must be numeric")
        elif o == 'set-reseller-default':
            set_data['ve_id'] = 0
            lve_commands.get_XML_cfg()  # load config or load reseller_name:reseller_id map
            lvp_id, set_data['user'] = pars_reseller(args[1])
            if not lve_commands.lve.proc.exist_lvp(lvp_id):
                # we can not setup defaults for exist reseller container
                print_error_and_exit('LVP container exist', 1)
        elif o == 'set-reseller':
            if args[1] == '--all':  # for anable lvp containers all resellers
                lvp_id = list()
                lve_commands.get_XML_cfg()  # load config or load reseller_name:reseller_id map
                for reseller_name in lve_commands.lve.map.resellers():
                    lvp_id.append((reseller_name, lve_commands.lve.map.get_reseller_id(reseller_name)))
            else:
                lvp_id, set_data['user'] = pars_reseller(args[1])
                set_data['ve_id'] = lvp_id
        else:
            if '--save-username' in args:
                set_data['save-username'] = True
                args.remove('--save-username')
            try:
                set_data['ve_id'] = pwd.getpwnam(args[1]).pw_uid
            except KeyError:
                print_error_and_exit("Invalid user name")
        # initializing 'save' flag.
        # then it will be set True in parse_set_data if "--save-all-parameters" argument is given
        # LU-574
        # Force to save the limits for reseller (not for user with the same name, but for reseller as virtual entity)
        set_data['save'] = True if o == 'set-reseller' else False
        parse_set_data(args, set_data)
        if isinstance(lvp_id, list):  # if lvp list use for many lvp
            for reseller_name, lvp_id_ in lvp_id:
                set_data_ = dict(set_data)
                set_data_.update({'user': reseller_name, 've_id': lvp_id_})
                lve_commands.lve_set(set_data_, lvp_id=lvp_id_)
        else:
            # Set limits for user's LVE
            if not lve_commands.lve_set(set_data, lvp_id=lvp_id):
                # Error lve_id does not match reseller
                print_error_and_exit(
                    "User id '%s' is not owned by '%s'" % (
                        set_data['ve_id'],
                        set_data.get('reseller_name', 'root')
                    )
                )
        send_json_ok()


def process_lve_package_set(args, lve_command_func=lve_commands.package_set):
        if len(args) == 1:
            print_error_and_exit('No package ID provided')
        else:
            set_data = {'ve_id': args[1], 'save': True}
        parse_set_data(args, set_data)
        if 'reseller_name' in set_data:
            if not lve_commands.reseller_package_set(set_data):
                print_error_and_exit("Reseller '%s' does not have package '%s'" % (set_data['reseller_name'], set_data['ve_id']))
        else:
            lve_command_func(set_data)
        send_json_ok()


def check_support_reseller_limits():
    """
    Check support resellers limits
    If not enabled or not support exit end print diagnostic message
    """
    # error code 38 - ENOSYS Function not implemented
    # check supported kernel
    if not lve_commands.lve.is_lve10():
        print_error_and_exit(
            'Please, update your LVE to 1.5.x to use this command',
            exit_code=38
        )
    # check supported clcommon
    panel_name = detect.getCPName()
    # check supported control panel
    if not lve_commands.lve.is_panel_supported():
        print_error_and_exit(
            'Reseller limits is not supported on "{}" control panel now'.format(panel_name),
            exit_code=38
        )


def sync_map():
    """
    load/update reseller:user map
    :return:
    """
    lve_commands.get_XML_cfg()
    lve_commands.lve.sync_map()
    lve_commands.remove_absent_users()
    lve_commands.remove_absent_resellers()


def main(argv=None):
    opts = ()
    args = ()
    try:
        opts, args = getopt.getopt(argv or command_prompt[1:], 'vh:',
                                   ['help', 'version', 'apply=', 'list', 'list-reseller','list-user',
                                    'limits=', 'all-user-list', 'destroy=', 'delete=', 'remove-reseller=',
                                    "delete-user=", 'set=', 'set-reseller=', 'set-user=', 'ubc',
                                    'package-set=', 'package-set-ext=','package-delete=',
                                    'package-list', 'reseller-package-list', 'all-package-list', 'sync-map',
                                    'start', 'lve-version', 'destroy-many', 'apply-many',
                                    'paneluserslimits', 'limit=', 'release=',
                                    'set-binary', 'del-binary', 'list-binaries',
                                    'load-binaries', 'reload-binaries', 'paneluserlimits'])
    except getopt.GetoptError:
        print_error_and_exit('unknown or missing command', 1, True)

    if len(opts) == 0 and len(args) == 0:
        print_error_and_exit('unknown or missing command', 1, True)
        # hack for using line like lvectl command argument
    if len(opts) == 0:
        opts = []
        for i in xrange(0, len(args), 2):
            if i + 1 < len(args):
                opts.append((args[i], args[i + 1]))
            else:
                opts.append((args[i], ''))

    executed = False
    for o, a in opts:
        if o in ('-v', '--version', 'version'):
            print PKG_VERSION
            sys.exit(0)
        if o in ('--lve-version', 'lve-version'):
            if lve_commands.JSON:
                lve_commands.json_format(
                    lve_commands.MULTI_FORMAT,
                    ['OK', LVE_VERSION]
                )
            else:
                print LVE_VERSION
            sys.exit(0)
        if o in ('-h', '--help', 'help'):
            print_usage()
            sys.exit(0)
        if o in ('start', '--start'):
            executed = True
            lve_commands.get_XML_cfg()
            lve_commands.lve_start()
        if o in ('sync-map',):
            check_support_reseller_limits()
            sync_map()
            sys.exit(0)
        if o in ('set-binary', '--set-binary'):
            lve_commands.get_global_lock(True)
            lve_commands.set_binary(a)
            send_json_ok()
            sys.exit(0)
        if o in ('del-binary', '--del-binary'):
            lve_commands.get_global_lock(True)
            lve_commands.del_binary(a)
            send_json_ok()
            sys.exit(0)
        if o in ('list-binaries', '--list-binaries'):
            lve_commands.list_binaries()
            sys.exit(0)
        if o in ('load-binaries', '--load-binaries'):
            lve_commands.load_binaries()
            send_json_ok()
            sys.exit(0)
        if o in ('reload-binaries', '--reload-binaries'):
            lve_commands.reload_binaries()
            send_json_ok()
            sys.exit(0)
        if o in ('apply-many', '--apply-many'):
            executed = True
            users = sys.stdin.readlines()
            if len(users) == 0:
                print_error_and_exit('user list is empty')
            lve_commands.apply_many(users)
        if o in ('destroy-many', '--destroy-many'):
            executed = True
            users = sys.stdin.readlines()
            if len(users) == 0:
                print_error_and_exit('user list is empty')
            lve_commands.destroy_many(users)
        if o in ('apply', '--apply'):
            executed = True
            if len(a) != 0:
                if a == 'all':
                    lve_commands.lve_apply_all()
                    if lve_commands.lve.reseller_limit_supported():
                        lve_commands.lve.sync_map()
                    send_json_ok()
                    sys.exit(0)
                else:
                    lve_id = convert_to_int_or_exit(a, 'Invalid LVE ID')
                    if lve_id >= 0:
                        lve_commands.get_XML_cfg()
                        if lve_commands.lve.reseller_limit_supported():
                            lve_commands.get_XML_cfg(lvp_id=lve_commands.lve.lve2lvp(lve_id))
                        lve_commands.lve_apply(lve_id)
                        if lve_commands.lve.reseller_limit_supported():
                            lve_commands.lve.sync_map()
                    else:
                        print_error_and_exit("Invalid LVE Id")
                    send_json_ok()
        if o in ('limit', '--limit'):
            executed = True
            PID = convert_to_int_or_exit(a, "PID must be numeric")
            VE = 0
            if (len(args) == 2) or (len(args) == 0):
                print_error_and_exit('LVE ID is missing')
            else:
                VE = convert_to_int_or_exit(args[len(args) - 1], "LVE ID must be numeric")
            lve_commands.limit_pid(VE, PID)
            send_json_ok()

        if o in ('release', '--release'):
            executed = True
            PID = convert_to_int_or_exit(a, 'PID must be numeric')
            lve_commands.release_pid(PID)
            send_json_ok()
        if o in ('list', '--list'):
            executed = True
            limits_print('all')
        if o in ('list-user', '--list-user'):
            executed = True
            limits_print('all', True)
        if o in ('paneluserslimits', '--paneluserslimits'):
            executed = True
            if len(a) == 0:
                lve_commands.paneluserslimits()
            # LU-530
            else:
                reseller = str(a)
                lve_commands.paneluserslimits(reseller=reseller)
        if o in ('paneluserlimits', '--paneluserlimits'):
            executed = True
            if len(a) == 0:
                print_error_and_exit('paneluserlimits takes USER ID of control panel')
            user_id = convert_to_int_or_exit(a, "bad USER ID")
            if user_id >= 0:
                lve_commands.paneluserslimits(userid=user_id)
            else:
                print_error_and_exit("bad USER ID")
        if o in ('limits', '--limits'):
            executed = True
            if len(a) == 0:
                print_error_and_exit("limits takes LVE ID or 'all' argument")
            if a == 'default':
                a = 0
            if a == 'all':
                limits_print(a)
                sys.exit(0)
            lve_id = convert_to_int_or_exit(a, "bad LVE ID")
            if lve_id >= 0:
                limits_print(lve_id)
            else:
                print_error_and_exit("bad LVE id")
        if o in ('list-reseller', ):
            executed = True
            check_support_reseller_limits()
            if len(a) == 0 or a in ('--bytes', ):
                limits_print('all', lvp=True)  # print limits for all resellers
            elif '--with-name' == a:
                lve_commands.get_XML_cfg()  # for loading reseller_name<=>reseller_id map form ve.cfg
                limits_print('all', lvp=True, with_reseller_name=True)
            else:
                lvp_id, _ = pars_reseller(a)
                limits_print(lvp_id, lvp=True)

        if o in ('destroy', '--destroy'):
            executed = True
            if len(a) == 0:
                print_error_and_exit('destroy takes 2 arguments')
            if a == 'default' or a == '0':
                print_error_and_exit('cannot destroy default LVE')
            if a == 'all':
                lve_commands.lve_destroy('all')
            else:
                lve_id = convert_to_int_or_exit(a, 'BAD LVE ID')
                if lve_id > 0:
                    lve_commands.lve_destroy(int(a))
                else:
                    print_error_and_exit("Bad LVE ID")
        if o in ('delete', '--delete', 'remove-reseller'):
            executed = True
            lve_commands.get_global_lock(True)
            if len(a) == 0 or a == 'all':
                print_error_and_exit("wrong arguments for --delete")
            if a == '0' or a == 'default':
                print_error_and_exit("cannot delete default LVE")
            if o == 'remove-reseller':
                check_support_reseller_limits()
                lvp_id, reseller_name = pars_reseller(a)
                lve_commands.disable_reseller_limits(reseller_name=reseller_name, lvp_id=lvp_id)
            else:
                delete_lve(a)
        if o in ('delete-user', '--delete-user'):
            executed = True
            lve_commands.get_global_lock(True)
            delete_lve_by_name(a)
        if o in ('--set', ):
            executed = True
            print_usage()
        if o in ('--ubc', 'ubc'):
            executed = True
            print 'UBC option is no longer available. If you would like to disable virtual memory limit,' \
                  ' please use --vmem=0 option'
        if o in ('--package-set',):
        # not accepted with dashes
            executed = True
            print_usage()
        # LU-400
        # call custom script for Endurance than we set/delete limits for user
        if o in ('set', 'set-user', 'delete', 'delete-user', 'package-set', 'package-set-ext', 'package-delete'):
            if '--skip-custom' not in args:
                lve_commands.call_endurance_custom_script(args=args)
        if o in ('set', 'set-user'):
            executed = True
            lve_commands.get_global_lock(True)
            process_lve_set(args, o)
        if o in ('set-reseller', 'set-reseller-default'):
            executed = True
            check_support_reseller_limits()
            lve_commands.get_global_lock(True)
            process_lve_set(args, o)
        if o in ('package-set',):
            executed = True
            lve_commands.get_global_lock(True)
            process_lve_package_set(args)
        if o in ('package-set-ext',):
            executed = True
            lve_commands.get_global_lock(True)
            process_lve_package_set(args, lve_command_func=lve_commands.package_set_ext)
        if o in ('package-delete', '--package-delete'):
            executed = True
            lve_commands.get_global_lock(True)
            if len(a) == 0:
                print_error_and_exit("wrong arguments for delete command")
            reseller = [el for el in args if el.startswith("--reseller=")]
            if len(reseller) == 1:
                lve_commands.reseller_plan_delete(a, reseller[0].split("=")[1])
            else:
                lve_commands.plan_delete(a)
            send_json_ok()
        if o in ('package-list', '--package-list'):
            executed = True
            lve_commands.get_packages_list()
        if o in ('all-package-list',):
            executed = True
            lve_commands.get_all_packages_list()
        if o in ('all-user-list',):
            executed = True
            lve_commands.all_users_limits()
        if o in ('reseller-package-list', '--reseller-package-list'):
            executed = True
            lve_commands.get_resellers_packages_list()

    if not executed:
        print_error_and_exit('unknown or missing command', exit_code=1, print_usage_flag=True)


if __name__ == "__main__":
    try:
        main()
    except (NoSuchLvp, lveapi.PyLveError, CPAPIException) as e:
        print_error_and_exit(str(e), exit_code=1)
