335 lines
9.0 KiB
Python
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)
|