Mini Shell
#!/opt/cloudlinux/venv/bin/python3 -bb
# -*- coding: utf-8 -*-
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
from __future__ import absolute_import
import argparse
import os
import sys
import logging
from typing import List, Optional
from cllimits.cagefs_lib import CageFs
from clwpos.constants import ALT_PHP_PREFIX, CLWPOS_VAR_DIR
from clwpos.php.base import PHP
from clwpos.php.alt_php import (
create_generic_php,
get_alt_php_versions,
update_selector_built_in_extensions,
disable_selector_extensions
)
from clwpos.logsetup import setup_logging
from clwpos.optimization_features import OBJECT_CACHE_FEATURE
from clwpos.feature_suites import is_module_visible_for_user
from clwpos.utils import (
PhpIniConfig,
create_pid_file,
acquire_lock,
)
from clwpos.object_cache.redis_utils import (
filter_php_versions_with_not_loaded_redis
)
logger = setup_logging(
caller_name='enable_redis_for_alt_php',
file_level=logging.DEBUG,
logfile_path='/var/log/clwpos/enable_redis_for_alt_php.log',
)
awp_ini = 'acceleratewp.ini'
# marker for those clients, who do not want us modifying global ini
DO_NOT_MODIFY_PHP_GLOBAL_MARKER = f'{CLWPOS_VAR_DIR}/admin/do_not_modify_global_php.flag'
def remove_custom_ini(version: Optional[str], all_ini: Optional[bool]):
if not os.path.exists(DO_NOT_MODIFY_PHP_GLOBAL_MARKER):
logger.debug('Nothing to do')
return
supported_version = supported_alt_php(version)
for php_version in supported_version:
php_ini_config = PhpIniConfig(php_version, custom_logger=logger)
php_ini_config.remove_custom_ini(f'etc/php.d/{awp_ini}', all_ini)
def update_user_ini(php_version: PHP):
php_ini_config = PhpIniConfig(php_version, custom_logger=logger)
modules_to_sync = php_ini_config.get_ini_content(f'etc/php.d/{awp_ini}')
php_ini_config.update_user_ini('acceleratewp.ini', modules_to_sync)
def enable_redis_extension(php_version: PHP):
"""
Enable 'redis' extension for specified alt-php version
by adding 'extension=redis.so' into php.ini file.
Also enable 'json' and 'igbinary' extensions if exist which are reqired by 'redis' extension.
At the same time:
- disable extension in 'php.d.all/<extension>.ini' to deny user
to enable extension via PHP Selector.
- disable extensions in 'php.d/default.ini' to eliminate duplicate loading of extensions.
"""
php_ini_config = PhpIniConfig(php_version, custom_logger=logger)
redis_required_modules = php_ini_config.get_required_modules('etc/php.d.all/redis.ini')
if not redis_required_modules:
logger.debug('Redis extension is not present in the system, skip')
return
modules = ['json'] + redis_required_modules
modules.sort(key=lambda x: x.endswith('redis'))
module_status_map = {}
for module in modules:
if module == 'redis':
module_status_map['redis'] = php_ini_config.disable_modules(f'etc/php.d.all/redis.ini',
redis_required_modules)
else:
module_status_map[module] = php_ini_config.disable_modules(f'etc/php.d.all/{module}.ini', [module])
logger.debug(f'Final current modules status: {module_status_map}')
existing_modules = [
module
for module, exists in module_status_map.items()
if exists
]
logger.debug(f'Existing extensions: {existing_modules}')
php_ini_config.disable_modules('etc/php.d/default.ini', existing_modules)
non_loaded_modules = [
module for module in existing_modules
if not php_version.is_extension_loaded(module)
]
if not os.path.exists(DO_NOT_MODIFY_PHP_GLOBAL_MARKER):
php_ini_config.enable_modules('etc/php.ini', non_loaded_modules)
else:
logger.debug('Marker exists: %s, adding those modules to custom awp ini: %s',
DO_NOT_MODIFY_PHP_GLOBAL_MARKER,
str(non_loaded_modules))
php_ini_config.create_custom_ini(f'etc/php.d/{awp_ini}', non_loaded_modules)
disable_selector_extensions(non_loaded_modules, php_version.version, logger)
def supported_alt_php(version: Optional[str]):
alt_php_versions = [create_generic_php(f'{ALT_PHP_PREFIX}{version}')] if version else get_alt_php_versions()
supported_alt_php_versions = [
version for version in alt_php_versions if OBJECT_CACHE_FEATURE.is_php_supported(version)
]
return supported_alt_php_versions
def get_php_versions_to_enable_redis(version: Optional[str]) -> List[PHP]:
"""
Return list of php version supported by AccelerateWP
and on which redis is presented but not enabled.
"""
supported_alt_php_versions = supported_alt_php(version)
return filter_php_versions_with_not_loaded_redis(supported_alt_php_versions)
def parse_args() -> argparse.Namespace:
"""
Parse arguments for enable_redis_for_alt_php.py utility.
"""
parser = argparse.ArgumentParser(
prog='enable_redis_for_alt_php',
description='Utility for enabling redis extension for alt-php',
)
parser.add_argument(
'--version',
type=int,
help='Enable redis extension for specific alt-php version',
)
parser.add_argument(
'--no-rebuild',
action='store_true',
help='Run without rebuilding users\' alt_php.ini files',
)
parser.add_argument(
'--remove-custom-ini',
action='store_true',
help='Run to remove custom acceleratewp.ini'
)
parser.add_argument('--all', action='store_true', help='Run to remove ALL custom acceleratewp.ini')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-f', '--force', action='store_true')
opts, _ = parser.parse_known_args()
if opts.verbose:
logging.basicConfig(level=logging.DEBUG)
return opts
def main():
"""
Enable redis extension for alt-php versions supported by AccelerateWP.
"""
opts = parse_args()
if not is_module_visible_for_user(OBJECT_CACHE_FEATURE) and not opts.force:
print('Run only if object caching is allowed for at least one user')
sys.exit(0)
cagefs = CageFs()
if opts.remove_custom_ini:
remove_custom_ini(opts.version, opts.all)
sys.exit(0)
with acquire_lock(os.path.join('/var/run', ALT_PHP_PREFIX), attempts=None):
php_versions_to_enable_redis = get_php_versions_to_enable_redis(opts.version)
# we don't continue if there are no alt-php versions (supported by WPOS)
# with redis presented but not loaded
if not php_versions_to_enable_redis:
logger.info('All php versions have redis extension installed and active')
sys.exit(0)
with create_pid_file(ALT_PHP_PREFIX):
for php_version in php_versions_to_enable_redis:
enable_redis_extension(php_version)
# iter all supported, to sync users ini in case we already enabled in global .ini
if os.path.exists(DO_NOT_MODIFY_PHP_GLOBAL_MARKER):
for php_version in supported_alt_php(None):
update_user_ini(php_version)
# we don't continue if there are no alt-php versions (supported by WPOS)
# with redis presented but not loaded
if not php_versions_to_enable_redis:
sys.exit(0)
# Rebuild users' alt_php.ini if all condition are satisfied:
# - script called with no flag --no-rebuild
# - cagefs package is installed
if not opts.no_rebuild and cagefs.is_cagefs_present():
cagefs.rebuild_alt_php_ini()
update_selector_built_in_extensions(logger)
if __name__ == '__main__':
main()