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