<commit_before>#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
Dispatcher: choose the best demorunner according to a policy

All exposed WS return JSON with a status OK/KO, along with an error
description if that's the case.
"""

import configparser
import json
import logging
import os
import random
import re
import sys

import cherrypy

import requests


def authenticate(func):
    """
    Wrapper to authenticate before using an exposed function
    """

    def authenticate_and_call(*args, **kwargs):
        """
        Invokes the wrapped function if authenticated
        """
        if not is_authorized_ip(cherrypy.request.remote.ip) and not (
                "X-Real-IP" in cherrypy.request.headers and is_authorized_ip(cherrypy.request.headers["X-Real-IP"])):
            error = {"status": "KO", "error": "Authentication Failed"}
            return json.dumps(error).encode()
        return func(*args, **kwargs)

    def is_authorized_ip(ip):
        """
        Validates the given IP
        """
        dispatcher = Dispatcher.get_instance()
        patterns = []
        # Creates the patterns  with regular expressions
        for authorized_pattern in dispatcher.authorized_patterns:
            patterns.append(re.compile(authorized_pattern.replace(".", r"\.").replace("*", "[0-9]*")))
        # Compare the IP with the patterns
        for pattern in patterns:
            if pattern.match(ip) is not None:
                return True
        return False

    return authenticate_and_call


class Dispatcher():
    """
    The Dispatcher chooses the best DR according to a policy
    """

    instance = None

    @staticmethod
    def get_instance():
        """
        Singleton pattern
        """
        if Dispatcher.instance is None:
            Dispatcher.instance = Dispatcher()
        return Dispatcher.instance

    def __init__(self):
        """
        Initialize Dispatcher class
        """
        self.base_directory = os.getcwd()
        self.logs_dir = cherrypy.config.get("logs_dir")
        self.host_name = cherrypy.config.get("server.socket_host")
        self.demorunners = None
        self.config_common_dir = cherrypy.config.get("config_common_dir")

        # Default policy: lowest_workload
        self.policy = Policy.factory('lowest_workload')

        # Logs
        try:
            if not os.path.exists(self.logs_dir):
                os.makedirs(self.logs_dir)
            self.logger = self.init_logging()
        except Exception as e:
            self.logger.exception("Failed to create log dir (using file dir) : {}".format(e))

        # Security: authorized IPs
        self.authorized_patterns = self.read_authorized_patterns()

    def read_authorized_patterns(self):
        """
        Read from the IPs conf file
        """
        # Check if the config file exists
        authorized_patterns_path = os.path.join(self.config_common_dir, "authorized_patterns.conf")
        if not os.path.isfile(authorized_patterns_path):
            self.error_log("read_authorized_patterns",
                           "Can't open {}".format(authorized_patterns_path))
            return []

        # Read config file
        try:
            cfg = configparser.ConfigParser()
            cfg.read([authorized_patterns_path])
            patterns = []
            for item in cfg.items('Patterns'):
                patterns.append(item[1])
            return patterns
        except configparser.Error:
            self.logger.exception("Bad format in {}".format(authorized_patterns_path))
            return []

    def refresh_demorunners(self):
        """
        Refresh the value of the demorunners
        """
        url = 'http://{}/api/{}/{}'.format(
            self.host_name,
            'core',
            'get_demorunners'
        )
        resp = requests.post(url)

        self.demorunners = []
        dict_demorunners = json.loads(resp.json()['demorunners'].replace('\'', '\"'))

        for demorunner_name in list(dict_demorunners.keys()):
            self.demorunners.append(DemoRunnerInfo(
                dict_demorunners[demorunner_name]["server"],
                demorunner_name,
                dict_demorunners[demorunner_name]["capability"]
            ))<commit_msg>[modules/dispatcher] Connect to the FQDN when refreshing the DRs<commit_after>#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
Dispatcher: choose the best demorunner according to a policy

All exposed WS return JSON with a status OK/KO, along with an error
description if that's the case.
"""

import configparser
import json
import logging
import os
import random
import re
import socket
import sys

import cherrypy

import requests


def authenticate(func):
    """
    Wrapper to authenticate before using an exposed function
    """

    def authenticate_and_call(*args, **kwargs):
        """
        Invokes the wrapped function if authenticated
        """
        if not is_authorized_ip(cherrypy.request.remote.ip) and not (
                "X-Real-IP" in cherrypy.request.headers and is_authorized_ip(cherrypy.request.headers["X-Real-IP"])):
            error = {"status": "KO", "error": "Authentication Failed"}
            return json.dumps(error).encode()
        return func(*args, **kwargs)

    def is_authorized_ip(ip):
        """
        Validates the given IP
        """
        dispatcher = Dispatcher.get_instance()
        patterns = []
        # Creates the patterns  with regular expressions
        for authorized_pattern in dispatcher.authorized_patterns:
            patterns.append(re.compile(authorized_pattern.replace(".", r"\.").replace("*", "[0-9]*")))
        # Compare the IP with the patterns
        for pattern in patterns:
            if pattern.match(ip) is not None:
                return True
        return False

    return authenticate_and_call


class Dispatcher():
    """
    The Dispatcher chooses the best DR according to a policy
    """

    instance = None

    @staticmethod
    def get_instance():
        """
        Singleton pattern
        """
        if Dispatcher.instance is None:
            Dispatcher.instance = Dispatcher()
        return Dispatcher.instance

    def __init__(self):
        """
        Initialize Dispatcher class
        """
        self.base_directory = os.getcwd()
        self.logs_dir = cherrypy.config.get("logs_dir")
        self.demorunners = None
        self.config_common_dir = cherrypy.config.get("config_common_dir")

        # Default policy: lowest_workload
        self.policy = Policy.factory('lowest_workload')

        # Logs
        try:
            if not os.path.exists(self.logs_dir):
                os.makedirs(self.logs_dir)
            self.logger = self.init_logging()
        except Exception as e:
            self.logger.exception("Failed to create log dir (using file dir) : {}".format(e))

        # Security: authorized IPs
        self.authorized_patterns = self.read_authorized_patterns()

    def read_authorized_patterns(self):
        """
        Read from the IPs conf file
        """
        # Check if the config file exists
        authorized_patterns_path = os.path.join(self.config_common_dir, "authorized_patterns.conf")
        if not os.path.isfile(authorized_patterns_path):
            self.error_log("read_authorized_patterns",
                           "Can't open {}".format(authorized_patterns_path))
            return []

        # Read config file
        try:
            cfg = configparser.ConfigParser()
            cfg.read([authorized_patterns_path])
            patterns = []
            for item in cfg.items('Patterns'):
                patterns.append(item[1])
            return patterns
        except configparser.Error:
            self.logger.exception("Bad format in {}".format(authorized_patterns_path))
            return []

    def refresh_demorunners(self):
        """
        Refresh the value of the demorunners
        """
        url = 'http://{}/api/core/get_demorunners'.format(socket.getfqdn())
        resp = requests.post(url)

        self.demorunners = []
        dict_demorunners = json.loads(resp.json()['demorunners'].replace('\'', '\"'))

        for demorunner_name in list(dict_demorunners.keys()):
            self.demorunners.append(DemoRunnerInfo(
                dict_demorunners[demorunner_name]["server"],
                demorunner_name,
                dict_demorunners[demorunner_name]["capability"]
            ))
{'fraction_non_alphanumeric': 0.10292728989612843, 'fraction_numerical': 0.0011803588290840415, 'mean_word_length': 2.2204484986697075, 'pattern_counts': {'":': 4, '<': 3, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4927774', 'n_tokens_mistral': 2400, 'n_tokens_neox': 2276, 'n_words': 559}