Skip to main content

Getting started with the ONTAP REST API

Contributors

ONTAP adds support for an expansive RESTful API. The documentation below provides information about the types of API calls available with ONTAP, as well as details about using each API endpoint. You can learn more about the ONTAP REST API in the ONTAP automation doc site: https://docs.netapp.com/us-en/ontap-automation/index.html.

Using the ONTAP REST API online documentation

Each API method includes usage examples, as well as a model that displays all the required and optional properties supported by the method. Click the Model link, available with each API method, to see all the required and optional properties supported by each method.

Features for all ONTAP APIs

Getting started with the ONTAP REST API

Overview

Let's review some key things about RESTful APIs and how they're implemented in ONTAP:

  • REST API URLs identify the resources that you'll be working with, including clusters, SVMs, and storage.

  • REST APIs use HTTP methods GET, POST, PATCH, DELETE, and OPTIONS to indicate their actions.

  • REST APIs return common HTTP status codes to indicate the results of each call. Additional error details can be included in the results body.

  • REST APIs request and response bodies are encoded using JSON.

  • REST APIs support hyperlinking among resources using the Content-Type "application/hal+json".

  • REST API requests will be rejected when the wait queue is full. This is an uncommon occurrence and is designed to prevent a DOS attack.

  • GET calls on collections usually return only name and UUID by default. If you want to retrieve additional properties, you need to specify them using the “fields” query parameter.

  • ONTAP supports query-based DELETE or PATCH for all collection endpoints.

  • REST APIs follows RFC3986 Section 5.2.4 regarding relative reference resolution.

If you're already familiar with the ONTAPI API (also known as ZAPI), there are some similarities between ONTAP REST APIs and ONTAPI. For example:

  • Both support the same transport and security mechanisms.

  • Both paginate results based on either number of seconds or number of records.

  • Both support filtering the returned records based on property values.

  • Both support limiting the returned properties.

  • Both support concurrent requests. If ONTAP temporarily can't handle additional calls, it will respond with an HTTP error status code of 429 or 503 (depending on the kind of limit hit) and an error message in the body explaining the limit encountered.

However, there are important differences between REST APIs and the ONTAP CLI and ONTAPI that you should understand as well:

  • In many cases, ONTAP REST APIs use different names for fields and features.

  • REST APIs do not expose infrequently used CLI parameters.

  • REST APIs do not treat the cluster or nodes as an SVM (aka Vserver).

  • REST GET APIs support specifying a maximum time before paginating results. However, the default time is 15 seconds for REST (instead of 90 seconds for ONTAPI).

  • REST APIs are generally ordered by UUID or ID, so a rename operation using the PATCH method doesn't change the path keys.

  • REST APIs use one or more of the following properties to identify a resource: "name", "uuid", "id".

  • REST APIs often execute the equivalent of multiple CLI commands in a single request.

  • REST API properties use underscores instead of hyphens between words.

  • REST API dates are always in ISO-8601 format.

  • REST API comparisons between enum values (for <, >, ranges, and order_by) are done alphabetically. (In CLI and ONTAPI, enum comparisons are done based on an internal value for the enum.)

  • REST API field '<' queries exclude records where the specified field is not set. You can add "\|null" (eg: limit=<10\|null) to also return records where the specified field is not set.

Relative reference resolution

In accordance with RFC3986 Section 5.2.4, ONTAP REST API removes dot segments to resolve the path. The examples below describe this behavior.

This request uses non-URL-encoded dots:

curl -siku <username>:<password> -X GET "https://mgmt_ip_address/api/storage/volumes/<volume_uuid>/files/.."

The previous request is resolved to this:

curl -siku <username>:<password> -X GET "https://mgmt_ip_address/api/storage/volumes/<volume_uuid>"

In the previous example, the volume information is returned rather than the file. Users should be cautious when using non-URL-encoded ".." since it can cause accidental data loss when making a delete request.

This request is interpreted as a delete to the volume rather than the file because of ...

curl -siku <username>:<password> -X DELETE "https://<mgmt_ip_address>/api/storage/volumes/<volume_uuid>/files/.."

To avoid this issue, dots should be URL-encoded to %2E. The previous request should look like this:

curl -siku <username>:<password> -X DELETE "https://<mgmt_ip_address>/api/storage/volumes/<volume_uuid>/files/%2E%2E"

HAL linking

Hypertext Application Language (HAL)

ONTAP REST APIs use HAL as the mechanism to support Hypermedia as the Engine of Application State (HATEOAS). When an object or attribute is returned that identifies a specific resource, a HAL-encoded link is also returned so that you can easily discover resources and be able to obtain more details about the resource.

Example

"aggregate": {
    "uuid": "19425837-f2fa-4a9f-8f01-712f626c983c",
    "name": "aggr0",
    "_links": {
        "self": {
            "href": "/api/storage/aggregates/19425837-f2fa-4a9f-8f01-712f626c983c"
        }
    }
}

Query parameters

Overview

The following is a list of all the globally supported query parameters. This list is intended as a quick reference for syntax purposes. The query parameters are described in more detail in other sections of this documentation. Note that multiple queries can be combined using an "&".

# Request specific fields
fields=<field>[,...]

# Don't error due to an unknown field in "fields=" (default is false)
ignore_unknown_fields=<true|false>

# Query fields by value. If the value contains query characters (*|,!<>..), it must be quoted to avoid their special query meaning
<field>=<query value>

# Return the records array
return_records=<true|false>

# Timeout and return after the specified number of seconds
return_timeout=<0..120 seconds>

# The number of records to collect (or act on for query-based PATCH/DELETE) before returning
max_records=<number of records>

# Request a customized sort ordering
order_by=<field [asc|desc]>[,...]
$orderBy=<field [asc|desc]>[,...]

# Pretty print JSON response bodies
pretty=<true|false>

# Continue after encountering a failure. Only applicable to query-based PATCH and DELETE.
continue_on_failure=<true|false>

# Begin returning records starting at an offset from the first record
offset=<offset from first record>

Operations on multiple records

Overview

Although they are not documented as individual methods in the list of REST APIs, every API supporting POST, PATCH, or DELETE, also supports operations on multiple records in the collection. These collection based operations can be specified as either a list of records, allowing each record to be mutated individually, or as a query that applies the same change to multiple records in the collection.

Option 1: Record-based POST, PATCH, and DELETE

APIs supporting POST, PATCH, or DELETE support these operations on the collection itself by specifying a records array in the JSON body. Each entry in this array must match the schema of an individual record of the API. Parsing failures are returned synchronously. All other validations are performed asynchronously, including those that are usually returned synchronously for asynchronous APIs.

Rolling back changes on failure

POST and PATCH make a best-effort attempt to perform the operation atomically by rolling back any changes made to prior records if an operation on a later record fails. This behavior can be overridden via the continue_on_failure query parameter. When this parameter is true (default: false), the job performs the operation on every input record regardless of prior failures. If any failures are encountered in continue_on_failure mode, they are reported as the result of the job, but the successfully mutated records are not rolled back. DELETE operations do not attempt to roll back failures and always operate in continue_on_failure mode.

Serial and parallel operations

Most APIs support performing operations in parallel, and record-based operations take advantage of this, operating on multiple of the provided records at once. If there is some interdependence between the provided records, such as a hierarchical set of objects, the serial_records=true query parameter can be used to force the operation to be performed in order. APIs that do not support parallel operations run serially, by default. From a job tracking perspective, there is no difference between these two modes other than the speed at which the operation completes.

Asynchronous job and results

Unless the records array is directly documented in the API as synchronous, every record-based operation is performed as a job and follows the usual rules for asynchronous jobs. In addition to the usual /api/cluster/jobs record of the job, these operations return a job results href link. The link refers back to the API on which the operation was performed and can be followed after the job is complete. Job results include the current state of the records and fields operated on by the job. If a POST operation was successful on a record, that record is included in job results. If a DELETE operation was successful, that record is not included in the job results because it is no longer part of the collection. The same concept applies to failures. If the POST of a record fails, the record is not included in the job results because it is not part of the collection. The results also include any errors or rollback errors encountered by the job. The job results link is also returned as the Location header from a record-based POST job, supporting the same pattern for determining any generated identifiers as a single record POST. Note that the job results link does not support all of the usual GET features. Requesting specific fields and querying a subset of the records is supported, but max_records and return_timeout as well as other such query parameters for controlling the iteration of the collection is are not supported.

Unique identifiers go in the body

For PATCH and DELETE, any unique identifiers not included in the URI must be included in the JSON body of each record. For example, to DELETE a single volume, the API is usually /api/storage/volumes/{uuid}. To delete multiple volumes via a records array, the API is /api/storage/volumes, and the UUID of each volume is included in the JSON body of each record. For sub-endpoints with unique identifiers embedded in the path, such as /api/storage/volumes/{volume.uuid}/files/{path}, the volume.uuid is included once in the URI and is not included in each record. The path is included with each record.

Examples

Creating two volumes

# The API:
POST /api/storage/volumes
# The call:
curl -X POST 'https://<mgmt-ip>/api/storage/volumes' -H 'accept: application/hal+json' -d '{ "records": [
    { "svm": { "name": "svm1" }, "name" : "vol1", "size": "1GB", "aggregates": [ { "name": "aggr1" } ] },
    { "svm": { "name": "svm1" }, "name" : "vol2", "size": "1GB", "aggregates": [ { "name": "aggr1" } ] } ] }'
# The response:
{
  "job": {
    "uuid": "cc979874-a441-11eb-a707-005056bbbc41",
    "_links": {
      "self": {
        "href": "/api/cluster/jobs/cc979874-a441-11eb-a707-005056bbbc41"
      },
      "results": {
        "href": "/api/storage/volumes?job_results_uuid=cc979874-a441-11eb-a707-005056bbbc41"
      }
    }
  }
}
# GET the job results:
curl -X GET 'https://<mgmt-ip>/api/storage/volumes?job_results_uuid=cc979874-a441-11eb-a707-005056bbbc41' -H 'accept: application/hal+json'
# The response:
{
  "records": [
    {
      "uuid": "cc9915c8-a441-11eb-a707-005056bbbc41",
      "name": "vol1",
      "size": 1073741824,
      "aggregates": [
        {
          "name": "aggr1",
          "uuid": "a589e251-a096-44be-b5d3-67ccf92d27e7"
        }
      ],
      "svm": {
        "name": "svm1"
      },
      "_links": {
        "self": {
          "href": "/api/storage/volumes/cc9915c8-a441-11eb-a707-005056bbbc41"
        }
      }
    },
    {
      "uuid": "cc9c1eea-a441-11eb-a707-005056bbbc41",
      "name": "vol2",
      "size": 1073741824,
      "aggregates": [
        {
          "name": "aggr1",
          "uuid": "a589e251-a096-44be-b5d3-67ccf92d27e7"
        }
      ],
      "svm": {
        "name": "svm1"
      },
      "_links": {
        "self": {
          "href": "/api/storage/volumes/cc9c1eea-a441-11eb-a707-005056bbbc41"
        }
      }
    }
  ],
  "num_records": 2,
  "_links": {
    "self": {
      "href": "/api/storage/volumes?job_results_uuid=cc979874-a441-11eb-a707-005056bbbc41"
    }
  }
}

Creating two LUNs with a failure and an additional failure during rollback

Note that the LUN that was not rolled back is reported as being present in the results because it now exists in the collection.

# The API:
POST /api/storage/luns
# The call:
curl -X POST 'https://<mgmt-ip>/api/storage/luns' -H 'accept: application/hal+json' -d '{ "records": [
    { "svm": { "name": "svm1" }, "name" : "/vol/vol1/lun1", "space": { "size": "1GB" }, "os_type": "linux" },
    { "svm": { "name": "svm1" }, "name" : "/vol/vol1/lun2", "space": { "size": "5GB" }, "os_type": "linux" } ] }'
# The response
{
  "job": {
    "uuid": "09d0c372-a445-11eb-a707-005056bbbc41",
    "_links": {
      "self": {
        "href": "/api/cluster/jobs/09d0c372-a445-11eb-a707-005056bbbc41"
      },
      "results": {
        "href": "/api/storage/luns?job_results_uuid=09d0c372-a445-11eb-a707-005056bbbc41"
      }
    }
  }
}
# GET the job results:
curl -X GET 'https://<mgmt-ip>/api/storage/luns?job_results_uuid=09d0c372-a445-11eb-a707-005056bbbc41' -H 'accept: application/hal+json'
# The response:
{
  "records": [
    {
      "uuid": "2287871f-fbcf-4d64-ae89-59328dd755b3",
      "svm": {
        "name": "svm1"
      },
      "name": "/vol/vol1/lun1",
      "os_type": "linux",
      "space": {
        "size": 1073741824
      },
      "_links": {
        "self": {
          "href": "/api/storage/luns/2287871f-fbcf-4d64-ae89-59328dd755b3"
        }
      }
    }
  ],
  "num_records": 1,
  "errors": [
    {
      "message": "POST of record \"svm.name: svm1, name: /vol/vol1/lun2\" failed. Reason: Volume offline.",
      "code": "262287"
    }
  ],
  "rollback_errors": [
    {
      "message": "DELETE of record \"uuid: 2287871f-fbcf-4d64-ae89-59328dd755b3\" failed. Reason: Volume offline.",
      "code": "262287"
    }
  ],
  "_links": {
    "self": {
      "href": "/api/storage/luns?job_results_uuid=09d0c372-a445-11eb-a707-005056bbbc41"
    }
  }
}

Record-based Errors

ONTAP Error Response Codes

Error Code Description HTTP Code

262287

A single record in a bulk records request failed for the given reason.

400

262288

A record-based operation failed and was left in a partially completed state.

400

262289

A record-based job progress message. N of M records complete.

200

262290

A record-based job progress message during rollback. N of M records remaining.

200

262291

The operation did not complete within the system timeout period. Wait a few minutes, and then try the operation again.

400

262292

The records operation was partially completed. The following failures were encountered.

400

262293

Job is still running. Wait for the job to complete and then try the operation again.

400

262294

Job is not associated with this API.

400

Option 2: Query-based PATCH and DELETE

APIs supporting PATCH or DELETE requests on a resource instance also support PATCH or DELETE on the collection, as l