aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorUbitUmarov2017-06-21 03:31:07 +0100
committerUbitUmarov2017-06-21 03:31:07 +0100
commit651952e01eef5ad98e87fbe3909dd11f1d2d9db1 (patch)
tree2010c4d5776915f8408b56117ec83514c937470c
parent revert EnvironmentTick back to orignal clock, since change may cause issues ... (diff)
downloadopensim-SC-651952e01eef5ad98e87fbe3909dd11f1d2d9db1.zip
opensim-SC-651952e01eef5ad98e87fbe3909dd11f1d2d9db1.tar.gz
opensim-SC-651952e01eef5ad98e87fbe3909dd11f1d2d9db1.tar.bz2
opensim-SC-651952e01eef5ad98e87fbe3909dd11f1d2d9db1.tar.xz
changes to AutoBackModule. Time interval is now unique and only definable on OpenSim.ini. All enabled regions will be saved in sequence whne it expires. Interval is time since last region save end, so will not follow wall clock. This reduces the chance of overlapped saves. Console command renamed to dooarbackup to be more distint from db backup. Lost the region load checks, to put back later
-rw-r--r--OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs718
-rw-r--r--OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs59
2 files changed, 221 insertions, 556 deletions
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 79b80f8..eab6705 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -59,23 +59,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
59 /// </summary> 59 /// </summary>
60 /// <remarks> 60 /// <remarks>
61 /// Config Settings Documentation. 61 /// Config Settings Documentation.
62 /// Each configuration setting can be specified in two places: OpenSim.ini or Regions.ini. 62 /// Configuration setting can be specified in two places: OpenSim.ini and/or Regions.ini.
63 /// If specified in Regions.ini, the settings should be within the region's section name.
64 /// If specified in OpenSim.ini, the settings should be within the [AutoBackupModule] section.
65 /// Region-specific settings take precedence.
66 /// 63 ///
67 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis. 64 /// OpenSim.ini only settings section [AutoBackupModule]
68 /// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings! 65 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module.
69 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality. 66 /// if false module is disable and all rest is ignored
70 /// This is the only required option for enabling auto-backup; the other options have sane defaults.
71 /// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
72 /// If False globally (the default), only regions that specifically override it in Regions.ini will get AutoBackup functionality.
73 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours). 67 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
74 /// The number of minutes between each backup attempt. 68 /// The number of minutes between each backup attempt.
75 /// If a negative or zero value is given, it is equivalent to setting AutoBackup = False. 69 ///
76 /// AutoBackupBusyCheck: True/False. Default: True. 70 /// Next can be set on OpenSim.ini, as default, and or per region in Regions.ini
77 /// If True, we will only take an auto-backup if a set of conditions are met. 71 /// Region-specific settings take precedence.
78 /// These conditions are heuristics to try and avoid taking a backup when the sim is busy. 72 ///
73 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
74 /// controls backup per region, with default optionaly set on OpenSim.ini
75
79 /// AutoBackupSkipAssets 76 /// AutoBackupSkipAssets
80 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately 77 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately
81 /// AutoBackupKeepFilesForDays 78 /// AutoBackupKeepFilesForDays
@@ -93,36 +90,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
93 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file. 90 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
94 /// AutoBackupDir: String. Default: "." (the current directory). 91 /// AutoBackupDir: String. Default: "." (the current directory).
95 /// A directory (absolute or relative) where backups should be saved. 92 /// A directory (absolute or relative) where backups should be saved.
96 /// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
97 /// If the time dilation is below this value, don't take a backup right now.
98 /// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
99 /// If the number of agents is greater than this value, don't take a backup right now
100 /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
101 /// Also helps if you don't want AutoBackup at all.
102 /// </remarks> 93 /// </remarks>
103 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")] 94 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")]
104 public class AutoBackupModule : ISharedRegionModule 95 public class AutoBackupModule : ISharedRegionModule
105 { 96 {
106 private static readonly ILog m_log = 97 private static readonly ILog m_log =
107 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 98 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
108 private readonly Dictionary<Guid, IScene> m_pendingSaves = new Dictionary<Guid, IScene>(1);
109 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState(); 99 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
110 private readonly Dictionary<IScene, AutoBackupModuleState> m_states = 100 private readonly Dictionary<IScene, AutoBackupModuleState> m_states =
111 new Dictionary<IScene, AutoBackupModuleState>(1); 101 new Dictionary<IScene, AutoBackupModuleState>(1);
112 private readonly Dictionary<Timer, List<IScene>> m_timerMap =
113 new Dictionary<Timer, List<IScene>>(1);
114 private readonly Dictionary<double, Timer> m_timers = new Dictionary<double, Timer>(1);
115 102
116 private delegate T DefaultGetter<T>(string settingName, T defaultValue); 103 private delegate T DefaultGetter<T>(string settingName, T defaultValue);
117 private bool m_enabled; 104 private bool m_enabled;
118 private ICommandConsole m_console; 105 private ICommandConsole m_console;
119 private List<Scene> m_Scenes = new List<Scene> (); 106 private List<Scene> m_Scenes = new List<Scene> ();
120 107 private Timer m_masterTimer;
121 108 private bool m_busy;
122 /// <summary>
123 /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
124 /// </summary>
125 private bool m_closed;
126 109
127 private IConfigSource m_configSource; 110 private IConfigSource m_configSource;
128 111
@@ -159,36 +142,34 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
159 void IRegionModuleBase.Initialise(IConfigSource source) 142 void IRegionModuleBase.Initialise(IConfigSource source)
160 { 143 {
161 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module 144 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
162 this.m_configSource = source; 145 m_configSource = source;
163 IConfig moduleConfig = source.Configs["AutoBackupModule"]; 146 IConfig moduleConfig = source.Configs["AutoBackupModule"];
164 if (moduleConfig == null) 147 if (moduleConfig == null)
165 { 148 {
166 this.m_enabled = false; 149 m_enabled = false;
167 return; 150 return;
168 } 151 }
169 else
170 {
171 this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
172 if (this.m_enabled)
173 {
174 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
175 }
176 else
177 {
178 return;
179 }
180 }
181 152
182 Timer defTimer = new Timer(43200000); 153 m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
183 this.m_defaultState.Timer = defTimer; 154 if(!m_enabled)
184 this.m_timers.Add(43200000, defTimer); 155 return;
185 defTimer.Elapsed += this.HandleElapsed; 156
186 defTimer.AutoReset = true; 157 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
187 defTimer.Start(); 158 m_masterTimer = new Timer(43200000);
159 m_masterTimer.Elapsed += HandleElapsed;
160 m_masterTimer.AutoReset = false;
161
162 ParseDefaultConfig();
163 m_log.Debug("[AUTO BACKUP]: Default config:");
164 m_log.Debug(m_defaultState.ToString());
188 165
189 AutoBackupModuleState abms = this.ParseConfig(null, true); 166 m_console = MainConsole.Instance;
190 m_log.Debug("[AUTO BACKUP]: Here is the default config:"); 167
191 m_log.Debug(abms.ToString()); 168 m_console.Commands.AddCommand (
169 "AutoBackup", true, "dooarbackup",
170 "dooarbackup <regionName>",
171 "do single region backup into a oar. Identical to save oar but using AutoBackup settings for name etc", DoBackup);
172 m_busy = true;
192 } 173 }
193 174
194 /// <summary> 175 /// <summary>
@@ -196,13 +177,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
196 /// </summary> 177 /// </summary>
197 void IRegionModuleBase.Close() 178 void IRegionModuleBase.Close()
198 { 179 {
199 if (!this.m_enabled) 180 if (!m_enabled)
200 {
201 return; 181 return;
202 }
203 182
204 // We don't want any timers firing while the sim's coming down; strange things may happen. 183 // We don't want any timers firing while the sim's coming down; strange things may happen.
205 this.StopAllTimers(); 184 m_masterTimer.Dispose();
206 } 185 }
207 186
208 /// <summary> 187 /// <summary>
@@ -211,18 +190,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
211 /// <param name="scene"></param> 190 /// <param name="scene"></param>
212 void IRegionModuleBase.AddRegion (Scene scene) 191 void IRegionModuleBase.AddRegion (Scene scene)
213 { 192 {
214 if (!this.m_enabled) { 193 if (!m_enabled)
215 return; 194 return;
216 }
217 lock (m_Scenes) {
218 m_Scenes.Add (scene);
219 }
220 m_console = MainConsole.Instance;
221 195
222 m_console.Commands.AddCommand ( 196 lock (m_Scenes)
223 "AutoBackup", false, "dobackup", 197 m_Scenes.Add (scene);
224 "dobackup",
225 "do backup.", DoBackup);
226 } 198 }
227 199
228 /// <summary> 200 /// <summary>
@@ -231,28 +203,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
231 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param> 203 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
232 void IRegionModuleBase.RemoveRegion(Scene scene) 204 void IRegionModuleBase.RemoveRegion(Scene scene)
233 { 205 {
234 if (!this.m_enabled) 206 if (m_enabled)
235 {
236 return; 207 return;
237 }
238 m_Scenes.Remove (scene);
239 if (this.m_states.ContainsKey(scene))
240 {
241 AutoBackupModuleState abms = this.m_states[scene];
242
243 // Remove this scene out of the timer map list
244 Timer timer = abms.Timer;
245 List<IScene> list = this.m_timerMap[timer];
246 list.Remove(scene);
247 208
248 // Shut down the timer if this was the last scene for the timer 209 lock(m_Scenes)
249 if (list.Count == 0) 210 {
250 { 211 if (m_states.ContainsKey(scene))
251 this.m_timerMap.Remove(timer); 212 m_states.Remove(scene);
252 this.m_timers.Remove(timer.Interval); 213 m_Scenes.Remove(scene);
253 timer.Close();
254 }
255 this.m_states.Remove(scene);
256 } 214 }
257 } 215 }
258 216
@@ -263,22 +221,29 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
263 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param> 221 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
264 void IRegionModuleBase.RegionLoaded(Scene scene) 222 void IRegionModuleBase.RegionLoaded(Scene scene)
265 { 223 {
266 if (!this.m_enabled) 224 if (!m_enabled)
267 {
268 return; 225 return;
269 }
270 226
271 // This really ought not to happen, but just in case, let's pretend it didn't... 227 // This really ought not to happen, but just in case, let's pretend it didn't...
272 if (scene == null) 228 if (scene == null)
273 {
274 return; 229 return;
275 }
276 230
277 AutoBackupModuleState abms = this.ParseConfig(scene, false); 231 AutoBackupModuleState abms = ParseConfig(scene);
278 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName); 232 if(abms == null)
279 m_log.Debug((abms == null ? "DEFAULT" : abms.ToString())); 233 {
234 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
235 m_log.Debug("DEFAULT");
236 abms = new AutoBackupModuleState(m_defaultState);
237 }
238 else
239 {
240 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
241 m_log.Debug(abms.ToString());
242 }
280 243
281 m_states.Add(scene, abms); 244 m_states.Add(scene, abms);
245 m_busy = false;
246 m_masterTimer.Start();
282 } 247 }
283 248
284 /// <summary> 249 /// <summary>
@@ -286,280 +251,193 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
286 /// </summary> 251 /// </summary>
287 void ISharedRegionModule.PostInitialise() 252 void ISharedRegionModule.PostInitialise()
288 { 253 {
254
289 } 255 }
290 256
291 #endregion 257 #endregion
292 258
293 private void DoBackup (string module, string[] args) 259 private void DoBackup (string module, string[] args)
294 { 260 {
295 if (args.Length != 2) { 261 if (!m_enabled)
296 MainConsole.Instance.OutputFormat ("Usage: dobackup <regionname>"); 262 return;
263
264 if(m_busy)
265 {
266 MainConsole.Instance.OutputFormat ("Already doing a backup, please try later");
267 return;
268 }
269
270 if (args.Length != 2)
271 {
272 MainConsole.Instance.OutputFormat ("Usage: dooarbackup <regionname>");
297 return; 273 return;
298 } 274 }
275
276 m_busy = true;
277
299 bool found = false; 278 bool found = false;
300 string name = args [1]; 279 string name = args [1];
301 lock (m_Scenes) { 280 Scene[] scenes;
302 foreach (Scene s in m_Scenes) { 281 lock (m_Scenes)
303 string test = s.Name.ToString (); 282 scenes = m_Scenes.ToArray();
304 if (test == name) { 283
284 if(scenes == null)
285 return;
286
287 Scene s;
288 try
289 {
290 for(int i = 0; i < scenes.Length; i++)
291 {
292 s = scenes[i];
293 if (s.Name == name)
294 {
305 found = true; 295 found = true;
306 DoRegionBackup (s); 296 m_masterTimer.Stop();
297 DoRegionBackup(s);
298 break;
307 } 299 }
308 } 300 }
309 if (!found) { 301 } catch { }
302
303 if (!found)
310 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name); 304 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name);
311 } 305 m_busy = false;
312 }
313 } 306 }
314 307
315 /// <summary> 308 private void ParseDefaultConfig()
316 /// Set up internal state for a given scene. Fairly complex code.
317 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
318 /// </summary>
319 /// <param name="scene">The scene to look at.</param>
320 /// <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>
321 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
322 private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
323 { 309 {
324 string sRegionName; 310 IConfig config = m_configSource.Configs["AutoBackupModule"];
325 string sRegionLabel; 311 if (config == null)
326// string prepend; 312 return;
327 AutoBackupModuleState state;
328 313
329 if (parseDefault) 314 // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
330 { 315 double interval = config.GetDouble("AutoBackupInterval", 720);
331 sRegionName = null; 316 interval *= 60000.0;
332 sRegionLabel = "DEFAULT"; 317 m_masterTimer.Interval = interval;
333// prepend = "";
334 state = this.m_defaultState;
335 }
336 else
337 {
338 sRegionName = scene.RegionInfo.RegionName;
339 sRegionLabel = sRegionName;
340// prepend = sRegionName + ".";
341 state = null;
342 }
343 318
344 // Read the config settings and set variables. 319 m_defaultState.Enabled = config.GetBoolean("AutoBackup", m_defaultState.Enabled);
345 IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
346 IConfig config = this.m_configSource.Configs["AutoBackupModule"];
347 if (config == null)
348 {
349 // defaultState would be disabled too if the section doesn't exist.
350 state = this.m_defaultState;
351 return state;
352 }
353 320
354 bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig); 321 // Included Option To Skip Assets
355 if (state == null && tmpEnabled != this.m_defaultState.Enabled) 322 m_defaultState.SkipAssets = config.GetBoolean("AutoBackupSkipAssets",m_defaultState.SkipAssets);
356 //Varies from default state
357 {
358 state = new AutoBackupModuleState();
359 }
360 323
361 if (state != null) 324 // How long to keep backup files in days, 0 Disables this feature
362 { 325 m_defaultState.KeepFilesForDays = config.GetInt("AutoBackupKeepFilesForDays",m_defaultState.KeepFilesForDays);
363 state.Enabled = tmpEnabled;
364 }
365 326
366 // If you don't want AutoBackup, we stop. 327 // Set file naming algorithm
367 if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled)) 328 string stmpNamingType = config.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
368 { 329 NamingType tmpNamingType;
369 return state; 330 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
370 } 331 tmpNamingType = NamingType.Time;
332 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
333 tmpNamingType = NamingType.Sequential;
334 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
335 tmpNamingType = NamingType.Overwrite;
371 else 336 else
372 { 337 {
373 m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED."); 338 m_log.Warn("Unknown naming type specified for Default");
339 tmpNamingType = NamingType.Time;
374 } 340 }
341 m_defaultState.NamingType = tmpNamingType;
375 342
376 // Borrow an existing timer if one exists for the same interval; otherwise, make a new one. 343 m_defaultState.Script = config.GetString("AutoBackupScript", m_defaultState.Script);
377 double interval =
378 this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
379 config, regionConfig) * 60000.0;
380 if (state == null && interval != this.m_defaultState.IntervalMinutes * 60000.0)
381 {
382 state = new AutoBackupModuleState();
383 }
384 344
385 if (this.m_timers.ContainsKey(interval)) 345 string backupDir = config.GetString("AutoBackupDir", ".");
346 if (backupDir != ".")
386 { 347 {
387 if (state != null) 348 try
388 { 349 {
389 state.Timer = this.m_timers[interval]; 350 DirectoryInfo dirinfo = new DirectoryInfo(backupDir);
351 if (!dirinfo.Exists)
352 dirinfo.Create();
390 } 353 }
391 m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " + 354 catch (Exception e)
392 sRegionLabel);
393 }
394 else
395 {
396 // 0 or negative interval == do nothing.
397 if (interval <= 0.0 && state != null)
398 { 355 {
399 state.Enabled = false; 356 m_log.Warn(
400 return state; 357 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
401 } 358 backupDir +
402 if (state == null) 359 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
403 { 360 e);
404 state = new AutoBackupModuleState();
405 } 361 }
406 Timer tim = new Timer(interval);
407 state.Timer = tim;
408 //Milliseconds -> minutes
409 this.m_timers.Add(interval, tim);
410 tim.Elapsed += this.HandleElapsed;
411 tim.AutoReset = true;
412 tim.Start();
413 } 362 }
363 m_defaultState.BackupDir = backupDir;
364 }
414 365
415 // Add the current region to the list of regions tied to this timer. 366 /// <summary>
416 if (scene != null) 367 /// Set up internal state for a given scene. Fairly complex code.
417 { 368 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
418 if (state != null) 369 /// </summary>
419 { 370 /// <param name="scene">The scene to look at.</param>
420 if (this.m_timerMap.ContainsKey(state.Timer)) 371 /// <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>
421 { 372 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
422 this.m_timerMap[state.Timer].Add(scene); 373 private AutoBackupModuleState ParseConfig(IScene scene)
423 } 374 {
424 else 375 if(scene == null)
425 { 376 return null;
426 List<IScene> scns = new List<IScene>(1);
427 scns.Add(scene);
428 this.m_timerMap.Add(state.Timer, scns);
429 }
430 }
431 else
432 {
433 if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
434 {
435 this.m_timerMap[this.m_defaultState.Timer].Add(scene);
436 }
437 else
438 {
439 List<IScene> scns = new List<IScene>(1);
440 scns.Add(scene);
441 this.m_timerMap.Add(this.m_defaultState.Timer, scns);
442 }
443 }
444 }
445 377
446 bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck", 378 string sRegionName;
447 this.m_defaultState.BusyCheck, config, regionConfig); 379 string sRegionLabel;
448 if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck) 380 AutoBackupModuleState state = null;
449 {
450 state = new AutoBackupModuleState();
451 }
452 381
453 if (state != null) 382 sRegionName = scene.RegionInfo.RegionName;
454 { 383 sRegionLabel = sRegionName;
455 state.BusyCheck = tmpBusyCheck;
456 }
457 384
458 // Included Option To Skip Assets 385 // Read the config settings and set variables.
459 bool tmpSkipAssets = ResolveBoolean("AutoBackupSkipAssets", 386 IConfig regionConfig = scene.Config.Configs[sRegionName];
460 this.m_defaultState.SkipAssets, config, regionConfig); 387 if (regionConfig == null)
461 if (state == null && tmpSkipAssets != this.m_defaultState.SkipAssets) 388 return null;
462 {
463 state = new AutoBackupModuleState();
464 }
465 389
466 if (state != null) 390 state = new AutoBackupModuleState();
467 {
468 state.SkipAssets = tmpSkipAssets;
469 }
470 391
471 // How long to keep backup files in days, 0 Disables this feature 392 state.Enabled = regionConfig.GetBoolean("AutoBackup", m_defaultState.Enabled);
472 int tmpKeepFilesForDays = ResolveInt("AutoBackupKeepFilesForDays",
473 this.m_defaultState.KeepFilesForDays, config, regionConfig);
474 if (state == null && tmpKeepFilesForDays != this.m_defaultState.KeepFilesForDays)
475 {
476 state = new AutoBackupModuleState();
477 }
478 393
479 if (state != null) 394 // Included Option To Skip Assets
480 { 395 state.SkipAssets = regionConfig.GetBoolean("AutoBackupSkipAssets", m_defaultState.SkipAssets);
481 state.KeepFilesForDays = tmpKeepFilesForDays; 396
482 } 397 // How long to keep backup files in days, 0 Disables this feature
398 state.KeepFilesForDays = regionConfig.GetInt("AutoBackupKeepFilesForDays", m_defaultState.KeepFilesForDays);
483 399
484 // Set file naming algorithm 400 // Set file naming algorithm
485 string stmpNamingType = ResolveString("AutoBackupNaming", 401 string stmpNamingType = regionConfig.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
486 this.m_defaultState.NamingType.ToString(), config, regionConfig);
487 NamingType tmpNamingType; 402 NamingType tmpNamingType;
488 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase)) 403 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
489 {
490 tmpNamingType = NamingType.Time; 404 tmpNamingType = NamingType.Time;
491 }
492 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase)) 405 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
493 {
494 tmpNamingType = NamingType.Sequential; 406 tmpNamingType = NamingType.Sequential;
495 }
496 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase)) 407 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
497 {
498 tmpNamingType = NamingType.Overwrite; 408 tmpNamingType = NamingType.Overwrite;
499 }
500 else 409 else
501 { 410 {
502 m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " + 411 m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
503 stmpNamingType); 412 stmpNamingType);
504 tmpNamingType = NamingType.Time; 413 tmpNamingType = NamingType.Time;
505 } 414 }
415 m_defaultState.NamingType = tmpNamingType;
506 416
507 if (state == null && tmpNamingType != this.m_defaultState.NamingType) 417 state.Script = regionConfig.GetString("AutoBackupScript", m_defaultState.Script);
508 {
509 state = new AutoBackupModuleState();
510 }
511
512 if (state != null)
513 {
514 state.NamingType = tmpNamingType;
515 }
516 418
517 string tmpScript = ResolveString("AutoBackupScript", 419 string tmpBackupDir = regionConfig.GetString("AutoBackupDir", ".");
518 this.m_defaultState.Script, config, regionConfig); 420 // Let's give the user some convenience and auto-mkdir
519 if (state == null && tmpScript != this.m_defaultState.Script) 421 if (tmpBackupDir != "." && tmpBackupDir != m_defaultState.BackupDir)
520 { 422 {
521 state = new AutoBackupModuleState(); 423 try
522 }
523
524 if (state != null)
525 {
526 state.Script = tmpScript;
527 }
528
529 string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);
530 if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
531 {
532 state = new AutoBackupModuleState();
533 }
534
535 if (state != null)
536 {
537 state.BackupDir = tmpBackupDir;
538 // Let's give the user some convenience and auto-mkdir
539 if (state.BackupDir != ".")
540 { 424 {
541 try 425 DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
426 if (!dirinfo.Exists)
542 { 427 {
543 DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir); 428 dirinfo.Create();
544 if (!dirinfo.Exists)
545 {
546 dirinfo.Create();
547 }
548 }
549 catch (Exception e)
550 {
551 m_log.Warn(
552 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
553 state.BackupDir +
554 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
555 e);
556 } 429 }
557 } 430 }
431 catch (Exception e)
432 {
433 m_log.Warn(
434 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
435 state.BackupDir +
436 " because it doesn't exist or there's a permissions issue with it:",
437 e);
438 }
558 } 439 }
559 440 state.BackupDir = tmpBackupDir;
560 if(state == null)
561 return m_defaultState;
562
563 return state; 441 return state;
564 } 442 }
565 443
@@ -584,128 +462,27 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
584 } 462 }
585 463
586 /// <summary> 464 /// <summary>
587 /// Helper function for ParseConfig.
588 /// </summary>
589 /// <param name="settingName"></param>
590 /// <param name="defaultValue"></param>
591 /// <param name="global"></param>
592 /// <param name="local"></param>
593 /// <returns></returns>
594 private double ResolveDouble(string settingName, double defaultValue, IConfig global, IConfig local)
595 {
596 if (local != null)
597 {
598 return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
599 }
600 else
601 {
602 return global.GetDouble(settingName, defaultValue);
603 }
604 }
605
606 /// <summary>
607 /// Helper function for ParseConfig.
608 /// </summary>
609 /// <param name="settingName"></param>
610 /// <param name="defaultValue"></param>
611 /// <param name="global"></param>
612 /// <param name="local"></param>
613 /// <returns></returns>
614 private int ResolveInt(string settingName, int defaultValue, IConfig global, IConfig local)
615 {
616 if (local != null)
617 {
618 return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
619 }
620 else
621 {
622 return global.GetInt(settingName, defaultValue);
623 }
624 }
625
626 /// <summary>
627 /// Helper function for ParseConfig.
628 /// </summary>
629 /// <param name="settingName"></param>
630 /// <param name="defaultValue"></param>
631 /// <param name="global"></param>
632 /// <param name="local"></param>
633 /// <returns></returns>
634 private string ResolveString(string settingName, string defaultValue, IConfig global, IConfig local)
635 {
636 if (local != null)
637 {
638 return local.GetString(settingName, global.GetString(settingName, defaultValue));
639 }
640 else
641 {
642 return global.GetString(settingName, defaultValue);
643 }
644 }
645
646 /// <summary>
647 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup. 465 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
648 /// </summary> 466 /// </summary>
649 /// <param name="sender"></param> 467 /// <param name="sender"></param>
650 /// <param name="e"></param> 468 /// <param name="e"></param>
651 private void HandleElapsed(object sender, ElapsedEventArgs e) 469 private void HandleElapsed(object sender, ElapsedEventArgs e)
652 { 470 {
653 // TODO: heuristic thresholds are per-region, so we should probably run heuristics once per region 471 if (!m_enabled || m_busy)
654 // XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
655 // check whether the region is too busy! Especially on sims with LOTS of regions.
656 // Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
657 // but would allow us to be semantically correct while being easier on perf.
658 // Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
659 // Alternative 3: Don't support per-region heuristics at all; just accept them as a global only parameter.
660 // Since this is pretty experimental, I haven't decided which alternative makes the most sense.
661 if (this.m_closed)
662 {
663 return; 472 return;
664 } 473
665 bool heuristicsRun = false; 474 m_busy = true;
666 bool heuristicsPassed = false; 475 foreach (IScene scene in m_Scenes)
667 if (!this.m_timerMap.ContainsKey((Timer) sender))
668 { 476 {
669 m_log.Debug("[AUTO BACKUP]: Code-up error: timerMap doesn't contain timer " + sender); 477 if (!m_enabled)
478 return;
479 DoRegionBackup(scene);
670 } 480 }
671 481
672 List<IScene> tmap = this.m_timerMap[(Timer) sender]; 482 if (m_enabled)
673 if (tmap != null && tmap.Count > 0)
674 { 483 {
675 foreach (IScene scene in tmap) 484 m_masterTimer.Start();
676 { 485 m_busy = false;
677 AutoBackupModuleState state = this.m_states[scene];
678 bool heuristics = state.BusyCheck;
679
680 // Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
681 if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
682 {
683 this.DoRegionBackup(scene);
684 // Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
685 }
686 else if (heuristicsRun)
687 {
688 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
689 scene.RegionInfo.RegionName + " right now.");
690 continue;
691 // Logical Deduction: heuristics are on but haven't been run
692 }
693 else
694 {
695 heuristicsPassed = this.RunHeuristics(scene);
696 heuristicsRun = true;
697 if (!heuristicsPassed)
698 {
699 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
700 scene.RegionInfo.RegionName + " right now.");
701 continue;
702 }
703 this.DoRegionBackup(scene);
704 }
705
706 // Remove Old Backups
707 this.RemoveOldFiles(state);
708 }
709 } 486 }
710 } 487 }
711 488
@@ -723,8 +500,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
723 return; 500 return;
724 } 501 }
725 502
726 AutoBackupModuleState state = this.m_states[scene]; 503 m_busy = true;
504
505 AutoBackupModuleState state;
506 if(!m_states.TryGetValue(scene, out state))
507 return;
508
509 if(state == null || !state.Enabled)
510 return;
511
727 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>(); 512 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>();
513 if(iram == null)
514 return;
515
728 string savePath = BuildOarPath(scene.RegionInfo.RegionName, 516 string savePath = BuildOarPath(scene.RegionInfo.RegionName,
729 state.BackupDir, 517 state.BackupDir,
730 state.NamingType); 518 state.NamingType);
@@ -733,11 +521,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
733 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed"); 521 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
734 return; 522 return;
735 } 523 }
736 Guid guid = Guid.NewGuid();
737 m_pendingSaves.Add(guid, scene);
738 state.LiveRequests.Add(guid, savePath);
739 ((Scene) scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
740 524
525 Guid guid = Guid.NewGuid();
741 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName); 526 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName);
742 527
743 // Must pass options, even if dictionary is empty! 528 // Must pass options, even if dictionary is empty!
@@ -747,6 +532,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
747 options["noassets"] = true; 532 options["noassets"] = true;
748 533
749 iram.ArchiveRegion(savePath, guid, options); 534 iram.ArchiveRegion(savePath, guid, options);
535 ExecuteScript(state.Script, savePath);
750 } 536 }
751 537
752 // For the given state, remove backup files older than the states KeepFilesForDays property 538 // For the given state, remove backup files older than the states KeepFilesForDays property
@@ -774,23 +560,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
774 } 560 }
775 } 561 }
776 562
777 /// <summary>
778 /// Called by the Event Manager when the OnOarFileSaved event is fired.
779 /// </summary>
780 /// <param name="guid"></param>
781 /// <param name="message"></param>
782 void EventManager_OnOarFileSaved(Guid guid, string message)
783 {
784 // Ignore if the OAR save is being done by some other part of the system
785 if (m_pendingSaves.ContainsKey(guid))
786 {
787 AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
788 ExecuteScript(abms.Script, abms.LiveRequests[guid]);
789 m_pendingSaves.Remove(guid);
790 abms.LiveRequests.Remove(guid);
791 }
792 }
793
794 /// <summary>This format may turn out to be too unwieldy to keep... 563 /// <summary>This format may turn out to be too unwieldy to keep...
795 /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID? 564 /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
796 /// Sequential numbers, right? We support those, too!</summary> 565 /// Sequential numbers, right? We support those, too!</summary>
@@ -817,63 +586,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
817 return output; 586 return output;
818 } 587 }
819 588
820 /// <summary>Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.</summary>
821 private bool RunHeuristics(IScene region)
822 {
823 try
824 {
825 return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
826 }
827 catch (Exception e)
828 {
829 m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
830 return false;
831 }
832 }
833
834 /// <summary>
835 /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
836 /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
837 /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
838 /// </summary>
839 /// <param name="region"></param>
840 /// <returns>Returns true if we're not too busy; false means we've got worse time dilation than the threshold.</returns>
841 private bool RunTimeDilationHeuristic(IScene region)
842 {
843 string regionName = region.RegionInfo.RegionName;
844 return region.TimeDilation >=
845 this.m_configSource.Configs["AutoBackupModule"].GetFloat(
846 regionName + ".AutoBackupDilationThreshold", 0.5f);
847 }
848
849 /// <summary>
850 /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
851 /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
852 /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
853 /// </summary>
854 /// <param name="region"></param>
855 /// <returns>Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.</returns>
856 private bool RunAgentLimitHeuristic(IScene region)
857 {
858 string regionName = region.RegionInfo.RegionName;
859 try
860 {
861 Scene scene = (Scene) region;
862 // TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
863 return scene.GetRootAgentCount() <=
864 this.m_configSource.Configs["AutoBackupModule"].GetInt(
865 regionName + ".AutoBackupAgentThreshold", 10);
866 }
867 catch (InvalidCastException ice)
868 {
869 m_log.Debug(
870 "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
871 ice);
872 return true;
873 // Non-obstructionist safest answer...
874 }
875 }
876
877 /// <summary> 589 /// <summary>
878 /// Run the script or executable specified by the "AutoBackupScript" config setting. 590 /// Run the script or executable specified by the "AutoBackupScript" config setting.
879 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script. 591 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
@@ -920,18 +632,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
920 } 632 }
921 633
922 /// <summary> 634 /// <summary>
923 /// Quickly stop all timers from firing.
924 /// </summary>
925 private void StopAllTimers()
926 {
927 foreach (Timer t in this.m_timerMap.Keys)
928 {
929 t.Close();
930 }
931 this.m_closed = true;
932 }
933
934 /// <summary>
935 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType. 635 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
936 /// </summary> 636 /// </summary>
937 /// <param name="dirName"></param> 637 /// <param name="dirName"></param>
@@ -1033,5 +733,3 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
1033 } 733 }
1034 } 734 }
1035} 735}
1036
1037
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
index b90f0c4..be5f2ae 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
@@ -38,26 +38,24 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
38 /// 38 ///
39 public class AutoBackupModuleState 39 public class AutoBackupModuleState
40 { 40 {
41 private Dictionary<Guid, string> m_liveRequests = null;
42
43 public AutoBackupModuleState() 41 public AutoBackupModuleState()
44 { 42 {
45 this.Enabled = false; 43 Enabled = false;
46 this.BackupDir = "."; 44 BackupDir = ".";
47 this.BusyCheck = true; 45 SkipAssets = false;
48 this.SkipAssets = false; 46 NamingType = NamingType.Time;
49 this.Timer = null; 47 Script = null;
50 this.NamingType = NamingType.Time; 48 KeepFilesForDays = 0;
51 this.Script = null;
52 this.KeepFilesForDays = 0;
53 } 49 }
54 50
55 public Dictionary<Guid, string> LiveRequests 51 public AutoBackupModuleState(AutoBackupModuleState copyFrom)
56 { 52 {
57 get { 53 Enabled = copyFrom.Enabled;
58 return this.m_liveRequests ?? 54 BackupDir = copyFrom.BackupDir;
59 (this.m_liveRequests = new Dictionary<Guid, string>(1)); 55 SkipAssets = copyFrom.SkipAssets;
60 } 56 NamingType = copyFrom.NamingType;
57 Script = copyFrom.Script;
58 KeepFilesForDays = copyFrom.KeepFilesForDays;
61 } 59 }
62 60
63 public bool Enabled 61 public bool Enabled
@@ -66,33 +64,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
66 set; 64 set;
67 } 65 }
68 66
69 public System.Timers.Timer Timer
70 {
71 get;
72 set;
73 }
74
75 public double IntervalMinutes
76 {
77 get
78 {
79 if (this.Timer == null)
80 {
81 return -1.0;
82 }
83 else
84 {
85 return this.Timer.Interval / 60000.0;
86 }
87 }
88 }
89
90 public bool BusyCheck
91 {
92 get;
93 set;
94 }
95
96 public bool SkipAssets 67 public bool SkipAssets
97 { 68 {
98 get; 69 get;
@@ -126,10 +97,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
126 public new string ToString() 97 public new string ToString()
127 { 98 {
128 string retval = ""; 99 string retval = "";
129
130 retval += "[AUTO BACKUP]: AutoBackup: " + (Enabled ? "ENABLED" : "DISABLED") + "\n"; 100 retval += "[AUTO BACKUP]: AutoBackup: " + (Enabled ? "ENABLED" : "DISABLED") + "\n";
131 retval += "[AUTO BACKUP]: Interval: " + IntervalMinutes + " minutes" + "\n";
132 retval += "[AUTO BACKUP]: Do Busy Check: " + (BusyCheck ? "Yes" : "No") + "\n";
133 retval += "[AUTO BACKUP]: Naming Type: " + NamingType.ToString() + "\n"; 101 retval += "[AUTO BACKUP]: Naming Type: " + NamingType.ToString() + "\n";
134 retval += "[AUTO BACKUP]: Backup Dir: " + BackupDir + "\n"; 102 retval += "[AUTO BACKUP]: Backup Dir: " + BackupDir + "\n";
135 retval += "[AUTO BACKUP]: Script: " + Script + "\n"; 103 retval += "[AUTO BACKUP]: Script: " + Script + "\n";
@@ -137,4 +105,3 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
137 } 105 }
138 } 106 }
139} 107}
140