Add various scripts for server power management and build process
- Introduced .gitignore to exclude unnecessary files - Added scripts for powering on/off servers via IPMI and iLO - Implemented multi_powercycle script to manage multiple servers - Created build script for packaging multi_powercycle with PyInstaller - Enhanced logging functionality across scripts Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/python3
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
import urllib3
|
||||
import redfish
|
||||
from datetime import datetime
|
||||
|
||||
def log(message, is_error=False):
|
||||
"""Print message with timestamp"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
if is_error:
|
||||
print(f"[{timestamp}] {message}", file=sys.stderr)
|
||||
else:
|
||||
print(f"[{timestamp}] {message}")
|
||||
|
||||
# Suppress SSL warnings for self-signed iLO certificates
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
parser = argparse.ArgumentParser(description="Power off an HPE server via iLO Redfish")
|
||||
parser.add_argument("hostname", help="iLO hostname or IP address")
|
||||
parser.add_argument("-c", "--credentials", default="/etc/ilo_credentials",
|
||||
help="Path to credentials file (default: /etc/ilo_credentials)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read credentials from file (chmod 600 recommended: sudo chmod 600 /etc/ilo_credentials)
|
||||
# File format:
|
||||
# [ilo]
|
||||
# username = root
|
||||
# password = yourpassword
|
||||
cred_path = args.credentials
|
||||
if not os.path.exists(cred_path):
|
||||
log(f"Error: credentials file not found: {cred_path}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(cred_path)
|
||||
try:
|
||||
LOGIN_ACCOUNT = config.get("ilo_credentials", "username", fallback="root")
|
||||
LOGIN_PASSWORD = config.get("ilo_credentials", "password")
|
||||
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
||||
log(f"Error reading credentials file: {e}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
BASE_URL = f"https://{args.hostname}"
|
||||
|
||||
REDFISHOBJ = redfish.RedfishClient(
|
||||
base_url=BASE_URL,
|
||||
username=LOGIN_ACCOUNT,
|
||||
password=LOGIN_PASSWORD,
|
||||
)
|
||||
REDFISHOBJ.login(auth="session")
|
||||
|
||||
# Discover the correct reset action URI from the system resource
|
||||
sys_response = REDFISHOBJ.get("/redfish/v1/Systems/1/")
|
||||
reset_uri = sys_response.dict["Actions"]["#ComputerSystem.Reset"]["target"]
|
||||
|
||||
body = {"ResetType": "PushPowerButton"} # iLO 4: PushPowerButton = graceful shutdown; use "ForceOff" for hard power cut
|
||||
response = REDFISHOBJ.post(reset_uri, body=body)
|
||||
|
||||
log(f"Status: {response.status}")
|
||||
if response.status >= 400:
|
||||
log(f"Error: {response.read}", is_error=True)
|
||||
|
||||
REDFISHOBJ.logout()
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/python3
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
import urllib3
|
||||
import redfish
|
||||
from datetime import datetime
|
||||
|
||||
def log(message, is_error=False):
|
||||
"""Print message with timestamp"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
if is_error:
|
||||
print(f"[{timestamp}] {message}", file=sys.stderr)
|
||||
else:
|
||||
print(f"[{timestamp}] {message}")
|
||||
|
||||
# Suppress SSL warnings for self-signed iLO certificates
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
parser = argparse.ArgumentParser(description="Power off an HPE server via iLO Redfish")
|
||||
parser.add_argument("hostname", help="iLO hostname or IP address")
|
||||
parser.add_argument("-c", "--credentials", default="/etc/ilo_credentials",
|
||||
help="Path to credentials file (default: /etc/ilo_credentials)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read credentials from file (chmod 600 recommended: sudo chmod 600 /etc/ilo_credentials)
|
||||
# File format:
|
||||
# [ilo]
|
||||
# username = root
|
||||
# password = yourpassword
|
||||
cred_path = args.credentials
|
||||
if not os.path.exists(cred_path):
|
||||
log(f"Error: credentials file not found: {cred_path}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(cred_path)
|
||||
try:
|
||||
LOGIN_ACCOUNT = config.get("ilo", "username", fallback="root")
|
||||
LOGIN_PASSWORD = config.get("ilo", "password")
|
||||
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
||||
log(f"Error reading credentials file: {e}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
BASE_URL = f"https://{args.hostname}"
|
||||
|
||||
REDFISHOBJ = redfish.RedfishClient(
|
||||
base_url=BASE_URL,
|
||||
username=LOGIN_ACCOUNT,
|
||||
password=LOGIN_PASSWORD,
|
||||
)
|
||||
REDFISHOBJ.login(auth="session")
|
||||
|
||||
# Discover the correct reset action URI from the system resource
|
||||
sys_response = REDFISHOBJ.get("/redfish/v1/Systems/1/")
|
||||
reset_uri = sys_response.dict["Actions"]["#ComputerSystem.Reset"]["target"]
|
||||
|
||||
body = {"ResetType": "PushPowerButton"} # iLO 4: PushPowerButton = graceful shutdown; use "ForceOff" for hard power cut
|
||||
response = REDFISHOBJ.post(reset_uri, body=body)
|
||||
|
||||
log(f"Status: {response.status}")
|
||||
if response.status >= 400:
|
||||
log(f"Error: {response.read}", is_error=True)
|
||||
|
||||
REDFISHOBJ.logout()
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
# Examples:
|
||||
# ./ipmi_multipoweron.py host1 host2 host3
|
||||
# ./ipmi_multipoweron.py host1 host2 host3 -c /path/to/credentials
|
||||
# ./ipmi_multipoweron.py -f serverlist.txt
|
||||
# ./ipmi_multipoweron.py -f serverlist.txt -c /path/to/credentials
|
||||
|
||||
def ts_print(msg):
|
||||
"""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}")
|
||||
|
||||
DEFAULT_EK = "0000000000000000000000000000000000000000"
|
||||
|
||||
|
||||
def find_credentials_file(provided_path):
|
||||
"""Find credentials file in standard locations if provided path doesn't exist."""
|
||||
# If provided path exists, use it
|
||||
if os.path.exists(provided_path):
|
||||
return provided_path
|
||||
|
||||
# Check standard locations
|
||||
standard_paths = [
|
||||
os.path.expanduser("~/credentials"),
|
||||
"/etc/credentials",
|
||||
]
|
||||
|
||||
for path in standard_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
# Return the provided path (will fail with proper error message later)
|
||||
return provided_path
|
||||
|
||||
|
||||
def load_credentials(path):
|
||||
cred_path = find_credentials_file(path)
|
||||
|
||||
if not os.path.exists(cred_path):
|
||||
ts_print(f"Error: credentials file not found: {cred_path}")
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(cred_path)
|
||||
|
||||
try:
|
||||
username = config.get("credentials", "username", fallback="root")
|
||||
password = config.get("credentials", "password")
|
||||
encryption_key = config.get("credentials", "encryption_key", fallback=DEFAULT_EK)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
||||
ts_print(f"Error reading credentials file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
return username, password, encryption_key
|
||||
|
||||
|
||||
def power_on_host(host, ipmi_user, ipmi_pw, ipmi_ek):
|
||||
"""Power on a single host via IPMI. Returns True if successful."""
|
||||
cmd = [
|
||||
"ipmitool",
|
||||
"-I", "lanplus",
|
||||
"-H", host,
|
||||
"-U", ipmi_user,
|
||||
"-P", ipmi_pw,
|
||||
"-y", ipmi_ek,
|
||||
"power", "on",
|
||||
]
|
||||
|
||||
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if proc.returncode != 0:
|
||||
ts_print(f"ERROR: Failed to power on {host}: {proc.stderr.strip()}")
|
||||
return False
|
||||
else:
|
||||
ts_print(f"SUCCESS: {host} - {proc.stdout.strip()}")
|
||||
return True
|
||||
|
||||
|
||||
def load_servers_from_file(filepath):
|
||||
"""Load server list from a file (one server per line, lines starting with # are ignored)."""
|
||||
if not os.path.exists(filepath):
|
||||
ts_print(f"Error: server list file not found: {filepath}")
|
||||
sys.exit(1)
|
||||
|
||||
servers = []
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
# Skip empty lines and comments
|
||||
if line and not line.startswith('#'):
|
||||
servers.append(line)
|
||||
except Exception as e:
|
||||
ts_print(f"Error reading server list file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Send IPMI power on to multiple servers")
|
||||
|
||||
# Either provide servers as arguments or a file
|
||||
group = p.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument(
|
||||
"servers",
|
||||
nargs="*",
|
||||
help="List of IPMI host addresses (space-separated)"
|
||||
)
|
||||
group.add_argument(
|
||||
"-f", "--file",
|
||||
help="Path to file containing list of servers (one per line)"
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"-c",
|
||||
"--credentials",
|
||||
default="credentials",
|
||||
help="Path to credentials file (default: credentials; also checks ~/ and /etc/ if not found)",
|
||||
)
|
||||
args = p.parse_args()
|
||||
|
||||
# Determine server list
|
||||
if args.file:
|
||||
servers = load_servers_from_file(args.file)
|
||||
else:
|
||||
servers = args.servers
|
||||
|
||||
if not servers:
|
||||
ts_print("Error: No servers specified")
|
||||
sys.exit(1)
|
||||
|
||||
ts_print(f"Starting power on sequence for {len(servers)} server(s)")
|
||||
|
||||
ipmi_user, ipmi_pw, ipmi_ek = load_credentials(args.credentials)
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for host in servers:
|
||||
if power_on_host(host, ipmi_user, ipmi_pw, ipmi_ek):
|
||||
success_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
|
||||
ts_print(f"Completed: {success_count} successful, {failed_count} failed")
|
||||
|
||||
if failed_count > 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
DEFAULT_EK = "0000000000000000000000000000000000000000"
|
||||
|
||||
def log(message, is_error=False):
|
||||
"""Print message with timestamp"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
if is_error:
|
||||
print(f"[{timestamp}] {message}", file=sys.stderr)
|
||||
else:
|
||||
print(f"[{timestamp}] {message}")
|
||||
|
||||
|
||||
def load_credentials(path):
|
||||
if not os.path.exists(path):
|
||||
log(f"Error: credentials file not found: {path}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(path)
|
||||
|
||||
try:
|
||||
username = config.get("credentials", "username", fallback="root")
|
||||
password = config.get("credentials", "password")
|
||||
encryption_key = config.get("credentials", "encryption_key", fallback=DEFAULT_EK)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
||||
log(f"Error reading credentials file: {e}", is_error=True)
|
||||
sys.exit(1)
|
||||
|
||||
return username, password, encryption_key
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Send IPMI power off")
|
||||
p.add_argument("-H", "--host", required=True, help="IPMI host address")
|
||||
p.add_argument(
|
||||
"-c",
|
||||
"--credentials",
|
||||
default="credentials",
|
||||
help="Path to credentials file (default: credentials)",
|
||||
)
|
||||
args = p.parse_args()
|
||||
|
||||
ipmi_user, ipmi_pw, ipmi_ek = load_credentials(args.credentials)
|
||||
|
||||
cmd = [
|
||||
"ipmitool",
|
||||
"-I", "lanplus",
|
||||
"-H", args.host,
|
||||
"-U", ipmi_user,
|
||||
"-P", ipmi_pw,
|
||||
"-y", ipmi_ek,
|
||||
"power", "off",
|
||||
]
|
||||
|
||||
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if proc.returncode != 0:
|
||||
log("ipmitool failed: " + proc.stderr.strip(), is_error=True)
|
||||
sys.exit(proc.returncode)
|
||||
|
||||
log(proc.stdout.strip())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
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}")
|
||||
|
||||
DEFAULT_EK = "0000000000000000000000000000000000000000"
|
||||
|
||||
|
||||
def load_credentials(path):
|
||||
if not os.path.exists(path):
|
||||
print(f"Error: credentials file not found: {path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(path)
|
||||
|
||||
try:
|
||||
username = config.get("ilo", "username", fallback="root")
|
||||
password = config.get("ilo", "password")
|
||||
encryption_key = config.get("ilo", "encryption_key", fallback=DEFAULT_EK)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
||||
print(f"Error reading credentials file: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return username, password, encryption_key
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Send IPMI power on")
|
||||
p.add_argument("-H", "--host", required=True, help="IPMI host address")
|
||||
p.add_argument(
|
||||
"-c",
|
||||
"--credentials",
|
||||
default="credentials",
|
||||
help="Path to credentials file (default: credentials)",
|
||||
)
|
||||
args = p.parse_args()
|
||||
|
||||
ipmi_user, ipmi_pw, ipmi_ek = load_credentials(args.credentials)
|
||||
|
||||
cmd = [
|
||||
"ipmitool",
|
||||
"-I", "lanplus",
|
||||
"-H", args.host,
|
||||
"-U", ipmi_user,
|
||||
"-P", ipmi_pw,
|
||||
"-y", ipmi_ek,
|
||||
"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()
|
||||
Reference in New Issue
Block a user