Mini Shell

Direktori : /bin/
Upload File :
Current File : //bin/isppackagesreducer

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

# cloudlinux-license Utility to erase package limits in /etc/container/ve.cfg
# for ISPManager5 panels
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT

import sys
import subprocess
import simplejson as json
from future.utils import iteritems

import cldetectlib as detect
from copy import deepcopy
from clcommon import ClPwd
from collections import defaultdict
from cllimits.lib import exec_utility
from clcontrollib import ISPManagerGetPackagesException
from lvectllib import get_XML_cfg, get_globals, check_value
import lvectllib
import xml.dom.minidom as xml
import time

UTILITY_PATH = "/usr/sbin/lvectl"


def check_if_isp5_master():
    if detect.getCP() and detect.is_ispmanager():
        return detect.ispmanager5_is_master()
    print("Either this server hasn't ControlPanel or that ControlPanel isn't ISPManager5")
    sys.exit(1)


def get_user_packages_dict():
    """
    Is copy-pasted from clcontrollib.py source code before it was replaced with LU-256.
    Returns dict of {'uid': 'package_name'}
    """
    clpwd = ClPwd()
    dict_uid_package = {}
    if check_if_isp5_master():
        # ISP5 master
        try:
            # Get users list
            # Call mgrctl: /usr/local/mgr5/sbin/mgrctl -m ispmgr user -o json
            p = subprocess.Popen(['/usr/local/mgr5/sbin/mgrctl', '-m', 'ispmgr', 'user', '-o', 'json'],
                                 stdout=subprocess.PIPE)
            out, _ = p.communicate()
        except OSError as e:
            raise ISPManagerGetPackagesException("ERROR: Can't execute /usr/local/mgr5/sbin/mgrctl: %s" % str(e))
        try:
            mgrctl_out = json.loads(out)
        except ValueError:
            raise ISPManagerGetPackagesException("ERROR: mgrctl invalid output:\n%s" % out)
        if 'doc' not in mgrctl_out or 'elem' not in mgrctl_out['doc']:
            print("ControlPanel has no users, maybe current license is outdated")
            sys.exit(1)
        users_data = mgrctl_out['doc']['elem']
        for user_data in users_data:
            try:
                # determine user location
                user_location = user_data['loc']['$']
                # user_location example: 'localhost (95.164.68.74)'
                # We don't support non-local users
                if not user_location.startswith('localhost'):
                    continue
                # determine uid
                user_name = user_data['name']['$']
                uid = clpwd.get_uid(user_name)
                # determine package
                package_data = user_data['preset']
                if '$orig' in package_data:
                    # non-package user
                    package_name = package_data['$orig'].replace('#', '')
                else:
                    # Package user
                    package_name = package_data['$']
                # Add uid-package to dictionary
                dict_uid_package[uid] = package_name
            except IndexError:
                # Ignore record, if any index absent
                continue
    else:
        # ISP5 node
        try:
            # Get users list
            # Call mgrctl: /usr/local/mgr5/sbin/mgrctl -m ispmgrnode user
            # JSON output not supported on node, use TEXT output
            p = subprocess.Popen(['/usr/local/mgr5/sbin/mgrctl', '-m', 'ispmgrnode', 'user'],
                                 stdout=subprocess.PIPE)
            out, _ = p.communicate()
        except (OSError, ) as e:
            raise ISPManagerGetPackagesException("ERROR: Can't execute /usr/local/mgr5/sbin/mgrctl: %s" % str(e))
        # Parse output
        users_data = out.split('\n')
        for user_data in users_data:
            if not user_data:
                continue
            user_name = None
            try:
                # user parametres
                # user_params_list example:
                # ['name=bogdan1', 'fullname=', 'active=on', 'quota_total=0', 'quota_used=0',
                #  'limit_cpu=', 'limit_db=', 'limit_db_users=', 'limit_dirindex=index.html', 'index.php',
                #  'limit_emaildomains=', 'limit_emails=', 'limit_ftp_users=', 'limit_mailrate=',
                #  'limit_maxclientsvhost=', 'limit_memory=', 'limit_mysql_maxconn=',
                #  'limit_mysql_maxuserconn=', 'limit_mysql_query=', 'limit_mysql_update=',
                #  'limit_nginxlimitconn=', 'limit_php=', 'limit_process=', 'limit_webdomains=']
                user_params_list = user_data.split(' ')
                # print user_params_list
                for user_param in user_params_list:
                    if user_param.startswith('name'):
                        # username found
                        user_name = user_param.split('=')[1]
                        break
            except IndexError:
                # Ignore record, if any index absent
                pass
            # determine uid of user
            uid = clpwd.get_uid(user_name)
            # Add uid to dictionary.
            # Package name unavailable on ISP5 node, so create it manually by agreement with ISP Manager support
            dict_uid_package[uid] = "package_%s" % str(uid)
    return dict_uid_package


def construct_args(package_data, user_data):
    """
    Generates list of lvectl arguments to set limits

    :param dict package_data: Limits for given package as "limit_name": "limit_value"
    :param set user_data: Limits that user already has and we don't want to change
    :return: List of strings
    """
    res = []
    for key, value in iteritems(package_data):
        if key not in user_data:
            res.append("--%s=%s" % (key, value))
    return res

# Save XML config ve.cfg.timestamp
def save_ve_config_backup(XML):
    XML = XML.toprettyxml(encoding='utf-8',indent='', newl='')
    XML = XML.replace("\n",'').replace("\t",'')
    new_xml = xml.parseString(XML)
    ve_cfg_backup_name = '/etc/container/ve.cfg.' + str(int(time.time()))
    f = open(ve_cfg_backup_name, "wb")
    f.write(new_xml.toprettyxml(encoding='utf-8'))
    f.close()


def main():
    lvectllib.get_global_lock(True)
    get_XML_cfg()
    # lvectllib uses global variables, so we have to get their values
    global_vars = get_globals()
    ve_defaults = global_vars['ve_defaults']
    # If we have no packages' limits in /etc/container/ve.cfg
    if len(global_vars['ve_package']) == 0:
        print("Here isn't any package. No changes needed")
        return
    # Create backup for ve.cfg
    save_ve_config_backup(global_vars['ve_cfg'])
    # Get list of users and their packages from mgrctl utility
    dict_uid_package = get_user_packages_dict()
    # Group uids by package names
    packages_uids = defaultdict(list)
    for uid, package in dict_uid_package.items():
        packages_uids[package].append(uid)
    packages = {}
    to_delete = {}

    # For every package, get it's limits and put that to packages dict
    # store packages' names that exist in /etc/container/ve.cfg into to_delete dict
    for el in global_vars['ve_package']:
        pack_id = el.getAttribute('id')
        setup_data = {}
        if el.getAttribute('reseller'):
            to_delete[pack_id] = "--reseller=%s" % el.getAttribute('reseller')
        else:
            to_delete[pack_id] = ''
        if pack_id in list(packages_uids.keys()):
            check_value('ncpu', el, ve_defaults, setup_data)
            try:
                cpu = el.getElementsByTagName('cpu')[0].getAttribute('limit')
                if cpu.endswith("%") or cpu.endswith("mhz") or cpu.endswith("ghz"):
                    setup_data['speed'] = cpu
                else:
                    setup_data['cpu'] = cpu
            except (ValueError, IndexError, TypeError):
                pass
            check_value('io', el, ve_defaults, setup_data)
            check_value('mem', el, ve_defaults, setup_data)
            # mem limit is in 4K bloks, convert it to M
            if setup_data.get('mem', False):
                setup_data['mem'] = str(int(setup_data['mem'])*4//1024) + 'M'
            try:
                ep = int(el.getElementsByTagName('other')[0].getAttribute('maxentryprocs'))
                setup_data['ep'] = ep
            except (ValueError, IndexError, TypeError):
                pass
            check_value('nproc', el, ve_defaults, setup_data)
            check_value('pmem', el, ve_defaults, setup_data)
            # pmem limit is in 4K bloks, convert it to M
            if setup_data.get('pmem', False):
                setup_data['pmem'] = str(int(setup_data['pmem'])*4//1024) + 'M'
            check_value('iops', el, ve_defaults, setup_data)
            packages[pack_id] = deepcopy(setup_data)

    users = defaultdict(set)
    for el in global_vars['ve_lve']:
        user_set = set(['vmem',])
        for child in el.childNodes:
            if child.nodeType == child.ELEMENT_NODE:
                if child.nodeName == 'other':
                    user_set.update(['ep', 'maxEntryProcs'])
                elif child.nodeName == 'cpu':
                    user_set.update(['cpu', 'speed'])
                elif child.nodeName == 'mem':
                    user_set.add('mem')
                else:
                    user_set.add(child.nodeName)
        users[str(el.getAttribute('id'))].update(user_set)

    for uid, package in dict_uid_package.items():
        package_data = packages.get(package)
        if package_data is not None:
            # Call lvectl set uid --limit=value --limit=value to set limits from package to lve user
            limits = construct_args(package_data, users[str(uid)])
            if len(limits) > 0:
                exec_utility(UTILITY_PATH, ["set", str(uid)] + limits)

    for package, reseller in to_delete.items():
        # Call lvectl package-delete package_name to delete package limits from /etc/container/ve.cfg
        exec_utility(UTILITY_PATH, ["package-delete", package, reseller])


if __name__ == "__main__":
    main()

Zerion Mini Shell 1.0