mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-23 14:52:26 +00:00
164 lines
6.1 KiB
Python
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)
|
|
|