Newer
Older
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;
namespace ATXCommon.Serializables
/// AutoTx service configuration class.
/// </summary>
[Serializable]
public class ServiceConfig
{
[XmlIgnore] private static readonly Logger Log = LogManager.GetCurrentClassLogger();
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public ServiceConfig() {
// set values for the optional XML elements:
SmtpHost = "";
SmtpPort = 25;
SmtpUserCredential = "";
SmtpPasswortCredential = "";
EmailPrefix = "";
AdminEmailAdress = "";
AdminDebugEmailAdress = "";
GraceNotificationDelta = 720;
InterPacketGap = 0;
EnforceInheritedACLs = true;
}
#region required configuration parameters
/// <summary>
/// A human friendly name for the host, to be used in emails etc.
/// </summary>
public string HostAlias { get; set; }
/// <summary>
/// A human friendly name for the target, to be used in emails etc.
/// </summary>
public string DestinationAlias { get; set; }
/// <summary>
/// The base drive for the spooling directories (incoming and managed).
/// </summary>
public string SourceDrive { get; set; }
/// <summary>
/// The name of a directory on SourceDrive that is monitored for new files.
/// </summary>
public string IncomingDirectory { get; set; }
/// <summary>
/// The name of a marker file to be placed in all **sub**directories
/// inside the IncomingDirectory.
/// </summary>
public string MarkerFile { get; set; }
/// <summary>
/// A directory on SourceDrive to hold the three subdirectories "DONE",
/// "PROCESSING" and "UNMATCHED" used during and after transfers.
/// </summary>
public string ManagedDirectory { get; set; }
/// <summary>
/// Target path to transfer files to. Usually a UNC location.
/// </summary>
public string DestinationDirectory { get; set; }
/// <summary>
/// The name of a subdirectory in the DestinationDirectory to be used
/// to keep the temporary data of running transfers.
/// </summary>
public string TmpTransferDir { get; set; }
/// <summary>
/// The email address to be used as "From:" when sending mail notifications.
/// </summary>
public string EmailFrom { get; set; }
/// <summary>
/// The interval (in ms) for checking for new files and system parameters.
/// </summary>
public int ServiceTimer { get; set; }
/// <summary>
/// Maximum allowed CPU usage across all cores in percent. Running transfers will be paused
/// if this limit is exceeded.
/// </summary>
public int MaxCpuUsage { get; set; }
/// <summary>
/// Minimum amount of free RAM (in MB) required for the service to operate.
/// </summary>
public int MinAvailableMemory { get; set; }
/// <summary>
/// Minimum amount of time in minutes between two mail notifications to the admin address.
/// </summary>
public int AdminNotificationDelta { get; set; }
/// <summary>
/// Minimum amount of time in minutes between two low-storage-space notifications.
/// </summary>
public int StorageNotificationDelta { get; set; }
/// <summary>
/// GracePeriod: number of days after data in the "DONE" location expires,
/// which will trigger a summary email to the admin address.
/// </summary>
public int GracePeriod { get; set; }
/// <summary>
/// Flag whether to send explicit mail notifications to the admin on selected events.
/// </summary>
public bool SendAdminNotification { get; set; }
/// <summary>
/// Flag whether to send a mail notification to the user upon completed transfers.
/// </summary>
public bool SendTransferNotification { get; set; }
/// <summary>
/// Switch on debug log messages.
/// </summary>
public bool Debug { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "DriveToCheck")]
public List<DriveToCheck> SpaceMonitoring { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "ProcessName")]
public List<string> BlacklistedProcesses { get; set; }
#endregion
#region optional configuration parameters
/// <summary>
/// SMTP server used to send mails (if configured) and Fatal/Error log messages.
///
/// No mails will be sent if this is omitted.
/// </summary>
public string SmtpHost { get; set; }
/// <summary>
/// SMTP username to authenticate when sending emails (if required).
/// </summary>
public string SmtpUserCredential { get; set; }
/// <summary>
/// SMTP password to authenticate when sending emails (if required).
/// </summary>
public string SmtpPasswortCredential { get; set; }
/// <summary>
/// SMTP port for sending emails (25 will be used if this entry is omitted).
/// </summary>
public int SmtpPort { get; set; }
/// <summary>
/// A string to be added as a prefix to the subject when sending emails.
/// </summary>
public string EmailPrefix { get; set; }
/// <summary>
/// The mail recipient address for admin notifications (including "Fatal" log messages).
/// </summary>
public string AdminEmailAdress { get; set; }
/// <summary>
/// The mail recipient address for debug notifications (including "Error" log messages).
/// </summary>
public string AdminDebugEmailAdress { get; set; }
/// <summary>
/// Minimum time in minutes between two mails about expired folders in the grace location.
/// </summary>
public int GraceNotificationDelta { get; set; }
/// <summary>
/// RoboCopy parameter for limiting the bandwidth (mostly for testing purposes).
/// </summary>
/// See the RoboCopy documentation for more details.
public int InterPacketGap { get; set; }
/// <summary>
/// EnforceInheritedACLs: whether to enforce ACL inheritance when moving files and
/// directories, see https://support.microsoft.com/en-us/help/320246 for more details.
/// </summary>
public bool EnforceInheritedACLs { get; set; }
#endregion
public static void Serialize(string file, ServiceConfig c) {
// the config is never meant to be written by us, therefore:
throw new SettingsPropertyIsReadOnlyException("The config file must not be written by the service!");
}
public static ServiceConfig Deserialize(string file) {
Log.Debug("Trying to read service configuration XML file: [{0}]", file);
var xs = new XmlSerializer(typeof(ServiceConfig));
var reader = File.OpenText(file);
var config = (ServiceConfig) xs.Deserialize(reader);
reader.Close();
ValidateConfiguration(config);
Log.Debug("Finished deserializing service configuration XML file.");
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
return config;
}
private static void ValidateConfiguration(ServiceConfig c) {
if (string.IsNullOrEmpty(c.SourceDrive) ||
string.IsNullOrEmpty(c.IncomingDirectory) ||
string.IsNullOrEmpty(c.ManagedDirectory))
throw new ConfigurationErrorsException("mandatory parameter missing!");
if (c.SourceDrive.Substring(1) != @":\")
throw new ConfigurationErrorsException("SourceDrive must be a drive " +
@"letter followed by a colon and a backslash, e.g. 'D:\'!");
// make sure SourceDrive is a local (fixed) disk:
var driveInfo = new DriveInfo(c.SourceDrive);
if (driveInfo.DriveType != DriveType.Fixed)
throw new ConfigurationErrorsException("SourceDrive (" + c.SourceDrive +
") must be a local (fixed) drive, OS reports '" +
driveInfo.DriveType + "')!");
// spooling directories: IncomingDirectory + ManagedDirectory
if (c.IncomingDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("IncomingDirectory must not start with a backslash!");
if (c.ManagedDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("ManagedDirectory must not start with a backslash!");
if (!Directory.Exists(c.DestinationDirectory))
throw new ConfigurationErrorsException("can't find destination: " + c.DestinationDirectory);
var tmpTransferPath = Path.Combine(c.DestinationDirectory, c.TmpTransferDir);
if (!Directory.Exists(tmpTransferPath))
throw new ConfigurationErrorsException("temporary transfer dir doesn't exist: " + tmpTransferPath);
if (c.ServiceTimer < 1000)
throw new ConfigurationErrorsException("ServiceTimer must not be smaller than 1000 ms!");
// NON-CRITICAL stuff is simply reported to the logs:
if (!c.DestinationDirectory.StartsWith(@"\\")) {
ReportNonOptimal("DestinationDirectory", c.DestinationDirectory, "is not a UNC path!");
/// <summary>
/// Print a standardized msg about a non-optimal configuration setting to the log.
/// </summary>
private static void ReportNonOptimal(string attribute, string value, string msg) {
Log.Warn(">>> Non-optimal setting detected: <{0}> [{1}] {2}", attribute, value, msg);
}
/// <summary>
/// Generate a human-readable sumary of the current configuration.
/// </summary>
/// <returns>A string with details on the configuration.</returns>
public string Summary() {
var msg =
"HostAlias: " + HostAlias + "\n" +
"SourceDrive: " + SourceDrive + "\n" +
"IncomingDirectory: " + IncomingDirectory + "\n" +
"MarkerFile: " + MarkerFile + "\n" +
"ManagedDirectory: " + ManagedDirectory + "\n" +
"GracePeriod: " + GracePeriod + "\n" +
"DestinationDirectory: " + DestinationDirectory + "\n" +
"TmpTransferDir: " + TmpTransferDir + "\n" +
"EnforceInheritedACLs: " + EnforceInheritedACLs + "\n" +
"ServiceTimer: " + ServiceTimer + "\n" +
"InterPacketGap: " + InterPacketGap + "\n" +
"MaxCpuUsage: " + MaxCpuUsage + "\n" +
"MinAvailableMemory: " + MinAvailableMemory + "\n";
foreach (var processName in BlacklistedProcesses) {
msg += "BlacklistedProcess: " + processName + "\n";
}
foreach (var driveToCheck in SpaceMonitoring) {
msg += "Drive to check free space: " + driveToCheck.DriveName +
" (threshold: " +
Conv.BytesToString(driveToCheck.SpaceThreshold * Conv.MegaBytes) +
")" + "\n";
}
if (string.IsNullOrEmpty(SmtpHost)) {
msg += "SmtpHost: ====== Not configured, disabling email! ======" + "\n";
} else {
msg +=
"SmtpHost: " + SmtpHost + "\n" +
"EmailFrom: " + EmailFrom + "\n" +
"AdminEmailAdress: " + AdminEmailAdress + "\n" +
"AdminDebugEmailAdress: " + AdminDebugEmailAdress + "\n" +
"StorageNotificationDelta: " + StorageNotificationDelta + "\n" +
"AdminNotificationDelta: " + AdminNotificationDelta + "\n" +
"GraceNotificationDelta: " + GraceNotificationDelta + "\n";
}
return msg;
}
}
}