Mini Shell
# General class, implementing common methods, using all cpapi plugins by default
import os
from clcommon.features import Feature
from clcommon.utils import exec_utility
from collections import defaultdict
from clcommon import ClPwd
from clcommon.clcustomscript import lvectl_custompanel_script
from clcommon.cpapi.GeneralPanel import GeneralPanelPluginV1
from clcommon.cpapi.cpapiexceptions import CPAPIExternalProgramFailed
# This flag means that we use enchanced custom long script.
# With keys, which aren't described in official documentation.
USE_ENCHANCED_CUSTOM_LONG_SCRIPT = os.environ.get('USE_ENCHANCED_CUSTOM_LONG_SCRIPT') == '1'
class PanelPlugin(GeneralPanelPluginV1):
def __init__(self):
super().__init__()
self._custom_script_name = lvectl_custompanel_script()
self._cp_name = "Unknown"
def getCPName(self):
"""
Return panel name
:return:
"""
return self._cp_name
def get_cp_description(self):
"""
Retrieve panel name and it's version
:return: dict: { 'name': 'panel_name', 'version': 'panel_version', 'additional_info': 'add_info'}
or None if can't get info
"""
if self._custom_script_name is not None:
return {'name': self._cp_name, 'version': '0', 'additional_info': None}
# panel detect fail if custom script not found
return None
# proxy to avoid arguments-differ
def cpinfo(self, cpuser=None, keyls=('cplogin', 'package', 'mail', 'reseller', 'dns', 'locale'),
search_sys_users=True):
return self._cpinfo(cpuser, keyls=keyls, search_sys_users=search_sys_users)
def _cpinfo(self, cpuser=None, keyls=('cplogin', 'package', 'mail', 'reseller', 'dns', 'locale', 'uid'),
search_sys_users=True, raise_exc=False):
"""
Retrives specified info about panel users
:param str|unicode|list|tuple|None cpuser: user login
:param keyls: List or cortege of data which is necessary to obtain the user,
values can be:
cplogin - name/login user control panel
mail - Email users
reseller - name reseller/owner users
locale - localization of the user account
package - User name of the package
dns - domain of the user
userid - uid
:param search_sys_users:
:param raise_exc: hack for clquota that raises exception in case of exit code != 0
:return: returns a tuple of tuples of data in the same sequence as specified keys in keylst
Examples:
cpinfo('cltest1')
(('cltest1', 'default', '', 'root', 'cltest1.com', 'en'),)
cpinfo()
(('res1usr1', 'res1_pack1', '', 'res1', 'res1usr1.com', 'en'),
('res1', 'default', '', 'root', 'res1.com', 'en'),
('res2', 'default', '', 'res2', 'res2.com', 'en'),
('cltest1', 'default', '', 'root', 'cltest1.com', 'en'),
('system', 'undefined', None, 'root', '', 'en'))
:rtype: tuple
"""
try:
if USE_ENCHANCED_CUSTOM_LONG_SCRIPT:
all_users_data = super().list_users(raise_exc=raise_exc)
else:
# all_users_data Example:
# {1000: 'Package1', 1001: 'BusinessPackage'}
all_users_data = self.list_all(raise_exc=raise_exc)
except (OSError, IOError, IndexError, AttributeError, CPAPIExternalProgramFailed):
all_users_data = {}
cl_pwd = ClPwd()
# Build full users data dict
users_dict = defaultdict(dict)
for uid, value in all_users_data.items():
try:
user_name = cl_pwd.get_names(uid)[0]
except ClPwd.NoSuchUserException:
# user with uid absent in system, use some fictive name
# We use : in username, because system does not allow this symbol in username
user_name = f'::{uid}::'
if isinstance(value, dict):
package = value['package']
reseller = value['reseller']
else:
package = value
reseller = ''
try:
users_dict[user_name] = {'cplogin': user_name,
'package': package,
'mail': None,
'reseller': reseller,
'dns': None,
'locale': None,
'uid': uid,
}
except Exception:
# we are ignoring all errors
pass
out_list = []
user_name_list = []
if isinstance(cpuser, str):
user_name_list = [cpuser]
elif isinstance(cpuser, (list, tuple)):
user_name_list = list(cpuser)
elif cpuser is None:
user_name_list = list(users_dict.keys())
for user_name in user_name_list:
# for case if users_dict has fictive user names
if user_name not in users_dict:
continue
user_data = users_dict[user_name]
user_data_list = []
for data_key in keyls:
user_data_list.append(user_data[data_key])
out_list.append(tuple(user_data_list))
return tuple(out_list)
# inherited implementation does not work because
# old plugin does not contain resellers
def reseller_package_by_uid(self, user_id):
"""
Retrieves reseller name and package name by uid
:param user_id: User id
:return: Cortege: (Reseller_name, Package_name)
"""
package_name = ''
try:
_, stdout = exec_utility(self._custom_script_name, ['--userid='+str(user_id)])
package_name = stdout.split('\n').pop(0)
except (OSError, IOError, IndexError, AttributeError):
pass
return '', package_name
# This is reseller method.
# Key `--list-resellers-packages` of custom long script isn't
# described in official documentation, so we use this method
# only in tests and implement the key only in tests
# for real client's custom long script we return empty result
def resellers_packages(self, raise_exc=False):
"""
Return dictionary, contains available resellers packages, grouped by resellers
:return: Dictionary.
Example:
{'res1': ['BusinessPackage', 'UltraPackage', 'Package'],
'res2': ['SimplePackage', 'Package'] }
"""
if not USE_ENCHANCED_CUSTOM_LONG_SCRIPT:
return {}
try:
return super().resellers_packages(raise_exc=raise_exc)
except (OSError, IOError, IndexError, AttributeError, CPAPIExternalProgramFailed):
return {}
# This is reseller method.
# Key `--list-users` of custom long script isn't
# described in official documentation, so we use this method
# only in tests and implement the key only in tests
# for real client's custom long script we return empty result
def resellers(self):
"""
Generates a list of resellers in the control panel
:return: list/tuple of cpusers registered in the control panel
:rtype: tuple
:raise: NotSupported
"""
if not USE_ENCHANCED_CUSTOM_LONG_SCRIPT:
return tuple()
resellers_list = [value['reseller'] for value in self.list_users().values()]
return tuple(set(resellers_list))
def get_admin_emails_list(self):
"""
Gets admin emails list
:rtype: List
:return: List: ['admin1@mail.com', 'admin2@mail.com' ]
"""
return []
def docroot(self, domain):
"""
Return document root for domain
:return: Cortege: (document_root, owner)
"""
return ()
def userdomains(self, cpuser):
"""
Return domain and document root pairs for control panel user
first domain is main domain
:param str|unicode cpuser: user login
:rtype: List
:return: list of tuples (domain_name, documen_root)
"""
return []
def homedirs(self):
"""
Detects and returns list of folders contained the home dirs of users of the cPanel
:rtype: List
:return: list of folders, which are parent of home dirs of users of the panel
"""
return []
def reseller_users(self, resellername=None):
"""
Return reseller users
:param resellername: reseller name; autodetect name if None
:rtype: List
:return list[str]: user names list
"""
return []
def reseller_domains(self, resellername=None):
"""
Return reseller users and their main domains
:param resellername: reseller name; autodetect name if None
:rtype: List
:return dict[str, str]: pairs user <==> domain
"""
return {}
def get_user_login_url(self, domain):
"""
Get login url for current panel;
:type domain: str
:rtype: str
:return: Panel login URL
"""
return ""
def get_reseller_id_pairs(self):
"""
Get dict reseller => id
Optional method for panels without hard
link reseller <=> system user
:rtype: dict[str,int] - {'res1': id1}
:return:
"""
return {}
# This is reseller method.
# Key `--list-reseller-users` of custom long script isn't
# described in official documentation, so we use this method
# only in tests and implement the key only in tests
# for real client's custom long script we return empty result
def get_reseller_users(self, reseller):
if not USE_ENCHANCED_CUSTOM_LONG_SCRIPT:
return {}
try:
return super().get_reseller_users(reseller)
except (OSError, IOError, IndexError, AttributeError, CPAPIExternalProgramFailed):
return {}
def list_users(self, raise_exc=False):
"""
Return list of <userid package reseller> triples
:rtype: dict
:return: Dictionary. Example:
{1000: {'reseller': '', 'package': 'Package1'},
1001: {'reseller': '', 'package': 'BusinessPackage'},
}
"""
packages_users = defaultdict(dict)
users_info = self._cpinfo(keyls=('uid', 'reseller', 'package'), raise_exc=True)
for uid, reseller, package in users_info:
packages_users[uid] = {'reseller': reseller, 'package': package}
return packages_users
def get_unsupported_cl_features(self) -> tuple[Feature, ...]:
"""
Return list of cloudlinux features that can be used
on current control panel.
"""
# empty list mostly for historical reasons
return tuple()
Zerion Mini Shell 1.0