diff --git a/src/.vs/Lithnet.Miiserver.AutoSync/v15/sqlite3/storage.ide b/src/.vs/Lithnet.Miiserver.AutoSync/v15/sqlite3/storage.ide index 425a076..b05ba5d 100644 Binary files a/src/.vs/Lithnet.Miiserver.AutoSync/v15/sqlite3/storage.ide and b/src/.vs/Lithnet.Miiserver.AutoSync/v15/sqlite3/storage.ide differ diff --git a/src/Lithnet.Miiserver.AutoSync/Lithnet.Miiserver.AutoSync.csproj b/src/Lithnet.Miiserver.AutoSync/Lithnet.Miiserver.AutoSync.csproj index 53e6f4c..87c2940 100644 --- a/src/Lithnet.Miiserver.AutoSync/Lithnet.Miiserver.AutoSync.csproj +++ b/src/Lithnet.Miiserver.AutoSync/Lithnet.Miiserver.AutoSync.csproj @@ -57,8 +57,8 @@ ..\packages\Lithnet.Logging.1.0.5774.20685\lib\net40\Lithnet.Logging.dll True - - ..\packages\Lithnet.Miiserver.Client.1.0.6434.38666\lib\net45\Lithnet.Miiserver.Client.dll + + ..\packages\Lithnet.Miiserver.Client.1.0.6450.37102\lib\net45\Lithnet.Miiserver.Client.dll ..\packages\Lithnet.ResourceManagement.Client.1.0.6297.14599\lib\net40\Lithnet.ResourceManagement.Client.dll @@ -186,6 +186,9 @@ + + + diff --git a/src/Lithnet.Miiserver.AutoSync/MAInterface/MAExecutor.cs b/src/Lithnet.Miiserver.AutoSync/MAInterface/MAExecutor.cs index 41f0ea1..18691c2 100644 --- a/src/Lithnet.Miiserver.AutoSync/MAInterface/MAExecutor.cs +++ b/src/Lithnet.Miiserver.AutoSync/MAInterface/MAExecutor.cs @@ -251,7 +251,7 @@ private void StartTriggers() t.Message += this.NotifierTriggerMessage; t.Error += this.NotifierTriggerError; t.TriggerFired += this.NotifierTriggerFired; - t.Start(); + t.Start(this.ManagementAgentName); } catch (Exception ex) { diff --git a/src/Lithnet.Miiserver.AutoSync/Mail/MessageBuilder.cs b/src/Lithnet.Miiserver.AutoSync/Mail/MessageBuilder.cs index dbbc51c..d3c9088 100644 --- a/src/Lithnet.Miiserver.AutoSync/Mail/MessageBuilder.cs +++ b/src/Lithnet.Miiserver.AutoSync/Mail/MessageBuilder.cs @@ -35,6 +35,16 @@ private static string GetTemplate(string name) } } + public static string GetMessageBody(string maName, string triggerType, string triggerDetails, DateTime errorTime, bool hasTerminated, Exception ex) + { + StringBuilder builder = new StringBuilder(); + builder.AppendFormat(MessageBuilder.GetTemplate("TriggerErrorFragment"), maName, triggerType, triggerDetails, errorTime, hasTerminated ? "Yes" : "No", ex); + + InlineResult result = PreMailer.Net.PreMailer.MoveCssInline(MessageBuilder.GetTemplate("EmailTemplate").Replace("%BODY%", builder.ToString())); + + return result.Html; + } + public static string GetMessageBody(RunDetails r) { StringBuilder builder = new StringBuilder(); @@ -64,6 +74,13 @@ public static string GetMessageBody(RunDetails r) builder.AppendLine(exportErrors); } + string syncErrors = MessageBuilder.BuildSyncErrorDetails(r.StepDetails); + + if (syncErrors != null) + { + builder.AppendLine(syncErrors); + } + InlineResult result = PreMailer.Net.PreMailer.MoveCssInline(MessageBuilder.GetTemplate("EmailTemplate").Replace("%BODY%", builder.ToString())); @@ -198,6 +215,30 @@ private static string BuildExportErrorDetails(IReadOnlyList details } } + private static string BuildSyncErrorDetails(IReadOnlyList details) + { + StringBuilder b = new StringBuilder(); + + foreach (StepDetails d in details) + { + string result = BuildSyncErrorDetails(d); + + if (result != null) + { + b.AppendLine(result); + } + } + + if (b.Length == 0) + { + return null; + } + else + { + return b.ToString(); + } + } + private static string BuildStagingErrorDetails(StepDetails d) { if (d.MADiscoveryErrors.Count == 0) @@ -290,7 +331,7 @@ private static string BuildImportErrorDetails(StepDetails d) StringBuilder errorsBuilder = new StringBuilder(); - errorsBuilder.AppendLine("

Synchronization errors

"); + errorsBuilder.AppendLine("

Inbound synchronization errors

"); foreach (ImportError error in errors) { @@ -301,13 +342,8 @@ private static string BuildImportErrorDetails(StepDetails d) errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Date occurred", error.DateOccurred); errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Retry count", error.RetryCount); - if (error.ExtensionErrorInfo != null) - { - errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Extension name", error.ExtensionErrorInfo.ExtensionName); - errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Context", error.ExtensionErrorInfo.ExtensionContext); - errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Call site", error.ExtensionErrorInfo.ExtensionCallSite); - errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Stack trace", error.ExtensionErrorInfo.CallStack); - } + MessageBuilder.BuildErrorExtensionInfo(error.ExtensionErrorInfo, errorBuilder); + MessageBuilder.BuildRulesErrorInfo(error.RulesErrorInfo, errorBuilder); errorsBuilder.AppendLine(string.Format(MessageBuilder.GetTemplate("ErrorTableFragment"), errorBuilder)); errorsBuilder.AppendLine("
"); @@ -404,5 +440,81 @@ private static string BuildSyncStepDetails(StepDetails d) return builder.ToString(); } + + private static string BuildSyncErrorDetails(StepDetails d) + { + if (d.MVRetryErrors == null || d.MVRetryErrors.Count == 0) + { + return null; + } + + IEnumerable errors; + int remainingErrors = 0; + + if (Program.ActiveConfig.Settings.MailMaxErrors <= 0 || d.MVRetryErrors.Count <= Program.ActiveConfig.Settings.MailMaxErrors) + { + errors = d.MVRetryErrors; + } + else + { + errors = d.MVRetryErrors.Take(Program.ActiveConfig.Settings.MailMaxErrors); + remainingErrors = d.MVRetryErrors.Count - Program.ActiveConfig.Settings.MailMaxErrors; + } + + StringBuilder errorsBuilder = new StringBuilder(); + + errorsBuilder.AppendLine("

Outbound synchronization errors

"); + + foreach (MVRetryError error in errors) + { + StringBuilder errorBuilder = new StringBuilder(); + + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Object", error.DisplayName ?? error.MVID); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Step", error.AlgorithmStep?.Value); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Error type", error.ErrorType); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Date occurred", error.DateOccurred); + + MessageBuilder.BuildErrorExtensionInfo(error.ExtensionErrorInfo, errorBuilder); + MessageBuilder.BuildRulesErrorInfo(error.RulesErrorInfo, errorBuilder); + + errorsBuilder.AppendLine(string.Format(MessageBuilder.GetTemplate("ErrorTableFragment"), errorBuilder)); + errorsBuilder.AppendLine("
"); + } + + if (remainingErrors > 0) + { + errorsBuilder.Append($"There are {remainingErrors} more errors that are not shown in this report
"); + } + + return errorsBuilder.ToString(); + } + + private static void BuildRulesErrorInfo(RulesErrorInfoContext c, StringBuilder errorBuilder) + { + if (c != null) + { + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "CS Object ID", c.CSObjectID); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "DN", c.DN); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "MA Name", c.MAName); + + if (c.AttributeFlow != null) + { + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Context", c.AttributeFlow.ContextID); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Destination attribute", c.AttributeFlow.DestinationAttribute); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Flow rule", c.AttributeFlow.FlowRule); + } + } + } + + private static void BuildErrorExtensionInfo(ExtensionErrorInfo error, StringBuilder errorBuilder) + { + if (error != null) + { + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Extension name", error.ExtensionName); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Context", error.ExtensionContext); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Call site", error.ExtensionCallSite); + errorBuilder.AppendFormat(MessageBuilder.SimpleRow, "Call stack", error.CallStack); + } + } } } diff --git a/src/Lithnet.Miiserver.AutoSync/Resources/TriggerErrorFragment.html b/src/Lithnet.Miiserver.AutoSync/Resources/TriggerErrorFragment.html new file mode 100644 index 0000000..8a7b687 --- /dev/null +++ b/src/Lithnet.Miiserver.AutoSync/Resources/TriggerErrorFragment.html @@ -0,0 +1,12 @@ + + + + + + +
Management agent{0}
Trigger type{1}
Detail{2}
Error time{3}
Has been terminated{4}
+
+

Error Details

+
+{5}
+
\ No newline at end of file diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/ActiveDirectoryChangeTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/ActiveDirectoryChangeTrigger.cs index 666977a..f461710 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/ActiveDirectoryChangeTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/ActiveDirectoryChangeTrigger.cs @@ -119,6 +119,13 @@ private void SetupListener() catch (Exception ex) { this.LogError("Could not start the listener", ex); + + if (MessageSender.CanSendMail()) + { + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, true, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); + } + throw; } } @@ -213,16 +220,30 @@ private void Notify(IAsyncResult result) else { this.LogError("The AD change listener encountered an unexpected error", ex); + + if (MessageSender.CanSendMail()) + { + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, false, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); + } } } catch (Exception ex) { this.LogError("The AD change listener encountered an unexpected error", ex); + + if (MessageSender.CanSendMail()) + { + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, false, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); + } } } - public override void Start() + public override void Start(string managementAgentName) { + this.ManagementAgentName = managementAgentName; + if (this.Disabled) { this.Log("AD/LDS change listener disabled"); diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/FimServicePendingImportTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/FimServicePendingImportTrigger.cs index 352e5a2..f93bf7a 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/FimServicePendingImportTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/FimServicePendingImportTrigger.cs @@ -64,11 +64,19 @@ private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) catch (Exception ex) { this.LogError("Change detection failed", ex); + + if (MessageSender.CanSendMail()) + { + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, false, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); + } } } - - public override void Start() + + public override void Start(string managementAgentName) { + this.ManagementAgentName = managementAgentName; + this.checkTimer = new Timer { AutoReset = true, diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/IMAExecutionTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/IMAExecutionTrigger.cs index edbdc3a..5b9dc0f 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/IMAExecutionTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/IMAExecutionTrigger.cs @@ -20,7 +20,7 @@ public interface IMAExecutionTrigger event TriggerMessageEventHandler Error; - void Start(); + void Start(string managementAgentName); void Stop(); } diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/IntervalExecutionTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/IntervalExecutionTrigger.cs index 002227e..0859839 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/IntervalExecutionTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/IntervalExecutionTrigger.cs @@ -27,8 +27,10 @@ private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) this.Fire(this.RunProfileName); } - public override void Start() + public override void Start(string managementAgentName) { + this.ManagementAgentName = managementAgentName; + if (this.RunProfileName == null) { this.LogError("Ignoring interval trigger with no run profile name"); diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/MAExecutionTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/MAExecutionTrigger.cs index b91434a..0755678 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/MAExecutionTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/MAExecutionTrigger.cs @@ -24,7 +24,9 @@ public abstract class MAExecutionTrigger : IMAExecutionTrigger public event TriggerMessageEventHandler Error; - public abstract void Start(); + protected string ManagementAgentName { get; set; } + + public abstract void Start(string managementAgentName); public abstract void Stop(); diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/PowerShellExecutionTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/PowerShellExecutionTrigger.cs index 429c54e..dfad2ce 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/PowerShellExecutionTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/PowerShellExecutionTrigger.cs @@ -36,11 +36,13 @@ public class PowerShellExecutionTrigger : MAExecutionTrigger [DataMember(Name = "interval")] public TimeSpan Interval { get; set; } - public override void Start() + public override void Start(string managementAgentName) { + this.ManagementAgentName = managementAgentName; + if (!System.IO.File.Exists(this.ScriptPath)) { - this.LogError($"Could not start PowerShell trigger as the script '{this.ScriptPath}' could not be found"); + this.LogError($"Could not start PowerShell trigger for MA {managementAgentName} as the script '{this.ScriptPath}' could not be found"); return; } @@ -91,26 +93,19 @@ private void Run() } catch (Exception ex) { - string message; - - if (this.ExceptionBehaviour == ExecutionErrorBehaviour.Terminate) - { - message = $"The PowerShell execution trigger '{this.DisplayName}' encountered an error and has been terminated"; - } - else - { - message = $"The PowerShell execution trigger '{this.DisplayName}' encountered an error"; - } + bool shouldTerminate = this.ExceptionBehaviour == ExecutionErrorBehaviour.Terminate; - this.LogError(message, ex); + this.LogError($"The PowerShell execution trigger '{this.DisplayName}' encountered an error", ex); if (MessageSender.CanSendMail()) { - MessageSender.SendMessage(message, ex.ToString()); + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, shouldTerminate, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); } - if (this.ExceptionBehaviour == ExecutionErrorBehaviour.Terminate) + if (shouldTerminate) { + this.Log("The PowerShell trigger has been terminated as specified by config"); break; } else @@ -152,7 +147,8 @@ private void Run() if (MessageSender.CanSendMail()) { - MessageSender.SendMessage($"The PowerShell execution trigger '{this.DisplayName}' encountered an error and has been terminated", ex.ToString()); + string messageContent = MessageBuilder.GetMessageBody(this.ManagementAgentName, this.Type, this.Description, DateTime.Now, true, ex); + MessageSender.SendMessage($"{this.ManagementAgentName}: {this.Type} trigger error", messageContent); } } } diff --git a/src/Lithnet.Miiserver.AutoSync/Triggers/ScheduledExecutionTrigger.cs b/src/Lithnet.Miiserver.AutoSync/Triggers/ScheduledExecutionTrigger.cs index 4f58142..2bacb19 100644 --- a/src/Lithnet.Miiserver.AutoSync/Triggers/ScheduledExecutionTrigger.cs +++ b/src/Lithnet.Miiserver.AutoSync/Triggers/ScheduledExecutionTrigger.cs @@ -54,17 +54,24 @@ private void SetRemainingMilliseconds() private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) { this.Fire(this.RunProfileName); - this.Start(); + this.ResetTimer(); } - public override void Start() + public override void Start(string managementAgentName) { + this.ManagementAgentName = managementAgentName; + if (this.RunProfileName == null) { this.LogError("Ignoring scheduled trigger with no run profile name"); return; } + this.ResetTimer(); + } + + private void ResetTimer() + { this.SetRemainingMilliseconds(); this.checkTimer = new Timer { diff --git a/src/Lithnet.Miiserver.AutoSync/packages.config b/src/Lithnet.Miiserver.AutoSync/packages.config index a6be3a0..f0d642e 100644 --- a/src/Lithnet.Miiserver.AutoSync/packages.config +++ b/src/Lithnet.Miiserver.AutoSync/packages.config @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/src/Lithnet.Miiserver.Autosync.UI/Lithnet.Miiserver.AutoSync.Editor.csproj b/src/Lithnet.Miiserver.Autosync.UI/Lithnet.Miiserver.AutoSync.Editor.csproj index 2a310a6..0a97e6d 100644 --- a/src/Lithnet.Miiserver.Autosync.UI/Lithnet.Miiserver.AutoSync.Editor.csproj +++ b/src/Lithnet.Miiserver.Autosync.UI/Lithnet.Miiserver.AutoSync.Editor.csproj @@ -54,8 +54,8 @@ ..\packages\Lithnet.Logging.1.0.5774.20685\lib\net40\Lithnet.Logging.dll - - ..\packages\Lithnet.Miiserver.Client.1.0.6434.38666\lib\net45\Lithnet.Miiserver.Client.dll + + ..\packages\Lithnet.Miiserver.Client.1.0.6450.37102\lib\net45\Lithnet.Miiserver.Client.dll ..\packages\MahApps.Metro.1.5.0\lib\net45\MahApps.Metro.dll diff --git a/src/Lithnet.Miiserver.Autosync.UI/packages.config b/src/Lithnet.Miiserver.Autosync.UI/packages.config index 324bb30..042f67e 100644 --- a/src/Lithnet.Miiserver.Autosync.UI/packages.config +++ b/src/Lithnet.Miiserver.Autosync.UI/packages.config @@ -5,7 +5,7 @@ - +