Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions mgmtd/assets/beegfs-mgmtd.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
19 changes: 16 additions & 3 deletions mgmtd/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,15 @@ generate_structs! {
#[serde(skip)]
fs_uuid: Option<Uuid> = 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.
///
Expand Down Expand Up @@ -584,3 +586,14 @@ impl From<LogLevel> 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,
}
39 changes: 36 additions & 3 deletions mgmtd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -82,8 +83,19 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
info.use_ipv6,
);

let mut db = sqlite::Connections::new(info.user_config.db_file.as_path());
sqlite::check_schema_async(&mut db, db::MIGRATIONS).await?;
let db = sqlite::Connections::new(info.user_config.db_file.as_path());

let schema_check = db
.read_tx(|tx| Ok(sqlite::check_schema(tx, db::MIGRATIONS)))
.await?;

if info.user_config.db_upgrade == DbUpgrade::Auto {
if schema_check.is_err() {
upgrade_db(&db).await?;
}
} else {
schema_check?;
}

log::info!(
"Opened database at {:?}",
Expand Down Expand Up @@ -152,6 +164,27 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
})
}

// Db schema auto upgrade. This requires slightly different handling and logging than the version
// in main.rs.
async fn upgrade_db(db: &sqlite::Connections) -> 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 {
Expand Down
12 changes: 7 additions & 5 deletions mgmtd/src/main.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -16,6 +16,7 @@ use uuid::Uuid;

fn main() -> Result<(), i32> {
inner_main().map_err(|err| {
log::error!("{err:#}");
eprintln!("{err:#}");
1
})?;
Expand Down Expand Up @@ -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(());
}
Expand Down Expand Up @@ -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(())
}

Expand Down
16 changes: 4 additions & 12 deletions sqlite/src/migration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::Connections;
use anyhow::{Context, Result, anyhow, bail};
use std::fmt::Write;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -125,18 +124,11 @@ pub fn flatten_migrations(migrations: &[OwnedMigration]) -> Result<String> {
}

/// 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(())
Expand Down Expand Up @@ -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<PathBuf> {
pub fn backup_db(conn: &rusqlite::Connection) -> Result<PathBuf> {
let version: u32 = conn.query_row("PRAGMA user_version", [], |row| row.get(0))?;

let Some(db_file) = conn.path() else {
Expand Down
Loading