def _get_featurestores():
    """
    Sends a REST request to get all featurestores for the project

    Returns:
        a list of Featurestore JSON DTOs

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    connection = util._get_http_connection(https=True)
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE)
    response = util.send_request(connection, method, resource_url)
    resp_body = response.read()
    response_object = json.loads(resp_body)
    # for python 3
    if sys.version_info > (3, 0):
        if response.code != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(response_object)
            raise RestAPIError("Could not fetch feature stores (url: {}), server response: \n " \
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.code, response.reason, error_code, error_msg, user_msg))
    else:  # for python 2
        if response.status != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(response_object)
            raise RestAPIError("Could not fetch feature stores (url: {}), server response: \n " \
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.code, response.reason, error_code, error_msg, user_msg))
    return response_object
Exemple #2
0
def _get_training_dataset_rest(training_dataset_id, featurestore_id):
    """
    Makes a REST call to hopsworks for getting the metadata of a particular training dataset (including the statistics)

    Args:
        :training_dataset_id: id of the training_dataset
        :featurestore_id: id of the featurestore where the training dataset resides

    Returns:
        The REST response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_GET
    connection = util._get_http_connection(https=True)
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER + util.project_id() +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    str(featurestore_id) +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_TRAININGDATASETS_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    str(training_dataset_id))
    response = util.send_request(connection,
                                 method,
                                 resource_url,
                                 headers=headers)
    resp_body = response.read().decode('utf-8')
    response_object = json.loads(resp_body)
    try:  # for python 3
        if (response.code != 200):
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError("Could not get the metadata of featuregroup (url: {}), server response: \n " \
                               "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.code, response.reason, error_code, error_msg, user_msg))
    except:  # for python 2
        if (response.status != 200):
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError("Could not get the metadata of featuregroup (url: {}), server response: \n " \
                               "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.status, response.reason, error_code, error_msg, user_msg))
    return response_object
Exemple #3
0
def _get_featurestore_metadata(featurestore):
    """
    Makes a REST call to hopsworks to get all metadata of a featurestore (featuregroups and
    training datasets) for the provided featurestore.

    Args:
        :featurestore: the name of the database, defaults to the project's featurestore

    Returns:
        JSON response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    connection = util._get_http_connection(https=True)
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + util.project_id() +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + featurestore +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_METADATA_RESOURCE)
    response = util.send_request(connection, method, resource_url)
    resp_body = response.read().decode('utf-8')
    response_object = json.loads(resp_body)
    # for python 3
    if sys.version_info > (3, 0):
        if response.code != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError(
                "Could not fetch featurestore metadata for featurestore: {} (url: {}), "
                "server response: \n "
                "HTTP code: {}, HTTP reason: {}, error code: {}, "
                "error msg: {}, user msg: {}".format(
                    resource_url, featurestore, response.code, response.reason,
                    error_code, error_msg, user_msg))
    else:  # for python 2
        if response.status != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError("Could not fetch featurestore metadata for featurestore: {} (url: {}), "
                                 "server response: \n " \
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, "
                                 "error msg: {}, user msg: {}".format(
                resource_url, featurestore, response.status, response.reason, error_code, error_msg, user_msg))
    return response_object
Exemple #4
0
def _job_execution_action(name, action):
    """
    Manages execution for the given job, start or stop. Submits an http request to the HOPSWORKS REST API.

    Returns:
        The job status.
    """
    method = constants.HTTP_CONFIG.HTTP_POST
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_JOBS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   name + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXECUTIONS_RESOURCE + "?action=" + action
    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not perform action on job's execution (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))

    return response_object
def get_authorization_token():
    """
    Get the authorization token to interact with elasticsearch.

    Args:

    Returns:
        The authorization token to be used in http header.
    """
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_ELASTIC_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_ELASTIC_JWT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id()
    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get authorization token for elastic (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    return "Bearer " + response_object["token"]
def _attach_model_xattr(ml_id, json_data):
    """
    Utility method for putting JSON data into elastic search

    Args:
        :ml_id: the id of the model
        :json_data: the data to put

    Returns:
        None

    """
    headers = {'Content-type': 'application/json'}
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_MODELS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   ml_id

    response = util.send_request('PUT',
                                 resource_url,
                                 data=json_data,
                                 headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not attach model summary to model (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
    else:
        return response_object
Exemple #7
0
def _http(resource_url,
          headers=None,
          method=constants.HTTP_CONFIG.HTTP_GET,
          data=None):
    response = util.send_request(method,
                                 resource_url,
                                 headers=headers,
                                 data=data)
    try:
        response_object = response.json()
    except JSONDecodeError:
        response_object = None

    if (response.status_code // 100) != 2:
        if response_object:
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
        else:
            error_code, error_msg, user_msg = "", "", ""

        raise RestAPIError(
            "Could not execute HTTP request (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))

    return response_object
Exemple #8
0
def _get_online_featurestore_jdbc_connector_rest(featurestore_id):
    """
    Makes a REST call to Hopsworks to get the JDBC connection to the online feature store

    Args:
        :featurestore_id: the id of the featurestore

    Returns:
        the http response

    """
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + hdfs.project_id() +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + str(featurestore_id) +
        constants.DELIMITERS.SLASH_DELIMITER + constants.REST_CONFIG.
        HOPSWORKS_FEATURESTORES_STORAGE_CONNECTORS_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + constants.REST_CONFIG.
        HOPSWORKS_ONLINE_FEATURESTORE_STORAGE_CONNECTOR_RESOURCE)
    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code != 200:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not fetch online featurestore connector (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))
    return response_object
Exemple #9
0
def get_executions(name, query=""):
    """
    Get a list of the currently running executions for this job.
    Returns:
        The job status.
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_JOBS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   name + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXECUTIONS_RESOURCE + query
    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code >= 500:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not get current job's execution (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
    if response.status_code >= 400:
        return None
    return response_object
Exemple #10
0
def _get_fs_tags():
    """
    Makes a REST call to Hopsworks to get tags that can be attached to featuregroups or training datasets

    Returns:
        List of tags

    """
    method = constants.HTTP_CONFIG.HTTP_GET
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_TAGS_RESOURCE)
    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get tags (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    results = []
    if 'items' in response_object:
        for tag in response_object['items']:
            results.append(tag['name'])
    return results
Exemple #11
0
def remove_xattr(hdfs_path, xattr_name):
    """
    Remove an extended attribute attached to an hdfs_path

    Args:
        :hdfs_path: path of a file or directory
        :xattr_name: name of the extended attribute

    Returns:
        None
    """
    hdfs_path = urllib.parse.quote(hdfs._expand_path(hdfs_path))
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_DELETE
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_XATTR_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs_path + constants.DELIMITERS.QUESTION_MARK_DELIMITER + constants.XATTRS.XATTRS_PARAM_NAME + \
                   constants.DELIMITERS.JDBC_CONNECTION_STRING_VALUE_DELIMITER + xattr_name
    response = util.send_request(method, resource_url, headers=headers)
    if response.status_code >= 400:
        response_object = response.json()
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not remove extened attributes from a path (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))
Exemple #12
0
def get_schema(topic):
    """
    Gets the Avro schema for a particular Kafka topic.

    Args:
        :topic: Kafka topic name

    Returns:
        Avro schema as a string object in JSON format
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_KAFKA_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_TOPICS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   topic + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_SUBJECTS_RESOURCE
    response = util.send_request(method, resource_url)
    response_object = response.json()

    if response.status_code != 200:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not get Avro schema (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))

    return response_object['schema']
Exemple #13
0
def delete_secret(name):
    """

    Delete a secret for this user

    >>> from hops import secret
    >>> secret.delete_secret('my_secret')

    Args:
        name: Name of the secret to delete
    """
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_USER_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_SECRETS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   name

    method = constants.HTTP_CONFIG.HTTP_DELETE

    response = util.send_request(method, resource_url)
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not delete secret (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
Exemple #14
0
def start_job(name, args=None):
    """
    Start an execution of the job. Only one execution can be active for a job.

    Returns:
        The job status.
    """
    method = constants.HTTP_CONFIG.HTTP_POST
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_JOBS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   name + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXECUTIONS_RESOURCE

    response = util.send_request(method, resource_url, args)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not perform action on job's execution (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))

    return response_object
Exemple #15
0
def _post_fs_schema(schemaName, schemaValue):
    """
    Makes a REST call to Hopsworks to create schemas for tags that can be attached to datasets

    """
    method = constants.HTTP_CONFIG.HTTP_POST
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_TAGS_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + "?name=" + schemaName)
    response = util.send_request(method,
                                 resource_url,
                                 headers=headers,
                                 data=schemaValue)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not create tag schema (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))
Exemple #16
0
def _get_fs_schema(tagName):
    """
    Makes a REST call to Hopsworks to get tag schema that can be attached to featuregroups or training datasets

    """
    method = constants.HTTP_CONFIG.HTTP_GET
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_TAGS_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + tagName)
    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get tags (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    tag = {}
    if ('name' in response_object) and ('value' in response_object):
        tag[response_object['name']] = json.loads(response_object['value'])
    return tag
Exemple #17
0
    def download(self):
        with util.send_request(constants.HTTP_CONFIG.HTTP_GET,
                               resource="/" + self.resource,
                               stream=True) as response:

            if response.status_code // 100 != 2:
                error_code, error_msg, user_msg = util._parse_rest_error(
                    response.json())
                raise RestAPIError(
                    "Could not perform action on job's execution (url: {}), server response: \n "
                    "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
                    .format(self.resource, response.status_code,
                            response.reason, error_code, error_msg, user_msg))

            with open(self.file, "wb") as f:
                downloaded = 0
                file_size = response.headers.get('Content-Length')
                if not file_size:
                    print("Downloading file ...", end=" ")
                for chunk in response.iter_content(chunk_size=self.chunk_size):
                    f.write(chunk)
                    downloaded += len(chunk)
                    if file_size:
                        progress = round(downloaded / int(file_size) * 100, 3)
                        print("Progress: " + str(progress) + "%")
                if not file_size:
                    print("DONE")
Exemple #18
0
def _get_project_info(project_name):
    """
    Makes a REST call to hopsworks to get all metadata of a project for the provided project.

    Args:
        :project_name: the name of the project

    Returns:
        JSON response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    connection = util._get_http_connection(https=True)
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_INFO_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER + project_name)
    response = util.send_request(connection, method, resource_url)
    resp_body = response.read().decode('utf-8')
    response_object = json.loads(resp_body)
    # for python 3
    if sys.version_info > (3, 0):
        if response.code != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError(
                "Could not fetch project metadata for project: {} (url: {}), "
                "server response: \n "
                "HTTP code: {}, HTTP reason: {}, error code: {}, "
                "error msg: {}, user msg: {}".format(
                    project_name, resource_url, response.code, response.reason,
                    error_code, error_msg, user_msg))
    else:  # for python 2
        if response.status != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(
                response_object)
            raise RestAPIError("Could not fetch project metadata for project: {} (url: {}), "
                                 "server response: \n " \
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, "
                                 "error msg: {}, user msg: {}".format(
                project_name, resource_url, response.status, response.reason, error_code, error_msg, user_msg))
    return response_object
def _parse_response(response, url):
    if response.ok:
        return response.json()
    else:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response.json())
        raise RestAPIError(
            "Error calling {}. Got status: HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}"
            ", user msg: {}".format(url, response.status_code, response.reason,
                                    error_code, error_msg, user_msg))
Exemple #20
0
def _delete_table_contents(featuregroup_id, featurestore_id):
    """
    Sends a request to clear the contents of a featuregroup by dropping the featuregroup and recreating it with
    the same metadata.

    Args:
        :featuregroup_id: id of the featuregroup
        :featurestore_id: id of the featurestore

    Returns:
        The JSON response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    method = constants.HTTP_CONFIG.HTTP_POST
    connection = util._get_http_connection(https=True)
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    str(featurestore_id) + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATUREGROUPS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER +
                    str(featuregroup_id) + constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATUREGROUP_CLEAR_RESOURCE)
    response = util.send_request(connection, method, resource_url)
    resp_body = response.read()
    response_object = json.loads(resp_body)
    # for python 3
    if sys.version_info > (3, 0):
        if response.code != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(response_object)
            raise RestAPIError("Could not clear featuregroup contents (url: {}), server response: \n "
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.code, response.reason, error_code, error_msg, user_msg))
    else:  # for python 2
        if response.status != 200:
            error_code, error_msg, user_msg = util._parse_rest_error(response_object)
            raise RestAPIError("Could not clear featuregroup contents (url: {}), server response: \n " \
                                 "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
                resource_url, response.code, response.reason, error_code, error_msg, user_msg))
    return response_object
Exemple #21
0
def create_secret(name, secret, project_name=None):
    """
    Create a secret

    Creating a secret for this user

    >>> from hops import secret
    >>> secret_token = 'DIOK4jmgFdwadjnDDW98'
    >>> secret.create_secret('my_secret', secret_token)

    Creating a secret and share it with all members of a project

    >>> from hops import secret
    >>> secret_token = 'DIOK4jmgFdwadjnDDW98'
    >>> secret.create_secret('my_secret', secret_token, project_name='someproject')

    Args:
        name: Name of the secret to create
        secret: Value of the secret
        project_name: Name of the project to share the secret with
    """

    secret_config = {'name': name, 'secret': secret}

    if project_name is None:
        secret_config['visibility'] = "PRIVATE"
    else:
        scope_project = project.get_project_info(project_name)
        secret_config['scope'] = scope_project['projectId']
        secret_config['visibility'] = "PROJECT"

    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_POST
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_USER_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_SECRETS_RESOURCE
    response = util.send_request(method,
                                 resource_url,
                                 data=json.dumps(secret_config),
                                 headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not create secret (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
Exemple #22
0
def _get_tags(featurestore_id, id, resource):
    """
    Makes a REST call to Hopsworks to get tags attached to a featuregroup or training dataset

    Args:
        :featurestore_id: the id of the featurestore
        :id: the id of the featuregroup or training dataset
        :resource: featuregroup or training dataset resource

    Returns:
        A dictionary containing the tags

    """
    method = constants.HTTP_CONFIG.HTTP_GET
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER + hdfs.project_id() +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    str(featurestore_id) +
                    constants.DELIMITERS.SLASH_DELIMITER + resource +
                    constants.DELIMITERS.SLASH_DELIMITER + str(id) +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_TAGS_RESOURCE)
    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get tags (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    tags = {}
    if "items" in response_object:
        for tag in response_object["items"]:
            tags[tag["name"]] = tag["value"]

    return tags
Exemple #23
0
def get_secret(name, owner=None):
    """
    Get a secret

    Get a secret for this user

    >>> from hops import secret
    >>> secret.get_secret('my_secret')

    Get a secret shared with this project by the secret owner

    >>> from hops import secret
    >>> secret.get_secret('shared_secret', owner='username')
    Args:
        name: Name of the secret to get
        owner: The username of the user that created the secret
    Returns:
        The secret
    """
    if owner is None:
        resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_USER_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_SECRETS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       name
    else:
        resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_USER_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_SECRETS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                       constants.REST_CONFIG.HOPSWORKS_SHARED + "?name=" + name + "&owner=" + owner

    method = constants.HTTP_CONFIG.HTTP_GET

    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not get secret (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))

    return response_object['items'][0]['secret']
Exemple #24
0
def _get_featuregroup_rest(featuregroup_id, featurestore_id):
    """
    Makes a REST call to hopsworks for getting the metadata of a particular featuregroup (including the statistics)

    Args:
        :featuregroup_id: id of the featuregroup
        :featurestore_id: id of the featurestore where the featuregroup resides

    Returns:
        The REST response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = (constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER + hdfs.project_id() +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    str(featurestore_id) +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    constants.REST_CONFIG.HOPSWORKS_FEATUREGROUPS_RESOURCE +
                    constants.DELIMITERS.SLASH_DELIMITER +
                    str(featuregroup_id))
    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code != 200:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get the metadata of featuregroup (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    return response_object
Exemple #25
0
def _get_featurestore_metadata(featurestore):
    """
    Makes a REST call to hopsworks to get all metadata of a featurestore (featuregroups and
    training datasets) for the provided featurestore.

    Args:
        :featurestore: the name of the database, defaults to the project's featurestore

    Returns:
        JSON response

    Raises:
        :RestAPIError: if there was an error in the REST call to Hopsworks
    """
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + hdfs.project_id() +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + featurestore +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_METADATA_RESOURCE)
    response = util.send_request(method, resource_url)
    response_object = response.json()

    if response.status_code != 200:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not fetch featurestore metadata for featurestore: {} (url: {}), "
            "server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, "
            "error msg: {}, user msg: {}".format(resource_url, featurestore,
                                                 response.status_code,
                                                 response.reason, error_code,
                                                 error_msg, user_msg))

    return response_object
Exemple #26
0
def get_current_execution(name):
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_JOBS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   name + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXECUTIONS_RESOURCE + "?offset=0&limit=1&sort_by=id:desc"
    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not get current job's execution (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
    return response_object
Exemple #27
0
def _add_tag(featurestore_id, id, tag, value, resource):
    """
    Makes a REST call to Hopsworks to attach tags to a featuregroup or training dataset

    Args:
        :featurestore_id: the id of the featurestore
        :id: the id of the featuregroup or training dataset
        :tag: name of the tag to attach
        :value: value of the tag
        :resource: featuregroup or training dataset resource

    Returns:
        None

    """
    method = constants.HTTP_CONFIG.HTTP_PUT
    resource_url = (
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + hdfs.project_id() +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORES_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + str(featurestore_id) +
        constants.DELIMITERS.SLASH_DELIMITER + resource +
        constants.DELIMITERS.SLASH_DELIMITER + str(id) +
        constants.DELIMITERS.SLASH_DELIMITER +
        constants.REST_CONFIG.HOPSWORKS_FEATURESTORE_TAGS_RESOURCE +
        constants.DELIMITERS.SLASH_DELIMITER + tag)
    if value is not None:
        resource_url = resource_url + "?value=" + value

    response = util.send_request(method, resource_url)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not attach tags (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))
Exemple #28
0
def _attach_experiment_xattr(app_id, run_id, json_data, xattr):
    """
    Utility method for putting JSON data into elastic search

    Args:
        :project: the project of the user/app
        :appid: the YARN appid
        :elastic_id: the id in elastic
        :json_data: the data to put

    Returns:
        None

    """

    json_data = dumps(json_data)

    headers = {'Content-type': 'application/json'}
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXPERIMENTS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   app_id + "_" + str(run_id) + "?xattr=" + xattr

    response = util.send_request('PUT',
                                 resource_url,
                                 data=json_data,
                                 headers=headers)

    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not create experiment (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
    else:
        return response_object
Exemple #29
0
def get_xattr(hdfs_path, xattr_name=None):
    """
    Get the extended attribute attached to an hdfs_path.

    Args:
        :hdfs_path: path of a file or directory
        :xattr_name: name of the extended attribute

    Returns:
        A dictionary with the extended attribute(s) as key value pair(s). If the :xattr_name is None,
         the API returns all associated extended attributes.
    """
    hdfs_path = urllib.parse.quote(hdfs._expand_path(hdfs_path))
    headers = {
        constants.HTTP_CONFIG.HTTP_CONTENT_TYPE:
        constants.HTTP_CONFIG.HTTP_APPLICATION_JSON
    }
    method = constants.HTTP_CONFIG.HTTP_GET
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_XATTR_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs_path
    if xattr_name is not None:
        resource_url += constants.DELIMITERS.QUESTION_MARK_DELIMITER + constants.XATTRS.XATTRS_PARAM_NAME + \
                        constants.DELIMITERS.JDBC_CONNECTION_STRING_VALUE_DELIMITER + xattr_name

    response = util.send_request(method, resource_url, headers=headers)
    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError("Could not get extened attributes attached to a path (url: {}), server response: \n " \
                           "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}".format(
            resource_url, response.status_code, response.reason, error_code, error_msg, user_msg))

    results = {}
    for item in response_object["items"]:
        results[item["name"]] = item["value"]
    return results
def _attach_experiment_xattr(ml_id, json_data, op_type):
    """
    Utility method for putting JSON data into elastic search

    Args:
        :ml_id: experiment id
        :json_data: the experiment json object
        :op_type: operation type INIT/MODEL_UPDATE/FULL_UPDATE

    Returns:
        None

    """

    json_data = dumps(json_data)

    headers = {'Content-type': 'application/json'}
    resource_url = constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_REST_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_PROJECT_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   hdfs.project_id() + constants.DELIMITERS.SLASH_DELIMITER + \
                   constants.REST_CONFIG.HOPSWORKS_EXPERIMENTS_RESOURCE + constants.DELIMITERS.SLASH_DELIMITER + \
                   ml_id + "?type=" + op_type

    response = util.send_request('PUT',
                                 resource_url,
                                 data=json_data,
                                 headers=headers)

    response_object = response.json()
    if response.status_code >= 400:
        error_code, error_msg, user_msg = util._parse_rest_error(
            response_object)
        raise RestAPIError(
            "Could not create experiment (url: {}), server response: \n "
            "HTTP code: {}, HTTP reason: {}, error code: {}, error msg: {}, user msg: {}"
            .format(resource_url, response.status_code, response.reason,
                    error_code, error_msg, user_msg))
    else:
        return response_object