def run(self): hwList = [] nv = Nvidia(self.distribution, self.log) ati = ATI(self.distribution, self.log) bc = Broadcom(self.distribution, self.log) pae = PAE(self.distribution, self.log) # Collect supported hardware hwNvidia = nv.getNvidia() hwATI = ati.getATI() hwBroadcom = bc.getBroadcom() hwPae = pae.getPae() # Combine all found hardware in a single list for line in hwNvidia: hwList.append(line) for line in hwATI: hwList.append(line) for line in hwBroadcom: hwList.append(line) for line in hwPae: hwList.append(line) return hwList
def installPAE(self): try: cmdPae = 'apt-get -y --force-yes install linux-headers-686-pae linux-image-686-pae' # Check if already installed status = functions.getPackageStatus('linux-headers-686-pae') if status == packageStatus[0]: cmdPae += ' --reinstall' self.log.write('PAE kernel install command: ' + cmdPae, 'pae.installPAE', 'debug') self.ec.run(cmdPae) # Check for Nvidia nv = Nvidia(self.distribution, self.log) nvList = nv.getNvidia() self.log.write('Nvidia info: ' + str(nvList), 'pae.installPAE', 'debug') for nvInfo in nvList: if nvInfo[2] == packageStatus[0]: self.log.write('Install Nvidia drivers', 'pae.installPAE', 'info') nv.installNvidia() # Remove xorg.conf #xorg = '/etc/X11/xorg.conf' #if os.path.exists(xorg): # shutil.move(xorg, xorg + '.ddm') # self.log.write('Moved ' + xorg + ' to ' + xorg + '.ddm', 'pae.installPAE', 'info') except Exception, detail: self.log.write(detail, 'pae.installPAE', 'error')
def __init__(self): Config().load() if len(sys.argv) > 1 and sys.argv[1] == "--silent": Log().silent() Log().add('info', 'gpustatd %s starting up' % (Version().get())) if self.already_running(): Log().add('fatal', 'gpustatd is already running') self.create_pid_file() self.throttled = [] self.target_temperature = Config().get('target_temperature') self.fan_speed_temperature_ratio = Config().get( 'fan_speed_temperature_ratio') os.environ["DISPLAY"] = ":%d" % (Config().get('xorg_display_no')) Log().add('info', 'scanning devices') self.devices = Nvidia().refresh(True) self.speeds = {} for device_id in self.devices.keys(): self.speeds[device_id] = self.devices[device_id]['fan'] self.self_test()
def run(self): hwList = [] #mir = Mirror(self.distribution, self.log) nv = Nvidia(self.distribution, self.log) ati = ATI(self.distribution, self.log) bc = Broadcom(self.distribution, self.log) pae = PAE(self.distribution, self.log) # Collect supported hardware #mirror = mir.getFastestMirror() hwNvidia = nv.getNvidia() hwATI = ati.getATI() hwBroadcom = bc.getBroadcom() hwPae = pae.getPae() # Combine all found hardware in a single list #for line in mirror: # hwList.append(line) for line in hwPae: hwList.append(line) for line in hwNvidia: hwList.append(line) for line in hwATI: hwList.append(line) for line in hwBroadcom: hwList.append(line) return hwList
def start(self): i = 0 while True: if i % 10 == 0: self.devices = Nvidia().refresh(True) else: self.devices = Nvidia().refresh() for device_id in self.devices.keys(): device = self.devices[device_id] if "ignore_devices" in Config().keys() and Config().get( 'ignore_devices') and device['id'] in Config().get( 'ignore_devices'): continue self.speeds[device_id] = self.calculate_fan_speed(device) self.regulate_power_level(device) self.adjust_fan_speeds() if "automatically_reload_config_on_change" in Config().keys( ) and Config().get("automatically_reload_config_on_change"): if Config().reload_if_changed(): self.target_temperature = Config().get( 'target_temperature') self.fan_speed_temperature_ratio = Config().get( 'fan_speed_temperature_ratio') os.environ["DISPLAY"] = ":%d" % ( Config().get('xorg_display_no')) time.sleep(1) i += 1
def run(self): # Instantiate driver classes mir = Mirror(self.distribution, self.log) nv = Nvidia(self.distribution, self.log) ati = ATI(self.distribution, self.log) bc = Broadcom(self.distribution, self.log) pae = PAE(self.distribution, self.log) # First check for mirror for code in self.hwCodesWithStatusList: if code[0] == hwCodes[4]: if code[1] != packageStatus[2]: mir.installMirror() # Now install the hardware drivers for code in self.hwCodesWithStatusList: # First check for mirror if code[0] != hwCodes[4]: if code[0] == hwCodes[0]: if code[1] != packageStatus[2]: nv.installNvidia() elif code[0] == hwCodes[1]: if code[1] != packageStatus[2]: ati.installATI() elif code[0] == hwCodes[2]: if code[1] != packageStatus[2]: bc.installBroadcom() elif code[0] == hwCodes[3]: if code[1] != packageStatus[2]: pae.installPAE()
def regulate_power_level(self, device, force=False): card_limit = Config().get("temperature_limit") if not card_limit: card_limit = 80 if os.path.exists("/var/run/minotaur/%d.powerlimit" % (device['id'])): desired_limit = float( open("/var/run/minotaur/%d.powerlimit" % (device['id'])).read()) else: desired_limit = device['default_power_limit_f'] temp = device['gpu_t_i'] limit = device['limit_f'] if temp >= card_limit: deduction = 10 * (2**(temp - card_limit)) new_limit = desired_limit - deduction if new_limit < device['min_power_limit_f']: new_limit = device['min_power_limit_f'] if new_limit != limit: Log().add( 'warning', 'device %d: temperature is at %dC, throttling power limit to %dW' % (device['id'], temp, new_limit)) Nvidia().set_power_limit(device['id'], new_limit) if not device['id'] in self.throttled: self.throttled.append(device['id']) else: if device['id'] in self.throttled and limit < desired_limit: Log().add( 'info', 'device %d: temperature is at %dC, restoring optimum power limit of %dW' % (device['id'], temp, desired_limit)) Nvidia().set_power_limit(device['id'], desired_limit) self.throttled.remove(device['id']) elif force: if int(device['limit_f']) != int(desired_limit): Log().add( 'info', 'device %d: setting power limit %dW' % (device['id'], desired_limit)) Nvidia().set_power_limit(device['id'], desired_limit)
def sigint_handler(self, a, b): Log().add( 'info', 'interrupt received, setting fans to 85% and cards to default power limit' ) Nvidia().set_all_fans(85) for device_id in self.devices.keys(): if self.devices[device_id]['default_power_limit_f']: Nvidia().set_power_limit( device_id, self.devices[device_id]['default_power_limit_f']) sys.exit(0)
def print_algorithms(self): algorithms = Config().get('algorithms.single') + Config().get( 'algorithms.double') Calibration().load() device_classes = Nvidia().get_nvidia_device_classes() for device_class in device_classes: data = {} for algorithm in algorithms: data[algorithm] = {} for miner_name in Config().get('miners'): miner = eval('%s()' % (miner_name.title())) if algorithm in miner.supported_algorithms(): data[algorithm][miner_name] = Calibration( ).get_miner_hashrate_for_algorithm_on_device( miner_name, algorithm, device_class) else: data[algorithm][miner_name] = "-" self.display_device_algorithm_table(device_class, data) print "\nnote: - means not supported\n"
def sighup_handler(self, x, y): Log().add('info', 'SIGHUP caught, reloading config and benchmark data') Config().reload() Calibration().load() Miners().reload_config() for device in Nvidia().get_nvidia_devices(): if not self.device_in_list(device): device.update() if device.state == 'active': device.log( 'info', 'currently running algorithm %s with %s [profile=%s] [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.profile, device.algos[0]['region'])) elif device.state == 'calibrating': device.log( 'info', 'calibration in progress with algorithm %s using %s [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.algos[0]['region'])) self.devices.append(device) for device in self.devices: if device.state == 'active': device.apply_profile() Log().add('info', 'reload complete')
def run(self): # Instantiate driver classes nv = Nvidia(self.distribution, self.log) ati = ATI(self.distribution, self.log) bc = Broadcom(self.distribution, self.log) pae = PAE(self.distribution, self.log) for code in self.hwCodesWithStatusList: if code[0] == hwCodes[0]: if code[1] == packageStatus[0]: nv.removeNvidia() elif code[0] == hwCodes[1]: if code[1] == packageStatus[0]: ati.removeATI() elif code[0] == hwCodes[2]: if code[1] == packageStatus[0]: bc.removeBroadcom() elif code[0] == hwCodes[3]: if code[1] == packageStatus[0]: pae.removePAE()
def cleanup(self, all=0, device_id=None): if all: Miners().cleanup_workers(True) elif not all and not device_id: Miners().cleanup_workers(False) else: device = Device({"id": device_id}) Nvidia().set_default_profile(device) Miners().stop_device(device) sys.exit(0)
def pin(self, device_id, pool_name, pin_miner_name, algorithm, region): device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device = Device({"id": int(device_id)}) if pool_name not in Config().get('pools').keys(): Log().add('fatal', 'unknown pool') if not algorithm in Config().get( 'algorithms.single') and not algorithm in Config().get( 'algorithms.double'): Log().add('fatal', 'unknown algorithm') if pool_name != 'nicehash': region = None else: if region not in ['eu', 'usa', 'hk', 'jp', 'in', 'br']: Log().add('fatal', 'valid region is required for nicehash') Miners().poll() Miners().get_device_state(device) if device.state == "calibrating": Log().add( 'fatal', 'not pinning device %d - currently calibrating' % (device.id)) pin = { "pool_name": pool_name, "miner_name": pin_miner_name, "algorithm": algorithm, "region": region } with open("/var/run/minotaur/pin%d" % (device.id), "w") as f: f.write(yaml.dump(pin)) Log().add( 'info', 'pinned device %d to miner: %s pool %s algorithm: %s region: %s' % (device.id, pin_miner_name, pool_name, algorithm, region))
def do_self_test(self): for device_id in self.devices.keys(): device = self.devices[device_id] if not "ignore_devices" in Config().keys() or not Config().get( 'ignore_devices') or device['id'] not in Config().get( 'ignore_devices'): if not Nvidia().set_fan_control_state(device['id'], 1): Log().add( 'fatal', 'failed to toggle the fan control state for device: %d' % (device['id'])) self.fan_states[device['id']] = True self.regulate_power_level(device, True) return True
def scan_devices(self): self.devices = Nvidia().get_nvidia_devices() Calibration().load() if len(self.devices) < 1: Log().add('fatal', "no nvidia GPUs found") Log().add("info", "found %d nvidia GPUs" % (len(self.devices))) Calibration().check_calibrated_algorithms(self.devices) Log().add('info', 'retrieving state from miner backends') Miners().poll() for device in self.devices: device.update() if device.state == 'active': device.log( 'info', 'currently running algorithm %s with %s [profile=%s] [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.profile, device.algos[0]['region'])) device.apply_profile() elif device.state == 'calibrating': device.log( 'info', 'calibration in progress with algorithm %s using %s [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.algos[0]['region'])) device.period_start = self.startup_time device.grubtime = device.period_start + ( 60 * random.randrange(15, 1424))
def adjust_fan_speeds(self): fan_speed_changes = {} for device_id in self.devices.keys(): if self.devices[device_id]['fan'] != self.speeds[device_id]: if Config().get('informative'): Log().add( 'debug', '%d: device temp: %s target temperature: %s target fan speed: %d' % (device_id, self.devices[device_id]['gpu_t_i'], self.target_temperature, self.speeds[device_id])) fan_speed_changes[device_id] = self.speeds[device_id] if len(fan_speed_changes) > 0: Nvidia().apply_fan_speed_changes(fan_speed_changes)
def unpin(self, device_id): device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device_id = int(device_id) if os.path.exists("/var/run/minotaur/pin%d" % (device_id)): os.remove("/var/run/minotaur/pin%d" % (device_id)) Log().add('info', 'unpinned device %d' % (device_id)) else: Log().add('info', 'device %d is not pinned' % (device_id))
def pin_calibration(self, device_id): pin = {"calibration": True} device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device = Device({"id": int(device_id)}) with open("/var/run/minotaur/pin%d" % (device.id), "w") as f: f.write(yaml.dump(pin)) Log().add('info', 'pinned device %d to calibration' % (device.id))
def get_max_hashrate_and_power_draw(self, miner, device, time_limit): max_power_draw = 0 timeout = 30 count = 0 while len(device.algos) == 0: time.sleep(1) Miners.poll() count += 1 if count >= 30: device.log('fatal', 'timed out waiting for worker to start') algorithm = device.algos[0]['algo'] if "_" in algorithm: readings_a = [] readings_b = [] else: readings = [] time_start = time.time() count = 0 err = 0 reading_count = 0 stable = False variance_pc = None while stable == False and (time.time() - time_start) <= time_limit: time.sleep(1) if count % 5 == 0: Miners().poll() if not Miners().is_up(miner.name): device.log('fatal', 'miner API went away, aborting') Miners().get_device_state(device) if len(device.algos) == 0: device.log('fatal', 'worker was stopped by an external process') rate_a, rate_b = device.algos[0]['hashrate'] if "_" in algorithm: r_len = len(readings_a) else: r_len = len(readings) if r_len > 0: if "_" in algorithm: variance_pc, n = Calibration().get_variance( readings_a + [rate_a], readings_b + [rate_b]) else: variance_pc, n = Calibration().get_variance(readings + [rate_a]) device.log( 'info', 'hashrate: %s (variance %.2f%%)' % (device.hashrate_str(), variance_pc)) else: device.log('info', 'hashrate: %s' % (device.hashrate_str())) if rate_a == 0: if reading_count >= 3: device.log('warning', 'hashrate dropped to 0!') if device.state != 'calibrating': device.log( 'fatal', 'our worker was stopped by an external process, aborting.' ) err += 1 if err >= 10: device.stop() device.log( 'fatal', 'failed to get a hashrate reading from the worker' ) else: if "_" in algorithm: readings_a.append(rate_a) readings_b.append(rate_b) else: readings.append(rate_a) if variance_pc != None and Calibration().is_stable( variance_pc, n): device.log('info', 'hashrate stabilised') stable = True reading_count += 1 count += 1 metrics = Nvidia().get_nvidia_metrics_for_device(device.id) if metrics['power_f']: max_power_draw = metrics['power_f'] else: max_power_draw = None if stable == False: device.stop(True) device.log( 'error', 'hashrate failed to stabilise after %d minutes\ntry increasing the timeout or loosen the stabilisation tolerance by increasing the "hashrate_stabilisation_tolerance" parameter' % (time_limit / 60)) return [None, None] if "_" in algorithm: hashrate = [ Calibration().get_nominal_hashrate_from_range(readings_a), Calibration().get_nominal_hashrate_from_range(readings_b) ] else: hashrate = Calibration().get_nominal_hashrate_from_range(readings) return [hashrate, max_power_draw]
class GPUStatD: def __init__(self): Config().load() if len(sys.argv) > 1 and sys.argv[1] == "--silent": Log().silent() Log().add('info', 'gpustatd %s starting up' % (Version().get())) if self.already_running(): Log().add('fatal', 'gpustatd is already running') self.create_pid_file() self.throttled = [] self.target_temperature = Config().get('target_temperature') self.fan_speed_temperature_ratio = Config().get( 'fan_speed_temperature_ratio') os.environ["DISPLAY"] = ":%d" % (Config().get('xorg_display_no')) Log().add('info', 'scanning devices') self.devices = Nvidia().refresh(True) self.speeds = {} for device_id in self.devices.keys(): self.speeds[device_id] = self.devices[device_id]['fan'] self.self_test() def already_running(self): if not os.path.exists("/var/run/gpustatd"): try: ok.mkdir("/var/run/gpustatd", 0755) except: Log().add('fatal', 'unable to create /var/run/gpustatd') if os.path.exists("/var/run/gpustatd/gpustatd.pid"): pid = open("/var/run/gpustatd/gpustatd.pid").read().rstrip() return pid.isdigit() and os.path.exists("/proc/%s" % (pid)) def create_pid_file(self): if self.already_running(): Log().add( 'fatal', 'unable to start, there is another gpustatd process running') with open("/var/run/gpustatd/gpustatd.pid", "w") as f: f.write("%d" % (os.getpid())) def self_test(self): self.fan_states = {} Log().add('info', 'self-test') self.do_self_test() Log().add('info', 'looks good, monitoring') signal.signal(signal.SIGINT, self.sigint_handler) signal.signal(signal.SIGTERM, self.sigint_handler) signal.signal(signal.SIGHUP, self.sighup_handler) self.start() def sighup_handler(self): Log().add('info', 'HUP received, reloading config') Config().load() def sigint_handler(self, a, b): Log().add( 'info', 'interrupt received, setting fans to 85% and cards to default power limit' ) Nvidia().set_all_fans(85) for device_id in self.devices.keys(): if self.devices[device_id]['default_power_limit_f']: Nvidia().set_power_limit( device_id, self.devices[device_id]['default_power_limit_f']) sys.exit(0) def do_self_test(self): for device_id in self.devices.keys(): device = self.devices[device_id] if not "ignore_devices" in Config().keys() or not Config().get( 'ignore_devices') or device['id'] not in Config().get( 'ignore_devices'): if not Nvidia().set_fan_control_state(device['id'], 1): Log().add( 'fatal', 'failed to toggle the fan control state for device: %d' % (device['id'])) self.fan_states[device['id']] = True self.regulate_power_level(device, True) return True def start(self): i = 0 while True: if i % 10 == 0: self.devices = Nvidia().refresh(True) else: self.devices = Nvidia().refresh() for device_id in self.devices.keys(): device = self.devices[device_id] if "ignore_devices" in Config().keys() and Config().get( 'ignore_devices') and device['id'] in Config().get( 'ignore_devices'): continue self.speeds[device_id] = self.calculate_fan_speed(device) self.regulate_power_level(device) self.adjust_fan_speeds() if "automatically_reload_config_on_change" in Config().keys( ) and Config().get("automatically_reload_config_on_change"): if Config().reload_if_changed(): self.target_temperature = Config().get( 'target_temperature') self.fan_speed_temperature_ratio = Config().get( 'fan_speed_temperature_ratio') os.environ["DISPLAY"] = ":%d" % ( Config().get('xorg_display_no')) time.sleep(1) i += 1 def ensure_control_state(self, device_id, state): if self.fan_states[device_id] != state: Nvidia().set_fan_control_state(device_id, state) self.fan_states[device_id] = state def calculate_fan_speed(self, device): if device['gpu_t_i'] >= self.target_temperature: fan_speed = 100 else: fan_speed = 100 - ((self.target_temperature - device['gpu_t_i']) * self.fan_speed_temperature_ratio) if fan_speed < 0: fan_speed = 20 return fan_speed def adjust_fan_speeds(self): fan_speed_changes = {} for device_id in self.devices.keys(): if self.devices[device_id]['fan'] != self.speeds[device_id]: if Config().get('informative'): Log().add( 'debug', '%d: device temp: %s target temperature: %s target fan speed: %d' % (device_id, self.devices[device_id]['gpu_t_i'], self.target_temperature, self.speeds[device_id])) fan_speed_changes[device_id] = self.speeds[device_id] if len(fan_speed_changes) > 0: Nvidia().apply_fan_speed_changes(fan_speed_changes) def regulate_power_level(self, device, force=False): card_limit = Config().get("temperature_limit") if not card_limit: card_limit = 80 if os.path.exists("/var/run/minotaur/%d.powerlimit" % (device['id'])): desired_limit = float( open("/var/run/minotaur/%d.powerlimit" % (device['id'])).read()) else: desired_limit = device['default_power_limit_f'] temp = device['gpu_t_i'] limit = device['limit_f'] if temp >= card_limit: deduction = 10 * (2**(temp - card_limit)) new_limit = desired_limit - deduction if new_limit < device['min_power_limit_f']: new_limit = device['min_power_limit_f'] if new_limit != limit: Log().add( 'warning', 'device %d: temperature is at %dC, throttling power limit to %dW' % (device['id'], temp, new_limit)) Nvidia().set_power_limit(device['id'], new_limit) if not device['id'] in self.throttled: self.throttled.append(device['id']) else: if device['id'] in self.throttled and limit < desired_limit: Log().add( 'info', 'device %d: temperature is at %dC, restoring optimum power limit of %dW' % (device['id'], temp, desired_limit)) Nvidia().set_power_limit(device['id'], desired_limit) self.throttled.remove(device['id']) elif force: if int(device['limit_f']) != int(desired_limit): Log().add( 'info', 'device %d: setting power limit %dW' % (device['id'], desired_limit)) Nvidia().set_power_limit(device['id'], desired_limit)
def ensure_control_state(self, device_id, state): if self.fan_states[device_id] != state: Nvidia().set_fan_control_state(device_id, state) self.fan_states[device_id] = state
def start(self, device, pool_name, miner, algorithm, region, quick=False, force=False): self.miner = miner self.device = device self.default_profile = Config().get('device_profiles.default') Miners().reload_config() device.pin = { 'pool_name': pool_name, 'miner_name': miner.name, 'algorithm': algorithm, 'region': region } if not Config().get('calibration'): Log().add('fatal', "calibration config missing from config file") for key in [ "hashrate_stabilisation_timeout_mins", "hashrate_stabilisation_tolerance", "hashrate_stabilisation_consecutive_readings_required", "algorithm_start_timeout" ]: if Config().get('calibration.%s' % (key)) == None: Log().add('fatal', "missing config option: calibration.%s" % (key)) if Config().get('calibration.power_tuning.enable'): for key in ["decrement_watts", "acceptable_loss_percent"]: if not Config().get('calibration.power_tuning.%s' % (key)): Log().add( 'fatal', "missing config option: calibration.power_tuning.%s" % (key)) Config().set('pools.%s.append_device_id_to_worker_name' % (pool_name), False) if pool_name == 'miningpoolhub': if Config().get('pools.miningpoolhub.hub_workers.%s' % (algorithm))[-5:] != 'CALIB': Config().set( 'pools.miningpoolhub.hub_workers.%s' % (algorithm), Config().get('pools.miningpoolhub.hub_workers.%s' % (algorithm)) + 'CALIB') else: if Config().get('pools.%s.worker_name' % (pool_name))[-5:] != 'CALIB': Config().set( 'pools.%s.worker_name' % (pool_name), Config().get('pools.%s.worker_name' % (pool_name)) + "CALIB") if quick: Config().set('calibration.power_tuning.enable', False) profile = Profiles().get_for_device_algo(device, algorithm) Log().add('info', ' device id: %d' % (device.id)) Log().add('info', ' pool: %s' % (pool_name)) Log().add('info', ' device class: %s' % (device.dclass)) Log().add('info', 'device profile: %s' % (profile.name)) Log().add('info', ' miner: %s' % (miner.name)) Log().add('info', ' algorithm: %s' % (algorithm)) Log().add('info', ' region: %s' % (region)) Miners().poll() Miners().get_device_state(device) if device.state != 'inactive': if device.state == 'calibrating': device.log( 'fatal', 'this device is already being used for calibration') if force: device.log('info', 'terminating existing worker') if not device.stop(True): device.log('fatal', 'failed to stop existing worker') else: device.log( 'fatal', 'unable to start calibration - device is in use (use --force to override)' ) self.set_pin(device.id) default_power_limit = device.default_power_limit_f min_power_limit = device.min_power_limit_f max_power_limit = device.max_power_limit_f if max_power_limit == 0: device.log('info', 'device does not support power monitoring') else: device.log('info', 'max power limit: %d W' % (max_power_limit)) device.log( 'info', 'stabilisation tolerance: %.2f%%' % (Config().get('calibration.hashrate_stabilisation_tolerance'))) if Config().get('debug'): device.log('debug', 'loading default profile') Nvidia().set_default_profile(device) if Config().get('debug'): device.log('debug', 'initialising pools') Pools() sample_timeout = Config().get( 'calibration.hashrate_stabilisation_timeout_mins') if default_power_limit != None and Config().get( 'calibration.power_tuning.enable'): device.log( 'info', 'starting initial run at max power limit [timeout=%dmins]' % (sample_timeout)) else: device.log( 'info', 'starting single run [timeout=%dmins]' % (sample_timeout)) Miners().poll() Miners().get_device_state(device) if device.state != 'calibrating': if not device.start(True): device.log('fatal', 'worker failed to start') if Config().get('calibration.power_tuning.enable'): if not device.power_supported(): device.log('info', 'device does not support power monitoring') else: device.set_power_limit(max_power_limit) initial_hashrate, initial_max_power_draw = self.get_max_hashrate_and_power_draw( miner, device, sample_timeout * 60) if initial_hashrate == None: device.log( 'info', 'skipping algorithm as we failed to get a stable reading') print "" return if initial_max_power_draw != None and initial_max_power_draw > max_power_limit: initial_max_power_draw = max_power_limit device.log( 'info', 'benchmark hashrate: %s' % (Units().hashrate_str(initial_hashrate))) if initial_max_power_draw != None: device.log('info', 'max power draw: %.2f' % (initial_max_power_draw)) initial_power_limit = int(math.ceil(initial_max_power_draw)) hashrate = initial_hashrate if initial_max_power_draw == None: power_limit = None elif Config().get('calibration.power_tuning.enable'): if max_power_limit == 0 or min_power_limit == 0 or default_power_limit == 0: device.log( 'error', 'device did not give us sane values for its min/max/default power limits' ) device.log( 'error', 'unable to proceed with power calibration - device may not support changing the power limit' ) power_limit = default_power_limit else: power_limit = initial_power_limit device.log('info', 'tuning power limit for optimum efficiency') hashrate, power_limit = self.do_power_calibration( device, power_limit, miner, hashrate, min_power_limit) else: power_limit = default_power_limit Nvidia().set_default_profile(device) self.unset_pin(device.id) device.log('info', 'storing calibration data') Calibration().update_calibration_data(device.dclass, miner.name, algorithm, hashrate, power_limit) if not device.stop(True): device.log('fatal', "failed to stop the miner process") print "" device.log( 'info', ' calibrated hashrate: %s' % (Units().hashrate_str(hashrate))) if power_limit != None: device.log('info', 'calibrated power limit: %.2f W' % (power_limit)) print ""
def run(self): self.create_pid_file() self.initialise() signal.signal(signal.SIGINT, self.sigint_handler) signal.signal(signal.SIGTERM, self.sigint_handler) signal.signal(signal.SIGHUP, self.sighup_handler) power_draw_readings = self.load_power_draw_readings() profitability_readings = self.load_profitability_readings() main_loop_index = 0 self.miner_state = {} self.pool_refresh = None while True: if main_loop_index == 0: if self.pool_refresh == None or ( time.time() - self.pool_refresh ) >= Config().get('pool_refresh_interval'): Pools().refresh() self.pool_refresh = time.time() show_mbtc_total = False Miners().poll() for i in range(0, len(self.devices)): device = self.devices[i] device.update() device.log_stats() if not device.can_run(): continue #device.grubtime = self.startup_time += (60 * random.randrange(15, 1424)) # if device.grub == False and time.time() >= device.grubtime and time.time() <= (device.grubtime + (60 * 15)): # device.log('info', 'starting 15min donation period to the author') # device.grub = True # # Miners().schedule('restart', device) # elif device.grub == True and time.time() < device.grubtime or time.time() > (device.grubtime + (60 * 15)): # device.log('info', 'stopping donation period') # device.grub = False # device.period_start += 86400 # device.grubtime = device.period_start + (60 * random.randrange(15, 1424)) # # while not self.grubtime_is_unique(device.grubtime, i): # device.grubtime = device.period_start + (60 * random.randrange(15, 1424)) # # Miners().schedule('restart', device) # else: if True: if not device.running(): if Config().get('debug'): device.log( 'debug', 'device not running - starting worker') Miners().schedule('start', device) else: switched = False if not device.pin: if Pools().should_switch(device): Miners().schedule('restart', device) switched = True if not switched and Config( ).get('calibration.update_calibration_data_over_time' ): Calibration().handle_device_update(device) queued_device_ids = Miners().queue.keys() Miners().execute_queue() for device in self.devices: if device.state == 'active' and device.id not in queued_device_ids and not device.warming_up( ): show_mbtc_total = True device.log( 'info', '%s/%s[%s]: %s' % (device.algos[0]['pool'], device.algos[0]['algo'], device.algos[0]['miner'], device.hashrate_str())) device.check_hashrate() total_power, total_power_limit, total_mbtc_per_day = Nvidia( ).get_device_metrics(self.devices) if show_mbtc_total: Log().add( 'info', 'total profitability: %.2f mBTC/day' % (total_mbtc_per_day)) power_draw_readings = self.update_power_draw_readings( power_draw_readings, total_power + Config().get('system_draw_watts')) profitability_readings = self.update_profitability_readings( profitability_readings, total_mbtc_per_day) # load in calibration data from other devices that may be calibrating Calibration().load() Miners().wait_for_queue() time.sleep(1) main_loop_index += 1 if main_loop_index >= Config().get('refresh_interval'): main_loop_index = 0
def print_devices(self): for device in Nvidia().get_nvidia_devices(1): print "%d: %s [class: %s]" % (device.id, device.name, device.dclass) sys.exit()
class Minotaur: def __init__(self): self.data_path = os.getenv("HOME") + "/.minotaur" if not os.path.exists(self.data_path): os.mkdir(self.data_path) self.power_file = self.data_path + "/power.dat" self.profitability_file = self.data_path + "/profitability.dat" self.currency_rate_file = self.data_path + "/currencies.dat" self.startup_time = time.time() Config().load() def already_running(self): if not os.path.exists("/var/run/minotaur"): try: ok.mkdir("/var/run/minotaur", 0755) except: Log().add('fatal', 'unable to create /var/run/minotaur') if os.path.exists("/var/run/minotaur/minotaur.pid"): pid = open("/var/run/minotaur/minotaur.pid").read().rstrip() return pid.isdigit() and os.path.exists("/proc/%s" % (pid)) def create_pid_file(self): if self.already_running(): Log().add( 'fatal', 'unable to start, there is another minotaur process running') with open("/var/run/minotaur/minotaur.pid", "w") as f: f.write("%d" % (os.getpid())) def banner(self): version = Version().get() line = "-" * (20 + len(version)) print print " /%s/" % (line) print " / minotaur %s by m4rkw /" % (version) print "/%s/" % (line) print def usage(self): self.banner() print "usage: %s [command] [options]" % (sys.argv[0]) print print "## quickstart" print print " %s --quickstart" % (sys.argv[0]) print print "quickstart mode will calibrate the 5 most profitable Nicehash algorithms" print "in fast mode (no power tuning) and then start mining. See the README for" print "more information." print print "## calibration" print print " %s --calibrate <device id / name / class | all> <pool (nicehash/ethermine/all)> <miner> <algorithms | all> [region (eu/usa)] [--quick] [--overwrite] [--force]" % ( sys.argv[0]) print print " --quick : skip power calibration and just do the initial run at default power" print " --overwrite : replace existing calibration data" print " --force : take-over a device from minotaur and use it for calibration" print print "## mining" print print " %s --mine" % (sys.argv[0]) print print "## misc" print print " %s --devices # list devices" % ( sys.argv[0]) print " %s --algos # list algorithms and calibrated hashrates" % ( sys.argv[0]) print " %s --cleanup # stop workers (not calibration runs)" % ( sys.argv[0]) print " %s --cleanup <device id> # stop workers for device" % ( sys.argv[0]) print " %s --cleanup-all # stop all workers (including calibration runs)" % ( sys.argv[0]) print " %s --pin <device id> <pool> <miner> <algo> [region] # pin a device to a specific miner+algorithm" % ( sys.argv[0]) print " %s --pin <device id> idle # pin a device to idle (ie don't use)" % ( sys.argv[0]) print " %s --unpin <device id> # unpin a device from a specific miner+algorithm" % ( sys.argv[0]) print " %s --stats [hours] # show stats (default last 24 hours)" % ( sys.argv[0]) print " %s --help # display this help page" % ( sys.argv[0]) print sys.exit() def initialise(self): self.banner() Log().add("info", "initialising") self.check_for_update() Pools() Miners() Log().add("info", "scanning devices") self.scan_devices() def check_for_update(self): try: opener = urllib2.build_opener(NoRedirection, urllib2.HTTPCookieProcessor()) resp = opener.open( "https://github.com/m4rkw/minotaur/releases/latest") latest_version = resp.headers['Location'].split('/')[-1][1:] if self.is_newer(latest_version, Version().get()): Log().add('info', 'new version available: v%s' % (latest_version)) except: Log().add('warning', 'failed to check for updates') pass def is_newer(self, latest_version, current_version): latest_version = latest_version.replace('v', '').split(".") current_version = current_version.replace('v', '').split(".") if int(latest_version[0]) > int(current_version[0]): return True elif int(latest_version[0]) < int(current_version[0]): return False if int(latest_version[1]) > int(current_version[1]): return True elif int(latest_version[1]) < int(current_version[1]): return False if len(latest_version) > 2 and len(current_version) < 3: return True if len(latest_version) > 2 and len(current_version) < 3: return True if len(latest_version) > 2 and len(current_version) > 2: return int(latest_version[2]) > int(current_version[2]) return False def sigint_handler(self, a, b): Log().add('info', 'interrupt received, cleaning up') try: Miners().cleanup_workers() except: pass sys.exit(0) def cleanup(self, all=0, device_id=None): if all: Miners().cleanup_workers(True) elif not all and not device_id: Miners().cleanup_workers(False) else: device = Device({"id": device_id}) Nvidia().set_default_profile(device) Miners().stop_device(device) sys.exit(0) def scan_devices(self): self.devices = Nvidia().get_nvidia_devices() Calibration().load() if len(self.devices) < 1: Log().add('fatal', "no nvidia GPUs found") Log().add("info", "found %d nvidia GPUs" % (len(self.devices))) Calibration().check_calibrated_algorithms(self.devices) Log().add('info', 'retrieving state from miner backends') Miners().poll() for device in self.devices: device.update() if device.state == 'active': device.log( 'info', 'currently running algorithm %s with %s [profile=%s] [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.profile, device.algos[0]['region'])) device.apply_profile() elif device.state == 'calibrating': device.log( 'info', 'calibration in progress with algorithm %s using %s [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.algos[0]['region'])) device.period_start = self.startup_time device.grubtime = device.period_start + ( 60 * random.randrange(15, 1424)) def run(self): self.create_pid_file() self.initialise() signal.signal(signal.SIGINT, self.sigint_handler) signal.signal(signal.SIGTERM, self.sigint_handler) signal.signal(signal.SIGHUP, self.sighup_handler) power_draw_readings = self.load_power_draw_readings() profitability_readings = self.load_profitability_readings() main_loop_index = 0 self.miner_state = {} self.pool_refresh = None while True: if main_loop_index == 0: if self.pool_refresh == None or ( time.time() - self.pool_refresh ) >= Config().get('pool_refresh_interval'): Pools().refresh() self.pool_refresh = time.time() show_mbtc_total = False Miners().poll() for i in range(0, len(self.devices)): device = self.devices[i] device.update() device.log_stats() if not device.can_run(): continue #device.grubtime = self.startup_time += (60 * random.randrange(15, 1424)) # if device.grub == False and time.time() >= device.grubtime and time.time() <= (device.grubtime + (60 * 15)): # device.log('info', 'starting 15min donation period to the author') # device.grub = True # # Miners().schedule('restart', device) # elif device.grub == True and time.time() < device.grubtime or time.time() > (device.grubtime + (60 * 15)): # device.log('info', 'stopping donation period') # device.grub = False # device.period_start += 86400 # device.grubtime = device.period_start + (60 * random.randrange(15, 1424)) # # while not self.grubtime_is_unique(device.grubtime, i): # device.grubtime = device.period_start + (60 * random.randrange(15, 1424)) # # Miners().schedule('restart', device) # else: if True: if not device.running(): if Config().get('debug'): device.log( 'debug', 'device not running - starting worker') Miners().schedule('start', device) else: switched = False if not device.pin: if Pools().should_switch(device): Miners().schedule('restart', device) switched = True if not switched and Config( ).get('calibration.update_calibration_data_over_time' ): Calibration().handle_device_update(device) queued_device_ids = Miners().queue.keys() Miners().execute_queue() for device in self.devices: if device.state == 'active' and device.id not in queued_device_ids and not device.warming_up( ): show_mbtc_total = True device.log( 'info', '%s/%s[%s]: %s' % (device.algos[0]['pool'], device.algos[0]['algo'], device.algos[0]['miner'], device.hashrate_str())) device.check_hashrate() total_power, total_power_limit, total_mbtc_per_day = Nvidia( ).get_device_metrics(self.devices) if show_mbtc_total: Log().add( 'info', 'total profitability: %.2f mBTC/day' % (total_mbtc_per_day)) power_draw_readings = self.update_power_draw_readings( power_draw_readings, total_power + Config().get('system_draw_watts')) profitability_readings = self.update_profitability_readings( profitability_readings, total_mbtc_per_day) # load in calibration data from other devices that may be calibrating Calibration().load() Miners().wait_for_queue() time.sleep(1) main_loop_index += 1 if main_loop_index >= Config().get('refresh_interval'): main_loop_index = 0 def grubtime_is_unique(self, grubtime, i): _from = grubtime _to = grubtime + (15 * 60) for j in range(0, len(self.devices)): if i != j: _from2 = self.devices[j].grubtime _to2 = _from2 + (15 * 60) for x in range(_from2, _to2): if x in range(_from, _to): return False return True def load_power_draw_readings(self): if os.path.exists( self.power_file) and os.path.getsize(self.power_file) > 0: return pickle.loads(open(self.power_file).read()) return [] def load_profitability_readings(self): if os.path.exists(self.profitability_file) and os.path.getsize( self.profitability_file) > 0: return pickle.loads(open(self.profitability_file).read()) return [] def trim_readings(self, readings, max_time): new_readings = [] for reading in readings: if int(time.time()) - reading["timestamp"] <= max_time: new_readings.append(reading) return new_readings def update_power_draw_readings(self, power_draw_readings, total_power): power_draw_readings.append({ "timestamp": int(time.time()), "reading": total_power }) power_draw_readings = self.trim_readings( power_draw_readings, max(Config().get('live_data')['power_draw_averages'])) with open(self.power_file + ".new", "w") as f: f.write(pickle.dumps(power_draw_readings)) os.rename(self.power_file + ".new", self.power_file) return power_draw_readings def update_profitability_readings(self, profitability_readings, total_mbtc_per_day): profitability_readings.append({ "timestamp": int(time.time()), "reading": total_mbtc_per_day }) profitability_readings = self.trim_readings( profitability_readings, max(Config().get('live_data')['profitability_averages'])) with open(self.profitability_file + ".new", "w") as f: f.write(pickle.dumps(profitability_readings)) os.rename(self.profitability_file + ".new", self.profitability_file) return profitability_readings def sighup_handler(self, x, y): Log().add('info', 'SIGHUP caught, reloading config and benchmark data') Config().reload() Calibration().load() Miners().reload_config() for device in Nvidia().get_nvidia_devices(): if not self.device_in_list(device): device.update() if device.state == 'active': device.log( 'info', 'currently running algorithm %s with %s [profile=%s] [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.profile, device.algos[0]['region'])) elif device.state == 'calibrating': device.log( 'info', 'calibration in progress with algorithm %s using %s [region=%s]' % (device.algos[0]['algo'], device.algos[0]['miner'], device.algos[0]['region'])) self.devices.append(device) for device in self.devices: if device.state == 'active': device.apply_profile() Log().add('info', 'reload complete') def device_in_list(self, device): for l_device in self.devices: if l_device.id == device.id: return True return False def calibration_banner(self): print "-" * 84 Log().add( 'info', 'PLEASE NOTE: this process will not automatically overclock your card unless' ) Log().add( 'info', 'you have enabled overclocking and configured a device profile with overclock' ) Log().add( 'info', 'settings. You and you alone are responsible for any overclock settings that' ) Log().add( 'info', 'you want to run this benchmark process with. We do not advise overclocking' ) Log().add( 'info', 'and are not responsible for any hardware damage that may occur when using' ) Log().add('info', 'this tool.') print "-" * 84 def calibrate(self, device_params, pool, miner_name, algorithm, region, quick, overwrite, force): devices = Nvidia().get_nvidia_devices(1) if pool == 'nicehash' and region not in [ 'eu', 'usa', 'hk', 'jp', 'in', 'br' ]: Log().add('fatal', 'a valid region is required for nicehash') devices_to_calibrate = [] device_classes = [] for device_param in device_params.split(','): if device_param.isdigit(): if int(device_param) >= len(devices): Log().add('fatal', 'device %d not found' % (int(device_param))) else: devices_to_calibrate.append(devices[int(device_param)]) else: found = False for device in devices: if device.name == device_param: devices_to_calibrate.append(device) found = True elif (device_param == 'all' or device.dclass == device_param ) and device.dclass not in device_classes: devices_to_calibrate.append(device) device_classes.append(device.dclass) found = True if not found: Log().add('fatal', 'device %s not found' % (device_param)) log_dir = Config().get('logging.calibration_log_dir') if not log_dir: log_dir = "/var/log/minotaur" if miner_name == "all": miners = [] for miner_name in Config().get('miners').keys(): if Config().get('miners')[miner_name]['enable']: miners.append(eval("%s()" % (miner_name.title()))) else: if not miner_name in Config().get('miners').keys(): Log().add('fatal', 'miner %s is not configured' % (miner_name)) miners = [eval("%s()" % (miner_name.title()))] if len(miners) == 0: Log().add('fatal', "no miners available") if pool == 'all': pools = [] for pool_name in Config().get('pools').keys(): if Config().get('pools.%s.enable' % (pool_name)): pools.append(pool_name) elif pool not in Config().get('pools').keys(): Log().add('fatal', 'unknown pool: %s' % (pool)) else: pools = [pool] algorithms = {} for pool_name in pools: algorithms[pool_name] = {} for miner in miners: if not pool_name in Pools().pools.keys(): Log().add('fatal', 'pool %s is not enabled' % (pool_name)) pool = Pools().pools[pool_name] if miner.name not in pool.supported_miners: continue if algorithm == "all": algorithms[pool_name][ miner.name] = miner.supported_algorithms() else: algorithms[pool_name][miner.name] = [] for algo_param in algorithm.split(","): if algo_param == 'all': algorithms[pool_name][ miner.name] = miner.supported_algorithms() else: if algo_param[0] == '!': exclude_algo = algo_param[1:] if miner.name in algorithms.keys( ) and exclude_algo in algorithms[miner.name]: algorithms[pool_name][miner.name].remove( exclude_algo) else: if algo_param in miner.supported_algorithms(): algorithms[pool_name][miner.name].append( algo_param) print "" self.calibration_banner() print "" n = 0 for device in devices_to_calibrate: log_file = "%s/calibration_%d.log" % (log_dir, device.id) Log().set_log_file(log_file) for pool_name in algorithms.keys(): for miner in miners: if miner.name in algorithms[pool_name].keys(): for algorithm in algorithms[pool_name][miner.name]: n += 1 if algorithm in Config( ).get('algorithms.single') or algorithm in Config( ).get('algorithms.double'): Calibration().load() if not overwrite and Calibration().get( '%s.%s.%s' % (device.dclass, miner.name, algorithm)): device.log( 'info', 'not overwriting existing calibration data for miner %s algorithm %s (use --overwrite to override)' % (miner.name, algorithm)) else: Calibrate().start(device, pool_name, miner, algorithm, region, quick, force) else: Log().add( 'warning', 'algorithm %s is not in the config file - skipping' % (algorithm)) Log().add('info', 'nothing to do.') def print_devices(self): for device in Nvidia().get_nvidia_devices(1): print "%d: %s [class: %s]" % (device.id, device.name, device.dclass) sys.exit() def print_algorithms(self): algorithms = Config().get('algorithms.single') + Config().get( 'algorithms.double') Calibration().load() device_classes = Nvidia().get_nvidia_device_classes() for device_class in device_classes: data = {} for algorithm in algorithms: data[algorithm] = {} for miner_name in Config().get('miners'): miner = eval('%s()' % (miner_name.title())) if algorithm in miner.supported_algorithms(): data[algorithm][miner_name] = Calibration( ).get_miner_hashrate_for_algorithm_on_device( miner_name, algorithm, device_class) else: data[algorithm][miner_name] = "-" self.display_device_algorithm_table(device_class, data) print "\nnote: - means not supported\n" def display_device_algorithm_table(self, device_class, data): print "\n" algo_width = self.get_width(data.keys()) + 2 total_width = algo_width sys.stdout.write(device_class.ljust(algo_width)) miner_widths = {} for miner_name in sorted(Config().get('miners').keys()): miner_widths[miner_name] = self.get_miner_width(data, miner_name) + 2 total_width += miner_widths[miner_name] + 2 sys.stdout.write(miner_name.rjust(miner_widths[miner_name])) sys.stdout.write("\n") sys.stdout.write("-" * total_width) sys.stdout.write("\n") sys.stdout.flush() for algorithm in sorted(data.keys()): sys.stdout.write(algorithm.ljust(algo_width)) for miner_name in sorted(data[algorithm].keys()): sys.stdout.write(data[algorithm][miner_name].rjust( miner_widths[miner_name])) sys.stdout.write("\n") def get_width(self, values): width = 0 for s in values: if len(s) > width: width = len(s) return width def get_miner_width(self, data, miner_name): width = len(miner_name) for a in data.keys(): if len(data[a][miner_name]) > width: width = len(data[a][miner_name]) return width def get_miners_for_algorithm(self, algorithm): miners = [] for miner_name in Config().get('miners'): if Config().get('miners')[miner_name]['enable']: miner = eval('%s()' % (miner_name.title())) if algorithm in miner.supported_algorithms(): miners.append(miner_name) return miners def pin(self, device_id, pool_name, pin_miner_name, algorithm, region): device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device = Device({"id": int(device_id)}) if pool_name not in Config().get('pools').keys(): Log().add('fatal', 'unknown pool') if not algorithm in Config().get( 'algorithms.single') and not algorithm in Config().get( 'algorithms.double'): Log().add('fatal', 'unknown algorithm') if pool_name != 'nicehash': region = None else: if region not in ['eu', 'usa', 'hk', 'jp', 'in', 'br']: Log().add('fatal', 'valid region is required for nicehash') Miners().poll() Miners().get_device_state(device) if device.state == "calibrating": Log().add( 'fatal', 'not pinning device %d - currently calibrating' % (device.id)) pin = { "pool_name": pool_name, "miner_name": pin_miner_name, "algorithm": algorithm, "region": region } with open("/var/run/minotaur/pin%d" % (device.id), "w") as f: f.write(yaml.dump(pin)) Log().add( 'info', 'pinned device %d to miner: %s pool %s algorithm: %s region: %s' % (device.id, pin_miner_name, pool_name, algorithm, region)) def pin_idle(self, device_id): pin = {"idle": True} device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device = Device({"id": int(device_id)}) with open("/var/run/minotaur/pin%d" % (device.id), "w") as f: f.write(yaml.dump(pin)) Log().add('info', 'pinned device %d to idle' % (device.id)) def pin_calibration(self, device_id): pin = {"calibration": True} device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device = Device({"id": int(device_id)}) with open("/var/run/minotaur/pin%d" % (device.id), "w") as f: f.write(yaml.dump(pin)) Log().add('info', 'pinned device %d to calibration' % (device.id)) def unpin(self, device_id): device_ids = [] if device_id == 'all': for device in Nvidia().get_nvidia_devices(): device_ids.append(device.id) else: for device_id in device_id.split(","): device_ids.append(int(device_id)) if len(device_ids) == 0: Log().add('fatal', 'no devices selected') for device_id in device_ids: device_id = int(device_id) if os.path.exists("/var/run/minotaur/pin%d" % (device_id)): os.remove("/var/run/minotaur/pin%d" % (device_id)) Log().add('info', 'unpinned device %d' % (device_id)) else: Log().add('info', 'device %d is not pinned' % (device_id)) def device_pinned(self, device_id): if os.path.exists("/var/run/minotaur/pin%d" % (device_id)): return yaml.load( open("/var/run/minotaur/pin%d" % (device_id)).read()) return False def stats(self, hours=24): if hours == 1: suffix = '' else: suffix = 's' print "\nstats for the last %d hour%s:\n" % (hours, suffix) stats_file = "/var/log/minotaur/minotaur.csv" if not os.path.exists(stats_file): print "%s not found" % (stats_file) sys.exit(1) else: total = 0 algos = {} devices = {} logs = Stats().get_logs_for_period(stats_file, hours) width = 0 for item in logs: if len(item['algorithm']) > width: width = len(item['algorithm']) if item['device_id'] not in devices.keys(): devices[item['device_id']] = item else: time_on_algo = ( parser.parse(item['timestamp']) - parser.parse( devices[item['device_id']]['timestamp'])).seconds if devices[item['device_id']]['net_mbtc'] != '': net = float(devices[item['device_id']]['net_mbtc']) earning = (net / 86400) * time_on_algo if item['algorithm'] not in algos.keys(): algos[item['algorithm']] = 0 total += earning algos[item['algorithm']] += earning devices[item['device_id']] = item print " from: %s" % (logs[0]['timestamp']) print " to: %s\n" % (logs[len(logs) - 1]['timestamp']) total_s = "%.2f" % (total) print "total: %s mBTC" % (total_s.rjust(7)) if os.path.exists(self.currency_rate_file): exchange_rate = pickle.loads( open(self.currency_rate_file).read()) fiat = (total / 1000) * float(exchange_rate['rate']) fiat_s = "%.2f" % (fiat) print " %s %s" % (fiat_s.rjust(7), Config().get('electricity_currency')) total_seconds = hours * 3600 rate = ((total / total_seconds) * 86400) rate_s = "%.2f" % (rate) print "\n rate: %s mBTC/day" % (rate_s.rjust(7)) if os.path.exists(self.currency_rate_file): exchange_rate = pickle.loads( open(self.currency_rate_file).read()) fiat = (rate / 1000) * float(exchange_rate['rate']) day_s = "%.2f" % (fiat) month_s = "%.2f" % (fiat * 30) print " %s %s/day" % ( day_s.rjust(7), Config().get('electricity_currency')) print " %s %s/month" % ( month_s.rjust(7), Config().get('electricity_currency')) print "\nincome by algorithm:\n" for w in sorted(algos, key=algos.get, reverse=True): if os.path.exists(self.currency_rate_file): exchange_rate = pickle.loads( open(self.currency_rate_file).read()) fiat = (algos[w] / 1000) * float(exchange_rate['rate']) suffix = " %.2f %s" % ( fiat, Config().get('electricity_currency')) else: suffix = "" print "%s: %.4f mBTC%s" % (w.rjust(width), algos[w], suffix) print "" sys.exit() def get_latest_version(self): opener = urllib2.build_opener(NoRedirection, urllib2.HTTPCookieProcessor()) resp = opener.open("https://github.com/m4rkw/minotaur/releases/latest") return resp.headers['Location'].split('/')[-1][1:] def upgrade(self): print "checking for update" latest_version = self.get_latest_version() current_version = Version().get current_version = '0.8.9' if self.is_newer(latest_version, current_version): print 'new version available: v%s' % (latest_version) sys.stdout.write('upgrade? (y/n) ') line = sys.stdin.readline() if line[0].lower() == 'y': self.do_upgrade() else: print 'you are already running the latest version.' def do_upgrade(self): if os.getuid() != 0: print 'elevating with sudo, you may need to enter your password below..' os.system("sudo %s --do-upgrade" % (sys.argv[0])) sys.exit(0) else: latest_version = self.get_latest_version() print "downloading minotaur-%s_centos7.tar.gz" % (latest_version) os.system( "curl -L -s -o /tmp/minotaur.tar.gz https://github.com/m4rkw/minotaur/releases/download/v%s/minotaur-%s_centos7.tar.gz" % (latest_version, latest_version)) os.system("tar -C /tmp -zxf /tmp/minotaur.tar.gz") print "installing minotaur" copyfile("/tmp/minotaur-%s/minotaur" % (latest_version), sys.argv[0]) print "cleaning up" os.system("rm -rf /tmp/minotaur-%s /tmp/minotaur.tar.gz" % (latest_version)) print "done!" sys.exit(0)
def calibrate(self, device_params, pool, miner_name, algorithm, region, quick, overwrite, force): devices = Nvidia().get_nvidia_devices(1) if pool == 'nicehash' and region not in [ 'eu', 'usa', 'hk', 'jp', 'in', 'br' ]: Log().add('fatal', 'a valid region is required for nicehash') devices_to_calibrate = [] device_classes = [] for device_param in device_params.split(','): if device_param.isdigit(): if int(device_param) >= len(devices): Log().add('fatal', 'device %d not found' % (int(device_param))) else: devices_to_calibrate.append(devices[int(device_param)]) else: found = False for device in devices: if device.name == device_param: devices_to_calibrate.append(device) found = True elif (device_param == 'all' or device.dclass == device_param ) and device.dclass not in device_classes: devices_to_calibrate.append(device) device_classes.append(device.dclass) found = True if not found: Log().add('fatal', 'device %s not found' % (device_param)) log_dir = Config().get('logging.calibration_log_dir') if not log_dir: log_dir = "/var/log/minotaur" if miner_name == "all": miners = [] for miner_name in Config().get('miners').keys(): if Config().get('miners')[miner_name]['enable']: miners.append(eval("%s()" % (miner_name.title()))) else: if not miner_name in Config().get('miners').keys(): Log().add('fatal', 'miner %s is not configured' % (miner_name)) miners = [eval("%s()" % (miner_name.title()))] if len(miners) == 0: Log().add('fatal', "no miners available") if pool == 'all': pools = [] for pool_name in Config().get('pools').keys(): if Config().get('pools.%s.enable' % (pool_name)): pools.append(pool_name) elif pool not in Config().get('pools').keys(): Log().add('fatal', 'unknown pool: %s' % (pool)) else: pools = [pool] algorithms = {} for pool_name in pools: algorithms[pool_name] = {} for miner in miners: if not pool_name in Pools().pools.keys(): Log().add('fatal', 'pool %s is not enabled' % (pool_name)) pool = Pools().pools[pool_name] if miner.name not in pool.supported_miners: continue if algorithm == "all": algorithms[pool_name][ miner.name] = miner.supported_algorithms() else: algorithms[pool_name][miner.name] = [] for algo_param in algorithm.split(","): if algo_param == 'all': algorithms[pool_name][ miner.name] = miner.supported_algorithms() else: if algo_param[0] == '!': exclude_algo = algo_param[1:] if miner.name in algorithms.keys( ) and exclude_algo in algorithms[miner.name]: algorithms[pool_name][miner.name].remove( exclude_algo) else: if algo_param in miner.supported_algorithms(): algorithms[pool_name][miner.name].append( algo_param) print "" self.calibration_banner() print "" n = 0 for device in devices_to_calibrate: log_file = "%s/calibration_%d.log" % (log_dir, device.id) Log().set_log_file(log_file) for pool_name in algorithms.keys(): for miner in miners: if miner.name in algorithms[pool_name].keys(): for algorithm in algorithms[pool_name][miner.name]: n += 1 if algorithm in Config( ).get('algorithms.single') or algorithm in Config( ).get('algorithms.double'): Calibration().load() if not overwrite and Calibration().get( '%s.%s.%s' % (device.dclass, miner.name, algorithm)): device.log( 'info', 'not overwriting existing calibration data for miner %s algorithm %s (use --overwrite to override)' % (miner.name, algorithm)) else: Calibrate().start(device, pool_name, miner, algorithm, region, quick, force) else: Log().add( 'warning', 'algorithm %s is not in the config file - skipping' % (algorithm)) Log().add('info', 'nothing to do.')