Compare commits

..

1 commit
qt ... master

Author SHA1 Message Date
a69764e45d looking for specifics 2025-02-05 22:02:39 -05:00
6 changed files with 468 additions and 417 deletions

4
.gitignore vendored
View file

@ -1,4 +0,0 @@
2024/05/30.csv
2024/05/30-2.txt
results.csv
miner_logs.log

View file

@ -1,16 +1,5 @@
{
"error_keywords": {
"ERROR_TEMP_TOO_HIGH": "Temperature Error",
"ERROR_NETWORK_DISCONNECTED": "probably doesn't exist",
"ERROR_POWER_LOST: power voltage rise or drop": "voltage drop",
"_pic_write_iic failed!": "PIC Error",
"PLL read exceeded wait time": "PLL Error",
"ERROR_SOC_INIT: soc init failed": "SoC failure",
"fail to read 0:1": "eeprom",
"fail to write 0:1": "eeprom",
"bitmain_get_power_status failed": "PSU",
"power voltage can not meet the target": "PSU",
"reg crc error": "black hole",
"ERROR_POWER_LOST: pic check voltage drop": "voltage drop"
"BLOCK ACCEPTED": "Block found"
}
}

12
errors2.json Normal file
View file

@ -0,0 +1,12 @@
// "ERROR_TEMP_TOO_HIGH": "Temperature Error",
// "ERROR_NETWORK_DISCONNECTED": "probably doesn't exist",
// "ERROR_POWER_LOST: power voltage rise or drop": "voltage drop",
// "_pic_write_iic failed!": "PIC Error",
// "PLL read exceeded wait time": "PLL Error",
// "ERROR_SOC_INIT: soc init failed": "SoC failure",
// "fail to read 0:1": "eeprom",
// "fail to write 0:1": "eeprom",
// "bitmain_get_power_status failed": "PSU",
// "power voltage can not meet the target": "PSU",
// "reg crc error": "black hole",
// "ERROR_POWER_LOST: pic check voltage drop": "voltage drop",

191
finder.py
View file

@ -2,17 +2,8 @@ import paramiko
import re
import json
import csv
import logging
from datetime import datetime
# Constants for error types
ASIC_ERROR = "ASIC Error"
EEPROM_ERROR = "EEPROM Error"
CHIP_BIN_ERROR = "Chip Bin Error"
# Logging configuration
logging.basicConfig(filename='miner_logs.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Load credentials from a JSON file
def load_credentials(file_path):
with open(file_path, 'r') as file:
@ -35,92 +26,94 @@ def read_ips(file_path):
ips = file.readlines()
return [ip.strip() for ip in ips]
# Function to establish an SSH connection
def establish_ssh_connection(ip, username, password):
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh_client.connect(ip, username=username, password=password, timeout=5)
logging.info(f"Connected to {ip} with {username}")
return ssh_client
except Exception as e:
logging.error(f"Failed to connect to {ip} with {username}:{password} - {e}")
return None
# Function to execute a command via SSH and return the output
def execute_ssh_command(ssh_client, command):
try:
stdin, stdout, stderr = ssh_client.exec_command(command)
return stdout.read().decode('utf-8')
except Exception as e:
logging.error(f"Error executing command '{command}': {e}")
return None
# Function to get worker ID
def get_worker_id(ssh_client):
config_content = execute_ssh_command(ssh_client, "cat /config/cgminer.conf")
if config_content:
match = re.search(r'"user" *: *"[^.]*\.(\w+)"', config_content)
if match:
return match.group(1)
return "Unknown"
# Function to check log files for keywords and ASIC errors
def check_logs(ip, ssh_client, worker_id, current_date, error_keywords):
def check_logs(ip, ssh_client, worker_id, current_date):
logs = []
asic_errors = set() # Using set to avoid duplicate errors
results = [] # Using list to avoid duplicate entries
log_files_content = execute_ssh_command(ssh_client, "find /var/log/ -type f")
if log_files_content:
log_files = log_files_content.splitlines()
try:
print(f"Checking logs on {ip}")
stdin, stdout, stderr = ssh_client.exec_command("find /nvdata/2025/01/29/ -type f")
log_files = stdout.readlines()
for log_file in log_files:
log_content = execute_ssh_command(ssh_client, f"cat {log_file}")
if log_content:
seen_errors = set()
for keyword, error_type in error_keywords.items():
if keyword in log_content and (log_file, error_type, keyword) not in seen_errors:
logs.append((log_file, error_type, keyword))
seen_errors.add((log_file, error_type, keyword))
log_file = log_file.strip()
print(f"Checking file: {log_file}") # Debug statement
for match in asic_pattern.finditer(log_content):
chain, asic_count = match.groups()
asic_errors.add((chain, int(asic_count)))
# directory?
if is_directory:
for nested_file in nested_files:
stdin, stdout, stderr = ssh_client.exec_command(f"cat '{nested_file_path}'")
log_content = stdout.read().decode('utf-8', errors='ignore')
print(f"Content of {nested_file_path}: {log_content[:500]}")
else:
stdin, stdout, stderr = ssh_client.exec_command(f"cat '{log_file}'")
log_content = stdout.read().decode('utf-8', errors='ignore')
print(f"Content of {log_file}: {log_content[:500]}")
# Read the log file content directly
stdin, stdout, stderr = ssh_client.exec_command(f"cat {log_file}")
log_content = stdout.read().decode('utf-8', errors='ignore')
print(f"Content of {log_file}: {log_content[:500]}") # Debug statement to show part of the log content
# Track unique errors within this log file
seen_errors = set()
for keyword, error_type in error_keywords.items():
if keyword in log_content and (log_file, error_type, keyword) not in seen_errors:
print(f"Found keyword '{keyword}' in {log_file}") # Debug statement
logs.append((log_file, error_type, keyword))
seen_errors.add((log_file, error_type, keyword))
for match in power_off_pattern.finditer(log_content):
chain, found_asic_count, board = match.groups()
chain = int(chain)
found_asic_count = int(found_asic_count)
if (log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
seen_errors.add((log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
# Check for ASIC chip errors and power-off messages
for match in asic_pattern.finditer(log_content):
chain, asic_count = match.groups()
asic_count = int(asic_count)
asic_errors.add((chain, asic_count))
print(f"Chain {chain} has {asic_count} chips.") # Debug statement
# Check for power-off messages
for match in power_off_pattern.finditer(log_content):
chain, found_asic_count, board = match.groups()
found_asic_count = int(found_asic_count)
chain = int(chain)
if (log_file, "ASIC Error", f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, "ASIC Error", f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
seen_errors.add((log_file, "ASIC Error", f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
for match in eeprom_error_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, EEPROM_ERROR, f"Data load fail for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, EEPROM_ERROR, f"Data load fail for chain {chain}"))
seen_errors.add((log_file, EEPROM_ERROR, f"Data load fail for chain {chain}"))
# Check for EEPROM errors
for match in eeprom_error_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, "EEPROM Error", f"Data load fail for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, "EEPROM Error", f"Data load fail for chain {chain}"))
seen_errors.add((log_file, "EEPROM Error", f"Data load fail for chain {chain}"))
# Check for chip bin errors
for match in chip_bin_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, "Chip Bin Error", f"No chip bin for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, "Chip Bin Error", f"No chip bin for chain {chain}"))
seen_errors.add((log_file, "Chip Bin Error", f"No chip bin for chain {chain}"))
for match in chip_bin_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}"))
seen_errors.add((log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}"))
except Exception as e:
print(f"Error checking logs on {ip}: {e}")
return logs, asic_errors, results
# Function to write results to a text file in the specified format
def write_text_file(file_path, results):
with open(file_path, 'w') as file:
current_worker = None
for result in results:
date, worker_id, ip, log_file, error_type, error_message = result
if worker_id != current_worker:
if current_worker is not None:
file.write("\n") # Add a blank line between different workers
file.write(f"{worker_id}\n")
current_worker = worker_id
file.write(f"- {error_type}\n")
file.write(f"--- {error_message}\n")
file.write(f"-" * 80 + "\n")
# Function to get worker ID
def get_worker_id(ssh_client):
try:
print("Getting worker ID")
stdin, stdout, stderr = ssh_client.exec_command("cat /config/cgminer.conf")
config_content = stdout.read().decode('utf-8')
# Extract the worker ID from the user field
match = re.search(r'"user" *: *"[^.]*\.(\w+)"', config_content)
if match:
worker_id = match.group(1)
print(f"Got Worker ID: {worker_id}")
else:
worker_id = "Unknown"
except Exception as e:
print(f"Error getting worker ID: {e}")
worker_id = "Unknown"
return worker_id
# Main function to iterate over IPs and check for errors
def main():
@ -129,46 +122,46 @@ def main():
current_date = datetime.now().strftime('%Y-%m-%d')
for ip in ips:
logging.info(f"Processing IP: {ip}")
print(f"Processing IP: {ip}")
connected = False
for os_type, creds in credentials.items():
if connected:
break
for username, password in creds:
ssh_client = establish_ssh_connection(ip, username, password)
if ssh_client:
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
print(f"Trying {username}:{password} on {ip}")
ssh_client.connect(ip, username=username, password=password)
connected = True
worker_id = get_worker_id(ssh_client)
logs, asic_errors, asic_results = check_logs(ip, ssh_client, worker_id, current_date, error_keywords)
logs, asic_errors, asic_results = check_logs(ip, ssh_client, worker_id, current_date)
results.extend(asic_results)
for log in logs:
results.append((current_date, worker_id, ip, log[0], log[1], log[2]))
unique_asic_errors = {} # Using a dictionary to store chain and failed check count.
for chain, asic_count in asic_errors:
failed_checks = unique_asic_errors.get(chain, 0) + 1
unique_asic_errors[chain] = failed_checks
if asic_count == 0 and failed_checks == 3:
results.append((current_date, worker_id, ip, "N/A", ASIC_ERROR, f"Chain {chain} has 3 failed checks with {asic_count} ASICs found"))
results.append((current_date, worker_id, ip, log[0], "ASIC Error", f"Chain {chain} has 3 failed checks with {asic_count} ASICs found"))
ssh_client.close()
break
except Exception as e:
print(f"Connection failed for {ip} with {username}:{password} - {e}")
ssh_client.close()
# Write results to CSV
csv_file = 'results.csv'
logging.info(f"Writing results to {csv_file}")
print(f"Writing results to {csv_file}")
with open(csv_file, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(["Date", "Worker ID", "IP Address", "Log File", "Error Type", "Error Message"])
for result in results:
writer.writerow(result)
# Write results to text file
text_file = 'results.txt'
logging.info(f"Writing results to {text_file}")
write_text_file(text_file, results)
logging.info("Done")
print("Done")
if __name__ == "__main__":
# Load credentials and error keywords

364
ips.txt
View file

@ -1 +1,363 @@
192.168.1.171
10.0.10.19
10.0.10.24
10.0.10.27
10.0.10.35
10.0.10.42
10.0.10.46
10.0.10.50
10.0.10.57
10.0.10.70
10.0.10.71
10.0.10.77
10.0.10.81
10.0.10.85
10.0.10.94
10.0.10.108
10.0.10.112
10.0.10.119
10.0.10.135
10.0.10.146
10.0.10.169
10.0.10.176
10.0.10.177
10.0.10.178
10.0.10.185
10.0.10.197
10.0.10.198
10.0.10.203
10.0.10.204
10.0.10.205
10.0.10.212
10.0.10.214
10.0.10.235
10.0.10.240
10.0.11.13
10.0.11.40
10.0.11.47
10.0.11.165
10.0.11.192
10.0.11.208
10.0.11.221
10.0.20.8
10.0.20.10
10.0.20.12
10.0.20.14
10.0.20.21
10.0.20.24
10.0.20.26
10.0.20.34
10.0.20.35
10.0.20.36
10.0.20.37
10.0.20.38
10.0.20.43
10.0.20.48
10.0.20.50
10.0.20.52
10.0.20.58
10.0.20.60
10.0.20.61
10.0.20.68
10.0.20.71
10.0.20.75
10.0.20.76
10.0.20.81
10.0.20.83
10.0.20.84
10.0.20.89
10.0.20.90
10.0.20.92
10.0.20.105
10.0.20.106
10.0.20.107
10.0.20.113
10.0.20.114
10.0.20.119
10.0.20.120
10.0.20.134
10.0.20.135
10.0.20.137
10.0.20.141
10.0.20.149
10.0.20.151
10.0.20.153
10.0.20.155
10.0.20.165
10.0.20.168
10.0.20.170
10.0.20.174
10.0.20.175
10.0.20.177
10.0.20.189
10.0.20.190
10.0.20.194
10.0.20.196
10.0.20.198
10.0.20.202
10.0.20.203
10.0.20.206
10.0.20.216
10.0.20.219
10.0.20.232
10.0.20.233
10.0.20.241
10.0.20.244
10.0.20.245
10.0.20.246
10.0.20.247
10.0.30.9
10.0.30.10
10.0.30.15
10.0.30.19
10.0.30.22
10.0.30.25
10.0.30.35
10.0.30.36
10.0.30.37
10.0.30.42
10.0.30.49
10.0.30.50
10.0.30.55
10.0.30.56
10.0.30.57
10.0.30.61
10.0.30.69
10.0.30.78
10.0.30.85
10.0.30.94
10.0.30.98
10.0.30.100
10.0.30.101
10.0.30.102
10.0.30.108
10.0.30.122
10.0.30.132
10.0.30.137
10.0.30.138
10.0.30.142
10.0.30.149
10.0.30.152
10.0.30.153
10.0.30.160
10.0.30.164
10.0.30.167
10.0.30.168
10.0.30.169
10.0.30.176
10.0.30.182
10.0.30.183
10.0.30.184
10.0.30.188
10.0.30.197
10.0.30.198
10.0.30.207
10.0.30.208
10.0.30.213
10.0.30.217
10.0.30.218
10.0.30.219
10.0.30.227
10.0.30.229
10.0.30.239
10.0.30.245
10.0.40.12
10.0.40.14
10.0.40.21
10.0.40.68
10.0.40.106
10.0.40.113
10.0.40.119
10.0.40.139
10.0.40.148
10.0.40.186
10.0.40.196
10.0.40.203
10.0.40.212
10.0.40.230
10.0.40.233
10.0.50.16
10.0.50.18
10.0.50.19
10.0.50.22
10.0.50.23
10.0.50.24
10.0.50.27
10.0.50.30
10.0.50.31
10.0.50.34
10.0.50.37
10.0.50.39
10.0.50.42
10.0.50.43
10.0.50.45
10.0.50.49
10.0.50.54
10.0.50.73
10.0.50.74
10.0.50.78
10.0.50.82
10.0.50.86
10.0.50.88
10.0.50.90
10.0.50.93
10.0.50.96
10.0.50.99
10.0.50.100
10.0.50.102
10.0.50.105
10.0.50.107
10.0.50.108
10.0.50.109
10.0.50.115
10.0.50.116
10.0.50.117
10.0.50.123
10.0.50.125
10.0.50.126
10.0.50.127
10.0.50.129
10.0.50.131
10.0.50.138
10.0.50.139
10.0.50.141
10.0.50.143
10.0.50.147
10.0.50.152
10.0.50.155
10.0.50.156
10.0.50.157
10.0.50.158
10.0.50.160
10.0.50.161
10.0.50.168
10.0.50.169
10.0.50.170
10.0.50.171
10.0.50.172
10.0.50.180
10.0.50.181
10.0.50.197
10.0.50.198
10.0.50.200
10.0.50.201
10.0.50.205
10.0.50.211
10.0.50.212
10.0.50.213
10.0.50.215
10.0.50.220
10.0.50.221
10.0.50.223
10.0.50.226
10.0.50.227
10.0.50.229
10.0.50.232
10.0.50.234
10.0.50.235
10.0.50.238
10.0.50.239
10.0.50.240
10.0.50.242
10.0.50.243
10.0.50.244
10.0.50.249
10.0.50.253
10.0.60.14
10.0.60.57
10.0.60.87
10.0.60.94
10.0.60.167
10.0.60.185
10.0.60.196
10.0.60.210
10.0.60.214
10.0.60.223
10.0.70.97
10.0.70.134
10.0.70.147
10.0.70.175
10.0.70.189
10.0.70.194
10.0.70.198
10.0.80.20
10.0.80.25
10.0.80.59
10.0.80.63
10.0.80.64
10.0.80.65
10.0.80.66
10.0.80.69
10.0.80.99
10.0.80.111
10.0.80.134
10.0.80.146
10.0.80.150
10.0.80.155
10.0.80.165
10.0.80.192
10.0.80.196
10.0.80.226
10.0.80.228
10.0.80.231
10.0.90.39
10.0.90.40
10.0.90.44
10.0.90.48
10.0.90.58
10.0.90.63
10.0.90.64
10.0.90.83
10.0.90.88
10.0.90.104
10.0.90.111
10.0.90.126
10.0.90.128
10.0.90.155
10.0.90.159
10.0.90.163
10.0.90.171
10.0.90.190
10.0.90.191
10.0.90.192
10.0.90.201
10.0.90.218
10.0.90.222
10.0.90.224
10.0.90.249
10.0.90.251
10.0.91.63
10.0.91.192
10.0.91.202
10.0.100.12
10.0.100.25
10.0.100.37
10.0.100.42
10.0.100.56
10.0.100.73
10.0.100.84
10.0.100.97
10.0.100.105
10.0.100.108
10.0.100.111
10.0.100.115
10.0.100.117
10.0.100.118
10.0.100.119
10.0.100.124
10.0.100.138
10.0.100.139
10.0.100.142
10.0.100.148
10.0.100.149
10.0.100.152
10.0.100.154
10.0.100.160
10.0.100.162
10.0.100.164
10.0.100.170
10.0.100.172
10.0.100.175
10.0.100.176
10.0.100.181
10.0.100.183
10.0.100.184

301
qt.py
View file

@ -1,301 +0,0 @@
import sys
import os
import paramiko
import re
import json
import csv
import logging
from datetime import datetime
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel, QFileDialog, QTextEdit, QTreeWidget, QTreeWidgetItem, QSplitter, QHBoxLayout, QPlainTextEdit
from PyQt5.QtCore import Qt
# Constants for error types
ASIC_ERROR = "ASIC Error"
EEPROM_ERROR = "EEPROM Error"
CHIP_BIN_ERROR = "Chip Bin Error"
# Logging configuration
logging.basicConfig(filename='miner_logs.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Load credentials from a JSON file
def load_credentials(file_path):
with open(file_path, 'r') as file:
return json.load(file)
# Load error keywords and types from a JSON file
def load_error_keywords(file_path):
with open(file_path, 'r') as file:
return json.load(file)['error_keywords']
# Regex patterns for ASIC chip errors and power-off messages
asic_pattern = re.compile(r"Chain\[(\d+)\]: find (\d+) asic, times \d+")
power_off_pattern = re.compile(r"Chain (\d+) only find (\d+) asic, will power off hash board (\d+)")
eeprom_error_pattern = re.compile(r"Data load fail for chain (\d+)\.")
chip_bin_pattern = re.compile(r"No chip bin, chain = (\d+)")
# Function to read IP addresses from a file
def read_ips(file_path):
with open(file_path, 'r') as file:
ips = file.readlines()
return [ip.strip() for ip in ips]
# Function to establish an SSH connection
def establish_ssh_connection(ip, username, password):
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh_client.connect(ip, username=username, password=password, timeout=5)
logging.info(f"Connected to {ip} with {username}")
return ssh_client
except Exception as e:
logging.error(f"Failed to connect to {ip} with {username}:{password} - {e}")
return None
# Function to execute a command via SSH and return the output
def execute_ssh_command(ssh_client, command):
try:
stdin, stdout, stderr = ssh_client.exec_command(command)
return stdout.read().decode('utf-8')
except Exception as e:
logging.error(f"Error executing command '{command}': {e}")
return None
# Function to get worker ID
def get_worker_id(ssh_client):
config_content = execute_ssh_command(ssh_client, "cat /config/cgminer.conf")
if config_content:
match = re.search(r'"user" *: *"[^.]*\.(\w+)"', config_content)
if match:
return match.group(1)
return "Unknown"
# Function to check log files for keywords and ASIC errors
def check_logs(ip, ssh_client, worker_id, current_date, error_keywords):
logs = []
asic_errors = set() # Using set to avoid duplicate errors
results = [] # Using list to avoid duplicate entries
log_files_content = execute_ssh_command(ssh_client, "find /var/log/ -type f")
if log_files_content:
log_files = log_files_content.splitlines()
for log_file in log_files:
log_content = execute_ssh_command(ssh_client, f"cat {log_file}")
if log_content:
seen_errors = set()
for keyword, error_type in error_keywords.items():
if keyword in log_content and (log_file, error_type, keyword) not in seen_errors:
logs.append((log_file, error_type, keyword))
seen_errors.add((log_file, error_type, keyword))
for match in asic_pattern.finditer(log_content):
chain, asic_count = match.groups()
asic_errors.add((chain, int(asic_count)))
for match in power_off_pattern.finditer(log_content):
chain, found_asic_count, board = match.groups()
chain = int(chain)
found_asic_count = int(found_asic_count)
if (log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
seen_errors.add((log_file, ASIC_ERROR, f"Chain {chain} has failed with {found_asic_count} ASICs found and will power off hash board {board}"))
for match in eeprom_error_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, EEPROM_ERROR, f"Data load fail for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, EEPROM_ERROR, f"Data load fail for chain {chain}"))
seen_errors.add((log_file, EEPROM_ERROR, f"Data load fail for chain {chain}"))
for match in chip_bin_pattern.finditer(log_content):
chain = match.group(1)
if (log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}") not in seen_errors:
results.append((current_date, worker_id, ip, log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}"))
seen_errors.add((log_file, CHIP_BIN_ERROR, f"No chip bin for chain {chain}"))
return logs, asic_errors, results
# Function to write results to a text file in the specified format
def write_text_file(file_path, results):
with open(file_path, 'w') as file:
current_worker = None
for result in results:
date, worker_id, ip, log_file, error_type, error_message = result
if worker_id != current_worker:
if current_worker is not None:
file.write("\n") # Add a blank line between different workers
file.write(f"{worker_id}\n")
current_worker = worker_id
file.write(f"- {error_type}\n")
file.write(f"--- {error_message}\n")
file.write(f"-" * 80 + "\n")
# Function to browse for a file
def browse_file(label):
options = QFileDialog.Options()
options |= QFileDialog.ReadOnly
file_path, _ = QFileDialog.getOpenFileName(None, "Select File", "", "All Files (*);;Text Files (*.txt)", options=options)
if file_path:
label.setText(file_path)
# Main application class
class MinerApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
main_layout = QVBoxLayout()
# File path labels
self.ips_label = QLabel("No IPs file selected")
self.credentials_label = QLabel("No credentials file selected")
self.errors_label = QLabel("No errors file selected")
# Set default paths if files exist
self.set_default_paths()
# Text edit for logs
self.logs_text = QPlainTextEdit()
self.logs_text.setReadOnly(True)
# Tree views for machines and errors
self.machines_tree = QTreeWidget()
self.machines_tree.setHeaderLabel("Machines")
self.machines_tree.itemClicked.connect(self.display_errors)
self.errors_tree = QTreeWidget()
self.errors_tree.setHeaderLabel("Errors")
# Buttons
browse_ips_btn = QPushButton('Browse IPs File')
browse_ips_btn.clicked.connect(lambda: browse_file(self.ips_label))
browse_credentials_btn = QPushButton('Browse Credentials File')
browse_credentials_btn.clicked.connect(lambda: browse_file(self.credentials_label))
browse_errors_btn = QPushButton('Browse Errors File')
browse_errors_btn.clicked.connect(lambda: browse_file(self.errors_label))
start_btn = QPushButton('Start')
start_btn.clicked.connect(self.start_process)
# Splitter for tree views
tree_splitter = QSplitter()
tree_splitter.addWidget(self.machines_tree)
tree_splitter.addWidget(self.errors_tree)
# Splitter for the main layout and logs
main_splitter = QSplitter(Qt.Vertical)
main_splitter.addWidget(tree_splitter)
main_splitter.addWidget(self.logs_text)
main_splitter.setSizes([500, 100]) # Initial sizes: larger for the tree views, smaller for the logs
# Add widgets to layout
main_layout.addWidget(browse_ips_btn)
main_layout.addWidget(self.ips_label)
main_layout.addWidget(browse_credentials_btn)
main_layout.addWidget(self.credentials_label)
main_layout.addWidget(browse_errors_btn)
main_layout.addWidget(self.errors_label)
main_layout.addWidget(start_btn)
main_layout.addWidget(main_splitter)
self.setLayout(main_layout)
self.setWindowTitle('Miner Error Checker')
self.show()
def set_default_paths(self):
default_ips_path = "ips.txt"
default_credentials_path = "credentials.json"
default_errors_path = "errors.json"
if os.path.exists(default_ips_path):
self.ips_label.setText(default_ips_path)
if os.path.exists(default_credentials_path):
self.credentials_label.setText(default_credentials_path)
if os.path.exists(default_errors_path):
self.errors_label.setText(default_errors_path)
def start_process(self):
ips_path = self.ips_label.text()
credentials_path = self.credentials_label.text()
errors_path = self.errors_label.text()
if ips_path == "No IPs file selected" or credentials_path == "No credentials file selected" or errors_path == "No errors file selected":
self.logs_text.appendPlainText("Please select all required files.")
return
credentials = load_credentials(credentials_path)
error_keywords = load_error_keywords(errors_path)
ips = read_ips(ips_path)
results = []
current_date = datetime.now().strftime('%Y-%m-%d')
self.machines_tree.clear()
self.errors_tree.clear()
for ip in ips:
self.logs_text.appendPlainText(f"Processing IP: {ip}")
logging.info(f"Processing IP: {ip}")
connected = False
for os_type, creds in credentials.items():
if connected:
break
for username, password in creds:
ssh_client = establish_ssh_connection(ip, username, password)
if ssh_client:
connected = True
worker_id = get_worker_id(ssh_client)
logs, asic_errors, asic_results = check_logs(ip, ssh_client, worker_id, current_date, error_keywords)
results.extend(asic_results)
for log in logs:
results.append((current_date, worker_id, ip, log[0], log[1], log[2]))
unique_asic_errors = {}
for chain, asic_count in asic_errors:
failed_checks = unique_asic_errors.get(chain, 0) + 1
unique_asic_errors[chain] = failed_checks
if asic_count == 0 and failed_checks == 3:
results.append((current_date, worker_id, ip, "N/A", ASIC_ERROR, f"Chain {chain} has 3 failed checks with {asic_count} ASICs found"))
self.add_machine_item(ip, worker_id, results)
ssh_client.close()
break
# Write results to CSV
csv_file = 'results.csv'
logging.info(f"Writing results to {csv_file}")
with open(csv_file, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(["Date", "Worker ID", "IP Address", "Log File", "Error Type", "Error Message"])
for result in results:
writer.writerow(result)
# Write results to text file
text_file = 'results.txt'
logging.info(f"Writing results to {text_file}")
write_text_file(text_file, results)
self.logs_text.appendPlainText("Process completed. Results saved to results.csv and results.txt.")
def add_machine_item(self, ip, worker_id, results):
machine_item = QTreeWidgetItem([f"{worker_id} ({ip})"])
machine_item.setData(0, 1, results) # Store results in the item for later retrieval
self.machines_tree.addTopLevelItem(machine_item)
def display_errors(self, item, column):
self.errors_tree.clear()
results = item.data(0, 1) # Retrieve stored results from the item
if results:
for result in results:
date, worker_id, ip, log_file, error_type, error_message = result
error_item = QTreeWidgetItem([f"{error_type}: {error_message}"])
self.errors_tree.addTopLevelItem(error_item)
# Main function to run the PyQt5 application
def main():
app = QApplication(sys.argv)
ex = MinerApp()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
# lol