Hello community, here is the log from the commit of package yast2-snapper for openSUSE:Factory checked in at Tue Apr 19 12:59:20 CEST 2011.
-------- --- yast2-snapper/yast2-snapper.changes 2011-03-24 13:25:29.000000000 +0100 +++ /mounts/work_src_done/STABLE/yast2-snapper/yast2-snapper.changes 2011-04-18 09:20:39.000000000 +0200 @@ -1,0 +2,15 @@ +Mon Apr 18 09:16:36 CEST 2011 - [email protected] + +- removed YCP code for Tree recursive selection, widget can do it + itself now (bnc#686639) +- 2.21.4 + +------------------------------------------------------------------- +Fri Apr 15 11:35:28 CEST 2011 - [email protected] + +- return map structures from agent, to speed up building tree + content (bnc#686639) +- solve restoring directories (bnc#686619) +- 2.21.3 + +------------------------------------------------------------------- calling whatdependson for head-i586 Old: ---- yast2-snapper-2.21.2.tar.bz2 New: ---- yast2-snapper-2.21.4.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-snapper.spec ++++++ --- /var/tmp/diff_new_pack.6VEbIc/_old 2011-04-19 12:57:42.000000000 +0200 +++ /var/tmp/diff_new_pack.6VEbIc/_new 2011-04-19 12:57:42.000000000 +0200 @@ -19,16 +19,16 @@ Name: yast2-snapper -Version: 2.21.2 +Version: 2.21.4 Release: 1 Group: System/YaST BuildRoot: %{_tmppath}/%{name}-%{version}-build -Source0: yast2-snapper-2.21.2.tar.bz2 +Source0: yast2-snapper-2.21.4.tar.bz2 Prefix: /usr -Requires: yast2 +Requires: yast2 libsnapper License: GPLv2+ BuildRequires: doxygen gcc-c++ libsnapper libsnapper-devel perl-XML-Writer update-desktop-files yast2 yast2-core-devel yast2-devtools yast2-testsuite @@ -38,7 +38,7 @@ YaST module for accessing and managing btrfs system snapshots %prep -%setup -n yast2-snapper-2.21.2 +%setup -n yast2-snapper-2.21.4 %build %{prefix}/bin/y2tool y2autoconf ++++++ yast2-snapper-2.21.2.tar.bz2 -> yast2-snapper-2.21.4.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-snapper-2.21.2/VERSION new/yast2-snapper-2.21.4/VERSION --- old/yast2-snapper-2.21.2/VERSION 2011-03-24 13:22:02.000000000 +0100 +++ new/yast2-snapper-2.21.4/VERSION 2011-04-18 09:18:55.000000000 +0200 @@ -1 +1 @@ -2.21.2 +2.21.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-snapper-2.21.2/agent-snapper/src/SnapperAgent.cc new/yast2-snapper-2.21.4/agent-snapper/src/SnapperAgent.cc --- old/yast2-snapper-2.21.2/agent-snapper/src/SnapperAgent.cc 2011-03-07 08:25:45.000000000 +0100 +++ new/yast2-snapper-2.21.4/agent-snapper/src/SnapperAgent.cc 2011-04-15 11:37:11.000000000 +0200 @@ -9,6 +9,7 @@ #include "SnapperAgent.h" #include <ctype.h> +#include <boost/algorithm/string.hpp> #define PC(n) (path->component_str(n)) @@ -45,6 +46,19 @@ return deflt; } +/** + * Search the map for value of given key; + * key is string and value is YCPList + */ +YCPList SnapperAgent::getListValue (const YCPMap map, const string key) +{ + if (!map->value(YCPString(key)).isNull() && map->value(YCPString(key))->isList()) + return map->value(YCPString(key))->asList(); + else + return YCPList(); +} + + string statusToString(unsigned int status) { string ret; @@ -93,12 +107,27 @@ return YCPNull(); } + +struct Tree { map<string, Tree> trees; }; + +static YCPMap +make_ycpmap(const Tree& tree) +{ + YCPMap ret; + + for (map<string, Tree>::const_iterator it = tree.trees.begin(); it != tree.trees.end(); ++it) + ret->add(YCPString(it->first), make_ycpmap(it->second)); + + return ret; +} + + /** * Read */ YCPValue SnapperAgent::Read(const YCPPath &path, const YCPValue& arg, const YCPValue& opt) { - y2internal ("path in Read: '%s'.", path->toString().c_str()); + y2debug ("path in Read: '%s'.", path->toString().c_str()); YCPValue ret = YCPVoid(); YCPMap argmap; @@ -145,35 +174,79 @@ s->add (YCPString ("pre_num"), YCPInteger (it->getPreNum())); } - y2internal ("snapshot %s", s.toString().c_str()); + y2debug ("snapshot %s", s.toString().c_str()); retlist->add (s); } return retlist; } + unsigned int num1 = getIntValue (argmap, "from", 0); + unsigned int num2 = getIntValue (argmap, "to", 0); + /** - * Read(.snapper.diff) -> show difference between snapnots num1 and num2. + * Read(.snapper.diff_list) -> show difference between snapnots num1 and num2 as list. */ - if (PC(0) == "diff") { + if (PC(0) == "diff_list") { YCPList retlist; - unsigned int num1 = getIntValue (argmap, "from", 0); - unsigned int num2 = getIntValue (argmap, "to", 0); const Snapshots& snapshots = sh->getSnapshots(); - const Comparison comparison(sh, snapshots.find(num1), snapshots.find(num2)); - const Files& files = comparison.getFiles(); + for (Files::const_iterator it = files.begin(); it != files.end(); ++it) { YCPMap filemap; filemap->add (YCPString ("name"), YCPString (it->getName())); - // FIXME it's PreToPostStatus! filemap->add (YCPString ("changes"), YCPString (statusToString (it->getPreToPostStatus()))); retlist->add (filemap); } return retlist; } + /** + * Read(.snapper.diff_index) -> show difference between snapnots num1 and num2 as one-level map: + * (mapping each file to its changes) + */ + if (PC(0) == "diff_index") { + YCPMap retmap; + + const Snapshots& snapshots = sh->getSnapshots(); + const Comparison comparison(sh, snapshots.find(num1), snapshots.find(num2)); + const Files& files = comparison.getFiles(); + + for (Files::const_iterator it = files.begin(); it != files.end(); ++it) + { + retmap->add (YCPString (it->getName()), YCPString (statusToString (it->getPreToPostStatus()))); + } + return retmap; + } + /** + * Read(.snapper.diff_tree) -> show difference between snapnots num1 and num2 as tree. + */ + else if (PC(0) == "diff_tree") + { + Tree ret1; + + const Snapshots& snapshots = sh->getSnapshots(); + const Comparison comparison(sh, snapshots.find(num1), snapshots.find(num2)); + const Files& files = comparison.getFiles(); + for (Files::const_iterator it = files.begin(); it != files.end(); ++it) + { + deque<string> parts; + boost::split(parts, it->getName(), boost::is_any_of("/")); + parts.pop_front(); + + Tree* tmp = &ret1; + for (deque<string>::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + map<string, Tree>::iterator pos = tmp->trees.find(*it); + if (pos == tmp->trees.end()) + pos = tmp->trees.insert(tmp->trees.begin(), make_pair(*it, Tree())); + tmp = &pos->second; + } + } + + return make_ycpmap(ret1); + } else { y2error("Wrong path '%s' in Read().", path->toString().c_str()); } @@ -194,7 +267,7 @@ YCPBoolean SnapperAgent::Write(const YCPPath &path, const YCPValue& arg, const YCPValue& arg2) { - y2internal ("path in Write: '%s'.", path->toString().c_str()); + y2debug ("path in Write: '%s'.", path->toString().c_str()); YCPBoolean ret = YCPBoolean(true); return ret; @@ -206,9 +279,53 @@ YCPValue SnapperAgent::Execute(const YCPPath &path, const YCPValue& arg, const YCPValue& arg2) { - y2internal ("path in Execute: '%s'.", path->toString().c_str()); + y2debug ("path in Execute: '%s'.", path->toString().c_str()); YCPValue ret = YCPBoolean (true); - return ret; + + YCPMap argmap; + if (!arg.isNull() && arg->isMap()) + argmap = arg->asMap(); + + if (path->length() == 1) { + + /** + * Rollback the list of given files from snapshot num1 to num2 (system by default) + */ + if (PC(0) == "rollback") { + + unsigned int num1 = getIntValue (argmap, "from", 0); + unsigned int num2 = getIntValue (argmap, "to", 0); + const Snapshots& snapshots = sh->getSnapshots(); + Comparison comparison(sh, snapshots.find(num1), snapshots.find(num2)); + Files& files = comparison.getFiles(); + + YCPList selected = getListValue (argmap, "files"); + for (int i=0; i < selected->size(); i++) { + if (selected.value(i)->isString()) + { + string name = selected->value(i)->asString()->value(); + y2debug ("file to rollback: %s", name.c_str()); + Files::iterator it = files.find(name); + if (it == files.end()) + { + y2error ("file %s not found in diff", name.c_str()); + } + else + { + it->setRollback(true); + it->doRollback (); + } + } + } + return ret; + } + + } + else { + y2error("Wrong path '%s' in Execute().", path->toString().c_str()); + } + + return YCPVoid (); } /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-snapper-2.21.2/agent-snapper/src/SnapperAgent.h new/yast2-snapper-2.21.4/agent-snapper/src/SnapperAgent.h --- old/yast2-snapper-2.21.2/agent-snapper/src/SnapperAgent.h 2011-03-07 08:25:45.000000000 +0100 +++ new/yast2-snapper-2.21.4/agent-snapper/src/SnapperAgent.h 2011-04-14 10:50:11.000000000 +0200 @@ -43,6 +43,12 @@ */ int getIntValue ( const YCPMap map, const string key, int deflt); + /** + * Search the map for value of given key; + * key is string and value is YCPList + */ + YCPList getListValue (const YCPMap map, const string key); + public: /** * Default constructor. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-snapper-2.21.2/src/Snapper.ycp new/yast2-snapper-2.21.4/src/Snapper.ycp --- old/yast2-snapper-2.21.2/src/Snapper.ycp 2011-03-07 10:21:12.000000000 +0100 +++ new/yast2-snapper-2.21.4/src/Snapper.ycp 2011-04-15 11:34:54.000000000 +0200 @@ -23,7 +23,7 @@ * Summary: Snapper settings, input and output functions * Authors: Jiri Suchomel <[email protected]> * - * $Id: Snapper.ycp 63520 2011-03-07 09:21:10Z jsuchome $ + * $Id: Snapper.ycp 63782 2011-04-15 09:33:15Z jsuchome $ * * Representation of the configuration of snapper. * Input and output routines. @@ -42,7 +42,7 @@ // global list of all snapshot global list<map> snapshots = []; -global string snapshots_path = "/snapshots"; // FIXME +global string snapshots_path = "/snapshots"; global map selected_snapshot = $[]; @@ -53,12 +53,19 @@ global integer selected_snapshot_index = 0; /** - * Return list of files modified between given snapshots - * File is a map with name (path) and some parameters + * Return map of files modified between given snapshots + * Return structure has just one level, and maps each modified file to it's modification map */ -global list<map> ReadModifiedFiles (integer from, integer to) { +global map<string,string> ReadModifiedFilesIndex (integer from, integer to) { + return (map<string,string>) SCR::Read (.snapper.diff_index, $[ "from" : from, "to" : to]); +} - return (list<map>) SCR::Read (.snapper.diff, $[ "from" : from, "to" : to]); +/** + * Return map of files modified between given snapshots + * Map is recursively describing the filesystem structure; helps to build Tree widget contents + */ +global map<string, map> ReadModifiedFilesMap (integer from, integer to) { + return (map<string, map>) SCR::Read (.snapper.diff_tree, $[ "from" : from, "to" : to]); } /** @@ -95,7 +102,7 @@ // check mode and ownerships out = (map) SCR::Execute (.target.bash_output, - sformat ("ls -l %1 %2 | cut -f 1,3,4 -d ' '", file1, file2)); + sformat ("ls -ld %1 %2 | cut -f 1,3,4 -d ' '", file1, file2)); list<string> parts = splitstring (out["stdout"]:""," \n"); if (parts[0]:"" != parts[3]:"") @@ -190,7 +197,19 @@ } /** + * Return the given file mode as octal number + */ +integer GetFileMode (string file) { + + map out = (map) SCR::Execute (.target.bash_output, "/bin/stat --printf=%a " + file); + return tointeger (out["stdout"]:"755"); +} + +/** * Copy given files from selected snapshot to current filesystem + * @param snapshot_num snapshot identifier + * @param files list of full paths to files + * @return success */ global boolean RestoreFiles (integer snapshot_num, list<string> files) { @@ -211,17 +230,36 @@ UI::ChangeWidget (`id (`progress ), `Value, progress); - string orig = sformat ("%1/%2/snapshot%3", snapshots_path, snapshot_num, file); + string orig = sformat ("%1/%2/snapshot", snapshots_path, snapshot_num) + file; string dir = substring (file, 0, findlastof (file, "/")); - y2milestone ("copying file %1 to %2 (dir: %3)", orig, file, dir); - if (FileUtils::CheckAndCreatePath (dir)) + if (!FileUtils::Exists (orig)) + { + // log entry (%1 is file name) + UI::ChangeWidget (`id (`log), `LastLine, sformat (_("%1 does not exist in snapshot %2\n"), file, snapshot_num)); + } + else if (FileUtils::CheckAndCreatePath (dir)) { - SCR::Execute (.target.bash, sformat ("/bin/cp -a %1 %2", orig, file)); + y2milestone ("copying '%1' to '%2' (dir: %3)", orig, file, dir); + if (FileUtils::IsDirectory (orig) == true) + { + map stat = (map) SCR::Read (.target.stat, orig); + if (!FileUtils::Exists (file)) + { + SCR::Execute (.target.mkdir, file); + } + SCR::Execute (.target.bash, sformat ("/bin/chown %1:%2 '%3'", stat["uid"]:0, stat["gid"]:0, file)); + SCR::Execute (.target.bash, sformat ("/bin/chmod %1 '%2'", GetFileMode (orig), file)); + } + else + { + SCR::Execute (.target.bash, sformat ("/bin/cp -a '%1' '%2'", orig, file)); + } UI::ChangeWidget (`id (`log), `LastLine, file + "\n"); } else { + y2milestone ("failed to copy file '%1' to '%2' (dir: %3)", orig, file, dir); // log entry (%1 is file name) UI::ChangeWidget (`id (`log), `LastLine, sformat (_("%1 skipped\n"), file)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-snapper-2.21.2/src/dialogs.ycp new/yast2-snapper-2.21.4/src/dialogs.ycp --- old/yast2-snapper-2.21.2/src/dialogs.ycp 2011-03-07 10:21:12.000000000 +0100 +++ new/yast2-snapper-2.21.4/src/dialogs.ycp 2011-04-18 09:18:55.000000000 +0200 @@ -24,7 +24,7 @@ * Summary: Dialogs definitions * Authors: Jiri Suchomel <[email protected]> * - * $Id: dialogs.ycp 63520 2011-03-07 09:21:10Z jsuchome $ + * $Id: dialogs.ycp 63806 2011-04-18 07:18:26Z jsuchome $ */ { @@ -32,6 +32,7 @@ textdomain "snapper"; import "Confirm"; +import "FileUtils"; import "Label"; import "Popup"; import "Wizard"; @@ -122,6 +123,10 @@ Wizard::HideAbortButton (); UI::SetFocus (`id (`snapshots_table)); + if (snapshot_items == []) + { + UI::ChangeWidget (`id (`show_c), `Enabled, false); + } any ret = nil; while(true) { @@ -180,9 +185,9 @@ map snapshot = Snapper::selected_snapshot; integer snapshot_num = snapshot["num"]:0; - list<map> file_list = snapshot["files"]:[]; + // map of whole tree (recursive) + map<string, map> tree_map = snapshot["tree_map"]:$[]; integer previous_num = snapshot["pre_num"]:snapshot_num; - integer pre_index = Snapper::id2index[previous_num]:0; string description = Snapper::snapshots[pre_index,"description"]:""; string pre_date = timestring ("%c", Snapper::snapshots[pre_index,"date"]:0, false); @@ -198,57 +203,50 @@ } }); - if (!haskey (snapshot, "files")) + integer from = snapshot_num; + integer to = 0; // current system + if (snapshot["type"]:`NONE == `POST) { - integer from = snapshot_num; - integer to = 0; // current system - if (snapshot["type"]:`NONE == `POST) - { - from = snapshot["pre_num"]:0; - to = snapshot_num; - } - else if (snapshot["type"]:`NONE == `PRE) - { - to = snapshot["post_num"]:0; - } - // busy popup message - Popup::ShowFeedback ("", _("Calculating changed files...")); - snapshot["files"] = Snapper::ReadModifiedFiles (from, to); + from = snapshot["pre_num"]:0; + to = snapshot_num; + } + else if (snapshot["type"]:`NONE == `PRE) + { + to = snapshot["post_num"]:0; + } + + // busy popup message + Popup::ShowFeedback ("", _("Calculating changed files...")); + + if (!haskey (snapshot, "tree_map")) + { + snapshot["tree_map"] = Snapper::ReadModifiedFilesMap (from, to); Popup::ClearFeedback (); - file_list = snapshot["files"]:[]; - // update the global snapshots list + tree_map = snapshot["tree_map"]:$[]; + } + // full paths of files marked as modified, mapping to changes string + map<string,string> files_index = $[]; + if (!haskey (snapshot, "files_index")) + { + snapshot["files_index"] = Snapper::ReadModifiedFilesIndex (from, to); Snapper::snapshots[Snapper::selected_snapshot_index] = snapshot; } + files_index = snapshot["files_index"]:$[]; - string snapshot_name = tostring (snapshot_num); + // update the global snapshots list + Snapper::snapshots[Snapper::selected_snapshot_index] = snapshot; - // map of tree (recursive) - map<string,map> tree_map = $[]; + string snapshot_name = tostring (snapshot_num); // map of all items in tree (just one level) map<string,boolean> selected_items = $[]; - // helper function: update the global map with directory tree structure - map<string,map> update_tree_map (map<string,map> current_map, list<string> path_list) - { - string first = path_list[0]:""; - - if (current_map[first]:nil == nil) - { - current_map[first] = $[]; - } - if (size (path_list) > 1) - { - current_map[first] = - update_tree_map ((map<string,map>)current_map[first]:$[], - (list<string>) remove (path_list, 0) - ); - } - return current_map; + boolean file_was_created (string file) { + return (substring (files_index[file]:"", 0, 1) == "+"); + } + boolean file_was_removed (string file) { + return (substring (files_index[file]:"", 0, 1) == "-"); } - - // save full paths of files marked as modified - map<string,map> files_index = $[]; // go through the map defining filesystem tree and create the widget items list<term> generate_tree_items (string current_path, map<string,map> current_branch) { @@ -256,15 +254,14 @@ list<term> ret = []; foreach (string node, map branch, current_branch, { string new_path = current_path + "/" + node; - if (files_index[new_path]:$[] != $[]) + if (haskey (files_index, new_path)) { - string changes = files_index[new_path,"changes"]:""; string icon_f = "16x16/apps/gdu-smart-unknown.png"; - if (substring (changes, 0, 1) == "+") + if (file_was_created (new_path)) { icon_f = "16x16/apps/gdu-smart-healthy.png"; } - else if (substring (changes, 0, 1) == "-") + else if (file_was_removed (new_path)) { icon_f = "16x16/apps/gdu-smart-failing.png"; } @@ -358,18 +355,36 @@ content, `VSquash (`HBox ( `HStretch (), - type == `SINGLE ? `Empty () : `PushButton (`id (`restore_pre), _("Restore From First")), // button label - `PushButton (`id (`restore), type == `SINGLE ? _("Restore") : _("Restore From Second")) + type == `SINGLE ? `Empty () : `PushButton (`id (`restore_pre), _("R&estore From First")), + // button label + `PushButton (`id (`restore), type == `SINGLE ? _("Restore") : _("Res&tore From Second")) )) ), `HSpacing (0.5)) ); + if (file_was_created (file)) + { + // file created after taking first snapshot cannot be restored from that snapshot + if (type == `SINGLE) + { + UI::ChangeWidget (`id (`restore), `Enabled, false); + } + else + { + UI::ChangeWidget (`id (`restore_pre), `Enabled, false); + } + } + else if (type != `SINGLE && file_was_removed (file)) + { + // file removed in 2nd snapshot cannot be restored from that snapshot + UI::ChangeWidget (`id (`restore), `Enabled, false); + } } // create the term for selected file void set_entry_term () { - if (current_file != "" && files_index[current_file]:$[] != $[]) + if (current_file != "" && haskey (files_index, current_file)) { if (type == `SINGLE) { @@ -486,24 +501,12 @@ // button label Label::CancelButton(), _("Restore Selected")); - // generate the map with directory tree structure - foreach (map file, file_list, { - string full_path = file["name"]:""; - list<string> path_l = splitstring (full_path, "/"); - if (path_l[0]:"" == "") - path_l = remove (path_l, 0); - tree_map = update_tree_map (tree_map, path_l); - files_index[full_path] = $[ - "changes" : file["changes"]:"" - ]; - }); - - tree_items = generate_tree_items ("", tree_map); + tree_items = generate_tree_items ("", tree_map); if (size (tree_items) > 0) { UI::ReplaceWidget (`id (`reptree), - `Tree (`id(`tree), `opt (`notify, `immediate, `multiSelection), tree_label, tree_items) + `Tree (`id(`tree), `opt (`notify, `immediate, `multiSelection, `recursiveSelection), tree_label, tree_items) ); // no item is selected UI::ChangeWidget (`tree, `CurrentItem, nil); @@ -526,57 +529,8 @@ current_file = (string) UI::QueryWidget (`id(`tree),`CurrentItem); if (current_file == nil) current_file = ""; - // tree checkbox clicked - if (ret == `tree && event["EventReason"]:"" == "ValueChanged") - { - // non-tracked file (so, it should be directory) - if (current_file != "" && files_index[current_file]:$[] == $[]) - { - void select_items (map<string,map> current_map, string prefix) { - selected_items[prefix] = ! selected_items[prefix]:false; - foreach (string key, map submap, current_map, { - string new_path = prefix + "/" + key; - if (submap != $[]) - { - select_items ((map<string,map>) submap, new_path); - } - else - { - selected_items[new_path] = ! selected_items[new_path]:false; - } - }); - } - void update_selection (map<string,map> current_map, list<string> path_list) - { - string first = path_list[0]:""; - if (size (path_list) == 0) - { - select_items (current_map, current_file); - } - else if (haskey (current_map, first)) - { - update_selection ((map<string,map>)current_map[first]:$[], - (list<string>) remove (path_list, 0) - ); - } - } - list<string> path_l = splitstring (current_file, "/"); - if (path_l[0]:"" == "") - path_l = remove (path_l, 0); - - update_selection (tree_map, path_l); - - list<string> selection = []; - foreach (string key, boolean val, selected_items, { - if (val) - selection = add (selection, key); - }); - UI::ChangeWidget (`tree, `SelectedItems, selection); - } - - } // other tree events - else if (ret == `tree) + if (ret == `tree) { // seems like tree widget emits 2 SelectionChanged events if (current_file != previous_file) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Remember to have fun... -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
