AutoBackupModule: Implement per-region settings in Regions.ini.

bulletsim
Sean McNamara 2011-05-02 04:32:31 -04:00
parent fffd42f5cb
commit 0995fedcac
1 changed files with 186 additions and 105 deletions

View File

@ -55,7 +55,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
///<summary> ///<summary>
/// AutoBackupModule: save OAR region backups to disk periodically /// AutoBackupModule: save OAR region backups to disk periodically
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Config Settings Documentation. /// Config Settings Documentation.
/// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options: /// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
@ -96,7 +96,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
/// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass. /// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
/// If the number of agents is greater than this value, don't take a backup right now /// If the number of agents is greater than this value, don't take a backup right now
/// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions. /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
/// Also helps if you don't want AutoBackup at all. /// Also helps if you don't want AutoBackup at all.
/// </remarks> /// </remarks>
public class AutoBackupModule : ISharedRegionModule public class AutoBackupModule : ISharedRegionModule
{ {
@ -110,17 +110,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
new Dictionary<Timer, List<IScene>>(1); new Dictionary<Timer, List<IScene>>(1);
private readonly Dictionary<double, Timer> m_timers = new Dictionary<double, Timer>(1); private readonly Dictionary<double, Timer> m_timers = new Dictionary<double, Timer>(1);
private delegate T DefaultGetter<T>(string settingName, T defaultValue);
private bool m_enabled; private bool m_enabled;
/// <summary> /// <summary>
/// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState! /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
/// </summary> /// </summary>
private bool m_closed; private bool m_closed;
private IConfigSource m_configSource; private IConfigSource m_configSource;
/// <summary> /// <summary>
/// Required by framework. /// Required by framework.
/// </summary> /// </summary>
public bool IsSharedModule public bool IsSharedModule
{ {
@ -129,25 +130,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#region ISharedRegionModule Members #region ISharedRegionModule Members
/// <summary> /// <summary>
/// Identifies the module to the system. /// Identifies the module to the system.
/// </summary> /// </summary>
string IRegionModuleBase.Name string IRegionModuleBase.Name
{ {
get { return "AutoBackupModule"; } get { return "AutoBackupModule"; }
} }
/// <summary> /// <summary>
/// We don't implement an interface, this is a single-use module. /// We don't implement an interface, this is a single-use module.
/// </summary> /// </summary>
Type IRegionModuleBase.ReplaceableInterface Type IRegionModuleBase.ReplaceableInterface
{ {
get { return null; } get { return null; }
} }
/// <summary> /// <summary>
/// Called once in the lifetime of the module at startup. /// Called once in the lifetime of the module at startup.
/// </summary> /// </summary>
/// <param name="source">The input config source for OpenSim.ini.</param> /// <param name="source">The input config source for OpenSim.ini.</param>
void IRegionModuleBase.Initialise(IConfigSource source) void IRegionModuleBase.Initialise(IConfigSource source)
{ {
@ -184,8 +185,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug(abms.ToString()); m_log.Debug(abms.ToString());
} }
/// <summary> /// <summary>
/// Called once at de-init (sim shutting down). /// Called once at de-init (sim shutting down).
/// </summary> /// </summary>
void IRegionModuleBase.Close() void IRegionModuleBase.Close()
{ {
@ -198,17 +199,17 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.StopAllTimers(); this.StopAllTimers();
} }
/// <summary> /// <summary>
/// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded. /// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
/// </summary> /// </summary>
/// <param name="scene"></param> /// <param name="scene"></param>
void IRegionModuleBase.AddRegion(Scene scene) void IRegionModuleBase.AddRegion(Scene scene)
{ {
} }
/// <summary> /// <summary>
/// Here we just clean up some resources and stop the OAR backup (if any) for the given scene. /// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
/// </summary> /// </summary>
/// <param name="scene">The scene (region) to stop performing AutoBackup on.</param> /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
void IRegionModuleBase.RemoveRegion(Scene scene) void IRegionModuleBase.RemoveRegion(Scene scene)
{ {
@ -237,10 +238,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
/// <summary> /// <summary>
/// Most interesting/complex code paths in AutoBackup begin here. /// Most interesting/complex code paths in AutoBackup begin here.
/// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc. /// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc.
/// </summary> /// </summary>
/// <param name="scene">The scene to (possibly) perform AutoBackup on.</param> /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
void IRegionModuleBase.RegionLoaded(Scene scene) void IRegionModuleBase.RegionLoaded(Scene scene)
{ {
@ -260,8 +261,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug((abms == null ? "DEFAULT" : abms.ToString())); m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
} }
/// <summary> /// <summary>
/// Currently a no-op. /// Currently a no-op.
/// </summary> /// </summary>
void ISharedRegionModule.PostInitialise() void ISharedRegionModule.PostInitialise()
{ {
@ -269,12 +270,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#endregion #endregion
/// <summary> /// <summary>
/// Set up internal state for a given scene. Fairly complex code. /// Set up internal state for a given scene. Fairly complex code.
/// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene. /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
/// </summary> /// </summary>
/// <param name="scene">The scene to look at.</param> /// <param name="scene">The scene to look at.</param>
/// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param> /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
/// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns> /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault) private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
{ {
@ -299,16 +300,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
// Read the config settings and set variables. // Read the config settings and set variables.
IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
IConfig config = this.m_configSource.Configs["AutoBackupModule"]; IConfig config = this.m_configSource.Configs["AutoBackupModule"];
if (config == null) if (config == null)
{ {
// defaultState would be disabled too if the section doesn't exist. // defaultState would be disabled too if the section doesn't exist.
state = this.m_defaultState; state = this.m_defaultState;
m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
return state; return state;
} }
bool tmpEnabled = config.GetBoolean(prepend + "AutoBackup", this.m_defaultState.Enabled); bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig);
if (state == null && tmpEnabled != this.m_defaultState.Enabled) if (state == null && tmpEnabled != this.m_defaultState.Enabled)
//Varies from default state //Varies from default state
{ {
@ -332,8 +333,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
// Borrow an existing timer if one exists for the same interval; otherwise, make a new one. // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
double interval = double interval =
config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)* this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
60000.0; config, regionConfig) * 60000.0;
if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0) if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
{ {
state = new AutoBackupModuleState(); state = new AutoBackupModuleState();
@ -400,8 +401,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck", bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck",
this.m_defaultState.BusyCheck); this.m_defaultState.BusyCheck, config, regionConfig);
if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck) if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
{ {
state = new AutoBackupModuleState(); state = new AutoBackupModuleState();
@ -413,8 +414,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
// Set file naming algorithm // Set file naming algorithm
string stmpNamingType = config.GetString(prepend + "AutoBackupNaming", string stmpNamingType = ResolveString("AutoBackupNaming",
this.m_defaultState.NamingType.ToString()); this.m_defaultState.NamingType.ToString(), config, regionConfig);
NamingType tmpNamingType; NamingType tmpNamingType;
if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase)) if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
{ {
@ -445,8 +446,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.NamingType = tmpNamingType; state.NamingType = tmpNamingType;
} }
string tmpScript = config.GetString(prepend + "AutoBackupScript", string tmpScript = ResolveString("AutoBackupScript",
this.m_defaultState.Script); this.m_defaultState.Script, config, regionConfig);
if (state == null && tmpScript != this.m_defaultState.Script) if (state == null && tmpScript != this.m_defaultState.Script)
{ {
state = new AutoBackupModuleState(); state = new AutoBackupModuleState();
@ -457,7 +458,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.Script = tmpScript; state.Script = tmpScript;
} }
string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", "."); string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);
if (state == null && tmpBackupDir != this.m_defaultState.BackupDir) if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
{ {
state = new AutoBackupModuleState(); state = new AutoBackupModuleState();
@ -491,10 +492,90 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return state; return state;
} }
/// <summary> /// <summary>
/// Called when any auto-backup timer expires. This starts the code path for actually performing a backup. /// Helper function for ParseConfig.
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <param name="global"></param>
/// <param name="local"></param>
/// <returns></returns>
private bool ResolveBoolean(string settingName, bool defaultValue, IConfig global, IConfig local)
{
if(local != null)
{
return local.GetBoolean(settingName, global.GetBoolean(settingName, defaultValue));
}
else
{
return global.GetBoolean(settingName, defaultValue);
}
}
/// <summary>
/// Helper function for ParseConfig.
/// </summary>
/// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <param name="global"></param>
/// <param name="local"></param>
/// <returns></returns>
private double ResolveDouble(string settingName, double defaultValue, IConfig global, IConfig local)
{
if (local != null)
{
return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
}
else
{
return global.GetDouble(settingName, defaultValue);
}
}
/// <summary>
/// Helper function for ParseConfig.
/// </summary>
/// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <param name="global"></param>
/// <param name="local"></param>
/// <returns></returns>
private int ResolveInt(string settingName, int defaultValue, IConfig global, IConfig local)
{
if (local != null)
{
return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
}
else
{
return global.GetInt(settingName, defaultValue);
}
}
/// <summary>
/// Helper function for ParseConfig.
/// </summary>
/// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <param name="global"></param>
/// <param name="local"></param>
/// <returns></returns>
private string ResolveString(string settingName, string defaultValue, IConfig global, IConfig local)
{
if (local != null)
{
return local.GetString(settingName, global.GetString(settingName, defaultValue));
}
else
{
return global.GetString(settingName, defaultValue);
}
}
/// <summary>
/// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void HandleElapsed(object sender, ElapsedEventArgs e) private void HandleElapsed(object sender, ElapsedEventArgs e)
{ {
@ -554,9 +635,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
/// <summary> /// <summary>
/// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable). /// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
/// </summary> /// </summary>
/// <param name="scene"></param> /// <param name="scene"></param>
private void DoRegionBackup(IScene scene) private void DoRegionBackup(IScene scene)
{ {
@ -585,25 +666,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
iram.ArchiveRegion(savePath, guid, null); iram.ArchiveRegion(savePath, guid, null);
} }
/// <summary> /// <summary>
/// Called by the Event Manager when the OnOarFileSaved event is fired. /// Called by the Event Manager when the OnOarFileSaved event is fired.
/// </summary> /// </summary>
/// <param name="guid"></param> /// <param name="guid"></param>
/// <param name="message"></param> /// <param name="message"></param>
void EventManager_OnOarFileSaved(Guid guid, string message) void EventManager_OnOarFileSaved(Guid guid, string message)
{ {
// Ignore if the OAR save is being done by some other part of the system // Ignore if the OAR save is being done by some other part of the system
if (m_pendingSaves.ContainsKey(guid)) if (m_pendingSaves.ContainsKey(guid))
{ {
AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])]; AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
ExecuteScript(abms.Script, abms.LiveRequests[guid]); ExecuteScript(abms.Script, abms.LiveRequests[guid]);
m_pendingSaves.Remove(guid); m_pendingSaves.Remove(guid);
abms.LiveRequests.Remove(guid); abms.LiveRequests.Remove(guid);
} }
} }
/// <summary>This format may turn out to be too unwieldy to keep... /// <summary>This format may turn out to be too unwieldy to keep...
/// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID? /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
/// Sequential numbers, right? We support those, too!</summary> /// Sequential numbers, right? We support those, too!</summary>
private static string GetTimeString() private static string GetTimeString()
{ {
@ -642,12 +723,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
/// <summary> /// <summary>
/// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5), /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
/// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR). /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
/// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy". /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
/// </summary> /// </summary>
/// <param name="region"></param> /// <param name="region"></param>
/// <returns>Returns true if we're not too busy; false means we've got worse time dilation than the threshold.</returns> /// <returns>Returns true if we're not too busy; false means we've got worse time dilation than the threshold.</returns>
private bool RunTimeDilationHeuristic(IScene region) private bool RunTimeDilationHeuristic(IScene region)
{ {
@ -657,12 +738,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
regionName + ".AutoBackupDilationThreshold", 0.5f); regionName + ".AutoBackupDilationThreshold", 0.5f);
} }
/// <summary> /// <summary>
/// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10), /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
/// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR). /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
/// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy". /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
/// </summary> /// </summary>
/// <param name="region"></param> /// <param name="region"></param>
/// <returns>Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.</returns> /// <returns>Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.</returns>
private bool RunAgentLimitHeuristic(IScene region) private bool RunAgentLimitHeuristic(IScene region)
{ {
@ -685,12 +766,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
/// <summary> /// <summary>
/// Run the script or executable specified by the "AutoBackupScript" config setting. /// Run the script or executable specified by the "AutoBackupScript" config setting.
/// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script. /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
/// But there are plenty of other nasty things that can be done with an untrusted OpenSim.ini, such as running high threat level scripting functions. /// But there are plenty of other nasty things that can be done with an untrusted OpenSim.ini, such as running high threat level scripting functions.
/// </summary> /// </summary>
/// <param name="scriptName"></param> /// <param name="scriptName"></param>
/// <param name="savePath"></param> /// <param name="savePath"></param>
private static void ExecuteScript(string scriptName, string savePath) private static void ExecuteScript(string scriptName, string savePath)
{ {
@ -719,10 +800,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
} }
} }
/// <summary> /// <summary>
/// Called if a running script process writes to stderr. /// Called if a running script process writes to stderr.
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e) private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
{ {
@ -730,8 +811,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
" is yacking on stderr: " + e.Data); " is yacking on stderr: " + e.Data);
} }
/// <summary> /// <summary>
/// Quickly stop all timers from firing. /// Quickly stop all timers from firing.
/// </summary> /// </summary>
private void StopAllTimers() private void StopAllTimers()
{ {
@ -742,11 +823,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.m_closed = true; this.m_closed = true;
} }
/// <summary> /// <summary>
/// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType. /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
/// </summary> /// </summary>
/// <param name="dirName"></param> /// <param name="dirName"></param>
/// <param name="regionName"></param> /// <param name="regionName"></param>
/// <returns></returns> /// <returns></returns>
private static string GetNextFile(string dirName, string regionName) private static string GetNextFile(string dirName, string regionName)
{ {
@ -760,12 +841,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return uniqueFile.FullName; return uniqueFile.FullName;
} }
/// <summary> /// <summary>
/// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants. /// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
/// </summary> /// </summary>
/// <param name="regionName">Name of the region to save.</param> /// <param name="regionName">Name of the region to save.</param>
/// <param name="baseDir">Absolute or relative path to the directory where the file should reside.</param> /// <param name="baseDir">Absolute or relative path to the directory where the file should reside.</param>
/// <param name="naming">The naming scheme for the file name.</param> /// <param name="naming">The naming scheme for the file name.</param>
/// <returns></returns> /// <returns></returns>
private static string BuildOarPath(string regionName, string baseDir, NamingType naming) private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
{ {
@ -792,11 +873,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return null; return null;
} }
/// <summary> /// <summary>
/// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile). /// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
/// </summary> /// </summary>
/// <param name="dirName"></param> /// <param name="dirName"></param>
/// <param name="regionName"></param> /// <param name="regionName"></param>
/// <returns></returns> /// <returns></returns>
private static long GetNextOarFileNumber(string dirName, string regionName) private static long GetNextOarFileNumber(string dirName, string regionName)
{ {