Bit.ASICmon-p/checker.py

335 lines
9.0 KiB
Python

from threading import Thread
from mysql.connector import connect, Error
from time import sleep, time
from asics import ASICs
from macros import *
import os, re, json, socket, requests
class CThread(Thread):
conn = None
def __init__(self, event):
super(CThread, self).__init__()
self.event = event
def getreg(self, reg, str):
res = re.findall(reg, str)
if res: return res[0][1]
else: return False
def getstat_AntminerL3(self, log):
status = 'ok'
return status
def getstat_Avalon1XXX(self, log, type):
status = 'ok'
rej = float(self.getreg('("Device Rejected%":([0-9.]+),)', log))
ghs = int(float(self.getreg('(GHSmm\[([0-9.:,]+)\])', log)))
avg = int(float(self.getreg('(GHSavg\[([0-9.: ]+)\])', log)))
# brd = self.getreg('(MGHS\[([0-9.:, ]+)\])', log)
tmp = int(self.getreg('(TMax\[([0-9]+)\])', log))
chash = CONF.get('asics', ASICs(type).name, 'crit', 'hash')
whash = CONF.get('asics', ASICs(type).name, 'warn', 'hash')
ctemp = CONF.get('asics', ASICs(type).name, 'crit', 'temp')
wtemp = CONF.get('asics', ASICs(type).name, 'warn', 'temp')
crej = CONF.get('asics', ASICs(type).name, 'crit', 'rej')
wrej = CONF.get('asics', ASICs(type).name, 'warn', 'rej')
if rej > crej or ghs < chash or avg < chash or tmp > ctemp: status = 'crit'
elif rej > wrej or ghs < whash or avg < whash or tmp > wtemp: status = 'warn'
DEBUG(f"{rej} | {ghs} | {avg} | {brd} | {tmp}")
return status
def getstat_AntminerS19(self, log):
status = 'ok'
rej = float(self.getreg('("Device Rejected%": ([0-9.]+),)', log))
ghs = int(float(self.getreg('("GHS 5s": ([0-9.]+),)', log)))
avg = int(float(self.getreg('("GHS av": ([0-9.]+),)', log)))
tmp = int(self.getreg('("TMax": ([0-9]+),)', log))
chash = CONF.get('asics', ASICs(type).name, 'crit', 'hash')
whash = CONF.get('asics', ASICs(type).name, 'warn', 'hash')
ctemp = CONF.get('asics', ASICs(type).name, 'crit', 'temp')
wtemp = CONF.get('asics', ASICs(type).name, 'warn', 'temp')
crej = CONF.get('asics', ASICs(type).name, 'crit', 'rej')
wrej = CONF.get('asics', ASICs(type).name, 'warn', 'rej')
if rej > crej or ghs < chash or avg < chash or tmp > ctemp: status = 'crit'
elif rej > wrej or ghs < whash or avg < whash or tmp > wtemp: status = 'warn'
DEBUG(f"{rej} | {ghs} | {avg} | {brd} | {tmp}")
return status
def gettype(self, ip):
info = self.rtcp(ip, 4028, '{"command": "version"}')
if not info:
return False
if CONF.get('debug') and info:
info1 = self.rtcp(ip, 4028, '{"command": "stats"}')
info2 = self.rtcp(ip, 4028, '{"command": "version"}')
info3 = self.rtcp(ip, 4028, '{"command": "summary"}')
info4 = self.rtcp(ip, 4028, '{"command": "pools"}')
info5 = self.rtcp(ip, 4028, '{"command": "devdetails"}')
info6 = self.rtcp(ip, 4028, '{"command": "get_version"}')
info7 = self.rtcp(ip, 4028, '{"command": "status"}')
info8 = self.rtcp(ip, 4028, '{"command": "get_miner_info"}')
info9 = self.rtcp(ip, 4028, '{"command": "devs"}')
info10 = self.rtcp(ip, 4028, '{"command": "notify"}')
info11 = self.rtcp(ip, 4028, '{"command": "coin"}')
info12 = self.rtcp(ip, 4028, '{"command": "edevs"}')
info13 = self.rtcp(ip, 4028, '{"command": "estats"}')
DEBUG(f"[{ip}] {info1}")
DEBUG(f"[{ip}] {info2}")
DEBUG(f"[{ip}] {info3}")
DEBUG(f"[{ip}] {info4}")
DEBUG(f"[{ip}] {info5}")
DEBUG(f"[{ip}] {info6}")
DEBUG(f"[{ip}] {info7}")
DEBUG(f"[{ip}] {info8}")
DEBUG(f"[{ip}] {info9}")
DEBUG(f"[{ip}] {info10}")
DEBUG(f"[{ip}] {info11}")
DEBUG(f"[{ip}] {info12}")
DEBUG(f"[{ip}] {info13}")
info += self.rtcp(ip, 4028, '{"command": "devdetails"}')
# Bitmain
if "Antminer" in info:
if "S19J Pro" in info:
return ASICs.AntminerS19JPro.value
if "S19J" in info:
return ASICs.AntminerS19J.value
if "S19 Pro" in info:
return ASICs.AntminerS19Pro.value
if "S19" in info:
return ASICs.AntminerS19.value
if "L3+" in info:
return ASICs.AntminerL3plus.value
if "L7" in info:
return ASICs.AntminerL3plus.value
if "T17+" in info:
return ASICs.AntminerT17plus.value
if "T17" in info:
return ASICs.AntminerT17.value
# Avalon
if "Avalon" in info:
if "1066" in info:
return ASICs.Avalon1066.value
if "1126" in info:
return ASICs.Avalon1126.value
# WhatsMiner
if "bitmicro" in info:
if "M20s":
return ASICs.WhatsMinerM20s.value
if "M21s":
return ASICs.WhatsMinerM21s.value
if "M30":
return ASICs.WhatsMinerM30.value
if "M31":
return ASICs.WhatsMinerM31.value
if "M50":
return ASICs.WhatsMinerM50.value
# Innosilicon
if False:
pass
return False
def rtcp(self, ip, port, cmd):
cmd = cmd.encode('utf-8')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.connect((ip, port))
s.sendall(cmd)
tdata = []
while 1:
data = s.recv(1024*32)
if not data: break
tdata.append(data)
data = b''.join(tdata).decode("utf-8")
return data
except OSError as e:
# ERR(e)
return False
def check_AntminerL3(self, ip):
# f'http://{ip}/cgi-bin/get_miner_status.cgi'
# f'http://{ip}/cgi-bin/get_miner_conf.cgi'
summ = self.rtcp(ip, 4028, "summary")
stat = self.rtcp(ip, 4028, "estats")
info = self.rtcp(ip, 4028, "version")
if summ and stat:
mac = json.loads(requests.get(
f'http://{ip}/cgi-bin/get_system_info.cgi',
auth=requests.auth.HTTPDigestAuth('root', 'root')).text)['macaddr'].lower()
log = f"{info} ||| {summ} ||| {stat}"
return log, mac
return False, False
def check_AntminerS19(self, ip):
# f'http://{ip}/cgi-bin/get_miner_status.cgi'
# f'http://{ip}/cgi-bin/get_miner_conf.cgi'
summ = self.rtcp(ip, 4028, "summary")
stat = self.rtcp(ip, 4028, "stats")
info = self.rtcp(ip, 4028, "version")
if summ and stat:
mac = json.loads(requests.get(
f'http://{ip}/cgi-bin/get_network_info.cgi',
auth=requests.auth.HTTPDigestAuth('root', 'root')).text)['macaddr'].lower()
log = f"{info} ||| {summ} ||| {stat}"
return log, mac
return False, False
def check_Avalon1XXX(self, ip):
summ = self.rtcp(ip, 4028, "summary")
stat = self.rtcp(ip, 4028, "estats")
info = self.rtcp(ip, 4028, "version")
if summ and stat:
mac = self.getreg(r'(MAC=([A-z0-9]+))', info)
if mac: mac = ':'.join(mac[i:i+2] for i in range(0,12,2))
else: mac = ':'
log = f"{info} ||| {summ} ||| {stat}"
return log, mac
return False, False
def check(self, ip):
try:
type = self.gettype(ip)
if not type:
return (False, False, False, False, False), False
if type in [ASICs.AntminerL3plus.value]:
log, mac = self.check_AntminerL3(ip)
status = self.getstat_AntminerL3(log, type)
elif type in [ASICs.Avalon1066.value, ASICs.Avalon1126.value]:
log, mac = self.check_Avalon1XXX(ip)
status = self.getstat_Avalon1XXX(log, type)
elif type in [ASICs.AntminerS19.value, ASICs.AntminerS19J.value, ASICs.AntminerS19Pro.value, ASICs.AntminerS19JPro.value]:
log, mac = self.check_AntminerS19(ip)
status = self.getstat_AntminerS19(log, type)
return (ip, mac, type, int(time()), log), status
except Exception as e:
WARN('Minor error in <lambda>Thread: ' + str(e))
return (False, False, False, False, False), False
def checkall(self):
ips = []
with self.conn.cursor(dictionary=True) as c:
c.execute("SELECT * FROM `ips` WHERE `location` = %s", (CONF.get('location'),))
for ip in c.fetchall():
ip = ip['ip']
if ip.isalpha() or not ip.find('-'):
ips.append(ip)
else:
i_p = ip.split('.')
i_p_new = []
for i in i_p:
i_p_new.append(i.split('-'))
i_p_new[-1][0] = int(i_p_new[-1][0])
i_p_new[-1][-1] = int(i_p_new[-1][-1])
for i1 in range(i_p_new[0][0], i_p_new[0][-1] + 1):
for i2 in range(i_p_new[1][0], i_p_new[1][-1] + 1):
for i3 in range(i_p_new[2][0], i_p_new[2][-1] + 1):
for i4 in range(i_p_new[3][0], i_p_new[3][-1] + 1):
ips.append(f"{i1}.{i2}.{i3}.{i4}")
Tca = []
ca = []
for ip in ips:
T = Thread(target=lambda x: ca.append(self.check(x)), args=(ip,))
Tca.append(T)
for i in range(len(Tca)):
Tca[i].start()
for i in range(len(Tca)):
Tca[i].join()
with self.conn.cursor() as c:
c.execute(f"DELETE FROM `laststate` WHERE `location` = %s", (CONF.get('location'),))
for i in ca:
i, status = i
if i[4]:
c.execute(f"INSERT INTO `asiclogs` (`ip`, `mac`, `type`, `time`, `log`) VALUES (%s, %s, %s, %s, %s)", i)
if i[1] == ':':
continue
c.execute(
f"INSERT INTO `laststate` (`ip`, `mac`, `type`, `location`, `status`, `time`) VALUES (%s, %s, %s, %s, %s, %s)",
(i[0], i[1], i[2], CONF.get('location'), status, int(time())))
self.conn.commit()
def run(self):
try:
SUCC("Checker started!")
sleep(5)
self.conn = connect(
host=CONF.get('db', 'host'),
user=CONF.get('db', 'user'),
password=CONF.get('db', 'password'),
database=CONF.get('db', 'name'))
while 1:
if self.event.is_set():
SUCC("Checker stopped!")
break
self.checkall()
sleep(10)
except Exception as e:
CRIT(str(e))
os._exit(1)