1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00
Files
mariadb-AlanMologorsky a079a2c944 MCOL-5496: Merge CMAPI code to engine repo.
[add] cmapi code to engine
2023-06-07 10:00:16 +03:00

232 lines
8.5 KiB
Python

"""Module contains systemd process dispatcher class implementation."""
import logging
import re
from typing import Union, Tuple
from cmapi_server.process_dispatchers.base import BaseDispatcher
class SystemdDispatcher(BaseDispatcher):
"""Manipulates with systemd services."""
systemctl_version: int = 219 #CentOS 7 version
@classmethod
def _systemctl_call(
cls, command: str, service: str, use_sudo: bool = True,
return_output=False, *args, **kwargs
) -> Union[Tuple[bool, str], bool]:
"""Run "systemctl" with arguments.
:param command: command for systemctl
:type command: str
:param service: systemd service name
:type service: str
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: return status of operation, True if success, otherwise False
:rtype: Union[Tuple[bool, str], bool]
"""
cmd = f'systemctl {command} {service}'
if use_sudo:
cmd = f'sudo {cmd}'
logging.debug(f'Call "{command}" on service "{service}" with "{cmd}".')
success, output = cls.exec_command(cmd, *args, **kwargs)
if return_output:
return success, output
return success
@classmethod
def init(cls):
cmd = 'systemctl --version'
success, output = cls.exec_command(cmd)
if success:
# raw result will be like
# "systemd 239 (245.4-4ubuntu3.17)\n <string with compile flags>"
cls.systemctl_version = int(
re.search(r'systemd (\d+)', output).group(1)
)
logging.info(f'Detected {cls.systemctl_version} SYSTEMD version.')
else:
logging.error('Couldn\'t detect SYSTEMD version')
@classmethod
def is_service_running(cls, service: str, use_sudo: bool = True) -> bool:
"""Check if systemd service is running.
:param service: service name
:type service: str, optional
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: True if service is running, otherwise False
:rtype: bool
..Note:
Not working with multiple services at a time.
"""
logging.debug(f'Checking "{service}" is running.')
# TODO: remove conditions below when we'll drop CentOS 7 support
cmd = 'show -p ActiveState --value'
if cls.systemctl_version < 230: # not supported --value in old version
cmd = 'show -p ActiveState'
_, output = cls._systemctl_call(
cmd,
service, use_sudo, return_output=True
)
service_state = output.strip()
if cls.systemctl_version < 230: # result like 'ActiveState=active'
service_state = service_state.split('=')[1]
logging.debug(f'Service "{service}" is in "{service_state}" state')
# interpret non "active" state as not running service
if service_state == 'active':
return True
# output could be inactive, activating or even empty if
# command execution was unsuccessfull
return False
@staticmethod
def _workernode_get_service_name(is_primary: bool) -> str:
"""Get proper workernode service name based on primary status.
:param is_primary: is node where we running primary?
:type is_primary: bool
:return: correct workernode service name
:rtype: str
"""
service = 'mcs-workernode'
return f'{service}@1.service' if is_primary else f'{service}@2.service'
@classmethod
def _workernode_enable(cls, enable: bool, use_sudo: bool = True) -> None:
"""Enable workernode service.
:param enable: enable or disable
:type enable: bool
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
"""
sub_cmd = 'enable' if enable else 'disable'
service = 'mcs-workernode@1.service'
if not cls._systemctl_call(sub_cmd, service, use_sudo):
# enabling\disabling service is not critical, just log failure
logging.warning(f'Failed to {sub_cmd} {service}')
@classmethod
def start(
cls, service: str, is_primary: bool = True, use_sudo: bool = True
) -> bool:
"""Start systemd service.
:param service: service name
:type service: str, optional
:param is_primary: is node primary or not
:type is_primary: bool, optional
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: True if service started successfully
:rtype: bool
"""
service_name = service
if service_name == 'mcs-workernode':
service_name = cls._workernode_get_service_name(is_primary)
if is_primary:
cls._workernode_enable(True, use_sudo)
if cls.is_service_running(service_name, use_sudo):
return True
logging.debug(f'Starting "{service_name}".')
if not cls._systemctl_call('start', service_name, use_sudo):
logging.error(f'Failed while starting "{service_name}".')
return False
if is_primary and service == 'mcs-ddlproc':
cls._run_dbbuilder(use_su=True)
logging.debug(f'Successfully started {service_name}.')
return cls.is_service_running(service_name, use_sudo)
@classmethod
def stop(
cls, service: str, is_primary: bool = True, use_sudo: bool = True
) -> bool:
"""Stop systemd service.
:param service: service name
:type service: str, optional
:param is_primary: is node primary or not
:type is_primary: bool, optional
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: True if service stopped successfully
:rtype: bool
"""
service_name = service
if service_name == 'mcs-workernode':
service_name = f'{service_name}@1.service {service_name}@2.service'
cls._workernode_enable(False, use_sudo)
logging.debug(f'Stopping "{service_name}".')
if not cls._systemctl_call('stop', service_name, use_sudo):
logging.error(f'Failed while stopping "{service_name}".')
return False
return not cls.is_service_running(service, use_sudo)
@classmethod
def restart(
cls, service: str, is_primary: bool = True, use_sudo: bool = True
) -> bool:
"""Restart systemd service.
:param service: service name
:type service: str, optional
:param is_primary: is node primary or not, defaults to True
:type is_primary: bool, optional
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: True if service restarted successfully
:rtype: bool
"""
service_name = service
if service_name == 'mcs-workernode':
service_name = cls._workernode_get_service_name(is_primary)
logging.debug(f'Restarting "{service_name}".')
if not cls._systemctl_call('restart', service_name, use_sudo):
logging.error(f'Failed while restarting "{service_name}".')
return False
return cls.is_service_running(service, use_sudo)
@classmethod
def reload(
cls, service: str, is_primary: bool = True, use_sudo: bool=True
) -> bool:
"""Reload systemd service.
:param service: service name, defaults to 'Unknown_service'
:type service: str, optional
:param is_primary: is node primary or not, defaults to True
:type is_primary: bool, optional
:param use_sudo: use sudo or not, defaults to True
:type use_sudo: bool, optional
:return: True if service reloaded successfully
:rtype: bool
..NOTE: For next releases. It should become important when we teach
MCS to add/remove nodes w/o whole cluster restart.
Additional error handling?
"""
service_name = service
if service_name == 'mcs-workernode':
service_name = cls._workernode_get_service_name(is_primary)
logging.debug(f'Reloading "{service_name}".')
if not cls._systemctl_call('reload', service_name, use_sudo):
logging.error(f'Failed while reloading "{service_name}".')
return False
return not cls.is_service_running(service, use_sudo)