using System; using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; using LeafWeb.Core.Utility; using Renci.SshNet; namespace LeafWeb.Core.Remote { public class PiscalSshClient : IPiscalClient { private const string BaseDirectory = "./LeafWeb"; private const string RemoteScriptPath = BaseDirectory + "/piscal_manager.sh"; private readonly PasswordConnectionInfo _connectionInfo; private const string StatusSuccess = "success"; private const string StatusRunning = "running"; private const string StatusError = "error"; public PiscalSshClient(string connectionString) { var conn = new DbConnectionStringBuilder {ConnectionString = connectionString}; var host = conn["host"] as string; var username = conn["username"] as string; var password = conn["password"] as string; _connectionInfo = new PasswordConnectionInfo(host, username, password); } private SshClient GetSshClient() { return new SshClient(_connectionInfo); } private ScpClient GetScpClient() { return new ScpClient(_connectionInfo); } public void RunLeafInputFile(PiscalLeafInputFile file) { var inputPath = $"{BaseDirectory}/{file.DirectoryName}/{file.Filename}"; // copy file using (var scp = GetScpClient()) using (var stream = new MemoryStream(file.Contents)) { Console.WriteLine(inputPath); scp.Connect(); scp.Upload(stream, inputPath); scp.Disconnect(); } // begin processing using (var ssh = GetSshClient()) { ssh.Connect(); var commandText = $"{RemoteScriptPath} -d {file.DirectoryName} -f {file.Filename}"; var command = ssh.CreateCommand(commandText); command.Execute(); ssh.Disconnect(); if (command.ExitStatus != 0) throw new PiscalClientException(command.Error); Console.Write(command.Result); } } public PiscalStatus GetLeafInputFileStatus(PiscalLeafInputFile file) { var statusRaw = GetLeafInputStatusRaw(file); switch (statusRaw[0]) { case StatusRunning: return PiscalStatus.Running; case StatusSuccess: return PiscalStatus.Success; default: return PiscalStatus.Error; } } private string[] GetLeafInputStatusRaw(PiscalLeafInputFile file) { using (var ssh = GetSshClient()) { ssh.Connect(); var commandText = $"{RemoteScriptPath} -d {file.DirectoryName} -s"; var command = ssh.CreateCommand(commandText); command.Execute(); ssh.Disconnect(); if (command.ExitStatus != 0) throw new PiscalClientException(command.Error); return command.Result .SplitNewLine() .Where(s => s.Length > 0) .Select(s => s.Trim()).ToArray(); } } /// /// Gets the leaf output from piscal, only run on if result status is success /// public IEnumerable RetrieveLeafOutput(PiscalLeafInputFile file) { // get output files var status = GetLeafInputStatusRaw(file); if (status[0] != StatusSuccess) throw new PiscalClientException("output not available, status is " + status[0]); var filePaths = status.Skip(1); using (var scp = GetScpClient()) { scp.Connect(); foreach (var filePath in filePaths) { using (var stream = new MemoryStream()) { scp.Download(filePath, stream); yield return new PiscalLeafOutputFile { Contents = stream.ToArray(), Filename = filePath.FilenameFromPath(), DirectoryName = file.DirectoryName }; } } scp.Disconnect(); } } public void CleanupLeafProcess(PiscalLeafInputFile file) { var status = GetLeafInputStatusRaw(file); if (status[0] == StatusRunning) throw new PiscalClientException("Trying to cleanup a running process"); using (var ssh = GetSshClient()) { ssh.Connect(); var commandText = $"{RemoteScriptPath} -d {file.DirectoryName} -c"; var command = ssh.CreateCommand(commandText); command.Execute(); ssh.Disconnect(); if (command.ExitStatus != 0) throw new PiscalClientException(command.Error); } } } }