#!/opt/alt/python38/bin/python3.8
"""
The watchdog script that checks the webshield and restarts it if error found
"""
import json
import logging
import logging.handlers
import requests
import subprocess
import uuid
import yaml

import sentry_sdk
from sentry_sdk import configure_scope

logging.raiseExceptions = False


class Watchdog:

    port = 52224
    request_timeout = 2
    config_path = '/etc/sysconfig/imunify360/imunify360.config'
    user_agent = 'Webshield-watchdog-agent'
    sentry_dsn_path = '/usr/share/imunify360-webshield/sentry'
    package_name = 'imunify360-webshield-bundle'
    license_path = '/var/imunify360/license.json'
    services = ('imunify360-webshield',
                'wsshdict',
                'imunify360-webshield-ssl-cache')


    def __init__(self):
        self.is_enabled = self._get_config_status()
        self.is_running = self._get_current_status()
        self.sentry_dsn = self._get_dsn()
        self.log_level = logging.INFO
        self.logger = self._setup_logging()

    def _setup_logging(self):
        logger = logging.getLogger('imunify360-webshield-watchdog')
        logger.setLevel(self.log_level)
        handler = logging.handlers.SysLogHandler('/dev/log')
        formatter = logging.Formatter('%(name)s: %(message)s')
        handler.formatter = formatter
        logger.addHandler(handler)
        self._init_sentry()
        return logger

    @classmethod
    def _get_server_id(cls):
        try:
            with open(cls.license_path) as f:
                data = json.load(f)
        except Exception:
            return
        return data.get('id')

    @classmethod
    def _get_dsn(cls):
        try:
            with open(cls.sentry_dsn_path) as f:
                return f.read().strip()
        except Exception:
            return

    def _init_sentry(self):
        sentry_sdk.init(dsn=self.sentry_dsn, release=self._imunify360_version())
        with configure_scope() as scope:
            scope.user = {'id': self._get_server_id()}

    @classmethod
    def _get_config_status(cls):
        with open(cls.config_path) as f:
            parsed_config = yaml.safe_load(f)
        if not 'WEBSHIELD' in parsed_config:
            return False
        return parsed_config["WEBSHIELD"].get('enable', False)

    @classmethod
    def _get_current_status(self):
        errors = 0
        for service in self.services:
            proc = subprocess.run(['service', service, 'status'],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL)
            errors += proc.returncode
        if errors:
            return False
        return True

    def _check_http_request(self):
        url = "http://0.0.0.0:{}/selfcheck.py?uuid={}".format(
            self.port, uuid.uuid4())
        try:
            requests.get(
                url,
                headers={'User-Agent': self.user_agent},
                timeout=self.request_timeout)
        except Exception:
            return False
        return True

    def _call_service(self, action='restart'):
        service = self.services[0]
        proc = subprocess.run(
            ['service', service, action],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL)
        if proc.returncode != 0:
            return False
        return True

    @staticmethod
    def _collect_output(cmd):
        try:
            cp = subprocess.run(
                cmd,
                stdin=subprocess.DEVNULL,
                stdout=subprocess.PIPE,
                stderr=subprocess.DEVNULL)
        except OSError:
            return ''
        if cp.returncode != 0:
            return ''
        return cp.stdout.decode()

    @classmethod
    def _get_rpm_version(cls):
        cmd = ['rpm', '-q', '--queryformat=%{VERSION}-%{RELEASE}',
               cls.package_name]
        return cls._collect_output(cmd)

    @classmethod
    def _get_dpkg_version(cls):
        cmd = ['dpkg', '--status', cls.package_name]
        out = cls._collect_output(cmd)
        if not out:
            return
        for line in out.splitlines():
            if line.startswith("Version:"):
                return line.strip().split()[1]

    @classmethod
    def _imunify360_version(cls):
        version = cls._get_rpm_version()
        if not version:
            version = cls._get_dpkg_version()
        return version


    def run(self):
        if self.is_enabled and self.is_running:
            result = self._check_http_request()
            if not result:
                self.logger.error(
                    '%s is inaccessible. Restarting...', self.services[0])
                self._call_service('restart')
            else:
                self.logger.info('%s is running. OK', self.services[0])
            return

        if self.is_enabled and not self.is_running:
            self.logger.error(
                    '%s is not running. Restarting...', self.services[0])
            self._call_service('restart')
            return

        if not self.is_enabled and self.is_running:
            self.logger.warn(
                    '%s is running while being disabled. Stopping...',
                    self.services[0])
            self._call_service('stop')
            return

        self.logger.info('%s is disabled. OK', self.services[0])


if __name__ == '__main__':
    w = Watchdog()
    w.run()
