|  | 
|  | 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