Passed
Push — master ( 13e7ac...87c632 )
by Dave
01:22
created

check_miner_should_provision()   A

Complexity

Conditions 4

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 4
nop 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
'''#David Foderick, Skylake Software Inc.
2
#Runs behind firewall
3
'''
4
import datetime
5
from threading import Thread
6
from queue import Queue
7
from colorama import Fore
8
import pika
9
from helpers.antminerhelper import MinerMonitorException, stats
10
from helpers.queuehelper import QueueName, QueueEntries
11
from domain import mining
12
from backend.fcmapp import Component
13
14
APPMONITOR = Component('monitorminer')
15
MONITOR_PREFETCH = int(APPMONITOR.app.configuration("monitoring.queue.prefetch"))
16
17
def enthread(target, args):
18
    '''put a method on a queue to be run in background'''
19
    thread_queue = Queue()
20
    def wrapper():
21
        thread_queue.put(target(*args))
22
    thread = Thread(target=wrapper)
23
    thread.start()
24
    return thread_queue
25
26
def when_monitorminer(channel, method, properties, body):
27
    try:
28
        print("[{0}] Received monitorminer command".format(APPMONITOR.app.now()))
29
        APPMONITOR.app.queuestatus()
30
        minermsg = APPMONITOR.app.messagedecodeminer(body)
31
        #monitor in background so pika socket doesnt get messed up
32
        qmon = enthread(target=domonitorminer, args=(minermsg, ))
33
        APPMONITOR.app.enqueue(qmon.get())
34
    except Exception as ex:
35
        APPMONITOR.app.logexception(ex)
36
37
def domonitorminer(miner):
38
    '''get statistics from miner'''
39
    entries = QueueEntries()
40
    savedminer = APPMONITOR.app.getminer(miner)
41
    if savedminer is None:
42
        savedminer = miner
43
    try:
44
        #individual miner can be monitored even if manually disabled
45
        #todo:savedminer and knownminer out of sync. this will be fixed in refactoring redis
46
        if not savedminer.should_monitor() and not miner.should_monitor():
47
            print('skipped monitoring {0}'.format(miner.name))
48
            return entries
49
        mineroriginalstatus = savedminer.status
50
        minerstats, minerinfo, apicall, minerpool = stats(savedminer)
51
        #minerlcd = antminerhelper.getminerlcd(miner)
52
        if minerstats is None:
53
            print('could not monitor {0}({1})'.format(savedminer.name, savedminer.ipaddress))
54
        else:
55
            #what to do if monitored miner type conflicts with saved miner type???
56
            #should probably provision?
57
            foundpool = APPMONITOR.app.findpool(minerpool)
58
            if foundpool is not None:
59
                minerpool.poolname = foundpool.name
60
            savedminer.monitored(minerstats, minerpool, minerinfo, apicall.elapsed())
61
            if mineroriginalstatus == '':
62
                #first time monitoring since bootup
63
                print(Fore.GREEN + APPMONITOR.app.now(), savedminer.name, 'first time monitoring')
64
            elif savedminer.status == mining.MinerStatus.Online and (mineroriginalstatus == mining.MinerStatus.Disabled or mineroriginalstatus == mining.MinerStatus.Offline):
65
                #changing status from offline to online so raise event
66
                entries.add(QueueName.Q_ONLINE, APPMONITOR.app.messageencode(savedminer))
67
                print(Fore.GREEN + APPMONITOR.app.now(), savedminer.name, 'back online!')
68
            #TODO: if stats.elapsed < previous.elapsed then raise provision or online events
69
70
            APPMONITOR.app.putminerandstats(savedminer, minerstats, minerpool)
71
            #show name of current pool instead of worker
72
            print('{0} mining at {1}'.format(savedminer.name, getpoolname(minerpool)))
73
            check_miner_should_provision(entries, savedminer, minerpool)
74
75
            print(Fore.CYAN+str(APPMONITOR.app.now()), savedminer.name, savedminer.status,
76
                  'h='+str(minerstats.currenthash), str(minerstats.minercount),
77
                  '{0}/{1}/{2}'.format(str(minerstats.tempboard1),
78
                                       str(minerstats.tempboard2),
79
                                       str(minerstats.tempboard3)),
80
                  savedminer.uptime(minerstats.elapsed),
81
                  '{0:d}ms'.format(int(savedminer.monitorresponsetime() * 1000)))
82
            msg = APPMONITOR.app.createmessagestats(savedminer, minerstats, minerpool)
83
            entries.addbroadcast(QueueName.Q_STATISTICSUPDATED, msg)
84
85
    except pika.exceptions.ConnectionClosed as qex:
86
        #could not enqueue a message
87
        print(Fore.RED + '{0} Queue Error: {1}'.format(savedminer.name,
88
                                                       APPMONITOR.app.exceptionmessage(qex)))
89
        APPMONITOR.app.logexception(qex)
90
    except MinerMonitorException as monitorex:
91
        print(Fore.RED + '{0} Miner Error: {1}'.format(savedminer.name,
92
                                                       APPMONITOR.app.exceptionmessage(monitorex)))
93
        savedminer.lastmonitor = datetime.datetime.utcnow()
94
        #TODO: this should be a rule. publish miner offline event
95
        #and let event handler decide how to handle it
96
        savedminer.offline_now()
97
        print(Fore.RED + APPMONITOR.app.now(), savedminer.name, savedminer.status)
98
        entries.add(QueueName.Q_OFFLINE, APPMONITOR.app.messageencode(savedminer))
99
100
    except BaseException as ex:
101
        print(Fore.RED+'{0} Unexpected Error in monitorminer: {1}'.format(savedminer.name,
102
                                                                          APPMONITOR.app.exceptionmessage(ex)))
103
        # we have to consider any exception to be a miner error. sets status to offline
104
        #if str(e) == "timed out": #(timeout('timed out',),)
105
        APPMONITOR.app.logexception(ex)
106
    #TODO: review usage of savedminer and knownminer. should only go with one
107
    APPMONITOR.app.putminer(savedminer)
108
    APPMONITOR.app.updateknownminer(savedminer)
109
    return entries
110
111
def check_miner_should_provision(entries, savedminer, minerpool):
112
    #most users won't want to mine solo, so provision the miner
113
    if not APPMONITOR.app.configuration('mining.allowsolomining'):
114
        if not minerpool.currentpool or minerpool.currentpool.startswith(APPMONITOR.app.configuration('mining.solopool')):
115
            entries.add(QueueName.Q_PROVISION, APPMONITOR.app.messageencode(savedminer))
116
117
def getpoolname(minerpool):
118
    poolname = '?'
119
    if minerpool:
120
        poolname = '{0} {1}'.format(minerpool.currentpool, minerpool.currentworker)
121
    foundpool = APPMONITOR.app.findpool(minerpool)
122
    if foundpool is not None:
123
        poolname = foundpool.name
124
    return poolname
125
126
def main():
127
    '''main'''
128
    if APPMONITOR.app.isrunnow or APPMONITOR.app.isdebug:
129
        test_miner = APPMONITOR.app.getknownminerbyname('192.168.1.177')
130
        if not test_miner:
131
            test_miner = APPMONITOR.app.getknownminerbyname('#S9000')
132
        if test_miner:
133
            domonitorminer(test_miner)
134
        APPMONITOR.app.shutdown()
135
    else:
136
        APPMONITOR.listeningqueue = APPMONITOR.app.subscribe(QueueName.Q_MONITORMINER, when_monitorminer, MONITOR_PREFETCH)
137
        APPMONITOR.listen()
138
139
if __name__ == "__main__":
140
    main()
141