#
UniConfig - Sending and receiving data (RESTCONF)
The RESTCONF protocol is described in RFC 8040. Put simply, RESTCONF represents a REST API for accessing datastores and UniConfig operations.
#
Datastores
There are two datastores:
- Config: Contains data representing the intended state. Possible to read and write via RESTCONF.
- Operational: Contains data representing the actual state. Possible only to read via RESTCONF.
Each request must start with the URI /rests/. By default, RESTCONF listens on port 8181 for HTTP requests.
#
REST Operations
RESTCONF supports: OPTIONS, GET, PUT, POST, PATCH, and DELETE operations. Request and response data can be either in the XML or JSON format.
- XML structures according to YANG are defined at: XML-YANG.
- JSON structures are defined at: JSON-YANG.
Data in the request must set the Content-Type field correctly in the HTTP header with the allowed value of the media type. The media type of the requested data must be set in the Accept field. Get the media types for each resource by calling the OPTIONS operation.
Most of the paths use Instance Identifier. <identifier> is used in the explanation of the operations and must adhere to these rules:
- Identifier must start with <moduleName>:<nodeName>> where <moduleName> is a name of the YANG module and <nodeName> is the name of a node in the module. If the next node name is placed in the same namespace as the previous one, it is sufficient to just use <nodeName> after the first definition of <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.
<nodeName> can represent a data node which is a list node, container, leaf, or leaf-list YANG built-in type. If the data node is a list, there must be defined ordered keys of the list behind the data node name, for example, <nodeName>=<valueOfKey1>,<valueOfKey2>. ..
The following example shows how reserved characters are percent-encoded within a key value. The value of "key1" contains a comma, single-quote, double-quote, colon, double-quote, and space (,'":" ). Note that double-quote is not a reserved character and does not need to be percent-encoded. The value of "key2" is the empty string, and the value of "key3" is the string "foo".
Example URL: /rests/data/example-top:top/list1=%2C%27"%3A"%20,,foo
- The format <moduleName>:<nodeName> has to be used in this case as well. Module A has node A1. Module B augments node A1 by adding node X. Module C augments node A1 by adding node X. For clarity, it has to be known which node is X (for example: C:X).
#
Mount point
The purpose of yang-ext:mount container is to access southbound mountpoint, when the node is already installed in Uniconfig (After install-node RPC). It exposes operations for reading device data which can only be done under connection-specific topology (cli/netconf) with defined node-id in URI. In this case, the URI has to be in the format <identifier>/yang-ext:mount/<identifier>. The first <identifier> is the path to a mount point and the second <identifier> is the path to subtree behind the mount point. An URI can end in a mount point itself by using <identifier>/yang-ext:mount. In this case, if there is no content parameter, whole operational and configuration data will be read.
Examples of retrieving data behind yang-ext:mount
In this request, we are using parameter content=config, this means we are reading candidate NETCONF datastore. Value config of parameter content is translated into get-config NETCONF RPC.
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=cli/node=R1/yang-ext:mount?content=config' \
--header 'Accept: application/json'
{
"frinx-oam:oam": {
...
},
"frinx-evpn:evpn": {
...
},
"frinx-openconfig-interfaces:interfaces": {
"interface": [
{
"name": "Loopback123",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"description": "commitbadconfig",
"name": "Loopback123"
}
}
...
]
},
"frinx-logging:logging": {
...
},
"frinx-snmp:snmp": {
...
},
"frinx-openconfig-network-instance:network-instances": {
"network-instance": [
{
"name": "default",
"protocols": {
"protocol": [
{
"identifier": "frinx-openconfig-policy-types:STATIC",
"name": "default",
"config": {
"identifier": "frinx-openconfig-policy-types:STATIC",
"name": "default"
}
}
]
},
"config": {
"name": "default",
"type": "frinx-openconfig-network-instance-types:DEFAULT_INSTANCE"
}
}
]
}
}
In this request we are using parameter content=nonconfig, which means that we are reading running NETCONF datastore. Value nonconfig is translated into get NETCONF RPC. We can compare it with data directly from device using show running-config command.
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=cli/node=R1/yang-ext:mount?content=nonconfig' \
--header 'Accept: application/json'
{
"frinx-openconfig-platform:components": {
"component": [
{
"name": "OS",
"state": {
"software-version": "5.3.4[Default]",
"name": "OS",
"id": "IOS XR"
},
"config": {
"name": "OS"
}
}
]
},
"frinx-oam:oam": {
"cfm": {
"config": {
"enabled": false
}
}
},
"frinx-configuration-metadata:configuration-metadata": {
"last-configuration-fingerprint": "Wed Sep 29 09:55:23 2021"
},
"frinx-evpn:evpn": {
...
},
"frinx-openconfig-interfaces:interfaces": {
...
},
"frinx-openconfig-lldp:lldp": {
"config": {
"system-name": "XR5"
}
},
"frinx-logging:logging": {
...
},
"frinx-snmp:snmp": {
...
},
"frinx-openconfig-network-instance:network-instances": {
...
}
}
Examples of invocation of yang actions behind yang-ext:mount.
curl --location --request POST 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=R1/yang-ext:mount/clear:clear/clear:statistics/vrrp/global' \
--header 'Accept: application/json'
{
}
Invocation of yang action -> List available firmware packages on disk
curl --location --request POST 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=R1/yang-ext:mount/system:system/firmware/list' \
--header 'Accept: application/json'
{
"output" : {
"packages" : [
{
"name" : "Name of the package"
}
...
]
}
}
Invocation of yang action -> Erase running-config-then load
curl --location --request POST 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=R1/yang-ext:mount/system:erase/running-config-then/load' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"file": "/home/admin/start_config.cfg"
}
}'
<output xmlns="namespace">
<status>Erasing config and restarting services</status>
</output>
To completely understand installing of node see Device installation.
#
HTTP methods
#
OPTIONS /rests
- Returns the XML description of the resources with the required request and response media types in Web Application Description Language (WADL).
#
GET /rests/data/<identifier>?content=config
- Returns a data node from the Config datastore.
- <identifier> points to a data node that must be retrieved.
- Value 'config' represents default value of content query parameter - it doesn't have to be specified, if user would like to read intended/uncommitted changes from Config datastore.
- Request GET '/rests/data/<identifier>' would return the same data.
#
GET /rests/data/<identifier>?content=nonconfig
- Returns the value of the data node from the Operational datastore.
- <identifier> points to a data node that must be retrieved.
#
GET /rests/data/<identifier>?content=all
- Returns a data node from both Config and Operational datastores. The outputs from both datastores are merged into one output.
- <identifier> points to a data node that must be retrieved.
#
PUT /rests/data/<identifier>
- Updates or creates data in the Config datastore and returns the state about success.
- <identifier> points to a data node that must be stored.
- Content type does not have to be specified in URI - it can only be the Configuration datastore.
curl --location --request PUT 'http://<uniconfig-ip>:8181/rests/data/module1:foo/bar' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '<bar>
…
</bar>'
curl --location --request PUT 'http://<uniconfig-ip>:8181/rests/data/module1:foo1/foo2/yang-ext:mount/module2:foo/bar' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '<bar>
…
</bar>'
#
POST /rests/data/<identifier>
- Creates the data if it does not exist in the Config datastore, and returns the state about success.
- <identifier> points to a data node where data must be stored.
- The root element of data must have the namespace (data is in XML) or module name (data is in JSON).
curl --location --request POST 'http://<uniconfig-ip>:8181/rests/data/<identifier>' \
--header 'Accept: application/xml' \
--header 'Content-Type: application/xml' \
--data-raw '<bar xmlns=“module2namespace”>
…
</bar>'
curl --location --request POST 'http://<uniconfig-ip>:8181/rests/data/module1:foo1/foo2/yang-ext:mount/module2:foo' \
--header 'Accept: application/xml' \
--header 'Content-Type: application/xml' \
--data-raw '<bar xmlns=“module2namespace”>
…
</bar>'
#
POST /rests/data
- Creates the data if it does not exist under data root.
- In the following example, the 'toaster' module is the root container in YANG (it doesn't have any parent). This example also makes it clear that URI doesn't contain 'toaster' node in comparison to a PUT request that must contain the name of the created node in URI.
curl --location --request POST 'http://localhost:8181/rests/data' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"toaster:toaster": {
"toaster:toasterManufacturer": "General Electric",
"toaster:toasterModelNumber": "123",
"toaster:toasterStatus": "up"
}
}'
#
DELETE /rests/data/<identifier>
- Removes the data node in the Config datastore and returns the state about success.
- <identifier> points to a data node that must be removed.
#
PATCH /rests/data/<identifier>
- The patch request merges the contents of the message-body with the target resource in the Configuration datastore (content-type query parameter is not specified).
- <identifier> points to a data node on which PATCH operations is invoked.
- This request is implemented by Plain PATCH functionality, see more details on the following page: RFC-8040 documentation - Plain PATCH operation.
- Plain patch can be used to create or update, but not delete, a child resource within the target resource. Any pre-existing data which is not explicitly overwritten will be preserved. This means that if you store a container, its child entities will also merge recursively.
The following example shows the PATCH request used for modification of Ethernet interface IP address and two connection settings. Note that other settings under system:system container are left untouched including other leaves under 'connection' container and 'ethernet' list item.
curl --location --request PATCH 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=n1/configuration/system:system' \
--header 'Content-Type: application/json' \
--data-raw '{
"system:system": {
"wan": {
"endpoint": {
"interfaces": {
"ethernet": [
{
"name": "ethernet1",
"inet": {
"address": "192.168.10.1/24"
}
}
]
}
}
},
"connection": {
"syn-flood-check": true,
"ack-flood-check": true
}
}
}'
#
PATCH /rests/data/<identifier>?apply-tags=true
- The patch request with parameter apply-tags=true allows to use tags.
- Tags allows us to use differrent operation for separate elements instead of merging whole content as without tags.
- The following tags are supported: merge, replace, delete, create and update.
- Usage of these tags are explained in Templates manager : here.
The following example shows PATCH request used for modification of interfaces on IOS XE device including creating, deleting, and replacing interface configuration.
curl --location --request PATCH 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=ASR920/configuration/interfaces?apply-tags=true' \
--header 'Content-Type: application/json' \
--data-raw '{
"frinx-openconfig-interfaces:interfaces": {
"interface": [
{
"name": "Loopback0",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback0"
},
"@": {
"operation": "delete"
}
},
{
"name": "Loopback1",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback1"
},
"@": {
"operation": "replace"
}
},
{
"name": "Loopback2",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback2"
},
"@": {
"operation": "create"
}
},
{
"name": "Loopback61",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": false,
"name": "Loopback61"
}
},
{
"name": "GigabitEthernet1",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"name": "GigabitEthernet1"
},
"subinterfaces": {
"subinterface": [
{
"index": 0,
"frinx-openconfig-if-ip:ipv4": {
"addresses": {
"address": [
{
"ip": "192.168.1.253",
"config": {
"prefix-length": 24,
"ip": "192.168.1.253"
}
}
]
}
}
}
]
}
},
{
"name": "GigabitEthernet2",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": false,
"name": "GigabitEthernet2"
}
}
]
}
}'
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=ASR920/configuration/interfaces
{
"frinx-openconfig-interfaces:interfaces": {
"interface": [
{
"name": "Loopback0",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback0"
}
},
{
"name": "Loopback1",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": false,
"name": "Loopback1"
}
},
{
"name": "Loopback61",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": false,
"name": "Loopback61"
}
},
{
"name": "GigabitEthernet1",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"name": "GigabitEthernet1"
},
"subinterfaces": {
"subinterface": [
{
"index": 0,
"frinx-openconfig-if-ip:ipv4": {
"addresses": {
"address": [
{
"ip": "192.168.1.253",
"config": {
"prefix-length": 24,
"ip": "192.168.1.253"
}
}
]
}
}
}
]
}
},
{
"name": "GigabitEthernet2",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": false,
"name": "GigabitEthernet2"
}
}
]
}
}
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=ASR920/configuration/interfaces
{
"frinx-openconfig-interfaces:interfaces": {
"interface": [
{
"name": "Loopback1",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback1"
}
},
{
"name": "Loopback61",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": false,
"name": "Loopback61"
}
},
{
"name": "Loopback2",
"config": {
"type": "iana-if-type:softwareLoopback",
"enabled": true,
"name": "Loopback2"
}
},
{
"name": "GigabitEthernet2",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": false,
"name": "GigabitEthernet2"
}
},
{
"name": "GigabitEthernet1",
"config": {
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"name": "GigabitEthernet1"
},
"subinterfaces": {
"subinterface": [
{
"index": 0,
"frinx-openconfig-if-ip:ipv4": {
"addresses": {
"address": [
{
"ip": "192.168.1.253",
"config": {
"prefix-length": 24,
"ip": "192.168.1.253"
}
}
]
}
}
}
]
}
}
]
}
}
#
POST /rests/operations/<moduleName>:<rpcName>
- Invokes RPC on the specified path.
- <moduleName>:<rpcName> - <moduleName> is the name of the module and <rpcName> is the name of the RPC in this module.
- The Root element of the data sent to RPC must have the name “input”.
- The result has the status code and optionally retrieved data having the root element “output”.
curl --location --request POST 'http://<uniconfig-ip>:8181/rests/operations/module1:fooRpc' \
--header 'Content-Type: application/xml' \
--header 'Accept: application/xml' \
--data-raw '<input>
…
</input>'
The answer from the server could be:
<output>
…
</output>
curl --location --request POST 'http://localhost:8181/rests/operations/toaster:make-toast' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"toaster:toasterDoneness": "10",
"toaster:toasterToastType": "wheat-bread"
}
}'
GET /rests/operations request can be used to retrieve all available RPCs that are registered in distribution.
More information is available in the RESTCONF RFC 8040.
#
POST /rests/data/<path-to-operation>
- Invokes action on the specified path in the data tree.
- Placeholder <path-to-operation> represents data path to operation definition that is specified under composite data schema node in YANG (only containers and lists may contain action definition).
- Content query parameter doesn't have to be specified (it will be ignored), action is represented equally in Operational and Config datastore.
- Both RFC-8040 (YANG 1.1) and TAIL-F actions are supported. TAIL-F actions can be placed in both YANG 1.0 and YANG 1.1 schemas. There aren't any differences in the invocation of these types of actions using RESTCONF API.
- The body of the action invocation request may contain a root 'input' container. If the action definition has no specified input container, it is not required to specify the body in the request.
- The response contains the status code and optionally retrieved data having the root element 'output'.
- Currently, FRINX UniConfig only supports invocation of actions under NETCONF mountpoint, <path-to-operation> must contain 'yang-ext:mount' container.
- Structure of 'input' and 'output' elements are the same as the structure of these containers when we invoke YANG RPC.
Assume the following YANG snippet with root container named 'interfaces':
container interfaces {
action compute-stats {
input {
leaf month {
type string;
}
leaf year {
type uint16;
}
leaf percentile {
type uint8;
}
}
output {
leaf avg-rx-kbps {
type uint32;
}
leaf avg-tx-kbps {
type uint32;
}
}
}
}
Invocation of the action named 'compute-stats' that is placed under the 'interfaces' container of NETCONF mountpoint:
curl --location --request POST 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=dev/yang-ext:mount/interfaces:interfaces/compute-stats' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"month": "April",
"year": 2018,
"percentile": 50
}
}'
{
"interfaces:output": {
"avg-rx-kbps": 14524,
"avg-tx-kbps": 47787
}
}
Difference between RPCs and actions: Actions are bound to a data tree and they can be placed under containers and lists (they cannot be specified as root entities in YANG schema). RPCs are not placed in the data tree and for this reason, they can only be specified as root entities in the YANG schema.
#
Selecting Data
For selecting and identifying data is good to use query parameter fields. This parameter has to be used only with the GET method. The response body is output filtered by field-expression as the value of fields parameter.
#
Fields
The response body is the output filtered by the field-expression as a value of the fields parameter.
The example of using the fields parameter: path?fields=field_expression
There are several rules, that need to be followed:
- For filtering more than one field of the same parent, ";" needs to be used. Example : path?fields=field1;field2, where field1 and field2 has the same parent, which is the very last part of the path.
- For nesting, "/" needs to be used. Example : path?fields=field1;pathField/field2, where field1 and field2 has not the same parent, but pathField is on the same level as field1.
-
- Third character "(" , ")" is used to specify sub-selectors.
- Example: path ? fields = pathField( field1; pathField2( field2; pathField3( field3 ) ) )
- This is a different approach to do nesting, however, the difference between "(" and "/" is that once we use "/" for specifying some field, we cannot identify another field from the upper layers.
- Example: path ? fields = pathField / field1; pathField2 / field2
- This is the case where pathField1 and pathField2 have the same parent, this is not allowed, because once we use ";" it is expected to specify fields on the same layer as field1
Examples: With 2 approaches (nesting, sub-selecting)
Example of filtering the entire configuration of all interfaces (name, with the config):
**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface)&content=nonconfig
**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config&content=nonconfig
Example of filtering all names of interfaces and all names of configs of interfaces:
**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface(name;config(name)))&content=nonconfig
**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config/name&content=nonconfig
Example of filtering all names of interfaces with type from the config of interfaces:
**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface(name;config(type)))&content=nonconfig
**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config/type&content=nonconfig
#
Data filtering
To filter data based on specific values, use the jsonb-filter
query parameter.
This parameter can only be used with the GET
method.
For more information, see JSONB filtering.
#
Pagination
To filter data even further, you can use pagination with the GET
method.
There are three parameters related to pagination, which can be combined or used individually:
offset
- The starting point in a list for data rendering, based on the index list of entry values. Indexing begins at 0, so thatoffset=2
would start rendering at the third entry. The specified offset index entry is included in the output.limit
- The number of node values displayed in aGET
request. Specifies the maximum count for entries included in the response, starting with the entry defined by theoffset
parameter (if provided).fetch=count
- Retrieves the total number of child nodes under a specific node. Instead of returning node values, provides a count of how many child nodes exist under the specified node.
Note that pagination only works for list nodes.
Example - Using one individual pagination parameter:
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?offset=1
Example - Using two pagination parameters simultaneously:
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?offset=1&limit=4
Example - Using the fetch=count
parameter:
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?fetch=count
Response body for the fetch=count
parameter with a path from the previous
example:
6
#
Sorting
This utility helps us to sort list data from GET request according to our needs in ascending or descending order.
To sort some data, use a query parameter called sortby that will include at least one identifier of child leaf and sort direction. The first part of the value represents leaf identifier, the second part enclosed in brackets represents sort direction ('asc' or 'desc'). If there are multiple leaves based on which sorting is done, they are separated by semicolon.
Sorting, just like pagination, can only be used on list nodes.
The example of using sortby parameter with 1 value (sorting by the value of 'name' leaf):
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces/interface?sortby=name(asc)
The example of using sortby parameter with 2 values (sorting by values of 'name' and 'revision' leaves, in that order):
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces/interface?sortby=name(asc);revision(desc);
The example of using sortby and pagination simultaneously:
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces/interface?offset=1&limit=7&sortby=name(asc);type(asc);
It is possible to specify module-name as part of the leaf identifier. Module-name must be specified only if there are multiple children leaves with the same identifier but specified from different namespaces. Example:
http://127.0.0.1:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=dev1/configuration/ietf-netconf-acm:nacm/rule-list=oper/rule?sortby=tailf-acm:context(asc)
In the case of union types specified on leaf nodes, sorting is done in the blocks that are ordered by the following strategy:
- leaves without value
- empty type
- boolean type
- random numeric type
- types that can be represented by JSON string
#
Inserting
#
Insert query parameter
The 'insert' query parameter can be used to specify how an item should be inserted within an list or leaf-list. This parameter is only supported for the POST and PUT methods. It is also only supported if the target list or leaf-list is marked as 'ordered-by user' in YANG model.
The allowed values for 'insert' query parameter:
If the values 'before' or 'after' are used, then a 'point' query parameter for the 'insert' query parameter MUST also be present.
#
Point query parameter
The 'point' query parameter is used to specify the insertion point for an item that is being created or moved within an 'ordered-by user' list or leaf-list. Like the 'insert' query parameter, 'point' query parameter is only supported for the POST and PUT methods and also if the target list or leaf-list is marked as 'ordered-by user' in YANG model. The value of the 'point' query parameter is a string that indicates the key of the insertion point item. If the key is composite, the key items must be separated by a comma.
#
Examples
Next five examples show usage of 'insert' and 'point' query parameters for leaf-list. First example shows how leaf-list looks before update. There are no differences in the use of the list and leaf-list.
#
List before update
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=xr5/configuration/frinx-openconfig-qos:qos/classifiers/classifier=test/terms/term=1/conditions/frinx-qos-extension:qos-group?content=nonconfig`' \
--header 'Content-Type: application/json' \
--header 'Content-Type: content=nonconfig' \
--header 'Accept: application/json'
{
"frinx-qos-extension:qos-group": [
1,
2,
3
]
}
#
Insert item at the top of the list
curl --location --request PUT 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=xr5/configuration/frinx-openconfig-qos:qos/classifiers/classifier=test/terms/term=1/conditions/frinx-qos-extension:qos-group=4?insert=first' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"frinx-qos-extension:qos-group": [
4
]
}'
{
"frinx-qos-extension:qos-group": [
4,
1,
2,
3
]
}
#
Insert item at the bottom of the list
curl --location --request PUT 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=xr5/configuration/frinx-openconfig-qos:qos/classifiers/classifier=test/terms/term=1/conditions/frinx-qos-extension:qos-group=4?insert=last' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"frinx-qos-extension:qos-group": [
4
]
}'
{
"frinx-qos-extension:qos-group": [
1,
2,
3,
4
]
}
#
Insert item after specific item
curl --location --request PUT 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=xr5/configuration/frinx-openconfig-qos:qos/classifiers/classifier=test/terms/term=1/conditions/frinx-qos-extension:qos-group=4?insert=after&point=2' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"frinx-qos-extension:qos-group": [
4
]
}'
{
"frinx-qos-extension:qos-group": [
1,
2,
4,
3
]
}
#
Insert item before specific item
curl --location --request PUT 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=xr5/configuration/frinx-openconfig-qos:qos/classifiers/classifier=test/terms/term=1/conditions/frinx-qos-extension:qos-group=4?insert=before&point=2' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"frinx-qos-extension:qos-group": [
4
]
}'
{
"frinx-qos-extension:qos-group": [
1,
4,
2,
3
]
}
#
Retrieving data
#
With-defaults query parameter
The 'with-defaults' query parameter is used to specify how information about default data nodes is returned in response to GET requests on data resources. The response body is output filtered by value of with-defaults parameter.
The example of using the with-defaults query parameter: path?with-defaults or path?with-defaults=value
The allowed values for 'with-defaults' query parameter:
Example YANG Module:
module example {
namespace "http://example.com/ns/interfaces";
prefix exam;
container interfaces {
list interface {
key name;
leaf name {
type string;
}
leaf mtu {
type uint32;
default 1000;
}
leaf status {
type string;
}
}
}
Example Data Set By User:
{
"interfaces": {
"interface": [
{
"name": "eth0",
"mtu": 1000,
"status": "up"
},
{
"name": "eth1",
"mtu": 2000,
},
{
"name": "eth2",
"status": "down"
},
]
}
}
Value Report-All or Without Value
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults
OR
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults=report-all
{
"interfaces": {
"interface": [
{
"name": "eth0",
"mtu": 1000,
"status": "up"
},
{
"name": "eth1",
"mtu": 2000,
},
{
"name": "eth2",
"mtu": "1000",
"status": "up"
},
]
}
}
Value Explicit
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults=explicit
{
"interfaces": {
"interface": [
{
"name": "eth0",
"mtu": 1000,
"status": "up"
},
{
"name": "eth1",
"mtu": 2000,
},
{
"name": "eth2",
"status": "up"
},
]
}
}
Value Trim
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults=trim
{
"interfaces": {
"interface": [
{
"name": "eth0",
"status": "up"
},
{
"name": "eth1",
"mtu": 2000,
},
{
"name": "eth2",
"status": "up"
},
]
}
}
#
JSON Attributes
Node attributes can be encoded in JSON by wrapping all the attributes in the '@' container and values or arrays in the '#' JSON element. This notation is inspired by one that is used in the 'js2xmlparser' open-source tool (conversion between JSON and XML structures): js2xmlparser
RESTCONF supports both serialization and deserialization of attributes, GET response shows all set attributes in the read data-tree and PUT/POST/PLAIN PATCH methods can be used for the writing of data nodes with attributes. Warning: attributes cannot be directly addressed using RESTCONF URI that would contain the '@' element in the path, because attributes are always bound to some data node, they are not represented by distinct nodes in the data-tree.
Reserved '@' container may contain multiple attributes. Each attribute is encoded in the same fashion as leaf nodes, there is an identifier of the attribute and attribute value.
Format of the attribute that is defined in the [module]:
"[module]:[attribute-name]": [value]
Format of the attribute that is defined in the same module as the parent data entity:
"[attribute-name]": [value]
#
Example - leaf with attributes
Leaf without attributes:
{
"sample-leaf": 3
}
The same leaf with set 2 attributes: 'm1:attribute-1' and 'm1:attribute-2':
{
"sample-leaf": {
"@": {
"m1:attribute-1": "value",
"m1:attribute-2": 7
},
"#": 3
}
}
#
Example: Container with Attributes
A container without attributes:
{
"sample-container": {
"nested-leaf-1": "str1",
"nested-container": {
"l1": 10,
"l2": true
}
}
}
The same container with set 2 attributes: 'm1:switch' and 'm2:multiplier':
{
"sample-container": {
"@": {
"m1:switch": true,
"m2:multiplier": 10
},
"nested-leaf-1": "str1",
"nested-container": {
"l1": 10,
"l2": true
}
}
}
#
Example: Leaf-list with Attributes
Leaf-list without attributes:
{
"sample-leaf-list": [10, 20, 30]
}
The same leaf with set 1 attribute: 'mx:split':
{
"sample-leaf-list": {
"@": {
"mx:split": true
},
"#": [10, 20, 30]
}
}
#
Example: Leaf-list Entry with Attributes
Leaf-list without attributes:
{
"sample-leaf-list": [10, 20]
}
Two leaf-list entries, leaf-list entry with value '10' has one attribute with identifier 'm1:prefix'. The second leaf-list entry '20' doesn't have any attributes assigned.
{
"sample-leaf-list": [
{
"@": {
"m1:prefix": "arp-"
},
"#": 10
},
20
]
}
#
Example: List with Attributes
List without attributes:
{
"sample-list": [
{
"key": "k1",
"value": 1
},
{
"key": "k2",
"value": 2
}
]
}
The same list with applied single attribute: 'constraints:length'.
{
"sample-list": {
"@": {
"constraints:length": 100
},
"#": [
{
"key": "k1",
"value": 1
},
{
"key": "k2",
"value": 2
}
]
}
}
#
Example: List Entry with Attributes
List with two list entries without attributes:
{
"sample-list": [
{
"key": "k1",
"value": 1
},
{
"key": "k2",
"value": 2
}
]
}
The same list entries, the first list entry doesn't contain any attribute, but the second list entry contains 2 attributes: 'm1:switch' and 'm2:multiplier'.
{
"sample-list": [
{
"key": "k1",
"value": 1
},
{
"key": "k2",
"value": 2,
"@": {
"m1:switch": true,
"m2:multiplier": 10
}
}
]
}
#
Device Schema Filters
By default, all input and output data produced by RESTCONF for the selected device is fully compliant with its YANG models. Any violation of the YANG schema definitions will result in an error. Some of these restrictions can be addressed by adding the 'schemaFilters' configuration parameter for the RESTCONF.
#
Configuration Options Overview
Following configuration options for 'schemaFilters' make RESTCONF processing less restrictive:
#
Configuration Example
The following example demonstrates how to enable schema filters for selected extensions and make RESTCONF ignore unknown definitions and definitions with a 'deprecated status' attribute.
# Indicates if the data for non-existing schema nodes will be ignored during PUT/POST/PATCH operation
# or during GET operation in the 'fields' query parameter value.
restconf.schema-filters.ignore-unsupported-definitions-on-write=true
# Indicates if the definition with "DEPRECATED" status should be hidden during GET operation.
restconf.schema-filters.hide-deprecated-definitions-on-read=true
# Indicates if constraint will throw an exception for an invalid input value.
# This is used on DB layer (serializing/deserializing), RestConf layer (reading/writing),
# gNMI layer (serializing/deserializing). Currently, string patterns + enums are ignored.
restconf.schema-filters.skip-constraint-checking=false
# List of extension definitions that can be used to filter out data during PUT/POST/PATCH operation.
restconf.schema-filters.ignored-data-on-write-by-extensions[0]=tailf:hidden full
# List of extension definitions that can be used to filter out data during GET operation.
restconf.schema-filters.hidden-data-on-read-by-extensions[0]=tailf:hidden deprecated
restconf.schema-filters.hidden-data-on-read-by-extensions[1]=tailf:hidden debug
#
Unhide Parameter for READ/WRITE Operations
RESTCONF supports the 'unhide' query parameter for the GET requests to include hidden definitions into the response and for PUT/POST/PATCH requests to accept hidden definitions in the input. This parameter value can be populated with a comma-separated list of extensions to unhide or the keyword 'all' to include all possible hidden definitions in the response.
Example of using the 'unhide' parameter for the GET and PUT/POST/PATCH requests.
Using unhide with a list of extensions
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration?unhide=tailf:hidden debug,tailf:hidden deprecated
Using unhide parameter to unhide all hidden definitions
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration?unhide=all
#
Leafref validation
According to YANG standard there are constraints for leafrefs. These constraints are not validated by default. Leafref validation can be enabled using checkForReferences query parameter with value set to true.
#
Example:
#
Using leafref validation
curl --location --request DELETE 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration/
> frinx-openconfig-interfaces:interfaces/interface=eth0?checkForReferences=true' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json'
#
Example output of failed validation
{
"errors": {
"error": [
{
"error-message": "Leafref validation failed. Violated leafref constraint on leaf /network-topology/topology/node/configuration/interfaces/interface/name - node is referenced by leaf on path: /network-topology/topology/node/configuration/referencing/path",
"error-tag": "invalid-value",
"error-type": "protocol"
}
]
}
}
If checkForReferences parameter is set to false or is not provided UniConfig will not perform leafref validation and there will be no leafref validation error.
#
Hide Empty Data Nodes
Query parameter 'hideEmptyDataNodes' is used to hide empty composite data-tree nodes in response to GET call. Data nodes that contain only attribute tag are considered to be empty too. Default value is 'false' - empty nodes are displayed in the GET response.
#
Example
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=templates/node=t1/configuration/system' \
--header 'Accept: application/json'
{
"system:system": {
"routing": "enabled",
"setting": {
"keepalive-timeout": 10,
"response-timeout": 50
},
"flags": [],
"state": {},
"users": {
"@": {
"template-tags:operation": "merge"
},
"#": []
}
}
}
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=templates/node=t1/configuration/system?hideEmptyDataNodes=true' \
--header 'Accept: application/json'
{
"system:system": {
"routing": "enabled",
"setting": {
"keepalive-timeout": 10,
"response-timeout": 50
}
}
}
#
Escaping keys in URI
Following characters must be escaped, if they are contained in a list key value: ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', ''', '(', ')', '*', '+', ',', ';', '='.
There are 2 ways how to escape special characters in a key value: by encoding reserved UTF-8 characters using '%HH' patten or using key delimiter.
Using key delimiter should be the preferred way of dealing with reserved characters in keys since it avoids various issues with URL parsing constraints imposed by the web server.
#
Encoding reserved characters
RESTCONF RFC-8040 natively allows to specify reserved characters in a key value, if they are encoded using '%HH' pattern, where 'HH' refers to hexadecimal representation of UTF-8 character.
Starting with Uniconfig 7.0.0, it is not possible to URL-encode / as %2F because the web server will throw away the request due to stricter URL parsing rules.
The following request demonstrates encoding of special characters in the 'ge0/0/1' interface name, which does not work anymore starting with Uniconfig 7.0.0.
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration/frinx-openconfig-interfaces:interfaces/interface=ge0%2F0%2F1 \
--header 'Accept: application/json'
You should use the following request:
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration/frinx-openconfig-interfaces:interfaces/interface=%22ge0/0/1%22 \
--header 'Accept: application/json'
Mappings between special characters and UTF-8 codes can be found on following site: https://www.urlencoder.org/
#
Demarcate key using delimiter
UniConfig lets you specify a key delimiter used to demarcate list key values. Once defined, all special characters inside the key are automatically escaped.
The delimiter is enabled by default.
It can be defined in the
config/application.properties
file:
# RESTCONF settings
# This flag removes namespaces from GET response (only if there are no duplicate localnames)
# (E.g. if there is: namespace1:test and namespace2:test the namespace won't be removed because
# there would be two identical localnames).
restconf.show-namespace-in-json-response=true
# Trying to parse input body and URI using the latest schema if parsing using another context failed.
# If parsing using the latest schema is successful, then error returned to user is adjusted.
restconf.format-error-with-latest-schema=false
# Flag that if enabled, changes yang-patch responses containing errors into RFC-8040 format.
restconf.yang-patch-rfc8040-error-response=false
# Default value 0 makes the behavior as defined in RFC8040 (returns status code 404 [Not Found]),
# if changed to anything else, that status code will be returned.
restconf.status-code-for-empty-get-response=0
# Delimiter used for escaping of list keys in URI (for example, '%22')
# if it is set to 'null' (default), keys cannot be escaped and must be directly encoded according to RFC-8040.
restconf.keyDelimiter=%22
The following request demonstrates the demarcation of an interface named
ge0/0/1
using the %22
delimiter.
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration/frinx-openconfig-interfaces:interfaces/interface=%22ge0/0/1%22 \
--header 'Accept: application/json'
#
Hide Attributes
Query parameter 'hideAttributes' is used to hide composite data-tree nodes attributes in response to GET call. Default value is 'false' - nodes attributes are displayed in the GET response.
#
Example
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=templates/node=tmpl/configuration/service-node-groups' \
--header 'Accept: application/json'
{
"sfc:service-node-groups": {
"@": {
"template-tags:operation": "replace"
},
"service-node-group": [
{
"@": {
"template-tags:operation": "update"
},
"name": "service-node-group-test-name"
}
]
}
}
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=templates/node=tmpl/configuration/service-node-groups?hideAttributes=true' \
--header 'Accept: application/json'
{
"sfc:service-node-groups": {
"service-node-group": [
{
"name": "service-node-group-test-name"
}
]
}
}
#
Callbacks (http-client)
Callbacks include sending GET (call-point) and POST (action) requests to the remote server. They are implemented mainly for UniConfig Shell, but can also be used by RESTCONF for UniStore nodes by using the URI prefix:
http://localhost:8181/rests/http-client/...
#
Examples
Example - call-point invocation in RESTCONF
curl --location --request GET 'http://localhost:8181/rests/http-client/network-topology:network-topology/topology=unistore/node=node1/configuration/test/get-request'
Response:
{
"response": {
"value": "some-value"
}
}
Example - action invocation in RESTCONF
curl --location --request POST 'http://localhost:8181/rests/http-client/network-topology:network-topology/topology=unistore/node=node1/configuration/post-request/test-action' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"body": {
"data": "some-data"
}
}
}'
Response:
{
"response": {
"value": "some-data was processed"
}
}
Callbacks must be configured before use. For more details, see Callbacks.