From ef54b4438f4a4003213edc86da54fcef27f7b78f Mon Sep 17 00:00:00 2001 From: Joe McCormick <31295332+iamjoemccormick@users.noreply.github.com> Date: Wed, 14 May 2025 00:54:26 +0000 Subject: [PATCH 1/3] feat: add optional fs-uuid parameter for database initialization --- mgmtd/src/config.rs | 9 +++++++++ mgmtd/src/db.rs | 7 +++++-- mgmtd/src/main.rs | 11 ++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mgmtd/src/config.rs b/mgmtd/src/config.rs index 341a8d5..5e77a03 100644 --- a/mgmtd/src/config.rs +++ b/mgmtd/src/config.rs @@ -107,6 +107,15 @@ generate_structs! { #[serde(skip)] init: bool = false, + /// Optionally specifies the FsUuid when initializing the database. + /// + /// If not provided, a new FsUuid will be generated. + #[arg(long)] + #[arg(hide = true)] + #[arg(value_name = "UUID")] + #[serde(skip)] + fs_uuid: Option = None, + /// Upgrades an outdated management database to the current version, then exits. /// /// Automatically creates a backup of the existing database file in the same directory. diff --git a/mgmtd/src/db.rs b/mgmtd/src/db.rs index 327287a..53ea17e 100644 --- a/mgmtd/src/db.rs +++ b/mgmtd/src/db.rs @@ -37,8 +37,11 @@ pub const MIGRATIONS: &[sqlite::Migration] = include!(concat!(env!("OUT_DIR"), " /// Inserts initial entries into a new database. Remember to commit the transaction after calling /// this function. -pub fn initial_entries(tx: &Transaction) -> Result<()> { - config::set(tx, Config::FsUuid, Uuid::new_v4().to_string())?; +/// +/// If `fs_uuid` is provided, it will be used. Otherwise, a new FsUUID will be generated. +pub fn initial_entries(tx: &Transaction, fs_uuid: Option) -> Result<()> { + let uuid = fs_uuid.unwrap_or_else(|| Uuid::new_v4().to_string()); + config::set(tx, Config::FsUuid, uuid)?; config::set( tx, Config::FsInitDateSecs, diff --git a/mgmtd/src/main.rs b/mgmtd/src/main.rs index 0a85b41..c95fd40 100644 --- a/mgmtd/src/main.rs +++ b/mgmtd/src/main.rs @@ -62,7 +62,11 @@ fn inner_main() -> Result<()> { } if user_config.init || user_config.import_from_v7.is_some() { - init_db(&user_config.db_file, user_config.import_from_v7.as_deref())?; + init_db( + &user_config.db_file, + user_config.import_from_v7.as_deref(), + user_config.fs_uuid.clone(), + )?; return Ok(()); } @@ -150,9 +154,10 @@ doc.beegfs.io.", } /// Create and initialize a new database. Optionally import v7 data from the given path. +/// Optionally the FsUUID can be specified otherwise it will be autogenerated. /// /// The database file is only written to disk if the initialization succeeds. -fn init_db(db_file: &Path, v7_path: Option<&Path>) -> Result<()> { +fn init_db(db_file: &Path, v7_path: Option<&Path>, fs_uuid: Option) -> Result<()> { if db_file.try_exists()? { bail!("Database file {db_file:?} already exists"); } @@ -165,7 +170,7 @@ fn init_db(db_file: &Path, v7_path: Option<&Path>) -> Result<()> { let version = sqlite::migrate_schema(&tx, db::MIGRATIONS).context("Creating schema failed")?; - db::initial_entries(&tx).context("Creating initial entries failed")?; + db::initial_entries(&tx, fs_uuid).context("Creating initial entries failed")?; if let Some(v7_path) = v7_path { db::import_v7(&tx, v7_path).context("v7 management data import failed")?; From 26fee834f4c206f56fc7cf14e6ad89f060bb4ca7 Mon Sep 17 00:00:00 2001 From: Joe McCormick <31295332+iamjoemccormick@users.noreply.github.com> Date: Fri, 16 May 2025 20:01:07 +0000 Subject: [PATCH 2/3] todo(squash): validate provided fs_uuid is a valid uuid --- mgmtd/src/db.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mgmtd/src/db.rs b/mgmtd/src/db.rs index 53ea17e..3234644 100644 --- a/mgmtd/src/db.rs +++ b/mgmtd/src/db.rs @@ -40,7 +40,17 @@ pub const MIGRATIONS: &[sqlite::Migration] = include!(concat!(env!("OUT_DIR"), " /// /// If `fs_uuid` is provided, it will be used. Otherwise, a new FsUUID will be generated. pub fn initial_entries(tx: &Transaction, fs_uuid: Option) -> Result<()> { - let uuid = fs_uuid.unwrap_or_else(|| Uuid::new_v4().to_string()); + let uuid = match fs_uuid { + Some(ref s) => { + let parsed = + Uuid::parse_str(s).map_err(|_| anyhow!("Provided fs_uuid is not a valid UUID"))?; + if parsed.get_version_num() != 4 { + bail!("Provided fs_uuid is not a valid v4 UUID"); + } + s.clone() + } + None => Uuid::new_v4().to_string(), + }; config::set(tx, Config::FsUuid, uuid)?; config::set( tx, From 9de3a93fe540e9ee2a678dd51f8a75891e42e05f Mon Sep 17 00:00:00 2001 From: Joe McCormick <31295332+iamjoemccormick@users.noreply.github.com> Date: Thu, 22 May 2025 11:18:41 +0000 Subject: [PATCH 3/3] todo(squash): address review feedback --- mgmtd/src/config.rs | 10 +++++++++- mgmtd/src/db.rs | 15 ++------------- mgmtd/src/main.rs | 11 ++++++----- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/mgmtd/src/config.rs b/mgmtd/src/config.rs index 5e77a03..245228b 100644 --- a/mgmtd/src/config.rs +++ b/mgmtd/src/config.rs @@ -11,6 +11,7 @@ use std::fmt::Debug; use std::ops::RangeInclusive; use std::path::PathBuf; use std::time::Duration; +use uuid::Uuid; /// Generates `Config` to be filled by the functions below and exported to be used by the program. /// @@ -111,10 +112,11 @@ generate_structs! { /// /// If not provided, a new FsUuid will be generated. #[arg(long)] + #[arg(num_args = 1)] #[arg(hide = true)] #[arg(value_name = "UUID")] #[serde(skip)] - fs_uuid: Option = None, + fs_uuid: Option = None, /// Upgrades an outdated management database to the current version, then exits. /// @@ -402,6 +404,12 @@ generate_structs! { impl Config { pub fn check_validity(&self) -> Result<()> { + if let Some(ref uuid) = self.fs_uuid { + if uuid.get_version_num() != 4 { + bail!("Provided file system UUID is not a valid v4 UUID"); + } + } + if self.quota_enforce && !self.quota_enable { bail!("Quota enforcement requires quota being enabled"); } diff --git a/mgmtd/src/db.rs b/mgmtd/src/db.rs index 3234644..e6fb0ab 100644 --- a/mgmtd/src/db.rs +++ b/mgmtd/src/db.rs @@ -39,19 +39,8 @@ pub const MIGRATIONS: &[sqlite::Migration] = include!(concat!(env!("OUT_DIR"), " /// this function. /// /// If `fs_uuid` is provided, it will be used. Otherwise, a new FsUUID will be generated. -pub fn initial_entries(tx: &Transaction, fs_uuid: Option) -> Result<()> { - let uuid = match fs_uuid { - Some(ref s) => { - let parsed = - Uuid::parse_str(s).map_err(|_| anyhow!("Provided fs_uuid is not a valid UUID"))?; - if parsed.get_version_num() != 4 { - bail!("Provided fs_uuid is not a valid v4 UUID"); - } - s.clone() - } - None => Uuid::new_v4().to_string(), - }; - config::set(tx, Config::FsUuid, uuid)?; +pub fn initial_entries(tx: &Transaction, fs_uuid: Option) -> Result<()> { + config::set(tx, Config::FsUuid, fs_uuid.unwrap_or_else(Uuid::new_v4))?; config::set( tx, Config::FsInitDateSecs, diff --git a/mgmtd/src/main.rs b/mgmtd/src/main.rs index c95fd40..385d0a7 100644 --- a/mgmtd/src/main.rs +++ b/mgmtd/src/main.rs @@ -11,6 +11,7 @@ use std::fmt::Write; use std::path::Path; use std::{fs, panic}; use tokio::signal::ctrl_c; +use uuid::Uuid; fn main() -> Result<(), i32> { inner_main().map_err(|err| { @@ -65,7 +66,7 @@ fn inner_main() -> Result<()> { init_db( &user_config.db_file, user_config.import_from_v7.as_deref(), - user_config.fs_uuid.clone(), + user_config.fs_uuid, )?; return Ok(()); } @@ -153,11 +154,11 @@ doc.beegfs.io.", }) } -/// Create and initialize a new database. Optionally import v7 data from the given path. -/// Optionally the FsUUID can be specified otherwise it will be autogenerated. +/// Create and initialize a new database. /// -/// The database file is only written to disk if the initialization succeeds. -fn init_db(db_file: &Path, v7_path: Option<&Path>, fs_uuid: Option) -> Result<()> { +/// Optionally import v7 data from the given path. Optionally the FsUUID can be specified otherwise +/// it will be autogenerated. The database file is only written to disk if initialization succeeds. +fn init_db(db_file: &Path, v7_path: Option<&Path>, fs_uuid: Option) -> Result<()> { if db_file.try_exists()? { bail!("Database file {db_file:?} already exists"); }