veridian_kernel/services/csi/
snapshot.rs1#![allow(dead_code)]
6
7use alloc::{collections::BTreeMap, string::String, vec::Vec};
8use core::sync::atomic::{AtomicU64, Ordering};
9
10#[derive(Debug, Clone)]
16pub struct Snapshot {
17 pub id: u64,
19 pub source_volume_id: u64,
21 pub size_bytes: u64,
23 pub created_tick: u64,
25 pub ready: bool,
27 pub name: String,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum SnapshotError {
34 NotFound(u64),
36 SourceNotFound(u64),
38 AlreadyExists(String),
40 NotReady(u64),
42}
43
44static NEXT_SNAPSHOT_ID: AtomicU64 = AtomicU64::new(1);
50
51fn alloc_snapshot_id() -> u64 {
52 NEXT_SNAPSHOT_ID.fetch_add(1, Ordering::Relaxed)
53}
54
55#[derive(Debug)]
57pub struct SnapshotService {
58 snapshots: BTreeMap<u64, Snapshot>,
60 name_index: BTreeMap<String, u64>,
62 known_volumes: BTreeMap<u64, u64>, }
65
66impl Default for SnapshotService {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl SnapshotService {
73 pub fn new() -> Self {
75 SnapshotService {
76 snapshots: BTreeMap::new(),
77 name_index: BTreeMap::new(),
78 known_volumes: BTreeMap::new(),
79 }
80 }
81
82 pub fn register_volume(&mut self, volume_id: u64, capacity_bytes: u64) {
84 self.known_volumes.insert(volume_id, capacity_bytes);
85 }
86
87 pub fn unregister_volume(&mut self, volume_id: u64) {
89 self.known_volumes.remove(&volume_id);
90 }
91
92 pub fn create_snapshot(
94 &mut self,
95 name: String,
96 source_volume_id: u64,
97 current_tick: u64,
98 ) -> Result<u64, SnapshotError> {
99 if self.name_index.contains_key(&name) {
101 return Err(SnapshotError::AlreadyExists(name));
102 }
103
104 let capacity = self
106 .known_volumes
107 .get(&source_volume_id)
108 .ok_or(SnapshotError::SourceNotFound(source_volume_id))?;
109
110 let id = alloc_snapshot_id();
111 let snapshot = Snapshot {
112 id,
113 source_volume_id,
114 size_bytes: *capacity,
115 created_tick: current_tick,
116 ready: true, name: name.clone(),
118 };
119
120 self.name_index.insert(name, id);
121 self.snapshots.insert(id, snapshot);
122 Ok(id)
123 }
124
125 pub fn delete_snapshot(&mut self, snapshot_id: u64) -> Result<(), SnapshotError> {
127 let snapshot = self
128 .snapshots
129 .remove(&snapshot_id)
130 .ok_or(SnapshotError::NotFound(snapshot_id))?;
131 self.name_index.remove(&snapshot.name);
132 Ok(())
133 }
134
135 pub fn list_snapshots(&self, source_filter: Option<u64>) -> Vec<&Snapshot> {
137 self.snapshots
138 .values()
139 .filter(|s| source_filter.is_none() || Some(s.source_volume_id) == source_filter)
140 .collect()
141 }
142
143 pub fn get_snapshot(&self, snapshot_id: u64) -> Option<&Snapshot> {
145 self.snapshots.get(&snapshot_id)
146 }
147
148 pub fn get_snapshot_by_name(&self, name: &str) -> Option<&Snapshot> {
150 self.name_index
151 .get(name)
152 .and_then(|id| self.snapshots.get(id))
153 }
154
155 pub fn snapshot_count(&self) -> usize {
157 self.snapshots.len()
158 }
159}
160
161#[cfg(test)]
166mod tests {
167 #[allow(unused_imports)]
168 use alloc::string::ToString;
169
170 use super::*;
171
172 fn make_service() -> SnapshotService {
173 let mut svc = SnapshotService::new();
174 svc.register_volume(1, 1024 * 1024 * 1024);
175 svc.register_volume(2, 2048 * 1024 * 1024);
176 svc
177 }
178
179 #[test]
180 fn test_create_snapshot() {
181 let mut svc = make_service();
182 let id = svc.create_snapshot(String::from("snap-1"), 1, 100).unwrap();
183 let snap = svc.get_snapshot(id).unwrap();
184 assert_eq!(snap.source_volume_id, 1);
185 assert!(snap.ready);
186 }
187
188 #[test]
189 fn test_create_snapshot_unknown_volume() {
190 let mut svc = make_service();
191 assert_eq!(
192 svc.create_snapshot(String::from("snap"), 999, 100),
193 Err(SnapshotError::SourceNotFound(999))
194 );
195 }
196
197 #[test]
198 fn test_create_duplicate_name() {
199 let mut svc = make_service();
200 svc.create_snapshot(String::from("snap-1"), 1, 100).unwrap();
201 assert!(svc.create_snapshot(String::from("snap-1"), 2, 200).is_err());
202 }
203
204 #[test]
205 fn test_delete_snapshot() {
206 let mut svc = make_service();
207 let id = svc.create_snapshot(String::from("snap-1"), 1, 100).unwrap();
208 svc.delete_snapshot(id).unwrap();
209 assert_eq!(svc.snapshot_count(), 0);
210 }
211
212 #[test]
213 fn test_list_snapshots_filter() {
214 let mut svc = make_service();
215 svc.create_snapshot(String::from("s1"), 1, 100).unwrap();
216 svc.create_snapshot(String::from("s2"), 1, 200).unwrap();
217 svc.create_snapshot(String::from("s3"), 2, 300).unwrap();
218
219 let all = svc.list_snapshots(None);
220 assert_eq!(all.len(), 3);
221
222 let vol1 = svc.list_snapshots(Some(1));
223 assert_eq!(vol1.len(), 2);
224 }
225
226 #[test]
227 fn test_get_by_name() {
228 let mut svc = make_service();
229 svc.create_snapshot(String::from("my-snap"), 1, 100)
230 .unwrap();
231 assert!(svc.get_snapshot_by_name("my-snap").is_some());
232 assert!(svc.get_snapshot_by_name("other").is_none());
233 }
234}