hypha: implement subcommands and introduce destroy that tears things down properly

This commit is contained in:
Alex Zenla
2024-01-21 01:58:07 -08:00
parent ba156e43da
commit ece88e16cc
8 changed files with 228 additions and 46 deletions

View File

@ -25,6 +25,9 @@ url = "2.5.0"
ureq = "2.9.1"
path-clean = "1.0.1"
[dependencies.xenstore]
path = "../xenstore"
[dependencies.clap]
version = "4.4.18"
features = ["derive"]

View File

@ -1,4 +1,4 @@
use clap::Parser;
use clap::{Parser, Subcommand};
use hypha::ctl::Controller;
use hypha::error::{HyphaError, Result};
use std::path::PathBuf;
@ -12,17 +12,27 @@ struct ControllerArgs {
#[arg(short = 'r', long)]
initrd: String,
#[arg(short, long)]
image: String,
#[arg(short, long, default_value_t = 1)]
cpus: u32,
#[arg(short, long, default_value_t = 512)]
mem: u64,
#[arg(short, long, default_value = "auto")]
store: String,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Launch {
#[arg(short, long)]
image: String,
#[arg(short, long, default_value_t = 1)]
cpus: u32,
#[arg(short, long, default_value_t = 512)]
mem: u64,
},
Destroy {
#[arg(short, long)]
domain: u32,
},
}
fn main() -> Result<()> {
@ -41,16 +51,17 @@ fn main() -> Result<()> {
.map(|x| x.to_string())
.ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
let mut controller = Controller::new(
store_path,
args.kernel,
args.initrd,
args.image,
args.cpus,
args.mem,
)?;
let domid = controller.launch()?;
println!("launched domain: {}", domid);
let mut controller = Controller::new(store_path, args.kernel, args.initrd)?;
match args.command {
Commands::Launch { image, cpus, mem } => {
let domid = controller.launch(image.as_str(), cpus, mem)?;
println!("launched domain: {}", domid);
}
Commands::Destroy { domain } => {
controller.destroy(domain)?;
}
}
Ok(())
}

View File

@ -2,7 +2,7 @@ pub mod cfgblk;
use crate::autoloop::AutoLoop;
use crate::ctl::cfgblk::ConfigBlock;
use crate::error::Result;
use crate::error::{HyphaError, Result};
use crate::image::cache::ImageCache;
use crate::image::name::ImageName;
use crate::image::{ImageCompiler, ImageInfo};
@ -11,27 +11,18 @@ use std::fs;
use std::path::PathBuf;
use uuid::Uuid;
use xenclient::{DomainConfig, DomainDisk, XenClient};
use xenstore::client::{XsdClient, XsdInterface};
pub struct Controller {
image_cache: ImageCache,
autoloop: AutoLoop,
image: String,
client: XenClient,
kernel_path: String,
initrd_path: String,
vcpus: u32,
mem: u64,
}
impl Controller {
pub fn new(
store_path: String,
kernel_path: String,
initrd_path: String,
image: String,
vcpus: u32,
mem: u64,
) -> Result<Controller> {
pub fn new(store_path: String, kernel_path: String, initrd_path: String) -> Result<Controller> {
let mut image_cache_path = PathBuf::from(store_path);
image_cache_path.push("cache");
fs::create_dir_all(&image_cache_path)?;
@ -43,25 +34,22 @@ impl Controller {
Ok(Controller {
image_cache,
autoloop: AutoLoop::new(LoopControl::open()?),
image,
client,
kernel_path,
initrd_path,
vcpus,
mem,
})
}
fn compile(&mut self) -> Result<ImageInfo> {
let image = ImageName::parse(&self.image)?;
fn compile(&mut self, image: &str) -> Result<ImageInfo> {
let image = ImageName::parse(image)?;
let compiler = ImageCompiler::new(&self.image_cache)?;
compiler.compile(&image)
}
pub fn launch(&mut self) -> Result<u32> {
pub fn launch(&mut self, image: &str, vcpus: u32, mem: u64) -> Result<u32> {
let uuid = Uuid::new_v4();
let name = format!("hypha-{uuid}");
let image_info = self.compile()?;
let image_info = self.compile(image)?;
let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
cfgblk.build()?;
@ -74,8 +62,8 @@ impl Controller {
let config = DomainConfig {
backend_domid: 0,
name: &name,
max_vcpus: self.vcpus,
mem_mb: self.mem,
max_vcpus: vcpus,
mem_mb: mem,
kernel_path: self.kernel_path.as_str(),
initrd_path: self.initrd_path.as_str(),
cmdline: "quiet elevator=noop",
@ -91,6 +79,16 @@ impl Controller {
writable: false,
},
],
extra_keys: vec![
("hypha/uuid".to_string(), uuid.to_string()),
(
"hypha/loops".to_string(),
format!(
"{},{}",
&image_squashfs_loop.path, &cfgblk_squashfs_loop.path
),
),
],
};
match self.client.create(&config) {
Ok(domid) => Ok(domid),
@ -102,4 +100,34 @@ impl Controller {
}
}
}
pub fn destroy(&mut self, domid: u32) -> Result<Uuid> {
let mut store = XsdClient::open()?;
let dom_path = store.get_domain_path(domid)?;
let uuid = match store.read_string_optional(format!("{}/hypha/uuid", dom_path).as_str())? {
None => {
return Err(HyphaError::new(&format!(
"domain {} was not found or not created by hypha",
domid
)))
}
Some(value) => value,
};
if uuid.is_empty() {
return Err(HyphaError::new(
"unable to find hypha uuid based on the domain",
));
}
let uuid = Uuid::parse_str(&uuid)?;
let loops = store.read_string(format!("{}/hypha/loops", dom_path).as_str())?;
let loops = loops
.split(',')
.map(|x| x.to_string())
.collect::<Vec<String>>();
self.client.destroy(domid)?;
for lop in &loops {
self.autoloop.unloop(lop)?;
}
Ok(uuid)
}
}

View File

@ -5,6 +5,7 @@ use std::fmt::{Display, Formatter};
use std::num::ParseIntError;
use std::path::StripPrefixError;
use xenclient::XenClientError;
use xenstore::bus::XsdBusError;
pub type Result<T> = std::result::Result<T, HyphaError>;
@ -98,3 +99,15 @@ impl From<std::fmt::Error> for HyphaError {
HyphaError::new(value.to_string().as_str())
}
}
impl From<uuid::Error> for HyphaError {
fn from(value: uuid::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<XsdBusError> for HyphaError {
fn from(value: XsdBusError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}