Add tests for yang patch
This commit is contained in:
parent
e0ee365958
commit
8f110331d5
4 changed files with 2098 additions and 0 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
338
test/test_restconf_yang_patch.sh
Executable file
338
test/test_restconf_yang_patch.sh
Executable file
|
|
@ -0,0 +1,338 @@
|
||||||
|
#!/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 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": "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: 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 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": "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 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