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>
This commit is contained in:
@@ -13,7 +13,6 @@ from wo.core.fileutils import WOFileUtils
|
||||
from wo.core.git import WOGit
|
||||
from wo.core.logging import Log
|
||||
from wo.core.mysql import WOMysql
|
||||
from wo.core.nginxhashbucket import hashbucket
|
||||
from wo.core.services import WOService
|
||||
from wo.core.shellexec import CommandExecutionError, WOShellExec
|
||||
from wo.core.sslutils import SSL
|
||||
@@ -66,46 +65,26 @@ def pre_pref(self, apt_packages):
|
||||
with os.fdopen(os.open(conf_path, os.O_WRONLY | os.O_CREAT, 0o600), 'w', encoding='utf-8') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
# add nginx repository
|
||||
if set(WOVar.wo_nginx).issubset(set(apt_packages)):
|
||||
if (WOVar.wo_distro == 'ubuntu'):
|
||||
Log.info(self, "Adding repository for NGINX, please wait...")
|
||||
WORepo.add(self, ppa=WOVar.wo_nginx_repo)
|
||||
Log.debug(self, 'Adding ppa for Nginx')
|
||||
else:
|
||||
if not os.path.exists('/etc/apt/sources.list.d/wordops.list'):
|
||||
Log.info(self, "Adding repository for NGINX, please wait...")
|
||||
Log.debug(self, 'Adding repository for Nginx')
|
||||
WORepo.add(self, repo_url=WOVar.wo_nginx_repo, repo_name="wordops")
|
||||
# add OpenLiteSpeed repository
|
||||
if set(WOVar.wo_ols).issubset(set(apt_packages)):
|
||||
if not os.path.exists('/etc/apt/sources.list.d/openlitespeed.list'):
|
||||
Log.info(self, "Adding repository for OpenLiteSpeed, please wait...")
|
||||
Log.debug(self, 'Adding repository for OpenLiteSpeed')
|
||||
WORepo.add(self, repo_url=WOVar.wo_ols_repo, repo_name="openlitespeed")
|
||||
|
||||
# add php repository
|
||||
if (('php7.3-fpm' in apt_packages) or
|
||||
('php7.2-fpm' in apt_packages) or
|
||||
('php7.4-fpm' in apt_packages) or
|
||||
('php8.0-fpm' in apt_packages) or
|
||||
('php8.1-fpm' in apt_packages) or
|
||||
('php8.2-fpm' in apt_packages) or
|
||||
('php8.3-fpm' in apt_packages) or
|
||||
('php8.4-fpm' in apt_packages)):
|
||||
if (WOVar.wo_distro == 'ubuntu'):
|
||||
Log.debug(self, 'Adding ppa for PHP')
|
||||
Log.info(self, "Adding repository for PHP, please wait...")
|
||||
WORepo.add(self, ppa=WOVar.wo_php_repo)
|
||||
else:
|
||||
# Add repository for php
|
||||
if (WOVar.wo_platform_codename == 'buster'):
|
||||
php_pref = ("Package: *\nPin: origin "
|
||||
"packages.sury.org"
|
||||
"\nPin-Priority: 1000\n")
|
||||
with open(
|
||||
'/etc/apt/preferences.d/'
|
||||
'PHP.pref', mode='w',
|
||||
encoding='utf-8') as php_pref_file:
|
||||
php_pref_file.write(php_pref)
|
||||
if not os.path.exists('/etc/apt/sources.list.d/php.list'):
|
||||
Log.debug(self, 'Adding repo_url of php for debian')
|
||||
Log.info(self, "Adding repository for PHP, please wait...")
|
||||
WORepo.add(self, repo_url=WOVar.wo_php_repo, repo_name="php")
|
||||
# add LSPHP repository (same as OLS repo)
|
||||
lsphp_in_packages = False
|
||||
for version in list(WOVar.wo_php_versions.values()):
|
||||
short_ver = version.replace('.', '')
|
||||
if 'lsphp{0}'.format(short_ver) in apt_packages:
|
||||
lsphp_in_packages = True
|
||||
break
|
||||
|
||||
if lsphp_in_packages:
|
||||
if not os.path.exists('/etc/apt/sources.list.d/openlitespeed.list'):
|
||||
Log.info(self, "Adding repository for LSPHP, please wait...")
|
||||
Log.debug(self, 'Adding repository for LSPHP')
|
||||
WORepo.add(self, repo_url=WOVar.wo_ols_repo, repo_name="openlitespeed")
|
||||
|
||||
# add redis repository
|
||||
if set(WOVar.wo_redis).issubset(set(apt_packages)):
|
||||
@@ -116,287 +95,133 @@ def pre_pref(self, apt_packages):
|
||||
def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
"""Post activity after installation of packages"""
|
||||
if (apt_packages):
|
||||
# Nginx configuration
|
||||
if set(WOVar.wo_nginx).issubset(set(apt_packages)):
|
||||
Log.wait(self, "Configuring Nginx")
|
||||
# Nginx main configuration
|
||||
ngxcnf = '/etc/nginx/conf.d'
|
||||
ngxcom = '/etc/nginx/common'
|
||||
# OpenLiteSpeed configuration
|
||||
if set(WOVar.wo_ols).issubset(set(apt_packages)):
|
||||
Log.wait(self, "Configuring OpenLiteSpeed")
|
||||
ols_conf = WOVar.wo_ols_conf_dir
|
||||
ols_vhost = WOVar.wo_ols_vhost_dir
|
||||
ngxroot = '/var/www/'
|
||||
WOGit.add(self, ["/etc/nginx"], msg="Adding Nginx into Git")
|
||||
data = dict(tls13=True, release=WOVar.wo_version)
|
||||
|
||||
WOGit.add(self, ["/usr/local/lsws/conf"],
|
||||
msg="Adding OpenLiteSpeed into Git")
|
||||
|
||||
# Create vhost directory structure
|
||||
if not os.path.exists(ols_vhost):
|
||||
os.makedirs(ols_vhost)
|
||||
|
||||
# Determine default PHP version
|
||||
default_php_short = '84'
|
||||
for ver_key, ver_num in WOVar.wo_php_versions.items():
|
||||
short = ver_num.replace('.', '')
|
||||
if os.path.exists('/usr/local/lsws/lsphp{0}/bin/lsphp'.format(short)):
|
||||
default_php_short = short
|
||||
break
|
||||
|
||||
# Deploy main httpd_config.conf
|
||||
data = dict(
|
||||
server_name=WOVar.wo_fqdn,
|
||||
release=WOVar.wo_version,
|
||||
backend_port='22222',
|
||||
default_php_short=default_php_short)
|
||||
WOTemplate.deploy(self,
|
||||
'/etc/nginx/nginx.conf',
|
||||
'nginx-core.mustache', data, overwrite=True)
|
||||
'{0}/httpd_config.conf'.format(ols_conf),
|
||||
'ols-httpd.mustache', data, overwrite=True)
|
||||
|
||||
if not os.path.isfile('{0}/gzip.conf.disabled'.format(ngxcnf)):
|
||||
data = dict(release=WOVar.wo_version)
|
||||
WOTemplate.deploy(self, '{0}/gzip.conf'.format(ngxcnf),
|
||||
'gzip.mustache', data)
|
||||
# Deploy extApp configs for all PHP versions
|
||||
WOConf.olscommon(self)
|
||||
|
||||
if not os.path.isfile('{0}/brotli.conf'.format(ngxcnf)):
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/brotli.conf.disabled'
|
||||
.format(ngxcnf),
|
||||
'brotli.mustache', data)
|
||||
# Create log and cert folder for backend
|
||||
if not os.path.exists('{0}22222/logs'.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/logs".format(ngxroot))
|
||||
os.makedirs('{0}22222/logs'.format(ngxroot))
|
||||
|
||||
WOTemplate.deploy(self, '{0}/tweaks.conf'.format(ngxcnf),
|
||||
'tweaks.mustache', data)
|
||||
if not os.path.exists('{0}22222/cert'.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/cert".format(ngxroot))
|
||||
os.makedirs('{0}22222/cert'.format(ngxroot))
|
||||
|
||||
# Fix for white screen death with NGINX PLUS
|
||||
if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params',
|
||||
'SCRIPT_FILENAME'):
|
||||
with open('/etc/nginx/fastcgi_params',
|
||||
encoding='utf-8', mode='a') as wo_nginx:
|
||||
wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME '
|
||||
'\t$request_filename;\n')
|
||||
if not WOFileUtils.grep(self, '/etc/nginx/fastcgi_params',
|
||||
'HTTP_HOST'):
|
||||
WOFileUtils.textappend(self, '/etc/nginx/fastcgi_params',
|
||||
'# Fix for HTTP/3 QUIC HTTP_HOST\n'
|
||||
'fastcgi_param\tHTTP_HOST\t$host;\n')
|
||||
if not WOFileUtils.grep(self, '/etc/nginx/proxy_params',
|
||||
'X-Forwarded-Host'):
|
||||
WOFileUtils.textappend(self, '/etc/nginx/proxy_params',
|
||||
'proxy_set_header X-Forwarded-Host $host;\n')
|
||||
if not WOFileUtils.grep(self, '/etc/nginx/proxy_params',
|
||||
'X-Forwarded-Port'):
|
||||
WOFileUtils.textappend(self, '/etc/nginx/proxy_params',
|
||||
'proxy_set_header X-Forwarded-Port $server_port;\n')
|
||||
try:
|
||||
data = dict(php="9000", debug="9001",
|
||||
php7="9070", debug7="9170",
|
||||
release=WOVar.wo_version)
|
||||
WOTemplate.deploy(
|
||||
self, '{0}/upstream.conf'.format(ngxcnf),
|
||||
'upstream.mustache', data, overwrite=True)
|
||||
|
||||
data = dict(phpconf=(
|
||||
bool(WOAptGet.is_installed(self, 'php7.2-fpm'))),
|
||||
release=WOVar.wo_version)
|
||||
WOTemplate.deploy(
|
||||
self, '{0}/stub_status.conf'.format(ngxcnf),
|
||||
'stub_status.mustache', data)
|
||||
data = dict(release=WOVar.wo_version)
|
||||
WOTemplate.deploy(
|
||||
self, '{0}/webp.conf'.format(ngxcnf),
|
||||
'webp.mustache', data, overwrite=False)
|
||||
WOTemplate.deploy(
|
||||
self, '{0}/avif.conf'.format(ngxcnf),
|
||||
'avif.mustache', data, overwrite=False)
|
||||
WOTemplate.deploy(
|
||||
self,
|
||||
'{0}/map-wp-fastcgi-cache.conf'.format(ngxcnf),
|
||||
'map-wp.mustache', data)
|
||||
except CommandExecutionError as e:
|
||||
Log.debug(self, "{0}".format(e))
|
||||
|
||||
# Setup Nginx common directory
|
||||
if not os.path.exists('{0}'.format(ngxcom)):
|
||||
Log.debug(self, 'Creating directory'
|
||||
'/etc/nginx/common')
|
||||
os.makedirs('/etc/nginx/common')
|
||||
|
||||
try:
|
||||
data = dict(release=WOVar.wo_version)
|
||||
|
||||
# Common Configuration
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/locations-wo.conf'
|
||||
.format(ngxcom),
|
||||
'locations.mustache', data)
|
||||
# traffic advice file
|
||||
WOTemplate.deploy(self,
|
||||
'/var/www/html/'
|
||||
'.well-known/traffic-advice',
|
||||
'traffic-advice.mustache', data)
|
||||
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/wpsubdir.conf'
|
||||
.format(ngxcom),
|
||||
'wpsubdir.mustache', data)
|
||||
|
||||
for wo_php in WOVar.wo_php_versions:
|
||||
data = dict(upstream="{0}".format(wo_php),
|
||||
release=WOVar.wo_version)
|
||||
WOConf.nginxcommon(self)
|
||||
|
||||
except CommandExecutionError as e:
|
||||
Log.debug(self, "{0}".format(e))
|
||||
|
||||
with open("/etc/nginx/common/release",
|
||||
"w", encoding='utf-8') as release_file:
|
||||
release_file.write("v{0}"
|
||||
.format(WOVar.wo_version))
|
||||
release_file.close()
|
||||
|
||||
# Following files should not be overwrited
|
||||
|
||||
data = dict(webroot=ngxroot, release=WOVar.wo_version)
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/acl.conf'
|
||||
.format(ngxcom),
|
||||
'acl.mustache', data, overwrite=False)
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/blockips.conf'
|
||||
.format(ngxcnf),
|
||||
'blockips.mustache', data, overwrite=False)
|
||||
WOTemplate.deploy(self,
|
||||
'{0}/fastcgi.conf'
|
||||
.format(ngxcnf),
|
||||
'fastcgi.mustache', data, overwrite=True)
|
||||
|
||||
# add redis cache format if not already done
|
||||
if (os.path.isfile("/etc/nginx/nginx.conf") and
|
||||
not os.path.isfile("/etc/nginx/conf.d"
|
||||
"/redis.conf")):
|
||||
with open("/etc/nginx/conf.d/"
|
||||
"redis.conf", "a") as redis_file:
|
||||
redis_file.write(
|
||||
"# Log format Settings\n"
|
||||
"log_format rt_cache_redis "
|
||||
"'$remote_addr "
|
||||
"$upstream_response_time "
|
||||
"$srcache_fetch_status "
|
||||
"[$time_local] '\n"
|
||||
"'$host \"$request\" $status"
|
||||
" $body_bytes_sent '\n"
|
||||
"'\"$http_referer\" "
|
||||
"\"$http_user_agent\"';\n")
|
||||
|
||||
if not os.path.exists('/etc/nginx/bots.d'):
|
||||
WOFileUtils.textwrite(
|
||||
self, '/etc/nginx/conf.d/variables-hash.conf',
|
||||
'variables_hash_max_size 4096;\n'
|
||||
'variables_hash_bucket_size 4096;')
|
||||
|
||||
# Nginx-Plus does not have nginx
|
||||
# package structure like this
|
||||
# So creating directories
|
||||
if not os.path.exists('/etc/nginx/sites-available'):
|
||||
Log.debug(self, 'Creating directory'
|
||||
'/etc/nginx/sites-available')
|
||||
os.makedirs('/etc/nginx/sites-available')
|
||||
|
||||
if not os.path.exists('/etc/nginx/sites-enabled'):
|
||||
Log.debug(self, 'Creating directory'
|
||||
'/etc/nginx/sites-available')
|
||||
os.makedirs('/etc/nginx/sites-enabled')
|
||||
|
||||
# 22222 port settings
|
||||
if os.path.exists('/etc/nginx/sites-available/22222'):
|
||||
Log.debug(self, "looking for the current backend port")
|
||||
for line in open('/etc/nginx/sites-available/22222',
|
||||
encoding='utf-8'):
|
||||
if 'listen' in line:
|
||||
listen_line = line.strip()
|
||||
break
|
||||
port = (listen_line).split(' ')
|
||||
current_backend_port = (port[1]).strip()
|
||||
else:
|
||||
current_backend_port = '22222'
|
||||
|
||||
if 'current_backend_port' not in locals():
|
||||
current_backend_port = '22222'
|
||||
if not os.path.isdir('{0}22222/conf/ols'.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/conf/ols".format(ngxroot))
|
||||
os.makedirs('{0}22222/conf/ols'.format(ngxroot))
|
||||
|
||||
# Deploy backend vhost
|
||||
data = dict(webroot=ngxroot,
|
||||
release=WOVar.wo_version, port=current_backend_port)
|
||||
release=WOVar.wo_version,
|
||||
port='22222',
|
||||
default_php_short=default_php_short)
|
||||
backend_vhost_dir = '{0}/_backend'.format(ols_vhost)
|
||||
if not os.path.exists(backend_vhost_dir):
|
||||
os.makedirs(backend_vhost_dir)
|
||||
WOTemplate.deploy(
|
||||
self,
|
||||
'/etc/nginx/sites-available/22222',
|
||||
'22222.mustache', data, overwrite=True)
|
||||
'{0}/vhconf.conf'.format(backend_vhost_dir),
|
||||
'ols-backend.mustache', data, overwrite=True)
|
||||
|
||||
# Setup admin password
|
||||
passwd = ''.join([random.choice
|
||||
(string.ascii_letters + string.digits)
|
||||
for n in range(24)])
|
||||
if not os.path.isfile('/etc/nginx/htpasswd-wo'):
|
||||
if not os.path.isfile('{0}/htpasswd-wo'.format(ols_conf)):
|
||||
try:
|
||||
WOShellExec.cmd_exec(
|
||||
self, "printf \"WordOps:"
|
||||
"$(openssl passwd -apr1 "
|
||||
"{password} 2> /dev/null)\n\""
|
||||
"> /etc/nginx/htpasswd-wo "
|
||||
"> {conf}/htpasswd-wo "
|
||||
"2>/dev/null"
|
||||
.format(password=passwd))
|
||||
.format(password=passwd, conf=ols_conf))
|
||||
except CommandExecutionError as e:
|
||||
Log.debug(self, "{0}".format(e))
|
||||
Log.error(self, "Failed to save HTTP Auth")
|
||||
if not os.path.islink('/etc/nginx/sites-enabled/22222'):
|
||||
# Create Symbolic link for 22222
|
||||
WOFileUtils.create_symlink(
|
||||
self, ['/etc/nginx/'
|
||||
'sites-available/'
|
||||
'22222',
|
||||
'/etc/nginx/'
|
||||
'sites-enabled/'
|
||||
'22222'])
|
||||
# Create log and cert folder and softlinks
|
||||
if not os.path.exists('{0}22222/logs'
|
||||
.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/logs "
|
||||
.format(ngxroot))
|
||||
os.makedirs('{0}22222/logs'
|
||||
.format(ngxroot))
|
||||
|
||||
if not os.path.exists('{0}22222/cert'
|
||||
.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/cert"
|
||||
.format(ngxroot))
|
||||
os.makedirs('{0}22222/cert'
|
||||
.format(ngxroot))
|
||||
|
||||
if not os.path.isdir('{0}22222/conf/nginx'
|
||||
.format(ngxroot)):
|
||||
Log.debug(self, "Creating directory "
|
||||
"{0}22222/conf/nginx"
|
||||
.format(ngxroot))
|
||||
os.makedirs('{0}22222/conf/nginx'
|
||||
.format(ngxroot))
|
||||
|
||||
WOFileUtils.create_symlink(
|
||||
self,
|
||||
['/var/log/nginx/'
|
||||
'22222.access.log',
|
||||
'{0}22222/'
|
||||
'logs/access.log'
|
||||
.format(ngxroot)]
|
||||
)
|
||||
|
||||
WOFileUtils.create_symlink(
|
||||
self,
|
||||
['/var/log/nginx/'
|
||||
'22222.error.log',
|
||||
'{0}22222/'
|
||||
'logs/error.log'
|
||||
.format(ngxroot)]
|
||||
)
|
||||
# Generate self-signed cert for backend if missing
|
||||
if (not os.path.isfile('{0}22222/cert/22222.key'
|
||||
.format(ngxroot))):
|
||||
SSL.selfsignedcert(self, proftpd=False, backend=True)
|
||||
|
||||
if not os.path.exists('{0}22222/conf/nginx/ssl.conf'
|
||||
.format(ngxroot)):
|
||||
with open("/var/www/22222/conf/nginx/"
|
||||
"ssl.conf", "w") as php_file:
|
||||
php_file.write("ssl_certificate "
|
||||
"/var/www/22222/cert/22222.crt;\n"
|
||||
"ssl_certificate_key "
|
||||
"/var/www/22222/cert/22222.key;\n"
|
||||
"ssl_stapling off;\n")
|
||||
# Deploy OLS admin password via admpass.sh
|
||||
if os.path.isfile('/usr/local/lsws/admin/misc/admpass.sh'):
|
||||
try:
|
||||
WOShellExec.cmd_exec(
|
||||
self,
|
||||
'/usr/local/lsws/admin/misc/admpass.sh '
|
||||
'--password "{0}"'.format(passwd))
|
||||
except CommandExecutionError as e:
|
||||
Log.debug(self, "{0}".format(e))
|
||||
|
||||
# traffic advice file
|
||||
data = dict(release=WOVar.wo_version)
|
||||
WOTemplate.deploy(self,
|
||||
'/var/www/html/'
|
||||
'.well-known/traffic-advice',
|
||||
'traffic-advice.mustache', data)
|
||||
|
||||
# Start/Restart OLS
|
||||
if not WOService.restart_service(self, 'lsws'):
|
||||
Log.info(self, "Rolling back to previous configuration")
|
||||
WOGit.rollback(self, ["/usr/local/lsws/conf"])
|
||||
if not WOService.restart_service(self, 'lsws'):
|
||||
Log.error(
|
||||
self, "There is an error in OpenLiteSpeed configuration.\n"
|
||||
"Use the command '/usr/local/lsws/bin/openlitespeed -t' to identify "
|
||||
"the cause of this issue", False)
|
||||
else:
|
||||
Log.valide(self, "Configuring OpenLiteSpeed")
|
||||
WOGit.add(self, ["/usr/local/lsws/conf"],
|
||||
msg="Adding OpenLiteSpeed into Git")
|
||||
|
||||
server_ip = WOFqdn.get_server_ip(self)
|
||||
if server_ip is None:
|
||||
server_ip = WOVar.wo_fqdn
|
||||
|
||||
if set(["nginx"]).issubset(set(apt_packages)):
|
||||
if set(["openlitespeed"]).issubset(set(apt_packages)):
|
||||
print("WordOps backend configuration was successful\n"
|
||||
"You can access it on : https://{0}:22222"
|
||||
.format(server_ip))
|
||||
print("HTTP Auth User Name: WordOps" +
|
||||
"\nHTTP Auth Password : {0}".format(passwd))
|
||||
WOService.reload_service(self, 'nginx')
|
||||
else:
|
||||
self.msg = (self.msg + ["HTTP Auth User "
|
||||
"Name: WordOps"] +
|
||||
@@ -405,177 +230,69 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
self.msg = (self.msg + [f'WordOps backend is available on https://{server_ip}:22222 '
|
||||
f'or https://{WOVar.wo_fqdn}:22222'])
|
||||
|
||||
data = dict(release=WOVar.wo_version)
|
||||
WOTemplate.deploy(self, '/opt/cf-update.sh',
|
||||
'cf-update.mustache',
|
||||
data, overwrite=True)
|
||||
WOFileUtils.chmod(self, "/opt/cf-update.sh", 0o775)
|
||||
Log.debug(self, 'Creating Cloudflare.conf')
|
||||
WOShellExec.cmd_exec(self, '/opt/cf-update.sh')
|
||||
WOCron.setcron_weekly(self, '/opt/cf-update.sh '
|
||||
'> /dev/null 2>&1',
|
||||
comment='Cloudflare IP refresh cronjob '
|
||||
'added by WordOps')
|
||||
|
||||
# Nginx Configation into GIT
|
||||
if not WOService.restart_service(self, 'nginx'):
|
||||
try:
|
||||
hashbucket(self)
|
||||
WOService.restart_service(self, 'nginx')
|
||||
except Exception:
|
||||
Log.warn(
|
||||
self, "increasing nginx server_names_hash_bucket_size "
|
||||
"do not fix the issue")
|
||||
Log.info(self, "Rolling back to previous configuration")
|
||||
WOGit.rollback(self, ["/etc/nginx"])
|
||||
if not WOService.restart_service(self, 'nginx'):
|
||||
Log.error(
|
||||
self, "There is an error in Nginx configuration.\n"
|
||||
"Use the command nginx -t to identify "
|
||||
"the cause of this issue", False)
|
||||
else:
|
||||
Log.valide(self, "Configuring Nginx")
|
||||
WOGit.add(self, ["/etc/nginx"], msg="Adding Nginx into Git")
|
||||
if not os.path.isdir('/etc/systemd/system/nginx.service.d'):
|
||||
WOFileUtils.mkdir(self,
|
||||
'/etc/systemd/system/nginx.service.d')
|
||||
if not os.path.isdir(
|
||||
'/etc/systemd/system/nginx.service.d/limits.conf'):
|
||||
with open(
|
||||
'/etc/systemd/system/nginx.service.d/limits.conf',
|
||||
encoding='utf-8', mode='w') as ngx_limit:
|
||||
ngx_limit.write('[Service]\nLimitNOFILE=500000')
|
||||
WOShellExec.cmd_exec(self, 'systemctl daemon-reload')
|
||||
WOService.restart_service(self, 'nginx')
|
||||
|
||||
# php conf
|
||||
# LSPHP configuration
|
||||
php_list = []
|
||||
for version in list(WOVar.wo_php_versions.values()):
|
||||
package_name = 'php' + version + '-fpm'
|
||||
short_ver = version.replace('.', '')
|
||||
package_name = 'lsphp{0}'.format(short_ver)
|
||||
if package_name in apt_packages:
|
||||
php_list.append([version])
|
||||
php_list.append([version, short_ver])
|
||||
|
||||
for php_version in php_list:
|
||||
WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git")
|
||||
Log.wait(self, "Configuring php{0}-fpm".format(php_version[0]))
|
||||
for php_info in php_list:
|
||||
php_version = php_info[0]
|
||||
php_short = php_info[1]
|
||||
Log.wait(self, "Configuring lsphp{0}".format(php_short))
|
||||
ngxroot = '/var/www/'
|
||||
|
||||
# Create log directories
|
||||
if not os.path.exists('/var/log/php/{0}/'.format(php_version[0])):
|
||||
if not os.path.exists('/var/log/php/{0}/'.format(php_version)):
|
||||
Log.debug(
|
||||
self, 'Creating directory /var/log/php/{0}/'
|
||||
.format(php_version[0]))
|
||||
os.makedirs('/var/log/php/{0}/'.format(php_version[0]))
|
||||
.format(php_version))
|
||||
os.makedirs('/var/log/php/{0}/'.format(php_version))
|
||||
|
||||
if not os.path.isfile(
|
||||
'/etc/php/{0}/fpm/php.ini.orig'.format(php_version[0])):
|
||||
WOFileUtils.copyfile(self,
|
||||
'/etc/php/{0}/fpm/php.ini'.format(
|
||||
php_version[0]),
|
||||
'/etc/php/{0}/fpm/php.ini.orig'
|
||||
.format(php_version[0]))
|
||||
# Configure LSPHP php.ini
|
||||
lsphp_ini = '/usr/local/lsws/lsphp{0}/etc/php/{1}/litespeed/php.ini'.format(
|
||||
php_short, php_version)
|
||||
lsphp_ini_orig = lsphp_ini + '.orig'
|
||||
|
||||
# Parse etc/php/x.x/fpm/php.ini
|
||||
config = configparser.ConfigParser()
|
||||
Log.debug(self, "configuring php file "
|
||||
"/etc/php/{0}/fpm/php.ini".format(php_version[0]))
|
||||
config.read('/etc/php/{0}/fpm/php.ini.orig'.format(php_version[0]))
|
||||
config['PHP']['expose_php'] = 'Off'
|
||||
config['PHP']['post_max_size'] = '100M'
|
||||
config['PHP']['upload_max_filesize'] = '100M'
|
||||
config['PHP']['max_execution_time'] = '300'
|
||||
config['PHP']['max_input_time'] = '300'
|
||||
config['PHP']['max_input_vars'] = '20000'
|
||||
config['Date']['date.timezone'] = WOVar.wo_timezone
|
||||
config['opcache']['opcache.enable'] = '1'
|
||||
config['opcache']['opcache.interned_strings_buffer'] = '8'
|
||||
config['opcache']['opcache.max_accelerated_files'] = '10000'
|
||||
config['opcache']['opcache.memory_consumption'] = '256'
|
||||
config['opcache']['opcache.save_comments'] = '1'
|
||||
config['opcache']['opcache.revalidate_freq'] = '5'
|
||||
config['opcache']['opcache.consistency_checks'] = '0'
|
||||
config['opcache']['opcache.validate_timestamps'] = '1'
|
||||
with open('/etc/php/{0}/fpm/php.ini'.format(php_version[0]),
|
||||
encoding='utf-8', mode='w') as configfile:
|
||||
Log.debug(self, "Writting php configuration into "
|
||||
"/etc/php/{0}/fpm/php.ini".format(php_version[0]))
|
||||
config.write(configfile)
|
||||
if os.path.isfile(lsphp_ini):
|
||||
if not os.path.isfile(lsphp_ini_orig):
|
||||
WOFileUtils.copyfile(self, lsphp_ini, lsphp_ini_orig)
|
||||
|
||||
# Render php-fpm pool template for phpx.x
|
||||
data = dict(pid="/run/php/php{0}-fpm.pid".format(php_version[0]),
|
||||
error_log="/var/log/php{0}-fpm.log".format(
|
||||
php_version[0]),
|
||||
include="/etc/php/{0}/fpm/pool.d/*.conf"
|
||||
.format(php_version[0]))
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/php/{0}/fpm/php-fpm.conf'.format(php_version[0]),
|
||||
'php-fpm.mustache', data)
|
||||
php_short = php_version[0].replace(".", "")
|
||||
data = dict(pool='www-php{0}'.format(php_short),
|
||||
listen='php{0}-fpm.sock'.format(php_short),
|
||||
user='www-data',
|
||||
group='www-data', listenuser='root',
|
||||
listengroup='www-data', openbasedir=True)
|
||||
WOTemplate.deploy(self, '/etc/php/{0}/fpm/pool.d/www.conf'
|
||||
.format(php_version[0]),
|
||||
'php-pool.mustache', data)
|
||||
data = dict(pool='www-two-php{0}'.format(php_short),
|
||||
listen='php{0}-two-fpm.sock'.format(php_short),
|
||||
user='www-data',
|
||||
group='www-data', listenuser='root',
|
||||
listengroup='www-data', openbasedir=True)
|
||||
config = configparser.ConfigParser()
|
||||
Log.debug(self, "configuring php file {0}".format(lsphp_ini))
|
||||
config.read(lsphp_ini_orig)
|
||||
config['PHP']['expose_php'] = 'Off'
|
||||
config['PHP']['post_max_size'] = '100M'
|
||||
config['PHP']['upload_max_filesize'] = '100M'
|
||||
config['PHP']['max_execution_time'] = '300'
|
||||
config['PHP']['max_input_time'] = '300'
|
||||
config['PHP']['max_input_vars'] = '20000'
|
||||
config['Date']['date.timezone'] = WOVar.wo_timezone
|
||||
config['opcache']['opcache.enable'] = '1'
|
||||
config['opcache']['opcache.interned_strings_buffer'] = '8'
|
||||
config['opcache']['opcache.max_accelerated_files'] = '10000'
|
||||
config['opcache']['opcache.memory_consumption'] = '256'
|
||||
config['opcache']['opcache.save_comments'] = '1'
|
||||
config['opcache']['opcache.revalidate_freq'] = '5'
|
||||
config['opcache']['opcache.consistency_checks'] = '0'
|
||||
config['opcache']['opcache.validate_timestamps'] = '1'
|
||||
with open(lsphp_ini,
|
||||
encoding='utf-8', mode='w') as configfile:
|
||||
Log.debug(self, "Writing php configuration into "
|
||||
"{0}".format(lsphp_ini))
|
||||
config.write(configfile)
|
||||
|
||||
# Deploy extApp config for this PHP version
|
||||
data = dict(
|
||||
php_version=php_version,
|
||||
short_version=php_short,
|
||||
release=WOVar.wo_version)
|
||||
WOTemplate.deploy(self,
|
||||
'/etc/php/{0}/fpm/pool.d/www-two.conf'.format(
|
||||
php_version[0]),
|
||||
'php-pool.mustache', data)
|
||||
|
||||
# Generate /etc/php/x.x/fpm/pool.d/debug.conf
|
||||
WOFileUtils.copyfile(self,
|
||||
"/etc/php/{0}/fpm/pool.d/www.conf".format(
|
||||
php_version[0]),
|
||||
"/etc/php/{0}/fpm/pool.d/debug.conf"
|
||||
.format(php_version[0]))
|
||||
WOFileUtils.searchreplace(self,
|
||||
"/etc/php/{0}/fpm/pool.d/"
|
||||
"debug.conf".format(php_version[0]),
|
||||
"[www-php{0}]".format(php_short),
|
||||
"[debug]")
|
||||
config = configparser.ConfigParser()
|
||||
config.read(
|
||||
'/etc/php/{0}/fpm/pool.d/debug.conf'.format(php_version[0]))
|
||||
config['debug']['listen'] = '127.0.0.1:91{0}'.format(php_short)
|
||||
config['debug']['rlimit_core'] = 'unlimited'
|
||||
config['debug']['slowlog'] = '/var/log/php/{0}/slow.log'.format(
|
||||
php_version[0])
|
||||
config['debug']['request_slowlog_timeout'] = '10s'
|
||||
with open('/etc/php/{0}/fpm/pool.d/debug.conf'
|
||||
.format(php_version[0]),
|
||||
encoding='utf-8', mode='w') as confifile:
|
||||
Log.debug(self,
|
||||
"writting PHP configuration into "
|
||||
"/etc/php/{0}/fpm/pool.d/debug.conf"
|
||||
.format(php_version[0]))
|
||||
config.write(confifile)
|
||||
|
||||
with open("/etc/php/{0}/fpm/pool.d/debug.conf"
|
||||
.format(php_version[0]),
|
||||
encoding='utf-8', mode='a') as myfile:
|
||||
myfile.write("php_admin_value[xdebug.profiler_output_dir] "
|
||||
"= /tmp/ \nphp_admin_value[xdebug.profiler_"
|
||||
"output_name] = cachegrind.out.%p-%H-%R "
|
||||
"\nphp_admin_flag[xdebug.profiler_enable"
|
||||
"_trigger] = on \nphp_admin_flag[xdebug."
|
||||
"profiler_enable] = off\n")
|
||||
|
||||
# Disable xdebug
|
||||
if not WOShellExec.cmd_exec(self, "grep -q \';zend_extension\'"
|
||||
" /etc/php/{0}/mods-available/"
|
||||
"xdebug.ini".format(php_version[0])):
|
||||
WOFileUtils.searchreplace(self, "/etc/php/{0}/"
|
||||
"mods-available/"
|
||||
"xdebug.ini".format(php_version[0]),
|
||||
"zend_extension",
|
||||
";zend_extension")
|
||||
'{0}/lsphp{1}.conf'
|
||||
.format(WOVar.wo_ols_conf_dir, php_short),
|
||||
'ols-extapp.mustache', data)
|
||||
|
||||
# PHP and Debug pull configuration
|
||||
if not os.path.exists('{0}22222/htdocs/fpm/status/'
|
||||
@@ -585,12 +302,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
.format(ngxroot))
|
||||
os.makedirs('{0}22222/htdocs/fpm/status/'
|
||||
.format(ngxroot))
|
||||
open('{0}22222/htdocs/fpm/status/debug{1}'
|
||||
.format(ngxroot, php_short),
|
||||
encoding='utf-8', mode='a').close()
|
||||
open('{0}22222/htdocs/fpm/status/php{1}'
|
||||
.format(ngxroot, php_short),
|
||||
encoding='utf-8', mode='a').close()
|
||||
|
||||
# Write info.php
|
||||
if not os.path.exists('{0}22222/htdocs/php/'
|
||||
@@ -621,31 +332,10 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
'www-data',
|
||||
'www-data', recursive=True)
|
||||
|
||||
# enable imagick php extension
|
||||
WOShellExec.cmd_exec(self, 'phpenmod -v ALL imagick')
|
||||
|
||||
# check service restart or rollback configuration
|
||||
if not WOService.restart_service(self,
|
||||
'php{0}-fpm'
|
||||
.format(php_version[0])):
|
||||
WOGit.rollback(self, ["/etc/php"], msg="Rollback PHP")
|
||||
else:
|
||||
Log.valide(
|
||||
self, "Configuring php{0}-fpm".format(php_version[0]))
|
||||
WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git")
|
||||
|
||||
if os.path.exists('/etc/nginx/conf.d/upstream.conf'):
|
||||
if not WOFileUtils.grepcheck(
|
||||
self, '/etc/nginx/conf.d/upstream.conf',
|
||||
'php{0}'.format(php_short)):
|
||||
data = dict(php="9000", debug="9001",
|
||||
php7="9070", debug7="9170",
|
||||
php8="9080", debug8="9180",
|
||||
release=WOVar.wo_version)
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/nginx/conf.d/upstream.conf',
|
||||
'upstream.mustache', data, True)
|
||||
WOConf.nginxcommon(self)
|
||||
# Restart OLS to pick up new PHP config
|
||||
WOService.restart_service(self, 'lsws')
|
||||
Log.valide(
|
||||
self, "Configuring lsphp{0}".format(php_short))
|
||||
|
||||
# create mysql config if it doesn't exist
|
||||
if "mariadb-server" in apt_packages:
|
||||
@@ -707,7 +397,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
WOFileUtils.copyfile(self, "/etc/mysql/my.cnf",
|
||||
"/etc/mysql/my.cnf.default-pkg")
|
||||
wo_ram = psutil.virtual_memory().total / (1024 * 1024)
|
||||
# set InnoDB variable depending on the RAM available
|
||||
wo_ram_innodb = int(wo_ram * 0.3)
|
||||
wo_ram_log_buffer = int(wo_ram_innodb * 0.25)
|
||||
wo_ram_log_size = int(wo_ram_log_buffer * 0.5)
|
||||
@@ -735,7 +424,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
else:
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/mysql/my.cnf', 'my.mustache', data)
|
||||
# replacing default values
|
||||
Log.debug(self, "Tuning MySQL configuration")
|
||||
if os.path.isdir('/etc/systemd/system/mariadb.service.d'):
|
||||
if not os.path.isfile(
|
||||
@@ -748,16 +436,9 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
'[Service]\nLimitNOFILE=500000')
|
||||
WOShellExec.cmd_exec(self, 'systemctl daemon-reload')
|
||||
Log.valide(self, "Tuning MySQL configuration")
|
||||
# set innodb_buffer_pool_instances depending
|
||||
# on the amount of RAM
|
||||
|
||||
WOService.restart_service(self, 'mariadb')
|
||||
|
||||
# WOFileUtils.mvfile(self, '/var/lib/mysql/ib_logfile0',
|
||||
# '/var/lib/mysql/ib_logfile0.bak')
|
||||
# WOFileUtils.mvfile(self, '/var/lib/mysql/ib_logfile1',
|
||||
# '/var/lib/mysql/ib_logfile1.bak')
|
||||
|
||||
WOCron.setcron_weekly(self, 'mysqlcheck -Aos --auto-repair '
|
||||
'> /dev/null 2>&1',
|
||||
comment='MySQL optimization cronjob '
|
||||
@@ -771,8 +452,8 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
WOGit.add(self, ["/etc/fail2ban"],
|
||||
msg="Adding Fail2ban into Git")
|
||||
Log.wait(self, "Configuring Fail2Ban")
|
||||
nginxf2b = bool(os.path.exists('/var/log/nginx'))
|
||||
data = dict(release=WOVar.wo_version, nginx=nginxf2b)
|
||||
olsf2b = bool(os.path.exists('/usr/local/lsws/bin/openlitespeed'))
|
||||
data = dict(release=WOVar.wo_version, ols=olsf2b)
|
||||
WOTemplate.deploy(
|
||||
self,
|
||||
'/etc/fail2ban/jail.d/custom.conf',
|
||||
@@ -820,7 +501,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
WOService.restart_service(self, 'proftpd')
|
||||
|
||||
if os.path.isfile('/etc/ufw/ufw.conf'):
|
||||
# add rule for proftpd with UFW
|
||||
if WOFileUtils.grepcheck(
|
||||
self, '/etc/ufw/ufw.conf', 'ENABLED=yes'):
|
||||
try:
|
||||
@@ -861,18 +541,15 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
Log.failed(self, "Configuring Sendmail")
|
||||
|
||||
if "ufw" in apt_packages:
|
||||
# check if ufw is already enabled
|
||||
if not WOFileUtils.grep(self,
|
||||
'/etc/ufw/ufw.conf', 'ENABLED=yes'):
|
||||
Log.wait(self, "Configuring UFW")
|
||||
# check if ufw script is already created
|
||||
if not os.path.isfile("/opt/ufw.sh"):
|
||||
data = dict()
|
||||
WOTemplate.deploy(self, '/opt/ufw.sh',
|
||||
'ufw.mustache',
|
||||
data, overwrite=False)
|
||||
WOFileUtils.chmod(self, "/opt/ufw.sh", 0o700)
|
||||
# setup ufw rules
|
||||
WOShellExec.cmd_exec(self, "bash /opt/ufw.sh")
|
||||
Log.valide(self, "Configuring UFW")
|
||||
else:
|
||||
@@ -880,31 +557,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
|
||||
# Redis configuration
|
||||
if "redis-server" in apt_packages:
|
||||
if os.path.isfile("/etc/nginx/conf.d/upstream.conf"):
|
||||
if not WOFileUtils.grep(self, "/etc/nginx/conf.d/"
|
||||
"upstream.conf",
|
||||
"redis"):
|
||||
with open("/etc/nginx/conf.d/upstream.conf",
|
||||
"a") as redis_file:
|
||||
redis_file.write("upstream redis {\n"
|
||||
" server 127.0.0.1:6379;\n"
|
||||
" keepalive 10;\n}\n")
|
||||
|
||||
if os.path.isfile("/etc/nginx/nginx.conf"):
|
||||
if not os.path.isfile("/etc/nginx/conf.d/redis.conf"):
|
||||
with open("/etc/nginx/conf.d/redis.conf",
|
||||
"a") as redis_file:
|
||||
redis_file.write(
|
||||
"# Log format Settings\n"
|
||||
"log_format rt_cache_redis '$remote_addr "
|
||||
"$upstream_response_time $srcache_fetch_status "
|
||||
"[$time_local] '\n '$host \"$request\" "
|
||||
"$status $body_bytes_sent '\n'\"$http_referer\" "
|
||||
"\"$http_user_agent\"';\n")
|
||||
# set redis.conf parameter
|
||||
# set maxmemory 10% for ram below 512MB and 20% for others
|
||||
# set maxmemory-policy allkeys-lru
|
||||
# enable systemd service
|
||||
WOGit.add(self, ["/etc/redis"],
|
||||
msg="Adding Redis into Git")
|
||||
Log.debug(self, "Enabling redis systemd service")
|
||||
@@ -1157,7 +809,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
wo_grant_host = self.app.config.get('mysql', 'grant-host')
|
||||
else:
|
||||
wo_grant_host = 'localhost'
|
||||
# check if mysql credentials are available
|
||||
if (WOMysql.mariadb_ping(self)
|
||||
and wo_grant_host == 'localhost'):
|
||||
try:
|
||||
@@ -1284,20 +935,6 @@ def post_pref(self, apt_packages, packages, upgrade=False):
|
||||
for x in packages):
|
||||
WOFileUtils.chmod(self, "/usr/bin/pt-query-advisor", 0o775)
|
||||
|
||||
# ngxblocker
|
||||
if any('/usr/local/sbin/install-ngxblocker' == x[1]
|
||||
for x in packages):
|
||||
# remove duplicate directives
|
||||
if os.path.exists('/etc/nginx/conf.d/variables-hash.conf'):
|
||||
WOFileUtils.rm(self, '/etc/nginx/conf.d/variables-hash.conf')
|
||||
WOFileUtils.chmod(
|
||||
self, "/usr/local/sbin/install-ngxblocker", 0o700)
|
||||
WOShellExec.cmd_exec(self, '/usr/local/sbin/install-ngxblocker -x')
|
||||
WOFileUtils.chmod(
|
||||
self, "/usr/local/sbin/update-ngxblocker", 0o700)
|
||||
if not WOService.restart_service(self, 'nginx'):
|
||||
Log.error(self, 'ngxblocker install failed')
|
||||
|
||||
|
||||
def pre_stack(self):
|
||||
"""Inital server configuration and tweak"""
|
||||
@@ -1309,20 +946,15 @@ def pre_stack(self):
|
||||
if os.path.exists('/var/lib/wo/version.txt'):
|
||||
with open('/var/lib/wo/version.txt',
|
||||
mode='r', encoding='utf-8') as wo_ver:
|
||||
# check version written in version.txt
|
||||
wo_check = bool(wo_ver.read().strip() ==
|
||||
'{0}'.format(WOVar.wo_version))
|
||||
else:
|
||||
wo_check = False
|
||||
if wo_check is False:
|
||||
# wo sysctl tweaks
|
||||
# check system type
|
||||
wo_arch = bool((os.uname()[4]) == 'x86_64')
|
||||
if os.path.isfile('/proc/1/environ'):
|
||||
# detect lxc containers
|
||||
wo_lxc = WOFileUtils.grepcheck(
|
||||
self, '/proc/1/environ', 'container=lxc')
|
||||
# detect wsl
|
||||
wo_wsl = WOFileUtils.grepcheck(
|
||||
self, '/proc/1/environ', 'wsl')
|
||||
else:
|
||||
@@ -1334,12 +966,12 @@ def pre_stack(self):
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/sysctl.d/60-wo-tweaks.conf',
|
||||
'sysctl.mustache', data, True)
|
||||
# use tcp_bbr congestion algorithm only on new kernels
|
||||
if (WOVar.wo_platform_codename == 'focal' or
|
||||
WOVar.wo_platform_codename == 'buster' or
|
||||
WOVar.wo_platform_codename == 'jammy' or
|
||||
WOVar.wo_platform_codename == 'bullseye' or
|
||||
WOVar.wo_platform_codename == 'bookworm'):
|
||||
WOVar.wo_platform_codename == 'bookworm' or
|
||||
WOVar.wo_platform_codename == 'trixie'):
|
||||
try:
|
||||
WOShellExec.cmd_exec(
|
||||
self, 'modprobe tcp_bbr')
|
||||
@@ -1373,7 +1005,6 @@ def pre_stack(self):
|
||||
Log.debug(self, str(e))
|
||||
Log.warn(self, "failed to tweak sysctl")
|
||||
|
||||
# apply sysctl tweaks
|
||||
WOShellExec.cmd_exec(
|
||||
self, 'sysctl -eq -p /etc/sysctl.d/60-wo-tweaks.conf')
|
||||
|
||||
@@ -1401,9 +1032,7 @@ def pre_stack(self):
|
||||
'root soft nofile 500000\n')
|
||||
# custom motd-news
|
||||
data = dict()
|
||||
# check if update-motd.d directory exist
|
||||
if os.path.isdir('/etc/update-motd.d/'):
|
||||
# render custom motd template
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/update-motd.d/98-wo-update',
|
||||
'wo-update.mustache', data)
|
||||
|
||||
Reference in New Issue
Block a user