/*
* Copyright (c) [2012-2015] Novell, Inc.
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016,2018] SUSE LLC
*
* All Rights Reserved.
*
}
+void
+command_calculate_used_space(DBus::Connection& conn, const string& config_name)
+{
+ DBus::MessageMethodCall call(SERVICE, OBJECT, INTERFACE, "CalculateUsedSpace");
+
+ DBus::Hoho hoho(call);
+ hoho << config_name;
+
+ try
+ {
+ conn.send_with_reply_and_block(call);
+ }
+ catch (const DBus::ErrorException& e)
+ {
+ SN_CAUGHT(e);
+
+ if (strcmp(e.name(), "error.quota") == 0)
+ SN_THROW(QuotaException(e.message()));
+
+ SN_RETHROW(e);
+ }
+}
+
+
+uint64_t
+command_get_used_space(DBus::Connection& conn, const string& config_name, unsigned int num)
+{
+ DBus::MessageMethodCall call(SERVICE, OBJECT, INTERFACE, "GetUsedSpace");
+
+ DBus::Hoho hoho(call);
+ hoho << config_name << num;
+
+ DBus::Message reply = conn.send_with_reply_and_block(call);
+
+ uint64_t used_space;
+
+ DBus::Hihi hihi(reply);
+ hihi >> used_space;
+
+ return used_space;
+}
+
+
string
command_mount_snapshot(DBus::Connection& conn, const string& config_name,
unsigned int num, bool user_request)
/*
* Copyright (c) [2012-2015] Novell, Inc.
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016,2018] SUSE LLC
*
* All Rights Reserved.
*
command_delete_snapshots(DBus::Connection& conn, const string& config_name,
const vector<unsigned int>& nums, bool verbose);
+void
+command_calculate_used_space(DBus::Connection& conn, const string& config_name);
+
+uint64_t
+command_get_used_space(DBus::Connection& conn, const string& config_name, unsigned int num);
+
string
command_mount_snapshot(DBus::Connection& conn, const string& config_name,
unsigned int num, bool user_request);
/*
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016,2018] SUSE LLC
*
* All Rights Reserved.
*
}
+void
+ProxySnapperDbus::calculateUsedSpace() const
+{
+ command_calculate_used_space(conn(), config_name);
+}
+
+
+uint64_t
+ProxySnapshotDbus::getUsedSpace() const
+{
+ return command_get_used_space(conn(), configName(), num);
+}
+
+
string
ProxySnapshotDbus::mountFilesystemSnapshot(bool user_request) const
{
/*
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016,2018] SUSE LLC
*
* All Rights Reserved.
*
virtual bool isCurrent() const override { return num == 0; }
+ virtual uint64_t getUsedSpace() const override;
+
virtual string mountFilesystemSnapshot(bool user_request) const override;
virtual void umountFilesystemSnapshot(bool user_request) const override;
virtual QuotaData queryQuotaData() const override;
+ virtual void calculateUsedSpace() const override;
+
DBus::Connection& conn() const;
private:
/*
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016,2018] SUSE LLC
*
* All Rights Reserved.
*
virtual bool isCurrent() const override { return it->isCurrent(); }
+ virtual uint64_t getUsedSpace() const override
+ {
+ return it->getUsedSpace();
+ }
+
virtual string mountFilesystemSnapshot(bool user_request) const override
{
it->mountFilesystemSnapshot(user_request);
virtual QuotaData queryQuotaData() const override { return snapper->queryQuotaData(); }
+ virtual void calculateUsedSpace() const override { snapper->calculateUsedSpace(); }
+
std::unique_ptr<Snapper> snapper;
private:
/*
- * Copyright (c) [2016-2017] SUSE LLC
+ * Copyright (c) [2016-2018] SUSE LLC
*
* All Rights Reserved.
*
bool isCurrent() const { return impl->isCurrent(); }
+ uint64_t getUsedSpace() const { return impl->getUsedSpace(); }
+
void mountFilesystemSnapshot(bool user_request) const
{ impl->mountFilesystemSnapshot(user_request); }
virtual bool isCurrent() const = 0;
+ virtual uint64_t getUsedSpace() const = 0;
+
virtual string mountFilesystemSnapshot(bool user_request) const = 0;
virtual void umountFilesystemSnapshot(bool user_request) const = 0;
virtual QuotaData queryQuotaData() const = 0;
+ virtual void calculateUsedSpace() const = 0;
+
};
#include "utils/text.h"
#include "utils/Table.h"
#include "utils/GetOpts.h"
+#include "utils/HumanString.h"
#include "cleanup.h"
#include "errors.h"
<< endl
<< _(" Options for 'list' command:") << endl
<< _("\t--type, -t <type>\t\tType of snapshots to list.") << endl
+ << _("\t--disable-used-space\t\tDisable showing used space.") << endl
<< _("\t--all-configs, -a\t\tList snapshots from all accessible configs.") << endl
<< endl;
}
void
-list_from_one_config(ProxySnapper* snapper, ListMode list_mode);
+list_from_one_config(ProxySnapper* snapper, ListMode list_mode, bool show_used_space);
void
{
const struct option options[] = {
{ "type", required_argument, 0, 't' },
+ { "disable-used-space", no_argument, 0, 0 },
{ "all-configs", no_argument, 0, 'a' },
{ 0, 0, 0, 0 }
};
}
ListMode list_mode = LM_ALL;
+ bool show_used_space = true;
GetOpts::parsed_opts::const_iterator opt;
}
}
+ if ((opt = opts.find("disable-used-space")) != opts.end())
+ {
+ show_used_space = false;
+ }
+
vector<string> tmp;
if ((opt = opts.find("all-configs")) == opts.end())
<< snapper->getConfig().getSubvolume() << endl;
}
- list_from_one_config(snapper, list_mode);
+ list_from_one_config(snapper, list_mode, show_used_space);
}
}
void
-list_from_one_config(ProxySnapper* snapper, ListMode list_mode)
+list_from_one_config(ProxySnapper* snapper, ListMode list_mode, bool show_used_space)
{
+ if (list_mode != LM_ALL && list_mode != LM_SINGLE)
+ show_used_space = false;
+
+ if (show_used_space)
+ {
+ try
+ {
+ snapper->calculateUsedSpace();
+ }
+ catch (const QuotaException& e)
+ {
+ SN_CAUGHT(e);
+
+ show_used_space = false;
+ }
+ }
+
+ auto add_date = [](TableRow& row, const ProxySnapshot& snapshot) {
+ row.add(snapshot.isCurrent() ? "" : datetime(snapshot.getDate(), utc, iso));
+ };
+
+ auto add_used_space = [show_used_space](TableRow& row, const ProxySnapshot& snapshot) {
+ if (show_used_space)
+ row.add(snapshot.isCurrent() ? "" : byte_to_humanstring(snapshot.getUsedSpace(), 2));
+ };
+
Table table;
switch (list_mode)
header.add(_("Pre #"));
header.add(_("Date"));
header.add(_("User"));
+ if (show_used_space)
+ header.add(_("Used Space"));
header.add(_("Cleanup"));
header.add(_("Description"));
header.add(_("Userdata"));
row.add(toString(snapshot.getType()));
row.add(decString(snapshot.getNum()));
row.add(snapshot.getType() == POST ? decString(snapshot.getPreNum()) : "");
- row.add(snapshot.isCurrent() ? "" : datetime(snapshot.getDate(), utc, iso));
+ add_date(row, snapshot);
row.add(username(snapshot.getUid()));
+ add_used_space(row, snapshot);
row.add(snapshot.getCleanup());
row.add(snapshot.getDescription());
row.add(show_userdata(snapshot.getUserdata()));
header.add(_("#"));
header.add(_("Date"));
header.add(_("User"));
+ if (show_used_space)
+ header.add(_("Used Space"));
header.add(_("Description"));
header.add(_("Userdata"));
table.setHeader(header);
TableRow row;
row.add(decString(snapshot.getNum()));
- row.add(snapshot.isCurrent() ? "" : datetime(snapshot.getDate(), utc, iso));
+ add_date(row, snapshot);
row.add(username(snapshot.getUid()));
+ add_used_space(row, snapshot);
row.add(snapshot.getDescription());
row.add(show_userdata(snapshot.getUserdata()));
table.add(row);
--- /dev/null
+/*
+ * Copyright (c) [2004-2014] Novell, Inc.
+ * Copyright (c) [2016-2018] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you may
+ * find current contact information at www.novell.com.
+ */
+
+
+#include <locale>
+#include <cmath>
+#include <sstream>
+#include <stdexcept>
+
+#include "HumanString.h"
+#include "text.h"
+
+
+namespace snapper
+{
+ using std::string;
+
+
+ /*
+ * These are simplified versions of the functions in libstorage-ng.
+ */
+
+
+ int
+ num_suffixes()
+ {
+ return 7;
+ }
+
+
+ string
+ get_suffix(int i)
+ {
+ switch (i)
+ {
+ case 0:
+ // TRANSLATORS: symbol for "bytes" (best keep untranslated)
+ return _("B");
+
+ case 1:
+ // TRANSLATORS: symbol for "kibi bytes" (best keep untranslated)
+ return _("KiB");
+
+ case 2:
+ // TRANSLATORS: symbol for "mebi bytes" (best keep untranslated)
+ return _("MiB");
+
+ case 3:
+ // TRANSLATORS: symbol for "gibi bytes" (best keep untranslated)
+ return _("GiB");
+
+ case 4:
+ // TRANSLATORS: symbol for "tebi bytes" (best keep untranslated)
+ return _("TiB");
+
+ case 5:
+ // TRANSLATORS: symbol for "pebi bytes" (best keep untranslated)
+ return _("PiB");
+
+ case 6:
+ // TRANSLATORS: symbol for "exbi bytes" (best keep untranslated)
+ return _("EiB");
+
+ default:
+ throw std::logic_error("invalid prefix");
+ }
+ }
+
+
+ bool
+ is_multiple_of(unsigned long long i, unsigned long long j)
+ {
+ return i % j == 0;
+ }
+
+
+ int
+ clz(unsigned long long i)
+ {
+ return __builtin_clzll(i);
+ }
+
+
+ // byte_to_humanstring uses integer arithmetic as long as possible
+ // to provide exact results for those cases.
+
+
+ string
+ byte_to_humanstring(unsigned long long size, int precision)
+ {
+ const std::locale loc = std::locale();
+
+ // Calculate the index of the suffix to use. The index increases by 1
+ // when the number of leading zeros decreases by 10.
+
+ int i = size > 0 ? (64 - (clz(size) + 1)) / 10 : 0;
+
+ if (i == 0)
+ {
+ unsigned long long v = size >> 10 * i;
+
+ std::ostringstream s;
+ s.imbue(loc);
+ s << v << ' ' << get_suffix(i);
+ return s.str();
+ }
+ else
+ {
+ long double v = std::ldexp((long double)(size), - 10 * i);
+
+ std::ostringstream s;
+ s.imbue(loc);
+ s.setf(std::ios::fixed);
+ s.precision(precision);
+ s << v << ' ' << get_suffix(i);
+ return s.str();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) [2004-2014] Novell, Inc.
+ * Copyright (c) [2016,2018] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you may
+ * find current contact information at www.novell.com.
+ */
+
+
+#include <string>
+
+
+namespace snapper
+{
+
+ const unsigned long long B = 1;
+ const unsigned long long KiB = 1024 * B;
+ const unsigned long long MiB = 1024 * KiB;
+ const unsigned long long GiB = 1024 * MiB;
+ const unsigned long long TiB = 1024 * GiB;
+ const unsigned long long PiB = 1024 * TiB;
+ const unsigned long long EiB = 1024 * PiB;
+
+
+ /**
+ * Return number of suffixes.
+ *
+ * @return number of suffixes
+ */
+ int num_suffixes();
+
+
+ /**
+ * Return a suffix.
+ *
+ * @param i index of suffix
+ * @param classic use classic locale instead of global C++ locale
+ * @return suffix
+ */
+ std::string get_suffix(int i);
+
+
+ /**
+ * Return a pretty description of a size with required precision and using
+ * B, KiB, MiB, GiB, TiB, PiB or EiB as unit as appropriate. Supported
+ * range is 0 B to (16 EiB - 1 B).
+ *
+ * @param size size in bytes
+ * @param precision number of fraction digits in output
+ * @return formatted string
+ */
+ std::string byte_to_humanstring(unsigned long long size, int precision);
+
+}
console.cc console.h \
equal-date.cc equal-date.h \
GetOpts.cc GetOpts.h \
- Range.cc Range.h
+ Range.cc Range.h \
+ HumanString.cc HumanString.h
libutils_la_LIBADD = ../../snapper/libsnapper.la
all, single and pre-post.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--disable-used-space</option></term>
+ <listitem>
+ <para>Disable display of used space.</para>
+ <para>Calculating the used space needs some time. Thus
+ this option can speedup the listing.</para>
+ <para>Display of used space is automatically disable
+ is not available, e.g. quota not enabled on btrfs.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>-a, --all-configs</option></term>
<listitem>
+-------------------------------------------------------------------
+Tue Oct 02 09:56:59 CEST 2018 - aschnell@suse.com
+
+- show used space (exclusive space of btrfs qgroup) for each
+ snapshot (fate#323843)
+
-------------------------------------------------------------------
Fri Sep 14 12:13:05 CEST 2018 - aschnell@suse.com
LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la -lboost_unit_test_framework
check_PROGRAMS = sysconfig-get1.test dirname1.test basename1.test \
- equal-date.test dbus-escape.test cmp-lt.test
+ equal-date.test dbus-escape.test cmp-lt.test humanstring.test
if ENABLE_BTRFS_QUOTA
check_PROGRAMS += qgroup1.test
equal_date_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la
+humanstring_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la
+
--- /dev/null
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE libstorage
+
+#include <boost/test/unit_test.hpp>
+
+#include <stdlib.h>
+#include <iostream>
+#include <locale>
+
+#include "../client/utils/HumanString.h"
+
+
+using namespace std;
+using namespace snapper;
+
+
+// Tests here must work when using double instead of long double in
+// HumanString.cc.
+
+
+string
+test(const char* loc, unsigned long long size, int precision)
+{
+ locale::global(locale(loc));
+
+ return byte_to_humanstring(size, precision);
+}
+
+
+
+BOOST_AUTO_TEST_CASE(test_byte_to_humanstring)
+{
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 0, 2), "0 B");
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1023, 2), "1,023 B");
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1024, 2), "1.00 KiB");
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1025, 2), "1.00 KiB");
+
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", 123456789, 4), "117,7376 MiB");
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1000 * KiB, 2), "1,000.00 KiB");
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", 1000 * KiB, 2), "1.000,00 KiB");
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", 1000 * KiB, 2), "1'000.00 KiB");
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 50 * MiB, 2), "50.00 MiB");
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", 50 * MiB, 2), "50,00 MiB");
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", 50 * MiB, 2), "50.00 MiB");
+}
+
+
+BOOST_AUTO_TEST_CASE(test_big_numbers)
+{
+ // 1 EiB - 1 B
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1 * EiB - 1 * B, 2), "1,024.00 PiB");
+
+ // 1 EiB
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1 * EiB, 2), "1.00 EiB");
+
+ // 1 EiB + 1 B
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1 * EiB + 1 * B, 2), "1.00 EiB");
+
+ // 16 EiB - 1 B
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 16 * EiB - 1 * B, 2), "16.00 EiB");
+}