Implement resizing of zpool
Some checks failed
/ dev-shell (push) Successful in 2m7s
/ rust-packages (push) Successful in 2m22s
/ terraform-providers (push) Successful in 53s
/ check (push) Failing after 3m31s
/ systems (push) Successful in 26m15s

This commit is contained in:
Kaare Hoff Skovgaard 2025-08-04 02:20:26 +02:00
parent f410517ffa
commit f0725c503f
Signed by: khs
GPG key ID: C7D890804F01E9F0
10 changed files with 234 additions and 17 deletions

View file

@ -0,0 +1,13 @@
[package]
name = "disko-zpool-expand"
edition = "2024"
version = "1.0.0"
metadata.crane.name = "disko-zpool-expand"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true }
common = { path = "../../lib/common" }
log = { workspace = true }
serde = { workspace = true }
hakari = { version = "0.1", path = "../../lib/hakari" }

View file

@ -0,0 +1,117 @@
use serde::Deserialize;
use std::{collections::BTreeMap, path::PathBuf};
use anyhow::Context as _;
use clap::{Parser, Subcommand};
fn main() {
common::entrypoint(program);
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
/// Expands the partitions based on a zpool and brings the pool up to the new size.
#[command(name = "expand-zpool")]
ExpandZpool(ExpandZpool),
}
#[derive(Debug, Clone, clap::Args)]
pub struct ExpandZpool {
/// Name of the pool to expand
pool_name: String,
}
fn program() -> anyhow::Result<()> {
let args = Args::parse();
match args.command {
Commands::ExpandZpool(pool) => expand_zpool(pool),
}
}
#[derive(Deserialize)]
struct ZpoolStatus {
pools: BTreeMap<String, ZpoolStatusPool>,
}
#[derive(Deserialize)]
struct ZpoolStatusPool {
state: Option<ZpoolState>,
vdevs: BTreeMap<String, ZpoolStatusVdev>,
}
#[derive(Clone, Copy, Deserialize, PartialEq)]
enum ZpoolState {
#[serde(rename = "ONLINE")]
Online,
}
#[derive(Deserialize)]
struct ZpoolStatusVdev {
vdevs: BTreeMap<String, ZpoolStatusVdevVdev>,
}
#[derive(Deserialize)]
struct ZpoolStatusVdevVdev {
path: PathBuf,
}
fn expand_zpool(p: ExpandZpool) -> anyhow::Result<()> {
let mut proc = common::proc::Command::new("zpool");
proc.args(["status", "--json", &p.pool_name]);
let result: ZpoolStatus = proc
.try_spawn_to_json()
.context("Could not get zpool status")?;
let pool = result
.pools
.get(&p.pool_name)
.context("Could not find requested pool in status output")?;
if !pool
.state
.as_ref()
.is_some_and(|st| *st == ZpoolState::Online)
{
return Err(anyhow::format_err!("Zpool {} is not online", p.pool_name));
}
for vdev in pool.vdevs.values() {
for vdev in vdev.vdevs.values() {
let partition_dev = vdev.path.display().to_string();
let Some(dev) = partition_dev.strip_suffix("-part1") else {
return Err(anyhow::format_err!(
"Expected vdev path {} to end with -part1",
vdev.path.display()
));
};
let mut proc = common::proc::Command::new("growpart");
proc.args([dev, "1"]);
let (stdout, _stderr, status) = proc.spawn_into_parts()?;
if !status.success() && !stdout.starts_with("NOCHANGE: ") {
return Err(anyhow::format_err!(
"Could not resize partitin for {}, err: {stdout}",
vdev.path.display()
));
}
// let name = partition_dev
// .split("/")
// .last()
// .expect("Should always have at least one element");
let mut proc = common::proc::Command::new("zpool");
proc.args(["online", "-e", &p.pool_name, &partition_dev]);
proc.try_spawn_to_string().with_context(|| {
format!(
"Could not bring zpool {} online with expand flag",
p.pool_name
)
})?;
}
}
Ok(())
}