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}