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