Add new power cycle scripts for servers
This commit is contained in:
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
Reference in New Issue
Block a user