create: Auto-detect image architecture
When combined with --emulated, this enables running VMs with an architecture different from the host's. Closes #62. Signed-off-by: Alberto Faria <afaria@redhat.com>
This commit is contained in:
parent
8944e2df28
commit
dc4616c19d
|
@ -336,8 +336,9 @@ $ podman run \
|
||||||
### System emulation
|
### System emulation
|
||||||
|
|
||||||
To use system emulation instead of hardware-assisted virtualization, specify the
|
To use system emulation instead of hardware-assisted virtualization, specify the
|
||||||
`--emulated` flag. Without this flag, attempting to create a VM on a host tbat
|
`--emulated` flag. Without this flag, attempting to create a VM from a guest
|
||||||
doesn't support KVM will fail.
|
with a different architecture from the host's or on a host that doesn't support
|
||||||
|
KVM will fail.
|
||||||
|
|
||||||
It's not currently possible to use this flag when the container image is a bootc
|
It's not currently possible to use this flag when the container image is a bootc
|
||||||
bootable container.
|
bootable container.
|
||||||
|
|
|
@ -58,11 +58,13 @@ fn generate(
|
||||||
st(w, "memory", &[("unit", "b")], memory.as_str())?;
|
st(w, "memory", &[("unit", "b")], memory.as_str())?;
|
||||||
|
|
||||||
s(w, "os", &[("firmware", "efi")], |w| {
|
s(w, "os", &[("firmware", "efi")], |w| {
|
||||||
let attrs = match ["x86", "x86_64"].contains(&env::consts::ARCH) {
|
let guest_arch = vm_image_info.arch.as_deref().unwrap_or(env::consts::ARCH);
|
||||||
true => [("machine", "q35")].as_slice(),
|
|
||||||
false => [].as_slice(), // use libvirt's default
|
let mut attrs = vec![("arch", guest_arch)];
|
||||||
};
|
if ["x86", "x86_64"].contains(&guest_arch) {
|
||||||
st(w, "type", attrs, "hvm")?;
|
attrs.push(("machine", "q35"));
|
||||||
|
}
|
||||||
|
st(w, "type", &attrs, "hvm")?;
|
||||||
|
|
||||||
s(w, "firmware", &[], |w| {
|
s(w, "firmware", &[], |w| {
|
||||||
se(w, "feature", &[("enabled", "no"), ("name", "secure-boot")])
|
se(w, "feature", &[("enabled", "no"), ("name", "secure-boot")])
|
||||||
|
|
|
@ -290,6 +290,7 @@ fn set_up_vm_image(
|
||||||
// the image will be generated later
|
// the image will be generated later
|
||||||
return Ok(VmImageInfo {
|
return Ok(VmImageInfo {
|
||||||
path: mirror_vm_image_path_in_container,
|
path: mirror_vm_image_path_in_container,
|
||||||
|
arch: None,
|
||||||
size: 0,
|
size: 0,
|
||||||
format: "qcow2".to_string(),
|
format: "qcow2".to_string(),
|
||||||
});
|
});
|
||||||
|
@ -315,7 +316,7 @@ fn set_up_vm_image(
|
||||||
fs::create_dir_all(&image_dir_path)?;
|
fs::create_dir_all(&image_dir_path)?;
|
||||||
|
|
||||||
if !image_dir_path.join("image").try_exists()? {
|
if !image_dir_path.join("image").try_exists()? {
|
||||||
fs::hard_link(vm_image_path_in_host, image_dir_path.join("image"))?;
|
fs::hard_link(&vm_image_path_in_host, image_dir_path.join("image"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if custom_options.persistent {
|
if custom_options.persistent {
|
||||||
|
@ -338,7 +339,8 @@ fn set_up_vm_image(
|
||||||
|
|
||||||
bind_mount_file(&mirror_vm_image_path_in_host, &mirror_vm_image_path_in_host)?;
|
bind_mount_file(&mirror_vm_image_path_in_host, &mirror_vm_image_path_in_host)?;
|
||||||
|
|
||||||
let mut vm_image_info = VmImageInfo::of(&mirror_vm_image_path_in_host)?;
|
let mut vm_image_info =
|
||||||
|
VmImageInfo::of(&mirror_vm_image_path_in_host, custom_options.emulated)?;
|
||||||
vm_image_info.path = mirror_vm_image_path_in_container;
|
vm_image_info.path = mirror_vm_image_path_in_container;
|
||||||
|
|
||||||
Ok(vm_image_info)
|
Ok(vm_image_info)
|
||||||
|
@ -366,7 +368,8 @@ fn set_up_vm_image(
|
||||||
let overlay_vm_image_path_in_container =
|
let overlay_vm_image_path_in_container =
|
||||||
Utf8Path::new("/").join(overlay_vm_image_path_in_container);
|
Utf8Path::new("/").join(overlay_vm_image_path_in_container);
|
||||||
|
|
||||||
let mut base_vm_image_info = VmImageInfo::of(&mirror_vm_image_path_in_host)?;
|
let mut base_vm_image_info =
|
||||||
|
VmImageInfo::of(&vm_image_path_in_host, custom_options.emulated)?;
|
||||||
base_vm_image_info.path = mirror_vm_image_path_in_container;
|
base_vm_image_info.path = mirror_vm_image_path_in_container;
|
||||||
|
|
||||||
if is_first_create {
|
if is_first_create {
|
||||||
|
@ -375,6 +378,7 @@ fn set_up_vm_image(
|
||||||
|
|
||||||
Ok(VmImageInfo {
|
Ok(VmImageInfo {
|
||||||
path: Utf8Path::new("/").join(overlay_vm_image_path_in_container),
|
path: Utf8Path::new("/").join(overlay_vm_image_path_in_container),
|
||||||
|
arch: base_vm_image_info.arch,
|
||||||
size: base_vm_image_info.size,
|
size: base_vm_image_info.size,
|
||||||
format: "qcow2".to_string(),
|
format: "qcow2".to_string(),
|
||||||
})
|
})
|
||||||
|
|
70
src/util.rs
70
src/util.rs
|
@ -1,8 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::ffi::{c_char, CString, OsStr};
|
use std::ffi::{c_char, CString, OsStr};
|
||||||
use std::fs::{self, OpenOptions, Permissions};
|
use std::fs::{self, OpenOptions, Permissions};
|
||||||
use std::io::{self, ErrorKind};
|
use std::io::{self, ErrorKind, Write};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
@ -336,6 +337,9 @@ pub struct VmImageInfo {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub path: Utf8PathBuf,
|
pub path: Utf8PathBuf,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub arch: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "virtual-size")]
|
#[serde(rename = "virtual-size")]
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
|
|
||||||
|
@ -343,7 +347,7 @@ pub struct VmImageInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmImageInfo {
|
impl VmImageInfo {
|
||||||
pub fn of(vm_image_path: impl AsRef<Utf8Path>) -> Result<VmImageInfo> {
|
pub fn of(vm_image_path: impl AsRef<Utf8Path>, identify_arch: bool) -> Result<VmImageInfo> {
|
||||||
let vm_image_path = vm_image_path.as_ref().to_path_buf();
|
let vm_image_path = vm_image_path.as_ref().to_path_buf();
|
||||||
|
|
||||||
let output = Command::new("qemu-img")
|
let output = Command::new("qemu-img")
|
||||||
|
@ -362,6 +366,12 @@ impl VmImageInfo {
|
||||||
let mut info: VmImageInfo = serde_json::from_slice(&output.stdout)?;
|
let mut info: VmImageInfo = serde_json::from_slice(&output.stdout)?;
|
||||||
info.path = vm_image_path;
|
info.path = vm_image_path;
|
||||||
|
|
||||||
|
if identify_arch {
|
||||||
|
info.arch = identify_image_arch(&info.path)?
|
||||||
|
.ok_or_else(|| anyhow!("Could not identify VM image architecture"))?
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,6 +403,62 @@ pub fn create_overlay_vm_image(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn identify_image_arch(image_path: impl AsRef<Utf8Path>) -> Result<Option<String>> {
|
||||||
|
let xml = virt_inspector([
|
||||||
|
"--add",
|
||||||
|
image_path.as_ref().as_str(),
|
||||||
|
"--no-applications",
|
||||||
|
"--no-icon",
|
||||||
|
])?;
|
||||||
|
|
||||||
|
xpath(&xml, "string(//arch)")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virt_inspector(args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> Result<String> {
|
||||||
|
let cache_dir = format!("/var/tmp/crun-vm-{}", env::var("_CONTAINERS_ROOTLESS_UID")?);
|
||||||
|
fs::create_dir_all(&cache_dir)?;
|
||||||
|
|
||||||
|
let output = Command::new("virt-inspector")
|
||||||
|
.args(args)
|
||||||
|
.env("LIBGUESTFS_BACKEND", "direct")
|
||||||
|
.env("LIBGUESTFS_CACHEDIR", &cache_dir)
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
output.status.success(),
|
||||||
|
"virt-inspector failed: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(String::from_utf8(output.stdout)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xpath(xml: &str, path: &str) -> Result<Option<String>> {
|
||||||
|
let mut child = Command::new("virt-inspector")
|
||||||
|
.arg("--xpath")
|
||||||
|
.arg(path)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
child.stdin.take().unwrap().write_all(xml.as_bytes())?;
|
||||||
|
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
output.status.success(),
|
||||||
|
"virt-inspector --xpath failed: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr),
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = String::from_utf8(output.stdout)?;
|
||||||
|
|
||||||
|
if result.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Run `crun`.
|
/// Run `crun`.
|
||||||
///
|
///
|
||||||
/// `crun` will inherit this process' standard streams.
|
/// `crun` will inherit this process' standard streams.
|
||||||
|
|
Loading…
Reference in New Issue