def __init__(self, generic_scheduler_arguments, baseurl, username, password, login_baseurl=None, experiments_map=None, **kwargs): super(ExternalWebLabDeustoScheduler, self).__init__(generic_scheduler_arguments, **kwargs) self.baseurl = baseurl self.login_baseurl = login_baseurl self.username = username self.password = password if experiments_map is None: self.experiments_map = {} else: self.experiments_map = experiments_map from weblab.core.coordinator.coordinator import POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME post_reservation_expiration_time = self.cfg_manager.get_value( POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME) self.expiration_delta = datetime.timedelta( seconds=post_reservation_expiration_time) period = self.cfg_manager.get_value(RETRIEVAL_PERIOD_PROPERTY_NAME, DEFAULT_RETRIEVAL_PERIOD) self.retriever = ResultsRetriever(self, period, self._create_logged_in_client) self.retriever.start() self.external_weblabdeusto_reservations = self.EXTERNAL_WEBLABDEUSTO_RESERVATIONS % ( self.baseurl, self.resource_type_name)
def __init__(self, generic_scheduler_arguments, baseurl, username, password, login_baseurl = None, experiments_map = None, uuid = None, **kwargs): super(ExternalWebLabDeustoScheduler, self).__init__(generic_scheduler_arguments, **kwargs) self.baseurl = baseurl self.login_baseurl = login_baseurl self.username = username self.password = password if experiments_map is None: self.experiments_map = {} else: self.experiments_map = experiments_map if uuid is None: self.uuids = [] elif isinstance(uuid, basestring): human = baseurl self.uuids = [ (uuid, human) ] else: self.uuids = [uuid] from weblab.core.coordinator.coordinator import POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME post_reservation_expiration_time = self.cfg_manager.get_value(POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME) self.expiration_delta = datetime.timedelta(seconds=post_reservation_expiration_time) period = self.cfg_manager.get_value(RETRIEVAL_PERIOD_PROPERTY_NAME, DEFAULT_RETRIEVAL_PERIOD) self.retriever = ResultsRetriever(self, period, self._create_logged_in_client) self.retriever.start() self.external_weblabdeusto_reservations = self.EXTERNAL_WEBLABDEUSTO_RESERVATIONS % (self.baseurl, self.resource_type_name)
class ExternalWebLabDeustoScheduler(Scheduler): EXTERNAL_WEBLABDEUSTO_RESERVATIONS = 'weblab:externals:weblabdeusto:%s:%s:reservations' EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS = 'weblab:externals:weblabdeusto:pending:%s:%s' def __init__(self, generic_scheduler_arguments, baseurl, username, password, login_baseurl=None, experiments_map=None, uuid=None, **kwargs): super(ExternalWebLabDeustoScheduler, self).__init__(generic_scheduler_arguments, **kwargs) self.baseurl = baseurl self.login_baseurl = login_baseurl self.username = username self.password = password if experiments_map is None: self.experiments_map = {} else: self.experiments_map = experiments_map if uuid is None: self.uuids = [] elif isinstance(uuid, basestring): human = baseurl self.uuids = [(uuid, human)] else: self.uuids = [uuid] from weblab.core.coordinator.coordinator import POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME post_reservation_expiration_time = self.cfg_manager.get_value( POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME) self.expiration_delta = datetime.timedelta( seconds=post_reservation_expiration_time) period = self.cfg_manager.get_value(RETRIEVAL_PERIOD_PROPERTY_NAME, DEFAULT_RETRIEVAL_PERIOD) self.retriever = ResultsRetriever(self, period, self._create_logged_in_client) self.retriever.start() self.external_weblabdeusto_reservations = self.EXTERNAL_WEBLABDEUSTO_RESERVATIONS % ( self.baseurl, self.resource_type_name) def stop(self): self.retriever.stop() @Override(Scheduler) def is_remote(self): return True @Override(Scheduler) def get_uuids(self): return self.uuids @logged() @Override(Scheduler) def removing_current_resource_slot(self, session, resource_instance_id): # Will in fact never be called return False # TODO: pooling def _create_client(self, cookies=None): client = WebLabDeustoClient(self.baseurl) if cookies is not None: client.set_cookies(cookies) return client def _create_login_client(self, cookies=None): client = WebLabDeustoClient(self.login_baseurl or self.baseurl) if cookies is not None: client.set_cookies(cookies) return client def _create_logged_in_client(self, cookies): login_client = self._create_login_client(cookies) session_id = login_client.login(self.username, self.password) client = self._create_client(login_client.get_cookies()) return session_id, client ####################################################################### # # Given a reservation_id, it returns in which state the reservation is # @logged('info') @Override(Scheduler) def reserve_experiment(self, reservation_id, experiment_id, time, priority, initialization_in_accounting, client_initial_data, request_info): server_uuids = list(request_info.get(SERVER_UUIDS, [])) server_uuids.append( (self.core_server_uuid, self.core_server_uuid_human)) consumer_data = { 'time_allowed': time, 'priority': priority, 'initialization_in_accounting': initialization_in_accounting, 'external_user': request_info.get('username', ''), SERVER_UUIDS: server_uuids, } for forwarded_key in FORWARDED_KEYS: if forwarded_key in request_info: consumer_data[forwarded_key] = request_info[forwarded_key] # TODO: identifier of the server login_client = self._create_login_client() session_id = login_client.login(self.username, self.password) client = self._create_client(login_client.get_cookies()) serialized_client_initial_data = json.dumps(client_initial_data) serialized_consumer_data = json.dumps(consumer_data) # If the administrator has mapped that this experiment_id is other, take that other. Otherwide, take the same one requested_experiment_id_str = self.experiments_map.get( experiment_id.to_weblab_str(), experiment_id.to_weblab_str()) requested_experiment_id = ExperimentId.parse( requested_experiment_id_str) external_reservation = client.reserve_experiment( session_id, requested_experiment_id, serialized_client_initial_data, serialized_consumer_data) if external_reservation.is_null(): return None remote_reservation_id = external_reservation.reservation_id.id log.log( ExternalWebLabDeustoScheduler, log.level.Warning, "Local reservation_id %s is linked to remote reservation %s" % (reservation_id, remote_reservation_id)) cookies = client.get_cookies() serialized_cookies = pickle.dumps(cookies) redis_client = self.redis_maker() pipeline = redis_client.pipeline() pipeline.hset( self.external_weblabdeusto_reservations, reservation_id, json.dumps({ 'remote_reservation_id': remote_reservation_id, 'cookies': serialized_cookies, 'start_time': time_mod.time(), })) external_weblabdeusto_pending_results = self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % ( self.resource_type_name, self.core_server_route) pipeline.hset( external_weblabdeusto_pending_results, reservation_id, json.dumps({ 'remote_reservation_id': remote_reservation_id, 'username': request_info.get('username', ''), 'serialized_request_info': pickle.dumps(request_info), 'experiment_id_str': experiment_id.to_weblab_str(), })) pipeline.execute() reservation_status = self._convert_reservation_to_status( external_reservation, reservation_id, remote_reservation_id) return reservation_status ####################################################################### # # Given a reservation_id, it returns in which state the reservation is # @logged('info') @Override(Scheduler) def get_reservation_status(self, reservation_id): reservation_found = False max_iterations = 15 while not reservation_found and max_iterations >= 0: client = self.redis_maker() reservation_str = client.hget( self.external_weblabdeusto_reservations, reservation_id) if reservation_str is None: external_weblabdeusto_pending_results = self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % ( self.resource_type_name, self.core_server_route) pending_result = client.hget( external_weblabdeusto_pending_results, reservation_id) if pending_result is None: # reservation not yet stored in local database pass else: return WSS.PostReservationStatus(reservation_id, False, '', '') else: reservation = json.loads(reservation_str) reservation_found = True remote_reservation_id = reservation['remote_reservation_id'] serialized_cookies = reservation['cookies'] # Introduce a delay to let the system store the reservation in the local database if not reservation_found: time_mod.sleep(0.1) max_iterations -= 1 if not reservation_found: return WSS.PostReservationStatus(reservation_id, False, '', '') cookies = pickle.loads(str(serialized_cookies)) client = self._create_client(cookies) reservation = client.get_reservation_status( SessionId(remote_reservation_id)) return self._convert_reservation_to_status(reservation, reservation_id, remote_reservation_id) def _convert_reservation_to_status(self, reservation, local_reservation_id, remote_reservation_id): reservation_status = reservation.to_status() reservation_status.set_reservation_id(local_reservation_id) if reservation_status.status == WSS.WebLabSchedulingStatus.RESERVED_REMOTE: # # If it has been successfully reserved in a remote server, it can mean two things: # # a) the remote_reservation_id can be empty, and therefore the remote server we're # contacting is the one with the resource # # b) the remote_reservation_id is other address, and therefore the remote server is # proxying the communications another server # # We have to change the remote reservation id if it is empty # if reservation_status.remote_reservation_id == '': reservation_status.set_remote_reservation_id( remote_reservation_id) reservation_id_with_route = '%s;%s.%s' % ( local_reservation_id, local_reservation_id, self.core_server_route) reservation_status.reservation_id = reservation_id_with_route return reservation_status ################################################################ # # Called when it is confirmed by the Laboratory Server. # @logged('info') @Override(Scheduler) def confirm_experiment(self, reservation_id, lab_session_id, initial_configuration, exp_info): # At some point, we must call the upper level to say that we want to confirm # at this point, it's normal that they call us back, even if there is nothing # to do pass ################################################################ # # Called when the user disconnects or finishes the resource. # @logged('info') @Override(Scheduler) def finish_reservation(self, reservation_id): redis_client = self.redis_maker() reservation_str = redis_client.hget( self.external_weblabdeusto_reservations, reservation_id) if reservation_str is not None: reservation = json.loads(reservation_str) remote_reservation_id = reservation['remote_reservation_id'] serialized_cookies = reservation['cookies'] else: log.log( ExternalWebLabDeustoScheduler, log.level.Info, "Not finishing reservation %s since somebody already did it" % reservation_id) return cookies = pickle.loads(str(serialized_cookies)) client = self._create_client(cookies) client.finished_experiment(SessionId(remote_reservation_id)) try: client.get_reservation_status(SessionId(remote_reservation_id)) except: # TODO: Actually check that the reservation was expired pass # Expired reservation else: now = self.time_provider.get_datetime() self.post_reservation_data_manager.create( reservation_id, now, now + self.expiration_delta, json.dumps("''")) result = redis_client.hdel(self.external_weblabdeusto_reservations, reservation_id) if not result: log.log( ExternalWebLabDeustoScheduler, log.level.Info, "Not deleting reservation %s from ExternalWebLabDeustoReservation since somebody already did it" % reservation_id) return ############################################################## # # ONLY FOR TESTING: It completely removes the whole database # @Override(Scheduler) def _clean(self): client = self.redis_maker() client.delete(self.external_weblabdeusto_reservations) for key in client.keys(self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % (self.resource_type_name, '*')): client.delete(key)
class ExternalWebLabDeustoScheduler(Scheduler): EXTERNAL_WEBLABDEUSTO_RESERVATIONS = 'weblab:externals:weblabdeusto:%s:%s:reservations' EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS = 'weblab:externals:weblabdeusto:pending:%s:%s' def __init__(self, generic_scheduler_arguments, baseurl, username, password, login_baseurl = None, experiments_map = None, uuid = None, **kwargs): super(ExternalWebLabDeustoScheduler, self).__init__(generic_scheduler_arguments, **kwargs) self.baseurl = baseurl self.login_baseurl = login_baseurl self.username = username self.password = password if experiments_map is None: self.experiments_map = {} else: self.experiments_map = experiments_map if uuid is None: self.uuids = [] elif isinstance(uuid, basestring): human = baseurl self.uuids = [ (uuid, human) ] else: self.uuids = [uuid] from weblab.core.coordinator.coordinator import POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME post_reservation_expiration_time = self.cfg_manager.get_value(POST_RESERVATION_EXPIRATION_TIME, DEFAULT_POST_RESERVATION_EXPIRATION_TIME) self.expiration_delta = datetime.timedelta(seconds=post_reservation_expiration_time) period = self.cfg_manager.get_value(RETRIEVAL_PERIOD_PROPERTY_NAME, DEFAULT_RETRIEVAL_PERIOD) self.retriever = ResultsRetriever(self, period, self._create_logged_in_client) self.retriever.start() self.external_weblabdeusto_reservations = self.EXTERNAL_WEBLABDEUSTO_RESERVATIONS % (self.baseurl, self.resource_type_name) def stop(self): self.retriever.stop() @Override(Scheduler) def is_remote(self): return True @Override(Scheduler) def get_uuids(self): return self.uuids @logged() @Override(Scheduler) def removing_current_resource_slot(self, session, resource_instance_id): # Will in fact never be called return False # TODO: pooling def _create_client(self, cookies = None): client = WebLabDeustoClient(self.baseurl) if cookies is not None: client.set_cookies(cookies) return client def _create_login_client(self, cookies = None): client = WebLabDeustoClient(self.login_baseurl or self.baseurl) if cookies is not None: client.set_cookies(cookies) return client def _create_logged_in_client(self, cookies): login_client = self._create_login_client(cookies) session_id = login_client.login(self.username, self.password) client = self._create_client(login_client.get_cookies()) return session_id, client ####################################################################### # # Given a reservation_id, it returns in which state the reservation is # @logged('info') @Override(Scheduler) def reserve_experiment(self, reservation_id, experiment_id, time, priority, initialization_in_accounting, client_initial_data, request_info): server_uuids = list(request_info.get(SERVER_UUIDS, [])) server_uuids.append((self.core_server_uuid, self.core_server_uuid_human)) consumer_data = { 'time_allowed' : time, 'priority' : priority, 'initialization_in_accounting' : initialization_in_accounting, 'external_user' : request_info.get('username', ''), SERVER_UUIDS : server_uuids, } for forwarded_key in FORWARDED_KEYS: if forwarded_key in request_info: consumer_data[forwarded_key] = request_info[forwarded_key] # TODO: identifier of the server login_client = self._create_login_client() session_id = login_client.login(self.username, self.password) client = self._create_client(login_client.get_cookies()) serialized_client_initial_data = json.dumps(client_initial_data) serialized_consumer_data = json.dumps(consumer_data) # If the administrator has mapped that this experiment_id is other, take that other. Otherwide, take the same one requested_experiment_id_str = self.experiments_map.get(experiment_id.to_weblab_str(), experiment_id.to_weblab_str()) requested_experiment_id = ExperimentId.parse(requested_experiment_id_str) external_reservation = client.reserve_experiment(session_id, requested_experiment_id, serialized_client_initial_data, serialized_consumer_data) if external_reservation.is_null(): return None remote_reservation_id = external_reservation.reservation_id.id log.log(ExternalWebLabDeustoScheduler, log.level.Warning, "Local reservation_id %s is linked to remote reservation %s" % (reservation_id, remote_reservation_id)) cookies = client.get_cookies() serialized_cookies = pickle.dumps(cookies) redis_client = self.redis_maker() pipeline = redis_client.pipeline() pipeline.hset(self.external_weblabdeusto_reservations, reservation_id, json.dumps({ 'remote_reservation_id' : remote_reservation_id, 'cookies' : serialized_cookies, 'start_time' : time_mod.time(), })) external_weblabdeusto_pending_results = self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % (self.resource_type_name, self.core_server_route) pipeline.hset(external_weblabdeusto_pending_results, reservation_id, json.dumps({ 'remote_reservation_id' : remote_reservation_id, 'username' : request_info.get('username',''), 'serialized_request_info' : pickle.dumps(request_info), 'experiment_id_str' : experiment_id.to_weblab_str(), })) pipeline.execute() reservation_status = self._convert_reservation_to_status(external_reservation, reservation_id, remote_reservation_id) return reservation_status ####################################################################### # # Given a reservation_id, it returns in which state the reservation is # @logged('info') @Override(Scheduler) def get_reservation_status(self, reservation_id): reservation_found = False max_iterations = 15 while not reservation_found and max_iterations >= 0: client = self.redis_maker() reservation_str = client.hget(self.external_weblabdeusto_reservations, reservation_id) if reservation_str is None: external_weblabdeusto_pending_results = self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % (self.resource_type_name, self.core_server_route) pending_result = client.hget(external_weblabdeusto_pending_results, reservation_id) if pending_result is None: # reservation not yet stored in local database pass else: return WSS.PostReservationStatus(reservation_id, False, '', '') else: reservation = json.loads(reservation_str) reservation_found = True remote_reservation_id = reservation['remote_reservation_id'] serialized_cookies = reservation['cookies'] # Introduce a delay to let the system store the reservation in the local database if not reservation_found: time_mod.sleep(0.1) max_iterations -= 1 if not reservation_found: return WSS.PostReservationStatus(reservation_id, False, '', '') cookies = pickle.loads(str(serialized_cookies)) client = self._create_client(cookies) reservation = client.get_reservation_status(SessionId(remote_reservation_id)) return self._convert_reservation_to_status(reservation, reservation_id, remote_reservation_id) def _convert_reservation_to_status(self, reservation, local_reservation_id, remote_reservation_id): reservation_status = reservation.to_status() reservation_status.set_reservation_id(local_reservation_id) if reservation_status.status == WSS.WebLabSchedulingStatus.RESERVED_REMOTE: # # If it has been successfully reserved in a remote server, it can mean two things: # # a) the remote_reservation_id can be empty, and therefore the remote server we're # contacting is the one with the resource # # b) the remote_reservation_id is other address, and therefore the remote server is # proxying the communications another server # # We have to change the remote reservation id if it is empty # if reservation_status.remote_reservation_id == '': reservation_status.set_remote_reservation_id(remote_reservation_id) reservation_id_with_route = '%s;%s.%s' % (local_reservation_id, local_reservation_id, self.core_server_route) reservation_status.reservation_id = reservation_id_with_route return reservation_status ################################################################ # # Called when it is confirmed by the Laboratory Server. # @logged('info') @Override(Scheduler) def confirm_experiment(self, reservation_id, lab_session_id, initial_configuration, exp_info): # At some point, we must call the upper level to say that we want to confirm # at this point, it's normal that they call us back, even if there is nothing # to do pass ################################################################ # # Called when the user disconnects or finishes the resource. # @logged('info') @Override(Scheduler) def finish_reservation(self, reservation_id): redis_client = self.redis_maker() reservation_str = redis_client.hget(self.external_weblabdeusto_reservations, reservation_id) if reservation_str is not None: reservation = json.loads(reservation_str) remote_reservation_id = reservation['remote_reservation_id'] serialized_cookies = reservation['cookies'] else: log.log(ExternalWebLabDeustoScheduler, log.level.Info, "Not finishing reservation %s since somebody already did it" % reservation_id) return cookies = pickle.loads(str(serialized_cookies)) client = self._create_client(cookies) client.finished_experiment(SessionId(remote_reservation_id)) try: client.get_reservation_status(SessionId(remote_reservation_id)) except: # TODO: Actually check that the reservation was expired pass # Expired reservation else: now = self.time_provider.get_datetime() self.post_reservation_data_manager.create(reservation_id, now, now + self.expiration_delta, json.dumps("''")) result = redis_client.hdel(self.external_weblabdeusto_reservations, reservation_id) if not result: log.log(ExternalWebLabDeustoScheduler, log.level.Info, "Not deleting reservation %s from ExternalWebLabDeustoReservation since somebody already did it" % reservation_id) return ############################################################## # # ONLY FOR TESTING: It completely removes the whole database # @Override(Scheduler) def _clean(self): client = self.redis_maker() client.delete(self.external_weblabdeusto_reservations) for key in client.keys(self.EXTERNAL_WEBLABDEUSTO_PENDING_RESULTS % (self.resource_type_name, '*')): client.delete(key)