From: José Iván López González Date: Wed, 30 Oct 2019 23:16:26 +0000 (+0000) Subject: Add ListSnapshots command X-Git-Tag: v0.8.6~10^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1dc2a85d66e5d220906701fb0fb351f5d47c88d7;p=thirdparty%2Fsnapper.git Add ListSnapshots command --- diff --git a/client/Command/.gitignore b/client/Command/.gitignore new file mode 100644 index 00000000..66a3f3fe --- /dev/null +++ b/client/Command/.gitignore @@ -0,0 +1,2 @@ +*.lo +*.la diff --git a/client/Command/ColumnsOption.cc b/client/Command/ColumnsOption.cc new file mode 100644 index 00000000..8384c000 --- /dev/null +++ b/client/Command/ColumnsOption.cc @@ -0,0 +1,78 @@ +/* + * Copyright (c) [2019] 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 + +#include + +#include "client/Command/ColumnsOption.h" +#include "client/utils/text.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + vector Command::ColumnsOption::selected_columns() const + { + if (_raw_columns.empty()) + return {}; + + vector columns; + + boost::split(columns, _raw_columns, boost::is_any_of(","), boost::token_compress_on); + + return columns; + } + + + string Command::ColumnsOption::error() const + { + if (wrong_columns().empty()) + return ""; + + return _("Unknown columns: ") + boost::algorithm::join(wrong_columns(), ", "); + } + + + vector Command::ColumnsOption::wrong_columns() const + { + vector wrong; + + for (auto column : selected_columns()) + { + if (wrong_column(column)) + wrong.push_back(column); + } + + return wrong; + } + + + bool Command::ColumnsOption::wrong_column(const std::string& column) const + { + return find(_all_columns.begin(), _all_columns.end(), column) == _all_columns.end(); + } + + } +} diff --git a/client/Command/ColumnsOption.h b/client/Command/ColumnsOption.h new file mode 100644 index 00000000..93bcb8d6 --- /dev/null +++ b/client/Command/ColumnsOption.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_COLUMNS_OPTION_H +#define SNAPPER_CLI_COMMAND_COLUMNS_OPTION_H + +#include +#include + +#include "client/Command.h" + +namespace snapper +{ + namespace cli + { + + class Command::ColumnsOption + { + + public: + + ColumnsOption(const std::vector& all_columns) : + _all_columns(all_columns), _raw_columns() + {} + + void set_raw_columns(const string& raw_columns) + { + _raw_columns = raw_columns; + } + + std::vector selected_columns() const; + + std::string error() const; + + std::vector wrong_columns() const; + + private: + + bool wrong_column(const std::string& column) const; + + const std::vector& _all_columns; + + std::string _raw_columns; + + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots.cc b/client/Command/ListSnapshots.cc new file mode 100644 index 00000000..c62d005e --- /dev/null +++ b/client/Command/ListSnapshots.cc @@ -0,0 +1,115 @@ +/* + * Copyright (c) [2019] 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 + +#include "client/utils/text.h" +#include "client/Command/ListSnapshots.h" +#include "client/Command/ListSnapshots/Options.h" +#include "client/Command/ListSnapshots/SnappersData/Table.h" +#include "client/Command/ListSnapshots/SnappersData/Csv.h" +#include "client/Command/ListSnapshots/SnappersData/Json.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + string Command::ListSnapshots::help() + { + return + string( + _(" List snapshots:\n" + "\tsnapper list\n" + "\n") + ) + Options::help_text(); + } + + + Command::ListSnapshots::ListSnapshots(const GlobalOptions& global_options, + GetOpts& options_parser, ProxySnappers& snappers) : + Command(global_options, options_parser, snappers), _options(new Options(options_parser)) + {} + + + Command::ListSnapshots::ListSnapshots::~ListSnapshots() + {} + + + const Command::ListSnapshots::Options& Command::ListSnapshots::options() const + { + return *_options.get(); + } + + + vector Command::ListSnapshots::errors() const + { + return options().errors(); + } + + + void Command::ListSnapshots::run() + { + if (_options_parser.hasArgs()) + { + cerr << _("Command 'list' does not take arguments.") << endl; + exit(EXIT_FAILURE); + } + + if (options().has_errors()) + { + cerr << options().errors().front() << endl; + exit(EXIT_FAILURE); + } + + string output; + + switch (global_options().output_format()) + { + case GlobalOptions::OutputFormat::TABLE: + { + SnappersData::Table table(*this, global_options().table_style()); + output = table.output(); + } + break; + + case GlobalOptions::OutputFormat::CSV: + { + SnappersData::Csv csv(*this, global_options().separator()); + output = csv.output(); + } + break; + + case GlobalOptions::OutputFormat::JSON: + { + SnappersData::Json json(*this); + output = json.output() + "\n"; + } + break; + } + + cout << output; + } + + } +} diff --git a/client/Command/ListSnapshots.h b/client/Command/ListSnapshots.h new file mode 100644 index 00000000..4ceaea0f --- /dev/null +++ b/client/Command/ListSnapshots.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_H + +#include + +#include "client/Command.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots : public Command + { + + public: + + class Options; + + class ColumnsOption; + + class SnappersData; + + static std::string help(); + + ListSnapshots(const GlobalOptions& global_options, GetOpts& options_parser, + ProxySnappers& snappers); + + const Options& options() const; + + virtual std::vector errors() const override; + + virtual void run() override; + + virtual ~ListSnapshots(); + + private: + + std::unique_ptr _options; + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots/Makefile.am b/client/Command/ListSnapshots/Makefile.am new file mode 100644 index 00000000..0ff57341 --- /dev/null +++ b/client/Command/ListSnapshots/Makefile.am @@ -0,0 +1,19 @@ +# +# Makefile.am for snapper/client/Command/ListSnapshots +# + +SUBDIRS = SnappersData + +AM_CPPFLAGS = -I$(top_srcdir) $(DBUS_CFLAGS) + +noinst_LTLIBRARIES = libcommandlistsnapshots.la + +libcommandlistsnapshots_la_SOURCES = \ + Options.cc Options.h \ + SnappersData.cc SnappersData.h + +libcommandlistsnapshots_la_LIBADD = \ + ../../../snapper/libsnapper.la \ + ../../utils/libutils.la \ + ../../../dbus/libdbus.la \ + SnappersData/libcommandlistsnapshotssnappersdata.la diff --git a/client/Command/ListSnapshots/Options.cc b/client/Command/ListSnapshots/Options.cc new file mode 100644 index 00000000..c503d4e8 --- /dev/null +++ b/client/Command/ListSnapshots/Options.cc @@ -0,0 +1,364 @@ +/* + * Copyright (c) [2019] 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 + +#include + +#include "client/Command/ListSnapshots/Options.h" +#include "client/utils/text.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + namespace + { + const option OPTIONS[] = { + { "type", required_argument, 0, 't' }, + { "disable-used-space", no_argument, 0, 0 }, + { "all-configs", no_argument, 0, 'a' }, + { "columns", required_argument, 0, 0}, + { 0, 0, 0, 0 } + }; + } + + + string Command::ListSnapshots::Options::help_text() + { + return _(" Options for 'list' command:\n" + "\t--type, -t \t\tType of snapshots to list.\n" + "\t--disable-used-space\t\tDisable showing used space.\n" + "\t--all-configs, -a\t\tList snapshots from all accessible configs.\n" + "\t--columns \t\tColumns to show separated by comma.\n" + "\t\t\t\t\tPossible columns: config, subvolume, number, default, active,\n" + "\t\t\t\t\ttype, date, user, used-space, cleanup, description, userdata,\n" + "\t\t\t\t\tpre-number, post-number, post-date.\n"); + } + + + const string Command::ListSnapshots::Options::Columns::CONFIG = "config"; + const string Command::ListSnapshots::Options::Columns::SUBVOLUME = "subvolume"; + const string Command::ListSnapshots::Options::Columns::NUMBER = "number"; + const string Command::ListSnapshots::Options::Columns::DEFAULT = "default"; + const string Command::ListSnapshots::Options::Columns::ACTIVE = "active"; + const string Command::ListSnapshots::Options::Columns::TYPE = "type"; + const string Command::ListSnapshots::Options::Columns::DATE = "date"; + const string Command::ListSnapshots::Options::Columns::USER = "user"; + const string Command::ListSnapshots::Options::Columns::USED_SPACE = "used-space"; + const string Command::ListSnapshots::Options::Columns::CLEANUP = "cleanup"; + const string Command::ListSnapshots::Options::Columns::DESCRIPTION = "description"; + const string Command::ListSnapshots::Options::Columns::USERDATA = "userdata"; + const string Command::ListSnapshots::Options::Columns::PRE_NUMBER = "pre-number"; + const string Command::ListSnapshots::Options::Columns::POST_NUMBER = "post-number"; + const string Command::ListSnapshots::Options::Columns::POST_DATE = "post-date"; + + + const vector Command::ListSnapshots::Options::ALL_COLUMNS = { + Columns::CONFIG, + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::TYPE, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::CLEANUP, + Columns::DESCRIPTION, + Columns::USERDATA, + Columns::PRE_NUMBER, + Columns::POST_NUMBER, + Columns::POST_DATE + }; + + + Command::ListSnapshots::Options::Options(GetOpts& parser) : + cli::Options(parser), _columns_option(ALL_COLUMNS) + { + parse_options(); + + _disable_used_space = has_option("disable-used-space"); + _all_configs = has_option("all-configs"); + _list_mode = list_mode_value(); + + _columns_option.set_raw_columns(columns_raw()); + } + + + void Command::ListSnapshots::Options::parse_options() + { + _options = _parser.parse("list", OPTIONS); + } + + + vector + Command::ListSnapshots::Options::columns(GlobalOptions::OutputFormat format) const + { + if (has_option("columns")) + return _columns_option.selected_columns(); + + return list_mode_columns(format); + } + + + vector Command::ListSnapshots::Options::errors() const + { + vector detected_errors; + + if (wrong_type()) + detected_errors.push_back(type_error()); + + if (wrong_columns()) + detected_errors.push_back(columns_error()); + + return detected_errors; + } + + + Command::ListSnapshots::Options::ListMode + Command::ListSnapshots::Options::list_mode_value() const + { + string type = has_option("type") ? get_option("type")->second : ""; + + if (type == "all") + return LM_ALL; + + if (type == "single") + return LM_SINGLE; + + if (type == "pre-post") + return LM_PRE_POST; + + return LM_ALL; + } + + + string Command::ListSnapshots::Options::columns_raw() const + { + return has_option("columns") ? get_option("columns")->second : ""; + } + + + vector + Command::ListSnapshots::Options::list_mode_columns(GlobalOptions::OutputFormat format) const + { + vector columns; + + switch (list_mode()) + { + case LM_ALL: + columns = all_mode_columns(format); + break; + + case LM_SINGLE: + columns = single_mode_columns(format); + break; + + case LM_PRE_POST: + columns = pre_post_mode_columns(format); + break; + } + + if (disable_used_space()) + { + columns.erase( + remove(columns.begin(), columns.end(), Columns::USED_SPACE), columns.end()); + } + + return columns; + } + + + vector + Command::ListSnapshots::Options::all_mode_columns(GlobalOptions::OutputFormat format) const + { + if (format == GlobalOptions::OutputFormat::TABLE) + return { + Columns::NUMBER, + Columns::TYPE, + Columns::PRE_NUMBER, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::CLEANUP, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::CSV) + return { + Columns::CONFIG, + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::TYPE, + Columns::PRE_NUMBER, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::CLEANUP, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::JSON) + return { + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::TYPE, + Columns::PRE_NUMBER, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::CLEANUP, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + throw logic_error("unknown output format"); + } + + + vector + Command::ListSnapshots::Options::single_mode_columns(GlobalOptions::OutputFormat format) const + { + if (format == GlobalOptions::OutputFormat::TABLE) + return { + Columns::NUMBER, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::CSV) + return { + Columns::CONFIG, + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::JSON) + return { + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::DATE, + Columns::USER, + Columns::USED_SPACE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + throw logic_error("unknown output format"); + } + + + vector + Command::ListSnapshots::Options::pre_post_mode_columns(GlobalOptions::OutputFormat format) const + { + if (format == GlobalOptions::OutputFormat::TABLE) + return { + Columns::NUMBER, + Columns::POST_NUMBER, + Columns::DATE, + Columns::POST_DATE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::CSV) + return { + Columns::CONFIG, + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::POST_NUMBER, + Columns::DATE, + Columns::POST_DATE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + if (format == GlobalOptions::OutputFormat::JSON) + return { + Columns::SUBVOLUME, + Columns::NUMBER, + Columns::DEFAULT, + Columns::ACTIVE, + Columns::POST_NUMBER, + Columns::DATE, + Columns::POST_DATE, + Columns::DESCRIPTION, + Columns::USERDATA + }; + + throw logic_error("unknown output format"); + } + + + bool Command::ListSnapshots::Options::wrong_type() const + { + if (!has_option("type")) + return false; + + string type = get_option("type")->second; + + return type != "all" && type != "single" && type != "pre-post"; + } + + + bool Command::ListSnapshots::Options::wrong_columns() const + { + return !_columns_option.wrong_columns().empty(); + } + + + string Command::ListSnapshots::Options::type_error() const + { + return _("Unknown type of snapshots."); + } + + + string Command::ListSnapshots::Options::columns_error() const + { + return _columns_option.error(); + } + + } +} diff --git a/client/Command/ListSnapshots/Options.h b/client/Command/ListSnapshots/Options.h new file mode 100644 index 00000000..0bc1d36d --- /dev/null +++ b/client/Command/ListSnapshots/Options.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_OPTIONS_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_OPTIONS_H + +#include "client/Options.h" +#include "client/GlobalOptions.h" +#include "client/Command/ColumnsOption.h" +#include "client/Command/ListSnapshots.h" +#include "client/Command/ListSnapshots/SnappersData.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots::Options : public cli::Options + { + + public: + + struct Columns + { + static const std::string CONFIG; + static const std::string SUBVOLUME; + static const std::string NUMBER; + static const std::string DEFAULT; + static const std::string ACTIVE; + static const std::string TYPE; + static const std::string DATE; + static const std::string USER; + static const std::string USED_SPACE; + static const std::string CLEANUP; + static const std::string DESCRIPTION; + static const std::string USERDATA; + static const std::string PRE_NUMBER; + static const std::string POST_NUMBER; + static const std::string POST_DATE; + }; + + static const std::vector ALL_COLUMNS; + + enum ListMode { LM_ALL, LM_SINGLE, LM_PRE_POST }; + + static std::string help_text(); + + Options(GetOpts& parser); + + ListMode list_mode() const + { + return _list_mode; + } + + bool disable_used_space() const + { + return _disable_used_space; + } + + bool all_configs() const + { + return _all_configs; + } + + std::vector columns(GlobalOptions::OutputFormat format) const; + + virtual std::vector errors() const override; + + private: + + void parse_options(); + + ListMode list_mode_value() const; + + string columns_raw() const; + + std::vector list_mode_columns(GlobalOptions::OutputFormat format) const; + + std::vector all_mode_columns(GlobalOptions::OutputFormat format) const; + + std::vector single_mode_columns(GlobalOptions::OutputFormat format) const; + + std::vector pre_post_mode_columns(GlobalOptions::OutputFormat format) const; + + bool wrong_type() const; + + bool wrong_columns() const; + + std::string type_error() const; + + std::string columns_error() const; + + ListMode _list_mode; + + bool _disable_used_space; + + bool _all_configs; + + Command::ColumnsOption _columns_option; + + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots/SnappersData.cc b/client/Command/ListSnapshots/SnappersData.cc new file mode 100644 index 00000000..fda2770b --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData.cc @@ -0,0 +1,486 @@ +/* + * Copyright (c) [2019] 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "client/Command/ListSnapshots/SnappersData.h" +#include "client/Command/ListSnapshots/Options.h" +#include "client/utils/HumanString.h" +#include "client/misc.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + Command::ListSnapshots::SnappersData::SnappersData(const Command::ListSnapshots& command) : + _command(command) + { + calculate_used_space(); + } + + + Command::ListSnapshots::SnappersData::~SnappersData() + {} + + + vector Command::ListSnapshots::SnappersData::snappers() const + { + vector snappers; + + for (auto config_name : configs()) + { + ProxySnapper* snapper = command().snappers().getSnapper(config_name); + + snappers.push_back(snapper); + } + + return snappers; + } + + + vector + Command::ListSnapshots::SnappersData::selected_snapshots(const ProxySnapper* snapper) const + { + const ProxySnapshots& snapshots = this->snapshots(snapper); + + vector selected; + + for (const ProxySnapshot& snapshot : snapshots) + { + switch (command().options().list_mode()) + { + case Options::ListMode::LM_ALL: + { + selected.push_back(&snapshot); + } + break; + + case Options::ListMode::LM_SINGLE: + { + if (snapshot.getType() == SINGLE) + selected.push_back(&snapshot); + } + break; + + case Options::ListMode::LM_PRE_POST: + { + ProxySnapshots::const_iterator post_it = find_post(snapshot, snapshots); + + if (post_it != snapshots.end()) + selected.push_back(&snapshot); + } + break; + } + } + + return selected; + } + + + vector + Command::ListSnapshots::SnappersData::selected_columns() const + { + return command().options().columns(command().global_options().output_format()); + } + + + string Command::ListSnapshots::SnappersData::value_for(const string& column, + const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots, + const ProxySnapper* snapper) const + { + if (column == Options::Columns::CONFIG) + return config_value(snapper); + + if (column == Options::Columns::SUBVOLUME) + return subvolume_value(snapper); + + if (column == Options::Columns::NUMBER) + return number_value(snapshot, snapshots); + + if (column == Options::Columns::DEFAULT) + return default_value(snapshot, snapshots); + + if (column == Options::Columns::ACTIVE) + return active_value(snapshot, snapshots); + + if (column == Options::Columns::TYPE) + return type_value(snapshot); + + if (column == Options::Columns::DATE) + return date_value(snapshot); + + if (column == Options::Columns::USER) + return user_value(snapshot); + + if (column == Options::Columns::USED_SPACE) + return used_space_value(snapshot, snapper); + + if (column == Options::Columns::CLEANUP) + return cleanup_value(snapshot); + + if (column == Options::Columns::DESCRIPTION) + return description_value(snapshot); + + if (column == Options::Columns::USERDATA) + return userdata_value(snapshot); + + if (column == Options::Columns::PRE_NUMBER) + return pre_number_value(snapshot); + + if (column == Options::Columns::POST_NUMBER) + return post_number_value(snapshot, snapshots); + + if (column == Options::Columns::POST_DATE) + return post_date_value(snapshot, snapshots); + + return ""; + } + + + vector Command::ListSnapshots::SnappersData::configs() const + { + vector names; + + if (!command().options().all_configs()) + { + names.push_back(command().global_options().config()); + } + else + { + map configs = command().snappers().getConfigs(); + + for (map::value_type it : configs) + names.push_back(it.first); + } + + return names; + } + + + const ProxySnapshots& + Command::ListSnapshots::SnappersData::snapshots(const ProxySnapper* snapper) const + { + return snapper->getSnapshots(); + } + + + bool + Command::ListSnapshots::SnappersData::calculated_used_space(const ProxySnapper* snapper) const + { + map::const_iterator it = _calculated_used_space.find(snapper); + + if (it == _calculated_used_space.end()) + return false; + + return it->second; + } + + + void Command::ListSnapshots::SnappersData::calculate_used_space() + { + for (ProxySnapper* snapper : snappers()) + calculate_used_space(snapper); + } + + + void + Command::ListSnapshots::SnappersData::calculate_used_space(ProxySnapper* snapper) + { + bool calculated; + + if (!need_used_space()) + { + calculated = false; + } + else + { + try + { + snapper->calculateUsedSpace(); + + calculated = true; + } + catch (const QuotaException& e) + { + SN_CAUGHT(e); + + calculated = false; + } + } + + _calculated_used_space[snapper] = calculated; + } + + + bool Command::ListSnapshots::SnappersData::need_used_space() const + { + vector columns = selected_columns(); + + vector::iterator it = + find(columns.begin(), columns.end(), Options::Columns::USED_SPACE); + + return it != columns.end(); + } + + + string + Command::ListSnapshots::SnappersData::config_value(const ProxySnapper* snapper) const + { + return snapper->configName(); + } + + + string + Command::ListSnapshots::SnappersData::subvolume_value(const ProxySnapper* snapper) const + { + return snapper->getConfig().getSubvolume(); + } + + + string + Command::ListSnapshots::SnappersData::number_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + return decString(snapshot.getNum()); + } + + + string Command::ListSnapshots::SnappersData::default_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + return boolean_text(is_default_snapshot(snapshot, snapshots)); + } + + + string Command::ListSnapshots::SnappersData::active_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + return boolean_text(is_active_snapshot(snapshot, snapshots)); + } + + + string + Command::ListSnapshots::SnappersData::date_value(const ProxySnapshot& snapshot) const + { + if (command().options().list_mode() == Options::ListMode::LM_PRE_POST) + return snapshot_date(snapshot); + + return snapshot.isCurrent() ? "" : snapshot_date(snapshot); + } + + string + Command::ListSnapshots::SnappersData::type_value(const ProxySnapshot& snapshot) const + { + return toString(snapshot.getType()); + } + + + string + Command::ListSnapshots::SnappersData::user_value(const ProxySnapshot& snapshot) const + { + return username(snapshot.getUid()); + } + + + string Command::ListSnapshots::SnappersData::used_space_value( + const ProxySnapshot& snapshot, const ProxySnapper* snapper) const + { + if (!calculated_used_space(snapper)) + return ""; + + return snapshot.isCurrent() ? "" : snapshot_used_value(snapshot); + } + + + string + Command::ListSnapshots::SnappersData::cleanup_value(const ProxySnapshot& snapshot) const + { + return snapshot.getCleanup(); + } + + + string + Command::ListSnapshots::SnappersData::description_value(const ProxySnapshot& snapshot) const + { + return snapshot.getDescription(); + } + + + string + Command::ListSnapshots::SnappersData::userdata_value(const ProxySnapshot& snapshot) const + { + return show_userdata(snapshot.getUserdata()); + } + + + string + Command::ListSnapshots::SnappersData::pre_number_value(const ProxySnapshot& snapshot) const + { + return snapshot.getType() == POST ? decString(snapshot.getPreNum()) : ""; + } + + + string + Command::ListSnapshots::SnappersData::post_number_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + ProxySnapshots::const_iterator post_it = find_post(snapshot, snapshots); + + if (post_it == snapshots.end()) + return ""; + + return number_value(*post_it, snapshots); + } + + + string + Command::ListSnapshots::SnappersData::post_date_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + ProxySnapshots::const_iterator post_it = find_post(snapshot, snapshots); + + if (post_it == snapshots.end()) + return ""; + + return snapshot_date(*post_it); + } + + + string + Command::ListSnapshots::SnappersData::snapshot_date(const ProxySnapshot& snapshot) const + { + bool iso = command().global_options().iso(); + + if (command().global_options().output_format() != GlobalOptions::OutputFormat::TABLE) + iso = true; + + return datetime(snapshot.getDate(), command().global_options().utc(), iso); + } + + + string + Command::ListSnapshots::SnappersData::snapshot_used_value(const ProxySnapshot& snapshot) const + { + uint64_t used_space = snapshot.getUsedSpace(); + + if (command().global_options().output_format() == GlobalOptions::OutputFormat::TABLE) + return byte_to_humanstring(used_space, 2); + else + return to_string(used_space); + } + + + bool + Command::ListSnapshots::SnappersData::is_default_snapshot(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + auto default_snapshot = this->default_snapshot(snapshots); + + return default_snapshot != snapshots.end() && + default_snapshot->getNum() == snapshot.getNum(); + } + + + bool + Command::ListSnapshots::SnappersData::is_active_snapshot(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + auto active_snapshot = this->active_snapshot(snapshots); + + return active_snapshot != snapshots.end() && + active_snapshot->getNum() == snapshot.getNum(); + } + + + ProxySnapshots::const_iterator + Command::ListSnapshots::SnappersData::default_snapshot(const ProxySnapshots& snapshots) const + { + try + { + return snapshots.getDefault(); + } + catch (const DBus::ErrorException& e) + { + SN_CAUGHT(e); + + // If snapper was just updated and the old snapperd is still + // running it might not know the GetDefaultSnapshot method. + + if (strcmp(e.name(), "error.unknown_method") != 0) + SN_RETHROW(e); + + return snapshots.end(); + } + } + + + ProxySnapshots::const_iterator + Command::ListSnapshots::SnappersData::active_snapshot(const ProxySnapshots& snapshots) const + { + try + { + return snapshots.getActive(); + } + catch (const DBus::ErrorException& e) + { + SN_CAUGHT(e); + + // If snapper was just updated and the old snapperd is still + // running it might not know the GetActiveSnapshot method. + + if (strcmp(e.name(), "error.unknown_method") != 0) + SN_RETHROW(e); + + return snapshots.end(); + } + } + + + ProxySnapshots::const_iterator Command::ListSnapshots::SnappersData::find_post( + const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const + { + if (snapshot.getType() != SnapshotType::PRE) + return snapshots.end(); + + ProxySnapshots::const_iterator it = snapshots.find(snapshot.getNum()); + ProxySnapshots::const_iterator post_it = snapshots.findPost(it); + + if (post_it == snapshots.end()) + return snapshots.end(); + + return post_it; + } + + } +} diff --git a/client/Command/ListSnapshots/SnappersData.h b/client/Command/ListSnapshots/SnappersData.h new file mode 100644 index 00000000..204eca51 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_H + +#include +#include +#include + +#include "client/Command/ListSnapshots.h" +#include "client/proxy.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots::SnappersData + { + + public: + + class Table; + + class Csv; + + class Json; + + SnappersData(const Command::ListSnapshots& command); + + virtual ~SnappersData(); + + virtual std::string output() const = 0; + + protected: + + const ListSnapshots& command() const + { + return _command; + } + + std::vector snappers() const; + + vector selected_snapshots(const ProxySnapper* snapper) const; + + std::vector selected_columns() const; + + std::string value_for(const string& column, + const ProxySnapshot& snapshot, const ProxySnapshots& snapshots, + const ProxySnapper* snapper) const; + + private: + + std::vector configs() const; + + const ProxySnapshots& snapshots(const ProxySnapper* snapper) const; + + bool calculated_used_space(const ProxySnapper* snapper) const; + + void calculate_used_space(); + + void calculate_used_space(ProxySnapper* snapper); + + bool need_used_space() const; + + std::string config_value(const ProxySnapper* snapper) const; + + std::string subvolume_value(const ProxySnapper* snapper) const; + + virtual std::string + number_value(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + std::string + default_value(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + std::string + active_value(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + std::string type_value(const ProxySnapshot& snapshot) const; + + std::string date_value(const ProxySnapshot& snapshot) const; + + std::string user_value(const ProxySnapshot& snapshot) const; + + std::string + used_space_value(const ProxySnapshot& snapshot, const ProxySnapper* snapper) const; + + std::string cleanup_value(const ProxySnapshot& snapshot) const; + + std::string description_value(const ProxySnapshot& snapshot) const; + + std::string userdata_value(const ProxySnapshot& snapshot) const; + + std::string pre_number_value(const ProxySnapshot& snapshot) const; + + std::string post_number_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const; + + std::string + post_date_value(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + std::string snapshot_date(const ProxySnapshot& snapshot) const; + + std::string snapshot_used_value(const ProxySnapshot& snapshot) const; + + bool + is_default_snapshot(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + bool + is_active_snapshot(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + ProxySnapshots::const_iterator default_snapshot(const ProxySnapshots& snapshots) const; + + ProxySnapshots::const_iterator active_snapshot(const ProxySnapshots& snapshots) const; + + ProxySnapshots::const_iterator + find_post(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const; + + virtual string boolean_text(bool value) const = 0; + + const ListSnapshots& _command; + + std::map _calculated_used_space; + + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots/SnappersData/Csv.cc b/client/Command/ListSnapshots/SnappersData/Csv.cc new file mode 100644 index 00000000..9be1ba62 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Csv.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) [2019] 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 + +#include "client/Command/ListSnapshots/SnappersData/Csv.h" +#include "client/utils/CsvFormatter.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + Command::ListSnapshots::SnappersData::Csv::Csv( + const Command::ListSnapshots& command, const string separator) : + SnappersData(command), _separator(separator) + {} + + + std::string Command::ListSnapshots::SnappersData::Csv::output() const + { + vector columns; + + vector> rows; + + for (const string& column : selected_columns()) + columns.push_back(column); + + for (ProxySnapper* snapper : snappers()) + { + vector> snapper_rows = this->snapper_rows(snapper); + + rows.insert(rows.end(), snapper_rows.begin(), snapper_rows.end()); + } + + CsvFormatter formatter(columns, rows, _separator); + + return formatter.output(); + } + + + vector> + Command::ListSnapshots::SnappersData::Csv::snapper_rows(const ProxySnapper* snapper) const + { + vector> rows; + + const ProxySnapshots& snapshots = this->snapshots(snapper); + + for (const ProxySnapshot* snapshot : selected_snapshots(snapper)) + { + vector row; + + for (const string& column : selected_columns()) + { + row.push_back(value_for(column, *snapshot, snapshots, snapper)); + } + + rows.push_back(row); + } + + return rows; + } + + + string Command::ListSnapshots::SnappersData::Csv::boolean_text(bool value) const + { + return value ? "yes" : "no"; + } + + } +} diff --git a/client/Command/ListSnapshots/SnappersData/Csv.h b/client/Command/ListSnapshots/SnappersData/Csv.h new file mode 100644 index 00000000..59421917 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Csv.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_CSV_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_CSV_H + +#include +#include + +#include "client/Command/ListSnapshots/SnappersData.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots::SnappersData::Csv : public Command::ListSnapshots::SnappersData + { + + public: + + Csv(const ListSnapshots& command, const std::string separator); + + virtual std::string output() const override; + + private: + + std::vector> snapper_rows(const ProxySnapper* snapper) const; + + virtual string boolean_text(bool value) const override; + + const std::string _separator; + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots/SnappersData/Json.cc b/client/Command/ListSnapshots/SnappersData/Json.cc new file mode 100644 index 00000000..582a716a --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Json.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) [2019] 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 + +#include "client/Command/ListSnapshots/SnappersData/Json.h" +#include "client/Command/ListSnapshots/Options.h" +#include "client/utils/JsonFormatter.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + namespace + { + + string userdata_json(const map& userdata) + { + if (userdata.empty()) + return ""; + + JsonFormatter::Data data; + + for (auto udata : userdata) + data.emplace_back(udata.first, udata.second); + + JsonFormatter formatter(data); + + formatter.set_inline(true); + + return formatter.output(3); + } + + } + + + std::string Command::ListSnapshots::SnappersData::Json::output() const + { + JsonFormatter::Data data; + + vector keys; + + for (ProxySnapper* snapper : snappers()) + { + data.emplace_back(snapper_key(snapper), snapper_value(snapper)); + + keys.push_back(snapper_key(snapper)); + } + + JsonFormatter formatter(data); + + formatter.skip_format_values(keys); + + return formatter.output(); + } + + + string + Command::ListSnapshots::SnappersData::Json::snapper_key(const ProxySnapper* snapper) const + { + return snapper->configName(); + } + + + string + Command::ListSnapshots::SnappersData::Json::snapper_value(const ProxySnapper* snapper) const + { + vector data; + + const ProxySnapshots& snapshots = this->snapshots(snapper); + + for (const ProxySnapshot* snapshot : selected_snapshots(snapper)) + { + JsonFormatter::Data snapshot_data; + + vector skip_format; + + for (const string& column : selected_columns()) + { + string value = value_for(column, *snapshot, snapshots, snapper); + + if (column == Options::Columns::USERDATA) + value = userdata_json(snapshot->getUserdata()); + + if (value.empty()) + { + value = "null"; + skip_format.push_back(column); + } + + if (skip_format_value(column)) + skip_format.push_back(column); + + snapshot_data.emplace_back(column, value); + } + + JsonFormatter formatter(snapshot_data); + + formatter.skip_format_values(skip_format); + + data.push_back(formatter.output(2)); + } + + JsonFormatter::List json_list(data); + + return json_list.output(1); + } + + + bool Command::ListSnapshots::SnappersData::Json::skip_format_value(const string& column) const + { + if (column == Options::Columns::NUMBER || + column == Options::Columns::DEFAULT || + column == Options::Columns::ACTIVE || + column == Options::Columns::USED_SPACE || + column == Options::Columns::USERDATA || + column == Options::Columns::PRE_NUMBER || + column == Options::Columns::POST_NUMBER) + return true; + else + return false; + } + + + string Command::ListSnapshots::SnappersData::Json::boolean_text(bool value) const + { + return value ? "true" : "false"; + } + + } +} diff --git a/client/Command/ListSnapshots/SnappersData/Json.h b/client/Command/ListSnapshots/SnappersData/Json.h new file mode 100644 index 00000000..95786219 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Json.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_JSON_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_JSON_H + +#include +#include + +#include "client/Command/ListSnapshots/SnappersData.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots::SnappersData::Json : public Command::ListSnapshots::SnappersData + { + + public: + + using Command::ListSnapshots::SnappersData::SnappersData; + + virtual std::string output() const override; + + private: + + std::string snapper_key(const ProxySnapper* snapper) const; + + std::string snapper_value(const ProxySnapper* snapper) const; + + std::string snapshot_json(const ProxySnapshot* snapshot) const; + + bool skip_format_value(const string& column) const; + + virtual string boolean_text(bool value) const override; + + }; + + } +} + +#endif diff --git a/client/Command/ListSnapshots/SnappersData/Makefile.am b/client/Command/ListSnapshots/SnappersData/Makefile.am new file mode 100644 index 00000000..41ebdb2e --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Makefile.am @@ -0,0 +1,17 @@ +# +# Makefile.am for snapper/client/Command/ListSnapshots/SnappersData +# + +AM_CPPFLAGS = -I$(top_srcdir) $(DBUS_CFLAGS) + +noinst_LTLIBRARIES = libcommandlistsnapshotssnappersdata.la + +libcommandlistsnapshotssnappersdata_la_SOURCES = \ + Table.cc Table.h \ + Csv.cc Csv.h \ + Json.cc Json.h + +libcommandlistsnapshotssnappersdata_la_LIBADD = \ + ../../../../snapper/libsnapper.la \ + ../../../utils/libutils.la \ + ../../../../dbus/libdbus.la diff --git a/client/Command/ListSnapshots/SnappersData/Table.cc b/client/Command/ListSnapshots/SnappersData/Table.cc new file mode 100644 index 00000000..424214c4 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Table.cc @@ -0,0 +1,205 @@ +/* + * Copyright (c) [2019] 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 + +#include + +#include "client/Command/ListSnapshots/SnappersData/Table.h" +#include "client/Command/ListSnapshots/Options.h" +#include "client/utils/TableFormatter.h" +#include "client/utils/text.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + Command::ListSnapshots::SnappersData::Table::Table( + const Command::ListSnapshots& command, TableLineStyle style) : + SnappersData(command), _style(style) + {} + + + string Command::ListSnapshots::SnappersData::Table::output() const + { + string output; + + vector snappers = this->snappers(); + + for (ProxySnapper* snapper : snappers) + { + if (snappers.size() > 1) + { + if (snapper != *snappers.begin()) + output += "\n"; + + output += snapper_description(snapper) + "\n"; + } + + output += snapper_table(snapper); + } + + return output; + } + + + string Command::ListSnapshots::SnappersData::Table::label_for(const string& column) const + { + Options::ListMode list_mode = command().options().list_mode(); + + if (column == Options::Columns::CONFIG) + return _("Config"); + + if (column == Options::Columns::SUBVOLUME) + return _("Subvolume"); + + if (column == Options::Columns::NUMBER) + return list_mode == Options::LM_PRE_POST ? _("Pre #") : _("#"); + + if (column == Options::Columns::DEFAULT) + return _("Default"); + + if (column == Options::Columns::ACTIVE) + return _("Active"); + + if (column == Options::Columns::TYPE) + return _("Type"); + + if (column == Options::Columns::DATE) + return list_mode == Options::LM_PRE_POST ? _("Pre Date") : _("Date"); + + if (column == Options::Columns::USER) + return _("User"); + + if (column == Options::Columns::USED_SPACE) + return _("Used Space"); + + if (column == Options::Columns::CLEANUP) + return _("Cleanup"); + + if (column == Options::Columns::DESCRIPTION) + return _("Description"); + + if (column == Options::Columns::USERDATA) + return _("Userdata"); + + if (column == Options::Columns::PRE_NUMBER) + return _("Pre #"); + + if (column == Options::Columns::POST_NUMBER) + return _("Post #"); + + if (column == Options::Columns::POST_DATE) + return _("Post Date"); + + return ""; + } + + + string Command::ListSnapshots::SnappersData::Table::number_value(const ProxySnapshot& snapshot, + const ProxySnapshots& snapshots) const + { + bool is_default = is_default_snapshot(snapshot, snapshots); + bool is_active = is_active_snapshot(snapshot, snapshots); + + static const char sign[2][2] = { { ' ', '-' }, { '+', '*' } }; + + return Command::ListSnapshots::SnappersData::number_value(snapshot, snapshots) + + sign[is_default][is_active]; + } + + + string Command::ListSnapshots::SnappersData::Table::boolean_text(bool value) const + { + return value ? _("yes") : _("no"); + } + + + string Command::ListSnapshots::SnappersData::Table::snapper_description( + const ProxySnapper* snapper) const + { + return sformat(_("Config: %s, subvolume: %s"), config_value(snapper).c_str(), + subvolume_value(snapper).c_str()); + } + + + string Command::ListSnapshots::SnappersData::Table::snapper_table( + const ProxySnapper* snapper) const + { + const ProxySnapshots& snapshots = this->snapshots(snapper); + + vector> columns; + + vector> rows; + + for (const string& column : selected_columns()) + { + if (skip_column(column, snapper)) + continue; + + columns.emplace_back(label_for(column), column_alignment(column)); + } + + for (const ProxySnapshot* snapshot : selected_snapshots(snapper)) + { + vector row; + + for (const string& column : selected_columns()) + { + if (skip_column(column, snapper)) + continue; + + row.push_back(value_for(column, *snapshot, snapshots, snapper)); + } + + rows.push_back(row); + } + + TableFormatter formatter(columns, rows, _style); + + return formatter.output(); + } + + + bool Command::ListSnapshots::SnappersData::Table::skip_column(const string& column, + const ProxySnapper* snapper) const + { + return column == Options::Columns::USED_SPACE && !calculated_used_space(snapper); + } + + + TableAlign + Command::ListSnapshots::SnappersData::Table::column_alignment(const string& column) const + { + if (column == Options::Columns::NUMBER || + column == Options::Columns::PRE_NUMBER || + column == Options::Columns::POST_NUMBER || + column == Options::Columns::USED_SPACE) + return TableAlign::RIGHT; + else + return TableAlign::LEFT; + } + + } +} diff --git a/client/Command/ListSnapshots/SnappersData/Table.h b/client/Command/ListSnapshots/SnappersData/Table.h new file mode 100644 index 00000000..b32d3ce7 --- /dev/null +++ b/client/Command/ListSnapshots/SnappersData/Table.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) [2019] 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. + */ + +#ifndef SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_TABLE_H +#define SNAPPER_CLI_COMMAND_LIST_SNAPSHOTS_SNAPPERS_DATA_TABLE_H + +#include + +#include "client/Command/ListSnapshots/SnappersData.h" +#include "client/utils/Table.h" + +namespace snapper +{ + namespace cli + { + + class Command::ListSnapshots::SnappersData::Table : public Command::ListSnapshots::SnappersData + { + + public: + + Table(const ListSnapshots& command, TableLineStyle style); + + virtual std::string output() const override; + + private: + + std::string label_for(const string& column) const; + + virtual std::string + number_value(const ProxySnapshot& snapshot, const ProxySnapshots& snapshots) const override; + + virtual string boolean_text(bool value) const override; + + std::string snapper_description(const ProxySnapper* snapper) const; + + std::string snapper_table(const ProxySnapper* snapper) const; + + bool skip_column(const string& column, const ProxySnapper* snapper) const; + + TableAlign column_alignment(const string& column) const; + + TableLineStyle _style; + + }; + + } +} + +#endif