Exemplo n.º 1
0
    def POST(self, req):
        """
        Handles Delete Multiple Objects.
        """
        def object_key_iter(xml):
            dom = parseString(xml)
            delete = dom.getElementsByTagName('Delete')[0]
            for obj in delete.getElementsByTagName('Object'):
                key = obj.getElementsByTagName('Key')[0].firstChild.data
                version = None
                if obj.getElementsByTagName('VersionId').length > 0:
                    version = obj.getElementsByTagName('VersionId')[0]\
                        .firstChild.data
                yield (key, version)

        def get_deleted_elem(key):
            return '  <Deleted>\r\n' \
                   '    <Key>%s</Key>\r\n' \
                   '  </Deleted>\r\n' % (key)

        def get_err_elem(key, err_code, message):
            return '  <Error>\r\n' \
                   '    <Key>%s</Key>\r\n' \
                   '    <Code>%s</Code>\r\n' \
                   '    <Message>%s</Message>\r\n' \
                   '  </Error>\r\n' % (key, err_code, message)

        body = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
               '<DeleteResult ' \
               'xmlns="http://doc.s3.amazonaws.com/2006-03-01">\r\n'
        for key, version in object_key_iter(req.body):
            if version is not None:
                # TODO: delete the specific version of the object
                return get_err_response('Unsupported')

            sub_req = Request(req.environ.copy())
            sub_req.query_string = ''
            sub_req.content_length = 0
            sub_req.method = 'DELETE'
            controller = ObjectController(sub_req, self.app, self.account_name,
                                          req.environ['HTTP_X_AUTH_TOKEN'],
                                          self.container_name, key)
            sub_resp = controller.DELETE(sub_req)
            status = sub_resp.status_int

            if status == HTTP_NO_CONTENT or status == HTTP_NOT_FOUND:
                body += get_deleted_elem(key)
            else:
                if status == HTTP_UNAUTHORIZED:
                    body += get_err_elem(key, 'AccessDenied', 'Access Denied')
                else:
                    body += get_err_elem(key, 'InvalidURI', 'Invalid URI')

        body += '</DeleteResult>\r\n'
        return HTTPOk(body=body)
Exemplo n.º 2
0
    def _delete_multiple_objects(self, req):
        def _object_key_iter(xml):
            dom = parseString(xml)
            delete = dom.getElementsByTagName('Delete')[0]
            for obj in delete.getElementsByTagName('Object'):
                key = obj.getElementsByTagName('Key')[0].firstChild.data
                version = None
                if obj.getElementsByTagName('VersionId').length > 0:
                    version = obj.getElementsByTagName('VersionId')[0]\
                        .firstChild.data
                yield (key, version)

        def _get_deleted_elem(key):
            return '  <Deleted>\r\n' \
                   '    <Key>%s</Key>\r\n' \
                   '  </Deleted>\r\n' % (key)

        def _get_err_elem(key, err_code, message):
            return '  <Error>\r\n' \
                   '    <Key>%s</Key>\r\n' \
                   '    <Code>%s</Code>\r\n' \
                   '    <Message>%s</Message>\r\n' \
                   '  </Error>\r\n' % (key, err_code, message)

        body = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
               '<DeleteResult ' \
               'xmlns="http://doc.s3.amazonaws.com/2006-03-01">\r\n'
        for key, version in _object_key_iter(req.body):
            if version is not None:
                # TODO: delete the specific version of the object
                return get_err_response('Unsupported')

            sub_req = Request(req.environ.copy())
            sub_req.query_string = ''
            sub_req.content_length = 0
            sub_req.method = 'DELETE'
            controller = ObjectController(sub_req, self.app, self.account_name,
                                          req.environ['HTTP_X_AUTH_TOKEN'],
                                          self.container_name, key)
            sub_resp = controller.DELETE(sub_req)
            status = sub_resp.status_int

            if status == HTTP_NO_CONTENT or status == HTTP_NOT_FOUND:
                body += _get_deleted_elem(key)
            else:
                if status == HTTP_UNAUTHORIZED:
                    body += _get_err_elem(key, 'AccessDenied', 'Access Denied')
                else:
                    body += _get_err_elem(key, 'InvalidURI', 'Invalid URI')

        body += '</DeleteResult>\r\n'
        return HTTPOk(body=body)
    def transition(self, env):
        # GET Object body
        req = Request(copy(env))
        req.method = 'GET'
        resp = req.get_response(self.app)

        obj_body = resp.body

        # Glacier로 업로드
        tmpfile = self.save_to_tempfile(obj_body)
        try:
            glacier = self._init_glacier()
            archive_id = glacier.upload_archive(tmpfile)
            glacier_obj = make_glacier_hidden_object_name(self.obj, archive_id)
        except Exception as e:
            return Response(status=HTTP_INTERNAL_SERVER_ERROR, body=e.message)
        finally:
            self.delete_tempfile(tmpfile)

        # Object를 0KB로 만들기
        req = Request(copy(env))
        req.headers[GLACIER_FLAG_META] = True
        resp = req.get_response(self.app)

        # Glacier Hidden account에 기록
        glacier_account = self.glacier_account_prefix + self.account
        part, nodes = self.container_ring.get_nodes(glacier_account,
                                                    self.container)
        hidden_path = '/%s/%s/%s' % (glacier_account, self.container,
                                     glacier_obj)
        for node in nodes:
            ip = node['ip']
            port = node['port']
            dev = node['device']
            headers = dict()
            headers['user-agent'] = 'transition-middleware'
            headers['X-Timestamp'] = normalize_timestamp(time.time())
            headers['referer'] = req.as_referer()
            headers['x-size'] = '0'
            headers['x-content-type'] = 'text/plain'
            headers['x-etag'] = 'd41d8cd98f00b204e9800998ecf8427e'

            conn = http_connect(ip, port, dev, part, 'PUT', hidden_path,
                                headers)
            conn.getresponse().read()
        return Response(status=HTTP_NO_CONTENT)
    def _initialize(self):
        resp = None

        if self.swift_client:
            resp = self.swift_client.make_request('HEAD', self.path, {},
                                                  (2, 4))
        elif self.env:
            req = Request(self.env)
            req.method = 'HEAD'
            req.path_info = self.path
            req.headers['Content-Length'] = '0'
            resp = req.get_response(self.app)

        if resp is None:
            return

        self.status = resp.status_int

        if is_success(self.status):
            self.headers = resp.headers
Exemplo n.º 5
0
    def DELETE(self, env, start_response):
        req = Request(env)

        def deleteSegmentObj(env, auth, container, obj):
            req.method = 'DELETE'

            env['PATH_INFO'] = ('/v1/%s/%s_segments/%s' %
                                (auth, container, obj['name']))
            env['RAW_PATH_INFO'] = ('/v1/%s/%s_segments/%s' %
                                    (auth, container, obj['name']))
            env['SCRIPT_NAME'] = ''

            self.app(env, start_response)
            status = self._get_status_int()

            return status

        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}

        if 'uploadId' not in args:
            return self.app(env, start_response)

        uploadId = args['uploadId']

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)
        #
        # First check to see if this multi-part upload was already
        # completed.  Look in the primary container, if the object exists,
        # then it was completed and we return an error here.
        #
        req.method = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth,
                                                  container, obj))
        del env['QUERY_STRING']
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if is_success(status):
            # The object was found, so we must return an error
            return get_err_response('NoSuchUpload')
        elif status == HTTP_UNAUTHORIZED:
            return get_err_response('AccessDenied')
        elif status != HTTP_NOT_FOUND:
            return get_err_response('InvalidURI')

        #
        # The completed object was not found so this
        # must be a multipart upload abort.
        # We must delete any uploaded segments for this UploadID and then
        # delete the object in the main container as well
        #
        req.method = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s_segments/' % (version, auth, container))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments/' %
                                (version, auth, container))
        env['QUERY_STRING'] = 'format=json&limit=1001&prefix=%s/%s/' \
                              '&delimiter=/' % (obj, uploadId)
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if not is_success(status):
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            elif status == HTTP_NOT_FOUND:
                return get_err_response('NoSuchUpload')
            else:
                return get_err_response('InvalidURI')

        #
        #  Iterate over the segment objects and delete them individually
        #
        del env['QUERY_STRING']
        objects = loads(''.join(list(body_iter)))
        for o in objects:
            status = deleteSegmentObj(env, auth, container, o)
            if not is_success(status):
                if status == HTTP_UNAUTHORIZED:
                    return get_err_response('AccessDenied')
                elif status == HTTP_NOT_FOUND:
                    return get_err_response('NoSuchObject')
                else:
                    return get_err_response('InvalidURI')

        #
        # Delete the object from the segment container
        #
        req.method = 'DELETE'
        env['PATH_INFO'] = ('/%s/%s/%s_segments/%s' %
                            (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments/%s' %
                                (version, auth, container, obj))
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if is_success(status) is False and status != HTTP_NOT_FOUND:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            else:
                return get_err_response('InvalidURI')

        resp = Response(status=204, body='')
        return resp(env, start_response)
Exemplo n.º 6
0
    def POST(self, env, start_response):
        req = Request(env)

        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}
        if 'uploads' in args:
            #
            # return multi-upload body start
            #
            parts = urlparse.urlparse(req.url)
            version, auth, cont = split_path(parts.path, 0, 3, True)

            path = env['RAW_PATH_INFO']
            container, obj = split_path(path, 0, 2, True)

            #
            # Create a unique S3 upload id from the request string and salt
            # it with the current time to avoid duplicates.
            #
            m = md5.new(('/%s/%s/%s/%s-%d' %
                        (version, auth, container, obj, time.time())))
            upload_id = m.hexdigest()

            req.method = 'PUT'
            env['PATH_INFO'] = ('/v1/%s/%s_segments' % (auth, container))
            env['RAW_PATH_INFO'] = ('/v1/%s/%s_segments' % (auth, container))
            env['SCRIPT_NAME'] = ''
            env['QUERY_STRING'] = ''

            self._app_call(env)
            status = self._get_status_int()

            if not is_success(status):
                if status == HTTP_UNAUTHORIZED:
                    return get_err_response('AccessDenied')
                elif status == HTTP_NOT_FOUND:
                    return get_err_response('NoSuchBucket')
                else:
                    return get_err_response('InvalidURI')

            #
            # Return the S3 response
            #
            body = ('<?xml version="1.0" encoding="UTF-8"?>\r\n'
                    '<InitiateMultipartUploadResult xmlns='
                    '"http://doc.s3.amazonaws.com/2006-03-01/">\r\n'
                    '<Bucket>%s</Bucket>\r\n'
                    '<Key>%s</Key>\r\n'
                    '<UploadId>%s</UploadId>\r\n'
                    '</InitiateMultipartUploadResult>\r\n'
                    % (container, obj, upload_id))

            resp = Response(status=200, body=body,
                            content_type='application/xml')
            return resp
        elif 'uploadId' in args:
            # Handle an individual S3 multipart upload segment
            return self.completeMultipartUpload(env, start_response)
        else:
            return self.app(env, start_response)
Exemplo n.º 7
0
    def completeMultipartUpload(self, env, start_response):
        req = Request(env)
        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}

        uploadId = args['uploadId']

        urlparts = urlparse.urlparse(req.url)
        version, auth, ignored = split_path(urlparts.path, 2, 3, True)

        # We must get the actual container/object info from the RAW_PATH_INFO
        path = env['RAW_PATH_INFO']
        container, obj = split_path(path, 0, 2, True)
        if obj is None:
            obj = os.path.basename(env['RAW_PATH_INFO'])

        #
        # Query for the objects in the segments area to make sure it completed
        #
        env['REQUEST_METHOD'] = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s_segments' % (version, auth, container))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments' % (version, auth,
                                                        container))
        env['QUERY_STRING'] = 'format=json&limit=1001&prefix=%s/%s/' \
                              '&delimiter=/' % (obj, uploadId)
        env['SCRIPT_NAME'] = ''

        req = Request(env)

        body_iter = self._app_call(env)
        status = self._get_status_int()

        objinfo = loads(''.join(list(body_iter)))

        if len(objinfo) == 0:
            return get_err_response('NoSuchBucket')

        #
        # Tell Swift the manifest info
        # The content length should be 0 and the manifest should point to
        # the segment container.
        #
        req.method = 'PUT'
        req.headers['X-Object-Manifest'] = ('%s_segments/%s/%s' % (container,
                                            obj, uploadId))
        req.headers['Content-Length'] = '0'
        env['PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s/%s' %
                                (version, auth, container, obj))
        del env['QUERY_STRING']
        env['SCRIPT_NAME'] = ''
        req.body = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if status != HTTP_OK and status != HTTP_CREATED:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            elif status == HTTP_NOT_FOUND:
                return get_err_response('NoSuchBucket')
            else:
                return get_err_response('InvalidURI')

        o = objinfo[0]

        body = ('<?xml version="1.0" encoding="UTF-8"?>'
                '<CompleteMultipartUploadResult '
                'xmlns="http://s3.amazonaws.com/doc/2006-03-01">'
                '<Location>%s://%s/%s/%s</Location>'
                '<Bucket>%s</Bucket>'
                '<Key>%s</Key>'
                '<ETag>"%s"</ETag>'
                '</CompleteMultipartUploadResult>' %
                (urlparts.scheme, urlparts.netloc, container, obj, container,
                 o['name'], o['hash']))

        resp = Response(body=body, content_type="application/xml")

        return resp
Exemplo n.º 8
0
    def GET(self, env, start_response):
        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}

        #
        # If 'uploadId' parameter is not present, then pass it along as a
        # standard 'GET' request
        #
        if 'uploadId' not in args and 'uploads' not in args:
            return self.app

        req = Request(env)
        if 'uploadId' in args:
            uploadId = args['uploadId']

        if 'Authorization' not in req.headers:
            return get_err_response('AccessDenied')
        try:
            keyword, info = req.headers['Authorization'].split(' ')
        except:
            return get_err_response('AccessDenied')

        if keyword != 'AWS':
            return get_err_response('AccessDenied')
        try:
            account, signature = info.rsplit(':', 1)
        except:
            return get_err_response('InvalidArgument')

        if 'uploads' in args:
                return self.GET_uploads(env, start_response)

        maxparts = 1000
        partNumMarker = 0
        if 'max-parts' in args:
            try:
                maxparts = int(args['max-parts'])
            except:
                maxparts = 1000
        if 'part-number-marker' in args:
            try:
                partNumMarker = int(args['part-number-marker'])
            except:
                partNumMarker = 0

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)

        # check if upload was completed or not.

        req.method = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth,
                                                  container, obj))
        del env['QUERY_STRING']
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if is_success(status):
            return get_err_response('NoSuchUpload')

        # fetch all upload parts.
        env['PATH_INFO'] = ('/%s/%s/%s_segments/' % (version, auth, container))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments/' % (version, auth,
                                container))
        env['QUERY_STRING'] = 'format=json&limit=1001&prefix=%s/%s/' \
                              '&delimiter=/' % (obj, uploadId)
        body_iter = self._app_call(env)
        status = self._get_status_int()

        if is_success(status) is False:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            elif status == HTTP_NOT_FOUND:
                return get_err_response('NoSuchBucket')
            else:
                return get_err_response('InvalidURI')

        objects = loads(''.join(list(body_iter)))

        lastPart = ''
        firstPart = ''

        objList = []
        #
        # If the caller requested a list starting at a specific part number,
        # construct a sub-set of the object list.
        #
        if partNumMarker > 0 and len(objects) > 0:
            for o in objects:
                num = self.getObjNum(o)
                if num >= partNumMarker:
                    objList.append(o)
        else:
            objList = objects

        if maxparts > 0 and len(objList) == (maxparts + 1):
            truncated = True
            o = objList[-1]
            lastPart = os.path.basename(o['name'])
        else:
            truncated = False

        body = ('<?xml version="1.0" encoding="UTF-8"?>'
                '<ListPartsResult '
                'xmlns="http://s3.amazonaws.com/doc/2006-03-01">'
                '<Bucket>%s</Bucket>'
                '<Key>%s</Key>'
                '<UploadId>%s</UploadId>'
                '<Initiator><ID>%s</ID><DisplayName>%s</DisplayName>'
                '</Initiator>'
                '<Owner><ID>%s</ID><DisplayName>%s</DisplayName></Owner>'
                '<StorageClass>STANDARD</StorageClass>'
                '<IsTruncated>%s</IsTruncated>'
                '<MaxParts>%d</MaxParts>' %
                (xml_escape(container),
                 xml_escape(obj),
                 uploadId,
                 account, account, account, account,
                 'true' if truncated else 'false',
                 maxparts))

        if len(objList) > 0:
            o = objList[0]
            firstPart = os.path.basename(o['name'])
            looped = "".join(['<Part><PartNumber>%s</PartNumber>'
                              '<ETag>%s</ETag>'
                              '<LastModified>%s</LastModified>'
                              '<Size>%s</Size></Part>'
                             % (xml_escape(unquote(os.path.basename(
                                                   i['name']))),
                                i['hash'], i['last_modified'], i['bytes'])
                     for i in objList[:maxparts] if 'subdir' not in i])

            body = body + ('<PartNumberMarker>%s</PartNumberMarker>' %
                           firstPart) + \
                ('%s' % ('<NextPartNumberMarker>%s</NextPartNumberMarker>' %
                         lastPart) if truncated else '') + \
                looped

        body = body + '</ListPartsResult>'

        return Response(status=HTTP_OK, body=body,
                        content_type='application/xml')
Exemplo n.º 9
0
    def DELETE(self, env, start_response):
        req = Request(env)

        def deleteSegmentObj(env, auth, container, obj):
            req.method = "DELETE"

            env["PATH_INFO"] = "/v1/%s/%s_segments/%s" % (auth, container, obj["name"])
            env["RAW_PATH_INFO"] = "/v1/%s/%s_segments/%s" % (auth, container, obj["name"])
            env["SCRIPT_NAME"] = ""

            self.app(env, start_response)
            status = self._get_status_int()

            return status

        if "QUERY_STRING" in env:
            args = dict(urlparse.parse_qsl(env["QUERY_STRING"], 1))
        else:
            args = {}

        if "uploadId" not in args:
            return self.app(env, start_response)

        uploadId = args["uploadId"]

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)
        #
        # First check to see if this multi-part upload was already
        # completed.  Look in the primary container, if the object exists,
        # then it was completed and we return an error here.
        #
        req.method = "GET"
        env["PATH_INFO"] = "/%s/%s/%s/%s" % (version, auth, container, obj)
        env["RAW_PATH_INFO"] = "/%s/%s/%s/%s" % (version, auth, container, obj)
        del env["QUERY_STRING"]
        env["SCRIPT_NAME"] = ""

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if is_success(status):
            # The object was found, so we must return an error
            return get_err_response("NoSuchUpload")
        elif status == HTTP_UNAUTHORIZED:
            return get_err_response("AccessDenied")
        elif status != HTTP_NOT_FOUND:
            return get_err_response("InvalidURI")

        #
        # The completed object was not found so this
        # must be a multipart upload abort.
        # We must delete any uploaded segments for this UploadID and then
        # delete the object in the main container as well
        #
        req.method = "GET"
        env["PATH_INFO"] = "/%s/%s/%s_segments/" % (version, auth, container)
        env["RAW_PATH_INFO"] = "/%s/%s/%s_segments/" % (version, auth, container)
        env["QUERY_STRING"] = "format=json&limit=1001&prefix=%s/%s/" "&delimiter=/" % (obj, uploadId)
        env["SCRIPT_NAME"] = ""

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if not is_success(status):
            if status == HTTP_UNAUTHORIZED:
                return get_err_response("AccessDenied")
            elif status == HTTP_NOT_FOUND:
                return get_err_response("NoSuchUpload")
            else:
                return get_err_response("InvalidURI")

        #
        #  Iterate over the segment objects and delete them individually
        #
        del env["QUERY_STRING"]
        objects = loads("".join(list(body_iter)))
        for o in objects:
            status = deleteSegmentObj(env, auth, container, o)
            if not is_success(status):
                if status == HTTP_UNAUTHORIZED:
                    return get_err_response("AccessDenied")
                elif status == HTTP_NOT_FOUND:
                    return get_err_response("NoSuchObject")
                else:
                    return get_err_response("InvalidURI")

        #
        # Delete the object from the segment container
        #
        req.method = "DELETE"
        env["PATH_INFO"] = "/%s/%s/%s_segments/%s" % (version, auth, container, obj)
        env["RAW_PATH_INFO"] = "/%s/%s/%s_segments/%s" % (version, auth, container, obj)
        env["SCRIPT_NAME"] = ""

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if is_success(status) is False and status != HTTP_NOT_FOUND:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response("AccessDenied")
            else:
                return get_err_response("InvalidURI")

        return Response(status=204, body="")
Exemplo n.º 10
0
    def completeMultipartUpload(self, env, start_response):
        req = Request(env)
        if "QUERY_STRING" in env:
            args = dict(urlparse.parse_qsl(env["QUERY_STRING"], 1))
        else:
            args = {}

        uploadId = args["uploadId"]

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)

        if obj is None:
            obj = os.path.basename(env["RAW_PATH_INFO"])

        #
        # Query for the objects in the segments area to make sure it completed
        #
        env["REQUEST_METHOD"] = "GET"
        env["PATH_INFO"] = "/%s/%s/%s_segments" % (version, auth, container)
        env["RAW_PATH_INFO"] = "/%s/%s/%s_segments" % (version, auth, container)
        env["QUERY_STRING"] = "format=json&limit=1001&prefix=%s/%s/" "&delimiter=/" % (obj, uploadId)
        env["SCRIPT_NAME"] = ""

        req = Request(env)

        body_iter = self._app_call(env)
        status = self._get_status_int()

        objinfo = loads("".join(list(body_iter)))

        if len(objinfo) == 0:
            return get_err_response("NoSuchBucket")

        #
        # Tell Swift the manifest info
        # The content length should be 0 and the manifest should point to
        # the segment container.
        #
        req.method = "PUT"
        req.headers["X-Object-Manifest"] = "%s_segments/%s/%s" % (container, obj, uploadId)
        req.headers["Content-Length"] = "0"
        env["PATH_INFO"] = "/%s/%s/%s/%s" % (version, auth, container, obj)
        env["RAW_PATH_INFO"] = "/%s/%s/%s/%s" % (version, auth, container, obj)
        del env["QUERY_STRING"]
        env["SCRIPT_NAME"] = ""
        req.body = ""

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if status != HTTP_OK and status != HTTP_CREATED:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response("AccessDenied")
            elif status == HTTP_NOT_FOUND:
                return get_err_response("NoSuchBucket")
            else:
                return get_err_response("InvalidURI")

        o = objinfo[0]

        body = (
            '<?xml version="1.0" encoding="UTF-8"?>'
            "<CompleteMultipartUploadResult "
            'xmlns="http://s3.amazonaws.com/doc/2006-03-01">'
            "<Location>%s://%s/%s/%s</Location>"
            "<Bucket>%s</Bucket>"
            "<Key>%s</Key>"
            '<ETag>"%s"</ETag>'
            "</CompleteMultipartUploadResult>"
            % (parts.scheme, parts.netloc, container, obj, container, o["name"], o["hash"])
        )

        resp = Response(body=body, content_type="application/xml")
        return resp
Exemplo n.º 11
0
    def DELETE(self, env, start_response):
        req = Request(env)

        def deleteSegmentObj(env, auth, container, obj):
            req.method = 'DELETE'

            env['PATH_INFO'] = ('/v1/%s/%s_segments/%s' %
                                (auth, container, obj['name']))
            env['RAW_PATH_INFO'] = ('/v1/%s/%s_segments/%s' %
                                    (auth, container, obj['name']))
            env['SCRIPT_NAME'] = ''

            self.app(env, start_response)
            status = self._get_status_int()

            return status

        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}

        if 'uploadId' not in args:
            return self.app(env, start_response)

        uploadId = args['uploadId']

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)
        #
        # First check to see if this multi-part upload was already
        # completed.  Look in the primary container, if the object exists,
        # then it was completed and we return an error here.
        #
        req.method = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s/%s' %
                                (version, auth, container, obj))
        del env['QUERY_STRING']
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if is_success(status):
            # The object was found, so we must return an error
            return get_err_response('NoSuchUpload')
        elif status == HTTP_UNAUTHORIZED:
            return get_err_response('AccessDenied')
        elif status != HTTP_NOT_FOUND:
            return get_err_response('InvalidURI')

        #
        # The completed object was not found so this
        # must be a multipart upload abort.
        # We must delete any uploaded segments for this UploadID and then
        # delete the object in the main container as well
        #
        req.method = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s_segments/' % (version, auth, container))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments/' %
                                (version, auth, container))
        env['QUERY_STRING'] = 'format=json&limit=1001&prefix=%s/%s/' \
                              '&delimiter=/' % (obj, uploadId)
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if not is_success(status):
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            elif status == HTTP_NOT_FOUND:
                return get_err_response('NoSuchUpload')
            else:
                return get_err_response('InvalidURI')

        #
        #  Iterate over the segment objects and delete them individually
        #
        del env['QUERY_STRING']
        objects = loads(''.join(list(body_iter)))
        for o in objects:
            status = deleteSegmentObj(env, auth, container, o)
            if not is_success(status):
                if status == HTTP_UNAUTHORIZED:
                    return get_err_response('AccessDenied')
                elif status == HTTP_NOT_FOUND:
                    return get_err_response('NoSuchObject')
                else:
                    return get_err_response('InvalidURI')

        #
        # Delete the object from the segment container
        #
        req.method = 'DELETE'
        env['PATH_INFO'] = ('/%s/%s/%s_segments/%s' %
                            (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments/%s' %
                                (version, auth, container, obj))
        env['SCRIPT_NAME'] = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()
        if is_success(status) is False and status != HTTP_NOT_FOUND:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            else:
                return get_err_response('InvalidURI')

        return Response(status=204, body='')
Exemplo n.º 12
0
    def POST(self, env, start_response):
        req = Request(env)

        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}
        if 'uploads' in args:
            #
            # return multi-upload body start
            #
            parts = urlparse.urlparse(req.url)
            version, auth, cont = split_path(parts.path, 0, 3, True)

            path = env['RAW_PATH_INFO']
            container, obj = split_path(path, 0, 2, True)

            #
            # Create a unique S3 upload id from the request string and salt
            # it with the current time to avoid duplicates.
            #
            m = md5.new(('/%s/%s/%s/%s-%d' %
                         (version, auth, container, obj, time.time())))
            upload_id = m.hexdigest()

            req.method = 'PUT'
            env['PATH_INFO'] = ('/v1/%s/%s_segments' % (auth, container))
            env['RAW_PATH_INFO'] = ('/v1/%s/%s_segments' % (auth, container))
            env['SCRIPT_NAME'] = ''
            env['QUERY_STRING'] = ''

            self._app_call(env)
            status = self._get_status_int()

            if not is_success(status):
                if status == HTTP_UNAUTHORIZED:
                    return get_err_response('AccessDenied')
                elif status == HTTP_NOT_FOUND:
                    return get_err_response('NoSuchBucket')
                else:
                    return get_err_response('InvalidURI')

            #
            # Return the S3 response
            #
            body = ('<?xml version="1.0" encoding="UTF-8"?>\r\n'
                    '<InitiateMultipartUploadResult xmlns='
                    '"http://doc.s3.amazonaws.com/2006-03-01/">\r\n'
                    '<Bucket>%s</Bucket>\r\n'
                    '<Key>%s</Key>\r\n'
                    '<UploadId>%s</UploadId>\r\n'
                    '</InitiateMultipartUploadResult>\r\n' %
                    (container, obj, upload_id))

            resp = Response(status=200,
                            body=body,
                            content_type='application/xml')
            return resp
        elif 'uploadId' in args:
            # Handle an individual S3 multipart upload segment
            return self.completeMultipartUpload(env, start_response)
        else:
            return self.app(env, start_response)
Exemplo n.º 13
0
    def completeMultipartUpload(self, env, start_response):
        req = Request(env)
        if 'QUERY_STRING' in env:
            args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1))
        else:
            args = {}

        uploadId = args['uploadId']

        parts = urlparse.urlparse(req.url)
        version, auth, container, obj = split_path(parts.path, 0, 4, True)

        if obj is None:
            obj = os.path.basename(env['RAW_PATH_INFO'])

        #
        # Query for the objects in the segments area to make sure it completed
        #
        env['REQUEST_METHOD'] = 'GET'
        env['PATH_INFO'] = ('/%s/%s/%s_segments' % (version, auth, container))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s_segments' %
                                (version, auth, container))
        env['QUERY_STRING'] = 'format=json&limit=1001&prefix=%s/%s/' \
                              '&delimiter=/' % (obj, uploadId)
        env['SCRIPT_NAME'] = ''

        req = Request(env)

        body_iter = self._app_call(env)
        status = self._get_status_int()

        objinfo = loads(''.join(list(body_iter)))

        if len(objinfo) == 0:
            return get_err_response('NoSuchBucket')

        #
        # Tell Swift the manifest info
        # The content length should be 0 and the manifest should point to
        # the segment container.
        #
        req.method = 'PUT'
        req.headers['X-Object-Manifest'] = ('%s_segments/%s/%s' %
                                            (container, obj, uploadId))
        req.headers['Content-Length'] = '0'
        env['PATH_INFO'] = ('/%s/%s/%s/%s' % (version, auth, container, obj))
        env['RAW_PATH_INFO'] = ('/%s/%s/%s/%s' %
                                (version, auth, container, obj))
        del env['QUERY_STRING']
        env['SCRIPT_NAME'] = ''
        req.body = ''

        body_iter = self._app_call(env)
        status = self._get_status_int()

        if status != HTTP_OK and status != HTTP_CREATED:
            if status == HTTP_UNAUTHORIZED:
                return get_err_response('AccessDenied')
            elif status == HTTP_NOT_FOUND:
                return get_err_response('NoSuchBucket')
            else:
                return get_err_response('InvalidURI')

        o = objinfo[0]

        body = ('<?xml version="1.0" encoding="UTF-8"?>'
                '<CompleteMultipartUploadResult '
                'xmlns="http://s3.amazonaws.com/doc/2006-03-01">'
                '<Location>%s://%s/%s/%s</Location>'
                '<Bucket>%s</Bucket>'
                '<Key>%s</Key>'
                '<ETag>"%s"</ETag>'
                '</CompleteMultipartUploadResult>' %
                (parts.scheme, parts.netloc, container, obj, container,
                 o['name'], o['hash']))

        resp = Response(body=body, content_type="application/xml")
        return resp