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
|
||||
|
||||
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
|
||||
doesn't support KVM will fail.
|
||||
`--emulated` flag. Without this flag, attempting to create a VM from a guest
|
||||
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
|
||||
bootable container.
|
||||
|
|
|
@ -58,11 +58,13 @@ fn generate(
|
|||
st(w, "memory", &[("unit", "b")], memory.as_str())?;
|
||||
|
||||
s(w, "os", &[("firmware", "efi")], |w| {
|
||||
let attrs = match ["x86", "x86_64"].contains(&env::consts::ARCH) {
|
||||
true => [("machine", "q35")].as_slice(),
|
||||
false => [].as_slice(), // use libvirt's default
|
||||
};
|
||||
st(w, "type", attrs, "hvm")?;
|
||||
let guest_arch = vm_image_info.arch.as_deref().unwrap_or(env::consts::ARCH);
|
||||
|
||||
let mut attrs = vec![("arch", guest_arch)];
|
||||
if ["x86", "x86_64"].contains(&guest_arch) {
|
||||
attrs.push(("machine", "q35"));
|
||||
}
|
||||
st(w, "type", &attrs, "hvm")?;
|
||||
|
||||
s(w, "firmware", &[], |w| {
|
||||
se(w, "feature", &[("enabled", "no"), ("name", "secure-boot")])
|
||||
|
|
|
@ -290,6 +290,7 @@ fn set_up_vm_image(
|
|||
// the image will be generated later
|
||||
return Ok(VmImageInfo {
|
||||
path: mirror_vm_image_path_in_container,
|
||||
arch: None,
|
||||
size: 0,
|
||||
format: "qcow2".to_string(),
|
||||
});
|
||||
|
@ -315,7 +316,7 @@ fn set_up_vm_image(
|
|||
fs::create_dir_all(&image_dir_path)?;
|
||||
|
||||
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 {
|
||||
|
@ -338,7 +339,8 @@ fn set_up_vm_image(
|
|||
|
||||
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;
|
||||
|
||||
Ok(vm_image_info)
|
||||
|
@ -366,7 +368,8 @@ fn set_up_vm_image(
|
|||
let 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;
|
||||
|
||||
if is_first_create {
|
||||
|
@ -375,6 +378,7 @@ fn set_up_vm_image(
|
|||
|
||||
Ok(VmImageInfo {
|
||||
path: Utf8Path::new("/").join(overlay_vm_image_path_in_container),
|
||||
arch: base_vm_image_info.arch,
|
||||
size: base_vm_image_info.size,
|
||||
format: "qcow2".to_string(),
|
||||
})
|
||||
|
|
70
src/util.rs
70
src/util.rs
|
@ -1,8 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::env;
|
||||
use std::ffi::{c_char, CString, OsStr};
|
||||
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::fs::{MetadataExt, PermissionsExt};
|
||||
use std::process::{Command, Stdio};
|
||||
|
@ -336,6 +337,9 @@ pub struct VmImageInfo {
|
|||
#[serde(skip)]
|
||||
pub path: Utf8PathBuf,
|
||||
|
||||
#[serde(skip)]
|
||||
pub arch: Option<String>,
|
||||
|
||||
#[serde(rename = "virtual-size")]
|
||||
pub size: u64,
|
||||
|
||||
|
@ -343,7 +347,7 @@ pub struct 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 output = Command::new("qemu-img")
|
||||
|
@ -362,6 +366,12 @@ impl VmImageInfo {
|
|||
let mut info: VmImageInfo = serde_json::from_slice(&output.stdout)?;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -393,6 +403,62 @@ pub fn create_overlay_vm_image(
|
|||
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`.
|
||||
///
|
||||
/// `crun` will inherit this process' standard streams.
|
||||
|
|
Loading…
Reference in New Issue