backend.when_provision.when_provision()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 18
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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