mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
feat: implement kernel / initrd oci image support (#103)
* feat: implement kernel / initrd oci image support * fix: implement image urls more faithfully
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1451,6 +1451,7 @@ dependencies = [
|
||||
"krata",
|
||||
"krata-oci",
|
||||
"krata-runtime",
|
||||
"krata-tokio-tar",
|
||||
"log",
|
||||
"prost",
|
||||
"redb",
|
||||
|
@ -64,6 +64,10 @@ pub struct LauchCommand {
|
||||
help = "Wait for the guest to start, implied by --attach"
|
||||
)]
|
||||
wait: bool,
|
||||
#[arg(short = 'k', long, help = "OCI kernel image for guest to use")]
|
||||
kernel: Option<String>,
|
||||
#[arg(short = 'I', long, help = "OCI initrd image for guest to use")]
|
||||
initrd: Option<String>,
|
||||
#[arg(help = "Container image for guest to use")]
|
||||
oci: String,
|
||||
#[arg(
|
||||
@ -80,27 +84,41 @@ impl LauchCommand {
|
||||
mut client: ControlServiceClient<Channel>,
|
||||
events: EventStream,
|
||||
) -> Result<()> {
|
||||
let response = client
|
||||
.pull_image(PullImageRequest {
|
||||
image: self.oci.clone(),
|
||||
format: match self.image_format {
|
||||
LaunchImageFormat::Squashfs => OciImageFormat::Squashfs.into(),
|
||||
LaunchImageFormat::Erofs => OciImageFormat::Erofs.into(),
|
||||
let image = self
|
||||
.pull_image(
|
||||
&mut client,
|
||||
&self.oci,
|
||||
match self.image_format {
|
||||
LaunchImageFormat::Squashfs => OciImageFormat::Squashfs,
|
||||
LaunchImageFormat::Erofs => OciImageFormat::Erofs,
|
||||
},
|
||||
overwrite_cache: self.pull_overwrite_cache,
|
||||
})
|
||||
)
|
||||
.await?;
|
||||
let reply = pull_interactive_progress(response.into_inner()).await?;
|
||||
|
||||
let kernel = if let Some(ref kernel) = self.kernel {
|
||||
let kernel_image = self
|
||||
.pull_image(&mut client, kernel, OciImageFormat::Tar)
|
||||
.await?;
|
||||
Some(kernel_image)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let initrd = if let Some(ref initrd) = self.initrd {
|
||||
let kernel_image = self
|
||||
.pull_image(&mut client, initrd, OciImageFormat::Tar)
|
||||
.await?;
|
||||
Some(kernel_image)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let request = CreateGuestRequest {
|
||||
spec: Some(GuestSpec {
|
||||
name: self.name.unwrap_or_default(),
|
||||
image: Some(GuestImageSpec {
|
||||
image: Some(Image::Oci(GuestOciImageSpec {
|
||||
digest: reply.digest,
|
||||
format: reply.format,
|
||||
})),
|
||||
}),
|
||||
image: Some(image),
|
||||
kernel,
|
||||
initrd,
|
||||
vcpus: self.cpus,
|
||||
mem: self.mem,
|
||||
task: Some(GuestTaskSpec {
|
||||
@ -146,6 +164,28 @@ impl LauchCommand {
|
||||
StdioConsoleStream::restore_terminal_mode();
|
||||
std::process::exit(code.unwrap_or(0));
|
||||
}
|
||||
|
||||
async fn pull_image(
|
||||
&self,
|
||||
client: &mut ControlServiceClient<Channel>,
|
||||
image: &str,
|
||||
format: OciImageFormat,
|
||||
) -> Result<GuestImageSpec> {
|
||||
let response = client
|
||||
.pull_image(PullImageRequest {
|
||||
image: image.to_string(),
|
||||
format: format.into(),
|
||||
overwrite_cache: self.pull_overwrite_cache,
|
||||
})
|
||||
.await?;
|
||||
let reply = pull_interactive_progress(response.into_inner()).await?;
|
||||
Ok(GuestImageSpec {
|
||||
image: Some(Image::Oci(GuestOciImageSpec {
|
||||
digest: reply.digest,
|
||||
format: reply.format,
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_guest_started(id: &str, events: EventStream) -> Result<()> {
|
||||
|
@ -27,6 +27,7 @@ scopeguard = { workspace = true }
|
||||
signal-hook = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-stream = { workspace = true }
|
||||
krata-tokio-tar = { workspace = true }
|
||||
tonic = { workspace = true, features = ["tls"] }
|
||||
uuid = { workspace = true }
|
||||
|
||||
|
@ -378,7 +378,7 @@ impl ControlService for DaemonControlService {
|
||||
format: match packed.format {
|
||||
OciPackedFormat::Squashfs => OciImageFormat::Squashfs.into(),
|
||||
OciPackedFormat::Erofs => OciImageFormat::Erofs.into(),
|
||||
_ => OciImageFormat::Unknown.into(),
|
||||
OciPackedFormat::Tar => OciImageFormat::Tar.into(),
|
||||
},
|
||||
};
|
||||
yield reply;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{net::SocketAddr, path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use console::{DaemonConsole, DaemonConsoleHandle};
|
||||
use control::DaemonControlService;
|
||||
use db::GuestStore;
|
||||
@ -74,9 +74,11 @@ impl Daemon {
|
||||
generated
|
||||
};
|
||||
|
||||
let packer = OciPackerService::new(None, &image_cache_dir, OciPlatform::current()).await?;
|
||||
let initrd_path = detect_guest_file(&store, "initrd")?;
|
||||
let kernel_path = detect_guest_file(&store, "kernel")?;
|
||||
|
||||
let runtime = Runtime::new(store.clone()).await?;
|
||||
let packer = OciPackerService::new(None, &image_cache_dir, OciPlatform::current()).await?;
|
||||
let runtime = Runtime::new().await?;
|
||||
let glt = GuestLookupTable::new(0, host_uuid);
|
||||
let guests_db_path = format!("{}/guests.db", store);
|
||||
let guests = GuestStore::open(&PathBuf::from(guests_db_path))?;
|
||||
@ -97,6 +99,8 @@ impl Daemon {
|
||||
runtime_for_reconciler,
|
||||
packer.clone(),
|
||||
guest_reconciler_notify.clone(),
|
||||
kernel_path,
|
||||
initrd_path,
|
||||
)?;
|
||||
|
||||
let guest_reconciler_task = guest_reconciler.launch(guest_reconciler_receiver).await?;
|
||||
@ -181,3 +185,16 @@ impl Drop for Daemon {
|
||||
self.generator_task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_guest_file(store: &str, name: &str) -> Result<PathBuf> {
|
||||
let mut path = PathBuf::from(format!("{}/guest/{}", store, name));
|
||||
if path.is_file() {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
path = PathBuf::from(format!("/usr/share/krata/guest/{}", name));
|
||||
if path.is_file() {
|
||||
return Ok(path);
|
||||
}
|
||||
Err(anyhow!("unable to find required guest file: {}", name))
|
||||
}
|
||||
|
@ -1,20 +1,17 @@
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use krata::launchcfg::LaunchPackedFormat;
|
||||
use anyhow::Result;
|
||||
use krata::v1::{
|
||||
common::{
|
||||
guest_image_spec::Image, Guest, GuestErrorInfo, GuestExitInfo, GuestNetworkState,
|
||||
GuestState, GuestStatus, OciImageFormat,
|
||||
},
|
||||
common::{Guest, GuestErrorInfo, GuestExitInfo, GuestNetworkState, GuestState, GuestStatus},
|
||||
control::GuestChangedEvent,
|
||||
};
|
||||
use krataoci::packer::{service::OciPackerService, OciPackedFormat};
|
||||
use kratart::{launch::GuestLaunchRequest, GuestInfo, Runtime};
|
||||
use krataoci::packer::service::OciPackerService;
|
||||
use kratart::{GuestInfo, Runtime};
|
||||
use log::{error, info, trace, warn};
|
||||
use tokio::{
|
||||
select,
|
||||
@ -33,6 +30,10 @@ use crate::{
|
||||
glt::GuestLookupTable,
|
||||
};
|
||||
|
||||
use self::start::GuestStarter;
|
||||
|
||||
mod start;
|
||||
|
||||
const PARALLEL_LIMIT: u32 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -59,12 +60,15 @@ pub struct GuestReconciler {
|
||||
events: DaemonEventContext,
|
||||
runtime: Runtime,
|
||||
packer: OciPackerService,
|
||||
kernel_path: PathBuf,
|
||||
initrd_path: PathBuf,
|
||||
tasks: Arc<Mutex<HashMap<Uuid, GuestReconcilerEntry>>>,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
reconcile_lock: Arc<RwLock<()>>,
|
||||
}
|
||||
|
||||
impl GuestReconciler {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
glt: GuestLookupTable,
|
||||
guests: GuestStore,
|
||||
@ -72,6 +76,8 @@ impl GuestReconciler {
|
||||
runtime: Runtime,
|
||||
packer: OciPackerService,
|
||||
guest_reconciler_notify: Sender<Uuid>,
|
||||
kernel_path: PathBuf,
|
||||
initrd_path: PathBuf,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
glt,
|
||||
@ -79,6 +85,8 @@ impl GuestReconciler {
|
||||
events,
|
||||
runtime,
|
||||
packer,
|
||||
kernel_path,
|
||||
initrd_path,
|
||||
tasks: Arc::new(Mutex::new(HashMap::new())),
|
||||
guest_reconciler_notify,
|
||||
reconcile_lock: Arc::new(RwLock::with_max_readers((), PARALLEL_LIMIT)),
|
||||
@ -246,76 +254,14 @@ impl GuestReconciler {
|
||||
}
|
||||
|
||||
async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
let Some(ref spec) = guest.spec else {
|
||||
return Err(anyhow!("guest spec not specified"));
|
||||
let starter = GuestStarter {
|
||||
kernel_path: &self.kernel_path,
|
||||
initrd_path: &self.initrd_path,
|
||||
packer: &self.packer,
|
||||
glt: &self.glt,
|
||||
runtime: &self.runtime,
|
||||
};
|
||||
|
||||
let Some(ref image) = spec.image else {
|
||||
return Err(anyhow!("image spec not provided"));
|
||||
};
|
||||
let oci = match image.image {
|
||||
Some(Image::Oci(ref oci)) => oci,
|
||||
None => {
|
||||
return Err(anyhow!("oci spec not specified"));
|
||||
}
|
||||
};
|
||||
let task = spec.task.as_ref().cloned().unwrap_or_default();
|
||||
|
||||
let image = self
|
||||
.packer
|
||||
.recall(
|
||||
&oci.digest,
|
||||
match oci.format() {
|
||||
OciImageFormat::Unknown => OciPackedFormat::Squashfs,
|
||||
OciImageFormat::Squashfs => OciPackedFormat::Squashfs,
|
||||
OciImageFormat::Erofs => OciPackedFormat::Erofs,
|
||||
OciImageFormat::Tar => {
|
||||
return Err(anyhow!("tar image format is not supported for guests"));
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some(image) = image else {
|
||||
return Err(anyhow!(
|
||||
"image {} in the requested format did not exist",
|
||||
oci.digest
|
||||
));
|
||||
};
|
||||
|
||||
let info = self
|
||||
.runtime
|
||||
.launch(GuestLaunchRequest {
|
||||
format: LaunchPackedFormat::Squashfs,
|
||||
uuid: Some(uuid),
|
||||
name: if spec.name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(spec.name.clone())
|
||||
},
|
||||
image,
|
||||
vcpus: spec.vcpus,
|
||||
mem: spec.mem,
|
||||
env: task
|
||||
.environment
|
||||
.iter()
|
||||
.map(|x| (x.key.clone(), x.value.clone()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
run: empty_vec_optional(task.command.clone()),
|
||||
debug: false,
|
||||
})
|
||||
.await?;
|
||||
self.glt.associate(uuid, info.domid).await;
|
||||
info!("started guest {}", uuid);
|
||||
guest.state = Some(GuestState {
|
||||
status: GuestStatus::Started.into(),
|
||||
network: Some(guestinfo_to_networkstate(&info)),
|
||||
exit_info: None,
|
||||
error_info: None,
|
||||
host: self.glt.host_uuid().to_string(),
|
||||
domid: info.domid,
|
||||
});
|
||||
Ok(GuestReconcilerResult::Changed { rerun: false })
|
||||
starter.start(uuid, guest).await
|
||||
}
|
||||
|
||||
async fn exited(&self, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
@ -390,15 +336,7 @@ impl GuestReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_vec_optional<T>(value: Vec<T>) -> Option<Vec<T>> {
|
||||
if value.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn guestinfo_to_networkstate(info: &GuestInfo) -> GuestNetworkState {
|
||||
pub fn guestinfo_to_networkstate(info: &GuestInfo) -> GuestNetworkState {
|
||||
GuestNetworkState {
|
||||
guest_ipv4: info.guest_ipv4.map(|x| x.to_string()).unwrap_or_default(),
|
||||
guest_ipv6: info.guest_ipv6.map(|x| x.to_string()).unwrap_or_default(),
|
182
crates/daemon/src/reconcile/guest/start.rs
Normal file
182
crates/daemon/src/reconcile/guest/start.rs
Normal file
@ -0,0 +1,182 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::StreamExt;
|
||||
use krata::launchcfg::LaunchPackedFormat;
|
||||
use krata::v1::common::GuestOciImageSpec;
|
||||
use krata::v1::common::{guest_image_spec::Image, Guest, GuestState, GuestStatus, OciImageFormat};
|
||||
use krataoci::packer::{service::OciPackerService, OciPackedFormat};
|
||||
use kratart::{launch::GuestLaunchRequest, Runtime};
|
||||
use log::info;
|
||||
|
||||
use tokio::fs::{self, File};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio_tar::Archive;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
glt::GuestLookupTable,
|
||||
reconcile::guest::{guestinfo_to_networkstate, GuestReconcilerResult},
|
||||
};
|
||||
|
||||
// if a kernel is >= 100MB, that's kinda scary.
|
||||
const OCI_SPEC_TAR_FILE_MAX_SIZE: usize = 100 * 1024 * 1024;
|
||||
|
||||
pub struct GuestStarter<'a> {
|
||||
pub kernel_path: &'a Path,
|
||||
pub initrd_path: &'a Path,
|
||||
pub packer: &'a OciPackerService,
|
||||
pub glt: &'a GuestLookupTable,
|
||||
pub runtime: &'a Runtime,
|
||||
}
|
||||
|
||||
impl GuestStarter<'_> {
|
||||
pub async fn oci_spec_tar_read_file(
|
||||
&self,
|
||||
file: &Path,
|
||||
oci: &GuestOciImageSpec,
|
||||
) -> Result<Vec<u8>> {
|
||||
if oci.format() != OciImageFormat::Tar {
|
||||
return Err(anyhow!(
|
||||
"oci image spec for {} is required to be in tar format",
|
||||
oci.digest
|
||||
));
|
||||
}
|
||||
|
||||
let image = self
|
||||
.packer
|
||||
.recall(&oci.digest, OciPackedFormat::Tar)
|
||||
.await?;
|
||||
|
||||
let Some(image) = image else {
|
||||
return Err(anyhow!("image {} was not found in tar format", oci.digest));
|
||||
};
|
||||
|
||||
let mut archive = Archive::new(File::open(&image.path).await?);
|
||||
let mut entries = archive.entries()?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let mut entry = entry?;
|
||||
let path = entry.path()?;
|
||||
if entry.header().size()? as usize > OCI_SPEC_TAR_FILE_MAX_SIZE {
|
||||
return Err(anyhow!(
|
||||
"file {} in image {} is larger than the size limit",
|
||||
file.to_string_lossy(),
|
||||
oci.digest
|
||||
));
|
||||
}
|
||||
if path == file {
|
||||
let mut buffer = Vec::new();
|
||||
entry.read_to_end(&mut buffer).await?;
|
||||
return Ok(buffer);
|
||||
}
|
||||
}
|
||||
Err(anyhow!(
|
||||
"unable to find file {} in image {}",
|
||||
file.to_string_lossy(),
|
||||
oci.digest
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn start(&self, uuid: Uuid, guest: &mut Guest) -> Result<GuestReconcilerResult> {
|
||||
let Some(ref spec) = guest.spec else {
|
||||
return Err(anyhow!("guest spec not specified"));
|
||||
};
|
||||
|
||||
let Some(ref image) = spec.image else {
|
||||
return Err(anyhow!("image spec not provided"));
|
||||
};
|
||||
let oci = match image.image {
|
||||
Some(Image::Oci(ref oci)) => oci,
|
||||
None => {
|
||||
return Err(anyhow!("oci spec not specified"));
|
||||
}
|
||||
};
|
||||
let task = spec.task.as_ref().cloned().unwrap_or_default();
|
||||
|
||||
let image = self
|
||||
.packer
|
||||
.recall(
|
||||
&oci.digest,
|
||||
match oci.format() {
|
||||
OciImageFormat::Unknown => OciPackedFormat::Squashfs,
|
||||
OciImageFormat::Squashfs => OciPackedFormat::Squashfs,
|
||||
OciImageFormat::Erofs => OciPackedFormat::Erofs,
|
||||
OciImageFormat::Tar => {
|
||||
return Err(anyhow!("tar image format is not supported for guests"));
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some(image) = image else {
|
||||
return Err(anyhow!(
|
||||
"image {} in the requested format did not exist",
|
||||
oci.digest
|
||||
));
|
||||
};
|
||||
|
||||
let kernel = if let Some(ref spec) = spec.kernel {
|
||||
let Some(Image::Oci(ref oci)) = spec.image else {
|
||||
return Err(anyhow!("kernel image spec must be an oci image"));
|
||||
};
|
||||
self.oci_spec_tar_read_file(&PathBuf::from("kernel/image"), oci)
|
||||
.await?
|
||||
} else {
|
||||
fs::read(&self.kernel_path).await?
|
||||
};
|
||||
let initrd = if let Some(ref spec) = spec.initrd {
|
||||
let Some(Image::Oci(ref oci)) = spec.image else {
|
||||
return Err(anyhow!("initrd image spec must be an oci image"));
|
||||
};
|
||||
self.oci_spec_tar_read_file(&PathBuf::from("krata/initrd"), oci)
|
||||
.await?
|
||||
} else {
|
||||
fs::read(&self.initrd_path).await?
|
||||
};
|
||||
|
||||
let info = self
|
||||
.runtime
|
||||
.launch(GuestLaunchRequest {
|
||||
format: LaunchPackedFormat::Squashfs,
|
||||
uuid: Some(uuid),
|
||||
name: if spec.name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(spec.name.clone())
|
||||
},
|
||||
image,
|
||||
kernel,
|
||||
initrd,
|
||||
vcpus: spec.vcpus,
|
||||
mem: spec.mem,
|
||||
env: task
|
||||
.environment
|
||||
.iter()
|
||||
.map(|x| (x.key.clone(), x.value.clone()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
run: empty_vec_optional(task.command.clone()),
|
||||
debug: false,
|
||||
})
|
||||
.await?;
|
||||
self.glt.associate(uuid, info.domid).await;
|
||||
info!("started guest {}", uuid);
|
||||
guest.state = Some(GuestState {
|
||||
status: GuestStatus::Started.into(),
|
||||
network: Some(guestinfo_to_networkstate(&info)),
|
||||
exit_info: None,
|
||||
error_info: None,
|
||||
host: self.glt.host_uuid().to_string(),
|
||||
domid: info.domid,
|
||||
});
|
||||
Ok(GuestReconcilerResult::Changed { rerun: false })
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_vec_optional<T>(value: Vec<T>) -> Option<Vec<T>> {
|
||||
if value.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
@ -17,10 +17,14 @@ message Guest {
|
||||
message GuestSpec {
|
||||
string name = 1;
|
||||
GuestImageSpec image = 2;
|
||||
uint32 vcpus = 3;
|
||||
uint64 mem = 4;
|
||||
GuestTaskSpec task = 5;
|
||||
repeated GuestSpecAnnotation annotations = 6;
|
||||
// If not specified, defaults to the daemon default kernel.
|
||||
GuestImageSpec kernel = 3;
|
||||
// If not specified, defaults to the daemon default initrd.
|
||||
GuestImageSpec initrd = 4;
|
||||
uint32 vcpus = 5;
|
||||
uint64 mem = 6;
|
||||
GuestTaskSpec task = 7;
|
||||
repeated GuestSpecAnnotation annotations = 8;
|
||||
}
|
||||
|
||||
message GuestImageSpec {
|
||||
|
@ -217,6 +217,13 @@ impl OciImageFetcher {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref digest) = image.digest {
|
||||
if digest != manifest.digest() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
found = Some(manifest);
|
||||
break;
|
||||
}
|
||||
@ -240,7 +247,7 @@ impl OciImageFetcher {
|
||||
|
||||
let mut client = OciRegistryClient::new(image.registry_url()?, self.platform.clone())?;
|
||||
let (manifest, descriptor, digest) = client
|
||||
.get_manifest_with_digest(&image.name, &image.reference)
|
||||
.get_manifest_with_digest(&image.name, image.reference.as_ref(), image.digest.as_ref())
|
||||
.await?;
|
||||
let descriptor = descriptor.unwrap_or_else(|| {
|
||||
DescriptorBuilder::default()
|
||||
|
@ -2,33 +2,39 @@ use anyhow::Result;
|
||||
use std::fmt;
|
||||
use url::Url;
|
||||
|
||||
const DOCKER_HUB_MIRROR: &str = "mirror.gcr.io";
|
||||
const DEFAULT_IMAGE_TAG: &str = "latest";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ImageName {
|
||||
pub hostname: String,
|
||||
pub port: Option<u16>,
|
||||
pub name: String,
|
||||
pub reference: String,
|
||||
pub reference: Option<String>,
|
||||
pub digest: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ImageName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if DOCKER_HUB_MIRROR == self.hostname && self.port.is_none() {
|
||||
let mut suffix = String::new();
|
||||
|
||||
if let Some(ref reference) = self.reference {
|
||||
suffix.push(':');
|
||||
suffix.push_str(reference);
|
||||
}
|
||||
|
||||
if let Some(ref digest) = self.digest {
|
||||
suffix.push('@');
|
||||
suffix.push_str(digest);
|
||||
}
|
||||
|
||||
if ImageName::DOCKER_HUB_MIRROR == self.hostname && self.port.is_none() {
|
||||
if self.name.starts_with("library/") {
|
||||
write!(f, "{}:{}", &self.name[8..], self.reference)
|
||||
write!(f, "{}{}", &self.name[8..], suffix)
|
||||
} else {
|
||||
write!(f, "{}:{}", self.name, self.reference)
|
||||
write!(f, "{}{}", self.name, suffix)
|
||||
}
|
||||
} else if let Some(port) = self.port {
|
||||
write!(
|
||||
f,
|
||||
"{}:{}/{}:{}",
|
||||
self.hostname, port, self.name, self.reference
|
||||
)
|
||||
write!(f, "{}:{}/{}{}", self.hostname, port, self.name, suffix)
|
||||
} else {
|
||||
write!(f, "{}/{}:{}", self.hostname, self.name, self.reference)
|
||||
write!(f, "{}/{}{}", self.hostname, self.name, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,13 +47,21 @@ impl Default for ImageName {
|
||||
}
|
||||
|
||||
impl ImageName {
|
||||
pub const DOCKER_HUB_MIRROR: &'static str = "registry.docker.io";
|
||||
pub const DEFAULT_IMAGE_TAG: &'static str = "latest";
|
||||
|
||||
pub fn parse(name: &str) -> Result<Self> {
|
||||
let full_name = name.to_string();
|
||||
let name = full_name.clone();
|
||||
let (mut hostname, mut name) = name
|
||||
.split_once('/')
|
||||
.map(|x| (x.0.to_string(), x.1.to_string()))
|
||||
.unwrap_or_else(|| (DOCKER_HUB_MIRROR.to_string(), format!("library/{}", name)));
|
||||
.unwrap_or_else(|| {
|
||||
(
|
||||
ImageName::DOCKER_HUB_MIRROR.to_string(),
|
||||
format!("library/{}", name),
|
||||
)
|
||||
});
|
||||
|
||||
// heuristic to find any docker hub image formats
|
||||
// that may be in the hostname format. for example:
|
||||
@ -55,7 +69,7 @@ impl ImageName {
|
||||
// and neither will abc/hello/xyz:latest
|
||||
if !hostname.contains('.') && full_name.chars().filter(|x| *x == '/').count() == 1 {
|
||||
name = format!("{}/{}", hostname, name);
|
||||
hostname = DOCKER_HUB_MIRROR.to_string();
|
||||
hostname = ImageName::DOCKER_HUB_MIRROR.to_string();
|
||||
}
|
||||
|
||||
let (hostname, port) = if let Some((hostname, port)) = hostname
|
||||
@ -66,15 +80,54 @@ impl ImageName {
|
||||
} else {
|
||||
(hostname, None)
|
||||
};
|
||||
let (name, reference) = name
|
||||
.split_once(':')
|
||||
.map(|x| (x.0.to_string(), x.1.to_string()))
|
||||
.unwrap_or((name.to_string(), DEFAULT_IMAGE_TAG.to_string()));
|
||||
|
||||
let name_has_digest = if name.contains('@') {
|
||||
let digest_start = name.chars().position(|c| c == '@');
|
||||
let ref_start = name.chars().position(|c| c == ':');
|
||||
if let (Some(digest_start), Some(ref_start)) = (digest_start, ref_start) {
|
||||
digest_start < ref_start
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let (name, digest) = if name_has_digest {
|
||||
name.split_once('@')
|
||||
.map(|(name, digest)| (name.to_string(), Some(digest.to_string())))
|
||||
.unwrap_or_else(|| (name, None))
|
||||
} else {
|
||||
(name, None)
|
||||
};
|
||||
|
||||
let (name, reference) = if name.contains(':') {
|
||||
name.split_once(':')
|
||||
.map(|(name, reference)| (name.to_string(), Some(reference.to_string())))
|
||||
.unwrap_or((name, None))
|
||||
} else {
|
||||
(name, None)
|
||||
};
|
||||
|
||||
let (reference, digest) = if let Some(reference) = reference {
|
||||
if let Some(digest) = digest {
|
||||
(Some(reference), Some(digest))
|
||||
} else {
|
||||
reference
|
||||
.split_once('@')
|
||||
.map(|(reff, digest)| (Some(reff.to_string()), Some(digest.to_string())))
|
||||
.unwrap_or_else(|| (Some(reference), None))
|
||||
}
|
||||
} else {
|
||||
(None, digest)
|
||||
};
|
||||
|
||||
Ok(ImageName {
|
||||
hostname,
|
||||
port,
|
||||
name,
|
||||
reference,
|
||||
digest,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,9 @@ impl OciPackerCache {
|
||||
let image_name = packed.name.to_string();
|
||||
annotations.insert(ANNOTATION_IMAGE_NAME.to_string(), image_name);
|
||||
let image_ref = packed.name.reference.clone();
|
||||
if let Some(image_ref) = image_ref {
|
||||
annotations.insert(ANNOTATION_REF_NAME.to_string(), image_ref);
|
||||
}
|
||||
descriptor.set_annotations(Some(annotations));
|
||||
manifests.push(descriptor.clone());
|
||||
index.set_manifests(manifests);
|
||||
|
@ -61,6 +61,10 @@ impl OciPackerService {
|
||||
digest: &str,
|
||||
format: OciPackedFormat,
|
||||
) -> Result<Option<OciPackedImage>> {
|
||||
if digest.contains('/') || digest.contains('\\') || digest.contains("..") {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
self.cache
|
||||
.recall(ImageName::parse("cached:latest")?, digest, format)
|
||||
.await
|
||||
|
@ -7,7 +7,7 @@ use reqwest::{Client, RequestBuilder, Response, StatusCode};
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
use url::Url;
|
||||
|
||||
use crate::{progress::OciBoundProgress, schema::OciSchema};
|
||||
use crate::{name::ImageName, progress::OciBoundProgress, schema::OciSchema};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OciPlatform {
|
||||
@ -176,7 +176,7 @@ impl OciRegistryClient {
|
||||
let url = self.url.join(&format!(
|
||||
"/v2/{}/manifests/{}",
|
||||
name.as_ref(),
|
||||
reference.as_ref()
|
||||
reference.as_ref(),
|
||||
))?;
|
||||
let accept = format!(
|
||||
"{}, {}, {}, {}",
|
||||
@ -202,13 +202,20 @@ impl OciRegistryClient {
|
||||
pub async fn get_manifest_with_digest<N: AsRef<str>, R: AsRef<str>>(
|
||||
&mut self,
|
||||
name: N,
|
||||
reference: R,
|
||||
reference: Option<R>,
|
||||
digest: Option<N>,
|
||||
) -> Result<(OciSchema<ImageManifest>, Option<Descriptor>, String)> {
|
||||
let url = self.url.join(&format!(
|
||||
"/v2/{}/manifests/{}",
|
||||
name.as_ref(),
|
||||
reference.as_ref()
|
||||
))?;
|
||||
let what = digest
|
||||
.as_ref()
|
||||
.map(|x| x.as_ref().to_string())
|
||||
.unwrap_or_else(|| {
|
||||
reference
|
||||
.map(|x| x.as_ref().to_string())
|
||||
.unwrap_or_else(|| ImageName::DEFAULT_IMAGE_TAG.to_string())
|
||||
});
|
||||
let url = self
|
||||
.url
|
||||
.join(&format!("/v2/{}/manifests/{}", name.as_ref(), what,))?;
|
||||
let accept = format!(
|
||||
"{}, {}, {}, {}",
|
||||
MediaType::ImageManifest.to_docker_v2s2()?,
|
||||
@ -239,9 +246,10 @@ impl OciRegistryClient {
|
||||
let digest = response
|
||||
.headers()
|
||||
.get("Docker-Content-Digest")
|
||||
.ok_or_else(|| anyhow!("fetching manifest did not yield a content digest"))?
|
||||
.to_str()?
|
||||
.to_string();
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.map(|x| x.to_string())
|
||||
.or_else(|| digest.map(|x: N| x.as_ref().to_string()))
|
||||
.ok_or_else(|| anyhow!("fetching manifest did not yield a content digest"))?;
|
||||
let bytes = response.bytes().await?;
|
||||
let manifest = serde_json::from_slice(&bytes)?;
|
||||
Ok((OciSchema::new(bytes.to_vec(), manifest), None, digest))
|
||||
|
@ -23,6 +23,8 @@ use super::{GuestInfo, GuestState};
|
||||
|
||||
pub struct GuestLaunchRequest {
|
||||
pub format: LaunchPackedFormat,
|
||||
pub kernel: Vec<u8>,
|
||||
pub initrd: Vec<u8>,
|
||||
pub uuid: Option<Uuid>,
|
||||
pub name: Option<String>,
|
||||
pub vcpus: u32,
|
||||
@ -173,22 +175,22 @@ impl GuestLauncher {
|
||||
|
||||
let config = DomainConfig {
|
||||
backend_domid: 0,
|
||||
name: &xen_name,
|
||||
name: xen_name,
|
||||
max_vcpus: request.vcpus,
|
||||
mem_mb: request.mem,
|
||||
kernel_path: &context.kernel,
|
||||
initrd_path: &context.initrd,
|
||||
cmdline: &cmdline,
|
||||
use_console_backend: Some("krata-console"),
|
||||
kernel: request.kernel,
|
||||
initrd: request.initrd,
|
||||
cmdline,
|
||||
use_console_backend: Some("krata-console".to_string()),
|
||||
disks: vec![
|
||||
DomainDisk {
|
||||
vdev: "xvda",
|
||||
block: &image_squashfs_loop,
|
||||
vdev: "xvda".to_string(),
|
||||
block: image_squashfs_loop.clone(),
|
||||
writable: false,
|
||||
},
|
||||
DomainDisk {
|
||||
vdev: "xvdb",
|
||||
block: &cfgblk_squashfs_loop,
|
||||
vdev: "xvdb".to_string(),
|
||||
block: cfgblk_squashfs_loop.clone(),
|
||||
writable: false,
|
||||
},
|
||||
],
|
||||
@ -197,7 +199,7 @@ impl GuestLauncher {
|
||||
initialized: false,
|
||||
}],
|
||||
vifs: vec![DomainNetworkInterface {
|
||||
mac: &guest_mac_string,
|
||||
mac: guest_mac_string.clone(),
|
||||
mtu: 1500,
|
||||
bridge: None,
|
||||
script: None,
|
||||
|
@ -1,9 +1,4 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{fs, path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use ipnetwork::IpNetwork;
|
||||
@ -52,43 +47,17 @@ pub struct GuestInfo {
|
||||
pub struct RuntimeContext {
|
||||
pub autoloop: AutoLoop,
|
||||
pub xen: XenClient,
|
||||
pub kernel: String,
|
||||
pub initrd: String,
|
||||
}
|
||||
|
||||
impl RuntimeContext {
|
||||
pub async fn new(store: String) -> Result<Self> {
|
||||
let mut image_cache_path = PathBuf::from(&store);
|
||||
image_cache_path.push("cache");
|
||||
fs::create_dir_all(&image_cache_path)?;
|
||||
|
||||
pub async fn new() -> Result<Self> {
|
||||
let xen = XenClient::open(0).await?;
|
||||
image_cache_path.push("image");
|
||||
fs::create_dir_all(&image_cache_path)?;
|
||||
let kernel = RuntimeContext::detect_guest_file(&store, "kernel")?;
|
||||
let initrd = RuntimeContext::detect_guest_file(&store, "initrd")?;
|
||||
|
||||
Ok(RuntimeContext {
|
||||
autoloop: AutoLoop::new(LoopControl::open()?),
|
||||
xen,
|
||||
kernel,
|
||||
initrd,
|
||||
})
|
||||
}
|
||||
|
||||
fn detect_guest_file(store: &str, name: &str) -> Result<String> {
|
||||
let mut path = PathBuf::from(format!("{}/guest/{}", store, name));
|
||||
if path.is_file() {
|
||||
return path_as_string(&path);
|
||||
}
|
||||
|
||||
path = PathBuf::from(format!("/usr/share/krata/guest/{}", name));
|
||||
if path.is_file() {
|
||||
return path_as_string(&path);
|
||||
}
|
||||
Err(anyhow!("unable to find required guest file: {}", name))
|
||||
}
|
||||
|
||||
pub async fn list(&self) -> Result<Vec<GuestInfo>> {
|
||||
let mut guests: Vec<GuestInfo> = Vec::new();
|
||||
for domid_candidate in self.xen.store.list("/local/domain").await? {
|
||||
@ -248,16 +217,14 @@ impl RuntimeContext {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Runtime {
|
||||
store: Arc<String>,
|
||||
context: RuntimeContext,
|
||||
launch_semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub async fn new(store: String) -> Result<Self> {
|
||||
let context = RuntimeContext::new(store.clone()).await?;
|
||||
pub async fn new() -> Result<Self> {
|
||||
let context = RuntimeContext::new().await?;
|
||||
Ok(Self {
|
||||
store: Arc::new(store),
|
||||
context,
|
||||
launch_semaphore: Arc::new(Semaphore::new(1)),
|
||||
})
|
||||
@ -320,12 +287,6 @@ impl Runtime {
|
||||
}
|
||||
|
||||
pub async fn dupe(&self) -> Result<Runtime> {
|
||||
Runtime::new((*self.store).clone()).await
|
||||
Runtime::new().await
|
||||
}
|
||||
}
|
||||
|
||||
fn path_as_string(path: &Path) -> Result<String> {
|
||||
path.to_str()
|
||||
.ok_or_else(|| anyhow!("unable to convert path to string"))
|
||||
.map(|x| x.to_string())
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{env, process};
|
||||
use tokio::fs;
|
||||
use xenclient::error::Result;
|
||||
use xenclient::{DomainConfig, XenClient};
|
||||
|
||||
@ -16,12 +17,12 @@ async fn main() -> Result<()> {
|
||||
let client = XenClient::open(0).await?;
|
||||
let config = DomainConfig {
|
||||
backend_domid: 0,
|
||||
name: "xenclient-test",
|
||||
name: "xenclient-test".to_string(),
|
||||
max_vcpus: 1,
|
||||
mem_mb: 512,
|
||||
kernel_path: kernel_image_path.as_str(),
|
||||
initrd_path: initrd_path.as_str(),
|
||||
cmdline: "debug elevator=noop",
|
||||
kernel: fs::read(&kernel_image_path).await?,
|
||||
initrd: fs::read(&initrd_path).await?,
|
||||
cmdline: "debug elevator=noop".to_string(),
|
||||
use_console_backend: None,
|
||||
disks: vec![],
|
||||
channels: vec![],
|
||||
|
@ -107,17 +107,15 @@ impl ElfImageLoader {
|
||||
ElfImageLoader::load_xz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_kernel(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
|
||||
for start in find_iter(file.as_slice(), &[0x1f, 0x8b]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_gz(&file[start..]) {
|
||||
pub fn load_file_kernel(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
for start in find_iter(data, &[0x1f, 0x8b]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_xz(&file[start..]) {
|
||||
for start in find_iter(data, &[0xfd, 0x37, 0x7a, 0x58]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ use boot::BootState;
|
||||
use log::{debug, trace, warn};
|
||||
use tokio::time::timeout;
|
||||
|
||||
use std::fs::read;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
@ -40,60 +39,60 @@ pub struct XenClient {
|
||||
call: XenCall,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockDeviceRef {
|
||||
pub path: String,
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainDisk<'a> {
|
||||
pub vdev: &'a str,
|
||||
pub block: &'a BlockDeviceRef,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainDisk {
|
||||
pub vdev: String,
|
||||
pub block: BlockDeviceRef,
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainFilesystem<'a> {
|
||||
pub path: &'a str,
|
||||
pub tag: &'a str,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainFilesystem {
|
||||
pub path: String,
|
||||
pub tag: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainNetworkInterface<'a> {
|
||||
pub mac: &'a str,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainNetworkInterface {
|
||||
pub mac: String,
|
||||
pub mtu: u32,
|
||||
pub bridge: Option<&'a str>,
|
||||
pub script: Option<&'a str>,
|
||||
pub bridge: Option<String>,
|
||||
pub script: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainChannel {
|
||||
pub typ: String,
|
||||
pub initialized: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainEventChannel<'a> {
|
||||
pub name: &'a str,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainEventChannel {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainConfig<'a> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DomainConfig {
|
||||
pub backend_domid: u32,
|
||||
pub name: &'a str,
|
||||
pub name: String,
|
||||
pub max_vcpus: u32,
|
||||
pub mem_mb: u64,
|
||||
pub kernel_path: &'a str,
|
||||
pub initrd_path: &'a str,
|
||||
pub cmdline: &'a str,
|
||||
pub disks: Vec<DomainDisk<'a>>,
|
||||
pub use_console_backend: Option<&'a str>,
|
||||
pub kernel: Vec<u8>,
|
||||
pub initrd: Vec<u8>,
|
||||
pub cmdline: String,
|
||||
pub disks: Vec<DomainDisk>,
|
||||
pub use_console_backend: Option<String>,
|
||||
pub channels: Vec<DomainChannel>,
|
||||
pub vifs: Vec<DomainNetworkInterface<'a>>,
|
||||
pub filesystems: Vec<DomainFilesystem<'a>>,
|
||||
pub event_channels: Vec<DomainEventChannel<'a>>,
|
||||
pub vifs: Vec<DomainNetworkInterface>,
|
||||
pub filesystems: Vec<DomainFilesystem>,
|
||||
pub event_channels: Vec<DomainEventChannel>,
|
||||
pub extra_keys: Vec<(String, String)>,
|
||||
pub extra_rw_paths: Vec<String>,
|
||||
}
|
||||
@ -117,7 +116,7 @@ impl XenClient {
|
||||
Ok(XenClient { store, call })
|
||||
}
|
||||
|
||||
pub async fn create(&self, config: &DomainConfig<'_>) -> Result<CreatedDomain> {
|
||||
pub async fn create(&self, config: &DomainConfig) -> Result<CreatedDomain> {
|
||||
let mut domain = CreateDomain {
|
||||
max_vcpus: config.max_vcpus,
|
||||
..Default::default()
|
||||
@ -143,7 +142,7 @@ impl XenClient {
|
||||
&self,
|
||||
domid: u32,
|
||||
domain: &CreateDomain,
|
||||
config: &DomainConfig<'_>,
|
||||
config: &DomainConfig,
|
||||
) -> Result<CreatedDomain> {
|
||||
trace!(
|
||||
"XenClient init domid={} domain={:?} config={:?}",
|
||||
@ -237,9 +236,9 @@ impl XenClient {
|
||||
&Uuid::from_bytes(domain.handle).to_string(),
|
||||
)
|
||||
.await?;
|
||||
tx.write_string(format!("{}/name", dom_path).as_str(), config.name)
|
||||
tx.write_string(format!("{}/name", dom_path).as_str(), &config.name)
|
||||
.await?;
|
||||
tx.write_string(format!("{}/name", vm_path).as_str(), config.name)
|
||||
tx.write_string(format!("{}/name", vm_path).as_str(), &config.name)
|
||||
.await?;
|
||||
|
||||
for (key, value) in &config.extra_keys {
|
||||
@ -257,7 +256,7 @@ impl XenClient {
|
||||
|
||||
self.call.set_max_vcpus(domid, config.max_vcpus).await?;
|
||||
self.call.set_max_mem(domid, config.mem_mb * 1024).await?;
|
||||
let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?;
|
||||
let image_loader = ElfImageLoader::load_file_kernel(&config.kernel)?;
|
||||
|
||||
let xenstore_evtchn: u32;
|
||||
let xenstore_mfn: u64;
|
||||
@ -270,18 +269,17 @@ impl XenClient {
|
||||
let mut arch = Box::new(X86BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let mut arch = Box::new(Arm64BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
|
||||
let initrd = read(config.initrd_path)?;
|
||||
state = boot
|
||||
.initialize(
|
||||
&mut arch,
|
||||
&image_loader,
|
||||
initrd.as_slice(),
|
||||
&config.initrd,
|
||||
config.max_vcpus,
|
||||
config.mem_mb,
|
||||
1,
|
||||
)
|
||||
.await?;
|
||||
boot.boot(&mut arch, &mut state, config.cmdline).await?;
|
||||
boot.boot(&mut arch, &mut state, &config.cmdline).await?;
|
||||
xenstore_evtchn = state.store_evtchn;
|
||||
xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
p2m = boot.phys.p2m;
|
||||
@ -291,19 +289,9 @@ impl XenClient {
|
||||
let tx = self.store.transaction().await?;
|
||||
tx.write_string(format!("{}/image/os_type", vm_path).as_str(), "linux")
|
||||
.await?;
|
||||
tx.write_string(
|
||||
format!("{}/image/kernel", vm_path).as_str(),
|
||||
config.kernel_path,
|
||||
)
|
||||
.await?;
|
||||
tx.write_string(
|
||||
format!("{}/image/ramdisk", vm_path).as_str(),
|
||||
config.initrd_path,
|
||||
)
|
||||
.await?;
|
||||
tx.write_string(
|
||||
format!("{}/image/cmdline", vm_path).as_str(),
|
||||
config.cmdline,
|
||||
&config.cmdline,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -352,7 +340,8 @@ impl XenClient {
|
||||
&DomainChannel {
|
||||
typ: config
|
||||
.use_console_backend
|
||||
.unwrap_or("xenconsoled")
|
||||
.clone()
|
||||
.unwrap_or("xenconsoled".to_string())
|
||||
.to_string(),
|
||||
initialized: true,
|
||||
},
|
||||
@ -429,7 +418,7 @@ impl XenClient {
|
||||
.await?;
|
||||
let channel_path = format!("{}/evtchn/{}", dom_path, channel.name);
|
||||
self.store
|
||||
.write_string(&format!("{}/name", channel_path), channel.name)
|
||||
.write_string(&format!("{}/name", channel_path), &channel.name)
|
||||
.await?;
|
||||
self.store
|
||||
.write_string(&format!("{}/channel", channel_path), &id.to_string())
|
||||
@ -447,7 +436,7 @@ impl XenClient {
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
index: usize,
|
||||
disk: &DomainDisk<'_>,
|
||||
disk: &DomainDisk,
|
||||
) -> Result<()> {
|
||||
let id = (202 << 8) | (index << 4) as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
@ -567,7 +556,7 @@ impl XenClient {
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
index: usize,
|
||||
filesystem: &DomainFilesystem<'_>,
|
||||
filesystem: &DomainFilesystem,
|
||||
) -> Result<()> {
|
||||
let id = 90 + index as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
@ -605,7 +594,7 @@ impl XenClient {
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
index: usize,
|
||||
vif: &DomainNetworkInterface<'_>,
|
||||
vif: &DomainNetworkInterface,
|
||||
) -> Result<()> {
|
||||
let id = 20 + index as u64;
|
||||
let mut backend_items: Vec<(&str, String)> = vec![
|
||||
@ -619,12 +608,12 @@ impl XenClient {
|
||||
];
|
||||
|
||||
if vif.bridge.is_some() {
|
||||
backend_items.extend_from_slice(&[("bridge", vif.bridge.unwrap().to_string())]);
|
||||
backend_items.extend_from_slice(&[("bridge", vif.bridge.clone().unwrap())]);
|
||||
}
|
||||
|
||||
if vif.script.is_some() {
|
||||
backend_items.extend_from_slice(&[
|
||||
("script", vif.script.unwrap().to_string()),
|
||||
("script", vif.script.clone().unwrap()),
|
||||
("hotplug-status", "".to_string()),
|
||||
]);
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user