Passed
Push — master ( 1199d3...33094e )
by Dave
01:05
created

backend.when_provision.main()   A

Complexity

Conditions 5

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nop 0
dl 0
loc 11
rs 9.3333
c 0
b 0
f 0
1
'''# use to provision miners with default pools
2
# runs behind firewall
3
# Listens for Discovered event
4
'''
5
from threading import Thread
6
from queue import Queue
7
from colorama import Fore
8
from helpers import antminerhelper
9
from helpers.queuehelper import QueueName, QueueEntries
10
from domain import services
11
from domain.mining import MinerAccessLevel, MinerPool
12
from backend.fcmapp import Component
13
14
PROVISION = Component('provision', option='')
15
16
def enthread(target, args):
17
    '''put a method on a queue to be run in background'''
18
    thread_queue = Queue()
19
    def wrapper():
20
        thread_queue.put(target(*args))
21
    thread = Thread(target=wrapper)
22
    thread.start()
23
    return thread_queue
24
25
def when_provision(channel, method, properties, body):
26
    '''when provision event raised'''
27
    try:
28
        print("[{0}] Received provision command".format(PROVISION.app.now()))
29
        minermsg = PROVISION.app.messagedecodeminer(body)
30
        entries = None
31
        qprov = enthread(target=doprovision, args=(minermsg, ))
32
        while True:
33
            try:
34
                entries = qprov.get(False, timeout=3)
35
                break
36
            except Exception as ex:
37
                PROVISION.app.bus.sleep(3)
38
        PROVISION.app.enqueue(entries)
39
        PROVISION.app.bus.acknowledge(PROVISION.listeningqueue, method.delivery_tag)
40
    except Exception as ex:
41
        PROVISION.app.bus.reject(PROVISION.listeningqueue, method.delivery_tag)
42
        PROVISION.app.logexception(ex)
43
44
def doprovision(miner):
45
    '''provision/configure a miner'''
46
    entries = QueueEntries()
47
    poollist = PROVISION.app.pools()
48
    print("{0} pools configured".format(len(poollist)))
49
    print('{0} {1}'.format(miner.name, miner.ipaddress))
50
    mineraccess = ''
51
    addpools = None
52
    minerinfo = None
53
    minerpool = None
54
    try:
55
        minerstats, minerinfo, apicall, minerpool = antminerhelper.stats(miner)
56
        miner.setminerinfo(minerinfo)
57
        #find the current pool in known pools
58
        knownpool = PROVISION.app.findpool(minerpool)
59
        if knownpool is not None:
60
            minerpool.poolname = knownpool.name
61
        miner.minerpool = minerpool
62
        PROVISION.app.updateknownminer(miner)
63
        #find pools that need to be added and add them
64
        addpools = services.poolstoadd(miner, minerpool, poollist)
65
        mineraccess = PROVISION.app.antminer.getaccesslevel(miner)
66
    except antminerhelper.MinerMonitorException as ex:
67
        if ex.istimedout():
68
            mineraccess = MinerAccessLevel.Waiting
69
    if mineraccess == MinerAccessLevel.Restricted or mineraccess == MinerAccessLevel.Waiting:
70
        if mineraccess == MinerAccessLevel.Restricted:
71
            PROVISION.app.antminer.set_privileged(miner)
72
        PROVISION.app.antminer.waitforonline(miner)
73
        mineraccess = PROVISION.app.antminer.getaccesslevel(miner)
74
75
    if mineraccess == MinerAccessLevel.Restricted:
76
        entries.addalert('could not set {0} to privileged access'.format(miner.name))
77
        #try a few more times then give up
78
    else:
79
        addpoolstominer(miner, addpools)
80
81
        addminerpools(miner)
82
83
        switchtodefaultpool(miner, poollist, minerpool)
84
85
        enforcedefaultpool(miner, poollist, minerpool)
86
87
        entries.add(QueueName.Q_MONITORMINER, PROVISION.app.messageencode(miner))
88
    return entries
89
90
def enforcedefaultpool(miner, poollist, minerpool):
91
    #enforce default pool if it doesnt have one. find highest priority pool
92
    if not miner.defaultpool:
93
        def sort_by_priority(j):
94
            return j.priority
95
        filtered = [x for x in poollist if miner.miner_type.startswith(x.pool_type)]
96
        filtered.sort(key=sort_by_priority)
97
        #foundpriority = next((p for p in poollist if p.priority == 0), None)
98
        if filtered:
99
            switchtopool(miner, filtered[0], minerpool)
100
101
def switchtodefaultpool(miner, poollist, minerpool):
102
    #enforce default pool if miner has one set up
103
    if miner.defaultpool:
104
        founddefault = next((p for p in poollist if p.name == miner.defaultpool), None)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable p does not seem to be defined.
Loading history...
105
        if founddefault is not None:
106
            switchtopool(miner, founddefault, minerpool)
107
108
def addminerpools(miner):
109
    namedpools = PROVISION.app.pools()
110
    #process the pools found on the miner. This will pick up any pools added manually
111
    for pool in miner.pools_available:
112
        #check if pools is a named pool...
113
        foundnamed = None
114
        for namedpool in namedpools:
115
            if namedpool.is_same_as(pool):
116
                foundnamed = namedpool
117
                break
118
        if foundnamed:
119
            #pool should take on the cononical attributes of the named pool
120
            pool.named_pool = foundnamed
121
            pool.user = foundnamed.user
122
        PROVISION.app.add_pool(MinerPool(miner, pool.priority, pool))
123
124
def addpoolstominer(miner, addpools):
125
    for pool in addpools or []:
126
        print(Fore.YELLOW + "     Add", pool.name, "(addpool|{0},{1},{2})".format(pool.url, pool.user + miner.name, "x"))
127
        #this command adds the pool to miner and prints the result
128
        result = antminerhelper.addpool(miner, pool)
129
        if result.startswith("Access denied"):
130
            print(Fore.RED + result)
131
        else:
132
            print(result)
133
134
135
def switchtopool(miner, pooltoswitch, minerpool):
136
    '''switch pool'''
137
    if minerpool is not None:
138
        #find pool number of default pool and switch to it
139
        switchtopoolnumber = minerpool.findpoolnumberforpool(pooltoswitch.url, pooltoswitch.user)
140
        if switchtopoolnumber is not None and switchtopoolnumber > 0:
141
            antminerhelper.switch(miner, switchtopoolnumber)
142
            print(Fore.YELLOW + PROVISION.app.now(), miner.name, 'switched to {0}({1})'.format(pooltoswitch.name, pooltoswitch.url))
143
        else:
144
            print(Fore.RED + PROVISION.app.now(), miner.name, 'could not switch to {0}({1})'.format(pooltoswitch.name, pooltoswitch.url))
145
146
def main():
147
    if PROVISION.app.isrunnow or PROVISION.app.isdebug:
148
        for miner in PROVISION.app.knownminers():
149
            try:
150
                doprovision(miner)
151
            except Exception as ex:
152
                PROVISION.app.logexception(ex)
153
        PROVISION.app.shutdown()
154
    else:
155
        PROVISION.listeningqueue = PROVISION.app.subscribe(QueueName.Q_PROVISION, when_provision, no_acknowledge=False)
156
        PROVISION.app.listen(PROVISION.listeningqueue)
157
158
if __name__ == "__main__":
159
    main()
160