Added schedules to the RemoteSynchronizationModule
This commit is contained in:
@@ -21,14 +21,35 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Duplicati.Library.Interface;
|
||||
using Duplicati.Library.SQLiteHelper;
|
||||
using Duplicati.Library.Utility;
|
||||
using RemoteSynchronization;
|
||||
|
||||
namespace Duplicati.Library.Modules.Builtin;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger modes for remote synchronization.
|
||||
/// </summary>
|
||||
public enum RemoteSyncTriggerMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Trigger after every successful backup.
|
||||
/// </summary>
|
||||
Inline,
|
||||
/// <summary>
|
||||
/// Trigger based on a schedule.
|
||||
/// </summary>
|
||||
Scheduled,
|
||||
/// <summary>
|
||||
/// Trigger after a certain number of backups.
|
||||
/// </summary>
|
||||
Counting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Module for synchronizing backup data to a remote destination after a successful backup operation.
|
||||
/// </summary>
|
||||
@@ -46,12 +67,18 @@ public class RemoteSynchronizationModule : IGenericCallbackModule
|
||||
private const string OPTION_RETENTION = "remote-sync-retention";
|
||||
private const string OPTION_BACKEND_RETRIES = "remote-sync-backend-retries";
|
||||
private const string OPTION_RETRY = "remote-sync-retry";
|
||||
private const string OPTION_MODE = "remote-sync-mode";
|
||||
private const string OPTION_SCHEDULE = "remote-sync-schedule";
|
||||
private const string OPTION_COUNT = "remote-sync-count";
|
||||
|
||||
private IReadOnlyDictionary<string, string> m_options = new Dictionary<string, string>();
|
||||
private string m_source;
|
||||
private string m_destination;
|
||||
private string m_operationName;
|
||||
private bool m_enabled;
|
||||
private RemoteSyncTriggerMode m_mode = RemoteSyncTriggerMode.Inline;
|
||||
private TimeSpan m_schedule = TimeSpan.Zero;
|
||||
private int m_count = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key identifier for this module.
|
||||
@@ -80,6 +107,9 @@ public class RemoteSynchronizationModule : IGenericCallbackModule
|
||||
new CommandLineArgument(OPTION_RETENTION, CommandLineArgument.ArgumentType.Boolean, Strings.RemoteSynchronization.RetentionShort, Strings.RemoteSynchronization.RetentionLong, "false"),
|
||||
new CommandLineArgument(OPTION_BACKEND_RETRIES, CommandLineArgument.ArgumentType.Integer, Strings.RemoteSynchronization.BackendRetriesShort, Strings.RemoteSynchronization.BackendRetriesLong, "3"),
|
||||
new CommandLineArgument(OPTION_RETRY, CommandLineArgument.ArgumentType.Integer, Strings.RemoteSynchronization.RetryShort, Strings.RemoteSynchronization.RetryLong, "3"),
|
||||
new CommandLineArgument(OPTION_MODE, CommandLineArgument.ArgumentType.Enumeration, Strings.RemoteSynchronization.ModeShort, Strings.RemoteSynchronization.ModeLong, "inline", null, ["inline", "scheduled", "counting"]),
|
||||
new CommandLineArgument(OPTION_SCHEDULE, CommandLineArgument.ArgumentType.Timespan, Strings.RemoteSynchronization.ScheduleShort, Strings.RemoteSynchronization.ScheduleLong),
|
||||
new CommandLineArgument(OPTION_COUNT, CommandLineArgument.ArgumentType.Integer, Strings.RemoteSynchronization.CountShort, Strings.RemoteSynchronization.CountLong),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
@@ -91,6 +121,21 @@ public class RemoteSynchronizationModule : IGenericCallbackModule
|
||||
m_options = commandlineOptions.AsReadOnly();
|
||||
commandlineOptions.TryGetValue(OPTION_BACKEND_DST, out m_destination);
|
||||
m_enabled = !string.IsNullOrWhiteSpace(m_destination);
|
||||
|
||||
if (commandlineOptions.TryGetValue(OPTION_MODE, out var modeStr) && !string.IsNullOrWhiteSpace(modeStr))
|
||||
{
|
||||
if (Enum.TryParse<RemoteSyncTriggerMode>(modeStr, true, out var mode))
|
||||
m_mode = mode;
|
||||
}
|
||||
|
||||
if (commandlineOptions.TryGetValue(OPTION_SCHEDULE, out var scheduleStr) && !string.IsNullOrWhiteSpace(scheduleStr))
|
||||
{
|
||||
if (TimeSpan.TryParse(scheduleStr, out var schedule))
|
||||
m_schedule = schedule;
|
||||
}
|
||||
|
||||
if (commandlineOptions.TryGetValue(OPTION_COUNT, out var countStr) && int.TryParse(countStr, out var count))
|
||||
m_count = count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,6 +186,14 @@ public class RemoteSynchronizationModule : IGenericCallbackModule
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldTriggerSync())
|
||||
{
|
||||
Logging.Log.WriteInformationMessage(LOGTAG, "RemoteSyncSkipped", "Remote synchronization skipped due to trigger mode conditions not met.");
|
||||
return;
|
||||
}
|
||||
|
||||
RecordSyncOperation();
|
||||
|
||||
var args = BuildArguments();
|
||||
try
|
||||
{
|
||||
@@ -154,6 +207,62 @@ public class RemoteSynchronizationModule : IGenericCallbackModule
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if remote synchronization should be triggered based on the configured mode.
|
||||
/// </summary>
|
||||
/// <returns>True if synchronization should be triggered.</returns>
|
||||
private bool ShouldTriggerSync()
|
||||
{
|
||||
if (!m_options.TryGetValue("dbpath", out var dbpath) || string.IsNullOrWhiteSpace(dbpath))
|
||||
return false;
|
||||
|
||||
using var db = SQLiteLoader.LoadConnection(dbpath);
|
||||
using var cmd = db.CreateCommand();
|
||||
|
||||
switch (m_mode)
|
||||
{
|
||||
case RemoteSyncTriggerMode.Inline:
|
||||
return true;
|
||||
case RemoteSyncTriggerMode.Scheduled:
|
||||
// Find last remote sync operation
|
||||
cmd.CommandText = "SELECT Timestamp FROM Operation WHERE Description = 'Remote Synchronization' ORDER BY Timestamp DESC LIMIT 1";
|
||||
var lastSync = cmd.ExecuteScalar();
|
||||
if (lastSync == null)
|
||||
return true; // No previous sync, trigger
|
||||
var lastSyncTime = DateTime.FromFileTimeUtc((long)lastSync);
|
||||
var now = DateTime.UtcNow;
|
||||
return (now - lastSyncTime) >= m_schedule;
|
||||
case RemoteSyncTriggerMode.Counting:
|
||||
// Count backup operations since last sync
|
||||
cmd.CommandText = @"
|
||||
SELECT COUNT(*) FROM Operation
|
||||
WHERE Description = 'Backup'
|
||||
AND Timestamp > COALESCE(
|
||||
(SELECT Timestamp FROM Operation WHERE Description = 'Remote Synchronization' ORDER BY Timestamp DESC LIMIT 1),
|
||||
0
|
||||
)";
|
||||
var count = (long)cmd.ExecuteScalar();
|
||||
return count >= m_count;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records a remote synchronization operation in the database.
|
||||
/// </summary>
|
||||
private void RecordSyncOperation()
|
||||
{
|
||||
if (!m_options.TryGetValue("dbpath", out var dbpath) || string.IsNullOrWhiteSpace(dbpath))
|
||||
return;
|
||||
|
||||
using var db = SQLiteLoader.LoadConnection(dbpath);
|
||||
using var cmd = db.CreateCommand();
|
||||
cmd.CommandText = "INSERT INTO Operation (Description, Timestamp) VALUES ('Remote Synchronization', @timestamp)";
|
||||
cmd.Parameters.AddWithValue("@timestamp", DateTime.UtcNow.ToFileTimeUtc());
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the arguments for the remote synchronization command.
|
||||
/// </summary>
|
||||
|
||||
@@ -97,6 +97,12 @@ namespace Duplicati.Library.Modules.Builtin.Strings
|
||||
public static string VerifyContentsLong { get { return LC.L(@"Verify file contents before overwriting destination files."); } }
|
||||
public static string VerifyGetAfterPutShort { get { return LC.L(@"Verify after upload"); } }
|
||||
public static string VerifyGetAfterPutLong { get { return LC.L(@"Verify files after uploading them."); } }
|
||||
public static string ModeShort { get { return LC.L(@"Remote sync trigger mode"); } }
|
||||
public static string ModeLong { get { return LC.L(@"Specify the trigger mode for remote synchronization: inline (default, after every backup), scheduled (after schedule has passed), counting (after N backups)."); } }
|
||||
public static string ScheduleShort { get { return LC.L(@"Remote sync schedule"); } }
|
||||
public static string ScheduleLong { get { return LC.L(@"Specify the schedule interval for remote synchronization, e.g. '7.00:00:00' for weekly."); } }
|
||||
public static string CountShort { get { return LC.L(@"Remote sync backup count"); } }
|
||||
public static string CountLong { get { return LC.L(@"Specify the number of backups after which to trigger remote synchronization."); } }
|
||||
}
|
||||
internal static class RunScript
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user