more work
This commit is contained in:
Generated
+151
-2
@@ -4,13 +4,162 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.98"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.9.12+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow 0.7.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
|
dependencies = [
|
||||||
|
"winnow 1.0.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.1.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zfs_backup"
|
name = "zfs_backup"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
toml = "0.9.2"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
backup
|
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
snapshot_tag = "znap"
|
||||||
|
snapshot_interval = "monthly"
|
||||||
|
|
||||||
|
local_pool_name = "tank"
|
||||||
|
|
||||||
|
[backup_pools]
|
||||||
|
backup1 = 'a235c116-764f-4082-97fc-47ca31e11e53'
|
||||||
|
backup2 = '200ced55-894c-4f70-b089-b140e60193fc'
|
||||||
|
|
||||||
|
|
||||||
@@ -17,6 +17,20 @@ pub fn zpool_export(pool_name: &str) -> Result<()> {
|
|||||||
run_command("zpool", vec!["export", pool_name])
|
run_command("zpool", vec!["export", pool_name])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zpool_scrub(pool_name: &str) -> Result<()> {
|
||||||
|
run_command("zpool", vec!["scrub", pool_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zpool_wait_scrub(pool_name: &str) -> Result<()> {
|
||||||
|
run_command("zpool", vec!["wait", "-t", "scrub", pool_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zpool_status(pool_name: &str) -> Result<String> {
|
||||||
|
let output = get_output("zpool", vec!["status", pool_name])?;
|
||||||
|
let status = String::from_utf8(output.stdout)?;
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn zfs_list_snapshots(dataset_name: &str) -> Result<Vec<String>> {
|
pub fn zfs_list_snapshots(dataset_name: &str) -> Result<Vec<String>> {
|
||||||
let output = get_output(
|
let output = get_output(
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
pub fn read_var_from_file(filename: &str) -> Result<String> {
|
|
||||||
let content = std::fs::read_to_string(PathBuf::from(filename))?;
|
|
||||||
let s = content.trim();
|
|
||||||
Ok(s.trim().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_vars_from_file(filename: &str) -> Result<Vec<String>> {
|
|
||||||
let content = std::fs::read_to_string(PathBuf::from(filename))?;
|
|
||||||
let s = content.trim();
|
|
||||||
Ok(s.lines().map(|s| s.to_owned()).collect())
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
pub mod io;
|
|
||||||
pub mod zfs;
|
pub mod zfs;
|
||||||
|
|||||||
+101
-54
@@ -1,36 +1,65 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs::read_to_string;
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use zfs_backup::cmd::cryptsetup;
|
use zfs_backup::cmd::cryptsetup;
|
||||||
use zfs_backup::cmd::zfs::{zpool_export, zpool_import};
|
use zfs_backup::cmd::zfs::zpool_import;
|
||||||
use zfs_backup::io::read_var_from_file;
|
use zfs_backup::zfs::{Dataset, Snapshot, ZPool};
|
||||||
use zfs_backup::zfs::{Dataset, Pool, Snapshot};
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
verify_running_as_root()?;
|
verify_running_as_root()?;
|
||||||
|
|
||||||
decrypt_backup()?;
|
let config = Config::read("config.toml")?;
|
||||||
import_backup_pool()?;
|
|
||||||
do_backup()?;
|
let local_pool = ZPool::new(&config.local_pool_name)?; // will error if pool does not exist
|
||||||
export_backup_pool()?;
|
|
||||||
encrypt_backup()?;
|
let (found_pool_name, crypt_device) = decrypt_backup(&config.backup_pools)?;
|
||||||
|
let backup_pool = import_backup_pool(&found_pool_name)?;
|
||||||
|
|
||||||
|
backup_pool.scrub()?;
|
||||||
|
|
||||||
|
let something_was_done = do_backup(
|
||||||
|
&local_pool,
|
||||||
|
&backup_pool,
|
||||||
|
config.snapshot_tag,
|
||||||
|
config.snapshot_interval,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if something_was_done {
|
||||||
|
backup_pool.scrub()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_pool.export()?;
|
||||||
|
encrypt_backup(crypt_device)?;
|
||||||
|
|
||||||
println!("all done");
|
println!("all done");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Export the ZFS pool after the backup so it can be re-encrypted.
|
#[derive(Deserialize)]
|
||||||
///
|
struct Config {
|
||||||
/// # Errors
|
snapshot_tag: String,
|
||||||
///
|
snapshot_interval: String,
|
||||||
/// This function will return an error if the name of the backup pool is not provided or the pool
|
local_pool_name: String,
|
||||||
/// cannot be exported.
|
backup_pools: BackupPools,
|
||||||
fn export_backup_pool() -> Result<()> {
|
}
|
||||||
let pool_name = &read_var_from_file("backup_pool.txt")?;
|
|
||||||
zpool_export(pool_name)?;
|
#[derive(Deserialize)]
|
||||||
Ok(())
|
struct BackupPools {
|
||||||
|
backup1: String,
|
||||||
|
backup2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
fn read(filename: &str) -> Result<Self> {
|
||||||
|
let content = read_to_string(filename).context("reading toml file")?;
|
||||||
|
let parsed: Self = toml::from_str(&content).context("parsing toml")?;
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import the decrypted ZFS pool so that a backup can be sent.
|
/// Import the decrypted ZFS pool so that a backup can be sent.
|
||||||
@@ -39,26 +68,31 @@ fn export_backup_pool() -> Result<()> {
|
|||||||
///
|
///
|
||||||
/// This function will return an error if the name of the backup pool is not provided or the pool
|
/// This function will return an error if the name of the backup pool is not provided or the pool
|
||||||
/// cannot be imported.
|
/// cannot be imported.
|
||||||
fn import_backup_pool() -> Result<()> {
|
fn import_backup_pool(pool_name: &str) -> Result<ZPool> {
|
||||||
let pool_name = &read_var_from_file("backup_pool.txt")?;
|
|
||||||
zpool_import(pool_name)?;
|
zpool_import(pool_name)?;
|
||||||
Ok(())
|
ZPool::new(pool_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_backup() -> Result<()> {
|
/// Returns whether something was done.
|
||||||
let (local, remote) =
|
fn do_backup(
|
||||||
load_zfs_pools("local_pool.txt", "backup_pool.txt").context("loading zfs pools")?;
|
local_pool: &ZPool,
|
||||||
let snapshot_tag = read_var_from_file("snapshot.txt").context("fetching snapshot tag")?;
|
backup_pool: &ZPool,
|
||||||
let todos = Todo::plan(&local, &remote, snapshot_tag).context("planning todos")?;
|
snapshot_tag: String,
|
||||||
if ask_user_confirmation(&todos).context("asking user confirmation")? {
|
snapshot_interval: String,
|
||||||
|
) -> Result<bool> {
|
||||||
|
let todos = Todo::plan(local_pool, backup_pool, snapshot_tag, snapshot_interval)
|
||||||
|
.context("planning todos")?;
|
||||||
|
|
||||||
|
if !todos.is_empty() && ask_user_confirmation(&todos).context("asking user confirmation")? {
|
||||||
println!("executing:");
|
println!("executing:");
|
||||||
execute_todos(local, remote, todos)?;
|
execute_todos(local_pool, backup_pool, todos)?;
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_todos(local: Pool, remote: Pool, todos: Vec<Todo>) -> Result<()> {
|
fn execute_todos(local: &ZPool, remote: &ZPool, todos: Vec<Todo>) -> Result<()> {
|
||||||
for todo in todos {
|
for todo in todos {
|
||||||
todo.print();
|
todo.print();
|
||||||
match todo {
|
match todo {
|
||||||
@@ -95,34 +129,42 @@ fn ask_user_confirmation(todos: &Vec<Todo>) -> Result<bool> {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_zfs_pools(local_pool: &str, backup_pool: &str) -> Result<(Pool, Pool)> {
|
fn decrypt_backup(pools: &BackupPools) -> Result<(String, String)> {
|
||||||
let mut local =
|
let mut pathbuf = PathBuf::new();
|
||||||
Pool::new(&read_var_from_file(local_pool).context("getting local pool from config")?)
|
let mut found = false;
|
||||||
.context("finding local pool")?;
|
|
||||||
local
|
|
||||||
.load_datasets_from_file("datasets.txt")
|
|
||||||
.context("loading datasets to send")?;
|
|
||||||
|
|
||||||
let mut remote =
|
let mut the_name = String::new();
|
||||||
Pool::new(&read_var_from_file(backup_pool).context("getting backup pool from config")?)
|
let mut the_crypt_device = String::new();
|
||||||
.context("finding backup pool")?;
|
|
||||||
remote
|
|
||||||
.get_datasets_from_system()
|
|
||||||
.context("loading remote datasets")?;
|
|
||||||
|
|
||||||
Ok((local, remote))
|
for (device, name) in [&pools.backup1, &pools.backup2]
|
||||||
|
.iter()
|
||||||
|
.zip(["backup1", "backup2"])
|
||||||
|
{
|
||||||
|
pathbuf.push("/dev/disk/by-uuid/");
|
||||||
|
pathbuf.push(device);
|
||||||
|
|
||||||
|
if pathbuf.exists() {
|
||||||
|
found = true;
|
||||||
|
the_name = name.to_string();
|
||||||
|
|
||||||
|
the_crypt_device.push_str("crypt-");
|
||||||
|
the_crypt_device.push_str(name); // content is then e.g. "crypt-backup1"
|
||||||
|
|
||||||
|
println!("found backup disk {name}, will decrypt to {the_crypt_device}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_backup() -> Result<()> {
|
if found {
|
||||||
// let mut uuid = "/dev/disk/by-uuid/".to_string(); // TODO change back
|
cryptsetup::open(&pathbuf, &the_crypt_device)?;
|
||||||
let mut pathbuf = PathBuf::from("/home/timeshifter/rust/zfs_backup/");
|
Ok((the_name, the_crypt_device))
|
||||||
pathbuf.push(&read_var_from_file("backup_uuid.txt")?);
|
} else {
|
||||||
|
bail!("no backup disk found")
|
||||||
cryptsetup::open(&pathbuf, "crypt-backup")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_backup() -> Result<()> {
|
fn encrypt_backup(crypt_device: String) -> Result<()> {
|
||||||
cryptsetup::close("crypt-backup")
|
cryptsetup::close(&crypt_device)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_running_as_root() -> Result<()> {
|
fn verify_running_as_root() -> Result<()> {
|
||||||
@@ -154,12 +196,17 @@ impl Todo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plan(local: &Pool, remote: &Pool, snapshot_tag: String) -> Result<Vec<Self>> {
|
fn plan(
|
||||||
|
local: &ZPool,
|
||||||
|
remote: &ZPool,
|
||||||
|
snapshot_tag: String,
|
||||||
|
snapshot_interval: String,
|
||||||
|
) -> Result<Vec<Self>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
|
|
||||||
for local_dataset in &local.datasets {
|
for local_dataset in &local.datasets {
|
||||||
let last_snapshot_with_tag =
|
let last_snapshot_with_tag =
|
||||||
local_dataset.find_last_snapshot_with_tag(&snapshot_tag)?;
|
local_dataset.find_last_snapshot_with_tag(&snapshot_tag, &snapshot_interval)?;
|
||||||
|
|
||||||
let remote_dataset = local_dataset.change_pool_name(remote);
|
let remote_dataset = local_dataset.change_pool_name(remote);
|
||||||
if !remote.contains(&remote_dataset) {
|
if !remote.contains(&remote_dataset) {
|
||||||
|
|||||||
+31
-34
@@ -1,22 +1,17 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::{
|
use std::{fmt::Display, path::PathBuf, process::Child};
|
||||||
fmt::Display,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Child,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Result, anyhow, bail};
|
use anyhow::{Result, anyhow, bail};
|
||||||
|
|
||||||
use crate::cmd::zfs::*;
|
use crate::cmd::zfs::*;
|
||||||
use crate::io::read_vars_from_file;
|
|
||||||
|
|
||||||
/// A ZFS pool. It holds the name of the pool and the list of datasets belonging to it.
|
/// A ZFS pool. It holds the name of the pool and the list of datasets belonging to it.
|
||||||
pub struct Pool {
|
pub struct ZPool {
|
||||||
name: String,
|
pub name: String,
|
||||||
pub datasets: Vec<Dataset>,
|
pub datasets: Vec<Dataset>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pool {
|
impl ZPool {
|
||||||
pub fn new(arg: &str) -> Result<Self> {
|
pub fn new(arg: &str) -> Result<Self> {
|
||||||
let result = Self {
|
let result = Self {
|
||||||
name: arg.to_string(),
|
name: arg.to_string(),
|
||||||
@@ -62,11 +57,6 @@ impl Pool {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_datasets_from_file(&mut self, filename: &str) -> Result<()> {
|
|
||||||
self.datasets = Dataset::from_file(&PathBuf::from(filename), &self.name)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_datasets_from_system(&mut self) -> Result<()> {
|
pub fn get_datasets_from_system(&mut self) -> Result<()> {
|
||||||
let datasets = zfs_list_main_datasets(&self.name)?;
|
let datasets = zfs_list_main_datasets(&self.name)?;
|
||||||
|
|
||||||
@@ -99,13 +89,25 @@ impl Pool {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn export(self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import(name: &str) -> Result<Pool> {
|
pub fn scrub(&self) -> Result<()> {
|
||||||
|
zpool_scrub(&self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(&self) -> Result<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import(name: &str) -> Result<ZPool> {
|
||||||
zpool_import(name)?;
|
zpool_import(name)?;
|
||||||
let result = Pool::new(name)?;
|
let result = ZPool::new(name)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Full path of a ZFS dataset.
|
/// Full path of a ZFS dataset.
|
||||||
///
|
///
|
||||||
@@ -119,27 +121,18 @@ impl Dataset {
|
|||||||
Self(path)
|
Self(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_file(filename: &Path, pool: &str) -> Result<Vec<Self>> {
|
pub fn find_snapshots_like_tag(
|
||||||
let datasets = read_vars_from_file(filename.to_str().unwrap())?;
|
&self,
|
||||||
Ok(datasets
|
snapshot_tag: &str,
|
||||||
.into_iter()
|
snapshot_interval: &str,
|
||||||
.map(|ds| {
|
) -> Result<Vec<Snapshot>> {
|
||||||
let mut pb = PathBuf::new();
|
|
||||||
pb.push(pool);
|
|
||||||
pb.push(ds);
|
|
||||||
Self::new(pb)
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_snapshots_like_tag(&self, snapshot_tag: &str) -> Result<Vec<Snapshot>> {
|
|
||||||
let snapshots = zfs_list_snapshots(self.0.to_str().unwrap())?;
|
let snapshots = zfs_list_snapshots(self.0.to_str().unwrap())?;
|
||||||
|
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
|
|
||||||
for item in snapshots {
|
for item in snapshots {
|
||||||
let snapshot_name = item.split('@').next_back().unwrap();
|
let snapshot_name = item.split('@').next_back().unwrap();
|
||||||
if snapshot_name.contains(snapshot_tag) {
|
if snapshot_name.contains(snapshot_tag) && snapshot_name.contains(snapshot_interval) {
|
||||||
result.push(Snapshot(snapshot_name.to_string()));
|
result.push(Snapshot(snapshot_name.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,8 +140,12 @@ impl Dataset {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_last_snapshot_with_tag(&self, snapshot_tag: &str) -> Result<Snapshot> {
|
pub fn find_last_snapshot_with_tag(
|
||||||
let mut snapshots = self.find_snapshots_like_tag(snapshot_tag)?;
|
&self,
|
||||||
|
snapshot_tag: &str,
|
||||||
|
snapshot_interval: &str,
|
||||||
|
) -> Result<Snapshot> {
|
||||||
|
let mut snapshots = self.find_snapshots_like_tag(snapshot_tag, snapshot_interval)?;
|
||||||
snapshots.sort_unstable();
|
snapshots.sort_unstable();
|
||||||
let result = snapshots
|
let result = snapshots
|
||||||
.last()
|
.last()
|
||||||
@@ -167,7 +164,7 @@ impl Dataset {
|
|||||||
Ok(snapshots)
|
Ok(snapshots)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_pool_name(&self, new_pool: &Pool) -> Self {
|
pub fn change_pool_name(&self, new_pool: &ZPool) -> Self {
|
||||||
let new_pool = new_pool.name.as_str();
|
let new_pool = new_pool.name.as_str();
|
||||||
let mut pathbuf = PathBuf::new();
|
let mut pathbuf = PathBuf::new();
|
||||||
pathbuf.push(new_pool);
|
pathbuf.push(new_pool);
|
||||||
|
|||||||
Reference in New Issue
Block a user