diff --git a/NetworkResurrector.Api/Controllers/ResurrectorController.cs b/NetworkResurrector.Api/Controllers/ResurrectorController.cs index e9160e0..85e5c93 100644 --- a/NetworkResurrector.Api/Controllers/ResurrectorController.cs +++ b/NetworkResurrector.Api/Controllers/ResurrectorController.cs @@ -39,5 +39,12 @@ namespace NetworkResurrector.Api.Controllers var result = await _mediator.Send(wakeMachine); return Ok(result); } + + [HttpPost("ping")] + public async Task PingMachine([FromBody] PingMachine pingMachine) + { + var result = await _mediator.Send(pingMachine); + return Ok(result); + } } } diff --git a/NetworkResurrector.Application/CommandHandlers/PingMachineHandler.cs b/NetworkResurrector.Application/CommandHandlers/PingMachineHandler.cs new file mode 100644 index 0000000..e323b3b --- /dev/null +++ b/NetworkResurrector.Application/CommandHandlers/PingMachineHandler.cs @@ -0,0 +1,40 @@ +using MediatR; +using Microsoft.Extensions.Logging; +using NetworkResurrector.Application.Commands; +using NetworkResurrector.Application.Events; +using NetworkResurrector.Application.Services; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace NetworkResurrector.Application.CommandHandlers +{ + public class PingMachineHandler : IRequestHandler + { + private readonly ILogger _logger; + private readonly IPingService _pingService; + + public PingMachineHandler(ILogger logger, IPingService pingService) + { + _logger = logger; + _pingService = pingService; + } + + public async Task Handle(PingMachine command, CancellationToken cancellationToken) + { + try + { + _logger.LogDebug($"Start pinging '{command.IPAddress}'."); + var (success, status) = await _pingService.PingMachine(command.IPAddress); + _logger.LogDebug($"Pinging on '{command.IPAddress}' finished. Status: {status}"); + return new MachinePinged(success, status); + } + catch (Exception ex) + { + var correlationIdMsg = $"CorrelationId: {command.Metadata.CorrelationId}"; + _logger.LogError(ex, $"An unexpected error has occurred. {correlationIdMsg}"); + return new MachinePinged(false, $"{ex.Message} {correlationIdMsg}"); + } + } + } +} diff --git a/NetworkResurrector.Application/Commands/PingMachine.cs b/NetworkResurrector.Application/Commands/PingMachine.cs new file mode 100644 index 0000000..eb5ac76 --- /dev/null +++ b/NetworkResurrector.Application/Commands/PingMachine.cs @@ -0,0 +1,15 @@ +using NetworkResurrector.Application.Events; +using System; + +namespace NetworkResurrector.Application.Commands +{ + public class PingMachine : Command + { + public string IPAddress { get; set; } + + public PingMachine(string ipAddress) : base(new Metadata() { CorrelationId = Guid.NewGuid() }) + { + IPAddress = ipAddress; + } + } +} diff --git a/NetworkResurrector.Application/DependencyInjectionExtensions.cs b/NetworkResurrector.Application/DependencyInjectionExtensions.cs index 89a9f1f..a5804aa 100644 --- a/NetworkResurrector.Application/DependencyInjectionExtensions.cs +++ b/NetworkResurrector.Application/DependencyInjectionExtensions.cs @@ -12,6 +12,7 @@ namespace NetworkResurrector.Application services.AddSingleton(); services.AddScoped(); services.AddStores(); + services.AddSingleton(); } private static void AddStores(this IServiceCollection services) diff --git a/NetworkResurrector.Application/Events/MachinePinged.cs b/NetworkResurrector.Application/Events/MachinePinged.cs new file mode 100644 index 0000000..5acd12f --- /dev/null +++ b/NetworkResurrector.Application/Events/MachinePinged.cs @@ -0,0 +1,14 @@ +namespace NetworkResurrector.Application.Events +{ + public class MachinePinged + { + public bool Success { get; } + public string Status { get; } + + public MachinePinged(bool success, string status) + { + Success = success; + Status = status; + } + } +} diff --git a/NetworkResurrector.Application/Services/IPingService.cs b/NetworkResurrector.Application/Services/IPingService.cs new file mode 100644 index 0000000..2fcef1a --- /dev/null +++ b/NetworkResurrector.Application/Services/IPingService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace NetworkResurrector.Application.Services +{ + public interface IPingService + { + Task<(bool success, string status)> PingMachine(string ipAddress); + } +} \ No newline at end of file diff --git a/NetworkResurrector.Application/Services/PingService.cs b/NetworkResurrector.Application/Services/PingService.cs new file mode 100644 index 0000000..8589afa --- /dev/null +++ b/NetworkResurrector.Application/Services/PingService.cs @@ -0,0 +1,50 @@ +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace NetworkResurrector.Application.Services +{ + public class PingService : IPingService + { + /// + /// Ping machine by IP + /// https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping?redirectedfrom=MSDN&view=net-5.0 + /// + /// + /// (bool success, string status) + public async Task<(bool success, string status)> PingMachine(string ipAddress) + { + var ping = new Ping(); + + // Use the default Ttl value which is 128, + // but change the fragmentation behavior. + PingOptions options = new PingOptions + { + DontFragment = true + }; + + // Create a buffer of 32 bytes of data to be transmitted. + string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + byte[] buffer = Encoding.ASCII.GetBytes(data); + int timeout = 120; + + PingReply reply = await ping.SendPingAsync(ipAddress, timeout, buffer, options); + + if (reply.Status == IPStatus.Success) + { + var builder = new StringBuilder(); + builder.AppendLine($"Machine '{ipAddress}' has responded to ping."); + + builder.AppendLine($"Address: {reply.Address.ToString()}"); + builder.AppendLine($"RoundTrip time: {reply.RoundtripTime}"); + builder.AppendLine($"Time to live: {reply.Options.Ttl}"); + builder.AppendLine($"Don't fragment: {reply.Options.DontFragment}"); + builder.AppendLine($"Buffer size: {reply.Buffer.Length}"); + + return (true, builder.ToString()); + } + else + return (false, $"Machine '{ipAddress}' does not respond to ping. Status: {reply.Status}"); + } + } +}