Table of Contents
This document describes a RESTful API over HTTP for accessing data defined in YANG. It tries to follow the RESTCONF Internet Draft [draft-ietf-netconf-restconf-00] but since it predates the creation of RESTCONF, a number of differences exists. Whenever such a difference occur, it will be clearly stated.
The RESTCONF protocol operates in the configuration datastores defined in NETCONF. It defines a set of Create, Retrieve, Update, Delete (CRUD) operations that can be used to access these datastores. The YANG language defines the syntax and semantics of datastore content, operational data and protocol operations. REST operations are used to access the hierarchical data within a datastore. Request and response data can be in XML or JSON format, where XML is the default format.
To get a quick introduction to how to enable REST in ConfD and how to run the CRUD operations, continue to Section 21.2, “Getting started”.
To read more about resources, continue to Section 21.4, “Resources”.
All REST request/response examples in this chapter are based on the examples.confd/rest/router example. Go to this example to play around and get familiar with the REST api.
Sending REST requests using a browser is an easy way to test things. But using REST with Basic Authentication in the browser should not be used because of security considerations. Use the JSON-RPC login mechanism instead. Once authenticated, REST requests don't need Basic Authentication anymore. You can read more about the JSON login mechanism in ????.
In order to enable REST in ConfD, REST must be enabled in
confd.conf
. The web server
configuration for REST is shared with the WebUI's config. However,
the WebUI does not have to be enabled for REST to work.
Here's a minimal example of what is needed in the conf file:
Example 21.1. ConfD configuration for REST
<rest> <enabled>true</enabled> </rest> <webui> <enabled>false</enabled> <transport> <tcp> <enabled>true</enabled> <ip>0.0.0.0</ip> <port>8008</port> </tcp> </transport> </webui>
Resources are represented with URIs following the structure for generic URIs in [RFC3986].
Example 21.2. Request URI structure
<OP> /api/<path>?<query>#<fragment> ^ ^ ^ ^ ^ | | | | | method entry resource query fragment M M O O I M=mandatory, O=optional, I=ignored <text> replaced by client with real values
A REST operation is derived from the HTTP method and the request URI, using the following conceptual fields:
"method": the HTTP method identifying the REST operation requested by the client, to act upon the target resource specified in the request URI.
"entry": the well-known REST entry point ("/api").
THIS DIFFERS FROM RESTCONF!
"resource": the path expression identifying the resource that is being accessed by the operation. If this field is not present, then the target resource is the API itself, represented by the media type "application/vnd.yang.api".
THE MEDIA TYPE DIFFERS FROM RESTCONF!
"query": the set of parameters associated with the REST message. These have the familiar form of "name=value" pairs. There is a specific set of parameters defined, see Section 21.2.10, “Query Parameters”. The contents of the query parameter value must be encoded according to [RFC2396], section 3.4. Any reserved characters must be encoded with escape sequences, according to [RFC2396], section 2.4.
"fragment": This field is not used by the REST protocol.
The REST protocol uses HTTP methods to identify the CRUD operation requested for a particular resource. The following table shows how the REST operations relate to NETCONF protocol operations:
Table 21.1. REST vs NETCONF operations
REST | NETCONF |
---|---|
GET | <get-config>, <get> |
POST | <edit-config> (operation="create") |
PUT | <edit-config> (operation="replace") |
PATCH | <edit-config> (operation="merge") |
DELETE | <edit-config> (operation="delete") |
OPTIONS | none |
HEAD | none |
The REST API can be accessed, e.g., by using curl:
Example 21.3. Using curl for accessing ConfD
curl -u admin:admin -s http://localhost:8008/api/foo/bar -X GET
To provide an HTTP header use the -H
switch:
... -H "Accept: application/vnd.data+xml"
Note that in the following examples we will shorten the curl calls to:
GET /foo/bar Accept: application/vnd.data+xml
By default curl will display responses on standard output. The headers are not included. To include these the "-i" switch has to be added.
curl -i ...
The response can then typically look like:
HTTP/1.1 200 OK Server: ConfD/5.3.0 Cache-control: private, no-cache, must-revalidate, proxy-revalidate Date: Fri, 05 Sep 2014 13:09:46 GMT Content-Type: application/vnd.yang.data+json Transfer-Encoding: chunked Etag: 1409-922585-953711 Last-Modified: Fri, 01 Jan 1971 00:00:00 GMT { "some-data": { "some-value": "value" } }
In the examples shown in this chapter we will from the beginning show all headers with the response. In later examples the headers will be omitted for brevity.
The GET method is sent by the client to retrieve data and meta-data
for a resource. It is supported for all resource types, except
operation resources. The request must contain a request URI that
contains at least the entry point component (/api
).
Note how we make use of
the Accept
HTTP header to indicate what format we
want the returned result to be in. The value of the Accept
HTTP header, in this example: application/yang.data+json
,
must be a valid media type. Since XML is the default format we
need to explicitly request JSON in the example below.
To read more about the media types, see
Section 21.4, “Resources”.
Example 21.4. Get the "sys/interfaces" resource represented as JSON
GET /running/sys/interfaces/ex:serial Accept: application/vnd.yang.collection+json
The server might respond:
HTTP/1.1 200 OK Server: Date: Wed, 12 Aug 2015 11:38:20 GMT Last-Modified: Fri, 01 Jan 1971 00:00:00 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379499-898453 Content-Type: application/vnd.yang.collection+json Transfer-Encoding: chunked Pragma: no-cache { "collection": { "example-serial:serial": [ { "name": "ppp0", "ppp": { "accounting": "acme" }, "authentication": { "method": "pap" }, "authorization": "admin", "ipcp": { } } ] } }
To indicate a particular YANG namespace in the URI, the
YANG module prefix is used. In the example
we are using a module prefix: ex
.
THIS DIFFERS FROM RESTCONF!
In accordance with RESTCONF,
the returned "serial"
container
is using a module name: example-serial
to
indicate a particular YANG namespace.
To read more about the ETag
and Last-Modified
response headers, see Section 21.6, “Request/Response headers”.
Refer to Section 21.3.1, “GET and Query examples” for more resource retrieval examples.
The POST method is sent by the client to create a data resource or invoke an operation resource ("rpc" or "tailf:action").
Here we show an example of resource creation using POST. For an example of operation invokation see Section 21.3.3, “Invoke Operations”
Example 21.5. Create a new "sys/routes/inet/route" resource, with JSON payload
POST /running/sys/routes/inet Content-Type: application/vnd.yang.data+json
{ "route": { "name": "10.20.1.0", "prefix-length": "24" } }
If the resource is created, the server might respond as follows:
HTTP/1.1 100 Continue Server: Allow: GET, POST, OPTIONS, HEAD Content-Length: 0 HTTP/1.1 201 Created Server: Location: http://127.0.0.1:8008/api/running/sys/routes/inet/route/10.20.1.0,24 Date: Wed, 12 Aug 2015 11:38:20 GMT Allow: GET, POST, OPTIONS, HEAD Last-Modified: Wed, 12 Aug 2015 11:38:20 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379500-235361 Content-Length: 0 Content-Type: text/html Pragma: no-cache
If the POST method succeeds, a "201 Created"
Status-Line is returned and there is no response message body.
Also, a "Location"
header
identifying the child resource that was created is present in
the response.
Refer to the section called “Create a List Instance with POST” for more examples of creating resources.
If the target resource type is an operation resource, then the POST method is treated as a request to invoke that operation. The message body (if any) is processed as the operation input parameters. Refer to Section 21.4.5, “Operations and Actions” for details on operation resources.
The PUT method is sent by the client to create or replace the target resource. The request must contain a request URI that contains a target resource that identifies the data resource to create or replace.
Example 21.6. Replace the "sys/routes/inet/route" resource contents
PUT /running/sys/routes/inet/route/10.20.1.0,24 Content-Type: application/vnd.yang.data+json
{ "route": { "name": "10.20.1.0", "prefix-length": "24", "description": "Example route", "enabled" : "false" } }
If the resource is updated, the server might respond:
HTTP/1.1 100 Continue Server: Allow: GET, POST, OPTIONS, HEAD Content-Length: 0 HTTP/1.1 204 No Content Server: Date: Wed, 12 Aug 2015 11:38:20 GMT Allow: GET, POST, OPTIONS, HEAD Last-Modified: Wed, 12 Aug 2015 11:38:20 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379500-401146 Content-Length: 0 Content-Type: text/html Pragma: no-cache
The "insert" and "resource" query parameters are supported by the PUT method for data resources, for more examples see the section called “Insert Data into Resources”.
The "resource" query parameter correspond
to the "point" query parameter in RESTCONF.
THIS DIFFERS FROM RESTCONF!
If the PUT method creates a new resource, a "201 Created" Status-Line is returned. If an existing resource is modified, either "200 OK" or "204 No Content" are returned.
Refer to the section called “Create and Replace a List Instance with PUT” for more examples on how to use PUT.
The PATCH method is used to create or update a sub-resource within the target resource. If the target resource instance does not exist, the server WILL NOT create it.
To replace just the "enabled" field in the "route" list resource (instead of replacing the entire resource with the PUT method), the client might send a plain patch as follows.
Example 21.7. Update the "sys/routes/inet/route" resource contents
PATCH /running/sys/routes/inet/route/10.20.1.0,24 Content-Type: application/vnd.yang.data+json
{ "route": { "enabled" : "true" } }
If the resource is updated, the server might respond:
HTTP/1.1 100 Continue Server: Allow: GET, POST, OPTIONS, HEAD Content-Length: 0 HTTP/1.1 204 No Content Server: Date: Wed, 12 Aug 2015 11:38:20 GMT Allow: GET, POST, OPTIONS, HEAD Last-Modified: Wed, 12 Aug 2015 11:38:20 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379500-570215 Content-Length: 0 Content-Type: text/html Pragma: no-cache
Refer to the section called “Update Existing List Instance with PATCH” for more examples on how to use PATCH.
The DELETE method is used to delete the target resource. If the DELETE method succeeds, a "204 No Content" Status-Line is returned, and there is no response message body.
Example 21.8. Delete the "sys/routes/inet/route" resource contents
DELETE /running/sys/routes/inet/route/10.20.1.0,24
If the resource is successfully deleted, the server might respond:
HTTP/1.1 204 No Content Server: Date: Wed, 12 Aug 2015 11:38:20 GMT Last-Modified: Wed, 12 Aug 2015 11:38:20 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379500-746781 Content-Length: 0 Content-Type: text/html Pragma: no-cache
Refer to Section 21.3.4, “Delete Data Resources” for more examples on how to use DELETE.
The OPTIONS method is sent by the client to discover which methods are supported by the server for a specific resource. The supported methods are listed in the ALLOW header.
Example 21.9. Get options for the "sys" resource
OPTIONS /running/sys
The server might respond:
HTTP/1.1 200 OK Server: Allow: DELETE, GET, HEAD, PATCH, POST, PUT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Content-Length: 0 Content-Type: text/html Pragma: no-cache
Here the options method responds with an ALLOW header indicating that all methods are allowed on the "sys" resource.
The HEAD method is sent by the client to retrieve just the headers that would be returned for the comparable GET method, without the response body. The access control behavior is enforced as if the method was GET instead of HEAD. The server will respond the same as if the method was GET instead of HEAD, except that no response body is included.
Example 21.10. Get head for the "sys/interfaces/ex:serial" resource
HEAD /running/sys/interfaces/ex:serial
The server might respond:
HTTP/1.1 200 OK Server: Date: Wed, 12 Aug 2015 11:38:20 GMT Last-Modified: Wed, 12 Aug 2015 11:38:20 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379500-746781 Content-Length: 0 Content-Type: application/vnd.yang.collection+xml Pragma: no-cache
Here the "Content-Length" header is 0. This is because the content length calculation is not eligible when the actual content is suppressed as with the HEAD method.
Each REST operation allows zero or more query parameters to be present in the request URI. The specific parameters that are allowed depends on the resource type, and sometimes the specific target resource used, in the request.
Table 21.2. Query Parameters
Name | Methods | Description |
---|---|---|
deep | GET | Retrieve a resource with all subresources inline. |
insert | POST | For an ordered-by user list, we can specify where a resource, to be created, should be inserted. This query parameter is used together with the resource query parameter. Possible values are: after, before,first and last. See the section called “Insert Data into Resources” for details. |
limit | GET | Used by the client to specify a limited set of list entries to retrieve. See the section called “Partial Responses” for details. |
offset | GET | Used by the client to specify a limited set of list entries to retrieve. See the section called “Partial Responses” for details. |
operations | GET | Used by the client to include/exclude operations (tailf:actions) in the result. Possible values are: true, which is the default value, and false. |
resource | POST | For an ordered-by user list, we can specify where a resource, to be created, should be inserted. This query parameter is used together with the insert query parameter. See the section called “Insert Data into Resources” for details. |
select | GET | Used by the client to select which nodes and subresources in a resource to retrieve. See the section called “Partial Responses” for details. |
shallow | GET | Retrieve a resource with no subresources inline. |
unhide | GET | Used by the client to unhide hidden nodes. See the section called “Hidden Nodes” for details. |
verbose | GET | Used by the client to control display of the "self" and "path" attributes of the resource. See the section called “Displaying Default Data” for details. |
with-defaults | GET | Used by the client to control display of default data in GET requests. See the section called “Displaying Default Data” for details. |
The query parameters of the REST API is not the same as for RESTCONF.
THIS DIFFERS FROM RESTCONF!
If neither "deep" nor "shallow" is used, you will get a variable depth returned. The output shown will stop at the first encountered presence container or list key value(s).
If a resource only needs to be outlined, the shallow query parameter can be used on the GET request.
Example 21.11. Shallow get for the "sys" resource
GET /running/sys?shallow
The server might respond:
<sys xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <interfaces/> <routes/> <syslog/> <ntp/> <dns/> </sys>
On the other hand, if the full subtree under a resource is required, the deep query parameter can be used on the GET request.
Example 21.12. Deep get for the "sys/interfaces/interface" resource
GET /running/sys/interfaces/interface?deep
The server might respond:
<collection xmlns:y="http://tail-f.com/ns/rest"> <interface xmlns="http://example.com/router"> <name>eth0</name> <unit xmlns="http://example.com/router"> <name>0</name> <enabled>true</enabled> <status xmlns="http://example.com/router"> </status> <family xmlns="http://example.com/router"> <inet xmlns="http://example.com/router"> <address xmlns="http://example.com/router"> <name>192.168.1.2</name> <prefix-length>16</prefix-length> </address> </inet> </family> </unit> <unit xmlns="http://example.com/router"> <name>1</name> <enabled>true</enabled> <status xmlns="http://example.com/router"> </status> <family xmlns="http://example.com/router"> <inet xmlns="http://example.com/router"> <address xmlns="http://example.com/router"> <name>192.168.1.3</name> <prefix-length>16</prefix-length> </address> </inet> </family> </unit> <unit xmlns="http://example.com/router"> <name>2</name> <enabled>true</enabled> <description>My Vlan</description> <vlan-id>18</vlan-id> <status xmlns="http://example.com/router"> </status> </unit> </interface> </collection>
For more info about "deep" vs "shallow", see: Section 21.2.10, “Query Parameters”
By default, the server sends back the full representation of a resource after processing a request. For better performance, the server can be instructed to send only the nodes the client really needs in a partial response.
To request a partial response for a set of list entries, use the "offset" and "limit" query parameters to specify a limited set of entries to be returned.
For example, if we what to retrieve only 2 entries from the
sys/routes/inte/route
list we can issue the command:
Example 21.13. Limit the response
GET /running/sys/routes/inet/route?offset=3&limit=2
The following request retrieves 2 entries starting from entry 3 (the first entry is 0):
<collection xmlns:y="http://tail-f.com/ns/rest"> <route xmlns="http://example.com/router"> <name>10.40.0.0</name> <prefix-length>16</prefix-length> <description>Route 4</description> <next-hop xmlns="http://example.com/router"> <name>192.168.10.4</name> </next-hop> </route> <route xmlns="http://example.com/router"> <name>10.50.0.0</name> <prefix-length>16</prefix-length> <description>Route 5</description> <next-hop xmlns="http://example.com/router"> <name>192.168.10.5</name> </next-hop> </route> </collection>
To request a filtered partial response, use the "select" query parameter to specify the nodes and subresources to be returned.
Use a semicolon-separated list to select multiple nodes.
Use "a/b" to select a node "b" that is nested within node "a"; use "a/b/c" to select a node "c" nested within "b".
Specify node subselectors to request only specific subnodes by placing expressions in parentheses "( )" after any selected node.
To specify all subnodes of a specific node use the special wildcard notion within parentheses "(*)"
NOTE: "a/b/c;a/b/d" is equivalent to "a/b(c;d)"
The following request selects the routes name and next-hop/name nodes:
Example 21.14. Limit the response with select
GET /running/sys/routes/inet/route/10.20.0.0,16?select=name;next-hop(name)
The server might respond:
<route xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <name>10.20.0.0</name> <next-hop> <name>192.168.10.2</name> </next-hop> </route>
Hidden nodes are described in Section 10.7, “Hidden Data”. By default, hidden nodes are not visible in the REST interface. In order to unhide hidden nodes for retrieval or editing, clients can use the query parameter "unhide". The format of the "unhide" parameter is a comma-separated list of
<groupname>[;<password>]
As an example:
unhide=extra,debug;secret
This example unhides the normal group "extra" and the password-protected group "debug" with the password "secret".
Normally, leaf nodes that are not set but has a default value are not displayed at a GET request. This behavior can be controlled by the use of the "with-defaults" query parameter. This parameter can take one of four values:
"report-all": all data nodes are be reported, including any data nodes considered to be default data by the server.
"explicit": a data node that has been explicitly set is reported. This is also the case when a data node is explicitly set to a value that coincide with the default value for the node.
"trim": data nodes are not reported if they contain default value even if this was explicitly set to that value.
"report-all-tagged": In this mode the server returns all data nodes, just like the "report-all" mode, except a data node that is considered by the server to contain default data will include an attribute to indicate this condition.
A request with the "with-defaults" query parameter without a specified value will be interpreted as "report-all".
If we make a normal GET of the "sys/ntp/server" resource. The default values are not retrieved:
Example 21.15. The "sys/ntp/server" list (no defaults)
GET /running/sys/ntp/server
<collection xmlns:y="http://tail-f.com/ns/rest"> <server xmlns="http://example.com/router"> <name>10.2.3.4</name> <key>2</key> </server> </collection>
We can get also the default values by setting the query parameter
with-defaults=report-all
.
Example 21.16. The "sys/ntp/server" list with all defaults
GET /running/sys/ntp/server?with-defaults=report-all
<collection xmlns:y="http://tail-f.com/ns/rest"> <server xmlns="http://example.com/router"> <name>10.2.3.4</name> <enabled>true</enabled> <peer>false</peer> <version>4</version> <key>2</key> </server> </collection>
To
create a child resource you POST the child resource to
its parent (target) resource, and in a successful result you will
get returned the URI to the created resource in
the Location
header.
With PUT you will create or replace a target (data) resource. The message body is expected to contain the content used to create or replace the target resource.
POST can be used to create a child resource to the resource specified by the uri. In this example we create a "route" list entry. The URI points to the parent container of the list. The payload in the POST request contain a "route" node that, at least, contain the list keys.
When creating a list entry all keys must be given in the payload.
Example 21.17. Creating a "sys/routes/inet/route" resource
POST /running/sys/routes/inet Content-Type: application/vnd.yang.data+xml
<route> <name>10.20.3.0</name> <prefix-length>24</prefix-length> </route>
The server might respond:
HTTP/1.1 201
When an element is successfully created the HTTP status response is 201. If the element already existed the status response is 409.
In this example we create a "multilink" presence container. The URI points to the list instance that contains the presence container, i.e the URI ends with the key(s). The payload in the POST request contain a "multilink" node that contain a value for the leaf node "group".
It is not possible to create a non-presence container with POST, as non-presence containers per definition always exists.
Example 21.18. Creating a "sys/interfaces/serial/ppp0/multilink" resource
POST /running/sys/interfaces/serial/ppp0 Content-Type: application/vnd.yang.data+xml | egrep '(HTTP.*Created.*|Locat*)'
<multilink> <group>1</group> </multilink>
The server might respond:
HTTP/1.1 201 Created Location: http://127.0.0.1:8008/api/running/sys/interfaces/ex:serial/ppp0/multil ink
PUT can be used to create or replace a resource. No distinction is made in the prerequisites for PUT. If no resource existed it is created, but if it existed it is replaced. In the example, note how the URI ends with the keys.
Example 21.19. Creating a "route" resource using PUT
PUT /running/sys/routes/inet/route/10.30.3.0,24 Content-Type: application/vnd.yang.data+xml
<route> <name>10.30.3.0</name> <prefix-length>24</prefix-length> <next-hop> <name>192.168.4.4</name> <metric>100</metric> </next-hop> </route>
The server might respond:
HTTP/1.1 201
When an element is successfully created the HTTP status response is 201. We make a GET to verify the "route" list after the PUT of the element.
Example 21.20. The "route" resource after creation
GET /running/sys/routes/inet/route/10.30.3.0,24
<route xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <name>10.30.3.0</name> <prefix-length>24</prefix-length> <next-hop> <name>192.168.4.4</name> </next-hop> </route>
We didn't get the "metric" in the above result from the GET! This has to do with the (non) use of "deep" and "shallow", see: Section 21.2.10, “Query Parameters”
We will now do a replace of the same "route" element.
Example 21.21. Replacing a "route" resource using PUT
PUT /running/sys/routes/inet/route/10.30.3.0,24 Content-Type: application/vnd.yang.data+xml
<route> <name>10.30.3.0</name> <prefix-length>24</prefix-length> <next-hop> <name>192.168.3.1</name> <metric>100</metric> </next-hop> <next-hop> <name>192.168.4.2</name> <metric>200</metric> </next-hop> </route>
The server might respond:
HTTP/1.1 204
We get the 204 response as the resource already existed. We can verify that the element is replaced.
Example 21.22. The "route" resource after replace
GET /running/sys/routes/inet/route/10.30.3.0,24
<route xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <name>10.30.3.0</name> <prefix-length>24</prefix-length> <next-hop> <name>192.168.3.1</name> </next-hop> <next-hop> <name>192.168.4.2</name> </next-hop> </route>
In this example we create a "multilink" presence container. The uri points to the not yet existing URI for the presence container "multilink". The payload in the PUT request contain a "multilink" node that contain a value for the leaf node 'group'.
Example 21.23. Creating a "sys/interfaces/serial/ppp0/multilink" resource
PUT /running/sys/interfaces/serial/ppp0/multilink Content-Type: application/vnd.yang.data+xml
<multilink> <group>1</group> </multilink>
The server might respond:
HTTP/1.1 201
Any subsequent request to the same URI will replace the content of "multilink" with the new payload. But the response code will instead be 204, since the resource already exists.
Have in mind that PUT will replace everything with the provided payload. So in the example above, if the container "multilink" contained additional subnodes, other than "group", they would have been deleted as they were not present in the payload.
In this example we "populate" a non-presence container with subnode data. The URI points to the existing URI for the non-presence container "authentication", since a non-presence container always exist if its parent exist.
Example 21.24. Creating a "sys/interfaces/serial/ppp0/authentication" resource
PUT /running/sys/interfaces/serial/ppp0/authentication Content-Type: application/vnd.yang.data+xml
<authentication> <method>pap</method> <list-name>foobar</list-name> </authentication>
The server might respond:
HTTP/1.1 204
Have in mind that PUT will replace everything with the provided payload. So in the example above, if the container "authentication" contained additional subnodes, they would have been deleted as they were not present in the payload.
Example 21.25. The "authentication" resource after replace
GET /running/sys/interfaces/serial/ppp0/authentication
<authentication xmlns="http://example.com/example-serial" xmlns:y="http://tail-f.com/ns/rest" xmlns:ex="http://example.com/example-serial" xmlns:r="http://example.com/router"> <method>pap</method> <list-name>foobar</list-name> </authentication>
To update an existing resource the PATCH method can be used. PATCH will not be allowed on non-existent resources. When we use PATCH the payload should only contain the data that should be modified.
Example 21.26. Updating a "route" resource using PATCH
PATCH /running/sys/routes/inet/route/10.30.3.0,24 Content-Type: application/vnd.yang.data+xml
<route> <next-hop> <name>192.168.5.1</name> <metric>400</metric> </next-hop> </route>
The server might respond:
HTTP/1.1 204
The response status 204 indicated successful update. Here we can see that a new next-hop entry has been added to the route.
Example 21.27. The "route" resource after update
GET /running/sys/routes/inet/route/10.30.3.0,24
<route xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <name>10.30.3.0</name> <prefix-length>24</prefix-length> <next-hop> <name>192.168.3.1</name> </next-hop> <next-hop> <name>192.168.4.2</name> </next-hop> <next-hop> <name>192.168.5.1</name> </next-hop> </route>
PATCH can not be used to create new resources directly, since it must operate on existing resources. PATCH, in contrast to PUT, only merges the provided payload with the existing configuration, which makes it possible to create child resources within the target resource. In this example we create a "multilink" presence container. The uri points to the existing parent resource for the presence container "multilink".
Example 21.28. Creating a "sys/interfaces/serial/ppp0/multilink" resource
PATCH /running/sys/interfaces/serial/ppp0 Content-Type: application/vnd.yang.data+xml
<serial> <multilink> <group>1</group> </multilink> </serial>
The server might respond:
HTTP/1.1 204
In this example we "populate" a non-presence container with subnode data. The URI points to the existing URI for the non-presence container "authentication", since a non-presence container always exist if its parent exist.
Example 21.29. Creating a "sys/interfaces/serial/ppp0/authentication" resource
PATCH /running/sys/interfaces/serial/ppp0/authentication Content-Type: application/vnd.yang.data+xml
<authentication> <method>eap</method> </authentication>
The server might respond:
HTTP/1.1 204
Have in mind that PATCH will merge the existing configuration with the provided payload. So in the example above, if the container "authentication" contained additional subnodes not present in the payload, they will remain in the resulting configuration. Below the node 'list-name' was not present in the payload, but is still present in the resulting configuration.
Example 21.30. The "authentication" resource after update
GET /running/sys/interfaces/serial/ppp0/authentication
<authentication xmlns="http://example.com/example-serial" xmlns:y="http://tail-f.com/ns/rest" xmlns:ex="http://example.com/example-serial" xmlns:r="http://example.com/router"> <method>eap</method> <list-name>foobar</list-name> </authentication>
For an ordered-by user list, the POST request can include the query parameters insert and resource.
The insert parameter indicated where the new element should be created. Legal values are:
first: insert on top of list.
last: insert on bottom of list.
before: insert before the element indicated by the resource parameter.
after: insert after the element indicated by the resource parameter.
The resource parameter contains the uri to an existing element in the list.
So we verify this functionality by first retrieving the "sys/dns/server" list:
Example 21.31. The "sys/dns/server" list before insert
GET /running/sys/dns
<dns xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <server> <address>10.2.3.4</address> </server> </dns>
We now insert a new element before the existing element:
Example 21.32. Insert=before in the "sys/dns/server" list
POST /running/sys/dns?insert=before&resource=/api/running/sys/dns/server/10 .2.3.4 Content-Type: application/vnd.yang.data+xml
<server> <address>10.1.1.2</address> </server>
The server might respond:
HTTP/1.1 201
We get a 201 status when successful and we verify the result by retreiving the list once again:
Example 21.33. The "sys/dns/server" list after insert
GET /running/sys/dns
<dns xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <server> <address>10.1.1.2</address> </server> <server> <address>10.2.3.4</address> </server> </dns>
To invoke an operation, use the POST method. The message body (if any) is processed as the operation input parameters.
Example 21.34. An "archive-log" action request example
The following yang model snippet shows the definition of the action archive-log:
grouping syslog { list server { key "name"; leaf name { type inet:host; } leaf enabled { type boolean; } list selector { key "name"; leaf name { type int32; } leaf negate { type boolean; } leaf comparison { type enumeration { enum "same-or-higher"; enum "same"; } } leaf level { type syslogLevel; } leaf-list facility { type syslogFacility; min-elements 1; max-elements "8"; } } leaf administrator { type string; tailf:hidden maint; } tailf:action archive-log { tailf:exec "./scripts/archive-log"; input { leaf archive-path { type string; } leaf compress { type boolean; } } output { leaf result { type string; } } } } }
The action is invoked using the following URI. Note, the _operations tag that indicates the action invocation:
POST /running/sys/syslog/server/10.3.4.5/ operations/archive-log Content-Type: application/vnd.yang.data+xml
<input> <archive-path>/tmp</archive-path> <compress>false</compress> </input>
The server might respond:
<output xmlns='http://example.com/router'> <result>success</result> </output>
If the POST method succeeds, a "200 OK" Status-Line is returned if there is a response message body, and a "204 No Content" Status-Line is returned if there is no response message body.
If the user is not authorized to invoke the target operation, an error response containing a "403 Forbidden" Status-Line is returned to the client.
A delete removes all data in the subtree under a resource. In this example we remove the complete "sys/interfaces/ex:serial" list.
Example 21.35. delete the "sys/interfaces/ex:serial" list
DELETE /running/sys/interfaces/ex:serial
The server might respond:
HTTP/1.1 204
The response status 204 indicates success. If we retrieve the "sys/interfaces" resource we can verify that the "ex:serial" list is removed.
Example 21.36. The "sys/interfaces" resource after delete
GET /running/sys/interfaces
<interfaces xmlns="http://example.com/router" xmlns:y="http://tail-f.com/ns/rest" xmlns:r="http://example.com/router"> <interface> <name>eth0</name> </interface> </interfaces>
Whenever a change is made and a rollback resource is created we can set a label and a comment for that change. If we used that together with the DELETE above, we could have used this command instead.
Example 21.37. delete the "sys/interfaces/ex:serial" list with rollback label and comment
DELETE /running/sys/interfaces/ex:serial?rollback-comment=remove%20subtree&ro
llback-label=delete
The RESTCONF protocol operates on a hierarchy of resources, starting with the top-level API resource itself. Each resource represents a manageable component within the device.
A resource can be considered a collection of conceptual data and the set of allowed methods on that data. It can contain child nodes that are nested resources. The child resource types and methods allowed on them are data-model specific.
A resource has its own media type identifier, represented by the
Content-Type
header in the HTTP response message.
A resource can contain zero or more nested resources.
A resource can be created and deleted independently of its parent
resource, as long as the parent resource exists.
The RESTCONF resources are accessed via a set of URIs defined in this document. The set of YANG modules supported by the server will determine the additional data model specific operations, top-level data node resources, and notification event messages supported by the server.
The resources used in the RESTCONF protocol are identified by the "path" component in the request URI, see Example 21.2, “Request URI structure”. Each operation is performed on a target resource.
The RESTCONF protocol defines some application specific media types to identify each of the available resource types. The following resource types are defined in the REST API:
Table 21.3. Resources and their Media Types
Resource | Media Type |
---|---|
API | application/vnd.yang.api |
Datastore | application/vnd.yang.datastore |
Data | application/vnd.yang.data |
Operation | application/vnd.yang.operation |
The REST API does not support all of the datastores
defined in RESTCONF, neither does it use the same media types.
THIS DIFFERS FROM RESTCONF!
A resource is represented in XML as an XML element, with an XML attribute "y:self" that contains the URI for the resource. In the XML representation, every resource has an XML attribute:
y:self="..."
Leafs are properties of the resource. They are encoded in XML as elements.
XML namespaces must be used whenever there are multiple sibling nodes with the same local name. This only happens if a YANG module augments a node with the same name as another node in the same container or list. XML namespaces MAY always be used, even if there are no risk of a conflict.
In the JSON representation, this URI is encoded as:
"_self": "..."
In the representation of a list resource, the keys are always present, and encoded first.
JSON doesn't have anything similar to XML namespaces. However, we have adopted the notion defined in the Internet Draft: "Modeling JSON Text with YANG", where a <yang-module-name>:<tag> tag name is used in the JSON data to indicate the namespace. Note that it is only when the namespace changes that this notion is used.
Example 21.38. Namespaces in JSON
... "foo": [ { "_self": "/api/operational/x/foo/1", "id": 1, // Note: the 'card' container exist in a different namespace // compared to its parent 'foo' list element. The // 'a' is the Yang module name where 'card' is defined "a:card": { "_self": "/api/operational/x/foo/1/a:card", "id": 1 } ...
The top-level resource has the media type "application/vnd.yang.api+xml" or "application/vnd.yang.api+json". It is accessible through the well-known URI "/api".
This resource has the following fields:
Table 21.4. Fields of the /api resource
Field | Description |
---|---|
version | The version of the REST api. |
config | Link to the "config" resource. |
running | Link to the "running" resource. |
startup | Link to the "startup" resource. |
candidate | Link to the "candidate" resource. |
operational | Link to the "operational" resource. |
operations | Container for available operations (i.e: YANG rpc statements). |
rollbacks | Container for available rollback files. |
In XML, this resource is represented as an XML document with the document root element "y:api", underneath which all the resource's fields are represented as subelements.
In JSON, this resource is represented as a JSON object.
Supported HTTP methods: GET, HEAD, OPTIONS.
The "config" resource represents a unified configuration datastore, used to simplify resource management for the client. The underlying NETCONF datastores are used to implement the unified datastore, but the clients do not have to care about which underlying datastore to used.
The server hides all NETCONF datastore details for edit operations, such as the :candidate and :startup capabilities. When a client writes to this resource, the server performs the edits in the datastores used; if the candidate is enabled, the changes are written to the candidate, and then the candidate is committed; if the startup is enabled, the changes are written to running and running is copied to startup.
The media type of this resource is either "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
This resource resembles the RESTCONF "Datastore Resource" except that it does not contain operational data. RESTCONF differs from the REST API as it does not have separate NETCONF datastores for "running", "candidate" and "startup".
The "running" resource represents the running configuration datastore, and is present on all devices. Not all devices support direct modification to this resource.
The media type of this resource is either "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
The "startup" resource represents the startup configuration datastore. Not all devices support this resource.
The media type of this resource is "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
The "candidate" resource represents the candidate configuration datastore. Not all devices support this resource.
The media type of this resource is "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
The "operational" read-only resource represents the state data as well as the config data on the device, and is present on all devices. Note that actions defined as config false also will show up in this resource.
The media type of this resource is "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
This resource resembles the RESTCONF "Datastore Resource" except that it is read-only.
The "transaction" resource represents a transaction datastore created by the JSON-RPC API. This allows REST API requests that read or write towards an existing transaction, without committing the transaction (this is left up to other entities e.g. the JSON-RPC API).
The media type of this resource is either "application/vnd.yang.datastore+xml" or "application/vnd.yang.datastore+json".
The "operations" container contains all operations defined with the "rpc" statement in the YANG models supported on the device.
The "rollbacks" container contains all available rollback files to be used to rollback the configuration state to an earlier incarnation.
The "logout" read-only resource is a meta-resource that always replies with 401 Unauthorized in order to aid scenarios where the REST API credentials are being cached by the HTTP client (e.g. a browser). Calling this resource will prompt for new credentials on subsequent requests to any other resource on the same realm.
In order to retrieve the representation of this resource, a client can send:
Note the use of the 'verbose' query parameter, see Section 21.2.10, “Query Parameters”.
Example 21.39. GET the /api resource
GET /api?verbose Application:vnd.yang.api+xml
The server might respond:
HTTP/1.1 200 OK Server: Date: Wed, 12 Aug 2015 11:38:23 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Content-Length: 288 Content-Type: application/vnd.yang.api+xml Vary: Accept-Encoding Pragma: no-cache <api xmlns="http://tail-f.com/ns/rest" xmlns:y="http://tail-f.com/ns/rest" y:self="/api"> <version>0.5</version> <config y:self="/api/config"/> <running y:self="/api/running"/> <operational y:self="/api/operational"/> <operations/> <rollbacks y:self="/api/rollbacks"/> </api>
The media types "application/vnd.yang.datastore+xml" and "application/vnd.yang.datastore+json" represent a complete datastore. All three configuration datastores defined by NETCONF are available, as the resources:
running
candidate
startup
operational (read-only)
The state data defined by NETCONF is
available as a read-only resource "operational".
THIS DIFFERS FROM RESTCONF!
A config datastore (i.e one of "running", "candidate","startup") resource has the following fields:
Table 21.5. Fields of the /api/<datastore> resource
Field | Description |
---|---|
operations | Container for available built-in operations. |
y:operations | Container for available user defined actions. |
<all top-level data models nodes> | Top-level nodes from the YANG models. |
The "operational" datastore contains both operational and config data; as well as the containers as shown in the table: Table 21.5, “Fields of the /api/<datastore> resource”.
The REST API has a number of built-in operations that may be applicable for a particular datastore, and if they are applicable they are included in the operations container as described below:
Table 21.6. Built in operations
Field | Description |
---|---|
commit | Link to the "commit" resource. |
copy-running-to-startup | Link to the "copy-running-to-startup" resource. |
discard-changes | Link to the "discard-changes" resource. |
lock | Link to the "lock" resource. |
validate | Link to the "validate" resource. |
In XML, the /api/<datastore> resource is represented as an XML document with the document root element "y:data", underneath which all the resource's fields are represented as subelements.
In JSON, this resource is represented as a JSON object.
The operations are described below, and all represented by the media type "application/vnd.yang.operation", see Section 21.3.3, “Invoke Operations”.
In order to access a datastore through a lock, the client needs to POST to the "lock" resource. If the server is able to lock the datastore, the POST request succeeds with the status code "201 Created", and the "Location" HTTP header contains the URI to a newly created resource representing the locked datastore. To unlock the datastore, the client deletes the newly created resource using the HTTP method DELETE.
The "config" resource cannot be locked.
In the representation of a locked datastore, the "lock" operation is not available.
In order to facilitate recovery from failing clients with outstanding locks, the REST server deletes the resource representing the lock after some time of inactivity.
The media type of this resource is "application/vnd.yang.operation+xml".
In the representation of the candidate datastore, the "commit" operation is present, and can be POSTed to by the client to commit candidate to running, as described in section 8.3 in RFC 6241.
The media type of this resource is "application/vnd.yang.operation".
In the representation of the running datastore, the "copy-running-to-startup" operation is present, if the server also supports the startup datastore, and can be POSTed to by the client to copy the contents of running to startup, as described in section 8.7 in RFC 6241.
The media type of this resource is "application/vnd.yang.operation".
In the representation of the candidate datastore, the "discard-changes" operation is present, and can be POSTed to by the client to revert the candidate to the current running configuration, as described in section 8.3.4.2 in RFC 6241.
The media type of this resource is "application/vnd.yang.operation".
In the representation of the candidate datastore, the "validate" operation is present, and can be POSTed to by the client to validate the contents of the candidate, as described in section 8.6.4.1 in RFC 6241.
The media type of this resource is "application/vnd.yang.operation".
The examples in this section could instead have been performed using JSON specific mime types such as "application/vnd.yang.api+json", "application/vnd.yang.datastore+json" and "application/vnd.yang.data+json".
To retrieve a representation of the running datastore in XML format, a client can send:
(Note the use of the 'verbose' query parameter, see Section 21.2.10, “Query Parameters”)
Example 21.40. GET the /api/running resource
GET /api/running?verbose Application:vnd.yang.api+xml
The server might respond:
HTTP/1.1 200 OK Server: Date: Wed, 12 Aug 2015 11:38:23 GMT Last-Modified: Wed, 12 Aug 2015 11:38:23 GMT Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Etag: 1439-379503-31029 Content-Type: application/vnd.yang.datastore+xml Transfer-Encoding: chunked Pragma: no-cache <data xmlns:y="http://tail-f.com/ns/rest" y:self="/api/running"> <sys xmlns="http://example.com/router" y:self="/api/running/sys"> ... </sys> ... <operations> <lock y:self="/api/running/_lock">/api/running/_lock</lock> <rollback y:self="/api/running/_rollback">/api/running/_rollback</rollback> </operations> </data>
To copy running to startup, a client can send:
POST /api/running/_copy-running-to-startup Host: example.com Accept: application/vnd.yang.operations+xml
Note that any action defined as "config false" will only show up under the /api/operational datastore.
Example 21.41. Action in /api/operational
... <blaha xmlns="http://example.com/ns/ktm" y:self="/api/operational/blaha"> <y:operations y:path="/blaha"> <bar y:self="/api/operational/blaha/_operations/bar"/> <!-- NOTE: this action is defined as 'config false' and hence, shows up under the 'operational' datastore --> </y:operations> </blaha> ...
By default, all top-level objects, list entries, and containers are resources. Any resource derived from a YANG module is represented with the media type "application/vnd.yang.data+xml".
When such a resource is retrieved, a "path" property is included in its representation (a "y:path" attribute in XML). The value of this property is the resource's instance-identifier.
Supported HTTP methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT.
Note that resources representing non-presence containers cannot be deleted, and thus they do not support the DELETE method.
Refer to Section 21.3, “Resource Examples” for examples of how to operate on "application/vnd.yang.data+xml".
YANG-defined operations, defined with the YANG statements "rpc" or "tailf:action", and the built-in operations, are represented with the media type "application/vnd.yang.operation".
Resources of this type accept only the method "POST".
In XML, such resources are encoded as subelements to the XML element "y:operations". In JSON, they are encoded under "_operations".
If an operation does not require any parameters, the POST message has no body. If the client wishes to send parameters to the operation, they are encoded as an XML document with the document element "input".
If an operation does not produce any output, the HTTP response code is 204 (No Content). If it produces output, the HTTP response code is 200 (OK), and the output of the operation is encoded as an XML document with the document element "output".
Supported HTTP methods: POST
The rollback resource can be accessed from the top level "/api/rollbacks" resource as described above, and a rollback file can be applied to any database.
In order to list available a rollback files, a client can send:
Example 21.42. GET rollback files information
GET /api/rollbacks Application:vnd.yang.api+xml
The server might respond:
<rollbacks xmlns="http://tail-f.com/ns/rest" xmlns:y="http://tail-f.com/ns/rest"> <file> <name>0</name> <creator>admin</creator> <date>2015-08-12 13:38:23</date> <via>rest</via> <label></label> <comment></comment> </file> <file> <name>1</name> <creator>admin</creator> <date>2015-08-12 13:38:22</date> <via>rest</via> <label></label> <comment></comment> </file> </rollbacks>
Note how each rollback file is represented as separate resources, e.g. "/api/rollbacks/0". We can also see how the 'rollback-label' and 'rollback-comment' is used in "/api/rollbacks/0/label" and "/api/rollbacks/0/comment". These resources can be inspected individually and a client can send:
Example 21.43. GET rollback file content
GET /api/rollbacks/0 Application:vnd.yang.api+xml
The server might respond:
# Created by: admin # Date: 2015-08-12 13:38:23 # Via: rest # Type: delta # Label: # Comment: # No: 10016 sys { interfaces { serial ppp0 { ppp { accounting acme; } authentication { method eap; list-name foobar; } authorization admin; multilink { group 1; } } } }
The payload is in the same curly bracket rollback format as used in the NETCONF, CLI and Web UI agents.
To apply a rollback file to a database use the appropriate "rollback" resource/operation in the datastore of your choice:
Example 21.44. Find and use the rollback operation resource
GET /api/running Application:vnd.yang.datastore+xml
The server might respond:
<data xmlns:y="http://tail-f.com/ns/rest"> <sys xmlns="http://example.com/router"> ... </sys> ... <operations> <lock>/api/running/_lock</lock> <rollback>/api/running/_rollback</rollback> </operations> </data>
Note the "/api/running/_rollback" resource operation.
POST an appropriate rollback file name to the "/api/running/_rollback" resource operation to apply it:
POST /api/running/_rollback Content-Type: application/vnd.yang.data+xml <file>0</file>
The server might respond:
HTTP/1.1 204
As described in Chapter 8, Configuration Meta-Data it is possible to associate meta-data with the configuration data. For REST, resources such as containers, lists as well as leafs and leaf-lists can have such meta-data. For XML, this meta-data is represented as attributes, attached to the XML element in question. For JSON, there does not exist a natural way to represent this info. Hence we have introduced a special notation, see the example below.
Example 21.45. XML representation of meta-data
<x xmlns="urn:x" y:self="/api/running/x" xmlns:y="http://tail-f.com/ns/rest" xmlns:x="urn:x" y:path="/x:x"> <id tags=" important ethernet " annotation="hello world">42</id> <person y:self="/api/running/x/person" annotation="This is a person"> <name>Bill</name> <person annotation="This is another person">grandma</person> </person> </x>
Example 21.46. JSON representation of meta-data
{ "x": { "_self": "/api/running/x", "_path": "/x:x", "id": 42, "@id": {"tags": ["important","ethernet"],"annotation": "hello world"}, "person": { "_self": "/api/running/x/person", // NB: the below refers to the parent object "@@person": {"annotation": "This is a person"}, "name": "Bill", "person": "grandma", // NB: the below refers to the sibling object "@person": {"annotation": "This is another person"} } } }
For JSON, note how we represent the meta data for a certain object "x" by another object constructed of the object name prefixed with either one or two "@" signs. The meta-data object "@x" refers to the sibling object "x" and the "@@x" object refers to the parent object.
THIS DIFFERS FROM RESTCONF!
There are some optional request and response headers that are of interest
since some functionality is obtained by their use.
We here focus on the Etag
and
Last-Modified
response headers, and the request
headers that are correlated to these (If-Match
,
If-None-Match
, If-Modified-Since
and If-Unmodified-since
).
Etag
: This header (entity-tag) is a string
representing the latest transaction id in the
database. This header is only available for the
"running" resource or equivalent.
Last-Modified
: This header contains the
timestamp for the last change in the database.
This header is only available for the
"running" resource or equivalent. Also, this
header is only available if rollback files are enabled.
If-None-Match
: This header evaluates to true
if the supplied value does not match the latest
Etag
value. If evaluated to false an error
response with status 304 (Not Modified) will be sent with
no body. The usage of this is for instance for a GET
operation to get information if the data has changed since
last retrieval. This header carry only meaning if the
Etag
response header has previously been
acquired.
If-Modified-Since
: This request-header field
is used with a HTTP method to make it conditional, i.e
if the requested variant has not been modified since the
time specified in this field, an entity will not be returned
from the server; instead, a 304 (Not Modified) response
will be returned without any message-body.
Usage of this is for instance for a GET
operation to get information if (and only if) the data has
changed since last retrieval.
Thus, this header should use the value of a
Last-Modified
response header that has previously
been acquired.
If-Match
: This header evaluates to true
if the supplied value matches the latest
Etag
value. If evaluated to false an error
response with status 412 (Precondition Failed) will be sent with
no body.
The usage of this can be to control if a POST
operation should be executed or not (i.e. do not execute
if the database has changed).
This header carry only meaning if the
Etag
response header has previously been
acquired.
If-Unmodified-Since
: This header evaluates to true
if the supplied value is later or equal to the last acquired
Last-Modified
timestamp.
If evaluated to false an error
response with status 412 (Precondition Failed) will be sent with
no body.
The usage of this can be to control if a POST
operation should be executed or not (i.e. do not execute
if the database has changed).
This header carry only meaning if the
Last-Modified
response header has previously been
acquired.
When setting or retrieving data it is sometimes necessary to represent special characters in the payload. In the REST api the payload can have both XML and JSON format. The special characters handled in the REST api are:
"new line": representing a line feed (decimal ascii value 10)
"carriage return": representing carriage return (decimal ascii value 13)
"horizontal tab": representing a tabulation (decimal ascii value 9)
The ambition in the REST api is that special characters should be handled in the same way as they are in the CLI. Since the CLI is capable to present configuration data both as strings and XML the CLI representation can be used as template for both the XML and JSON format.
When in the XML case the special characters uses the representation &#xH; where H is the ascii hex value or &#DD; where DD is the ascii decimal value. Since "&" is the quoting character this is also treated as a special character, and so is "<" since this is the separator character indicating the beginning of a tag value. The following is the list of XML special characters:
"new line": represented by 
 or
"carriage return": represented by 
 or
"horizontal tab": represented by 	 or 	
"&": represented by &
"<": represented by <
An example XML fragment is
<foo>123\n456\\n <&></foo> which is interpreted as: 123 456\n <&>
In the JSON formatted string case the quote character "\" and string separator characters are used "\"" which also becomes special characters in the string case. The complete list of JSON special characters become:
"new line": represented by \n
"carriage return": represented by \r
"horizontal tab": represented by \t
"\": represented by \ or \\
""": represented by \"
The \ quote character is used in the following way: A single \ in a string that is not directly followed by characters n,r or t are unaltered (as a \ character). Two \\ are interpreted as \ (the escaped \). This implies that both \\\ and \\\\ are are interpreted as \\ and so forth.
An example JSON string is
"123\n456\\n \\\" which is interpreted as: 123 456\n \\
Error responses are formatted either in XML and JSON depending on the preferred MIME type in the request.
In your installed release you should be able to find a Yang
file named tailf-rest-error.yang
that
defines the structure of these error replies. An easy way
to find the file run, from the top directory of your
installation:
find . -name tailf-rest-error.yang
Let's start by looking at an example of how a structured error reply can look like.
Example 21.47. Example of a XML formatted error message
curl -i -X PUT "localhost:8008/api/running/hosts" -H "Content-Type: application/vnd.yang.data+xml" ... HTTP/1.1 500 Internal Server Error Server: ConfD/5.3.0 Cache-control: private, no-cache, must-revalidate, proxy-revalidate Date: Thu, 10 Jul 2014 07:59:04 GMT Content-Length: 259 Content-Type: text/xml <errors xmlns="http://tail-f.com/ns/tailf-rest-error"> <error> <error-tag>operation-failed</error-tag> <error-urlpath>/api/running/hosts</error-urlpath> <error-message>internal error</error-message> </error> </errors>
Example 21.48. Example of a JSON formatted error message
curl -i -X POST "localhost:8008/api/running/hosts2" \ -H "Content-Type: application/vnd.yang.data+json" ... HTTP/1.1 400 Bad Request Server: ConfD/5.3.0 Cache-control: private, no-cache, must-revalidate, proxy-revalidate Date: Thu, 10 Jul 2014 09:11:13 GMT Content-Length: 199 Content-Type: text/json {"errors": {"error": [{ "error-message": "unexpected trailing data: hosts", "error-urlpath": "/api/running/hosts", "error-tag": "malformed-message" } ] } }
The YANG model for the error messages is taken from the NETCONF specification. However, note that the REST API currently only sets three values when reporting an error:
"error-tag": An error classification tag.
"error-urlpath": The URI used by the error generating request.
"error-message": A descriptive error message.
The error-tag element has a number of predefined values and there is also a preferred HTTP status code connected to each error-tag. These are:
"in-use": HTTP Status: 409 Conflict
"invalid-value": HTTP Status: 400 Bad Request
"too-big": HTTP Status: 413 Request Entity Too Large
"missing-attribute": HTTP Status: 400 Bad Request
"bad-attribute": HTTP Status: 400 Bad Request
"unknown-attribute": HTTP Status: 400 Bad Request
"bad-element": HTTP Status: 400 Bad Request
"unknown-element": HTTP Status: 400 Bad Request
"unknown-namespace": HTTP Status: 400 Bad Request
"access-denied": HTTP Status: 403 Forbidden
"lock-denied": HTTP Status: 409 Conflict
"resource-denied": HTTP Status: 409 Conflict
"rollback-failed":
HTTP Status: 500 Internal Server Error
"data-exists": HTTP Status: 409 Conflict
"data-missing": HTTP Status: 409 Conflict
"operation-not-supported":
HTTP Status: 501 Not Implemented
"operation-failed":
HTTP Status: 500 Internal Server Error
"partial-operation":
HTTP Status: 500 Internal Server Error
"malformed-message": HTTP Status: 400 Bad Request
For data providers, hooks, transforms etc there exist a
possibility to add extensions to error messages and change
error codes before sending errors back to the server from the
callbacks (see: Section 28.14, “Error Message Customization”).
These codes and error messages will also be visible
over the REST interface. More on how to use these options can
be found in the confd_lib_dp(3)
man page, e.g under
the confd_db_seterr_extended
function,
or in the Javadoc for
the com.tailf.dp.DpCallbackExtendedException
class.
Using the above mechanism to change the errorcode for an emitted error, will have effect on the REST HTTP response statuses. The following table show their relationship:
Table 21.7. Error code vs HTTP Status
Error Code | HTTP Status |
---|---|
CONFD_ERRCODE_IN_USE | 409 Conflict |
CONFD_ERRCODE_RESOURCE_DENIED | 409 Conflict |
CONFD_ERRCODE_INCONSISTENT_VALUE | 400 Bad Request |
CONFD_ERRCODE_ACCESS_DENIED | 403 Forbidden |
CONFD_ERRCODE_APPLICATION | 400 Bad Request |
CONFD_ERRCODE_APPLICATION_INTERNAL | 500 Internal Server Error |
CONFD_ERRCODE_DATA_MISSING | 409 Conflict |
CONFD_ERRCODE_INTERRUPT | 500 Internal Server Error |
The Query API consists of a number of Requests and Replies which are sent as payload via the (REST) HTTP connection.
In your installed release you should be able to find two YANG
files named tailf-rest-query.yang
and
tailf-common-query.yang
that defines the
structure of these Requests / Replies. An easy way to find the
files is to run the following command from the top directory of
your installation:
find . -name tailf-rest-query.yang
The API consists of the following Requests:
"start-query": Start a query and return a query handle.
"fetch-query-result": Use a query handle to repeatedly fetch chunks of the result.
"reset-query": (Re)set where the next fetched result will begin from.
"stop-query": Stop (and close) the query.
The API consists of the following Replies:
"start-query-result": Reply to the start-query request
"query-result": Reply to the fetch-query-result request
In the following examples, we'll use this data model:
container x { list host { key number; leaf number { type int32; } leaf enabled { type boolean; } leaf name { type string; } leaf address { type inet:ip-address; } } }
The actual format of the payload should be represented either in XML or JSON. For XML it could look like this:
<start-query xmlns="http://tail-f.com/ns/tailf-rest-query"> <foreach> /x/host[enabled = 'true'] </foreach> <select> <label>Host name</label> <expression>name</expression> <result-type>string</result-type> </select> <select> <expression>address</expression> <result-type>string</result-type> </select> <sort-by>name</sort-by> <limit>100</limit> <offset>1</offset> </start-query>
An informal interpretation of this query is:
For each '/x/host' where 'enabled' is true, select its 'name', and 'address', and return the result sorted by 'name', in chunks of 100 results at the time.
Let us discuss the various pieces of this request. To start with, when using XML, we need to specify the name space as shown:
<start-query xmlns="http://tail-f.com/ns/tailf-rest-query">
The actual XPath query to run is specified by the 'foreach' element. In the example below will search for all '/x/host' nodes that has the 'enabled' node set to 'true':
<foreach> /x/host[enabled = 'true'] </foreach>
Now we need to define what we want to have returned from the node set by using one or more 'select' sections. What to actually return is defined by the XPath 'expression'.
Choose how the result should be represented. Basically, it can be the actual value or the path leading to the value. This is specified per select chunk The possible result-types are: 'string' , 'path' , 'leaf-value' and 'inline'.
The difference between 'string' and 'leaf-value' is somewhat subtle. In the case of 'string' the result will be processed by the XPath function: string() (which if the result is a node-set will concatenate all the values). The 'leaf-value' will return the value of the first node in the result. As long as the result is a leaf node, 'string' and 'leaf-value' will return the same result. In the example above, the 'string' is used as shown below. At least one result-type must be specified.
The result-type 'inline' makes it possible to return the full sub-tree of data, either in XML or in JSON format. The data will be enclosed with a tag: 'data'.
It is possible to specify an optional 'label' for a convenient way of labeling the returned data:
<select> <label>Host name</label> <expression>name</expression> <result-type>string</result-type> </select> <select> <expression>address</expression> <result-type>string</result-type> </select>
The returned result can be sorted. This is expressed as XPath expressions, which in most cases are very simple and refers to the found node set. In this example we sort the result by the content of the 'name' node:
<sort-by>name</sort-by>
To limit the max amount of results in each chunk that 'fetch-query-result' will return we can set the 'limit' element. The default is to get all results in one chunk.
<limit>100</limit>
With the 'offset' element we can specify at which node we should start to receive the result. The default is 1, i.e., the first node in the resulting node-set.
<offset>1</offset>
This request, expressed in JSON, would look like this:
{ "start-query": { "foreach": "/x/host[enabled = 'true']", "select": [ { "label": "Host name", "expression": "name", "result-type": ["string"] }, { "expression": "address", "result-type": ["string"] } ], "sort-by": ["name"], "limit": 100, "offset": 1 } }
Now, if we continue by putting this XML example in a file
test.xml
we can send a request, using the
command 'curl', like this:
curl -i 'http://admin:admin@localhost:8008/api/query' \ -X POST -T test.xml \ -H "Content-Type: application/vnd.yang.data+xml"
The important parts of the above is the '/api/query' in the URI and that we send a HTTP 'POST' with the correct 'Content-Type'.
The result would look something like this:
<start-query-result> <query-handle>12345</query-handle> </start-query-result>
The query handle (in this example '12345') must be used in all subsequent calls. To retrieve the result, we can now send:
<fetch-query-result xmlns="http://tail-f.com/ns/tailf-rest-query"> <query-handle>12345</query-handle> </fetch-query-result>
Which will result in something like the following:
<query-result xmlns="http://tail-f.com/ns/tailf-rest-query"> <result> <select> <label>Host name</label> <value>One</value> </select> <select> <value>10.0.0.1</value> </select> </result> <result> <select> <label>Host name</label> <value>Three</value> </select> <select> <value>10.0.0.3</value> </select> </result> </query-result>
If we try to get more data with the 'fetch-query-result' we might get more 'result' entries in return until no more data exists and we get an empty query result back:
<query-result xmlns="http://tail-f.com/ns/tailf-rest-query"> </query-result>
If we want to go back in the "stream" of received data chunks and have them repeated, we can do that with the 'reset-query' request. In the example below we ask to get results from the 42:nd result entry:
<reset-query xmlns=\"http://tail-f.com/ns/tailf-rest-query\"> <query-handle>12345</query-handle> <offset>42</offset> </reset-query>
Finally, when we are done we stop the query:
<stop-query xmlns="http://tail-f.com/ns/tailf-rest-query"> <query-handle>12345</query-handle> </stop-query>
The REST server can be configured to reply with particular HTTP headers in the HTTP response. For example, to support Cross-Origin Resource Sharing (CORS, http://www.w3.org/TR/cors/) there is a need to add a couple of headers to the HTTP Response.
We add the extra configuration parameter in confd.conf
Example 21.49. ConfD configuration for REST
<rest> <enabled>true</enabled> <customHeaders> <header> <name>Access-Control-Allow-Origin</name> <value>*</value> </header> </customHeaders> </rest>
Example 21.50.
Send a request with Origin header:
OPTIONS .bob.com
A result can then look like
HTTP/1.1 200 OK Server: Allow: GET, HEAD Cache-Control: private, no-cache, must-revalidate, proxy-revalidate Content-Length: 0 Content-Type: text/html Access-Control-Allow-Origin: http://api.bob.com Pragma: no-cache
The REST server will return standard HTTP response codes, as described in the list below:
The request was successfully completed, and a response body is returned containing a representation of the resource.
A resource was created, and the new resource URI is returned in the "Location" header.
The request was successfully completed, but no response body is returned.
The request could not be processed because it contains missing or invalid information (such as validation error on an input field, a missing required value, and so on).
The request requires user authentication. The response includes a "WWW-Authenticate" header field for basic authentication.
Access to the resource was denied by the server, due to authorization rules.
The requested resource does not exist.
The HTTP method specified in the request (DELETE, GET, HEAD, PATCH, POST, PUT) is not supported for this resource.
The resource identified by this request is not capable of generating the requested representation, specified in the "Accept" header or in the "format" query parameter.
This code is used if a request tries to create a resource that already exists.
The format of the request is not supported.
The server encountered an unexpected condition which prevented it from fulfilling the request.
The server does not (currently) support the functionality required to fulfill the request.
The server is currently unable to handle the request due to the resource being used by someone else, or the server is temporarily overloaded.