Added shutdown functionalities
parent
a0c1d12647
commit
9c5f624dae
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
<Import Project="dependencies.props" />
|
<Import Project="dependencies.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.0.0.0</Version>
|
<Version>1.0.0.1</Version>
|
||||||
<Authors>Tudor Stanciu</Authors>
|
<Authors>Tudor Stanciu</Authors>
|
||||||
<Company>STA</Company>
|
<Company>STA</Company>
|
||||||
<PackageTags>NetworkResurrector</PackageTags>
|
<PackageTags>NetworkResurrector</PackageTags>
|
||||||
|
|
|
@ -46,5 +46,12 @@ namespace NetworkResurrector.Api.Controllers
|
||||||
var result = await _mediator.Send(pingMachine);
|
var result = await _mediator.Send(pingMachine);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("shutdown")]
|
||||||
|
public async Task<IActionResult> ShutdownMachine([FromBody] ShutdownMachine shutdownMachine)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(shutdownMachine);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,8 @@
|
||||||
"Use": "Inhouse",
|
"Use": "Inhouse",
|
||||||
"Options": [ "Inhouse", "Nikeee" ]
|
"Options": [ "Inhouse", "Nikeee" ]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Shutdown": {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ namespace NetworkResurrector.Application.CommandHandlers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Start pinging '{command.IPAddress}'.");
|
_logger.LogDebug($"Start pinging '{command.IPAddressOrMachineName}'.");
|
||||||
var (success, status) = await _pingService.PingMachine(command.IPAddress);
|
var (success, status) = await _pingService.PingMachine(command.IPAddressOrMachineName);
|
||||||
_logger.LogDebug($"Pinging on '{command.IPAddress}' finished. Status: {status}");
|
_logger.LogDebug($"Pinging on '{command.IPAddressOrMachineName}' finished. Status: {status}");
|
||||||
return new MachinePinged(success, status);
|
return new MachinePinged(success, status);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
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 ShutdownMachineHandler : IRequestHandler<ShutdownMachine, MachineShutdown>
|
||||||
|
{
|
||||||
|
private readonly ILogger<ShutdownMachineHandler> _logger;
|
||||||
|
private readonly IShutdownService _shutdownService;
|
||||||
|
|
||||||
|
public ShutdownMachineHandler(ILogger<ShutdownMachineHandler> logger, IShutdownService shutdownService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_shutdownService = shutdownService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MachineShutdown> Handle(ShutdownMachine command, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await Task.Run(() => Handle(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MachineShutdown Handle(ShutdownMachine command)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogDebug($"Start shutting down '{command.IPAddressOrMachineName}'.");
|
||||||
|
var status = _shutdownService.ShutdownMachine(command.IPAddressOrMachineName);
|
||||||
|
_logger.LogDebug($"Shutting down machine '{command.IPAddressOrMachineName}' finished. Status: {status}");
|
||||||
|
return new MachineShutdown(true, status);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var correlationIdMsg = $"CorrelationId: {command.Metadata.CorrelationId}";
|
||||||
|
_logger.LogError(ex, $"An unexpected error has occurred. {correlationIdMsg}");
|
||||||
|
return new MachineShutdown(false, $"{ex.Message} {correlationIdMsg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,11 @@ namespace NetworkResurrector.Application.Commands
|
||||||
{
|
{
|
||||||
public class PingMachine : Command<MachinePinged>
|
public class PingMachine : Command<MachinePinged>
|
||||||
{
|
{
|
||||||
public string IPAddress { get; set; }
|
public string IPAddressOrMachineName { get; set; }
|
||||||
|
|
||||||
public PingMachine(string ipAddress) : base(new Metadata() { CorrelationId = Guid.NewGuid() })
|
public PingMachine(string ipAddressOrMachineName) : base(new Metadata() { CorrelationId = Guid.NewGuid() })
|
||||||
{
|
{
|
||||||
IPAddress = ipAddress;
|
IPAddressOrMachineName = ipAddressOrMachineName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using NetworkResurrector.Application.Events;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Commands
|
||||||
|
{
|
||||||
|
public class ShutdownMachine : Command<MachineShutdown>
|
||||||
|
{
|
||||||
|
public string IPAddressOrMachineName { get; set; }
|
||||||
|
|
||||||
|
public ShutdownMachine(string ipAddressOrMachineName) : base(new Metadata() { CorrelationId = Guid.NewGuid() })
|
||||||
|
{
|
||||||
|
IPAddressOrMachineName = ipAddressOrMachineName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,9 @@ namespace NetworkResurrector.Application
|
||||||
services.AddSingleton<IParamProvider, ParamProvider>();
|
services.AddSingleton<IParamProvider, ParamProvider>();
|
||||||
services.AddScoped<IUserService, UserService>();
|
services.AddScoped<IUserService, UserService>();
|
||||||
services.AddStores();
|
services.AddStores();
|
||||||
|
services.AddSingleton<IValidationService, ValidationService>();
|
||||||
services.AddSingleton<IPingService, PingService>();
|
services.AddSingleton<IPingService, PingService>();
|
||||||
|
services.AddSingleton<IShutdownService, ShutdownService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddStores(this IServiceCollection services)
|
private static void AddStores(this IServiceCollection services)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Events
|
||||||
|
{
|
||||||
|
public class MachineShutdown
|
||||||
|
{
|
||||||
|
public bool Success { get; }
|
||||||
|
public string Status { get; }
|
||||||
|
|
||||||
|
public MachineShutdown(bool success, string status)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||||
|
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -4,6 +4,6 @@ namespace NetworkResurrector.Application.Services
|
||||||
{
|
{
|
||||||
public interface IPingService
|
public interface IPingService
|
||||||
{
|
{
|
||||||
Task<(bool success, string status)> PingMachine(string ipAddress);
|
Task<(bool success, string status)> PingMachine(string ipAddressOrMachineName);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace NetworkResurrector.Application.Services
|
||||||
|
{
|
||||||
|
public interface IShutdownService
|
||||||
|
{
|
||||||
|
string ShutdownMachine(string ipAddressOrMachineName);
|
||||||
|
string ShutdownMachineWithManagementScope(string ipAddressOrMachineName);
|
||||||
|
string ShutdownMachineWithManagementScope(string ipAddressOrMachineName, string user, string password, string domain);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace NetworkResurrector.Application.Services
|
||||||
|
{
|
||||||
|
public interface IValidationService
|
||||||
|
{
|
||||||
|
bool IsValidIPAddress(string ipAddress);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,9 @@ namespace NetworkResurrector.Application.Services
|
||||||
/// Ping machine by IP
|
/// Ping machine by IP
|
||||||
/// https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping?redirectedfrom=MSDN&view=net-5.0
|
/// https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping?redirectedfrom=MSDN&view=net-5.0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ipAddress"></param>
|
/// <param name="ipAddressOrMachineName"></param>
|
||||||
/// <returns>(bool success, string status)</returns>
|
/// <returns>(bool success, string status)</returns>
|
||||||
public async Task<(bool success, string status)> PingMachine(string ipAddress)
|
public async Task<(bool success, string status)> PingMachine(string ipAddressOrMachineName)
|
||||||
{
|
{
|
||||||
var ping = new Ping();
|
var ping = new Ping();
|
||||||
|
|
||||||
|
@ -28,12 +28,12 @@ namespace NetworkResurrector.Application.Services
|
||||||
byte[] buffer = Encoding.ASCII.GetBytes(data);
|
byte[] buffer = Encoding.ASCII.GetBytes(data);
|
||||||
int timeout = 120;
|
int timeout = 120;
|
||||||
|
|
||||||
PingReply reply = await ping.SendPingAsync(ipAddress, timeout, buffer, options);
|
PingReply reply = await ping.SendPingAsync(ipAddressOrMachineName, timeout, buffer, options);
|
||||||
|
|
||||||
if (reply.Status == IPStatus.Success)
|
if (reply.Status == IPStatus.Success)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
builder.AppendLine($"Machine '{ipAddress}' has responded to ping.");
|
builder.AppendLine($"Machine '{ipAddressOrMachineName}' has responded to ping.");
|
||||||
|
|
||||||
builder.AppendLine($"Address: {reply.Address}");
|
builder.AppendLine($"Address: {reply.Address}");
|
||||||
builder.AppendLine($"RoundTrip time: {reply.RoundtripTime}");
|
builder.AppendLine($"RoundTrip time: {reply.RoundtripTime}");
|
||||||
|
@ -44,7 +44,7 @@ namespace NetworkResurrector.Application.Services
|
||||||
return (true, builder.ToString());
|
return (true, builder.ToString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return (false, $"Machine '{ipAddress}' does not respond to ping. Status: {reply.Status}");
|
return (false, $"Machine '{ipAddressOrMachineName}' does not respond to ping. Status: {reply.Status}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Services
|
||||||
|
{
|
||||||
|
public class ShutdownService : IShutdownService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb491003(v=technet.10)?redirectedfrom=MSDN
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ipAddress"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string ShutdownMachine(string ipAddressOrMachineName)
|
||||||
|
{
|
||||||
|
Process commandProcess = new Process();
|
||||||
|
|
||||||
|
commandProcess.StartInfo.FileName = "cmd.exe";
|
||||||
|
commandProcess.StartInfo.UseShellExecute = false;
|
||||||
|
commandProcess.StartInfo.CreateNoWindow = true;
|
||||||
|
commandProcess.StartInfo.RedirectStandardError = true;
|
||||||
|
commandProcess.StartInfo.RedirectStandardInput = true;
|
||||||
|
commandProcess.StartInfo.RedirectStandardOutput = true;
|
||||||
|
commandProcess.Start();
|
||||||
|
commandProcess.StandardInput.WriteLine($"shutdown /r /m {ipAddressOrMachineName} /t 200 /f");
|
||||||
|
commandProcess.StandardInput.WriteLine("exit");
|
||||||
|
|
||||||
|
for (; !commandProcess.HasExited;) //wait executed
|
||||||
|
{
|
||||||
|
System.Threading.Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
string errorOutput = commandProcess.StandardError.ReadToEnd();
|
||||||
|
string output = commandProcess.StandardOutput.ReadToEnd();
|
||||||
|
|
||||||
|
if (commandProcess != null)
|
||||||
|
commandProcess.Dispose();
|
||||||
|
|
||||||
|
return $"Output:{Environment.NewLine}{output}{Environment.NewLine}{Environment.NewLine}Errors:{Environment.NewLine}{errorOutput}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ShutdownMachineWithManagementScope(string ipAddressOrMachineName)
|
||||||
|
=> ShutdownMachineWithManagementScope(ipAddressOrMachineName, false, null, null, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/system.management.managementscope(v=vs.110).aspx
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ipAddress"></param>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="password"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string ShutdownMachineWithManagementScope(string ipAddressOrMachineName, string user, string password, string domain)
|
||||||
|
=> ShutdownMachineWithManagementScope(ipAddressOrMachineName, true, user, password, domain);
|
||||||
|
|
||||||
|
private string ShutdownMachineWithManagementScope(string ipAddressOrMachineName, bool useSpecificCredentials, string user, string password, string domain)
|
||||||
|
{
|
||||||
|
Validate(ipAddressOrMachineName, useSpecificCredentials, user, password, domain);
|
||||||
|
/* Build an options object for the remote connection
|
||||||
|
* if you plan to connect to the remote computer with a different user name and password than the one you are currently using
|
||||||
|
|
||||||
|
ConnectionOptions options = new ConnectionOptions();
|
||||||
|
|
||||||
|
* and then set the options.Username and options.Password properties to the correct values
|
||||||
|
* and also set options.Authority = "ntlmdomain:DOMAIN";
|
||||||
|
* and replace DOMAIN with the remote computer's domain. You can also use Kerberos instead of ntlmdomain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ConnectionOptions options = new ConnectionOptions
|
||||||
|
{
|
||||||
|
Username = user,
|
||||||
|
Password = password,
|
||||||
|
Authority = $"ntlmdomain:{domain}"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a connection to a remote computer.
|
||||||
|
ManagementScope scope = new ManagementScope($"\\\\{ipAddressOrMachineName}\\root\\cimv2", options);
|
||||||
|
scope.Connect();
|
||||||
|
|
||||||
|
//Query system for Operating System information
|
||||||
|
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
|
||||||
|
|
||||||
|
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
|
||||||
|
ManagementObjectCollection queryCollection = searcher.Get();
|
||||||
|
|
||||||
|
foreach (ManagementObject obj in queryCollection)
|
||||||
|
{
|
||||||
|
// Display the remote computer information
|
||||||
|
Console.WriteLine("Computer Name : {0}", obj["csname"]);
|
||||||
|
Console.WriteLine("Windows Directory : {0}", obj["WindowsDirectory"]);
|
||||||
|
Console.WriteLine("Operating System: {0}", obj["Caption"]);
|
||||||
|
Console.WriteLine("Version: {0}", obj["Version"]);
|
||||||
|
Console.WriteLine("Manufacturer : {0}", obj["Manufacturer"]);
|
||||||
|
|
||||||
|
obj.InvokeMethod("ShutDown", null); //shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
return "x";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Validate(string ipAddressOrMachineName, bool useSpecificCredentials, string user, string password, string domain)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(ipAddressOrMachineName))
|
||||||
|
throw new ArgumentException("The provided ipAddressOrMachineName input is null or empty.", nameof(ipAddressOrMachineName));
|
||||||
|
|
||||||
|
if (useSpecificCredentials && OneOfStringsIsNullOrEmpty(user, password, domain))
|
||||||
|
throw new ArgumentException($"One of user, password or domain inputs is null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OneOfStringsIsNullOrEmpty(params string[] inputs)
|
||||||
|
{
|
||||||
|
return inputs.Any(z => string.IsNullOrEmpty(z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Services
|
||||||
|
{
|
||||||
|
public class ValidationService : IValidationService
|
||||||
|
{
|
||||||
|
private readonly ILogger<ValidationService> _logger;
|
||||||
|
|
||||||
|
public ValidationService(ILogger<ValidationService> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidIPAddress(string ipAddress)
|
||||||
|
{
|
||||||
|
if (IPAddress.TryParse(ipAddress, out IPAddress address))
|
||||||
|
{
|
||||||
|
switch (address.AddressFamily)
|
||||||
|
{
|
||||||
|
case System.Net.Sockets.AddressFamily.InterNetwork:
|
||||||
|
_logger.LogDebug($"IP address '{ipAddress}' is v4.");
|
||||||
|
break;
|
||||||
|
case System.Net.Sockets.AddressFamily.InterNetworkV6:
|
||||||
|
_logger.LogDebug($"IP address '{ipAddress}' is v6.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_logger.LogWarning($"IP address '{ipAddress}' is valid but is not v4 or v6! Family: [{address.AddressFamily}]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue