Last active
April 2, 2023 10:47
-
-
Save alfonsrv/063447338a51579ab8df46980bebbab6 to your computer and use it in GitHub Desktop.
Start and stop Supermicro servers for replica job via IPMI – since wake on lan does not work reliably for one of my servers. Uses the web interface directly so no ipmi binary is required – since supplying the username for ipmiutil didn't want to work reliably for me either
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | |
# Rau Systemberatung GmbH (www.rausys.de) | |
# Licensed under the GNU General Public License, Version 3 | |
import argparse | |
import re | |
from time import sleep | |
from base64 import b64encode | |
from urllib3.exceptions import InsecureRequestWarning | |
from typing import Literal | |
import requests | |
from requests import Session | |
# Suppress only the single warning from urllib3 needed. | |
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) | |
IPMI_HOST = '10.100.10.12' | |
IPMI_USER = 'admin' | |
IPMI_PASSWORD = 'hunter42' | |
IPMI_POST_HEADERS = { | |
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', | |
'Accept-Encoding': 'gzip, deflate, br', | |
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', | |
} | |
def login(*, user: str, password: str): | |
payload = { | |
'name': b64encode(user.encode()), | |
'pwd': b64encode(password.encode()), | |
'check': '00' | |
} | |
s.post( | |
f'https://{IPMI_HOST}/cgi/login.cgi', | |
data=payload, | |
headers=IPMI_POST_HEADERS | |
) | |
def csrf_token() -> str: | |
reply = s.get( | |
f'https://{IPMI_HOST}/cgi/url_redirect.cgi', | |
params={'url_name': 'topmenu'}, | |
) | |
token = re.search(r'\((?:\"CSRF_TOKEN\")\, \"(?P<token>.*)\"\);', reply.text) | |
return token.groupdict()['token'] | |
def is_online() -> bool: | |
payload = { | |
'op': 'GET_POWER_INFO.XML', | |
'r': '(0,0)' | |
} | |
reply = s.post( | |
f'https://{IPMI_HOST}/cgi/ipmi.cgi', | |
data=payload, | |
headers=IPMI_POST_HEADERS | |
) | |
status = re.search(r'POWER STATUS=\"(?P<status>.*)\"', reply.text) | |
return status.groupdict()['status'] == 'ON' | |
def set_power(*, status: Literal['(1,1)', '(1,3)', '(1,5)']) -> None: | |
""" (1,1) power up; (1,5) power down; (1,3) reset """ | |
payload = { | |
'op': 'SET_POWER_INFO.XML', | |
'r': status | |
} | |
reply = s.post( | |
f'https://{IPMI_HOST}/cgi/ipmi.cgi', | |
data=payload, | |
headers=IPMI_POST_HEADERS | |
) | |
return reply | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser() | |
opts = parser.add_mutually_exclusive_group(required=True) | |
opts.add_argument('--start', help='Turn on Supermicro server via IPMI', action='store_true') | |
opts.add_argument('--stop', help='Turn off Supermicro server via IPMI', action='store_true') | |
opts.add_argument('--status', help='Get current power status of Supermicro server', action='store_true') | |
parser.add_argument('--no-wait', help='Do not wait for server to start before script exists', action='store_true') | |
# TODO: Maybe add specifying username, password + host via argparse too | |
args = parser.parse_args() | |
if not (args.start or args.stop or args.status): | |
print('No action specified; exiting...') | |
exit(1); | |
s = Session() | |
s.verify = False | |
s.get(f'https://{IPMI_HOST}/') | |
login(user=IPMI_USER, password=IPMI_PASSWORD) | |
csrf_token = csrf_token() | |
IPMI_POST_HEADERS.setdefault('CSRF_TOKEN', csrf_token) | |
if args.start: | |
if is_online(): | |
print(f'Server already online! Cannot start server ({IPMI_HOST})') | |
exit() | |
print(f'Starting Supermicro server via IPMI... ({IPMI_HOST})') | |
set_power(status='(1,1)') | |
if not args.no_wait: sleep(180) | |
elif args.stop: | |
if not is_online(): | |
print(f'Server already offline! Cannot shutdown server ({IPMI_HOST})') | |
exit() | |
print(f'Shutting down Supermicro server via IPMI... ({IPMI_HOST})') | |
set_power(status='(1,5)') | |
elif args.status: | |
status = 'Online' if is_online() else 'Offline' | |
print(f'Current status: {status} ({IPMI_HOST})') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment