<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 University of Deusto
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
#
# This software consists of contributions made by many individuals, 
# listed below:
#
# Author: <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>
# 

import re

import voodoo.log as log
from voodoo.log import logged
from voodoo.sessions.checker import check_session
import voodoo.sessions.session_type as SessionType
import voodoo.gen.coordinator.CoordAddress as CoordAddress
import voodoo.gen.exceptions.exceptions as GeneratorExceptions

from voodoo.threaded import threaded
import weblab.lab.async_request as AsyncRequest

import weblab.lab.exc as LaboratoryExceptions

from voodoo.gen.caller_checker import caller_check

import weblab.data.server_type as ServerType
from weblab.data.experiments import ExperimentInstanceId
import weblab.data.command as Command

import weblab.core.coordinator.coordinator as Coordinator

import weblab.lab.assigned_experiments as AssignedExperiments
import weblab.lab.status_handler as IsUpAndRunningHandler

import weblab.experiment.level as ExperimentApiLevel

import voodoo.sessions.manager as SessionManager
from voodoo.sessions import generator as SessionGenerator
import json

check_session_params = (
        LaboratoryExceptions.SessionNotFoundInLaboratoryServerException,
        "Laboratory Server"
    )


WEBLAB_LABORATORY_SERVER_SESSION_TYPE            = "laboratory_session_type"
DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_TYPE    = SessionType.Memory
WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID         = "laboratory_session_pool_id"
DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID = "LaboratoryServer"
WEBLAB_LABORATORY_SERVER_ASSIGNED_EXPERIMENTS    = "laboratory_assigned_experiments"
WEBLAB_LABORATORY_EXCLUDE_CHECKING               = "laboratory_exclude_checking"
DEFAULT_WEBLAB_LABORATORY_EXCLUDE_CHECKING       = []


##########################################################
#
# The Laboratory Server is a proxy server between WebLab 
# and the experiments. The main purpose is to provide
# low level features (such as encryption, low level 
# protocols, etc.) to the experiment, through the WebLab
# stack.
# 
class LaboratoryServer(object):

    # exp_inst_name|exp_name|exp_cat;coord_address
    EXPERIMENT_INSTANCE_ID_REGEX = r"^(.*)\:(.*)\@(.*)$"
    
    def __init__(self, coord_address, locator, cfg_manager, *args, **kwargs):
        super(LaboratoryServer,self).__init__(*args, **kwargs)

        session_type    = cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_SESSION_TYPE, 
                                           DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_TYPE)
        session_pool_id = cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID, 
                                           DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID)
        self._session_manager = SessionManager.SessionManager(
                cfg_manager,
                getattr(SessionType, session_type),
                session_pool_id
            )
        self._coord_address         = coord_address
        self._locator               = locator
        self._cfg_manager           = cfg_manager
        
        # This dictionary will be used to store the ongoing and not-yet-queried 
        # async requests. They will be stored by session.
        # TODO: Consider refactoring this.
        self._async_requests = {}

        self._load_assigned_experiments()
        


    #######################################################
    #
    # Parse the configuration and load all the experiments 
    # found. This task is executed at the beginning.
    # 
    def _parse_assigned_experiments(self):
        assigned_experiments = self._cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_ASSIGNED_EXPERIMENTS)

        parsed_experiments = []

        for experiment_instance_id, data in assigned_experiments.items():
            mo = re.match(self.EXPERIMENT_INSTANCE_ID_REGEX, experiment_instance_id)
            if mo == None:
                raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid configuration entry. Expected format: %s; found: %s" % 
                    (LaboratoryServer.EXPERIMENT_INSTANCE_ID_REGEX, experiment_instance_id))
            else:
                # ExperimentInstanceId
                groups = mo.groups()
                (   exp_inst_name,
                    exp_name,
                    exp_cat_name
                ) = groups
                experiment_instance_id = ExperimentInstanceId(exp_inst_name, exp_name, exp_cat_name)

                # CoordAddress
                try:
                    coord_address = CoordAddress.CoordAddress.translate_address(data['coord_address'])
                except GeneratorExceptions.GeneratorException:
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid coordination address: %s" % data['coord_address'])
                
                # CheckingHandlers
                checkers = data.get('checkers', ())
                checking_handlers = []
                for checker in checkers:
                    klazz = checker[0]
                    if klazz in IsUpAndRunningHandler.HANDLERS:
                        argss, kargss = (), {}
                        if len(checker) >= 3:
                            kargss = checker[2]
                        if len(checker) >= 2:
                            argss = checker[1]
                        checking_handlers.append(eval('IsUpAndRunningHandler.'+klazz)(*argss, **kargss))
                    else:
                        raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid IsUpAndRunningHandler: %s" % klazz)

                api = data.get('api', 'current')
                if not ExperimentApiLevel.is_level(api):
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid api: %s. See %s" % (api, ExperimentApiLevel.__file__))

                if not ExperimentApiLevel.is_supported(api):
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Unsupported api: %s" % api)

                parsed_experiments.append( (experiment_instance_id, coord_address, checking_handlers, ExperimentApiLevel.get_level(api)) )
        return parsed_experiments

    def _load_assigned_experiments(self):
        self._assigned_experiments = AssignedExperiments.AssignedExperiments()
        parsed_experiments         = self._parse_assigned_experiments()
        for exp_inst_id, coord_address, checking_handlers, api in parsed_experiments:
            self._assigned_experiments.add_server(exp_inst_id, coord_address, checking_handlers, None)
            
            # TODO / TOFIX: Right now, an experiment which does not override the get_api method returns None,
            # exactly in the same way that do_get_api returns None when it fails. This would lead to 
            # unnecessary do_get_api calls, so it should be fixed.
            
            # Upon loading, we will also try to find out which API each experiment should use, by asking
            # the experiment server itself. Sometimes, however, the experiment server will not really be<commit_msg>[api]: Removed tofix notice which had been left over.<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 University of Deusto
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
#
# This software consists of contributions made by many individuals, 
# listed below:
#
# Author: <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>
# 

import re

import voodoo.log as log
from voodoo.log import logged
from voodoo.sessions.checker import check_session
import voodoo.sessions.session_type as SessionType
import voodoo.gen.coordinator.CoordAddress as CoordAddress
import voodoo.gen.exceptions.exceptions as GeneratorExceptions

from voodoo.threaded import threaded
import weblab.lab.async_request as AsyncRequest

import weblab.lab.exc as LaboratoryExceptions

from voodoo.gen.caller_checker import caller_check

import weblab.data.server_type as ServerType
from weblab.data.experiments import ExperimentInstanceId
import weblab.data.command as Command

import weblab.core.coordinator.coordinator as Coordinator

import weblab.lab.assigned_experiments as AssignedExperiments
import weblab.lab.status_handler as IsUpAndRunningHandler

import weblab.experiment.level as ExperimentApiLevel

import voodoo.sessions.manager as SessionManager
from voodoo.sessions import generator as SessionGenerator
import json

check_session_params = (
        LaboratoryExceptions.SessionNotFoundInLaboratoryServerException,
        "Laboratory Server"
    )


WEBLAB_LABORATORY_SERVER_SESSION_TYPE            = "laboratory_session_type"
DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_TYPE    = SessionType.Memory
WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID         = "laboratory_session_pool_id"
DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID = "LaboratoryServer"
WEBLAB_LABORATORY_SERVER_ASSIGNED_EXPERIMENTS    = "laboratory_assigned_experiments"
WEBLAB_LABORATORY_EXCLUDE_CHECKING               = "laboratory_exclude_checking"
DEFAULT_WEBLAB_LABORATORY_EXCLUDE_CHECKING       = []


##########################################################
#
# The Laboratory Server is a proxy server between WebLab 
# and the experiments. The main purpose is to provide
# low level features (such as encryption, low level 
# protocols, etc.) to the experiment, through the WebLab
# stack.
# 
class LaboratoryServer(object):

    # exp_inst_name|exp_name|exp_cat;coord_address
    EXPERIMENT_INSTANCE_ID_REGEX = r"^(.*)\:(.*)\@(.*)$"
    
    def __init__(self, coord_address, locator, cfg_manager, *args, **kwargs):
        super(LaboratoryServer,self).__init__(*args, **kwargs)

        session_type    = cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_SESSION_TYPE, 
                                           DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_TYPE)
        session_pool_id = cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID, 
                                           DEFAULT_WEBLAB_LABORATORY_SERVER_SESSION_POOL_ID)
        self._session_manager = SessionManager.SessionManager(
                cfg_manager,
                getattr(SessionType, session_type),
                session_pool_id
            )
        self._coord_address         = coord_address
        self._locator               = locator
        self._cfg_manager           = cfg_manager
        
        # This dictionary will be used to store the ongoing and not-yet-queried 
        # async requests. They will be stored by session.
        # TODO: Consider refactoring this.
        self._async_requests = {}

        self._load_assigned_experiments()
        


    #######################################################
    #
    # Parse the configuration and load all the experiments 
    # found. This task is executed at the beginning.
    # 
    def _parse_assigned_experiments(self):
        assigned_experiments = self._cfg_manager.get_value(WEBLAB_LABORATORY_SERVER_ASSIGNED_EXPERIMENTS)

        parsed_experiments = []

        for experiment_instance_id, data in assigned_experiments.items():
            mo = re.match(self.EXPERIMENT_INSTANCE_ID_REGEX, experiment_instance_id)
            if mo == None:
                raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid configuration entry. Expected format: %s; found: %s" % 
                    (LaboratoryServer.EXPERIMENT_INSTANCE_ID_REGEX, experiment_instance_id))
            else:
                # ExperimentInstanceId
                groups = mo.groups()
                (   exp_inst_name,
                    exp_name,
                    exp_cat_name
                ) = groups
                experiment_instance_id = ExperimentInstanceId(exp_inst_name, exp_name, exp_cat_name)

                # CoordAddress
                try:
                    coord_address = CoordAddress.CoordAddress.translate_address(data['coord_address'])
                except GeneratorExceptions.GeneratorException:
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid coordination address: %s" % data['coord_address'])
                
                # CheckingHandlers
                checkers = data.get('checkers', ())
                checking_handlers = []
                for checker in checkers:
                    klazz = checker[0]
                    if klazz in IsUpAndRunningHandler.HANDLERS:
                        argss, kargss = (), {}
                        if len(checker) >= 3:
                            kargss = checker[2]
                        if len(checker) >= 2:
                            argss = checker[1]
                        checking_handlers.append(eval('IsUpAndRunningHandler.'+klazz)(*argss, **kargss))
                    else:
                        raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid IsUpAndRunningHandler: %s" % klazz)

                api = data.get('api', 'current')
                if not ExperimentApiLevel.is_level(api):
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Invalid api: %s. See %s" % (api, ExperimentApiLevel.__file__))

                if not ExperimentApiLevel.is_supported(api):
                    raise LaboratoryExceptions.InvalidLaboratoryConfigurationException("Unsupported api: %s" % api)

                parsed_experiments.append( (experiment_instance_id, coord_address, checking_handlers, ExperimentApiLevel.get_level(api)) )
        return parsed_experiments

    def _load_assigned_experiments(self):
        self._assigned_experiments = AssignedExperiments.AssignedExperiments()
        parsed_experiments         = self._parse_assigned_experiments()
        for exp_inst_id, coord_address, checking_handlers, api in parsed_experiments:
            self._assigned_experiments.add_server(exp_inst_id, coord_address, checking_handlers, None)
            
            # Upon loading, we will also try to find out which API each experiment should use, by asking
            # the experiment server itself. Sometimes, however, the experiment server will not really be
{'fraction_non_alphanumeric': 0.07996702390766694, 'fraction_numerical': 0.0019236053860950811, 'mean_word_length': 2.742159383033419, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3494570', 'n_tokens_mistral': 4040, 'n_tokens_neox': 3829, 'n_words': 882}