]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- added quota aware cleanup
authorArvin Schnell <aschnell@suse.de>
Wed, 6 Apr 2016 13:29:13 +0000 (15:29 +0200)
committerArvin Schnell <aschnell@suse.de>
Wed, 6 Apr 2016 13:29:13 +0000 (15:29 +0200)
client/cleanup.cc
client/cleanup.h
data/default-config

index 634c680ddac01fe990b83fdb007d035cbcc53844..adf563bd7582c077dbeff02252df3f93898a8ddb 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) [2011-2014] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
  *
  * All Rights Reserved.
  *
  */
 
 
-#include <algorithm>
-
-#include <snapper/SnapperTmpl.h>
-
+#include "utils/Range.h"
 #include "utils/equal-date.h"
-
 #include "commands.h"
 
+#include <vector>
+
 using namespace std;
 
 
-struct younger_than
+struct Parameters
 {
-    younger_than(time_t t)
-       : t(t) {}
-    bool operator()(XSnapshots::const_iterator it)
-       { return it->getDate() > t; }
-    const time_t t;
+    Parameters(DBus::Connection& conn, const string& config_name);
+    virtual ~Parameters() {}
+
+    virtual bool is_degenerated() const { return true; }
+
+    friend ostream& operator<<(ostream& s, const Parameters& parameters);
+
+    time_t min_age;
+    double quota_limit;
+};
+
+
+Parameters::Parameters(DBus::Connection& conn, const string& config_name)
+    : min_age(1800), quota_limit(0.5)
+{
+    XConfigInfo ci = command_get_xconfig(conn, config_name);
+
+    ci.read("QUOTA_LIMIT", quota_limit);
+}
+
+
+ostream&
+operator<<(ostream& s, const Parameters& parameters)
+{
+    return s << "min-age:" << parameters.min_age << endl
+            << "quota-limit:" << parameters.quota_limit;
+}
+
+
+class Cleaner
+{
+public:
+
+    Cleaner(DBus::Connection& conn, const string& config_name, bool verbose, const Parameters& parameters)
+       : conn(conn), config_name(config_name), verbose(verbose), parameters(parameters) {}
+
+    virtual ~Cleaner() {}
+
+    void cleanup();
+
+protected:
+
+    virtual list<XSnapshots::iterator> calculate_candidates(Range::Value value) = 0;
+
+    struct younger_than
+    {
+       younger_than(time_t t)
+           : t(t) {}
+       bool operator()(XSnapshots::const_iterator it)
+           { return it->getDate() > t; }
+       const time_t t;
+    };
+
+    void filter(list<XSnapshots::iterator>& tmp) const;
+
+    // Removes snapshots younger than parameters.min_age from tmp
+    void filter_min_age(list<XSnapshots::iterator>& tmp) const;
+
+    // Removes pre and post snapshots from tmp that do have a corresponding
+    // snapshot but which is not included in tmp.
+    void filter_pre_post(list<XSnapshots::iterator>& tmp) const;
+
+    void remove(const list<XSnapshots::iterator>& tmp);
+
+    bool is_quota_aware() const;
+    bool is_quota_satisfied() const;
+
+    void cleanup_quota_unaware();
+    void cleanup_quota_aware();
+
+    DBus::Connection& conn;
+    const string& config_name;
+    bool verbose;
+
+    const Parameters& parameters;
+
+    XSnapshots snapshots;
 };
 
 
-// Removes snapshots younger than min_age from tmp
 void
-filter1(list<XSnapshots::const_iterator>& tmp, time_t min_age)
+Cleaner::filter(list<XSnapshots::iterator>& tmp) const
 {
-    tmp.remove_if(younger_than(time(NULL) - min_age));
+    filter_min_age(tmp);
+    filter_pre_post(tmp);
 }
 
 
-// Removes pre and post snapshots from tmp that do have a corresponding
-// snapshot but which is not included in tmp.
 void
-filter2(const XSnapshots& snapshots, list<XSnapshots::const_iterator>& tmp)
+Cleaner::filter_min_age(list<XSnapshots::iterator>& tmp) const
 {
-    list<XSnapshots::const_iterator> ret;
+    time_t now = time(NULL);
+    tmp.remove_if(younger_than(now - parameters.min_age));
+}
 
-    for (list<XSnapshots::const_iterator>::const_iterator it1 = tmp.begin(); it1 != tmp.end(); ++it1)
+
+void
+Cleaner::filter_pre_post(list<XSnapshots::iterator>& tmp) const
+{
+    list<XSnapshots::iterator> ret;
+
+    for (list<XSnapshots::iterator>::iterator it1 = tmp.begin(); it1 != tmp.end(); ++it1)
     {
        if ((*it1)->getType() == PRE)
        {
@@ -85,292 +162,496 @@ filter2(const XSnapshots& snapshots, list<XSnapshots::const_iterator>& tmp)
 }
 
 
-bool
-is_important(XSnapshots::const_iterator it1)
+void
+Cleaner::remove(const list<XSnapshots::iterator>& tmp)
 {
-    map<string, string>::const_iterator it2 = it1->getUserdata().find("important");
-    return it2 != it1->getUserdata().end() && it2->second == "yes";
+    for (list<XSnapshots::iterator>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+    {
+       command_delete_xsnapshots(conn, config_name, { (*it)->getNum() }, verbose);
+       snapshots.erase(*it);
+    }
 }
 
 
 bool
-do_cleanup_number(DBus::Connection& conn, const string& config_name, bool verbose)
+Cleaner::is_quota_aware() const
 {
-    time_t min_age = 1800;
-    size_t limit = 50;
-    size_t limit_important = 10;
-
-    XConfigInfo ci = command_get_xconfig(conn, config_name);
-    map<string, string>::const_iterator pos;
-    if ((pos = ci.raw.find("NUMBER_MIN_AGE")) != ci.raw.end())
-       pos->second >> min_age;
-    if ((pos = ci.raw.find("NUMBER_LIMIT")) != ci.raw.end())
-       pos->second >> limit;
-    if ((pos = ci.raw.find("NUMBER_LIMIT_IMPORTANT")) != ci.raw.end())
-       pos->second >> limit_important;
-
-    size_t num = 0;
-    size_t num_important = 0;
-
-    XSnapshots snapshots = command_list_xsnapshots(conn, config_name);
-
-    list<XSnapshots::const_iterator> tmp;
+    if (parameters.is_degenerated())
+       return false;
 
-    for (XSnapshots::const_iterator it = snapshots.begin(); it != snapshots.end(); ++it)
+    try
     {
-       if (it->getCleanup() == "number")
-           tmp.push_front(it);
+       command_prepare_quota(conn, config_name);
     }
-
-    list<XSnapshots::const_iterator>::iterator it = tmp.begin();
-    while (it != tmp.end())
+    catch (const DBus::ErrorException& e)
     {
-       bool keep = false;
+       SN_CAUGHT(e);
 
-       if (num_important < limit_important && is_important(*it))
-       {
-           ++num_important;
-           keep = true;
-       }
-       if (num < limit)
+       if (strcmp(e.name(), "error.quota") == 0)
        {
-           ++num;
-           keep = true;
+           cerr << "quota not working" << endl;
+           return false;
        }
 
-       if (keep)
-           it = tmp.erase(it);
-       else
-           ++it;
+       SN_RETHROW(e);
     }
 
-    tmp.reverse();
+    return true;
+}
 
-    filter1(tmp, min_age);
-    filter2(snapshots, tmp);
 
-    for (list<XSnapshots::const_iterator>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
-    {
-       list<unsigned int> nums;
-       nums.push_back((*it)->getNum());
-       command_delete_xsnapshots(conn, config_name, nums, verbose);
-    }
+bool
+Cleaner::is_quota_satisfied() const
+{
+    XQuotaData quota_data = command_query_quota(conn, config_name);
 
-    return true;
+    return quota_data.used < parameters.quota_limit * quota_data.size;
 }
 
 
-bool
-is_first(list<XSnapshots::const_iterator>::const_iterator first,
-        list<XSnapshots::const_iterator>::const_iterator last,
-        XSnapshots::const_iterator it1,
-        std::function<bool(const struct tm& tmp1, const struct tm& tmp2)> pred)
+void
+Cleaner::cleanup_quota_unaware()
 {
-    time_t t1 = it1->getDate();
-    struct tm tmp1;
-    localtime_r(&t1, &tmp1);
+    list<XSnapshots::iterator> candidates = calculate_candidates(Range::MAX);
 
-    for (list<XSnapshots::const_iterator>::const_iterator it2 = first; it2 != last; ++it2)
+    filter(candidates);
+
+    remove(candidates);
+}
+
+
+void
+Cleaner::cleanup_quota_aware()
+{
+    while (!is_quota_satisfied())
     {
-       if (it1 == *it2)
-           continue;
+       list<XSnapshots::iterator> candidates = calculate_candidates(Range::MIN);
+       if (candidates.empty())
+       {
+           // not enough candidates to satisfy quota
+           return;
+       }
 
-       time_t t2 = (*it2)->getDate();
-       struct tm tmp2;
-       localtime_r(&t2, &tmp2);
+       // take more and more candidates into a temporary candidates
+       // list. this is required since the filter will e.g. remove a pre
+       // snapshot candidate if the post snapshot is missing so simply
+       // removing the first candidate is not possible.
 
-       if (!pred(tmp1, tmp2))
-           return true;
+       for (list<XSnapshots::iterator>::iterator e = candidates.begin(); e != candidates.end(); ++e)
+       {
+           list<XSnapshots::iterator> tmp = list<XSnapshots::iterator>(candidates.begin(), next(e));
 
-       if (t1 > t2)
-           return false;
-    }
+           filter(tmp);
 
-    return true;
+           if (!tmp.empty())
+           {
+               remove(tmp);
+               // after removing snapshots is_quota_satisfied must be reevaluated
+               break;
+           }
+
+           if (next(e) == candidates.end())
+           {
+               // not enough candidates to satisfy quota
+               return;
+           }
+       }
+    }
 }
 
 
-bool
-is_first_yearly(list<XSnapshots::const_iterator>::const_iterator first,
-               list<XSnapshots::const_iterator>::const_iterator last,
-               XSnapshots::const_iterator it1)
+void
+Cleaner::cleanup()
 {
-    return is_first(first, last, it1, equal_year);
+    snapshots = command_list_xsnapshots(conn, config_name);
+
+    cleanup_quota_unaware();
+
+    if (is_quota_aware())
+       cleanup_quota_aware();
 }
 
-bool
-is_first_monthly(list<XSnapshots::const_iterator>::const_iterator first,
-                list<XSnapshots::const_iterator>::const_iterator last,
-                XSnapshots::const_iterator it1)
+
+struct NumberParameters : public Parameters
 {
-    return is_first(first, last, it1, equal_month);
-}
+    NumberParameters(DBus::Connection& conn, const string& config_name);
 
-bool
-is_first_weekly(list<XSnapshots::const_iterator>::const_iterator first,
-               list<XSnapshots::const_iterator>::const_iterator last,
-               XSnapshots::const_iterator it1)
+    bool is_degenerated() const;
+
+    friend ostream& operator<<(ostream& s, const NumberParameters& parameters);
+
+    Range limit;
+    Range limit_important;
+};
+
+
+NumberParameters::NumberParameters(DBus::Connection& conn, const string& config_name)
+    : Parameters(conn, config_name), limit(50), limit_important(10)
 {
-    return is_first(first, last, it1, equal_week);
+    XConfigInfo ci = command_get_xconfig(conn, config_name);
+
+    ci.read("NUMBER_MIN_AGE", min_age);
+
+    ci.read("NUMBER_LIMIT", limit);
+    ci.read("NUMBER_LIMIT_IMPORTANT", limit_important);
 }
 
-bool
-is_first_daily(list<XSnapshots::const_iterator>::const_iterator first,
-              list<XSnapshots::const_iterator>::const_iterator last,
-              XSnapshots::const_iterator it1)
+
+ostream&
+operator<<(ostream& s, const NumberParameters& parameters)
 {
-    return is_first(first, last, it1, equal_day);
+    return s << dynamic_cast<const Parameters&>(parameters) << endl
+            << "limit:" << parameters.limit << endl
+            << "limit-important:" << parameters.limit_important;
 }
 
+
 bool
-is_first_hourly(list<XSnapshots::const_iterator>::const_iterator first,
-               list<XSnapshots::const_iterator>::const_iterator last,
-               XSnapshots::const_iterator it1)
+NumberParameters::is_degenerated() const
 {
-    return is_first(first, last, it1, equal_hour);
+    return limit.is_degenerated() && limit_important.is_degenerated();
 }
 
 
-bool
-do_cleanup_timeline(DBus::Connection& conn, const string& config_name, bool verbose)
+class NumberCleaner : public Cleaner
 {
-    time_t min_age = 1800;
-    size_t limit_hourly = 10;
-    size_t limit_daily = 10;
-    size_t limit_monthly = 10;
-    size_t limit_weekly = 0;
-    size_t limit_yearly = 10;
 
-    XConfigInfo ci = command_get_xconfig(conn, config_name);
-    map<string, string>::const_iterator pos;
-    if ((pos = ci.raw.find("TIMELINE_MIN_AGE")) != ci.raw.end())
-       pos->second >> min_age;
-    if ((pos = ci.raw.find("TIMELINE_LIMIT_HOURLY")) != ci.raw.end())
-       pos->second >> limit_hourly;
-    if ((pos = ci.raw.find("TIMELINE_LIMIT_DAILY")) != ci.raw.end())
-       pos->second >> limit_daily;
-    if ((pos = ci.raw.find("TIMELINE_LIMIT_WEEKLY")) != ci.raw.end())
-       pos->second >> limit_weekly;
-    if ((pos = ci.raw.find("TIMELINE_LIMIT_MONTHLY")) != ci.raw.end())
-       pos->second >> limit_monthly;
-    if ((pos = ci.raw.find("TIMELINE_LIMIT_YEARLY")) != ci.raw.end())
-       pos->second >> limit_yearly;
-
-    size_t num_hourly = 0;
-    size_t num_daily = 0;
-    size_t num_weekly = 0;
-    size_t num_monthly = 0;
-    size_t num_yearly = 0;
-
-    XSnapshots snapshots = command_list_xsnapshots(conn, config_name);
-
-    list<XSnapshots::const_iterator> tmp;
-
-    for (XSnapshots::const_iterator it = snapshots.begin(); it != snapshots.end(); ++it)
+public:
+
+    NumberCleaner(DBus::Connection& conn, const string& config_name, bool verbose,
+                 const NumberParameters& parameters)
+       : Cleaner(conn, config_name, verbose, parameters) {}
+
+private:
+
+    bool
+    is_important(XSnapshots::const_iterator it1)
     {
-       if (it->getCleanup() == "timeline")
-           tmp.push_front(it);
+       map<string, string>::const_iterator it2 = it1->getUserdata().find("important");
+       return it2 != it1->getUserdata().end() && it2->second == "yes";
     }
 
-    list<XSnapshots::const_iterator>::iterator it = tmp.begin();
-    while (it != tmp.end())
+
+    list<XSnapshots::iterator>
+    calculate_candidates(Range::Value value)
     {
-       bool keep = false;
+       const NumberParameters& parameters = dynamic_cast<const NumberParameters&>(Cleaner::parameters);
 
-       if (num_hourly < limit_hourly && is_first_hourly(it, tmp.end(), *it))
+       list<XSnapshots::iterator> ret;
+
+       for (XSnapshots::iterator it = snapshots.begin(); it != snapshots.end(); ++it)
        {
-           ++num_hourly;
-           keep = true;
+           if (it->getCleanup() == "number")
+               ret.push_front(it);
        }
-       if (num_daily < limit_daily && is_first_daily(it, tmp.end(), *it))
+
+       size_t num = 0;
+       size_t num_important = 0;
+
+       list<XSnapshots::iterator>::iterator it = ret.begin();
+       while (it != ret.end())
        {
-           ++num_daily;
-           keep = true;
+           bool keep = false;
+
+           if (num_important < parameters.limit_important.value(value) && is_important(*it))
+           {
+               ++num_important;
+               keep = true;
+           }
+           if (num < parameters.limit.value(value))
+           {
+               ++num;
+               keep = true;
+           }
+
+           if (keep)
+               it = ret.erase(it);
+           else
+               ++it;
        }
-       if (num_weekly < limit_weekly && is_first_weekly(it, tmp.end(), *it))
+
+       ret.reverse();
+
+       return ret;
+    }
+};
+
+
+void
+do_cleanup_number(DBus::Connection& conn, const string& config_name, bool verbose)
+{
+    NumberParameters parameters(conn, config_name);
+    NumberCleaner cleaner(conn, config_name, verbose, parameters);
+    cleaner.cleanup();
+}
+
+
+struct TimelineParameters : public Parameters
+{
+    TimelineParameters(DBus::Connection& conn, const string& config_name);
+
+    bool is_degenerated() const;
+
+    Range limit_hourly;
+    Range limit_daily;
+    Range limit_monthly;
+    Range limit_weekly;
+    Range limit_yearly;
+};
+
+
+TimelineParameters::TimelineParameters(DBus::Connection& conn, const string& config_name)
+    : Parameters(conn, config_name), limit_hourly(10), limit_daily(10), limit_monthly(10),
+      limit_weekly(0), limit_yearly(10)
+{
+    XConfigInfo ci = command_get_xconfig(conn, config_name);
+
+    ci.read("TIMELINE_MIN_AGE", min_age);
+
+    ci.read("TIMELINE_LIMIT_HOURLY", limit_hourly);
+    ci.read("TIMELINE_LIMIT_DAILY", limit_daily);
+    ci.read("TIMELINE_LIMIT_WEEKLY", limit_weekly);
+    ci.read("TIMELINE_LIMIT_MONTHLY", limit_monthly);
+    ci.read("TIMELINE_LIMIT_YEARLY", limit_yearly);
+}
+
+
+ostream&
+operator<<(ostream& s, const TimelineParameters& parameters)
+{
+    return s << dynamic_cast<const Parameters&>(parameters) << endl
+            << "limit-hourly:" << parameters.limit_hourly << endl
+            << "limit-daily:" << parameters.limit_daily << endl
+            << "limit-weekly:" << parameters.limit_weekly << endl
+            << "limit-monthly:" << parameters.limit_monthly << endl
+            << "limit-yearly:" << parameters.limit_yearly;
+}
+
+
+bool
+TimelineParameters::is_degenerated() const
+{
+    return limit_hourly.is_degenerated() && limit_daily.is_degenerated() &&
+       limit_monthly.is_degenerated() && limit_weekly.is_degenerated() &&
+       limit_yearly.is_degenerated();
+}
+
+
+class TimelineCleaner : public Cleaner
+{
+public:
+
+    TimelineCleaner(DBus::Connection& conn, const string& config_name, bool verbose,
+                   const TimelineParameters& parameters)
+       : Cleaner(conn, config_name, verbose, parameters) {}
+
+private:
+
+    bool
+    is_first(list<XSnapshots::iterator>::const_iterator first,
+            list<XSnapshots::iterator>::const_iterator last,
+            XSnapshots::const_iterator it1,
+            std::function<bool(const struct tm& tmp1, const struct tm& tmp2)> pred)
+    {
+       time_t t1 = it1->getDate();
+       struct tm tmp1;
+       localtime_r(&t1, &tmp1);
+
+       for (list<XSnapshots::iterator>::const_iterator it2 = first; it2 != last; ++it2)
        {
-           ++num_weekly;
-           keep = true;
+           if (it1 == *it2)
+               continue;
+
+           time_t t2 = (*it2)->getDate();
+           struct tm tmp2;
+           localtime_r(&t2, &tmp2);
+
+           if (!pred(tmp1, tmp2))
+               return true;
+
+           if (t1 > t2)
+               return false;
        }
-       if (num_monthly < limit_monthly && is_first_monthly(it, tmp.end(), *it))
+
+       return true;
+    }
+
+
+    bool
+    is_first_yearly(list<XSnapshots::iterator>::const_iterator first,
+                   list<XSnapshots::iterator>::const_iterator last,
+                   XSnapshots::const_iterator it1)
+    {
+       return is_first(first, last, it1, equal_year);
+    }
+
+    bool
+    is_first_monthly(list<XSnapshots::iterator>::const_iterator first,
+                    list<XSnapshots::iterator>::const_iterator last,
+                    XSnapshots::const_iterator it1)
+    {
+       return is_first(first, last, it1, equal_month);
+    }
+
+    bool
+    is_first_weekly(list<XSnapshots::iterator>::const_iterator first,
+                   list<XSnapshots::iterator>::const_iterator last,
+                   XSnapshots::const_iterator it1)
+    {
+       return is_first(first, last, it1, equal_week);
+    }
+
+    bool
+    is_first_daily(list<XSnapshots::iterator>::const_iterator first,
+                  list<XSnapshots::iterator>::const_iterator last,
+                  XSnapshots::const_iterator it1)
+    {
+       return is_first(first, last, it1, equal_day);
+    }
+
+    bool
+    is_first_hourly(list<XSnapshots::iterator>::const_iterator first,
+                   list<XSnapshots::iterator>::const_iterator last,
+                   XSnapshots::const_iterator it1)
+    {
+       return is_first(first, last, it1, equal_hour);
+    }
+
+
+    list<XSnapshots::iterator>
+    calculate_candidates(Range::Value value)
+    {
+       const TimelineParameters& parameters = dynamic_cast<const TimelineParameters&>(Cleaner::parameters);
+
+       list<XSnapshots::iterator> ret;
+
+       for (XSnapshots::iterator it = snapshots.begin(); it != snapshots.end(); ++it)
        {
-           ++num_monthly;
-           keep = true;
+           if (it->getCleanup() == "timeline")
+               ret.push_front(it);
        }
-       if (num_yearly < limit_yearly && is_first_yearly(it, tmp.end(), *it))
+
+       size_t num_hourly = 0;
+       size_t num_daily = 0;
+       size_t num_weekly = 0;
+       size_t num_monthly = 0;
+       size_t num_yearly = 0;
+
+       list<XSnapshots::iterator>::iterator it = ret.begin();
+       while (it != ret.end())
        {
-           ++num_yearly;
-           keep = true;
-       }
+           bool keep = false;
 
-       if (keep)
-           it = tmp.erase(it);
-       else
-           ++it;
-    }
+           if (num_hourly < parameters.limit_hourly.value(value) && is_first_hourly(it, ret.end(), *it))
+           {
+               ++num_hourly;
+               keep = true;
+           }
+           if (num_daily < parameters.limit_daily.value(value) && is_first_daily(it, ret.end(), *it))
+           {
+               ++num_daily;
+               keep = true;
+           }
+           if (num_weekly < parameters.limit_weekly.value(value) && is_first_weekly(it, ret.end(), *it))
+           {
+               ++num_weekly;
+               keep = true;
+           }
+           if (num_monthly < parameters.limit_monthly.value(value) && is_first_monthly(it, ret.end(), *it))
+           {
+               ++num_monthly;
+               keep = true;
+           }
+           if (num_yearly < parameters.limit_yearly.value(value) && is_first_yearly(it, ret.end(), *it))
+           {
+               ++num_yearly;
+               keep = true;
+           }
 
-    tmp.reverse();
+           if (keep)
+               it = ret.erase(it);
+           else
+               ++it;
+       }
 
-    filter1(tmp, min_age);
-    filter2(snapshots, tmp);
+       ret.reverse();
 
-    for (list<XSnapshots::const_iterator>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
-    {
-       list<unsigned int> nums;
-       nums.push_back((*it)->getNum());
-       command_delete_xsnapshots(conn, config_name, nums, verbose);
+       return ret;
     }
+};
 
-    return true;
+
+void
+do_cleanup_timeline(DBus::Connection& conn, const string& config_name, bool verbose)
+{
+    TimelineParameters parameters(conn, config_name);
+    TimelineCleaner cleaner(conn, config_name, verbose, parameters);
+    cleaner.cleanup();
 }
 
 
-bool
-do_cleanup_empty_pre_post(DBus::Connection& conn, const string& config_name, bool verbose)
+struct EmptyPrePostParameters : public Parameters
 {
-    time_t min_age = 1800;
+    EmptyPrePostParameters(DBus::Connection& conn, const string& config_name);
+};
+
 
+EmptyPrePostParameters::EmptyPrePostParameters(DBus::Connection& conn, const string& config_name)
+    : Parameters(conn, config_name)
+{
     XConfigInfo ci = command_get_xconfig(conn, config_name);
-    map<string, string>::const_iterator pos;
-    if ((pos = ci.raw.find("EMPTY_PRE_POST_MIN_AGE")) != ci.raw.end())
-       pos->second >> min_age;
 
-    XSnapshots snapshots = command_list_xsnapshots(conn, config_name);
+    ci.read("EMPTY_PRE_POST_MIN_AGE", min_age);
+}
 
-    list<XSnapshots::const_iterator> tmp;
 
-    for (XSnapshots::const_iterator it1 = snapshots.begin(); it1 != snapshots.end(); ++it1)
+class EmptyPrePostCleaner : public Cleaner
+{
+public:
+
+    EmptyPrePostCleaner(DBus::Connection& conn, const string& config_name, bool verbose,
+                       const EmptyPrePostParameters& parameters)
+       : Cleaner(conn, config_name, verbose, parameters) {}
+
+private:
+
+    list<XSnapshots::iterator>
+    calculate_candidates(Range::Value value)
     {
-       if (it1->getType() == PRE)
-       {
-           XSnapshots::const_iterator it2 = snapshots.findPost(it1);
+       list<XSnapshots::iterator> ret;
 
-           if (it2 != snapshots.end())
+       for (XSnapshots::iterator it1 = snapshots.begin(); it1 != snapshots.end(); ++it1)
+       {
+           if (it1->getType() == PRE)
            {
-               command_create_xcomparison(conn, config_name, it1->getNum(), it2->getNum());
+               XSnapshots::iterator it2 = snapshots.findPost(it1);
 
-               list<XFile> files = command_get_xfiles(conn, config_name, it1->getNum(), it2->getNum());
-
-               if (files.empty())
+               if (it2 != snapshots.end())
                {
-                   tmp.push_back(it1);
-                   tmp.push_back(it2);
-               }
+                   command_create_xcomparison(conn, config_name, it1->getNum(), it2->getNum());
+
+                   list<XFile> files = command_get_xfiles(conn, config_name, it1->getNum(), it2->getNum());
+
+                   if (files.empty())
+                   {
+                       ret.push_back(it1);
+                       ret.push_back(it2);
+                   }
 
-               command_delete_xcomparison(conn, config_name, it1->getNum(), it2->getNum());
+                   command_delete_xcomparison(conn, config_name, it1->getNum(), it2->getNum());
+               }
            }
        }
-    }
-
-    filter1(tmp, min_age);
-    filter2(snapshots, tmp);
 
-    for (list<XSnapshots::const_iterator>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
-    {
-       list<unsigned int> nums;
-       nums.push_back((*it)->getNum());
-       command_delete_xsnapshots(conn, config_name, nums, verbose);
+       return ret;
     }
+};
 
-    return true;
+
+void
+do_cleanup_empty_pre_post(DBus::Connection& conn, const string& config_name, bool verbose)
+{
+    EmptyPrePostParameters parameters(conn, config_name);
+    EmptyPrePostCleaner cleaner(conn, config_name, verbose, parameters);
+    cleaner.cleanup();
 }
index 9d00a4d243a7cc129c571c19422db7fe29b8b920..35955bf21aafec64d179c10dc55cece252bb9ab5 100644 (file)
  */
 
 
-bool
+void
 do_cleanup_number(DBus::Connection& conn, const string& config_name, bool verbose);
 
-bool
+void
 do_cleanup_timeline(DBus::Connection& conn, const string& config_name, bool verbose);
 
-bool
+void
 do_cleanup_empty_pre_post(DBus::Connection& conn, const string& config_name, bool verbose);
index 2a7a12438a36691af24516214e31d60089107447..cc4fcf2f492b4fcb1023c3e630de7640888ca65c 100644 (file)
@@ -10,6 +10,10 @@ FSTYPE="btrfs"
 QGROUP=""
 
 
+# fraction of the filesystems space the snapshots may use
+QUOTA_LIMIT="0.5"
+
+
 # users and groups allowed to work with config
 ALLOW_USERS=""
 ALLOW_GROUPS=""
@@ -29,7 +33,7 @@ NUMBER_CLEANUP="yes"
 
 # limit for number cleanup
 NUMBER_MIN_AGE="1800"
-NUMBER_LIMIT="50"
+NUMBER_LIMIT="10-50"
 NUMBER_LIMIT_IMPORTANT="10"
 
 
@@ -41,11 +45,11 @@ TIMELINE_CLEANUP="yes"
 
 # limits for timeline cleanup
 TIMELINE_MIN_AGE="1800"
-TIMELINE_LIMIT_HOURLY="10"
-TIMELINE_LIMIT_DAILY="10"
+TIMELINE_LIMIT_HOURLY="4-10"
+TIMELINE_LIMIT_DAILY="4-10"
 TIMELINE_LIMIT_WEEKLY="0"
-TIMELINE_LIMIT_MONTHLY="10"
-TIMELINE_LIMIT_YEARLY="10"
+TIMELINE_LIMIT_MONTHLY="4-10"
+TIMELINE_LIMIT_YEARLY="4-10"
 
 
 # cleanup empty pre-post-pairs
@@ -53,4 +57,3 @@ EMPTY_PRE_POST_CLEANUP="yes"
 
 # limits for empty pre-post-pair cleanup
 EMPTY_PRE_POST_MIN_AGE="1800"
-