namespace JD.Nas.Reporting.Console.Module { using JD.Nas.Reporting.Console.Module.Constants; using JD.Nas.Reporting.Console.Module.Exception; using JD.Nas.Service; using JD.Nas.Service.Exception; using JD.Nas.Service.NasReportingDto; using Newtonsoft.Json; using Plume.Core.Logging; using Plume.Interfaces.CommandLines; using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; public class ReportingConsoleAction : CommandLineAction { private readonly INasReportingService _nasReportingService; private ILogger _logger; private readonly string _applicationLogPath; private string _archiveDirectoryPath; private string _reportDirectoryPath; private string _reportPath; private string _reportZipFilePath; private string _reportZipFileArchivePath; private NasReportConfiguration _nasReportConfiguration; private NasReport _nasReport; #region Constructor public ReportingConsoleAction(INasReportingService nasReportingService, string applicationLogPath) { _nasReportingService = nasReportingService ?? throw new ArgumentNullException(nameof(nasReportingService)); _applicationLogPath = applicationLogPath; } #endregion protected override int Execute(ReportingConsoleOption option, ILogger logger) { if (logger == null) { return (int)ReportingConsoleExitCode.ExecuteLoggerError; } _logger = logger; try { if (option == null) { throw new ReportingConsoleException( ReportingConsoleExitCode.ExecuteOptionError, string.Concat("Option undefined : ", nameof(option)) ); } if (string.IsNullOrEmpty(option.ConfigurationFilePath)) { throw new ReportingConsoleException( ReportingConsoleExitCode.ConfigurationFilePathNeeded, "If ApiUrlForConfig arguments is feeled also ConfigFilePath is needed !" ); } #region ConfigurationFilePath verifications part 1 if (!Directory.Exists(Path.GetDirectoryName(option.ConfigurationFilePath))) { throw new ReportingConsoleException( ReportingConsoleExitCode.ConfigurationFilePathNotExist, string.Concat("Directory not exist : ", Path.GetDirectoryName(option.ConfigurationFilePath)) ); } if (!AccessDirectory(Path.GetDirectoryName(option.ConfigurationFilePath))) { throw new ReportingConsoleException( ReportingConsoleExitCode.ConfigurationFilePathAccessDenied, string.Concat("Access denied to directory : ", Path.GetDirectoryName(option.ConfigurationFilePath)) ); } #endregion if (!string.IsNullOrEmpty(option.ApiUrlForConfiguration)) { this.DownloadJsonFile(option.ApiUrlForConfiguration, option.ConfigurationFilePath); } #region ConfigurationFilePath verifications part 2 if (!option.ConfigurationFilePath.EndsWith(ReportingConsoleConstants.JsonExtension)) { throw new ReportingConsoleException( ReportingConsoleExitCode.ConfigurationFileNotJson, string.Concat("Configuration file is not a json file : ", option.ConfigurationFilePath) ); } #endregion // Set _nasReportConfiguration variable with the option.ConfigFilePath configuration file this._nasReportConfiguration = this.DeserializeJsonFile(option.ConfigurationFilePath); #region NasReportConfiguration verifications Part 1 if (_nasReportConfiguration == null) { throw new ReportingConsoleException( ReportingConsoleExitCode.ConfigurationFileEmpty, "Configuration file is empty" ); } // OutputPath attribut if (string.IsNullOrEmpty(_nasReportConfiguration.OutputPath)) { throw new ReportingConsoleException( ReportingConsoleExitCode.OutputPathNeeded, "OutputPath arguments is needed !" ); } if (!Directory.Exists(Path.GetDirectoryName(_nasReportConfiguration.OutputPath))) { throw new ReportingConsoleException( ReportingConsoleExitCode.OutputPathNotExist, string.Concat("Directory not exist : ", Path.GetDirectoryName(_nasReportConfiguration.OutputPath)) ); } if (!AccessDirectory(Path.GetDirectoryName(_nasReportConfiguration.OutputPath))) { throw new ReportingConsoleException( ReportingConsoleExitCode.OutputPathNoAccess, string.Concat("Access denied to directory : ", Path.GetDirectoryName(_nasReportConfiguration.OutputPath)) ); } // NasReportId attribut if (string.IsNullOrEmpty(_nasReportConfiguration.NasReportId)) { throw new ReportingConsoleException( ReportingConsoleExitCode.ReportIdNeeded, "ReportId is needed !" ); } #endregion this._archiveDirectoryPath = Path.Combine(this._nasReportConfiguration.OutputPath, ReportingConsoleConstants.ArchiveFile); this._reportDirectoryPath = Path.Combine(this._nasReportConfiguration.OutputPath, this._nasReportConfiguration.NasReportId); #region NasReportConfiguration api verifications if(!string.IsNullOrEmpty(this._nasReportConfiguration.ApiUrlForData) && string.IsNullOrEmpty(this._nasReportConfiguration.ApiUrlForError)) { throw new ReportingConsoleException( ReportingConsoleExitCode.ExecuteOptionError, string.Concat("If ApiUrlForData is fill also ApiUrlForError is needed : ", nameof(option)) ); } if (this._nasReportConfiguration.IsDebugApi == true && !string.IsNullOrEmpty(this._nasReportConfiguration.ApiUrlForData) && !string.IsNullOrEmpty(this._nasReportConfiguration.ApiUrlForError)) { this.WriteLogWarn("Api debug enabled"); this.WriteLogWarn("Try to post the report exclusively"); this._reportZipFilePath = Path.Combine(this._nasReportConfiguration.OutputPath, string.Concat(this._nasReportConfiguration.NasReportId, ReportingConsoleConstants.ZipExtension)); if (!File.Exists(Path.Combine(this._nasReportConfiguration.OutputPath, string.Concat(this._nasReportConfiguration.NasReportId, ReportingConsoleConstants.ZipExtension)))) { throw new ReportingConsoleException( ReportingConsoleExitCode.ZipFileReportNotExist, string.Concat("ZipFileReport not exist : ", this._reportZipFilePath) ); } this.PostZipFile(this._reportZipFilePath, this._nasReportConfiguration.ApiUrlForData); this.WriteLogInfo("Api debug finished !"); return (int)ReportingConsoleExitCode.Ok; } #endregion #region NasReportConfiguration verification Part 2 string reportPath = Path.Combine( _reportDirectoryPath, string.Concat(this._nasReportConfiguration.NasReportId, ReportingConsoleConstants.JsonExtension)); // NasReportId already use if (Directory.Exists( Path.Combine( this._nasReportConfiguration.OutputPath, this._nasReportConfiguration.NasReportId)) && File.Exists(reportPath)) { throw new ReportingConsoleException( ReportingConsoleExitCode.ReportIdAlreadyUsed, string.Concat("Report already exists : ", reportPath) ); } // NasServerConfigurations if (this._nasReportConfiguration.NasServerConfigurations == null || this._nasReportConfiguration.NasServerConfigurations.Count == 0) { throw new ReportingConsoleException( ReportingConsoleExitCode.NasServerConfigurationEmpty, string.Concat("No server configuration detected", reportPath) ); } #endregion #region Reporting this.WriteLogInfo("Reporting started !"); this.WriteLogInfo(string.Concat("They are ", this._nasReportConfiguration.NasServerConfigurations.Count, " to analyse")); // Analyse this.WriteLogInfo(string.Concat("The nas report analyse start")); this.AnalyseReport(); this.WriteLogInfo(string.Concat("The nas report analyse is finish")); // Setup directory result this.CompleteReportDirectory(); // Zip directory result this.ZipReport(); #endregion // ApiUrl if (!string.IsNullOrEmpty(_nasReportConfiguration.ApiUrlForData)) { // Send data this.PostZipFile(this._reportZipFilePath, this._nasReportConfiguration.ApiUrlForData); // Archive the zip this.ArchiveReport(); } this.WriteLogInfo(string.Concat("Work finished !")); return (int)ReportingConsoleExitCode.Ok; } catch (ReportingConsoleException ex) { this.WriteLogError(ex); this.PostZipFileError(); return ex.GetExitCodeInt(); } catch (System.Exception ex) { ReportingConsoleException exception = new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedError, ex ); this.WriteLogError(exception); this.PostZipFileError(); return exception.GetExitCodeInt(); } } #region Private /// /// Zip, post and archive the report error data /// private void PostZipFileError() { // Create the report directory, if not exist this.CreateReportDirectory(); // Complete it with logs and other this.CompleteReportDirectory(); // Zip it this.ZipReport(); // Send the zip error this.PostZipFile(this._reportZipFilePath, this._nasReportConfiguration.ApiUrlForError); this.WriteLogInfo(string.Concat("The zip error has been send")); // Archive this.ArchiveReport(); } /// /// Analyse report with current report configuration /// private void AnalyseReport() { this.CreateReportDirectory(); foreach (NasServerConfiguration nasServerConfiguration in this._nasReportConfiguration.NasServerConfigurations) { if (string.IsNullOrEmpty(nasServerConfiguration.NasServerConfigurationId)) { throw new ReportingConsoleException( ReportingConsoleExitCode.NasServerConfigurationIdNeeded ); } else { try { AnalyseServer(this._reportPath, nasServerConfiguration); } catch (NasReportingServiceException ex) { this.WriteLogError(ex); } catch (ReportingConsoleException ex) { this.WriteLogError(ex); } } } // Save the date of end this._nasReport.DateStop = DateTime.UtcNow; } /// /// Alanyse one nas server /// /// /// private void AnalyseServer(string outputPath, NasServerConfiguration nasServerConfiguration) { // Start sniffing NasServerAnalyse nasServerAnalyse = this._nasReportingService.AnalyseNasServer(this._nasReport, nasServerConfiguration); if (nasServerAnalyse != null) { // Order nasServerAnalyse.Maps = nasServerAnalyse?.Maps.OrderBy(x => x.Name).ToList(); nasServerAnalyse.DateStop = DateTime.UtcNow; // Save analyse file string filePath = Path.Combine(outputPath, nasServerConfiguration.FileAnalyseName); SaveFileAsJson( filePath, nasServerAnalyse, (nasServerConfiguration.IsOutputFormatIndented ?? this._nasReportConfiguration.IsOutputFormatIndented ?? false) ); this._nasReport.Servers.Add(filePath); this.WriteLogInfo(string.Concat($"Nas server analyse finished of : ", nasServerConfiguration.Folder, " || ", DateTime.Now)); } else { this.WriteLogWarn(string.Concat($"Nas server analyse of : ", nasServerConfiguration.Folder, " is empty || ", DateTime.Now)); } } /// /// Post zip file /// /// /// private void PostZipFile(string zipPath, string url) { try { HttpResponseMessage message; using (HttpClient client = new HttpClient()) using (MultipartFormDataContent formData = new MultipartFormDataContent()) { message = this.PostZipFileTask(client, formData, zipPath, url).GetAwaiter().GetResult(); } if (message == null) { throw new ReportingConsoleException( ReportingConsoleExitCode.PostZipFileError, string.Concat("No response of http post : " + url) ); } else if (!message.IsSuccessStatusCode) { throw new ReportingConsoleException( ReportingConsoleExitCode.PostZipFileError, string.Concat("The server return ", message.StatusCode.ToString(), " of http post of ", url) ); } else { this.WriteLogInfo("PostZipFileReport() success !"); } } catch (ReportingConsoleException ex) { throw ex; } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorPostZipFile, ex ); } } /// /// Post zip file (task) /// /// /// /// /// /// private async Task PostZipFileTask(HttpClient client, MultipartFormDataContent formData, string zipPath, string url) { try { byte[] byteArray = File.ReadAllBytes(zipPath); formData.Add(new ByteArrayContent(byteArray, 0, byteArray.Length), Path.GetFileNameWithoutExtension(zipPath), Path.GetFileName(zipPath)); return await client.PostAsync(url, formData); } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorPostZipFileTask, ex ); } } /// /// Crete the report and his directory /// private void CreateReportDirectory() { try { // Setup report if not exist if (_nasReport == null) { this._nasReport = new NasReport(this._nasReportConfiguration); this._reportPath = Path.Combine(this._nasReportConfiguration.OutputPath, this._nasReport.Id); } // Create report directory if not exist if (!Directory.Exists(this._reportPath)) { Directory.CreateDirectory(this._reportPath); } } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorCreateReportDirectory, ex ); } } /// /// Save log, config, report files in the report directory /// private void CompleteReportDirectory() { try { // Save the report and reportConfiguration files SaveFileAsJson( Path.Combine( this._reportDirectoryPath, ReportingConsoleConstants.reportFile), this._nasReport, (this._nasReportConfiguration.IsOutputFormatIndented ?? false) ); SaveFileAsJson( Path.Combine( this._reportDirectoryPath, ReportingConsoleConstants.configurationFile), this._nasReportConfiguration, (this._nasReportConfiguration.IsOutputFormatIndented ?? false) ); // Copy the log file File.Copy( Path.Combine( this._applicationLogPath, ReportingConsoleConstants.nasReportingLogsFile), Path.Combine( this._reportDirectoryPath, ReportingConsoleConstants.logsFile), true); } catch (ReportingConsoleException ex) { throw ex; } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorCompleteReportDirectory, ex ); } } /// /// Create the compress file of report /// private void ZipReport() { try { _reportZipFilePath = Path.Combine( this._nasReportConfiguration.OutputPath, string.Concat(this._nasReportConfiguration.NasReportId, ReportingConsoleConstants.ZipExtension)); // If zip already exist, destoy it to regenerate it if (File.Exists(this._reportZipFilePath)) File.Delete(this._reportZipFilePath); // Create zip from directory of report ZipFile.CreateFromDirectory( this._reportDirectoryPath, this._reportZipFilePath ); // Destroy directory of current analyse Directory.Delete(this._reportDirectoryPath, true); } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorZipReport, ex ); } } /// /// Archive the zip file report /// private void ArchiveReport() { try { this._reportZipFileArchivePath = Path.Combine(this._archiveDirectoryPath, this._nasReportConfiguration.NasReportId + ReportingConsoleConstants.ZipExtension); //Create directory for archive if (!Directory.Exists(this._archiveDirectoryPath)) Directory.CreateDirectory(this._archiveDirectoryPath); // Copy the log file File.Move(this._reportZipFilePath, this._reportZipFileArchivePath); // Delete file File.Delete(this._reportZipFilePath); } catch(System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorArchiveReport, ex ); } } /// /// Save object as JsonFile /// /// /// /// private void SaveFileAsJson(string filePath, object obj, bool formatIndented = false) { try { string json = JsonConvert.SerializeObject(obj, formatIndented ? Formatting.Indented : Formatting.None); if (filePath.EndsWith(ReportingConsoleConstants.JsonExtension)) { File.WriteAllText(filePath, json); } else { File.WriteAllText(filePath + ReportingConsoleConstants.JsonExtension, json); } } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorSaveFileAsJson, ex ); } } /// /// Deserialization of the configuration file /// /// public T DeserializeJsonFile(string filePath) { try { if (filePath.EndsWith(ReportingConsoleConstants.JsonExtension)) { return JsonConvert.DeserializeObject(File.ReadAllText(filePath)); } else { return JsonConvert.DeserializeObject(File.ReadAllText(filePath + ReportingConsoleConstants.JsonExtension)); } } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorDeserializeJsonFile, ex ); } } /// /// Download a file at the url adress and save it in the path /// /// /// public void DownloadJsonFile(string url, string filePath) { try { HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(string.Format(url)); WebReq.Method = "GET"; HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse(); using (Stream stream = WebResp.GetResponseStream()) { StreamReader reader = new StreamReader(stream, System.Text.Encoding.UTF8); if (filePath.EndsWith(ReportingConsoleConstants.JsonExtension)) { File.WriteAllText(filePath, reader.ReadToEnd()); } else { File.WriteAllText(filePath + ReportingConsoleConstants.JsonExtension, reader.ReadToEnd()); } } } catch (System.Exception ex) { throw new ReportingConsoleException( ReportingConsoleExitCode.UnexpectedErrorDownloadJsonFile, ex ); } } /// /// Try to get GetAccessControl of directory to detect if we have access /// /// /// private bool AccessDirectory(string path) { try { System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(path); return true; } catch { return false; } } #region Logs private void WriteLogError(NasReportingServiceException exception, NasServerAnalyse nasServerAnalyse = null) { this._nasReport?.Logs.Add(exception.GetIdString()); nasServerAnalyse?.Logs.Add(exception.GetIdString()); this._logger.Error(exception.ToString()); } private void WriteLogError(ReportingConsoleException exception) { this._nasReport?.Logs.Add(exception.GetIdString()); this._logger.Error(exception.ToString()); } private void WriteLogWarn(string message) { this._nasReport?.Logs.Add(message); this._logger.Warn(message); } private void WriteLogInfo(string message) { this._nasReport?.Logs.Add(message); this._logger.Info(message); } #endregion #endregion } }