]> git.proxmox.com Git - proxmox-backup.git/blame - src/backup/datastore.rs
backup/datastore.rs: try to create useful directory layout
[proxmox-backup.git] / src / backup / datastore.rs
CommitLineData
529de6c7
DM
1use failure::*;
2
3d5c11e5 3use std::path::{PathBuf, Path};
2c32fdde
DM
4use std::collections::HashMap;
5use lazy_static::lazy_static;
6use std::sync::{Mutex, Arc};
529de6c7
DM
7
8use crate::config::datastore;
9use super::chunk_store::*;
10use super::image_index::*;
0433db19 11use super::archive_index::*;
529de6c7 12
ff3d3100
DM
13use chrono::{Utc, TimeZone};
14
529de6c7 15pub struct DataStore {
1629d2ad 16 chunk_store: Arc<ChunkStore>,
64e53b28 17 gc_mutex: Mutex<bool>,
529de6c7
DM
18}
19
2c32fdde
DM
20lazy_static!{
21 static ref datastore_map: Mutex<HashMap<String, Arc<DataStore>>> = Mutex::new(HashMap::new());
22}
23
529de6c7
DM
24impl DataStore {
25
2c32fdde
DM
26 pub fn lookup_datastore(name: &str) -> Result<Arc<DataStore>, Error> {
27
28 let config = datastore::config()?;
29 let (_, store_config) = config.sections.get(name)
30 .ok_or(format_err!("no such datastore '{}'", name))?;
31
32 let path = store_config["path"].as_str().unwrap();
33
34 let mut map = datastore_map.lock().unwrap();
35
36 if let Some(datastore) = map.get(name) {
37 // Compare Config - if changed, create new Datastore object!
a198d74f 38 if datastore.chunk_store.base == PathBuf::from(path) {
2c32fdde
DM
39 return Ok(datastore.clone());
40 }
41 }
42
43 if let Ok(datastore) = DataStore::open(name) {
44 let datastore = Arc::new(datastore);
45 map.insert(name.to_string(), datastore.clone());
46 return Ok(datastore);
47 }
48
49 bail!("store not found");
50 }
51
529de6c7
DM
52 pub fn open(store_name: &str) -> Result<Self, Error> {
53
54 let config = datastore::config()?;
55 let (_, store_config) = config.sections.get(store_name)
56 .ok_or(format_err!("no such datastore '{}'", store_name))?;
57
58 let path = store_config["path"].as_str().unwrap();
59
277fc5a3 60 let chunk_store = ChunkStore::open(store_name, path)?;
529de6c7
DM
61
62 Ok(Self {
1629d2ad 63 chunk_store: Arc::new(chunk_store),
64e53b28 64 gc_mutex: Mutex::new(false),
529de6c7
DM
65 })
66 }
67
bcd879cf 68 pub fn create_image_writer<P: AsRef<Path>>(&self, filename: P, size: usize, chunk_size: usize) -> Result<ImageIndexWriter, Error> {
529de6c7 69
150f1bd8 70 let index = ImageIndexWriter::create(self.chunk_store.clone(), filename.as_ref(), size, chunk_size)?;
529de6c7
DM
71
72 Ok(index)
73 }
74
03e4753d 75 pub fn open_image_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ImageIndexReader, Error> {
529de6c7 76
150f1bd8 77 let index = ImageIndexReader::open(self.chunk_store.clone(), filename.as_ref())?;
529de6c7
DM
78
79 Ok(index)
80 }
3d5c11e5 81
0433db19
DM
82 pub fn create_archive_writer<P: AsRef<Path>>(
83 &self, filename: P,
84 chunk_size: usize
85 ) -> Result<ArchiveIndexWriter, Error> {
86
1629d2ad
DM
87 let index = ArchiveIndexWriter::create(
88 self.chunk_store.clone(), filename.as_ref(), chunk_size)?;
0433db19
DM
89
90 Ok(index)
91 }
ff3d3100 92
77703d95
DM
93 pub fn open_archive_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ArchiveIndexReader, Error> {
94
150f1bd8 95 let index = ArchiveIndexReader::open(self.chunk_store.clone(), filename.as_ref())?;
77703d95
DM
96
97 Ok(index)
98 }
99
ff3d3100
DM
100 pub fn base_path(&self) -> PathBuf {
101 self.chunk_store.base_path()
102 }
103
104 pub fn create_backup_dir(
105 &self,
106 backup_type: &str,
107 backup_id: &str,
108 backup_time: i64,
109 ) -> Result<PathBuf, Error> {
110 let mut relative_path = PathBuf::new();
111
112 relative_path.push(backup_type);
113
114 relative_path.push(backup_id);
115
116 let dt = Utc.timestamp(backup_time, 0);
117 let date_str = dt.format("%Y-%m-%dT%H:%M:%S").to_string();
118
119 println!("date: {}", date_str);
120
121 relative_path.push(&date_str);
122
123
124 let mut full_path = self.base_path();
125 full_path.push(&relative_path);
126
127 std::fs::create_dir_all(&full_path)?;
128
129 Ok(relative_path)
130 }
131
3d5c11e5 132 pub fn list_images(&self) -> Result<Vec<PathBuf>, Error> {
ff3d3100 133 let base = self.base_path();
3d5c11e5
DM
134
135 let mut list = vec![];
136
ff3d3100 137 // fixme: walk into subdirs ...
3d5c11e5
DM
138 for entry in std::fs::read_dir(base)? {
139 let entry = entry?;
140 if entry.file_type()?.is_file() {
141 let path = entry.path();
142 if let Some(ext) = path.extension() {
64e53b28 143 if ext == "iidx" {
3d5c11e5 144 list.push(path);
77703d95
DM
145 } else if ext == "aidx" {
146 list.push(path);
3d5c11e5
DM
147 }
148 }
149 }
150 }
151
152 Ok(list)
153 }
154
64e53b28 155 fn mark_used_chunks(&self, status: &mut GarbageCollectionStatus) -> Result<(), Error> {
3d5c11e5
DM
156
157 let image_list = self.list_images()?;
158
159 for path in image_list {
77703d95
DM
160 if let Some(ext) = path.extension() {
161 if ext == "iidx" {
bc616633 162 let index = self.open_image_reader(&path)?;
77703d95
DM
163 index.mark_used_chunks(status)?;
164 } else if ext == "aidx" {
bc616633 165 let index = self.open_archive_reader(&path)?;
77703d95
DM
166 index.mark_used_chunks(status)?;
167 }
168 }
3d5c11e5
DM
169 }
170
171 Ok(())
172 }
173
03e4753d 174 pub fn garbage_collection(&self) -> Result<(), Error> {
3d5c11e5 175
a198d74f 176 if let Ok(ref mut _mutex) = self.gc_mutex.try_lock() {
e95950e4 177
64e53b28
DM
178 let mut gc_status = GarbageCollectionStatus::default();
179 gc_status.used_bytes = 0;
6ea3a0b7 180
64e53b28
DM
181 println!("Start GC phase1 (mark chunks)");
182
183 self.mark_used_chunks(&mut gc_status)?;
184
185 println!("Start GC phase2 (sweep unused chunks)");
77703d95 186 self.chunk_store.sweep_unused_chunks(&mut gc_status)?;
64e53b28
DM
187
188 println!("Used bytes: {}", gc_status.used_bytes);
189 println!("Used chunks: {}", gc_status.used_chunks);
190 println!("Disk bytes: {}", gc_status.disk_bytes);
191 println!("Disk chunks: {}", gc_status.disk_chunks);
192
193 } else {
194 println!("Start GC failed - (already running/locked)");
195 }
3d5c11e5
DM
196
197 Ok(())
198 }
529de6c7 199}