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 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)