This document provides a comprehensive overview of the REST API interactions enabled via the yang-express middleware framework built on Express.js.
While much care has been taken to auto-generate the API endpoint routing to conform as closely as possible to RESTCONF, there are many key deviations and currently no plans to adapt the default REST API generator to be RESTCONF compatible.
Instead, the REST API generated by the YANG data models are much more closely aligned to conventional REST APIs developed for use in the web application development community. Also, there's currently no support for XML but instead follows the YANG-JSON specifications for representing configuration data in JSON.
Below is the complete snippet from server.coffee demonstrating how to use yang-express middleware framework to fire up a web server instance capable of serving up the YANG model-driven CORD reference implementation.
yang = require('yang-js') require('yang-express').run { port: 5050 models: [ yang.require 'cord-core' yang.require 'xos-core' ] data: require '../../sample-data.json' }
Yes, it's really that simple.
The following section presents various CRUD operations that are runtime generated from the cord-core
YANG module. These operations are essentially interpreted and served during runtime depending on the YANG runtime model along with the configuration data state at any given point in time. Basically, it performs adaptive API routing and rendering.
Valid URIs:
Request:
$ curl localhost:5050/cord:subscriber
Response:
{ "cord-core:subscriber": [ { "id": 1, "service-specific-id": 1000, "humanReadableName": "cordSubscriber-1", "status": "enabled", "demo": false, "uplink-speed": 1000000000, "downlink-speed": 1000000000, "kind": "cord-subscriber", "related": {} }, { "id": 2, "service-specific-id": 1001, "humanReadableName": "cordSubscriber-2", "status": "enabled", "demo": false, "uplink-speed": 1000000000, "downlink-speed": 1000000000, "kind": "cord-subscriber", "related": {} } ] }
Valid URIs:
Request:
$ curl localhost:5050/cord:subscriber/1
Response:
{ "cord-core:subscriber": { "id": 1, "service-specific-id": 1000, "humanReadableName": "cordSubscriber-1", "status": "enabled", "demo": false, "uplink-speed": 1000000000, "downlink-speed": 1000000000, "kind": "cord-subscriber", "related": {} } }
Valid URIs:
Request:
$ curl -X PUT localhost:5050/cord:subscriber/1 -H 'content-type: application/json' -d '{ "id": 10, "status": "suspended", "services": { "cdn": { "enabled": true } } }'
Response:
{ "cord-core:subscriber": { "id": 10, "service-specific-id": 1000, "humanReadableName": "cordSubscriber-10", "status": "suspended", "demo": false, "uplink-speed": 1000000000, "downlink-speed": 1000000000, "kind": "cord-subscriber", "related": {}, "services": { "cdn": { "enabled": true } } } }
This operation highlights an interesting scenario related to invisible YANG schema properties. Any configuration tree segment that does not contain any data behave as shadow properties, which basically means they spring to life once valid data gets placed into it but stays invisible otherwise.
Valid URIs:
Request:
$ curl -X DELETE localhost:5050/cord:subscriber/2
Response:
HTTP 204
Valid URIs:
Request:
$ curl -X POST localhost:5050/cord:subscriber -H 'content-type: application/json' -d '{ "id": 12, "service-specific-id": 2020, "demo": true }'
Response:
{ "cord-core:subscriber": [ { "id": 12, "service-specific-id": 2020, "demo": true, "humanReadableName": "cordSubscriber-12", "status": "enabled", "uplink-speed": 1000000000, "downlink-speed": 1000000000, "kind": "cord-subscriber", "related": {} } ] }
The POST operation is only available on YANG list
schema elements as well as rpc
and action
elements. This operation also accepts bulk create request data by supplying a JSON array.
This operation provides discovery of a given data model's characteristics as well as other data node elements available inside the data model. It can be used by REST API consumers to auto-discover additional paths and metadata associated with the target entity. It's also useful for dynamically generating API documentation for a given data model.
Valid URIs:
Request:
$ curl -X OPTIONS localhost:5050/cord:subscriber
Response:
{ "name": "cord-core:subscriber", "kind": "list", "path": "/cord-core:subscriber", "exists": true, "key": "id", "description": "Authorative list of all subscriber instances", "data": { "humanReadableName": { "kind": "leaf", "config": false, "type": { "string": { "pattern": "/^cordSubscriber-\\w+$/" } } }, "status": { "kind": "leaf", "type": { "enumeration": { "enum": { "enabled": { "description": "Enabled", "value": "1" }, "suspended": { "description": "Suspended", "value": "2" }, "delinquent": { "description": "Delinquent", "value": "3" }, "violation": { "description": "Copyright Violation", "value": "4" } } } }, "default": "enabled" }, "demo": { "kind": "leaf", "type": "boolean", "default": "false" }, "uplink-speed": { "kind": "leaf", "type": { "dev:bandwidth": { "range": "1000000..max" } }, "default": "1000000000", "units": "bps" }, "downlink-speed": { "kind": "leaf", "type": { "dev:bandwidth": { "range": "1000000..max" } }, "default": "1000000000", "units": "bps" }, "id": { "kind": "leaf", "type": { "unique-identifier": { "type": { "uint32": { "range": "1..max" }, "yang:uuid": { "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/" } } } }, "mandatory": true }, "kind": { "kind": "leaf", "type": { "identityref": { "base": "kind" } }, "default": "generic" }, "name": { "kind": "leaf", "type": { "string": { "length": "0..255" } }, "description": "Specify name of the TenantRoot" }, "service-specific-attribute": { "kind": "leaf", "type": "string" }, "service-specific-id": { "kind": "leaf", "type": { "unique-identifier": { "type": { "uint32": { "range": "1..max" }, "yang:uuid": { "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/" } } } }, "mandatory": true }, "delete": { "kind": "action" }, "services": { "kind": "container", "description": "Contains various services available to the subscriber" }, "device": { "kind": "list", "key": "mac" }, "subscribed-tenants": { "kind": "list", "config": false } } }
There are a number of additional operational endpoints dynamically rendered for the YANG data model. Since YANG is a hierarchical data modeling language, you can have deeply nested structures of various containers
and lists
. The appropriate CRUD operations can take effect as deep into the endpoint entity hierarchy according to the YANG schema.
Here's a sample list of various URIs for the Subscriber model:
Methods | Endpoint URI |
---|---|
GET/POST | /cord:subscriber |
GET/PUT/DELETE | /cord:subscriber/:id |
GET/PUT | /cord:subscriber/:id/services/cdn |
GET/POST | /cord:subscriber/:id/device |
GET/PUT/DELETE | /cord:subscriber/:id/device/:mac |
GET/PUT | /cord:subscriber/:id/device/:mac/features |
Whenever POST/PUT requests are transacted, the incoming data is YANG schema validated to ensure fitness of the data before it gets applied into the runtime configuration data state.
For example, if you tried to POST /cord:subscriber without the mandatory
id and service-specific-id properties, it will generate an error and reject the request.
Also, if you tried to PUT using data values that does not conform to the underlying YANG schema for that data element, it will generate an error and reject the requst.
Invalid Request:
$ curl -X PUT localhost:5050/cord:subscriber/10 -H 'content-type: application/json' -d '{ "status": false }'
Error Response:
{ "error": { "message": "[enumeration] type violation for 'false' on enabled,suspended,delinquent,violation" } }
For additional information on comprehensive validation enforcement support, please refer to yang-js YANG compliance status report.