Skip to content

Commit 4e1b319

Browse files
committed
test: enable serial console in integration tests
If SSH to guest is failing, we don't really get much useful in terms of logs in our ci artifacts. Enable serial console so that we get guest dmesg always, at least if we have the API server around (which is the case for the vast majority of tests). Since serial console is enabled by default now, this means we can stop explicitly passing modified bootargs in a bunch of tests now. Disable it for the tests that interact with the serial console via stdin/stdout (here we are not daemonizing, so we are capturing serial output anyway). Disable pylint's "too-many-statements" lint, because it started firing in microvm.spawn() Signed-off-by: Patrick Roy <[email protected]>
1 parent 883527e commit 4e1b319

File tree

12 files changed

+53
-41
lines changed

12 files changed

+53
-41
lines changed

tests/framework/http_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,4 @@ def __init__(self, api_usocket_full_name, *, on_error=None):
132132
self.snapshot_load = Resource(self, "/snapshot/load")
133133
self.cpu_config = Resource(self, "/cpu-config")
134134
self.entropy = Resource(self, "/entropy")
135+
self.serial = Resource(self, "/serial")

tests/framework/microvm.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def __init__(
259259

260260
self.api = None
261261
self.log_file = None
262+
self.serial_out_path = None
262263
self.metrics_file = None
263264
self._spawned = False
264265
self._killed = False
@@ -624,6 +625,7 @@ def add_pre_cmd(self, pre_cmd):
624625
def spawn(
625626
self,
626627
log_file="fc.log",
628+
serial_out_path="serial.log",
627629
log_level="Debug",
628630
log_show_level=False,
629631
log_show_origin=False,
@@ -654,6 +656,11 @@ def spawn(
654656
if log_show_origin:
655657
self.jailer.extra_args["show-log-origin"] = None
656658

659+
if serial_out_path is not None:
660+
self.serial_out_path = Path(self.path) / serial_out_path
661+
self.serial_out_path.touch()
662+
self.create_jailed_resource(self.serial_out_path)
663+
657664
if metrics_path is not None:
658665
self.metrics_file = Path(self.path) / metrics_path
659666
self.metrics_file.touch()
@@ -718,13 +725,18 @@ def spawn(
718725
# Firecracker process itself at least came up by checking
719726
# for the startup log message. Otherwise, you're on your own kid.
720727
if "config-file" in self.jailer.extra_args and self.iface:
728+
assert not serial_out_path
721729
self.wait_for_ssh_up()
722730
elif "no-api" not in self.jailer.extra_args:
723731
if self.log_file and log_level in ("Trace", "Debug", "Info"):
724732
self.check_log_message("API server started.")
725733
else:
726734
self._wait_for_api_socket()
735+
736+
if serial_out_path is not None:
737+
self.api.serial.put(serial_out_path=serial_out_path)
727738
elif self.log_file and log_level in ("Trace", "Debug", "Info"):
739+
assert not serial_out_path
728740
self.check_log_message("Running Firecracker")
729741

730742
@retry(wait=wait_fixed(0.2), stop=stop_after_attempt(5), reraise=True)
@@ -795,12 +807,9 @@ def basic_config(
795807
The function checks the response status code and asserts that
796808
the response is within the interval [200, 300).
797809
798-
If boot_args is None, the default boot_args in Firecracker is
799-
reboot=k panic=1 nomodule 8250.nr_uarts=0 i8042.noaux i8042.nomux
800-
i8042.nopnp i8042.dumbkbd swiotlb=noforce
801-
802-
if PCI is disabled, Firecracker also passes to the guest pci=off
803-
810+
If boot_args is None, the default boot_args used in tests is
811+
reboot=k panic=1 nomodule swiotlb=noforce console=ttyS0 [pci=off]
812+
which differs from Firecracker's default only in the enabling of the serial console.
804813
Reference: file:../../src/vmm/src/vmm_config/boot_source.rs::DEFAULT_KERNEL_CMDLINE
805814
"""
806815
self.api.machine_config.put(
@@ -824,6 +833,10 @@ def basic_config(
824833

825834
if boot_args is not None:
826835
self.boot_args = boot_args
836+
else:
837+
self.boot_args = "reboot=k panic=1 nomodule swiotlb=noforce console=ttyS0"
838+
if not self.pci_enabled:
839+
self.boot_args += " pci=off"
827840
boot_source_args = {
828841
"kernel_image_path": self.create_jailed_resource(self.kernel_file),
829842
"boot_args": self.boot_args,
@@ -1313,9 +1326,9 @@ def open(self):
13131326
time.sleep(0.2)
13141327
attempt += 1
13151328

1316-
screen_log_fd = os.open(self._vm.screen_log, os.O_RDONLY)
1329+
serial_log_fd = os.open(self._vm.screen_log, os.O_RDONLY)
13171330
self._poller = select.poll()
1318-
self._poller.register(screen_log_fd, select.POLLIN | select.POLLHUP)
1331+
self._poller.register(serial_log_fd, select.POLLIN | select.POLLHUP)
13191332

13201333
def tx(self, input_string, end="\n"):
13211334
# pylint: disable=invalid-name

tests/integration_tests/functional/test_api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,8 +1136,10 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_nano):
11361136
expected_cfg["boot-source"] = {
11371137
"kernel_image_path": uvm_nano.get_jailed_resource(uvm_nano.kernel_file),
11381138
"initrd_path": None,
1139-
"boot_args": None,
1139+
"boot_args": "reboot=k panic=1 nomodule swiotlb=noforce console=ttyS0",
11401140
}
1141+
if not uvm_nano.pci_enabled:
1142+
expected_cfg["boot-source"]["boot_args"] += " pci=off"
11411143

11421144
# no ipv4_address or imds_compat specified during PUT /mmds/config so we expect the default
11431145
expected_cfg["mmds-config"] = {

tests/integration_tests/functional/test_api_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def test_api_socket_in_use(uvm_plain):
2323

2424
sock = socket.socket(socket.AF_UNIX)
2525
sock.bind(microvm.jailer.api_socket_path())
26-
microvm.spawn(log_level="warn")
26+
microvm.spawn(log_level="warn", serial_out_path=None)
2727
msg = "Failed to open the API socket at: /run/firecracker.socket. Check that it is not already used."
2828
microvm.check_log_message(msg)
2929

tests/integration_tests/functional/test_cmd_line_parameters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_describe_snapshot_all_versions(
2929
jailer_binary_path=firecracker_release.jailer,
3030
)
3131
# FIXME: Once only FC versions >= 1.12 are supported, drop log_level="warn"
32-
vm.spawn(log_level="warn")
32+
vm.spawn(log_level="warn", serial_out_path=None)
3333
vm.basic_config(track_dirty_pages=True)
3434
vm.start()
3535
snapshot = vm.snapshot_diff()

tests/integration_tests/functional/test_cmd_line_start.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def test_config_start_with_api(uvm_plain, vm_config_file):
117117
"""
118118
test_microvm = uvm_plain
119119
vm_config = _configure_vm_from_json(test_microvm, vm_config_file)
120-
test_microvm.spawn()
120+
test_microvm.spawn(serial_out_path=None)
121121

122122
assert test_microvm.state == "Running"
123123

@@ -134,7 +134,7 @@ def test_config_start_no_api(uvm_plain, vm_config_file):
134134
test_microvm = uvm_plain
135135
_configure_vm_from_json(test_microvm, vm_config_file)
136136
test_microvm.jailer.extra_args.update({"no-api": None})
137-
test_microvm.spawn()
137+
test_microvm.spawn(serial_out_path=None)
138138

139139
# Get names of threads in Firecracker.
140140
cmd = f"ps -T --no-headers -p {test_microvm.firecracker_pid} | awk '{{print $5}}'"
@@ -165,7 +165,7 @@ def test_config_start_no_api_exit(uvm_plain, vm_config_file):
165165
_configure_network_interface(test_microvm)
166166
test_microvm.jailer.extra_args.update({"no-api": None})
167167

168-
test_microvm.spawn() # Start Firecracker and MicroVM
168+
test_microvm.spawn(serial_out_path=None) # Start Firecracker and MicroVM
169169
test_microvm.ssh.run("reboot") # Exit
170170

171171
test_microvm.mark_killed() # waits for process to terminate
@@ -189,7 +189,7 @@ def test_config_bad_machine_config(uvm_plain, vm_config_file):
189189
test_microvm = uvm_plain
190190
_configure_vm_from_json(test_microvm, vm_config_file)
191191
test_microvm.jailer.extra_args.update({"no-api": None})
192-
test_microvm.spawn()
192+
test_microvm.spawn(serial_out_path=None)
193193
test_microvm.check_log_message("Configuration for VMM from one single json failed")
194194

195195
test_microvm.mark_killed()
@@ -215,7 +215,7 @@ def test_config_machine_config_params(uvm_plain, test_config):
215215
_configure_vm_from_json(test_microvm, vm_config_file)
216216
test_microvm.jailer.extra_args.update({"no-api": None})
217217

218-
test_microvm.spawn()
218+
test_microvm.spawn(serial_out_path=None)
219219

220220
should_fail = False
221221
if cpu_template_used and "C3" not in SUPPORTED_CPU_TEMPLATES:
@@ -247,7 +247,7 @@ def test_config_start_with_limit(uvm_plain, vm_config_file):
247247

248248
_configure_vm_from_json(test_microvm, vm_config_file)
249249
test_microvm.jailer.extra_args.update({"http-api-max-payload-size": "250"})
250-
test_microvm.spawn()
250+
test_microvm.spawn(serial_out_path=None)
251251

252252
assert test_microvm.state == "Running"
253253

@@ -277,7 +277,7 @@ def test_config_with_default_limit(uvm_plain, vm_config_file):
277277
test_microvm = uvm_plain
278278

279279
_configure_vm_from_json(test_microvm, vm_config_file)
280-
test_microvm.spawn()
280+
test_microvm.spawn(serial_out_path=None)
281281

282282
assert test_microvm.state == "Running"
283283

@@ -311,7 +311,7 @@ def test_start_with_metadata(uvm_plain):
311311
metadata_file = DIR / "metadata.json"
312312
_add_metadata_file(test_microvm, metadata_file)
313313

314-
test_microvm.spawn()
314+
test_microvm.spawn(serial_out_path=None)
315315

316316
test_microvm.check_log_message("Successfully added metadata to mmds from file")
317317

@@ -332,7 +332,7 @@ def test_start_with_metadata_limit(uvm_plain):
332332
metadata_file = DIR / "metadata.json"
333333
_add_metadata_file(test_microvm, metadata_file)
334334

335-
test_microvm.spawn()
335+
test_microvm.spawn(serial_out_path=None)
336336

337337
test_microvm.check_log_message(
338338
"Populating MMDS from file failed: The MMDS patch request doesn't fit."
@@ -352,7 +352,7 @@ def test_start_with_metadata_default_limit(uvm_plain):
352352

353353
_add_metadata_file(test_microvm, metadata_file)
354354

355-
test_microvm.spawn()
355+
test_microvm.spawn(serial_out_path=None)
356356

357357
test_microvm.check_log_message(
358358
"Populating MMDS from file failed: The MMDS patch request doesn't fit."
@@ -372,7 +372,7 @@ def test_start_with_missing_metadata(uvm_plain):
372372
test_microvm.metadata_file = vm_metadata_path
373373

374374
try:
375-
test_microvm.spawn()
375+
test_microvm.spawn(serial_out_path=None)
376376
except: # pylint: disable=bare-except
377377
pass
378378
finally:
@@ -395,7 +395,7 @@ def test_start_with_invalid_metadata(uvm_plain):
395395
test_microvm.metadata_file = vm_metadata_path
396396

397397
try:
398-
test_microvm.spawn()
398+
test_microvm.spawn(serial_out_path=None)
399399
except: # pylint: disable=bare-except
400400
pass
401401
finally:
@@ -418,7 +418,7 @@ def test_config_start_and_mmds_with_api(uvm_plain, vm_config_file):
418418
_configure_network_interface(test_microvm)
419419

420420
# Network namespace has already been created.
421-
test_microvm.spawn()
421+
test_microvm.spawn(serial_out_path=None)
422422

423423
data_store = {
424424
"latest": {
@@ -480,7 +480,7 @@ def test_with_config_and_metadata_no_api(uvm_plain, vm_config_file, metadata_fil
480480
_add_metadata_file(test_microvm, metadata_file)
481481
_configure_network_interface(test_microvm)
482482
test_microvm.jailer.extra_args.update({"no-api": None})
483-
test_microvm.spawn()
483+
test_microvm.spawn(serial_out_path=None)
484484

485485
# Get MMDS version and IPv4 address configured from the file.
486486
version, ipv4_address, imds_compat = _get_optional_fields_from_file(vm_config_file)

tests/integration_tests/functional/test_kernel_cmdline.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_init_params(uvm_plain):
1414
"""
1515
vm = uvm_plain
1616
vm.help.enable_console()
17-
vm.spawn()
17+
vm.spawn(serial_out_path=None)
1818
vm.memory_monitor = None
1919

2020
# We will override the init with /bin/cat so that we try to read the

tests/integration_tests/functional/test_max_devices.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ def max_devices(uvm):
1818
match platform.machine():
1919
case "aarch64":
2020
# On aarch64, IRQs are available from 32 to 127. We always use one IRQ each for
21-
# the VMGenID and RTC devices, so the maximum number of devices supported
22-
# at the same time is 94.
23-
return 94
21+
# the VMGenID, RTC and serial devices, so the maximum number of devices supported
22+
# at the same time is 93.
23+
return 93
2424
case "x86_64":
2525
# IRQs are available from 5 to 23. We always use one IRQ for VMGenID device, so
2626
# the maximum number of devices supported at the same time is 18.

tests/integration_tests/functional/test_serial_io.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@ def test_serial_after_snapshot(uvm_plain, microvm_factory):
5151
"""
5252
microvm = uvm_plain
5353
microvm.help.enable_console()
54-
microvm.spawn()
54+
microvm.spawn(serial_out_path=None)
5555
microvm.basic_config(
5656
vcpu_count=2,
5757
mem_size_mib=256,
58-
boot_args="console=ttyS0 reboot=k panic=1 swiotlb=noforce",
5958
)
6059
serial = Serial(microvm)
6160
serial.open()
@@ -72,7 +71,7 @@ def test_serial_after_snapshot(uvm_plain, microvm_factory):
7271
# Load microVM clone from snapshot.
7372
vm = microvm_factory.build()
7473
vm.help.enable_console()
75-
vm.spawn()
74+
vm.spawn(serial_out_path=None)
7675
vm.restore_from_snapshot(snapshot, resume=True)
7776
serial = Serial(vm)
7877
serial.open()
@@ -92,16 +91,14 @@ def test_serial_console_login(uvm_plain_any):
9291
"""
9392
microvm = uvm_plain_any
9493
microvm.help.enable_console()
95-
microvm.spawn()
94+
microvm.spawn(serial_out_path=None)
9695

9796
# We don't need to monitor the memory for this test because we are
9897
# just rebooting and the process dies before pmap gets the RSS.
9998
microvm.memory_monitor = None
10099

101100
# Set up the microVM with 1 vCPU and a serial console.
102-
microvm.basic_config(
103-
vcpu_count=1, boot_args="console=ttyS0 reboot=k panic=1 swiotlb=noforce"
104-
)
101+
microvm.basic_config(vcpu_count=1)
105102

106103
microvm.start()
107104

@@ -146,7 +143,6 @@ def test_serial_dos(uvm_plain_any):
146143
# Set up the microVM with 1 vCPU and a serial console.
147144
microvm.basic_config(
148145
vcpu_count=1,
149-
boot_args="console=ttyS0 reboot=k panic=1 swiotlb=noforce",
150146
)
151147
microvm.add_net_iface()
152148
microvm.start()
@@ -174,13 +170,12 @@ def test_serial_block(uvm_plain_any):
174170
"""
175171
test_microvm = uvm_plain_any
176172
test_microvm.help.enable_console()
177-
test_microvm.spawn()
173+
test_microvm.spawn(serial_out_path=None)
178174
# Set up the microVM with 1 vCPU so we make sure the vCPU thread
179175
# responsible for the SSH connection will also run the serial.
180176
test_microvm.basic_config(
181177
vcpu_count=1,
182178
mem_size_mib=512,
183-
boot_args="console=ttyS0 reboot=k panic=1 swiotlb=noforce",
184179
)
185180
test_microvm.add_net_iface()
186181
test_microvm.start()

tests/integration_tests/performance/test_initrd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_microvm_initrd_with_serial(uvm_with_initrd, huge_pages):
2929
"""
3030
vm = uvm_with_initrd
3131
vm.help.enable_console()
32-
vm.spawn()
32+
vm.spawn(serial_out_path=None)
3333
vm.memory_monitor = None
3434

3535
vm.basic_config(

0 commit comments

Comments
 (0)