-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
180 lines (175 loc) · 7.28 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import asyncio,logging,os,shlex,subprocess,config,re
from time import sleep
from libs.pyboinc._parse import parse_generic
from libs.pyboinc import init_rpc_client
import libs.pyboinc
from typing import Union,Dict,List,Tuple,Any
import xml.etree.ElementTree as ET
import logging.handlers
loop = asyncio.get_event_loop()
enable_temp_control=True
temp_sleep_time=10
# Translates BOINC's CPU and GPU Mode replies into English. Note difference between keys integer vs string.
CPU_MODE_DICT = {
1: 'always',
2: 'auto',
3: 'never'
}
GPU_MODE_DICT = {
'1': 'always',
'2': 'auto',
'3': 'never'
}
# import user settings
from config import *
# setup logging
handler = logging.handlers.RotatingFileHandler(os.environ.get("LOGFILE", "debug.log"),maxBytes=max_logfile_size_in_mb*1024*1024,backupCount=1)
formatter = logging.Formatter(logging.BASIC_FORMAT)
handler.setFormatter(formatter)
log = logging.getLogger()
log.setLevel(os.environ.get("LOGLEVEL", log_level))
log.addHandler(handler)
async def setup_connection(boinc_ip:str=boinc_ip,boinc_password:str=boinc_password,port:int=31416)->libs.pyboinc.rpc_client:
"""
Sets up a BOINC RPC client connection
"""
rpc_client = await init_rpc_client(boinc_ip, boinc_password, port=port)
return rpc_client
def print_and_log(msg:str,log_level:str)->None:
"""
Print a message and add it to the log at log_level. Valid log_levels are DEBUG, INFO, WARNING, ERROR
"""
print(msg)
if log_level=='DEBUG':
log.debug(msg)
elif log_level=='WARNING':
log.warning(msg)
elif log_level=='INFO':
log.info(msg)
elif log_level=='ERROR':
log.error(msg)
else:
print('Being asked to log at an unknown level: {}'.format(log_level))
def temp_check()->bool:
"""
Returns True if we should keep crunching based on temperature, False otherwise
"""
if not enable_temp_control:
return True
match=None
text=''
if temp_url:
import requests as req
try:
text=req.get(temp_url).text
except Exception as e:
print('Error checking temp: {}'.format(e))
log.error('Error checking temp: {}'.format(e))
return True
elif temp_command:
command=shlex.split(temp_command)
try:
text=subprocess.check_output(command)
except Exception as e:
print('Error checking temp: {}'.format(e))
log.error('Error checking temp: {}'.format(e))
return True
command_output=config.temp_function()
if command_output:
text=str(command_output)
pattern=re.compile(temp_regex)
match = re.search(pattern, text)
if match:
found_temp=int(match.group(0))
log.debug('Found temp {}'.format(found_temp))
if found_temp > stop_temp or found_temp < start_temp:
return False
else:
print('No temps found!')
log.error('No temps found!')
return True
return True
async def run_rpc_command(rpc_client:libs.pyboinc.rpc_client,command:str,arg1:Union[str,None]=None,arg1_val:Union[str,None]=None,arg2:Union[str,None]=None,arg2_val:Union[str,None]=None)->Union[str,Dict[Any,Any]]:
"""
Runs command on BOINC client via RPC
Example: run_rpc_command(rpc_client,'project_nomorework','http://project.com/project')
"""
full_command='{} {} {} {}'.format(command,arg1,arg1_val,arg2,arg2_val) # added for debugging purposes
log.debug('Running BOINC rpc request '+full_command)
req = ET.Element(command)
if arg1 is not None:
a = ET.SubElement(req, arg1)
if arg1_val is not None:
a.text = arg1_val
if arg2 is not None:
b = ET.SubElement(req, arg2)
if arg2_val is not None:
b.text = arg2_val
response = await rpc_client._request(req)
parsed = parse_generic(response)
if not str(parsed):
print('Warning: Error w RPC command {}: {}'.format(full_command,parsed))
log.error('Warning: Error w RPC command {}: {}'.format(full_command, parsed))
return parsed
def boinc_loop(dev_loop:bool=False,rpc_client=None,client_rpc_client=None,time:int=0):
"""
Main routine which manages BOINC
:param dev_loop: set to True if we are crunching for developer
:param rpc_client BOINC rpc client. Pass in developer client if crunching for developer
:param client_rpc_client client BOINC rpc client, as it must be accessed in dev mode and kept in suspend
:param time How long to crunch for. Only used by dev mode at the moment
"""
if not client_rpc_client:
client_rpc_client=rpc_client
# these variables are referenced outside the loop (or in recursive calls of the loop) so should be made global
global combined_stats
global final_project_weights
global total_preferred_weight
global total_mining_weight
global highest_priority_projects
global priority_results
global dev_project_weights
global DEV_BOINC_PASSWORD
global DEV_LOOP_RUNNING
while True:
# Re-authorize in case we have become de-authorized since last run
authorize_response = loop.run_until_complete(rpc_client.authorize())
# Get BOINC's starting CPU and GPU modes
existing_mode_info = loop.run_until_complete(run_rpc_command(rpc_client, 'get_cc_status'))
existing_cpu_mode = existing_mode_info['task_mode']
existing_gpu_mode = str(existing_mode_info['gpu_mode'])
if existing_cpu_mode in CPU_MODE_DICT:
existing_cpu_mode = CPU_MODE_DICT[existing_cpu_mode]
else:
print_and_log('Error: Unknown cpu mode {}'.format(existing_cpu_mode),'ERROR')
if existing_gpu_mode in GPU_MODE_DICT:
existing_gpu_mode = GPU_MODE_DICT[existing_gpu_mode]
else:
print_and_log('Error: Unknown gpu mode {}'.format(existing_gpu_mode),"ERROR")
# If temp is too high:
if not temp_check():
while True: # Keep sleeping until we pass a temp check
print_and_log('Sleeping due to temperature','INFO')
# Put BOINC into sleep mode, automatically reverting if script closes unexpectedly
sleep_interval=str(int(((60*temp_sleep_time)+60)))
loop.run_until_complete(
run_rpc_command(rpc_client, 'set_run_mode', 'never', sleep_interval))
loop.run_until_complete(
run_rpc_command(rpc_client, 'set_gpu_mode', 'never', sleep_interval))
sleep(60*temp_sleep_time)
if temp_check():
# Reset to initial crunching modes now that temp is satisfied
print_and_log('Crunching again due to temperature', 'INFO')
loop.run_until_complete(
run_rpc_command(rpc_client, 'set_run_mode', existing_cpu_mode))
loop.run_until_complete(
run_rpc_command(rpc_client, 'set_gpu_mode', existing_gpu_mode))
break
else:
print('Temperature is good, crunching...')
sleep(temp_sleep_time)
if __name__=='__main__':
print('Setting up BOINC connection...')
rpc_client = loop.run_until_complete(setup_connection(boinc_ip,boinc_password,boinc_port)) # setup BOINC RPC connection
print('Starting BOINC control...')
boinc_loop(False,rpc_client)