1use serde::Serialize;
13
14use crate::schemas::{
15 BalloonConfig, BalloonHintingOp, BalloonStatsUpdate, BalloonUpdate, BootSourceConfig,
16 CpuConfig, DriveConfig, DriveId, DrivePatch, EntropyConfig, HotplugMemoryConfig,
17 HotplugMemoryUpdate, IfaceId, InstanceAction, LoggerConfig, MachineConfig, MachineConfigPatch,
18 MetricsConfig, MmdsConfig, MmdsContents, NetworkInterfaceConfig, NetworkPatch, PmemConfig,
19 PmemPatch, SerialConfig, SnapshotCreateConfig, SnapshotLoadConfig, VmStateChange, VsockConfig,
20};
21
22#[derive(Debug, Clone, Copy, Eq, PartialEq)]
25pub enum ActionClass {
26 PreBootConfig,
29 InstanceStart,
32 SnapshotCreate,
34 SnapshotLoad,
36 VmStateChange,
38 BalloonResize,
41 Other,
43}
44
45impl ActionClass {
46 #[must_use]
48 pub const fn label(self) -> &'static str {
49 match self {
50 Self::PreBootConfig => "pre-boot config",
51 Self::InstanceStart => "InstanceStart",
52 Self::SnapshotCreate => "PUT /snapshot/create",
53 Self::SnapshotLoad => "PUT /snapshot/load",
54 Self::VmStateChange => "PATCH /vm",
55 Self::BalloonResize => "PATCH /balloon",
56 Self::Other => "VMM action",
57 }
58 }
59}
60
61#[derive(Debug)]
63pub enum ApiAction {
64 PutBootSource(BootSourceConfig),
66 PutDrive(DriveConfig),
68 PatchDrive(DrivePatch),
70 DeleteDrive {
72 drive_id: DriveId,
74 },
75 PutNetwork(NetworkInterfaceConfig),
77 PatchNetwork(NetworkPatch),
79 DeleteNetwork {
81 iface_id: IfaceId,
83 },
84 PutVsock(VsockConfig),
86 PutMmds(MmdsContents),
88 PatchMmds(MmdsContents),
90 PutMmdsConfig(MmdsConfig),
92 PutBalloon(BalloonConfig),
94 PatchBalloon(BalloonUpdate),
96 PatchBalloonStats(BalloonStatsUpdate),
98 PatchBalloonHinting {
100 op: BalloonHintingOp,
102 },
103 PutEntropy(EntropyConfig),
105 PutSerial(SerialConfig),
107 PutPmem(PmemConfig),
109 PatchPmem(PmemPatch),
111 DeletePmem {
113 pmem_id: DriveId,
115 },
116 PutHotplugMemory(HotplugMemoryConfig),
118 PatchHotplugMemory(HotplugMemoryUpdate),
120 PutCpuConfig(CpuConfig),
122 PutMachineConfig(MachineConfig),
124 PatchMachineConfig(MachineConfigPatch),
126 PutLogger(LoggerConfig),
128 PutMetrics(MetricsConfig),
130 Action(InstanceAction),
132 PatchVm(VmStateChange),
134 SnapshotCreate(SnapshotCreateConfig),
136 SnapshotLoad(SnapshotLoadConfig),
138 Shutdown,
140}
141
142impl ApiAction {
143 #[must_use]
145 pub const fn class(&self) -> ActionClass {
146 match self {
147 Self::Action(InstanceAction::InstanceStart) => ActionClass::InstanceStart,
148 Self::Action(_) | Self::Shutdown => ActionClass::Other,
149 Self::PatchVm(_) => ActionClass::VmStateChange,
150 Self::PatchBalloon(_) => ActionClass::BalloonResize,
151 Self::SnapshotCreate(_) => ActionClass::SnapshotCreate,
152 Self::SnapshotLoad(_) => ActionClass::SnapshotLoad,
153 _ => ActionClass::PreBootConfig,
154 }
155 }
156
157 #[must_use]
159 pub const fn is_pre_boot(&self) -> bool {
160 matches!(
161 self,
162 Self::PutBootSource(_)
163 | Self::PutDrive(_)
164 | Self::PutNetwork(_)
165 | Self::PutVsock(_)
166 | Self::PutMmds(_)
167 | Self::PatchMmds(_)
168 | Self::PutMmdsConfig(_)
169 | Self::PutBalloon(_)
170 | Self::PutEntropy(_)
171 | Self::PutSerial(_)
172 | Self::PutPmem(_)
173 | Self::PutHotplugMemory(_)
174 | Self::PutCpuConfig(_)
175 | Self::PutMachineConfig(_)
176 | Self::PatchMachineConfig(_)
177 | Self::PutLogger(_)
178 | Self::PutMetrics(_)
179 | Self::PatchDrive(_)
180 | Self::PatchNetwork(_)
181 | Self::Action(InstanceAction::InstanceStart)
182 | Self::SnapshotLoad(_)
183 | Self::Shutdown
184 )
185 }
186
187 #[must_use]
189 pub const fn is_post_boot(&self) -> bool {
190 matches!(
191 self,
192 Self::PatchVm(_)
193 | Self::PatchBalloon(_)
194 | Self::PatchBalloonStats(_)
195 | Self::PatchBalloonHinting { .. }
196 | Self::PatchHotplugMemory(_)
197 | Self::PatchMmds(_)
198 | Self::PutMmds(_)
199 | Self::PatchDrive(_)
200 | Self::PatchNetwork(_)
201 | Self::PatchPmem(_)
202 | Self::DeleteDrive { .. }
203 | Self::DeleteNetwork { .. }
204 | Self::DeletePmem { .. }
205 | Self::SnapshotCreate(_)
206 | Self::Action(InstanceAction::FlushMetrics)
207 | Self::Shutdown
208 )
209 }
210
211 #[must_use]
213 pub const fn label(&self) -> &'static str {
214 match self {
215 Self::PutBootSource(_) => "PUT /boot-source",
216 Self::PutDrive(_) => "PUT /drives/{id}",
217 Self::PatchDrive(_) => "PATCH /drives/{id}",
218 Self::DeleteDrive { .. } => "DELETE /drives/{id}",
219 Self::PutNetwork(_) => "PUT /network-interfaces/{id}",
220 Self::PatchNetwork(_) => "PATCH /network-interfaces/{id}",
221 Self::DeleteNetwork { .. } => "DELETE /network-interfaces/{id}",
222 Self::PutVsock(_) => "PUT /vsock",
223 Self::PutMmds(_) => "PUT /mmds",
224 Self::PatchMmds(_) => "PATCH /mmds",
225 Self::PutMmdsConfig(_) => "PUT /mmds/config",
226 Self::PutBalloon(_) => "PUT /balloon",
227 Self::PatchBalloon(_) => "PATCH /balloon",
228 Self::PatchBalloonStats(_) => "PATCH /balloon/statistics",
229 Self::PatchBalloonHinting { .. } => "PATCH /balloon/hinting/{op}",
230 Self::PutEntropy(_) => "PUT /entropy",
231 Self::PutSerial(_) => "PUT /serial",
232 Self::PutPmem(_) => "PUT /pmem/{id}",
233 Self::PatchPmem(_) => "PATCH /pmem/{id}",
234 Self::DeletePmem { .. } => "DELETE /pmem/{id}",
235 Self::PutHotplugMemory(_) => "PUT /hotplug/memory",
236 Self::PatchHotplugMemory(_) => "PATCH /hotplug/memory",
237 Self::PutCpuConfig(_) => "PUT /cpu-config",
238 Self::PutMachineConfig(_) => "PUT /machine-config",
239 Self::PatchMachineConfig(_) => "PATCH /machine-config",
240 Self::PutLogger(_) => "PUT /logger",
241 Self::PutMetrics(_) => "PUT /metrics",
242 Self::Action(_) => "PUT /actions",
243 Self::PatchVm(_) => "PATCH /vm",
244 Self::SnapshotCreate(_) => "PUT /snapshot/create",
245 Self::SnapshotLoad(_) => "PUT /snapshot/load",
246 Self::Shutdown => "Shutdown",
247 }
248 }
249}
250
251#[derive(Debug, Clone, Serialize)]
253#[serde(untagged)]
254pub enum ApiResponse {
255 NoContent,
257 Json(serde_json::Value),
259 Fault {
261 status: u16,
263 fault_message: String,
265 },
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_should_classify_instance_start_for_30s_timeout() {
274 let action = ApiAction::Action(InstanceAction::InstanceStart);
275 assert_eq!(action.class(), ActionClass::InstanceStart);
276 }
277
278 #[test]
279 fn test_should_classify_snapshot_actions_for_5min_timeout() {
280 let create_label = ActionClass::SnapshotCreate.label();
281 assert_eq!(create_label, "PUT /snapshot/create");
282 }
283
284 #[test]
285 fn test_should_classify_pre_boot_config_default() {
286 let action = ApiAction::PutEntropy(EntropyConfig::default());
287 assert_eq!(action.class(), ActionClass::PreBootConfig);
288 }
289
290 #[test]
291 fn test_should_label_actions_human_readable() {
292 let action = ApiAction::PatchVm(VmStateChange::Paused);
293 assert_eq!(action.label(), "PATCH /vm");
294 }
295}