Merge branch 'yang-patch-test' of https://github.com/alanyanigersiklu/clixon into alanyanigersiklu-yang-patch-test
This commit is contained in:
commit
89ca5ac154
5 changed files with 2254 additions and 47 deletions
234
test/example.sh
Executable file
234
test/example.sh
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
#!/usr/bin/env bash
|
||||
# clixon example
|
||||
# Assumes fexample is set to name of yang file
|
||||
|
||||
cat <<EOF2 > $fexample
|
||||
module clixon-example {
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
import ietf-interfaces {
|
||||
/* is in yang/optional which means clixon must be installed using --opt-yang-installdir */
|
||||
prefix if;
|
||||
}
|
||||
import ietf-ip {
|
||||
prefix ip;
|
||||
}
|
||||
import iana-if-type {
|
||||
prefix ianaift;
|
||||
}
|
||||
import ietf-datastores {
|
||||
prefix ds;
|
||||
}
|
||||
description
|
||||
"Clixon example used as a part of the Clixon test suite.
|
||||
It can be used as a basis for making new Clixon applications.
|
||||
Note, may change without updating revision, just for testing current master.
|
||||
";
|
||||
revision 2020-12-01 {
|
||||
description "Added table/paramater/value as the primary data example";
|
||||
}
|
||||
revision 2020-03-11 {
|
||||
description "Added container around translation list. Released in Clixon 4.4.0";
|
||||
}
|
||||
revision 2019-11-05 {
|
||||
description "Augment interface. Released in Clixon 4.3.0";
|
||||
}
|
||||
revision 2019-07-23 {
|
||||
description "Extension e4. Released in Clixon 4.1.0";
|
||||
}
|
||||
revision 2019-01-13 {
|
||||
description "Released in Clixon 3.9";
|
||||
}
|
||||
/* Example interface type for tests, local callbacks, etc */
|
||||
identity eth {
|
||||
base if:interface-type;
|
||||
}
|
||||
identity loopback {
|
||||
base if:interface-type;
|
||||
}
|
||||
/* Generic config data */
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
leaf value{
|
||||
type string;
|
||||
}
|
||||
leaf stat{
|
||||
description "Inline state data for example application";
|
||||
config false;
|
||||
type int32;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* State data (not config) for the example application*/
|
||||
container state {
|
||||
config false;
|
||||
description "state data for the example application (must be here for example get operation)";
|
||||
leaf-list op {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
augment "/if:interfaces/if:interface" {
|
||||
container my-status {
|
||||
config false;
|
||||
description "For testing augment+state";
|
||||
leaf int {
|
||||
type int32;
|
||||
}
|
||||
leaf str {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* yang extension implemented by the example backend code. */
|
||||
extension e4 {
|
||||
description
|
||||
"The first child of the ex:e4 (unknown) statement is inserted into
|
||||
the module as a regular data statement. This means that 'uses bar;'
|
||||
in the ex:e4 statement below is a valid data node";
|
||||
argument arg;
|
||||
}
|
||||
grouping bar {
|
||||
leaf bar{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
ex:e4 arg1{
|
||||
uses bar;
|
||||
}
|
||||
/* Example notification as used in RFC 5277 and RFC 8040 */
|
||||
notification event {
|
||||
description "Example notification event.";
|
||||
leaf event-class {
|
||||
type string;
|
||||
description "Event class identifier.";
|
||||
}
|
||||
container reportingEntity {
|
||||
description "Event specific information.";
|
||||
leaf card {
|
||||
type string;
|
||||
description "Line card identifier.";
|
||||
}
|
||||
}
|
||||
leaf severity {
|
||||
type string;
|
||||
description "Event severity description.";
|
||||
}
|
||||
}
|
||||
rpc client-rpc {
|
||||
description "Example local client-side RPC that is processed by the
|
||||
the netconf/restconf and not sent to the backend.
|
||||
This is a clixon implementation detail: some rpc:s
|
||||
are better processed by the client for API or perf reasons";
|
||||
input {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
output {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc empty {
|
||||
description "Smallest possible RPC with no input or output sections";
|
||||
}
|
||||
rpc optional {
|
||||
description "Small RPC with optional input and output";
|
||||
input {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
output {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc example {
|
||||
description "Some example input/output for testing RFC7950 7.14.
|
||||
RPC simply echoes the input for debugging.";
|
||||
input {
|
||||
leaf x {
|
||||
description
|
||||
"If a leaf in the input tree has a 'mandatory' statement with
|
||||
the value 'true', the leaf MUST be present in an RPC invocation.";
|
||||
type string;
|
||||
mandatory true;
|
||||
}
|
||||
leaf y {
|
||||
description
|
||||
"If a leaf in the input tree has a 'mandatory' statement with the
|
||||
value 'true', the leaf MUST be present in an RPC invocation.";
|
||||
type string;
|
||||
default "42";
|
||||
}
|
||||
leaf-list z {
|
||||
description
|
||||
"If a leaf-list in the input tree has one or more default
|
||||
values, the server MUST use these values (XXX not supported)";
|
||||
type string;
|
||||
}
|
||||
leaf w {
|
||||
description
|
||||
"If any node has a 'when' statement that would evaluate to
|
||||
'false',then this node MUST NOT be present in the input tree.
|
||||
(XXX not supported)";
|
||||
type string;
|
||||
}
|
||||
list u0 {
|
||||
description "list without key";
|
||||
leaf uk{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
list u1 {
|
||||
description "list with key";
|
||||
key uk;
|
||||
leaf uk{
|
||||
type string;
|
||||
}
|
||||
leaf val{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
output {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
leaf y {
|
||||
type string;
|
||||
}
|
||||
leaf z {
|
||||
type string;
|
||||
}
|
||||
leaf w {
|
||||
type string;
|
||||
}
|
||||
list u0 {
|
||||
leaf uk{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
list u1 {
|
||||
key uk;
|
||||
leaf uk{
|
||||
type string;
|
||||
}
|
||||
leaf val{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF2
|
||||
|
||||
|
||||
1130
test/interfaces.sh
Executable file
1130
test/interfaces.sh
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -7,14 +7,17 @@
|
|||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
echo "...skipped: YANG_PATCH NYI"
|
||||
if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
#echo "...skipped: YANG_PATCH JSON NYI"
|
||||
#if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf.xml
|
||||
startupdb=$dir/startup_db
|
||||
fjukebox=$dir/example-jukebox.yang
|
||||
fyangpatch=$dir/ietf-yang-patch.yang
|
||||
finterfaces=$dir/ietf-interfaces.yang
|
||||
fexample=$dir/clixon-example.yang
|
||||
|
||||
# Define default restconfig config: RESTCONFIG
|
||||
RESTCONFIG=$(restconf_config user false)
|
||||
|
|
@ -102,6 +105,15 @@ cat <<EOF > $dir/example-system.yang
|
|||
}
|
||||
EOF
|
||||
|
||||
# Yang Patch spec (fyangpatch must be set)
|
||||
. ./yang-patch.sh
|
||||
|
||||
# Interfaces spec (finterfaces must be set)
|
||||
. ./interfaces.sh
|
||||
|
||||
# clixon example spec (fexample must be set)
|
||||
. ./example.sh
|
||||
|
||||
# Common Jukebox spec (fjukebox must be set)
|
||||
. ./jukebox.sh
|
||||
|
||||
|
|
@ -132,53 +144,174 @@ fi
|
|||
new "wait restconf"
|
||||
wait_restconf
|
||||
|
||||
# RFC 8072 A.1.1
|
||||
REQ='<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
|
||||
<patch-id>add-songs-patch</patch-id>
|
||||
<edit>
|
||||
<edit-id>edit1</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/song=Bridge%20Burning</target>
|
||||
<value>
|
||||
<song xmlns="http://example.com/ns/example-jukebox">
|
||||
<name>Bridge Burning</name>
|
||||
<location>/media/bridge_burning.mp3</location>
|
||||
<format>MP3</format>
|
||||
<length>288</length>
|
||||
</song>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit2</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/song=Rope</target>
|
||||
<value>
|
||||
<song xmlns="http://example.com/ns/example-jukebox">
|
||||
<name>Rope</name>
|
||||
<location>/media/rope.mp3</location>
|
||||
<format>MP3</format>
|
||||
<length>259</length>
|
||||
</song>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit3</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/song=Dear%20Rosemary</target>
|
||||
<value>
|
||||
<song xmlns="http://example.com/ns/example-jukebox">
|
||||
<name>Dear Rosemary</name>
|
||||
<location>/media/dear_rosemary.mp3</location>
|
||||
<format>MP3</format>
|
||||
<length>269</length>
|
||||
</song>
|
||||
</value>
|
||||
</edit>
|
||||
</yang-patch>'
|
||||
# Modify several interfaces with a YANG patch, testing create, merge, and delete
|
||||
REQ='{
|
||||
"ietf-yang-patch:yang-patch": {
|
||||
"patch-id": "alan-test-patch",
|
||||
"edit": [
|
||||
{
|
||||
"edit-id": "edit-1",
|
||||
"operation": "create",
|
||||
"target": "/interface=eth1",
|
||||
"value": {
|
||||
"interface": [
|
||||
{
|
||||
"name": "eth1",
|
||||
"type": "clixon-example:eth",
|
||||
"enabled": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-2",
|
||||
"operation": "create",
|
||||
"target": "/interface=eth2",
|
||||
"value": {
|
||||
"interface": [
|
||||
{
|
||||
"name": "eth2",
|
||||
"type": "clixon-example:eth",
|
||||
"enabled": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-3",
|
||||
"operation": "create",
|
||||
"target": "/interface=eth4",
|
||||
"value": {
|
||||
"interface": [
|
||||
{
|
||||
"name": "eth4",
|
||||
"type": "clixon-example:eth",
|
||||
"enabled": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-4",
|
||||
"operation": "merge",
|
||||
"target": "/interface=eth2",
|
||||
"value": {
|
||||
"interface": [
|
||||
{
|
||||
"enabled": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-5",
|
||||
"operation": "delete",
|
||||
"target": "/interface=eth1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
new "RFC 8072 YANG Patch JSON: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+json' -H 'Accept: application/yang-patch+json' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces -d "$REQ")" 0 "HTTP/$HVER 204 No Content"
|
||||
#
|
||||
# Create artist in jukebox example
|
||||
REQ='{"example-jukebox:artist":[{"name":"Foo Fighters"}]}'
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 1: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
new "RFC 8072 A.1.1 Add resources: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+xml' -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d "$REQ")" 0 "HTTP/$HVER 409"
|
||||
# Create album in jukebox example
|
||||
REQ='<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>'
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 2: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add fields to album in jukebox example
|
||||
REQ='{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}'
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 3: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d "$REQ")" 0 "HTTP/$HVER 204 No Content"
|
||||
|
||||
# Uncomment to get info about album in jukebox example
|
||||
#new "RFC 8072 YANG Patch jukebox example get 2: Error."
|
||||
#expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library)" 0 "HTTP/$HVER 201 OK"
|
||||
|
||||
# Add songs to playlist in jukebox example
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 4: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":2,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}"
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 5: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":4,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Still More Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 6: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":3,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='More Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch JSON jukebox example 7: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Run YANG patch on the playlist, testing "insert after" and "insert before"
|
||||
REQ='{
|
||||
"ietf-yang-patch:yang-patch": {
|
||||
"patch-id": "alan-test-patch-jukebox",
|
||||
"edit": [
|
||||
{
|
||||
"edit-id": "edit-2",
|
||||
"operation": "insert",
|
||||
"target": "/song=5",
|
||||
"point": "/song=1",
|
||||
"where" : "after",
|
||||
"value": {
|
||||
"example-jukebox:song": [
|
||||
{
|
||||
"index": 5,
|
||||
"id" : "Rope Galore"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-3",
|
||||
"operation": "insert",
|
||||
"target": "/song=6",
|
||||
"point": "/song=4",
|
||||
"where" : "before",
|
||||
"value": {
|
||||
"example-jukebox:song": [
|
||||
{
|
||||
"index": 6,
|
||||
"id" : "How Much Rope Does a Man Need"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"edit-id": "edit-2",
|
||||
"operation": "insert",
|
||||
"target": "/song=24",
|
||||
"point": "/song=6",
|
||||
"where" : "after",
|
||||
"value": {
|
||||
"example-jukebox:song": [
|
||||
{
|
||||
"index": 24,
|
||||
"id" : "The twenty fourth song"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
new "RFC 8072 YANG Patch JSON jukebox example: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+json' -H 'Accept: application/yang-patch+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Uncomment to get info about playlist in jukebox example
|
||||
#new "RFC 8072 YANG Patch jukebox example get : Error."
|
||||
#expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One)" 0 "HTTP/$HVER 201 OK"
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
|
|
|
|||
314
test/test_restconf_yang_patch_xml.sh
Executable file
314
test/test_restconf_yang_patch_xml.sh
Executable file
|
|
@ -0,0 +1,314 @@
|
|||
#!/usr/bin/env bash
|
||||
# Restconf RFC8072 yang patch
|
||||
# XXX enable YANG_PACTH in include/clixon_custom.h to run this test
|
||||
# Use nacm module in example/main/example_restconf.c hardcoded to
|
||||
# andy:bar and wilma:bar
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
#echo "...skipped: YANG_PATCH XML NYI"
|
||||
#if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf.xml
|
||||
startupdb=$dir/startup_db
|
||||
fjukebox=$dir/example-jukebox.yang
|
||||
fyangpatch=$dir/ietf-yang-patch.yang
|
||||
finterfaces=$dir/ietf-interfaces.yang
|
||||
fexample=$dir/clixon-example.yang
|
||||
|
||||
# Define default restconfig config: RESTCONFIG
|
||||
RESTCONFIG=$(restconf_config user false)
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||
<CLICON_BACKEND_PIDFILE>$dir/restconf.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||
$RESTCONFIG
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
NACM0="<nacm xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-acm\">
|
||||
<enable-nacm>true</enable-nacm>
|
||||
<read-default>deny</read-default>
|
||||
<write-default>deny</write-default>
|
||||
<exec-default>permit</exec-default>
|
||||
<groups>
|
||||
<group>
|
||||
<name>admin</name>
|
||||
<user-name>andy</user-name>
|
||||
</group>
|
||||
<group>
|
||||
<name>limited</name>
|
||||
<user-name>wilma</user-name>
|
||||
</group>
|
||||
</groups>
|
||||
<rule-list>
|
||||
<name>admin</name>
|
||||
<group>admin</group>
|
||||
<rule>
|
||||
<name>permit-all</name>
|
||||
<module-name>*</module-name>
|
||||
<access-operations>*</access-operations>
|
||||
<action>permit</action>
|
||||
<comment>
|
||||
Allow the 'admin' group complete access to all operations and data.
|
||||
</comment>
|
||||
</rule>
|
||||
</rule-list>
|
||||
<rule-list>
|
||||
<name>limited</name>
|
||||
<group>limited</group>
|
||||
<rule>
|
||||
<name>limit-jukebox</name>
|
||||
<module-name>jukebox-example</module-name>
|
||||
<access-operations>read create delete</access-operations>
|
||||
<action>deny</action>
|
||||
</rule>
|
||||
</rule-list>
|
||||
</nacm>
|
||||
"
|
||||
|
||||
cat<<EOF > $startupdb
|
||||
<${DATASTORE_TOP}>
|
||||
$NACM0
|
||||
</${DATASTORE_TOP}>
|
||||
EOF
|
||||
|
||||
# An extra testmodule that includes nacm
|
||||
cat <<EOF > $dir/example-system.yang
|
||||
module example-system {
|
||||
namespace "http://example.com/ns/example-system";
|
||||
prefix "ex";
|
||||
import ietf-netconf-acm {
|
||||
prefix nacm;
|
||||
}
|
||||
container system {
|
||||
leaf enable-jukebox-streaming {
|
||||
type boolean;
|
||||
}
|
||||
leaf extraleaf {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Yang Patch spec (fyangpatch must be set)
|
||||
. ./yang-patch.sh
|
||||
|
||||
# Interfaces spec (finterfaces must be set)
|
||||
. ./interfaces.sh
|
||||
|
||||
# clixon example spec (fexample must be set)
|
||||
. ./example.sh
|
||||
|
||||
# Common Jukebox spec (fjukebox must be set)
|
||||
. ./jukebox.sh
|
||||
|
||||
new "test params: -s startup -f $cfg"
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
sudo pkill -f clixon_backend # to be sure
|
||||
|
||||
new "start backend -s startup -f $cfg"
|
||||
start_backend -s startup -f $cfg
|
||||
fi
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "kill old restconf daemon"
|
||||
stop_restconf_pre
|
||||
|
||||
new "start restconf daemon"
|
||||
start_restconf -f $cfg
|
||||
fi
|
||||
|
||||
new "wait restconf"
|
||||
wait_restconf
|
||||
|
||||
# Modify several interfaces with a YANG patch, testing create, merge, and delete
|
||||
REQ='<ietf-yang-patch:yang-patch>
|
||||
<patch-id>test-patch-xml</patch-id>
|
||||
<edit>
|
||||
<edit-id>edit-1</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/interface=eth1</target>
|
||||
<value>
|
||||
<interface>
|
||||
<name>eth1</name>
|
||||
<type>clixon-example:eth</type>
|
||||
<enabled>false</false>
|
||||
</interface>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-2</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/interface=eth2</target>
|
||||
<value>
|
||||
<interface>
|
||||
<name>eth2</name>
|
||||
<type>clixon-example:eth</type>
|
||||
<enabled>false</false>
|
||||
</interface>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-3</edit-id>
|
||||
<operation>create</operation>
|
||||
<target>/interface=eth4</target>
|
||||
<value>
|
||||
<interface>
|
||||
<name>eth4</name>
|
||||
<type>clixon-example:eth</type>
|
||||
<enabled>false</false>
|
||||
</interface>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-4</edit-id>
|
||||
<operation>merge</operation>
|
||||
<target>/interface=eth2</target>
|
||||
<value>
|
||||
<interface>
|
||||
<enabled>true</false>
|
||||
</interface>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-5</edit-id>
|
||||
<operation>delete</operation>
|
||||
<target>/interface=eth1</target>
|
||||
</edit>
|
||||
</ietf-yang-patch:yang-patch>'
|
||||
new "RFC 8072 YANG Patch XML Media: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+xml' -H 'Accept: application/yang-patch+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces -d "$REQ")" 0 "HTTP/$HVER 204 No Content"
|
||||
#
|
||||
# Create artist in jukebox example
|
||||
REQ='{"example-jukebox:artist":[{"name":"Foo Fighters"}]}'
|
||||
new "RFC 8072 YANG Patch jukebox example 1: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Create album in jukebox example
|
||||
REQ='<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>'
|
||||
new "RFC 8072 YANG Patch jukebox example 2: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add fields to album in jukebox example
|
||||
REQ='{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}'
|
||||
new "RFC 8072 YANG Patch jukebox example 3: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d "$REQ")" 0 "HTTP/$HVER 204 No Content"
|
||||
|
||||
# Uncomment to get info about album in jukebox example
|
||||
#new "RFC 8072 YANG Patch jukebox example get 2: Error."
|
||||
#expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library)" 0 "HTTP/$HVER 201 OK"
|
||||
|
||||
# Add songs to playlist in jukebox example
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch jukebox example 4: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":2,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}"
|
||||
new "RFC 8072 YANG Patch jukebox example 5: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":4,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Still More Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch jukebox example 6: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Add song at end of playlist
|
||||
REQ="{\"example-jukebox:song\":[{\"index\":3,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='More Rope']\"}]}"
|
||||
new "RFC 8072 YANG Patch jukebox example 7: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=last -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Run YANG patch on the playlist, testing "insert after" and "insert before"
|
||||
REQ='<ietf-yang-patch:yang-patch>
|
||||
<patch-id>test-patch-jukebox</patch-id>
|
||||
<edit>
|
||||
<edit-id>edit-1</edit-id>
|
||||
<operation>insert</operation>
|
||||
<target>/song=5</target>
|
||||
<point>/song=1</point>
|
||||
<where>after</where>
|
||||
<value>
|
||||
<example-jukebox:song>
|
||||
<index>5</index>
|
||||
<id>Rope Galore</id>
|
||||
</example-jukebox:song>
|
||||
</value>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-2</edit-id>
|
||||
<operation>insert</operation>
|
||||
<target>/song=6</target>
|
||||
<point>/song=4</point>
|
||||
<where>before</where>
|
||||
<value>
|
||||
<example-jukebox:song>
|
||||
<index>6</index>
|
||||
<id>How Much Rope Does a Man Need</id>
|
||||
</example-jukebox:song>
|
||||
</edit>
|
||||
<edit>
|
||||
<edit-id>edit-3</edit-id>
|
||||
<operation>insert</operation>
|
||||
<target>/song=24</target>
|
||||
<point>/song=6</point>
|
||||
<where>before</where>
|
||||
<value>
|
||||
<example-jukebox:song>
|
||||
<index>24</index>
|
||||
<id>The twenty-fourth song</id>
|
||||
</example-jukebox:song>
|
||||
</edit>
|
||||
</ietf-yang-patch:yang-patch>'
|
||||
new "RFC 8072 YANG Patch XML jukebox example: Error."
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+json' -H 'Accept: application/yang-patch+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -d "$REQ")" 0 "HTTP/$HVER 201 Created"
|
||||
|
||||
# Uncomment to get info about playlist in jukebox example
|
||||
#new "RFC 8072 YANG Patch jukebox example get : Error."
|
||||
#expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One)" 0 "HTTP/$HVER 201 OK"
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
fi
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
fi
|
||||
|
||||
# Set by restconf_config
|
||||
unset RESTCONFIG
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
new "endtest"
|
||||
endtest
|
||||
396
test/yang-patch.sh
Executable file
396
test/yang-patch.sh
Executable file
|
|
@ -0,0 +1,396 @@
|
|||
#!/usr/bin/env bash
|
||||
# for rfc 8072
|
||||
# Assumes fyangpatch is set to name of yang file
|
||||
|
||||
cat <<EOF1 > $fyangpatch
|
||||
module ietf-yang-patch {
|
||||
yang-version 1.1;
|
||||
namespace "urn:ietf:params:xml:ns:yang:ietf-yang-patch";
|
||||
prefix "ypatch";
|
||||
|
||||
import ietf-restconf { prefix rc; }
|
||||
|
||||
organization
|
||||
"IETF NETCONF (Network Configuration) Working Group";
|
||||
|
||||
contact
|
||||
"WG Web: <https://datatracker.ietf.org/wg/netconf/>
|
||||
WG List: <mailto:netconf@ietf.org>
|
||||
|
||||
Author: Andy Bierman
|
||||
<mailto:andy@yumaworks.com>
|
||||
|
||||
Author: Martin Bjorklund
|
||||
<mailto:mbj@tail-f.com>
|
||||
|
||||
Author: Kent Watsen
|
||||
<mailto:kwatsen@juniper.net>";
|
||||
|
||||
description
|
||||
"This module contains conceptual YANG specifications
|
||||
for the YANG Patch and YANG Patch Status data structures.
|
||||
|
||||
Note that the YANG definitions within this module do not
|
||||
represent configuration data of any kind.
|
||||
The YANG grouping statements provide a normative syntax
|
||||
for XML and JSON message-encoding purposes.
|
||||
|
||||
Copyright (c) 2017 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, is permitted pursuant to, and subject
|
||||
to the license terms contained in, the Simplified BSD License
|
||||
set forth in Section 4.c of the IETF Trust's Legal Provisions
|
||||
Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
|
||||
This version of this YANG module is part of RFC 8072; see
|
||||
the RFC itself for full legal notices.";
|
||||
|
||||
revision 2017-02-22 {
|
||||
description
|
||||
"Initial revision.";
|
||||
reference
|
||||
"RFC 8072: YANG Patch Media Type.";
|
||||
}
|
||||
|
||||
typedef target-resource-offset {
|
||||
type string;
|
||||
description
|
||||
"Contains a data resource identifier string representing
|
||||
a sub-resource within the target resource.
|
||||
The document root for this expression is the
|
||||
target resource that is specified in the
|
||||
protocol operation (e.g., the URI for the PATCH request).
|
||||
|
||||
This string is encoded according to the same rules as those
|
||||
for a data resource identifier in a RESTCONF request URI.";
|
||||
reference
|
||||
"RFC 8040, Section 3.5.3.";
|
||||
}
|
||||
|
||||
rc:yang-data "yang-patch" {
|
||||
uses yang-patch;
|
||||
}
|
||||
|
||||
rc:yang-data "yang-patch-status" {
|
||||
uses yang-patch-status;
|
||||
}
|
||||
|
||||
grouping yang-patch {
|
||||
|
||||
description
|
||||
"A grouping that contains a YANG container representing the
|
||||
syntax and semantics of a YANG Patch edit request message.";
|
||||
|
||||
container yang-patch {
|
||||
description
|
||||
"Represents a conceptual sequence of datastore edits,
|
||||
called a patch. Each patch is given a client-assigned
|
||||
patch identifier. Each edit MUST be applied
|
||||
in ascending order, and all edits MUST be applied.
|
||||
If any errors occur, then the target datastore MUST NOT
|
||||
be changed by the YANG Patch operation.
|
||||
|
||||
It is possible for a datastore constraint violation to occur
|
||||
due to any node in the datastore, including nodes not
|
||||
included in the 'edit' list. Any validation errors MUST
|
||||
be reported in the reply message.";
|
||||
|
||||
reference
|
||||
"RFC 7950, Section 8.3.";
|
||||
|
||||
leaf patch-id {
|
||||
type string;
|
||||
mandatory true;
|
||||
description
|
||||
"An arbitrary string provided by the client to identify
|
||||
the entire patch. Error messages returned by the server
|
||||
that pertain to this patch will be identified by this
|
||||
'patch-id' value. A client SHOULD attempt to generate
|
||||
unique 'patch-id' values to distinguish between
|
||||
transactions from multiple clients in any audit logs
|
||||
maintained by the server.";
|
||||
}
|
||||
|
||||
leaf comment {
|
||||
type string;
|
||||
description
|
||||
"An arbitrary string provided by the client to describe
|
||||
the entire patch. This value SHOULD be present in any
|
||||
audit logging records generated by the server for the
|
||||
patch.";
|
||||
}
|
||||
|
||||
list edit {
|
||||
key edit-id;
|
||||
ordered-by user;
|
||||
|
||||
description
|
||||
"Represents one edit within the YANG Patch request message.
|
||||
The 'edit' list is applied in the following manner:
|
||||
|
||||
- The first edit is conceptually applied to a copy
|
||||
of the existing target datastore, e.g., the
|
||||
running configuration datastore.
|
||||
- Each ascending edit is conceptually applied to
|
||||
the result of the previous edit(s).
|
||||
- After all edits have been successfully processed,
|
||||
the result is validated according to YANG constraints.
|
||||
- If successful, the server will attempt to apply
|
||||
the result to the target datastore.";
|
||||
|
||||
leaf edit-id {
|
||||
type string;
|
||||
description
|
||||
"Arbitrary string index for the edit.
|
||||
Error messages returned by the server that pertain
|
||||
to a specific edit will be identified by this value.";
|
||||
}
|
||||
|
||||
leaf operation {
|
||||
type enumeration {
|
||||
enum create {
|
||||
description
|
||||
"The target data node is created using the supplied
|
||||
value, only if it does not already exist. The
|
||||
'target' leaf identifies the data node to be
|
||||
created, not the parent data node.";
|
||||
}
|
||||
enum delete {
|
||||
description
|
||||
"Delete the target node, only if the data resource
|
||||
currently exists; otherwise, return an error.";
|
||||
}
|
||||
|
||||
enum insert {
|
||||
description
|
||||
"Insert the supplied value into a user-ordered
|
||||
list or leaf-list entry. The target node must
|
||||
represent a new data resource. If the 'where'
|
||||
parameter is set to 'before' or 'after', then
|
||||
the 'point' parameter identifies the insertion
|
||||
point for the target node.";
|
||||
}
|
||||
enum merge {
|
||||
description
|
||||
"The supplied value is merged with the target data
|
||||
node.";
|
||||
}
|
||||
enum move {
|
||||
description
|
||||
"Move the target node. Reorder a user-ordered
|
||||
list or leaf-list. The target node must represent
|
||||
an existing data resource. If the 'where' parameter
|
||||
is set to 'before' or 'after', then the 'point'
|
||||
parameter identifies the insertion point to move
|
||||
the target node.";
|
||||
}
|
||||
enum replace {
|
||||
description
|
||||
"The supplied value is used to replace the target
|
||||
data node.";
|
||||
}
|
||||
enum remove {
|
||||
description
|
||||
"Delete the target node if it currently exists.";
|
||||
}
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"The datastore operation requested for the associated
|
||||
'edit' entry.";
|
||||
}
|
||||
|
||||
leaf target {
|
||||
type target-resource-offset;
|
||||
mandatory true;
|
||||
description
|
||||
"Identifies the target data node for the edit
|
||||
operation. If the target has the value '/', then
|
||||
the target data node is the target resource.
|
||||
The target node MUST identify a data resource,
|
||||
not the datastore resource.";
|
||||
}
|
||||
|
||||
leaf point {
|
||||
when "(../operation = 'insert' or ../operation = 'move')"
|
||||
+ "and (../where = 'before' or ../where = 'after')" {
|
||||
description
|
||||
"This leaf only applies for 'insert' or 'move'
|
||||
operations, before or after an existing entry.";
|
||||
}
|
||||
type target-resource-offset;
|
||||
description
|
||||
"The absolute URL path for the data node that is being
|
||||
used as the insertion point or move point for the
|
||||
target of this 'edit' entry.";
|
||||
}
|
||||
|
||||
leaf where {
|
||||
when "../operation = 'insert' or ../operation = 'move'" {
|
||||
description
|
||||
"This leaf only applies for 'insert' or 'move'
|
||||
operations.";
|
||||
}
|
||||
type enumeration {
|
||||
enum before {
|
||||
description
|
||||
"Insert or move a data node before the data resource
|
||||
identified by the 'point' parameter.";
|
||||
}
|
||||
enum after {
|
||||
description
|
||||
"Insert or move a data node after the data resource
|
||||
identified by the 'point' parameter.";
|
||||
}
|
||||
|
||||
enum first {
|
||||
description
|
||||
"Insert or move a data node so it becomes ordered
|
||||
as the first entry.";
|
||||
}
|
||||
enum last {
|
||||
description
|
||||
"Insert or move a data node so it becomes ordered
|
||||
as the last entry.";
|
||||
}
|
||||
}
|
||||
default last;
|
||||
description
|
||||
"Identifies where a data resource will be inserted
|
||||
or moved. YANG only allows these operations for
|
||||
list and leaf-list data nodes that are
|
||||
'ordered-by user'.";
|
||||
}
|
||||
|
||||
anydata value {
|
||||
when "../operation = 'create' "
|
||||
+ "or ../operation = 'merge' "
|
||||
+ "or ../operation = 'replace' "
|
||||
+ "or ../operation = 'insert'" {
|
||||
description
|
||||
"The anydata 'value' is only used for 'create',
|
||||
'merge', 'replace', and 'insert' operations.";
|
||||
}
|
||||
description
|
||||
"Value used for this edit operation. The anydata 'value'
|
||||
contains the target resource associated with the
|
||||
'target' leaf.
|
||||
|
||||
For example, suppose the target node is a YANG container
|
||||
named foo:
|
||||
|
||||
container foo {
|
||||
leaf a { type string; }
|
||||
leaf b { type int32; }
|
||||
}
|
||||
|
||||
The 'value' node contains one instance of foo:
|
||||
|
||||
<value>
|
||||
<foo xmlns='example-foo-namespace'>
|
||||
<a>some value</a>
|
||||
<b>42</b>
|
||||
</foo>
|
||||
</value>
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // grouping yang-patch
|
||||
|
||||
grouping yang-patch-status {
|
||||
|
||||
description
|
||||
"A grouping that contains a YANG container representing the
|
||||
syntax and semantics of a YANG Patch Status response
|
||||
message.";
|
||||
|
||||
container yang-patch-status {
|
||||
description
|
||||
"A container representing the response message sent by the
|
||||
server after a YANG Patch edit request message has been
|
||||
processed.";
|
||||
|
||||
leaf patch-id {
|
||||
type string;
|
||||
mandatory true;
|
||||
description
|
||||
"The 'patch-id' value used in the request.";
|
||||
}
|
||||
|
||||
choice global-status {
|
||||
description
|
||||
"Report global errors or complete success.
|
||||
If there is no case selected, then errors
|
||||
are reported in the 'edit-status' container.";
|
||||
|
||||
case global-errors {
|
||||
uses rc:errors;
|
||||
description
|
||||
"This container will be present if global errors that
|
||||
are unrelated to a specific edit occurred.";
|
||||
}
|
||||
leaf ok {
|
||||
type empty;
|
||||
description
|
||||
"This leaf will be present if the request succeeded
|
||||
and there are no errors reported in the 'edit-status'
|
||||
container.";
|
||||
}
|
||||
}
|
||||
|
||||
container edit-status {
|
||||
description
|
||||
"This container will be present if there are
|
||||
edit-specific status responses to report.
|
||||
If all edits succeeded and the 'global-status'
|
||||
returned is 'ok', then a server MAY omit this
|
||||
container.";
|
||||
|
||||
list edit {
|
||||
key edit-id;
|
||||
|
||||
description
|
||||
"Represents a list of status responses,
|
||||
corresponding to edits in the YANG Patch
|
||||
request message. If an 'edit' entry was
|
||||
skipped or not reached by the server,
|
||||
then this list will not contain a corresponding
|
||||
entry for that edit.";
|
||||
|
||||
leaf edit-id {
|
||||
type string;
|
||||
description
|
||||
"Response status is for the 'edit' list entry
|
||||
with this 'edit-id' value.";
|
||||
}
|
||||
|
||||
choice edit-status-choice {
|
||||
description
|
||||
"A choice between different types of status
|
||||
responses for each 'edit' entry.";
|
||||
leaf ok {
|
||||
type empty;
|
||||
description
|
||||
"This 'edit' entry was invoked without any
|
||||
errors detected by the server associated
|
||||
with this edit.";
|
||||
}
|
||||
case errors {
|
||||
uses rc:errors;
|
||||
description
|
||||
"The server detected errors associated with the
|
||||
edit identified by the same 'edit-id' value.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // grouping yang-patch-status
|
||||
|
||||
}
|
||||
EOF1
|
||||
Loading…
Add table
Add a link
Reference in a new issue