1
1
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
# SPDX-License-Identifier: Apache-2.0
3
3
"""Tests scenario for the Firecracker serial console."""
4
+
5
+ import fcntl
6
+ import os
7
+ import termios
4
8
import time
9
+ from subprocess import run , PIPE
10
+
5
11
from framework .microvm import Serial
6
12
from framework .state_machine import TestState
7
13
8
14
9
- class WaitLogin (TestState ):
15
+ class WaitLogin (TestState ): # pylint: disable=too-few-public-methods
10
16
"""Initial state when we wait for the login prompt."""
11
17
12
18
def handle_input (self , serial , input_char ) -> TestState :
13
19
"""Handle input and return next state."""
14
20
if self .match (input_char ):
21
+ time .sleep (2 )
15
22
# Send login username.
16
23
serial .tx ("root" )
24
+ time .sleep (2 )
17
25
return WaitPasswordPrompt ("Password:" )
18
26
return self
19
27
20
28
21
- class WaitPasswordPrompt (TestState ):
29
+ class WaitPasswordPrompt (TestState ): # pylint: disable=too-few-public-methods
22
30
"""Wait for the password prompt to be shown."""
23
31
24
32
def handle_input (self , serial , input_char ) -> TestState :
25
33
"""Handle input and return next state."""
26
34
if self .match (input_char ):
27
35
serial .tx ("root" )
28
- # Wait 1 second for shell
29
- time .sleep (1 )
36
+ time .sleep (2 )
30
37
serial .tx ("id" )
38
+ time .sleep (2 )
31
39
return WaitIDResult ("uid=0(root) gid=0(root) groups=0(root)" )
32
40
return self
33
41
34
42
35
- class WaitIDResult (TestState ):
43
+ class WaitIDResult (TestState ): # pylint: disable=too-few-public-methods
36
44
"""Wait for the console to show the result of the 'id' shell command."""
37
45
38
46
def handle_input (self , unused_serial , input_char ) -> TestState :
@@ -42,10 +50,10 @@ def handle_input(self, unused_serial, input_char) -> TestState:
42
50
return self
43
51
44
52
45
- class TestFinished (TestState ):
53
+ class TestFinished (TestState ): # pylint: disable=too-few-public-methods
46
54
"""Test complete and successful."""
47
55
48
- def handle_input (self , unused_serial , input_char ) -> TestState :
56
+ def handle_input (self , unused_serial , _ ) -> TestState :
49
57
"""Return self since the test is about to end."""
50
58
return self
51
59
@@ -63,16 +71,61 @@ def test_serial_console_login(test_microvm_with_ssh):
63
71
# Set up the microVM with 1 vCPU and a serial console.
64
72
microvm .basic_config (vcpu_count = 1 ,
65
73
boot_args = 'console=ttyS0 reboot=k panic=1 pci=off' )
66
-
67
74
microvm .start ()
68
75
69
76
serial = Serial (microvm )
70
77
serial .open ()
71
78
72
79
# Set initial state - wait for 'login:' prompt
73
80
current_state = WaitLogin ("login:" )
74
-
75
81
while not isinstance (current_state , TestFinished ):
76
82
output_char = serial .rx_char ()
77
83
current_state = current_state .handle_input (
78
84
serial , output_char )
85
+
86
+
87
+ def get_total_mem_size (pid ):
88
+ """Get total memory usage for a process."""
89
+ cmd = f"pmap { pid } | tail -n 1 | sed 's/^ //' | tr -s ' ' | cut -d' ' -f2"
90
+ proc = run (cmd , check = True , shell = True , stdout = PIPE )
91
+ return proc .stdout .decode ()
92
+
93
+
94
+ def send_bytes (tty , bytes_count , timeout = 60 ):
95
+ """Send data to the terminal."""
96
+ start = time .time ()
97
+ for _ in range (bytes_count ):
98
+ fcntl .ioctl (tty , termios .TIOCSTI , 'a' )
99
+ current = time .time ()
100
+ if current - start > timeout :
101
+ break
102
+
103
+
104
+ def test_serial_dos (test_microvm_with_ssh ):
105
+ """Test serial console behavior when it is under DoS."""
106
+ microvm = test_microvm_with_ssh
107
+ microvm .jailer .daemonize = False
108
+ microvm .spawn ()
109
+ microvm .memory_events_queue = None
110
+
111
+ # Set up the microVM with 1 vCPU and a serial console.
112
+ microvm .basic_config (vcpu_count = 1 ,
113
+ boot_args = 'console=ttyS0 reboot=k panic=1 pci=off' )
114
+ microvm .start ()
115
+
116
+ # Get Firecracker process TTY.
117
+ tty_path = f"/proc/{ microvm .jailer_clone_pid } /fd/0"
118
+ tty_fd = os .open (tty_path , os .O_RDWR )
119
+
120
+ # Check if the total memory size changed.
121
+ before_size = get_total_mem_size (microvm .jailer_clone_pid )
122
+ # Send 100MB at maximum to the terminal backed by our serial device.
123
+ # In practice, we send bytes in a burst for only 1 seconds. This is
124
+ # sufficient for exercising serial device buffer capacity.
125
+ send_bytes (tty_fd , bytes_count = 100000000 , timeout = 1 )
126
+ after_size = get_total_mem_size (microvm .jailer_clone_pid )
127
+ assert before_size == after_size , "The memory size of the " \
128
+ "Firecracker process " \
129
+ "changed from {} to {}." \
130
+ .format (before_size ,
131
+ after_size )
0 commit comments