xref: /cloud-hypervisor/vmm/src/api/mod.rs (revision 9af2968a7dc47b89bf07ea9dc5e735084efcfa3a)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 //! The internal VMM API for Cloud Hypervisor.
7 //!
8 //! This API is a synchronous, [mpsc](https://doc.rust-lang.org/std/sync/mpsc/)
9 //! based IPC for sending commands to the VMM thread, from other
10 //! Cloud Hypervisor threads. The IPC follows a command-response protocol, i.e.
11 //! each command will receive a response back.
12 //!
13 //! The main Cloud Hypervisor thread creates an API event file descriptor
14 //! to notify the VMM thread about pending API commands, together with an
15 //! API mpsc channel. The former is the IPC control plane, the latter is the
16 //! IPC data plane.
17 //! In order to use the IPC, a Cloud Hypervisor thread needs to have a clone
18 //! of both the API event file descriptor and the channel Sender. Then it must
19 //! go through the following steps:
20 //!
21 //! 1. The thread creates an mpsc channel for receiving the command response.
22 //! 2. The thread sends an ApiRequest to the Sender endpoint. The ApiRequest
23 //!    contains the response channel Sender, for the VMM API server to be able
24 //!    to send the response back.
25 //! 3. The thread writes to the API event file descriptor to notify the VMM
26 //!    API server about a pending command.
27 //! 4. The thread reads the response back from the VMM API server, from the
28 //!    response channel Receiver.
29 //! 5. The thread handles the response and forwards potential errors.
30 
31 pub use self::http::start_http_fd_thread;
32 pub use self::http::start_http_path_thread;
33 
34 pub mod http;
35 pub mod http_endpoint;
36 
37 use crate::config::{
38     DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, VsockConfig,
39 };
40 use crate::device_tree::DeviceTree;
41 use crate::vm::{Error as VmError, VmState};
42 use micro_http::Body;
43 use std::io;
44 use std::sync::mpsc::{channel, RecvError, SendError, Sender};
45 use std::sync::{Arc, Mutex};
46 use vm_migration::MigratableError;
47 use vmm_sys_util::eventfd::EventFd;
48 
49 /// API errors are sent back from the VMM API server through the ApiResponse.
50 #[derive(Debug)]
51 pub enum ApiError {
52     /// Cannot write to EventFd.
53     EventFdWrite(io::Error),
54 
55     /// API request send error
56     RequestSend(SendError<ApiRequest>),
57 
58     /// Wrong response payload type
59     ResponsePayloadType,
60 
61     /// API response receive error
62     ResponseRecv(RecvError),
63 
64     /// The VM could not boot.
65     VmBoot(VmError),
66 
67     /// The VM could not be created.
68     VmCreate(VmError),
69 
70     /// The VM could not be deleted.
71     VmDelete(VmError),
72 
73     /// The VM info is not available.
74     VmInfo(VmError),
75 
76     /// The VM could not be paused.
77     VmPause(VmError),
78 
79     /// The VM could not resume.
80     VmResume(VmError),
81 
82     /// The VM is not booted.
83     VmNotBooted,
84 
85     /// The VM is not created.
86     VmNotCreated,
87 
88     /// The VM could not shutdown.
89     VmShutdown(VmError),
90 
91     /// The VM could not reboot.
92     VmReboot(VmError),
93 
94     /// The VM could not be snapshotted.
95     VmSnapshot(VmError),
96 
97     /// The VM could not restored.
98     VmRestore(VmError),
99 
100     /// The VMM could not shutdown.
101     VmmShutdown(VmError),
102 
103     /// The VM could not be resized
104     VmResize(VmError),
105 
106     /// The memory zone could not be resized.
107     VmResizeZone(VmError),
108 
109     /// The device could not be added to the VM.
110     VmAddDevice(VmError),
111 
112     /// The device could not be removed from the VM.
113     VmRemoveDevice(VmError),
114 
115     /// Cannot create seccomp filter
116     CreateSeccompFilter(seccomp::SeccompError),
117 
118     /// Cannot apply seccomp filter
119     ApplySeccompFilter(seccomp::Error),
120 
121     /// The disk could not be added to the VM.
122     VmAddDisk(VmError),
123 
124     /// The fs could not be added to the VM.
125     VmAddFs(VmError),
126 
127     /// The pmem device could not be added to the VM.
128     VmAddPmem(VmError),
129 
130     /// The network device could not be added to the VM.
131     VmAddNet(VmError),
132 
133     /// The vsock device could not be added to the VM.
134     VmAddVsock(VmError),
135 
136     /// Error starting migration receiever
137     VmReceiveMigration(MigratableError),
138 
139     /// Error starting migration sender
140     VmSendMigration(MigratableError),
141 
142     /// Error triggering power button
143     VmPowerButton(VmError),
144 }
145 pub type ApiResult<T> = std::result::Result<T, ApiError>;
146 
147 #[derive(Clone, Deserialize, Serialize)]
148 pub struct VmInfo {
149     pub config: Arc<Mutex<VmConfig>>,
150     pub state: VmState,
151     pub memory_actual_size: u64,
152     pub device_tree: Option<Arc<Mutex<DeviceTree>>>,
153 }
154 
155 #[derive(Clone, Deserialize, Serialize)]
156 pub struct VmmPingResponse {
157     pub version: String,
158 }
159 
160 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
161 pub struct VmResizeData {
162     pub desired_vcpus: Option<u8>,
163     pub desired_ram: Option<u64>,
164     pub desired_balloon: Option<u64>,
165 }
166 
167 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
168 pub struct VmResizeZoneData {
169     pub id: String,
170     pub desired_ram: u64,
171 }
172 
173 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
174 pub struct VmRemoveDeviceData {
175     pub id: String,
176 }
177 
178 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
179 pub struct VmSnapshotConfig {
180     /// The snapshot destination URL
181     pub destination_url: String,
182 }
183 
184 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
185 pub struct VmReceiveMigrationData {
186     /// URL for the reception of migration state
187     pub receiver_url: String,
188 }
189 
190 #[derive(Clone, Deserialize, Serialize, Default, Debug)]
191 pub struct VmSendMigrationData {
192     /// URL to migrate the VM to
193     pub destination_url: String,
194 }
195 
196 pub enum ApiResponsePayload {
197     /// No data is sent on the channel.
198     Empty,
199 
200     /// Virtual machine information
201     VmInfo(VmInfo),
202 
203     /// Vmm ping response
204     VmmPing(VmmPingResponse),
205 
206     /// Vm action response
207     VmAction(Vec<u8>),
208 }
209 
210 /// This is the response sent by the VMM API server through the mpsc channel.
211 pub type ApiResponse = std::result::Result<ApiResponsePayload, ApiError>;
212 
213 #[allow(clippy::large_enum_variant)]
214 #[derive(Debug)]
215 pub enum ApiRequest {
216     /// Create the virtual machine. This request payload is a VM configuration
217     /// (VmConfig).
218     /// If the VMM API server could not create the VM, it will send a VmCreate
219     /// error back.
220     VmCreate(Arc<Mutex<VmConfig>>, Sender<ApiResponse>),
221 
222     /// Boot the previously created virtual machine.
223     /// If the VM was not previously created, the VMM API server will send a
224     /// VmBoot error back.
225     VmBoot(Sender<ApiResponse>),
226 
227     /// Delete the previously created virtual machine.
228     /// If the VM was not previously created, the VMM API server will send a
229     /// VmDelete error back.
230     /// If the VM is booted, we shut it down first.
231     VmDelete(Sender<ApiResponse>),
232 
233     /// Request the VM information.
234     VmInfo(Sender<ApiResponse>),
235 
236     /// Request the VMM API server status
237     VmmPing(Sender<ApiResponse>),
238 
239     /// Pause a VM.
240     VmPause(Sender<ApiResponse>),
241 
242     /// Resume a VM.
243     VmResume(Sender<ApiResponse>),
244 
245     /// Get counters for a VM.
246     VmCounters(Sender<ApiResponse>),
247 
248     /// Shut the previously booted virtual machine down.
249     /// If the VM was not previously booted or created, the VMM API server
250     /// will send a VmShutdown error back.
251     VmShutdown(Sender<ApiResponse>),
252 
253     /// Reboot the previously booted virtual machine.
254     /// If the VM was not previously booted or created, the VMM API server
255     /// will send a VmReboot error back.
256     VmReboot(Sender<ApiResponse>),
257 
258     /// Shut the VMM down.
259     /// This will shutdown and delete the current VM, if any, and then exit the
260     /// VMM process.
261     VmmShutdown(Sender<ApiResponse>),
262 
263     /// Resize the VM.
264     VmResize(Arc<VmResizeData>, Sender<ApiResponse>),
265 
266     /// Resize the memory zone.
267     VmResizeZone(Arc<VmResizeZoneData>, Sender<ApiResponse>),
268 
269     /// Add a device to the VM.
270     VmAddDevice(Arc<DeviceConfig>, Sender<ApiResponse>),
271 
272     /// Remove a device from the VM.
273     VmRemoveDevice(Arc<VmRemoveDeviceData>, Sender<ApiResponse>),
274 
275     /// Add a disk to the VM.
276     VmAddDisk(Arc<DiskConfig>, Sender<ApiResponse>),
277 
278     /// Add a fs to the VM.
279     VmAddFs(Arc<FsConfig>, Sender<ApiResponse>),
280 
281     /// Add a pmem device to the VM.
282     VmAddPmem(Arc<PmemConfig>, Sender<ApiResponse>),
283 
284     /// Add a network device to the VM.
285     VmAddNet(Arc<NetConfig>, Sender<ApiResponse>),
286 
287     /// Add a vsock device to the VM.
288     VmAddVsock(Arc<VsockConfig>, Sender<ApiResponse>),
289 
290     /// Take a VM snapshot
291     VmSnapshot(Arc<VmSnapshotConfig>, Sender<ApiResponse>),
292 
293     /// Restore from a VM snapshot
294     VmRestore(Arc<RestoreConfig>, Sender<ApiResponse>),
295 
296     /// Incoming migration
297     VmReceiveMigration(Arc<VmReceiveMigrationData>, Sender<ApiResponse>),
298 
299     /// Outgoing migration
300     VmSendMigration(Arc<VmSendMigrationData>, Sender<ApiResponse>),
301 
302     // Trigger power button
303     VmPowerButton(Sender<ApiResponse>),
304 }
305 
306 pub fn vm_create(
307     api_evt: EventFd,
308     api_sender: Sender<ApiRequest>,
309     config: Arc<Mutex<VmConfig>>,
310 ) -> ApiResult<()> {
311     let (response_sender, response_receiver) = channel();
312 
313     // Send the VM creation request.
314     api_sender
315         .send(ApiRequest::VmCreate(config, response_sender))
316         .map_err(ApiError::RequestSend)?;
317     api_evt.write(1).map_err(ApiError::EventFdWrite)?;
318 
319     response_receiver.recv().map_err(ApiError::ResponseRecv)??;
320 
321     Ok(())
322 }
323 
324 /// Represents a VM related action.
325 /// This is mostly used to factorize code between VM routines
326 /// that only differ by the IPC command they send.
327 pub enum VmAction {
328     /// Boot a VM
329     Boot,
330 
331     /// Delete a VM
332     Delete,
333 
334     /// Shut a VM down
335     Shutdown,
336 
337     /// Reboot a VM
338     Reboot,
339 
340     /// Pause a VM
341     Pause,
342 
343     /// Resume a VM
344     Resume,
345 
346     /// Return VM counters
347     Counters,
348 
349     /// Add VFIO device
350     AddDevice(Arc<DeviceConfig>),
351 
352     /// Add disk
353     AddDisk(Arc<DiskConfig>),
354 
355     /// Add filesystem
356     AddFs(Arc<FsConfig>),
357 
358     /// Add pmem
359     AddPmem(Arc<PmemConfig>),
360 
361     /// Add network
362     AddNet(Arc<NetConfig>),
363 
364     /// Add vsock
365     AddVsock(Arc<VsockConfig>),
366 
367     /// Remove VFIO device
368     RemoveDevice(Arc<VmRemoveDeviceData>),
369 
370     /// Resize VM
371     Resize(Arc<VmResizeData>),
372 
373     /// Resize memory zone
374     ResizeZone(Arc<VmResizeZoneData>),
375 
376     /// Restore VM
377     Restore(Arc<RestoreConfig>),
378 
379     /// Snapshot VM
380     Snapshot(Arc<VmSnapshotConfig>),
381 
382     /// Incoming migration
383     ReceiveMigration(Arc<VmReceiveMigrationData>),
384 
385     /// Outgoing migration
386     SendMigration(Arc<VmSendMigrationData>),
387 
388     /// Power Button for clean shutdown
389     PowerButton,
390 }
391 
392 fn vm_action(
393     api_evt: EventFd,
394     api_sender: Sender<ApiRequest>,
395     action: VmAction,
396 ) -> ApiResult<Option<Body>> {
397     let (response_sender, response_receiver) = channel();
398 
399     use VmAction::*;
400     let request = match action {
401         Boot => ApiRequest::VmBoot(response_sender),
402         Delete => ApiRequest::VmDelete(response_sender),
403         Shutdown => ApiRequest::VmShutdown(response_sender),
404         Reboot => ApiRequest::VmReboot(response_sender),
405         Pause => ApiRequest::VmPause(response_sender),
406         Resume => ApiRequest::VmResume(response_sender),
407         Counters => ApiRequest::VmCounters(response_sender),
408         AddDevice(v) => ApiRequest::VmAddDevice(v, response_sender),
409         AddDisk(v) => ApiRequest::VmAddDisk(v, response_sender),
410         AddFs(v) => ApiRequest::VmAddFs(v, response_sender),
411         AddPmem(v) => ApiRequest::VmAddPmem(v, response_sender),
412         AddNet(v) => ApiRequest::VmAddNet(v, response_sender),
413         AddVsock(v) => ApiRequest::VmAddVsock(v, response_sender),
414         RemoveDevice(v) => ApiRequest::VmRemoveDevice(v, response_sender),
415         Resize(v) => ApiRequest::VmResize(v, response_sender),
416         ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender),
417         Restore(v) => ApiRequest::VmRestore(v, response_sender),
418         Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender),
419         ReceiveMigration(v) => ApiRequest::VmReceiveMigration(v, response_sender),
420         SendMigration(v) => ApiRequest::VmSendMigration(v, response_sender),
421         PowerButton => ApiRequest::VmPowerButton(response_sender),
422     };
423 
424     // Send the VM request.
425     api_sender.send(request).map_err(ApiError::RequestSend)?;
426     api_evt.write(1).map_err(ApiError::EventFdWrite)?;
427 
428     let body = match response_receiver.recv().map_err(ApiError::ResponseRecv)?? {
429         ApiResponsePayload::VmAction(response) => Some(Body::new(response)),
430         ApiResponsePayload::Empty => None,
431         _ => return Err(ApiError::ResponsePayloadType),
432     };
433 
434     Ok(body)
435 }
436 
437 pub fn vm_boot(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
438     vm_action(api_evt, api_sender, VmAction::Boot)
439 }
440 
441 pub fn vm_delete(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
442     vm_action(api_evt, api_sender, VmAction::Delete)
443 }
444 
445 pub fn vm_shutdown(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
446     vm_action(api_evt, api_sender, VmAction::Shutdown)
447 }
448 
449 pub fn vm_reboot(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
450     vm_action(api_evt, api_sender, VmAction::Reboot)
451 }
452 
453 pub fn vm_pause(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
454     vm_action(api_evt, api_sender, VmAction::Pause)
455 }
456 
457 pub fn vm_resume(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
458     vm_action(api_evt, api_sender, VmAction::Resume)
459 }
460 
461 pub fn vm_counters(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> {
462     vm_action(api_evt, api_sender, VmAction::Counters)
463 }
464 
465 pub fn vm_power_button(
466     api_evt: EventFd,
467     api_sender: Sender<ApiRequest>,
468 ) -> ApiResult<Option<Body>> {
469     vm_action(api_evt, api_sender, VmAction::PowerButton)
470 }
471 
472 pub fn vm_receive_migration(
473     api_evt: EventFd,
474     api_sender: Sender<ApiRequest>,
475     data: Arc<VmReceiveMigrationData>,
476 ) -> ApiResult<Option<Body>> {
477     vm_action(api_evt, api_sender, VmAction::ReceiveMigration(data))
478 }
479 
480 pub fn vm_send_migration(
481     api_evt: EventFd,
482     api_sender: Sender<ApiRequest>,
483     data: Arc<VmSendMigrationData>,
484 ) -> ApiResult<Option<Body>> {
485     vm_action(api_evt, api_sender, VmAction::SendMigration(data))
486 }
487 
488 pub fn vm_snapshot(
489     api_evt: EventFd,
490     api_sender: Sender<ApiRequest>,
491     data: Arc<VmSnapshotConfig>,
492 ) -> ApiResult<Option<Body>> {
493     vm_action(api_evt, api_sender, VmAction::Snapshot(data))
494 }
495 
496 pub fn vm_restore(
497     api_evt: EventFd,
498     api_sender: Sender<ApiRequest>,
499     data: Arc<RestoreConfig>,
500 ) -> ApiResult<Option<Body>> {
501     vm_action(api_evt, api_sender, VmAction::Restore(data))
502 }
503 
504 pub fn vm_info(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<VmInfo> {
505     let (response_sender, response_receiver) = channel();
506 
507     // Send the VM request.
508     api_sender
509         .send(ApiRequest::VmInfo(response_sender))
510         .map_err(ApiError::RequestSend)?;
511     api_evt.write(1).map_err(ApiError::EventFdWrite)?;
512 
513     let vm_info = response_receiver.recv().map_err(ApiError::ResponseRecv)??;
514 
515     match vm_info {
516         ApiResponsePayload::VmInfo(info) => Ok(info),
517         _ => Err(ApiError::ResponsePayloadType),
518     }
519 }
520 
521 pub fn vmm_ping(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<VmmPingResponse> {
522     let (response_sender, response_receiver) = channel();
523 
524     api_sender
525         .send(ApiRequest::VmmPing(response_sender))
526         .map_err(ApiError::RequestSend)?;
527     api_evt.write(1).map_err(ApiError::EventFdWrite)?;
528 
529     let vmm_pong = response_receiver.recv().map_err(ApiError::ResponseRecv)??;
530 
531     match vmm_pong {
532         ApiResponsePayload::VmmPing(pong) => Ok(pong),
533         _ => Err(ApiError::ResponsePayloadType),
534     }
535 }
536 
537 pub fn vmm_shutdown(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<()> {
538     let (response_sender, response_receiver) = channel();
539 
540     // Send the VMM shutdown request.
541     api_sender
542         .send(ApiRequest::VmmShutdown(response_sender))
543         .map_err(ApiError::RequestSend)?;
544     api_evt.write(1).map_err(ApiError::EventFdWrite)?;
545 
546     response_receiver.recv().map_err(ApiError::ResponseRecv)??;
547 
548     Ok(())
549 }
550 
551 pub fn vm_resize(
552     api_evt: EventFd,
553     api_sender: Sender<ApiRequest>,
554     data: Arc<VmResizeData>,
555 ) -> ApiResult<Option<Body>> {
556     vm_action(api_evt, api_sender, VmAction::Resize(data))
557 }
558 
559 pub fn vm_resize_zone(
560     api_evt: EventFd,
561     api_sender: Sender<ApiRequest>,
562     data: Arc<VmResizeZoneData>,
563 ) -> ApiResult<Option<Body>> {
564     vm_action(api_evt, api_sender, VmAction::ResizeZone(data))
565 }
566 
567 pub fn vm_add_device(
568     api_evt: EventFd,
569     api_sender: Sender<ApiRequest>,
570     data: Arc<DeviceConfig>,
571 ) -> ApiResult<Option<Body>> {
572     vm_action(api_evt, api_sender, VmAction::AddDevice(data))
573 }
574 
575 pub fn vm_remove_device(
576     api_evt: EventFd,
577     api_sender: Sender<ApiRequest>,
578     data: Arc<VmRemoveDeviceData>,
579 ) -> ApiResult<Option<Body>> {
580     vm_action(api_evt, api_sender, VmAction::RemoveDevice(data))
581 }
582 
583 pub fn vm_add_disk(
584     api_evt: EventFd,
585     api_sender: Sender<ApiRequest>,
586     data: Arc<DiskConfig>,
587 ) -> ApiResult<Option<Body>> {
588     vm_action(api_evt, api_sender, VmAction::AddDisk(data))
589 }
590 
591 pub fn vm_add_fs(
592     api_evt: EventFd,
593     api_sender: Sender<ApiRequest>,
594     data: Arc<FsConfig>,
595 ) -> ApiResult<Option<Body>> {
596     vm_action(api_evt, api_sender, VmAction::AddFs(data))
597 }
598 
599 pub fn vm_add_pmem(
600     api_evt: EventFd,
601     api_sender: Sender<ApiRequest>,
602     data: Arc<PmemConfig>,
603 ) -> ApiResult<Option<Body>> {
604     vm_action(api_evt, api_sender, VmAction::AddPmem(data))
605 }
606 
607 pub fn vm_add_net(
608     api_evt: EventFd,
609     api_sender: Sender<ApiRequest>,
610     data: Arc<NetConfig>,
611 ) -> ApiResult<Option<Body>> {
612     vm_action(api_evt, api_sender, VmAction::AddNet(data))
613 }
614 
615 pub fn vm_add_vsock(
616     api_evt: EventFd,
617     api_sender: Sender<ApiRequest>,
618     data: Arc<VsockConfig>,
619 ) -> ApiResult<Option<Body>> {
620     vm_action(api_evt, api_sender, VmAction::AddVsock(data))
621 }
622