Пример #1
0
 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
Пример #2
0
 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
Пример #3
0
    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
Пример #4
0
 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
Пример #5
0
 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
Пример #6
0
    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
Пример #7
0
    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
Пример #8
0
 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
Пример #9
0
 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
Пример #10
0
 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
Пример #11
0
    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
Пример #12
0
 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
Пример #13
0
 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
Пример #14
0
    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
Пример #15
0
    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
Пример #16
0
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
Пример #17
0
 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
Пример #18
0
 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
Пример #19
0
    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
Пример #20
0
    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
Пример #21
0
 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
Пример #22
0
    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
Пример #23
0
 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
Пример #24
0
    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
Пример #25
0
 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)
Пример #27
0
    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
Пример #28
0
    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
Пример #29
0
 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
Пример #30
0
    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
Пример #31
0
 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)
Пример #32
0
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
Пример #33
0
    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)
Пример #34
0
    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)
Пример #35
0
 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
Пример #36
0
 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()
Пример #37
0
 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)
Пример #38
0
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
Пример #39
0
 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)
Пример #40
0
    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.')
Пример #41
0
    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'))
Пример #42
0
    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)
Пример #43
0
 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)
Пример #44
0
    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
Пример #45
0
    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)
Пример #46
0
 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)
Пример #47
0
    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
Пример #48
0
    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)
Пример #49
0
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
Пример #50
0
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
Пример #51
0
    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)
Пример #52
0
    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
Пример #53
0
 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
Пример #54
0
    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
Пример #55
0
    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)
Пример #56
0
    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
Пример #57
0
    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)
Пример #58
0
    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.')
Пример #59
0
                        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)
Пример #60
0
    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