Exemplo n.º 1
0
    def get_size_multi(self, *blobrefs):
        """
        Get the size of several blobs at once, given their blobrefs.

        This is a batch version of :py:meth:`get_size`, returning a
        mapping object whose keys are the request blobrefs and whose
        values are either the size of each corresponding blob or
        ``None`` if the blobref is not known to the server.
        """
        import json

        form_data = {}
        form_data["camliversion"] = "1"
        for i, blobref in enumerate(blobrefs):
            form_data["blob%i" % (i + 1)] = blobref

        stat_url = self._make_url('camli/stat')
        resp = self.http_session.post(stat_url, data=form_data)

        if resp.status_code != 200:
            from camlistore.exceptions import ServerError
            raise ServerError("Failed to get sizes of blobs: got %i %s" % (
                resp.status_code,
                resp.reason,
            ))

        data = json.loads(resp.content)

        ret = {blobref: None for blobref in blobrefs}
        for raw_meta in data["stat"]:
            ret[raw_meta["blobRef"]] = int(raw_meta["size"])

        return ret
Exemplo n.º 2
0
    def get_claims_for_permanode(self, blobref):
        """
        Get the claims for a particular permanode, as an iterable of
        :py:class:`ClaimMeta`.

        The concept of "claims" is what allows a permanode to appear
        mutable even though the underlying storage is immutable. The
        indexer processes each of the valid claims on a given permanode
        to produce an aggregated set of its attributes for a given point
        in time.

        Most callers should prefer to use :py:meth:`describe_blob` instead,
        since that returns the flattened result of processing all
        attributes, rather than requiring the client to process the claims
        itself.
        """
        import json
        req_url = self._make_url("camli/search/claims")
        resp = self.http_session.get(
            req_url,
            params={"permanode": blobref},
        )

        if resp.status_code != 200:
            from camlistore.exceptions import ServerError
            raise ServerError(
                "Failed to get claims for %s: server returned %i %s" % (
                    blobref,
                    resp.status_code,
                    resp.reason,
                ))

        raw = json.loads(resp.content)
        return [ClaimMeta(x) for x in raw["claims"]]
Exemplo n.º 3
0
    def put_multi(self, *blobs):
        """
        Upload several blobs to the store.

        This is a batch version of :py:meth:`put`, uploading several
        blobs at once and returning a list of their blobrefs in the
        same order as they were provided in the arguments.

        At present this method does *not* correctly handle the protocol
        restriction that only 32MB of data can be uploaded at once, so
        this function will fail if that limit is exceeded. It is intended
        that this will be fixed in a future version.
        """
        import hashlib

        upload_url = self._make_url('camli/upload')

        blobrefs = [blob.blobref for blob in blobs]

        sizes = self.get_size_multi(*blobrefs)

        files_to_post = {}

        for blob in blobs:
            blobref = blob.blobref

            if sizes[blobref] is not None:
                # Server already has this blob, so skip
                continue

            files_to_post[blobref] = (
                blobref,
                blob.data,
                'application/octet-stream',
            )

        if len(files_to_post) == 0:
            # Server already has everything, so nothing to do.
            return blobrefs

        # FIXME: We should detect if our total upload size is >32MB
        # and automatically split it into multiple requests, since the
        # protocol forbids upload payloads greater than 32MB.
        resp = self.http_session.post(upload_url, files=files_to_post)

        if resp.status_code != 200:
            from camlistore.exceptions import ServerError
            raise ServerError("Failed to upload blobs: got %i %s" % (
                resp.status_code,
                resp.reason,
            ))

        return blobrefs
Exemplo n.º 4
0
    def enumerate(self):
        """
        Enumerate all of the blobs on the server, in blobref order.

        Returns an iterable over all of the blobs. The underlying server
        interface returns the resultset in chunks, so beginning iteration
        will cause one request but continued iteration may cause followup
        requests to retrieve additional chunks.

        Most applications do not need to enumerate all blobs and can instead
        use the facilities provided by the search interface. The enumeration
        interface exists primarily to enable the Camlistore indexer to build
        its search index, but may be useful for other alternative index
        implementations.
        """
        from urllib.parse import urljoin
        import json
        plain_enum_url = self._make_url("camli/enumerate-blobs")
        next_enum_url = plain_enum_url

        while next_enum_url is not None:

            resp = self.http_session.get(next_enum_url)
            if resp.status_code != 200:
                from camlistore.exceptions import ServerError
                raise ServerError(
                    "Failed to enumerate blobs from %s: got %i %s" % (
                        next_enum_url,
                        resp.status_code,
                        resp.reason,
                    ))

            data = json.loads(resp.content)

            if "continueAfter" in data:
                next_enum_url = urljoin(
                    plain_enum_url,
                    "?after=" + data["continueAfter"],
                )
            else:
                next_enum_url = None

            for raw_blob_reference in data["blobs"]:
                yield BlobMeta(
                    raw_blob_reference["blobRef"],
                    size=raw_blob_reference["size"],
                    blob_client=self,
                )
Exemplo n.º 5
0
    def query(self, expression):
        """
        Run a query against the index, returning an iterable of
        :py:class:`SearchResult`.

        The given expression is just passed on verbatim to the underlying
        query interface.

        Query constraints are not yet supported.
        """
        import json
        req_url = self._make_url("camli/search/query")

        data = {
            # TODO: Understand how constraints work and implement them
            # https://github.com/bradfitz/camlistore/blob/
            # ca58231336e5711abacb059763beb06e8b2b1788/pkg/search/query.go#L255
            # "constraint": "",
            "expression": expression,
        }

        resp = self.http_session.post(
            req_url,
            data=json.dumps(data),
        )

        if resp.status_code != 200:
            from camlistore.exceptions import ServerError
            raise ServerError(
                "Failed to search for %r: server returned %i %s" % (
                    expression,
                    resp.status_code,
                    resp.reason,
                ))

        raw_data = json.loads(resp.content)

        if raw_data["blobs"] is not None:
            return [SearchResult(x["blob"]) for x in raw_data["blobs"]]
        else:
            return []
Exemplo n.º 6
0
    def get_size(self, blobref):
        """
        Get the size of a blob, given its blobref.

        Returns the size of the blob as an :py:class:`int` in bytes,
        or raises :py:class:`camlistore.exceptions.NotFoundError` if
        the given blobref is not known to the server.
        """
        blob_url = self._make_blob_url(blobref)
        resp = self.http_session.request('HEAD', blob_url)
        if resp.status_code == 200:
            return int(resp.headers['content-length'])
        elif resp.status_code == 404:
            from camlistore.exceptions import NotFoundError
            raise NotFoundError("Blob not found: %s" % blobref, )
        else:
            from camlistore.exceptions import ServerError
            raise ServerError(
                "Failed to get metadata for blob %s: server returned %i %s" % (
                    blobref,
                    resp.status_code,
                    resp.reason,
                ))
Exemplo n.º 7
0
    def get(self, blobref):
        """
        Get the data for a blob, given its blobref.

        Returns a :py:class:`camlistore.Blob` instance describing the
        blob, or raises :py:class:`camlistore.exceptions.NotFoundError` if
        the given blobref is not known to the server.
        """
        blob_url = self._make_blob_url(blobref)
        resp = self.http_session.get(blob_url)
        if resp.status_code == 200:
            return Blob(resp.content, blobref=blobref)
        elif resp.status_code == 404:
            from camlistore.exceptions import NotFoundError
            raise NotFoundError("Blob not found: %s" % blobref, )
        else:
            from camlistore.exceptions import ServerError
            raise ServerError("Failed to get blob %s: server returned %i %s" %
                              (
                                  blobref,
                                  resp.status_code,
                                  resp.reason,
                              ))
Exemplo n.º 8
0
    def describe_blob(self, blobref):
        """
        Request a description of a particular blob, returning a
        :py:class:`BlobDescription` object.

        The "description" of a blob is the indexer's record of the blob,
        so it contains only the subset of information retained by the
        indexer. The level of detail in the returned object will thus
        depend on what the indexer knows about the given object.
        """
        import json
        req_url = self._make_url("camli/search/describe")
        resp = self.http_session.get(
            req_url,
            params={
                "blobref": blobref,
            },
        )

        if resp.status_code != 200:
            from camlistore.exceptions import ServerError
            raise ServerError("Failed to describe %s: server returned %i %s" %
                              (
                                  blobref,
                                  resp.status_code,
                                  resp.reason,
                              ))

        raw = json.loads(resp.content)
        my_raw = raw["meta"][blobref]
        other_raw = raw["meta"]
        return BlobDescription(
            self,
            my_raw,
            other_raw_dicts=other_raw,
        )