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