Skip to content

Commit bface3b

Browse files
authored
Merge branch 'master' into rust-vers-bump
2 parents e42ca91 + 60c5102 commit bface3b

File tree

11 files changed

+426
-192
lines changed

11 files changed

+426
-192
lines changed

src/vmm/src/vstate/vcpu/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
vmm_config::machine_config::CpuFeaturesTemplate, vstate::vm::Vm, FC_EXIT_CODE_GENERIC_ERROR,
2222
FC_EXIT_CODE_OK,
2323
};
24+
use kvm_bindings::{KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN};
2425
use kvm_ioctls::VcpuExit;
2526
use logger::{error, info, IncMetric, METRICS};
2627
use seccomp::{BpfProgram, SeccompFilter};
@@ -473,6 +474,26 @@ impl Vcpu {
473474
VcpuExit::InternalError
474475
)))
475476
}
477+
VcpuExit::SystemEvent(event_type, event_flags) => match event_type {
478+
KVM_SYSTEM_EVENT_RESET | KVM_SYSTEM_EVENT_SHUTDOWN => {
479+
info!(
480+
"Received KVM_SYSTEM_EVENT: type: {}, event: {}",
481+
event_type, event_flags
482+
);
483+
Ok(VcpuEmulation::Stopped)
484+
}
485+
_ => {
486+
METRICS.vcpu.failures.inc();
487+
error!(
488+
"Received KVM_SYSTEM_EVENT signal type: {}, flag: {}",
489+
event_type, event_flags
490+
);
491+
Err(Error::FaultyKvmExit(format!(
492+
"{:?}",
493+
VcpuExit::SystemEvent(event_type, event_flags)
494+
)))
495+
}
496+
},
476497
arch_specific_reason => {
477498
// run specific architecture emulation.
478499
self.kvm_vcpu.run_arch_emulation(arch_specific_reason)

tests/framework/artifacts.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,9 @@ def copy(self, vm_root_folder):
238238

239239
return Snapshot(dst_mem_path,
240240
dst_state_file,
241-
disk_paths,
242-
dst_ssh_key)
241+
disks=disk_paths,
242+
net_ifaces=None,
243+
ssh_key=dst_ssh_key)
243244

244245

245246
class DiskArtifact(Artifact):
@@ -433,7 +434,7 @@ class SnapshotType(Enum):
433434
class Snapshot:
434435
"""Manages Firecracker snapshots."""
435436

436-
def __init__(self, mem, vmstate, disks, ssh_key):
437+
def __init__(self, mem, vmstate, disks, net_ifaces, ssh_key):
437438
"""Initialize mem, vmstate, disks, key."""
438439
assert mem is not None
439440
assert vmstate is not None
@@ -443,6 +444,7 @@ def __init__(self, mem, vmstate, disks, ssh_key):
443444
self._vmstate = vmstate
444445
self._disks = disks
445446
self._ssh_key = ssh_key
447+
self._net_ifaces = net_ifaces
446448

447449
def rebase_snapshot(self, base):
448450
"""Rebases current incremental snapshot onto a specified base layer."""
@@ -468,3 +470,58 @@ def disks(self):
468470
def ssh_key(self):
469471
"""Return the ssh key file path."""
470472
return self._ssh_key
473+
474+
@property
475+
def net_ifaces(self):
476+
"""Return the list of net interface configs."""
477+
return self._net_ifaces
478+
479+
480+
# Default configuration values for network interfaces.
481+
DEFAULT_HOST_IP = "192.168.0.1"
482+
DEFAULT_GUEST_IP = "192.168.0.2"
483+
DEFAULT_TAP_NAME = "tap0"
484+
DEFAULT_DEV_NAME = "eth0"
485+
DEFAULT_NETMASK = 30
486+
487+
488+
class NetIfaceConfig:
489+
"""Defines a network interface configuration."""
490+
491+
def __init__(self,
492+
host_ip=DEFAULT_HOST_IP,
493+
guest_ip=DEFAULT_GUEST_IP,
494+
tap_name=DEFAULT_TAP_NAME,
495+
dev_name=DEFAULT_DEV_NAME,
496+
netmask=DEFAULT_NETMASK):
497+
"""Initialize object."""
498+
self._host_ip = host_ip
499+
self._guest_ip = guest_ip
500+
self._tap_name = tap_name
501+
self._dev_name = dev_name
502+
self._netmask = netmask
503+
504+
@property
505+
def host_ip(self):
506+
"""Return the host IP."""
507+
return self._host_ip
508+
509+
@property
510+
def guest_ip(self):
511+
"""Return the guest IP."""
512+
return self._guest_ip
513+
514+
@property
515+
def tap_name(self):
516+
"""Return the tap device name."""
517+
return self._tap_name
518+
519+
@property
520+
def dev_name(self):
521+
"""Return the guest device name."""
522+
return self._dev_name
523+
524+
@property
525+
def netmask(self):
526+
"""Return the netmask."""
527+
return self._netmask

tests/framework/builder.py

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,14 @@
77
import tempfile
88
from pathlib import Path
99
from conftest import init_microvm
10-
from framework.artifacts import Artifact, DiskArtifact, Snapshot, SnapshotType
10+
from framework.artifacts import (
11+
Artifact, DiskArtifact, Snapshot, SnapshotType, NetIfaceConfig
12+
)
1113
import framework.utils as utils
1214
import host_tools.logging as log_tools
1315
import host_tools.network as net_tools
1416

1517

16-
DEFAULT_HOST_IP = "192.168.0.1"
17-
DEFAULT_GUEST_IP = "192.168.0.2"
18-
DEFAULT_TAP_NAME = "tap0"
19-
DEFAULT_DEV_NAME = "eth0"
20-
DEFAULT_NETMASK = 30
21-
22-
2318
class MicrovmBuilder:
2419
"""Build fresh microvms or restore from snapshot."""
2520

@@ -55,7 +50,7 @@ def build(self,
5550
disks: [DiskArtifact],
5651
ssh_key: Artifact,
5752
config: Artifact,
58-
network_config: net_tools.UniqueIPv4Generator = None,
53+
net_ifaces=None,
5954
enable_diff_snapshots=False,
6055
cpu_template=None):
6156
"""Build a fresh microvm."""
@@ -73,25 +68,27 @@ def build(self,
7368
ssh_key.download(self.root_path)
7469
vm.ssh_config['ssh_key_path'] = ssh_key.local_path()
7570
os.chmod(vm.ssh_config['ssh_key_path'], 0o400)
76-
if network_config:
77-
(host_ip, guest_ip) = network_config.get_next_available_ips(2)
78-
else:
79-
host_ip, guest_ip = DEFAULT_HOST_IP, DEFAULT_GUEST_IP
80-
vm.create_tap_and_ssh_config(host_ip=host_ip,
81-
guest_ip=guest_ip,
82-
netmask_len=DEFAULT_NETMASK,
83-
tapname=DEFAULT_TAP_NAME)
84-
85-
# TODO: propper network configuraiton with artifacts.
86-
guest_mac = net_tools.mac_from_ip(guest_ip)
87-
response = vm.network.put(
88-
iface_id=DEFAULT_DEV_NAME,
89-
host_dev_name=DEFAULT_TAP_NAME,
90-
guest_mac=guest_mac,
91-
allow_mmds_requests=True,
92-
)
9371

94-
assert vm.api_session.is_status_no_content(response.status_code)
72+
# Provide a default network configuration.
73+
if net_ifaces is None or len(net_ifaces) == 0:
74+
ifaces = [NetIfaceConfig()]
75+
else:
76+
ifaces = net_ifaces
77+
78+
# Configure network interfaces using artifacts.
79+
for iface in ifaces:
80+
vm.create_tap_and_ssh_config(host_ip=iface.host_ip,
81+
guest_ip=iface.guest_ip,
82+
netmask_len=iface.netmask,
83+
tapname=iface.tap_name)
84+
guest_mac = net_tools.mac_from_ip(iface.guest_ip)
85+
response = vm.network.put(
86+
iface_id=iface.dev_name,
87+
host_dev_name=iface.tap_name,
88+
guest_mac=guest_mac,
89+
allow_mmds_requests=True,
90+
)
91+
assert vm.api_session.is_status_no_content(response.status_code)
9592

9693
with open(config.local_path()) as microvm_config_file:
9794
microvm_config = json.load(microvm_config_file)
@@ -144,16 +141,16 @@ def build_from_snapshot(self,
144141
_jailed_disks.append(vm.create_jailed_resource(disk))
145142
vm.ssh_config['ssh_key_path'] = snapshot.ssh_key
146143

147-
vm.create_tap_and_ssh_config(host_ip=DEFAULT_HOST_IP,
148-
guest_ip=DEFAULT_GUEST_IP,
149-
netmask_len=DEFAULT_NETMASK,
150-
tapname=DEFAULT_TAP_NAME)
151-
144+
# Create network interfaces.
145+
for iface in snapshot.net_ifaces:
146+
vm.create_tap_and_ssh_config(host_ip=iface.host_ip,
147+
guest_ip=iface.guest_ip,
148+
netmask_len=iface.netmask,
149+
tapname=iface.tap_name)
152150
response = vm.snapshot.load(mem_file_path=jailed_mem,
153151
snapshot_path=jailed_vmstate,
154152
diff=enable_diff_snapshots,
155153
resume=resume)
156-
157154
assert vm.api_session.is_status_no_content(response.status_code)
158155

159156
# Reset root path so next microvm is built some place else.
@@ -192,7 +189,8 @@ def create(self,
192189
snapshot_type: SnapshotType = SnapshotType.FULL,
193190
target_version: str = None,
194191
mem_file_name: str = "vm.mem",
195-
snapshot_name: str = "vm.vmstate"):
192+
snapshot_name: str = "vm.vmstate",
193+
net_ifaces=None):
196194
"""Create a Snapshot object from a microvm and artifacts."""
197195
# Disable API timeout as the APIs for snapshot related procedures
198196
# take longer.
@@ -215,4 +213,5 @@ def create(self,
215213
# in S3. This should be done in a PR where we add tests
216214
# that resume from S3 snapshot artifacts.
217215
disks=disks,
216+
net_ifaces=net_ifaces or [NetIfaceConfig()],
218217
ssh_key=ssh_key_copy.local_path())

tests/framework/microvms.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class VMBase:
5555
@classmethod
5656
def from_artifacts(cls, bin_cloner_path, config,
5757
kernel, disks, cpu_template, start=False,
58-
fc_binary=None, jailer_binary=None):
58+
fc_binary=None, jailer_binary=None,
59+
net_ifaces=None, enable_diff_snapshots=False):
5960
"""Spawns a new Firecracker and applies specified config."""
6061
artifacts = ArtifactCollection(_test_images_s3_bucket())
6162
# Pick the first artifact in the set.
@@ -82,7 +83,9 @@ def from_artifacts(cls, bin_cloner_path, config,
8283
disks=attached_disks,
8384
ssh_key=ssh_key,
8485
config=config,
85-
cpu_template=cpu_template)
86+
cpu_template=cpu_template,
87+
net_ifaces=net_ifaces,
88+
enable_diff_snapshots=enable_diff_snapshots)
8689

8790
if start:
8891
basevm.start()
@@ -100,7 +103,8 @@ class VMNano(VMBase):
100103

101104
@classmethod
102105
def spawn(cls, bin_cloner_path, start=False,
103-
fc_binary=None, jailer_binary=None):
106+
fc_binary=None, jailer_binary=None,
107+
net_ifaces=None, diff_snapshots=False):
104108
"""Spawns and optionally starts the vm."""
105109
return VMBase.from_artifacts(bin_cloner_path,
106110
"2vcpu_256mb",
@@ -109,15 +113,18 @@ def spawn(cls, bin_cloner_path, start=False,
109113
None,
110114
start,
111115
fc_binary,
112-
jailer_binary)
116+
jailer_binary,
117+
net_ifaces=net_ifaces,
118+
enable_diff_snapshots=diff_snapshots)
113119

114120

115121
class VMMicro(VMBase):
116122
"""Create VMs with 2vCPUs and 512 MB RAM."""
117123

118124
@classmethod
119125
def spawn(cls, bin_cloner_path, start=False,
120-
fc_binary=None, jailer_binary=None):
126+
fc_binary=None, jailer_binary=None,
127+
net_ifaces=None, diff_snapshots=False):
121128
"""Spawns and optionally starts the vm."""
122129
return VMBase.from_artifacts(bin_cloner_path,
123130
"2vcpu_512mb",
@@ -126,4 +133,6 @@ def spawn(cls, bin_cloner_path, start=False,
126133
None,
127134
start,
128135
fc_binary,
129-
jailer_binary)
136+
jailer_binary,
137+
net_ifaces=net_ifaces,
138+
enable_diff_snapshots=diff_snapshots)

tests/integration_tests/build/test_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# this contains the frequency while on AMD it does not.
2424
# Checkout the cpuid crate. In the future other
2525
# differences may appear.
26-
COVERAGE_DICT = {"Intel": 85.35, "AMD": 84.63, "ARM": 82.81}
26+
COVERAGE_DICT = {"Intel": 85.25, "AMD": 84.50, "ARM": 82.72}
2727
PROC_MODEL = proc.proc_type()
2828

2929
COVERAGE_MAX_DELTA = 0.05

tests/integration_tests/functional/test_balloon.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -120,29 +120,8 @@ def copy_util_to_rootfs(rootfs_path, util):
120120
subprocess.check_call("rmdir tmpfs", shell=True)
121121

122122

123-
# pylint: disable=C0103
124-
def test_rss_memory_lower(test_microvm_with_ssh_and_balloon, network_config):
123+
def _test_rss_memory_lower(test_microvm):
125124
"""Check inflating the balloon makes guest use less rss memory."""
126-
test_microvm = test_microvm_with_ssh_and_balloon
127-
test_microvm.spawn()
128-
test_microvm.basic_config()
129-
_tap, _, _ = test_microvm.ssh_network_config(network_config, '1')
130-
test_microvm.ssh_config['ssh_key_path'] = os.path.join(
131-
test_microvm.fsfiles,
132-
'debian.rootfs.id_rsa'
133-
)
134-
135-
# Add a memory balloon.
136-
response = test_microvm.balloon.put(
137-
amount_mb=0,
138-
deflate_on_oom=True,
139-
stats_polling_interval_s=0
140-
)
141-
assert test_microvm.api_session.is_status_no_content(response.status_code)
142-
143-
# Start the microvm.
144-
test_microvm.start()
145-
146125
# Get the firecracker pid, and open an ssh connection.
147126
firecracker_pid = test_microvm.jailer_clone_pid
148127
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
@@ -172,6 +151,32 @@ def test_rss_memory_lower(test_microvm_with_ssh_and_balloon, network_config):
172151
assert balloon_rss - init_rss <= 15000
173152

174153

154+
# pylint: disable=C0103
155+
def test_rss_memory_lower(test_microvm_with_ssh_and_balloon, network_config):
156+
"""Check inflating the balloon makes guest use less rss memory."""
157+
test_microvm = test_microvm_with_ssh_and_balloon
158+
test_microvm.spawn()
159+
test_microvm.basic_config()
160+
_tap, _, _ = test_microvm.ssh_network_config(network_config, '1')
161+
test_microvm.ssh_config['ssh_key_path'] = os.path.join(
162+
test_microvm.fsfiles,
163+
'debian.rootfs.id_rsa'
164+
)
165+
166+
# Add a memory balloon.
167+
response = test_microvm.balloon.put(
168+
amount_mb=0,
169+
deflate_on_oom=True,
170+
stats_polling_interval_s=0
171+
)
172+
assert test_microvm.api_session.is_status_no_content(response.status_code)
173+
174+
# Start the microvm.
175+
test_microvm.start()
176+
177+
_test_rss_memory_lower(test_microvm)
178+
179+
175180
# pylint: disable=C0103
176181
def test_inflate_reduces_free(test_microvm_with_ssh_and_balloon,
177182
network_config):

tests/integration_tests/functional/test_shut_down.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import time
66
import json
7+
import platform
78

89
import framework.utils as utils
910

@@ -69,5 +70,10 @@ def test_reboot(test_microvm_with_ssh, network_config):
6970
lines = metrics_fifo.sequential_reader(100)
7071
assert len(lines) == 1
7172

73+
if platform.machine() != "x86_64":
74+
log_data = test_microvm.log_data
75+
assert "Received KVM_SYSTEM_EVENT: type: 2, event: 0" in log_data
76+
assert "Vmm is stopping." in log_data
77+
7278
# Make sure that the FC process was not killed by a seccomp fault
7379
assert json.loads(lines[0])["seccomp"]["num_faults"] == 0

0 commit comments

Comments
 (0)