# Copyright (c) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import json
import traceback
import subprocess
import configparser
import exec_command
from lve_diagnostic import get_cp
from dashboard_malfunctions import criu_settings_malfunctions
from dashboard_malfunctions import lsapi_settings_malfunctions
from dashboard_malfunctions import liblsapi_malfunctions
from stat_utils import cpanel_whmapi, plesk_bin_php_handler, dump_loaded_modules, dump_lsapi, query_strings
from stat_utils import get_da_domains, get_da_php_options, read_da_config, liblsapi_path, pretty_version_keys, count_domains, StatUtilsException
import selector_usage_lib
class ModLsapiStatException(Exception):
pass
def get(as_json=False):
"""
Return statistics data
:param as_json: return json string if True, dict otherwise
:return: statistics data dict(
`controlPanel`: EA3/EA4/Plesk/DirectAdmin/Unknown CP,
`criu`: dict(
`status`: running/stopped,
`version`: version
),
`domainStat`: dict(
`version`: `domains_num`,
...
) or dict(`error`: description) if some error occurred,
`lsapiConf`: dict(
lsapi_with_connection_pool: on/off,
lsapi_criu: on/off
)
) or its json-packed version
"""
error = None
stats = {}
control_panel = get_cp()
try:
if control_panel.name == 'cPanel':
if os.path.exists('/etc/cpanel/ea4/is_ea4'):
stats['domainStat'] = get_cpanel_ea4_stat()
else:
error = 'mod_lsapi domains stat is currently unsupported for EA3'
elif control_panel.name == 'Plesk':
stats['domainStat'] = get_plesk_stat()
elif control_panel.name == 'DirectAdmin':
stats['domainStat'] = get_da_stat()
else:
error = 'mod_lsapi domains stat is currently unsupported for {0}'.format(control_panel.name)
except ModLsapiStatException as e:
error = str(e)
if error is not None:
stats['domainStat'] = dict()
stats['domainStatError'] = error
stats['controlPanel'] = str(control_panel)
stats.update(get_lsapi_conf())
stats.update(get_crui_stat())
stats.update({'totalDomain': sum(stats['domainStat'].values())})
analyze_malfunctions(stats)
if as_json:
return json.dumps(stats, sort_keys=True)
else:
return stats
def get_cpanel_ea4_stat(lsapi_only=True, with_selector=True):
"""
Collect mod_lsapi statistics for cPanel EA4 through WHM API
:param lsapi_only: return only lsapi domains statistics if True, or full statistics if False
:param with_selector: take into account the statistics of php selector
:return: if lsapi_only return lsapi domains per version
stat dict(
`version`: `domains_num`,
...
)
else return full statistics per handler
stat dict(
`handler`: {`version`: `domains_num`, ... }
...
)
"""
domains_per_version = dict() # to store `version`: `domains_list`
handlers_stat = dict() # to store `handler`: {`version`: `domains_num`, ...}
domain_users = dict() # to store `domain`: `user` correspondence
try:
# get all vhosts along with versions
vhosts_data = cpanel_whmapi('php_get_vhost_versions').get('versions')
for vhost in vhosts_data:
domains_per_version.setdefault(vhost.get('version'), set()).add(vhost.get('vhost'))
domain_users[vhost.get('vhost')] = vhost.get('account')
# get handlers for versions
handlers_data = cpanel_whmapi('php_get_handlers').get('version_handlers')
version_handlers = dict([(h.get('version'), h.get('current_handler')) for h in handlers_data])
all_versions = list(version_handlers.keys())
# map {version: domains_list} to handlers, domains count in place
for ver, handler in version_handlers.items():
handlers_stat.setdefault(handler, dict()).update({ver: domains_per_version.get(ver, set())})
# reinspect handlers stat against selector
if with_selector:
s_checked_version_handlers = selector_usage_lib.ea4_selector_check(domains_per_version, domain_users, handlers_stat)
handlers_stat = s_checked_version_handlers
# update structure with versions, which are not used by one handler, e.g. `ver: 0 domains`
for h, v in handlers_stat.items():
v.update(dict.fromkeys(set(all_versions).difference(list(v.keys())), set()))
except (KeyError, TypeError, StatUtilsException):
raise ModLsapiStatException(''.join(traceback.format_exc().split('\n')))
# return only number of domains
return count_domains(handlers_stat, all_versions, lsapi_only)
def get_plesk_stat(lsapi_only=True, with_selector=True):
"""
Collect mod_lsapi statistics for Plesk
Collects lsapi domains ONLY
:param lsapi_only: return only lsapi domains statistics if True, or full statistics if False
:param with_selector: take into account the statistics of php selector
:return: if lsapi_only return lsapi domains per version dict(
`version`: `domains_num`
...
)
else return stats with handler dict(
`lsapi`: {`version`: `domains_num`, ... }
)
"""
handler_tmpl = 'alt-php{v}'
custom_version = 'alt-php56'
domain_version_stat = dict()
try:
all_handlers = plesk_bin_php_handler('list')
# on Plesk mod_lsapi is used only through handlers x-httpd-lsphp-*, which are added by --setup
lsphp_handlers = [h for h in all_handlers if 'lsphp' in h.get('id')]
for handler in lsphp_handlers:
handler_id = handler.get('id')
php_version = ''.join(handler.get('fullVersion').split('.')[:-1])
domains = set([d.get('domain') for d in plesk_bin_php_handler('get-usage', id=handler_id)])
# x-httpd-lsphp-custom domains are to be checked withon selector
if 'custom' in handler_id:
version_id = 'custom'
custom_version = handler_tmpl.format(v=php_version)
else:
version_id = handler_tmpl.format(v=php_version)
domain_version_stat[version_id] = domains
# PLACE SELECTOR CHECK HERE (should be done for custom handler)
if with_selector:
domain_version_stat = selector_usage_lib.plesk_selector_check(domain_version_stat, custom_version)
except (KeyError, TypeError, AttributeError, StatUtilsException, selector_usage_lib.SelectorStatException):
raise ModLsapiStatException(''.join(traceback.format_exc().split('\n')))
# return only number of domains
result_stat = {
'lsapi': {k: len(v) for k, v in domain_version_stat.items()}
}
return result_stat['lsapi'] if lsapi_only else result_stat
def get_da_stat(lsapi_only=True, with_selector=True):
"""
Collect lsapi domains statistics fro DirectAdmin
:param lsapi_only: return only lsapi domains statistics if True, or full statistics if False
:param with_selector: take into account the statistics of php selector
:return: if lsapi_only return lsapi domains per version
stat dict(
`version`: `domains_num`,
...
)
else return full statistics per handler
stat dict(
`handler`: {`version`: `domains_num`, ... }
...
)
"""
domain_conf_path = '/usr/local/directadmin/data/users/{user}/domains/{domain}.conf'
handler_stat = dict()
try:
# get php settings from option.conf
php_options = get_da_php_options()
php1_ver = php_options[1]['version']
php2_ver = php_options[2]['version']
php1_handler = php_options[1]['handler']
php2_handler = php_options[2]['handler']
php1_label = pretty_version_keys(php1_ver)
php2_label = pretty_version_keys(php2_ver)
# get user: domains correspondence
user_domains = get_da_domains()
joined = set()
[joined.update(v) for v in user_domains.values()]
# analyze options.conf settings for versions
if php2_ver == 'no':
# no secondary php set, assume all domains use primary
version_stat = {php1_label: joined}
handler_stat[php1_handler] = {php1_label: joined}
elif php1_ver == 'no':
# no primary php set, assume all domains use secondary
version_stat = {php2_label: joined}
handler_stat[php2_handler] = {php2_label: joined}
else:
version_stat = {php1_label: set(), php2_label: set()}
# if both php releases in options.conf are set, that means that DA PHP selector is enabled
for user, domains in user_domains.items():
# find php release settings for each domain
for domain in domains:
config_path = domain_conf_path.format(user=user, domain=domain)
try:
# try to find which version domain uses as primary
conf_parser, global_section = read_da_config(config_path)
php_setting = conf_parser.getint(global_section, 'php1_select')
version = pretty_version_keys(php_options[php_setting]['version'])
version_stat.get(version).add(domain)
except configparser.NoOptionError:
# means that domain do not use DA PHP selector and uses primary php version from options.conf
version_stat.get(php1_label).add(domain)
# if both php releases in options.conf are set, that means that DA PHP selector is enabled
# create per-handler statistics
if php1_handler == php2_handler:
handler_stat[php1_handler] = {php1_label: version_stat[php1_label],
php2_label: version_stat[php2_label]}
else:
handler_stat[php1_handler] = {php1_label: version_stat[php1_label],
php2_label: set()}
handler_stat[php2_handler] = {php2_label: version_stat[php2_label],
php1_label: set()}
if with_selector:
selector_updated = selector_usage_lib.da_selector_check(version_stat.get(php1_label), user_domains, php1_label)
handler_stat.get(php1_handler).update(selector_updated)
except (KeyError, TypeError, AttributeError, StatUtilsException):
raise ModLsapiStatException(''.join(traceback.format_exc().split('\n')))
# return only number of domains
return count_domains(handler_stat, [php1_label, php2_label], lsapi_only)
def get_crui_stat():
"""
Get criu service info
:return: dict(
`criu`: dict(
`status`: running/stopped,
`version`: version
)
)
"""
criu_version = ''.join(exec_command.exec_command('/usr/sbin/criu -V'))
try:
subprocess.check_call(['/sbin/service', 'criu', 'status'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
criu_service_status = 'running'
except subprocess.CalledProcessError:
criu_service_status = 'stopped'
return {
'criu': {
'status': criu_service_status,
'version': criu_version.split(' ')[1] if criu_version else 'not installed'
}
}
def get_lsapi_conf():
"""
Retrieve lsapi configuration:
- crui
- connection pool
and versions of module and library
:return: dict(
lsapiConf: dict(
lsapi_with_connection_pool: on/off,
lsapi_criu: on/off
),
modVersion: version,
libVersion: version
)
"""
# Plesk carries upstream apache 2.4.6, which says
# `Passing arguments to httpd using apachectl is no longer supported.`
# have to query with httpd instead of apachectl
apache_conf = dump_lsapi()
apache_modules = dump_loaded_modules()
mod_status = apache_modules.get('lsapi_module', None)
return {
'lsapiConf': {
'lsapi_criu': apache_conf.get('lsapi_criu', 'off'),
'lsapi_with_connection_pool': apache_conf.get('lsapi_with_connection_pool', 'off')
},
'modVersion': apache_conf.get('version', None),
'libVersion': query_strings(liblsapi_path(), 'liblsapi_version'),
'modStatus': 'disabled' if not mod_status else 'enabled'
}
def analyze_malfunctions(stats_dict):
"""
Detect configuration malfunctions and update resulting statistics dict accordingly
For now only criu malfunctions are presented
See malfunctions in dashboard_malfunctions.py module
:param stats_dict: resulting statistics dict
"""
malfunctions = list()
def add_malfunction(malfunc_dict, key):
try:
# try to detect one malfunction
malfunctions.append(malfunc_dict[key])
except KeyError:
# no malfunction found
pass
criu_settings = '{opt}_{serv}'.format(opt=stats_dict['lsapiConf']['lsapi_criu'],
serv=stats_dict['criu']['status'])
lsapi_settings = stats_dict['modStatus']
# for further extension of similar malfunctions:
# malfuncs = tuple of malfunctions
# keys = tuple of according keys
# for malfunc, k in zip(malfuncs, keys):
# add_malfunction(malfunc, k)
add_malfunction(liblsapi_malfunctions, stats_dict['libVersion'])
add_malfunction(lsapi_settings_malfunctions, lsapi_settings)
add_malfunction(criu_settings_malfunctions, criu_settings)
# no need in `malfunctions` field if there are no malfunctions
if not malfunctions:
return
else:
stats_dict.update({'malfunctions': malfunctions})
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]