diff --git a/mgmtd/assets/beegfs-mgmtd.toml b/mgmtd/assets/beegfs-mgmtd.toml index 55acc4f..b5bd451 100644 --- a/mgmtd/assets/beegfs-mgmtd.toml +++ b/mgmtd/assets/beegfs-mgmtd.toml @@ -8,6 +8,12 @@ # binary with `--help` to display it. +# Decides how to upgrade the managements database schema, when required. +# Can be set to "auto" to perform an auto upgrade on startup without having to manually run with +# the --db-upgrade flag. Automatically creates a backup of the existing database file in the same +# directory. +# db-upgrade = "false" + # Managements database file location. # db-file = "/var/lib/beegfs/mgmtd.sqlite" diff --git a/mgmtd/src/config.rs b/mgmtd/src/config.rs index 19fe43a..302e5f0 100644 --- a/mgmtd/src/config.rs +++ b/mgmtd/src/config.rs @@ -119,13 +119,15 @@ generate_structs! { #[serde(skip)] fs_uuid: Option = None, - /// Upgrades an outdated management database to the current version, then exits. + /// Upgrades an outdated management database schema to the current version, then exits. /// + /// Can be set to "auto" to perform an auto upgrade on startup without having to manually run + /// with the --db-upgrade flag. /// Automatically creates a backup of the existing database file in the same directory. #[arg(long)] #[arg(num_args = 0..=1, default_missing_value = "true")] - #[serde(skip)] - upgrade: bool = false, + #[arg(alias("upgrade"))] + db_upgrade: DbUpgrade = DbUpgrade::False, /// Imports a BeeGFS v7 installation from the provided directory into a new database. /// @@ -584,3 +586,14 @@ impl From for LevelFilter { } } } + +/// DB upgrade mode +#[derive(Clone, Debug, PartialEq, Eq, ValueEnum, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum DbUpgrade { + // We never want the True setting in the config file, so we skip this one + #[serde(skip)] + True, + False, + Auto, +} diff --git a/mgmtd/src/lib.rs b/mgmtd/src/lib.rs index ff25820..0dbb24b 100644 --- a/mgmtd/src/lib.rs +++ b/mgmtd/src/lib.rs @@ -14,8 +14,9 @@ mod types; use crate::app::RuntimeApp; use crate::config::Config; -use anyhow::Result; +use anyhow::{Context, Result}; use app::App; +use config::DbUpgrade; use db::node_nic::ReplaceNic; use license::LicenseVerifier; use shared::bee_msg::target::RefreshTargetStates; @@ -82,8 +83,19 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result Result Result<()> { + db.conn(|conn| { + let backup_file = sqlite::backup_db(conn)?; + log::warn!("Old database backed up to {backup_file:?}"); + Ok(()) + }) + .await?; + + let version = db + .write_tx(|tx| { + sqlite::migrate_schema(tx, db::MIGRATIONS) + .with_context(|| "Upgrading database schema failed") + }) + .await?; + + log::warn!("Database upgraded to version {version}"); + Ok(()) +} + /// Controls the running application. #[derive(Debug)] pub struct RunControl { diff --git a/mgmtd/src/main.rs b/mgmtd/src/main.rs index 4ce5b95..0b90f8b 100644 --- a/mgmtd/src/main.rs +++ b/mgmtd/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result, anyhow, bail}; use log::LevelFilter; -use mgmtd::config::LogTarget; +use mgmtd::config::{DbUpgrade, LogTarget}; use mgmtd::db::{self}; use mgmtd::license::LicenseVerifier; use mgmtd::{StaticInfo, start}; @@ -16,6 +16,7 @@ use uuid::Uuid; fn main() -> Result<(), i32> { inner_main().map_err(|err| { + log::error!("{err:#}"); eprintln!("{err:#}"); 1 })?; @@ -72,7 +73,7 @@ fn inner_main() -> Result<()> { return Ok(()); } - if user_config.upgrade { + if user_config.db_upgrade == DbUpgrade::True { upgrade_db(&user_config.db_file)?; return Ok(()); } @@ -217,15 +218,16 @@ beegfs-mgmtd.conf. Before starting the management, you must MANUALLY transfer yo fn upgrade_db(db_file: &Path) -> Result<()> { let mut conn = sqlite::open(db_file)?; - let backup_file = sqlite::backup_db(&mut conn)?; + let tx = conn.transaction()?; + + let backup_file = sqlite::backup_db(&tx)?; println!("Old database backed up to {backup_file:?}"); - let tx = conn.transaction()?; let version = sqlite::migrate_schema(&tx, db::MIGRATIONS) .with_context(|| "Upgrading database schema failed")?; tx.commit()?; - println!("Upgraded database to version {version}"); + println!("Database upgraded to version {version}"); Ok(()) } diff --git a/sqlite/src/migration.rs b/sqlite/src/migration.rs index ef32870..b5348cb 100644 --- a/sqlite/src/migration.rs +++ b/sqlite/src/migration.rs @@ -1,4 +1,3 @@ -use crate::Connections; use anyhow::{Context, Result, anyhow, bail}; use std::fmt::Write; use std::path::{Path, PathBuf}; @@ -125,18 +124,11 @@ pub fn flatten_migrations(migrations: &[OwnedMigration]) -> Result { } /// Checks that the database schema is up to date to the current latest migrations -pub async fn check_schema_async( - conn: &mut Connections, - migrations: &'static [Migration], -) -> Result<()> { +pub fn check_schema(tx: &rusqlite::Transaction, migrations: &'static [Migration]) -> Result<()> { let (base, latest) = check_migration_versions(migrations.iter().map(|m| m.version))?; - let version: u32 = conn - .read_tx(|tx| { - // The databases version is stored in this special sqlite header variable - Ok(tx.query_row("PRAGMA user_version", [], |row| row.get(0))?) - }) - .await?; + // The databases version is stored in this special sqlite header variable + let version: u32 = tx.query_row("PRAGMA user_version", [], |row| row.get(0))?; if version == latest { Ok(()) @@ -189,7 +181,7 @@ pub fn migrate_schema(tx: &rusqlite::Transaction, migrations: &[Migration]) -> R } /// Safely backs up the database -pub fn backup_db(conn: &mut rusqlite::Connection) -> Result { +pub fn backup_db(conn: &rusqlite::Connection) -> Result { let version: u32 = conn.query_row("PRAGMA user_version", [], |row| row.get(0))?; let Some(db_file) = conn.path() else {