Files
WPIQ/wo/cli/plugins/info.py
Malin fa5bf17eb8
Some checks failed
CI / test WordOps (ubuntu-22.04) (push) Has been cancelled
CI / test WordOps (ubuntu-24.04) (push) Has been cancelled
feat: convert WordOps from Nginx to OpenLiteSpeed + LSPHP + LSCache
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>
2026-02-08 18:55:16 +01:00

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)