Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/cl_plus/daemon/daemon_control.py
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2020 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT
#

import os
import platform
import shutil
import logging
from .logsetup import setup_logging

_log = None

SENDER_SERVICE_NAME = 'cl_plus_sender'
NODE_EXPORTER_SERVICE_NAME = 'cl_node_exporter'
OLD_NODE_EXPORTER_SERVICE_NAME = 'node_exporter'

_CL_PLUS_DIR: str = '/usr/share/cloudlinux/cl_plus'
_CL_PLUS_SERVICE_DIR: str = f'{_CL_PLUS_DIR}/service'
# CL6 service files
_CL6_SERVICE_FILE_PATH: str = '/etc/rc.d/init.d'
# CL7/8 service files
_CL7_8_SYSTEMD_UNIT_FILE_PATH: str = '/etc/systemd/system'

# Utilities
_SERVICE_BIN = '/sbin/service'
_CHKCONFIG_BIN = '/sbin/chkconfig'
_SYSTEMCTL_BIN = '/usr/bin/systemctl'


def init_logging():
    setup_logging(console_level=logging.CRITICAL)
    global _log, _collectors_list
    _log = logging.getLogger('CL+_sender_daemon')


def _install_daemon(service_name: str, is_cl6: bool):
    """
    Install Node Exporter daemon to system
    :param service_name: name of service to install
    :param is_cl6: True is working on CL6, False - CL7/8
    :return: None
    """
    if is_cl6:
        # CL6
        # /usr/share/cloudlinux/cl_plus/service/{service_name} -> /etc/rc.d/init.d/{service_name}
        src = os.path.join(_CL_PLUS_SERVICE_DIR, service_name)
        dest = os.path.join(_CL6_SERVICE_FILE_PATH, service_name)
        if os.path.isfile(dest):
            # daemon is installed skip
            return
        shutil.copy(src, dest)
        os.chmod(dest, 0o755)
        # Start service
        os.system(f'{_SERVICE_BIN} {service_name} start >/dev/null 2>&1')
        os.system(f'{_CHKCONFIG_BIN} --add {service_name} >/dev/null 2>&1')
    else:
        # CL7, CL8
        # /usr/share/cloudlinux/cl_plus/service/{service_name}.service -> /etc/systemd/system/{service_name}.service
        src = os.path.join(_CL_PLUS_SERVICE_DIR, f'{service_name}.service')
        dest = os.path.join(_CL7_8_SYSTEMD_UNIT_FILE_PATH, f'{service_name}.service')
        if os.path.isfile(dest):
            # daemon is installed skip
            return
        shutil.copy(src, dest)
        os.chmod(dest, 0o644)
        os.system(f'{_SYSTEMCTL_BIN} daemon-reload')
        # Start daemon
        os.system(f'{_SYSTEMCTL_BIN} enable {service_name}')
        os.system(f'{_SYSTEMCTL_BIN} start {service_name}')


def _stop_and_remove_daemon(service_name: str, is_cl6: bool):
    """
    Stop and remove daemon from system
    :param service_name: name of service to remove
    :param is_cl6: True is working on CL6, False - CL7/8
    :return:
    """
    if is_cl6:
        # CL6
        service_file = os.path.join(_CL6_SERVICE_FILE_PATH, service_name)
        if not os.path.exists(service_file):
            return
        os.system(f'{_SERVICE_BIN} {service_name} stop >/dev/null 2>&1')
        os.system(f'{_CHKCONFIG_BIN} --del {service_name} >/dev/null 2>&1')
        try:
            # Remove /etc/rc.d/init.d/{service_name}
            os.remove(service_file)
        except (OSError, IOError):
            pass
    else:
        # CL7, CL8
        unit_file = os.path.join(_CL7_8_SYSTEMD_UNIT_FILE_PATH, f'{service_name}.service')
        if not os.path.exists(unit_file):
            return
        os.system(f'{_SYSTEMCTL_BIN} kill {service_name} >/dev/null 2>&1')
        os.system(f'{_SYSTEMCTL_BIN} disable {service_name} >/dev/null 2>&1')
        try:
            # Remove /etc/systemd/system/{service_name}.service
            os.remove(unit_file)
        except (OSError, IOError):
            pass
        os.system(f'{_SYSTEMCTL_BIN} daemon-reload >/dev/null 2>&1')


def _is_cl_node_exporter(service_path):
    """
    Checks that node_exporter service is ours and we must stop it
    path /usr/share/cloudlinux/cl_plus/node_exporter must be present in our service
    """
    with open(service_path) as f:
        data = f.read()
    return f'{_CL_PLUS_DIR}/node_exporter' in data


def clean_service_if_needed(service_name, is_cl6):
    if is_cl6:
        service_file = os.path.join(_CL6_SERVICE_FILE_PATH, service_name)
    else:
        service_file = os.path.join(_CL7_8_SYSTEMD_UNIT_FILE_PATH, f'{service_name}.service')
    if not os.path.exists(service_file) or not _is_cl_node_exporter(service_file):
        return
    _log.info('%s service detected, going to cleanup' % service_name)
    _stop_and_remove_daemon(service_name, is_cl6)


def install_daemons():
    """
    Install CL+ sender and Node exporter daemons to system
    :return:
    """
    init_logging()
    is_cl6 = 'el6' in platform.release()
    _install_daemon(NODE_EXPORTER_SERVICE_NAME, is_cl6)
    _install_daemon(SENDER_SERVICE_NAME, is_cl6)
    _log.info("CloudLinux+ daemons are installed")


def remove_daemons():
    """
    Remove CL+ sender and Node exporter daemons from system
    :return:
    """
    init_logging()
    is_cl6 = 'el6' in platform.release()
    _stop_and_remove_daemon(SENDER_SERVICE_NAME, is_cl6)
    _stop_and_remove_daemon(NODE_EXPORTER_SERVICE_NAME, is_cl6)
    clean_service_if_needed(OLD_NODE_EXPORTER_SERVICE_NAME, is_cl6)
    _log.info("CloudLinux+ daemons are removed")