kratad/db/
network.rs

1use crate::db::KrataDatabase;
2use advmac::MacAddr6;
3use anyhow::Result;
4use krata::v1::common::NetworkReservation as ApiNetworkReservation;
5use log::error;
6use redb::{ReadableTable, TableDefinition};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::net::{Ipv4Addr, Ipv6Addr};
10use uuid::Uuid;
11
12const NETWORK_RESERVATION_TABLE: TableDefinition<u128, &[u8]> =
13    TableDefinition::new("network-reservation");
14
15#[derive(Clone)]
16pub struct NetworkReservationStore {
17    db: KrataDatabase,
18}
19
20impl NetworkReservationStore {
21    pub fn open(db: KrataDatabase) -> Result<Self> {
22        let write = db.database.begin_write()?;
23        let _ = write.open_table(NETWORK_RESERVATION_TABLE);
24        write.commit()?;
25        Ok(NetworkReservationStore { db })
26    }
27
28    pub async fn read(&self, id: Uuid) -> Result<Option<NetworkReservation>> {
29        let read = self.db.database.begin_read()?;
30        let table = read.open_table(NETWORK_RESERVATION_TABLE)?;
31        let Some(entry) = table.get(id.to_u128_le())? else {
32            return Ok(None);
33        };
34        let bytes = entry.value();
35        Ok(Some(serde_json::from_slice(bytes)?))
36    }
37
38    pub async fn list(&self) -> Result<HashMap<Uuid, NetworkReservation>> {
39        enum ListEntry {
40            Valid(Uuid, NetworkReservation),
41            Invalid(Uuid),
42        }
43        let mut reservations: HashMap<Uuid, NetworkReservation> = HashMap::new();
44
45        let corruptions = {
46            let read = self.db.database.begin_read()?;
47            let table = read.open_table(NETWORK_RESERVATION_TABLE)?;
48            table
49                .iter()?
50                .flat_map(|result| {
51                    result.map(|(key, value)| {
52                        let uuid = Uuid::from_u128_le(key.value());
53                        match serde_json::from_slice::<NetworkReservation>(value.value()) {
54                            Ok(reservation) => ListEntry::Valid(uuid, reservation),
55                            Err(error) => {
56                                error!(
57                                    "found invalid network reservation in database for uuid {}: {}",
58                                    uuid, error
59                                );
60                                ListEntry::Invalid(uuid)
61                            }
62                        }
63                    })
64                })
65                .filter_map(|entry| match entry {
66                    ListEntry::Valid(uuid, reservation) => {
67                        reservations.insert(uuid, reservation);
68                        None
69                    }
70
71                    ListEntry::Invalid(uuid) => Some(uuid),
72                })
73                .collect::<Vec<Uuid>>()
74        };
75
76        if !corruptions.is_empty() {
77            let write = self.db.database.begin_write()?;
78            let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?;
79            for corruption in corruptions {
80                table.remove(corruption.to_u128_le())?;
81            }
82        }
83
84        Ok(reservations)
85    }
86
87    pub async fn update(&self, id: Uuid, entry: NetworkReservation) -> Result<()> {
88        let write = self.db.database.begin_write()?;
89        {
90            let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?;
91            let bytes = serde_json::to_vec(&entry)?;
92            table.insert(id.to_u128_le(), bytes.as_slice())?;
93        }
94        write.commit()?;
95        Ok(())
96    }
97
98    pub async fn remove(&self, id: Uuid) -> Result<()> {
99        let write = self.db.database.begin_write()?;
100        {
101            let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?;
102            table.remove(id.to_u128_le())?;
103        }
104        write.commit()?;
105        Ok(())
106    }
107}
108
109#[derive(Serialize, Deserialize, Clone, Debug)]
110pub struct NetworkReservation {
111    pub uuid: String,
112    pub ipv4: Ipv4Addr,
113    pub ipv6: Ipv6Addr,
114    pub mac: MacAddr6,
115    pub ipv4_prefix: u8,
116    pub ipv6_prefix: u8,
117    pub gateway_ipv4: Ipv4Addr,
118    pub gateway_ipv6: Ipv6Addr,
119    pub gateway_mac: MacAddr6,
120}
121
122impl From<NetworkReservation> for ApiNetworkReservation {
123    fn from(val: NetworkReservation) -> Self {
124        ApiNetworkReservation {
125            uuid: val.uuid,
126            ipv4: format!("{}/{}", val.ipv4, val.ipv4_prefix),
127            ipv6: format!("{}/{}", val.ipv6, val.ipv6_prefix),
128            mac: val.mac.to_string().to_lowercase().replace('-', ":"),
129            gateway_ipv4: format!("{}/{}", val.gateway_ipv4, val.ipv4_prefix),
130            gateway_ipv6: format!("{}/{}", val.gateway_ipv6, val.ipv6_prefix),
131            gateway_mac: val.gateway_mac.to_string().to_lowercase().replace('-', ":"),
132        }
133    }
134}