using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace ServerMonitorApp
{
/// Checks the available disk space on a remote server.
[DisplayName("Disk space check"), Description("Check the remaining free disk space"), DisplayWeight(11)]
public class DiskSpaceCheck : SshCheck
{
/// The command to execute. Must return the POSIX output format of df(1).
public override string Command => string.Format(DiskSpaceCommand, Device);
/// The command to execute, with a placeholder of {0} for the device to check.
protected string DiskSpaceCommand => "df -P -k {0}";
/// The device or file on the device to check.
public string Device { get; set; }
/// The minimum free space allowed for the check to pass.
public double MinFreeSpace { get; set; }
/// The storage units or percentage for MinFreeSpace.
public FreeSpaceUnits FreeSpaceUnits { get; set; }
public DiskSpaceCheck()
{
// Set general SSH check settings for disk space checks.
CheckExitCode = true;
ExitCode = 0;
}
/// Processes the output of the disk space check command.
protected override List ProcessCommandResult(string output, int exitCode)
{
// Check for general SSH failures.
List results = base.ProcessCommandResult(output, exitCode);
// If there was an error running the command, fail immediately.
if (results.Any(r => r.Failed))
return results;
/* Parse the command results, expected in the POSIX output format of df(1):
Filesystem 1024-blocks Used Available Capacity Mounted on
*/
// Split the output into lines and remove the header row.
List lines = output.Split('\n').ToList();
lines.RemoveAt(0);
// Make sure there is only a single line of output remaining.
if (lines.Count > 1)
{
results.Add(Fail("df output was more than one line: " + string.Join("\n", lines)));
}
else
{
// Split the string into tokens on whitespace.
string[] tokens = lines[0].Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
if (FreeSpaceUnits == FreeSpaceUnits.percent)
{
// Test on percentage: calculate the capacity free percent from the capacity used percent reported.
if (int.TryParse(tokens[4].Replace("%", ""), out int percent))
{
percent = 100 - percent;
string message = string.Format("Free disk space is {0}%", percent);
if (percent < MinFreeSpace)
results.Add(Fail(message));
else
results.Add(Pass(message));
}
else
{
results.Add(Fail("Unable to parse df output as integer: " + tokens[4].Replace("%", "")));
}
}
else
{
// Test on bytes: calculate the remaining available space from the reported available space.
if (int.TryParse(tokens[3], out int freeSpace))
{
// Available space is returned in KB. Convert to MB (our default unit).
freeSpace /= 1024;
// If the unit is GB, convert MB to GB.
if (FreeSpaceUnits == FreeSpaceUnits.GB)
freeSpace /= 1024;
string message = string.Format("Free disk space is {0} {1}", freeSpace, FreeSpaceUnits);
if (freeSpace < MinFreeSpace)
results.Add(Fail(message));
else
results.Add(Pass(message));
}
else
{
results.Add(Fail("Unable to parse df output as integer: " + tokens[3]));
}
}
}
return results;
}
/// Validates disk space check options.
public override string Validate(bool saving = true)
{
string message = base.Validate();
if (Device.IsNullOrEmpty())
message += "Device is required." + Environment.NewLine;
if (MinFreeSpace <= 0)
message += "Free space must be greater than 0." + Environment.NewLine;
else if (FreeSpaceUnits == FreeSpaceUnits.percent && MinFreeSpace > 100)
message += "Free space percent must be between 0 and 100." + Environment.NewLine;
return message;
}
}
/// The units to use when testing available disk space.
public enum FreeSpaceUnits
{
/// Tests available disk space in megabytes.
MB = 0,
/// Tests available disk space in gigabytes.
GB = 1,
/// Tests available disk space as a percentage of the total space.
percent = 2
}
}