summaryrefslogtreecommitdiff
path: root/tests/ovs-macros.at
blob: 39fbfceeb81f23b7e9ac73d39a6100a499356a90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
AT_TESTED([ovs-vswitchd])
AT_TESTED([ovs-vsctl])

m4_include([m4/compat.m4])

dnl Make AT_SETUP automatically run the ovs_init() shell function
dnl as the first step in every test.
m4_rename([AT_SETUP], [OVS_AT_SETUP])
m4_define([AT_SETUP], [OVS_AT_SETUP($@)
ovs_init
])

dnl Make AT_CLEANUP check for Address Sanitizer errors as the last step
dnl in every test.
m4_rename([AT_CLEANUP], [OVS_AT_CLEANUP])
m4_define([AT_CLEANUP], [ovs_cleanup
OVS_AT_CLEANUP($@)
])

dnl OVS_START_SHELL_HELPERS...OVS_END_SHELL_HELPERS may bracket shell
dnl function definitions that invoke AT_CHECK and other Autotest macros
dnl that can ordinarily be run only within AT_SETUP...AT_CLEANUP.
m4_define([OVS_START_SHELL_HELPERS],
  [m4_ifdef([AT_ingroup], [m4_fatal([$0: AT_SETUP and OVS_DEFINE_SHELL_HELPERS may not nest])])
   m4_define([AT_ingroup])
   m4_divert_push([PREPARE_TESTS])])
m4_define([OVS_END_SHELL_HELPERS], [
   m4_divert_pop([PREPARE_TESTS])
   m4_undefine([AT_ingroup])])

m4_divert_push([PREPARE_TESTS])
[
# Set ovs_base to the base directory in which the test is running and
# initialize the OVS_*DIR environment variables to point to this
# directory.
ovs_init() {
    ovs_base=`pwd`
    trap ovs_on_exit 0
    : > cleanup
    ovs_setenv
}

# Catch testsuite error condition and cleanup test environment by tearing down
# all interfaces and processes spawned.
# User has an option to leave the test environment in error state so that system
# can be poked around to get more information. User can enable this option by setting
# environment variable OVS_PAUSE_TEST=1. User needs to press CTRL-D to resume the
# cleanup operation.
ovs_pause() {
    echo "====================================================="
    echo "Set following environment variable to use various ovs utilities"
    echo "export OVS_RUNDIR=$ovs_base"
    echo "Press ENTER to continue: "
    read
}

ovs_on_exit () {
    if [ ! -z "${OVS_PAUSE_TEST}" ] && [ -z $at_verbose ]; then
        trap '' INT
        ovs_pause
    fi
    . "$ovs_base/cleanup"
}

# With no parameter or an empty parameter, sets the OVS_*DIR
# environment variables to point to $ovs_base, the base directory in
# which the test is running.
#
# With a parameter, sets them to $ovs_base/$1.
ovs_setenv() {
    sandbox=$1
    ovs_dir=$ovs_base${1:+/$1}
    OVS_RUNDIR=$ovs_dir; export OVS_RUNDIR
    OVS_LOGDIR=$ovs_dir; export OVS_LOGDIR
    OVS_DBDIR=$ovs_dir; export OVS_DBDIR
    OVS_SYSCONFDIR=$ovs_dir; export OVS_SYSCONFDIR
    OVS_PKGDATADIR=$ovs_dir; export OVS_PKGDATADIR
}

# Prints the integers from $1 to $2, increasing by $3 (default 1) on stdout.
seq () {
    if test $# = 1; then
        set 1 $1
    fi
    while test $1 -le $2; do
        echo $1
        set `expr $1 + ${3-1}` $2 $3
    done
}

if test "$IS_WIN32" = "yes"; then
    pwd () {
        command pwd -W "$@"
    }

    diff () {
        command diff --strip-trailing-cr "$@"
    }

    # tskill is more effective than taskkill but it isn't always installed.
    if (tskill //?) >/dev/null 2>&1; then :; else
        tskill () { taskkill //F //PID $1 >/dev/null; }
    fi

    kill () {
        signal=
        retval=0
        for arg; do
            arg=$(echo $arg | tr -d '\n\r')
            case $arg in
            -*) signal=$arg ;;
            [1-9][0-9]*)
                # tasklist always returns 0.
                # If pid does exist, there will be a line with the pid.
                if tasklist //fi "PID eq $arg" | grep $arg >/dev/null; then
                    if test "X$signal" != "X-0"; then
                        tskill $arg
                    fi
                else
                    retval=1
                fi
                ;;
            esac
        done
        return $retval
    }
fi

# parent_pid PID
#
# Prints the PID of the parent of process PID.
parent_pid () {
    # Using "ps" is portable to any POSIX system, but busybox "ps" (used in
    # e.g. Alpine Linux) is noncompliant, so we use a Linux-specific approach
    # when it's available.  We check the format of the status file to avoid
    # the NetBSD file with the same name but different contents.
    if grep -E '^PPid:[[:space:]]*[0-9]*$' /proc/$1/status > /dev/null 2>&1; then
        sed -n 's/^PPid:	\([0-9]*\)/\1/p' /proc/$1/status
    else
        ps -o ppid= -p $1
    fi
}

# kill_ovs_vswitchd [PID]
#
# Signal the ovs-vswitchd daemon to exit gracefully and wait for it to
# terminate or kill it if that takes too long.
#
# It is used to cleanup all sorts of tests and results. It can't assume
# any state, including the availability of PID file which can be provided.
kill_ovs_vswitchd () {
    # Use provided PID or save the current PID if available.
    TMPPID=$1
    if test -z "$TMPPID"; then
        TMPPID=$(cat $OVS_RUNDIR/ovs-vswitchd.pid 2>/dev/null)
    fi

    # Tell the daemon to terminate gracefully
    ovs-appctl -t ovs-vswitchd exit --cleanup 2>/dev/null

    # Nothing else to be done if there is no PID
    test -z "$TMPPID" && return

    for i in 1 2 3 4 5 6 7 8 9; do
        # Check if the daemon is alive.
        kill -0 $TMPPID 2>/dev/null || return

        # Fallback to whole number since POSIX doesn't require
        # fractional times to work.
        sleep 0.1 || sleep 1
    done

    # Make sure it is terminated.
    kill $TMPPID
}

# Normalize the output of 'wc' to match POSIX.
# POSIX says 'wc' should print "%d %d %d", but GNU prints "%7d %7d %7d".
# POSIX says 'wc -l' should print "%d %s", but BSD prints "%8d".
#
# This fixes all of those (it will screw up filenames that contain
# multiple sequential spaces, but that doesn't really matter).
wc () {
   command wc "$@" | tr -s ' ' ' ' | sed 's/^ *//'
}

uuidfilt () {
    $PYTHON3 "$top_srcdir"/tests/uuidfilt.py "$@"
}

# run_as PROGRAM_NAME COMMAND [ARG...]
#
# Runs a command with argv[0] set to PROGRAM_NAME, if possible, in a
# subshell.  Most utilities print argc[0] as part of their messages,
# so this makes it easier to figure out which particular utility
# prints a message if a bunch of identical processes are running.
#
# Not all shells support "exec -a NAME", so test for it.
if (exec -a myname true 2>/dev/null); then
    run_as () {
        (exec -a "$@")
    }
else
    run_as () {
        shift
        (exec "$@")
    }
fi
]
m4_divert_pop([PREPARE_TESTS])

OVS_START_SHELL_HELPERS
ovs_cleanup() {
    if test "$(echo asan.*)" != 'asan.*'; then
        echo "Address Sanitizer reported errors in:" asan.*
        cat asan.*
        AT_FAIL_IF([:])
    fi
    if test "$(echo ubsan.*)" != 'ubsan.*'; then
        echo "Undefined Behavior Sanitizer reported errors in:" ubsan.*
        cat ubsan.*
        AT_FAIL_IF([:])
    fi
}

ovs_wait () {
    echo "$1: waiting $2..." >&AS_MESSAGE_LOG_FD

    # First try the condition without waiting.
    if ovs_wait_cond; then echo "$1: wait succeeded immediately" >&AS_MESSAGE_LOG_FD; return 0; fi

    # Try a quick sleep, so that the test completes very quickly
    # in the normal case.  POSIX doesn't require fractional times to
    # work, so this might not work.
    sleep 0.1
    if ovs_wait_cond; then echo "$1: wait succeeded quickly" >&AS_MESSAGE_LOG_FD; return 0; fi

    # Then wait up to OVS_CTL_TIMEOUT seconds.
    local d
    for d in `seq 1 "$OVS_CTL_TIMEOUT"`; do
        sleep 1
        if ovs_wait_cond; then echo "$1: wait succeeded after $d seconds" >&AS_MESSAGE_LOG_FD; return 0; fi
    done

    echo "$1: wait failed after $d seconds" >&AS_MESSAGE_LOG_FD
    ovs_wait_failed
    AT_FAIL_IF([:])
}
OVS_END_SHELL_HELPERS
m4_define([OVS_WAIT], [dnl
ovs_wait_cond () {
    $1
}
ovs_wait_failed () {
    :
    $2
}
ovs_wait "AS_ESCAPE([$3])" "AS_ESCAPE([$4])"
])

dnl OVS_WAIT_UNTIL(COMMAND, [IF-FAILED])
dnl
dnl Executes shell COMMAND in a loop until it returns zero.  If COMMAND does
dnl not return zero within a reasonable time limit, executes the commands
dnl in IF-FAILED (if provided) and fails the test.
m4_define([OVS_WAIT_UNTIL],
  [AT_FAIL_IF([test "$#" -ge 3])
   dnl The second argument should not be a number (confused with AT_CHECK ?).
   AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null])
   OVS_WAIT([$1], [$2], [AT_LINE], [until $1])])

dnl OVS_WAIT_UNTIL_EQUAL(COMMAND, OUTPUT)
dnl
dnl Executes shell COMMAND in a loop until it returns zero and the output
dnl equals OUTPUT.  If COMMAND does not return zero or a desired output within
dnl a reasonable time limit, fails the test.
m4_define([OVS_WAIT_UNTIL_EQUAL],
  [AT_FAIL_IF([test "$#" -ge 3])
   echo "$2" > wait_until_expected
   OVS_WAIT_UNTIL([$1 | diff -u wait_until_expected - ])])

dnl OVS_WAIT_WHILE(COMMAND, [IF-FAILED])
dnl
dnl Executes shell COMMAND in a loop until it returns nonzero.  If COMMAND does
dnl not return nonzero within a reasonable time limit, executes the commands
dnl in IF-FAILED (if provided) and fails the test.
m4_define([OVS_WAIT_WHILE],
  [AT_FAIL_IF([test "$#" -ge 3])
   dnl The second argument should not be a number (confused with AT_CHECK ?).
   AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null])
   OVS_WAIT([if $1; then return 1; else return 0; fi], [$2],
            [AT_LINE], [while $1])])

dnl OVS_APP_EXIT_AND_WAIT(DAEMON)
dnl
dnl Ask the daemon named DAEMON to exit, via ovs-appctl, and then wait for it
dnl to exit.
m4_define([OVS_APP_EXIT_AND_WAIT],
  [AT_CHECK([test -e $OVS_RUNDIR/$1.pid])
   TMPPID=$(cat $OVS_RUNDIR/$1.pid)
   AT_CHECK(m4_if([$1],[ovs-vswitchd],
                  [ovs-appctl -t $1 exit --cleanup],
                  [ovs-appctl -t $1 exit]))
   OVS_WAIT_WHILE([kill -0 $TMPPID 2>/dev/null])])

dnl OVS_APP_EXIT_AND_WAIT_BY_TARGET(TARGET, PIDFILE)
dnl
dnl Ask the daemon identified by TARGET to exit, via ovs-appctl (using the target
dnl argument), and then wait for it to exit.
m4_define([OVS_APP_EXIT_AND_WAIT_BY_TARGET],
  [AT_CHECK([test -e $2])
   TMPPID=$(cat $2)
   AT_CHECK([ovs-appctl --target=$1 exit])
   OVS_WAIT_WHILE([kill -0 $TMPPID 2>/dev/null])])

dnl OVS_DAEMONIZE([command], [pidfile])
dnl
dnl Run 'command' as a background process and record its pid to 'pidfile' to
dnl allow cleanup on exit.
m4_define([OVS_DAEMONIZE],
   [$1 & echo $! > $2
    on_exit "kill `cat $2`"
   ])

dnl on_exit "COMMAND"
dnl
dnl Add the shell COMMAND to a collection executed when the current test
dnl completes, as a cleanup action.  (The most common use is to kill a
dnl daemon started by the test.  This is important to prevent tests that
dnl start daemons from hanging at exit.)
dnl
dnl Cleanup commands are executed in the reverse order of calls to this
dnl function.
m4_divert_text([PREPARE_TESTS], [dnl
on_exit () {
    (echo "$1"; cat cleanup) > cleanup.tmp
    mv cleanup.tmp cleanup
}
])

dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64:
m4_ifndef([AS_VAR_APPEND],
  [m4_divert_text([PREPARE_TESTS],
    [as_var_append () {
       eval $1=\$$1\$2
     }
])
   m4_define([AS_VAR_APPEND], [as_var_append $1 $2])])

dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64:
m4_ifndef([AT_CHECK_UNQUOTED],
  [m4_define([AT_CHECK_UNQUOTED],
  [_AT_CHECK([$1], [$2], AS_ESCAPE(m4_dquote(m4_expand([$3])), [""]),
    AS_ESCAPE(m4_dquote(m4_expand([$4])),[""]), [$5], [$6])])])

dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64:
m4_ifndef([AT_SKIP_IF],
  [m4_define([AT_SKIP_IF],
    [AT_CHECK([($1) \
    && exit 77 || exit 0], [0], [ignore], [ignore])])])

dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64:
m4_ifndef([AT_FAIL_IF],
  [m4_define([AT_FAIL_IF],
    [AT_CHECK([($1) \
    && exit 99 || exit 0], [0], [ignore], [ignore])])])

dnl Certain Linux distributions, like CentOS, have default iptable rules
dnl to reject input traffic from bridges such as br-underlay.
dnl Add a rule to always accept the traffic.
m4_define([IPTABLES_ACCEPT],
  [AT_CHECK([iptables -I INPUT 1 -i $1 -j ACCEPT])
   on_exit 'iptables -D INPUT 1 -i $1'])