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 {