Skip to content

Commit

Permalink
Merge pull request #5 from Steveiwonder/develop
Browse files Browse the repository at this point in the history
Add free memory healthcheck
  • Loading branch information
Steveiwonder authored Aug 13, 2023
2 parents d5b32be + 76a997d commit 860435f
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 4 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ jobs:

- name: Build & Publish WSM.Client
run: dotnet publish "./WSM.Client/WSM.Client.csproj" -o ./build/wsm.client/ -c Release

- name: Build & Copy Health Checks
run: |
dotnet build "./WSM.HealthChecks/WSM.HealthChecks.csproj" -c Release
mkdir -p ./build/wsm.client/healthchecks
cp ./WSM.HealthChecks/bin/Release/net6.0/WSM.HealthChecks.dll ./build/wsm.client/healthchecks/WSM.HealthChecks.dll
- name: Build WSM.Server Docker
uses: docker/build-push-action@v4
Expand All @@ -50,8 +57,7 @@ jobs:
cp ./WSM.Client/install-service.ps1 ./build/wsm.client/install-service.ps1
cp ./WSM.Client/uninstall-service.ps1 ./build/wsm.client/uninstall-service.ps1
- name: Create wsm.client.zip
if: github.ref == 'refs/heads/master'
- name: Create wsm.client.zip
run: zip -r ./build/wsm.client.zip ./build/wsm.client

- name: Create release
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
WSM is a service for monitoring different aspects of a Windows server and alerting when certain conditions are met.

### What can WSM monitor?
See [Health Check Types](#health-check-types) for more detail but in a nutshell processes, ports, docker containers and disk space & http request for now.
See [Health Check Types](#health-check-types) for more detail but in a nutshell processes, ports, docker containers and disk space, free memory & http request for now.

### Why?
I had a server which ran lots of different services, Plex, Game services, VPN, DNS and a bunch of docker containers and inevitably something would eventually fail, I wouldn't usually find this out until someone using one of the services let me know. I wanted a tool that was free and easy to set up but couldn't find one that did everything I wanted, also I like coding so I figured it was a good candidate for a project, 3 days later WSM was born.
Expand Down Expand Up @@ -272,6 +272,18 @@ Sends a HTTP request and check for the correct status code, it can also optional
- `RequestBody` (Optional) - The payload that can be sent, make sure you change the `Method` to the appropriate value
- `ExpectedResponseBody` (Optional) - An expected response body to validate upon request completion

### FreeMemory
Checks the system for free memory
```json
{
"Name": "Free Memory",
"Type": "Free Memory",
"Interval": "00:01:00",
"MinimumFreeMemory": 100000
}
```
- `MinimumFreeMemory` (Required) - The minimum number of free bytes you expect the server to have

### Installing the Windows service
Run `install-service.ps1` inside `C:\wsm.client\`, this will install and start the service

Expand All @@ -286,4 +298,4 @@ Put nginx in front of it and proxy to 443->80 on the docker image
2. ???

### What if a health check type isn't supported?
Create your own or raise an issue on github. Take a look at WSM.PluginExample to see how you write your own health check. Once you've compiled it, drop it into the clients install direction\HealthChecks alongside `WSM.HealthChecks.dll`
Create your own or raise an issue on github. Take a look at WSM.PluginExample to see how you write your own health check. Once you've compiled it, drop it into the clients install directory\HealthChecks alongside `WSM.HealthChecks.dll`
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using WSM.Client.Models;

namespace WSM.PluginExample
{
public class FreeMemoryHealthCheckConfiguration: HealthCheckConfigurationBase
{
public long MinimumFreeMemory { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using WSM.Client.Models;
using WSM.HealthChecks.FreeMemoryHealthCheck;

namespace WSM.PluginExample
{
public class FreeMemoryHealthCheckDefinition : HealthCheckDefinitionBase<FreeMemoryHealthCheckJob, FreeMemoryHealthCheckConfiguration>
{
public override string Type => "FreeMemory";
}
}
129 changes: 129 additions & 0 deletions WSM.HealthChecks/FreeMemoryHealthCheck/FreeMemoryHealthCheckJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using Microsoft.Extensions.Logging;
using Quartz;
using WSM.Client.Jobs;
using WSM.Shared;
using WSM.PluginExample;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace WSM.HealthChecks.FreeMemoryHealthCheck
{
[DisallowConcurrentExecution]
public class FreeMemoryHealthCheckJob : HealthCheckJobBase
{
private readonly ILogger<FreeMemoryHealthCheckJob> _logger;
private const string MemoryLowStatus = "Free memory low";
public FreeMemoryHealthCheckJob(ILogger<FreeMemoryHealthCheckJob> logger, WSMApiClient apiClient) : base(apiClient)
{
_logger = logger;
}
public override async Task Execute(IJobExecutionContext context)
{
var healthCheckConfiguration = GetConfiguration<FreeMemoryHealthCheckConfiguration>(context);
try
{
var freeSystemMemory = GetFreeSystemMemory();

if (freeSystemMemory < healthCheckConfiguration.MinimumFreeMemory)
{
string mbAvailable = FormatUtils.SizeSuffix(freeSystemMemory);
await CheckIn(healthCheckConfiguration, $"{MemoryLowStatus}, {mbAvailable} available");
return;
}

await CheckIn(healthCheckConfiguration, Constants.AvailableStatus);
}
catch (Exception ex)
{
_logger.LogError(ex, "");
}
}

private long GetFreeSystemMemory()
{

MemoryMetricsClient client = new MemoryMetricsClient();
return client.GetMetrics().Free;
}

public class MemoryMetrics
{
public long Total;
public long Used;
public long Free;
}

public class MemoryMetricsClient
{
public MemoryMetrics GetMetrics()
{
if (IsUnix())
{
return GetUnixMetrics();
}

return GetWindowsMetrics();
}

private bool IsUnix()
{
var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux);

return isUnix;
}

private MemoryMetrics GetWindowsMetrics()
{
var output = "";

var info = new ProcessStartInfo();
info.FileName = "wmic";
info.Arguments = "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value";
info.RedirectStandardOutput = true;

using (var process = Process.Start(info))
{
output = process.StandardOutput.ReadToEnd();
}

var lines = output.Trim().Split("\n");
var freeMemoryParts = lines[0].Split("=", StringSplitOptions.RemoveEmptyEntries);
var totalMemoryParts = lines[1].Split("=", StringSplitOptions.RemoveEmptyEntries);

var metrics = new MemoryMetrics();
metrics.Total = long.Parse(totalMemoryParts[1]) * 1000;
metrics.Free =long.Parse(freeMemoryParts[1]) * 1000;
metrics.Used = metrics.Total - metrics.Free;

return metrics;
}

private MemoryMetrics GetUnixMetrics()
{
var output = "";

var info = new ProcessStartInfo("free -m");
info.FileName = "/bin/bash";
info.Arguments = "-c \"free -m\"";
info.RedirectStandardOutput = true;

using (var process = Process.Start(info))
{
output = process.StandardOutput.ReadToEnd();
Console.WriteLine(output);
}

var lines = output.Split("\n");
var memory = lines[1].Split(" ", StringSplitOptions.RemoveEmptyEntries);

var metrics = new MemoryMetrics();
metrics.Total = long.Parse(memory[1]);
metrics.Used = long.Parse(memory[2]);
metrics.Free = long.Parse(memory[3]);

return metrics;
}
}
}
}

0 comments on commit 860435f

Please sign in to comment.