]>
git.proxmox.com Git - proxmox-backup.git/blob - src/backup/datastore.rs
3 use std
::path
::{PathBuf, Path}
;
4 use std
::collections
::HashMap
;
5 use lazy_static
::lazy_static
;
6 use std
::sync
::{Mutex, Arc}
;
8 use crate::config
::datastore
;
9 use super::chunk_store
::*;
10 use super::image_index
::*;
11 use super::archive_index
::*;
13 use chrono
::{Utc, TimeZone}
;
15 pub struct DataStore
{
16 chunk_store
: Arc
<ChunkStore
>,
17 gc_mutex
: Mutex
<bool
>,
21 static ref datastore_map
: Mutex
<HashMap
<String
, Arc
<DataStore
>>> = Mutex
::new(HashMap
::new());
26 pub fn lookup_datastore(name
: &str) -> Result
<Arc
<DataStore
>, Error
> {
28 let config
= datastore
::config()?
;
29 let (_
, store_config
) = config
.sections
.get(name
)
30 .ok_or(format_err
!("no such datastore '{}'", name
))?
;
32 let path
= store_config
["path"].as_str().unwrap();
34 let mut map
= datastore_map
.lock().unwrap();
36 if let Some(datastore
) = map
.get(name
) {
37 // Compare Config - if changed, create new Datastore object!
38 if datastore
.chunk_store
.base
== PathBuf
::from(path
) {
39 return Ok(datastore
.clone());
43 if let Ok(datastore
) = DataStore
::open(name
) {
44 let datastore
= Arc
::new(datastore
);
45 map
.insert(name
.to_string(), datastore
.clone());
49 bail
!("store not found");
52 pub fn open(store_name
: &str) -> Result
<Self, Error
> {
54 let config
= datastore
::config()?
;
55 let (_
, store_config
) = config
.sections
.get(store_name
)
56 .ok_or(format_err
!("no such datastore '{}'", store_name
))?
;
58 let path
= store_config
["path"].as_str().unwrap();
60 let chunk_store
= ChunkStore
::open(store_name
, path
)?
;
63 chunk_store
: Arc
::new(chunk_store
),
64 gc_mutex
: Mutex
::new(false),
68 pub fn create_image_writer
<P
: AsRef
<Path
>>(&self, filename
: P
, size
: usize, chunk_size
: usize) -> Result
<ImageIndexWriter
, Error
> {
70 let index
= ImageIndexWriter
::create(self.chunk_store
.clone(), filename
.as_ref(), size
, chunk_size
)?
;
75 pub fn open_image_reader
<P
: AsRef
<Path
>>(&self, filename
: P
) -> Result
<ImageIndexReader
, Error
> {
77 let index
= ImageIndexReader
::open(self.chunk_store
.clone(), filename
.as_ref())?
;
82 pub fn create_archive_writer
<P
: AsRef
<Path
>>(
85 ) -> Result
<ArchiveIndexWriter
, Error
> {
87 let index
= ArchiveIndexWriter
::create(
88 self.chunk_store
.clone(), filename
.as_ref(), chunk_size
)?
;
93 pub fn open_archive_reader
<P
: AsRef
<Path
>>(&self, filename
: P
) -> Result
<ArchiveIndexReader
, Error
> {
95 let index
= ArchiveIndexReader
::open(self.chunk_store
.clone(), filename
.as_ref())?
;
100 pub fn base_path(&self) -> PathBuf
{
101 self.chunk_store
.base_path()
104 pub fn create_backup_dir(
109 ) -> Result
<PathBuf
, Error
> {
110 let mut relative_path
= PathBuf
::new();
112 relative_path
.push(backup_type
);
114 relative_path
.push(backup_id
);
116 let dt
= Utc
.timestamp(backup_time
, 0);
117 let date_str
= dt
.format("%Y-%m-%dT%H:%M:%S").to_string();
119 println
!("date: {}", date_str
);
121 relative_path
.push(&date_str
);
124 let mut full_path
= self.base_path();
125 full_path
.push(&relative_path
);
127 std
::fs
::create_dir_all(&full_path
)?
;
132 pub fn list_images(&self) -> Result
<Vec
<PathBuf
>, Error
> {
133 let base
= self.base_path();
135 let mut list
= vec
![];
137 use walkdir
::WalkDir
;
139 let walker
= WalkDir
::new(&base
).same_file_system(true).into_iter();
141 // make sure we skip .chunks (and other hidden files to keep it simple)
142 fn is_hidden(entry
: &walkdir
::DirEntry
) -> bool
{
145 .map(|s
| s
.starts_with("."))
149 for entry
in walker
.filter_entry(|e
| !is_hidden(e
)) {
150 let path
= entry?
.into_path();
151 if let Some(ext
) = path
.extension() {
154 } else if ext
== "aidx" {
163 fn mark_used_chunks(&self, status
: &mut GarbageCollectionStatus
) -> Result
<(), Error
> {
165 let image_list
= self.list_images()?
;
167 for path
in image_list
{
168 if let Some(ext
) = path
.extension() {
170 let index
= self.open_image_reader(&path
)?
;
171 index
.mark_used_chunks(status
)?
;
172 } else if ext
== "aidx" {
173 let index
= self.open_archive_reader(&path
)?
;
174 index
.mark_used_chunks(status
)?
;
182 pub fn garbage_collection(&self) -> Result
<(), Error
> {
184 if let Ok(ref mut _mutex
) = self.gc_mutex
.try_lock() {
186 let mut gc_status
= GarbageCollectionStatus
::default();
187 gc_status
.used_bytes
= 0;
189 println
!("Start GC phase1 (mark chunks)");
191 self.mark_used_chunks(&mut gc_status
)?
;
193 println
!("Start GC phase2 (sweep unused chunks)");
194 self.chunk_store
.sweep_unused_chunks(&mut gc_status
)?
;
196 println
!("Used bytes: {}", gc_status
.used_bytes
);
197 println
!("Used chunks: {}", gc_status
.used_chunks
);
198 println
!("Disk bytes: {}", gc_status
.disk_bytes
);
199 println
!("Disk chunks: {}", gc_status
.disk_chunks
);
202 println
!("Start GC failed - (already running/locked)");