Mini Shell

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

"""Setup script for bakmgr"""
import shlex
from argparse import ArgumentParser
import shutil
import platform
from subprocess import check_call, call, CalledProcessError
import sys
import os
import bz2
import json
from pathlib import Path
from typing import Union
import requests
from ..server_info import is_vps
from ..configs import Conf, CONF_PATH

REGISTER_URL = "https://ash-sys-pro-bakauth1.imhadmin.net:4002/register"


def _step(msg: str):  # cyan
    print(f"\033[96m{msg}\033[0m")


def _warn(msg: str):  # yellow
    print(f"\033[93;1m{msg}\033[0m")


def main():
    """Entry point for /opt/bakmgr/bin/bakmgr-setup"""
    if os.getuid() != 0:
        sys.exit('This tool must run as root')
    args = parse_args()
    # get the */site-packages/bakmgr folder
    module_root = Path(__file__).resolve().parent.parent
    download_restic()
    try:
        Path('/usr/bin/bakmgr').unlink()
    except FileNotFoundError:
        pass
    try:
        Path('/usr/bin/backup').unlink()
    except FileNotFoundError:
        pass
    os.symlink('/opt/bakmgr/bin/backup', '/usr/bin/bakmgr')
    os.symlink('/opt/bakmgr/bin/backup', '/usr/bin/backup')
    for var_dir in ('.cache', 'monitoring'):
        var_path = Path('/opt/bakmgr/var') / var_dir
        var_path.mkdir(parents=True, exist_ok=True)
    install_certs()
    install_service(module_root)
    install_cron(module_root)
    install_config()
    install_logrotate(module_root)
    install_autocomplete(module_root)
    if args.host == 'test':
        _warn("Skipping registration due to --host 'test'")
    elif args.host:
        register(args.host)


def parse_args():
    """Parse CLI args to /opt/bakmgr/bin/bakmgr-setup"""
    parser = ArgumentParser(__doc__)
    parser.add_argument(
        '--host',
        required=not Path('/etc/bakmgr/.auth.json').is_file(),
        help='hostname this server is known as by the billing system',
    )
    return parser.parse_args()


def _print_and_call(cmd, **kwargs):
    print(f"Running: {' '.join(shlex.quote(arg) for arg in cmd)}")
    try:
        return call(cmd, **kwargs)
    except OSError as exc:
        print(exc)
        return None


def install_service(module_root: Path):
    _step("Installing dashboard")
    dash_path = Path('/opt/bakmgr/dash')
    if not dash_path.is_symlink():
        dash_path.symlink_to(module_root / 'dash')
    init = Path('/sbin/init').resolve().name
    if init == 'systemd' and Path('/usr/lib/systemd/system').is_dir():
        systemd_setup(module_root)
    elif init == 'upstart' and Path('/etc/init').is_dir():
        upstart_setup(module_root)
    elif Path('/etc/init.d').is_dir():
        sysv_setup(module_root)
    else:
        print("Could not determine init system", file=sys.stderr)


def _openssl(*args):
    cmd = ['openssl', *args]
    _print_and_call(cmd, cwd='/etc/bakmgr')


def install_certs():
    if Path('/etc/bakmgr/dash.crt').exists():
        return
    _step("Setting up SSL certificates in /etc/bakmgr")
    Path('/etc/bakmgr').mkdir(exist_ok=True)
    subj = (
        '/C=US/ST=CA/L=Los Angeles/O=InMotion Hosting/'
        f'OU=InMotion Hosting/CN={platform.node()}'
    )
    _openssl('genrsa', '-out', 'dash.key', '2048')
    # fmt: off
    _openssl(
        'req', '-new', '-key', 'dash.key', '-out', 'dash.csr',
        '-subj', subj, '-batch'
    )
    _openssl(
        'x509', '-req', '-in', 'dash.csr', '-signkey', 'dash.key',
        '-out', 'dash.crt', '-days', '5120', '-sha256'
    )
    # fmt: on


def systemd_setup(module_root: Path):
    _step("Setting up systemd service")
    shutil.copy(
        str(module_root / 'service_cfg/bakmgr.service'),
        '/usr/lib/systemd/system/bakmgr.service',
    )
    _print_and_call(['systemctl', 'daemon-reload'])
    _print_and_call(['systemctl', 'enable', 'bakmgr.service'])
    if _print_and_call(['systemctl', 'status', 'bakmgr.service']):
        _print_and_call(['systemctl', 'start', 'bakmgr.service'])


def upstart_setup(module_root: Path):
    _step("Setting up upstart service")
    shutil.copy(
        str(module_root / 'service_cfg/bakmgr.upstart'),
        '/etc/init/bakmgr.conf',
    )
    if _print_and_call(['status', 'bakmgr']):
        _print_and_call(['start', 'bakmgr'])


def sysv_setup(module_root: Path):
    _step("Setting up sysv service")
    shutil.copy(
        str(module_root / 'service_cfg/bakmgr.sysv'),
        '/etc/init.d/bakmgr',
    )
    Path('/etc/init.d/bakmgr').chmod(0o755)
    _print_and_call(['chkconfig', 'bakmgr', 'on'])
    if _print_and_call(['service', 'bakmgr', 'status']):
        _print_and_call(['service', 'bakmgr', 'start'])


def install_autocomplete(module_root: Path):
    """Setup /etc/bash_completion.d/bakmgr"""
    if not Path('/etc/bash_completion.d').is_dir():
        return
    if not Path('/etc/profile.d/bash_completion.sh').is_file():
        return
    dest = '/etc/bash_completion.d/bakmgr'
    _step(f'Setting up autocomplete in {dest}')
    src = str(module_root / 'service_cfg/bakmgr-autocomplete.sh')
    shutil.copyfile(src, dest)


def install_logrotate(module_root: Path):
    """Setup /etc/logrotate.d/bakmgr"""
    dest = '/etc/logrotate.d/bakmgr'
    if Path(dest).exists() or not Path('/etc/logrotate.d').is_dir():
        return
    _step(f'Setting up logrotate in {dest}')
    src = str(module_root / 'service_cfg/bakmgr.logrotate')
    shutil.copyfile(src, dest)


def install_config():
    """Setup /etc/bakmgr/*.yaml"""
    Path('/etc/bakmgr').mkdir(exist_ok=True)
    try:
        # no longer used
        os.remove('/etc/bakmgr/example.yaml')
    except OSError:
        pass
    if CONF_PATH.exists():
        return
    _step(f'Setting up config in {CONF_PATH}')
    Conf().save()


def download_restic():
    """Download and install restic"""
    _step("Setting up restic")
    print("Running: /usr/bin/restic self-update")
    try:
        call(['/usr/bin/restic', 'self-update'])
        return
    except FileNotFoundError:
        pass
    latest = requests.get(
        'https://api.github.com/repos/restic/restic/releases/latest',
        timeout=240,
    ).json()['tag_name'][1:]
    print(f'Installing restic v{latest} to /usr/bin/restic')
    url = (
        f'https://github.com/restic/restic/releases/download/v{latest}/'
        f'restic_{latest}_linux_amd64.bz2'
    )
    with requests.get(url, timeout=240, stream=True) as req:
        unzip = bz2.BZ2Decompressor()
        with open('/usr/bin/restic', 'wb') as restic:
            os.chown('/usr/bin/restic', 0, 0)
            os.chmod('/usr/bin/restic', 0o755)
            for chunk in req.iter_content(chunk_size=512):
                restic.write(unzip.decompress(chunk))


def install_cron(module_root: Path):
    """Setup /etc/cron.d/bakmgr"""
    _step("Checking crond")
    if not os.path.isdir('/etc/cron.d'):
        _warn(
            "crond is not installed or this crond "
            "implementation does not read /etc/cron.d"
        )
        return
    print("Setting up cron at /etc/cron.d/bakmgr")
    src = str(module_root / 'service_cfg/bakmgr.cron')
    shutil.copyfile(src, '/etc/cron.d/bakmgr')
    try:
        check_call(['killall', '-HUP', '-r', '^crond?$'])
    except (OSError, CalledProcessError):
        _warn('Failed to restart crond')


def register(host: str) -> Union[str, None]:
    """Setup /etc/bakmgr/.auth.json"""
    _step("Checking registration")
    auth_file = Path('/etc/bakmgr/.auth.json')
    if auth_file.is_file():
        print('Already registered:', auth_file, 'exists')
        return
    svr_class = 'sm_vps' if is_vps() else 'sm_ded'
    print(f"Registering as {host!r}...")
    try:
        response = requests.post(
            REGISTER_URL,
            timeout=180.0,
            data={
                'host': host,
                'svr_class': svr_class,
            },
        ).json()
    except (requests.RequestException, ValueError) as exc:
        print(exc, file=sys.stderr)
        return
    if response['status'] != 0:  # error code from backup authority
        print(response['data'], file=sys.stderr)
        return
    data = {
        'apiuser': response['data']['apiuser'],
        'authkey': response['data']['authkey'],
    }
    with auth_file.open('w', encoding='ascii') as handle:
        os.chown(str(auth_file), 0, 0)
        auth_file.chmod(0o600)
        json.dump(data, handle)


if __name__ == '__main__':
    main()

Zerion Mini Shell 1.0