Most of the openQA test cases run command sequentially. One command nicely after the next one. But in some cases it can be useful, to run a handful of commands in parallel and then wait for them to finish. Here we are covering the caveats of using the bash background operator
& in openQA.
A TL;DR is at the end of the post.
For instance, in virtualization testing, we install a handful of virtual machines in parallel via ``virt-install`:
assert_script_run('( virt-install --os-variant sles12sp3 --name sles12sp3 ... >> ~/virt-install_sles12sp3.txt 2>&1 &)'); assert_script_run('( virt-install --os-variant sles12sp4 --name sles12sp4 ... >> ~/virt-install_sles12sp4.txt 2>&1 &)'); assert_script_run('( virt-install --os-variant sles12sp5 --name sles12sp5 ... >> ~/virt-install_sles12sp5.txt 2>&1 &)');
The general problem is, that you would like to run a set of bash commands in parallel, and wait for all of them to complete. In terms of code, you would like to do the following:
$ sleep 30 & # or another command that takes some time $ sleep 30 & $ sleep 30 & $ sleep 30 & $ sleep 30 & $ wait
Unfortunately enclosing those comments by a simple
assert_script_run will not work, because of the handling with the serial terminal.
assert_script_run('sleep 30 &'); # will result in a bash syntax error
The problem is, that openqa adds something like
; echo ~59bn-\$?-" at the end of the command, resulting in
sleep 30 & ; echo ~59bn-\$?-
; are both terminators in bash resulting in an empty command between those two’s which is not allowed. A quick fix is to add
true in between those twos:
assert_script_run('sleep 30 & true'); # OK!
Enclosing within brackets is a bad idea
Another often seen solution is to enclose the commands between brackets:
assert_script_run('(sleep 30 & )'); # don't do that!
This is fine, unless you need to interact with your background jobs. The reason is, that the commands between the brackets are not available in the outside context. This means, bash does not keep track of those processes, so that a subsequent
wait will not wait for those. This can be seen in the following example
$ (sleep 30 & ); wait # will not wait for sleep, terminates immediately $ (sleep 30 & ); jobs # sleep is not visible
You can see, that the
sleep command is not accessible in the context outside of the brackets.
wait terminates immediately and
sleep does not appear as a job in
jobs. The correct way would be to omit the brackets and the semicolon:
$ sleep 30 & wait  12015 $ sleep 30 & jobs  12038 + Running sleep 30 &
So, back to our starting point, if you enclose the commands in brackets, a subsequent
wait will not work and this is why I believe that enclosing bash commands between brackets is a bad idea:
assert_script_run('(sleep 30 & )'); assert_script_run('wait'); # Will NOT wait for the sleep above!
One possible solution would be to add a
true at the end of the
assert_script_run('sleep 30 & true'); assert_script_run('wait'); # this would works
While thous would work, it is not really wise to use
script_run for background jobs. Both routines do much more than just running commands, and they might get confused by race conditions in the output or by the return value. Currently the suggested way is to use
type_string for the background job:
type_string("sleep 30 &\n");
script_run for the
wait. The reason to use here
script_run is that we do not only want to type
wait, but make openQA actually wait for that
wait to complete
script_run('wait'); # No "\n" here anymore, don't get confused :-)
Why I stick to
script_run as long as possible
You see in the code segments above, that in
type_string you have to add a “\n” at the end of the command. Plus, that one needs to be enclosed within
"" and not
'' because Perl. Since I’m not doing background jobs so often, that this becomes a routine, this will be a source of error and frustration every time I have to re-do it.
If find this confusing and because normally it’s already hard enough to figure out the other reasons why your test is failing, I’m gonna stick to
The correct way of running bash commands in the background is
type_string("sleep 30 &\n"); # Note: \n requires "" not '' type_string("sleep 30 &\n"); script_run('wait'); # Don't use \n here!
However, I find this confusing because you need to remember that
"" because of the required newline at the end. Also, don’t use
type_string for the
wait, otherwise openQA will go on without waiting for the jobs.
This is why I’m gonna stick to
script_run as long as possible:
script_run('sleep 30 & true'); # works with '' script_run("sleep 30 & true"); # works with "" script_run('wait'); # no confusion with "\n" in this example
For this case,
script_run is one unified command that behaves in the same way for all three and without those caveats.
I’m pretty sure, a lot of people will disagree with this approach though :-)