Author: jsrain
Date: Tue Aug 26 12:09:13 2008
New Revision: 50364
URL: http://svn.opensuse.org/viewcvs/yast?rev=50364&view=rev
Log:
working version to be tested
Modified:
branches/tmp/jsrain/config-history/src/ConfigHistory.ycp
Modified: branches/tmp/jsrain/config-history/src/ConfigHistory.ycp
URL: http://svn.opensuse.org/viewcvs/yast/branches/tmp/jsrain/config-history/src/ConfigHistory.ycp?rev=50364&r1=50363&r2=50364&view=diff
==============================================================================
--- branches/tmp/jsrain/config-history/src/ConfigHistory.ycp (original)
+++ branches/tmp/jsrain/config-history/src/ConfigHistory.ycp Tue Aug 26 12:09:13 2008
@@ -20,7 +20,7 @@
/**
* File: modules/ConfigHistory.ycp
- * Package: Configuration of config-history
+ * Package: Maintain history of configuration files
* Summary: ConfigHistory settings, input and output functions
* Authors: Jiri Srain
*
@@ -35,289 +35,378 @@
module "ConfigHistory";
textdomain "config-history";
-import "Progress";
-import "Report";
-import "Summary";
-import "Message";
-
/**
- * Prototypes
+ * Location of SVN repo
*/
-global boolean Modified();
+string history_location = "/var/lib/YaST2/config-history";
/**
- * Data was modified?
+ * Location of timestamp for detecting changed files out of version control
*/
-boolean modified = false;
+string changes_timestamp = "/var/lib/YaST2/config-history-timestamp";
/**
+ * Directories to put under version control
*/
-boolean proposal_valid = false;
+list<string> log_directories = [ "/etc" ];
/**
- * Write only, used during autoinstallation.
- * Don't run services and SuSEconfig, it's all done at one place.
+ * Is the SVN history active?
*/
-boolean write_only = false;
+boolean use_svn = nil;
/**
- * Abort function
- * return boolean return true if abort
+ * Always have whole subtree in SVN, not only files changed by YaST
*/
-boolean() AbortFunction = Modified;
+boolean store_whole_subtree = false;
/**
- * Abort function
- * @return boolean return true if abort
+ * Count of nested transactions (module calling another module)
*/
-global define boolean Abort() ``{
- if(AbortFunction != nil)
- {
- return AbortFunction () == true;
- }
- return false;
-}
+integer nested_transactions = 0;
/**
- * Data was modified?
- * @return true if modified
+ * If true, force commit at the end of initialization/finalization
*/
-global boolean Modified() {
- y2debug("modified=%1",modified);
- return modified;
-}
+boolean commit_needed = false;
/**
- * Mark as modified, for Autoyast.
+ * Is the SVN history in use?
+ * @return boolean true to log to SVN
*/
-global void SetModified(boolean value) {
- modified = true;
- }
-
-global boolean ProposalValid() {
- return proposal_valid;
-}
-
-global void SetProposalValid(boolean value) {
- proposal_valid = value;
+boolean UseSvn() {
+ if (use_svn == nil) {
+ // FIXME read from sysconfig
+ use_svn = true;
+ y2milestone ("Using SVN for configuration files: %1", use_svn);
+ }
+ return use_svn;
}
/**
- * @return true if module is marked as "write only" (don't start services etc...)
+ * Initialize a SVN repository for config files in /var/lib/YaST2
+ * @return boolean true on success, false otherwise
*/
-global boolean WriteOnly() {
- return write_only;
+boolean InitSvnRepository() {
+ y2milestone ("Initializing repo at %1", history_location);
+ map out = (map)SCR::Execute (.target.bash_output,
+ sformat ("svnadmin create %1", history_location));
+ if (out["exit"]:-1 != 0) {
+ y2error ("Failed to initialize SVN repository: %1", out["stderr"]:"");
+ return false;
+ }
+ y2milestone ("Repo initialized");
+ return true;
}
/**
- * Set write_only flag (for autoinstalation).
+ * Check the presence of SVN repo for storing changes
+ * @return boolean true if repo exists
*/
-global void SetWriteOnly(boolean value) {
- write_only = value;
-}
-
-
-global void SetAbortFunction(boolean() function) {
- AbortFunction = function;
+boolean CheckSvnRepository() {
+ y2milestone ("Checking repo presence");
+ map out = (map)SCR::Execute (.target.bash_output,
+ sformat ("test -d %1", history_location));
+ boolean ret = (out["exit"]:-1 == 0);
+ y2milestone ("Repo found: %1", ret);
+ return ret;
}
-
-// Settings: Define all variables needed for configuration of config-history
-// TODO FIXME: Define all the variables necessary to hold
-// TODO FIXME: the configuration here (with the appropriate
-// TODO FIXME: description)
-// TODO FIXME: For example:
-// /**
-// * List of the configured cards.
-// */
-// list cards = [];
-//
-// /**
-// * Some additional parameter needed for the configuration.
-// */
-// boolean additional_parameter = true;
-
/**
- * Read all config-history settings
- * @return true on success
- */
-global boolean Read() {
-
- /* ConfigHistory read dialog caption */
- string caption = _("Initializing config-history Configuration");
-
- // TODO FIXME Set the right number of stages
- integer steps = 4;
-
- integer sl = 500;
- sleep(sl);
-
- // TODO FIXME Names of real stages
- // We do not set help text here, because it was set outside
- Progress::New( caption, " ", steps, [
- /* Progress stage 1/3 */
- _("Read the database"),
- /* Progress stage 2/3 */
- _("Read the previous settings"),
- /* Progress stage 3/3 */
- _("Detect the devices")
- ], [
- /* Progress step 1/3 */
- _("Reading the database..."),
- /* Progress step 2/3 */
- _("Reading the previous settings..."),
- /* Progress step 3/3 */
- _("Detecting the devices..."),
- /* Progress finished */
- _("Finished")
- ],
- ""
- );
-
- // read database
- if(Abort()) return false;
- Progress::NextStage();
- /* Error message */
- if(false) Report::Error(_("Cannot read database1."));
- sleep(sl);
-
- // read another database
- if(Abort()) return false;
- Progress::NextStep();
- /* Error message */
- if(false) Report::Error(_("Cannot read database2."));
- sleep(sl);
-
- // read current settings
- if(Abort()) return false;
- Progress::NextStage();
- /* Error message */
- if(false) Report::Error(Message::CannotReadCurrentSettings());
- sleep(sl);
-
- // detect devices
- if(Abort()) return false;
- Progress::NextStage();
- /* Error message */
- if(false) Report::Warning(_("Cannot detect devices."));
- sleep(sl);
-
- if(Abort()) return false;
- /* Progress finished */
- Progress::NextStage();
- sleep(sl);
-
- if(Abort()) return false;
- modified = false;
- return true;
+ * Check whether repo has been deployed to the filesystem
+ * @return boolean true if yes (/.svn exists), false otherwise
+ */
+boolean CheckRepoLinked() {
+ y2milestone ("Checking whether repo is linked to root directory");
+ map out = (map)SCR::Execute (.target.bash_output,
+ sformat ("test -d %1", "/.svn"));
+ boolean ret = (out["exit"]:-1 == 0);
+ y2milestone ("Repo linked: %1", ret);
+ return ret;
}
/**
- * Write all config-history settings
- * @return true on success
- */
-global boolean Write() {
-
- /* ConfigHistory read dialog caption */
- string caption = _("Saving config-history Configuration");
-
- // TODO FIXME And set the right number of stages
- integer steps = 2;
-
- integer sl = 500;
- sleep(sl);
-
- // TODO FIXME Names of real stages
- // We do not set help text here, because it was set outside
- Progress::New(caption, " ", steps, [
- /* Progress stage 1/2 */
- _("Write the settings"),
- /* Progress stage 2/2 */
- _("Run SuSEconfig")
- ], [
- /* Progress step 1/2 */
- _("Writing the settings..."),
- /* Progress step 2/2 */
- _("Running SuSEconfig..."),
- /* Progress finished */
- _("Finished")
- ],
- ""
- );
-
- // write settings
- if(Abort()) return false;
- Progress::NextStage();
- /* Error message */
- if(false) Report::Error (_("Cannot write settings."));
- sleep(sl);
-
- // run SuSEconfig
- if(Abort()) return false;
- Progress::NextStage ();
- /* Error message */
- if(false) Report::Error (Message::SuSEConfigFailed());
- sleep(sl);
-
- if(Abort()) return false;
- /* Progress finished */
- Progress::NextStage();
- sleep(sl);
-
- if(Abort()) return false;
+ * Initialize predefined directories for SVN
+ * @param recursive boolean true to add whole directories incl. subtree,
+ * false to add directory itself only
+ * @return boolean true on success, false on failure
+ */
+boolean InitDirectories(boolean recursive) {
+ y2milestone ("Linking system with the repository; recursive: %1", recursive);
+ map out = (map)SCR::Execute (.target.bash_output,
+ sformat ("svn co file://%1 /", history_location));
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("svn check out to root failed: %1", out["stderr"]:"");
+ return false;
+ }
+ boolean success = true;
+ foreach (string dir, log_directories, {
+ y2milestone ("Initializing directory %1", dir);
+ string params = recursive ? "" : "-N";
+ out = (map)SCR::Execute (.target.bash_output,
+ sformat ("cd / ; svn add %2 %1", dir, params));
+ if (out["exit"]:-1 != 0)
+ {
+ success = false;
+ y2error ("Failed to add directory %1: %2", dir, out["stderr"]:"");
+ }
+ });
+ if (! success)
+ return false;
+ out = (map)SCR::Execute (.target.bash_output,
+ "cd / ; svn ci -m 'Initial check-in'");
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("Initial check-in to repo failed: %1", out["stderr"]:"");
+ return false;
+ }
+ y2milestone ("Initial check-in succeeded");
return true;
}
/**
- * Get all config-history settings from the first parameter
- * (For use by autoinstallation.)
- * @param settings The YCP structure to be imported.
- * @return boolean True on success
+ * Check for files in version control which had been changed but not committed
+ * @return boolean true on success
*/
-global boolean Import (map settings) {
- // TODO FIXME: your code here (fill the above mentioned variables)...
+boolean CheckUncommitedChanges() {
+ boolean success = true;
+ foreach (string dir, log_directories, {
+ y2milestone ("Checking for uncommitted changes in %1", dir);
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn st |grep '^M'", dir));
+ if (out["exit"]:-1 == 1 && ! commit_needed)
+ {
+ y2milestone ("No uncommitted change detected");
+ }
+ else
+ {
+ out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn ci -m 'Commit remaining changes before running YaST'",
+ dir));
+ if (out["exit"]:-1 != 0)
+ {
+ success = false;
+ y2error ("Failed to commit changes in %1: %2", dir, out["stderr"]:"");
+ }
+ }
+ });
+ y2milestone ("Commit successful: %1", success);
+ return success;
+}
+
+/**
+ * Create a timestamp to find changed files which are not under version control
+ * @return boolean true on success
+ */
+boolean CreateTimeStamp() {
+ y2milestone ("Creating timestamp to detect changes");
+ map out = (map)SCR::Execute (.target.bash_output, sformat ("touch %1", changes_timestamp));
+ boolean ret = out["exit"]:-1 == 0;
+ y2milestone ("Success: %1", ret);
+ return ret;
+}
+
+/**
+ * Check for changed files which are not under verison control (e.g. new created files)
+ * Schedule them for next commit
+ * @return boolean true on success, false on failure
+ */
+boolean CheckChangedFilesOutOfVersionControl() {
+ boolean success = true;
+ foreach (string dir, log_directories, {
+ y2milestone ("Checking for new files in %1", dir);
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "find %1 -newer %2 -type f |grep -v '/\\.'", dir, changes_timestamp));
+ if (out["exit"]:-1 == 1)
+ {
+ y2milestone ("No changes found");
+ return;
+ }
+ string param = out["stdout"]:"";
+ list<string> files = splitstring (param, "\n");
+ files = filter (string f, files, { return f != ""; });
+ files = filter (string f, files, {
+ return 0 == (integer)SCR::Execute (.target.bash, sformat (
+ "svn st %1 | grep '^?'", f));
+ });
+ commit_needed = commit_needed || (size (files) > 0);
+ if (size (files) > 0) {
+ param = mergestring (files, " ");
+ out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn add --parents %2", dir, param));
+ if (out["exit"]:-1 != 0)
+ {
+ success = false;
+ y2error ("Failed to add changes: %1", out["stderr"]:"");
+ }
+ }
+ });
+ SCR::Execute (.target.bash_output, sformat ("rm %1", changes_timestamp));
+ return success;
+}
+
+/**
+ * Find all files which are not under version control
+ * Schedule such files for next commit
+ * @return boolean true on success, false otherwise
+ */
+boolean CheckAllFilesOutOfVersionControl() {
+ boolean success = true;
+ y2milestone ("Adding all files out of version control");
+ foreach (string dir, log_directories, {
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn add `svn st |grep '^?'|cut -d ' ' -f 7`", dir));
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("Failed to add files in %1: %2", dir, out["stderr"]:"");
+ success = false;
+ }
+ });
+ commit_needed = true; // TODO check if really necessary
+ y2milestone ("Finished successfuly: %1", success);
+ return success;
+}
+
+/**
+ * Check for files which had been deleted, but are still in SVN
+ * Schedule such files for deletion with next commit
+ * @return boolean true on success, false otherwise
+ */
+boolean RemoveDeletedFiles() {
+ boolean success = true;
+ y2milestone ("Checking for removed files");
+ foreach (string dir, log_directories, {
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn st |grep '^!'|cut -d ' ' -f 7", dir));
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("Failed to check for deleted files in %1: %2", dir, out["stderr"]:"");
+ success = false;
+ return;
+ }
+ string filelist = out["stdout"]:"";
+ list<string> files = splitstring (filelist, " ");
+ files = filter (string f, files, { return f != ""; });
+ if (size (files) == 0)
+ return;
+ filelist = mergestring (files, " ");
+ out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn rm %2", dir, filelist));
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("Failed to remove files in %1: %2", dir, out["stderr"]:"");
+ success = false;
+ }
+ });
+ commit_needed = true; // TODO check if really necessary
+ y2milestone ("Finished successfuly: %1", success);
+ return success;
+}
+
+/**
+ * Do commit to subversion
+ * @return boolean tru eon success
+ */
+boolean DoCommit(string mod) {
+ y2milestone ("Committing changes");
+ string arg = mergestring (log_directories, " ");
+ y2debug ("Directories to commit: %1", arg);
+ string log = sformat ("Changes by YaST module %1", mod);
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd / ; svn ci -m '%1' %2", log, arg));
+ boolean ret = out["exit"]:-1 == 0;
+ y2milestone ("Success: %1", ret);
+ return ret;
+}
+
+/**
+ * Update check-out from SVN to avoid commit conflicts
+ * @return boolean true on success
+ */
+boolean UpdateCheckout() {
+ boolean success = true;
+ foreach (string dir, log_directories, {
+ y2milestone ("Updating configuration files in %1", dir);
+ map out = (map)SCR::Execute (.target.bash_output, sformat (
+ "cd %1; svn up", dir));
+ if (out["exit"]:-1 != 0)
+ {
+ y2error ("Failed to update %1 from SVN: %2", dir, out["stderr"]:"");
+ success = false;
+ }
+ });
+ return success;
+}
+
+/**
+ * Initialize before module is started
+ * Do not call CommitChanges unless Init returns true!
+ * @return boolean true on success, false on failure
+ */
+global boolean Init() {
+ if (! UseSvn())
+ return true;
+ if (nested_transactions > 0)
+ {
+ nested_transactions = nested_transactions + 1;
+ y2milestone ("Skiping SVN initialization, translaction already in progress");
+ return true;
+ }
+ //ensure the repo exists
+ if (! CheckSvnRepository ()) {
+ if (! InitSvnRepository())
+ return false;
+ }
+ if (! CheckRepoLinked()) {
+ if (! InitDirectories(store_whole_subtree))
+ return false;
+ }
+ if (! UpdateCheckout()) {
+ return false;
+ }
+ if (store_whole_subtree) {
+ CheckAllFilesOutOfVersionControl();
+ }
+ RemoveDeletedFiles();
+ if (! CheckUncommitedChanges())
+ return false;
+ if (! CreateTimeStamp())
+ return false;
+ nested_transactions = nested_transactions + 1;
return true;
}
/**
- * Dump the config-history settings to a single map
- * (For use by autoinstallation.)
- * @return map Dumped settings (later acceptable by Import ())
- */
-global map Export () {
- // TODO FIXME: your code here (return the above mentioned variables)...
- return $[];
+ * Commit changes done by YaST into the SVN repo
+ * @param module_name string name of YaST module which does commit
+ * used only in the commit log
+ * @return boolean true on success, false on failure
+ */
+global boolean CommitChanges(string module_name) {
+ if (! UseSvn())
+ return true;
+ nested_transactions = nested_transactions - 1;
+ if (nested_transactions > 0)
+ {
+ y2milestone ("Skipping commit, all nested transaction not yet finished");
+ return true;
+ }
+ boolean success = true;
+ if (! UpdateCheckout()) {
+ success = false;
+ }
+ if (store_whole_subtree)
+ success = CheckAllFilesOutOfVersionControl();
+ else
+ success = CheckChangedFilesOutOfVersionControl();
+ success = RemoveDeletedFiles() && success;
+ success = DoCommit(module_name) && success;
+ return true;
}
-/**
- * Create a textual summary and a list of unconfigured cards
- * @return summary of the current configuration
- */
-global list Summary() {
- // TODO FIXME: your code here...
- /* Configuration summary text for autoyast */
- return [ _("Configuration summary..."), [] ];
-}
-/**
- * Create an overview table with all configured cards
- * @return table items
- */
-global list Overview() {
- // TODO FIXME: your code here...
- return [];
-}
-/**
- * Return packages needed to be installed and removed during
- * Autoinstallation to insure module has all needed software
- * installed.
- * @return map with 2 lists.
- */
-global map AutoPackages() {
- // TODO FIXME: your code here...
- return $[ "install":[], "remove":[] ];
-}
/* EOF */
}
--
To unsubscribe, e-mail: yast-commit+unsubscribe@opensuse.org
For additional commands, e-mail: yast-commit+help@opensuse.org