Mini Shell
# vim: set ts=4 sw=4 expandtab syntax=python:
"""
ngxconf.control
Process Control
Copyright (c) 2018-2020 InMotion Hosting, Inc.
http://www.inmotionhosting.com/
@author J. Hipps <jacobh@inmotionhosting.com>
"""
import re
import sys
import os
import time
import subprocess
import logging
import logging.handlers
import json
from glob import glob
from argparse import ArgumentParser, Action
from signal import SIGHUP, SIGUSR1, SIGUSR2
import yaml
from yaml import CLoader
from ngxconf import builder
from ngxconf.util import gconf, cancel_cp_pending_tasks
from ngxconf import __version__, __date__, gconf_defaults
__all__ = ['check_nginx', 'reload_nginx', 'rebuild_phpfpm', 'cp_rebuild_httpd_conf',
'rebuild_phpfpm_list', 'reload_phpfpm', 'restart_phpfpm']
logger = logging.getLogger('ngxconf')
def check_nginx(nginx_path="/sbin/nginx", failfile="/var/run/nginx.fail"):
"""
Perform an Nginx configuration check (`nginx -t`)
"""
logger.info("Validating Nginx configuration...")
try:
ngxtest = subprocess.Popen([nginx_path, '-t'], stderr=subprocess.PIPE, stdout=subprocess.PIPE, encoding='UTF-8')
serr = ngxtest.communicate()[1]
retval = ngxtest.returncode
except Exception as e:
logger.error("Failed to execute Nginx configuration test: %s", str(e))
return False
if retval != 0:
logger.error("Nginx config validation failed (return code %d):\n%s", retval, serr)
try:
with open(failfile, 'w') as f:
f.write(serr)
logger.warning("Triggered failfile: %s", failfile)
except Exception as e:
logger.error("Failed to write failfile: %s", str(e))
return False
else:
# Ensure failfile does not exist
logger.info("Nginx config validated successfully")
try:
os.unlink(failfile)
except:
pass
return True
def reload_nginx(pidfile="/var/run/nginx.pid"):
"""
Reloads the Nginx master process
"""
logger.info("Triggering Nginx config reload...")
try:
with open(pidfile, 'r') as f:
ngxpid = int(f.read().strip())
except Exception as e:
logger.error("Failed to open %s; could not determine Nginx PID: %s", pidfile, str(e))
return False
try:
os.kill(ngxpid, SIGHUP)
logger.info("Sent SIGHUP to Nginx master process (%d)", ngxpid)
return True
except Exception as e:
logger.error("Failed to send SIGHUP signal to Nginx master (%d): %s", ngxpid, str(e))
return False
def rebuild_phpfpm(script_path="/scripts/php_fpm_config"):
"""
Full rebuild of PHP-FPM configuration via (modified) cPanel utility
"""
cancel_cp_pending_tasks('rebuild_fpm')
# Perform full rebuild
logger.info("Triggering full PHP-FPM config rebuild...")
try:
fprel = subprocess.Popen([script_path, '--rebuild'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
fprel.wait()
fprel.communicate()
retval = fprel.returncode
except Exception as e:
logger.error("Failed to execute %s: %s", script_path, str(e))
return False
if retval == 0:
logger.info("PHP-FPM config rebuilt")
else:
logger.warning("PHP-FPM builder exited uncleanly (%d)", retval)
return True
def reload_phpfpm():
"""
Reload all PHP-FPM masters gracefully
"""
fpms_reloaded = 0
for fmaster in glob('/opt/cpanel/*/root/usr/var/run/php-fpm/php-fpm.pid'):
try:
with open(fmaster, 'r') as f:
fpid = int(f.read().strip())
except:
logger.error("Failed to read PID file from %s", fmaster)
continue
try:
tphp = re.search('(ea-php[0-9]{2})', fmaster, re.I).group(1)
except:
tphp = 'ea-php'
try:
os.kill(fpid, SIGUSR2)
logger.info("Sent SIGUSR2 to PHP-FPM master process for %s (%d)", tphp, fpid)
fpms_reloaded += 1
except Exception as e:
logger.error("Failed to reload PHP-FPM master for %s (%d): %s", tphp, fpid, str(e))
continue
if fpms_reloaded == 0:
logger.warning("No FPM master PID files detected. PHP-FPM masters were NOT reloaded!")
return False
else:
logger.debug("%d FPM masters were reloaded OK", fpms_reloaded)
return True
def restart_phpfpm():
"""
Terminate and restart all PHP-FPM masters
"""
try:
fprel = subprocess.Popen(['/scripts/restartsrv_apache_php_fpm'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
fprel.wait()
fprel.communicate()
retval = fprel.returncode
except Exception as e:
logger.error("Failed to restart PHP-FPM: %s", str(e))
return False
if retval == 0:
logger.info("PHP-FPM restarted successfully")
else:
logger.warning("PHP-FPM restart returned non-zero exit code (%d)", retval)
return True
def cp_update_userdata_cache(user=None, force=False):
"""
Update cPanel userdata cache
If @user is specified, only that user is updated
"""
opts = []
if user: opts += [user]
if force: opts += ['--force']
try:
udc = subprocess.Popen(['/scripts/updateuserdatacache'] + opts,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
udc.wait()
udc.communicate()
retval = udc.returncode
except Exception as e:
logger.error("Failed to execute /scripts/updateuserdatacache: %s", str(e))
return False
if retval == 0:
logger.debug("Userdata cache updated")
return True
else:
logger.warning("Failed to update userdata cache; script returned non-zero exit code (%d)", retval)
return False
def cp_rebuild_httpd_conf(reload=False):
"""
Update cPanel HTTPd configuration, optionally reloading
"""
logger.debug("Queuing rebuildhttpdconf...")
try:
udc = subprocess.Popen(['/usr/local/cpanel/bin/servers_queue', 'queue', 'build_apache_conf'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
udc.wait()
udc.communicate()
retval = udc.returncode
except Exception as e:
logger.error("Failed to queue rebuildhttpdconf: %s", str(e))
return False
if retval == 0:
logger.debug("rebuildhttpdconf queued successfully")
else:
logger.warning("Failed to queue rebuildhttpdconf; script returned non-zero exit code (%d)", retval)
return False
if reload is True:
logger.debug("Queuing apache_restart restart...")
try:
udc = subprocess.Popen(['/usr/local/cpanel/bin/servers_queue', 'queue', 'apache_restart'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
udc.wait()
udc.communicate()
retval = udc.returncode
except Exception as e:
logger.error("Failed to queue apache_restart : %s", str(e))
return False
if retval == 0:
logger.debug("apache_restart queued successfully")
else:
logger.warning("Failed to queue apache_restart; script returned non-zero exit code (%d)", retval)
return False
return True
Zerion Mini Shell 1.0