<commit_before>from contextlib import closing
import json
import logging
import time
import datetime
from dockermon import DockerMon, DockermonError, DockerEvent
from sys import version_info

if version_info[:2] < (3, 0):
    from httplib import OK as HTTP_OK
    from httplib import NO_CONTENT as HTTP_NO_CONTENT
    from urlparse import urlparse
else:
    from http.client import OK as HTTP_OK
    from urllib.parse import urlparse

restart_logger = logging.getLogger('dockermon-restart')


class RestartParameters:
    def __init__(self, args):
        self.threshold = args.restart_threshold
        self.limit = args.restart_limit
        self.reset_period = args.restart_reset_period
        self.containers_to_restart = args.containers_to_restart


class RestartService:
    def __init__(self, socket_url, restart_params, notification_service):
        self.socket_url = socket_url
        self.event_dict = {}
        self.params = restart_params
        self.notification_service = notification_service
        self.cached_container_names = {'restart': [], 'do_not_restart': []}
        self.container_restarts = {}

    def save_docker_event(self, event):
        container_name = event.container_name
        if container_name not in self.event_dict:
            self.event_dict[container_name] = []

        self.event_dict[container_name].append(event)

    def handle_docker_event(self, parsed_json):
        if "status" in parsed_json:
            event_status = parsed_json['status']
            container_name = parsed_json['Actor']['Attributes']["name"]
            event_time = parsed_json['time']

            if event_status in DockerMon.event_types_to_watch:
                docker_event = DockerEvent(event_status, container_name, event_time)
                self.save_docker_event(docker_event)
                if event_status == 'start':
                    self.maintain_container_restarts(container_name)
                elif self.check_container_is_restartable(container_name) and \
                        self.check_restart_needed(container_name, parsed_json):
                    restart_logger.info("Container %s dead unexpectedly, restarting...", container_name)
                    self.do_restart(parsed_json)

            # restart immediately if container is unhealthy
            elif "health_status: unhealthy" in event_status \
                    and self.check_container_is_restartable(container_name) \
                    and self.check_restart_needed(container_name, parsed_json):
                restart_logger.info("Container %s became unhealthy, restarting...", container_name)
                docker_event = DockerEvent(event_status, container_name, event_time)
                self.save_docker_event(docker_event)
                self.maintain_container_restarts(container_name)
                self.do_restart(parsed_json)

    def check_restart_needed(self, container_name, parsed_json):
        docker_events = self.event_dict[container_name]

        if not docker_events:
            return False

        now = time.time()
        die_events = filter(lambda e: DockerMon.event_type_matches(e, 'die'), docker_events)
        die_events = filter(lambda e: DockerMon.event_max_age_in_seconds(e, 5, now), die_events)

        if die_events:
            stop_or_kill_events = filter(lambda e: DockerMon.event_type_matches_one_of(e, ['stop', 'kill']),
                                         docker_events)
            stop_or_kill_events = filter(lambda e: DockerMon.event_max_age_in_seconds(e, 12, now), stop_or_kill_events)

            if stop_or_kill_events:
                RestartService.log_stopped_killed_by_hand(container_name)
                return False
            else:
                if self.is_restart_allowed(container_name):
                    return True
                else:
                    RestartService.log_will_not_restart(container_name)
                    if not self.is_mail_sent(container_name):
                        subject = "Maximum restart count is reached for container %s" % container_name
                        self.notification_service.send_mail(subject, json.dumps(parsed_json))
                        self.set_mail_sent(container_name)
        else:
            return False

    @staticmethod
    def log_stopped_killed_by_hand(container_name):
        restart_logger.debug(
            "Container %s is stopped/killed, but WILL NOT BE restarted as it was stopped/killed by hand",
            container_name)

    def log_will_not_restart(self, container_name):
        restart_logger.warn(
            "Container %s is stopped/killed, but WILL NOT BE restarted again, as maximum restart count is reached: %s",
            container_name, self.params.restart_limit)

    def is_mail_sent(self, container_name):
        return self.container_restarts[container_name].mail_sent

    def set_mail_sent(self, container_name):
        self.container_restarts[container_name].mail_sent = True

    def save_restart_occasion(self, container_name):
        now = time.time()
        if container_name not in self.container_restarts:
            self.container_restarts[container_name] = RestartData(container_name, now)
        else:
            self.container_restarts[container_name].add_restart_occasion(now)

    def get_performed_restart_count(self, container_name):
        if container_name not in self.container_restarts:
            self.container_restarts[container_name] = RestartData(container_name)

        return len(self.container_restarts[container_name].occasions)

    def reset_restart_data(self, container_name):
        self.container_restarts[container_name] = RestartData(container_name)

    def check_container_is_restartable(self, container_name):
        if container_name in self.cached_container_names['restart']:
            return True
        elif container_name in self.cached_container_names['do_not_restart']:
            restart_logger.debug("Container %s is stopped/killed, "
                                 "but WILL NOT BE restarted as it does not match any names from configuration "
                                 "'containers-to-restart'.", container_name)
            return False
        else:
            for pattern in self.params.containers_to_restart:
                if pattern.match(container_name):
                    restart_logger.debug("Container %s is matched for container name pattern %s", container_name,
                                         pattern.pattern)
                    self.cached_container_names['restart'].append(container_name)
                    return True
            restart_logger.debug("Container %s is stopped/killed, "
                                 "but WILL NOT BE restarted as it does not match any names from configuration "
                                 "'containers-to-restart'.", container_name)
            self.cached_container_names['do_not_restart'].append(container_name)
            return False

    def is_restart_allowed(self, container_name):
        restart_count = self.get_performed_restart_count(container_name)
        last_restarts = self.container_restarts[container_name].occasions[-self.params.restart_limit:]

        now = time.time()
        restart_range_start = now - self.params.restart_threshold * 60
        for r in last_restarts:
            if r < restart_range_start:
                return False

        return restart_count < self.params.restart_limit

    def maintain_container_restarts(self, container_name):
        if container_name not in self.container_restarts:
            return
        last_restart = self.container_restarts[container_name].occasions[-1]
        now = time.time()
        restart_duration = now - last_restart
        needs_counter_reset = restart_duration < self.params.restart_reset_period * 60

        if needs_counter_reset:
            restart_logger.info("Start/healthy event received for container %s, clearing restart counter...",
                                container_name)
            restart_logger.info("Last restart time was %s", DateHelper.format_timestamp(last_restart))
            self.reset_restart_data(container_name)

    def do_restart(self, parsed_json):
        container_id = parsed_json['id']
        container_name = parsed_json['Actor']['Attributes']["name"]
        compose_service_name = parsed_json['Actor']['Attributes']["com.docker.compose.service"]
<commit_msg>restartservice.py: simplified naming of field<commit_after>from contextlib import closing
import json
import logging
import time
import datetime
from dockermon import DockerMon, DockermonError, DockerEvent
from sys import version_info

if version_info[:2] < (3, 0):
    from httplib import OK as HTTP_OK
    from httplib import NO_CONTENT as HTTP_NO_CONTENT
    from urlparse import urlparse
else:
    from http.client import OK as HTTP_OK
    from urllib.parse import urlparse

restart_logger = logging.getLogger('dockermon-restart')


class RestartParameters:
    def __init__(self, args):
        self.threshold = args.restart_threshold
        self.limit = args.restart_limit
        self.reset_period = args.restart_reset_period
        self.containers_to_restart = args.containers_to_restart


class RestartService:
    def __init__(self, socket_url, restart_params, notification_service):
        self.socket_url = socket_url
        self.event_dict = {}
        self.params = restart_params
        self.notification_service = notification_service
        self.cached_container_names = {'restart': [], 'do_not_restart': []}
        self.restarts = {}

    def save_docker_event(self, event):
        container_name = event.container_name
        if container_name not in self.event_dict:
            self.event_dict[container_name] = []

        self.event_dict[container_name].append(event)

    def handle_docker_event(self, parsed_json):
        if "status" in parsed_json:
            event_status = parsed_json['status']
            container_name = parsed_json['Actor']['Attributes']["name"]
            event_time = parsed_json['time']

            if event_status in DockerMon.event_types_to_watch:
                docker_event = DockerEvent(event_status, container_name, event_time)
                self.save_docker_event(docker_event)
                if event_status == 'start':
                    self.maintain_container_restarts(container_name)
                elif self.check_container_is_restartable(container_name) and \
                        self.check_restart_needed(container_name, parsed_json):
                    restart_logger.info("Container %s dead unexpectedly, restarting...", container_name)
                    self.do_restart(parsed_json)

            # restart immediately if container is unhealthy
            elif "health_status: unhealthy" in event_status \
                    and self.check_container_is_restartable(container_name) \
                    and self.check_restart_needed(container_name, parsed_json):
                restart_logger.info("Container %s became unhealthy, restarting...", container_name)
                docker_event = DockerEvent(event_status, container_name, event_time)
                self.save_docker_event(docker_event)
                self.maintain_container_restarts(container_name)
                self.do_restart(parsed_json)

    def check_restart_needed(self, container_name, parsed_json):
        docker_events = self.event_dict[container_name]

        if not docker_events:
            return False

        now = time.time()
        die_events = filter(lambda e: DockerMon.event_type_matches(e, 'die'), docker_events)
        die_events = filter(lambda e: DockerMon.event_max_age_in_seconds(e, 5, now), die_events)

        if die_events:
            stop_or_kill_events = filter(lambda e: DockerMon.event_type_matches_one_of(e, ['stop', 'kill']),
                                         docker_events)
            stop_or_kill_events = filter(lambda e: DockerMon.event_max_age_in_seconds(e, 12, now), stop_or_kill_events)

            if stop_or_kill_events:
                RestartService.log_stopped_killed_by_hand(container_name)
                return False
            else:
                if self.is_restart_allowed(container_name):
                    return True
                else:
                    RestartService.log_will_not_restart(container_name)
                    if not self.is_mail_sent(container_name):
                        subject = "Maximum restart count is reached for container %s" % container_name
                        self.notification_service.send_mail(subject, json.dumps(parsed_json))
                        self.set_mail_sent(container_name)
        else:
            return False

    @staticmethod
    def log_stopped_killed_by_hand(container_name):
        restart_logger.debug(
            "Container %s is stopped/killed, but WILL NOT BE restarted as it was stopped/killed by hand",
            container_name)

    def log_will_not_restart(self, container_name):
        restart_logger.warn(
            "Container %s is stopped/killed, but WILL NOT BE restarted again, as maximum restart count is reached: %s",
            container_name, self.params.restart_limit)

    def is_mail_sent(self, container_name):
        return self.restarts[container_name].mail_sent

    def set_mail_sent(self, container_name):
        self.restarts[container_name].mail_sent = True

    def save_restart_occasion(self, container_name):
        now = time.time()
        if container_name not in self.restarts:
            self.restarts[container_name] = RestartData(container_name, now)
        else:
            self.restarts[container_name].add_restart_occasion(now)

    def get_performed_restart_count(self, container_name):
        if container_name not in self.restarts:
            self.restarts[container_name] = RestartData(container_name)

        return len(self.restarts[container_name].occasions)

    def reset_restart_data(self, container_name):
        self.restarts[container_name] = RestartData(container_name)

    def check_container_is_restartable(self, container_name):
        if container_name in self.cached_container_names['restart']:
            return True
        elif container_name in self.cached_container_names['do_not_restart']:
            restart_logger.debug("Container %s is stopped/killed, "
                                 "but WILL NOT BE restarted as it does not match any names from configuration "
                                 "'containers-to-restart'.", container_name)
            return False
        else:
            for pattern in self.params.containers_to_restart:
                if pattern.match(container_name):
                    restart_logger.debug("Container %s is matched for container name pattern %s", container_name,
                                         pattern.pattern)
                    self.cached_container_names['restart'].append(container_name)
                    return True
            restart_logger.debug("Container %s is stopped/killed, "
                                 "but WILL NOT BE restarted as it does not match any names from configuration "
                                 "'containers-to-restart'.", container_name)
            self.cached_container_names['do_not_restart'].append(container_name)
            return False

    def is_restart_allowed(self, container_name):
        restart_count = self.get_performed_restart_count(container_name)
        last_restarts = self.restarts[container_name].occasions[-self.params.restart_limit:]

        now = time.time()
        restart_range_start = now - self.params.restart_threshold * 60
        for r in last_restarts:
            if r < restart_range_start:
                return False

        return restart_count < self.params.restart_limit

    def maintain_container_restarts(self, container_name):
        if container_name not in self.restarts:
            return
        last_restart = self.restarts[container_name].occasions[-1]
        now = time.time()
        restart_duration = now - last_restart
        needs_counter_reset = restart_duration < self.params.restart_reset_period * 60

        if needs_counter_reset:
            restart_logger.info("Start/healthy event received for container %s, clearing restart counter...",
                                container_name)
            restart_logger.info("Last restart time was %s", DateHelper.format_timestamp(last_restart))
            self.reset_restart_data(container_name)

    def do_restart(self, parsed_json):
        container_id = parsed_json['id']
        container_name = parsed_json['Actor']['Attributes']["name"]
        compose_service_name = parsed_json['Actor']['Attributes']["com.docker.compose.service"]

{'fraction_non_alphanumeric': 0.07299964332421828, 'fraction_numerical': 0.0013078111996195458, 'mean_word_length': 2.5476592155208775, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '870522', 'n_tokens_mistral': 4694, 'n_tokens_neox': 4467, 'n_words': 954}