????

Your IP : 3.16.212.27


Current Path : /usr/sbin/
Upload File :
Current File : //usr/sbin/lvectl

#!/opt/cloudlinux/venv/bin/python3 -bb
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT
#

from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import getopt
import logging
import sys
import pwd
import syslog
import os

from typing import Tuple, Text  # NOQA

from clcommon.utils import is_root_or_exit, get_cl_version
from cllimits_validator import LimitsValidator, ENTITY_USER, ENTITY_PACKAGE, ENTITY_RESELLER, ENTITY_DEFAULTS
from cllimitslib_v2 import DEFAULTS
from clveconfig import DEFAULT_PROVIDER, VE_CFG_PATH

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
from clcommon.lib.cledition import skip_without_lve
from cllvectl.log import get_synchronous_logger
import lveapi
import lvestat as LVEStat
from lve_utils.sentry import init_lve_utils_sentry_client

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

skip_without_lve()

logger = get_synchronous_logger('lvectl')

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')}

_WPOS_DAEMON_SIGHUP_FILE = '/var/lve/wpos_reload'


def _wpos_reload():
    """
    Create flag file for force sighup WPOS daemon
    """
    try:
        if os.path.exists('/etc/clwpos/clwpos.json'):
            open(_WPOS_DAEMON_SIGHUP_FILE, 'w').close()
    except (OSError, IOError,):
        pass


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 as 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('--enters-count                   enable limit for enters count (by default disabled), use only with command "limit"')
    if "cl6" not in get_cl_version():
        print('--io-and-memory                  enable limit for IO and memory (by default disabled), use only with command "limit"')
    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(f'--skip-update-cfg                skip updating LVE parameters in {VE_CFG_PATH} (use with set)')
    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[0] < 8):
            cpu_data = lve_commands.CPUINFO_DATA
            stat.cpu = stat.cpu * cpu_data[0]  # cpu_data[0] - Num_core
        elif (lve_ver[0] >= 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.maxsize, 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_from_speed(speed)
                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'] is 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
            if el == '--skip-update-cfg':
                set_data['skip-update-cfg'] = True
                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 parse_reseller(reseller_id):
    # type: (Text) -> Tuple[Text, int]
    """
    Try to obtain reseller name and LVP id from given info

    :param reseller_id: reseller name or LVP id
    """

    def _lvp_id_to_reseller_name(_lvp_id):
        # type: (int) -> Optional[Text]
        if _lvp_id <= 0:
            print_error_and_exit('Incorrect user id {}'.format(reseller_id))
        lve_commands.get_XML_cfg()
        reseller_name = lve_commands.lve.map.get_reseller_name(_lvp_id)
        return reseller_name

    def _reseller_name_to_lvp_id(name):
        # type: (Text) -> Optional[int]
        lve_commands.get_XML_cfg()
        _lvp_id = lve_commands.lve.map.get_reseller_id(name)
        return _lvp_id

    lvp_id = None
    reseller_name = None
    try:
        lvp_id = int(reseller_id)
    except ValueError:
        reseller_name = reseller_id
        lvp_id = _reseller_name_to_lvp_id(reseller_name)
    else:
        reseller_name = _lvp_id_to_reseller_name(lvp_id)
    if reseller_name is None or lvp_id is None:
        print_error_and_exit(
            "{} is neither an existing reseller's name nor an LVE id".format(reseller_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 = {}
        entity_type = None
        entity_id = None
        if o in ('set',):
            if args[1] == 'default':
                set_data['ve_id'] = 0
                entity_type = ENTITY_DEFAULTS
                entity_id = DEFAULTS
            else:
                set_data['ve_id'] = convert_to_int_or_exit(args[1], "LVE ID must be numeric")
                entity_type = ENTITY_USER if set_data['ve_id'] != 0 else ENTITY_DEFAULTS
                entity_id = set_data['ve_id'] if set_data['ve_id'] != 0 else DEFAULTS
        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'] = parse_reseller(args[1])
            if not lve_commands.lve.proc.exist_lvp(lvp_id):
                # We can not set up defaults for non-existing reseller container
                print_error_and_exit('LVP container does not exist', 1)
            entity_type = ENTITY_DEFAULTS
            entity_id = set_data['user']
        elif o == 'set-reseller':
            # TODO: LU-952
            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'] = parse_reseller(args[1])
                set_data['ve_id'] = lvp_id
                entity_type = ENTITY_RESELLER
                entity_id = set_data['user']
        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")
            entity_type = ENTITY_USER
            entity_id = set_data['ve_id']
        # 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)
        # TODO: LU-952
        if '--all' not in args:
            # Validation of settable limits. LU-919
            limits_validator = LimitsValidator()
            result, messages = limits_validator.validate_input_limits(
                entity_type,
                entity_id,
                set_data
            )
            if not result:
                print_error_and_exit(
                    'You\'re trying to set invalid LVE limits. {}'.format(' '.join(messages)),
                )
        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()
        return set_data


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)
    entity_id = set_data['ve_id']
    reseller = set_data.get('reseller_name', DEFAULT_PROVIDER)
    limits_validator = LimitsValidator()
    result, messages = limits_validator.validate_input_limits(
        ENTITY_PACKAGE,
        entity_id,
        set_data,
        reseller=reseller
    )
    if not result:
        print_error_and_exit(
            'You\'re trying to set invalid LVE limits. {}'.format(' '.join(messages)),
        )
    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:
        logger.info('Starting lvectl with arguments: %s', sys.argv)
        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',
                                    'destroy-and-recreate-all'])
    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 range(0, len(args), 2):
            if i + 1 < len(args):
                opts.append((args[i], args[i + 1]))
            else:
                opts.append((args[i], ''))

    executed = False
    for command, arg in opts:
        arg: str
        if command in ('-v', '--version', 'version'):
            print(PKG_VERSION)
            sys.exit(0)
        if command 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 command in ('-h', '--help', 'help'):
            print_usage()
            sys.exit(0)
        if command in ('start', '--start'):
            executed = True
            lve_commands.get_XML_cfg()
            lve_commands.lve_start()
        if command in ('sync-map',):
            check_support_reseller_limits()
            sync_map()
            lve_commands.write_effective_cache()
            sys.exit(0)
        if command in ('set-binary', '--set-binary'):
            lve_commands.get_global_lock(True)
            lve_commands.set_binary(arg)
            send_json_ok()
            sys.exit(0)
        if command in ('del-binary', '--del-binary'):
            lve_commands.get_global_lock(True)
            lve_commands.del_binary(arg)
            send_json_ok()
            sys.exit(0)
        if command in ('list-binaries', '--list-binaries'):
            lve_commands.list_binaries()
            sys.exit(0)
        if command in ('load-binaries', '--load-binaries'):
            lve_commands.load_binaries()
            send_json_ok()
            sys.exit(0)
        if command in ('reload-binaries', '--reload-binaries'):
            lve_commands.reload_binaries()
            send_json_ok()
            sys.exit(0)
        if command 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)
            # Cached effective limits will be written to file once per operation.
            lve_commands.write_effective_cache()
        if command 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)
            _wpos_reload()
        if command in ('apply', '--apply'):
            executed = True
            if len(arg) != 0:
                if arg == 'all':
                    lve_commands.lve_apply_all()
                    if lve_commands.lve.reseller_limit_supported():
                        lve_commands.lve.sync_map()
                    lve_commands.write_effective_cache(reset=True)
                    send_json_ok()
                    sys.exit(0)
                else:
                    lve_id = convert_to_int_or_exit(arg, '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")
                    lve_commands.write_effective_cache()
                    send_json_ok()
        if command in ('limit', '--limit'):
            executed = True

            flags = lveapi.LVE_NO_UBC | lveapi.LVE_NO_MAXENTER

            supported_flags = {
                "enters-count": lveapi.LVE_NO_MAXENTER
            }
            if "cl6" not in get_cl_version():
                supported_flags["io-and-memory"] = lveapi.LVE_NO_UBC

            flags_count = 0
            for arg in args[1:]:
                if not arg.startswith("--"):
                    break
                flag = arg[2:]
                if flag in supported_flags:
                    flags &= ~supported_flags[flag]
                    flags_count += 1
                else:
                    print_error_and_exit(f"No such flag: {arg}. Please, use only {list(supported_flags.keys())}.")

            try:
                PID = convert_to_int_or_exit(args[flags_count + 1], "PID must be numeric.")
            except IndexError:
                print_error_and_exit("You have to specify PID.")

            try:
                VE = convert_to_int_or_exit(args[flags_count + 2], "LVE ID must be numeric.")
            except IndexError:
                print_error_and_exit("You have to specify LVE ID.")

            lve_commands.limit_pid(VE, PID, flags)
            send_json_ok()

        if command in ('release', '--release'):
            executed = True
            PID = convert_to_int_or_exit(arg, 'PID must be numeric')
            lve_commands.release_pid(PID)
            send_json_ok()
        if command in ('list', '--list'):
            executed = True
            limits_print('all')
        if command in ('list-user', '--list-user'):
            executed = True
            limits_print('all', True)
        if command in ('paneluserslimits', '--paneluserslimits'):
            executed = True
            if len(arg) == 0:
                lve_commands.paneluserslimits()
            # LU-530
            else:
                reseller = str(arg)
                lve_commands.paneluserslimits(reseller=reseller)
        if command in ('paneluserlimits', '--paneluserlimits'):
            executed = True
            if len(arg) == 0:
                print_error_and_exit('paneluserlimits takes USER ID of control panel')
            user_id = convert_to_int_or_exit(arg, "bad USER ID")
            if user_id >= 0:
                lve_commands.paneluserslimits(userid=user_id)
            else:
                print_error_and_exit("bad USER ID")
        if command in ('limits', '--limits'):
            executed = True
            if len(arg) == 0:
                print_error_and_exit("limits takes LVE ID or 'all' argument")
            if arg == 'default':
                arg = 0
            if arg == 'all':
                limits_print(arg)
                sys.exit(0)
            lve_id = convert_to_int_or_exit(arg, "bad LVE ID")
            if lve_id >= 0:
                limits_print(lve_id)
            else:
                print_error_and_exit("bad LVE id")
        if command in ('list-reseller',):
            executed = True
            check_support_reseller_limits()
            if len(arg) == 0 or arg in ('--bytes',):
                limits_print('all', lvp=True)  # print limits for all resellers
            elif '--with-name' == arg:
                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, _ = parse_reseller(arg)
                limits_print(lvp_id, lvp=True)

        if command == 'destroy-and-recreate-all':
            # NOTE(vlebedev): This is a workaround/hack to avoid the two-step process of first destroying all lves
            #                 and only then recreating them all with proper limits. This proofed to be problematic
            #                 due to the fact that destroy (and possibly create/apply) might hang indefinitely and
            #                 the server will be left with limits already reset for all but not yet re-applied for
            #                 some due to lvectl transitive hanging.
            #                 Technically, this is an equivalent of `lvectl destroy all` and `lvectl apply all`
            #                 executed sequentially, but in a single command and this additionanl safety net.
            executed = True
            lve_commands.lve_destroy_and_recreate_all()
            if lve_commands.lve.reseller_limit_supported():
                lve_commands.lve.sync_map()
            lve_commands.write_effective_cache(reset=True)
            _wpos_reload()

        if command in ('destroy', '--destroy'):
            executed = True
            if len(arg) == 0:
                print_error_and_exit('destroy takes 2 arguments')
            if arg == 'default' or arg == '0':
                print_error_and_exit('cannot destroy default LVE')
            if arg == 'all':
                lve_commands.lve_destroy('all')
            else:
                lve_id = convert_to_int_or_exit(arg, 'BAD LVE ID')
                if lve_id > 0:
                    lve_commands.lve_destroy(int(arg))
                else:
                    print_error_and_exit("Bad LVE ID")
            _wpos_reload()
        if command in ('delete', '--delete', 'remove-reseller'):
            executed = True
            lve_commands.get_global_lock(True)
            if len(arg) == 0 or arg == 'all':
                print_error_and_exit("wrong arguments for --delete")
            if arg == '0' or arg == 'default':
                print_error_and_exit("cannot delete default LVE")
            if command == 'remove-reseller':
                check_support_reseller_limits()
                lvp_id, reseller_name = parse_reseller(arg)
                lve_commands.disable_reseller_limits(reseller_name=reseller_name, lvp_id=lvp_id)
                send_json_ok()
            else:
                delete_lve(arg)
            # Not resetting because not all effective limits would be calculated during this
            # lvectl run, and write_effective_cache with reset replaces the cache file data with
            # those specifically.
            lve_commands.write_effective_cache()
        if command in ('delete-user', '--delete-user'):
            executed = True
            lve_commands.get_global_lock(True)
            delete_lve_by_name(arg)
        if command in ('--set',):
            executed = True
            print_usage()
        if command 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 command 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 command 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 command in ('set', 'set-user'):
            executed = True
            lve_commands.get_global_lock(True)
            lve_set_data = process_lve_set(args, command)
            # We want to keep the effective limit cache matched to the ve.cfg file,
            # so if ve.cfg is not updated, neither should the cache.
            # "skip-update-cfg" might not be in the dict, so we get()
            if not lve_set_data.get('skip-update-cfg', False):
                lve_commands.write_effective_cache()
        if command in ('set-reseller', 'set-reseller-default'):
            executed = True
            check_support_reseller_limits()
            lve_commands.get_global_lock(True)
            lve_set_data = process_lve_set(args, command)
            if not lve_set_data.get('skip-update-cfg', False):
                lve_commands.write_effective_cache()
        if command in ('package-set',):
            executed = True
            lve_commands.get_global_lock(True)
            process_lve_package_set(args)
            lve_commands.write_effective_cache()
        if command 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)
            lve_commands.write_effective_cache()
        if command in ('package-delete', '--package-delete'):
            executed = True
            lve_commands.get_global_lock(True)
            if len(arg) == 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(arg, reseller[0].split("=")[1])
            else:
                lve_commands.plan_delete(arg)
            # lve_apply_all is called under the hood, so it's OK to reset the cache file after
            lve_commands.write_effective_cache(reset=True)
            send_json_ok()
        if command in ('package-list', '--package-list'):
            executed = True
            lve_commands.get_packages_list()
        if command in ('all-package-list',):
            executed = True
            lve_commands.get_all_packages_list()
        if command in ('all-user-list',):
            executed = True
            lve_commands.all_users_limits()
        if command 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__":
    init_lve_utils_sentry_client('lvectl')
    try:
        main()
    except (NoSuchLvp, lveapi.PyLveError, CPAPIException) as e:
        print_error_and_exit(str(e), exit_code=1)