Mercurial > servermonitor
comparison ServerMonitor/Objects/ServerMonitor.cs @ 8:052aa62cb42a
Single instance. Add autorun option. Add icons. General enhancements.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 09 Mar 2019 20:14:03 -0500 |
parents | c1dffaac66fa |
children | 68d7834dc28e |
comparison
equal
deleted
inserted
replaced
7:8486ab7d2357 | 8:052aa62cb42a |
---|---|
1 using Renci.SshNet; | 1 using Microsoft.Win32; |
2 using Renci.SshNet; | |
2 using Renci.SshNet.Common; | 3 using Renci.SshNet.Common; |
3 using ServerMonitorApp.Properties; | 4 using ServerMonitorApp.Properties; |
4 using System; | 5 using System; |
5 using System.Collections.Generic; | 6 using System.Collections.Generic; |
6 using System.Diagnostics; | 7 using System.Diagnostics; |
20 private readonly string configFileDir; | 21 private readonly string configFileDir; |
21 private readonly Logger logger; | 22 private readonly Logger logger; |
22 private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>(); | 23 private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>(); |
23 private readonly Dictionary<string, PrivateKeyFile> privateKeys = new Dictionary<string, PrivateKeyFile>(); | 24 private readonly Dictionary<string, PrivateKeyFile> privateKeys = new Dictionary<string, PrivateKeyFile>(); |
24 private readonly List<int> pausedChecks = new List<int>(); | 25 private readonly List<int> pausedChecks = new List<int>(); |
25 private bool running, networkAvailable; | 26 private bool running, networkAvailable, suspend; |
26 private Dictionary<Task<CheckResult>, int> tasks; | 27 private Dictionary<Task<CheckResult>, int> tasks; |
27 private ServerSummaryForm mainForm; | 28 private ServerSummaryForm mainForm; |
28 | 29 |
29 //private List<Task<CheckResult>> tasks; | 30 //private List<Task<CheckResult>> tasks; |
30 | 31 |
58 SaveServers(); | 59 SaveServers(); |
59 } | 60 } |
60 | 61 |
61 public void LoadServers() | 62 public void LoadServers() |
62 { | 63 { |
64 Read: | |
63 TextReader reader = null; | 65 TextReader reader = null; |
64 try | 66 try |
65 { | 67 { |
66 reader = new StreamReader(ConfigFile); | 68 reader = new StreamReader(ConfigFile); |
67 XmlSerializer serializer = CreateXmlSerializer(); | 69 XmlSerializer serializer = CreateXmlSerializer(); |
87 // If the file doesn't exist, no special handling is needed. It will be created later. | 89 // If the file doesn't exist, no special handling is needed. It will be created later. |
88 catch (FileNotFoundException) { } | 90 catch (FileNotFoundException) { } |
89 catch (DirectoryNotFoundException) { } | 91 catch (DirectoryNotFoundException) { } |
90 catch (InvalidOperationException) | 92 catch (InvalidOperationException) |
91 { | 93 { |
92 //TODO log | 94 reader?.Close(); |
93 throw; | 95 File.Copy(ConfigFile, ConfigFile + ".error", true); |
96 File.Copy(ConfigFile + ".bak", ConfigFile, true); | |
97 goto Read; | |
94 } | 98 } |
95 finally | 99 finally |
96 { | 100 { |
97 reader?.Close(); | 101 reader?.Close(); |
98 } | 102 } |
103 Application.ApplicationExit += Application_ApplicationExit; | |
99 NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; | 104 NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; |
105 SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; | |
100 logger.TrimLog(); | 106 logger.TrimLog(); |
101 Run(); | 107 Run(); |
102 } | 108 } |
103 | 109 |
104 public void SaveServers() | 110 public void SaveServers() |
106 GenerateIds(); | 112 GenerateIds(); |
107 TextWriter writer = null; | 113 TextWriter writer = null; |
108 XmlSerializer serializer = null; | 114 XmlSerializer serializer = null; |
109 try | 115 try |
110 { | 116 { |
117 File.Copy(ConfigFile, ConfigFile + ".bak", true); | |
118 } | |
119 catch { } | |
120 try | |
121 { | |
111 writer = new StreamWriter(ConfigFile); | 122 writer = new StreamWriter(ConfigFile); |
112 serializer = CreateXmlSerializer(); | 123 serializer = CreateXmlSerializer(); |
113 serializer.Serialize(writer, Servers); | 124 serializer.Serialize(writer, Servers); |
114 } | 125 } |
115 catch (DirectoryNotFoundException) | 126 catch (DirectoryNotFoundException) |
117 Directory.CreateDirectory(configFileDir); | 128 Directory.CreateDirectory(configFileDir); |
118 writer = new StreamWriter(ConfigFile); | 129 writer = new StreamWriter(ConfigFile); |
119 serializer = CreateXmlSerializer(); | 130 serializer = CreateXmlSerializer(); |
120 serializer.Serialize(writer, Servers); | 131 serializer.Serialize(writer, Servers); |
121 } | 132 } |
122 catch (Exception) | |
123 { | |
124 //TODO log | |
125 throw; | |
126 } | |
127 finally | 133 finally |
128 { | 134 { |
129 writer?.Close(); | 135 writer?.Close(); |
130 } | 136 } |
131 } | 137 } |
132 | 138 |
133 private async void Run() | 139 private async void Run() |
134 { | 140 { |
135 if (running) | 141 if (running || suspend) |
136 return; | 142 return; |
137 running = true; | 143 running = true; |
138 networkAvailable = Helpers.IsNetworkAvailable(); | 144 networkAvailable = Helpers.IsNetworkAvailable(); |
139 if (networkAvailable) | 145 if (networkAvailable) |
140 { | 146 { |
142 { | 148 { |
143 await ExecuteCheckAsync(Checks.FirstOrDefault(c => c.Id == id)); | 149 await ExecuteCheckAsync(Checks.FirstOrDefault(c => c.Id == id)); |
144 } | 150 } |
145 pausedChecks.Clear(); | 151 pausedChecks.Clear(); |
146 } | 152 } |
147 //TODO subscribe to power events. Find any check's NextExecutionTime is in the past. Cancel waiting task and run immediately (or after short delay). | |
148 //tasks = Checks.Select(c => ScheduleExecuteCheckAsync(c)).ToList(); | |
149 tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id); | 153 tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id); |
150 while (tasks.Count > 0) | 154 while (tasks.Count > 0) |
151 { | 155 { |
152 Task<CheckResult> task = await Task.WhenAny(tasks.Keys); | 156 Task<CheckResult> task = await Task.WhenAny(tasks.Keys); |
153 tasks.Remove(task); | 157 tasks.Remove(task); |
169 public async Task<CheckResult> ExecuteCheckAsync(Check check, CancellationToken token = default(CancellationToken)) | 173 public async Task<CheckResult> ExecuteCheckAsync(Check check, CancellationToken token = default(CancellationToken)) |
170 { | 174 { |
171 check.Status = CheckStatus.Running; | 175 check.Status = CheckStatus.Running; |
172 OnCheckStatusChanged(check); | 176 OnCheckStatusChanged(check); |
173 CheckResult result = await check.ExecuteAsync(token); | 177 CheckResult result = await check.ExecuteAsync(token); |
178 if (result.Failed) | |
179 check.ConsecutiveFailures++; | |
174 OnCheckStatusChanged(check, result); | 180 OnCheckStatusChanged(check, result); |
175 HandleResultAsync(result); | 181 HandleResultAsync(result); |
176 return result; | 182 return result; |
177 } | 183 } |
178 | 184 |
179 private void HandleResultAsync(CheckResult result) | 185 private void HandleResultAsync(CheckResult result) |
180 { | 186 { |
181 logger.Log(result); | 187 logger.Log(result); |
182 if (result.FailAction == FailAction.FlashTaskbar) | 188 if (result.Check.ConsecutiveFailures >= result.Check.MaxConsecutiveFailures) |
183 mainForm.AlertServerForm(result.Check); | 189 { |
184 if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon)) | 190 if (result.FailAction == FailAction.FlashTaskbar) |
185 mainForm.ShowBalloon(result); | 191 mainForm.AlertServerForm(result.Check); |
192 if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon)) | |
193 mainForm.ShowBalloon(result); | |
194 } | |
186 } | 195 } |
187 | 196 |
188 public IList<CheckResult> GetLog(Server server) | 197 public IList<CheckResult> GetLog(Server server) |
189 { | 198 { |
190 return logger.Read(server); | 199 return logger.Read(server); |
259 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); | 268 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); |
260 | 269 |
261 CancellationTokenSource cts = new CancellationTokenSource(); | 270 CancellationTokenSource cts = new CancellationTokenSource(); |
262 tokens[check.Id] = cts; | 271 tokens[check.Id] = cts; |
263 check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime); | 272 check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime); |
264 await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token); | 273 int delay = Math.Max(0, (int)(check.NextRunTime - DateTime.Now).TotalMilliseconds); |
274 await Task.Delay(delay, cts.Token); | |
265 check.LastScheduledRunTime = check.NextRunTime; | 275 check.LastScheduledRunTime = check.NextRunTime; |
266 if (networkAvailable) | 276 if (networkAvailable && !cts.IsCancellationRequested) |
277 { | |
267 return await ExecuteCheckAsync(check, cts.Token); | 278 return await ExecuteCheckAsync(check, cts.Token); |
279 } | |
268 else | 280 else |
269 { | 281 { |
270 if (!pausedChecks.Contains(check.Id)) | 282 if (!pausedChecks.Contains(check.Id)) |
271 pausedChecks.Add(check.Id); | 283 pausedChecks.Add(check.Id); |
272 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); | 284 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); |
278 networkAvailable = Helpers.IsNetworkAvailable(); | 290 networkAvailable = Helpers.IsNetworkAvailable(); |
279 if (networkAvailable) | 291 if (networkAvailable) |
280 mainForm.Invoke((MethodInvoker)(() => Run())); | 292 mainForm.Invoke((MethodInvoker)(() => Run())); |
281 } | 293 } |
282 | 294 |
295 private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) | |
296 { | |
297 if (e.Mode == PowerModes.Suspend) | |
298 { | |
299 foreach (Check check in Checks) | |
300 { | |
301 CancelCheck(check); | |
302 } | |
303 suspend = true; | |
304 } | |
305 else if (e.Mode == PowerModes.Resume) | |
306 { | |
307 pausedChecks.Clear(); | |
308 foreach (Check check in Checks) | |
309 { | |
310 //CancelCheck(check); | |
311 if (check.Enabled && check.Server.Enabled && check.NextRunTime < DateTime.Now) | |
312 { | |
313 pausedChecks.Add(check.Id); | |
314 } | |
315 } | |
316 await Task.Delay(10000); | |
317 suspend = false; | |
318 Run(); | |
319 } | |
320 } | |
321 | |
322 private void Application_ApplicationExit(object sender, EventArgs e) | |
323 { | |
324 NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged; | |
325 SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; | |
326 } | |
327 | |
283 public KeyStatus OpenPrivateKey(string path, string password = null) | 328 public KeyStatus OpenPrivateKey(string path, string password = null) |
284 { | 329 { |
285 KeyStatus keyStatus; | 330 KeyStatus keyStatus; |
286 if (path == null) | 331 if (path == null) |
287 return KeyStatus.NotAccessible; | 332 keyStatus = KeyStatus.NotAccessible; |
288 if (privateKeys.TryGetValue(path, out PrivateKeyFile key) && key != null) | 333 if (privateKeys.TryGetValue(path, out PrivateKeyFile key) && key != null) |
289 return KeyStatus.Open; | |
290 try | |
291 { | |
292 key = new PrivateKeyFile(path, password); | |
293 keyStatus = KeyStatus.Open; | 334 keyStatus = KeyStatus.Open; |
294 } | 335 else |
295 catch (Exception e) when (e is SshPassPhraseNullOrEmptyException || e is InvalidOperationException) | 336 { |
296 { | 337 try |
297 keyStatus = KeyStatus.NeedPassword; | 338 { |
298 } | 339 key = new PrivateKeyFile(path, password); |
299 catch (Exception) | 340 keyStatus = KeyStatus.Open; |
300 { | 341 } |
301 keyStatus = KeyStatus.NotAccessible; | 342 catch (Exception e) when (e is SshPassPhraseNullOrEmptyException || e is InvalidOperationException) |
343 { | |
344 keyStatus = KeyStatus.NeedPassword; | |
345 } | |
346 catch (Exception) | |
347 { | |
348 keyStatus = KeyStatus.NotAccessible; | |
349 } | |
302 } | 350 } |
303 foreach (Server server in Servers) | 351 foreach (Server server in Servers) |
304 { | 352 { |
305 if (server.KeyFile == path) | 353 if (server.KeyFile == path) |
306 { | 354 { |