vmm: Only build a new VM when booting it

In order to support further use cases where a VM configuration could be
modified through the HTTP API, we only store the passed VM config when
being asked to create a VM. The actual creation will happen when booting
a new config for the first time.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2019-09-30 16:17:28 +02:00
parent 9a93f4f0a6
commit 7e0cb078ed
2 changed files with 55 additions and 10 deletions

View File

@ -52,6 +52,12 @@ pub enum ApiError {
/// The VM could not boot. /// The VM could not boot.
VmBoot(VmError), VmBoot(VmError),
/// The VM is already created.
VmAlreadyCreated,
/// The VM config is missing.
VmMissingConfig,
/// The VM could not shutdown. /// The VM could not shutdown.
VmShutdown(VmError), VmShutdown(VmError),

View File

@ -15,6 +15,7 @@ extern crate serde_json;
extern crate vmm_sys_util; extern crate vmm_sys_util;
use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload}; use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload};
use crate::config::VmConfig;
use crate::vm::{Error as VmError, ExitBehaviour, Vm}; use crate::vm::{Error as VmError, ExitBehaviour, Vm};
use libc::EFD_NONBLOCK; use libc::EFD_NONBLOCK;
use std::io; use std::io;
@ -219,6 +220,7 @@ pub struct Vmm {
reset_evt: EventFd, reset_evt: EventFd,
api_evt: EventFd, api_evt: EventFd,
vm: Option<Vm>, vm: Option<Vm>,
vm_config: Option<Arc<VmConfig>>,
} }
impl Vmm { impl Vmm {
@ -249,6 +251,7 @@ impl Vmm {
reset_evt, reset_evt,
api_evt, api_evt,
vm: None, vm: None,
vm_config: None,
}) })
} }
@ -340,21 +343,57 @@ impl Vmm {
match api_request { match api_request {
ApiRequest::VmCreate(config, sender) => { ApiRequest::VmCreate(config, sender) => {
let exit_evt = // We only store the passed VM config.
self.exit_evt.try_clone().map_err(Error::EventFdClone)?; // The VM will be created when being asked to boot it.
let reset_evt = let response = if self.vm_config.is_none() {
self.reset_evt.try_clone().map_err(Error::EventFdClone)?; self.vm_config = Some(config);
let response = match Vm::new(config, exit_evt, reset_evt) {
Ok(vm) => {
self.vm = Some(vm);
Ok(ApiResponsePayload::Empty) Ok(ApiResponsePayload::Empty)
} } else {
Err(e) => Err(ApiError::VmCreate(e)), Err(ApiError::VmAlreadyCreated)
}; };
sender.send(response).map_err(Error::ApiResponseSend)?; sender.send(response).map_err(Error::ApiResponseSend)?;
} }
ApiRequest::VmBoot(sender) => { ApiRequest::VmBoot(sender) => {
// If we don't have a config, we can not boot a VM.
if self.vm_config.is_none() {
sender
.send(Err(ApiError::VmMissingConfig))
.map_err(Error::ApiResponseSend)?;
continue;
}
// Create a new VM is we don't have one yet.
if self.vm.is_none() {
let exit_evt = self
.exit_evt
.try_clone()
.map_err(Error::EventFdClone)?;
let reset_evt = self
.reset_evt
.try_clone()
.map_err(Error::EventFdClone)?;
if let Some(ref vm_config) = self.vm_config {
match Vm::new(
Arc::clone(vm_config),
exit_evt,
reset_evt,
) {
Ok(vm) => {
self.vm = Some(vm);
}
Err(e) => {
sender
.send(Err(ApiError::VmCreate(e)))
.map_err(Error::ApiResponseSend)?;
continue;
}
}
}
}
// Now let's boot it.
if let Some(ref mut vm) = self.vm { if let Some(ref mut vm) = self.vm {
let response = match vm.boot() { let response = match vm.boot() {
Ok(_) => Ok(ApiResponsePayload::Empty), Ok(_) => Ok(ApiResponsePayload::Empty),