225 lines
6.9 KiB
Zig
225 lines
6.9 KiB
Zig
// Depends on having IPMItool installed
|
|
|
|
const std = @import("std");
|
|
|
|
const IPMIHOST = "192.168.1.233";
|
|
const IPMIUSER = "root";
|
|
const IPMIPASS = "<change>";
|
|
|
|
// Build IPMI command
|
|
fn executeIpmiCommand(full_command: []const u8) !void {
|
|
const allocator = std.heap.page_allocator;
|
|
|
|
var args = std.ArrayList([]const u8).init(allocator);
|
|
defer args.deinit();
|
|
|
|
try args.appendSlice(&.{
|
|
"ipmitool",
|
|
"-I",
|
|
"lanplus",
|
|
"-H",
|
|
IPMIHOST,
|
|
"-U",
|
|
IPMIUSER,
|
|
"-P",
|
|
IPMIPASS,
|
|
});
|
|
|
|
var cmd_iter = std.mem.splitSequence(u8, full_command, " ");
|
|
while (cmd_iter.next()) |part| {
|
|
if (part.len > 0) try args.append(part);
|
|
}
|
|
|
|
const result = try std.process.Child.run(.{
|
|
.allocator = allocator,
|
|
.argv = args.items,
|
|
.max_output_bytes = 1024 * 1024,
|
|
});
|
|
|
|
std.debug.print("{s}\n", .{result.stdout});
|
|
if (result.stderr.len > 0) {
|
|
std.debug.print("{s}\n", .{result.stderr});
|
|
}
|
|
|
|
if (result.term != .Exited or result.term.Exited != 0) {
|
|
return error.CommandFailed;
|
|
}
|
|
}
|
|
|
|
// Get server temperature
|
|
fn getServerTemperature() !f32 {
|
|
const allocator = std.heap.page_allocator;
|
|
|
|
const result = try std.process.Child.run(.{
|
|
.allocator = allocator,
|
|
.argv = &.{ "ipmitool", "-I", "lanplus", "-H", IPMIHOST, "-U", IPMIUSER, "-P", IPMIPASS, "sdr", "type", "temperature" },
|
|
.max_output_bytes = 1024 * 1024,
|
|
});
|
|
|
|
var temps = std.ArrayList(f32).init(allocator);
|
|
defer temps.deinit();
|
|
|
|
var lines = std.mem.splitSequence(u8, result.stdout, "\n");
|
|
while (lines.next()) |line| {
|
|
var cols = std.mem.splitSequence(u8, line, "|");
|
|
while (cols.next()) |col| {
|
|
if (std.mem.indexOf(u8, col, "degrees C")) |_| {
|
|
var tokens = std.mem.tokenizeAny(u8, col, " ");
|
|
while (tokens.next()) |t| {
|
|
if (std.fmt.parseFloat(f32, t)) |temp| {
|
|
try temps.append(temp);
|
|
} else |_| {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (temps.items.len == 0) return error.NoData;
|
|
|
|
var sum: f32 = 0;
|
|
for (temps.items) |t| sum += t;
|
|
return sum / @as(f32, @floatFromInt(temps.items.len));
|
|
}
|
|
|
|
// Power controls
|
|
fn powerOn() !void {
|
|
std.debug.print("Powering on the server...\n", .{});
|
|
try executeIpmiCommand("chassis power on");
|
|
}
|
|
|
|
fn powerOff() !void {
|
|
std.debug.print("Powering off the server...\n", .{});
|
|
try executeIpmiCommand("chassis power off");
|
|
std.debug.print("Enabling dynamic fan control...\n", .{});
|
|
try executeIpmiCommand("raw 0x30 0x30 0x01 0x01");
|
|
}
|
|
|
|
// Fan controls
|
|
fn setFanSpeed(speed: u8) !void {
|
|
std.debug.print("Setting fan speed to {d}%\n", .{speed});
|
|
const cmd = try std.fmt.allocPrint(std.heap.page_allocator, "raw 0x30 0x30 0x02 0xff 0x{x:0>2}", .{speed});
|
|
defer std.heap.page_allocator.free(cmd);
|
|
try executeIpmiCommand(cmd);
|
|
}
|
|
|
|
fn enableDynamicFanControl() !void {
|
|
std.debug.print("Enabling dynamic fan control...\n", .{});
|
|
try executeIpmiCommand("raw 0x30 0x30 0x01 0x01");
|
|
}
|
|
|
|
fn disableDynamicFanControl() !void {
|
|
std.debug.print("Disabling dynamic fan control...\n", .{});
|
|
try executeIpmiCommand("raw 0x30 0x30 0x01 0x00");
|
|
}
|
|
|
|
// Fan speed logic
|
|
fn adjustFanSpeedBasedOnTemp() !void {
|
|
const temp = try getServerTemperature();
|
|
std.debug.print("Current temperature: {d:.1} degrees C\n", .{temp});
|
|
const warning_threshold: f32 = 75.0;
|
|
const critical_threshold: f32 = 90.0;
|
|
var fan_speed: u8 = 40;
|
|
|
|
if (temp >= critical_threshold) {
|
|
std.debug.print("Temperature critical, fans at 100%\n", .{});
|
|
fan_speed = 100;
|
|
} else if (temp >= warning_threshold) {
|
|
std.debug.print("Temperature high, fans at 80%\n", .{});
|
|
fan_speed = 80;
|
|
} else if (temp >= 40.0) {
|
|
std.debug.print("Temperature moderate, fans at 60%\n", .{});
|
|
fan_speed = 60;
|
|
} else if (temp >= 30.0) {
|
|
std.debug.print("Temperature low, fans at 50%\n", .{});
|
|
fan_speed = 50;
|
|
} else {
|
|
std.debug.print("Temperature very low, fans at 40%\n", .{});
|
|
}
|
|
try setFanSpeed(fan_speed);
|
|
}
|
|
|
|
// Main function
|
|
pub fn main() !void {
|
|
const args = try std.process.argsAlloc(std.heap.page_allocator);
|
|
defer std.process.argsFree(std.heap.page_allocator, args);
|
|
|
|
var power: ?[]const u8 = null;
|
|
var fan: ?u8 = null;
|
|
var temp: bool = false;
|
|
var dynamic: ?[]const u8 = null;
|
|
|
|
var i: usize = 1;
|
|
while (i < args.len) : (i += 1) {
|
|
if (std.mem.eql(u8, args[i], "-p") or std.mem.eql(u8, args[i], "--power")) {
|
|
i += 1;
|
|
if (i < args.len) {
|
|
power = args[i];
|
|
} else {
|
|
std.debug.print("Missing argument for -p/--power\n", .{});
|
|
return error.InvalidArgs;
|
|
}
|
|
} else if (std.mem.eql(u8, args[i], "-f") or std.mem.eql(u8, args[i], "--fan")) {
|
|
i += 1;
|
|
if (i < args.len) {
|
|
fan = std.fmt.parseInt(u8, args[i], 10) catch {
|
|
std.debug.print("Invalid fan speed\n", .{});
|
|
return error.InvalidArgs;
|
|
};
|
|
if (fan.? > 100) {
|
|
std.debug.print("Fan speed must be 0-100\n", .{});
|
|
return error.InvalidArgs;
|
|
}
|
|
} else {
|
|
std.debug.print("Missing argument for -f/--fan\n", .{});
|
|
return error.InvalidArgs;
|
|
}
|
|
} else if (std.mem.eql(u8, args[i], "-t") or std.mem.eql(u8, args[i], "--temp")) {
|
|
temp = true;
|
|
} else if (std.mem.eql(u8, args[i], "-d") or std.mem.eql(u8, args[i], "--dynamic")) {
|
|
i += 1;
|
|
if (i < args.len) {
|
|
dynamic = args[i];
|
|
} else {
|
|
std.debug.print("Missing argument for -d/--dynamic\n", .{});
|
|
return error.InvalidArgs;
|
|
}
|
|
} else {
|
|
std.debug.print("Unknown argument: {s}\n", .{args[i]});
|
|
return error.InvalidArgs;
|
|
}
|
|
}
|
|
|
|
if (power) |pwr| {
|
|
if (std.mem.eql(u8, pwr, "on")) {
|
|
try powerOn();
|
|
} else if (std.mem.eql(u8, pwr, "off")) {
|
|
try powerOff();
|
|
} else {
|
|
std.debug.print("Invalid power option: {s}\n", .{pwr});
|
|
return error.InvalidArgs;
|
|
}
|
|
}
|
|
|
|
if (fan) |spd| {
|
|
try setFanSpeed(spd);
|
|
}
|
|
|
|
if (temp) {
|
|
try adjustFanSpeedBasedOnTemp();
|
|
}
|
|
|
|
if (dynamic) |dyn| {
|
|
if (std.mem.eql(u8, dyn, "on")) {
|
|
try enableDynamicFanControl();
|
|
} else if (std.mem.eql(u8, dyn, "off")) {
|
|
try disableDynamicFanControl();
|
|
} else {
|
|
std.debug.print("Invalid dynamic option: {s}\n", .{dyn});
|
|
return error.InvalidArgs;
|
|
}
|
|
}
|
|
|
|
if (args.len == 1) {
|
|
std.debug.print("Usage: {s} [-p on|off] [-f 0-100] [-t] [-d on|off]\n", .{args[0]});
|
|
}
|
|
}
|