Complete conversion of the WordOps stack from Nginx + PHP-FPM to OpenLiteSpeed + LSPHP + LSCache. This is a full rewrite across all 7 phases of the codebase: - Foundation: OLS paths, variables, services, removed pynginxconfig dep - Templates: 11 new OLS mustache templates, removed nginx-specific ones - Stack: stack_pref, stack, stack_services, stack_upgrade, stack_migrate - Site: site_functions, site, site_create, site_update - Plugins: debug, info, log, clean rewritten for OLS - SSL/ACME: acme.sh deploy uses lswsctrl, OLS vhssl blocks - Other: secure, backup, clone, install script Additional features: - Debian 13 (trixie) support - PHP 8.5 support - WP Fort Knox mu-plugin integration (wo secure --lockdown/--unlock) - --nginx CLI flag preserved for backward compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
242 lines
9.9 KiB
Python
242 lines
9.9 KiB
Python
"""WOInfo Plugin for WordOps"""
|
|
|
|
import configparser
|
|
import os
|
|
|
|
from cement.core.controller import CementBaseController, expose
|
|
|
|
from wo.core.aptget import WOAptGet
|
|
from wo.core.logging import Log
|
|
from wo.core.variables import WOVar
|
|
from wo.core.mysql import WOMysql
|
|
|
|
|
|
def wo_info_hook(app):
|
|
pass
|
|
|
|
|
|
class WOInfoController(CementBaseController):
|
|
class Meta:
|
|
label = 'info'
|
|
stacked_on = 'base'
|
|
stacked_type = 'nested'
|
|
description = ('Display configuration information related to '
|
|
'OpenLiteSpeed, PHP and MySQL')
|
|
arguments = [
|
|
(['--mysql'],
|
|
dict(help='Get MySQL configuration information',
|
|
action='store_true')),
|
|
(['--php'],
|
|
dict(help='Get PHP configuration information',
|
|
action='store_true')),
|
|
(['--nginx'],
|
|
dict(help='Get OpenLiteSpeed configuration information',
|
|
action='store_true')),
|
|
]
|
|
usage = "wo info [options]"
|
|
for php_version, php_number in WOVar.wo_php_versions.items():
|
|
arguments.append(([f'--{php_version}'],
|
|
dict(help=f'Get PHP {php_number} configuration information',
|
|
action='store_true')))
|
|
|
|
@expose(hide=True)
|
|
def info_ols(self):
|
|
"""Display OpenLiteSpeed information"""
|
|
version = os.popen("{0} -v 2>&1 | head -1"
|
|
.format(WOVar.wo_ols_bin)).read().strip()
|
|
httpd_conf = '{0}/httpd_config.conf'.format(WOVar.wo_ols_conf_dir)
|
|
server_name = os.popen("hostname -f 2>/dev/null || hostname"
|
|
).read().strip()
|
|
|
|
# Parse OLS httpd_config.conf for key settings
|
|
max_connections = ''
|
|
max_ssl_connections = ''
|
|
keepalive_timeout = ''
|
|
gzip_compress = ''
|
|
brotli_compress = ''
|
|
quic_enabled = ''
|
|
|
|
if os.path.isfile(httpd_conf):
|
|
with open(httpd_conf, 'r', encoding='utf-8') as f:
|
|
for line in f:
|
|
stripped = line.strip()
|
|
parts = stripped.split(None, 1)
|
|
if len(parts) == 2:
|
|
key, val = parts
|
|
if key == 'maxConnections':
|
|
max_connections = val
|
|
elif key == 'maxSSLConnections':
|
|
max_ssl_connections = val
|
|
elif key == 'keepAliveTimeout':
|
|
keepalive_timeout = val
|
|
elif key == 'enableGzipCompress':
|
|
gzip_compress = 'On' if val == '1' else 'Off'
|
|
elif key == 'enableBr':
|
|
brotli_compress = 'On' if val == '1' else 'Off'
|
|
elif key == 'enableQuic':
|
|
quic_enabled = 'On' if val == '1' else 'Off'
|
|
|
|
data = dict(version=version, server_name=server_name,
|
|
max_connections=max_connections,
|
|
max_ssl_connections=max_ssl_connections,
|
|
keepalive_timeout=keepalive_timeout,
|
|
gzip_compress=gzip_compress,
|
|
brotli_compress=brotli_compress,
|
|
quic_enabled=quic_enabled)
|
|
self.app.render((data), 'info_ols.mustache')
|
|
|
|
@expose(hide=True)
|
|
def info_php(self):
|
|
"""Display PHP information"""
|
|
pargs = self.app.pargs
|
|
for parg_version, dot_ver in WOVar.wo_php_versions.items():
|
|
short_ver = dot_ver.replace('.', '')
|
|
if WOAptGet.is_installed(self, 'lsphp{0}'.format(short_ver)):
|
|
setattr(pargs, parg_version, True)
|
|
else:
|
|
Log.info(self, "PHP {0} is not installed".format(dot_ver))
|
|
|
|
for parg_version, dot_ver in WOVar.wo_php_versions.items():
|
|
if getattr(pargs, parg_version, False):
|
|
short_ver = dot_ver.replace('.', '')
|
|
self._info_lsphp(short_ver, dot_ver)
|
|
|
|
@expose(hide=True)
|
|
def _info_lsphp(self, short_ver, dot_ver):
|
|
"""Display LSPHP information for a given version"""
|
|
php_bin = '/usr/local/lsws/lsphp{0}/bin/php'.format(short_ver)
|
|
php_ini = ('/usr/local/lsws/lsphp{0}/etc/php/{1}'
|
|
'/litespeed/php.ini'.format(short_ver, dot_ver))
|
|
|
|
version = os.popen("{0} -v 2>/dev/null | "
|
|
"head -n1 | cut -d' ' -f2 |"
|
|
" cut -d'+' -f1 | tr -d '\\n'"
|
|
.format(php_bin)).read()
|
|
|
|
config = configparser.ConfigParser()
|
|
if os.path.isfile(php_ini):
|
|
config.read(php_ini)
|
|
else:
|
|
Log.info(self, "LSPHP {0} php.ini not found at {1}"
|
|
.format(dot_ver, php_ini))
|
|
return
|
|
|
|
try:
|
|
expose_php = config['PHP']['expose_php']
|
|
except KeyError:
|
|
expose_php = 'N/A'
|
|
try:
|
|
memory_limit = config['PHP']['memory_limit']
|
|
except KeyError:
|
|
memory_limit = 'N/A'
|
|
try:
|
|
post_max_size = config['PHP']['post_max_size']
|
|
except KeyError:
|
|
post_max_size = 'N/A'
|
|
try:
|
|
upload_max_filesize = config['PHP']['upload_max_filesize']
|
|
except KeyError:
|
|
upload_max_filesize = 'N/A'
|
|
try:
|
|
max_execution_time = config['PHP']['max_execution_time']
|
|
except KeyError:
|
|
max_execution_time = 'N/A'
|
|
|
|
data = dict(version=version, expose_php=expose_php,
|
|
memory_limit=memory_limit, post_max_size=post_max_size,
|
|
upload_max_filesize=upload_max_filesize,
|
|
max_execution_time=max_execution_time,
|
|
www_listen='LSAPI (managed by OLS)',
|
|
www_ping_path='N/A',
|
|
www_pm_status_path='N/A', www_pm='N/A',
|
|
www_pm_max_requests='N/A',
|
|
www_pm_max_children='N/A',
|
|
www_pm_start_servers='N/A',
|
|
www_pm_min_spare_servers='N/A',
|
|
www_pm_max_spare_servers='N/A',
|
|
www_request_terminate_timeout='N/A',
|
|
www_xdebug_profiler_enable_trigger='N/A',
|
|
debug_listen='N/A', debug_ping_path='N/A',
|
|
debug_pm_status_path='N/A',
|
|
debug_pm='N/A',
|
|
debug_pm_max_requests='N/A',
|
|
debug_pm_max_children='N/A',
|
|
debug_pm_start_servers='N/A',
|
|
debug_pm_min_spare_servers='N/A',
|
|
debug_pm_max_spare_servers='N/A',
|
|
debug_request_terminate_timeout='N/A',
|
|
debug_xdebug_profiler_enable_trigger='N/A')
|
|
self.app.render((data), 'info_php.mustache')
|
|
|
|
@expose(hide=True)
|
|
def info_mysql(self):
|
|
"""Display MySQL information"""
|
|
if os.path.exists('/usr/bin/mariadb'):
|
|
mariadb_exec = "/usr/bin/mariadb"
|
|
else:
|
|
mariadb_exec = "/usr/bin/mysql"
|
|
version = os.popen(f"{mariadb_exec} -V |"
|
|
"awk '{print($5)}' | "
|
|
"cut -d ',' "
|
|
"-f1 | tr -d '\n'").read()
|
|
host = "localhost"
|
|
port = os.popen(f"{mariadb_exec} -e \"show variables\" | "
|
|
"/bin/grep ^port | awk "
|
|
"'{print($2)}' | tr -d '\n'").read()
|
|
wait_timeout = os.popen(f"{mariadb_exec} -e \"show variables\" | grep "
|
|
"^wait_timeout | awk '{print($2)}' | "
|
|
"tr -d '\n'").read()
|
|
interactive_timeout = os.popen(f"{mariadb_exec} -e "
|
|
"\"show variables\" | grep "
|
|
"^interactive_timeout | awk "
|
|
"'{print($2)}' | tr -d '\n'").read()
|
|
max_used_connections = os.popen(f"{mariadb_exec} - e "
|
|
"\"show global status\" | "
|
|
"grep Max_used_connections | awk "
|
|
"'{print($2)}' | tr -d '\n'").read()
|
|
datadir = os.popen(f"{mariadb_exec} -e \"show variables\" | "
|
|
"/bin/grep datadir | awk"
|
|
" '{print($2)}' | tr -d '\n'").read()
|
|
socket = os.popen(f"{mariadb_exec} -e \"show variables\" | "
|
|
"/bin/grep \"^socket\" | "
|
|
"awk '{print($2)}' | tr -d '\n'").read()
|
|
data = dict(version=version, host=host, port=port,
|
|
wait_timeout=wait_timeout,
|
|
interactive_timeout=interactive_timeout,
|
|
max_used_connections=max_used_connections,
|
|
datadir=datadir, socket=socket)
|
|
self.app.render((data), 'info_mysql.mustache')
|
|
|
|
@expose(hide=True)
|
|
def default(self):
|
|
"""default function for info"""
|
|
pargs = self.app.pargs
|
|
if (not pargs.nginx and not pargs.php and not pargs.mysql):
|
|
pargs.nginx = True
|
|
pargs.mysql = True
|
|
pargs.php = True
|
|
|
|
if pargs.nginx:
|
|
if ((not WOAptGet.is_installed(self, 'openlitespeed')) and
|
|
(not os.path.exists(WOVar.wo_ols_bin))):
|
|
Log.info(self, "OpenLiteSpeed is not installed")
|
|
else:
|
|
self.info_ols()
|
|
|
|
if pargs.php:
|
|
self.info_php()
|
|
|
|
if pargs.mysql:
|
|
if WOMysql.mariadb_ping(self):
|
|
self.info_mysql()
|
|
else:
|
|
Log.info(self, "MySQL is not installed")
|
|
|
|
|
|
def load(app):
|
|
# register the plugin class.. this only happens if the plugin is enabled
|
|
app.handler.register(WOInfoController)
|
|
|
|
# register a hook (function) to run after arguments are parsed.
|
|
app.hook.register('post_argument_parsing', wo_info_hook)
|