/usr/lib/imh-ultrastack-common/venv/lib/python3.13/site-packages/ngxconf
NameSizeModeActions
__pycache__/-0755rm
builder.py318650644editdlrm
cli.py99970644editdlrm
control.py74070644editdlrm
cpfpm.py99180644editdlrm
fpm.py156450644editdlrm
masters.py67570644editdlrm
util.py132480644editdlrm
__init__.py66500644editdlrm
Edit: /usr/lib/imh-ultrastack-common/venv/lib/python3.13/site-packages/ngxconf/cpfpm.py (9918B)
# vim: set ts=4 sw=4 expandtab syntax=python: """ ngxconf.cpfpm cPanel PHP-FPM Interface cPanel FPM system with master-per-version schema Copyright (c) 2017-2020 InMotion Hosting, Inc. http://www.inmotionhosting.com/ @author J. Hipps """ # pylint: disable=invalid-name import os import re import hashlib import logging from glob import glob import requests import yaml from yaml import CLoader from fpmstatus.fpm import fetch_pools from ngxconf.util import WHMAPI, gconf, NGXDumper logger = logging.getLogger('ngxconf') whm = None phpvers = None pkgdata = None userlist = None default_phpver = None udom_map = {} def kill_orphans(): """ Parse all PHP-FPM pool config files and check that the chdir path exists. If not, the config file is renamed with a `.orphaned` suffix. """ stat = {'tot': 0, 'orphans': 0, 'errors': 0} logger.debug("Checking for orphaned pool configuration files...") plist = fetch_pools() if not plist: logger.warning("No pools detected") return for pname, tpool in plist.items(): stat['tot'] += 1 try: if not os.path.exists(tpool.get('chdir', '/')): newpath = tpool['_confpath'] + '.orphaned' os.rename(tpool['_confpath'], newpath) logger.warning("Orphaned pool for domain %s: %s -> %s", tpool['_vhost'], tpool['_confpath'], newpath) stat['orphans'] += 1 except Exception as e: stat['errors'] += 1 logger.error("Failed to fix orphaned pool [%s] at %s: %s", tpool['_vhost'], tpool['_confphat'], str(e)) continue logger.debug("%d total pools, %d orphans fixed, %d errors", stat['tot'], stat['orphans'], stat['errors']) def update_fpm(user, vlist, force=False): """ Ensure PHP-FPM is enabled for all domains on user's account """ # Establish new connection, otherwise, reuse existing connection global whm if whm is None: whm = WHMAPI('localhost') udata = get_user_data(user) upkg = get_package_data(udata.get('plan')) # Find existing PHP-FPM configs existing_conf = glob("/var/cpanel/userdata/%s/*.php-fpm.yaml" % (user)) actual_conf = [] tupdate = [] for tdom in vlist: realdom = get_real_domain(user, tdom) tver = get_user_php_version(user, realdom) tver_norm = xlate_phpver(tver) fyaml = "/var/cpanel/userdata/%s/%s.php-fpm.yaml" % (user, realdom) fconf = "/opt/cpanel/%s/root/etc/php-fpm.d/%s.conf" % (tver_norm, realdom) cpconf = "/var/cpanel/users/%s" % (user) actual_conf.append(fyaml) # Check mtime of conf files try: ytime = os.stat(fyaml).st_mtime except: ytime = 0 try: ctime = os.stat(fconf).st_mtime except: ctime = 0 try: cptime = os.stat(cpconf).st_mtime except: cptime = 0 if force or ytime > ctime or cptime > ytime or 0 in (ytime, ctime, cptime): if gconf.enforce_fpm_limits: tcall = { '_is_present': 1, 'pm_max_children': int(upkg.get('IMH_FPM_WORKERS', 5)), 'pm_process_idle_timeout': int(upkg.get('IMH_FPM_IDLE', 60)), 'pm_max_requests': int(upkg.get('IMH_FPM_MAXREQ', 128)), 'rlimit_files': gconf.fpm_worker_rlimit_files, } else: try: with open(fyaml, 'r') as f: tcall = yaml.load(f, Loader=CLoader) or {} except: tcall = {} tcall.update({'_is_present': 1}) try: with open(fyaml, 'w') as f: f.write('---\n') yaml.dump(tcall, stream=f, Dumper=NGXDumper, default_flow_style=False) tupdate.append(realdom) logger.debug("Wrote updated PHP-FPM limits for %s:%s", user, realdom) except Exception as e: logger.error("Failed to write updated PHP-FPM config [%s:%s]: %s", user, realdom, str(e)) else: logger.debug("PHP-FPM config for %s:%s is up-to-date", user, tdom) # Purge any domains that have been removed leftovers = set(existing_conf) - set(actual_conf) if len(leftovers): for tconf in leftovers: try: os.unlink(tconf) os.unlink(re.sub(r'\.yaml$', '.cache', tconf)) logger.info("Removed stale PHP-FPM config file: %s", tconf) except Exception as e: logger.error("Failed to remove stale PHP-FPM config file: %s", str(e)) return tupdate def get_user_php_version(user, vhost): """ Return selected PHP version for user/vhost On first run, queries whmapi1 for a list of all selected versions If the version cannot be determined, 'inherit' is returned """ global whm global phpvers if whm is None: whm = WHMAPI('localhost') if phpvers is None: # Query WHMAPI1 for list of all user version selections rargs = {'api.version': 1} rout = whm.whm('php_get_vhost_versions', **rargs) if rout.get('data') and rout['data'].get('versions'): phpvers = {} try: for thost in rout['data']['versions']: if thost['account'] not in phpvers: phpvers[thost['account']] = {thost['vhost']: thost['version']} else: phpvers[thost['account']][thost['vhost']] = thost['version'] except Exception as e: logger.error("Failed to enumerate user PHP version selections: %s", str(e)) return 'inherit' else: logger.error("Failed to enumerate user PHP version selections: %s", rout) phpvers = {} return 'inherit' if not phpvers.get(user): logger.warning("get_user_php_version: no matching user") return 'inherit' elif not phpvers[user].get(vhost): logger.warning("get_user_php_version: no matching vhost") return 'inherit' else: return phpvers[user][vhost] def get_package_data(pkg): """ Return info for specified package Retrieves a list of packages and package limits, then caches it """ global whm global pkgdata if whm is None: whm = WHMAPI('localhost') if pkgdata is None: pkgdata = {} packraw = whm.whm('listpkgs').get('package') if packraw: for tpack in packraw: pkgdata[tpack['name']] = tpack else: logger.error("Failed to retrieve list of packages; packraw = %s", packraw) return {} if not pkgdata.get(pkg): logger.warning("get_package_data: no matching package '%s'", pkg) return {} else: return pkgdata.get(pkg) def get_user_data(user): """ Returns listacct data for @user Runs listaccts for all users, then caches that data """ global whm global userlist if whm is None: whm = WHMAPI('localhost') if userlist is None: userlist = {} uraw = whm.whm('listaccts').get('acct') if uraw: for tuser in uraw: try: userlist[tuser['user']] = tuser except Exception as e: logger.warning("Failed to parse acct data: %s", str(e)) else: logger.error("Failed to retrieve list of users; uraw = %s", uraw) return {} if not userlist.get(user): logger.warning("get_user_data: no matching user '%s'", user) return {} else: return userlist.get(user) def get_real_domain(user, dom): """ Determine 'real' domain name, return as string PHP-FPM uses the actual domain name (eg. addon.com) as opposed to the virtualhost name (eg. addon.primary.com) like cPanel uses for everything else. Awesome. """ # pylint: disable=global-variable-not-assigned global udom_map if user not in udom_map: try: with open("/var/cpanel/userdata/%s/main" % (user), 'r') as f: rudata = yaml.load(f, Loader=CLoader) except Exception as e: logger.error("Failed to read userdata/main for %s: %s", user, str(e)) udom_map[user] = {} return dom try: udom_map[user] = dict(zip(rudata.get('sub_domains', []), rudata.get('sub_domains', []))) udom_map[user].update({rudata.get('main_domain', ''): rudata.get('main_domain', '')}) udom_map[user].update(dict(zip(rudata['addon_domains'].values(), rudata['addon_domains'].keys()))) except Exception as e: logger.error("Failed to parse userdata/main for %s: %s", user, str(e)) udom_map[user] = {} return udom_map[user].get(dom, dom) def xlate_phpver(verstr): """ Translate PHP version string to normalized version (eg. 'inherit' -> 'ea-php56', etc.) """ global whm global default_phpver if verstr == 'inherit': if default_phpver is None: if whm is None: whm = WHMAPI('localhost') try: api1 = {'api.version': 1} default_phpver = whm.whm('php_get_system_default_version', **api1)['data']['version'] except Exception as e: logger.error("Failed to determine default system PHP version: %s", str(e)) logger.warning("Falling back to %s", gconf.fallback_phpver) default_phpver = gconf.fallback_phpver return default_phpver else: return verstr