#!/usr/bin/env python3 import argparse import configparser import os import sys import urllib3 import redfish from datetime import datetime try: from cryptography.fernet import Fernet, InvalidToken except ImportError: sys.exit("Error: 'cryptography' package is required. Run: pip install cryptography") DEFAULT_CREDENTIALS_FILE = "/root/cron_serverlists/credentials.ini" DEFAULT_KEYFILE_NAME = "secret.key" def log(message, is_error=False): timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") stream = sys.stderr if is_error else sys.stdout print(f"[{timestamp}] {message}", file=stream) def resolve_credentials_path(cli_path): """Resolve credentials path. Prefer explicit CLI path, then search common locations.""" if cli_path and os.path.exists(cli_path): return cli_path candidates = [] if cli_path: candidates.append(cli_path) candidates.append(os.path.join(os.getcwd(), "credentials.ini")) if DEFAULT_CREDENTIALS_FILE not in candidates: candidates.append(DEFAULT_CREDENTIALS_FILE) for candidate in candidates: if os.path.exists(candidate): return candidate log( "Error: credentials file not found. Looked in: " + ", ".join(candidates), is_error=True, ) sys.exit(1) def resolve_keyfile(cli_keyfile, credentials_path): """Resolve keyfile path. Prefer explicit --keyfile, else search next to credentials file.""" if cli_keyfile: return cli_keyfile if os.path.exists(cli_keyfile) else None candidate = os.path.join(os.path.dirname(credentials_path), DEFAULT_KEYFILE_NAME) if os.path.exists(candidate): return candidate return None def load_fernet(keyfile): """Load Fernet instance from the key file created by set_password.py.""" if not os.path.exists(keyfile): log(f"Error: key file not found: {keyfile}", is_error=True) sys.exit(1) with open(keyfile, "rb") as fh: key = fh.read() try: return Fernet(key) except ValueError: log(f"Error: invalid key format in key file: {keyfile}", is_error=True) sys.exit(1) def is_fernet_token(value): return isinstance(value, str) and value.startswith("gAAAA") def decrypt_password(encrypted_password, fernet): """Decrypt password encrypted by set_password.py, or pass through plaintext.""" if not is_fernet_token(encrypted_password): return encrypted_password if fernet is None: log( "Error: encrypted password detected but key file was not found. " "Use --keyfile /path/to/secret.key.", is_error=True, ) sys.exit(1) try: return fernet.decrypt(encrypted_password.encode()).decode() except InvalidToken: log("Error: failed to decrypt password. Check that --keyfile matches the credentials file.", is_error=True) sys.exit(1) def load_ilo_credentials(path, fernet): config = configparser.ConfigParser() config.read(path) section = "ilo_credentials" if not config.has_section(section): log(f"Error: missing [{section}] section in {path}", is_error=True) sys.exit(1) username = config.get(section, "username", fallback="root") encrypted_password = config.get(section, "password", fallback=None) if not encrypted_password: log(f"Error: missing password in [{section}] section of {path}", is_error=True) sys.exit(1) password = decrypt_password(encrypted_password, fernet) return username, password # Suppress SSL warnings for self-signed iLO certificates urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) parser = argparse.ArgumentParser(description="Get the power status of an HPE server via iLO Redfish") parser.add_argument("hostname", help="iLO hostname or IP address") parser.add_argument("-c", "--credentials", default=None, help="Path to credentials file (default: auto-detected credentials.ini)") parser.add_argument("--keyfile", default=None, help="Path to Fernet key file for decrypting passwords (default: auto-detected secret.key)") args = parser.parse_args() cred_path = resolve_credentials_path(args.credentials) keyfile = resolve_keyfile(args.keyfile, cred_path) fernet = load_fernet(keyfile) if keyfile else None LOGIN_ACCOUNT, LOGIN_PASSWORD = load_ilo_credentials(cred_path, fernet) BASE_URL = f"https://{args.hostname}" REDFISHOBJ = redfish.RedfishClient( base_url=BASE_URL, username=LOGIN_ACCOUNT, password=LOGIN_PASSWORD, ) REDFISHOBJ.login(auth="session") sys_response = REDFISHOBJ.get("/redfish/v1/Systems/1/") power_state = sys_response.dict.get("PowerState", "Unknown") log(f"Power State: {power_state}") REDFISHOBJ.logout()