]> git.ipfire.org Git - thirdparty/lldpd.git/blame - tests/integration-tests.in
travis: also try to build OSX package
[thirdparty/lldpd.git] / tests / integration-tests.in
CommitLineData
8cd1f2d0
VB
1#!/bin/sh
2
3# Integration tests for lldpd. Those tests only run on Linux. They
4# spawn several VM and connect them together, then check lldpcli
5# results to see if everything is in order.
6
7# lldpd should be configure with:
8# ../configure --localstatedir=/var --sysconfdir=/etc --prefix=/usr CFLAGS="-O0 -g"
9
10LABNAME=lldpd
11
12set -e
13
14log_begin_msg () {
15 echo "[…] $1... "
16}
17log_ok_msg () {
18 echo "[✔] $1."
19}
20log_warn_msg () {
21 echo "[⚡] $1!"
22}
23log_error_msg () {
24 echo "[✘] $1!"
25 exit 1
26}
27log_info_msg () {
28 echo "[∗] $1."
29}
30
31check_kernel() {
32 log_begin_msg "Checking kernel version"
33 [ -f "$KERNEL" ] || log_error_msg "Unable to find kernel $KERNEL"
34 [ -r "$KERNEL" ] || log_error_msg "Kernel $KERNEL is not readable.\n Try \`setfacl -m u:$USER:r $KERNEL'"
35
36 # A recent version of `file` is able to extract the
37 # information. Since it is not widely available, let use some hack
38 # method.
39 VERSION=$(cat <<EOF |
40cat
41gunzip \\\037\\\213\\\010 xy
42unxz \\\3757zXZ\\\000 abcde
43bunzip2 BZh xy
44unlzma \\\135\\\0\\\0\\\0 xxx
45EOF
46 while read cmd sig1 sig2; do
47 case $sig1,$sig2 in
48 ,) poss="0:_" ;;
49 *) poss=$(tr "${sig1}\n${sig2}" "\n${sig2}=" < "$KERNEL" | grep -abo "^${sig2}" || true) ;;
50 esac
51 [ -n "$poss" ] || continue
52 for pos in $poss; do
53 pos=${pos%%:*}
54 tail -c+$pos "$KERNEL" | $cmd 2> /dev/null | strings -20 | \
55 grep ^Linux.version | head -1
56 done
57 done | head -1)
58
59 [ -n "$VERSION" ] || log_error_msg "Unable to determine version for $KERNEL"
60 VERSION="${VERSION#Linux version }"
61 VERSION="${VERSION%% *}"
62 log_ok_msg "Found kernel $VERSION"
63
64 log_begin_msg "Check kernel configuration"
65 CONFIG="$(dirname $KERNEL)/config-$VERSION"
66 [ -f "$CONFIG" ] || log_error_msg "Unable to find configuration file $CONFIG"
67 cat <<EOF | while read el; do
689P_FS=[ym]
69NET_9P=[ym]
70NET_9P_VIRTIO=[ym]
71VIRTIO=[ym]
72VIRTIO_PCI=[ym]
73SERIAL_8250=y
74SERIAL_8250_CONSOLE=y
75TMPFS=y
76BLK_DEV_INITRD=y
77DEVTMPFS=[ym]
78EOF
79 grep -qx "CONFIG_$el" $CONFIG || log_error_msg "Kernel not configured with CONFIG_$el"
80 done
81
82 log_begin_msg "Search for modules"
83 for dir in "$(dirname $KERNEL)/lib/modules/$VERSION" "/lib/modules/$VERSION"; do
84 [ -d $dir ] || continue
85 MODULES="$dir"
86 break
87 done
88 if [ -z "$MODULES" ]; then
89 log_warn_msg "Unable to find module directory"
90 else
91 log_ok_msg "Modules are in $MODULES"
92 fi
93}
94
95check_dependencies() {
96 log_begin_msg "Checking if dependencies are present"
97 for exec in \
98 busybox \
99 qemu-system-x86_64 \
100 tmux vde_switch \
101 start-stop-daemon \
102 $DEPS; do
103 which $exec 2> /dev/null > /dev/null || log_error_msg "$exec is not installed"
104 done
105 log_ok_msg "All dependencies are met"
106}
107
108setup_tmp () {
109 TMP=$(mktemp -d)
110 trap "ret=$? ; cleanup" EXIT
111 log_info_msg "TMP is $TMP"
112}
113
114# Run our lab in tmux
115setup_screen() {
116 if [ -z "$TMUX" ] || [ x"$(tmux list-panes -F '#{session_name}')" != x"$LABNAME" ]; then
117 unset TMUX
118 exec tmux new-session -s "$LABNAME" -n main \
119 "env ROOT=$ROOT VERSION=$VERSION LINUX=$LINUX $PROGNAME $PROGARGS || read"
120 fi
121 sleep 1
122 tmux set-option -q prefix C-b
123 tmux set-option -q set-remain-on-exit on
124 tmux bind-key r respawn-window
125}
126
127# Setup a VDE switch
128setup_switch() {
129 nb=$1 ; shift
130 log_begin_msg "Setup switch $nb"
131 cat <<EOF > "$TMP/switch-$nb.conf"
132plugin/add /usr/lib/vde2/plugins/pdump.so
133pdump/filename $TMP/switch-$nb.pcap
134pdump/buffered 0
135pdump/active 1
136EOF
137 start-stop-daemon -b --no-close --make-pidfile --pidfile "$TMP/switch-$nb.pid" \
138 --start --startas $(which vde_switch) -- \
139 --sock "$TMP/switch-$nb.sock" --mgmt "$TMP/switch-management-$nb.sock" \
140 --rcfile "$TMP/switch-$nb.conf" --hub
141 # Management socket can be used with:
142 # socat - UNIX-CONNECT:"$TMP/switch-management-$nb.sock"
143 log_ok_msg "Switch $nb started"
144}
145
146setup_initrd () {
147 log_begin_msg "Build initrd"
148 DESTDIR=$TMP/initrd
149 mkdir -p $DESTDIR
150
151 # Copy busybox and eventually insmod
152 bins="busybox"
153 busybox --list | grep -qFx insmod || bins="$bins insmod"
154 for bin in $bins; do
155 install -D "$(which $bin)" ${DESTDIR}/bin/$bin
156 for x in $(ldd "$(which $bin)" 2> /dev/null | sed -e '
157 /\//!d;
158 /linux-gate/d;
159 /=>/ {s/.*=>[[:blank:]]*\([^[:blank:]]*\).*/\1/};
160 s/[[:blank:]]*\([^[:blank:]]*\) (.*)/\1/' 2>/dev/null); do
161 [ -f "${DESTDIR}/$x" ] || install -D "$x" "${DESTDIR}/$x"
162 done
163 done
164
165 # Configure busybox
166 for applet in $(${DESTDIR}/bin/busybox --list); do
167 ln -s busybox ${DESTDIR}/bin/${applet}
168 done
169
170 # Add modules
171 [ -z "$MODULES" ] || {
172 modules="9pnet_virtio 9p virtio_pci $UNION"
173 for mod in $modules; do
174 modprobe --all --set-version="${VERSION}" -d ${MODULES}/../../.. \
175 --ignore-install --quiet --show-depends $mod > /dev/null || {
176 log_warn_msg "Unable to find module $mod"
177 log_begin_msg "Continue building initrd"
178 }
179 modprobe --all --set-version="${VERSION}" -d ${MODULES}/../../.. \
180 --ignore-install --quiet --show-depends $mod |
181 while read prefix kmod options ; do
182 [ "${prefix}" = "insmod" ] || continue
183 grep -qFw "$kmod" ${DESTDIR}/modules 2> /dev/null || {
184 install -D "$kmod" "${DESTDIR}/${kmod}"
185 echo $prefix $kmod $options >> ${DESTDIR}/modules
186 }
187 done
188 done
189 }
190
191 # Copy this program
192 cp "$PROGNAME" ${DESTDIR}/init
193
194 # Create /tmp
195 mkdir ${DESTDIR}/tmp
196
197 # Build initrd
198 (cd "${DESTDIR}" && find . | cpio --quiet -R 0:0 -o -H newc | gzip > $TMP/initrd.gz)
199
200 log_ok_msg "initrd built in $TMP/initrd.gz"
201}
202
203random_mac () {
204 # But, not random in fact
205 name=$1
206 net=$2
207 mac=$(echo $name-$net | sha1sum | \
208 awk '{print "50:54:" substr($1,0,2) ":" substr($1, 2, 2) ":" substr($1, 4, 2) ":" substr($1, 6, 2)}')
209 echo $mac
210}
211
212start_vm () {
213 name=$1
214 shift
215
216 netargs=""
217 saveifs="$IFS"
218 IFS=,
219 for net in $NET; do
220 mac=$(random_mac $name $net)
221 netargs="$netargs -net nic,model=virtio,macaddr=$mac,vlan=$net"
222 netargs="$netargs -net vde,sock=$TMP/switch-$net.sock,vlan=$net"
223 done
224 IFS="$saveifs"
225
226 log_info_msg "Start VM $name"
227 # /root is mounted with version 9p2000.u to allow access to /dev,
228 # /sys and to mount new partitions over them. This is not the case
229 # for 9p2000.L.
230 cat <<EOF > "$TMP/vm-$name.exec"
231#!/bin/sh
232 echo
233 exec start-stop-daemon --make-pidfile --pidfile "$TMP/vm-$name.pid" \
234 --start --startas $(which qemu-system-x86_64) -- \
235 -enable-kvm \
236 -nodefconfig -nodefaults \
237 -display none \
238 -m ${MEM:-128M} \
239 \
240 -chardev stdio,id=charserial0,signal=off \
241 -device isa-serial,chardev=charserial0,id=serial0 \
242 -chardev socket,id=charserial1,path=$TMP/vm-$name-serial.pipe,server,nowait \
243 -device isa-serial,chardev=charserial1,id=serial1 \
244 \
245 -chardev socket,id=con0,path=$TMP/vm-$name-console.pipe,server,nowait \
246 -mon chardev=con0,mode=readline,default \
247 \
248 -fsdev local,security_model=passthrough,id=fsdev-root,path=${ROOT} \
249 -device virtio-9p-pci,id=fs-root,fsdev=fsdev-root,mount_tag=rootshare \
250 -fsdev local,security_model=none,id=fsdev-lab,path=${PWD} \
251 -device virtio-9p-pci,id=fs-lab,fsdev=fsdev-lab,mount_tag=labshare \
252 -fsdev local,security_model=none,id=fsdev-tmp,path=${TMP} \
253 -device virtio-9p-pci,id=fs-tmp,fsdev=fsdev-tmp,mount_tag=tmpshare \
254 -fsdev local,security_model=none,id=fsdev-modules,path=${MODULES}/..,readonly \
255 -device virtio-9p-pci,id=fs-modules,fsdev=fsdev-modules,mount_tag=moduleshare \
256 \
257 -gdb unix:$TMP/vm-$name-gdb.pipe,server,nowait \
258 -kernel $KERNEL \
259 -no-reboot \
260 -initrd $TMP/initrd.gz \
261 -append "uts=$name console=ttyS0 panic=1 TERM=$TERM quiet" \
262 $netargs \
263 $@
264EOF
265 log_info_msg "GDB server listening on $TMP/vm-$name-gdb.pipe"
266 log_info_msg "monitor listening on $TMP/vm-$name-console.pipe"
267 log_info_msg "ttyS1 listening on $TMP/vm-$name-serial.pipe"
268 chmod +x "$TMP/vm-$name.exec"
269 tmux new-window -n $name "$TMP/vm-$name.exec"
270 tmux select-window -t ":^"
271}
272
273display_help() {
274 cat <<EOF
275
276Some tmux commands (assuming default keybindings) :
277 C-b d - Detach the lab (resume with "tmux attach -t $LABNAME")
278 C-b w - Select a window
279 C-b n - Next window
280 C-b p - Previous window
281 C-b l - Last window
282 C-b ? - Get help
283
284EOF
285}
286
287run() {
288 r=$1
289 shift
290 log_info_msg "$r: execute $*"
291 printf "%s\n" "$*" > $TMP/${r}.command
292 n=0
293 while [ -f $TMP/${r}.command ]; do
294 sleep 1
295 n=$((n + 1))
296 [ $n -le 15 ] || {
297 log_error_msg "Timeout while executing command on $r"
298 }
299 done
300}
301
302process_commands() {
303 cd /mnt/lab
304 log_info_msg "Waiting for commands"
305 cmd=/tmp/lab/${uts}.command
306 set +e
307 while true; do
308 while [ ! -f $cmd ]; do
309 sleep 1
310 done
311 log_info_msg "Execute command $(head -1 $cmd)"
312 sh $cmd
313 log_info_msg "End of command: $?"
314 rm $cmd
315 done
316}
317
318start_tests() {
319 # Start lldpd on each VM
320 run R1 libtool execute src/daemon/lldpd -M 1 -L \$PWD/src/client/lldpcli
321 run R2 libtool execute src/daemon/lldpd -M 2 -L \$PWD/src/client/lldpcli
322 run R3 libtool execute src/daemon/lldpd -M 3 -L \$PWD/src/client/lldpcli
323 sleep 2
324 # Query neighbors
325 run R1 libtool execute src/client/lldpcli show neighbor detail
326 run R2 libtool execute src/client/lldpcli show neighbor detail
327 run R3 libtool execute src/client/lldpcli show neighbor detail
328 # Add some VLAN
329 run R2 ip link add link iface2 name iface2.450 type vlan id 450
330 run R2 ip link set up dev iface2.450
331 run R2 ip link add link iface2 name iface2.451 type vlan id 451
332 run R2 ip link set up dev iface2.451
333 run R2 ip link add link iface2 name iface2.452 type vlan id 452
334 run R2 ip link set up dev iface2.452
335 sleep 2
336 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface2
337 # Remove one
338 run R2 ip link del iface2.451
339 sleep 2
340 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface2
341 # Add a bond
342 run R3 ip link set down dev iface2
343 run R3 ip link set down dev iface3
344 run R3 ip link set iface2 master bond0
345 run R3 ip link set iface3 master bond0
346 run R3 ip link set up dev bond0
347 sleep 2
348 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface4
349 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface5
350 # Add a VLAN on top of bond
351 run R3 ip link add link bond0 name bond0.453 type vlan id 453
352 run R3 ip link set up dev bond0.453
353 sleep 2
354 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface4
355 # Add a bridge
356 run R2 brctl addbr br0
357 run R2 brctl addif br0 iface3
358 run R2 ip link set up dev br0
359 sleep 2
360 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface3
361 # Modify some TLV
362 conf="libtool execute src/client/lldpcli configure ports iface2"
363 run R2 $conf lldp custom-tlv oui 33,44,55 subtype 44 oui-info 45,45,45,45,45
364 run R2 $conf med location elin 911
365 run R2 $conf med location coordinate latitude 48.58667N longitude 2.2014E altitude 117.47 m datum WGS84
366 run R2 $conf med power pd source pse priority high value 5000
367 run R2 $conf dot3 power pse supported enabled paircontrol powerpairs spare class class-3
368 sleep 2
369 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface2
370 # Configuration should stay when port go down and up
371 run R2 ip link set down dev iface2
372 sleep 2
373 run R2 ip link set up dev iface2
374 sleep 5
375 run R1 libtool execute src/client/lldpcli show neighbor detail ports iface2
376}
377
378cleanup() {
379 set +e
380 for pid in $TMP/*.pid; do
381 kill -15 -$(cat $pid) 2> /dev/null || true
382 done
383 sleep 1
384 for pid in $TMP/*.pid; do
385 kill -9 -$(cat $pid) 2> /dev/null || true
386 done
387 rm -rf $TMP
388 tmux kill-session -t $LABNAME
389}
390
391# FSM
392export STATE=${STATE:-BEGIN}
393case $$,$STATE in
394 1,BEGIN)
395 # In initrd
396 log_info_msg "initrd started"
397 hostname ${uts}
398 export PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/local/sbin:/usr/sbin
399 export HOME=/root
400
401 [ ! -f /modules ] || {
402 log_info_msg "Loading modules"
403 . /modules
404 }
405
406 log_begin_msg "Setup root file system"
407 mount -n -t tmpfs tmpfs /tmp -o rw
408 mkdir /tmp/target
409 mkdir /tmp/target/ro
410 mkdir /tmp/target/overlay
411 mount -n -t 9p rootshare /tmp/target/overlay -o trans=virtio,version=9p2000.u,ro
412 mount -n -t proc proc /tmp/target/overlay/proc
413 mount -n -t sysfs sys /tmp/target/overlay/sys
414 log_ok_msg "Root file system setup"
415
416 log_begin_msg "Clean /tmp and /run"
417 for fs in /run /var/run /var/tmp /var/log /tmp /mnt; do
418 if [ -d /tmp/target/overlay$fs ] && [ ! -h /tmp/target/overlay$fs ]; then
419 mount -t tmpfs tmpfs /tmp/target/overlay$fs -o rw,nosuid,nodev
420 fi
421 done
422 log_ok_msg "/tmp, /run and others are clean"
423
424 log_begin_msg "Mount /lib/modules"
425 mount -t 9p moduleshare /tmp/target/overlay/lib/modules -o trans=virtio,version=9p2000.L,access=0,ro || \
426 log_error_msg "Unable to mount /lib/modules"
427 log_ok_msg "/root and /lib/modules mounted"
428
429 log_begin_msg "Mount /mnt/lab"
430 mkdir /tmp/target/overlay/mnt/lab
431 mount -t 9p labshare /tmp/target/overlay/mnt/lab -o trans=virtio,version=9p2000.L,access=any,rw || \
432 log_error_msg "Unable to mount /mnt/lab"
433 log_ok_msg "/mnt/lab mounted"
434
435 log_begin_msg "Mount /tmp/lab"
436 mkdir /tmp/target/overlay/tmp/lab
437 mount -t 9p tmpshare /tmp/target/overlay/tmp/lab -o trans=virtio,version=9p2000.L,access=any,rw || \
438 log_error_msg "Unable to mount /tmp/lab"
439 log_ok_msg "/tmp/lab mounted"
440
441 log_info_msg "Change root"
442 export STATE=CHROOTED
443 exec chroot /tmp/target/overlay /mnt/lab/integration-tests
444 ;;
445
446 1,CHROOTED)
447 log_begin_msg "Starting udev"
448 udev_log=err
449 mount -n -o size=10M,mode=0755 -t devtmpfs devtmpfs /dev
450 udevadm info --cleanup-db
451 for udev in /lib/systemd/systemd-udevd /usr/lib/systemd/systemd-udevd $(command -v udevd 2> /dev/null); do
452 [ ! -x $udev ] || break
453 done
454 $udev --daemon
455 udevadm trigger --action=add
456 udevadm settle
457 log_ok_msg "udev started"
458
459 log_info_msg "Setup interfaces"
460 modprobe dummy 2>/dev/null || true
461 modprobe bonding 2>/dev/null || true
462 sleep 0.5 # Some interfaces may take some time to appear
463 # Rename all interfaces to "predictable" and "non-colliding"
464 # name. We don't have if we have eth* or ens* interfaces. Let
465 # take a totally different naming convention.
466 nb=1
467 for iface in $(echo /sys/bus/virtio/drivers/virtio_net/*/net/*); do
468 ip link set name iface$nb dev ${iface##*/}
469 nb=$((nb + 1))
470 done
471 for intf in /sys/class/net/*; do
472 intf=$(basename $intf)
473 ip a l dev $intf 2> /dev/null >/dev/null || continue
474 ip link set up dev $intf
475 done
476
477 log_info_msg "Setup IP addresses"
478 case $uts in
479 R1)
480 ip -4 addr add 192.0.2.15/24 dev iface1
481 ip -6 addr add 2001:db8::cafe:15/64 dev iface1
482 ;;
483 R2)
484 ip -4 addr add 192.0.2.16/24 dev iface1
485 ip -6 addr add 2001:db8::cafe:16/64 dev iface1
486 ;;
487 R3)
488 ip -4 addr add 192.0.2.17/24 dev iface1
489 ip -6 addr add 2001:db8::cafe:17/64 dev iface1
490 ;;
491 esac
492 process_commands 2>&1 | tee /mnt/lab/${uts}.output
493 ;;
494
495 *,BEGIN)
496 # Initial state
497 [ $(id -u) != 0 ] || {
498 log_error_msg "You should not run this as root"
499 exit 1
500 }
501 PROGNAME="$(readlink -f "$0")"
502 PROGARGS="$@"
503 ROOT="$(readlink -f "${ROOT:-/}")" # Root filesystem
504 KERNEL="$(readlink -f "${1:-/boot/vmlinuz-$(uname -r)}")" # Kernel
505 PATH="$PATH":/usr/local/sbin:/usr/sbin:/sbin
506 [ $# -lt 1 ] || shift
507
508 check_kernel
509 check_dependencies
510 setup_screen
511 setup_tmp
512 setup_initrd
513
514 setup_switch 1
515 setup_switch 2
516 setup_switch 3
517 setup_switch 4
518 setup_switch 5
519 sleep 0.3
520
521 NET=1,2,3,4,5 start_vm R1
522 NET=1,2,3 start_vm R2
523 NET=1,4,5 start_vm R3
524
525 display_help
526 start_tests
527
528 sed \
529 -e 's/^\(Interface:.*, Time: 0 day\).*/\1/' \
530 -e 's/^\( SysDescr:\).*/\1/' \
531 -e 's/^\( Hardware Revision: pc-i440fx\).*/\1/' \
532 -e 's/^\( Software Revision: \).*/\1/' \
533 -e 's/^\( Firmware Revision: \).*/\1/' \
534 R1.output > R1.output.redacted
535 diff -u ../tests/R1.expected R1.output.redacted || \
536 log_warn_msg "Unexpected differences"
537
538 log_info_msg "End of tests, press enter to exit."
539 read a
540 ;;
541esac
542
543# Local Variables:
544# mode: sh
545# indent-tabs-mode: nil
546# sh-basic-offset: 4
547# End: