mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-03 23:29:39 +00:00 
			
		
		
		
	hypha: implement list command
This commit is contained in:
		@ -25,6 +25,7 @@ url = "2.5.0"
 | 
				
			|||||||
ureq = "2.9.1"
 | 
					ureq = "2.9.1"
 | 
				
			||||||
path-clean = "1.0.1"
 | 
					path-clean = "1.0.1"
 | 
				
			||||||
termion = "3.0.0"
 | 
					termion = "3.0.0"
 | 
				
			||||||
 | 
					cli-tables = "0.2.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies.xenstore]
 | 
					[dependencies.xenstore]
 | 
				
			||||||
path = "../xenstore"
 | 
					path = "../xenstore"
 | 
				
			||||||
 | 
				
			|||||||
@ -15,10 +15,12 @@ struct ControllerArgs {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Subcommand, Debug)]
 | 
					#[derive(Subcommand, Debug)]
 | 
				
			||||||
enum Commands {
 | 
					enum Commands {
 | 
				
			||||||
 | 
					    List {},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Launch {
 | 
					    Launch {
 | 
				
			||||||
        #[arg(short, long)]
 | 
					        #[arg(short, long, default_value = "auto")]
 | 
				
			||||||
        kernel: String,
 | 
					        kernel: String,
 | 
				
			||||||
        #[arg(short = 'r', long)]
 | 
					        #[arg(short = 'r', long, default_value = "auto")]
 | 
				
			||||||
        initrd: String,
 | 
					        initrd: String,
 | 
				
			||||||
        #[arg(short, long)]
 | 
					        #[arg(short, long)]
 | 
				
			||||||
        image: String,
 | 
					        image: String,
 | 
				
			||||||
@ -53,7 +55,7 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        .map(|x| x.to_string())
 | 
					        .map(|x| x.to_string())
 | 
				
			||||||
        .ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
 | 
					        .ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut controller = Controller::new(store_path)?;
 | 
					    let mut controller = Controller::new(store_path.clone())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match args.command {
 | 
					    match args.command {
 | 
				
			||||||
        Commands::Launch {
 | 
					        Commands::Launch {
 | 
				
			||||||
@ -63,6 +65,8 @@ fn main() -> Result<()> {
 | 
				
			|||||||
            cpus,
 | 
					            cpus,
 | 
				
			||||||
            mem,
 | 
					            mem,
 | 
				
			||||||
        } => {
 | 
					        } => {
 | 
				
			||||||
 | 
					            let kernel = map_kernel_path(&store_path, kernel);
 | 
				
			||||||
 | 
					            let initrd = map_initrd_path(&store_path, initrd);
 | 
				
			||||||
            let domid = controller.launch(&kernel, &initrd, &image, cpus, mem)?;
 | 
					            let domid = controller.launch(&kernel, &initrd, &image, cpus, mem)?;
 | 
				
			||||||
            println!("launched domain: {}", domid);
 | 
					            println!("launched domain: {}", domid);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -74,10 +78,40 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        Commands::Console { domain } => {
 | 
					        Commands::Console { domain } => {
 | 
				
			||||||
            controller.console(domain)?;
 | 
					            controller.console(domain)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Commands::List { .. } => {
 | 
				
			||||||
 | 
					            let containers = controller.list()?;
 | 
				
			||||||
 | 
					            let mut table = cli_tables::Table::new();
 | 
				
			||||||
 | 
					            let header = vec!["domain", "uuid", "image"];
 | 
				
			||||||
 | 
					            table.push_row(&header)?;
 | 
				
			||||||
 | 
					            for container in containers {
 | 
				
			||||||
 | 
					                let row = vec![
 | 
				
			||||||
 | 
					                    container.domid.to_string(),
 | 
				
			||||||
 | 
					                    container.uuid.to_string(),
 | 
				
			||||||
 | 
					                    container.image,
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					                table.push_row_string(&row)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            println!("{}", table.to_string());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn map_kernel_path(store: &str, value: String) -> String {
 | 
				
			||||||
 | 
					    if value == "auto" {
 | 
				
			||||||
 | 
					        return format!("{}/default/kernel", store);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn map_initrd_path(store: &str, value: String) -> String {
 | 
				
			||||||
 | 
					    if value == "auto" {
 | 
				
			||||||
 | 
					        return format!("{}/default/initrd", store);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn default_store_path() -> Option<PathBuf> {
 | 
					fn default_store_path() -> Option<PathBuf> {
 | 
				
			||||||
    let user_dirs = directories::UserDirs::new()?;
 | 
					    let user_dirs = directories::UserDirs::new()?;
 | 
				
			||||||
    let mut path = user_dirs.home_dir().to_path_buf();
 | 
					    let mut path = user_dirs.home_dir().to_path_buf();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
use crate::error::{HyphaError, Result};
 | 
					use crate::error::{HyphaError, Result};
 | 
				
			||||||
use loopdev::{LoopControl, LoopDevice};
 | 
					use loopdev::{LoopControl, LoopDevice};
 | 
				
			||||||
use std::path::Path;
 | 
					 | 
				
			||||||
use xenclient::BlockDeviceRef;
 | 
					use xenclient::BlockDeviceRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct AutoLoop {
 | 
					pub struct AutoLoop {
 | 
				
			||||||
@ -12,7 +11,7 @@ impl AutoLoop {
 | 
				
			|||||||
        AutoLoop { control }
 | 
					        AutoLoop { control }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn loopify(&self, file: &Path) -> Result<BlockDeviceRef> {
 | 
					    pub fn loopify(&self, file: &str) -> Result<BlockDeviceRef> {
 | 
				
			||||||
        let device = self.control.next_free()?;
 | 
					        let device = self.control.next_free()?;
 | 
				
			||||||
        device.with().read_only(true).attach(file)?;
 | 
					        device.with().read_only(true).attach(file)?;
 | 
				
			||||||
        let path = device
 | 
					        let path = device
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ use loopdev::LoopControl;
 | 
				
			|||||||
use std::io::{Read, Write};
 | 
					use std::io::{Read, Write};
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
use std::process::exit;
 | 
					use std::process::exit;
 | 
				
			||||||
 | 
					use std::str::FromStr;
 | 
				
			||||||
use std::{fs, io, thread};
 | 
					use std::{fs, io, thread};
 | 
				
			||||||
use termion::raw::IntoRawMode;
 | 
					use termion::raw::IntoRawMode;
 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
@ -22,6 +23,19 @@ pub struct Controller {
 | 
				
			|||||||
    client: XenClient,
 | 
					    client: XenClient,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct ContainerLoopInfo {
 | 
				
			||||||
 | 
					    pub device: String,
 | 
				
			||||||
 | 
					    pub file: String,
 | 
				
			||||||
 | 
					    pub delete: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct ContainerInfo {
 | 
				
			||||||
 | 
					    pub uuid: Uuid,
 | 
				
			||||||
 | 
					    pub domid: u32,
 | 
				
			||||||
 | 
					    pub image: String,
 | 
				
			||||||
 | 
					    pub loops: Vec<ContainerLoopInfo>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Controller {
 | 
					impl Controller {
 | 
				
			||||||
    pub fn new(store_path: String) -> Result<Controller> {
 | 
					    pub fn new(store_path: String) -> Result<Controller> {
 | 
				
			||||||
        let mut image_cache_path = PathBuf::from(store_path);
 | 
					        let mut image_cache_path = PathBuf::from(store_path);
 | 
				
			||||||
@ -59,11 +73,22 @@ impl Controller {
 | 
				
			|||||||
        let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
 | 
					        let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
 | 
				
			||||||
        cfgblk.build()?;
 | 
					        cfgblk.build()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let image_squashfs_path = image_info.image_squashfs.clone();
 | 
					        let image_squashfs_path = image_info
 | 
				
			||||||
        let cfgblk_squashfs_path = cfgblk.file.clone();
 | 
					            .image_squashfs
 | 
				
			||||||
 | 
					            .to_str()
 | 
				
			||||||
 | 
					            .ok_or_else(|| HyphaError::new("failed to convert image squashfs path to string"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let image_squashfs_loop = self.autoloop.loopify(&image_squashfs_path)?;
 | 
					        let cfgblk_dir_path = cfgblk
 | 
				
			||||||
        let cfgblk_squashfs_loop = self.autoloop.loopify(&cfgblk_squashfs_path)?;
 | 
					            .dir
 | 
				
			||||||
 | 
					            .to_str()
 | 
				
			||||||
 | 
					            .ok_or_else(|| HyphaError::new("failed to convert cfgblk directory path to string"))?;
 | 
				
			||||||
 | 
					        let cfgblk_squashfs_path = cfgblk
 | 
				
			||||||
 | 
					            .file
 | 
				
			||||||
 | 
					            .to_str()
 | 
				
			||||||
 | 
					            .ok_or_else(|| HyphaError::new("failed to convert cfgblk squashfs path to string"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let image_squashfs_loop = self.autoloop.loopify(image_squashfs_path)?;
 | 
				
			||||||
 | 
					        let cfgblk_squashfs_loop = self.autoloop.loopify(cfgblk_squashfs_path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let config = DomainConfig {
 | 
					        let config = DomainConfig {
 | 
				
			||||||
            backend_domid: 0,
 | 
					            backend_domid: 0,
 | 
				
			||||||
@ -90,10 +115,15 @@ impl Controller {
 | 
				
			|||||||
                (
 | 
					                (
 | 
				
			||||||
                    "hypha/loops".to_string(),
 | 
					                    "hypha/loops".to_string(),
 | 
				
			||||||
                    format!(
 | 
					                    format!(
 | 
				
			||||||
                        "{},{}",
 | 
					                        "{}:{}:none,{}:{}:{}",
 | 
				
			||||||
                        &image_squashfs_loop.path, &cfgblk_squashfs_loop.path
 | 
					                        &image_squashfs_loop.path,
 | 
				
			||||||
 | 
					                        image_squashfs_path,
 | 
				
			||||||
 | 
					                        &cfgblk_squashfs_loop.path,
 | 
				
			||||||
 | 
					                        cfgblk_squashfs_path,
 | 
				
			||||||
 | 
					                        cfgblk_dir_path,
 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
 | 
					                ("hypha/image".to_string(), image.to_string()),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        match self.client.create(&config) {
 | 
					        match self.client.create(&config) {
 | 
				
			||||||
@ -126,13 +156,21 @@ impl Controller {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        let uuid = Uuid::parse_str(&uuid)?;
 | 
					        let uuid = Uuid::parse_str(&uuid)?;
 | 
				
			||||||
        let loops = store.read_string(format!("{}/hypha/loops", dom_path).as_str())?;
 | 
					        let loops = store.read_string(format!("{}/hypha/loops", dom_path).as_str())?;
 | 
				
			||||||
        let loops = loops
 | 
					        let loops = Controller::parse_loop_set(&loops);
 | 
				
			||||||
            .split(',')
 | 
					 | 
				
			||||||
            .map(|x| x.to_string())
 | 
					 | 
				
			||||||
            .collect::<Vec<String>>();
 | 
					 | 
				
			||||||
        self.client.destroy(domid)?;
 | 
					        self.client.destroy(domid)?;
 | 
				
			||||||
        for lop in &loops {
 | 
					        for info in &loops {
 | 
				
			||||||
            self.autoloop.unloop(lop)?;
 | 
					            self.autoloop.unloop(&info.device)?;
 | 
				
			||||||
 | 
					            match &info.delete {
 | 
				
			||||||
 | 
					                None => {}
 | 
				
			||||||
 | 
					                Some(delete) => {
 | 
				
			||||||
 | 
					                    let delete_path = PathBuf::from(delete);
 | 
				
			||||||
 | 
					                    if delete_path.is_file() || delete_path.is_symlink() {
 | 
				
			||||||
 | 
					                        fs::remove_file(&delete_path)?;
 | 
				
			||||||
 | 
					                    } else if delete_path.is_dir() {
 | 
				
			||||||
 | 
					                        fs::remove_dir_all(&delete_path)?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(uuid)
 | 
					        Ok(uuid)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -177,4 +215,60 @@ impl Controller {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn list(&mut self) -> Result<Vec<ContainerInfo>> {
 | 
				
			||||||
 | 
					        let mut containers: Vec<ContainerInfo> = Vec::new();
 | 
				
			||||||
 | 
					        for domid_candidate in self.client.store.list_any("/local/domain")? {
 | 
				
			||||||
 | 
					            let dom_path = format!("/local/domain/{}", domid_candidate);
 | 
				
			||||||
 | 
					            let uuid_string = match self
 | 
				
			||||||
 | 
					                .client
 | 
				
			||||||
 | 
					                .store
 | 
				
			||||||
 | 
					                .read_string_optional(&format!("{}/hypha/uuid", &dom_path))?
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                None => continue,
 | 
				
			||||||
 | 
					                Some(value) => value,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            let domid = u32::from_str(&domid_candidate)
 | 
				
			||||||
 | 
					                .map_err(|_| HyphaError::new("failed to parse domid"))?;
 | 
				
			||||||
 | 
					            let uuid = Uuid::from_str(&uuid_string)?;
 | 
				
			||||||
 | 
					            let image = self
 | 
				
			||||||
 | 
					                .client
 | 
				
			||||||
 | 
					                .store
 | 
				
			||||||
 | 
					                .read_string_optional(&format!("{}/hypha/image", &dom_path))?
 | 
				
			||||||
 | 
					                .unwrap_or("unknown".to_string());
 | 
				
			||||||
 | 
					            let loops = self
 | 
				
			||||||
 | 
					                .client
 | 
				
			||||||
 | 
					                .store
 | 
				
			||||||
 | 
					                .read_string_optional(&format!("{}/hypha/loops", &dom_path))?
 | 
				
			||||||
 | 
					                .unwrap_or("".to_string());
 | 
				
			||||||
 | 
					            let loops = Controller::parse_loop_set(&loops);
 | 
				
			||||||
 | 
					            containers.push(ContainerInfo {
 | 
				
			||||||
 | 
					                uuid,
 | 
				
			||||||
 | 
					                domid,
 | 
				
			||||||
 | 
					                image,
 | 
				
			||||||
 | 
					                loops,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(containers)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_loop_set(input: &str) -> Vec<ContainerLoopInfo> {
 | 
				
			||||||
 | 
					        let sets = input
 | 
				
			||||||
 | 
					            .split(',')
 | 
				
			||||||
 | 
					            .map(|x| x.to_string())
 | 
				
			||||||
 | 
					            .map(|x| x.split(':').map(|v| v.to_string()).collect::<Vec<String>>())
 | 
				
			||||||
 | 
					            .map(|x| (x[0].clone(), x[1].clone(), x[2].clone()))
 | 
				
			||||||
 | 
					            .collect::<Vec<(String, String, String)>>();
 | 
				
			||||||
 | 
					        sets.iter()
 | 
				
			||||||
 | 
					            .map(|(device, file, delete)| ContainerLoopInfo {
 | 
				
			||||||
 | 
					                device: device.clone(),
 | 
				
			||||||
 | 
					                file: file.clone(),
 | 
				
			||||||
 | 
					                delete: if delete == "none" {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Some(delete.clone())
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect::<Vec<ContainerLoopInfo>>()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
use backhand::BackhandError;
 | 
					use backhand::BackhandError;
 | 
				
			||||||
 | 
					use cli_tables::TableError;
 | 
				
			||||||
use oci_spec::OciSpecError;
 | 
					use oci_spec::OciSpecError;
 | 
				
			||||||
use std::error::Error;
 | 
					use std::error::Error;
 | 
				
			||||||
use std::fmt::{Display, Formatter};
 | 
					use std::fmt::{Display, Formatter};
 | 
				
			||||||
@ -111,3 +112,9 @@ impl From<XsdBusError> for HyphaError {
 | 
				
			|||||||
        HyphaError::new(value.to_string().as_str())
 | 
					        HyphaError::new(value.to_string().as_str())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<TableError> for HyphaError {
 | 
				
			||||||
 | 
					    fn from(value: TableError) -> Self {
 | 
				
			||||||
 | 
					        HyphaError::new(value.to_string().as_str())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user