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
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)
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?")
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
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)
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)