#include "utils/Range.h"
#include "utils/equal-date.h"
+#include "utils/HumanString.h"
#include "cleanup.h"
time_t min_age;
double space_limit;
+ double free_limit;
template<typename Type>
void read(const ProxyConfig& config, const char* name, Type& value)
Parameters::Parameters(const ProxySnapper* snapper)
- : min_age(1800), space_limit(0.5)
+ : min_age(1800), space_limit(0.5), free_limit(0.2)
{
ProxyConfig config = snapper->getConfig();
read(config, "SPACE_LIMIT", space_limit);
+ read(config, "FREE_LIMIT", free_limit);
}
operator<<(ostream& s, const Parameters& parameters)
{
return s << "min-age:" << parameters.min_age << endl
- << "space-limit:" << parameters.space_limit;
+ << "space-limit:" << parameters.space_limit
+ << "free-limit:" << parameters.free_limit;
}
void remove(const list<ProxySnapshots::iterator>& tmp);
+ // Should the cleanup with quota space be run?
bool is_quota_aware() const;
+
+ // Is the quota space condition satisfied?
bool is_quota_satisfied() const;
- void cleanup_quota_unaware(ProxySnapshots& snapshots);
- void cleanup_quota_aware(ProxySnapshots& snapshots);
+ // Should the cleanup with free space be run?
+ bool is_free_aware() const;
+
+ // Is the free space condition satisfied?
+ bool is_free_satisfied() const;
+
+ void cleanup(ProxySnapshots& snapshots);
+ void cleanup(ProxySnapshots& snapshots, std::function<bool()> condition);
ProxySnapper* snapper;
bool verbose;
{
snapper->prepareQuota();
}
- catch (const DBus::ErrorException& e)
+ catch (const QuotaException& e)
{
SN_CAUGHT(e);
- if (strcmp(e.name(), "error.quota") == 0)
- {
- cerr << "quota not working (" << e.message() << ")" << endl;
- return false;
- }
+ cerr << "quota not working (" << e.what() << ")" << endl;
+ return false;
+ }
+
+ return parameters.space_limit < 1.0;
+}
+
+
+bool
+Cleaner::is_quota_satisfied() const
+{
+ QuotaData quota_data = snapper->queryQuotaData();
+
+ double fraction = (double)(quota_data.used) / (double)(quota_data.size);
+
+ bool satisfied = fraction < parameters.space_limit;
+
+#ifdef VERBOSE_LOGGING
+ cout << byte_to_humanstring(quota_data.size, 2) << ", "
+ << byte_to_humanstring(quota_data.used, 2) << ", "
+ << fraction << ", " << satisfied << endl;
+#endif
+
+ return satisfied;
+}
+
+
+bool
+Cleaner::is_free_aware() const
+{
+ if (parameters.is_degenerated())
+ return false;
- SN_RETHROW(e);
+ try
+ {
+ snapper->queryFreeSpaceData();
}
- catch (const QuotaException& e)
+ catch (const FreeSpaceException& e)
{
SN_CAUGHT(e);
- cerr << "quota not working (" << e.what() << ")" << endl;
+ cerr << "free space not working (" << e.what() << ")" << endl;
return false;
}
- return true;
+ return parameters.free_limit > 0.0;
}
bool
-Cleaner::is_quota_satisfied() const
+Cleaner::is_free_satisfied() const
{
- QuotaData quota_data = snapper->queryQuotaData();
+ FreeSpaceData free_space_data = snapper->queryFreeSpaceData();
+
+ double fraction = (double)(free_space_data.free) / (double)(free_space_data.size);
- return quota_data.used < parameters.space_limit * quota_data.size;
+ bool satisfied = fraction > parameters.free_limit;
+
+#ifdef VERBOSE_LOGGING
+ cout << byte_to_humanstring(free_space_data.size, 2) << ", "
+ << byte_to_humanstring(free_space_data.free, 2) << ", "
+ << fraction << ", " << satisfied << endl;
+#endif
+
+ return satisfied;
}
void
-Cleaner::cleanup_quota_unaware(ProxySnapshots& snapshots)
+Cleaner::cleanup(ProxySnapshots& snapshots)
{
list<ProxySnapshots::iterator> candidates = calculate_candidates(snapshots, Range::MAX);
void
-Cleaner::cleanup_quota_aware(ProxySnapshots& snapshots)
+Cleaner::cleanup(ProxySnapshots& snapshots, std::function<bool()> condition)
{
- while (!is_quota_satisfied())
+ while (!condition())
{
list<ProxySnapshots::iterator> candidates = calculate_candidates(snapshots, Range::MIN);
if (candidates.empty())
{
- // not enough candidates to satisfy quota
+ // not enough candidates to satisfy the condition
+
+#ifdef VERBOSE_LOGGING
+ cout << "condition not satisfied" << endl;
+#endif
+
return;
}
if (!tmp.empty())
{
remove(tmp);
- // after removing snapshots is_quota_satisfied must be reevaluated
+
+ // after removing snapshots the condition must be reevaluated
break;
}
if (next(e) == candidates.end())
{
- // not enough candidates to satisfy quota
+ // not enough candidates to satisfy the condition
+
+#ifdef VERBOSE_LOGGING
+ cout << "condition not satisfied" << endl;
+#endif
+
return;
}
}
}
+
+#ifdef VERBOSE_LOGGING
+ cout << "condition satisfied" << endl;
+#endif
}
{
ProxySnapshots& snapshots = snapper->getSnapshots();
- cleanup_quota_unaware(snapshots);
+#ifdef VERBOSE_LOGGING
+ cout << "cleanup without condition" << endl;
+#endif
+
+ cleanup(snapshots);
if (is_quota_aware())
- cleanup_quota_aware(snapshots);
+ {
+#ifdef VERBOSE_LOGGING
+ cout << "cleanup with quota condition" << endl;
+#endif
+
+ cleanup(snapshots, std::bind(&Cleaner::is_quota_satisfied, this));
+ }
+ else
+ {
+#ifdef VERBOSE_LOGGING
+ cout << "no cleanup with quota condition" << endl;
+#endif
+ }
+
+ if (is_free_aware())
+ {
+#ifdef VERBOSE_LOGGING
+ cout << "cleanup with free condition" << endl;
+#endif
+
+ cleanup(snapshots, std::bind(&Cleaner::is_free_satisfied, this));
+ }
+ else
+ {
+#ifdef VERBOSE_LOGGING
+ cout << "no cleanup with free condition" << endl;
+#endif
+ }
}
<refentry id='snapper8'>
<refentryinfo>
- <date>2018-10-16</date>
+ <date>2018-10-29</date>
</refentryinfo>
<refmeta>
<refentrytitle>snapper</refentrytitle>
<manvolnum>8</manvolnum>
- <refmiscinfo class='date'>2018-10-16</refmiscinfo>
+ <refmiscinfo class='date'>2018-10-29</refmiscinfo>
<refmiscinfo class='version'>@VERSION@</refmiscinfo>
<refmiscinfo class='manual'>Filesystem Snapshot Management</refmiscinfo>
</refmeta>
</glossentry>
</glosslist>
- <para>The number and timeline cleanup algorithm can also try to keep the
- space used by snapshots below a limit. For that quota must be setup, see
- command setup-quota, and the LIMIT variables in the config file must
- have ranges (min- and max-value). The algorithms will then make two
- passes:
+ <para>The number and timeline cleanup algorithms can also try to
+ keep the space used by snapshots below a limit and the free space of
+ the filesystem above a limit. For the first condition quota must be
+ setup, see command setup-quota. Additional the NUMBER_LIMIT and
+ TIMELINE_LIMIT variables in the config file must have ranges (min- and
+ max-value). The algorithms will then make two passes:
+
<orderedlist>
<listitem>
- <para>Delete snapshots above the max-value independent of the used
- space.</para>
+ <para>Delete snapshots above the max-value independent of
+ the snapshot and filesystem space.</para>
</listitem>
<listitem>
- <para>Delete snapshots above the min-value until the limit for
- the used space is reached.</para>
+ <para>Delete snapshots above the min-value until the limits for
+ the snapshot and filesystem are reached.</para>
</listitem>
</orderedlist>
- The limit for the used space can be configured via the SPACE_LIMIT
- variable. Note: Only snapshots that have a cleanup algorithm set are
- taken into account when calculating the used space.
+
+ The limit for the used space can be configured via the
+ SPACE_LIMIT variable. Note: Only snapshots that have a cleanup
+ algorithm set are taken into account when calculating the space
+ used by snapshots.
+
+ The limit for the free space can be configured via the
+ FREE_LIMIT variable.
+
</para>
</refsect2>