From cbe73e14c7b746e002be9d6c086a3828ef822463 Mon Sep 17 00:00:00 2001 From: Marcin Jachymiak Date: Tue, 3 Mar 2020 18:26:51 -0500 Subject: [PATCH] Cleanup health check script --- setup-scripts/health-checker.py | 108 +++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 35 deletions(-) diff --git a/setup-scripts/health-checker.py b/setup-scripts/health-checker.py index 54a49edf..d1dc57aa 100755 --- a/setup-scripts/health-checker.py +++ b/setup-scripts/health-checker.py @@ -1,21 +1,45 @@ #!/usr/bin/env python3 from urllib.request import urlopen, Request -import urllib -import json -import sys +import urllib, json, os, traceback, discord +portal_name = os.environ["PORTAL_NAME"] # sc_precision is the number of hastings per siacoin sc_precision = 10 ** 24 - api_endpoint = "http://localhost:9980" # TODO get port from env/param +# Discord bot initialization +bot_token = os.environ["DISCORD_BOT_TOKEN"] +client = discord.Client() +channel_name = "skynet-portal-health-check" -# TODO: get values from a param -min_balance = 10_000 * sc_precision -min_unspent_renter_funds = 5_000 * sc_precision -default_renter_funds = 20_000 * sc_precision +@client.event +async def on_ready(): + await run_checks() + await client.close() + +# send_msg sends the msg to the specified discord channel. If force_notify is set to true it adds "@here". +async def send_msg(msg, force_notify=False): + await client.wait_until_ready() + + guild = client.guilds[0] + channels = guild.channels + + chan = None + for c in channels: + if c.name == channel_name: + chan = c + + if chan is None: + print("Can't find channel {}".format(channel_name)) + + # Add the portal name. + msg = "`{}`: {}".format(portal_name, msg) + + if force_notify: + msg = "@here: \n{}".format(msg) + await chan.send(msg) #siac class provides wrappers for the necessary siac commands. class siac: @@ -48,60 +72,74 @@ class siac: def load_json(resp): return json.loads(resp.decode("utf-8")) + @staticmethod def get_wallet(): resp = urllib.request.urlopen(api_endpoint + "/wallet").read() return siac.load_json(resp) + @staticmethod def get_renter(): resp = urllib.request.urlopen(api_endpoint + "/renter").read() return siac.load_json(resp) -# Initialize the API opener -siac.initialize() -# fail should ping discord for failures in health checks that can't be managed automatically. -def fail(err_msg): - print("Error message: ", err_msg) + @staticmethod + def get_renter_contracts(): + resp = urllib.request.urlopen(api_endpoint + "/renter/contracts").read() + return siac.load_json(resp) -# check_wallet_health_check checks that the wallet is unlocked and has at least min_balance SC. -def check_wallet_health(): +# check_health checks that the wallet is unlocked, that it has at least 1 +# allowance worth of money left, and if more than hald the allowance is spent. If +# all checks pass it sends a informational message. +async def check_health(): + print("\nChecking health...") wallet_get = siac.get_wallet() + renter_get = siac.get_renter() if not wallet_get['unlocked']: - fail("Wallet not unlocked") + await send_msg("Wallet locked", force_notify=True) + return confirmed_coins = int(wallet_get['confirmedsiacoinbalance']) unconfirmed_coins = int(wallet_get['unconfirmedincomingsiacoins']) unconfirmed_outgoing_coins = int(wallet_get['unconfirmedoutgoingsiacoins']) balance = confirmed_coins + unconfirmed_coins - unconfirmed_outgoing_coins - print("Balance: ", balance / sc_precision) - print("Min balance: ", min_balance / sc_precision) - - if balance < min_balance: - fail("Wallet balance too low: {}".format(balance)) - -def check_renter_health(): - renter_get = siac.get_renter() allowance = renter_get['settings']['allowance'] allowance_funds = int(allowance['funds']) unspent_funds = int(renter_get['financialmetrics']['unspent']) + spent_funds = allowance_funds - unspent_funds - # if funds are under a certain amount, re-balance the wallet. - if unspent_funds < min_unspent_renter_funds: - #siac.set_renter_funds(default_renter_funds) - #TODO + # Send an alert if there is less than 1 allowance worth of money left. + if balance < allowance_funds: + await send_msg("Wallet balance running low: {}".format(balance), force_notify=True) + return -check_renter_health() + # Alert devs when 1/2 the allowance is gone + if spent_funds >= unspent_funds: + await send_msg("Allowance half spent: \nUnspent: {}\nSpent: {}".format(unspent_funds/sc_precision, spent_funds/sc_precision), force_notify=True) + return -try: - check_wallet_health() - check_renter_health() + # Send an informational heartbeat if all checks passed. + pretty_renter_get = json.dumps(siac.get_renter(), indent=4) + pretty_renter_contracts = json.dumps(siac.get_renter_contracts(), indent=4) + await send_msg("Health checks passed:\n\nRenter Info:\n```\n{}\n```\n\nContract Info:\n```\n{}\n```\n".format(pretty_renter_get, pretty_renter_contracts)) -except: # catch all exceptions - e = sys.exc_info()[0] - fail(e) + +async def run_checks(): + # Initialize the siac API helper. + siac.initialize() + + print("Running Skynet portal health checks") + try: + await check_health() + + except: # catch all exceptions + trace = traceback.format_exc() + await send_msg("```\n{}\n```".format(trace), force_notify=True) + +client.run(bot_token)