1#!/usr/bin/env bash
2# Transaction functionality
3# The test uses two backend plugins (main and nacm) that logs to a file and a
4# netconf client to push operation. The tests then look at the log.
5# The test assumes the two plugins recognize the -- -t argument which includes
6# that one of them fails at validation at one point
7# The tests are as follows (first five only callbacks per se; then data vector tests)
8# 1. Validate-only transaction
9# 2. Commit transaction
10# 3. Validate system-error (invalid type detected by system)
11# 4. Validate user-error (invalidation by user callback)
12# 5. Commit user-error (invalidation by user callback)
13# -- to here only basic callback tests (that they occur). Below transaction data
14# 6. Detailed transaction vector add/del/change tests
15# For the last test, the yang is a list with three members, so that you can do
16# add/delete/change in a single go.
17# The user-error uses a trick feature in the example nacm plugin which is started
18# with an "error-trigger" xpath which triggers an error. This also toggles between
19# validation and commit errors
20
21# Magic line must be first in script (see README.md)
22s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
23
24APPNAME=example
25
26cfg=$dir/conf_yang.xml
27fyang=$dir/trans.yang
28flog=$dir/backend.log
29touch $flog
30
31# Used as a trigger for user-validittion errors, eg <a>$errnr</a> = <a>42</a> is invalid
32errnr=42
33
34cat <<EOF > $fyang
35module trans{
36   yang-version 1.1;
37   namespace "urn:example:clixon";
38   prefix ex;
39   container x {
40    list y {
41      key "a";
42      leaf a {
43        type int32;
44      }
45      leaf b {
46        description "change this (also use to check invalid)";
47        type int32{
48          range "0..100";
49        }
50      }
51      leaf c {
52        description "del this";
53        type int32;
54      }
55      leaf d {
56        description "add this";
57        type int32;
58      }
59    }
60  }
61}
62EOF
63
64cat <<EOF > $cfg
65<clixon-config xmlns="http://clicon.org/config">
66  <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
67  <CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
68  <CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
69  <CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
70  <CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
71  <CLICON_NETCONF_DIR>/usr/local/lib/$APPNAME/netconf</CLICON_NETCONF_DIR>
72  <CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
73  <CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
74  <CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
75  <CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
76  <CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
77  <CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
78</clixon-config>
79EOF
80
81# Check statements in log
82# arg1: a statement to look for
83# arg2: expected line number
84checklog(){
85    s=$1 # statement
86    l0=$2 # linenr
87    new "Check $s in log"
88#    echo "grep \"transaction_log $s line:$l0\"  $flog"
89    t=$(grep -n "transaction_log $s" $flog)
90    if [ -z "$t" ]; then
91	echo -e "\e[31m\nError in Test$testnr [$testname]:"
92	if [ $# -gt 0 ]; then
93	    echo "Not found in log"
94	    echo
95	fi
96	echo -e "\e[0m"
97	exit -1
98    fi
99    l1=$(echo "$t" | awk -F ":" '{print $1}')
100    if [ $l1 -ne $l0 ]; then
101	echo -e "\e[31m\nError in Test$testnr [$testname]:"
102	if [ $# -gt 0 ]; then
103	    echo "Expected match on line $l0, found on $l1"
104	    echo
105	fi
106	echo -e "\e[0m"
107	exit -1
108    fi
109}
110
111new "test params: -f $cfg -l f$flog -- -t -v /x/y[a=$errnr]" # Fail on this
112# Bring your own backend
113if [ $BE -ne 0 ]; then
114    # kill old backend (if any)
115    new "kill old backend"
116    sudo clixon_backend -zf $cfg
117    if [ $? -ne 0 ]; then
118	err
119    fi
120    new "start backend  -s init -f $cfg -l f$flog -- -t -v /x/y[a=$errnr]"
121    start_backend -s init -f $cfg -l f$flog -- -t -v /x/y[a=$errnr] # -t means transaction logging
122
123    new "waiting"
124    wait_backend
125fi
126
127let nr=0
128
129new "Basic transaction to add top-level x"
130expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$nr</a></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
131
132new "Commit base"
133expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
134
135let line=14 # Skipping basic transaction
136
137# 1. validate(-only) transaction
138let nr++
139let line
140new "1. Validate-only transaction"
141expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$nr</a></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
142
143new "Validate-only validate"
144expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
145
146xml="<y><a>$nr</a></y>"
147for op in begin validate complete end; do
148    checklog "$nr main_$op add: $xml" $line
149    let line++
150    checklog "$nr nacm_$op add: $xml" $line
151    let line++
152done
153
154new "Validate-only discard-changes"
155expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
156
157# 2. Commit transaction
158let nr++
159new "2. Commit transaction config"
160expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$nr</a></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
161
162new "Commit transaction: commit"
163expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
164
165xml="<y><a>$nr</a></y>"
166for op in begin validate complete commit commit_done end; do
167    checklog "$nr main_$op add: $xml" $line
168    let line++
169    checklog "$nr nacm_$op add: $xml" $line
170    let line++
171done
172
173# 3. Validate only system-error (invalid type detected by system)
174let nr++
175new "3. Validate system-error config (9999 not in range)"
176expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$nr</a><b>9999</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
177
178new "Validate system-error validate (should fail)"
179expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>b</bad-element></error-info><error-severity>error</error-severity><error-message>Number 9999 out of range: 0 - 100</error-message></rpc-error></rpc-reply>]]>]]>$"
180
181new "Validate system-error discard-changes"
182expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
183
184for op in begin abort; do
185    checklog "$nr main_$op add: <y><a>$nr</a><b>9999</b></y>" $line
186    let line++
187    checklog "$nr nacm_$op add: <y><a>$nr</a><b>9999</b></y>" $line
188    let line++
189done
190
191# 4. Validate only user-error (invalidation by user callback)
192let nr++
193new "4. Validate user-error config ($errnr is invalid)"
194expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$errnr</a></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
195
196new "Validate user-error validate (should fail)"
197expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>User error</error-message></rpc-error></rpc-reply>]]>]]>$"
198
199new "Validate user-error discard-changes"
200expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
201
202for op in begin validate; do
203    checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line
204    let line++
205    checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line
206    let line++
207done
208let line++ # error message
209for op in abort; do
210    checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line
211    let line++
212    checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line
213    let line++
214done
215
216# 5. Commit user-error (invalidation by user callback)
217# XXX Note Validate-only user-error must immediately preceede this due to toggling
218# in nacm/transaction example test module
219let nr++
220new "5. Commit user-error ($errnr is invalid)"
221expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$errnr</a></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
222
223new "Commit user-error commit"
224expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>User error</error-message></rpc-error></rpc-reply>]]>]]>$"
225
226new "Commit user-error discard-changes"
227expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
228
229for op in begin validate complete commit ; do
230    checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line
231    let line++
232    checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line
233    let line++
234done
235
236let line++ # error message
237checklog "$nr main_revert add: <y><a>$errnr</a></y>" $line
238let line++
239for op in abort; do
240    checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line
241    let line++
242    checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line
243    let line++
244done
245
246# 6. Detailed transaction vector add/del/change tests
247let nr++
248let base=nr
249new "Add base <a>$base entry"
250expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$base</a><b>0</b><c>0</c></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
251
252new "netconf commit base"
253expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
254#Ignore
255let line+=12
256
257let nr++
258new "6. netconf mixed change: change b, del c, add d"
259expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$base</a><b>42</b><d>0</d></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
260
261new "netconf commit change"
262expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
263
264# Check complete transaction $nr:
265for op in begin validate complete commit commit_done; do
266    checklog "$nr main_$op add: <d>0</d>" $line
267    let line++
268    checklog "$nr main_$op change: <b>0</b><b>42</b>" $line
269    let line++
270    checklog "$nr nacm_$op add: <d>0</d>" $line
271    let line++
272    checklog "$nr nacm_$op change: <b>0</b><b>42</b>" $line
273    let line++
274done
275
276# End is special because change does not have old element
277checklog "$nr main_end add: <d>0</d>" $line
278let line++
279# This check does not work if  MOVE_TRANS_END is set
280checklog "$nr main_end change: <b>42</b>" $line
281let line+=3 # skip nacm
282
283let nr++
284let base=nr
285new "Add base <a>$base entry"
286expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$base</a><d>1</d></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
287
288new "netconf commit base"
289expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
290let line+=12
291
292# Variant check that only b,c
293let nr++
294new "7. netconf insert b,c between end-points"
295expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$base</a><b>1</b><c>1</c></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
296
297new "netconf commit base"
298expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
299
300# check complete
301for op in begin validate complete commit commit_done end; do
302    checklog "$nr main_$op add: <b>1</b><c>1</c>" $line
303    let line++
304    checklog "$nr nacm_$op add: <b>1</b><c>1</c>" $line
305    let line++
306done
307
308if [ $BE -eq 0 ]; then
309    exit # BE
310fi
311
312new "Kill backend"
313# Check if premature kill
314pid=$(pgrep -u root -f clixon_backend)
315if [ -z "$pid" ]; then
316    err "backend already dead"
317fi
318# kill backend
319stop_backend -f $cfg
320
321rm -rf $dir
322
323unset nr
324