HOME


Mini Shell 1.0
DIR: /opt/cloudlinux/venv/lib64/python3.11/site-packages/xray/manager/
Upload File :
Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/xray/manager/plesk.py
# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

"""
This module contains classes implementing X-Ray Manager behaviour
for Plesk
"""

import os
import subprocess
import xml.etree.ElementTree as ET
from collections import ChainMap

from .base import BaseManager
from ..internal.exceptions import XRayManagerError
from ..internal.types import DomainInfo
from ..internal.user_plugin_utils import (
    user_mode_verification,
    with_fpm_reload_restricted
)


class PleskManager(BaseManager):
    """
    Class implementing an X-Ray manager behaviour for Plesk
    """

    VERSIONS_Plesk = {
        'plesk-php54': '/opt/plesk/php/5.4/etc/php.d',
        'plesk-php55': '/opt/plesk/php/5.5/etc/php.d',
        'plesk-php56': '/opt/plesk/php/5.6/etc/php.d',
        'plesk-php70': '/opt/plesk/php/7.0/etc/php.d',
        'plesk-php71': '/opt/plesk/php/7.1/etc/php.d',
        'plesk-php72': '/opt/plesk/php/7.2/etc/php.d',
        'plesk-php73': '/opt/plesk/php/7.3/etc/php.d',
        'plesk-php74': '/opt/plesk/php/7.4/etc/php.d',
        'plesk-php80': '/opt/plesk/php/8.0/etc/php.d',
        'plesk-php81': '/opt/plesk/php/8.1/etc/php.d',
        'plesk-php82': '/opt/plesk/php/8.2/etc/php.d',
        'plesk-php83': '/opt/plesk/php/8.3/etc/php.d'
    }

    def supported_versions(self) -> ChainMap:
        """
        Get supported PHP versions
        :return: a chained map with basic supported versions
                and Plesk supported versions
        """
        return ChainMap(self.VERSIONS,
                        self.VERSIONS_Plesk)

    @user_mode_verification
    @with_fpm_reload_restricted
    def get_domain_info(self, domain_name: str) -> DomainInfo:
        """
        Retrieve information about given domain from control panel environment:
        PHP version, user of domain, fpm status
        :param domain_name: name of domain
        :return: a DomainInfo object
        """

        def resolve_lsphp_version(h):
            """
            Resolve version for lsphp handlers
            :param h: original Plesk php handler id
            :return: resolved alt-php* version
            """
            if 'lsphp-custom' in h:
                ver = 'alt-php56'
            elif 'lsphp' in h:
                ver = f"alt-php{h.split('-')[-1]}"
            else:
                ver = '-'.join(h.split('-')[:2])
            return ver

        for domain, user, handler in (item for item in self.query_db() if
                                      item[0] == domain_name):
            self.logger.info(
                'Retrieved domain info: domain %s owned by %s uses php version %s',
                domain_name, user, handler)
            return DomainInfo(name=domain_name,
                              panel_php_version=resolve_lsphp_version(handler),
                              user=user,
                              panel_fpm='fpm' in handler,
                              handler=handler)
        self.logger.warning('Domain does not exist on the server',
                            extra={'domain_name': domain_name})
        raise XRayManagerError(
            f"Domain '{domain_name}' does not exist on this server",
            errno=1110, needs_logging=False)

    @staticmethod
    def query_db() -> tuple:
        """
        Query Plesk database through plesk db utility
        and yeild parsed xml result
        :return: tuple(domain_name, domain_user, domain_handler)
        """

        def check_path_env():
            """
            plesk db utility needs to be able to find mysql executable,
            which resides in /usr/bin.
            If we do not have it in PATH, the error will be thrown:
            'exec: "mysql": executable file not found in $PATH'
            """
            if '/usr/bin' not in os.environ.get('PATH', ''):
                return {'PATH': '/usr/bin'}
            else:
                return None

        query = """select d.name,s.login,h.php_handler_id from (select id, name from domains union select dom_id, name from domain_aliases) d join hosting h on d.id=h.dom_id join sys_users s on h.sys_user_id=s.id"""
        result = subprocess.run(['/usr/sbin/plesk',
                                 'db',
                                 query,
                                 '--xml'],
                                capture_output=True, text=True,
                                env=check_path_env())
        try:
            root = ET.fromstring(''.join(result.stdout))
            for row in root.iter('row'):
                domain_name = row.find("./field[@name='name']").text
                user_name = row.find("./field[@name='login']").text
                handler = row.find("./field[@name='php_handler_id']").text
                yield domain_name, user_name, handler
        except ET.ParseError as e:
            raise XRayManagerError(
                'Failed to parse XML from plesk db output: %s' % str(result.stdout)) from e

    def panel_specific_selector_enabled(self, domain_info: DomainInfo) -> bool:
        """
        Check if selector is enabled specifically for Plesk panel
        :param domain_info: a DomainInfo object
        :return: True if yes, False otherwise
        """

        def same_php_in_both_selectors():
            """
            Checks if php selector and cloudlinux selector have
            the same php version.
            :param domain_info: a DomainInfo object
            :return: digits as string or None
            """
            if domain_info.selector_php_version:
                return domain_info.selector_php_version[
                       -2:] in domain_info.handler
            return False

        if 'lsphp' in domain_info.handler:
            return ('custom' in domain_info.handler or
                    same_php_in_both_selectors())
        return not domain_info.panel_fpm

    def fpm_service_name(self, dom_info: DomainInfo) -> str:
        """
        Get Plesk FPM service name
        :param dom_info: a DomainInfo object
        :return: FPM service name
        """
        return dom_info.handler