-
Niko Ehrenfeuchter authoredNiko Ehrenfeuchter authored
ServiceConfig.cs 12.25 KiB
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;
using NLog;
using NLog.Config;
namespace ATXCommon.Serializables
{
/// <summary>
/// AutoTx service configuration class.
/// </summary>
[Serializable]
public class ServiceConfig
{
[XmlIgnore] private static readonly Logger Log = LogManager.GetCurrentClassLogger();
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.");
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;
}
}
}