tahoe-lafs/misc/awesome_weird_stuff/boodlegrid.tac

164 lines
6.1 KiB
Python

# -*- python -*-
from __future__ import print_function
"""Monitor a Tahoe grid, by playing sounds in response to remote events.
To install:
1: install Boodler, from http://www.eblong.com/zarf/boodler/
2: run "boodler.py -l listen.Sounds". This will run a daemon
that listens on a network socket (31863 by default) and
accepts commands in the form of "sound bird/crow1.aiff\n"
3: copy this file into a new directory, which we'll call $BASEDIR
4: write one or more logport FURLs into files named *.furl or *.furls, one
per line. All logports from all such files will be used.
5: launch this daemon with 'cd $BASEDIR && twistd -y boodlegrid.tac'
"""
import os, time
from zope.interface import implements
from twisted.application import service
from twisted.internet import protocol, reactor, defer
from foolscap import Tub, Referenceable
from foolscap.logging.interfaces import RILogObserver
from twisted.python import log
class Listener:
def __init__(self):
self.boodler = None # filled in when we connect to boodler
self.last = {}
def sound(self, name, slot=None, max=0.100):
if not self.boodler:
return
now = time.time()
if slot is None:
slot = name
if now < self.last.get(slot, 0) + max:
return # too soon
self.last[slot] = now
self.boodler.write("sound %s\n" % name)
def msg(self, m, furl):
#print "got it", m
message = m.get("message", m.get("format", ""))
format = m.get("format", "")
facility = m.get("facility", "")
# messages emitted by the Introducer: client join/leave
if message.startswith("introducer: subscription[storage] request"):
print("new client")
self.sound("voice/hooray.aiff")
if message.startswith("introducer: unsubscribing"):
print("unsubscribe")
self.sound("electro/zaptrill-fade.aiff")
# messages from the helper
if message == "file already found in grid":
print("already found")
self.sound("mech/ziplash-high.aiff")
#if message == "upload done":
if format == "plaintext_hash=%(plaintext_hash)s, SI=%(SI)s, size=%(size)d":
size = m.get("size")
print("upload done, size", size)
self.sound("mech/ziplash-low.aiff")
if "fetching " in message:
# helper grabbing ciphertext from client
self.sound("voice/phoneme/sh.aiff", max=0.5)
# messages from storage servers
if message.startswith("storage: slot_readv"):
#self.sound("voice/phoneme/r.aiff")
self.sound("percussion/wood-tap-hollow.aiff")
# messages from webapi
if message.startswith("Retrieve") and "starting" in message:
self.sound("mech/metal-clack.aiff")
if message.startswith("Publish") and "starting" in message:
self.sound("mech/door-slam.aiff")
#self.sound("mech/metal-clash.aiff")
if ("web: %(clientip)s" in format
and m.get("method") == "POST"
and ("t=set_children" in m.get("uri", "") # FIXME: may give false-positives
or "t=set-children" in m.get("uri", ""))):
self.sound("mech/clock-clang.aiff")
# generic messages
#if m['level'] < 20:
# self.sound("mech/keyboard-1.aiff")
if "_check_for_done but we're not running" in message:
pass
elif format == "excessive reactor delay (%ss)":
self.sound("animal/frog-cheep.aiff")
print("excessive delay %s: %s" % (m['args'][0], furl))
elif format == "excessive reactor delay (%(delay)ss)":
self.sound("animal/frog-cheep.aiff")
print("excessive delay %s: %s" % (m['delay'], furl))
elif facility == "foolscap.negotiation":
if (message == "got offer for an existing connection"
or "master told us to use a new connection" in message):
print("foolscap: got offer for an existing connection", message, furl)
else:
#print "foolscap:", message
pass
elif m['level'] > 30: # SCARY or BAD
#self.sound("mech/alarm-bell.aiff")
self.sound("environ/thunder-tense.aiff")
print(m, furl)
elif m['level'] == 30: # WEIRD
self.sound("mech/glass-breaking.aiff")
print(m, furl)
elif m['level'] > 20: # UNUSUAL or INFREQUENT or CURIOUS
self.sound("mech/telephone-ring-old.aiff")
print(m, furl)
class BoodleSender(protocol.Protocol):
def connectionMade(self):
print("connected to boodler")
self.factory.listener.boodler = self.transport
class Bridge(Referenceable):
implements(RILogObserver)
def __init__(self, furl, listener):
self.furl = furl
self.listener = listener
def remote_msg(self, m):
d = defer.maybeDeferred(self.listener.msg, m, self.furl)
d.addErrback(log.err)
# never send errors to the remote side
class Monitor(service.MultiService):
def __init__(self):
service.MultiService.__init__(self)
self.tub = Tub()
self.tub.setServiceParent(self)
self.listener = Listener()
self.targets = []
for fn in os.listdir("."):
if fn.endswith(".furl") or fn.endswith(".furls"):
for i,line in enumerate(open(fn, "r").readlines()):
target = line.strip()
if target:
self.tub.connectTo(target, self._got_logpublisher,
fn, i, target)
cf = protocol.ClientFactory()
cf.listener = self.listener
cf.protocol = BoodleSender
reactor.connectTCP("localhost", 31863, cf)
def _got_logpublisher(self, publisher, fn, i, target):
print("connected to %s:%d, %s" % (fn, i, target))
b = Bridge(target, self.listener)
publisher.callRemote("subscribe_to_all", b)
m = Monitor()
application = service.Application("boodlegrid")
m.setServiceParent(application)