Last active
June 6, 2020 04:09
-
-
Save stevennic22/2cd69cbb86bbdff38413c3633a7141c2 to your computer and use it in GitHub Desktop.
Updated Folding@Home Telnet monitoring script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sun May 17 05:20:08 2020 | |
StevensPC: 215k ppd | |
Slot: 01 - Unit: 02: DOWNLOAD, 0 attempts | |
Slot: 00 - Unit: 03: RUNNING, 41.23% done, ETA: 02:25:00, Points: 23466 | |
Slot: 01_01 - Unit: 01: RUNNING, 99.51% done, ETA: 00:01:55, Points: 131020 | |
Slot: 02 - Unit: 00: RUNNING, 53.54% done, ETA: 05:53:00, Points: 60611 | |
FoldingServer: 27k ppd | |
Slot: 00 - Unit: 01: RUNNING, 82.70% done, ETA: 00:33:09, Points: 26954 | |
HPFolding: 5902 ppd | |
Slot: 00 - Unit: 00: RUNNING, 2.23% done, ETA: 08:47:00, Points: 5902 | |
Total ppd= 248k ppd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
## Original script source: https://forums.anandtech.com/threads/folding-home-fahclient-config-control-manual-page.2574018/#post-40108202 | |
import time, sys, telnetlib | |
# Enter one or more hosts here. | |
# Can be 'localhost', or IP addresses like '192.168.0.5', or hostnames. | |
# Enclose in '' and separate by comma. | |
hosts = [ | |
#['IP or hostname', port , "DisplayName in output"], | |
['StevensPC.home', 36330 , "StevensPC"], | |
['10.10.10.250', 36330, "FoldingServer"], | |
['10.10.10.242', 36330, "HPFolding"] | |
] | |
# The intervals in which to check, in minutes: | |
loopdelay = 5 | |
# Enable/Disable continuous looping | |
loop = True | |
# Enable debug output | |
debug = False | |
def format_ppd(ppd): | |
if ppd >= 1e6: | |
return "%.2fM ppd" % (ppd / 1e6) | |
if ppd >= 1e4: | |
return "%.0fk ppd" % (ppd / 1e3) | |
return "%d ppd" % (ppd) | |
def format_duration(dur): | |
d = dur // (24*3600) | |
dur -= d*24*3600 | |
h = dur // 3600 | |
dur -= h*3600 | |
m = dur // 60 | |
dur -= m*60 | |
s = dur | |
if d > 0: | |
return "%02.0fd%02.0fh" % (d, h) | |
else: | |
return "%02.0f:%02.0f:%02.0f" % (h, m, s) | |
def parse_duration(dur): | |
d = 0. | |
for t in dur.split(): | |
if t == 'days': | |
return d*24.*3600. | |
elif t == 'hours': | |
d *= 60. | |
elif t == 'mins': | |
d *= 60. | |
elif t == 'secs': | |
pass | |
else: | |
d += float(t) | |
return d | |
# Class to handle client information and connections | |
class FAHClients: | |
def __init__(self, name, ip, port): | |
self.name = name | |
self.ip = ip | |
self.port = port | |
self.host_ppd = 0 | |
#Class function to gather queue information and handle connection to servers | |
def connect_to_server(self): | |
try: | |
x = 0 | |
while x < 2: | |
self.tn = telnetlib.Telnet(self.ip, self.port, 10) | |
if debug: | |
print("\nConnecting...") | |
self.tn.set_debuglevel(100) | |
buftest = self.tn.read_until(b'Welcome to the FAHClient command server.\n> ', 10).decode('utf-8') | |
if "FAHClient" not in buftest: | |
self.tn.write(b'exit\n') | |
self.tn.close() | |
x += 1 | |
continue | |
self.tn.write(b'slot-info\n') | |
slotbuf = self.tn.read_until(b'---\n', 10).decode('utf-8') | |
slots = eval(slotbuf.split('PyON 1 slots\n')[1].split('\n---\n')[0]) | |
trip = True | |
for x in slots: | |
if x["status"] == "RUNNING": | |
trip = False | |
if trip: | |
self.queue = [] | |
return("Information retrieved") | |
self.tn.write(b'queue-info\n') | |
bufdec = self.tn.read_until(b'---\n>', 10).decode('utf-8') | |
self.tn.write(b'exit\n') | |
self.tn.close() | |
if len(bufdec.split('PyON 1 units\n')) > 1: | |
self.queue = eval(bufdec.split('PyON 1 units\n')[1].split('\n---\n')[0]) | |
if len(self.queue) > 0: | |
break | |
x += 1 | |
else: | |
if not hasattr(self, "queue"): | |
raise(TimeoutError) | |
except TimeoutError as err: | |
return("Cannot retrieve info") | |
except: | |
if debug: | |
print(sys.exc_info()) | |
return("Cannot connect") | |
return("Information Retrieved") | |
#Parses the information for each host | |
def parse_info(self): | |
self.slots = {} | |
for i in self.queue: | |
if debug: | |
print(i) | |
indx = i['slot'] | |
x = 1 | |
#Adjusts slot index to handle multiple instances of slots (when downloading and working at the same time). | |
while indx in self.slots: | |
if x < 10: | |
indx = i['slot'] + "_0" + str(x) | |
else: | |
indx = i['slot'] + "_" + str(x) | |
x += 1 | |
self.slots[indx] = {"state": i['state'], "unit": i["id"], "waiting": i["waitingon"]} | |
#Store targeted information from queue based on state | |
if i['state'] == 'DOWNLOAD': | |
self.slots[indx]['attempts'] = i['attempts'] | |
if self.slots[indx]['attempts'] > 0: | |
self.slots[indx]["download_duration"] = parse_duration(i['nextattempt']) | |
self.slots[indx]["formatted_duration"] = format_duration(self.slots[indx]["download_duration"]) | |
else: | |
self.slots[indx]["download_duration"] = "" | |
self.slots[indx]["formatted_duration"] = "" | |
elif i['state'] == 'RUNNING': | |
self.host_ppd += int(i['ppd']) | |
self.slots[indx]["ppd"] = int(i['ppd']) | |
self.slots[indx]["pctdone"] = i['percentdone'] | |
self.slots[indx]["duration"] = format_duration(parse_duration(i['eta'])) | |
elif i['state'] == 'SEND': | |
self.slots[indx]["cs"] = i["cs"] | |
self.slots[indx]["ws"] = i["ws"] | |
self.slots[indx]["sunit"] = i["unit"] | |
def main(): | |
print(time.ctime(), end="\n\n") | |
total_ppd = 0 | |
for h in hosts: | |
if len(h) > 2: | |
host = FAHClients(h[2], h[0], h[1]) | |
else: | |
host = FAHClients(h[0], h[0], h[1]) | |
print(host.name, end="") | |
conn = host.connect_to_server() | |
#Skip parsing information if no information to parse. | |
if "cannot" in conn.lower(): | |
print("\n" + conn, end="\n\n") | |
continue | |
host.parse_info() | |
total_ppd += host.host_ppd | |
print(': {0!s}'.format(format_ppd(host.host_ppd))) | |
#Loop through hosts and output queue info based on state | |
for slot, info in host.slots.items(): | |
print("Slot: {slot!s} - Unit: {id!s}: {state!s}".format(slot=slot, id=info["unit"], state=info["state"]), end="") | |
if info["state"] == "DOWNLOAD": | |
if info['attempts'] > 0: | |
print(', {attempts!s} attempts, next in {duration}'.format(attempts=info['attempts'], duration=info['formatted_duration'])) | |
else: | |
print(', 0 attempts') | |
elif info["state"] == "RUNNING": | |
print(", {pctdone} done, ETA: {eta}, Points: {ppd}".format(pctdone=info["pctdone"], eta=info["duration"], ppd = info["ppd"])) | |
elif info['state'] == 'SEND': | |
print(' ~{unit} to ws {ws}, cs {cs}'.format(unit=info["sunit"][-8:], ws=info["ws"], cs=info["cs"])) | |
elif info['state'] == 'READY': | |
if info["waiting"] == "": | |
print(", PAUSED") | |
else: | |
print(', waiting on: {0}'.format(info["waiting"])) | |
if host.slots.__len__() < 1: | |
print("No active work units") | |
print("") | |
if len(hosts) >= 1: | |
print('\nTotal ppd= {total}'.format(total=format_ppd(total_ppd)), end="\n\n") | |
if __name__ == "__main__": | |
main() | |
while loop: | |
time.sleep(loopdelay*60) | |
print("------------------------------------------", end="\n\n\n") | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment