Exemple #1
0
    def __init__(self, shard=0, listen_on_address=None):
        signal.signal(signal.SIGINT, lambda unused_x, unused_y: self.exit())

        self.name = self.__class__.__name__
        self.shard = shard
        self._my_coord = ServiceCoord(self.name, self.shard)

        # Dictionaries of (to be) connected RemoteServiceClients.
        self.remote_services = {}

        self.initialize_logging()

        # We setup the listening address for services which want to
        # connect with us.
        try:
            address = get_service_address(self._my_coord)
        except KeyError:
            raise ConfigError("Unable to find address for service %r. "
                              "Is it specified in core_services in cms.conf?" %
                              (self._my_coord,))

        logger.info("--- %s %s %s", self.name, listen_on_address, address)
        if listen_on_address is not None:
            self.rpc_server = StreamServer(
                Address(listen_on_address, address.port),
                self._connection_handler)
        else:
            self.rpc_server = StreamServer(address, self._connection_handler)
        self.backdoor = None
Exemple #2
0
    def __init__(self, shard, contest):
        parameters = {
            "login_url":
            "/",
            "template_path":
            pkg_resources.resource_filename("cms.server.contest", "templates"),
            "static_files": [("cms.server", "static"),
                             ("cms.server.contest", "static")],
            "cookie_secret":
            base64.b64encode(config.secret_key),
            "debug":
            config.tornado_debug,
            "is_proxy_used":
            config.is_proxy_used,
        }

        try:
            listen_address = config.contest_listen_address[shard]
            listen_port = config.contest_listen_port[shard]
        except IndexError:
            raise ConfigError("Wrong shard number for %s, or missing "
                              "address/port configuration. Please check "
                              "contest_listen_address and contest_listen_port "
                              "in cms.conf." % __name__)

        super(ContestWebServer, self).__init__(listen_port,
                                               HANDLERS,
                                               parameters,
                                               shard=shard,
                                               listen_address=listen_address)

        self.contest = contest

        # This is a dictionary (indexed by username) of pending
        # notification. Things like "Yay, your submission went
        # through.", not things like "Your question has been replied",
        # that are handled by the db. Each username points to a list
        # of tuples (timestamp, subject, text).
        self.notifications = {}

        # Retrieve the available translations.
        self.langs = {
            lang_code: wrap_translations_for_tornado(trans)
            for lang_code, trans in get_translations().iteritems()
        }

        self.file_cacher = FileCacher(self)
        self.evaluation_service = self.connect_to(
            ServiceCoord("EvaluationService", 0))
        self.scoring_service = self.connect_to(
            ServiceCoord("ScoringService", 0))

        ranking_enabled = len(config.rankings) > 0
        self.proxy_service = self.connect_to(ServiceCoord("ProxyService", 0),
                                             must_be_present=ranking_enabled)

        printing_enabled = config.printer is not None
        self.printing_service = self.connect_to(
            ServiceCoord("PrintingService", 0),
            must_be_present=printing_enabled)
Exemple #3
0
def test_db_connection():
    """Perform an operation that raises if the DB is not reachable.

    raise (sqlalchemy.exc.OperationalError): if the DB cannot be
        accessed (usually for permission problems).

    """
    try:
        # We do not care of the specific query executed here, we just
        # use it to ensure that the DB is accessible.
        with SessionGen() as session:
            session.execute("select 0;")
    except OperationalError:
        raise ConfigError("Operational error while talking to the DB. "
                          "Is the connection string in cms.conf correct?")
Exemple #4
0
    def connect_to(self,
                   coord,
                   on_connect=None,
                   on_disconnect=None,
                   must_be_present=True):
        """Return a proxy to a remote service.

        Obtain a communication channel to the remote service at the
        given coord (reusing an existing one, if possible), attach the
        on_connect and on_disconnect handlers and return it.

        coord (ServiceCoord): the coord of the service to connect to.
        on_connect (function|None): to be called when the service
            connects.
        on_disconnect (function|None): to be called when it
            disconnects.
        must_be_present (bool): if True, the coord must be present in
            the configuration; otherwise, it can be missing and in
            that case the return value is a fake client (that is, a
            client that never connects and ignores all calls).

        return (RemoteServiceClient): a proxy to that service.

        """
        if coord not in self.remote_services:
            try:
                service = RemoteServiceClient(coord, auto_retry=0.5)
            except KeyError:
                # The coordinates are invalid: raise a ConfigError if
                # the service was needed, or return a dummy client if
                # the service was optional.
                if must_be_present:
                    raise ConfigError("Missing address and port for %s "
                                      "in cms.conf." % (coord, ))
                else:
                    service = FakeRemoteServiceClient(coord, None)
            service.connect()
            self.remote_services[coord] = service
        else:
            service = self.remote_services[coord]

        if on_connect is not None:
            service.add_on_connect_handler(on_connect)

        if on_disconnect is not None:
            service.add_on_disconnect_handler(on_disconnect)

        return service
Exemple #5
0
def default_argument_parser(description, cls, ask_contest=None):
    """Default argument parser for services.

    This has two versions, depending on whether the service needs a
    contest_id, or not.

    description (string): description of the service.
    cls (type): service's class.
    ask_contest (function|None): None if the service does not require
        a contest, otherwise a function that returns a contest_id
        (after asking the admins?)

    return (object): an instance of a service.

    """
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument("shard", action="store", type=int, nargs="?")

    # We need to allow using the switch "-c" also for services that do
    # not need the contest_id because RS needs to be able to restart
    # everything without knowing which is which.
    contest_id_help = "id of the contest to automatically load, " \
                      "or ALL to serve all contests"
    if ask_contest is None:
        contest_id_help += " (ignored)"
    parser.add_argument("-c",
                        "--contest-id",
                        action="store",
                        type=utf8_decoder,
                        help=contest_id_help)
    args = parser.parse_args()

    try:
        args.shard = get_safe_shard(cls.__name__, args.shard)
    except ValueError:
        raise ConfigError("Couldn't autodetect shard number and "
                          "no shard specified for service %s, "
                          "quitting." % (cls.__name__, ))

    if ask_contest is None:
        return cls(args.shard)
    contest_id = contest_id_from_args(args.contest_id, ask_contest)
    if contest_id is None:
        return cls(args.shard)
    else:
        return cls(args.shard, contest_id)
Exemple #6
0
    def __init__(self, shard, contest_id=None):
        parameters = {
            "static_files": [("cms.server", "static"),
                             ("cms.server.contest", "static")],
            "cookie_secret":
            hex_to_bin(config.secret_key),
            "debug":
            config.tornado_debug,
            "is_proxy_used":
            config.is_proxy_used,
            "num_proxies_used":
            config.num_proxies_used,
            "xsrf_cookies":
            True,
        }

        try:
            listen_address = config.contest_listen_address[shard]
            listen_port = config.contest_listen_port[shard]
        except IndexError:
            raise ConfigError("Wrong shard number for %s, or missing "
                              "address/port configuration. Please check "
                              "contest_listen_address and contest_listen_port "
                              "in cms.conf." % __name__)

        self.contest_id = contest_id

        if self.contest_id is None:
            HANDLERS.append((r"", MainHandler))
            handlers = [(r'/', ContestListHandler)]
            for h in HANDLERS:
                handlers.append((r'/([^/]+)' + h[0], ) + h[1:])
        else:
            HANDLERS.append((r"/", MainHandler))
            handlers = HANDLERS

        super().__init__(listen_port,
                         handlers,
                         parameters,
                         shard=shard,
                         listen_address=listen_address)

        self.wsgi_app = SharedDataMiddleware(
            self.wsgi_app, {"/stl": config.stl_path},
            cache=True,
            cache_timeout=SECONDS_IN_A_YEAR,
            fallback_mimetype="application/octet-stream")

        self.jinja2_environment = CWS_ENVIRONMENT

        # This is a dictionary (indexed by username) of pending
        # notification. Things like "Yay, your submission went
        # through.", not things like "Your question has been replied",
        # that are handled by the db. Each username points to a list
        # of tuples (timestamp, subject, text).
        self.notifications = {}

        # Retrieve the available translations.
        self.translations = get_translations()

        self.evaluation_service = self.connect_to(
            ServiceCoord("EvaluationService", 0))
        self.scoring_service = self.connect_to(
            ServiceCoord("ScoringService", 0))

        ranking_enabled = len(config.rankings) > 0
        self.proxy_service = self.connect_to(ServiceCoord("ProxyService", 0),
                                             must_be_present=ranking_enabled)

        printing_enabled = config.printer is not None
        self.printing_service = self.connect_to(
            ServiceCoord("PrintingService", 0),
            must_be_present=printing_enabled)