Skip to content

Instantly share code, notes, and snippets.

@stephanGarland
Last active April 2, 2023 10:43
Show Gist options
  • Save stephanGarland/93c02385e344d8b338aab67a113dd1e2 to your computer and use it in GitHub Desktop.
Save stephanGarland/93c02385e344d8b338aab67a113dd1e2 to your computer and use it in GitHub Desktop.
Bespoke WOL as one of my Supermicros has issues reliably coming up, often requiring a few boot attempts
#!/usr/bin/env python3
import datetime
import os
import socket
import subprocess
import sys
import time
MAC_ADDR = "0025904F3A00"
BCAST_IP_ADDR = "255.255.255.255"
BACKUP_IP_ADDR = "192.168.1.208"
IPMI_IP_ADDR = "192.168.1.252"
IPMI_USERNAME = os.environ.get("IPMI_USERNAME")
IPMI_PASSWORD = os.environ.get("IPMI_PASSWORD")
SSH_PORT = 22
UDP_PORT = 9
PACKET = "FF" * 6 + MAC_ADDR * 16
sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Wake up
#sock_udp.sendto(bytes(PACKET, "utf-8"), (BCAST_IP_ADDR, UDP_PORT))
def fake_logger(level: str, msg: str) -> str:
"""A terrible implementation of logging.
I want to write to pyznap's log along with pyznap,
and don't want to try to deal with multiple file handles. By redirecting
the script's output to the logfile in shell, print() writes to it.
Also, this was intended to be a very quick script, but here we are.
Args:
level: A string, ideally following POSIX standards e.g. INFO, WARN, etc.
msg: A string of the message you want to log.
Returns:
A formatted string to log.
"""
return f"{datetime.datetime.now().strftime('%b %d %H:%M:%S')} {level} {msg}"
def ipmi_cmd(cmd: str):
"""Issues an IPMI command.
Uses ipmipower and subprocess to issue power commands to the host.
Args:
cmd: The option(complete with hyphens) you want to issue. See https://linux.die.net/man/8/ipmipower.
Returns:
Nothing.
"""
subprocess.run(
["ipmipower", "-h", IPMI_IP_ADDR, "-u", IPMI_USERNAME, "-p", IPMI_PASSWORD, cmd],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
def check_is_booted() -> bool:
"""Attempts to boot the hypervisor.
First issues a PWRON command, and then checks for the VM to come up.
If it fails (in my case, likely because the BIOS doesn't always see the NVMe),
it will repeatedly power cycle the hypervisor until success.
Args:
None.
Returns:
A boolean indicating whether or not the VM is up.
"""
print(fake_logger("INFO", "Starting hypervisor..."))
ipmi_cmd("--on")
# Should be adequate time to cold boot and start the VM
print(fake_logger("INFO", "Sleeping 180 seconds..."))
time.sleep(180)
# Check if it woke up
try:
print(fake_logger("INFO", "Trying to connect to VM on port 22..."))
sock_tcp.connect((BACKUP_IP_ADDR, SSH_PORT))
print(fake_logger("INFO", "Successfully connected - VM is up"))
except socket.error as e:
print(fake_logger("ERROR", "Unable to connect to port 22, failed to boot"))
return False
sock_tcp.close()
return True
def get_secrets() -> bool:
"""Ensures we can read secrets.
Ensures that SOPS correctly decrypted the secrets,
and placed them into the environment. Due to the usage of
os.environ.get(), they will be None if not present.
Args:
None.
Returns:
A boolean indicating success or failure.
"""
return all([x for x in [IPMI_USERNAME, IPMI_PASSWORD]])
if __name__ == "__main__":
if not get_secrets():
print(fake_logger("CRITICAL", "Couldn't read secrets, exiting"))
raise SystemExit
if sys.argv[1] == "start":
while not check_is_booted():
print(fake_logger("ERROR", "Powercycling hypervisor..."))
ipmi_cmd("--reset")
elif sys.argv[1] == "stop":
print(fake_logger("INFO", "Shutting down hypervisor..."))
ipmi_cmd("--soft")
@alfonsrv
Copy link

alfonsrv commented Apr 2, 2023

A small logger configuration fyi that streams both to console as well as to file

LOG_FORMAT = '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s():%(lineno)d] – %(message)s'
LOG_LEVEL = logging.INFO
logging.basicConfig(
    format=LOG_FORMAT,
    datefmt='%Y/%m/%d %H:%M:%S',
    level=LOG_LEVEL,
    handlers=[
        logging.StreamHandler(),
        logging.handlers.TimedRotatingFileHandler(
            LOG_FILE,
            encoding='utf-8',
            when="d",
            interval=1,
            backupCount=LOG_BACKUPS
        )
    ]
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment