Merge lp:~slimey/engage/stockwell into lp:engage

Proposed by Simon C
Status: Merged
Approved by: Simon C
Approved revision: no longer in the source branch.
Merged at revision: 4
Proposed branch: lp:~slimey/engage/stockwell
Merge into: lp:engage
Diff against target: 816 lines (+464/-162)
21 files modified
.bzrignore (+2/-0)
bin/engage (+28/-0)
debian/changelog (+6/-0)
debian/control (+2/-3)
debian/engage.install (+3/-0)
debian/postinst (+25/-0)
etc/init/engage.conf (+1/-1)
lib-depends/get-egg-src (+40/-0)
restart (+0/-2)
setup.py (+3/-3)
src/engage/media/__init__.py (+91/-0)
src/engage/media/movie.py (+21/-0)
src/engage/media/static.py (+36/-0)
src/engage/monitor/percent_used.py (+3/-3)
src/engage/ui/carousel.py (+37/-48)
src/engage/ui/web.py (+2/-0)
src/engage/util/atv.py (+94/-0)
src/engage/util/twisted_plugin.py (+65/-0)
src/twisted/plugins/engage_plugin.py (+5/-66)
start (+0/-19)
stop (+0/-17)
To merge this branch: bzr merge lp:~slimey/engage/stockwell
Reviewer Review Type Date Requested Status
Simon C Approve
Review via email: mp+126278@code.launchpad.net

Commit message

Embedded Twisted 12 and further ATV support

Description of the change

 - embedded twisted 12 for deployment on ubuntu
 - further ATV support (video support still half-baked but harmless)

To post a comment you must log in.
Revision history for this message
Simon C (slimey) :
review: Approve
lp:~slimey/engage/stockwell updated
4. By Simon C

Embedded Twisted 12 and further ATV support

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2012-09-25 15:49:24 +0000
@@ -0,0 +1,2 @@
1dropin.cache
2twisted-engage.egg
03
=== added directory 'bin'
=== added file 'bin/engage'
--- bin/engage 1970-01-01 00:00:00 +0000
+++ bin/engage 2012-09-25 15:49:24 +0000
@@ -0,0 +1,28 @@
1#!/usr/bin/python
2#
3# This is just a crazy equivalent of
4#
5# /usr/bin/twistd --uid nobody --gid nogroup --logfile /var/log/engage.log --nodaemon engage
6#
7# but allowing Twisted to be embedded, for those ubutnu machines with
8# a stock (pre-12.0) version.
9#
10# Happy day when we can ditch this.
11#
12import os, platform, sys
13try:
14 import _preamble
15except ImportError:
16 sys.exc_clear()
17
18lib_dir = '/usr/lib/engage/lib-depends'
19sys.path.insert(0, lib_dir)
20sys.path.insert(0, os.path.join(lib_dir, 'twisted-engage.egg'))
21
22argv_head = sys.argv[0]
23argv_tail = sys.argv[1:]
24sys.argv = [argv_head, '--uid', 'nobody', '--gid', 'nogroup', '--logfile',
25 '/var/log/engage.log', '--nodaemon'] + argv_tail + ['engage']
26
27from twisted.scripts.twistd import run
28run()
029
=== modified file 'debian/changelog'
--- debian/changelog 2012-09-17 13:13:06 +0000
+++ debian/changelog 2012-09-25 15:49:24 +0000
@@ -1,3 +1,9 @@
1engage (0.6.1) precise; urgency=low
2
3 * Embedded twisted 12 for easy of installing on stock ubuntu
4
5 -- Simon C <simonc@ensoft.co.uk> Mon, 24 Sep 2012 22:42:58 +0100
6
1engage (0.6) lucid; urgency=low7engage (0.6) lucid; urgency=low
28
3 * Improved structure and added basic ATV support9 * Improved structure and added basic ATV support
410
=== modified file 'debian/control'
--- debian/control 2012-09-17 13:13:06 +0000
+++ debian/control 2012-09-25 15:49:24 +0000
@@ -8,8 +8,7 @@
8Package: engage8Package: engage
9Architecture: all9Architecture: all
10# snmp package is just for snmptranslate10# snmp package is just for snmptranslate
11Depends: python,python-twisted,python-twisted-web (>= 12.0.0),python-rrdtool,libsnmp-python,python-pynetsnmp,snmp,snmp-mibs-downloader,${misc:Depends}11Depends: python (>= ${python:Versions}), python-setuptools, python-zope.interface, python-rrdtool, libsnmp-python, python-pynetsnmp, snmp, snmp-mibs-downloader, ${python:Depends}, ${misc:Depends}
12Description: Combined system/network statistics and Apple TV control framework12Description: Combined system/network statistics and Apple TV control framework
13 EnGage is a plugin-based framework for gathering, summarizing, and displaying 13 EnGage is a plugin-based framework for gathering, summarizing, and displaying
14 system or network administration data. This base package provides basic14 system or network administration data.
15 monitoring of the linux host itself.
1615
=== modified file 'debian/engage.install'
--- debian/engage.install 2012-09-17 13:13:06 +0000
+++ debian/engage.install 2012-09-25 15:49:24 +0000
@@ -1,3 +1,6 @@
1etc/engage.conf etc1etc/engage.conf etc
2etc/init/engage.conf etc/init2etc/init/engage.conf etc/init
3static/ usr/share/engage3static/ usr/share/engage
4bin/engage /usr/sbin
5lib-depends/twisted-engage-egg-src.tar.bz2 /usr/lib/engage/lib-depends
6lib-depends/twisted-engage.egg /usr/lib/engage/lib-depends
47
=== added file 'debian/postinst'
--- debian/postinst 1970-01-01 00:00:00 +0000
+++ debian/postinst 2012-09-25 15:49:24 +0000
@@ -0,0 +1,25 @@
1#!/bin/sh
2#
3# Build the embedded Twisted 12.2 package into an egg for the target system
4# architecture. When it's reasonable to just put Twisted >= 12.0 as a
5# dependency and expect ubuntu to honour it, we can cheerfully flush this down
6# the can.
7#
8# We include a zero-byte twisted-engage.egg and mark it as a conffile, so it's
9# ok to overwrite it here with a real egg.
10#
11set -e
12egg_src=/usr/lib/engage/lib-depends/twisted-engage-egg-src.tar.bz2
13egg=/usr/lib/engage/lib-depends/twisted-engage.egg
14tmp=/tmp/engage-$$
15rm -rf $tmp
16mkdir $tmp
17cd $tmp
18tar jxf $egg_src
19cd Twisted
20python setup.py bdist_egg
21mv dist/*.egg $egg
22cd /tmp
23rm -rf $tmp
24
25#DEBHELPER#
026
=== modified file 'etc/init/engage.conf'
--- etc/init/engage.conf 2012-09-18 21:21:43 +0000
+++ etc/init/engage.conf 2012-09-25 15:49:24 +0000
@@ -5,5 +5,5 @@
5stop on stopping network5stop on stopping network
6stop on starting shutdown6stop on starting shutdown
77
8exec /usr/bin/twistd --uid nobody --gid nogroup --logfile /var/log/engage.log --nodaemon engage8exec /usr/sbin/engage
9respawn9respawn
1010
=== added directory 'lib-depends'
=== added file 'lib-depends/get-egg-src'
--- lib-depends/get-egg-src 1970-01-01 00:00:00 +0000
+++ lib-depends/get-egg-src 2012-09-25 15:49:24 +0000
@@ -0,0 +1,40 @@
1#!/bin/sh
2#
3# Download twisted from pypi, add our own plugin, and then tar up so
4# it can be built on each target build.
5#
6# When we can just impose twisted >= 12.0 as a dependency and expect a
7# normal ubuntu to be able to honour that, we can trash this whole
8# sorry effort. That includes:
9#
10# - Killing this file, the twisted egg source file, and the zero-byte
11# twisted-engage.egg file in this directory that is present just for
12# dpkg to keep track of.
13#
14# - Killing the engage binary and just running "twistd engage" from
15# etc/init with appropriate options.
16#
17# - Remove python-setuptools from the dependencies in debian/control
18# and add python-twisted-core and python-twisted-web.
19#
20# - Moving engage/util/twisted_plugin.py back to twisted/plugins.
21#
22# - Adding twisted.plugins back to setup.py.
23#
24# Simon C, September 2012
25
26set -e
27src_target=$PWD/twisted-engage-egg-src.tar.bz2
28tmp=/tmp/engage-$$
29plugin_file=twisted/plugins/engage_plugin.py
30plugin_src=$PWD/../src/$plugin_file
31rm -rf $tmp
32mkdir $tmp
33cd $tmp
34pip install --upgrade --no-install Twisted
35cd build/Twisted
36cp $plugin_src $plugin_file
37(cd ..; tar cf - Twisted | bzip2 -9 > $src_target)
38cd /
39rm -rf $tmp
40ls -l $src_target
041
=== added file 'lib-depends/twisted-engage-egg-src.tar.bz2'
1Binary files lib-depends/twisted-engage-egg-src.tar.bz2 1970-01-01 00:00:00 +0000 and lib-depends/twisted-engage-egg-src.tar.bz2 2012-09-25 15:49:24 +0000 differ42Binary files lib-depends/twisted-engage-egg-src.tar.bz2 1970-01-01 00:00:00 +0000 and lib-depends/twisted-engage-egg-src.tar.bz2 2012-09-25 15:49:24 +0000 differ
=== removed file 'restart'
--- restart 2012-09-14 12:24:16 +0000
+++ restart 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1./stop $*
2./start $*
30
=== modified file 'setup.py'
--- setup.py 2012-09-18 21:21:43 +0000
+++ setup.py 2012-09-25 15:49:24 +0000
@@ -7,10 +7,10 @@
7from distutils.core import setup7from distutils.core import setup
88
9setup(name='engage',9setup(name='engage',
10 version="0.6",10 version="0.6.1",
11 description='EnGage: system/network statistics monitoring and Apple TV control',11 description='EnGage: system/network statistics monitoring and Apple TV control',
12 url='http://open.ensoft.co.uk/EnGage',12 url='http://open.ensoft.co.uk/EnGage',
13 packages=['engage', 'engage.collector', 'engage.monitor', 'engage.util', 13 packages=['engage', 'engage.collector', 'engage.media', 'engage.monitor',
14 'engage.ui', 'twisted.plugins'],14 'engage.util', 'engage.ui'],
15 package_dir={'': 'src' },15 package_dir={'': 'src' },
16 )16 )
1717
=== added directory 'src/engage/media'
=== added file 'src/engage/media/__init__.py'
--- src/engage/media/__init__.py 1970-01-01 00:00:00 +0000
+++ src/engage/media/__init__.py 2012-09-25 15:49:24 +0000
@@ -0,0 +1,91 @@
1# Media base class
2#
3# Simon C, September 2012
4
5#
6# Unofficial AirPlay specification:
7#
8# http://nto.github.com/AirPlay.html
9#
10
11from zope.interface import Interface, implements
12from twisted.internet import reactor
13from twisted.plugin import IPlugin, getPlugins
14from twisted.application import service
15from twisted.python import log
16import engage.media
17
18class MediaSource(object):
19 """
20 Base classes for a source of media to be displayed on a carousel
21 """
22
23 cfg_defaults = {}
24
25 def __init__(self, name, cfg, atv, register_cb):
26 """
27 Initialize with the configuration for this media source. Call
28 back register_cb with the number of items this media source
29 can render (and call again whenever this updates).
30 """
31 self.name = '%s (%s)' % (name, self.__class__.__name__)
32 self.cfg = cfg.copy()
33 for k, v in self.cfg_defaults.items():
34 if k not in self.cfg:
35 self.cfg[k] = v
36 log.msg("Starting media source %s with config %s" % (self.name, self.cfg))
37 self.atv = atv
38 self.register_cb = register_cb
39 reactor.callLater(0, self.register_items)
40
41 def register_items(self):
42 """
43 For documentation only in the base class. Work out how many
44 different items we could render, and tell the carousel that.
45 """
46
47 def show(self):
48 """
49 For documentation only in the base class. Show a random media
50 object using the provided http_agent. Return a deferred that
51 fires when done.
52 """
53
54 @classmethod
55 def config_start(cls, cfg, ip_addr, register_cb):
56 """
57 Start all the media sources that are configured
58 """
59 media_sources = {}
60 for media_source in getPlugins(IMediaSource, engage.media):
61 for source in media_source.config_start(cfg, ip_addr, register_cb):
62 media_sources[source] = source.name
63 return media_sources
64
65class IMediaSource(Interface):
66 """
67 Twisted plugin specification for a config-started MediaSource
68 """
69 def config_start(self, cfg, atv, register_cb):
70 """
71 Create a MedaSource if configured
72 """
73
74class StartMediaSource(object):
75 """
76 Twisted plugin for starting a MediaSource
77 """
78 implements(IPlugin, IMediaSource)
79
80 def __init__(self, stanza_name, media_source_cls):
81 self.stanza_name = stanza_name
82 self.media_source_cls = media_source_cls
83
84 def config_start(self, cfg, atv, register_cb):
85 started = []
86 for key in cfg:
87 if key.startswith(self.stanza_name):
88 log.msg('Starting media source %s from config stanza %s' %
89 (self.media_source_cls.__name__, key))
90 started.append(self.media_source_cls(key, cfg[key], atv, register_cb))
91 return started
092
=== added file 'src/engage/media/movie.py'
--- src/engage/media/movie.py 1970-01-01 00:00:00 +0000
+++ src/engage/media/movie.py 2012-09-25 15:49:24 +0000
@@ -0,0 +1,21 @@
1# Static file-drive media source
2#
3# Simon C, September 2012
4
5from twisted.python import log
6from engage.media import MediaSource, StartMediaSource
7
8#
9# @@@ still working out structure here, this is probably all wrong
10#
11
12class MovieMediaSource(MediaSource):
13
14 def register_items(self):
15 self.register_cb(self, 1)
16
17 def show(self):
18 d = self.atv.show_movie('http://172.22.22.143:8421/mp4/amy-janet.mp4')
19 return d
20
21start = StartMediaSource('movie', MovieMediaSource)
022
=== added file 'src/engage/media/static.py'
--- src/engage/media/static.py 1970-01-01 00:00:00 +0000
+++ src/engage/media/static.py 2012-09-25 15:49:24 +0000
@@ -0,0 +1,36 @@
1# Static file-drive media source
2#
3# Simon C, September 2012
4
5import os, random, re
6from twisted.internet import reactor
7from twisted.python import log
8from engage.media import MediaSource, StartMediaSource
9
10class StaticMediaSource(MediaSource):
11
12 cfg_defaults = {
13 'refresh': '20',
14 'weight': '1.0',
15 }
16
17 def register_items(self):
18 self.photos = []
19 for folder, _, files in os.walk(self.cfg['photo_dir'], followlinks=True):
20 for filename in files:
21 if re.search('(?i)\.(jpg|png)', filename):
22 pathname = os.path.join(folder, filename)
23 self.photos.append(pathname)
24 log.msg("%s found %d photos to display with weight %s" %
25 (self.name, len(self.photos), self.cfg['weight']))
26 self.register_cb(self, len(self.photos) * float(self.cfg['weight']))
27 # re-scan every 20 minutes by default
28 reactor.callLater(60 * float(self.cfg['refresh']), self.register_items)
29
30 def show(self):
31 item = random.randint(0, len(self.photos) - 1)
32 pathname = self.photos[item]
33 d = self.atv.show_photo(pathname)
34 return d
35
36start = StartMediaSource('static', StaticMediaSource)
037
=== modified file 'src/engage/monitor/percent_used.py'
--- src/engage/monitor/percent_used.py 2012-09-18 21:21:43 +0000
+++ src/engage/monitor/percent_used.py 2012-09-25 15:49:24 +0000
@@ -32,9 +32,9 @@
3232
33 # Memory33 # Memory
34 with open('/proc/meminfo') as f:34 with open('/proc/meminfo') as f:
35 total, free, buffers = [int(f.readline().split()[1]) 35 total, free, buffers, cached = \
36 for i in xrange(0, 3)]36 [int(f.readline().split()[1]) for i in xrange(0, 4)]
37 mem_used = percent(total - free - buffers, total)37 mem_used = percent(total - free - buffers - cached, total)
38 mem_buff = percent(buffers, total)38 mem_buff = percent(buffers, total)
3939
40 # Disk40 # Disk
4141
=== modified file 'src/engage/ui/carousel.py'
--- src/engage/ui/carousel.py 2012-09-18 21:21:43 +0000
+++ src/engage/ui/carousel.py 2012-09-25 15:49:24 +0000
@@ -6,9 +6,9 @@
6from twisted.application import service6from twisted.application import service
7from twisted.internet import reactor7from twisted.internet import reactor
8from twisted.python import log8from twisted.python import log
9from twisted.web.client import Agent, FileBodyProducer, HTTPConnectionPool
10from twisted.web.http_headers import Headers
11from engage.monitor.traffic import TrafficMonitor9from engage.monitor.traffic import TrafficMonitor
10from engage.media import MediaSource
11from engage.util.atv import ATV
1212
13class Carousel(object):13class Carousel(object):
14 """14 """
@@ -36,13 +36,11 @@
36 self.cfg = carousel_cfg36 self.cfg = carousel_cfg
37 self.period = int(carousel_cfg.get('period', 7))37 self.period = int(carousel_cfg.get('period', 7))
38 self.threshold = int(carousel_cfg.get('threshold', 10000))38 self.threshold = int(carousel_cfg.get('threshold', 10000))
39 self.url = 'http://%s:7000/photo' % carousel_cfg['atv']39 self.atv = ATV(carousel_cfg['atv'])
40 self.agent = Agent(reactor, pool=HTTPConnectionPool(reactor))40 self.total_weight = 0
41 self.headers = Headers({41 self.media_weights = {}
42 'User-Agent': ['MediaControl/1.0'],42 self.media_source_names = MediaSource.config_start(
43 'X-Apple-Transition': ['Dissolve']})43 carousel_cfg, self.atv, self.register_media_source)
44 self.scan_photo_dir()
45 self.photo_idx = 0
4644
47 # Get initial SNMP stats45 # Get initial SNMP stats
48 self.snmp_results = (0, 0, 0) # fallback for failure46 self.snmp_results = (0, 0, 0) # fallback for failure
@@ -54,6 +52,14 @@
54 # Kick off periodic photo display52 # Kick off periodic photo display
55 reactor.callLater(self.period, self.maybe_show_next_photo)53 reactor.callLater(self.period, self.maybe_show_next_photo)
5654
55 def register_media_source(self, media_source, num_items):
56 self.media_weights[media_source] = num_items
57 self.total_weight = 0
58 for weight in self.media_weights.values():
59 self.total_weight += weight
60 log.msg("Registered %d out of %d items from %s" %
61 (num_items, self.total_weight, self.media_source_names[media_source]))
62
57 def maybe_show_next_photo(self):63 def maybe_show_next_photo(self):
58 #64 #
59 # Main workhorse of this class, that is called periodically.65 # Main workhorse of this class, that is called periodically.
@@ -76,8 +82,10 @@
76 log.msg("Carousel failure: %s" % fail)82 log.msg("Carousel failure: %s" % fail)
77 failure.printTraceback()83 failure.printTraceback()
78 d.addErrback(handle_failure)84 d.addErrback(handle_failure)
79 d.addBoth(lambda r: 85 def reset(result):
80 reactor.callLater(self.period, self.maybe_show_next_photo))86 log.msg("FINAL CALLBACK!!!")
87 reactor.callLater(self.period, self.maybe_show_next_photo)
88 d.addBoth(reset)
81 89
82 def check_snmp_results(self, results):90 def check_snmp_results(self, results):
83 #91 #
@@ -93,18 +101,28 @@
93101
94 def conditionally_show_next_photo(self, tx_bps):102 def conditionally_show_next_photo(self, tx_bps):
95 #103 #
96 # If the tx bps is low enough, pick a photo and send it.104 # If the tx bps is low enough, pick a media source at random
97 # Currently very dumb: round robin105 # and ask it to do something.
98 #106 #
99 if tx_bps >= self.threshold:107 if tx_bps >= self.threshold:
100 log.msg("Skipping ATV photo, tx bps = %d" % tx_bps)108 log.msg("Skipping ATV photo, tx bps = %d" % tx_bps)
109 self.atv.drop_connection()
101 else:110 else:
102 pathname = self.photos[self.photo_idx]111 r = random.randint(0, self.total_weight)
103 log.msg("Showing photo %s (tx bps = %d)" % (pathname, tx_bps))112 media_source = None
104 body = FileBodyProducer(file(pathname))113 for possible_source in self.media_source_names:
105 d = self.agent.request('PUT', self.url, self.headers, body)114 media_source = possible_source
106 d.addCallbacks(self.display_done, self.display_failed)115 r -= self.media_weights[possible_source]
107 return d116 if r < 0:
117 break
118 if media_source is None:
119 log.msg("No media source")
120 else:
121 log.msg("Using media source %s" % (self.media_source_names[media_source]))
122 d = media_source.show()
123 d.addCallback(lambda r: "MEDIA SOURCE SHOW CALLBACK!!!")
124 d.addErrback(self.display_failed)
125 return d
108126
109 def get_second_snmp_results(self, _):127 def get_second_snmp_results(self, _):
110 #128 #
@@ -125,35 +143,6 @@
125 log.msg("Display failed: %s" % failure)143 log.msg("Display failed: %s" % failure)
126 failure.printTraceback()144 failure.printTraceback()
127 145
128 def display_done(self, response):
129 #
130 # Something went wrong sending a photo to the ATV - log it
131 #
132 if response.code != 200:
133 log.msg("Bad display response: key %s, code %s, phrase %s" %
134 (response.version, response.code, response.phrase))
135
136 # Either way, pick a new photo at random
137 new_photo_idx = self.photo_idx
138 while new_photo_idx == self.photo_idx:
139 new_photo_idx = random.randint(0, len(self.photos) - 1)
140 self.photo_idx = new_photo_idx
141
142 def scan_photo_dir(self):
143 #
144 # Scan the configured path for any plausible photos. Re-scan
145 # sometimes in case new photos have arrived.
146 #
147 self.photos = []
148 for folder, _, files in os.walk(self.cfg['photo_dir']):
149 for filename in files:
150 if re.search('(?i)\.jpg', filename):
151 pathname = os.path.join(folder, filename)
152 self.photos.append(pathname)
153 log.msg("Found %d photos to display after scan" % len(self.photos))
154 # re-scan every 20 minutes
155 reactor.callLater(60 * 20, self.scan_photo_dir)
156
157class CarouselService(service.Service):146class CarouselService(service.Service):
158 """147 """
159 Twisted Service encapsulating one carousel148 Twisted Service encapsulating one carousel
160149
=== modified file 'src/engage/ui/web.py'
--- src/engage/ui/web.py 2012-09-18 21:21:43 +0000
+++ src/engage/ui/web.py 2012-09-25 15:49:24 +0000
@@ -159,6 +159,8 @@
159 site.putChild("", Main(config))159 site.putChild("", Main(config))
160 site.putChild("graph", Graph(config))160 site.putChild("graph", Graph(config))
161 site.putChild("png", File('%s/png' % web_config['web_static_dir']))161 site.putChild("png", File('%s/png' % web_config['web_static_dir']))
162 site.putChild("mp4", File('%s/mp4' % web_config['web_static_dir']))
163
162164
163 web = Site(site)165 web = Site(site)
164 tcp_service = internet.TCPServer(int(web_config['http_port']), web)166 tcp_service = internet.TCPServer(int(web_config['http_port']), web)
165167
=== added file 'src/engage/util/atv.py'
--- src/engage/util/atv.py 1970-01-01 00:00:00 +0000
+++ src/engage/util/atv.py 2012-09-25 15:49:24 +0000
@@ -0,0 +1,94 @@
1# Apple TV control
2#
3# Simon C, September 2012
4
5import re
6from StringIO import StringIO
7from twisted.internet import defer, reactor
8from twisted.python import log
9from twisted.internet.protocol import Protocol
10from twisted.web.client import Agent, HTTPConnectionPool
11from twisted.web.client import FileBodyProducer
12from twisted.web.http_headers import Headers
13
14POLL_INTERVAL = 1
15
16class ResponseReceiver(Protocol):
17
18 def __init__(self, finished, fire_again):
19 self.finished = finished
20 self.fire_again = fire_again
21 self.remaining = POLL_INTERVAL
22
23 def dataReceived(self, data):
24 position, duration = [float(re.search(key + ': ([\d.]+)', data).group(1))
25 for key in ('position', 'duration')]
26 if duration > 0:
27 remaining = duration - position
28 if remaining < self.remaining:
29 self.remaining = remaining
30
31 log.msg("Video at %f/%f; timer reset to %f" %
32 (position, duration, self.remaining))
33
34 def connectionLost(self, reason):
35 if self.remaining < POLL_INTERVAL:
36 log.msg("Last %f seconds, just waiting" % self.remaining)
37 else:
38 self.finished.addCallback(self.fire_again)
39 reactor.callLater(self.remaining, self.finished.callback, None)
40
41
42class ATV(object):
43
44 HEADERS = Headers({'User-Agent': ['MediaControl/1.0'],
45 'X-Apple-Transition': ['Dissolve']})
46
47 def __init__(self, ip_addr):
48 self.http_agent = None
49 self.base_url = 'http://%s:7000' % ip_addr
50
51 def drop_connection(self):
52 self.http_agent = None
53
54 def _check_connection(self):
55 if self.http_agent is None:
56 self.http_agent = Agent(reactor, pool=HTTPConnectionPool(reactor))
57
58 def show_photo(self, pathname):
59 self._check_connection()
60 body = FileBodyProducer(file(pathname))
61 d = self.http_agent.request('PUT', self.base_url + '/photo',
62 self.HEADERS, body)
63 def display_done(response):
64 if response.code != 200:
65 log.msg("Bad response from photo %s: key %s, code %s, phrase %s" %
66 (pathname, response.version, response.code, response.phrase))
67 d.addCallback(display_done)
68 return d
69
70 def show_movie(self, url):
71 self._check_connection()
72 body_string = 'Content-Location: %s\nStart-Position: 0\n' % url
73 body = FileBodyProducer(StringIO(body_string))
74 url = self.base_url + '/play'
75 d = self.http_agent.request('POST', url, self.HEADERS, body)
76 d.addCallback(self.get_scrub_soon)
77 return d
78
79 def get_scrub_soon(self, result):
80 d = defer.Deferred()
81 d.addCallback(self.get_scrub)
82 reactor.callLater(POLL_INTERVAL, lambda: d.callback(result))
83 return d
84
85 def get_scrub(self, result):
86 url = self.base_url + '/scrub'
87 d = self.http_agent.request('GET', url)
88 d.addCallback(self.display_scrub)
89 return d
90
91 def display_scrub(self, response):
92 d = defer.Deferred()
93 response.deliverBody(ResponseReceiver(d, self.get_scrub))
94 return d
095
=== added file 'src/engage/util/twisted_plugin.py'
--- src/engage/util/twisted_plugin.py 1970-01-01 00:00:00 +0000
+++ src/engage/util/twisted_plugin.py 2012-09-25 15:49:24 +0000
@@ -0,0 +1,65 @@
1# EnGage twisted plugin
2#
3# Simon C, September 2012
4
5from zope.interface import implements
6from twisted.python import usage
7from twisted.plugin import IPlugin
8from twisted.application import service
9
10from engage.collector.rrd import RRDCollector
11from engage.ui import web, carousel
12from engage.util import config
13
14class Options(usage.Options):
15
16 optParameters = [
17 ('conf', 'c', '/etc/engage.conf', 'EnGage configuration file.'),
18 ]
19
20#
21# Built-in configuration. We don't really want the admin messing with the first
22# two; the others are more just handy defaults.
23#
24collector_cfg = {
25 'interval': 10,
26 'rrd_dir': '/var/lib/engage/rrd',
27}
28
29web_cfg = {
30 'web_static_dir': '/usr/share/engage/static',
31 'http_prefix': '',
32 'http_port': '8421',
33 'title': 'EnGage',
34}
35
36class EngageServiceMaker(object):
37
38 implements(service.IServiceMaker, IPlugin)
39
40 tapname = "engage"
41 description = "EnGage system/network monitoring service."
42 options = Options
43
44 def makeService(self, options):
45 top_service = service.MultiService()
46
47 # Merge the user and built-in configuration
48 all_config = config.parse(options['conf'])
49 for k, v in all_config['Main'].items():
50 web_cfg[k] = v
51 collector_cfg[k] = v
52
53 # Start monitors based on configuration, with data collected by RRD
54 RRDCollector.config_start_monitors(collector_cfg,
55 all_config,
56 top_service)
57
58 # Start carousel if configured
59 if 'Carousel' in all_config:
60 carousel.start(all_config, top_service)
61
62 # Start the web server too
63 web.start(web_cfg, top_service)
64
65 return top_service
066
=== modified file 'src/twisted/plugins/engage_plugin.py'
--- src/twisted/plugins/engage_plugin.py 2012-09-18 21:21:43 +0000
+++ src/twisted/plugins/engage_plugin.py 2012-09-25 15:49:24 +0000
@@ -1,67 +1,6 @@
1# EnGage twisted plugin1#
2#2# Crazy file to allow embedding of Twisted as an egg, for stock Ubuntu
3# Simon C, September 20123# distributions with a pre-12.0.0 version
44#
5from zope.interface import implements5from engage.util.twisted_plugin import EngageServiceMaker
6from twisted.python import usage
7from twisted.plugin import IPlugin
8from twisted.application import service
9
10from engage.collector.rrd import RRDCollector
11from engage.ui import web, carousel
12from engage.util import config
13
14class Options(usage.Options):
15
16 optParameters = [
17 ('conf', 'c', '/etc/engage.conf', 'EnGage configuration file.'),
18 ]
19
20#
21# Built-in configuration. We don't really want the admin messing with the first
22# two; the others are more just handy defaults.
23#
24collector_cfg = {
25 'interval': 10,
26 'rrd_dir': '/var/lib/engage/rrd',
27}
28
29web_cfg = {
30 'web_static_dir': '/usr/share/engage/static',
31 'http_prefix': '',
32 'http_port': '8421',
33 'title': 'EnGage',
34}
35
36class EngageServiceMaker(object):
37
38 implements(service.IServiceMaker, IPlugin)
39
40 tapname = "engage"
41 description = "EnGage system/network monitoring service."
42 options = Options
43
44 def makeService(self, options):
45 top_service = service.MultiService()
46
47 # Merge the user and built-in configuration
48 all_config = config.parse(options['conf'])
49 for k, v in all_config['Main'].items():
50 web_cfg[k] = v
51 collector_cfg[k] = v
52
53 # Start monitors based on configuration, with data collected by RRD
54 RRDCollector.config_start_monitors(collector_cfg,
55 all_config,
56 top_service)
57
58 # Start carousel if configured
59 if 'Carousel' in all_config:
60 carousel.start(all_config, top_service)
61
62 # Start the web server too
63 web.start(web_cfg, top_service)
64
65 return top_service
66
67service_maker = EngageServiceMaker()6service_maker = EngageServiceMaker()
687
=== removed file 'start'
--- start 2012-09-14 12:24:16 +0000
+++ start 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
1hostname=$(hostname)
2if [ z$ENGAGE == z ]; then
3 engage=engage
4 instance=$hostname
5else
6 engage=test_engage
7 instance=test_$hostname
8fi
9CFGFILE=$PWD/etc/$instance.conf
10PIDFILE=$PWD/var/run/$engage.pid
11LOGFILE=$PWD/var/log/$instance.log
12
13if [ ! -f $CFGFILE ]; then
14 echo No specific config file for host $hostname, using default
15 CFGFILE=$PWD/etc/engage.conf
16fi
17
18cd src
19twistd $* --pidfile $PIDFILE --logfile $LOGFILE engage -c $CFGFILE
200
=== removed file 'stop'
--- stop 2012-09-14 12:24:16 +0000
+++ stop 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1#!/bin/bash
2
3if [ z$ENGAGE == z ]; then
4 engage=engage
5 rrd_dir=var/rrd
6else
7 engage=test_engage
8 rrd_dir=var/test-rrd
9fi
10PIDFILE=$PWD/var/run/$engage.pid
11
12if [ -f $PIDFILE ]; then
13 cd $rrd_dir
14 # pause for most recent data collection, to avoid holes in data set
15 perl -e '$| = 1; while (time - (stat((glob("*.rrd"))[0]))[9] != 1) { print "."; sleep 1; }; print "\n"'
16 kill $(cat $PIDFILE)
17fi

Subscribers

People subscribed via source and target branches

to all changes: