]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/backup/prune.rs: implement --keep-hourly
authorDietmar Maurer <dietmar@proxmox.com>
Sat, 7 Dec 2019 10:23:33 +0000 (11:23 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Sat, 7 Dec 2019 10:23:33 +0000 (11:23 +0100)
docs/administration-guide.rst
src/api2/admin/datastore.rs
src/backup/prune.rs
tests/prune.rs

index e2d3c6a5d3dc00ef5bf8abc45840b29174492280..584129a12ce82428cdf5885945f59efe7a9cf889 100644 (file)
@@ -425,12 +425,16 @@ following retention options:
 ``--keep-last <N>``
   Keep the last ``<N>`` backup snapshots.
 
+``--keep-hourly <N>``
+  Keep backups for the last ``<N>`` different hours. If there is more than one
+  backup for a single hour, only the latest one is kept.
+
 ``--keep-daily <N>``
-  Keep backups for ``<N>`` different days. If there is more than one
+  Keep backups for the last ``<N>`` different days. If there is more than one
   backup for a single day, only the latest one is kept.
 
 ``--keep-weekly <N>``
-  Keep backups for ``<N>`` different weeks. If there is more than one
+  Keep backups for the last ``<N>`` different weeks. If there is more than one
   backup for a single week, only the latest one is kept.
 
   .. note:: The weeks start on Monday and end on Sunday. The software
@@ -438,11 +442,11 @@ following retention options:
      the end of the year.
 
 ``--keep-monthly <N>``
-  Keep backups for ``<N>`` different months. If there is more than one
+  Keep backups for the last ``<N>`` different months. If there is more than one
   backup for a single month, only the latest one is kept.
 
 ``--keep-yearly <N>``
-  Keep backups for ``<N>`` different year. If there is more than one
+  Keep backups for the last ``<N>`` different years. If there is more than one
   backup for a single year, only the latest one is kept.
 
 
index 092ba9730dcca0d62f2f57efed023f0a1d39291d..3835149405670f404a605c3f495c36799d0546f7 100644 (file)
@@ -247,6 +247,13 @@ macro_rules! add_common_prune_prameters {
                     .minimum(1)
                     .schema()
             ),
+            (
+                "keep-hourly",
+                true,
+                &IntegerSchema::new("Number of hourly backups to keep.")
+                    .minimum(1)
+                    .schema()
+            ),
             (
                 "keep-last",
                 true,
@@ -310,6 +317,7 @@ fn test_prune(
 
     let prune_options = PruneOptions {
         keep_last: param["keep-last"].as_u64(),
+        keep_hourly: param["keep-hourly"].as_u64(),
         keep_daily: param["keep-daily"].as_u64(),
         keep_weekly: param["keep-weekly"].as_u64(),
         keep_monthly: param["keep-monthly"].as_u64(),
@@ -368,6 +376,7 @@ fn prune(
 
     let prune_options = PruneOptions {
         keep_last: param["keep-last"].as_u64(),
+        keep_hourly: param["keep-hourly"].as_u64(),
         keep_daily: param["keep-daily"].as_u64(),
         keep_weekly: param["keep-weekly"].as_u64(),
         keep_monthly: param["keep-monthly"].as_u64(),
index 573c6954d8f34a0caaa38135d045c68eb9eeba69..e55486dc1e39fc5ef4747b977a4b165ac50175b2 100644 (file)
@@ -2,7 +2,7 @@ use failure::*;
 use std::collections::{HashMap, HashSet};
 use std::path::PathBuf;
 
-use chrono::{DateTime, Datelike, Local};
+use chrono::{DateTime, Timelike, Datelike, Local};
 
 use super::{BackupDir, BackupInfo};
 
@@ -71,6 +71,7 @@ fn remove_incomplete_snapshots(
 
 pub struct PruneOptions {
     pub keep_last: Option<u64>,
+    pub keep_hourly: Option<u64>,
     pub keep_daily: Option<u64>,
     pub keep_weekly: Option<u64>,
     pub keep_monthly: Option<u64>,
@@ -82,6 +83,7 @@ impl PruneOptions {
     pub fn new() -> Self {
         Self {
             keep_last: None,
+            keep_hourly: None,
             keep_daily: None,
             keep_weekly: None,
             keep_monthly: None,
@@ -89,6 +91,11 @@ impl PruneOptions {
         }
     }
 
+    pub fn keep_hourly(mut self, value: Option<u64>) -> Self {
+        self.keep_hourly = value;
+        self
+    }
+
     pub fn keep_last(mut self, value: Option<u64>) -> Self {
         self.keep_last = value;
         self
@@ -117,6 +124,7 @@ impl PruneOptions {
     pub fn keeps_something(&self) -> bool {
         let mut keep_something = false;
         if let Some(count) = self.keep_last { if count > 0 { keep_something = true; } }
+        if let Some(count) = self.keep_hourly { if count > 0 { keep_something = true; } }
         if let Some(count) = self.keep_daily { if count > 0 { keep_something = true; } }
         if let Some(count) = self.keep_weekly { if count > 0 { keep_something = true; } }
         if let Some(count) = self.keep_monthly { if count > 0 { keep_something = true; } }
@@ -142,6 +150,13 @@ pub fn compute_prune_info(
         });
     }
 
+    if let Some(keep_hourly) = options.keep_hourly {
+        mark_selections(&mut mark, &list, keep_hourly as usize, |local_time, _info| {
+            format!("{}/{}/{}/{}", local_time.year(), local_time.month(),
+                    local_time.day(), local_time.hour())
+        });
+    }
+
     if let Some(keep_daily) = options.keep_daily {
         mark_selections(&mut mark, &list, keep_daily as usize, |local_time, _info| {
             format!("{}/{}/{}", local_time.year(), local_time.month(), local_time.day())
@@ -154,7 +169,7 @@ pub fn compute_prune_info(
             let week = iso_week.week();
             // Note: This year number might not match the calendar year number.
             let iso_week_year = iso_week.year();
-            format!("{}/{}", iso_week_year, week);
+            format!("{}/{}", iso_week_year, week)
         });
     }
 
index e1280eb0e1a0fe0beca3ea5339d34abad0929355..33e624045e878ff6543f9e1ec6c4b4b55bdc24bc 100644 (file)
@@ -42,6 +42,40 @@ fn create_info(
 }
 
 #[test]
+fn test_prune_hourly() -> Result<(), Error> {
+
+    let mut orig_list = Vec::new();
+
+    orig_list.push(create_info("host/elsa/2019-11-15T09:39:15Z", false));
+    orig_list.push(create_info("host/elsa/2019-11-15T10:49:15Z", false));
+    orig_list.push(create_info("host/elsa/2019-11-15T10:59:15Z", false));
+    orig_list.push(create_info("host/elsa/2019-11-15T11:39:15Z", false));
+    orig_list.push(create_info("host/elsa/2019-11-15T11:49:15Z", false));
+    orig_list.push(create_info("host/elsa/2019-11-15T11:59:15Z", false));
+
+    let list = orig_list.clone();
+    let options = PruneOptions::new().keep_hourly(Some(3));
+    let remove_list = get_prune_list(list, false, &options);
+    let expect: Vec<PathBuf> = vec![
+        PathBuf::from("host/elsa/2019-11-15T10:49:15Z"),
+        PathBuf::from("host/elsa/2019-11-15T11:39:15Z"),
+        PathBuf::from("host/elsa/2019-11-15T11:49:15Z"),
+    ];
+    assert_eq!(remove_list, expect);
+
+    let list = orig_list.clone();
+    let options = PruneOptions::new().keep_hourly(Some(2));
+    let remove_list = get_prune_list(list, true, &options);
+    let expect: Vec<PathBuf> = vec![
+        PathBuf::from("host/elsa/2019-11-15T10:59:15Z"),
+        PathBuf::from("host/elsa/2019-11-15T11:59:15Z"),
+    ];
+    assert_eq!(remove_list, expect);
+
+    Ok(())
+}
+
+    #[test]
 fn test_prune_simple2() -> Result<(), Error> {
 
     let mut orig_list = Vec::new();