Mercurial > servermonitor
comparison ServerMonitor/Objects/Checks/SshCheck.cs @ 17:68d7834dc28e
More comments.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 25 May 2019 15:14:26 -0400 |
parents | a36cc5c123f4 |
children | 7645122aa7a9 |
comparison
equal
deleted
inserted
replaced
16:7626b099aefd | 17:68d7834dc28e |
---|---|
1 using Renci.SshNet; | 1 using Renci.SshNet; |
2 using System; | 2 using System; |
3 using System.Collections.Generic; | 3 using System.Collections.Generic; |
4 using System.ComponentModel; | 4 using System.ComponentModel; |
5 using System.Linq; | |
6 using System.Text; | |
7 using System.Text.RegularExpressions; | 5 using System.Text.RegularExpressions; |
8 using System.Threading; | 6 using System.Threading; |
9 using System.Threading.Tasks; | 7 using System.Threading.Tasks; |
10 | 8 |
11 namespace ServerMonitorApp | 9 namespace ServerMonitorApp |
12 { | 10 { |
11 /// <summary>Executes an SSH command and checks the output or exit code.</summary> | |
13 [DisplayName("SSH check"), Description("Check the result of a command run over SSH"), DisplayWeight(10)] | 12 [DisplayName("SSH check"), Description("Check the result of a command run over SSH"), DisplayWeight(10)] |
14 public class SshCheck : Check | 13 public class SshCheck : Check |
15 { | 14 { |
15 /// <summary>The command to execute on the server.</summary> | |
16 public virtual string Command { get; set; } | 16 public virtual string Command { get; set; } |
17 | 17 |
18 /// <summary>Whether the exit code of the command should be checked.</summary> | |
18 public bool CheckExitCode { get; set; } | 19 public bool CheckExitCode { get; set; } |
19 | 20 |
21 /// <summary>The required exit code of the command if CheckExitCode is true.</summary> | |
20 public int ExitCode { get; set; } | 22 public int ExitCode { get; set; } |
21 | 23 |
24 /// <summary>Whether the text output of the command should be checked.</summary> | |
22 public bool CheckCommandOutput { get; set; } | 25 public bool CheckCommandOutput { get; set; } |
23 | 26 |
27 /// <summary>The method to use when checking the command output against the pattern.</summary> | |
24 public MatchType CommandOutputMatchType { get; set; } | 28 public MatchType CommandOutputMatchType { get; set; } |
25 | 29 |
30 /// <summary>The string or pattern that the command output must match if CommandOutputMatchType is true.</summary> | |
26 public string CommandOutputPattern { get; set; } | 31 public string CommandOutputPattern { get; set; } |
27 | 32 |
33 /// <summary>Whether the CommandOutputPattern should be interpreted as a regular expression.</summary> | |
28 public bool CommandOutputUseRegex { get; set; } | 34 public bool CommandOutputUseRegex { get; set; } |
29 | 35 |
36 /// <summary>Executes the SSH command on the server.</summary> | |
30 protected override Task<CheckResult> ExecuteCheckAsync(CancellationToken token) | 37 protected override Task<CheckResult> ExecuteCheckAsync(CancellationToken token) |
31 { | 38 { |
32 try | |
33 { | |
34 if (!Server.SshClient.IsConnected) | |
35 { | |
36 if (Server.LoginType == LoginType.PrivateKey && Server.PrivateKeyFile == null) | |
37 return Task.FromResult(Fail(string.Format("Private key '{0}' is locked or not accessible", Server.KeyFile))); | |
38 Server.SshClient.Connect(); | |
39 } | |
40 } | |
41 catch (Exception e) | |
42 { | |
43 return Task.FromResult(Fail(e)); | |
44 } | |
45 return Task.Run(() => | 39 return Task.Run(() => |
46 { | 40 { |
47 try | 41 try |
48 { | 42 { |
43 // Exit now if the user cancelled the execution. | |
49 token.ThrowIfCancellationRequested(); | 44 token.ThrowIfCancellationRequested(); |
45 | |
46 // Connect to the server if needed. | |
47 if (!Server.SshClient.IsConnected) | |
48 { | |
49 // If the server private key file has not been opened, it is probably encrypted. | |
50 // Report failure until the user enters the password. | |
51 if (Server.LoginType == LoginType.PrivateKey && Server.PrivateKeyFile == null) | |
52 return Fail(string.Format("Private key '{0}' is locked or not accessible", Server.KeyFile)); | |
53 Server.SshClient.Connect(); | |
54 } | |
55 | |
56 // Exit now if the user cancelled the execution. | |
57 token.ThrowIfCancellationRequested(); | |
58 | |
50 using (SshCommand command = Server.SshClient.CreateCommand(Command)) | 59 using (SshCommand command = Server.SshClient.CreateCommand(Command)) |
51 { | 60 { |
61 // Execute the command. | |
52 token.Register(command.CancelAsync); | 62 token.Register(command.CancelAsync); |
53 IAsyncResult ar = command.BeginExecute(); | 63 IAsyncResult ar = command.BeginExecute(); |
54 token.ThrowIfCancellationRequested(); | 64 token.ThrowIfCancellationRequested(); |
65 // Store both the command output and the error streams so they can | |
66 // be logged and checked. | |
55 string output = (command.EndExecute(ar).Trim() + command.Error.Trim()).ConvertNewlines(); | 67 string output = (command.EndExecute(ar).Trim() + command.Error.Trim()).ConvertNewlines(); |
68 // Process the results (exit code and command output) and merge them into a single result. | |
56 return MergeResults(ProcessCommandResult(output, command.ExitStatus).ToArray()); | 69 return MergeResults(ProcessCommandResult(output, command.ExitStatus).ToArray()); |
57 } | 70 } |
58 } | 71 } |
59 catch (Exception e) | 72 catch (Exception e) |
60 { | 73 { |
61 return Fail(e); | 74 return Fail(e); |
62 } | 75 } |
63 }, token); | 76 }, token); |
64 } | 77 } |
65 | 78 |
79 /// <summary>Processes the command results and checks they match the expected values.</summary> | |
80 /// <param name="output">The command output.</param> | |
81 /// <param name="exitCode">The command exit code.</param> | |
82 /// <returns>A list of check results according to user preferences.</returns> | |
66 protected virtual List<CheckResult> ProcessCommandResult(string output, int exitCode) | 83 protected virtual List<CheckResult> ProcessCommandResult(string output, int exitCode) |
67 { | 84 { |
68 List<CheckResult> results = new List<CheckResult>(); | 85 List<CheckResult> results = new List<CheckResult>(); |
86 // Check the actual output against the expected output if command output checking is enabled. | |
69 if (CheckCommandOutput) | 87 if (CheckCommandOutput) |
70 results.Add(GetStringResult(CommandOutputMatchType, CommandOutputPattern, CommandOutputUseRegex, output, "Command output")); | 88 results.Add(GetStringResult(CommandOutputMatchType, CommandOutputPattern, CommandOutputUseRegex, output, "Command output")); |
89 // Check the actual exit code against the expected exit code if exit code checking is enabled. | |
71 if (CheckExitCode) | 90 if (CheckExitCode) |
72 { | 91 { |
73 CheckResult result = GetIntResult(ExitCode, exitCode, "Exit code"); | 92 CheckResult result = GetIntResult(ExitCode, exitCode, "Exit code"); |
74 if (result.Failed) | 93 if (result.Failed) |
75 result.Message += ". Command output: " + output; | 94 result.Message += ". Command output: " + output; |
76 results.Add(result); | 95 results.Add(result); |
77 } | 96 } |
78 return results; | 97 return results; |
79 } | 98 } |
80 | 99 |
100 /// <summary>Validates SSH check options.</summary> | |
81 public override string Validate(bool saving = true) | 101 public override string Validate(bool saving = true) |
82 { | 102 { |
83 string message = base.Validate(); | 103 string message = base.Validate(); |
84 if (Server.Port <= 0) | 104 if (Server.Port <= 0) |
85 message += "Server SSH port is required." + Environment.NewLine; | 105 message += "Server SSH port is required." + Environment.NewLine; |