Skip to content

Commit 98f5c05

Browse files
JamesC1305roypat
authored andcommitted
Add API endpoint for hotplugging
Create a new endpoint `hotplug` in `parsed_request.rs` and implement basic skeleton of endpoint implementation. Signed-off-by: James Curtis <[email protected]>
1 parent be2f41a commit 98f5c05

File tree

9 files changed

+211
-0
lines changed

9 files changed

+211
-0
lines changed

src/firecracker/src/api_server/parsed_request.rs

+20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use super::request::boot_source::parse_put_boot_source;
1515
use super::request::cpu_configuration::parse_put_cpu_config;
1616
use super::request::drive::{parse_patch_drive, parse_put_drive};
1717
use super::request::entropy::parse_put_entropy;
18+
#[cfg(target_arch = "x86_64")]
19+
use super::request::hotplug::parse_put_hotplug;
1820
use super::request::instance_info::parse_get_instance_info;
1921
use super::request::logger::parse_put_logger;
2022
use super::request::machine_configuration::{
@@ -89,6 +91,8 @@ impl TryFrom<&Request> for ParsedRequest {
8991
(Method::Put, "boot-source", Some(body)) => parse_put_boot_source(body),
9092
(Method::Put, "cpu-config", Some(body)) => parse_put_cpu_config(body),
9193
(Method::Put, "drives", Some(body)) => parse_put_drive(body, path_tokens.next()),
94+
#[cfg(target_arch = "x86_64")]
95+
(Method::Put, "hotplug", Some(body)) => parse_put_hotplug(body),
9296
(Method::Put, "logger", Some(body)) => parse_put_logger(body),
9397
(Method::Put, "machine-config", Some(body)) => parse_put_machine_config(body),
9498
(Method::Put, "metrics", Some(body)) => parse_put_metrics(body),
@@ -746,6 +750,22 @@ pub mod tests {
746750
ParsedRequest::try_from(&req).unwrap();
747751
}
748752

753+
#[test]
754+
#[cfg(target_arch = "x86_64")]
755+
fn test_try_from_put_hotplug_vcpu() {
756+
let (mut sender, receiver) = UnixStream::pair().unwrap();
757+
let mut connection = HttpConnection::new(receiver);
758+
let body = r#"{
759+
"Vcpu": { "add": 1 }
760+
}"#;
761+
sender
762+
.write_all(http_request("PUT", "/hotplug", Some(body)).as_bytes())
763+
.unwrap();
764+
connection.try_read().unwrap();
765+
let req = connection.pop_parsed_request().unwrap();
766+
ParsedRequest::try_from(&req).unwrap();
767+
}
768+
749769
#[test]
750770
fn test_try_from_put_logger() {
751771
let (mut sender, receiver) = UnixStream::pair().unwrap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
use vmm::logger::{IncMetric, METRICS};
4+
use vmm::rpc_interface::VmmAction;
5+
use vmm::vmm_config::hotplug::HotplugRequestConfig;
6+
7+
use super::super::parsed_request::{ParsedRequest, RequestError};
8+
use super::Body;
9+
10+
pub(crate) fn parse_put_hotplug(body: &Body) -> Result<ParsedRequest, RequestError> {
11+
METRICS.put_api_requests.hotplug.inc();
12+
METRICS.hotplug.hotplug_request_count.inc();
13+
let config = serde_json::from_slice::<HotplugRequestConfig>(body.raw()).map_err(|err| {
14+
METRICS.hotplug.hotplug_request_fails.inc();
15+
err
16+
})?;
17+
Ok(ParsedRequest::new_sync(VmmAction::HotplugRequest(config)))
18+
}
19+
20+
#[cfg(test)]
21+
mod tests {
22+
23+
use hotplug::parse_put_hotplug;
24+
use vmm::rpc_interface::VmmAction;
25+
use vmm::vmm_config::hotplug::{HotplugRequestConfig, HotplugVcpuConfig};
26+
27+
use super::super::*;
28+
use crate::api_server::parsed_request::tests::vmm_action_from_request;
29+
30+
#[test]
31+
fn test_parse_put_hotplug() {
32+
// Case 1. Invalid body
33+
parse_put_hotplug(&Body::new("invalid body")).unwrap_err();
34+
35+
// Case 2. vCPU Resource
36+
let body = r#"{
37+
"Vcpu" : { "add": 4 }
38+
}"#;
39+
40+
let expected_config = HotplugVcpuConfig { add: 4 };
41+
42+
assert_eq!(
43+
vmm_action_from_request(parse_put_hotplug(&Body::new(body)).unwrap()),
44+
VmmAction::HotplugRequest(HotplugRequestConfig::Vcpu(expected_config))
45+
);
46+
}
47+
}

src/firecracker/src/api_server/request/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub mod boot_source;
77
pub mod cpu_configuration;
88
pub mod drive;
99
pub mod entropy;
10+
#[cfg(target_arch = "x86_64")]
11+
pub mod hotplug;
1012
pub mod instance_info;
1113
pub mod logger;
1214
pub mod machine_configuration;

src/firecracker/swagger/firecracker.yaml

+39
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,30 @@ paths:
282282
schema:
283283
$ref: "#/definitions/Error"
284284

285+
/hotplug:
286+
put:
287+
summary: Hotplugs resources to the microVM. Only available for x86_64 hosts. Post-boot only.
288+
description:
289+
Adds new resources to the VM.
290+
parameters:
291+
- name: body
292+
in: body
293+
description: The resource type and amount to be hotplugged.
294+
required: true
295+
schema:
296+
$ref: "#/definitions/Hotplug"
297+
responses:
298+
204:
299+
description: Resources hotplugged successfully.
300+
400:
301+
description: Resources cannot be hotplugged due to bad input or a VMM-related error.
302+
schema:
303+
$ref: "#/definitions/Error"
304+
default:
305+
description: Internal server error.
306+
schema:
307+
$ref: "#/definitions/Error"
308+
285309
/logger:
286310
put:
287311
summary: Initializes the logger by specifying a named pipe or a file for the logs output.
@@ -943,6 +967,21 @@ definitions:
943967
vsock:
944968
$ref: "#/definitions/Vsock"
945969

970+
Hotplug:
971+
type: object
972+
description:
973+
Defines the configuration for hot plugging.
974+
properties:
975+
Vcpu:
976+
type: object
977+
description: Hotplug requests relating to vCPU cores.
978+
properties:
979+
add:
980+
description: Number of vCPUs cores to be added to the VM
981+
type: integer
982+
minimum: 1
983+
984+
946985
InstanceActionInfo:
947986
type: object
948987
description:

src/vmm/src/logger/metrics.rs

+31
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ pub struct PutRequestsMetrics {
410410
pub drive_count: SharedIncMetric,
411411
/// Number of failures in attaching a block device.
412412
pub drive_fails: SharedIncMetric,
413+
/// Number of PUTs for hotplugging
414+
pub hotplug: SharedIncMetric,
415+
/// Number of failures for hotplugging.
416+
pub hotplug_fails: SharedIncMetric,
413417
/// Number of PUTs for initializing the logging system.
414418
pub logger_count: SharedIncMetric,
415419
/// Number of failures in initializing the logging system.
@@ -449,6 +453,8 @@ impl PutRequestsMetrics {
449453
boot_source_fails: SharedIncMetric::new(),
450454
drive_count: SharedIncMetric::new(),
451455
drive_fails: SharedIncMetric::new(),
456+
hotplug: SharedIncMetric::new(),
457+
hotplug_fails: SharedIncMetric::new(),
452458
logger_count: SharedIncMetric::new(),
453459
logger_fails: SharedIncMetric::new(),
454460
machine_cfg_count: SharedIncMetric::new(),
@@ -824,6 +830,28 @@ impl VmmMetrics {
824830
}
825831
}
826832

833+
/// Metrics specific to hotplugging
834+
#[derive(Debug, Default, Serialize)]
835+
pub struct HotplugMetrics {
836+
pub hotplug_request_count: SharedIncMetric,
837+
pub hotplug_request_fails: SharedIncMetric,
838+
pub vcpu_hotplug_request_fails: SharedIncMetric,
839+
pub vcpus_added: SharedIncMetric,
840+
}
841+
842+
impl HotplugMetrics {
843+
/// Const default construction.
844+
845+
pub const fn new() -> Self {
846+
Self {
847+
hotplug_request_count: SharedIncMetric::new(),
848+
hotplug_request_fails: SharedIncMetric::new(),
849+
vcpu_hotplug_request_fails: SharedIncMetric::new(),
850+
vcpus_added: SharedIncMetric::new(),
851+
}
852+
}
853+
}
854+
827855
// The sole purpose of this struct is to produce an UTC timestamp when an instance is serialized.
828856
#[derive(Debug, Default)]
829857
struct SerializeToUtcTimestampMs;
@@ -887,6 +915,8 @@ pub struct FirecrackerMetrics {
887915
pub deprecated_api: DeprecatedApiMetrics,
888916
/// Metrics related to API GET requests.
889917
pub get_api_requests: GetRequestsMetrics,
918+
/// Metrics related to hot-plugging.
919+
pub hotplug: HotplugMetrics,
890920
#[serde(flatten)]
891921
/// Metrics related to the legacy device.
892922
pub legacy_dev_ser: LegacyDevMetricsSerializeProxy,
@@ -931,6 +961,7 @@ impl FirecrackerMetrics {
931961
block_ser: BlockMetricsSerializeProxy {},
932962
deprecated_api: DeprecatedApiMetrics::new(),
933963
get_api_requests: GetRequestsMetrics::new(),
964+
hotplug: HotplugMetrics::new(),
934965
legacy_dev_ser: LegacyDevMetricsSerializeProxy {},
935966
latencies_us: PerformanceMetrics::new(),
936967
logger: LoggerSystemMetrics::new(),

src/vmm/src/rpc_interface.rs

+14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use super::{
2020
};
2121
use crate::builder::StartMicrovmError;
2222
use crate::cpu_config::templates::{CustomCpuTemplate, GuestConfigError};
23+
#[cfg(target_arch = "x86_64")]
24+
use crate::logger::METRICS;
2325
use crate::logger::{info, warn, LoggerConfig, *};
2426
use crate::mmds::data_store::{self, Mmds};
2527
use crate::persist::{CreateSnapshotError, RestoreFromSnapshotError, VmInfo};
@@ -31,6 +33,8 @@ use crate::vmm_config::balloon::{
3133
use crate::vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError};
3234
use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, DriveError};
3335
use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError};
36+
#[cfg(target_arch = "x86_64")]
37+
use crate::vmm_config::hotplug::{HotplugRequestConfig, HotplugRequestError};
3438
use crate::vmm_config::instance_info::InstanceInfo;
3539
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate, VmConfigError};
3640
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError};
@@ -75,6 +79,9 @@ pub enum VmmAction {
7579
GetVmmVersion,
7680
/// Flush the metrics. This action can only be called after the logger has been configured.
7781
FlushMetrics,
82+
/// Hotplug resources into the VM.
83+
#[cfg(target_arch = "x86_64")]
84+
HotplugRequest(HotplugRequestConfig),
7885
/// Add a new block device or update one that already exists using the `BlockDeviceConfig` as
7986
/// input. This action can only be called before the microVM has booted.
8087
InsertBlockDevice(BlockDeviceConfig),
@@ -144,6 +151,9 @@ pub enum VmmActionError {
144151
DriveConfig(#[from] DriveError),
145152
/// Entropy device error: {0}
146153
EntropyDevice(#[from] EntropyDeviceError),
154+
/// Hotplug error: {0}
155+
#[cfg(target_arch = "x86_64")]
156+
HotplugRequest(#[from] HotplugRequestError),
147157
/// Internal VMM error: {0}
148158
InternalVmm(#[from] VmmError),
149159
/// Load snapshot error: {0}
@@ -453,6 +463,8 @@ impl<'a> PrebootApiController<'a> {
453463
| UpdateNetworkInterface(_) => Err(VmmActionError::OperationNotSupportedPreBoot),
454464
#[cfg(target_arch = "x86_64")]
455465
SendCtrlAltDel => Err(VmmActionError::OperationNotSupportedPreBoot),
466+
#[cfg(target_arch = "x86_64")]
467+
HotplugRequest(_) => Err(VmmActionError::OperationNotSupportedPreBoot),
456468
}
457469
}
458470

@@ -657,6 +669,8 @@ impl RuntimeApiController {
657669
GetVmmVersion => Ok(VmmData::VmmVersion(
658670
self.vmm.lock().expect("Poisoned lock").version(),
659671
)),
672+
#[cfg(target_arch = "x86_64")]
673+
HotplugRequest(config) => Ok(VmmData::Empty),
660674
PatchMMDS(value) => self.patch_mmds(value),
661675
Pause => self.pause(),
662676
PutMMDS(value) => self.put_mmds(value),

src/vmm/src/vmm_config/hotplug.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use std::io;
5+
6+
use serde::{Deserialize, Serialize};
7+
8+
use crate::vstate::vcpu::VcpuError;
9+
use crate::StartVcpusError;
10+
/// Unifying enum for all types of hotplug request configs.
11+
/// Currently only Vcpus may be hotplugged.
12+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13+
pub enum HotplugRequestConfig {
14+
/// Vcpu hotplug request
15+
Vcpu(HotplugVcpuConfig),
16+
}
17+
18+
/// Errors related to different types of hotplugging.
19+
/// Currently only Vcpus can be hotplugged.
20+
#[derive(Debug, thiserror::Error, displaydoc::Display)]
21+
pub enum HotplugRequestError {
22+
/// Vcpu hotplugging error: {0}
23+
Vcpu(#[from] HotplugVcpuError),
24+
}
25+
26+
/// Errors associated with hot-plugging vCPUs
27+
#[derive(Debug, thiserror::Error, displaydoc::Display)]
28+
pub enum HotplugVcpuError {
29+
/// The number of vCPUs added must be greater than 0.
30+
VcpuCountTooLow,
31+
/// The number of vCPUs added must be less than 32.
32+
VcpuCountTooHigh,
33+
/// Event fd error: {0}
34+
EventFd(io::Error),
35+
/// Error creating the vcpu: {0}
36+
VcpuCreate(VcpuError),
37+
/// Failed to start vCPUs
38+
VcpuStart(StartVcpusError),
39+
}
40+
41+
/// Config for hotplugging vCPUS
42+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
43+
#[serde(deny_unknown_fields)]
44+
pub struct HotplugVcpuConfig {
45+
/// Number of vcpus to start.
46+
pub add: u8,
47+
}

src/vmm/src/vmm_config/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub mod boot_source;
2020
pub mod drive;
2121
/// Wrapper for configuring the entropy device attached to the microVM.
2222
pub mod entropy;
23+
/// Wrapper over hotplug configuration.
24+
#[cfg(target_arch = "x86_64")]
25+
pub mod hotplug;
2326
/// Wrapper over the microVM general information attached to the microVM.
2427
pub mod instance_info;
2528
/// Wrapper for configuring the memory and CPU of the microVM.

tests/host_tools/fcmetrics.py

+8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ def validate_fc_metrics(metrics):
152152
"mmds_count",
153153
"vmm_version_count",
154154
],
155+
"hotplug": [
156+
"hotplug_request_count",
157+
"hotplug_request_fails",
158+
"vcpu_hotplug_request_fails",
159+
"vcpus_added",
160+
],
155161
"i8042": [
156162
"error_count",
157163
"missed_read_count",
@@ -209,6 +215,8 @@ def validate_fc_metrics(metrics):
209215
"boot_source_fails",
210216
"drive_count",
211217
"drive_fails",
218+
"hotplug",
219+
"hotplug_fails",
212220
"logger_count",
213221
"logger_fails",
214222
"machine_cfg_count",

0 commit comments

Comments
 (0)