1#!/bin/bash
2
3set -e
4
5if [ -x ./xl ] ; then
6    export LD_LIBRARY_PATH=.:../libxc:../xenstore:
7    XL=./xl
8else
9    XL=xl
10fi
11
12fprefix=tmp.check-xl-vcpupin-parse
13outfile=check-xl-vcpupin-parse.data
14
15usage () {
16cat <<END
17usage: $0 [options]
18
19Tests various vcpu-pinning strings. If run without arguments acts
20as follows:
21 - generates some test data and saves them in $outfile;
22 - tests all the generated configurations (reading them back from
23   $outfile).
24
25An example of a test vector file is provided in ${outfile}-example.
26
27Options:
28 -h         prints this message
29 -r seed    uses seed for initializing the rundom number generator
30            (default: the script PID)
31 -s string  tries using string as a vcpu pinning configuration and
32            reports whether that succeeds or not
33 -o ofile   save the test data in ofile (default: $outfile)
34 -i ifile   read test data from ifile
35END
36}
37
38expected () {
39    cat >$fprefix.expected
40}
41
42# by default, re-seed with our PID
43seed=$$
44failures=0
45
46# Execute one test and check the result against the provided
47# rc value and output
48one () {
49    expected_rc=$1; shift
50    printf "test case %s...\n" "$*"
51    set +e
52    ${XL} -N vcpu-pin 0 all "$@" </dev/null >$fprefix.actual 2>/dev/null
53    actual_rc=$?
54    if [ $actual_rc != $expected_rc ]; then
55        diff -u $fprefix.expected $fprefix.actual
56        echo >&2 "test case \`$*' failed ($actual_rc $diff_rc)"
57        failures=$(( $failures + 1 ))
58    fi
59    set -e
60}
61
62# Write an entry in the test vector file. Format is as follows:
63#  test-string*expected-rc*expected-output
64write () {
65    printf "$1*$2*$3\n" >> $outfile
66}
67
68complete () {
69    if [ "$failures" = 0 ]; then
70        echo all ok.; exit 0
71    else
72        echo "$failures tests failed."; exit 1
73    fi
74}
75
76# Test a specific pinning string
77string () {
78    expected_rc=$1; shift
79    printf "test case %s...\n" "$*"
80    set +e
81    ${XL} -N vcpu-pin 0 all "$@" &> /dev/null
82    actual_rc=$?
83    set -e
84
85    if [ $actual_rc != $expected_rc ]; then
86        echo >&2 "test case \`$*' failed ($actual_rc)"
87    else
88        echo >&2 "test case \`$*' succeeded"
89    fi
90
91    exit 0
92}
93
94# Read a test vector file (provided as $1) line by line and
95# test all the entries it contains
96run ()
97{
98    while read line
99    do
100        if [ ${line:0:1} != '#' ]; then
101            test_string="`echo $line | cut -f1 -d'*'`"
102            exp_rc="`echo $line | cut -f2 -d'*'`"
103            exp_output="`echo $line | cut -f3 -d'*'`"
104
105            expected <<END
106$exp_output
107END
108            one $exp_rc "$test_string"
109        fi
110    done < $1
111
112    complete
113
114    exit 0
115}
116
117while getopts "hr:s:o:i:" option
118do
119    case $option in
120    h)
121        usage
122        exit 0
123        ;;
124    r)
125        seed=$OPTARG
126        ;;
127    s)
128        string 0 "$OPTARG"
129        ;;
130    o)
131        outfile=$OPTARG
132        ;;
133    i)
134        run $OPTARG
135        ;;
136    esac
137done
138
139#---------- test data ----------
140#
141nr_cpus=`xl info | grep nr_cpus | cut -f2 -d':'`
142nr_nodes=`xl info | grep nr_nodes | cut -f2 -d':'`
143nr_cpus_per_node=`xl info -n | sed '/cpu:/,/numa_info/!d' | head -n -1 | \
144    awk '{print $4}' | uniq -c | tail -1 | awk '{print $1}'`
145cat >$outfile <<END
146# WARNING: some of these tests are topology based tests.
147# Expect failures if the topology is not detected correctly
148# detected topology: $nr_cpus CPUs, $nr_nodes nodes, $nr_cpus_per_node CPUs per node.
149#
150# seed used for random number generation: seed=${seed}.
151#
152# Format is as follows:
153#  test-string*expected-return-code*expected-output
154#
155END
156
157# Re-seed the random number generator
158RANDOM=$seed
159
160echo "# Testing a wrong configuration" >> $outfile
161write foo 255 ""
162
163echo "# Testing the 'all' syntax" >> $outfile
164write "all" 0 "cpumap: all"
165write "nodes:all" 0 "cpumap: all"
166write "all,nodes:all" 0 "cpumap: all"
167write "all,^nodes:0,all" 0 "cpumap: all"
168
169echo "# Testing the empty cpumap case" >> $outfile
170write "^0" 0 "cpumap: none"
171
172echo "# A few attempts of pinning to just one random cpu" >> $outfile
173if [ $nr_cpus -gt 1 ]; then
174    for i in `seq 0 3`; do
175        cpu=$(($RANDOM % nr_cpus))
176        write "$cpu" 0 "cpumap: $cpu"
177    done
178fi
179
180echo "# A few attempts of pinning to all but one random cpu" >> $outfile
181if [ $nr_cpus -gt 2 ]; then
182    for i in `seq 0 3`; do
183        cpu=$(($RANDOM % nr_cpus))
184        if [ $cpu -eq 0 ]; then
185            expected_range="1-$((nr_cpus - 1))"
186        elif [ $cpu -eq 1 ]; then
187            expected_range="0,2-$((nr_cpus - 1))"
188        elif [ $cpu -eq $((nr_cpus - 2)) ]; then
189            expected_range="0-$((cpu - 1)),$((nr_cpus - 1))"
190        elif [ $cpu -eq $((nr_cpus - 1)) ]; then
191            expected_range="0-$((nr_cpus - 2))"
192        else
193            expected_range="0-$((cpu - 1)),$((cpu + 1))-$((nr_cpus - 1))"
194        fi
195        write "all,^$cpu" 0 "cpumap: $expected_range"
196    done
197fi
198
199echo "# A few attempts of pinning to a random range of cpus" >> $outfile
200if [ $nr_cpus -gt 2 ]; then
201    for i in `seq 0 3`; do
202        cpua=$(($RANDOM % nr_cpus))
203        range=$((nr_cpus - cpua))
204        cpub=$(($RANDOM % range))
205        cpubb=$((cpua + cpub))
206        if [ $cpua -eq $cpubb ]; then
207            expected_range="$cpua"
208        else
209            expected_range="$cpua-$cpubb"
210        fi
211        write "$expected_range" 0 "cpumap: $expected_range"
212    done
213fi
214
215echo "# A few attempts of pinning to just one random node" >> $outfile
216if [ $nr_nodes -gt 1 ]; then
217    for i in `seq 0 3`; do
218        node=$(($RANDOM % nr_nodes))
219        # this assumes that the first $nr_cpus_per_node (from cpu
220        # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node
221        # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node
222        # to 2*$nr_cpus_per_node-1) are assigned to the second node (node
223        # 1), etc. Expect failures if that is not the case.
224        write "nodes:$node" 0 "cpumap: $((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1)-1))"
225    done
226fi
227
228echo "# A few attempts of pinning to all but one random node" >> $outfile
229if [ $nr_nodes -gt 1 ]; then
230    for i in `seq 0 3`; do
231        node=$(($RANDOM % nr_nodes))
232        # this assumes that the first $nr_cpus_per_node (from cpu
233        # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node
234        # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node
235        # to 2*$nr_cpus_per_node-1) are assigned to the second node (node
236        # 1), etc. Expect failures if that is not the case.
237        if [ $node -eq 0 ]; then
238            expected_range="$nr_cpus_per_node-$((nr_cpus - 1))"
239        elif [ $node -eq $((nr_nodes - 1)) ]; then
240            expected_range="0-$((nr_cpus - nr_cpus_per_node - 1))"
241        else
242            expected_range="0-$((nr_cpus_per_node*node-1)),$((nr_cpus_per_node*(node+1)))-$nr_cpus"
243        fi
244        write "all,^nodes:$node" 0 "cpumap: $expected_range"
245    done
246fi
247
248echo "# A few attempts of pinning to a random range of nodes" >> $outfile
249if [ $nr_nodes -gt 1 ]; then
250    for i in `seq 0 3`; do
251        nodea=$(($RANDOM % nr_nodes))
252        range=$((nr_nodes - nodea))
253        nodeb=$(($RANDOM % range))
254        nodebb=$((nodea + nodeb))
255        # this assumes that the first $nr_cpus_per_node (from cpu
256        # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node
257        # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node
258        # to 2*$nr_cpus_per_node-1) are assigned to the second node (node
259        # 1), etc. Expect failures if that is not the case.
260        if [ $nodea -eq 0 ] && [ $nodebb -eq $((nr_nodes - 1)) ]; then
261            expected_range="all"
262        else
263            expected_range="$((nr_cpus_per_node*nodea))-$((nr_cpus_per_node*(nodebb+1) - 1))"
264        fi
265        write "nodes:$nodea-$nodebb" 0 "cpumap: $expected_range"
266    done
267fi
268
269echo "# A few attempts of pinning to a node but excluding one random cpu" >> $outfile
270if [ $nr_nodes -gt 1 ]; then
271    for i in `seq 0 3`; do
272        node=$(($RANDOM % nr_nodes))
273        # this assumes that the first $nr_cpus_per_node (from cpu
274        # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node
275        # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node
276        # to 2*$nr_cpus_per_node-1) are assigned to the second node (node
277        # 1), etc. Expect failures if that is not the case.
278        cpu=$(($RANDOM % nr_cpus_per_node + nr_cpus_per_node*node))
279        if [ $cpu -eq $((nr_cpus_per_node*node)) ]; then
280            expected_range="$((nr_cpus_per_node*node + 1))-$((nr_cpus_per_node*(node+1) - 1))"
281        elif [ $cpu -eq $((nr_cpus_per_node*node + 1)) ]; then
282            expected_range="$((nr_cpus_per_node*node)),$((nr_cpus_per_node*node + 2))-$((nr_cpus_per_node*(node+1) - 1))"
283        elif [ $cpu -eq $((nr_cpus_per_node*(node+1) - 2)) ]; then
284            expected_range="$((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1) - 3)),$((nr_cpus_per_node*(node+1) - 1))"
285        elif [ $cpu -eq $((nr_cpus_per_node*(node+1) - 1)) ]; then
286            expected_range="$((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1) - 2))"
287        else
288            expected_range="$((nr_cpus_per_node*node))-$((cpu - 1)),$((cpu + 1))-$((nr_cpus_per_node*(node+1) - 1))"
289        fi
290        write "nodes:$node,^$cpu" 0 "cpumap: $expected_range"
291    done
292fi
293
294run $outfile
295