|
| 1 | +// This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | +// License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | +// file, You can obtain one at https://mozilla.org/MPL/2.0/. |
| 4 | + |
| 5 | +//! Persistent storage for the trust quorum task |
| 6 | +//! |
| 7 | +//! We write two pieces of data to M.2 devices in production via |
| 8 | +//! [`omicron_common::ledger::Ledger`]: |
| 9 | +//! |
| 10 | +//! 1. [`trust_quorum_protocol::PersistentState`] for trust quorum state |
| 11 | +//! 2. A network config blob required for pre-rack-unlock configuration |
| 12 | +
|
| 13 | +use camino::Utf8PathBuf; |
| 14 | +use omicron_common::ledger::{Ledger, Ledgerable}; |
| 15 | +use serde::{Deserialize, Serialize}; |
| 16 | +use slog::{Logger, info}; |
| 17 | +use trust_quorum_protocol::PersistentState; |
| 18 | + |
| 19 | +/// A wrapper type around [`PersistentState`] for use as a [`Ledger`] |
| 20 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 21 | +pub struct PersistentStateLedger { |
| 22 | + pub generation: u64, |
| 23 | + pub state: PersistentState, |
| 24 | +} |
| 25 | + |
| 26 | +impl Ledgerable for PersistentStateLedger { |
| 27 | + fn is_newer_than(&self, other: &Self) -> bool { |
| 28 | + self.generation > other.generation |
| 29 | + } |
| 30 | + |
| 31 | + fn generation_bump(&mut self) { |
| 32 | + self.generation += 1; |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +impl PersistentStateLedger { |
| 37 | + /// Save the persistent state to a ledger and return the new generation |
| 38 | + /// number. |
| 39 | + /// |
| 40 | + /// Panics if the ledger cannot be saved. |
| 41 | + pub async fn save( |
| 42 | + log: &Logger, |
| 43 | + paths: Vec<Utf8PathBuf>, |
| 44 | + generation: u64, |
| 45 | + state: PersistentState, |
| 46 | + ) -> u64 { |
| 47 | + let persistent_state = PersistentStateLedger { generation, state }; |
| 48 | + let mut ledger = Ledger::new_with(log, paths, persistent_state); |
| 49 | + ledger |
| 50 | + .commit() |
| 51 | + .await |
| 52 | + .expect("Critical: Failed to save bootstore ledger for Fsm::State"); |
| 53 | + ledger.data().generation |
| 54 | + } |
| 55 | + |
| 56 | + /// Return Some(`PersistentStateLedger`) if it exists on disk, otherwise |
| 57 | + /// return `None`. |
| 58 | + pub async fn load( |
| 59 | + log: &Logger, |
| 60 | + paths: Vec<Utf8PathBuf>, |
| 61 | + ) -> Option<PersistentStateLedger> { |
| 62 | + let Some(ledger) = |
| 63 | + Ledger::<PersistentStateLedger>::new(&log, paths).await |
| 64 | + else { |
| 65 | + return None; |
| 66 | + }; |
| 67 | + let persistent_state = ledger.into_inner(); |
| 68 | + info!( |
| 69 | + log, |
| 70 | + "Loaded persistent state from ledger with generation {}", |
| 71 | + persistent_state.generation |
| 72 | + ); |
| 73 | + Some(persistent_state) |
| 74 | + } |
| 75 | +} |
0 commit comments