AutoBackup: Support region-independent settings too.

bulletsim
Sean McNamara 2011-04-11 12:34:26 -04:00
parent 9c0f3c73b1
commit 4974a1ce69
1 changed files with 176 additions and 58 deletions

View File

@ -49,6 +49,8 @@ using OpenSim.Region.Framework.Scenes;
* VERY IMPORTANT: You must create the key name as follows: <Region Name>.<Key Name> * VERY IMPORTANT: You must create the key name as follows: <Region Name>.<Key Name>
* Example: My region is named Foo. * Example: My region is named Foo.
* If I wanted to specify the "AutoBackupInterval" key below, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini. * If I wanted to specify the "AutoBackupInterval" key below, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
* Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
* Region-specific settings take precedence.
* AutoBackup: True/False. Default: False. If True, activate auto backup functionality. * AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
* This is the only required option for enabling auto-backup; the other options have sane defaults. * This is the only required option for enabling auto-backup; the other options have sane defaults.
* If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored. * If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
@ -122,6 +124,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return m_timer; return m_timer;
} }
public double GetIntervalMinutes ()
{
if(m_timer == null)
{
return -1.0;
}
else
{
return m_timer.Interval / 60000.0;
}
}
public void SetTimer (Timer t) public void SetTimer (Timer t)
{ {
m_timer = t; m_timer = t;
@ -167,6 +181,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{ {
m_naming = n; m_naming = n;
} }
public string ToString()
{
string retval = "";
retval += "[AUTO BACKUP]: AutoBackup: " + (GetEnabled() ? "ENABLED" : "DISABLED") + "\n";
retval += "[AUTO BACKUP]: Interval: " + GetIntervalMinutes() + " minutes" + "\n";
retval += "[AUTO BACKUP]: Do Busy Check: " + (GetBusyCheck() ? "Yes" : "No") + "\n";
retval += "[AUTO BACKUP]: Naming Type: " + GetNamingType().ToString() + "\n";
retval += "[AUTO BACKUP]: Backup Dir: " + GetBackupDir() + "\n";
retval += "[AUTO BACKUP]: Script: " + GetScript() + "\n";
return retval;
}
} }
//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.
@ -180,6 +207,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
private bool m_closed = false; private bool m_closed = false;
//True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP. //True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
//Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down. //Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
readonly AutoBackupModuleState defaultState = new AutoBackupModuleState();
public AutoBackupModule () public AutoBackupModule ()
{ {
@ -194,9 +223,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (moduleConfig != null) { if (moduleConfig != null) {
m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false); m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false);
if (m_Enabled) { if (m_Enabled) {
m_log.Info ("[AUTO BACKUP MODULE]: AutoBackupModule enabled"); m_log.Info ("[AUTO BACKUP]: AutoBackupModule enabled");
} }
} }
Timer defTimer = new Timer(720 * 60000);
defaultState.SetTimer(defTimer);
timers.Add (720*60000, defTimer);
defTimer.Elapsed += HandleElapsed;
defTimer.AutoReset = true;
defTimer.Start ();
AutoBackupModuleState abms = ParseConfig(null, false);
m_log.Debug("[AUTO BACKUP]: Config for default");
m_log.Debug(abms.ToString());
} }
void IRegionModuleBase.Close () void IRegionModuleBase.Close ()
@ -239,88 +279,163 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (scene == null) if (scene == null)
return; return;
string sRegionName = scene.RegionInfo.RegionName; AutoBackupModuleState abms = ParseConfig(scene, true);
AutoBackupModuleState st = new AutoBackupModuleState (); m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
states.Add (scene, st); m_log.Debug(abms.ToString());
}
AutoBackupModuleState ParseConfig (IScene scene, bool parseDefault)
{
string sRegionName;
string sRegionLabel;
string prepend;
AutoBackupModuleState state;
if(parseDefault)
{
sRegionName = null;
sRegionLabel = "DEFAULT";
prepend = "";
state = defaultState;
}
else
{
sRegionName = scene.RegionInfo.RegionName;
sRegionLabel = sRegionName;
prepend = sRegionName + ".";
state = null;
}
//Read the config settings and set variables. //Read the config settings and set variables.
IConfig config = m_configSource.Configs["AutoBackupModule"]; IConfig config = m_configSource.Configs["AutoBackupModule"];
if (config == null) { if (config == null) {
//No config settings for any regions, let's just give up. state = defaultState; //defaultState would be disabled too if the section doesn't exist.
st.SetEnabled (false); m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); return state;
return;
} }
st.SetEnabled (config.GetBoolean (sRegionName + ".AutoBackup", false));
bool tmpEnabled = config.GetBoolean (prepend + "AutoBackup", defaultState.GetEnabled());
if(state == null && tmpEnabled != defaultState.GetEnabled()) //Varies from default state
{
state = new AutoBackupModuleState();
state.SetEnabled (tmpEnabled);
}
//If you don't want AutoBackup, we stop. //If you don't want AutoBackup, we stop.
if (!st.GetEnabled ()) { if ((state == null && !defaultState.GetEnabled()) || !state.GetEnabled ()) {
m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
return; return state;
} else { } else {
m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED."); m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
} }
//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 = config.GetDouble (sRegionName + ".AutoBackupInterval", 720) * 60000; double interval = config.GetDouble (prepend + "AutoBackupInterval", defaultState.GetIntervalMinutes()) * 60000.0;
if(state == null && interval != defaultState.GetIntervalMinutes() * 60000.0)
{
state = new AutoBackupModuleState();
}
if (timers.ContainsKey (interval)) { if (timers.ContainsKey (interval)) {
st.SetTimer (timers[interval]); if(state != null)
m_log.Debug ("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName); state.SetTimer (timers[interval]);
m_log.Debug ("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " + sRegionLabel);
} else { } else {
//0 or negative interval == do nothing. //0 or negative interval == do nothing.
if (interval <= 0.0) { if (interval <= 0.0 && state != null) {
st.SetEnabled (false); state.SetEnabled (false);
return; return state;
} }
Timer tim = new Timer (interval); Timer tim = new Timer (interval);
st.SetTimer (tim); state.SetTimer (tim);
//Milliseconds -> minutes //Milliseconds -> minutes
timers.Add (interval, tim); timers.Add (interval, tim);
tim.Elapsed += HandleElapsed; tim.Elapsed += HandleElapsed;
tim.AutoReset = true; tim.AutoReset = true;
tim.Start (); tim.Start ();
//m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName); //m_log.Debug("[AUTO BACKUP]: New timer for " + interval + " msec for region " + sRegionName);
} }
//Add the current region to the list of regions tied to this timer. //Add the current region to the list of regions tied to this timer.
if (timerMap.ContainsKey (st.GetTimer ())) { if (timerMap.ContainsKey (state.GetTimer ())) {
timerMap[st.GetTimer ()].Add (scene); timerMap[state.GetTimer ()].Add (scene);
} else { } else {
List<IScene> scns = new List<IScene> (1); List<IScene> scns = new List<IScene> (1);
scns.Add (scene); scns.Add (scene);
timerMap.Add (st.GetTimer (), scns); timerMap.Add (state.GetTimer (), scns);
} }
st.SetBusyCheck (config.GetBoolean (sRegionName + ".AutoBackupBusyCheck", true)); bool tmpBusyCheck = config.GetBoolean (prepend + "AutoBackupBusyCheck", defaultState.GetBusyCheck());
if(state == null && tmpBusyCheck != defaultState.GetBusyCheck())
{
state = new AutoBackupModuleState();
}
if(state != null)
{
state.SetBusyCheck (tmpBusyCheck);
}
//Set file naming algorithm //Set file naming algorithm
string namingtype = config.GetString (sRegionName + ".AutoBackupNaming", "Time"); string stmpNamingType = config.GetString (prepend + "AutoBackupNaming", defaultState.GetNamingType().ToString());
if (namingtype.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) { NamingType tmpNamingType;
st.SetNamingType (NamingType.TIME); if (stmpNamingType.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) {
} else if (namingtype.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) { tmpNamingType = NamingType.TIME;
st.SetNamingType (NamingType.SEQUENTIAL); } else if (stmpNamingType.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) {
} else if (namingtype.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) { tmpNamingType = NamingType.SEQUENTIAL;
st.SetNamingType (NamingType.OVERWRITE); } else if (stmpNamingType.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) {
tmpNamingType = NamingType.OVERWRITE;
} else { } else {
m_log.Warn ("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype); m_log.Warn ("Unknown naming type specified for region " + sRegionLabel + ": " + stmpNamingType);
st.SetNamingType (NamingType.TIME); tmpNamingType = NamingType.TIME;
} }
st.SetScript (config.GetString (sRegionName + ".AutoBackupScript", null)); if(state == null && tmpNamingType != defaultState.GetNamingType())
st.SetBackupDir (config.GetString (sRegionName + ".AutoBackupDir", ".")); {
state = new AutoBackupModuleState();
}
if(state != null)
{
state.SetNamingType(tmpNamingType);
}
string tmpScript = config.GetString (prepend + "AutoBackupScript", defaultState.GetScript());
if(state == null && tmpScript != defaultState.GetScript())
{
state = new AutoBackupModuleState();
}
if(state != null)
{
state.SetScript (tmpScript);
}
string tmpBackupDir = config.GetString (prepend + "AutoBackupDir", ".");
if(state == null && tmpBackupDir != defaultState.GetBackupDir())
{
state = new AutoBackupModuleState();
}
if(state != null)
{
state.SetBackupDir (tmpBackupDir);
//Let's give the user *one* convenience and auto-mkdir //Let's give the user *one* convenience and auto-mkdir
if (st.GetBackupDir () != ".") { if (state.GetBackupDir () != ".") {
try { try {
DirectoryInfo dirinfo = new DirectoryInfo (st.GetBackupDir ()); DirectoryInfo dirinfo = new DirectoryInfo (state.GetBackupDir ());
if (!dirinfo.Exists) { if (!dirinfo.Exists) {
dirinfo.Create (); dirinfo.Create ();
} }
} catch (Exception e) { } catch (Exception e) {
m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e); m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + state.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
} }
} }
} }
return state;
}
void HandleElapsed (object sender, ElapsedEventArgs e) void HandleElapsed (object sender, ElapsedEventArgs e)
{ {
//TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region //TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region
@ -336,7 +451,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (!timerMap.ContainsKey ((Timer)sender)) { if (!timerMap.ContainsKey ((Timer)sender)) {
m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ()); m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ());
} }
foreach (IScene scene in timerMap[(Timer)sender]) {
List<IScene> tmap = timerMap[(Timer)sender];
if(tmap != null && tmap.Count > 0)
foreach (IScene scene in tmap) {
AutoBackupModuleState state = states[scene]; AutoBackupModuleState state = states[scene];
bool heuristics = state.GetBusyCheck (); bool heuristics = state.GetBusyCheck ();
@ -345,14 +463,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
doRegionBackup (scene); doRegionBackup (scene);
//Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off! //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
} else if (heuristics && heuristicsRun && !heuristicsPassed) { } else if (heuristics && heuristicsRun && !heuristicsPassed) {
m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now."); m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue; continue;
//Logical Deduction: heuristics are on but haven't been run //Logical Deduction: heuristics are on but haven't been run
} else { } else {
heuristicsPassed = RunHeuristics (scene); heuristicsPassed = RunHeuristics (scene);
heuristicsRun = true; heuristicsRun = true;
if (!heuristicsPassed) { if (!heuristicsPassed) {
m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now."); m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue; continue;
} }
doRegionBackup (scene); doRegionBackup (scene);
@ -364,16 +482,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{ {
if (scene.RegionStatus != RegionStatus.Up) { if (scene.RegionStatus != RegionStatus.Up) {
//We won't backup a region that isn't operating normally. //We won't backup a region that isn't operating normally.
m_log.Warn ("[AUTO BACKUP MODULE]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ()); m_log.Warn ("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ());
return; return;
} }
AutoBackupModuleState state = states[scene]; AutoBackupModuleState state = states[scene];
IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule> (); IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule> ();
string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ()); string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ());
//m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath); //m_log.Debug("[AUTO BACKUP]: savePath = " + savePath);
if (savePath == null) { if (savePath == null) {
m_log.Warn ("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed"); m_log.Warn ("[AUTO BACKUP]: savePath is null in HandleElapsed");
return; return;
} }
iram.ArchiveRegion (savePath, null); iram.ArchiveRegion (savePath, null);
@ -481,7 +599,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
try { try {
return RunTimeDilationHeuristic (region) && RunAgentLimitHeuristic (region); return RunTimeDilationHeuristic (region) && RunAgentLimitHeuristic (region);
} catch (Exception e) { } catch (Exception e) {
m_log.Warn ("[AUTO BACKUP MODULE]: Exception in RunHeuristics", e); m_log.Warn ("[AUTO BACKUP]: Exception in RunHeuristics", e);
return false; return false;
} }
} }
@ -512,7 +630,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful... //TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
return scene.GetRootAgentCount () <= m_configSource.Configs["AutoBackupModule"].GetInt (regionName + ".AutoBackupAgentThreshold", 10); return scene.GetRootAgentCount () <= m_configSource.Configs["AutoBackupModule"].GetInt (regionName + ".AutoBackupAgentThreshold", 10);
} catch (InvalidCastException ice) { } catch (InvalidCastException ice) {
m_log.Debug ("[AUTO BACKUP MODULE]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!"); m_log.Debug ("[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!");
return true; return true;
//Non-obstructionist safest answer... //Non-obstructionist safest answer...
} }