def test_miner_status_change_keeps_original(self): miner = Miner('test') miner.status = MinerStatus.Offline self.assertTrue(miner.laststatuschanged) originalstatuschangetime = miner.laststatuschanged miner.status = MinerStatus.Offline self.assertTrue(miner.laststatuschanged == originalstatuschangetime)
def getminerinfo(miner: Miner): minerid = 'unknown' minertype = 'unknown' if not miner.can_monitor(): raise MinerMonitorException( 'miner {0} cannot be monitored. ip={1} port={2}'.format( miner.name, miner.ipaddress, miner.port)) api = MinerApi(host=miner.ipaddress, port=int(miner.port), timeout=1) jstats = api.stats() #if there was an error then the return is STATUS not STATS! toplevelstatus = jstats['STATUS'][0] if toplevelstatus['STATUS'] == 'error': if not miner.is_disabled(): raise MinerMonitorException(toplevelstatus['description']) else: status = jstats['STATS'][0] details = jstats['STATS'][1] if 'Type' in status: minertype = status['Type'] else: if toplevelstatus['Description'].startswith('cgminer'): minertype = toplevelstatus['Description'] if minertype == 'Antminer S9': minerid = details['miner_id'] minerinfo = MinerInfo(minertype, minerid) return minerinfo
def test_miner_monitored_timer(self): miner = Miner('test') stats = domain.minerstatistics.MinerStatistics(miner) apicall = MinerApiCall(miner) apicall.start() apicall.stop() miner.monitored(stats, pool=None, info=None, sec=apicall.elapsed()) self.assertFalse(miner.lastmonitor is None)
def test_miner_monitored_pool(self): miner = Miner('test') stats = domain.minerstatistics.MinerStatistics(miner) miner.monitored(stats, pool=MinerCurrentPool(miner), info=None, sec=None) self.assertTrue(miner.minerpool)
def putminerandstats(self, miner: Miner, minerstats, minerpool): '''put miner and status in cache''' self.putminer(miner) schema = MinerStatsSchema() valstats = schema.dumps(minerstats).data self.tryputcache(miner.key() + '.stats', valstats) schema = MinerCurrentPoolSchema() valpool = schema.dumps(minerpool).data self.tryputcache(miner.key() + '.pool', valpool)
def test_miner_should_monitor(self): miner = Miner("#test", '', '', '', '', '', '', '', '') self.assertTrue(miner.should_monitor()) miner.monitored(domain.minerstatistics.MinerStatistics(miner), None, None, None) self.assertFalse(miner.should_monitor()) miner.name = "test" self.assertTrue(miner.should_monitor()) miner.status = MinerStatus.Disabled self.assertTrue(miner.should_monitor())
def test_miner_currentpoolname(self): miner = Miner('test') self.assertTrue(miner.currentpoolname() == '?') miner.minerpool = MinerCurrentPool(miner, 'test pool', 'test worker', allpools={}, poolname='unit test') self.assertTrue(miner.currentpoolname() == 'unit test') self.assertFalse( miner.minerpool.findpoolnumberforpool('test pool', 'test worker'))
def stats(miner: Miner): '''returns MinerStatistics, MinerInfo, and MinerApiCall''' if not miner.can_monitor(): raise MinerMonitorException( 'miner {0} cannot be monitored. ip={1} port={2}'.format( miner.name, miner.ipaddress, miner.port)) try: thecall = MinerApiCall(miner) entity = domain.minerstatistics.MinerStatistics( miner, when=datetime.datetime.utcnow()) api = MinerApi(host=miner.ipaddress, port=int(miner.port)) thecall.start() #jstats = api.stats() stats_and_pools = api.command('stats+pools') thecall.stop() if 'stats' in stats_and_pools: jstats = stats_and_pools['stats'][0] else: #if call failed then only one result is returned, so parse it jstats = stats_and_pools entity.rawstats = jstats jstatus = jstats['STATUS'] if jstatus[0]['STATUS'] == 'error': if not miner.is_disabled(): raise MinerMonitorException(jstatus[0]['description']) else: miner_software = parse_miner_software(jstats) if miner_software.startswith('sgminer'): jstats = stats_and_pools['STATS'] jsonstats = jstats status = jstats[0] jstatus = stats_and_pools['STATUS'] minerinfo = helpers.antminerhelper.parse_statistics_inno( entity, jsonstats, status) else: status = jstats['STATS'][0] jsonstats = jstats['STATS'][1] minerinfo = parse_minerinfo(status) #build MinerStatistics from stats parse_statistics(entity, jsonstats, status) minerpool = parse_minerpool(miner, stats_and_pools['pools'][0]) return entity, minerinfo, thecall, minerpool except BaseException as ex: print('Failed to call miner stats api: ' + str(ex)) raise MinerMonitorException(ex) return None, None, None, None
def test_miner_summary(self): miner = Miner("test", '', '', '', '', '', '', '', '') self.assertTrue(miner.summary() is not None) miner.status = MinerStatus.Online self.assertTrue(miner.summary() is not None) miner.minerstats = None self.assertTrue(miner.summary() is not None) miner.minerstats = domain.minerstatistics.MinerStatistics(miner) self.assertTrue(miner.summary() is not None)
def test_miner_update_port(self): miner = Miner('test') miner.port = 'port1' minerupdate = Miner('test') minerupdate.port = 'port2' miner.updatefrom(minerupdate) self.assertTrue(miner.port == minerupdate.port)
def test_miner_no_update(self): miner = Miner('test') miner.ipaddress = 'ip1' minerupdate = Miner('test') minerupdate.ipaddress = None miner.updatefrom(minerupdate) self.assertTrue(miner.ipaddress != minerupdate.ipaddress)
def pools(miner: Miner): '''Gets the current pool for the miner''' def sort_by_priority(j): return j['Priority'] try: api = MinerApi(host=miner.ipaddress, port=int(miner.port)) jstatuspools = api.pools() if jstatuspools['STATUS'][0]['STATUS'] == 'error': if not miner.is_disabled(): raise MinerMonitorException( jstatuspools['STATUS'][0]['description']) else: jpools = jstatuspools["POOLS"] #sort by priority jpools.sort(key=sort_by_priority) #try to do elegant way, but not working #cPool = namedtuple('Pool', 'POOL, URL, Status,Priority,Quota,Getworks,Accepted,Rejected,Long Poll') #colpools = [cPool(**k) for k in jsonpools["POOLS"]] #for pool in colpools: # print(pool.POOL) for pool in jpools: if str(pool["Status"]) == "Alive": currentpool = pool["URL"] currentworker = pool["User"] #print("{0} {1} {2} {3} {4} {5}".format(pool["POOL"],pool["Priority"],pool["URL"],pool["User"],pool["Status"],pool["Stratum Active"])) break minerpool = MinerCurrentPool(miner, currentpool, currentworker, jstatuspools) return minerpool except BaseException as ex: print('Failed to call miner pools api: ' + str(ex)) return None
def test_miner_status_no_you_cant(self): miner = Miner('test') def set_status_test(): miner.status = 'you can be anyting' self.assertRaises(ValueError, set_status_test)
def setUp(self): '''create test miner''' self.minerinfo = MinerInfo('Antminer S9', '') self.miner = Miner('Test', 'Online', 'Antminer S9', '', '', '', '', '', '', '', lastmonitor=None, offlinecount=0, defaultpool='', minerinfo=self.minerinfo) self.minerstats = MinerStatistics(self.miner, datetime.datetime.utcnow(), 3, currenthash=9999, controllertemp=0, tempboard1=0, tempboard2=0, tempboard3=0)
def readminers(self, file_name): with open(file_name, encoding='utf-8-sig') as config_file: jsonarray = json.loads(config_file.read()) miners = [Miner(**k) for k in jsonarray] #TODO: Remove disabled miners #if miner.name.startswith("#"): return miners
def make_minermessage(self, data): '''reconstitute a minermessage''' miner = None command = None minerstats = None minerpool = None if 'miner' in data: #miner comes in as dict instead of entity when there is an error in the schema if isinstance(data['miner'], Miner): miner = data['miner'] else: miner = Miner(**data['miner']) if 'command' in data: if isinstance(data['command'], MinerCommand): command = data['command'] else: command = MinerCommand(**data['command']) if 'minerstats' in data: #minerstats = MinerStatistics(Miner=miner,**data['minerstats']) minerstats = data['minerstats'] if 'minerpool' in data: #minerpool = MinerCurrentPool(Miner=miner, **data['minerpool']) minerpool = data['minerpool'] entity = MinerMessage(miner, command, minerstats, minerpool) return entity
def test_miner_can_monitor(self): miner = Miner("test", '', '', '', '', '', '', '', '') self.assertFalse(miner.can_monitor()) miner.ipaddress = '123.123.123.123' self.assertFalse(miner.can_monitor()) miner.port = '4028' self.assertTrue(miner.can_monitor())
def test_miner_pools_available(self): miner = Miner('test') self.assertTrue(miner.pools_available is None) miner.minerpool = MinerCurrentPool(miner, 'test pool', 'test worker', allpools={}) self.assertTrue(len(miner.pools_available) == 0) miner.minerpool.allpools = { "POOLS": [{ "Pool Stale%": 0, "Accepted": 421743, "Difficulty Stale": 0, "Stratum URL": "test", "Rejected": 85, "Difficulty Accepted": 6587318272, "Best Share": 4019408192, "User": "******", "Stratum Active": True, "Difficulty Rejected": 1343488, "Diff": "16.4K", "Remote Failures": 3, "Discarded": 1094132, "Long Poll": "N", "Proxy": "", "Priority": 0, "Has GBT": False, "Pool Rejected%": 0.0204, "Stale": 63, "Last Share Difficulty": 16384, "Diff1 Shares": 0, "Has Stratum": True, "Status": "Alive", "URL": "test", "Quota": 1, "Last Share Time": "0:00:05", "Getworks": 70163, "Get Failures": 3, "POOL": 3, "Proxy Type": "" }] } self.assertTrue(len(miner.pools_available) > 0) self.assertTrue(miner.minerpool.findpoolnumberforpool('test', 'test')) self.assertFalse(miner.minerpool.findpoolnumberforpool('not', 'found'))
def test_message_inapp(self): app = ApplicationService(component='test') values = '{"version":"1.1","sender":"fullcyclereact","type":"configuration","timestamp":"2018-09-16T07:18:34.431Z","body":"{\\"command\\":\\"save\\",\\"parameter\\":\\"\\",\\"id\\":\\"unknown\\",\\"entity\\":\\"miner\\",\\"values\\":[{\\"name\\":\\"S9102\\"},{\\"ipaddress\\":\\"test.com\\"},{\\"port\\":\\"4102\\"},{\\"location\\":\\"222\\"},{\\"in_service_date\\":null}]}"}' msg = app.messagedecode_configuration(values) self.assertTrue( isinstance(msg, messaging.messages.ConfigurationMessage)) self.assertTrue(msg.entity == 'miner') miner = Miner.create(msg.values) self.assertTrue(miner.name == "S9102")
def getminer(self, miner: Miner): '''strategies for getting miner from cache originally was key=miner.name but that was not good changed to key='miner.'+minerid ''' valu = self.trygetvaluefromcache('miner.{0}'.format(miner.key())) if valu is None: return None minerfromstore = self.deserialize(MinerSchema(), self.safestring(valu)) minerfromstore.store = 'mem' return minerfromstore
def test_minerserialization(self): sch = MinerSchema() miner = Miner('test') miner.minerinfo = MinerInfo('Antminer S9', '123') miner.minerpool = MinerCurrentPool(miner, 'test pool', 'test worker', allpools={}) miner.minerpool.poolname = 'unittest' miner.minerstats = MinerStatistics(miner, datetime.datetime.utcnow(), 0, 1, 0, 99, 98, 97, 123, '', '', '') jminer = sch.dumps(miner).data #rehydrate miner reminer = MinerSchema().loads(jminer).data self.assertTrue(isinstance(reminer.minerinfo, MinerInfo)) self.assertTrue(isinstance(reminer.minerpool, MinerCurrentPool)) self.assertTrue(reminer.minerpool.poolname == 'unittest') self.assertTrue(isinstance(reminer.minerstats, MinerStatistics))
def main(): '''main''' if COMPONENTDISCOVERED.app.isrunnow: miner = Miner('192.168.1.117') dodiscovered(miner) COMPONENTDISCOVERED.app.shutdown() else: print('Waiting for messages on {0}. To exit press CTRL+C'.format(QueueName.Q_DISCOVERED)) COMPONENTDISCOVERED.listeningqueue = COMPONENTDISCOVERED.app.makequeue(QueueName.Q_DISCOVERED) COMPONENTDISCOVERED.listeningqueue.subscribe(when_discovered, no_acknowledge=False) COMPONENTDISCOVERED.app.listen(COMPONENTDISCOVERED.listeningqueue)
def findminerbyname(minertofind): miners = MinerRepository() miner = miners.getminerbyname(minertofind, APP.getconfigfilename('config/miners.conf')) if miner is None: miner = APP.getknownminerbyname(minertofind) if miner is None: miner = APP.getminer(Miner(name=minertofind)) if miner is None: print('Miner {0} does not exist'.format(minertofind)) sys.exit(1) return miner
def pools(miner: Miner): '''Gets the current pool for the miner''' try: api = MinerApi(host=miner.ipaddress, port=int(miner.port)) jstatuspools = api.pools() if jstatuspools['STATUS'][0]['STATUS'] == 'error': if not miner.is_disabled(): raise MinerMonitorException( jstatuspools['STATUS'][0]['description']) else: return parse_minerpool(miner, jstatuspools) except BaseException as ex: print('Failed to call miner pools api: ' + str(ex)) return None
def test_miner_create(self): values = [] values.append({"name": "UnitTest"}) values.append({"minerid": "1"}) values.append({"ipaddress": "123.123.123.123"}) values.append({"port": "987"}) values.append({"location": "rack"}) values.append({"in_service_date": "2018-01-01T08:00:00.000Z"}) miner = Miner.create(values) self.assertTrue(miner.name == "UnitTest") self.assertTrue(miner.minerid == "1") self.assertTrue(miner.ipaddress == "123.123.123.123") self.assertTrue(miner.port == "987") self.assertTrue(miner.location == "rack") self.assertTrue( miner.in_service_date.date() == datetime.date(2018, 1, 1))
def stats(miner: Miner): try: entity = MinerStatistics(miner, when=datetime.datetime.utcnow()) api = MinerApi(host=miner.ipaddress, port=int(miner.port)) jstats = api.stats() if jstats['STATUS'][0]['STATUS'] == 'error': if not miner.is_disabled(): raise MinerMonitorException(jstats['STATUS'][0]['description']) else: status = jstats['STATS'][0] jsonstats = jstats['STATS'][1] entity.minercount = int(jsonstats['miner_count']) entity.elapsed = int(jsonstats['Elapsed']) entity.currenthash = int(float(jsonstats['GHS 5s'])) minertype = status['Type'] entity.controllertemp = None if 'temp_max' in jsonstats: entity.controllertemp = jsonstats['temp_max'] #should be 3 #tempcount = jsonstats['temp_num'] if minertype == 'Antminer S9': entity.tempboard1 = int(jsonstats['temp2_6']) entity.tempboard2 = int(jsonstats['temp2_7']) entity.tempboard3 = int(jsonstats['temp2_8']) entity.boardstatus1 = jsonstats['chain_acs6'] entity.boardstatus2 = jsonstats['chain_acs7'] entity.boardstatus3 = jsonstats['chain_acs8'] entity.fan1 = jsonstats['fan3'] entity.fan2 = jsonstats['fan6'] if minertype == 'Antminer D3': entity.tempboard1 = int(jsonstats['temp2_1']) entity.tempboard2 = int(jsonstats['temp2_2']) entity.tempboard3 = int(jsonstats['temp2_3']) if minertype == 'Antminer A3': entity.tempboard1 = int(jsonstats['temp2_1']) entity.tempboard2 = int(jsonstats['temp2_2']) entity.tempboard3 = int(jsonstats['temp2_3']) return entity except BaseException as ex: print('Failed to call miner stats api: ' + str(ex)) raise MinerMonitorException(ex) return None
def doit(args): if len(args) < 2: print('usage: python runcommand.py nameofcommand [nameofminer] [commandparameter]') APP.shutdown(1) cmd = args[1] if len(args) == 2: #single command, no miner specified queue_command = Queue(cmd, APP.getservice('rabbit')) queue_command.publish('{0} runcommand called on {1}'.format(APP.now(), cmd)) queue_command.close() print('sent command {0}'.format(cmd)) else: minertofind = args[2] cmdparam = '' if len(args) > 3: cmdparam = args[3] miners = MinerRepository() miner = miners.getminerbyname(minertofind, APP.getconfigfilename('config/miners.conf')) if miner is None: miner = APP.getknownminerbyname(minertofind) if miner is None: miner = APP.getminer(Miner(name=minertofind)) if miner is None: print('Miner {0} does not exist'.format(minertofind)) sys.exit(1) qnames = QueueName() if not qnames.isvalidqname(cmd): print('Queue {0} is not valid'.format(cmd)) sys.exit(1) queue_command = Queue(cmd, APP.getservice('rabbit')) #TODO: cleanup logic here. when to call app and when to override with just miner and command if cmd: qmess = MinerCommand(cmd, cmdparam) msg = APP.createmessagecommand(miner, qmess) queue_command.publish(msg) else: queue_command.publish(APP.messageencode(miner)) queue_command.close() print('sent command {0} for miner {1}'.format(cmd, miner.name))
def dosave(msg): entries = QueueEntries() if msg.entity == 'miner': #add or update miner minerid = name = ipaddress = port = None for pair in msg.values: if 'minerid' in pair: minerid = pair['minerid'] if 'name' in pair: name = pair['name'] if 'ipaddress' in pair: ipaddress = pair['ipaddress'] if 'port' in pair: port = pair['port'] miner = Miner(name, '', '', ipaddress, port, '', '') COMPONENTSAVE.app.save_miner(miner) entries.add(QueueName.Q_MONITORMINER, COMPONENTSAVE.app.messageencode(miner)) entries.add(QueueName.Q_PROVISION, COMPONENTSAVE.app.messageencode(miner)) if msg.entity == 'pool': #save the new named pool pool_type = name = url = user = priority = None for pair in msg.values: if 'pool_type' in pair: pool_type = pair['pool_type'] if 'name' in pair: name = pair['name'] if 'url' in pair: url = pair['url'] if 'user' in pair: user = pair['user'] if 'priority' in pair: priority = pair['priority'] pool = Pool(pool_type, name, url, user, priority) COMPONENTSAVE.app.save_pool(pool) return entries
def getknownminer(self, miner: Miner): '''get a known miner''' return self.getknownminerbykey(miner.key())
def putminer(self, miner: Miner): '''put miner in cache''' if miner and miner.key(): valu = self.serialize(miner) self.tryputcache('miner.{0}'.format(miner.key()), valu)