diff --git a/personal/tools/ilo_powercycle.py b/personal/tools/ilo_powercycle.py new file mode 100644 index 0000000..4e07fc7 --- /dev/null +++ b/personal/tools/ilo_powercycle.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +import argparse +import configparser +import os +import sys +import urllib3 +import redfish + +# 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): + print(f"Error: credentials file not found: {cred_path}", file=sys.stderr) + 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: + print(f"Error reading credentials file: {e}", file=sys.stderr) + 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) + +print(f"Status: {response.status}") +if response.status >= 400: + print(f"Error: {response.read}") + +REDFISHOBJ.logout() \ No newline at end of file diff --git a/personal/tools/ipmi_poweroff.py b/personal/tools/ipmi_poweroff.py new file mode 100644 index 0000000..9847003 --- /dev/null +++ b/personal/tools/ipmi_poweroff.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import argparse +import configparser +import os +import subprocess +import sys + +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("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: + 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 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: + print("ipmitool failed:", proc.stderr.strip(), file=sys.stderr) + sys.exit(proc.returncode) + + print(proc.stdout.strip()) + +if __name__ == "__main__": + main() diff --git a/personal/tools/ipmi_poweron.py b/personal/tools/ipmi_poweron.py new file mode 100644 index 0000000..1ed2228 --- /dev/null +++ b/personal/tools/ipmi_poweron.py @@ -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() diff --git a/personal/tools/powerball_reader.py b/personal/tools/powerball_reader.py new file mode 100644 index 0000000..8bc3cfd --- /dev/null +++ b/personal/tools/powerball_reader.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +""" +Script to read and parse Powerball numbers from a CSV file. +""" + +import csv +from datetime import datetime +from pathlib import Path +from typing import List, Dict +from collections import Counter + + +def read_powerball_csv(filepath: str) -> List[Dict]: + """ + Read a CSV file containing Powerball numbers. + + Expected CSV format: + Draw Date,Winning Numbers,Multiplier + 09/26/2020,11 21 27 36 62 24,3 + + Args: + filepath: Path to the CSV file + + Returns: + List of dictionaries containing parsed powerball data + """ + powerball_data = [] + + try: + with open(filepath, 'r', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile) + + for row_num, row in enumerate(reader, start=2): # start=2 because row 1 is header + try: + # Skip empty rows + if not any(row.values()): + continue + + # Parse the row + draw_date = datetime.strptime(row['Draw Date'].strip(), '%m/%d/%Y') + + # Parse winning numbers - filter out empty strings + numbers_str = row['Winning Numbers'].strip() + winning_numbers = [int(n) for n in numbers_str.split() if n.strip()] + + multiplier = int(row['Multiplier'].strip()) + + powerball_data.append({ + 'draw_date': draw_date, + 'date_str': draw_date.strftime('%Y-%m-%d'), + 'winning_numbers': winning_numbers, + 'multiplier': multiplier + }) + except ValueError as e: + print(f"Warning: Skipping row {row_num} due to error: {e}") + print(f" Row data: {row}") + continue + + except FileNotFoundError: + print(f"Error: File '{filepath}' not found.") + return [] + except Exception as e: + print(f"Error: Failed to read CSV file - {e}") + return [] + + return powerball_data + + +def display_powerball_data(data: List[Dict]) -> None: + """ + Display powerball data in a formatted table. + + Args: + data: List of powerball data dictionaries + """ + if not data: + print("No data to display.") + return + + print(f"{'Draw Date':<12} {'Winning Numbers':<30} {'Multiplier':<10}") + print("-" * 52) + + for entry in data: + numbers_str = ' '.join(map(str, entry['winning_numbers'])) + print(f"{entry['date_str']:<12} {numbers_str:<30} {entry['multiplier']:<10}") + + +def analyze_most_frequent_numbers(data: List[Dict]) -> None: + """ + Analyze and display the most frequent number in each slot position. + + Args: + data: List of powerball data dictionaries + """ + if not data: + print("No data to analyze.") + return + + # Initialize lists to hold numbers for each position + num_positions = len(data[0]['winning_numbers']) + position_numbers = [[] for _ in range(num_positions)] + + # Collect all numbers for each position + for entry in data: + for position, number in enumerate(entry['winning_numbers']): + position_numbers[position].append(number) + + # Find most common number in each position + print("\n" + "=" * 60) + print("Most Frequent Numbers by Slot Position") + print("=" * 60) + + most_frequent = [] + for position, numbers in enumerate(position_numbers, start=1): + counter = Counter(numbers) + most_common_num, frequency = counter.most_common(1)[0] + most_frequent.append(most_common_num) + percentage = (frequency / len(numbers)) * 100 + print(f"Position {position}: {most_common_num:<3} (appears {frequency} times, {percentage:.1f}%)") + + print("-" * 60) + print(f"Most frequent numbers combined: {' '.join(map(str, most_frequent))}") + + # Analyze most frequent number across first 5 positions combined + print("\n" + "=" * 60) + print("Additional Analysis") + print("=" * 60) + + # Combine all numbers from positions 1-5 + first_five_combined = [] + for i in range(min(5, num_positions)): + first_five_combined.extend(position_numbers[i]) + + if first_five_combined: + counter_first_five = Counter(first_five_combined) + top_five_numbers = counter_first_five.most_common(5) + print("Top 5 most drawn numbers in first 5 positions combined:") + for rank, (number, frequency) in enumerate(top_five_numbers, start=1): + percentage = (frequency / len(first_five_combined)) * 100 + print(f" {rank}. {number:<3} (appears {frequency} times, {percentage:.1f}%)") + + # Analyze the sixth position (Powerball) + most_common_sixth = None + if num_positions >= 6: + sixth_position_numbers = position_numbers[5] + counter_sixth = Counter(sixth_position_numbers) + most_common_sixth, freq_sixth = counter_sixth.most_common(1)[0] + percentage_sixth = (freq_sixth / len(sixth_position_numbers)) * 100 + print(f"\nMost drawn number in position 6 (Powerball): {most_common_sixth} (appears {freq_sixth} times, {percentage_sixth:.1f}%)") + + # Return statistics for prediction + return { + 'top_five_combined': [num for num, freq in top_five_numbers] if first_five_combined else [], + 'most_common_powerball': most_common_sixth + } + + +def generate_suggested_combination(stats: dict) -> None: + """ + Generate a suggested combination based on statistical analysis. + + Args: + stats: Dictionary containing statistical analysis results + """ + print("\n" + "=" * 60) + print("Suggested Combination Based on Statistical Analysis") + print("=" * 60) + + print("\n⚠️ DISCLAIMER: This is purely statistical analysis.") + print("Past results do NOT predict future outcomes.") + print("Each draw is random and independent.\n") + + top_five = stats['top_five_combined'] + powerball = stats['most_common_powerball'] + + if len(top_five) >= 5 and powerball: + # Use the top 5 most frequent numbers from positions 1-5 + suggested_numbers = sorted(top_five[:5]) + + print(f"Suggested white ball numbers: {' '.join(map(str, suggested_numbers))}") + print(f"Suggested Powerball number: {powerball}") + print() + print(f"Complete combination: {' '.join(map(str, suggested_numbers))} + {powerball}") + print() + print("This combination is based on:") + print(" • The 5 most frequently drawn numbers across all white ball positions") + print(f" • The most frequently drawn Powerball number ({powerball})") + else: + print("Insufficient data to generate a suggestion.") + + +def main(): + """Main function.""" + # Default to 'powerball_numbers.csv' in the same directory as the script + script_dir = Path(__file__).parent + csv_file = script_dir / 'powerball_numbers.csv' + + # You can also specify a different file path here + # csv_file = Path('path/to/your/file.csv') + + print(f"Reading Powerball numbers from: {csv_file}\n") + + powerball_data = read_powerball_csv(str(csv_file)) + + if powerball_data: + print(f"Successfully loaded {len(powerball_data)} records.\n") + display_powerball_data(powerball_data) + stats = analyze_most_frequent_numbers(powerball_data) + generate_suggested_combination(stats) + else: + print("Failed to load powerball data.") + + +if __name__ == '__main__': + main()