Example #1
0
class OSDF(object):
    """
    Communicates with an OSDF server's REST interface to facilitate several
    operations (node creation, deletion, queries, etc.)
    """

    def __init__(self, server, username, password, port=8123, ssl=False):
        self._server = server
        self._port = port
        self._username = username
        self._password = password
        self._ssl = ssl
        self._set_request()

    def _set_request(self):
        self._request = HttpRequest(self._server, self._username,
                                    self._password, self._port,
                                    self._ssl)

    @property
    def server(self):
        return self._server

    @server.setter
    def server(self, server):
        self._server = server
        # Redefine the request object
        self._set_request()

    @property
    def port(self):
        return self._port

    @port.setter
    def port(self, port):
        self._port = port
        # Redefine the request object
        self._set_request()

    @property
    def username(self):
        return self._username

    @username.setter
    def username(self, username):
        self._username = username
        # Redefine the request object
        self._set_request()

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, password):
        self._password = password
        # Redefine the request object
        self._set_request()

    @property
    def ssl(self):
        return self._ssl

    @ssl.setter
    def ssl(self, ssl):
        if type(ssl) is not bool:
            raise ValueError("Invalid value for ssl.")

        self._ssl = ssl
        # Redefine the request object
        self._set_request()

    def edit_node(self, json_data):
        """
        Updates a node with the provided data
        """
        # Get the node id from json_data
        if 'id' not in json_data:
            raise Exception("No node id in the provided JSON.")

        node_id = json_data['id']

        json_str = json.dumps(json_data);

        osdf_response = self._request.put("/nodes/" + node_id, json_str)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'edit', 'node')

    def _byteify(self, input):
        if isinstance(input, dict):
            return {self._byteify(key):self._byteify(value) for key,value in input.iteritems()}
        elif isinstance(input, list):
            return [self._byteify(element) for element in input]
        elif isinstance(input, unicode):
            return input.encode('utf-8')
        else:
            return input

    def get_info(self):
        """
        Retrieve's the OSDF server's information/contact document
        """
        osdf_response = self._request.get("/info")

        info = json.loads( osdf_response['content'] )

        info = self._byteify(info)

        return info

    def get_node(self, node_id):
        """
        Retrieves an OSDF node given the node's ID

        Returns the parsed form of the JSON document for the node
        """
        osdf_response = self._request.get("/nodes/" + node_id)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'node')

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def get_nodes_in(self, node_id):
        """
        Retrieves the nodes that link to the OSDF node identified by the
        given the node ID.

        """
        url = "/nodes/{}/in".format(node_id)
        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'node')

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def get_nodes_out(self, node_id):
        """
        Retrieves the OSDF nodes that the given node links to (via it's linkage
        field).
        """
        url = "/nodes/{}/out".format(node_id)
        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'node')

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def get_node_by_version(self, node_id, version):
        """
        Given a numerical version number, retrieves an OSDF node's data
        as it was at that version.

        Returns the parsed form of the JSON document for the node
        """
        osdf_response = self._request.get("/nodes/%s/ver/%s" % (node_id, version))

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'node')

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def get_schemas(self, namespace):
        """
        Retrieves all of the schemas for a particular namespace.
        """
        url = '/namespaces/%s/schemas/' % namespace

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'schemas')

        all_schema_data = json.loads( osdf_response['content'] )

        schema_data = self._byteify(all_schema_data)

        return all_schema_data

    def get_schema(self, namespace, schema_name):
        """
        Retrieves a namespace's document schema

        Returns the parsed form of the JSON-Schema document
        """
        url = '/namespaces/%s/schemas/%s' % (namespace, schema_name)

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'schema')

        schema_data = json.loads( osdf_response['content'] )

        schema_data = self._byteify(schema_data)

        return schema_data

    def get_aux_schemas(self, namespace):
        """
        Retrieves all of the auxiliary schemas for a particular namespace.

        Returns the parsed form of the auxiliary schemas.
        """
        url = '/namespaces/%s/schemas/aux/' % (namespace)

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'aux schemas')

        aux_schema_data = json.loads( osdf_response['content'] )

        aux_schema_data = self._byteify(aux_schema_data)

        return aux_schema_data

    def get_aux_schema(self, namespace, aux_schema_name):
        """
        Retrieves an auxiliary schema

        Returns the parsed form of the auxiliary schema JSON
        """
        url = '/namespaces/%s/schemas/aux/%s' % (namespace, aux_schema_name)

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']
            self.header_error(headers, 'retrieve', 'aux schema')

        aux_schema_data = json.loads( osdf_response['content'] )

        aux_schema_data = self._byteify(aux_schema_data)

        return aux_schema_data

    def insert_node(self, json_data):
        """
        Inserts a node with the provided data into OSDF

        Returns the node ID upon successful insertion.
        """
        json_str = json.dumps(json_data);

        osdf_response = self._request.post("/nodes", json_str)
        node_id = None

        headers = osdf_response["headers"]

        if osdf_response["code"] == 201:
            if 'location' in headers:
                node_id = headers['location'].split('/')[-1]
            else:
                raise Exception("No location header for the newly inserted node.")
        else:
            if 'x-osdf-error' in headers:
                msg = "Unable to insert node document. Reason: " + headers['x-osdf-error']
            else:
                msg = "Unable to insert node document."

            raise Exception(msg)

        return node_id

    def delete_node(self, node_id):
        """
        Deletes the specified node from OSDF.
        """
        osdf_response = self._request.delete("/nodes/" + node_id)

        if osdf_response['code'] != 204:
            headers = osdf_response['headers']
            self.header_error(headers, 'delete', 'node')

    def validate_node(self, json_data):
        """
        Report whether a node document validates against OSDF and its notion
        of what that node should look like according to any registered schemas.

        Returns a tuple with the first value holding a boolean of whether the
        document validated or not. The second value contains the error message
        if the document did not validate.
        """
        json_str = json.dumps(json_data);
        url = "/nodes/validate"

        osdf_response = self._request.post(url, json_str)
        headers = osdf_response["headers"]
        valid = False

        error_msg = None

        if osdf_response["code"] != 200:
            if 'x-osdf-error' in headers:
                error_msg = headers['x-osdf-error']
            else:
                error_msg = "Unknown"
        else:
            valid = True

        return (valid, error_msg)

    def oql_query(self, namespace, query, page=1):
        """
        Issue an OSDF Query Language (OQL) query against OSDF.

        Returns the specified page of results.
        """
        url = "/nodes/oql/%s/page/%s" % (namespace, str(page))

        osdf_response = self._request.post(url, query)

        if osdf_response["code"] != 200 and osdf_response["code"] != 206:
            headers = osdf_response["headers"]

            if 'x-osdf-error' in headers:
                msg = "Unable to query namespace %s. Reason: %s" % (namespace, headers['x-osdf-error'])
            else:
                msg = "Unable to query namespace."

            raise Exception(msg)

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def query(self, namespace, query, page=1):
        """
        Issue a query against OSDF. Queries are expressed in JSON form using
        the ElasticSearch Query DSL.

        Returns the specified page of results.
        """
        url = "/nodes/query/%s/page/%s" % (namespace, str(page))

        osdf_response = self._request.post(url, query)

        if osdf_response["code"] != 200 and osdf_response["code"] != 206:
            headers = osdf_response["headers"]

            if 'x-osdf-error' in headers:
                msg = "Unable to query namespace %s. Reason: %s" % (namespace, headers['x-osdf-error'])
            else:
                msg = "Unable to query namespace."

            raise Exception(msg)

        data = json.loads( osdf_response['content'] )

        data = self._byteify(data)

        return data

    def oql_query_all_pages(self, namespace, query):
        """
        Issue an OSDF Query Language (OQL) query against OSDF, as in the
        oql_query() method, but retrieves ALL results by aggregating all
        the available pages of results. Use with caution, as this may
        consume a lot of memory with large result sets.
        """
        more_results = True
        page = 1
        cumulative_results = []

        while more_results:
           results = self.oql_query(namespace, query, page)

           cumulative_results.extend(results['results'])

           if results['result_count'] > 0:
               page += 1
           else:
               more_results = False

        results['results'] = cumulative_results
        results['result_count'] = len(results['results'])
        del results['page']

        return results;

    def query_all_pages(self, namespace, query):
        """
        Issue a query against OSDF, as in the query() method, but retrieves
        ALL results by aggregating all the available pages of results. Use with
        caution, as this may consume a lot of memory with large result sets.
        """
        more_results = True
        page = 1
        cumulative_results = []

        while more_results:
           results = self.query(namespace, query, page)

           cumulative_results.extend(results['results'])

           if results['result_count'] > 0:
               page += 1
           else:
               more_results = False

        results['results'] = cumulative_results
        results['result_count'] = len(results['results'])
        del results['page']

        return results;

    def create_osdf_node(self, namespace, node_type, domain_json, linkage={}, read="all", write="all"):
        node_json = { 'ns': namespace,
                      'acl': { 'read': [ read ], 'write': [ write ] },
                      'linkage': linkage,
                      'meta': domain_json,
                      'node_type': node_type }

        return node_json

    def header_error(self, headers=[], method_type='retrieve',
            document_type=None):
        if 'x-osdf-error' in headers:
            msg = "Unable to %s %s document. Reason: %s" \
                % (method_type, document_type, headers['x-osdf-error'])
        else:
            msg = "Unable to %s %s document." \
                % (method_type, document_type)

        raise Exception(msg)
Example #2
0
class OSDF:
    """
    Communicates with an OSDF server's REST interface to facilitate several
    operations (node creation, deletion, queries, etc.)
    """

    def __init__(self, server, username, password, port=8123):
        self.server = server
        self.port = port
        self.username = username
        self.password = password
        self._request = HttpRequest(server, username, password, port=port)

    def edit_node(self, json_data):
        """
        Updates a node with the provided data
        """
        # Get the node id from json_data
        if 'id' not in json_data:
            raise Exception("No node id in the provided JSON.")

        node_id = json_data['id']

        json_str = json.dumps(json_data);

        osdf_response = self._request.put("/nodes/" + node_id, json_str)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']

            if 'x-osdf-error' in headers:
                msg = "Unable to edit node document. Reason: " \
                      + headers['x-osdf-error']
                raise Exception(msg)
            else:
                raise Exception("Unable to edit node document.")

    def get_info(self):
        """
        Retrieve's the OSDF server's information/contact document
        """
        osdf_response = self._request.get("/info")

        info = json.loads( osdf_response['content'] )

        return info

    def get_node(self, node_id):
        """
        Retrieves an OSDF node given the node's ID

        Returns the parsed form of the JSON document for the node
        """
        osdf_response = self._request.get("/nodes/" + node_id)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']

            if 'x-osdf-error' in headers:
                msg = "Unable to retrieve node document. Reason: " \
                      + headers['x-osdf-error']
            else:
                msg = "Unable to retrieve node document."

            raise Exception(msg)

        data = json.loads( osdf_response['content'] )

        return data

    def get_schema(self, namespace, schema_name):
        """
        Retrieves a namespace's document schema

        Returns the parsed form of the JSON-Schema document
        """
        url = '/namespaces/%s/schemas/%s' % (namespace, schema_name)

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']

            if 'x-osdf-error' in headers:
                msg = "Unable to retrieve schema document. Reason: " \
                      + headers['x-osdf-error']
            else:
                msg = "Unable to retrieve schema document."

            raise Exception(msg)

        schema_data = json.loads( osdf_response['content'] )

        return schema_data

    def get_aux_schema(self, namespace, aux_schema_name):
        """
        Retrieves an auxiliary schema

        Returns the parsed form of the auxiliary schema JSON
        """
        url = '/namespaces/%s/schemas/aux/%s' % (namespace, aux_schema_name)

        osdf_response = self._request.get(url)

        if osdf_response["code"] != 200:
            headers = osdf_response['headers']

            if 'x-osdf-error' in headers:
                msg = "Unable to retrieve schema document. Reason: " \
                      + headers['x-osdf-error']
            else:
                msg = "Unable to retrieve schema document."

            raise Exception(msg)

        aux_schema_data = json.loads( osdf_response['content'] )

        return aux_schema_data

    def insert_node(self, json_data):
        """
        Inserts a node with the provided data into OSDF

        Returns the node ID upon successful insertion.
        """
        json_str = json.dumps(json_data);

        osdf_response = self._request.post("/nodes", json_str)
        node_id = None

        headers = osdf_response["headers"]

        if osdf_response["code"] == 201:
            if 'location' in headers:
                node_id = headers['location'].split('/')[-1]
            else:
                raise Exception("No location header for the newly inserted node.")
        else:
            if 'x-osdf-error' in headers:
                msg = "Unable to insert node document. Reason: " + headers['x-osdf-error']
            else:
                msg = "Unable to insert node document."

            raise Exception(msg)

        return node_id

    def delete_node(self, node_id):
        """
        Deletes the specified node from OSDF.
        """
        osdf_response = self._request.delete("/nodes/" + node_id)

        if osdf_response['code'] != 200:
            if 'x-osdf-error' in headers:
                msg = "Unable to delete node document. Reason: " + headers['x-osdf-error']
            else:
                msg = "Unable to delete node document."

            raise Exception(msg)

    def validate_node(self, json_data):
        """
        Report whether a node document validates against OSDF and it's notion
        of what that node should look like according to any registered schemas.

        Returns a tuple with the first value holding a boolean of whether the
        document validated or not. The second value contains the error message
        if the document did not validate.
        """
        json_str = json.dumps(json_data);
        url = "/nodes/validate"

        osdf_response = self._request.post(url, json_str)
        headers = osdf_response["headers"]
        valid = False

        error_msg = None

        if osdf_response["code"] != 200:
            if 'x-osdf-error' in headers:
                error_msg = headers['x-osdf-error']
            else:
                error_msg = "Unknown"
        else:
            valid = True

        return (valid, error_msg)


    def query(self, namespace, query, page=1):
        """
        Issue a query against OSDF. Queries are expressed in JSON form using
        the ElasticSearch Query DSL.

        Returns the specified page of results.
        """
        url = "/nodes/query/%s/page/%s" % (namespace, str(page))

        osdf_response = self._request.post(url, query)

        if osdf_response["code"] != 200 and osdf_response["code"] != 206:
            headers = osdf_response["headers"]

            if 'x-osdf-error' in headers:
                msg = "Unable to query namespace %s. Reason: %s" % (namespace, headers['x-osdf-error'])
            else:
                msg = "Unable to query namespace."

            raise Exception(msg)

        data = json.loads( osdf_response['content'] )

        return data

    def query_all_pages(self, namespace, query):
        """
        Issue a query against OSDF, as in the query() method, but retrieves
        ALL results by aggregating all the available pages of results. Use with
        caution, as this may consume a lot of memory with large result sets.
        """
        more_results = True
        page = 1
        cumulative_results = []

        while more_results:
           results = self.query(namespace, query, page)

           cumulative_results.extend(results['results'])

           if results['result_count'] > 0:
               page += 1
           else:
               more_results = False

        results['results'] = cumulative_results
        results['result_count'] = len(results['results'])
        del results['page']

        return results;

    def create_osdf_node(self, namespace, node_type, domain_json, linkage={}, read="all", write="all"):
        node_json = { 'ns': namespace,
                      'acl': { 'read': [ read ], 'write': [ write ] },
                      'linkage': linkage,
                      'meta': domain_json,
                      'node_type': node_type }

        return node_json