use super::{filters::parse_filters, open_path};
use crate::{RunContext, Runnable};
use anyhow::{Context, Result};
use btrfs_uapi::balance::{BalanceFlags, balance};
use clap::Parser;
use nix::errno::Errno;
use std::{os::unix::io::AsFd, path::PathBuf, thread, time::Duration};
type Filters = String;
#[derive(Parser, Debug)]
#[allow(clippy::struct_excessive_bools)]
pub struct BalanceStartCommand {
#[clap(long, short)]
pub data_filters: Option<Filters>,
#[clap(long, short)]
pub metadata_filters: Option<Filters>,
#[clap(long, short, requires = "force")]
pub system_filters: Option<Filters>,
#[clap(long, short)]
pub force: bool,
#[clap(long)]
pub full_balance: bool,
#[clap(long, short, alias = "bg")]
pub background: bool,
#[clap(long)]
pub enqueue: bool,
pub path: PathBuf,
}
impl Runnable for BalanceStartCommand {
fn run(&self, _ctx: &RunContext) -> Result<()> {
if self.background {
anyhow::bail!("--background is not yet implemented");
}
let has_filters = self.data_filters.is_some()
|| self.metadata_filters.is_some()
|| self.system_filters.is_some();
let mut flags = BalanceFlags::empty();
let data_args = match &self.data_filters {
Some(f) => {
flags |= BalanceFlags::DATA;
Some(parse_filters(f).context("invalid data filter")?)
}
None => None,
};
let meta_args = match &self.metadata_filters {
Some(f) => {
flags |= BalanceFlags::METADATA | BalanceFlags::SYSTEM;
Some(parse_filters(f).context("invalid metadata filter")?)
}
None => None,
};
let sys_args = match &self.system_filters {
Some(f) => {
flags |= BalanceFlags::SYSTEM;
Some(parse_filters(f).context("invalid system filter")?)
}
None => meta_args.clone(),
};
if !has_filters {
flags |= BalanceFlags::DATA
| BalanceFlags::METADATA
| BalanceFlags::SYSTEM;
}
if self.force {
flags |= BalanceFlags::FORCE;
}
if !has_filters && !self.full_balance {
eprintln!("WARNING:\n");
eprintln!(
"\tFull balance without filters requested. This operation is very"
);
eprintln!(
"\tintense and takes potentially very long. It is recommended to"
);
eprintln!(
"\tuse the balance filters to narrow down the scope of balance."
);
eprintln!(
"\tUse 'btrfs balance start --full-balance' to skip this warning."
);
eprintln!(
"\tThe operation will start in 10 seconds. Use Ctrl-C to stop it."
);
thread::sleep(Duration::from_secs(10));
eprintln!("\nStarting balance without any filters.");
}
let file = open_path(&self.path)?;
match balance(file.as_fd(), flags, data_args, meta_args, sys_args) {
Ok(progress) => {
println!(
"Done, had to relocate {} out of {} chunks",
progress.completed, progress.considered
);
Ok(())
}
Err(Errno::ECANCELED) => {
eprintln!("Balance was paused or cancelled by user.");
Ok(())
}
Err(e) => Err(e).with_context(|| {
format!("error during balancing '{}'", self.path.display())
}),
}
}
}