AlkantarClanX12

Your IP : 13.58.188.166


Current Path : /opt/cloudlinux/venv/lib64/python3.11/site-packages/clcommon/public_hooks/bundle/plesk/
Upload File :
Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/clcommon/public_hooks/bundle/plesk/manager.py

# -*- coding: utf-8 -*-

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

from packaging.version import Version

from clcommon import cpapi
from clcommon.cpapi.plugins.plesk import query_sql
from clcommon.public_hooks import CLOUDLINUX_HOOKS, CONTACT_SUPPORT_MESSAGE_FOOTER

BIN_DIR = os.path.join(CLOUDLINUX_HOOKS, 'plesk/')
PLESK_HOOK_REGISTER_FILE = '/usr/local/psa/bin/event_handler'

HOOKS = {
    'phys_hosting_create': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_created')},
    'phys_hosting_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_updated')},
    'phys_hosting_delete': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_deleted')},
    'domain_update':       {'SCRIPT_FILE': os.path.join(BIN_DIR, 'domain_updated')},
    'template_domain_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'plan_updated')},
    'template_admin_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'plan_updated')},
}

logger = logging.getLogger(__name__)


def plesk_get_event_handler_name_list(plesk_version, bin_dir=BIN_DIR):
    """
    Get all installed events. Assume that we always have
    no more than one executable file of each event type.
    :param plesk_version: plesk cp version;
    :param bin_dir: filter hooks by directory where
                    their executable is stored
    :return: Dictionary of already installed events
             {event_name: {handler_id, action_id, command}}
    """
    if Version(plesk_version) < Version('17.8'):
        sql = "SELECT a.name, eh.id, eh.action_id, eh.command " \
              "FROM psa.event_handlers eh " \
              "INNER JOIN psa.actions a ON a.id=eh.action_id"
    else:
        sql = "SELECT action_name, id, 0, command " \
              "FROM psa.event_handlers"
    rows = query_sql(sql)
    result = {}
    for row in rows:
        # row[0] - action_name
        # row[1] - handler id
        # row[2] - action id (deprecated in PLESK 17.8 )
        # row[3] - command
        # take only hooks installed by this lib
        if bin_dir in row[3]:
            result[row[0]] = {
                'handler_id': int(row[1]),
                'action_id': int(row[2]),
                'command': row[3]
            }
    return result


def plesk_hook_install(event_id, script_file):
    """
    Plesk single hook install into /usr/local/bin
    :param event_id: Event ID that must be created
    :param script_file: Command for event
    """
    if not os.path.isfile(script_file):
        logger.error("File '%s' does not exist. "
                     "Maybe some rpm package is malformed. %s",
                     script_file, CONTACT_SUPPORT_MESSAGE_FOOTER)
        return

    try:
        output = subprocess.check_output([
            PLESK_HOOK_REGISTER_FILE, '--create',
            '-command', script_file, '-priority', '50',
            '-user', 'root', '-event', str(event_id)
        ], stderr=subprocess.STDOUT, text=True)
    except (OSError, subprocess.CalledProcessError) as e:
        if isinstance(e, subprocess.CalledProcessError):
            message = e.output.rstrip('\n')
        else:
            message = str(e)
        logger.error('failed to register Plesk hook %s: %s. %s',
                     script_file, message, CONTACT_SUPPORT_MESSAGE_FOOTER)
    else:
        logger.info('Register hook ended successfully; tool output: `%s`', output.rstrip())


def plesk_hook_remove(handler_id):
    """
    Remove Plesk hook
    :param handler_id: Handler ID of created event
    :return: Nothing
    """
    try:
        output = subprocess.check_output([
            PLESK_HOOK_REGISTER_FILE, '--delete', str(handler_id)
        ], stderr=subprocess.STDOUT, text=True)
    except (OSError, subprocess.CalledProcessError) as e:
        if isinstance(e, subprocess.CalledProcessError):
            message = e.output.rstrip('\n')
        else:
            message = str(e)
        logger.error('Failed to unregister Plesk hook %s. '
                     'Plesk reported following error: %s. %s',
                     handler_id, message, CONTACT_SUPPORT_MESSAGE_FOOTER)
    else:
        logger.info('Unregister hook ended successfully; tool output: `%s`', output.rstrip())


def define_hook_ids():
    """
    Defines IDs of hooks by name (extends existing PLESK_HOOKS)
    """
    hook_names_str = ','.join([f"'{i}'" for i in HOOKS])
    sql = f"SELECT id, name FROM psa.actions WHERE name IN({hook_names_str})"
    rows = query_sql(sql)
    if not rows:
        return
    for row in rows:
        HOOKS[row[1]]['EVENT_ID'] = int(row[0])


def install_hooks():
    """
    Plesk hooks install into /usr/local/bin
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        logger.warning('%s does not exist; skip installing hooks', PLESK_HOOK_REGISTER_FILE)
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    if Version(plesk_version) < Version('17.8'):
        define_hook_ids()

    installed_events = plesk_get_event_handler_name_list(plesk_version)
    for hook_name, hook_data in HOOKS.items():
        # Checking of not installed events
        if hook_name in installed_events:
            logger.info('Hook %s is already registered in plesk; skip', hook_name)
            continue
        logger.debug('Registering %s action hook', hook_name)
        if Version(plesk_version) < Version('17.8'):
            plesk_hook_install(hook_data['EVENT_ID'], hook_data['SCRIPT_FILE'])
        else:
            plesk_hook_install(hook_name, hook_data['SCRIPT_FILE'])


def remove_hooks():
    """
    All Plesk hooks remove
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        logger.warning('%s does not exist; skip installing hooks', PLESK_HOOK_REGISTER_FILE)
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    installed_events = plesk_get_event_handler_name_list(plesk_version)

    for hook in HOOKS:
        if hook not in installed_events:
            logger.info('Hook %s is not registered in plesk; skip', hook)
            continue
        logger.debug('Unregistering %s action hook', hook)
        plesk_hook_remove(installed_events[hook]['handler_id'])


# DOT NOT CHANGE OR REMOVE: USED IN CAGEFS
def remove_hook_by_str_id_and_location(event_name, bin_dir):
    """
    Remove hooks of given
    :param event_name: string id of hook type,
                       like 'phys_hosting_create'
    :param bin_dir: directory where we should search
                    for hooks subscribed to given event name
    :return: Nothing
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    installed_events = plesk_get_event_handler_name_list(plesk_version, bin_dir)

    if event_name not in installed_events:
        return
    plesk_hook_remove(installed_events[event_name]['handler_id'])