Skip to content

Commit 65e14b6

Browse files
committed
tests: enhance automated tests scripts to run on firecracker
This patch modifies the recently added scripts that help automate testing various application on OSv on firecracker. Signed-off-by: Waldemar Kozaczuk <[email protected]>
1 parent a1a1dd7 commit 65e14b6

File tree

9 files changed

+96
-25
lines changed

9 files changed

+96
-25
lines changed

modules/httpserver-api/test.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ CMD="java.so -Djetty.base=/jetty/demo-base -jar /jetty/start.jar"
66

77
case $PROTOCOL in
88
http)
9-
PYTHONPATH=$OSV_DIR/scripts $OSV_DIR/modules/httpserver-api/tests/testhttpserver-api.py --cmd "$CMD" ;;
9+
PYTHONPATH=$OSV_DIR/scripts $OSV_DIR/modules/httpserver-api/tests/testhttpserver-api.py --cmd "$CMD" --hypervisor $OSV_HYPERVISOR;;
1010
https)
11-
PYTHONPATH=$OSV_DIR/scripts $OSV_DIR/modules/httpserver-api/tests/testhttpserver-api.py --cmd "$CMD" --cert $OSV_DIR/modules/certs/build/client.pem --key $OSV_DIR/modules/certs/build/client.key --cacert $OSV_DIR/modules/certs/build/cacert.pem ;;
11+
PYTHONPATH=$OSV_DIR/scripts $OSV_DIR/modules/httpserver-api/tests/testhttpserver-api.py --cmd "$CMD" --cert $OSV_DIR/modules/certs/build/client.pem --key $OSV_DIR/modules/certs/build/client.key --cacert $OSV_DIR/modules/certs/build/cacert.pem --hypervisor $OSV_HYPERVISOR;;
1212
esac

modules/httpserver-api/tests/basetest.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class Basetest(unittest.TestCase):
1717
@classmethod
1818
def set_config(cls, parser):
1919
cls.config = parser.parse_args()
20+
if cls.config.hypervisor == 'firecracker':
21+
module_base = os.path.join(os.path.realpath(os.path.dirname(__file__)), '..')
22+
cls.config.run_script = os.path.join(module_base, "..", "..", "scripts", "firecracker.py")
23+
cls.config.host = '172.16.0.2'
2024
cls._client = client.Client(cls.config)
2125

2226
@classmethod
@@ -91,15 +95,15 @@ def assertHttpError(self, url, code=404):
9195
raise Exception('Expected failure but request succeeded')
9296

9397
@classmethod
94-
def curl(cls, api, method='GET', data=None):
98+
def curl(cls, api, method='GET', data=None, timeout=None):
9599
url = cls.get_url(api)
96100

97101
r = {
98102
'GET': requests.get,
99103
'POST': requests.post,
100104
'DELETE': requests.delete,
101105
'PUT': requests.put,
102-
}[method](url, data=data, **cls._client.get_request_kwargs())
106+
}[method](url, data=data, timeout=timeout, **cls._client.get_request_kwargs())
103107

104108
if r.status_code != 200:
105109
raise HttpError(r.status_code)
@@ -122,7 +126,9 @@ def get_ca_cert_path(cls):
122126
@classmethod
123127
def exec_os(cls):
124128
args = []
125-
if cls.config.use_sudo:
129+
if cls.config.hypervisor == 'firecracker':
130+
args += [cls.config.run_script, "-l", "-m 2048M", "-n", "-c 4"]
131+
elif cls.config.use_sudo:
126132
args += ["/usr/bin/sudo", cls.config.run_script, "-n"]
127133
else:
128134
args += [cls.config.run_script, "--forward", "tcp::" + str(cls._client.get_port()) + "-:" + str(cls._client.get_port())]
@@ -142,7 +148,7 @@ def shutdown(cls):
142148

143149
path = cls.path_by_nick(cls.os_api, "os_poweroff")
144150
try:
145-
cls.curl(path, method='POST')
151+
cls.curl(path, method='POST', timeout=0.5)
146152
except:
147153
pass
148154
retry = 10

modules/httpserver-api/tests/testhttpserver-api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
parser.add_argument('--use_sudo', help='Use sudo with -n option instead of port forwarding', action='store_true')
1919
parser.add_argument('--jsondir', help='location of the json files', default=os.path.join(module_base, 'api-doc/listings/'))
2020
parser.add_argument('--test_image', help='the path to the test image')
21+
parser.add_argument('--hypervisor', action="store", default="qemu", help="choose hypervisor to run: qemu, firecracker")
2122
client.Client.add_arguments(parser)
2223

2324
class test_httpserver(basetest.Basetest):

modules/tests/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22

33
THIS_DIR=$(readlink -f $(dirname $0))
4-
$THIS_DIR/../../scripts/test.py
4+
$THIS_DIR/../../scripts/test.py -p $OSV_HYPERVISOR

scripts/tests/compose_and_test_selected_apps.sh

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ usage() {
2121
-r Run test app only (must have been composed earlier)
2222
-R Compose test app image with RoFS (ZFS is the default)
2323
-l Use latest OSv kernel from build/last to build test image
24+
-f Run OSv on firecracker
2425
EOF
2526
exit ${1:-0}
2627
}
@@ -29,18 +30,22 @@ FS=zfs
2930
COMPOSE_ONLY=false
3031
RUN_ONLY=false
3132
LOADER="osv-loader"
33+
OSV_HYPERVISOR="qemu"
3234

33-
while getopts crRlh: OPT ; do
35+
while getopts crRlfh: OPT ; do
3436
case ${OPT} in
3537
c) COMPOSE_ONLY=true;;
3638
r) RUN_ONLY=true;;
3739
R) FS=rofs;;
3840
l) LOADER="osv-latest-loader";;
41+
f) OSV_HYPERVISOR="firecracker";;
3942
h) usage;;
4043
?) usage 1;;
4144
esac
4245
done
4346

47+
export OSV_HYPERVISOR
48+
4449
shift $((OPTIND - 1))
4550
[[ -z $1 ]] && usage 1
4651

@@ -171,7 +176,7 @@ test_apps_with_tester()
171176
{
172177
compose_and_run_test_app "iperf3"
173178
compose_and_run_test_app "graalvm-netty-plot"
174-
compose_test_app "ffmpeg" "libz" && run_test_app "ffmpeg" "video_subclip" && run_test_app "ffmpeg" "video_transcode"
179+
compose_test_app "ffmpeg" && run_test_app "ffmpeg" "video_subclip" && run_test_app "ffmpeg" "video_transcode"
175180
compose_and_run_test_app "redis-memonly"
176181
compose_and_run_test_app "cli"
177182
compose_and_run_test_app "mysql"
@@ -182,6 +187,9 @@ test_apps_with_tester()
182187

183188
run_unit_tests()
184189
{
190+
# Unit tests are special as the unit tests runner depends on usr.manifest which
191+
# needs to be placed in the tests module. So let us gegnerate it on the fly from the unit tests mpm
192+
capstan package describe osv.unit-tests -c | grep "/tests/tst-" | grep -o "/tests/tst-.*" | sed 's/$/: dummy/' > $OSV_DIR/modules/tests/usr.manifest
185193
compose_test_app "unit-tests" && run_test_app "tests"
186194
compose_test_app "httpserver-api-tests" && run_test_app "httpserver-api" "http"
187195
#compose_test_app "httpserver-api-https-tests" "httpserver-api-tests" && run_test_app "httpserver-api" "https"

scripts/tests/test_app.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def run(command, hypervisor_name, image_path=None, line=None, guest_port=None, h
3535
parser = argparse.ArgumentParser(prog='test_app')
3636
parser.add_argument("-i", "--image", action="store", default=None, metavar="IMAGE",
3737
help="path to disk image file. defaults to build/$mode/usr.img")
38-
parser.add_argument("-p", "--hypervisor", action="store", default="qemu",
38+
parser.add_argument("-p", "--hypervisor", action="store", default=None,
3939
help="choose hypervisor to run: qemu, firecracker")
4040
parser.add_argument("--line", action="store", default=None,
4141
help="expect line in guest output")
@@ -47,6 +47,15 @@ def run(command, hypervisor_name, image_path=None, line=None, guest_port=None, h
4747
parser.add_argument("--kill", action="store_true", help="kill the app instead of waiting until terminates itself")
4848

4949
cmdargs = parser.parse_args()
50+
51+
hypervisor_name = 'qemu'
52+
if cmdargs.hypervisor != None:
53+
hypervisor_name = cmdargs.hypervisor
54+
else:
55+
hypervisor_from_env = os.getenv('OSV_HYPERVISOR')
56+
if hypervisor_from_env != None:
57+
hypervisor_name = hypervisor_from_env
58+
5059
set_verbose_output(True)
51-
run(cmdargs.execute, cmdargs.hypervisor, cmdargs.image, cmdargs.line,
60+
run(cmdargs.execute, hypervisor_name, cmdargs.image, cmdargs.line,
5261
cmdargs.guest_port, cmdargs.host_port, cmdargs.input_line, cmdargs.kill)

scripts/tests/test_app_with_test_script.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def run(command, hypervisor_name, host_port, guest_port, script_path, image_path
3232
parser = argparse.ArgumentParser(prog='test_app')
3333
parser.add_argument("-i", "--image", action="store", default=None, metavar="IMAGE",
3434
help="path to disk image file. defaults to build/$mode/usr.img")
35-
parser.add_argument("-p", "--hypervisor", action="store", default="qemu",
35+
parser.add_argument("-p", "--hypervisor", action="store", default=None,
3636
help="choose hypervisor to run: qemu, firecracker")
3737
parser.add_argument("--start_line", action="store", default=None,
3838
help="expect line in guest output before executing test script")
@@ -46,5 +46,19 @@ def run(command, hypervisor_name, host_port, guest_port, script_path, image_path
4646
help="edit command line before execution")
4747

4848
cmdargs = parser.parse_args()
49+
50+
hypervisor_name = 'qemu'
51+
if cmdargs.hypervisor != None:
52+
hypervisor_name = cmdargs.hypervisor
53+
else:
54+
hypervisor_from_env = os.getenv('OSV_HYPERVISOR')
55+
if hypervisor_from_env != None:
56+
hypervisor_name = hypervisor_from_env
57+
58+
if hypervisor_name == 'firecracker':
59+
os.environ['OSV_HOSTNAME'] = '172.16.0.2'
60+
else:
61+
os.environ['OSV_HOSTNAME'] = 'localhost'
62+
4963
set_verbose_output(True)
50-
run(cmdargs.execute, cmdargs.hypervisor, cmdargs.host_port, cmdargs.guest_port, cmdargs.script_path, cmdargs.image, cmdargs.start_line, cmdargs.end_line)
64+
run(cmdargs.execute, hypervisor_name, cmdargs.host_port, cmdargs.guest_port, cmdargs.script_path, cmdargs.image, cmdargs.start_line, cmdargs.end_line)

scripts/tests/test_http_app_with_curl_and_ab.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ def run(command, hypervisor_name, host_port, guest_port, http_path, expected_htt
3030
print(pre_script)
3131
subprocess.check_output([pre_script])
3232

33-
app_url = "http://localhost:%s%s" % (host_port, http_path)
33+
if hypervisor_name == 'firecracker':
34+
app_url = "http://172.16.0.2:%s%s" % (guest_port, http_path)
35+
else:
36+
app_url = "http://localhost:%s%s" % (host_port, http_path)
37+
3438
if expected_http_line != None:
3539
check_with_curl(app_url, expected_http_line)
3640

@@ -85,7 +89,7 @@ def run(command, hypervisor_name, host_port, guest_port, http_path, expected_htt
8589
parser = argparse.ArgumentParser(prog='test_app')
8690
parser.add_argument("-i", "--image", action="store", default=None, metavar="IMAGE",
8791
help="path to disk image file. defaults to build/$mode/usr.img")
88-
parser.add_argument("-p", "--hypervisor", action="store", default="qemu",
92+
parser.add_argument("-p", "--hypervisor", action="store", default=None,
8993
help="choose hypervisor to run: qemu, firecracker")
9094
parser.add_argument("--line", action="store", default=None,
9195
help="expect line in guest output")
@@ -105,7 +109,21 @@ def run(command, hypervisor_name, host_port, guest_port, http_path, expected_htt
105109
help="error line to ignore on kill")
106110

107111
cmdargs = parser.parse_args()
112+
113+
hypervisor_name = 'qemu'
114+
if cmdargs.hypervisor != None:
115+
hypervisor_name = cmdargs.hypervisor
116+
else:
117+
hypervisor_from_env = os.getenv('OSV_HYPERVISOR')
118+
if hypervisor_from_env != None:
119+
hypervisor_name = hypervisor_from_env
120+
121+
if hypervisor_name == 'firecracker':
122+
os.environ['OSV_HOSTNAME'] = '172.16.0.2'
123+
else:
124+
os.environ['OSV_HOSTNAME'] = 'localhost'
125+
108126
set_verbose_output(True)
109-
run(cmdargs.execute, cmdargs.hypervisor, cmdargs.host_port, cmdargs.guest_port,
127+
run(cmdargs.execute, hypervisor_name, cmdargs.host_port, cmdargs.guest_port,
110128
cmdargs.http_path ,cmdargs.http_line, cmdargs.image, cmdargs.line,
111129
cmdargs.concurrency, cmdargs.count, cmdargs.pre_script, cmdargs.no_keep_alive, cmdargs.error_line_to_ignore_on_kill)

scripts/tests/testing.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
import os
33
import sys
4+
import signal
45
import subprocess
56
import threading
67
import socket
@@ -180,7 +181,7 @@ def join(self):
180181
if self.pipe_stdin:
181182
self.process.stdin.close()
182183
if self.process.returncode:
183-
raise Exception('Guest failed (returncode=%d)' % self.proces.returncode)
184+
raise Exception('Guest failed (returncode=%d)' % self.process.returncode)
184185
if self.failed:
185186
raise Exception('Guest failed')
186187

@@ -208,17 +209,24 @@ def line_with_error(self):
208209

209210
def run_command_in_guest(command, **kwargs):
210211
common_parameters = ["-e", "--power-off-on-abort " + command]
211-
if 'hypervisor' in kwargs.keys() and kwargs['hypervisor'] == 'firecracker':
212-
return Guest(["-m 1024M", "-n", "-c 4"] + common_parameters, **kwargs)
212+
213+
if kwargs.get('hypervisor') == 'firecracker':
214+
parameters = ["-l", "-m 2048M", "-n", "-c 4"] + common_parameters
213215
else:
214-
return Guest(["-s"] + common_parameters, **kwargs)
216+
parameters = ["-s"] + common_parameters
217+
218+
return Guest(parameters, **kwargs)
215219

216220
class Guest(SupervisedProcess):
217221
def __init__(self, args, forward=[], hold_with_poweroff=False, show_output_on_error=True,
218222
scan_for_failed_to_load_object_error=True, run_py_args=[], hypervisor='qemu', pipe_stdin=False):
219223

220224
if hypervisor == 'firecracker':
221225
run_script = os.path.join(osv_base, "scripts/firecracker.py")
226+
self.monitor_socket = None
227+
physical_nic = os.getenv('OSV_FC_NIC')
228+
if physical_nic:
229+
args.extend(['-p', physical_nic])
222230
else:
223231
run_script = os.path.join(osv_base, "scripts/run.py")
224232

@@ -233,18 +241,25 @@ def __init__(self, args, forward=[], hold_with_poweroff=False, show_output_on_er
233241

234242
args.extend(['--unsafe-cache'])
235243

244+
if _verbose_output:
245+
print('Running OSv on %s with parameters: [%s]' % (hypervisor, " ".join(args)))
246+
236247
SupervisedProcess.__init__(self, [run_script] + run_py_args + args,
237248
show_output=_verbose_output,
238249
show_output_on_error=show_output_on_error,
239250
scan_for_failed_to_load_object_error=scan_for_failed_to_load_object_error,
240251
pipe_stdin=pipe_stdin)
241252

242253
def kill(self):
243-
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
244-
s.connect(self.monitor_socket)
245-
s.send('quit\n'.encode())
246-
self.join()
247-
s.close()
254+
if self.monitor_socket != None:
255+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
256+
s.connect(self.monitor_socket)
257+
s.send('quit\n'.encode())
258+
self.join()
259+
s.close()
260+
else:
261+
os.kill(self.process.pid, signal.SIGINT)
262+
self.join()
248263

249264
def wait_for_line(guest, text):
250265
return _wait_for_line(guest, lambda line: line == text, text)

0 commit comments

Comments
 (0)