def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" partition, nodes = self.app.account_ring.get_nodes(self.account_name) resp = self.GETorHEAD_base( req, _('Account'), self.app.account_ring, partition, req.path_info.rstrip('/')) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp headers = self.generate_request_headers(req) resp = self.make_requests( Request.blank('/v1/' + self.account_name), self.app.account_ring, partition, 'PUT', '/' + self.account_name, [headers] * len(nodes)) if not is_success(resp.status_int): self.app.logger.warning('Could not autocreate account %r' % self.account_name) return resp resp = self.GETorHEAD_base( req, _('Account'), self.app.account_ring, partition, req.path_info.rstrip('/')) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = self.clean_acls(req) or check_metadata(req, "container") if error_response: return error_response if len(self.container_name) > MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = "Container name length of %d longer than %d" % ( len(self.container_name), MAX_CONTAINER_NAME_LENGTH, ) return resp account_partition, accounts, container_count = self.account_info(self.account_name, req) if not accounts and self.app.account_autocreate: self.autocreate_account(req.environ, self.account_name) account_partition, accounts, container_count = self.account_info(self.account_name, req) if not accounts: return HTTPNotFound(request=req) if ( self.app.max_containers_per_account > 0 and container_count >= self.app.max_containers_per_account and self.account_name not in self.app.max_containers_whitelist ): resp = HTTPForbidden(request=req) resp.body = "Reached container limit of %s" % self.app.max_containers_per_account return resp container_partition, containers = self.app.container_ring.get_nodes(self.account_name, self.container_name) headers = self._backend_requests(req, len(containers), account_partition, accounts) clear_info_cache(self.app, req.environ, self.account_name, self.container_name) resp = self.make_requests(req, self.app.container_ring, container_partition, "PUT", req.path_info, headers) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp partition = self.app.account_ring.get_part(self.account_name) concurrency = self.app.account_ring.replica_count \ if self.app.concurrent_gets else 1 node_iter = self.app.iter_nodes(self.app.account_ring, partition) resp = self.GETorHEAD_base( req, _('Account'), node_iter, partition, req.swift_entity_path.rstrip('/'), concurrency) if resp.status_int == HTTP_NOT_FOUND: if resp.headers.get('X-Account-Status', '').lower() == 'deleted': resp.status = HTTP_GONE elif self.app.account_autocreate: resp = account_listing_response(self.account_name, req, get_listing_content_type(req)) if req.environ.get('swift_owner'): self.add_acls_from_sys_metadata(resp) else: for header in self.app.swift_owner_headers: resp.headers.pop(header, None) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response if len(self.container_name) > MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), MAX_CONTAINER_NAME_LENGTH) return resp account_partition, accounts, container_count = \ self.account_info(self.account_name, autocreate=self.app.account_autocreate) if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp if not accounts: return HTTPNotFound(request=req) container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) headers = self._backend_requests(req, len(containers), account_partition, accounts) if self.app.memcache: cache_key = get_container_memcache_key(self.account_name, self.container_name) self.app.memcache.delete(cache_key) resp = self.make_requests( req, self.app.container_ring, container_partition, 'PUT', req.path_info, headers) return resp
def POST(self, req): """HTTP POST request handler.""" if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp error_response = check_metadata(req, 'account') if error_response: return error_response account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = self.generate_request_headers(req, transfer=True) clear_info_cache(self.app, req.environ, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, 'POST', req.swift_entity_path, [headers] * len(accounts)) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: self.autocreate_account(req, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, 'POST', req.swift_entity_path, [headers] * len(accounts)) self.add_acls_from_sys_metadata(resp) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" print 'in GETorHEAD function of accountcontroller class' if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp partition = self.app.account_ring.get_part(self.account_name) print 'partition',partition node_iter = self.app.iter_nodes(self.app.account_ring, partition) print 'node_iter',node_iter resp = self.GETorHEAD_base( req, _('Account'), node_iter, partition, req.swift_entity_path.rstrip('/')) print 'resp',resp if resp.status_int == HTTP_NOT_FOUND: print 'resp.status_int == HTTP_NOT_FOUND' if resp.headers.get('X-Account-Status', '').lower() == 'deleted': resp.status = HTTP_GONE elif self.app.account_autocreate: resp = account_listing_response(self.account_name, req, get_listing_content_type(req)) if req.environ.get('swift_owner'): print 'req.environ.get(swift_owner), true' self.add_acls_from_sys_metadata(resp) else: for header in self.app.swift_owner_headers: print 'header',header resp.headers.pop(header, None) print 'resp',resp print 'in GETorHEAD function of accountcontroller class end' return resp
def PUT(self, req): """HTTP PUT request handler.""" print 'in PUT function of accountcontroller class' if not self.app.allow_account_management: return HTTPMethodNotAllowed( request=req, headers={'Allow': ', '.join(self.allowed_methods)}) error_response = check_metadata(req, 'account') print 'error_response' if error_response: return error_response if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) print ' account_partition, accounts',account_partion,accounts headers = self.generate_request_headers(req, transfer=True) print 'headers',headers clear_info_cache(self.app, req.environ, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, 'PUT', req.swift_entity_path, [headers] * len(accounts)) print 'resp',resp self.add_acls_from_sys_metadata(resp) print 'in PUT function of accountcontroller class END' return resp
def POST(self, req): """HTTP POST request handler.""" error_response = check_metadata(req, 'account') if error_response: return error_response account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), 'X-Trans-Id': self.trans_id, 'Connection': 'close'} self.transfer_headers(req.headers, headers) if self.app.memcache: self.app.memcache.delete('account%s' % req.path_info.rstrip('/')) resp = self.make_requests( req, self.app.account_ring, account_partition, 'POST', req.path_info, [headers] * len(accounts)) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp resp = self.make_requests( Request.blank('/v1/' + self.account_name), self.app.account_ring, account_partition, 'PUT', '/' + self.account_name, [headers] * len(accounts)) if not is_success(resp.status_int): self.app.logger.warning('Could not autocreate account %r' % self.account_name) return resp return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" partition, nodes = self.app.account_ring.get_nodes(self.account_name) shuffle(nodes) resp = self.GETorHEAD_base( req, _('Account'), partition, nodes, req.path_info.rstrip('/'), len(nodes)) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp headers = {'X-Timestamp': normalize_timestamp(time.time()), 'X-Trans-Id': self.trans_id, 'Connection': 'close'} resp = self.make_requests( Request.blank('/v1/' + self.account_name), self.app.account_ring, partition, 'PUT', '/' + self.account_name, [headers] * len(nodes)) if not is_success(resp.status_int): self.app.logger.warning('Could not autocreate account %r' % self.account_name) return resp resp = self.GETorHEAD_base( req, _('Account'), partition, nodes, req.path_info.rstrip('/'), len(nodes)) return resp
def PUT(self, req): """HTTP PUT request handler.""" if not self.app.allow_account_management: return HTTPMethodNotAllowed( request=req, headers={'Allow': ', '.join(self.allowed_methods)}) error_response = check_metadata(req, 'account') if error_response: return error_response if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), 'x-trans-id': self.trans_id, 'Connection': 'close'} self.transfer_headers(req.headers, headers) if self.app.memcache: self.app.memcache.delete('account%s' % req.path_info.rstrip('/')) resp = self.make_requests( req, self.app.account_ring, account_partition, 'PUT', req.path_info, [headers] * len(accounts)) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) if len(self.container_name) > constraints.MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), constraints.MAX_CONTAINER_NAME_LENGTH) return resp container_count = self.account_info(self.account_name, req) clear_info_cache(self.app, req.environ, self.account_name, self.container_name) storage = self.app.storage try: storage.container_create(self.account_name, self.container_name) except exceptions.OioException: return HTTPServerError(request=req) resp = HTTPCreated(request=req) return resp
def POST(self, req): """HTTP POST request handler.""" error_response = check_metadata(req, 'account') if error_response: return error_response account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = self.generate_request_headers(req, transfer=True) if self.app.memcache: self.app.memcache.delete( get_account_memcache_key(self.account_name)) resp = self.make_requests( req, self.app.account_ring, account_partition, 'POST', req.path_info, [headers] * len(accounts)) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp resp = self.make_requests( Request.blank('/v1/' + self.account_name), self.app.account_ring, account_partition, 'PUT', '/' + self.account_name, [headers] * len(accounts)) if not is_success(resp.status_int): self.app.logger.warning('Could not autocreate account %r' % self.account_name) return resp return resp
def HEAD(self, request): """Handle HTTP HEAD requests for the Swift Object Server.""" try: device, partition, account, container, obj = split_path(unquote(request.path), 5, 5, True) validate_device_partition(device, partition) except ValueError, err: resp = HTTPBadRequest(request=request) resp.content_type = "text/plain" resp.body = str(err) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response policy_index = self._convert_policy_to_index(req) if policy_index is None: policy_index = int(POLICIES.default) if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) if len(self.container_name) > constraints.MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), constraints.MAX_CONTAINER_NAME_LENGTH) return resp account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts and self.app.account_autocreate: self.autocreate_account(req, self.account_name) account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts: return HTTPNotFound(request=req) if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: container_info = \ self.container_info(self.account_name, self.container_name, req) if not is_success(container_info.get('status')): resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) headers = self._backend_requests(req, len(containers), account_partition, accounts, policy_index) clear_info_cache(self.app, req.environ, self.account_name, self.container_name) resp = self.make_requests( req, self.app.container_ring, container_partition, 'PUT', req.swift_entity_path, headers) cloud_ring = CloudRing(self.container_name, POLICIES.get_by_index(policy_index)) return_flag, _info = cloud_ring.create_containers() if not return_flag: msg = 'Failed:' + str(_info) raise PUTCloudContainerException(msg) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" length_limit = self.get_name_length_limit() if len(self.account_name) > length_limit: resp = HTTPBadRequest(request=req) resp.body = b'Account name length of %d longer than %d' % \ (len(self.account_name), length_limit) # Don't cache this. We know the account doesn't exist because # the name is bad; we don't need to cache that because it's # really cheap to recompute. return resp partition = self.app.account_ring.get_part(self.account_name) concurrency = self.app.account_ring.replica_count \ if self.app.concurrent_gets else 1 node_iter = self.app.iter_nodes(self.app.account_ring, partition) params = req.params params['format'] = 'json' req.params = params resp = self.GETorHEAD_base( req, _('Account'), node_iter, partition, req.swift_entity_path.rstrip('/'), concurrency) if resp.status_int == HTTP_NOT_FOUND: if resp.headers.get('X-Account-Status', '').lower() == 'deleted': resp.status = HTTP_GONE elif self.app.account_autocreate: # This is kind of a lie; we pretend like the account is # there, but it's not. We'll create it as soon as something # tries to write to it, but we don't need databases on disk # to tell us that nothing's there. # # We set a header so that certain consumers can tell it's a # fake listing. The important one is the PUT of a container # to an autocreate account; the proxy checks to see if the # account exists before actually performing the PUT and # creates the account if necessary. If we feed it a perfect # lie, it'll just try to create the container without # creating the account, and that'll fail. resp = account_listing_response( self.account_name, req, listing_formats.get_listing_content_type(req)) resp.headers['X-Backend-Fake-Account-Listing'] = 'yes' # Cache this. We just made a request to a storage node and got # up-to-date information for the account. resp.headers['X-Backend-Recheck-Account-Existence'] = str( self.app.recheck_account_existence) set_info_cache(self.app, req.environ, self.account_name, None, resp) if req.environ.get('swift_owner'): self.add_acls_from_sys_metadata(resp) else: for header in self.app.swift_owner_headers: resp.headers.pop(header, None) return resp
def get_name_and_placement(request, *args): policy_idx = request.headers.get(POLICY_INDEX, '0') try: policy_idx = int(policy_idx) except ValueError: results = HTTPBadRequest( "Policy index must be numeric (was %s)" % policy_idx) results = split_and_validate_path(request, *args) results.append(policy_idx) return results
def PUT(self, req): """HTTP PUT request handler.""" #container元数据参数检查 error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response policy_index = self._convert_policy_to_index(req) if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) if len(self.container_name) > constraints.MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), constraints.MAX_CONTAINER_NAME_LENGTH) return resp #获取account的元数据信息,分区、节点、以及container数量 account_partition, accounts, container_count = \ self.account_info(self.account_name, req) #如果account不存在,则创建account if not accounts and self.app.account_autocreate: self.autocreate_account(req, self.account_name) account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts: return HTTPNotFound(request=req) #检查account里面container超过上限值,则报错 if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: container_info = \ self.container_info(self.account_name, self.container_name, req) if not is_success(container_info.get('status')): resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp #通过ring环计算分区、container所在节点 container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) #生成创建container请求的header headers = self._backend_requests(req, len(containers), account_partition, accounts, policy_index) #清除本地缓存account和container的元数据信息 clear_info_cache(self.app, req.environ, self.account_name, self.container_name) #发送请求到所有container节点,创建container resp = self.make_requests( req, self.app.container_ring, container_partition, 'PUT', req.swift_entity_path, headers) return resp
def HEAD(self, request): """Handle HTTP HEAD requests for the Swift Object Server.""" start_time = time.time() try: device, partition, account, container, obj = \ split_path(unquote(request.path), 5, 5, True) validate_device_partition(device, partition) except ValueError, err: self.logger.increment('HEAD.errors') resp = HTTPBadRequest(request=request) resp.content_type = 'text/plain' resp.body = str(err) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = "Account name length of %d longer than %d" % (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp partition, nodes = self.app.account_ring.get_nodes(self.account_name) resp = self.GETorHEAD_base(req, _("Account"), self.app.account_ring, partition, req.path_info.rstrip("/")) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: resp = account_listing_response(self.account_name, req, get_listing_content_type(req)) if not req.environ.get("swift_owner", False): for key in self.app.swift_owner_headers: if key in resp.headers: del resp.headers[key] return resp
def POST(self, req): """HTTP POST request handler.""" if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp error_response = check_metadata(req, 'account') if error_response: return error_response clear_info_cache(self.app, req.environ, self.account_name) resp = self.get_account_post_resp(req) self.add_acls_from_sys_metadata(resp) return resp
def PUT(self, req): """HTTP PUT request handler.""" if not self.app.allow_account_management: return HTTPMethodNotAllowed(request=req, headers={"Allow": ", ".join(self.allowed_methods)}) error_response = check_metadata(req, "account") if error_response: return error_response if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = "Account name length of %d longer than %d" % (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp account_partition, accounts = self.app.account_ring.get_nodes(self.account_name) headers = self.generate_request_headers(req, transfer=True) clear_info_cache(self.app, req.environ, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, "PUT", req.path_info, [headers] * len(accounts) ) return resp
def HEAD(self, req): """HTTP HEAD request handler.""" if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp resp = self.get_account_head_resp(req) _set_info_cache(self.app, req.environ, self.account_name, None, resp) if req.environ.get('swift_owner'): self.add_acls_from_sys_metadata(resp) else: for header in self.app.swift_owner_headers: resp.headers.pop(header, None) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response if len(self.container_name) > MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), MAX_CONTAINER_NAME_LENGTH) return resp account_partition, accounts, container_count = \ self.account_info(self.account_name, autocreate=self.app.account_autocreate) if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp if not accounts: return HTTPNotFound(request=req) container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) headers = [] for account in accounts: nheaders = {'X-Timestamp': normalize_timestamp(time.time()), 'x-trans-id': self.trans_id, 'X-Account-Host': '%(ip)s:%(port)s' % account, 'X-Account-Partition': account_partition, 'X-Account-Device': account['device'], 'Connection': 'close'} self.transfer_headers(req.headers, nheaders) headers.append(nheaders) if self.app.memcache: cache_key = get_container_memcache_key(self.account_name, self.container_name) self.app.memcache.delete(cache_key) resp = self.make_requests( req, self.app.container_ring, container_partition, 'PUT', req.path_info, headers) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp partition, nodes = self.app.account_ring.get_nodes(self.account_name) resp = self.GETorHEAD_base( req, _('Account'), self.app.account_ring, partition, req.path_info.rstrip('/')) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: content_type, error = account_listing_content_type(req) if error: return error return account_listing_response(self.account_name, req, content_type) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response policy_index = self._convert_policy_to_index(req) if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) if len(self.container_name) > constraints.MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), constraints.MAX_CONTAINER_NAME_LENGTH) return resp account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts and self.app.account_autocreate: self.autocreate_account(req.environ, self.account_name) account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts: return HTTPNotFound(request=req) if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) headers = self._backend_requests(req, len(containers), account_partition, accounts, policy_index) clear_info_cache(self.app, req.environ, self.account_name, self.container_name) resp = self.make_requests( req, self.app.container_ring, container_partition, 'PUT', req.swift_entity_path, headers) return resp
def __call__(self, env, start_response): request = Request(env) if request.method == 'PUT': try: version, account, container, obj = \ request.split_path(1, 4, True) except ValueError: return self.app(env, start_response) # check container creation request if account and container and not obj: policy_name = request.headers.get('X-Storage-Policy', '') default_policy = POLICIES.default.name if (policy_name in self.policies) or \ (policy_name == '' and default_policy in self.policies): container = unquote(container) if len(container) > constraints. \ SOF_MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=request) resp.body = \ 'Container name length of %d longer than %d' % \ (len(container), constraints.SOF_MAX_CONTAINER_NAME_LENGTH) return resp(env, start_response) elif account and container and obj: # check object creation request obj = unquote(obj) container_info = get_container_info( env, self.app) policy = POLICIES.get_by_index( container_info['storage_policy']) if policy.name in self.policies: error_response = sof_check_object_creation(request, obj) if error_response: self.logger.warn("returning error: %s", error_response) return error_response(env, start_response) return self.app(env, start_response)
def PUT(self, req): """HTTP PUT request handler.""" if not self.app.allow_account_management: return HTTPMethodNotAllowed( request=req, headers={'Allow': ', '.join(self.allowed_methods)}) error_response = check_metadata(req, 'account') if error_response: return error_response if len(self.account_name) > constraints.MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Account name length of %d longer than %d' % \ (len(self.account_name), constraints.MAX_ACCOUNT_NAME_LENGTH) return resp resp = self.get_account_put_resp(req) clear_info_cache(self.app, req.environ, self.account_name) self.add_acls_from_sys_metadata(resp) return resp
def PUT(self, req): """HTTP PUT request handler.""" error_response = \ self.clean_acls(req) or check_metadata(req, 'container') if error_response: return error_response if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) if len(self.container_name) > constraints.MAX_CONTAINER_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = 'Container name length of %d longer than %d' % \ (len(self.container_name), constraints.MAX_CONTAINER_NAME_LENGTH) return resp account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts and self.app.account_autocreate: self.autocreate_account(req, self.account_name) account_partition, accounts, container_count = \ self.account_info(self.account_name, req) if not accounts: return HTTPNotFound(request=req) if self.app.max_containers_per_account > 0 and \ container_count >= self.app.max_containers_per_account and \ self.account_name not in self.app.max_containers_whitelist: container_info = \ self.container_info(self.account_name, self.container_name, req) if not is_success(container_info.get('status')): resp = HTTPForbidden(request=req) resp.body = 'Reached container limit of %s' % \ self.app.max_containers_per_account return resp headers = self.generate_request_headers(req, transfer=True) clear_info_cache(self.app, req.environ, self.account_name, self.container_name) resp = self.get_container_create_resp(req, headers) return resp
def POST(self, req): """HTTP POST request handler.""" if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = "Account name length of %d longer than %d" % (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp error_response = check_metadata(req, "account") if error_response: return error_response account_partition, accounts = self.app.account_ring.get_nodes(self.account_name) headers = self.generate_request_headers(req, transfer=True) clear_info_cache(self.app, req.environ, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, "POST", req.path_info, [headers] * len(accounts) ) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: self.autocreate_account(req.environ, self.account_name) resp = self.make_requests( req, self.app.account_ring, account_partition, "POST", req.path_info, [headers] * len(accounts) ) return resp
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: resp = HTTPBadRequest(request=req) resp.body = "Account name length of %d longer than %d" % (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) return resp partition, nodes = self.app.account_ring.get_nodes(self.account_name) resp = self.GETorHEAD_base( req, _("Account"), self.app.account_ring, partition, req.swift_entity_path.rstrip("/") ) if resp.status_int == HTTP_NOT_FOUND: if resp.headers.get("X-Account-Status", "").lower() == "deleted": resp.status = HTTP_GONE elif self.app.account_autocreate: resp = account_listing_response(self.account_name, req, get_listing_content_type(req)) if req.environ.get("swift_owner"): self.add_acls_from_sys_metadata(resp) else: for header in self.app.swift_owner_headers: resp.headers.pop(header, None) return resp
def dispatch(self, replicate_args, args): if not hasattr(args, 'pop'): return HTTPBadRequest(body='Invalid object type') op = args.pop(0) drive, partition, hsh = replicate_args if self.mount_check and not ismount(os.path.join(self.root, drive)): return Response(status='507 %s is not mounted' % drive) db_file = os.path.join(self.root, drive, storage_directory(self.datadir, partition, hsh), hsh + '.db') if op == 'rsync_then_merge': return self.rsync_then_merge(drive, db_file, args) if op == 'complete_rsync': return self.complete_rsync(drive, db_file, args) else: # someone might be about to rsync a db to us, # make sure there's a tmp dir to receive it. mkdirs(os.path.join(self.root, drive, 'tmp')) if not os.path.exists(db_file): return HTTPNotFound() return getattr(self, op)(self.broker_class(db_file), args)
def get_param(req, name, default=None): """ Get parameters from an HTTP request ensuring proper handling UTF-8 encoding. :param req: request object :param name: parameter name :param default: result to return if the parameter is not found :returns: HTTP request parameter value (as UTF-8 encoded str, not unicode object) :raises: HTTPBadRequest if param not valid UTF-8 byte sequence """ value = req.params.get(name, default) if value and not isinstance(value, six.text_type): try: value.decode('utf8') # Ensure UTF8ness except UnicodeDecodeError: raise HTTPBadRequest(request=req, content_type='text/plain', body='"%s" parameter not valid UTF-8' % name) return value
def handle_request(self, env, req): if req.method not in ('GET', 'HEAD', 'OPTIONS'): headers = self._getCacheHeaders(CACHE_BAD_URL) return HTTPMethodNotAllowed(request=req, headers=headers) if req.method == 'OPTIONS' and \ req.headers.get('Access-Control-Request-Method') not in \ ('GET', 'HEAD', 'OPTIONS'): headers = self._getCacheHeaders(CACHE_BAD_URL) return HTTPUnauthorized(request=req, headers={'Allow': 'GET, HEAD, OPTIONS'}) if self.allowed_origin_remote_ips and \ req.remote_addr not in self.allowed_origin_remote_ips: raise OriginRequestNotAllowed( 'SOS Origin: Remote IP %s not allowed' % req.remote_addr) # allow earlier middleware to override hash and obj_name hsh = env.get('swift.cdn_hash') object_name = env.get('swift.cdn_object_name') if hsh is None or object_name is None: for regex in self.cdn_regexes: match_obj = regex.match(req.url) if match_obj: match_dict = match_obj.groupdict() if not hsh: hsh = match_dict.get('hash') if not object_name: object_name = match_dict.get('object_name') break if not hsh: self.logger.debug('Hash %s not found in %s' % (hsh, req.url)) headers = self._getCacheHeaders(CACHE_BAD_URL) return HTTPNotFound(request=req, headers=headers) if hsh.find('-') >= 0: hsh = hsh.split('-', 1)[1] try: cdn_obj_path = self.get_hsh_obj_path(hsh) except ValueError, e: self.logger.debug('get_hsh_obj_path error: %s' % e) headers = self._getCacheHeaders(CACHE_BAD_URL) return HTTPBadRequest(request=req, headers=headers)
def _call_copy(self, env, start_response): """ Run the retry loop (copy operations). """ account, container, obj = self._convert_path(env.get('PATH_INFO')) if obj is None: # This is probably an account request return self.app(env, start_response) env['PATH_INFO'] = "/v1/%s/%s/%s" % (account, container, obj) for hdr in ("HTTP_OIO_COPY_FROM", "HTTP_X_COPY_FROM"): if hdr in env: from_value = env.get(hdr) from_header = hdr break else: raise HTTPBadRequest(body="Malformed copy-source header") # HTTP_X_COPY_FROM_ACCOUNT will just pass through if self.account_first: src_path = "/fake_account" + from_value else: src_path = from_value def modify_copy_from(orig_env, alternative): env_ = orig_env.copy() env_[from_header] = "/%s/%s" % (quote_plus( alternative[1]), alternative[2]) return env_ def check_container_obj(alternative): if not alternative[1] or not alternative[2]: raise HTTPBadRequest(body="Malformed copy-source header") return True return self._retry_loop(env, start_response, src_path, env_modifier=modify_copy_from, alt_checker=check_container_obj)
def DELETE(self, request): """Handle HTTP DELETE requests for the Swift Object Server.""" device, partition, account, container, obj = \ split_and_validate_path(request, 5, 5, True) if 'x-timestamp' not in request.headers or \ not check_float(request.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=request, content_type='text/plain') try: disk_file = self._diskfile(device, partition, account, container, obj) except DiskFileDeviceUnavailable: return HTTPInsufficientStorage(drive=device, request=request) if 'x-if-delete-at' in request.headers and \ int(request.headers['x-if-delete-at']) != \ int(disk_file.metadata.get('X-Delete-At') or 0): return HTTPPreconditionFailed( request=request, body='X-If-Delete-At and X-Delete-At do not match') old_delete_at = int(disk_file.metadata.get('X-Delete-At') or 0) if old_delete_at: self.delete_at_update('DELETE', old_delete_at, account, container, obj, request, device) orig_timestamp = disk_file.metadata.get('X-Timestamp', 0) req_timestamp = request.headers['X-Timestamp'] if disk_file.is_deleted() or disk_file.is_expired(): response_class = HTTPNotFound else: if orig_timestamp < req_timestamp: response_class = HTTPNoContent else: response_class = HTTPConflict if orig_timestamp < req_timestamp: disk_file.delete(req_timestamp) self.container_update( 'DELETE', account, container, obj, request, HeaderKeyDict({'x-timestamp': req_timestamp}), device) resp = response_class(request=request) return resp
def DELETE(self, req): """Handle HTTP DELETE request.""" drive, part, account, container, obj = split_and_validate_path( req, 4, 5, True) if 'x-timestamp' not in req.headers or \ not check_float(req.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=req, content_type='text/plain') if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) if account.startswith(self.auto_create_account_prefix) and obj and \ not os.path.exists(broker.db_file): try: broker.initialize( normalize_timestamp( req.headers.get('x-timestamp') or time.time())) except DatabaseAlreadyExists: pass if not os.path.exists(broker.db_file): return HTTPNotFound() if obj: # delete object broker.delete_object(obj, req.headers.get('x-timestamp')) return HTTPNoContent(request=req) else: # delete container if not broker.empty(): return HTTPConflict(request=req) existed = float(broker.get_info()['put_timestamp']) and \ not broker.is_deleted() broker.delete_db(req.headers['X-Timestamp']) if not broker.is_deleted(): return HTTPConflict(request=req) resp = self.account_update(req, account, container, broker) if resp: return resp if existed: return HTTPNoContent(request=req) return HTTPNotFound()
def POST(self, request): """Handle HTTP POST requests for the Swift Object Server.""" device, partition, account, container, obj, policy_idx = \ get_name_and_placement(request, 5, 5, True) req_timestamp = valid_timestamp(request) new_delete_at = int(request.headers.get('X-Delete-At') or 0) if new_delete_at and new_delete_at < time.time(): return HTTPBadRequest(body='X-Delete-At in past', request=request, content_type='text/plain') try: disk_file = self.get_diskfile( device, partition, account, container, obj, policy_idx=policy_idx) except DiskFileDeviceUnavailable: return HTTPInsufficientStorage(drive=device, request=request) try: orig_metadata = disk_file.read_metadata() except (DiskFileNotExist, DiskFileQuarantined): return HTTPNotFound(request=request) orig_timestamp = Timestamp(orig_metadata.get('X-Timestamp', 0)) if orig_timestamp >= req_timestamp: return HTTPConflict(request=request) metadata = {'X-Timestamp': req_timestamp.internal} metadata.update(val for val in request.headers.iteritems() if is_user_meta('object', val[0])) for header_key in self.allowed_headers: if header_key in request.headers: header_caps = header_key.title() metadata[header_caps] = request.headers[header_key] orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0) if orig_delete_at != new_delete_at: if new_delete_at: self.delete_at_update('PUT', new_delete_at, account, container, obj, request, device, policy_idx) if orig_delete_at: self.delete_at_update('DELETE', orig_delete_at, account, container, obj, request, device, policy_idx) disk_file.write_metadata(metadata) return HTTPAccepted(request=request)
def check_metadata(req, target_type): """ Check metadata sent in the request headers. This should only check that the metadata in the request given is valid. Checks against account/container overall metadata should be forwarded on to its respective server to be checked. :param req: request object :param target_type: str: one of: object, container, or account: indicates which type the target storage for the metadata is :returns: HTTPBadRequest with bad metadata otherwise None """ prefix = 'x-%s-meta-' % target_type.lower() meta_count = 0 meta_size = 0 for key, value in req.headers.items(): if isinstance(value, basestring) and len(value) > MAX_HEADER_SIZE: return HTTPBadRequest(body='Header value too long: %s' % key[:MAX_META_NAME_LENGTH], request=req, content_type='text/plain') if not key.lower().startswith(prefix): continue key = key[len(prefix):] if not key: return HTTPBadRequest(body='Metadata name cannot be empty', request=req, content_type='text/plain') meta_count += 1 meta_size += len(key) + len(value) if len(key) > MAX_META_NAME_LENGTH: return HTTPBadRequest( body='Metadata name too long: %s%s' % (prefix, key), request=req, content_type='text/plain') elif len(value) > MAX_META_VALUE_LENGTH: return HTTPBadRequest( body='Metadata value longer than %d: %s%s' % ( MAX_META_VALUE_LENGTH, prefix, key), request=req, content_type='text/plain') elif meta_count > MAX_META_COUNT: return HTTPBadRequest( body='Too many metadata items; max %d' % MAX_META_COUNT, request=req, content_type='text/plain') elif meta_size > MAX_META_OVERALL_SIZE: return HTTPBadRequest( body='Total metadata too large; max %d' % MAX_META_OVERALL_SIZE, request=req, content_type='text/plain') return None
def POST(self, req): """Handle HTTP POST request.""" drive, part, account, container = get_container_name_and_placement(req) req_timestamp = valid_timestamp(req) if 'x-container-sync-to' in req.headers: err, sync_to, realm, realm_key = validate_sync_to( req.headers['x-container-sync-to'], self.allowed_sync_hosts, self.realms_conf) if err: return HTTPBadRequest(err) try: check_drive(self.root, drive, self.mount_check) except ValueError: return HTTPInsufficientStorage(drive=drive, request=req) if not self.check_free_space(drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) if broker.is_deleted(): return HTTPNotFound(request=req) broker.update_put_timestamp(req_timestamp.internal) self._update_metadata(req, broker, req_timestamp, 'POST') return HTTPNoContent(request=req)
def get_slo_segments(self, obj_name, req): """ Performs a swob.Request and returns the SLO manifest's segments. :raises HTTPServerError: on unable to load obj_name or on unable to load the SLO manifest data. :raises HTTPBadRequest: on not an SLO manifest :raises HTTPNotFound: on SLO manifest not found :returns: SLO manifest's segments """ vrs, account, _junk = req.split_path(2, 3, True) new_env = req.environ.copy() new_env['REQUEST_METHOD'] = 'GET' del(new_env['wsgi.input']) new_env['QUERY_STRING'] = 'multipart-manifest=get' new_env['CONTENT_LENGTH'] = 0 new_env['HTTP_USER_AGENT'] = \ '%s MultipartDELETE' % new_env.get('HTTP_USER_AGENT') new_env['swift.source'] = 'SLO' new_env['PATH_INFO'] = ( '/%s/%s/%s' % ( vrs, account, obj_name.lstrip('/'))).encode('utf-8') resp = Request.blank('', new_env).get_response(self.app) if resp.is_success: if config_true_value(resp.headers.get('X-Static-Large-Object')): try: return json.loads(resp.body) except ValueError: raise HTTPServerError('Unable to load SLO manifest') else: raise HTTPBadRequest('Not an SLO manifest') elif resp.status_int == HTTP_NOT_FOUND: raise HTTPNotFound('SLO manifest not found') elif resp.status_int == HTTP_UNAUTHORIZED: raise HTTPUnauthorized('401 Unauthorized') else: raise HTTPServerError('Unable to load SLO manifest or segment.')
def _validate_registration(self, req): """ Validate parameters about storlet/dependency object when registrating :params req: swob.Request instance :raises ValueError: If some parameters are wrong """ params = self._parse_storlet_params(req.headers) try: if self.container == self.storlet_container: self.logger.debug('updating object in storlet container. ' 'Sanity check') self.gateway_class.validate_storlet_registration( params, self.obj) else: self.logger.debug('updating object in storlet dependency. ' 'Sanity check') self.gateway_class.validate_dependency_registration( params, self.obj) except ValueError as e: self.logger.exception('Bad parameter') raise HTTPBadRequest(e.args[0].encode('utf8'))
def inner(*args, **kwargs): self = args[0] req = args[1] key1 = req.headers.get('x-s3auth-admin-key') if not key1: return HTTPBadRequest(body='x-s3auth-admin-key header required', request=req) path = quote('/v1/{}'.format(self.auth_account)) resp = make_pre_authed_request(req.environ, 'HEAD', path).get_response(self.app) if resp.status_int // 100 != 2: raise Exception('Could not HEAD account: {} {}'.format( path, resp.status_int)) hashed_key2 = resp.headers.get(HKEY_HASHED_ADMIN_KEY) hash_key = resp.headers[HKEY_HASH_KEY].encode('utf-8') if _hash_msg(key1, hash_key) == hashed_key2: return f(*args, **kwargs) else: return _denied_response(req)
def POST(self, req): """Handle HTTP POST request.""" #logging.info("...post...") drive, part, account = split_and_validate_path(req, 3) if 'x-timestamp' not in req.headers or \ not check_float(req.headers['x-timestamp']): return HTTPBadRequest(body='Missing or bad timestamp', request=req, content_type='text/plain') if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account) if broker.is_deleted(): return self._deleted_response(broker, req, HTTPNotFound) timestamp = normalize_timestamp(req.headers['x-timestamp']) metadata = {} metadata.update((key, (value, timestamp)) for key, value in req.headers.iteritems() if key.lower().startswith('x-account-meta-')) if metadata: broker.update_metadata(metadata) return HTTPNoContent(request=req)
def get_segments_to_delete_iter(self, req): """ A generator function to be used to delete all the segments and sub-segments referenced in a manifest. :params req: a swob.Request with an SLO manifest in path :raises HTTPPreconditionFailed: on invalid UTF8 in request path :raises HTTPBadRequest: on too many buffered sub segments and on invalid SLO manifest path """ if not check_utf8(req.path_info): raise HTTPPreconditionFailed( request=req, body='Invalid UTF8 or contains NULL') vrs, account, container, obj = req.split_path(4, 4, True) segments = [{ 'sub_slo': True, 'name': ('/%s/%s' % (container, obj)).decode('utf-8')}] while segments: if len(segments) > MAX_BUFFERED_SLO_SEGMENTS: raise HTTPBadRequest( 'Too many buffered slo segments to delete.') seg_data = segments.pop(0) if seg_data.get('sub_slo'): try: segments.extend( self.get_slo_segments(seg_data['name'], req)) except HTTPException as err: # allow bulk delete response to report errors seg_data['error'] = {'code': err.status_int, 'message': err.body} # add manifest back to be deleted after segments seg_data['sub_slo'] = False segments.append(seg_data) else: seg_data['name'] = seg_data['name'].encode('utf-8') yield seg_data
def handle_put(self, req): """ Handle put request when it contains X-Symlink-Target header. Symlink headers are validated and moved to sysmeta namespace. :param req: HTTP PUT object request :returns: Response Iterator """ if req.content_length != 0: raise HTTPBadRequest( body='Symlink requests require a zero byte body', request=req, content_type='text/plain') _check_symlink_header(req) symlink_usermeta_to_sysmeta(req.headers) # Store info in container update that this object is a symlink. # We have a design decision to use etag space to store symlink info for # object listing because it's immutable unless the object is # overwritten. This may impact the downgrade scenario that the symlink # info can appear as the suffix in the hash value of object # listing result for clients. # To create override etag easily, we have a constraint that the symlink # must be 0 byte so we can add etag of the empty string + symlink info # here, simply. Note that this override etag may be encrypted in the # container db by encryption middleware. etag_override = [ MD5_OF_EMPTY_STRING, 'symlink_target=%s' % req.headers[TGT_OBJ_SYSMETA_SYMLINK_HDR] ] if TGT_ACCT_SYSMETA_SYMLINK_HDR in req.headers: etag_override.append( 'symlink_target_account=%s' % req.headers[TGT_OBJ_SYSMETA_SYMLINK_HDR]) req.headers['X-Object-Sysmeta-Container-Update-Override-Etag'] = \ '; '.join(etag_override) return self._app_call(req.environ)
def origin_db_head(self, env, req): """ Handles HEAD requests into Origin database """ try: vsn, account, container = split_path(req.path, 3, 3) except ValueError: return HTTPBadRequest() hsh = self.hash_path(account, container) cdn_obj_path = self.get_hsh_obj_path(hsh) hash_data = self.get_cdn_data(env, cdn_obj_path) if hash_data: headers = self.get_cdn_urls(hsh, 'HEAD') headers.update({ 'X-TTL': hash_data.ttl, 'X-Log-Retention': hash_data.logs_enabled and 'True' or 'False', 'X-CDN-Enabled': hash_data.cdn_enabled and 'True' or 'False' }) return HTTPNoContent(headers=headers) return HTTPNotFound(request=req)
def handle_request(self, req): """ Entry point for auth requests (ones that match the self.auth_prefix). Should return a WSGI-style callable (such as swob.Response). :param req: swob.Request object """ req.start_time = time() handler = None try: version, account, user, _junk = req.split_path(1, 4, True) except ValueError: self.logger.increment('errors') return HTTPNotFound(request=req) if version in ('v1', 'v1.0', 'auth'): if req.method == 'GET': handler = self.handle_get_token if not handler: self.logger.increment('errors') req.response = HTTPBadRequest(request=req) else: req.response = handler(req) return req.response
def UPDATE(self, req): """ Handle HTTP UPDATE request (merge_items RPCs coming from the proxy.) """ drive, part, account, container = get_container_name_and_placement(req) req_timestamp = valid_timestamp(req) try: check_drive(self.root, drive, self.mount_check) except ValueError: return HTTPInsufficientStorage(drive=drive, request=req) if not self.check_free_space(drive): return HTTPInsufficientStorage(drive=drive, request=req) requested_policy_index = self.get_and_validate_policy_index(req) broker = self._get_container_broker(drive, part, account, container) self._maybe_autocreate(broker, req_timestamp, account, requested_policy_index) try: objs = json.load(req.environ['wsgi.input']) except ValueError as err: return HTTPBadRequest(body=str(err), content_type='text/plain') broker.merge_items(objs) return HTTPAccepted(request=req)
def get_listing_content_type(req): """ Determine the content type to use for an account or container listing response. :param req: request object :returns: content type as a string (e.g. text/plain, application/json) :raises HTTPNotAcceptable: if the requested content type is not acceptable :raises HTTPBadRequest: if the 'format' query param is provided and not valid UTF-8 """ query_format = get_param(req, 'format') if query_format: req.accept = FORMAT2CONTENT_TYPE.get( query_format.lower(), FORMAT2CONTENT_TYPE['plain']) try: out_content_type = req.accept.best_match( ['text/plain', 'application/json', 'application/xml', 'text/xml']) except ValueError: raise HTTPBadRequest(request=req, body='Invalid Accept header') if not out_content_type: raise HTTPNotAcceptable(request=req) return out_content_type
def check_metadata(req, target_type): """ Check metadata sent in the request headers. :param req: request object :param target_type: str: one of: object, container, or account: indicates which type the target storage for the metadata is :returns: HTTPBadRequest with bad metadata otherwise None """ prefix = 'x-%s-meta-' % target_type.lower() meta_count = 0 meta_size = 0 for key, value in req.headers.iteritems(): if isinstance(value, basestring) and len(value) > MAX_HEADER_SIZE: return HTTPBadRequest('Header Line Too Long') if not key.lower().startswith(prefix): continue key = key[len(prefix):] if not key: return HTTPBadRequest(body='Metadata name cannot be empty', request=req, content_type='text/plain') meta_count += 1 meta_size += len(key) + len(value) if len(key) > MAX_META_NAME_LENGTH: return HTTPBadRequest(body='Metadata name too long; max %d' % MAX_META_NAME_LENGTH, request=req, content_type='text/plain') elif len(value) > MAX_META_VALUE_LENGTH: return HTTPBadRequest(body='Metadata value too long; max %d' % MAX_META_VALUE_LENGTH, request=req, content_type='text/plain') elif meta_count > MAX_META_COUNT: return HTTPBadRequest(body='Too many metadata items; max %d' % MAX_META_COUNT, request=req, content_type='text/plain') elif meta_size > MAX_META_OVERALL_SIZE: return HTTPBadRequest(body='Total metadata too large; max %d' % MAX_META_OVERALL_SIZE, request=req, content_type='text/plain') return None
def get_and_validate_policy_index(self, req): """ Validate that the index supplied maps to a policy. :returns: policy index from request, or None if not present :raises HTTPBadRequest: if the supplied index is bogus """ header = 'X-Backend-Storage-Policy-Index' policy_index = req.headers.get(header, None) if policy_index is None: return None try: policy_index = int(policy_index) policy = POLICIES.get_by_index(policy_index) if policy is None: raise ValueError except ValueError: raise HTTPBadRequest(request=req, content_type="text/plain", body="Invalid %s %r" % (header, policy_index)) else: return int(policy)
def __call__(self, req): """ WSGI entry point """ try: vrs, account, container, obj = req.split_path(1, 4, True) except ValueError: return self.app if obj: if req.method == 'PUT' and \ req.params.get('multipart-manifest') == 'put': return self.handle_multipart_put(req) if req.method == 'DELETE' and \ req.params.get('multipart-manifest') == 'delete': return self.handle_multipart_delete(req) if 'X-Static-Large-Object' in req.headers: raise HTTPBadRequest( request=req, body='X-Static-Large-Object is a reserved header. ' 'To create a static large object add query param ' 'multipart-manifest=put.') return self.app
def handle_multipart_delete(self, req): """ Will delete all the segments in the SLO manifest and then, if successful, will delete the manifest file. :params req: a swob.Request with an obj in path :raises HTTPServerError: on invalid manifest :returns: swob.Response on failure, otherwise self.app """ new_env = req.environ.copy() new_env['REQUEST_METHOD'] = 'GET' del (new_env['wsgi.input']) new_env['QUERY_STRING'] = 'multipart-manifest=get' new_env['CONTENT_LENGTH'] = 0 new_env['HTTP_USER_AGENT'] = \ '%s MultipartDELETE' % req.environ.get('HTTP_USER_AGENT') new_env['swift.source'] = 'SLO' get_man_resp = \ Request.blank('', new_env).get_response(self.app) if get_man_resp.status_int // 100 == 2: if not config_true_value( get_man_resp.headers.get('X-Static-Large-Object')): raise HTTPBadRequest('Not an SLO manifest') try: manifest = json.loads(get_man_resp.body) except ValueError: raise HTTPServerError('Invalid manifest file') delete_resp = self.bulk_deleter.handle_delete( req, objs_to_delete=[o['name'].encode('utf-8') for o in manifest], user_agent='MultipartDELETE', swift_source='SLO') if delete_resp.status_int // 100 == 2: # delete the manifest file itself return self.app else: return delete_resp return get_man_resp
def get_objs_to_delete(self, req): """ Will populate objs_to_delete with data from request input. :params req: a Swob request :returns: a list of the contents of req.body when separated by newline. :raises: HTTPException on failures """ line = '' data_remaining = True objs_to_delete = [] if req.content_length is None and \ req.headers.get('transfer-encoding', '').lower() != 'chunked': raise HTTPLengthRequired(request=req) while data_remaining: if '\n' in line: obj_to_delete, line = line.split('\n', 1) obj_to_delete = obj_to_delete.strip() objs_to_delete.append( {'name': unquote(obj_to_delete)}) else: data = req.body_file.read(MAX_PATH_LENGTH) if data: line += data else: data_remaining = False obj_to_delete = line.strip() if obj_to_delete: objs_to_delete.append( {'name': unquote(obj_to_delete)}) if len(objs_to_delete) > self.max_deletes_per_request: raise HTTPRequestEntityTooLarge( 'Maximum Bulk Deletes: %d per request' % self.max_deletes_per_request) if len(line) > MAX_PATH_LENGTH * 2: raise HTTPBadRequest('Invalid File Name') return objs_to_delete
def __call__(self, env, start_response): """ WSGI entry point """ if env.get('swift.slo_override'): return self.app(env, start_response) req = Request(env) try: vrs, account, container, obj = req.split_path(3, 4, True) except ValueError: return self.app(env, start_response) if not obj: if req.method == 'GET': return self.handle_container_listing(req, start_response) return self.app(env, start_response) try: if req.method == 'PUT' and \ req.params.get('multipart-manifest') == 'put': return self.handle_multipart_put(req, start_response) if req.method == 'DELETE' and \ req.params.get('multipart-manifest') == 'delete': return self.handle_multipart_delete(req)(env, start_response) if req.method == 'GET' or req.method == 'HEAD': return self.handle_multipart_get_or_head(req, start_response) if 'X-Static-Large-Object' in req.headers: raise HTTPBadRequest( request=req, body='X-Static-Large-Object is a reserved header. ' 'To create a static large object add query param ' 'multipart-manifest=put.') except HTTPException as err_resp: return err_resp(env, start_response) return self.app(env, start_response)
def PUT(self, req): """HTTP PUT request handler.""" if req.if_none_match is not None and '*' not in req.if_none_match: # Sending an etag with if-none-match isn't currently supported return HTTPBadRequest(request=req, content_type='text/plain', body='If-None-Match only supports *') container_info = self.container_info(self.account_name, self.container_name, req) req.acl = container_info['write_acl'] req.environ['swift_sync_key'] = container_info['sync_key'] # is request authorized if 'swift.authorize' in req.environ: aresp = req.environ['swift.authorize'](req) if aresp: return aresp self._update_content_type(req) # check constraints on object name and request headers error_response = check_object_creation(req, self.object_name) or \ check_content_type(req) if error_response: return error_response self._update_x_timestamp(req) data_source = req.environ['wsgi.input'] if req.content_length: data_source = ExpectedSizeReader(data_source, req.content_length) headers = self._prepare_headers(req) resp = self._store_object(req, data_source, headers) return resp
def handle_extract(self, req, compress_type): """ :params req: a swob Request :params compress_type: specifying the compression type of the tar. Accepts '', 'gz, or 'bz2' :raises HTTPException: on unhandled errors :returns: a swob response to request """ success_count = 0 failed_files = [] existing_containers = set() out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS) if not out_content_type: return HTTPNotAcceptable(request=req) if req.content_length is None and \ req.headers.get('transfer-encoding', '').lower() != 'chunked': return HTTPLengthRequired(request=req) try: vrs, account, extract_base = req.split_path(2, 3, True) except ValueError: return HTTPNotFound(request=req) extract_base = extract_base or '' extract_base = extract_base.rstrip('/') try: tar = tarfile.open(mode='r|' + compress_type, fileobj=req.body_file) while True: tar_info = tar.next() if tar_info is None or \ len(failed_files) >= self.max_failed_extractions: break if tar_info.isfile(): obj_path = tar_info.name if obj_path.startswith('./'): obj_path = obj_path[2:] obj_path = obj_path.lstrip('/') if extract_base: obj_path = extract_base + '/' + obj_path if '/' not in obj_path: continue # ignore base level file destination = '/'.join(['', vrs, account, obj_path]) container = obj_path.split('/', 1)[0] if not check_utf8(destination): failed_files.append([ quote(destination[:MAX_PATH_LENGTH]), HTTPPreconditionFailed().status ]) continue if tar_info.size > MAX_FILE_SIZE: failed_files.append([ quote(destination[:MAX_PATH_LENGTH]), HTTPRequestEntityTooLarge().status ]) continue if container not in existing_containers: try: self.create_container( req, '/'.join(['', vrs, account, container])) existing_containers.add(container) except CreateContainerError, err: if err.status_int == HTTP_UNAUTHORIZED: return HTTPUnauthorized(request=req) failed_files.append([ quote(destination[:MAX_PATH_LENGTH]), err.status ]) continue except ValueError: failed_files.append([ quote(destination[:MAX_PATH_LENGTH]), HTTP_BAD_REQUEST ]) continue if len(existing_containers) > self.max_containers: return HTTPBadRequest( 'More than %d base level containers in tar.' % self.max_containers)
def handle_delete(self, req, objs_to_delete=None, user_agent='BulkDelete', swift_source='BD'): """ :params req: a swob Request :raises HTTPException: on unhandled errors :returns: a swob Response """ try: vrs, account, _junk = req.split_path(2, 3, True) except ValueError: return HTTPNotFound(request=req) incoming_format = req.headers.get('Content-Type') if incoming_format and not incoming_format.startswith('text/plain'): # For now only accept newline separated object names return HTTPNotAcceptable(request=req) out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS) if not out_content_type: return HTTPNotAcceptable(request=req) if objs_to_delete is None: objs_to_delete = self.get_objs_to_delete(req) failed_files = [] success_count = not_found_count = 0 failed_file_response_type = HTTPBadRequest for obj_to_delete in objs_to_delete: obj_to_delete = obj_to_delete.strip().lstrip('/') if not obj_to_delete: continue delete_path = '/'.join(['', vrs, account, obj_to_delete]) if not check_utf8(delete_path): failed_files.append( [quote(delete_path), HTTPPreconditionFailed().status]) continue new_env = req.environ.copy() new_env['PATH_INFO'] = delete_path del (new_env['wsgi.input']) new_env['CONTENT_LENGTH'] = 0 new_env['HTTP_USER_AGENT'] = \ '%s %s' % (req.environ.get('HTTP_USER_AGENT'), user_agent) new_env['swift.source'] = swift_source delete_obj_req = Request.blank(delete_path, new_env) resp = delete_obj_req.get_response(self.app) if resp.status_int // 100 == 2: success_count += 1 elif resp.status_int == HTTP_NOT_FOUND: not_found_count += 1 elif resp.status_int == HTTP_UNAUTHORIZED: return HTTPUnauthorized(request=req) else: if resp.status_int // 100 == 5: failed_file_response_type = HTTPBadGateway failed_files.append([quote(delete_path), resp.status]) resp_body = get_response_body(out_content_type, { 'Number Deleted': success_count, 'Number Not Found': not_found_count }, failed_files) if (success_count or not_found_count) and not failed_files: return HTTPOk(resp_body, content_type=out_content_type) if failed_files: return failed_file_response_type(resp_body, content_type=out_content_type) return HTTPBadRequest('Invalid bulk delete.')
success_count += 1 else: if resp.status_int == HTTP_UNAUTHORIZED: return HTTPUnauthorized(request=req) failed_files.append([ quote(destination[:MAX_PATH_LENGTH]), resp.status ]) resp_body = get_response_body( out_content_type, {'Number Files Created': success_count}, failed_files) if success_count and not failed_files: return HTTPCreated(resp_body, content_type=out_content_type) if failed_files: return HTTPBadGateway(resp_body, content_type=out_content_type) return HTTPBadRequest('Invalid Tar File: No Valid Files') except tarfile.TarError, tar_error: return HTTPBadRequest('Invalid Tar File: %s' % tar_error) @wsgify def __call__(self, req): extract_type = req.params.get('extract-archive') if extract_type is not None and req.method == 'PUT': archive_type = { 'tar': '', 'tar.gz': 'gz', 'tar.bz2': 'bz2' }.get(extract_type.lower().strip('.')) if archive_type is not None: return self.handle_extract(req, archive_type)
def _store_object(self, req, data_source, headers): kwargs = {} content_type = req.headers.get('content-type', 'octet/stream') policy = None container_info = self.container_info(self.account_name, self.container_name, req) if 'X-Oio-Storage-Policy' in req.headers: policy = req.headers.get('X-Oio-Storage-Policy') if not self.app.POLICIES.get_by_name(policy): raise HTTPBadRequest( "invalid policy '%s', must be in %s" % (policy, self.app.POLICIES.by_name.keys())) else: try: policy_index = int( req.headers.get('X-Backend-Storage-Policy-Index', container_info['storage_policy'])) except TypeError: policy_index = 0 if policy_index != 0: policy = self.app.POLICIES.get_by_index(policy_index).name else: content_length = int(req.headers.get('content-length', 0)) policy = self._get_auto_policy_from_size(content_length) ct_props = {'properties': {}, 'system': {}} metadata = self.load_object_metadata(headers) oio_headers = {REQID_HEADER: self.trans_id} oio_cache = req.environ.get('oio.cache') perfdata = req.environ.get('swift.perfdata') # only send headers if needed if SUPPORT_VERSIONING and headers.get(FORCEVERSIONING_HEADER): oio_headers[FORCEVERSIONING_HEADER] = \ headers.get(FORCEVERSIONING_HEADER) if req.environ.get('oio.force-version'): # In a case of MPU, it contains version of the UploadId # to be able to include version-id of MPU in S3 reponse kwargs['version'] = req.environ.get('oio.force-version') # In case a shard is being created, save the name of the S3 bucket # in a container property. This will be used when aggregating # container statistics to make bucket statistics. if BUCKET_NAME_HEADER in headers: bname = headers[BUCKET_NAME_HEADER] # FIXME(FVE): the segments container is not part of another bucket! # We should not have to strip this here. if bname and bname.endswith(MULTIUPLOAD_SUFFIX): bname = bname[:-len(MULTIUPLOAD_SUFFIX)] ct_props['system'][BUCKET_NAME_PROP] = bname try: _chunks, _size, checksum, _meta = self._object_create( self.account_name, self.container_name, obj_name=self.object_name, file_or_path=data_source, mime_type=content_type, policy=policy, headers=oio_headers, etag=req.headers.get('etag', '').strip('"'), properties=metadata, container_properties=ct_props, cache=oio_cache, perfdata=perfdata, **kwargs) # TODO(FVE): when oio-sds supports it, do that in a callback # passed to object_create (or whatever upload method supports it) footer_md = self.load_object_metadata(self._get_footers(req)) if footer_md: self.app.storage.object_set_properties(self.account_name, self.container_name, self.object_name, version=_meta.get( 'version', None), properties=footer_md, headers=oio_headers, cache=oio_cache, perfdata=perfdata) except exceptions.Conflict: raise HTTPConflict(request=req) except exceptions.PreconditionFailed: raise HTTPPreconditionFailed(request=req) except SourceReadTimeout as err: self.app.logger.warning(_('ERROR Client read timeout (%s)'), err) self.app.logger.increment('client_timeouts') raise HTTPRequestTimeout(request=req) except exceptions.SourceReadError: req.client_disconnect = True self.app.logger.warning( _('Client disconnected without sending last chunk')) self.app.logger.increment('client_disconnects') raise HTTPClientDisconnect(request=req) except exceptions.EtagMismatch: return HTTPUnprocessableEntity(request=req) except (exceptions.ServiceBusy, exceptions.OioTimeout, exceptions.DeadlineReached): raise except exceptions.NoSuchContainer: raise HTTPNotFound(request=req) except exceptions.ClientException as err: # 481 = CODE_POLICY_NOT_SATISFIABLE if err.status == 481: raise exceptions.ServiceBusy() self.app.logger.exception( _('ERROR Exception transferring data %s'), {'path': req.path}) raise HTTPInternalServerError(request=req) except Exception: self.app.logger.exception( _('ERROR Exception transferring data %s'), {'path': req.path}) raise HTTPInternalServerError(request=req) last_modified = int(_meta.get('mtime', math.ceil(time.time()))) # FIXME(FVE): if \x10 character in object name, decode version # number and set it in the response headers, instead of the oio # version number. version_id = _meta.get('version', 'null') resp = HTTPCreated(request=req, etag=checksum, last_modified=last_modified, headers={'x-object-sysmeta-version-id': version_id}) return resp