def response_load_gateway_by_type_and_id( gateway_type, gateway_id ): """ Given a gateway's numeric type and ID, load it from the datastore. """ if gateway_id == None: # invalid header return (None, 403, None) gateway_read_start = storagetypes.get_time() gateway = storage.read_gateway( gateway_id ) if gateway is None: # not present return (None, 404, None) if GATEWAY_TYPE_TO_STR.get( gateway.gateway_type ) == None: # bad type (shouldn't happen) return (None, 400, None) if GATEWAY_TYPE_TO_STR[ gateway.gateway_type ] != gateway_type: # caller has the wrong type return (None, 401, None) gateway_read_time = storagetypes.get_time() - gateway_read_start return (gateway, 200, gateway_read_time)
def response_load_gateway_by_type_and_id(gateway_type, gateway_id): """ Given a gateway's numeric type and ID, load it from the datastore. """ if gateway_id == None: # invalid header return (None, 403, None) gateway_read_start = storagetypes.get_time() gateway = storage.read_gateway(gateway_id) if gateway is None: # not present return (None, 404, None) if GATEWAY_TYPE_TO_STR.get(gateway.gateway_type) == None: # bad type (shouldn't happen) return (None, 400, None) if GATEWAY_TYPE_TO_STR[gateway.gateway_type] != gateway_type: # caller has the wrong type return (None, 401, None) gateway_read_time = storagetypes.get_time() - gateway_read_start return (gateway, 200, gateway_read_time)
def benchmark(category, benchmark_data, func): """ Call function func, and time how long it takes to run. Store it in the given benchmark_data (dict) under the key 'category'. Return the result of func. """ start = storagetypes.get_time() rc = func() t = storagetypes.get_time() - start if not benchmark_data.has_key(category): benchmark_data[category] = [] benchmark_data[category].append(t) return rc
def benchmark( category, benchmark_data, func ): """ Call function func, and time how long it takes to run. Store it in the given benchmark_data (dict) under the key 'category'. Return the result of func. """ start = storagetypes.get_time() rc = func() t = storagetypes.get_time() - start if not benchmark_data.has_key(category): benchmark_data[category] = [] benchmark_data[category].append( t ) return rc
def response_begin(request_handler, volume_id): """ Begin a response to a calling gateway, given the request handler and either the volume name or ID. Load up the calling gateway and the volume it's trying to access, and return both along with some benchmark information. Return (volume, gateway, status, benchmark dict) on success Return Nones on failure. """ timing = {} now = storagetypes.get_time() volume, gateway, volume_cert_bundle, status, read_time = response_load_volume_and_gateway( request_handler, volume_id) # transfer over cert bundle to volume if volume is not None and volume_cert_bundle is not None: cert_bundle = VolumeCertBundle.Load(volume_cert_bundle) volume.cert_bundle = cert_bundle timing['request_start'] = now timing['response_preprocess_time'] = read_time return (volume, gateway, status, timing)
def response_end( request_handler, status, data, content_type=None, timing=None ): """ Finish a response to a calling gateway, optionally including some benchmarking information in the HTTP headers. Reply with the given status and the raw data string (response MIME type is application/octet-stream unless otherwise specified). """ if content_type == None: content_type = "application/octet-stream" if timing != None: request_total = storagetypes.get_time() - timing['request_start'] timing['X-Total-Time'] = str(request_total) timing['X-Response-Preprocess-Time'] = str(timing['response_preprocess_time']) del timing['request_start'] del timing['response_preprocess_time'] for (time_header, time) in timing.items(): request_handler.response.headers[time_header] = time request_handler.response.headers['Content-Type'] = content_type request_handler.response.status = status request_handler.response.write( data ) return
def response_end(request_handler, status, data, content_type=None, timing=None): """ Finish a response to a calling gateway, optionally including some benchmarking information in the HTTP headers. Reply with the given status and the raw data string (response MIME type is application/octet-stream unless otherwise specified). """ if content_type == None: content_type = "application/octet-stream" if timing != None: request_total = storagetypes.get_time() - timing['request_start'] timing['X-Total-Time'] = str(request_total) timing['X-Response-Preprocess-Time'] = str( timing['response_preprocess_time']) del timing['request_start'] del timing['response_preprocess_time'] for (time_header, time) in timing.items(): request_handler.response.headers[time_header] = time request_handler.response.headers['Content-Type'] = content_type request_handler.response.status = status request_handler.response.write(data) return
def post(self, volume_id_str, volume_version_str, cert_version_str ): file_post_start = storagetypes.get_time() volume_id = None try: volume_id = int(volume_id_str) except Exception, e: response_user_error( self, 400 ) return
def create_async( cls, _requester_owner_id, _volume_id, _volume_name, _nonce, _status, **attrs ): ts = int(storagetypes.get_time()) return VolumeAccessRequest.get_or_insert_async( VolumeAccessRequest.make_key_name( _requester_owner_id, _volume_id ), requester_owner_id = _requester_owner_id, volume_id = _volume_id, nonce=_nonce, request_timestamp=ts, status=_status, volume_name=_volume_name, **attrs )
def post(self, volume_id_str, volume_version_str, cert_version_str): file_post_start = storagetypes.get_time() file_post_pre = file_post_start volume_id = None try: volume_id = int(volume_id_str) except Exception, e: response_user_error(self, 400) return
def response_begin(request_handler, volume_name_or_id, fail_if_no_auth_header=True): """ Begin a response to a calling gateway, given the request handler and either the volume name or ID. Load up the calling gateway and the volume it's trying to access, and return both along with some benchmark information. Return Nones on failure. TODO: load volume and gateway in parallel """ timing = {} timing['request_start'] = storagetypes.get_time() # get the Volume volume, status, volume_read_time = response_load_volume( request_handler, volume_name_or_id) if status != 200: return (None, None, None) gateway_read_time = 0 gateway = None # try to authenticate the gateway gateway, status, gateway_read_time = response_load_gateway( request_handler, volume) if fail_if_no_auth_header and (status != 200 or gateway == None): return (None, None, None) # make sure this gateway is allowed to access this Volume if volume.need_gateway_auth(): if gateway is not None: valid_gateway = volume.is_gateway_in_volume(gateway) if not valid_gateway: # gateway does not belong to this Volume logging.error("Not in this Volume") response_user_error(request_handler, 403) return (None, None, None) else: logging.error("No gateway, but we required authentication") response_user_error(request_handler, 403) return (None, None, None) # if we're still here, we're good to go timing['X-Volume-Time'] = str(volume_read_time) timing['X-Gateway-Time'] = str(gateway_read_time) return (gateway, volume, timing)
def create_async(cls, _requester_owner_id, _volume_id, _volume_name, _nonce, _status, **attrs): ts = int(storagetypes.get_time()) return VolumeAccessRequest.get_or_insert_async( VolumeAccessRequest.make_key_name(_requester_owner_id, _volume_id), requester_owner_id=_requester_owner_id, volume_id=_volume_id, nonce=_nonce, request_timestamp=ts, status=_status, volume_name=_volume_name, **attrs)
def response_begin( request_handler, volume_name_or_id, fail_if_no_auth_header=True ): """ Begin a response to a calling gateway, given the request handler and either the volume name or ID. Load up the calling gateway and the volume it's trying to access, and return both along with some benchmark information. Return Nones on failure. TODO: load volume and gateway in parallel """ timing = {} timing['request_start'] = storagetypes.get_time() # get the Volume volume, status, volume_read_time = response_load_volume( request_handler, volume_name_or_id ) if status != 200: return (None, None, None) gateway_read_time = 0 gateway = None # try to authenticate the gateway gateway, status, gateway_read_time = response_load_gateway( request_handler, volume ) if fail_if_no_auth_header and (status != 200 or gateway == None): return (None, None, None) # make sure this gateway is allowed to access this Volume if volume.need_gateway_auth(): if gateway is not None: valid_gateway = volume.is_gateway_in_volume( gateway ) if not valid_gateway: # gateway does not belong to this Volume logging.error("Not in this Volume") response_user_error( request_handler, 403 ) return (None, None, None) else: logging.error("No gateway, but we required authentication") response_user_error( request_handler, 403 ) return (None, None, None) # if we're still here, we're good to go timing['X-Volume-Time'] = str(volume_read_time) timing['X-Gateway-Time'] = str(gateway_read_time) return (gateway, volume, timing)
def response_load_volume(request_handler, volume_name_or_id): """ Load a volume from the data store, given either its name or ID. Automatically reply with an error message via the given request handler. """ volume_read_start = storagetypes.get_time() volume = storage.read_volume(volume_name_or_id) volume_read_time = storagetypes.get_time() - volume_read_start if volume == None: # no volume response_volume_error(request_handler, 404) return (None, 404, None) if not volume.active: # inactive volume response_volume_error(request_handler, 503) return (None, 503, None) return (volume, 200, volume_read_time)
def response_load_volume( request_handler, volume_name_or_id ): """ Load a volume from the data store, given either its name or ID. Automatically reply with an error message via the given request handler. """ volume_read_start = storagetypes.get_time() volume = storage.read_volume( volume_name_or_id ) volume_read_time = storagetypes.get_time() - volume_read_start if volume == None: # no volume response_volume_error( request_handler, 404 ) return (None, 404, None) if not volume.active: # inactive volume response_volume_error( request_handler, 503 ) return (None, 503, None) return (volume, 200, volume_read_time)
def response_begin( request_handler, volume_id ): """ Begin a response to a calling gateway, given the request handler and either the volume name or ID. Load up the calling gateway and the volume it's trying to access, and return both along with some benchmark information. Return (volume, gateway, status, benchmark dict) on success Return Nones on failure. """ timing = {} now = storagetypes.get_time() volume, gateway, volume_cert_bundle, status, read_time = response_load_volume_and_gateway( request_handler, volume_id ) # transfer over cert bundle to volume if volume is not None and volume_cert_bundle is not None: cert_bundle = VolumeCertBundle.Load( volume_cert_bundle ) volume.cert_bundle = cert_bundle timing['request_start'] = now timing['response_preprocess_time'] = read_time return (volume, gateway, status, timing)
def response_load_volume_and_gateway(request_handler, volume_id, gateway_id=None): """ Load a volume and the gateway from the request handler. Return (volume, gateway, volume_cert_bundle, status, time) """ read_start = storagetypes.get_time() # get the gateway's ID and credentials g_id = None if gateway_id is None: gateway_type, g_id, signature_b64 = response_read_gateway_basic_auth( request_handler.request.headers) else: g_id = gateway_id volume = None gateway = None cert_bundle = None volume_fut = Volume.Read(volume_id, async=True) cert_bundle_fut = VolumeCertBundle.Get(volume_id, async=True) gateway_fut = None if g_id is not None: gateway_fut = Gateway.Read(g_id, async=True) storagetypes.wait_futures([volume_fut, gateway_fut, cert_bundle_fut]) volume = volume_fut.get_result() cert_bundle = cert_bundle_fut.get_result() if gateway_fut is not None: gateway = gateway_fut.get_result() if volume is None or cert_bundle is None: logging.error("No volume, gateway, or cert bundle") response_user_error(request_handler, 404) return (None, None, None, 404, None) ''' Volume.SetCache( volume.volume_id, volume ) VolumeCertBundle.SetCache( volume.volume_id, cert_bundle ) if gateway is not None: Gateway.SetCache( gateway.g_id, gateway ) ''' # sanity checks if (volume.need_gateway_auth()) and (gateway is None or gateway_type is None or signature_b64 is None): # required authentication, but we don't have an Authentication header logging.error("Unable to authenticate gateway") return (None, None, None, 403, None) # need auth? if volume.need_gateway_auth() and gateway is None: logging.error("Unable to authenticate gateway") return (None, None, None, 403, None) # gateway validity if gateway is not None: # type match? if gateway_type is not None and gateway.gateway_type != gateway_type: logging.error("Type mismatch on %s:%s" % (gateway_type, g_id)) response_user_error(request_handler, 403) return (None, None, None, 403, None) # is the gateway in this volume? if not volume.is_gateway_in_volume(gateway): logging.error("Gateway '%s' is not in volume '%s'" % (gateway.name, volume.name)) response_user_error(request_handler, 403) return (None, None, None, 403, None) # make sure this gateway's cert is registered valid_gateway = gateway.authenticate_session( gateway_type, g_id, request_handler.request.url, signature_b64) if not valid_gateway and volume.need_gateway_auth(): # invalid credentials logging.error("Invalid authentication credentials") response_user_error(request_handler, 403) return (None, None, None, 403, None) read_time = storagetypes.get_time() - read_start return (volume, gateway, cert_bundle, 200, read_time)
if request.type not in MSFileHandler.post_validators.keys(): logging.error("Unrecognized request %s" % request.type) response_user_error(self, 501) return valid, failure_status = MSFileHandler.post_validators[ request.type](gateway, request) if not valid: logging.error("Failed to validate request") response_user_error(self, failure_status, "Argument validation failed") return timing = {} file_post_begin = storagetypes.get_time() - file_post_pre file_post_operations_pre = storagetypes.get_time() # carry out the operation(s), and count them num_processed = 0 types = {} for request in update_set.requests: if not types.has_key(request.type): types[request.type] = 0 types[request.type] += 1 # these are guaranteed to be non-None... api_call = MSFileHandler.post_api_calls.get(request.type, None)
for request in update_set.requests: if request.type not in MSFileHandler.post_validators.keys(): logging.error("Unrecognized request %s" % request.type) response_user_error( self, 501 ) return valid, failure_status = MSFileHandler.post_validators[request.type]( gateway, request ) if not valid: logging.error("Failed to validate request") response_user_error( self, failure_status, "Argument validation failed" ) return timing = {} file_post_begin = storagetypes.get_time() - file_post_pre file_post_operations_pre = storagetypes.get_time() # carry out the operation(s), and count them num_processed = 0 types = {} for request in update_set.requests: if not types.has_key(request.type): types[request.type] = 0 types[request.type] += 1 # these are guaranteed to be non-None... api_call = MSFileHandler.post_api_calls.get( request.type, None )
def response_load_volume_and_gateway( request_handler, volume_id, gateway_id=None ): """ Load a volume and the gateway from the request handler. Return (volume, gateway, volume_cert_bundle, status, time) """ read_start = storagetypes.get_time() # get the gateway's ID and credentials g_id = None if gateway_id is None: gateway_type, g_id, signature_b64 = response_read_gateway_basic_auth( request_handler.request.headers ) else: g_id = gateway_id volume = None gateway = None cert_bundle = None volume_fut = Volume.Read( volume_id, async=True ) cert_bundle_fut = VolumeCertBundle.Get( volume_id, async=True ) gateway_fut = None if g_id is not None: gateway_fut = Gateway.Read( g_id, async=True ) storagetypes.wait_futures( [volume_fut, gateway_fut, cert_bundle_fut] ) volume = volume_fut.get_result() cert_bundle = cert_bundle_fut.get_result() if gateway_fut is not None: gateway = gateway_fut.get_result() if volume is None or cert_bundle is None: logging.error("No volume, gateway, or cert bundle") response_user_error( request_handler, 404 ) return (None, None, None, 404, None) Volume.SetCache( volume.volume_id, volume ) VolumeCertBundle.SetCache( volume.volume_id, cert_bundle ) if gateway is not None: Gateway.SetCache( gateway.g_id, gateway ) # sanity checks if (volume.need_gateway_auth()) and (gateway is None or gateway_type is None or signature_b64 is None): # required authentication, but we don't have an Authentication header logging.error("Unable to authenticate gateway") return (None, None, None, 403, None) # need auth? if volume.need_gateway_auth() and gateway is None: logging.error("Unable to authenticate gateway") return (None, None, None, 403, None) # gateway validity if gateway is not None: # type match? if gateway_type is not None and gateway.gateway_type != gateway_type: logging.error("Type mismatch on %s:%s" % (gateway_type, g_id)) response_user_error( request_handler, 403 ) return (None, None, None, 403, None ) # is the gateway in this volume? if not volume.is_gateway_in_volume( gateway ): logging.error("Gateway '%s' is not in volume '%s'" % (gateway.name, volume.name)) response_user_error( request_handler, 403 ) return (None, None, None, 403, None) # make sure this gateway's cert is registered valid_gateway = gateway.authenticate_session( gateway_type, g_id, request_handler.request.url, signature_b64 ) if not valid_gateway and volume.need_gateway_auth(): # invalid credentials logging.error("Invalid authentication credentials") response_user_error( request_handler, 403 ) return (None, None, None, 403, None) read_time = storagetypes.get_time() - read_start return (volume, gateway, cert_bundle, 200, read_time)
def post(self, volume_id_str ): file_post_start = storagetypes.get_time() update_set = file_update_parse( self ) if update_set == None: # malformed data response_user_error( self, 202, "%s\n" % (-errno.EINVAL) ) return # begin the response gateway, volume, response_timing = response_begin( self, volume_id_str ) if volume == None: return allowed = file_update_auth( gateway, volume ) if not allowed: log.error("Failed to authenticate") response_user_error( self, 403 ) return # verify the message integrity and authenticity if not gateway.verify_message( update_set ): # authentication failure response_user_error( self, 401, "Signature verification failed") return # TODO: rate-limit # populate the reply reply = file_update_init_response( volume ) status = 200 # validate requests before processing them for update in update_set.updates: if update.type not in MSFileHandler.post_validators.keys(): logging.error("Unrecognized update %s" % update.type) response_user_error( self, 501 ) return valid, failure_status = MSFileHandler.post_validators[update.type]( gateway, update ) if not valid: log.error("Failed to validate update") response_user_error( self, failure_status, "Argument validation failed" ) return timing = {} # carry out the operation(s), and count them num_processed = 0 types = {} for update in update_set.updates: if not types.has_key(update.type): types[update.type] = 0 types[update.type] += 1 # these are guaranteed to be non-None... api_call = MSFileHandler.post_api_calls.get( update.type, None ) benchmark_header = MSFileHandler.post_benchmark_headers.get( update.type, None ) # run the API call, but benchmark it too try: rc = benchmark( benchmark_header, timing, lambda: api_call( reply, gateway, volume, update ) ) reply.errors.append( rc ) num_processed += 1 except storagetypes.RequestDeadlineExceededError, de: # quickly now... response_user_error( self, 503 ) return except Exception, e: logging.exception(e) reply.error = -errno.EREMOTEIO break
def post(self, volume_id_str): file_post_start = storagetypes.get_time() update_set = file_update_parse(self) if update_set == None: # malformed data response_user_error(self, 202, "%s\n" % (-errno.EINVAL)) return # begin the response gateway, volume, response_timing = response_begin(self, volume_id_str) if volume == None: return allowed = file_update_auth(gateway, volume) if not allowed: log.error("Failed to authenticate") response_user_error(self, 403) return # verify the message integrity and authenticity if not gateway.verify_message(update_set): # authentication failure response_user_error(self, 401, "Signature verification failed") return # TODO: rate-limit # populate the reply reply = file_update_init_response(volume) status = 200 # validate requests before processing them for update in update_set.updates: if update.type not in MSFileHandler.post_validators.keys(): logging.error("Unrecognized update %s" % update.type) response_user_error(self, 501) return valid, failure_status = MSFileHandler.post_validators[update.type]( gateway, update) if not valid: log.error("Failed to validate update") response_user_error(self, failure_status, "Argument validation failed") return timing = {} # carry out the operation(s), and count them num_processed = 0 types = {} for update in update_set.updates: if not types.has_key(update.type): types[update.type] = 0 types[update.type] += 1 # these are guaranteed to be non-None... api_call = MSFileHandler.post_api_calls.get(update.type, None) benchmark_header = MSFileHandler.post_benchmark_headers.get( update.type, None) # run the API call, but benchmark it too try: rc = benchmark( benchmark_header, timing, lambda: api_call(reply, gateway, volume, update)) reply.errors.append(rc) num_processed += 1 except storagetypes.RequestDeadlineExceededError, de: # quickly now... response_user_error(self, 503) return except Exception, e: logging.exception(e) reply.error = -errno.EREMOTEIO break