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>
/// AutoBackupModule: save OAR region backups to disk periodically
/// </summary>
/// </summary>
/// <remarks>
/// Config Settings Documentation.
/// 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.
/// 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.
/// Also helps if you don't want AutoBackup at all.
/// Also helps if you don't want AutoBackup at all.
/// </remarks>
public class AutoBackupModule : ISharedRegionModule
{
@ -110,17 +110,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
new Dictionary<Timer, List<IScene>>(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;
/// <summary>
/// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
/// <summary>
/// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
/// </summary>
private bool m_closed;
private IConfigSource m_configSource;
/// <summary>
/// Required by framework.
/// <summary>
/// Required by framework.
/// </summary>
public bool IsSharedModule
{
@ -129,25 +130,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#region ISharedRegionModule Members
/// <summary>
/// Identifies the module to the system.
/// <summary>
/// Identifies the module to the system.
/// </summary>
string IRegionModuleBase.Name
{
get { return "AutoBackupModule"; }
}
/// <summary>
/// We don't implement an interface, this is a single-use module.
/// <summary>
/// We don't implement an interface, this is a single-use module.
/// </summary>
Type IRegionModuleBase.ReplaceableInterface
{
get { return null; }
}
/// <summary>
/// Called once in the lifetime of the module at startup.
/// </summary>
/// <summary>
/// Called once in the lifetime of the module at startup.
/// </summary>
/// <param name="source">The input config source for OpenSim.ini.</param>
void IRegionModuleBase.Initialise(IConfigSource source)
{
@ -184,8 +185,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug(abms.ToString());
}
/// <summary>
/// Called once at de-init (sim shutting down).
/// <summary>
/// Called once at de-init (sim shutting down).
/// </summary>
void IRegionModuleBase.Close()
{
@ -198,17 +199,17 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.StopAllTimers();
}
/// <summary>
/// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
/// </summary>
/// <summary>
/// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
/// </summary>
/// <param name="scene"></param>
void IRegionModuleBase.AddRegion(Scene scene)
{
}
/// <summary>
/// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
/// </summary>
/// <summary>
/// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
/// </summary>
/// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
void IRegionModuleBase.RemoveRegion(Scene scene)
{
@ -237,10 +238,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
/// <summary>
/// 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.
/// </summary>
/// <summary>
/// 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.
/// </summary>
/// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
void IRegionModuleBase.RegionLoaded(Scene scene)
{
@ -260,8 +261,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
}
/// <summary>
/// Currently a no-op.
/// <summary>
/// Currently a no-op.
/// </summary>
void ISharedRegionModule.PostInitialise()
{
@ -269,12 +270,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#endregion
/// <summary>
/// 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.
/// </summary>
/// <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>
/// <summary>
/// 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.
/// </summary>
/// <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>
/// <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)
{
@ -299,16 +300,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
// Read the config settings and set variables.
IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
IConfig config = this.m_configSource.Configs["AutoBackupModule"];
if (config == null)
{
// defaultState would be disabled too if the section doesn't exist.
state = this.m_defaultState;
m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
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)
//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.
double interval =
config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)*
60000.0;
this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
config, regionConfig) * 60000.0;
if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
{
state = new AutoBackupModuleState();
@ -400,8 +401,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck",
this.m_defaultState.BusyCheck);
bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck",
this.m_defaultState.BusyCheck, config, regionConfig);
if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
{
state = new AutoBackupModuleState();
@ -413,8 +414,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
// Set file naming algorithm
string stmpNamingType = config.GetString(prepend + "AutoBackupNaming",
this.m_defaultState.NamingType.ToString());
string stmpNamingType = ResolveString("AutoBackupNaming",
this.m_defaultState.NamingType.ToString(), config, regionConfig);
NamingType tmpNamingType;
if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
{
@ -445,8 +446,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.NamingType = tmpNamingType;
}
string tmpScript = config.GetString(prepend + "AutoBackupScript",
this.m_defaultState.Script);
string tmpScript = ResolveString("AutoBackupScript",
this.m_defaultState.Script, config, regionConfig);
if (state == null && tmpScript != this.m_defaultState.Script)
{
state = new AutoBackupModuleState();
@ -457,7 +458,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.Script = tmpScript;
}
string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", ".");
string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);
if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
{
state = new AutoBackupModuleState();
@ -491,10 +492,90 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return state;
}
/// <summary>
/// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
/// </summary>
/// <param name="sender"></param>
/// <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 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>
private void HandleElapsed(object sender, ElapsedEventArgs e)
{
@ -554,9 +635,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
/// <summary>
/// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
/// </summary>
/// <summary>
/// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
/// </summary>
/// <param name="scene"></param>
private void DoRegionBackup(IScene scene)
{
@ -585,25 +666,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
iram.ArchiveRegion(savePath, guid, null);
}
/// <summary>
/// Called by the Event Manager when the OnOarFileSaved event is fired.
/// </summary>
/// <param name="guid"></param>
/// <summary>
/// Called by the Event Manager when the OnOarFileSaved event is fired.
/// </summary>
/// <param name="guid"></param>
/// <param name="message"></param>
void EventManager_OnOarFileSaved(Guid guid, string message)
{
// Ignore if the OAR save is being done by some other part of the system
if (m_pendingSaves.ContainsKey(guid))
{
AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
ExecuteScript(abms.Script, abms.LiveRequests[guid]);
m_pendingSaves.Remove(guid);
abms.LiveRequests.Remove(guid);
}
{
// Ignore if the OAR save is being done by some other part of the system
if (m_pendingSaves.ContainsKey(guid))
{
AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
ExecuteScript(abms.Script, abms.LiveRequests[guid]);
m_pendingSaves.Remove(guid);
abms.LiveRequests.Remove(guid);
}
}
/// <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>
private static string GetTimeString()
{
@ -642,12 +723,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
/// <summary>
/// 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).
/// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
/// </summary>
/// <param name="region"></param>
/// <summary>
/// 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).
/// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
/// </summary>
/// <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>
private bool RunTimeDilationHeuristic(IScene region)
{
@ -657,12 +738,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
regionName + ".AutoBackupDilationThreshold", 0.5f);
}
/// <summary>
/// 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).
/// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
/// </summary>
/// <param name="region"></param>
/// <summary>
/// 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).
/// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
/// </summary>
/// <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>
private bool RunAgentLimitHeuristic(IScene region)
{
@ -685,12 +766,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
/// <summary>
/// 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.
/// 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>
/// <param name="scriptName"></param>
/// <summary>
/// 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.
/// 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>
/// <param name="scriptName"></param>
/// <param name="savePath"></param>
private static void ExecuteScript(string scriptName, string savePath)
{
@ -719,10 +800,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
/// <summary>
/// Called if a running script process writes to stderr.
/// </summary>
/// <param name="sender"></param>
/// <summary>
/// Called if a running script process writes to stderr.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
{
@ -730,8 +811,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
" is yacking on stderr: " + e.Data);
}
/// <summary>
/// Quickly stop all timers from firing.
/// <summary>
/// Quickly stop all timers from firing.
/// </summary>
private void StopAllTimers()
{
@ -742,11 +823,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.m_closed = true;
}
/// <summary>
/// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
/// </summary>
/// <param name="dirName"></param>
/// <param name="regionName"></param>
/// <summary>
/// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
/// </summary>
/// <param name="dirName"></param>
/// <param name="regionName"></param>
/// <returns></returns>
private static string GetNextFile(string dirName, string regionName)
{
@ -760,12 +841,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return uniqueFile.FullName;
}
/// <summary>
/// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
/// </summary>
/// <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="naming">The naming scheme for the file name.</param>
/// <summary>
/// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
/// </summary>
/// <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="naming">The naming scheme for the file name.</param>
/// <returns></returns>
private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
{
@ -792,11 +873,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return null;
}
/// <summary>
/// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
/// </summary>
/// <param name="dirName"></param>
/// <param name="regionName"></param>
/// <summary>
/// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
/// </summary>
/// <param name="dirName"></param>
/// <param name="regionName"></param>
/// <returns></returns>
private static long GetNextOarFileNumber(string dirName, string regionName)
{