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;