power/power.py

209 lines
6.3 KiB
Python
Raw Permalink Normal View History

2024-10-08 22:24:23 +00:00
import time
import config
import logging
import smtplib
2024-10-09 22:00:42 +00:00
import asyncio
import discord
import paramiko
2024-10-12 02:38:20 +00:00
from ping3 import ping
2024-10-08 22:24:23 +00:00
from nut2 import PyNUTClient
2024-10-09 22:00:42 +00:00
from proxmoxer import ProxmoxAPI
2024-10-08 22:24:23 +00:00
from email.mime.text import MIMEText
2024-10-12 02:38:20 +00:00
from wakeonlan import send_magic_packet
2024-10-08 22:24:23 +00:00
from logging.handlers import TimedRotatingFileHandler
2024-10-12 02:38:20 +00:00
# Config
2024-10-09 14:10:57 +00:00
ups_id = config.UPS_ID
2024-10-08 22:24:23 +00:00
gmail_user = config.GMAIL_USER
gmail_password = config.GMAIL_PASS
recipient_email = config.GMAIL_ADDR
2024-10-09 22:00:42 +00:00
bot_token = config.BOT_TOKEN
user_id = config.USER_ID
pve_host = config.PVE_HOST
pve_user = config.PVE_USER
pve_pass = config.PVE_PASS
servers = ['pve', 'c530']
nas_ip = config.HOST1_IP
2024-10-09 22:00:42 +00:00
2024-10-12 02:38:20 +00:00
wol_targets = {
'host1': {'mac': config.HOST1_MAC, 'ip': config.HOST1_IP},
'host2': {'mac': config.HOST2_MAC, 'ip': config.HOST2_IP},
'host3': {'mac': config.HOST3_MAC, 'ip': config.HOST3_IP}
}
# Log Setup
2024-10-08 22:24:23 +00:00
def setup_logging():
logger = logging.getLogger('powerlog')
logger.setLevel(logging.INFO)
handler = TimedRotatingFileHandler(
'power.log',
when='midnight',
interval=1,
backupCount=7,
delay=True
)
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
2024-10-09 20:21:46 +00:00
logger = setup_logging()
2024-10-12 02:38:20 +00:00
# Email
2024-10-09 20:21:46 +00:00
def send_email(subject, body):
2024-10-08 22:24:23 +00:00
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = gmail_user
msg['To'] = recipient_email
try:
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login(gmail_user, gmail_password)
server.send_message(msg)
logger.info(f"Email message sent: {subject}")
except Exception as e:
logger.info(f"Failed to send email: {e}")
2024-10-09 22:24:35 +00:00
intents = discord.Intents.default()
client = discord.Client(intents=intents)
2024-10-09 22:00:42 +00:00
2024-10-12 02:38:20 +00:00
# Discord
2024-10-09 23:20:15 +00:00
def send_discord(message):
async def message_send():
intents = discord.Intents.default()
client = discord.Client(intents=intents)
@client.event
async def on_ready():
logger.info(f"Client connected as {client.user}")
try:
user = await client.fetch_user(user_id)
await user.send(message)
logger.info(f"Discord message sent: {message}")
except Exception as e:
logger.error(e)
finally:
await client.close()
await client.start(bot_token)
2024-10-09 23:23:23 +00:00
asyncio.run(message_send())
2024-10-09 22:00:42 +00:00
# UPS Monitor
def monitor_ups():
client = PyNUTClient()
try:
ups_status = client.list_vars('ups')
return {
'status': ups_status.get('ups.status'),
'battery_charge': float(ups_status.get('battery.charge'))
}
except Exception as e:
logger.error(f"Error communicating with NUT: {e}")
return None
2024-10-12 02:38:20 +00:00
# Server Power Management
proxmox = ProxmoxAPI(pve_host, user=pve_user, password=pve_pass, verify_ssl=False)
def shutdown_pve(node):
try:
proxmox.nodes(node).status.post(command='shutdown')
logger.warning(f"Node {node} is shutting down.")
except Exception as e:
logger.error(e)
def shutdown_nas():
2024-10-12 02:38:20 +00:00
try:
logger.warning("Nas is shutting down.")
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(nas_ip)
stdin, stdout, stderr = client.exec_command('sudo shutdown')
for line in stdout:
print(line.strip())
2024-10-12 08:24:28 +00:00
client.close()
2024-10-12 02:38:20 +00:00
except Exception as e:
logger.error(e)
2024-10-12 02:38:20 +00:00
def wake_up(battery):
for target, addr in wol_targets.items():
mac = addr['mac']
ip = addr['ip']
while True:
response = ping(ip)
if response:
logger.info(response)
logger.info(f"Host {mac} at {ip} is up. Ping {response}.")
time.sleep(5)
break
else:
send_magic_packet(mac)
logger.warning(f"Host {mac} at {ip} is offline. Sending magic packet.")
time.sleep(10)
# Status Communications
2024-10-10 13:24:29 +00:00
def pwr_online(battery):
message = f"{ups_id} UPS power has been restored. {battery}% charge remaining."
logger.info(message)
send_email(f"{ups_id}: Power On Line", message)
send_discord(message)
2024-10-12 08:24:28 +00:00
if ups_id == "Server" and battery > 75:
2024-10-12 02:38:20 +00:00
wake_up(battery)
2024-10-10 13:24:29 +00:00
2024-10-09 22:00:42 +00:00
def pwr_offline(battery):
message = f"{ups_id} UPS is running on battery power! {battery}% charge remaining."
logger.warning(message)
send_email(f"{ups_id}: Power Outage Detected!", message)
2024-10-09 23:20:15 +00:00
send_discord(message)
2024-10-09 22:00:42 +00:00
def batt_low(battery):
message = f"{ups_id} battery level low: {battery}% charge remaining."
logger.warning(message)
send_email(f"{ups_id} battery low.", message)
2024-10-09 23:20:15 +00:00
send_discord(message)
2024-10-09 22:00:42 +00:00
def batt_crit(battery):
message = f"{ups_id} battery level critial: {battery}% charge remaining, Shutting down PVE."
logger.warning(message)
send_email(f"{ups_id} battery critical!!", message)
2024-10-09 23:20:15 +00:00
send_discord(message)
if ups_id == "Server":
for node in servers:
shutdown_pve(node)
2024-10-12 07:39:58 +00:00
time.sleep(120)
shutdown_nas()
2024-10-09 22:00:42 +00:00
2024-10-12 02:38:20 +00:00
# Main Loop
2024-10-08 22:24:23 +00:00
def main():
logger.info("Starting UPS monitoring service.")
2024-10-12 02:38:20 +00:00
prev_status = "OL"
2024-10-08 22:24:23 +00:00
while True:
2024-10-09 20:21:46 +00:00
ups_data = monitor_ups()
2024-10-09 14:10:57 +00:00
if ups_data:
status = ups_data['status']
battery = ups_data['battery_charge']
if status and status != prev_status:
if status == "OB DISCHRG":
2024-10-09 23:20:15 +00:00
pwr_offline(battery)
2024-10-10 13:40:25 +00:00
elif status == "OL" or status == "OL CHRG":
2024-10-12 02:38:20 +00:00
pwr_online(battery)
2024-10-09 14:10:57 +00:00
else:
logger.info(f"UPS status changed to: {status}")
prev_status = status
2024-10-12 08:10:09 +00:00
elif status == "OB DISCHRG" and battery < 70:
if battery < 65:
2024-10-09 22:00:42 +00:00
batt_crit(battery)
2024-10-10 13:40:25 +00:00
if ups_id == "Server":
for node in servers:
2024-10-10 13:40:25 +00:00
shutdown_pve(node)
time.sleep(300)
2024-10-09 22:00:42 +00:00
else:
batt_low(battery)
time.sleep(60)
2024-10-08 22:24:23 +00:00
time.sleep(5)
if __name__ == "__main__":
main()