[PATCH] add LNST regression tests
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This commit adds LNST regression tests that we have had in a sepparate
git repository until now.
The test can now be run locally as well:
cd regression-tests
./run-git-test.sh
The regression-tests/env/ directory contains a configuration file that
will be used, however if you have local configuration in your ~/.lnst
directory it will override the env/ configuration.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
regression-tests/README.md | 15 ++
regression-tests/env/lnst-ctl.conf | 14 ++
regression-tests/run-git-test.sh | 222 +++++++++++++++++++++
regression-tests/tests/0/desc | 6 +
regression-tests/tests/0/run.sh | 24 +++
regression-tests/tests/01/desc | 6 +
regression-tests/tests/01/ipvs-ping-check.xml | 35 ++++
regression-tests/tests/01/run.sh | 12 ++
regression-tests/tests/1/desc | 4 +
regression-tests/tests/1/recipe.xml | 33 +++
regression-tests/tests/1/run.sh | 12 ++
regression-tests/tests/10/desc | 4 +
regression-tests/tests/10/prefix-check-taskapi.xml | 33 +++
regression-tests/tests/10/prefix-check.py | 15 ++
regression-tests/tests/10/prefix-check.xml | 44 ++++
regression-tests/tests/10/run.sh | 39 ++++
regression-tests/tests/11/desc | 3 +
regression-tests/tests/11/recipe.xml | 33 +++
regression-tests/tests/11/run.sh | 15 ++
regression-tests/tests/12/desc | 1 +
regression-tests/tests/12/run.sh | 12 ++
regression-tests/tests/12/sm01.xml | 7 +
regression-tests/tests/12/sm02.xml | 7 +
regression-tests/tests/12/xinclude-check.xml | 13 ++
regression-tests/tests/13/desc | 1 +
regression-tests/tests/13/run.sh | 22 ++
regression-tests/tests/13/taskapi-alias-check.py | 9 +
regression-tests/tests/13/taskapi-alias-check.xml | 38 ++++
.../tests/13/taskapi-alias-namespace-check.py | 8 +
.../tests/13/taskapi-alias-namespace-check.xml | 41 ++++
regression-tests/tests/14/desc | 3 +
regression-tests/tests/14/recipe1.xml | 68 +++++++
regression-tests/tests/14/recipe2.xml | 79 ++++++++
regression-tests/tests/14/run.sh | 17 ++
regression-tests/tests/15/desc | 3 +
regression-tests/tests/15/recipe.xml | 33 +++
regression-tests/tests/15/run.sh | 12 ++
regression-tests/tests/2/desc | 5 +
regression-tests/tests/2/recipe.xml | 74 +++++++
regression-tests/tests/2/run.sh | 12 ++
regression-tests/tests/3/desc | 4 +
regression-tests/tests/3/recipe.xml | 24 +++
regression-tests/tests/3/run.sh | 12 ++
regression-tests/tests/4/desc | 3 +
regression-tests/tests/4/recipe.xml | 43 ++++
regression-tests/tests/4/run.sh | 19 ++
regression-tests/tests/5/desc | 3 +
regression-tests/tests/5/recipe.xml | 29 +++
regression-tests/tests/5/run.sh | 20 ++
regression-tests/tests/6/desc | 4 +
regression-tests/tests/6/lnst-ctl.conf | 2 +
regression-tests/tests/6/recipe.xml | 35 ++++
regression-tests/tests/6/run.sh | 23 +++
regression-tests/tests/7/desc | 4 +
regression-tests/tests/7/lnst-ctl.conf | 2 +
regression-tests/tests/7/recipe.xml | 24 +++
regression-tests/tests/7/run.sh | 23 +++
regression-tests/tests/7/task.py | 23 +++
regression-tests/tests/8/desc | 3 +
regression-tests/tests/8/recipe.xml | 26 +++
regression-tests/tests/8/run.sh | 12 ++
regression-tests/tests/9/desc | 3 +
regression-tests/tests/9/recipe.xml | 24 +++
regression-tests/tests/9/run.sh | 12 ++
regression-tests/tests/9/task.py | 9 +
regression-tests/tests/lib.sh | 58 ++++++
66 files changed, 1478 insertions(+)
create mode 100644 regression-tests/README.md
create mode 100644 regression-tests/env/lnst-ctl.conf
create mode 100755 regression-tests/run-git-test.sh
create mode 100644 regression-tests/tests/0/desc
create mode 100755 regression-tests/tests/0/run.sh
create mode 100644 regression-tests/tests/01/desc
create mode 100644 regression-tests/tests/01/ipvs-ping-check.xml
create mode 100755 regression-tests/tests/01/run.sh
create mode 100644 regression-tests/tests/1/desc
create mode 100644 regression-tests/tests/1/recipe.xml
create mode 100755 regression-tests/tests/1/run.sh
create mode 100644 regression-tests/tests/10/desc
create mode 100644 regression-tests/tests/10/prefix-check-taskapi.xml
create mode 100644 regression-tests/tests/10/prefix-check.py
create mode 100644 regression-tests/tests/10/prefix-check.xml
create mode 100755 regression-tests/tests/10/run.sh
create mode 100644 regression-tests/tests/11/desc
create mode 100644 regression-tests/tests/11/recipe.xml
create mode 100755 regression-tests/tests/11/run.sh
create mode 100644 regression-tests/tests/12/desc
create mode 100755 regression-tests/tests/12/run.sh
create mode 100644 regression-tests/tests/12/sm01.xml
create mode 100644 regression-tests/tests/12/sm02.xml
create mode 100644 regression-tests/tests/12/xinclude-check.xml
create mode 100644 regression-tests/tests/13/desc
create mode 100755 regression-tests/tests/13/run.sh
create mode 100644 regression-tests/tests/13/taskapi-alias-check.py
create mode 100644 regression-tests/tests/13/taskapi-alias-check.xml
create mode 100644 regression-tests/tests/13/taskapi-alias-namespace-check.py
create mode 100644 regression-tests/tests/13/taskapi-alias-namespace-check.xml
create mode 100644 regression-tests/tests/14/desc
create mode 100644 regression-tests/tests/14/recipe1.xml
create mode 100644 regression-tests/tests/14/recipe2.xml
create mode 100755 regression-tests/tests/14/run.sh
create mode 100644 regression-tests/tests/15/desc
create mode 100644 regression-tests/tests/15/recipe.xml
create mode 100755 regression-tests/tests/15/run.sh
create mode 100644 regression-tests/tests/2/desc
create mode 100644 regression-tests/tests/2/recipe.xml
create mode 100755 regression-tests/tests/2/run.sh
create mode 100644 regression-tests/tests/3/desc
create mode 100644 regression-tests/tests/3/recipe.xml
create mode 100755 regression-tests/tests/3/run.sh
create mode 100644 regression-tests/tests/4/desc
create mode 100644 regression-tests/tests/4/recipe.xml
create mode 100755 regression-tests/tests/4/run.sh
create mode 100644 regression-tests/tests/5/desc
create mode 100644 regression-tests/tests/5/recipe.xml
create mode 100755 regression-tests/tests/5/run.sh
create mode 100644 regression-tests/tests/6/desc
create mode 100644 regression-tests/tests/6/lnst-ctl.conf
create mode 100644 regression-tests/tests/6/recipe.xml
create mode 100755 regression-tests/tests/6/run.sh
create mode 100644 regression-tests/tests/7/desc
create mode 100644 regression-tests/tests/7/lnst-ctl.conf
create mode 100644 regression-tests/tests/7/recipe.xml
create mode 100755 regression-tests/tests/7/run.sh
create mode 100644 regression-tests/tests/7/task.py
create mode 100644 regression-tests/tests/8/desc
create mode 100644 regression-tests/tests/8/recipe.xml
create mode 100755 regression-tests/tests/8/run.sh
create mode 100644 regression-tests/tests/9/desc
create mode 100644 regression-tests/tests/9/recipe.xml
create mode 100755 regression-tests/tests/9/run.sh
create mode 100644 regression-tests/tests/9/task.py
create mode 100644 regression-tests/tests/lib.sh
diff --git a/regression-tests/README.md b/regression-tests/README.md
new file mode 100644
index 0000000..e2d7f70
--- /dev/null
+++ b/regression-tests/README.md
@@ -0,0 +1,15 @@
+# lnst-tests
+
+**lnst-tests** is a regression test suite for the Linux Network Stack Test
+Project. It contains a number of test cases and scenario to verify LNST
+before releases and also to catch bugs and regressions.
+
+## Required Non-default Packages on slave machines
+
+* rsync
+* python-lxml (usually installed by default)
+* python-pyroute2 (available in fedora repositories, epel6 and epel7)
+* tar
+* bzip2
+* make
+* gcc
diff --git a/regression-tests/env/lnst-ctl.conf b/regression-tests/env/lnst-ctl.conf
new file mode 100644
index 0000000..a2f9343
--- /dev/null
+++ b/regression-tests/env/lnst-ctl.conf
@@ -0,0 +1,14 @@
+[environment]
+mac_pool_range = 52:54:01:00:00:01 52:54:01:FF:FF:FF
+rpcport = 9999
+machine_pool_dirs = ./pool/
+test_tool_dirs = ./test_tools
+test_module_dirs = ./test_modules
+log_dir = ./Logs
+xslt_url = http://www.lnst-project.org/files/result_xslt/xml_to_html.xsl
+allow_virtual = True
+
+[colours]
+faded = extended(242) default 0
+info = extended(228) default 1
+warning = extended(208) default 1
diff --git a/regression-tests/run-git-test.sh b/regression-tests/run-git-test.sh
new file mode 100755
index 0000000..ef84a2e
--- /dev/null
+++ b/regression-tests/run-git-test.sh
@@ -0,0 +1,222 @@
+#!/usr/bin/bash
+
+function get_hostname
+{
+ sed -n "s/.*param name=['\"]hostname['\"] value=['\"]\([^'\"]\+\)['\"].*/\1/p" $1
+}
+
+function log
+{
+ if [ -n "$log_dir" ]; then
+ echo "$1" | tee -a "$log_dir/log"
+ else
+ echo "$1"
+ fi
+}
+
+function logn
+{
+ if [ -n "$log_dir" ]; then
+ echo -n "$1" | tee -a "$log_dir/log"
+ else
+ echo -n "$1"
+ fi
+}
+
+function logtest
+{
+ if [ -n "$log_dir" ]; then
+ log_path="$log_dir/$test_dir"
+ mkdir -p "$log_path"
+ (tee $log_path/stdout.log) 3>&1 1>&2 2>&3 | tee $log_path/stderr.log
+ else
+ cat
+ fi
+}
+
+function print_separator
+{
+ log "--------------------------------------------------------------------------------"
+}
+
+function usage
+{
+ echo "Usage: $0 [-r revision] [-l logdir] [-t list_of_tests] [-g url] [-n]"
+ echo " -r revision Test a specific git branch/rev/tag"
+ echo " -l logdir Save test results to a directory"
+ echo " -t list_of_tests Run only these tests"
+ echo " Example: -t 0,1,2 will run tests 0, 1, and 2"
+ echo " -n Disable use of NetworkManager on slavemachines"
+ echo " Note: enabled by default"
+ echo " -g url URL pointing to LNST git repository that should be cloned"
+ exit 0
+}
+
+# ---
+
+#default repository url
+git_url="../"
+
+while getopts "hlr:t:g:n" OPTION
+do
+ case $OPTION in
+ h) usage ;;
+ l) log_dir_name="results-`date "+%Y-%m-%d-%I-%M-%S"`"
+ mkdir -p $log_dir_name
+ log_dir=`realpath $log_dir_name`;;
+ r) rev="$OPTARG";;
+ t) tests="`tr "," " " <<<"$OPTARG"`" ;;
+ n) nm_off="yes";;
+ g) git_url="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+
+# Check for test suite lock
+if [ -e .lock ]; then
+ pid=`cat .lock`
+ echo "The test suite is locked by process $pid." \
+ "Someone might be using it right now."
+ echo "Type 'unlock' if you wish to proceed anyway:"
+ read -e input
+ if [ "$input" != "unlock" ]; then
+ exit 1
+ fi
+fi
+
+# Lock the test suite
+echo "$$" >.lock
+
+# Clone the repo
+repo=`mktemp -d`
+git clone $git_url $repo
+
+# Checkout the appropriate revision/tag
+if [ ! -z "$rev" ]; then
+ pushd . >/dev/null
+ cd $repo
+ git checkout "$rev"
+ popd >/dev/null
+else
+ log "Revision not specified, using HEAD."
+ rev="HEAD"
+fi
+
+# Load the proper config
+#rm -rf ~/.lnst
+#cp -r env ~/.lnst
+cp -r env/* $repo/
+
+# Scan the pool and prepare the machines in it
+for machine in `ls -1 env/pool/`; do
+ hostname=`get_hostname env/pool/$machine`
+
+ # In case this script was killed and there are any slave processes
+ # left hanging on the machine.
+ ssh "root@$hostname" 'for p in `pgrep lnst-slave`; do kill -9 $p; done'
+
+ # Create a temporary dir for the git tree to be tested
+ remote_repo=`ssh "root@$hostname" "mktemp -d"`
+
+ # Transfer the repo to the machine
+ rsync -r --exclude "Logs" --exclude ".git" "$repo/" \
+ "root@$hostname:$remote_repo"
+
+ if [ ! -z $nm_off ]; then
+ ssh "root@$hostname" "cd $remote_repo && echo \"use_nm = false\" >> lnst-slave.conf"
+ fi
+
+ # Start the slave process
+ ssh -n -t -t "root@$hostname" "cd $remote_repo && ./lnst-slave" >/dev/null \
+ 2>/dev/null &
+ pid=$!
+
+ # Save the status (hostname, pid of the ssh session, repo path)
+ # so we can cleanup things properly after the tests.
+ if [ -z "$slave_status" ]; then
+ slave_status="$hostname $pid $remote_repo"
+ else
+ slave_status="$hostname $pid $remote_repo
+ $slave_status"
+ fi
+done
+
+sleep 1
+
+summary="Summary:"
+
+# Run the tests in the tests/ directory
+print_separator
+
+# In case the list of test names to run was omitted, run all of them
+if [ -z "$tests" ]; then
+ tests="`ls -1 tests/ | grep -v '\.sh$'`"
+fi
+
+for test_name in $tests; do
+ test_dir=`realpath tests/$test_name`
+ if [ -e "$test_dir/run.sh" ]; then
+ logn "Running test #$test_name: "
+ if [ -e $test_dir/desc ]; then
+ while read line; do log " $line"; done < $test_dir/desc
+ else
+ logn "\n"
+ fi
+ print_separator
+
+ pushd . >/dev/null
+ cd $test_dir
+
+ chmod +x run.sh
+ if [ -n "$log_dir" ]; then
+ log_path="$log_dir/$test_name"
+ mkdir -p "$log_path"
+
+ # A bit of black magic in bash.
+ # Explanation: http://bit.ly/1dxuMJI
+ log "`PATH="$repo:$PATH" ./run.sh \
+ > >(tee $log_path/stdout.log) \
+ 2> >(tee $log_path/stderr.log >&2)`"
+ else
+ PATH="$repo:$PATH" ./run.sh
+ fi
+
+ rv=$?
+
+ popd >/dev/null
+
+ print_separator
+ if [ "$rv" -eq "0" ]; then
+ log "Result #$test_name: PASS"
+ summary=`echo -e "$summary\n test #$test_name: PASS"`
+ else
+ log "Result #$test_name: FAIL"
+ summary=`echo -e "$summary\n test #$test_name: FAIL"`
+ #break
+ fi
+ print_separator
+ else
+ log "Skipping test #$test_name, run.sh file not found."
+ print_separator
+ summary=`echo -e "$summary\n test #$test_name: SKIPPED"`
+ fi
+done
+
+log "$summary"
+print_separator
+
+sleep 1
+
+# Cleanup
+while read line; do
+ hostname=`awk '{print $1}' <<<"$line"`
+ pid=`awk '{print $2}' <<<"$line"`
+ remote_repo=`awk '{print $3}' <<<"$line"`
+
+ kill $pid
+ ssh -n "root@$hostname" "rm -r $remote_repo"
+done <<< "$slave_status"
+
+rm -rf $repo
+
+rm -rf .lock
diff --git a/regression-tests/tests/0/desc b/regression-tests/tests/0/desc
new file mode 100644
index 0000000..3e9a2cf
--- /dev/null
+++ b/regression-tests/tests/0/desc
@@ -0,0 +1,6 @@
+Run lnst-ctl without any parameters
+
+A test for Issue #6. The lnst-ctl script failed in case it was run with
+no arguments at all.
+
+https://github.com/jpirko/lnst/issues/6
diff --git a/regression-tests/tests/0/run.sh b/regression-tests/tests/0/run.sh
new file mode 100755
index 0000000..ced2cbd
--- /dev/null
+++ b/regression-tests/tests/0/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+. ../lib.sh
+
+# initialize the test
+init_test
+
+# In this case, we want to catch the log for asserts.
+log="`lnst-ctl`"
+rv=$?
+
+# But we also want the log to be shown in test results.
+echo "$log"
+
+# To keep the lnst-ctl log separated from the asserts
+print_separator
+
+assert_log "ERROR" "No action specified" "$log"
+
+# Check the return value
+assert_status "error" $rv
+
+# finish the test
+end_test
diff --git a/regression-tests/tests/01/desc b/regression-tests/tests/01/desc
new file mode 100644
index 0000000..b59ca44
--- /dev/null
+++ b/regression-tests/tests/01/desc
@@ -0,0 +1,6 @@
+Check matching algorithm corner case
+
+Check that the controller fails in cas there's a machine in the recipe that has
+an interface connected to some network and there's no other connection from
+other machines to this network, a.k.a broken-connection. The controller should
+fail with an error.
diff --git a/regression-tests/tests/01/ipvs-ping-check.xml b/regression-tests/tests/01/ipvs-ping-check.xml
new file mode 100644
index 0000000..50d3556
--- /dev/null
+++ b/regression-tests/tests/01/ipvs-ping-check.xml
@@ -0,0 +1,35 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="vsnet">
+ <addresses>
+ <address value="192.168.210.4/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="40"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/01/run.sh b/regression-tests/tests/01/run.sh
new file mode 100755
index 0000000..4c1e9e4
--- /dev/null
+++ b/regression-tests/tests/01/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run ipvs-ping-check.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/1/desc b/regression-tests/tests/1/desc
new file mode 100644
index 0000000..d14cd8a
--- /dev/null
+++ b/regression-tests/tests/1/desc
@@ -0,0 +1,4 @@
+Basic sanity ping test
+
+This test just executes a ping between two arbitrary machines with a
+single plain ethernet interface.
diff --git a/regression-tests/tests/1/recipe.xml b/regression-tests/tests/1/recipe.xml
new file mode 100644
index 0000000..33fdaf1
--- /dev/null
+++ b/regression-tests/tests/1/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run module="IcmpPing" host="1" timeout="30">
+ <options>
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <option name="count" value="40"/>
+ <option name="interval" value="0.2"/>
+ <option name="limit_rate" value="95"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/1/run.sh b/regression-tests/tests/1/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/1/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/10/desc b/regression-tests/tests/10/desc
new file mode 100644
index 0000000..77fc69a
--- /dev/null
+++ b/regression-tests/tests/10/desc
@@ -0,0 +1,4 @@
+Check the prefix template function
+
+Check that the prefix function works correctly from the recipe and also from
+a python program using Task API.
diff --git a/regression-tests/tests/10/prefix-check-taskapi.xml b/regression-tests/tests/10/prefix-check-taskapi.xml
new file mode 100644
index 0000000..efc96b9
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check-taskapi.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.3/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.4/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task python="./prefix-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/10/prefix-check.py b/regression-tests/tests/10/prefix-check.py
new file mode 100644
index 0000000..34d674d
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check.py
@@ -0,0 +1,15 @@
+from lnst.Controller.Task import ctl
+
+m = ctl.get_host("lb")
+
+addr1 = m.get_ip("nic1")
+addr2 = m.get_ip("nic2")
+
+pfx1 = m.get_prefix("nic1")
+pfx2 = m.get_prefix("nic2")
+
+m.run("echo %s" % addr1)
+m.run("echo %s" % addr2)
+m.run("echo %s" % pfx1)
+m.run("echo %s" % pfx2)
+
diff --git a/regression-tests/tests/10/prefix-check.xml b/regression-tests/tests/10/prefix-check.xml
new file mode 100644
index 0000000..1b9ecba
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check.xml
@@ -0,0 +1,44 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.3/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.4/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="1"/>
+ </options>
+ </run>
+ <run host="lb" command="echo {ip(lb,nic1)}"/>
+ <run host="lb" command="echo {prefix(lb,nic1)}"/>
+ <run host="lb" command="echo {ip(lb,nic2,0)}"/>
+ <run host="lb" command="echo {prefix(lb,nic2,0)}"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/10/run.sh b/regression-tests/tests/10/run.sh
new file mode 100755
index 0000000..55d7815
--- /dev/null
+++ b/regression-tests/tests/10/run.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run prefix-check.xml | tee test10.log
+rv=${PIPESTATUS[0]}
+
+log=`cat test10.log`
+
+rm -f test10.log
+
+print_separator
+
+assert_status "pass" "$rv"
+
+assert_log "DEBUG" "echo 24" "$log"
+assert_log "DEBUG" "echo 192.168.200.4" "$log"
+assert_log "DEBUG" "echo 16" "$log"
+assert_log "DEBUG" "echo 192.168.202.4" "$log"
+
+lnst-ctl -d run prefix-check-taskapi.xml | tee test10-api.log
+rv=${PIPESTATUS[0]}
+
+log=`cat test10-api.log`
+
+rm -f test10-api.log
+
+print_separator
+
+assert_status "pass" "$rv"
+
+assert_log "DEBUG" "echo 24" "$log"
+assert_log "DEBUG" "echo 192.168.200.4" "$log"
+assert_log "DEBUG" "echo 16" "$log"
+assert_log "DEBUG" "echo 192.168.202.4" "$log"
+
+end_test
diff --git a/regression-tests/tests/11/desc b/regression-tests/tests/11/desc
new file mode 100644
index 0000000..6744e6c
--- /dev/null
+++ b/regression-tests/tests/11/desc
@@ -0,0 +1,3 @@
+An exception raised during configuration
+
+This causes the slave to die!
diff --git a/regression-tests/tests/11/recipe.xml b/regression-tests/tests/11/recipe.xml
new file mode 100644
index 0000000..2bb2c8d
--- /dev/null
+++ b/regression-tests/tests/11/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="a192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run module="IcmpPing" host="1" timeout="30">
+ <options>
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <option name="count" value="40"/>
+ <option name="interval" value="0.2"/>
+ <option name="limit_rate" value="95"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/11/run.sh b/regression-tests/tests/11/run.sh
new file mode 100755
index 0000000..d32561f
--- /dev/null
+++ b/regression-tests/tests/11/run.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+end_test
diff --git a/regression-tests/tests/12/desc b/regression-tests/tests/12/desc
new file mode 100644
index 0000000..e8dbb4b
--- /dev/null
+++ b/regression-tests/tests/12/desc
@@ -0,0 +1 @@
+Check xinclude resolving as described in issue #29
diff --git a/regression-tests/tests/12/run.sh b/regression-tests/tests/12/run.sh
new file mode 100755
index 0000000..db8e724
--- /dev/null
+++ b/regression-tests/tests/12/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run xinclude-check.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/12/sm01.xml b/regression-tests/tests/12/sm01.xml
new file mode 100644
index 0000000..3f3a042
--- /dev/null
+++ b/regression-tests/tests/12/sm01.xml
@@ -0,0 +1,7 @@
+<interfaces>
+ <eth id="1" label="testnet">
+ <addresses>
+ <address value="192.168.122.10/24" />
+ </addresses>
+ </eth>
+</interfaces>
diff --git a/regression-tests/tests/12/sm02.xml b/regression-tests/tests/12/sm02.xml
new file mode 100644
index 0000000..ef59e63
--- /dev/null
+++ b/regression-tests/tests/12/sm02.xml
@@ -0,0 +1,7 @@
+<interfaces>
+ <eth id="1" label="testnet">
+ <addresses>
+ <address value="192.168.122.20/24" />
+ </addresses>
+ </eth>
+</interfaces>
diff --git a/regression-tests/tests/12/xinclude-check.xml b/regression-tests/tests/12/xinclude-check.xml
new file mode 100644
index 0000000..6bdf7bf
--- /dev/null
+++ b/regression-tests/tests/12/xinclude-check.xml
@@ -0,0 +1,13 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <network>
+ <host id="1">
+ <xi:include href="sm01.xml"/>
+ </host>
+ <host id="2">
+ <xi:include href="sm02.xml"/>
+ </host>
+ </network>
+ <task>
+ <run command="echo {ip(1,1)}" host="2"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/13/desc b/regression-tests/tests/13/desc
new file mode 100644
index 0000000..22b8aeb
--- /dev/null
+++ b/regression-tests/tests/13/desc
@@ -0,0 +1 @@
+This test checks that aliases are visible in python tasks
diff --git a/regression-tests/tests/13/run.sh b/regression-tests/tests/13/run.sh
new file mode 100755
index 0000000..f988ae6
--- /dev/null
+++ b/regression-tests/tests/13/run.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run taskapi-alias-check.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+lnst-ctl -d -A alias1="value2" run taskapi-alias-check.xml
+rv=$?
+
+assert_status "fail" "$rv"
+
+lnst-ctl -d run taskapi-alias-namespace-check.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/13/taskapi-alias-check.py b/regression-tests/tests/13/taskapi-alias-check.py
new file mode 100644
index 0000000..422fdd3
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-check.py
@@ -0,0 +1,9 @@
+import sys
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("m1")
+m2 = ctl.get_host("m2")
+
+m1.run("[ \"%s\" == \"value1\" ]" % ctl.get_alias("alias1"))
+m2.run("[ \"%s\" == \"value2\" ]" % ctl.get_alias("alias2"))
+
diff --git a/regression-tests/tests/13/taskapi-alias-check.xml b/regression-tests/tests/13/taskapi-alias-check.xml
new file mode 100644
index 0000000..2745c00
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-check.xml
@@ -0,0 +1,38 @@
+<lnstrecipe>
+ <define>
+ <alias name="alias1" value="value1"/>
+ </define>
+ <network>
+ <define>
+ <alias name="m1_addr" value="192.168.111.1/24"/>
+ </define>
+ <host id="m1">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address>{$m1_addr}</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <define>
+ <alias name="m2_addr" value="192.168.111.2/24"/>
+ </define>
+ <host id="m2">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address value="{$m1_addr}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <define>
+ <alias name="alias2" value="value2"/>
+ </define>
+
+ <task python="taskapi-alias-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/13/taskapi-alias-namespace-check.py b/regression-tests/tests/13/taskapi-alias-namespace-check.py
new file mode 100644
index 0000000..afed22d
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-namespace-check.py
@@ -0,0 +1,8 @@
+import sys
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("m1")
+
+# this should fail
+m1.run("[ \"%s\" == \"value1\" ]" % ctl.get_alias("foo"))
+
diff --git a/regression-tests/tests/13/taskapi-alias-namespace-check.xml b/regression-tests/tests/13/taskapi-alias-namespace-check.xml
new file mode 100644
index 0000000..9f9109d
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-namespace-check.xml
@@ -0,0 +1,41 @@
+<lnstrecipe>
+ <define>
+ <alias name="alias1" value="value1"/>
+ </define>
+ <network>
+ <define>
+ <alias name="m1_addr" value="192.168.111.1/24"/>
+ </define>
+ <host id="m1">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address>{$m1_addr}</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <define>
+ <alias name="m2_addr" value="192.168.111.2/24"/>
+ </define>
+ <host id="m2">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address value="{$m1_addr}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <define>
+ <alias name="foo" value="bar"/>
+ </define>
+ </network>
+
+ <define>
+ <alias name="alias2" value="value2"/>
+ </define>
+
+ <task python="taskapi-alias-namespace-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/desc b/regression-tests/tests/14/desc
new file mode 100644
index 0000000..b595c43
--- /dev/null
+++ b/regression-tests/tests/14/desc
@@ -0,0 +1,3 @@
+crash with more than two masters in stack with NetworkManager
+
+https://github.com/jpirko/lnst/issues/70
diff --git a/regression-tests/tests/14/recipe1.xml b/regression-tests/tests/14/recipe1.xml
new file mode 100644
index 0000000..9d9d45a
--- /dev/null
+++ b/regression-tests/tests/14/recipe1.xml
@@ -0,0 +1,68 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A"/>
+ <team id="team">
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ <options>
+ <option name="teamd_config">
+ {
+ "hwaddr": "00:11:22:33:44:55",
+ "runner": {"name": "activebackup"},
+ "link_watch": {"name": "ethtool"}
+ }
+ </option>
+ </options>
+ <addresses>
+ <address value="192.168.100.3/24"/>
+ </addresses>
+ </team>
+ <vlan id="vlan">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.1/24"/>
+ </addresses>
+ <slaves>
+ <slave id="team"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="2" label="A"/>
+ <vlan id="testifc2">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" command="ip a"/>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2,testifc2)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan)}"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/recipe2.xml b/regression-tests/tests/14/recipe2.xml
new file mode 100644
index 0000000..4d165f6
--- /dev/null
+++ b/regression-tests/tests/14/recipe2.xml
@@ -0,0 +1,79 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A"/>
+ <vlan id="vlan1">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.100.1/24"/>
+ </addresses>
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ </vlan>
+ <vlan id="vlan2">
+ <options>
+ <option name="vlan_tci" value="2"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="2" label="A"/>
+ <vlan id="vlan1">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.100.3/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ <vlan id="vlan2">
+ <options>
+ <option name="vlan_tci" value="2"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.4/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2, vlan1)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan1)}"/>
+ </options>
+ </run>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2, vlan2)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan2)}"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/run.sh b/regression-tests/tests/14/run.sh
new file mode 100755
index 0000000..42ccbcc
--- /dev/null
+++ b/regression-tests/tests/14/run.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe1.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+lnst-ctl -d run recipe2.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/15/desc b/regression-tests/tests/15/desc
new file mode 100644
index 0000000..29c8517
--- /dev/null
+++ b/regression-tests/tests/15/desc
@@ -0,0 +1,3 @@
+devname updates not reflected in {Net,Nm}DeviceConfig classes
+
+https://github.com/jpirko/lnst/issues/65
diff --git a/regression-tests/tests/15/recipe.xml b/regression-tests/tests/15/recipe.xml
new file mode 100644
index 0000000..3225b26
--- /dev/null
+++ b/regression-tests/tests/15/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A">
+ <addresses>
+ <address value="192.168.101.1/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A">
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} down"/>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} name xyz"/>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} up"/>
+ <run host="slave1" command="ip a show dev xyz"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/15/run.sh b/regression-tests/tests/15/run.sh
new file mode 100755
index 0000000..50ab9ce
--- /dev/null
+++ b/regression-tests/tests/15/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/2/desc b/regression-tests/tests/2/desc
new file mode 100644
index 0000000..9f606a7
--- /dev/null
+++ b/regression-tests/tests/2/desc
@@ -0,0 +1,5 @@
+RecipeParser: Lots of comments in a XML
+
+There was a problem with comments at some places in the recipe XML.
+This recipe should cover all the possibilities and make sure they are
+properly ignored.
diff --git a/regression-tests/tests/2/recipe.xml b/regression-tests/tests/2/recipe.xml
new file mode 100644
index 0000000..0565f47
--- /dev/null
+++ b/regression-tests/tests/2/recipe.xml
@@ -0,0 +1,74 @@
+<!--comment-->
+<lnstrecipe>
+ <!--comment-->
+ <network>
+ <!--comment-->
+ <host id="1">
+ <!--comment-->
+ <params>
+ <!--comment-->
+ </params>
+ <!--comment-->
+ <interfaces>
+ <!--comment-->
+ <eth label="tnet" id="testiface">
+ <!--comment-->
+ <addresses>
+ <!--comment-->
+ <address value="192.168.100.10/24"/>
+ <!--comment-->
+ </addresses>
+ <!--comment-->
+ </eth>
+ <!--comment-->
+ </interfaces>
+ <!--comment-->
+ </host>
+ <!--comment-->
+ <host id="2">
+ <!--comment-->
+ <params>
+ <!--comment-->
+ </params>
+ <!--comment-->
+ <interfaces>
+ <!--comment-->
+ <eth label="tnet" id="testiface">
+ <!--comment-->
+ <addresses>
+ <!--comment-->
+ <address value="192.168.100.11/24"/>
+ <!--comment-->
+ </addresses>
+ <!--comment-->
+ </eth>
+ <!--comment-->
+ </interfaces>
+ <!--comment-->
+ </host>
+ <!--comment-->
+ </network>
+ <!--comment-->
+
+ <task>
+ <!--comment-->
+ <run module="IcmpPing" host="1" timeout="30">
+ <!--comment-->
+ <options>
+ <!--comment-->
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <!--comment-->
+ <option name="count" value="40"/>
+ <!--comment-->
+ <option name="interval" value="0.2"/>
+ <!--comment-->
+ <option name="limit_rate" value="95"/>
+ <!--comment-->
+ </options>
+ <!--comment-->
+ </run>
+ <!--comment-->
+ </task>
+ <!--comment-->
+</lnstrecipe>
+<!--comment-->
diff --git a/regression-tests/tests/2/run.sh b/regression-tests/tests/2/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/2/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/3/desc b/regression-tests/tests/3/desc
new file mode 100644
index 0000000..7eeaf6e
--- /dev/null
+++ b/regression-tests/tests/3/desc
@@ -0,0 +1,4 @@
+Check corner case in matching algorithm
+
+This test checks that controller fails if it runs a recipe with just one
+machine specified. The controller should fail.
diff --git a/regression-tests/tests/3/recipe.xml b/regression-tests/tests/3/recipe.xml
new file mode 100644
index 0000000..c261555
--- /dev/null
+++ b/regression-tests/tests/3/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <params>
+ <param name="os" value="rhel7"/>
+ </params>
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="40"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/3/run.sh b/regression-tests/tests/3/run.sh
new file mode 100755
index 0000000..e6e9a99
--- /dev/null
+++ b/regression-tests/tests/3/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/4/desc b/regression-tests/tests/4/desc
new file mode 100644
index 0000000..ad44b34
--- /dev/null
+++ b/regression-tests/tests/4/desc
@@ -0,0 +1,3 @@
+Aliases resolving
+
+This test aims to verify the correctness of aliases resloving.
diff --git a/regression-tests/tests/4/recipe.xml b/regression-tests/tests/4/recipe.xml
new file mode 100644
index 0000000..bc0c4b4
--- /dev/null
+++ b/regression-tests/tests/4/recipe.xml
@@ -0,0 +1,43 @@
+<lnstrecipe>
+ <define>
+ <alias name="one" value="192.168.100.10/24"/>
+ <alias name="two" value="192.168.100.11/24"/>
+ </define>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="{$one}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="{$two}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <define>
+ <alias name="one" value="one"/>
+ <alias name="two" value="two"/>
+ </define>
+
+ <task>
+ <run command="echo 1_{$one}_{$two}" host="1"/>
+ <define>
+ <alias name="one" value="eno"/>
+ <alias name="two" value="owt"/>
+ </define>
+ <run command="echo 2_{$one}_{$two}" host="1"/>
+ </task>
+ <task>
+ <run command="echo 3_{$one}_{$two}" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/4/run.sh b/regression-tests/tests/4/run.sh
new file mode 100755
index 0000000..f3d8ee2
--- /dev/null
+++ b/regression-tests/tests/4/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+log="`lnst-ctl -d run recipe.xml`"
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log "DEBUG" "echo 1_one_two" "$log"
+assert_log "DEBUG" "echo 2_eno_owt" "$log"
+assert_log "DEBUG" "echo 3_one_two" "$log"
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/5/desc b/regression-tests/tests/5/desc
new file mode 100644
index 0000000..7f3448f
--- /dev/null
+++ b/regression-tests/tests/5/desc
@@ -0,0 +1,3 @@
+Template functions resolving
+
+This test aims to verify the correctness of template functions resloving.
diff --git a/regression-tests/tests/5/recipe.xml b/regression-tests/tests/5/recipe.xml
new file mode 100644
index 0000000..1bc9137
--- /dev/null
+++ b/regression-tests/tests/5/recipe.xml
@@ -0,0 +1,29 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="echo ip1_{ip(1,testiface)}_" host="1"/>
+ <run command="echo ip2_{ip(1,testiface,0)}_" host="1"/>
+ <run command="echo devname_{devname(1,testiface)}_" host="1"/>
+ <run command="echo hwaddr_{hwaddr(1,testiface)}_" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/5/run.sh b/regression-tests/tests/5/run.sh
new file mode 100755
index 0000000..f19786e
--- /dev/null
+++ b/regression-tests/tests/5/run.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+log="`lnst-ctl -d run recipe.xml`"
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log "DEBUG" "echo ip1_192.168.100.10_" "$log"
+assert_log "DEBUG" "echo ip2_192.168.100.10_" "$log"
+assert_log "DEBUG" "echo hwaddr_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log "DEBUG" "echo devname_[a-zA-Z0-9]\+_" "$log"
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/6/desc b/regression-tests/tests/6/desc
new file mode 100644
index 0000000..f75fb3d
--- /dev/null
+++ b/regression-tests/tests/6/desc
@@ -0,0 +1,4 @@
+Dynamic hwaddr updates
+
+This test case can verify whether the dynamic configuration updates are
+propagated to the controller properly.
diff --git a/regression-tests/tests/6/lnst-ctl.conf b/regression-tests/tests/6/lnst-ctl.conf
new file mode 100644
index 0000000..7a8d46d
--- /dev/null
+++ b/regression-tests/tests/6/lnst-ctl.conf
@@ -0,0 +1,2 @@
+[environment]
+machine_pool_dirs = ./pool/
diff --git a/regression-tests/tests/6/recipe.xml b/regression-tests/tests/6/recipe.xml
new file mode 100644
index 0000000..1f084d0
--- /dev/null
+++ b/regression-tests/tests/6/recipe.xml
@@ -0,0 +1,35 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="echo `ip link show {devname(1,testiface)} | grep -o ' link/ether \([0-9a-fA-F]\{2\}:\?\)\{6\}' | cut -c 16-` >/tmp/lnst-hwaddr" host="1"/>
+ <run command="echo 1_{hwaddr(1, testiface)}_" host="1"/>
+ <run command="ip l set {devname(1, testiface)} address 52:54:00:12:34:56" host="1"/>
+ <ctl_wait seconds="2"/>
+ <run command="echo 2_{hwaddr(1, testiface)}_" host="1"/>
+ <run command="ip l set {devname(1, testiface)} address `cat /tmp/lnst-hwaddr`" host="1"/>
+ <ctl_wait seconds="2"/>
+ <run command="echo 3_{hwaddr(1, testiface)}_`cat /tmp/lnst-hwaddr | tr '[:lower:]' '[:upper:]'`_" host="1"/>
+ <run command="rm -f /tmp/lnst-hwaddr" host="1"/>
+ </task>
+</lnstrecipe>
+
diff --git a/regression-tests/tests/6/run.sh b/regression-tests/tests/6/run.sh
new file mode 100755
index 0000000..3882d0d
--- /dev/null
+++ b/regression-tests/tests/6/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+ln -s ../../env/pool
+
+log=`lnst-ctl -c lnst-ctl.conf -d run recipe.xml`
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log ".*" "1_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log ".*" "2_52:54:00:12:34:56_" "$log"
+assert_log ".*" "3_\(\([a-fA-F0-9]\{2\}:\?\)\{6\}\)_\1_" "$log"
+
+assert_status "pass" "$rv"
+
+rm -f ./pool
+
+end_test
diff --git a/regression-tests/tests/7/desc b/regression-tests/tests/7/desc
new file mode 100644
index 0000000..4a68dd6
--- /dev/null
+++ b/regression-tests/tests/7/desc
@@ -0,0 +1,4 @@
+Configuration updates (hwaddr) in python tasks
+
+The purpose of this test is to verify that the configuration updates
+propagate correctly from the slaves back to controller.
diff --git a/regression-tests/tests/7/lnst-ctl.conf b/regression-tests/tests/7/lnst-ctl.conf
new file mode 100644
index 0000000..7a8d46d
--- /dev/null
+++ b/regression-tests/tests/7/lnst-ctl.conf
@@ -0,0 +1,2 @@
+[environment]
+machine_pool_dirs = ./pool/
diff --git a/regression-tests/tests/7/recipe.xml b/regression-tests/tests/7/recipe.xml
new file mode 100644
index 0000000..88f26f8
--- /dev/null
+++ b/regression-tests/tests/7/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="task.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/7/run.sh b/regression-tests/tests/7/run.sh
new file mode 100755
index 0000000..3882d0d
--- /dev/null
+++ b/regression-tests/tests/7/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+ln -s ../../env/pool
+
+log=`lnst-ctl -c lnst-ctl.conf -d run recipe.xml`
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log ".*" "1_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log ".*" "2_52:54:00:12:34:56_" "$log"
+assert_log ".*" "3_\(\([a-fA-F0-9]\{2\}:\?\)\{6\}\)_\1_" "$log"
+
+assert_status "pass" "$rv"
+
+rm -f ./pool
+
+end_test
diff --git a/regression-tests/tests/7/task.py b/regression-tests/tests/7/task.py
new file mode 100644
index 0000000..640061e
--- /dev/null
+++ b/regression-tests/tests/7/task.py
@@ -0,0 +1,23 @@
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("1")
+m2 = ctl.get_host("2")
+
+devname = m1.get_devname("testiface")
+hwaddr = m1.get_hwaddr("testiface")
+
+m1.run("echo 1_%s_" % hwaddr)
+
+m1.run("echo `ip link show %s | grep -o ' link/ether \([0-9a-fA-F]\{2\}:\?\)\{6\}' | cut -c 16-` >/tmp/lnst-hwaddr" % devname)
+m1.run("ip l set %s address 52:54:00:12:34:56" % devname)
+ctl.wait(2)
+
+m1.run("echo 2_%s_" % hwaddr)
+m1.run("ip l set %s address `cat /tmp/lnst-hwaddr`" % devname)
+
+ctl.wait(2)
+
+m1.run("echo 3_%s_`cat /tmp/lnst-hwaddr | tr '[:lower:]' '[:upper:]'`_" % hwaddr)
+
+m1.run("rm -f /tmp/lnst-hwaddr")
+
diff --git a/regression-tests/tests/8/desc b/regression-tests/tests/8/desc
new file mode 100644
index 0000000..41c2ca2
--- /dev/null
+++ b/regression-tests/tests/8/desc
@@ -0,0 +1,3 @@
+Recipe Summary Crashing when Exec fails
+
+Regression test for a bug
diff --git a/regression-tests/tests/8/recipe.xml b/regression-tests/tests/8/recipe.xml
new file mode 100644
index 0000000..dc3a692
--- /dev/null
+++ b/regression-tests/tests/8/recipe.xml
@@ -0,0 +1,26 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="eecho weeee" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/8/run.sh b/regression-tests/tests/8/run.sh
new file mode 100755
index 0000000..1eefc21
--- /dev/null
+++ b/regression-tests/tests/8/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "fail" "$rv"
+
+end_test
diff --git a/regression-tests/tests/9/desc b/regression-tests/tests/9/desc
new file mode 100644
index 0000000..2e71f6a
--- /dev/null
+++ b/regression-tests/tests/9/desc
@@ -0,0 +1,3 @@
+Python Tasks
+
+Basic sanity of python tasks implementation (a simple ping test).
diff --git a/regression-tests/tests/9/recipe.xml b/regression-tests/tests/9/recipe.xml
new file mode 100644
index 0000000..88f26f8
--- /dev/null
+++ b/regression-tests/tests/9/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="task.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/9/run.sh b/regression-tests/tests/9/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/9/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/9/task.py b/regression-tests/tests/9/task.py
new file mode 100644
index 0000000..e718537
--- /dev/null
+++ b/regression-tests/tests/9/task.py
@@ -0,0 +1,9 @@
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("1")
+m2 = ctl.get_host("2")
+
+ping_mod = ctl.get_module("IcmpPing", options={"addr": m2.get_ip("testiface", 0),
+ "count":40, "interval": 0.2, "limit_rate": 95})
+
+ping_test = m1.run(ping_mod, timeout=30)
diff --git a/regression-tests/tests/lib.sh b/regression-tests/tests/lib.sh
new file mode 100644
index 0000000..df83ab1
--- /dev/null
+++ b/regression-tests/tests/lib.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+function init_test
+{
+ # test status 0 means everything went fine, 1 means there were
+ # one or more problems.
+ test_status=0
+}
+
+function end_test
+{
+ exit "$test_status"
+}
+
+function assert_log
+{
+ local level="$1"
+ local message_regexp="$2"
+ local log="$3"
+
+ if [ -z "`echo "$log" | grep "$level" | grep "$message_regexp"`" ]; then
+ echo "assert_log FAILED ($level, $message_regexp)"
+ test_status=1
+ else
+ echo "assert_log PASSED ($level, $message_regexp)"
+ fi
+}
+
+function assert_status
+{
+ local expect="$1"
+ local retval="$2"
+
+ case $expect in
+ "pass")
+ local value=0 ;;
+ "fail")
+ local value=1 ;;
+ "error")
+ local value=2 ;;
+ *)
+ echo "Unknown exit status '$expect'!"
+ exit 128
+ ;;
+ esac
+
+ if [ $value -ne $retval ]; then
+ test_status=1
+ echo "assert_status FAILED (expected: $value, real: $retval)"
+ else
+ echo "assert_status PASSED (expected: $value, real: $retval)"
+ fi
+}
+
+function print_separator
+{
+ echo "--------------------------------------------------------------------------------"
+}
--
1.9.3
8 years, 9 months
[lnst] add LNST regression tests
by Jiří Pírko
commit 0aa1afb0c4ab876d1758112b2caf783faadb55b8
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon Dec 15 13:31:39 2014 +0100
add LNST regression tests
This commit adds LNST regression tests that we have had in a separate
git repository until now.
The test can now be run locally as well:
cd regression-tests
./run-git-test.sh
The regression-tests/env/ directory contains a configuration file that
will be used, however if you have local configuration in your ~/.lnst
directory it will override the env/ configuration.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
regression-tests/README.md | 15 ++
regression-tests/env/lnst-ctl.conf | 14 ++
regression-tests/run-git-test.sh | 222 ++++++++++++++++++++
regression-tests/tests/0/desc | 6 +
regression-tests/tests/0/run.sh | 24 ++
regression-tests/tests/01/desc | 6 +
regression-tests/tests/01/ipvs-ping-check.xml | 35 +++
regression-tests/tests/01/run.sh | 12 +
regression-tests/tests/1/desc | 4 +
regression-tests/tests/1/recipe.xml | 33 +++
regression-tests/tests/1/run.sh | 12 +
regression-tests/tests/10/desc | 4 +
regression-tests/tests/10/prefix-check-taskapi.xml | 33 +++
regression-tests/tests/10/prefix-check.py | 15 ++
regression-tests/tests/10/prefix-check.xml | 44 ++++
regression-tests/tests/10/run.sh | 39 ++++
regression-tests/tests/11/desc | 3 +
regression-tests/tests/11/recipe.xml | 33 +++
regression-tests/tests/11/run.sh | 15 ++
regression-tests/tests/12/desc | 1 +
regression-tests/tests/12/run.sh | 12 +
regression-tests/tests/12/sm01.xml | 7 +
regression-tests/tests/12/sm02.xml | 7 +
regression-tests/tests/12/xinclude-check.xml | 13 ++
regression-tests/tests/13/desc | 1 +
regression-tests/tests/13/run.sh | 22 ++
regression-tests/tests/13/taskapi-alias-check.py | 9 +
regression-tests/tests/13/taskapi-alias-check.xml | 38 ++++
.../tests/13/taskapi-alias-namespace-check.py | 8 +
.../tests/13/taskapi-alias-namespace-check.xml | 41 ++++
regression-tests/tests/14/desc | 3 +
regression-tests/tests/14/recipe1.xml | 68 ++++++
regression-tests/tests/14/recipe2.xml | 79 +++++++
regression-tests/tests/14/run.sh | 17 ++
regression-tests/tests/15/desc | 3 +
regression-tests/tests/15/recipe.xml | 33 +++
regression-tests/tests/15/run.sh | 12 +
regression-tests/tests/2/desc | 5 +
regression-tests/tests/2/recipe.xml | 74 +++++++
regression-tests/tests/2/run.sh | 12 +
regression-tests/tests/3/desc | 4 +
regression-tests/tests/3/recipe.xml | 24 ++
regression-tests/tests/3/run.sh | 12 +
regression-tests/tests/4/desc | 3 +
regression-tests/tests/4/recipe.xml | 43 ++++
regression-tests/tests/4/run.sh | 19 ++
regression-tests/tests/5/desc | 3 +
regression-tests/tests/5/recipe.xml | 29 +++
regression-tests/tests/5/run.sh | 20 ++
regression-tests/tests/6/desc | 4 +
regression-tests/tests/6/lnst-ctl.conf | 2 +
regression-tests/tests/6/recipe.xml | 35 +++
regression-tests/tests/6/run.sh | 23 ++
regression-tests/tests/7/desc | 4 +
regression-tests/tests/7/lnst-ctl.conf | 2 +
regression-tests/tests/7/recipe.xml | 24 ++
regression-tests/tests/7/run.sh | 23 ++
regression-tests/tests/7/task.py | 23 ++
regression-tests/tests/8/desc | 3 +
regression-tests/tests/8/recipe.xml | 26 +++
regression-tests/tests/8/run.sh | 12 +
regression-tests/tests/9/desc | 3 +
regression-tests/tests/9/recipe.xml | 24 ++
regression-tests/tests/9/run.sh | 12 +
regression-tests/tests/9/task.py | 9 +
regression-tests/tests/lib.sh | 58 +++++
66 files changed, 1478 insertions(+), 0 deletions(-)
---
diff --git a/regression-tests/README.md b/regression-tests/README.md
new file mode 100644
index 0000000..e2d7f70
--- /dev/null
+++ b/regression-tests/README.md
@@ -0,0 +1,15 @@
+# lnst-tests
+
+**lnst-tests** is a regression test suite for the Linux Network Stack Test
+Project. It contains a number of test cases and scenario to verify LNST
+before releases and also to catch bugs and regressions.
+
+## Required Non-default Packages on slave machines
+
+* rsync
+* python-lxml (usually installed by default)
+* python-pyroute2 (available in fedora repositories, epel6 and epel7)
+* tar
+* bzip2
+* make
+* gcc
diff --git a/regression-tests/env/lnst-ctl.conf b/regression-tests/env/lnst-ctl.conf
new file mode 100644
index 0000000..a2f9343
--- /dev/null
+++ b/regression-tests/env/lnst-ctl.conf
@@ -0,0 +1,14 @@
+[environment]
+mac_pool_range = 52:54:01:00:00:01 52:54:01:FF:FF:FF
+rpcport = 9999
+machine_pool_dirs = ./pool/
+test_tool_dirs = ./test_tools
+test_module_dirs = ./test_modules
+log_dir = ./Logs
+xslt_url = http://www.lnst-project.org/files/result_xslt/xml_to_html.xsl
+allow_virtual = True
+
+[colours]
+faded = extended(242) default 0
+info = extended(228) default 1
+warning = extended(208) default 1
diff --git a/regression-tests/run-git-test.sh b/regression-tests/run-git-test.sh
new file mode 100755
index 0000000..ef84a2e
--- /dev/null
+++ b/regression-tests/run-git-test.sh
@@ -0,0 +1,222 @@
+#!/usr/bin/bash
+
+function get_hostname
+{
+ sed -n "s/.*param name=['\"]hostname['\"] value=['\"]\([^'\"]\+\)['\"].*/\1/p" $1
+}
+
+function log
+{
+ if [ -n "$log_dir" ]; then
+ echo "$1" | tee -a "$log_dir/log"
+ else
+ echo "$1"
+ fi
+}
+
+function logn
+{
+ if [ -n "$log_dir" ]; then
+ echo -n "$1" | tee -a "$log_dir/log"
+ else
+ echo -n "$1"
+ fi
+}
+
+function logtest
+{
+ if [ -n "$log_dir" ]; then
+ log_path="$log_dir/$test_dir"
+ mkdir -p "$log_path"
+ (tee $log_path/stdout.log) 3>&1 1>&2 2>&3 | tee $log_path/stderr.log
+ else
+ cat
+ fi
+}
+
+function print_separator
+{
+ log "--------------------------------------------------------------------------------"
+}
+
+function usage
+{
+ echo "Usage: $0 [-r revision] [-l logdir] [-t list_of_tests] [-g url] [-n]"
+ echo " -r revision Test a specific git branch/rev/tag"
+ echo " -l logdir Save test results to a directory"
+ echo " -t list_of_tests Run only these tests"
+ echo " Example: -t 0,1,2 will run tests 0, 1, and 2"
+ echo " -n Disable use of NetworkManager on slavemachines"
+ echo " Note: enabled by default"
+ echo " -g url URL pointing to LNST git repository that should be cloned"
+ exit 0
+}
+
+# ---
+
+#default repository url
+git_url="../"
+
+while getopts "hlr:t:g:n" OPTION
+do
+ case $OPTION in
+ h) usage ;;
+ l) log_dir_name="results-`date "+%Y-%m-%d-%I-%M-%S"`"
+ mkdir -p $log_dir_name
+ log_dir=`realpath $log_dir_name`;;
+ r) rev="$OPTARG";;
+ t) tests="`tr "," " " <<<"$OPTARG"`" ;;
+ n) nm_off="yes";;
+ g) git_url="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+
+# Check for test suite lock
+if [ -e .lock ]; then
+ pid=`cat .lock`
+ echo "The test suite is locked by process $pid." \
+ "Someone might be using it right now."
+ echo "Type 'unlock' if you wish to proceed anyway:"
+ read -e input
+ if [ "$input" != "unlock" ]; then
+ exit 1
+ fi
+fi
+
+# Lock the test suite
+echo "$$" >.lock
+
+# Clone the repo
+repo=`mktemp -d`
+git clone $git_url $repo
+
+# Checkout the appropriate revision/tag
+if [ ! -z "$rev" ]; then
+ pushd . >/dev/null
+ cd $repo
+ git checkout "$rev"
+ popd >/dev/null
+else
+ log "Revision not specified, using HEAD."
+ rev="HEAD"
+fi
+
+# Load the proper config
+#rm -rf ~/.lnst
+#cp -r env ~/.lnst
+cp -r env/* $repo/
+
+# Scan the pool and prepare the machines in it
+for machine in `ls -1 env/pool/`; do
+ hostname=`get_hostname env/pool/$machine`
+
+ # In case this script was killed and there are any slave processes
+ # left hanging on the machine.
+ ssh "root@$hostname" 'for p in `pgrep lnst-slave`; do kill -9 $p; done'
+
+ # Create a temporary dir for the git tree to be tested
+ remote_repo=`ssh "root@$hostname" "mktemp -d"`
+
+ # Transfer the repo to the machine
+ rsync -r --exclude "Logs" --exclude ".git" "$repo/" \
+ "root@$hostname:$remote_repo"
+
+ if [ ! -z $nm_off ]; then
+ ssh "root@$hostname" "cd $remote_repo && echo \"use_nm = false\" >> lnst-slave.conf"
+ fi
+
+ # Start the slave process
+ ssh -n -t -t "root@$hostname" "cd $remote_repo && ./lnst-slave" >/dev/null \
+ 2>/dev/null &
+ pid=$!
+
+ # Save the status (hostname, pid of the ssh session, repo path)
+ # so we can cleanup things properly after the tests.
+ if [ -z "$slave_status" ]; then
+ slave_status="$hostname $pid $remote_repo"
+ else
+ slave_status="$hostname $pid $remote_repo
+ $slave_status"
+ fi
+done
+
+sleep 1
+
+summary="Summary:"
+
+# Run the tests in the tests/ directory
+print_separator
+
+# In case the list of test names to run was omitted, run all of them
+if [ -z "$tests" ]; then
+ tests="`ls -1 tests/ | grep -v '\.sh$'`"
+fi
+
+for test_name in $tests; do
+ test_dir=`realpath tests/$test_name`
+ if [ -e "$test_dir/run.sh" ]; then
+ logn "Running test #$test_name: "
+ if [ -e $test_dir/desc ]; then
+ while read line; do log " $line"; done < $test_dir/desc
+ else
+ logn "\n"
+ fi
+ print_separator
+
+ pushd . >/dev/null
+ cd $test_dir
+
+ chmod +x run.sh
+ if [ -n "$log_dir" ]; then
+ log_path="$log_dir/$test_name"
+ mkdir -p "$log_path"
+
+ # A bit of black magic in bash.
+ # Explanation: http://bit.ly/1dxuMJI
+ log "`PATH="$repo:$PATH" ./run.sh \
+ > >(tee $log_path/stdout.log) \
+ 2> >(tee $log_path/stderr.log >&2)`"
+ else
+ PATH="$repo:$PATH" ./run.sh
+ fi
+
+ rv=$?
+
+ popd >/dev/null
+
+ print_separator
+ if [ "$rv" -eq "0" ]; then
+ log "Result #$test_name: PASS"
+ summary=`echo -e "$summary\n test #$test_name: PASS"`
+ else
+ log "Result #$test_name: FAIL"
+ summary=`echo -e "$summary\n test #$test_name: FAIL"`
+ #break
+ fi
+ print_separator
+ else
+ log "Skipping test #$test_name, run.sh file not found."
+ print_separator
+ summary=`echo -e "$summary\n test #$test_name: SKIPPED"`
+ fi
+done
+
+log "$summary"
+print_separator
+
+sleep 1
+
+# Cleanup
+while read line; do
+ hostname=`awk '{print $1}' <<<"$line"`
+ pid=`awk '{print $2}' <<<"$line"`
+ remote_repo=`awk '{print $3}' <<<"$line"`
+
+ kill $pid
+ ssh -n "root@$hostname" "rm -r $remote_repo"
+done <<< "$slave_status"
+
+rm -rf $repo
+
+rm -rf .lock
diff --git a/regression-tests/tests/0/desc b/regression-tests/tests/0/desc
new file mode 100644
index 0000000..3e9a2cf
--- /dev/null
+++ b/regression-tests/tests/0/desc
@@ -0,0 +1,6 @@
+Run lnst-ctl without any parameters
+
+A test for Issue #6. The lnst-ctl script failed in case it was run with
+no arguments at all.
+
+https://github.com/jpirko/lnst/issues/6
diff --git a/regression-tests/tests/0/run.sh b/regression-tests/tests/0/run.sh
new file mode 100755
index 0000000..ced2cbd
--- /dev/null
+++ b/regression-tests/tests/0/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+. ../lib.sh
+
+# initialize the test
+init_test
+
+# In this case, we want to catch the log for asserts.
+log="`lnst-ctl`"
+rv=$?
+
+# But we also want the log to be shown in test results.
+echo "$log"
+
+# To keep the lnst-ctl log separated from the asserts
+print_separator
+
+assert_log "ERROR" "No action specified" "$log"
+
+# Check the return value
+assert_status "error" $rv
+
+# finish the test
+end_test
diff --git a/regression-tests/tests/01/desc b/regression-tests/tests/01/desc
new file mode 100644
index 0000000..b59ca44
--- /dev/null
+++ b/regression-tests/tests/01/desc
@@ -0,0 +1,6 @@
+Check matching algorithm corner case
+
+Check that the controller fails in cas there's a machine in the recipe that has
+an interface connected to some network and there's no other connection from
+other machines to this network, a.k.a broken-connection. The controller should
+fail with an error.
diff --git a/regression-tests/tests/01/ipvs-ping-check.xml b/regression-tests/tests/01/ipvs-ping-check.xml
new file mode 100644
index 0000000..50d3556
--- /dev/null
+++ b/regression-tests/tests/01/ipvs-ping-check.xml
@@ -0,0 +1,35 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="vsnet">
+ <addresses>
+ <address value="192.168.210.4/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="40"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/01/run.sh b/regression-tests/tests/01/run.sh
new file mode 100755
index 0000000..4c1e9e4
--- /dev/null
+++ b/regression-tests/tests/01/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run ipvs-ping-check.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/1/desc b/regression-tests/tests/1/desc
new file mode 100644
index 0000000..d14cd8a
--- /dev/null
+++ b/regression-tests/tests/1/desc
@@ -0,0 +1,4 @@
+Basic sanity ping test
+
+This test just executes a ping between two arbitrary machines with a
+single plain ethernet interface.
diff --git a/regression-tests/tests/1/recipe.xml b/regression-tests/tests/1/recipe.xml
new file mode 100644
index 0000000..33fdaf1
--- /dev/null
+++ b/regression-tests/tests/1/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run module="IcmpPing" host="1" timeout="30">
+ <options>
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <option name="count" value="40"/>
+ <option name="interval" value="0.2"/>
+ <option name="limit_rate" value="95"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/1/run.sh b/regression-tests/tests/1/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/1/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/10/desc b/regression-tests/tests/10/desc
new file mode 100644
index 0000000..77fc69a
--- /dev/null
+++ b/regression-tests/tests/10/desc
@@ -0,0 +1,4 @@
+Check the prefix template function
+
+Check that the prefix function works correctly from the recipe and also from
+a python program using Task API.
diff --git a/regression-tests/tests/10/prefix-check-taskapi.xml b/regression-tests/tests/10/prefix-check-taskapi.xml
new file mode 100644
index 0000000..efc96b9
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check-taskapi.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.3/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.4/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task python="./prefix-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/10/prefix-check.py b/regression-tests/tests/10/prefix-check.py
new file mode 100644
index 0000000..34d674d
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check.py
@@ -0,0 +1,15 @@
+from lnst.Controller.Task import ctl
+
+m = ctl.get_host("lb")
+
+addr1 = m.get_ip("nic1")
+addr2 = m.get_ip("nic2")
+
+pfx1 = m.get_prefix("nic1")
+pfx2 = m.get_prefix("nic2")
+
+m.run("echo %s" % addr1)
+m.run("echo %s" % addr2)
+m.run("echo %s" % pfx1)
+m.run("echo %s" % pfx2)
+
diff --git a/regression-tests/tests/10/prefix-check.xml b/regression-tests/tests/10/prefix-check.xml
new file mode 100644
index 0000000..1b9ecba
--- /dev/null
+++ b/regression-tests/tests/10/prefix-check.xml
@@ -0,0 +1,44 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.3/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="lb">
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.4/24"/>
+ </addresses>
+ </eth>
+ <eth id="nic2" label="inet">
+ <addresses>
+ <address value="192.168.202.4/16"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="1"/>
+ </options>
+ </run>
+ <run host="lb" command="echo {ip(lb,nic1)}"/>
+ <run host="lb" command="echo {prefix(lb,nic1)}"/>
+ <run host="lb" command="echo {ip(lb,nic2,0)}"/>
+ <run host="lb" command="echo {prefix(lb,nic2,0)}"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/10/run.sh b/regression-tests/tests/10/run.sh
new file mode 100755
index 0000000..55d7815
--- /dev/null
+++ b/regression-tests/tests/10/run.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run prefix-check.xml | tee test10.log
+rv=${PIPESTATUS[0]}
+
+log=`cat test10.log`
+
+rm -f test10.log
+
+print_separator
+
+assert_status "pass" "$rv"
+
+assert_log "DEBUG" "echo 24" "$log"
+assert_log "DEBUG" "echo 192.168.200.4" "$log"
+assert_log "DEBUG" "echo 16" "$log"
+assert_log "DEBUG" "echo 192.168.202.4" "$log"
+
+lnst-ctl -d run prefix-check-taskapi.xml | tee test10-api.log
+rv=${PIPESTATUS[0]}
+
+log=`cat test10-api.log`
+
+rm -f test10-api.log
+
+print_separator
+
+assert_status "pass" "$rv"
+
+assert_log "DEBUG" "echo 24" "$log"
+assert_log "DEBUG" "echo 192.168.200.4" "$log"
+assert_log "DEBUG" "echo 16" "$log"
+assert_log "DEBUG" "echo 192.168.202.4" "$log"
+
+end_test
diff --git a/regression-tests/tests/11/desc b/regression-tests/tests/11/desc
new file mode 100644
index 0000000..6744e6c
--- /dev/null
+++ b/regression-tests/tests/11/desc
@@ -0,0 +1,3 @@
+An exception raised during configuration
+
+This causes the slave to die!
diff --git a/regression-tests/tests/11/recipe.xml b/regression-tests/tests/11/recipe.xml
new file mode 100644
index 0000000..2bb2c8d
--- /dev/null
+++ b/regression-tests/tests/11/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="a192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run module="IcmpPing" host="1" timeout="30">
+ <options>
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <option name="count" value="40"/>
+ <option name="interval" value="0.2"/>
+ <option name="limit_rate" value="95"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/11/run.sh b/regression-tests/tests/11/run.sh
new file mode 100755
index 0000000..d32561f
--- /dev/null
+++ b/regression-tests/tests/11/run.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+end_test
diff --git a/regression-tests/tests/12/desc b/regression-tests/tests/12/desc
new file mode 100644
index 0000000..e8dbb4b
--- /dev/null
+++ b/regression-tests/tests/12/desc
@@ -0,0 +1 @@
+Check xinclude resolving as described in issue #29
diff --git a/regression-tests/tests/12/run.sh b/regression-tests/tests/12/run.sh
new file mode 100755
index 0000000..db8e724
--- /dev/null
+++ b/regression-tests/tests/12/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run xinclude-check.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/12/sm01.xml b/regression-tests/tests/12/sm01.xml
new file mode 100644
index 0000000..3f3a042
--- /dev/null
+++ b/regression-tests/tests/12/sm01.xml
@@ -0,0 +1,7 @@
+<interfaces>
+ <eth id="1" label="testnet">
+ <addresses>
+ <address value="192.168.122.10/24" />
+ </addresses>
+ </eth>
+</interfaces>
diff --git a/regression-tests/tests/12/sm02.xml b/regression-tests/tests/12/sm02.xml
new file mode 100644
index 0000000..ef59e63
--- /dev/null
+++ b/regression-tests/tests/12/sm02.xml
@@ -0,0 +1,7 @@
+<interfaces>
+ <eth id="1" label="testnet">
+ <addresses>
+ <address value="192.168.122.20/24" />
+ </addresses>
+ </eth>
+</interfaces>
diff --git a/regression-tests/tests/12/xinclude-check.xml b/regression-tests/tests/12/xinclude-check.xml
new file mode 100644
index 0000000..6bdf7bf
--- /dev/null
+++ b/regression-tests/tests/12/xinclude-check.xml
@@ -0,0 +1,13 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <network>
+ <host id="1">
+ <xi:include href="sm01.xml"/>
+ </host>
+ <host id="2">
+ <xi:include href="sm02.xml"/>
+ </host>
+ </network>
+ <task>
+ <run command="echo {ip(1,1)}" host="2"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/13/desc b/regression-tests/tests/13/desc
new file mode 100644
index 0000000..22b8aeb
--- /dev/null
+++ b/regression-tests/tests/13/desc
@@ -0,0 +1 @@
+This test checks that aliases are visible in python tasks
diff --git a/regression-tests/tests/13/run.sh b/regression-tests/tests/13/run.sh
new file mode 100755
index 0000000..f988ae6
--- /dev/null
+++ b/regression-tests/tests/13/run.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run taskapi-alias-check.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+lnst-ctl -d -A alias1="value2" run taskapi-alias-check.xml
+rv=$?
+
+assert_status "fail" "$rv"
+
+lnst-ctl -d run taskapi-alias-namespace-check.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/13/taskapi-alias-check.py b/regression-tests/tests/13/taskapi-alias-check.py
new file mode 100644
index 0000000..422fdd3
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-check.py
@@ -0,0 +1,9 @@
+import sys
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("m1")
+m2 = ctl.get_host("m2")
+
+m1.run("[ \"%s\" == \"value1\" ]" % ctl.get_alias("alias1"))
+m2.run("[ \"%s\" == \"value2\" ]" % ctl.get_alias("alias2"))
+
diff --git a/regression-tests/tests/13/taskapi-alias-check.xml b/regression-tests/tests/13/taskapi-alias-check.xml
new file mode 100644
index 0000000..2745c00
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-check.xml
@@ -0,0 +1,38 @@
+<lnstrecipe>
+ <define>
+ <alias name="alias1" value="value1"/>
+ </define>
+ <network>
+ <define>
+ <alias name="m1_addr" value="192.168.111.1/24"/>
+ </define>
+ <host id="m1">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address>{$m1_addr}</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <define>
+ <alias name="m2_addr" value="192.168.111.2/24"/>
+ </define>
+ <host id="m2">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address value="{$m1_addr}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <define>
+ <alias name="alias2" value="value2"/>
+ </define>
+
+ <task python="taskapi-alias-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/13/taskapi-alias-namespace-check.py b/regression-tests/tests/13/taskapi-alias-namespace-check.py
new file mode 100644
index 0000000..afed22d
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-namespace-check.py
@@ -0,0 +1,8 @@
+import sys
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("m1")
+
+# this should fail
+m1.run("[ \"%s\" == \"value1\" ]" % ctl.get_alias("foo"))
+
diff --git a/regression-tests/tests/13/taskapi-alias-namespace-check.xml b/regression-tests/tests/13/taskapi-alias-namespace-check.xml
new file mode 100644
index 0000000..9f9109d
--- /dev/null
+++ b/regression-tests/tests/13/taskapi-alias-namespace-check.xml
@@ -0,0 +1,41 @@
+<lnstrecipe>
+ <define>
+ <alias name="alias1" value="value1"/>
+ </define>
+ <network>
+ <define>
+ <alias name="m1_addr" value="192.168.111.1/24"/>
+ </define>
+ <host id="m1">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address>{$m1_addr}</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <define>
+ <alias name="m2_addr" value="192.168.111.2/24"/>
+ </define>
+ <host id="m2">
+ <interfaces>
+ <eth id="nic1" label="testnet">
+ <addresses>
+ <address value="{$m1_addr}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <define>
+ <alias name="foo" value="bar"/>
+ </define>
+ </network>
+
+ <define>
+ <alias name="alias2" value="value2"/>
+ </define>
+
+ <task python="taskapi-alias-namespace-check.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/desc b/regression-tests/tests/14/desc
new file mode 100644
index 0000000..b595c43
--- /dev/null
+++ b/regression-tests/tests/14/desc
@@ -0,0 +1,3 @@
+crash with more than two masters in stack with NetworkManager
+
+https://github.com/jpirko/lnst/issues/70
diff --git a/regression-tests/tests/14/recipe1.xml b/regression-tests/tests/14/recipe1.xml
new file mode 100644
index 0000000..9d9d45a
--- /dev/null
+++ b/regression-tests/tests/14/recipe1.xml
@@ -0,0 +1,68 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A"/>
+ <team id="team">
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ <options>
+ <option name="teamd_config">
+ {
+ "hwaddr": "00:11:22:33:44:55",
+ "runner": {"name": "activebackup"},
+ "link_watch": {"name": "ethtool"}
+ }
+ </option>
+ </options>
+ <addresses>
+ <address value="192.168.100.3/24"/>
+ </addresses>
+ </team>
+ <vlan id="vlan">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.1/24"/>
+ </addresses>
+ <slaves>
+ <slave id="team"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="2" label="A"/>
+ <vlan id="testifc2">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" command="ip a"/>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2,testifc2)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan)}"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/recipe2.xml b/regression-tests/tests/14/recipe2.xml
new file mode 100644
index 0000000..4d165f6
--- /dev/null
+++ b/regression-tests/tests/14/recipe2.xml
@@ -0,0 +1,79 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A"/>
+ <vlan id="vlan1">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.100.1/24"/>
+ </addresses>
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ </vlan>
+ <vlan id="vlan2">
+ <options>
+ <option name="vlan_tci" value="2"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ <slaves>
+ <slave id="1"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="2" label="A"/>
+ <vlan id="vlan1">
+ <options>
+ <option name="vlan_tci" value="1"/>
+ </options>
+ <addresses>
+ <address value="192.168.100.3/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ <vlan id="vlan2">
+ <options>
+ <option name="vlan_tci" value="2"/>
+ </options>
+ <addresses>
+ <address value="192.168.101.4/24"/>
+ </addresses>
+ <slaves>
+ <slave id="2"/>
+ </slaves>
+ </vlan>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2, vlan1)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan1)}"/>
+ </options>
+ </run>
+ <run host="slave1" module="IcmpPing">
+ <options>
+ <option name="addr" value="{ip(slave2, vlan2)}"/>
+ <option name="count" value="3"/>
+ <option name="iface" value="{devname(slave1, vlan2)}"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/14/run.sh b/regression-tests/tests/14/run.sh
new file mode 100755
index 0000000..42ccbcc
--- /dev/null
+++ b/regression-tests/tests/14/run.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe1.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+lnst-ctl -d run recipe2.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/15/desc b/regression-tests/tests/15/desc
new file mode 100644
index 0000000..29c8517
--- /dev/null
+++ b/regression-tests/tests/15/desc
@@ -0,0 +1,3 @@
+devname updates not reflected in {Net,Nm}DeviceConfig classes
+
+https://github.com/jpirko/lnst/issues/65
diff --git a/regression-tests/tests/15/recipe.xml b/regression-tests/tests/15/recipe.xml
new file mode 100644
index 0000000..3225b26
--- /dev/null
+++ b/regression-tests/tests/15/recipe.xml
@@ -0,0 +1,33 @@
+<lnstrecipe>
+ <network>
+ <host id="slave1">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A">
+ <addresses>
+ <address value="192.168.101.1/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ <host id="slave2">
+ <params/>
+ <interfaces>
+ <eth id="1" label="A">
+ <addresses>
+ <address value="192.168.101.2/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+
+ </network>
+
+ <task>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} down"/>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} name xyz"/>
+ <run host="slave1" command="ip link set {devname(slave1, 1)} up"/>
+ <run host="slave1" command="ip a show dev xyz"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/15/run.sh b/regression-tests/tests/15/run.sh
new file mode 100755
index 0000000..50ab9ce
--- /dev/null
+++ b/regression-tests/tests/15/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/2/desc b/regression-tests/tests/2/desc
new file mode 100644
index 0000000..9f606a7
--- /dev/null
+++ b/regression-tests/tests/2/desc
@@ -0,0 +1,5 @@
+RecipeParser: Lots of comments in a XML
+
+There was a problem with comments at some places in the recipe XML.
+This recipe should cover all the possibilities and make sure they are
+properly ignored.
diff --git a/regression-tests/tests/2/recipe.xml b/regression-tests/tests/2/recipe.xml
new file mode 100644
index 0000000..0565f47
--- /dev/null
+++ b/regression-tests/tests/2/recipe.xml
@@ -0,0 +1,74 @@
+<!--comment-->
+<lnstrecipe>
+ <!--comment-->
+ <network>
+ <!--comment-->
+ <host id="1">
+ <!--comment-->
+ <params>
+ <!--comment-->
+ </params>
+ <!--comment-->
+ <interfaces>
+ <!--comment-->
+ <eth label="tnet" id="testiface">
+ <!--comment-->
+ <addresses>
+ <!--comment-->
+ <address value="192.168.100.10/24"/>
+ <!--comment-->
+ </addresses>
+ <!--comment-->
+ </eth>
+ <!--comment-->
+ </interfaces>
+ <!--comment-->
+ </host>
+ <!--comment-->
+ <host id="2">
+ <!--comment-->
+ <params>
+ <!--comment-->
+ </params>
+ <!--comment-->
+ <interfaces>
+ <!--comment-->
+ <eth label="tnet" id="testiface">
+ <!--comment-->
+ <addresses>
+ <!--comment-->
+ <address value="192.168.100.11/24"/>
+ <!--comment-->
+ </addresses>
+ <!--comment-->
+ </eth>
+ <!--comment-->
+ </interfaces>
+ <!--comment-->
+ </host>
+ <!--comment-->
+ </network>
+ <!--comment-->
+
+ <task>
+ <!--comment-->
+ <run module="IcmpPing" host="1" timeout="30">
+ <!--comment-->
+ <options>
+ <!--comment-->
+ <option name="addr" value="{ip(2,testiface)}"/>
+ <!--comment-->
+ <option name="count" value="40"/>
+ <!--comment-->
+ <option name="interval" value="0.2"/>
+ <!--comment-->
+ <option name="limit_rate" value="95"/>
+ <!--comment-->
+ </options>
+ <!--comment-->
+ </run>
+ <!--comment-->
+ </task>
+ <!--comment-->
+</lnstrecipe>
+<!--comment-->
diff --git a/regression-tests/tests/2/run.sh b/regression-tests/tests/2/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/2/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/3/desc b/regression-tests/tests/3/desc
new file mode 100644
index 0000000..7eeaf6e
--- /dev/null
+++ b/regression-tests/tests/3/desc
@@ -0,0 +1,4 @@
+Check corner case in matching algorithm
+
+This test checks that controller fails if it runs a recipe with just one
+machine specified. The controller should fail.
diff --git a/regression-tests/tests/3/recipe.xml b/regression-tests/tests/3/recipe.xml
new file mode 100644
index 0000000..c261555
--- /dev/null
+++ b/regression-tests/tests/3/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="client">
+ <params>
+ <param name="os" value="rhel7"/>
+ </params>
+ <interfaces>
+ <eth id="nic1" label="inet">
+ <addresses>
+ <address value="192.168.200.3/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+ <task>
+ <run module="IcmpPing" host="client">
+ <options>
+ <option name="addr" value="{ip(lb,nic1)}"/>
+ <option name="count" value="40"/>
+ </options>
+ </run>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/3/run.sh b/regression-tests/tests/3/run.sh
new file mode 100755
index 0000000..e6e9a99
--- /dev/null
+++ b/regression-tests/tests/3/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "error" "$rv"
+
+end_test
diff --git a/regression-tests/tests/4/desc b/regression-tests/tests/4/desc
new file mode 100644
index 0000000..ad44b34
--- /dev/null
+++ b/regression-tests/tests/4/desc
@@ -0,0 +1,3 @@
+Aliases resolving
+
+This test aims to verify the correctness of aliases resloving.
diff --git a/regression-tests/tests/4/recipe.xml b/regression-tests/tests/4/recipe.xml
new file mode 100644
index 0000000..bc0c4b4
--- /dev/null
+++ b/regression-tests/tests/4/recipe.xml
@@ -0,0 +1,43 @@
+<lnstrecipe>
+ <define>
+ <alias name="one" value="192.168.100.10/24"/>
+ <alias name="two" value="192.168.100.11/24"/>
+ </define>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="{$one}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="{$two}"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <define>
+ <alias name="one" value="one"/>
+ <alias name="two" value="two"/>
+ </define>
+
+ <task>
+ <run command="echo 1_{$one}_{$two}" host="1"/>
+ <define>
+ <alias name="one" value="eno"/>
+ <alias name="two" value="owt"/>
+ </define>
+ <run command="echo 2_{$one}_{$two}" host="1"/>
+ </task>
+ <task>
+ <run command="echo 3_{$one}_{$two}" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/4/run.sh b/regression-tests/tests/4/run.sh
new file mode 100755
index 0000000..f3d8ee2
--- /dev/null
+++ b/regression-tests/tests/4/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+log="`lnst-ctl -d run recipe.xml`"
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log "DEBUG" "echo 1_one_two" "$log"
+assert_log "DEBUG" "echo 2_eno_owt" "$log"
+assert_log "DEBUG" "echo 3_one_two" "$log"
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/5/desc b/regression-tests/tests/5/desc
new file mode 100644
index 0000000..7f3448f
--- /dev/null
+++ b/regression-tests/tests/5/desc
@@ -0,0 +1,3 @@
+Template functions resolving
+
+This test aims to verify the correctness of template functions resloving.
diff --git a/regression-tests/tests/5/recipe.xml b/regression-tests/tests/5/recipe.xml
new file mode 100644
index 0000000..1bc9137
--- /dev/null
+++ b/regression-tests/tests/5/recipe.xml
@@ -0,0 +1,29 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="echo ip1_{ip(1,testiface)}_" host="1"/>
+ <run command="echo ip2_{ip(1,testiface,0)}_" host="1"/>
+ <run command="echo devname_{devname(1,testiface)}_" host="1"/>
+ <run command="echo hwaddr_{hwaddr(1,testiface)}_" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/5/run.sh b/regression-tests/tests/5/run.sh
new file mode 100755
index 0000000..f19786e
--- /dev/null
+++ b/regression-tests/tests/5/run.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+log="`lnst-ctl -d run recipe.xml`"
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log "DEBUG" "echo ip1_192.168.100.10_" "$log"
+assert_log "DEBUG" "echo ip2_192.168.100.10_" "$log"
+assert_log "DEBUG" "echo hwaddr_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log "DEBUG" "echo devname_[a-zA-Z0-9]\+_" "$log"
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/6/desc b/regression-tests/tests/6/desc
new file mode 100644
index 0000000..f75fb3d
--- /dev/null
+++ b/regression-tests/tests/6/desc
@@ -0,0 +1,4 @@
+Dynamic hwaddr updates
+
+This test case can verify whether the dynamic configuration updates are
+propagated to the controller properly.
diff --git a/regression-tests/tests/6/lnst-ctl.conf b/regression-tests/tests/6/lnst-ctl.conf
new file mode 100644
index 0000000..7a8d46d
--- /dev/null
+++ b/regression-tests/tests/6/lnst-ctl.conf
@@ -0,0 +1,2 @@
+[environment]
+machine_pool_dirs = ./pool/
diff --git a/regression-tests/tests/6/recipe.xml b/regression-tests/tests/6/recipe.xml
new file mode 100644
index 0000000..1f084d0
--- /dev/null
+++ b/regression-tests/tests/6/recipe.xml
@@ -0,0 +1,35 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="echo `ip link show {devname(1,testiface)} | grep -o ' link/ether \([0-9a-fA-F]\{2\}:\?\)\{6\}' | cut -c 16-` >/tmp/lnst-hwaddr" host="1"/>
+ <run command="echo 1_{hwaddr(1, testiface)}_" host="1"/>
+ <run command="ip l set {devname(1, testiface)} address 52:54:00:12:34:56" host="1"/>
+ <ctl_wait seconds="2"/>
+ <run command="echo 2_{hwaddr(1, testiface)}_" host="1"/>
+ <run command="ip l set {devname(1, testiface)} address `cat /tmp/lnst-hwaddr`" host="1"/>
+ <ctl_wait seconds="2"/>
+ <run command="echo 3_{hwaddr(1, testiface)}_`cat /tmp/lnst-hwaddr | tr '[:lower:]' '[:upper:]'`_" host="1"/>
+ <run command="rm -f /tmp/lnst-hwaddr" host="1"/>
+ </task>
+</lnstrecipe>
+
diff --git a/regression-tests/tests/6/run.sh b/regression-tests/tests/6/run.sh
new file mode 100755
index 0000000..3882d0d
--- /dev/null
+++ b/regression-tests/tests/6/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+ln -s ../../env/pool
+
+log=`lnst-ctl -c lnst-ctl.conf -d run recipe.xml`
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log ".*" "1_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log ".*" "2_52:54:00:12:34:56_" "$log"
+assert_log ".*" "3_\(\([a-fA-F0-9]\{2\}:\?\)\{6\}\)_\1_" "$log"
+
+assert_status "pass" "$rv"
+
+rm -f ./pool
+
+end_test
diff --git a/regression-tests/tests/7/desc b/regression-tests/tests/7/desc
new file mode 100644
index 0000000..4a68dd6
--- /dev/null
+++ b/regression-tests/tests/7/desc
@@ -0,0 +1,4 @@
+Configuration updates (hwaddr) in python tasks
+
+The purpose of this test is to verify that the configuration updates
+propagate correctly from the slaves back to controller.
diff --git a/regression-tests/tests/7/lnst-ctl.conf b/regression-tests/tests/7/lnst-ctl.conf
new file mode 100644
index 0000000..7a8d46d
--- /dev/null
+++ b/regression-tests/tests/7/lnst-ctl.conf
@@ -0,0 +1,2 @@
+[environment]
+machine_pool_dirs = ./pool/
diff --git a/regression-tests/tests/7/recipe.xml b/regression-tests/tests/7/recipe.xml
new file mode 100644
index 0000000..88f26f8
--- /dev/null
+++ b/regression-tests/tests/7/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="task.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/7/run.sh b/regression-tests/tests/7/run.sh
new file mode 100755
index 0000000..3882d0d
--- /dev/null
+++ b/regression-tests/tests/7/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+ln -s ../../env/pool
+
+log=`lnst-ctl -c lnst-ctl.conf -d run recipe.xml`
+rv=$?
+
+echo "$log"
+print_separator
+
+assert_log ".*" "1_\([a-fA-F0-9]\{2\}:\?\)\{6\}_" "$log"
+assert_log ".*" "2_52:54:00:12:34:56_" "$log"
+assert_log ".*" "3_\(\([a-fA-F0-9]\{2\}:\?\)\{6\}\)_\1_" "$log"
+
+assert_status "pass" "$rv"
+
+rm -f ./pool
+
+end_test
diff --git a/regression-tests/tests/7/task.py b/regression-tests/tests/7/task.py
new file mode 100644
index 0000000..640061e
--- /dev/null
+++ b/regression-tests/tests/7/task.py
@@ -0,0 +1,23 @@
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("1")
+m2 = ctl.get_host("2")
+
+devname = m1.get_devname("testiface")
+hwaddr = m1.get_hwaddr("testiface")
+
+m1.run("echo 1_%s_" % hwaddr)
+
+m1.run("echo `ip link show %s | grep -o ' link/ether \([0-9a-fA-F]\{2\}:\?\)\{6\}' | cut -c 16-` >/tmp/lnst-hwaddr" % devname)
+m1.run("ip l set %s address 52:54:00:12:34:56" % devname)
+ctl.wait(2)
+
+m1.run("echo 2_%s_" % hwaddr)
+m1.run("ip l set %s address `cat /tmp/lnst-hwaddr`" % devname)
+
+ctl.wait(2)
+
+m1.run("echo 3_%s_`cat /tmp/lnst-hwaddr | tr '[:lower:]' '[:upper:]'`_" % hwaddr)
+
+m1.run("rm -f /tmp/lnst-hwaddr")
+
diff --git a/regression-tests/tests/8/desc b/regression-tests/tests/8/desc
new file mode 100644
index 0000000..41c2ca2
--- /dev/null
+++ b/regression-tests/tests/8/desc
@@ -0,0 +1,3 @@
+Recipe Summary Crashing when Exec fails
+
+Regression test for a bug
diff --git a/regression-tests/tests/8/recipe.xml b/regression-tests/tests/8/recipe.xml
new file mode 100644
index 0000000..dc3a692
--- /dev/null
+++ b/regression-tests/tests/8/recipe.xml
@@ -0,0 +1,26 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task>
+ <run command="eecho weeee" host="1"/>
+ </task>
+</lnstrecipe>
diff --git a/regression-tests/tests/8/run.sh b/regression-tests/tests/8/run.sh
new file mode 100755
index 0000000..1eefc21
--- /dev/null
+++ b/regression-tests/tests/8/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl -d run recipe.xml
+rv=$?
+
+assert_status "fail" "$rv"
+
+end_test
diff --git a/regression-tests/tests/9/desc b/regression-tests/tests/9/desc
new file mode 100644
index 0000000..2e71f6a
--- /dev/null
+++ b/regression-tests/tests/9/desc
@@ -0,0 +1,3 @@
+Python Tasks
+
+Basic sanity of python tasks implementation (a simple ping test).
diff --git a/regression-tests/tests/9/recipe.xml b/regression-tests/tests/9/recipe.xml
new file mode 100644
index 0000000..88f26f8
--- /dev/null
+++ b/regression-tests/tests/9/recipe.xml
@@ -0,0 +1,24 @@
+<lnstrecipe>
+ <network>
+ <host id="1">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.10/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="2">
+ <interfaces>
+ <eth label="tnet" id="testiface">
+ <addresses>
+ <address value="192.168.100.11/24"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="task.py"/>
+</lnstrecipe>
diff --git a/regression-tests/tests/9/run.sh b/regression-tests/tests/9/run.sh
new file mode 100755
index 0000000..45e9995
--- /dev/null
+++ b/regression-tests/tests/9/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. ../lib.sh
+
+init_test
+
+lnst-ctl run recipe.xml
+rv=$?
+
+assert_status "pass" "$rv"
+
+end_test
diff --git a/regression-tests/tests/9/task.py b/regression-tests/tests/9/task.py
new file mode 100644
index 0000000..e718537
--- /dev/null
+++ b/regression-tests/tests/9/task.py
@@ -0,0 +1,9 @@
+from lnst.Controller.Task import ctl
+
+m1 = ctl.get_host("1")
+m2 = ctl.get_host("2")
+
+ping_mod = ctl.get_module("IcmpPing", options={"addr": m2.get_ip("testiface", 0),
+ "count":40, "interval": 0.2, "limit_rate": 95})
+
+ping_test = m1.run(ping_mod, timeout=30)
diff --git a/regression-tests/tests/lib.sh b/regression-tests/tests/lib.sh
new file mode 100644
index 0000000..df83ab1
--- /dev/null
+++ b/regression-tests/tests/lib.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+function init_test
+{
+ # test status 0 means everything went fine, 1 means there were
+ # one or more problems.
+ test_status=0
+}
+
+function end_test
+{
+ exit "$test_status"
+}
+
+function assert_log
+{
+ local level="$1"
+ local message_regexp="$2"
+ local log="$3"
+
+ if [ -z "`echo "$log" | grep "$level" | grep "$message_regexp"`" ]; then
+ echo "assert_log FAILED ($level, $message_regexp)"
+ test_status=1
+ else
+ echo "assert_log PASSED ($level, $message_regexp)"
+ fi
+}
+
+function assert_status
+{
+ local expect="$1"
+ local retval="$2"
+
+ case $expect in
+ "pass")
+ local value=0 ;;
+ "fail")
+ local value=1 ;;
+ "error")
+ local value=2 ;;
+ *)
+ echo "Unknown exit status '$expect'!"
+ exit 128
+ ;;
+ esac
+
+ if [ $value -ne $retval ]; then
+ test_status=1
+ echo "assert_status FAILED (expected: $value, real: $retval)"
+ else
+ echo "assert_status PASSED (expected: $value, real: $retval)"
+ fi
+}
+
+function print_separator
+{
+ echo "--------------------------------------------------------------------------------"
+}
8 years, 9 months
[lnst] Created tag v6
by Jiří Pírko
The lightweight tag 'v6' was created pointing to:
ece3397... 6 release
8 years, 10 months
[lnst] 6 release
by Jiří Pírko
commit ece3397637ba688dac4e6dae9d7514c817662af2
Author: Jiri Pirko <jiri(a)resnulli.us>
Date: Fri Dec 5 11:51:04 2014 +0100
6 release
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
setup.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/setup.py b/setup.py
index f3df092..466ed76 100755
--- a/setup.py
+++ b/setup.py
@@ -171,7 +171,7 @@ DATA_FILES = CONFIG + TEST_MODULES + MULTICAST_TEST_TOOLS + MAN_PAGES + \
SCHEMAS + BASH_COMP + RECIPE_FILES
setup(name="lnst",
- version="5",
+ version="6",
description="Linux Network Stack Test",
author="LNST Team",
author_email="lnst-developers(a)lists.fedorahosted.org",
8 years, 10 months
[lnst] NetUtils: use IPRoute instead of IPRSocket
by Jiří Pírko
commit eb618cb62d8ee90983a0214ca91cc98d1d92a05e
Author: Peter V. Saveliev <peter(a)svinota.eu>
Date: Thu Nov 20 16:11:17 2014 +0100
NetUtils: use IPRoute instead of IPRSocket
Since IPRoute became threadless, it is simpler to use IPRoute
methods like `get_links()` to gather the info.
Signed-off-by: Peter V. Saveliev <peter(a)svinota.eu>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/NetUtils.py | 72 +++++++++++++++++------------------------------
1 files changed, 26 insertions(+), 46 deletions(-)
---
diff --git a/lnst/Common/NetUtils.py b/lnst/Common/NetUtils.py
index d9440ed..48a82a3 100644
--- a/lnst/Common/NetUtils.py
+++ b/lnst/Common/NetUtils.py
@@ -10,22 +10,10 @@ __author__ = """
rpazdera(a)redhat.com (Radek Pazdera)
"""
-import os
import re
import socket
import subprocess
-from pyroute2 import IPRSocket
-from pyroute2.netlink import NLM_F_REQUEST
-from pyroute2.netlink import NLM_F_DUMP
-from pyroute2.netlink import NLMSG_DONE
-from pyroute2.netlink import NLMSG_ERROR
-try:
- from pyroute2.netlink.iproute import RTM_GETLINK
- from pyroute2.netlink.iproute import RTM_NEWLINK
-except ImportError:
- from pyroute2.iproute import RTM_GETLINK
- from pyroute2.iproute import RTM_NEWLINK
-from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg
+from pyroute2 import IPRoute
def normalize_hwaddr(hwaddr):
@@ -37,39 +25,31 @@ def normalize_hwaddr(hwaddr):
def scan_netdevs():
scan = []
- nl_socket = IPRSocket()
- msg = ifinfmsg()
- msg["family"] = socket.AF_UNSPEC
- msg["header"]["type"] = RTM_GETLINK
- msg["header"]["flags"] = NLM_F_REQUEST | NLM_F_DUMP
- msg["header"]["pid"] = os.getpid()
- msg["header"]["sequence_number"] = 1
- msg.encode()
-
- nl_socket.sendto(msg.buf.getvalue(), (0, 0))
-
- finished = False
- while not finished:
- parts = nl_socket.get()
- for part in parts:
- if part["header"]["type"] in [NLMSG_DONE, NLMSG_ERROR]:
- finished = True
- continue
- if part["header"]["sequence_number"] != 1:
- continue
-
- if part["header"]["type"] == RTM_NEWLINK:
- new_link = {}
- new_link["netlink_msg"] = part
- new_link["index"] = part["index"]
- new_link["name"] = part.get_attr("IFLA_IFNAME")
-
- hwaddr = part.get_attr("IFLA_ADDRESS")
- new_link["hwaddr"] = normalize_hwaddr(hwaddr)
-
- scan.append(new_link)
-
- nl_socket.close()
+ ipr = IPRoute()
+
+ try:
+ for part in ipr.get_links():
+ new_link = {}
+ new_link["netlink_msg"] = part
+ new_link["index"] = part["index"]
+ new_link["name"] = part.get_attr("IFLA_IFNAME")
+ #
+ # FIXME:
+ #
+ # nlmsg.get_attr() returns None if there is no
+ # such attribute in the NLA chain; if hwaddr is None,
+ # normalize_hwaddr(hwaddr) will raise AttributeError(),
+ # since None has no upper(). The issue is that the
+ # AttributeError() will be a bit unrelated to the
+ # root cause, and since that it will be confusing.
+ #
+ hwaddr = part.get_attr("IFLA_ADDRESS")
+ new_link["hwaddr"] = normalize_hwaddr(hwaddr)
+ scan.append(new_link)
+ except:
+ raise
+ finally:
+ ipr.close()
return scan
8 years, 10 months
[lnst] NetUtils: fix PEP8 issues
by Jiří Pírko
commit 1b4783dcd8e28899b860963cacef1b1ec25f4ed5
Author: Peter V. Saveliev <peter(a)svinota.eu>
Date: Thu Nov 20 16:04:43 2014 +0100
NetUtils: fix PEP8 issues
Having automated flake8 testing it is easier to catch some
issues prior they will cause real bugs; but to be used
with flake8, the code must be PEP8-compliant.
Signed-off-by: Peter V. Saveliev <peter(a)svinota.eu>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/NetUtils.py | 14 +++++++++++---
1 files changed, 11 insertions(+), 3 deletions(-)
---
diff --git a/lnst/Common/NetUtils.py b/lnst/Common/NetUtils.py
index a578fb7..d9440ed 100644
--- a/lnst/Common/NetUtils.py
+++ b/lnst/Common/NetUtils.py
@@ -10,7 +10,6 @@ __author__ = """
rpazdera(a)redhat.com (Radek Pazdera)
"""
-import logging
import os
import re
import socket
@@ -28,12 +27,14 @@ except ImportError:
from pyroute2.iproute import RTM_NEWLINK
from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg
+
def normalize_hwaddr(hwaddr):
try:
return hwaddr.upper().rstrip("\n")
except:
return ""
+
def scan_netdevs():
scan = []
nl_socket = IPRSocket()
@@ -45,7 +46,7 @@ def scan_netdevs():
msg["header"]["sequence_number"] = 1
msg.encode()
- nl_socket.sendto(msg.buf.getvalue(), (0,0))
+ nl_socket.sendto(msg.buf.getvalue(), (0, 0))
finished = False
while not finished:
@@ -71,6 +72,7 @@ def scan_netdevs():
nl_socket.close()
return scan
+
def test_tcp_connection(host, port):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -80,6 +82,7 @@ def test_tcp_connection(host, port):
except:
return False
+
def verify_ip_address(addr):
if len(addr.split('.')) != 4:
return False
@@ -89,12 +92,14 @@ def verify_ip_address(addr):
except:
return False
+
def verify_mac_address(addr):
if re.match("^[0-9a-f]{2}([:][0-9a-f]{2}){5}$", addr, re.I):
return True
else:
return False
+
def get_corespond_local_ip(query_ip):
"""
Get ip address in local system which can communicate with query_ip.
@@ -107,11 +112,12 @@ def get_corespond_local_ip(query_ip):
shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
ip = ip.communicate()[0]
- ip = re.search(r"src ([0-9.]*)",ip)
+ ip = re.search(r"src ([0-9.]*)", ip)
if ip is None:
return ip
return ip.group(1)
+
class AddressPool:
def __init__(self, start, end):
self._next = self._addr_to_byte_string(start)
@@ -141,6 +147,7 @@ class AddressPool:
return addr_str
+
class MacPool(AddressPool):
def _addr_to_byte_string(self, addr):
if not verify_mac_address(addr):
@@ -153,6 +160,7 @@ class MacPool(AddressPool):
def _byte_string_to_addr(self, byte_string):
return ':'.join(map(lambda x: "%02x" % x, byte_string))
+
class IpPool(AddressPool):
def _addr_to_byte_string(self, addr):
if not verify_ip_address(addr):
8 years, 10 months
[lnst] Controller: sync resources also to namespaces
by Jiří Pírko
commit 01e6a1c34ff07442ded9d5c25d4214d96ffeb228
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Tue Dec 2 17:55:11 2014 +0100
Controller: sync resources also to namespaces
Using reduced syncing and MachineAPI.sync_resources() shows that the
resources are properly synced however not available for commands or
test modules run in specific netns. This patch extends resource syncing
to slavemachine's configured namespaces.
I had to add netns parameter to copy_file_to_machine method, too.
Copying the same file to slave machine is not the most efficient
approach, however it fixes the issue until better approach is found.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Machine.py | 31 +++++++++++++++++++++++++++----
1 files changed, 27 insertions(+), 4 deletions(-)
---
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 8c2c41e..9a5d243 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -375,8 +375,13 @@ class Machine(object):
else:
self._rpc_call_to_netns(netns, "stop_packet_capture")
- def copy_file_to_machine(self, local_path, remote_path=None):
- remote_path = self._rpc_call("start_copy_to", remote_path)
+ def copy_file_to_machine(self, local_path, remote_path=None, netns=None):
+ if netns:
+ remote_path = self._rpc_call_to_netns(netns, "start_copy_to",
+ remote_path)
+ else:
+ remote_path = self._rpc_call("start_copy_to", remote_path)
+
f = open(local_path, "rb")
while True:
@@ -384,9 +389,17 @@ class Machine(object):
if len(data) == 0:
break
- self._rpc_call("copy_part_to", remote_path, Binary(data))
+ if netns:
+ self._rpc_call_to_netns(netns, "copy_part_to", remote_path,
+ Binary(data))
+ else:
+ self._rpc_call("copy_part_to", remote_path, Binary(data))
+
+ if netns:
+ self._rpc_call_to_netns(netns, "finish_copy_to", remote_path)
+ else:
+ self._rpc_call("finish_copy_to", remote_path)
- self._rpc_call("finish_copy_to", remote_path)
return remote_path
def copy_file_from_machine(self, remote_path, local_path):
@@ -431,10 +444,20 @@ class Machine(object):
self._rpc_call("add_resource_to_cache", res["hash"],
remote_path, res_name, res["path"], res_type)
+ for ns in self._namespaces:
+ remote_path = self.copy_file_to_machine(local_path,
+ netns=ns)
+ self._rpc_call_to_netns(ns, "add_resource_to_cache",
+ res["hash"], remote_path, res_name, res["path"],
+ res_type)
+
if res_type == "tools":
os.unlink(archive_path)
self._rpc_call("map_resource", res["hash"], res_type, res_name)
+ for ns in self._namespaces:
+ self._rpc_call_to_netns(ns, "map_resource", res["hash"],
+ res_type, res_name)
def enable_nm(self):
return self._rpc_call("enable_nm")
8 years, 10 months
[PATCH] Controller: sync resources also to namespaces
by Jan Tluka
Using reduced syncing and MachineAPI.sync_resources() shows that the
resources are properly synced however not available for commands or
test modules run in specific netns. This patch extends resource syncing
to slavemachine's configured namespaces.
I had to add netns parameter to copy_file_to_machine method, too.
Copying the same file to slave machine is not the most efficient
approach, however it fixes the issue until better approach is found.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst/Controller/Machine.py | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 8c2c41e..9a5d243 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -375,8 +375,13 @@ class Machine(object):
else:
self._rpc_call_to_netns(netns, "stop_packet_capture")
- def copy_file_to_machine(self, local_path, remote_path=None):
- remote_path = self._rpc_call("start_copy_to", remote_path)
+ def copy_file_to_machine(self, local_path, remote_path=None, netns=None):
+ if netns:
+ remote_path = self._rpc_call_to_netns(netns, "start_copy_to",
+ remote_path)
+ else:
+ remote_path = self._rpc_call("start_copy_to", remote_path)
+
f = open(local_path, "rb")
while True:
@@ -384,9 +389,17 @@ class Machine(object):
if len(data) == 0:
break
- self._rpc_call("copy_part_to", remote_path, Binary(data))
+ if netns:
+ self._rpc_call_to_netns(netns, "copy_part_to", remote_path,
+ Binary(data))
+ else:
+ self._rpc_call("copy_part_to", remote_path, Binary(data))
+
+ if netns:
+ self._rpc_call_to_netns(netns, "finish_copy_to", remote_path)
+ else:
+ self._rpc_call("finish_copy_to", remote_path)
- self._rpc_call("finish_copy_to", remote_path)
return remote_path
def copy_file_from_machine(self, remote_path, local_path):
@@ -431,10 +444,20 @@ class Machine(object):
self._rpc_call("add_resource_to_cache", res["hash"],
remote_path, res_name, res["path"], res_type)
+ for ns in self._namespaces:
+ remote_path = self.copy_file_to_machine(local_path,
+ netns=ns)
+ self._rpc_call_to_netns(ns, "add_resource_to_cache",
+ res["hash"], remote_path, res_name, res["path"],
+ res_type)
+
if res_type == "tools":
os.unlink(archive_path)
self._rpc_call("map_resource", res["hash"], res_type, res_name)
+ for ns in self._namespaces:
+ self._rpc_call_to_netns(ns, "map_resource", res["hash"],
+ res_type, res_name)
def enable_nm(self):
return self._rpc_call("enable_nm")
--
1.9.3
8 years, 10 months
[lnst] Virtual bridge 2 VLANs over round-robin tests removed
by Jiří Pírko
commit 2f7f81f6024425b1455c1d890c98b88243e298d7
Author: Jiri Prochazka <jprochaz(a)redhat.com>
Date: Mon Dec 1 18:56:09 2014 +0100
Virtual bridge 2 VLANs over round-robin tests removed
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
...ual_bridge_2_vlans_over_round_robin_bond.README | 76 ----------
...irtual_bridge_2_vlans_over_round_robin_bond.xml | 153 --------------------
2 files changed, 0 insertions(+), 229 deletions(-)
8 years, 10 months
[lnst] setup.py: install recipes files
by Jiří Pírko
commit aa458a34a1c5250b36bc978a1fd14959f653d560
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon Dec 1 15:25:23 2014 +0100
setup.py: install recipes files
The setup.py script will now also install the contents of the recipes/
directory. This will be used to create a new lnst-recipes package.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
setup.py | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
---
diff --git a/setup.py b/setup.py
index 371fbc0..f3df092 100755
--- a/setup.py
+++ b/setup.py
@@ -20,6 +20,7 @@ rpazdera(a)redhat.com (Radek Pazdera)
import sys
import re
import gzip
+import os
from time import gmtime, strftime
from distutils.core import setup
@@ -103,6 +104,12 @@ project website <https://fedorahosted.org/lnst>.
PACKAGES = ["lnst", "lnst.Common", "lnst.Controller", "lnst.Slave"]
SCRIPTS = ["lnst-ctl", "lnst-slave"]
+RECIPE_FILES = []
+for dirpath, dirnames, files in os.walk("recipes/"):
+ if len(files) > 0:
+ RECIPE_FILES.append((CTL_RESOURCE_DIR + dirpath + "/",
+ [dirpath + "/" + f for f in files]))
+
TEST_MODULES = [
(CTL_MODULES_LOCATIONS,
["test_modules/DummyFailing.py",
@@ -161,7 +168,7 @@ BASH_COMP = [(BASH_COMP_DIR, ["install/lnst-ctl.bash",
SCHEMAS = [(CTL_RESOURCE_DIR, ["schema-recipe.rng", "schema-sm.rng"])]
DATA_FILES = CONFIG + TEST_MODULES + MULTICAST_TEST_TOOLS + MAN_PAGES + \
- SCHEMAS + BASH_COMP
+ SCHEMAS + BASH_COMP + RECIPE_FILES
setup(name="lnst",
version="5",
8 years, 10 months