~ubuntu-branches/ubuntu/precise/gozerbot/precise

« back to all changes in this revision

Viewing changes to gozerplugs/plugs/collective.py

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Malcolm
  • Date: 2008-06-02 19:26:39 UTC
  • mfrom: (1.1.3 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080602192639-3rn65nx4q1sgd6sy
Tags: 0.8.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
#
3
3
4
4
 
5
 
""" collective connections """
 
5
""" the collective is a network of gozerbots connected by use of the bots 
 
6
 builtin webserver. nodes are urls pointing to these webservers .. this plugin
 
7
 provides the client side of the collective, to join the collective the
 
8
 webserver must be enabled and open for other connections (see WEBSERVER)
 
9
"""
6
10
 
7
11
__copyright__ = 'this file is in the public domain'
 
12
__depend__ = ['webserver', ]
 
13
__gendocfirst__ = ['coll-disable', 'coll-enable', 'coll-boot']
8
14
 
9
15
from gozerbot.generic import rlog, geturl, toenc, fromenc, waitforqueue, \
10
 
strippedtxt
 
16
strippedtxt, handle_exception, now, geturl2
11
17
from gozerbot.rsslist import rsslist
12
18
from gozerbot.datadir import datadir
 
19
from gozerbot.pdod import Pdod
13
20
from gozerbot.persiststate import PersistState
14
21
from gozerbot.commands import cmnds
15
22
from gozerbot.plugins import plugins
19
26
from gozerbot.dol import Dol
20
27
from gozerbot.config import config
21
28
from gozerbot.periodical import periodical
 
29
from gozerplugs.plugs.webserver import cfg as webcfg
22
30
from xml.sax.saxutils import unescape
 
31
from gozerbot.contrib.simplejson import loads
23
32
import Queue, time, socket, re, os
24
33
 
25
 
plughelp.add('collective', 'manage links to other bots .. to be accessible a \
26
 
bot needs to run the webserver')
 
34
gotuuid = True
 
35
try:
 
36
    import uuid
 
37
except ImportError:
 
38
    gotuuid = False
 
39
 
 
40
plughelp.add('collective', 'query other gozerbots')
27
41
 
28
42
coll = PersistState(datadir + os.sep + 'collective')
29
43
coll.define('enable', 0)
34
48
 
35
49
waitre = re.compile(' wait (\d+)', re.I)
36
50
 
 
51
def getid(url):
 
52
    if gotuuid:
 
53
        return str(uuid.uuid3(uuid.NAMESPACE_URL, toenc(url)))
 
54
    else:
 
55
        return url
 
56
 
 
57
class CollNode(object):
 
58
 
 
59
    def __init__(self, name, url):
 
60
        self.name = name
 
61
        self.id = getid(url)
 
62
        self.url = url
 
63
        self.seen = time.time()
 
64
        self.regtime = time.time()
 
65
        self.filter = []
 
66
        self.catch = []
 
67
 
 
68
    def __str__(self):
 
69
        return "name=%s url=<%s> seen=%s" % (self.name, self.url, now())
 
70
        
 
71
    def cmnd(self, what, queue=None):
 
72
        if what.startswith('coll'):
 
73
            return
 
74
        try:
 
75
            what = re.sub('\s+', '+', what)
 
76
            data = geturl('%s/json?%s' % (self.url, what))
 
77
            result = loads(fromenc(data))
 
78
        except ValueError:
 
79
            # ValueError: No JSON object could be decoded
 
80
            result = ['decode error', ]
 
81
        except IOError, ex:
 
82
            result = [str(ex), ]
 
83
        except Exception, ex:
 
84
            handle_exception()
 
85
            rlog(10, 'collective', "can't fetch %s data: %s" % (self.url, \
 
86
str(ex)))
 
87
            return ['fetch error', ]
 
88
        if queue:
 
89
            queue.put((self.name, result))
 
90
        else:
 
91
            return result
 
92
 
 
93
    def ping(self):
 
94
        try:
 
95
            pongtxt = fromenc(geturl("%s/ping" % self.url))
 
96
        except IOError, ex:
 
97
            pongtxt = str(ex)
 
98
        except Exception, ex:
 
99
            pongtxt = ""
 
100
        return pongtxt
 
101
 
 
102
class Collective(object):
 
103
 
 
104
    def __init__(self):
 
105
        self.nodes = {}
 
106
        self.state = Pdod(datadir + os.sep + 'coll.state')
 
107
        self.startup = Pdod(datadir + os.sep + 'coll.startup')
 
108
        if not self.state.has_key('ignore'):
 
109
            self.state['ignore'] = []
 
110
        if not self.state.has_key('urls'):
 
111
            self.state['urls'] = {}
 
112
        if not self.state.has_key('names'):
 
113
            self.state['names'] = {}
 
114
        if not self.startup.has_key('start'):
 
115
            self.startup['start'] = {}
 
116
 
 
117
    def add(self, name, url):
 
118
        id = getid(url)
 
119
        if self.nodes.has_key(id):
 
120
            return self.nodes[id]
 
121
        self.nodes[id] = CollNode(name, url)
 
122
        self.state.set('urls', url, id)
 
123
        self.state.set('names', name,  id)
 
124
        self.persist(name, url)
 
125
        rlog(-10, 'collective', 'added %s node (%s) <%s>' % (name, id, url))
 
126
        return self.nodes[id]
 
127
 
 
128
    def start(self):
 
129
        for name, url in self.startup['start'].iteritems():
 
130
            self.add(name, url)
 
131
        start_new_thread(self.joinall, ())
 
132
 
 
133
    def persist(self, name, url):
 
134
        self.startup.set('start', name, url)
 
135
        self.startup.save()
 
136
 
 
137
    def get(self, id):
 
138
        try:
 
139
            return self.nodes[id]
 
140
        except KeyError:
 
141
            return
 
142
 
 
143
    def byname(self, name):
 
144
        id = self.state.get('names', name)
 
145
        if id:
 
146
            return self.get(id)
 
147
 
 
148
    def remove(self, id):
 
149
        target = self.get(id)
 
150
        if not target:
 
151
            return
 
152
        del self.state['urls'][target.url]
 
153
        del self.state['names'][target.name]
 
154
        del self.nodes[id]
 
155
 
 
156
    def unpersist(self, id):
 
157
        node = self.get(id)
 
158
        if not node:
 
159
            return
 
160
        try:
 
161
            del self.startup['start'][node.name]
 
162
            self.startup.save()
 
163
        except KeyError:
 
164
            pass
 
165
            
 
166
    def ignore(self, id):
 
167
        self.state['ignore'].append(id)
 
168
 
 
169
    def unignore(self, id):
 
170
        self.state.data['ignore'].remove(id)
 
171
        self.state.save()
 
172
 
 
173
    def stop(self):
 
174
        pass
 
175
 
 
176
    def cmndall(self, what, wait=5):
 
177
        total = {}
 
178
        queue = Queue.Queue()
 
179
        for id, node in self.nodes.iteritems():
 
180
            start_new_thread(node.cmnd, (what, queue))
 
181
        result = waitforqueue(queue, wait, maxitems=size())
 
182
        for res in result:
 
183
            total[res[0]] = res[1]
 
184
        return total
 
185
 
 
186
    def getnodes(self):
 
187
        return self.nodes.values()
 
188
 
 
189
    def getname(self, url):
 
190
        id = getid(url)
 
191
        return self.get(id).name
 
192
 
 
193
    def join(self, url=None):
 
194
        try:
 
195
            if url:
 
196
                result = geturl("%s/join?%s" % (url, \
 
197
webcfg.get('webport')))
 
198
            else:
 
199
                result = geturl('http://gozerbot.org:8088/join?%s' % \
 
200
webcfg.get('webport'))
 
201
        except Exception, ex:
 
202
            result = str(ex)
 
203
        result = result.strip()
 
204
        if 'added' in result:
 
205
            rlog(1, 'collective', "%s joined =>  %s" % (url, result))
 
206
        else:
 
207
            rlog(1, 'collective', "can't join %s => %s" % (url, str(result)))
 
208
        return result
 
209
 
 
210
    def joinall(self):
 
211
        for node in self.nodes.values():
 
212
           start_new_thread(self.join, (node.url, ))
 
213
 
 
214
    def boot(self, node=None):
 
215
        if node:
 
216
            self.sync(node)
 
217
        else:
 
218
            self.sync('http://gozerbot.org:8088')
 
219
        rlog(10, 'collective', 'booted %s nodes' % self.size())
 
220
 
 
221
    def fullboot(self):
 
222
        teller = 0
 
223
        threads = []
 
224
        for node in self.nodes.values():
 
225
            t = start_new_thread(self.sync, (node.url, False))        
 
226
            threads.append(t)
 
227
            teller += 1
 
228
        for i in threads:
 
229
            i.join()
 
230
        return teller
 
231
 
 
232
    def sync(self, url, throw=True):
 
233
        """ sync cacne with node """
 
234
        try:
 
235
            result = fromenc(geturl('%s/nodes' % url))
 
236
        except Exception, ex:
 
237
            if throw:
 
238
                raise
 
239
            else:
 
240
                return 0
 
241
        if not result:
 
242
            return 0
 
243
        rss = rsslist(result)
 
244
        got = 0
 
245
        for i in rss:
 
246
            try:
 
247
                url = i['url']
 
248
                id = i['id']
 
249
                name = i['name']
 
250
            except KeyError:
 
251
                continue
 
252
            try:
 
253
                self.add(name, url)
 
254
                got += 1
 
255
            except:
 
256
                handle_exception()
 
257
        return got
 
258
 
 
259
    def size(self):
 
260
        return len(self.nodes)
 
261
 
 
262
    def list(self):
 
263
        res = []
 
264
        for node in self.nodes.values():
 
265
            res.append(str(node))
 
266
        return res
 
267
 
 
268
    def names(self): 
 
269
        return self.state['names'].keys()
 
270
 
 
271
    def nodesxml(self):
 
272
        """ return nodes in xml format """
 
273
        result = "<xml>\n"
 
274
        gotit = False
 
275
        for name, node in self.nodes.iteritems():
 
276
            gotit = True
 
277
            result += "    <coll>\n"
 
278
            result += "        <url>%s</url>\n" % node.url
 
279
            result += "        <id>%s</id>\n" % node.id
 
280
            result += "        <name>%s</name>\n" % node.name
 
281
            result += "    </coll>\n"
 
282
        if gotit:
 
283
            result += "</xml>"
 
284
            return result
 
285
        return ""
 
286
 
 
287
colls = Collective()
 
288
 
37
289
def init():
38
290
    """ init the collective plugin """
39
291
    if not coll['enable']:
40
292
        return 1
41
 
    periodical.addjob(900, 0, checkone)
42
 
    start_new_thread(checkone, ())
 
293
    time.sleep(5)
 
294
    try:
 
295
        colls.boot()
 
296
    except Exception, ex:
 
297
        rlog(10, 'collective', 'error booting: %s' % str(ex))
 
298
    colls.start()
 
299
    rlog(10, 'collective', 'total of %s nodes' % size())
43
300
    return 1
44
301
 
45
302
def shutdown():
46
303
    """ shutdown the collective plugin """
47
 
    periodical.kill()
 
304
    return 1
48
305
 
49
306
def size():
50
307
    """ return number of active collective nodes """
51
 
    return len(coll['active'])
52
 
 
53
 
def checkone():
54
 
    """ do one active check """
55
 
    removed = []
56
 
    added = []
57
 
    q = Queue.Queue()
58
 
    for i in coll['nodes']:
59
 
        start_new_thread(gotpong, (i, q))
60
 
    result = waitforqueue(q, 4)
61
 
    for i in coll['nodes']:
62
 
        if i not in result:
63
 
            try:
64
 
                if not i in coll['active']:
65
 
                    continue
66
 
                coll['active'].remove(i)
67
 
                removed.append(i)
68
 
                rlog(0, 'collective', 'removed %s from active list' % i)
69
 
            except ValueError:
70
 
                pass
71
 
        else:
72
 
            if not i in coll['active']:
73
 
                coll['active'].append(i)
74
 
                added.append(i)
75
 
                rlog(0, 'collective', '%s added to active list' % i)
76
 
    return (removed, added)
77
 
 
78
 
def doping(node):
79
 
    """ do ping of a collective node """
80
 
    q = Queue.Queue()
81
 
    start_new_thread(gotpong, (node, q))
82
 
    return waitforqueue(q, 4)
83
 
         
84
 
def gotpong(node, queue):
85
 
    """ check if node returns pong """
86
 
    try:
87
 
        pongtxt = fromenc(geturl("http://%s/ping&how=direct" % node))
88
 
    except Exception, ex:
89
 
        pongtxt = ""
90
 
    pongtxt = pongtxt
91
 
    if 'pong' in pongtxt:
92
 
        queue.put_nowait(node)
93
 
 
94
 
def getnodes():
95
 
    """ return cached nodes cache """
96
 
    return coll['nodes']
97
 
 
98
 
def getactive():
99
 
    """ return active nodes """
100
 
    return coll['active']
101
 
 
102
 
def getname(node):
103
 
    """ return name of node or None """
104
 
    if coll['names'].has_key(node):
105
 
        return coll['names'][node]
106
 
 
107
 
def getnodefromname(name):
108
 
    for i, j in coll['names'].iteritems():
109
 
        if j == name:
110
 
            return i
111
 
 
112
 
def checkactive(node):
113
 
    if doping(node):
114
 
        coll['active'].append(node)
115
 
 
116
 
def addnode(node):
117
 
    """ add a node to cache """
118
 
    (host, port) = node.split(':')
119
 
    try:
120
 
        ipnr = socket.gethostbyname(host)
121
 
        if ipnr:
122
 
            node = "%s:%s" % (ipnr, port)
123
 
    except:
124
 
        pass
125
 
    if node not in coll['nodes']:
126
 
        rlog(0, 'collective', 'adding node %s (%s)' % (str(node), \
127
 
getname(node)))
128
 
        coll['nodes'].append(node)
129
 
        coll.save()
130
 
        return 1
131
 
    return 0
132
 
 
133
 
def syncnode(node):
134
 
    """ sync cacne with node """
135
 
    try:
136
 
        result = fromenc(geturl('http://%s/nodes&how=noescape' % node))
137
 
    except:
138
 
        rlog(10, 'collective', "can't fetch %s data" % node)
139
 
        return 0
140
 
    result = result
141
 
    rss = rsslist(result)
142
 
    got = 0
143
 
    for i in rss:
144
 
        try:
145
 
            node = i['node']
146
 
            if addnode(node):
147
 
                got = 1
148
 
        except:
149
 
            continue
150
 
        try:
151
 
            if not coll['names'].has_key(node):
152
 
                coll['names'][node] = i['name']
153
 
        except:
154
 
            pass
155
 
        start_new_thread(checkactive, (node, ))
156
 
    if got:
157
 
        coll.save()
158
 
        return 1
159
 
    else:
160
 
        return 0
161
 
 
162
 
def colldispatchone(node, what, queue):
163
 
    """ dispatch command on remote node .. place result in queue """
164
 
    colldispatch(node, what, queue)
165
 
    queue.put(None)
166
 
 
167
 
def colldispatch(node, what, queue):
168
 
    """ dispatch command on remote node .. place result in queue """
169
 
    if what.startswith('coll'):
170
 
        return
171
 
    try:
172
 
        what = re.sub('\s+', '+', what)
173
 
        result = fromenc(geturl('http://%s/dispatch?%s&how=direct' % \
174
 
(node, what)))
175
 
    except:
176
 
        rlog(10, 'collective', "can't fetch %s data" % node)
177
 
        return 0
178
 
    name = getname(node)
179
 
    if not name:
180
 
        name = node
181
 
    res = result.split('\n\n')
182
 
    queue.put((name, res))
183
 
 
184
 
def colldispatchall(what, wait=5):
185
 
    """ coll dispatch on all nodes """
186
 
    queue = Queue.Queue()
187
 
    for i in coll['active']:
188
 
        start_new_thread(colldispatch, (i, what, queue))
189
 
    for i in range(wait*10):
190
 
        if queue.qsize() == len(coll['active']):
191
 
            break
192
 
        time.sleep(0.1)
193
 
    queue.put(None)
194
 
    return queue
195
 
        
196
 
def boot(node=None):
197
 
    """ sync cache with main server or server provided """
198
 
    got = 0
199
 
    if not node:
200
 
        if config['collboot']:
201
 
            got = syncnode(config['collboot'])
202
 
        else:
203
 
            got = syncnode('irc.gozerbot.org:8088')
204
 
    else:
205
 
        got = syncnode(node)
206
 
    if got:
207
 
        nrnodes = len(coll['nodes'])
208
 
        rlog(10, 'collective', 'booted %s nodes' % nrnodes)
209
 
        return nrnodes
210
 
    else:
211
 
        return 0
212
 
    
213
 
def nodesxml():
214
 
    """ return nodes in xml format """
215
 
    result = "<xml>\n"
216
 
    gotit = False
217
 
    for i in coll['nodes']:
218
 
        gotit = True
219
 
        result += "    <coll>\n"
220
 
        result += "        <node>%s</node>\n" % i
221
 
        name = getname(i)
222
 
        if name: 
223
 
            result += "        <name>%s</name>\n" % name
224
 
        result += "    </coll>\n"
225
 
    if gotit:
226
 
        result += "</xml>"
227
 
        return result
228
 
    return ""
 
308
    return colls.size()
229
309
 
230
310
def handle_collping(bot, ievent):
231
311
    """ do a ping on a collective node """
237
317
    except IndexError:
238
318
        ievent.missing('<name>')
239
319
        return
240
 
    node = getnodefromname(name)
241
 
    if not node:
242
 
        ievent.reply('no node %s known' % name)
243
 
        return
244
 
    result = doping(node)
245
 
    if result:
 
320
    id = colls.state['names'][ievent.rest]
 
321
    node = colls.get(id)
 
322
    result = node.ping()
 
323
    if 'pong' in result:
246
324
        ievent.reply('%s is alive' % name)
247
325
    else:
248
326
        ievent.reply('%s is not alive' % name)
249
327
 
250
328
cmnds.add('coll-ping', handle_collping, 'OPER')
 
329
examples.add('coll-ping', 'ping a collective node', 'coll-ping gozerbot')
251
330
 
252
331
def handle_colllist(bot, ievent):
253
332
    """ coll-list .. list all nodes in cache """
254
333
    if not coll['enable']:
255
334
        ievent.reply('collective is not enabled')
256
335
        return
257
 
    ievent.reply("collective nodes: ", coll['nodes'], dot=True)
 
336
    ievent.reply("collective nodes: ", colls.list(), dot=' \002||\002 ')
258
337
 
259
338
cmnds.add('coll-list', handle_colllist, 'OPER')
260
339
examples.add('coll-list', 'list nodes cache', 'coll-list')
261
340
 
262
341
def handle_collenable(bot, ievent):
263
342
    """ coll-enable .. enable the collective """
 
343
    ievent.reply('enabling collective')
264
344
    coll['enable'] = 1
265
345
    coll.save()
266
346
    plugins.reload('gozerplugs.plugs', 'collective')
267
 
    boot()
268
 
    ievent.reply('collective enabled')
 
347
    time.sleep(4)
 
348
    ievent.reply('done')
269
349
 
270
350
cmnds.add('coll-enable', handle_collenable, 'OPER')
271
351
examples.add('coll-enable', 'enable the collective', 'coll-enable')
286
366
        ievent.reply('collective is not enabled')
287
367
        return
288
368
    try:
289
 
        node = ievent.args[0]
 
369
        url = ievent.args[0]
290
370
    except IndexError:
291
 
        ievent.missing('<node>')
292
 
        return
293
 
    result = syncnode(node)
 
371
        ievent.missing('<url>')
 
372
        return
 
373
    try:
 
374
        result = colls.sync(url)
 
375
    except IOError, ex:
 
376
        ievent.reply('ERROR: %s' % str(ex))
 
377
        return
294
378
    ievent.reply('%s nodes added' % result)
295
379
 
296
380
cmnds.add('coll-sync', handle_collsync, 'OPER', allowqueue=False)
297
 
examples.add('coll-sync', 'coll-sync <node> .. sync with provided node', \
298
 
'coll-sync gozerbot.org')
 
381
examples.add('coll-sync', 'coll-sync <url> .. sync with provided node', \
 
382
'coll-sync http://gozerbot.org:8088')
299
383
 
300
384
def handle_coll(bot, ievent):
301
385
    """ coll <cmnd> .. execute <cmnd> on nodes """
318
402
        command = re.sub(waitre, '', command)
319
403
    else:
320
404
        wait = 5
321
 
    result = colldispatchall(command + ' chan %s' % ievent.channel, wait)
322
 
    resultlist = []
323
 
    total = len(coll['nodes'])
324
 
    teller = 0
325
 
    got = Dol()
326
 
    while 1:
327
 
        try:
328
 
            res = result.get(1, wait+2)
329
 
        except Queue.Empty :
330
 
            break
331
 
        if not res:
332
 
            break
333
 
        for i in res[1]:
334
 
            data = fromenc(strippedtxt(unescape(i)))
335
 
            if data:
336
 
                got.add(res[0], data)
337
 
    r = []
338
 
    for i, j in got.iteritems():
339
 
        if not j:
340
 
            continue
341
 
        teller += 1
342
 
        r.append("||%s||" % i)
343
 
        for z in j:
344
 
            r.append(z)
345
 
    if r:
346
 
        ievent.reply('%s out of %s (%s) => ' % (teller, total, time.time() - starttime), r, \
347
 
dot=True)
 
405
    result = colls.cmndall(command + ' chan %s' % ievent.channel, wait)
 
406
    if result:
 
407
        reply = '%s out of %s (%s) => ' % (len(result), size(), time.time() \
 
408
- starttime)
 
409
        ievent.reply(reply, result, dot='\002||\002')
348
410
    else:
349
411
        ievent.reply('no results found')
350
412
 
362
424
    except ValueError:
363
425
        ievent.missing('<nodename> <command>')
364
426
        return
365
 
    node = getnodefromname(name)
 
427
    node = colls.byname(name)
366
428
    if not node:
367
429
        ievent.reply('no node %s found' % name)
368
430
        return
380
442
    starttime = time.time()
381
443
    queue = Queue.Queue()
382
444
    command = command + ' chan %s' % ievent.channel
383
 
    start_new_thread(colldispatchone, (node, command, queue))
384
 
    res = waitforqueue(queue, wait+2)
385
 
    r = []
386
 
    if res:
387
 
        for i in res:
388
 
            for j in i[1]:
389
 
                if j:
390
 
                    data = fromenc(strippedtxt(unescape(j)))
391
 
                    r.append(data)
392
 
        ievent.reply("(%s) => ||%s|| " % (time.time() - starttime, name), r, dot=True)
 
445
    start_new_thread(node.cmnd, (command, queue))
 
446
    res = waitforqueue(queue, wait+2, maxitems=1) 
 
447
    result = []
 
448
    for i in res:
 
449
        result.append(i[1])
 
450
    if result:
 
451
        ievent.reply("(%s) => [%s] " % (time.time() - starttime, name), \
 
452
result, dot=True)
393
453
    else:
394
454
        ievent.reply('no results found')
395
455
 
396
456
cmnds.add('coll-exec', handle_collexec, ['USER', 'WEB'], allowqueue=False)
397
457
examples.add('coll-exec', 'coll <nodename> <cmnd> .. execute command in \
398
 
the collective', 'coll gozerbot lq')
 
458
the collective', 'coll-exec gozerbot lq')
399
459
 
400
460
def handle_colladdnode(bot, ievent):
401
461
    """ coll-addnode <name> <host:port> .. add node to cache """
403
463
        ievent.reply('collective is not enabled')
404
464
        return
405
465
    try:
406
 
        (name, node) = ievent.args
 
466
        (name, url) = ievent.args
407
467
    except ValueError:
408
 
        ievent.missing('<name> <host:port>')
409
 
        return
410
 
    if ':' not in node:
411
 
        ievent.missing('<name> <host:port>')
412
 
        return
413
 
    (host, port) = node.split(':')
414
 
    try:
415
 
        ipnr = socket.gethostbyname(host)
416
 
        if ipnr:
417
 
            node = "%s:%s" % (ipnr, port)
418
 
    except:
419
 
        ievent.reply("can't find ipnr for %s" % host)
420
 
        return
421
 
    coll['names'][node] = name
422
 
    addnode(node)
423
 
    coll.save()
 
468
        ievent.missing('<name> <url>')
 
469
        return
 
470
    colls.add(name, url)
 
471
    colls.persist(name, url)
424
472
    ievent.reply('%s added' % name)
425
473
 
426
 
cmnds.add('coll-addnode', handle_colladdnode, 'OPER')
427
 
examples.add('coll-addnode', 'coll-addnode <name> <node> .. add a node to \
428
 
cache .. node is in host:port format', 'coll-addnode gozerbot \
429
 
gozerbot.org:8088')
430
 
 
431
 
def handle_colldelnode(bot, ievent):
432
 
    """ coll-delnode <name> .. delete node from cache """
433
 
    if not coll['enable']:
434
 
        ievent.reply('collective is not enabled')
435
 
        return
436
 
    try:
437
 
        what = ievent.args[0]
438
 
    except IndexError:
439
 
        ievent.missing('<name>')
440
 
        return
441
 
    whattodel = []
442
 
    for i, j in coll['names'].iteritems():
443
 
        if j == what:
444
 
            whattodel.append(i)
445
 
    if not whattodel:
446
 
        ievent.reply("can't find node %s" % what)
447
 
        return
448
 
    for i in whattodel:
449
 
        try:
450
 
            coll['nodes'].remove(i)
451
 
        except ValueError:
452
 
            pass
453
 
        try:
454
 
            del coll['names'][i]
455
 
        except ValueError:
456
 
            pass
457
 
        try:
458
 
            coll['active'].remove(i)
459
 
        except ValueError:
460
 
            pass
461
 
    coll.save()
462
 
    ievent.reply('%s deleted' % what)
463
 
 
464
 
cmnds.add('coll-delnode', handle_colldelnode, 'OPER')
465
 
examples.add('coll-delnode', 'coll-delnode <name> .. remove node from \
466
 
collective list', 'coll-delnode gozerbot')
 
474
cmnds.add('coll-add', handle_colladdnode, 'OPER')
 
475
examples.add('coll-add', 'coll-add <name> <url> .. add a node to cache and \
 
476
persist it', 'coll-add gozerbot http://gozerbot.org:8088')
467
477
 
468
478
def handle_collgetnode(bot, ievent):
469
479
    """ coll-getnode .. show node of <name>  """
475
485
    except IndexError:
476
486
        ievent.missing('<name>')
477
487
        return
478
 
    for i, j in coll['names'].iteritems():
479
 
        if j == name:
480
 
            ievent.reply(i)
481
 
            return
482
 
    ievent.reply('no node named %s found' % name)
 
488
    try:
 
489
        node = colls.byname(name)
 
490
    except KeyError:
 
491
        ievent.reply('no such node')
 
492
    ievent.reply(str(node))
483
493
 
484
494
cmnds.add('coll-getnode', handle_collgetnode, 'OPER')
485
495
examples.add('coll-getnode', 'coll-getnode <name> .. get node of <name>', \
486
496
'coll-getnode gozerbot')
487
497
 
488
 
def handle_collsetname(bot, ievent):
489
 
    """ coll-setname <node> <name> .. set name of node """
490
 
    if not coll['enable']:
491
 
        ievent.reply('collective is not enabled')
492
 
        return
493
 
    try:
494
 
        (what, name) = ievent.args
495
 
    except ValueError:
496
 
        ievent.missing('<node> <name>')
497
 
        return
498
 
    coll['names'][what] = name
499
 
    coll.save()
500
 
    ievent.reply('%s added' % name)
501
 
 
502
 
cmnds.add('coll-setname', handle_collsetname, 'OPER')
503
 
examples.add('coll-setname', 'set name of collective node', 'coll-setname \
504
 
gozerbot gozerbot.org:8088')
505
 
 
506
498
def handle_collnames(bot, ievent):
507
499
    """ coll-names .. show names with nodes in cache """
508
500
    if not coll['enable']:
509
501
        ievent.reply('collective is not enabled')
510
502
        return
511
 
    ievent.reply("collective node names: ", coll['names'].values(), \
512
 
dot=True)
 
503
    ievent.reply("collective node names: ", colls.names(), dot=True)
513
504
 
514
505
cmnds.add('coll-names', handle_collnames, 'OPER')
515
506
examples.add('coll-names', 'show all node names', 'coll-names')
520
511
        ievent.reply('collective is not enabled')
521
512
        return
522
513
    try:
523
 
        server = ievent.args[0]
 
514
        url = ievent.args[0]
524
515
    except IndexError:
525
 
        server = 'gozerbot.org:8088'
526
 
    bootnr = boot(server)
 
516
        url = 'http://gozerbot.org:8088'
 
517
    try:
 
518
        bootnr = colls.boot(url)
 
519
    except IOError, ex:
 
520
        ievent.reply('ERROR: %s' % str(ex))
 
521
        return
527
522
    if bootnr:
528
523
        ievent.reply('collective added %s nodes' % bootnr)
529
524
    else:
530
 
        ievent.reply("no new nodes added from %s" % server)
 
525
        ievent.reply("no new nodes added from %s" % url)
531
526
 
532
527
cmnds.add('coll-boot', handle_collboot, 'OPER')
533
528
examples.add('coll-boot', 'sync collective list with provided host', \
534
 
'1) coll-boot 2) coll-boot localhost:8888')
 
529
'1) coll-boot 2) coll-boot http://localhost:8888')
535
530
 
536
531
def handle_collfullboot(bot, ievent):
537
532
    """ coll-fullboot .. boot from all nodes in cache """
538
533
    if not coll['enable']:
539
534
        ievent.reply('collective is not enabled')
540
535
        return
541
 
    teller = 0
542
 
    snap = list(coll['nodes'])
543
 
    threads = []
544
 
    for i in snap:
545
 
        t = start_new_thread(boot, (i, ))
546
 
        threads.append(t)
547
 
        teller += 1
548
 
    for i in threads:
549
 
        i.join()
550
 
    ievent.reply('%s nodes checked .. current %s nodes in list' % (teller, 
551
 
len(coll['nodes'])))
 
536
    try:
 
537
        teller = colls.fullboot()
 
538
    except IOError, ex:
 
539
        ievent.reply('ERROR: %s' % str(ex))
 
540
        return
 
541
    ievent.reply('%s nodes checked .. current %s nodes in list' % (teller, \
 
542
size()))
552
543
 
553
544
cmnds.add('coll-fullboot', handle_collfullboot, 'OPER')
554
545
examples.add('coll-fullboot', 'do a boot on every node in the collective \
555
546
list', 'coll-fullboot')
556
547
 
557
 
def handle_collsave(bot, ievent):
558
 
    """ coll-save .. save collective data to disk """
559
 
    if not coll['enable']:
560
 
        ievent.reply('collective is not enabled')
561
 
        return
562
 
    coll.save()
563
 
    ievent.reply('collective saved')
564
 
 
565
 
cmnds.add('coll-save', handle_collsave, 'OPER')
566
 
examples.add('coll-save', 'save collective data' , 'coll-save')
567
 
 
568
 
def handle_clean(bot , ievent):
569
 
    """ coll-clean .. clear nodes cache """
570
 
    if not coll['enable']:
571
 
        ievent.reply('collective is not enabled')
572
 
        return
573
 
    coll['nodes'] = []
574
 
    coll['active'] = []
575
 
    coll.save()
576
 
    ievent.reply('done')
577
 
 
578
 
cmnds.add('coll-clean', handle_clean, 'OPER')
579
 
examples.add('coll-clean', 'clean the collective list', 'coll-clean')
580
 
 
581
 
def handle_collactive(bot, ievent):
582
 
    """ show active nodes """
583
 
    if not coll['enable']:
584
 
        ievent.reply('collective is not enabled')
585
 
        return
586
 
    res = []
587
 
    for i in coll['active']:
588
 
        name = getname(i)
589
 
        res.append("%s (%s)" % (i, name))
590
 
    if res:
591
 
        ievent.reply("active nodes: ", res, dot=True)
592
 
    else:
593
 
        ievent.reply("no nodes active")
594
 
        
595
 
cmnds.add('coll-active', handle_collactive, 'OPER')
596
 
examples.add('coll-active', 'show active nodes', 'coll-active')
597
 
 
598
 
def handle_collinactive(bot, ievent):
599
 
    """ show inactive nodes """
600
 
    if not coll['enable']:
601
 
        ievent.reply('collective is not enabled')
602
 
        return
603
 
    res = []
604
 
    for i in coll['nodes']:
605
 
        if i not in coll['active']:
606
 
            name = getname(i)
607
 
            res.append("%s (%s)" % (i, name))
608
 
    if res:
609
 
        ievent.reply("inactive nodes: ", res, dot=True)
610
 
    else:
611
 
        ievent.reply("no nodes inactive")
612
 
        
613
 
cmnds.add('coll-inactive', handle_collinactive, 'OPER')
614
 
examples.add('coll-inactive', 'show inactive nodes', 'coll-inactive')
615
 
 
616
 
def handle_collcheckactive(bot, ievent):
617
 
    """ run active check """
618
 
    if not coll['enable']:
619
 
        ievent.reply('collective is not enabled')
620
 
        return
621
 
    ievent.reply('calling active checker')
622
 
    result = checkone()
623
 
    ievent.reply('removed: %s .. added: %s' % result)
624
 
 
625
 
cmnds.add('coll-checkactive', handle_collcheckactive, 'OPER')
626
 
examples.add('coll-checkactive', 'run active nodes check', 'coll-checkactive')
627
 
 
628
 
def handle_collstatus(bot, ievent):
629
 
    """ show active collective nodes """
630
 
    print coll['enable']
631
 
    if not coll['enable']:
632
 
        ievent.reply('collective is not enabled')
633
 
        return
634
 
    there = []
635
 
    for i in coll['active']:
636
 
        try:
637
 
            name = coll['names'][i]
638
 
            there.append(name)
639
 
        except KeyError:
640
 
            pass
641
 
    if there:
642
 
        ievent.reply('%s active nodes: ' % len(coll['active']), there, \
643
 
dot=True)
644
 
    else:
645
 
        ievent.reply('collective void')
646
 
 
647
 
cmnds.add('coll-status', handle_collstatus, 'OPER')
648
 
examples.add('coll-status', 'show active  nodes', 'coll-status')
649
 
 
650
 
def handle_collrename(bot, ievent):
651
 
    """ rename a collective node """
652
 
    if not coll['enable']:
653
 
        ievent.reply('collective is not enabled')
654
 
        return
655
 
    try:
656
 
        name, toname = ievent.args
657
 
    except ValueError:
658
 
        ievent.missing('<from> <to>')
659
 
        return
660
 
    node = getnodefromname(name)
661
 
    if not node:
662
 
        ievent.reply("can't get node for %s" % name)
663
 
        return
664
 
    try:
665
 
        coll['names'][node] = toname
666
 
    except KeyError:
667
 
        ievent.reply('setting name failed')
668
 
        return
669
 
    ievent.reply('name %s changed to %s' % (name, toname))
670
 
 
671
 
cmnds.add('coll-rename', handle_collrename, 'OPER')
672
 
examples.add('coll-rename', 'rename a collective node name', \
673
 
'coll-rename dunk dunker')
674
 
 
675
548
def handle_collremove(bot, ievent):
676
 
    """ remove a collective node """
677
 
    try:
678
 
        name = ievent.args[0]
679
 
    except IndexError:
 
549
    if not coll['enable']:
 
550
        ievent.reply('collective is not enabled')
 
551
        return
 
552
    if not ievent.rest:
680
553
        ievent.missing('<name>')
681
554
        return
682
 
    node = getnodefromname(name)
683
 
    if not node:
684
 
        ievent.reply("can't get node for %s" % name)
685
 
        return
 
555
    got = False
686
556
    try:
687
 
        coll['nodes'].remove(node)
688
 
        coll['active'].remove(node)
689
 
        del coll['names'][node]
 
557
        id = colls.state['names'][ievent.rest]
 
558
        if id:
 
559
            colls.unpersist(id)
 
560
            colls.remove(id)
 
561
            got = True
690
562
    except Exception, ex:
691
 
        ievent.reply("failed to remove %s: %s" % (name, str(ex)))
692
 
        return 
693
 
    coll.save()
694
 
    ievent.reply('%s removed' % name)
 
563
        ievent.reply('error removing %s: %s' % (ievent.rest, str(ex)))
 
564
        return
 
565
    if got:
 
566
        ievent.reply('%s node removed' % ievent.rest)
 
567
    else:
 
568
        ievent.reply('error removing %s node' % ievent.rest)
695
569
 
696
570
cmnds.add('coll-remove', handle_collremove, 'OPER')
697
 
examples.add('coll-remove', 'coll-remove <name> .. rmeove node <name> from \
698
 
the colletive lists', 'coll-remove gozerbot')
 
571
examples.add('coll-remove', 'remove node with <name> from collective' , \
 
572
'coll-remove gozerbot')
 
573
 
 
574
def handle_colljoin(bot, ievent):
 
575
    if not coll['enable']:
 
576
        ievent.reply('collective is not enabled')
 
577
        return
 
578
    if not ievent.rest:
 
579
        ievent.missing('<name>')
 
580
        return
 
581
    try:
 
582
        id = colls.state['names'][ievent.rest]
 
583
        node = colls.get(id)
 
584
        result = colls.join(node.url)
 
585
    except Exception, ex:
 
586
        ievent.reply('error joining %s: %s' % (ievent.rest, str(ex)))
 
587
        return
 
588
    ievent.reply(result)
 
589
 
 
590
cmnds.add('coll-join', handle_colljoin, 'OPER')
 
591
examples.add('coll-join', 'join node with <name>' , 'coll-join gozerbot')