async-ify xenstore and xenclient

This commit is contained in:
Alex Zenla
2024-02-23 04:37:53 +00:00
parent cf0b62c9f5
commit 79acf4e814
15 changed files with 395 additions and 294 deletions

View File

@ -69,7 +69,7 @@ async fn main() -> Result<()> {
.map(|x| x.to_string())
.ok_or_else(|| anyhow!("unable to convert store path to string"))?;
let mut context = ControllerContext::new(store_path.clone())?;
let mut context = ControllerContext::new(store_path.clone()).await?;
match args.command {
Commands::Launch {
@ -96,7 +96,7 @@ async fn main() -> Result<()> {
run: if run.is_empty() { None } else { Some(run) },
debug,
};
let (uuid, _domid) = launch.perform(request)?;
let (uuid, _domid) = launch.perform(request).await?;
println!("launched container: {}", uuid);
if attach {
let mut console = ControllerConsole::new(&mut context);
@ -106,7 +106,7 @@ async fn main() -> Result<()> {
Commands::Destroy { container } => {
let mut destroy = ControllerDestroy::new(&mut context);
destroy.perform(&container)?;
destroy.perform(&container).await?;
}
Commands::Console { container } => {
@ -115,7 +115,7 @@ async fn main() -> Result<()> {
}
Commands::List { .. } => {
let containers = context.list()?;
let containers = context.list().await?;
let mut table = cli_tables::Table::new();
let header = vec!["uuid", "ipv4", "ipv6", "image"];
table.push_row(&header)?;

View File

@ -16,10 +16,11 @@ impl ControllerConsole<'_> {
pub async fn perform(&mut self, id: &str) -> Result<()> {
let info = self
.context
.resolve(id)?
.resolve(id)
.await?
.ok_or_else(|| anyhow!("unable to resolve container: {}", id))?;
let domid = info.domid;
let tty = self.context.xen.get_console_path(domid)?;
let tty = self.context.xen.get_console_path(domid).await?;
let console = XenConsole::new(&tty).await?;
console.attach().await?;
Ok(())

View File

@ -15,15 +15,19 @@ impl ControllerDestroy<'_> {
ControllerDestroy { context }
}
pub fn perform(&mut self, id: &str) -> Result<Uuid> {
pub async fn perform(&mut self, id: &str) -> Result<Uuid> {
let info = self
.context
.resolve(id)?
.resolve(id)
.await?
.ok_or_else(|| anyhow!("unable to resolve container: {}", id))?;
let domid = info.domid;
let mut store = XsdClient::open()?;
let dom_path = store.get_domain_path(domid)?;
let uuid = match store.read_string_optional(format!("{}/krata/uuid", dom_path).as_str())? {
let mut store = XsdClient::open().await?;
let dom_path = store.get_domain_path(domid).await?;
let uuid = match store
.read_string(format!("{}/krata/uuid", dom_path).as_str())
.await?
{
None => {
return Err(anyhow!(
"domain {} was not found or not created by krata",
@ -36,9 +40,11 @@ impl ControllerDestroy<'_> {
return Err(anyhow!("unable to find krata uuid based on the domain",));
}
let uuid = Uuid::parse_str(&uuid)?;
let loops = store.read_string(format!("{}/krata/loops", dom_path).as_str())?;
let loops = store
.read_string(format!("{}/krata/loops", dom_path).as_str())
.await?;
let loops = ControllerContext::parse_loop_set(&loops);
self.context.xen.destroy(domid)?;
self.context.xen.destroy(domid).await?;
for info in &loops {
self.context.autoloop.unloop(&info.device)?;
match &info.delete {

View File

@ -35,7 +35,7 @@ impl ControllerLaunch<'_> {
ControllerLaunch { context }
}
pub fn perform(&mut self, request: ControllerLaunchRequest) -> Result<(Uuid, u32)> {
pub async fn perform(&mut self, request: ControllerLaunchRequest<'_>) -> Result<(Uuid, u32)> {
let uuid = Uuid::new_v4();
let name = format!("krata-{uuid}");
let image_info = self.compile(request.image)?;
@ -47,7 +47,7 @@ impl ControllerLaunch<'_> {
container_mac.set_local(true);
container_mac.set_multicast(false);
let guest_ipv4 = self.allocate_ipv4()?;
let guest_ipv4 = self.allocate_ipv4().await?;
let guest_ipv6 = container_mac.to_link_local_ipv6();
let gateway_ipv4 = "192.168.42.1";
let gateway_ipv6 = "fe80::1";
@ -178,7 +178,7 @@ impl ControllerLaunch<'_> {
),
],
};
match self.context.xen.create(&config) {
match self.context.xen.create(&config).await {
Ok(domid) => Ok((uuid, domid)),
Err(error) => {
let _ = self.context.autoloop.unloop(&image_squashfs_loop.path);
@ -189,17 +189,17 @@ impl ControllerLaunch<'_> {
}
}
fn allocate_ipv4(&mut self) -> Result<Ipv4Addr> {
async fn allocate_ipv4(&mut self) -> Result<Ipv4Addr> {
let network = Ipv4Network::new(Ipv4Addr::new(192, 168, 42, 0), 24)?;
let mut used: Vec<Ipv4Addr> = vec![
Ipv4Addr::new(192, 168, 42, 0),
Ipv4Addr::new(192, 168, 42, 1),
Ipv4Addr::new(192, 168, 42, 255),
];
for domid_candidate in self.context.xen.store.list_any("/local/domain")? {
for domid_candidate in self.context.xen.store.list("/local/domain").await? {
let dom_path = format!("/local/domain/{}", domid_candidate);
let ip_path = format!("{}/krata/network/guest/ipv4", dom_path);
let existing_ip = self.context.xen.store.read_string_optional(&ip_path)?;
let existing_ip = self.context.xen.store.read_string(&ip_path).await?;
if let Some(existing_ip) = existing_ip {
let ipv4_network = Ipv4Network::from_str(&existing_ip)?;
used.push(ipv4_network.ip());

View File

@ -37,12 +37,12 @@ pub struct ContainerInfo {
}
impl ControllerContext {
pub fn new(store_path: String) -> Result<ControllerContext> {
pub async fn new(store_path: String) -> Result<ControllerContext> {
let mut image_cache_path = PathBuf::from(store_path);
image_cache_path.push("cache");
fs::create_dir_all(&image_cache_path)?;
let xen = XenClient::open()?;
let xen = XenClient::open().await?;
image_cache_path.push("image");
fs::create_dir_all(&image_cache_path)?;
let image_cache = ImageCache::new(&image_cache_path)?;
@ -53,14 +53,15 @@ impl ControllerContext {
})
}
pub fn list(&mut self) -> Result<Vec<ContainerInfo>> {
pub async fn list(&mut self) -> Result<Vec<ContainerInfo>> {
let mut containers: Vec<ContainerInfo> = Vec::new();
for domid_candidate in self.xen.store.list_any("/local/domain")? {
for domid_candidate in self.xen.store.list("/local/domain").await? {
let dom_path = format!("/local/domain/{}", domid_candidate);
let uuid_string = match self
.xen
.store
.read_string_optional(&format!("{}/krata/uuid", &dom_path))?
.read_string(&format!("{}/krata/uuid", &dom_path))
.await?
{
None => continue,
Some(value) => value,
@ -71,22 +72,25 @@ impl ControllerContext {
let image = self
.xen
.store
.read_string_optional(&format!("{}/krata/image", &dom_path))?
.read_string(&format!("{}/krata/image", &dom_path))
.await?
.unwrap_or("unknown".to_string());
let loops = self
.xen
.store
.read_string_optional(&format!("{}/krata/loops", &dom_path))?
.unwrap_or("".to_string());
.read_string(&format!("{}/krata/loops", &dom_path))
.await?;
let ipv4 = self
.xen
.store
.read_string_optional(&format!("{}/krata/network/guest/ipv4", &dom_path))?
.read_string(&format!("{}/krata/network/guest/ipv4", &dom_path))
.await?
.unwrap_or("unknown".to_string());
let ipv6: String = self
.xen
.store
.read_string_optional(&format!("{}/krata/network/guest/ipv6", &dom_path))?
.read_string(&format!("{}/krata/network/guest/ipv6", &dom_path))
.await?
.unwrap_or("unknown".to_string());
let loops = ControllerContext::parse_loop_set(&loops);
containers.push(ContainerInfo {
@ -101,8 +105,8 @@ impl ControllerContext {
Ok(containers)
}
pub fn resolve(&mut self, id: &str) -> Result<Option<ContainerInfo>> {
for container in self.list()? {
pub async fn resolve(&mut self, id: &str) -> Result<Option<ContainerInfo>> {
for container in self.list().await? {
let uuid_string = container.uuid.to_string();
let domid_string = container.domid.to_string();
if uuid_string == id || domid_string == id || id == format!("krata-{}", uuid_string) {
@ -112,7 +116,10 @@ impl ControllerContext {
Ok(None)
}
fn parse_loop_set(input: &str) -> Vec<ContainerLoopInfo> {
fn parse_loop_set(input: &Option<String>) -> Vec<ContainerLoopInfo> {
let Some(input) = input else {
return Vec::new();
};
let sets = input
.split(',')
.map(|x| x.to_string())