Add scripts for managing VM and LXC container power states and fan control

- Implemented set_fanspeed.py for dynamic fan speed control based on temperature.
- Created ilo_poweroff.py and ilo_poweron.py for iLO power management.
- Added ipmi_poweroff.py and ipmi_poweron.py for IPMI power control.
- Developed vm_start.py, vm_stop.py, vm_restart.py, and vm_shutdown.py for VM and LXC management.
- Introduced wrapper scripts (start_wrapper.sh, stop_wrapper.sh) for simplified execution.
- Removed deprecated shell scripts for power control.
This commit is contained in:
2025-10-06 10:10:54 -04:00
parent 6fe5db1adc
commit 95f132bdf8
16 changed files with 227 additions and 45 deletions
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
import subprocess
import re
import sys
import datetime
import argparse
# IPMI settings
IPMIUSER = "<username>"
IPMIPW = "<password>"
IPMIEK = "0000000000000000000000000000000000000000"
# Fan / temperature settings
FANSPEED = 20 # percent
MAXTEMP = 37 # Celsius
def run_ipmi(args):
base = ["ipmitool", "-I", "lanplus", "-H", IPMIHOST, "-U", IPMIUSER, "-P", IPMIPW, "-y", IPMIEK]
cmd = base + args
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if p.returncode != 0:
raise RuntimeError(f"ipmitool failed: {' '.join(cmd)}\n{p.stderr.strip()}")
return p.stdout
def log_systemd(tag, msg):
try:
subprocess.run(["systemd-cat", "-t", tag], input=msg + "\n", text=True, check=True)
except Exception:
# fallback to stdout
ts_print(msg)
def ts_print(msg):
"""ts_print message prefixed with a timestamp (YYYY-MM-DD HH:MM:SS)."""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{now} {msg}")
def get_ambient_temp():
out = run_ipmi(["sdr", "type", "temperature"])
# find lines like "Exhuat Temp | 29 degrees C"
lines = [l for l in out.splitlines() if "Exhaust" in l and "degrees" in l]
if not lines:
raise RuntimeError("Exhaust temperature line not found in ipmitool output")
# Extract last number found in the last matching line (like the original)
field = lines[-1].split("|")[4]
#ts_print("Debug: field =", field)
m = re.search(r"(\d{1,3})", field)
if not m:
raise RuntimeError("Could not parse temperature from line: " + lines[-1])
return int(m.group(1))
def main():
# parse CLI args (IPMI host can be overridden with -H/--host)
parser = argparse.ArgumentParser(description="Set fan speed via IPMI")
parser.add_argument("-H", "--host", default="127.0.0.1", help="IPMI host/IP (default: %(default)s)")
args = parser.parse_args()
global IPMIHOST
# set global IPMIHOST so run_ipmi() uses the provided host
IPMIHOST = args.host
ts_print(f"{IPMIHOST}: Fan speed check for host {IPMIHOST}")
try:
temp = get_ambient_temp()
ts_print(f"{IPMIHOST}: Exhaust temperature: {temp} C")
except Exception as e:
ts_print(f"{IPMIHOST}: Error reading temperature: {e}")
sys.exit(1)
speed_hex = format(FANSPEED, "x")
if temp > MAXTEMP:
msg = f"{IPMIHOST}:Warning: Temperature is too high! Activating dynamic fan control! ({temp} C)"
log_systemd("R710-IPMI-TEMP", msg)
ts_print(msg)
run_ipmi(["raw", "0x30", "0x30", "0x01", "0x01"])
else:
okmsg = f"{IPMIHOST}: Temperature is OK ({temp} C)"
log_systemd("R710-IPMI-TEMP", okmsg)
ts_print(okmsg)
ts_print(f"{IPMIHOST}: Activating manual fan speeds! (requested %d%%)" % FANSPEED)
run_ipmi(["raw", "0x30", "0x30", "0x01", "0x00"])
run_ipmi(["raw", "0x30", "0x30", "0x02", "0xff", "0x" + speed_hex])
if __name__ == "__main__":
main()
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import argparse
import subprocess
import getpass
import sys
import datetime
def ts_print(msg):
"""ts_print message prefixed with a timestamp (YYYY-MM-DD HH:MM:SS)."""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{now} {msg}")
def run(cmd):
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"Command failed: {' '.join(cmd)}", file=sys.stderr)
sys.exit(e.returncode)
def main():
p = argparse.ArgumentParser(description="Run iLO REST commands (login, serverinfo, reboot)")
p.add_argument("-H", "--host", required=True, help="iLO host/IP")
p.add_argument("-a", "--action", default="ForceOff", choices=["ForceOff", "GracefulRestart", "Reset"],
help="reboot action to run (default: ForceOff)")
p.add_argument("--no-serverinfo", action="store_true", help="skip running serverinfo")
args = p.parse_args()
# login
ts_print(f"Sending {args.action} to {args.host}")
run(['ilorest', 'login', args.host, '-u', '<username>', '-p', '<password>'])
# optional serverinfo
if not args.no_serverinfo:
run(['ilorest', 'serverinfo'])
# power off / reboot action
run(['ilorest', 'reboot', args.action])
if __name__ == "__main__":
main()
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import argparse
import subprocess
import getpass
import sys
import datetime
def ts_print(msg):
"""ts_print message prefixed with a timestamp (YYYY-MM-DD HH:MM:SS)."""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{now} {msg}")
def run(cmd):
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"Command failed: {' '.join(cmd)}", file=sys.stderr)
sys.exit(e.returncode)
def main():
p = argparse.ArgumentParser(description="Login to iLO, show serverinfo and power on")
p.add_argument("-H", "--host", required=True, help="iLO host/IP")
p.add_argument("-a", "--action", default="On", choices=["On","Off","GracefulRestart","ForceOff","Reset"],
help="power/reboot action to run (default: On)")
p.add_argument("--no-serverinfo", action="store_true", help="skip running serverinfo")
args = p.parse_args()
ts_print(f"Sending {args.action} to {args.host}")
run(['ilorest', 'login', args.host, '-u', '<username>', '-p', '<password>'])
if not args.no_serverinfo:
run(['ilorest', 'serverinfo'])
run(['ilorest', 'reboot', args.action])
if __name__ == "__main__":
main()
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
import argparse
import subprocess
import sys
IPMIUSER = "<username>"
IPMIPW = "<password>"
IPMIEK = "0000000000000000000000000000000000000000"
def main():
p = argparse.ArgumentParser(description="Send IPMI power off")
p.add_argument("-H", "--host", required=True, help="IPMI host address")
args = p.parse_args()
cmd = [
"ipmitool",
"-I", "lanplus",
"-H", args.host,
"-U", IPMIUSER,
"-P", IPMIPW,
"-y", IPMIEK,
"power", "off",
]
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if proc.returncode != 0:
print("ipmitool failed:", proc.stderr.strip(), file=sys.stderr)
sys.exit(proc.returncode)
print(proc.stdout.strip())
if __name__ == "__main__":
main()
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import argparse
import subprocess
import sys
import datetime
def ts_print(msg):
"""ts_print message prefixed with a timestamp (YYYY-MM-DD HH:MM:SS)."""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{now} {msg}")
IPMIUSER = "<username>"
IPMIPW = "<password>"
IPMIEK = "0000000000000000000000000000000000000000"
def main():
p = argparse.ArgumentParser(description="Send IPMI power off")
p.add_argument("-H", "--host", required=True, help="IPMI host address")
args = p.parse_args()
cmd = [
"ipmitool",
"-I", "lanplus",
"-H", args.host,
"-U", IPMIUSER,
"-P", IPMIPW,
"-y", IPMIEK,
"power", "on",
]
print(f"Sending power on to {args.host}")
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if proc.returncode != 0:
print("ipmitool failed:", proc.stderr.strip(), file=sys.stderr)
sys.exit(proc.returncode)
print(proc.stdout.strip())
if __name__ == "__main__":
main()
-41
View File
@@ -1,41 +0,0 @@
#!/bin/bash
# IPMI IP address
IPMIHOST=<hostname>
# IPMI Username
IPMIUSER=<username
# IPMI Password
IPMIPW=<password>
# Your IPMI Encryption Key
IPMIEK=0<IPMI Encryption Key>
# Fan Speed / utilisation in percentage, for example 9 for 9% utilisation
# Please note that each fan can have a different rpm and will not all be the same speed
FANSPEED=10
# TEMPERATURE
# Change this to the temperature in Celsius you are comfortable with.
# If the temperature goes above the set degrees it will send raw IPMI command to enable dynamic fan control
MAXTEMP=30
# This variable sends a IPMI command to get the temperature, and outputs it as two digits.
# Do not edit unless you know what you do.
# Side note, if you are running ipmitool on the system you are controlling, you don't need to specify -H,-U,-P - from the OS installed on the host, ipmitool is assumed permitted. You only need host/user/pass for remote access.
TEMP=$(ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature |grep Ambient |grep degrees |grep -Po '\d{2}' | tail -1)
# Dont edit this, this converts decimal to hex
SPEEDHEX=$( printf "%x" $FANSPEED )
if [[ $TEMP > $MAXTEMP ]];
then
printf "Warning: Temperature is too high! Activating dynamic fan control! ($TEMP C)" | systemd-cat -t R710-IPMI-TEMP
echo "Warning: Temperature is too high! Activating dynamic fan control! ($TEMP C)"
# This sets the fans to auto mode, so the motherboard will set it to a speed that it will need do cool the server down
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x01
else
printf "Temperature is OK ($TEMP C)" | systemd-cat -t R710-IPMI-TEMP
printf "Activating manual fan speeds! (1560 RPM)"
# This sets the fans to manual mode
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x00
# This is where we set the slower, quite speed
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x02 0xff 0x$SPEEDHEX
fi
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
ipmitool -I lanplus -H 192.168.128.223 -U root -P Optimus0329 -y 0000000000000000000000000000000000000000 power off
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
ipmitool -I lanplus -H 192.168.128.223 -U root -P Optimus0329 -y 0000000000000000000000000000000000000000 power on