Merge pull request #2003 from mcnewton/v3.0.x
[freeradius.git] / scripts / raduat
1 #!/bin/bash
2
3 # Simple test wrapper around radclient to allow automated UATs
4 #
5 # Author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
6 # Copyright 2014-2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
7 # Copyright 2015 The FreeRADIUS Project
8
9 # A POSIX variable
10 OPTIND=1         # Reset in case getopts has been used previously in the shell.
11
12 # Environmental variables
13 : ${TESTDIR=$(dirname $0)"/tests"}
14 : ${RADCLIENT='radclient'}
15 : ${FILTER_SUFFIX='_expected'}
16 : ${DICTPATH=$(dirname $0)/share}
17 PATH="$(dirname $0)/bin:${PATH}"
18
19 # Initialize our own variables
20 verbose=0
21 cluster=
22 role=
23 type=
24 parallel=40
25 retries=3
26 timeout=2
27 target='127.0.0.1'
28 secret='testing123'
29
30 # Some very basic logging functions
31 function ERROR
32 {
33     echo "$@" 1>&2;
34 }
35
36 function INFO
37 {
38     echo "$@"
39 }
40
41 function DEBUG
42 {
43     if [ $verbose -gt 0 ]; then
44         echo "$@"
45     fi
46 }
47
48 function show_help
49 {
50     echo $(basename $0)" [options] [-- <test_glob0> <test_glob1> <test_globN>]"
51     echo "  -h                        Display this help message."
52     echo "  -H <host>[:port]          Send test packets to specified host and port (defaults to 127.0.0.1)"
53     echo "  -v                        Verbose mode."
54     echo "  -p <number>               Run tests in parallel (defaults to 20)."
55     echo "  -s <secret>               Shared secret."
56     if [ ! -z "$role_types" ]; then
57         echo "  -c <cluster>              Specify cluster type one of ($cluster_types)."
58         echo "  -r <type>                 Specify server role one of ($role_types)."
59         echo
60         echo "Note: Test path globs are relative to ${TESTDIR}/<cluster>/<type>/"
61     fi
62
63     echo
64     echo "For role based test file layout create test files under ${TESTDIR}/<cluster>/<type>"
65     echo "Where <cluster> and <type> are substrings found in the FQDN of <host>."
66     echo "For simplified test layout create test files under ${TESTDIR}"
67     echo
68     echo "The directory containing the tests should contains pairs of request files and filter files."
69     echo "The request file name must contain 'test<num><num><num>."
70     echo "The filter name must match the test name but with a '${FILTER_SUFFIX}' suffix."
71     echo "For example:"
72     echo "  ${TESTDIR}/test000_my_first_test"
73     echo "  ${TESTDIR}/test000_my_first_test${FILTER_SUFFIX}"
74     echo
75     echo "The directory containing the tests may have multiple subdirectories to group the tests."
76 }
77
78 RADCLIENT=$(which "$RADCLIENT")
79 if [ ! -x "$RADCLIENT" ]; then
80     ERROR "Can't find radclient binary, modify your \$PATH or set RADCLIENT"
81     exit 64
82 fi
83
84 if [ ! -d "$TESTDIR" ]; then
85     ERROR "Test dir $TESTDIR does not exist, create it or specify it with TESTDIR=<dir>"
86     show_help
87     exit 64
88 fi
89
90 # Definitions (build these dynamically by looking at the files under tests)
91 cluster_dirs=$(find "$TESTDIR/" -mindepth 1 -maxdepth 1 -type d)
92 cluster_types=$(echo $cluster_dirs | sed 's/\s/ /g')
93
94 role_types=
95 for i in $cluster_dirs; do
96     for j in $(find "$TESTDIR/$(basename $i)/" -mindepth 1 -maxdepth 1 -type d); do
97         role=$(basename "$j")
98         if [ "$role_types" == '' ]; then
99             role_types="$role"
100         else
101             role_types+="\n$role"
102         fi
103     done
104 done
105
106 if [ -z "$role_types" ]; then
107     DEBUG "Using simple test file layout"
108 else
109     DEBUG "Using role based test file layout"
110     role_types=$(echo -e "$role_types" | sort | uniq)   # Remove duplicates
111     role_types=$(echo $role_types | sed 's/\s/ /g')     # Change \n back to spaces
112 fi
113
114 while getopts "h?H:vc:r:s:p:" opt; do
115     case "$opt" in
116     h|\?)
117         show_help
118         exit 0
119         ;;
120
121     v)
122         verbose=1
123         ;;
124
125     c)
126         found=0
127         for i in $cluster_types; do
128             if [ "$i" == "$OPTARG" ]; then
129                 found=1
130             fi
131         done
132         if [ $found -ne 1 ]; then
133             ERROR "'$OPTARG' is not a valid cluster type"
134             show_help
135             exit 64
136         fi
137         cluster="$OPTARG"
138         ;;
139
140     r)
141         found=0
142         for i in $role_types; do
143             if [ "$i" == "$OPTARG" ]; then
144                 found=1
145             fi
146         done
147         if [ $found -ne 1 ]; then
148             ERROR "'$OPTARG' is not a valid role type"
149             show_help
150             exit 64
151         fi
152         role="$OPTARG"
153         ;;
154
155     s)
156         secret="$OPTARG"
157         ;;
158
159     p)
160         if ! echo "$OPTARG" | grep -E '^[0-9]+$' > /dev/null; then
161             ERROR "Non integer argument '$OPTARG' specified for -p"
162             show_help
163             exit 64
164         fi
165         parallel=$OPTARG
166         ;;
167
168     H)
169         target="$OPTARG"
170         ;;
171
172     esac
173 done
174
175 shift $((OPTIND-1))
176
177 [ "$1" = "--" ] && shift
178 test_files=$@
179
180 #
181 #  Match keywords from the hostname to clusters or roles
182 #
183 if [ ! -z "$role_types" ]; then
184     this_host=$(hostname -f)
185     for tok in $(echo "$this_host" | sed 's/\./ /g'); do
186         for key in ${cluster_types}; do
187             if echo "$tok" | grep "$key" > /dev/null && [ "$cluster" = '' ]; then cluster="$key"; fi
188         done
189         for key in ${role_types}; do
190             if echo "$tok" | grep "$key" > /dev/null && [ "$role" = '' ]; then role="$key"; fi
191         done
192     done
193
194     if [ "$cluster" == '' ]; then
195         ERROR "Couldn't determine the cluster $this_host belongs to";
196         show_help
197         exit 64;
198     fi
199
200     if [ "$role" == '' ]; then
201         ERROR "Couldn't determine the role $this_host performs";
202         show_help
203         exit 64;
204     fi
205
206     test_path="${TESTDIR}/${cluster}/${role}"
207 #
208 #  Otherwise just use the tests in the test dir
209 #
210 else
211     test_path="${TESTDIR}"
212 fi
213
214 if [ "$test_files" != '' ]; then
215     tmp=
216     for glob in $test_files; do
217         # Filter out response files (makes wildcards easier), and expand the globs
218         for file in $(find "${test_path}" -depth -path "*${glob}" -and -not -path "*${FILTER_SUFFIX}" -and '(' -type f -or -type l ')'); do
219             tmp+="${file} "
220         done
221     done
222     test_files="${tmp}"
223 else
224     # Lexicographical, depth-first
225     test_files=$(find "$test_path" -depth -path '*test[0-9][0-9][0-9]*' -and -not -path "*${FILTER_SUFFIX}" -and '(' -type f -or -type l ')')
226     if [ "$test_files" == '' ]; then
227         ERROR "No test files found in $test_path"
228         exit 64;
229     fi
230     INFO "Executing"$(echo "$test_files" | wc -l)" test(s) from ${test_path}"
231 fi
232
233 #
234 #  Check if we got any test files
235 #
236 if [ "$test_files" == '' ]; then
237     ERROR "No test files to process"
238     exit 1
239 fi
240
241 #
242 #  Output which files were going to be using for testing
243 #
244 if [ $verbose -eq 0 ]; then
245     INFO "Executing specified tests"
246     INFO "Use -v to see full list"
247 else
248     INFO "Executing specified tests:"
249     for i in $test_files; do
250         DEBUG "$i"
251     done
252 fi
253
254 #
255 #  Figure out which tests we can munge into a single file which we can
256 #  use to parallelise testing
257 #
258 base=$(basename $0)
259 packets=$(mktemp -t "${base}XXX") || exit 1
260 filters=$(mktemp -t "${base}XXX") || exit 1
261
262 args=
263 file_args=
264 serial_file_args=
265 for i in $test_files; do
266     if [ ! -f "$i" -a ! -L "$i" ]; then
267         INFO "Skipping $i: not file"
268         continue
269     fi
270
271     if [ ! -r "$i" ]; then
272         INFO "Skipping $i: not readable (check permissions)"
273         continue
274     fi
275
276     expected="${i}${FILTER_SUFFIX}"
277     if [ ! -f "$expected" -a ! -L "$expected" ]; then
278         DEBUG "$i cannot be parallelised: Can't find 'expected' file"
279         file_args+=" -f \"$i\""
280         continue
281     fi
282
283     if [ ! -r "$expected" ]; then
284         INFO "$i cannot be parallelised: 'expected' file not readable"
285         file_args+=" -f \"${i}:${expected}\""
286         continue
287     fi
288
289     if head -n 1 "$i" | grep -i -E '^#\s*serial' > /dev/null; then
290         DEBUG "$i marked as serial only"
291         serial_file_args+=" -f \"${i}:${expected}\""
292         continue
293     fi
294
295     # Else add it to the master test file
296     printf '%s\n' "$(cat "$i")" >> "$packets"
297
298     # Add the name of the file so it appears in radclient debug output
299     # and can later be specified with -v -- <test> to drill down.
300     echo "Radclient-Test-Name := \""$(echo "$i" | sed -e "s@${test_path}/\?@@")"\"" >> "$packets"
301     echo >> "$packets"
302     printf '%s\n' "$(cat "${i}_expected")" >> "$filters"
303     echo >> "$filters"
304 done
305
306 if [ `cat "$packets" | wc -l` -gt 0 ]; then
307     file_args+=" -f \"${packets}:${filters}\""
308 fi
309
310 if [ ! -z "$file_args" ]; then
311         args="$file_args"
312
313         if [ $verbose -ne 0 ]; then
314                 args+=" -x"
315         fi
316
317         args+=" -s"
318         args+=" -t \"$timeout\""
319         args+=" -r \"$retries\""
320         args+=" -p \"$parallel\""
321         args+=" \"$target\""
322         args+=" auto"
323         args+=" \"$secret\""
324
325         DEBUG "Executing: radclient $args"
326         eval radclient $args; ret=$?
327         INFO "(Parallelised tests)"
328         INFO ""
329
330         rm -f "$packets" 2>&1 > /dev/null
331         rm -f "$filters" 2>&1 > /dev/null
332
333         if [ $ret -ne 0 ]; then
334                 ERROR "One or more tests failed (radclient exited with $ret)"
335                 exit $ret
336         fi
337 fi
338
339 if [ ! -z "$serial_file_args" ]; then
340         args="$serial_file_args"
341
342         if [ $verbose -ne 0 ]; then
343                 args+=" -x"
344         fi
345
346         args+=" -s"
347         args+=" -t \"$timeout\""
348         args+=" -r \"$retries\""
349         args+=" -p 1"
350         args+=" \"$target\""
351         args+=" auto"
352         args+=" \"$secret\""
353
354         DEBUG "Executing: radclient $args"
355         eval radclient $args; ret=$?
356         INFO "(Serialised tests)"
357
358         if [ $ret -ne 0 ]; then
359                 ERROR "One or more tests failed (radclient exited with $ret)"
360                 exit $ret
361         fi
362 fi
363
364 exit 0