# 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:

  1. Config: Contains data representing the intended state. Possible to read and write via RESTCONF.
  2. Operational: Contains data representing the actual state. Possible only to read via RESTCONF.

# 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 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.

RPC Request, Yang-ext:mount using content=config parameter
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'
RPC Response, Status: 200
{
    "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.

RPC Request, Yang-ext:mount using content=nonconfig parameter
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'
RPC Response, Status: 200
{
    "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.

RPC Request, Invocation of yang action -> Clear VRRP global statistics
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'
RPC Response, Status: 200
{
}

Invocation of yang action -> List available firmware packages on disk

RPC Request, 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'
RPC Response, Status: 200
{
    "output" : {
        "packages" : [
            {
                "name" : "Name of the package"
            }
            ...
        ]
    }
}

Invocation of yang action -> Erase running-config-then load

RPC Request, 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"
    }
}'
RPC Response, Status: 200
<output xmlns="namespace">
    <status>Erasing config and restarting services</status>
</output>

# 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.
Example:
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>'
Example with mountpoint:
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).
Example:
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>'
Example with mount point:
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.
Example:
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.

RPC Request
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.

PATCH RPC Request with apply-tags parameter
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"
                }
            }
        ]
    }
}'
GET RPC Request before PATCH
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=ASR920/configuration/interfaces
Response
    {
        "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"
                    }
                }
            ]
        }
    }
GET RPC Request after PATCH
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=ASR920/configuration/interfaces
Response
    {
        "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”.
Example:
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:

Response
<output>
  …
</output>
RPC Request, An example using a JSON payload:
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"
    }
}'

# 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:

Request
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
    }
}'
The response body:
{
    "interfaces:output": {
        "avg-rx-kbps": 14524,
        "avg-tx-kbps": 47787
    }
}

# 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.

There are several rules, that need to be followed:

  1. 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.
  2. 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.
  3. 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 that offset=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 a GET request. Specifies the maximum count for entries included in the response, starting with the entry defined by the offset 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.

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.

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:

  1. leaves without value
  2. empty type
  3. boolean type
  4. random numeric type
  5. 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:

Value Description
first Insert the new item as the new first entry.
last Insert the new item as the new last entry (default value).
before Insert the new item before the insertion point, as specified by the value of the 'point' query parameter.
after Insert the new data after the insertion point, as specified by the value of the "point" parameter.

# 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

RPC Request
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'
RPC Response
{
    "frinx-qos-extension:qos-group": [
        1,
        2,
        3
    ]
}

# Insert item at the top of the list

RPC Request
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
    ]
}'
RPC Response, Updated list
{
    "frinx-qos-extension:qos-group": [
        4,
        1,
        2,
        3
    ]
}

# Insert item at the bottom of the list

RPC Request
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
    ]
}'
RPC Response, Updated list
{
    "frinx-qos-extension:qos-group": [
        1,
        2,
        3,
        4
    ]
}

# Insert item after specific item

RPC Request
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
    ]
}'
RPC Response, Updated list
{
    "frinx-qos-extension:qos-group": [
        1,
        2,
        4,
        3
    ]
}

# Insert item before specific item

RPC Request
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
    ]
}'
RPC Response, Updated list
{
    "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 allowed values for 'with-defaults' query parameter:

Value Description
Using with-defaults without value is equivalent to value 'report-all'.
report-all All data nodes are reported, including any data nodes with YANG default in scheme, which are not set by client are reported.
explicit Data nodes set to its YANG schema default value by the client are reported.
trim Data nodes set to its YANG schema default value are not reported.

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

Example GET Request
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
Response
    {
        "interfaces": {
            "interface": [
                {
                    "name": "eth0",
                    "mtu": 1000,
                    "status": "up"
                },
                {
                    "name": "eth1",
                    "mtu": 2000,
                },
                {
                    "name": "eth2",
                    "mtu": "1000",
                    "status": "up"                   
                },
            ]
        }
    }

Value Explicit

Example GET Request
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults=explicit
Reponse
    {
        "interfaces": {
            "interface": [
                {
                    "name": "eth0",
                    "mtu": 1000,
                    "status": "up"
                },
                {
                    "name": "eth1",
                    "mtu": 2000,
                },
                {
                    "name": "eth2",
                    "status": "up"                   
                },
            ]
        }
    }

Value Trim

GET Request
http://127.0.0.1:8181/restconf/data/network-topology:network-topology/topology=uniconfig/node=iosxr/configuration/frinx-openconfig-interfaces:interfaces?with-defaults=trim
Reponse
    {
        "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.

RESTCONF Shema Filters Properties
# 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

GET request without 'hideEmptyDataNodes' parameter
curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=templates/node=t1/configuration/system' \
--header 'Accept: application/json'
GET response with empty data-tree nodes
{
    "system:system": {
        "routing": "enabled",
        "setting": {
            "keepalive-timeout": 10,
            "response-timeout": 50
        },
        "flags": [],
        "state": {},
        "users": {
            "@": {
                "template-tags:operation": "merge"
            },
            "#": []
        }
    }
}
GET request with set 'hideEmptyDataNodes' parameter to 'true'
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'
GET response with hidden empty data nodes
{
    "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.

# 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.

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.

Read interface (escaped interface name)
    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:

Read interface (Using key 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'

# 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 Properties
# 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/1using the %22 delimiter.

Read interface (escaped interface name)
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

GET request without 'hideAttributes' parameter
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'
GET response with empty data-tree nodes
{
   "sfc:service-node-groups": {
       "@": {
           "template-tags:operation": "replace"
       },
       "service-node-group": [
           {
               "@": {
                   "template-tags:operation": "update"
               },
               "name": "service-node-group-test-name"
           }
       ]
   }
}
GET request with set 'hideAttributes' parameter to 'true'
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'
GET response with hidden attributes
{
   "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-client
http://localhost:8181/rests/http-client/...

# Examples

Example - call-point invocation in RESTCONF

Call-point request
curl --location --request GET 'http://localhost:8181/rests/http-client/network-topology:network-topology/topology=unistore/node=node1/configuration/test/get-request'

Response:

Call-point response
{
  "response": {
    "value": "some-value"
  }
}

Example - action invocation in RESTCONF

Action request
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:

Action response
{
  "response": {
    "value": "some-data was processed"
  }
}

Callbacks must be configured before use. For more details, see Callbacks.