Mini Shell

Direktori : /opt/bakmgr/lib/python3.6/site-packages/bakmgr/
Upload File :
Current File : //opt/bakmgr/lib/python3.6/site-packages/bakmgr/configs.py

"""Functions for reading and validating configs"""
import os
from pathlib import Path
import sys
import logging
from typing import Type
from logging.handlers import WatchedFileHandler
import yaml
from .server_info import is_vps

CONF_PATH = Path('/etc/bakmgr/bakmgr.yaml')
CRON_PATH = Path('/etc/cron.d/bakmgr')
LOG_PATH = Path('/var/log/bakmgr.log')
DEFAULT_FILES_INCL = [
    '/etc',
    '/root',
    '/home',
    '/srv',
    '/var/www',
    '/var/spool/cron',
    '/var/named',
    '/var/lib',
]
DEFAULT_FILES_EXCL = [
    '/root/.cache',
    '/var/lib/mysql',
    '/var/lib/pgsql',
]


class TaskConf:
    task: str
    enabled: bool

    def __init__(self, conf: 'Conf', task: str, label: str, cfg_dict: dict):
        self.conf = conf
        self.task = task
        self.label = label
        self.enabled = self._parse(cfg_dict, 'enabled', bool, True)
        self.interval = self._parse(cfg_dict, 'interval', int, 3)

    @property
    def __dict__(self):
        return {'enabled': self.enabled, 'interval': self.interval}

    def _parse(self, cfg_dict: dict, key: str, var_type: Type, default):
        if self.conf.read_error:
            return default
        try:
            val = cfg_dict[self.task][key]
        except (KeyError, TypeError):
            return default
        if isinstance(val, var_type):
            return val
        self.conf.errors.append(f"{self.task}:{key} is the incorrect type")
        return default


class FilesConf(TaskConf):
    def __init__(self, conf: 'Conf', cfg_dict: dict):
        super().__init__(conf, 'files', 'File Backups', cfg_dict)
        self.include = list(
            map(str, self._parse(cfg_dict, 'include', list, DEFAULT_FILES_INCL))
        )
        self.exclude = list(
            map(str, self._parse(cfg_dict, 'exclude', list, DEFAULT_FILES_EXCL))
        )

    @property
    def __dict__(self):
        val = super().__dict__
        val.update({'include': self.include, 'exclude': self.exclude})
        return val


class Conf:
    loglevel: str
    files: FilesConf
    mysql: TaskConf
    pgsql: TaskConf
    loglevel: str
    is_vps: bool
    max_cpus: int
    max_load: float

    def __init__(self):
        self.errors = []
        self.read_error = False
        try:
            with open(CONF_PATH, encoding='utf-8') as handle:
                cfg_dict = yaml.load(handle, yaml.SafeLoader)
        except (OSError, ValueError, yaml.error.YAMLError) as exc:
            self.errors.append(f"Error reading {CONF_PATH}: {exc}")
            cfg_dict = {}
            self.read_error = True
        if not isinstance(cfg_dict, dict):
            self.errors.append(f"{CONF_PATH} is malformed")
            cfg_dict = {}
            self.read_error = True
        self.files = FilesConf(self, cfg_dict)
        self.mysql = TaskConf(self, 'mysql', 'MySQL', cfg_dict)
        self.pgsql = TaskConf(self, 'pgsql', 'PgSQL', cfg_dict)
        try:
            loglevel = str(cfg_dict['loglevel']).upper()
        except KeyError:
            loglevel = None
        if loglevel not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'):
            if not self.read_error:
                self.errors.append("invalid loglevel")
            loglevel = 'INFO'
        self.loglevel = loglevel
        self.is_vps = is_vps()
        if self.is_vps:
            self.max_cpus = 1
        else:
            try:
                self.max_cpus = int(cfg_dict['max_cpus'])
            except (ValueError, KeyError):
                self.max_cpus = 0
            if self.max_cpus < 1:
                if not self.read_error:
                    self.errors.append("invalid max_cpus")
                self.max_cpus = 2
        try:
            self.max_load = float(cfg_dict['max_load'])
        except (ValueError, KeyError):
            self.max_load = 0.0
        if self.max_load < 0.0:
            if not self.read_error:
                self.errors.append("invalid max_load")
            self.max_load = 2.0 if self.is_vps else os.cpu_count() * 1.5

    @property
    def __dict__(self):
        return {
            'files': vars(self.files),
            'mysql': vars(self.mysql),
            'pgsql': vars(self.pgsql),
            'loglevel': self.loglevel,
            'max_load': self.max_load,
            'max_cpus': self.max_cpus,
        }

    def save(self):
        tmp = Path(f'{CONF_PATH}.tmp')
        with open(tmp, 'w', encoding='utf-8') as file:
            yaml.dump(vars(self), file, default_flow_style=False)
        tmp.rename(CONF_PATH)


def setup_logging(conf: Conf, name: str):
    """Sets up logging using with loglevel in bakmgr.yaml"""
    loglevel = int(getattr(logging, conf.loglevel))
    LOG_PATH.touch(mode=0o600, exist_ok=True)
    root = logging.getLogger()
    formatter = logging.Formatter(
        fmt='%(asctime)s {0} %(levelname)s %(message)s'.format(name),
        datefmt='%Y-%m-%d %H:%M:%S',
    )
    main_handler = WatchedFileHandler(str(LOG_PATH))
    main_handler.setFormatter(formatter)
    main_handler.setLevel(loglevel)
    root.addHandler(main_handler)
    print_handler = logging.StreamHandler(stream=sys.stdout)
    print_handler.setFormatter(formatter)
    print_handler.setLevel(loglevel)
    root.addHandler(print_handler)
    root.setLevel(loglevel)

Zerion Mini Shell 1.0