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