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
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2012-09-25 15:49:24 +0000
4@@ -0,0 +1,2 @@
5+dropin.cache
6+twisted-engage.egg
7
8=== added directory 'bin'
9=== added file 'bin/engage'
10--- bin/engage 1970-01-01 00:00:00 +0000
11+++ bin/engage 2012-09-25 15:49:24 +0000
12@@ -0,0 +1,28 @@
13+#!/usr/bin/python
14+#
15+# This is just a crazy equivalent of
16+#
17+# /usr/bin/twistd --uid nobody --gid nogroup --logfile /var/log/engage.log --nodaemon engage
18+#
19+# but allowing Twisted to be embedded, for those ubutnu machines with
20+# a stock (pre-12.0) version.
21+#
22+# Happy day when we can ditch this.
23+#
24+import os, platform, sys
25+try:
26+ import _preamble
27+except ImportError:
28+ sys.exc_clear()
29+
30+lib_dir = '/usr/lib/engage/lib-depends'
31+sys.path.insert(0, lib_dir)
32+sys.path.insert(0, os.path.join(lib_dir, 'twisted-engage.egg'))
33+
34+argv_head = sys.argv[0]
35+argv_tail = sys.argv[1:]
36+sys.argv = [argv_head, '--uid', 'nobody', '--gid', 'nogroup', '--logfile',
37+ '/var/log/engage.log', '--nodaemon'] + argv_tail + ['engage']
38+
39+from twisted.scripts.twistd import run
40+run()
41
42=== modified file 'debian/changelog'
43--- debian/changelog 2012-09-17 13:13:06 +0000
44+++ debian/changelog 2012-09-25 15:49:24 +0000
45@@ -1,3 +1,9 @@
46+engage (0.6.1) precise; urgency=low
47+
48+ * Embedded twisted 12 for easy of installing on stock ubuntu
49+
50+ -- Simon C <simonc@ensoft.co.uk> Mon, 24 Sep 2012 22:42:58 +0100
51+
52 engage (0.6) lucid; urgency=low
53
54 * Improved structure and added basic ATV support
55
56=== modified file 'debian/control'
57--- debian/control 2012-09-17 13:13:06 +0000
58+++ debian/control 2012-09-25 15:49:24 +0000
59@@ -8,8 +8,7 @@
60 Package: engage
61 Architecture: all
62 # snmp package is just for snmptranslate
63-Depends: python,python-twisted,python-twisted-web (>= 12.0.0),python-rrdtool,libsnmp-python,python-pynetsnmp,snmp,snmp-mibs-downloader,${misc:Depends}
64+Depends: python (>= ${python:Versions}), python-setuptools, python-zope.interface, python-rrdtool, libsnmp-python, python-pynetsnmp, snmp, snmp-mibs-downloader, ${python:Depends}, ${misc:Depends}
65 Description: Combined system/network statistics and Apple TV control framework
66 EnGage is a plugin-based framework for gathering, summarizing, and displaying
67- system or network administration data. This base package provides basic
68- monitoring of the linux host itself.
69+ system or network administration data.
70
71=== modified file 'debian/engage.install'
72--- debian/engage.install 2012-09-17 13:13:06 +0000
73+++ debian/engage.install 2012-09-25 15:49:24 +0000
74@@ -1,3 +1,6 @@
75 etc/engage.conf etc
76 etc/init/engage.conf etc/init
77 static/ usr/share/engage
78+bin/engage /usr/sbin
79+lib-depends/twisted-engage-egg-src.tar.bz2 /usr/lib/engage/lib-depends
80+lib-depends/twisted-engage.egg /usr/lib/engage/lib-depends
81
82=== added file 'debian/postinst'
83--- debian/postinst 1970-01-01 00:00:00 +0000
84+++ debian/postinst 2012-09-25 15:49:24 +0000
85@@ -0,0 +1,25 @@
86+#!/bin/sh
87+#
88+# Build the embedded Twisted 12.2 package into an egg for the target system
89+# architecture. When it's reasonable to just put Twisted >= 12.0 as a
90+# dependency and expect ubuntu to honour it, we can cheerfully flush this down
91+# the can.
92+#
93+# We include a zero-byte twisted-engage.egg and mark it as a conffile, so it's
94+# ok to overwrite it here with a real egg.
95+#
96+set -e
97+egg_src=/usr/lib/engage/lib-depends/twisted-engage-egg-src.tar.bz2
98+egg=/usr/lib/engage/lib-depends/twisted-engage.egg
99+tmp=/tmp/engage-$$
100+rm -rf $tmp
101+mkdir $tmp
102+cd $tmp
103+tar jxf $egg_src
104+cd Twisted
105+python setup.py bdist_egg
106+mv dist/*.egg $egg
107+cd /tmp
108+rm -rf $tmp
109+
110+#DEBHELPER#
111
112=== modified file 'etc/init/engage.conf'
113--- etc/init/engage.conf 2012-09-18 21:21:43 +0000
114+++ etc/init/engage.conf 2012-09-25 15:49:24 +0000
115@@ -5,5 +5,5 @@
116 stop on stopping network
117 stop on starting shutdown
118
119-exec /usr/bin/twistd --uid nobody --gid nogroup --logfile /var/log/engage.log --nodaemon engage
120+exec /usr/sbin/engage
121 respawn
122
123=== added directory 'lib-depends'
124=== added file 'lib-depends/get-egg-src'
125--- lib-depends/get-egg-src 1970-01-01 00:00:00 +0000
126+++ lib-depends/get-egg-src 2012-09-25 15:49:24 +0000
127@@ -0,0 +1,40 @@
128+#!/bin/sh
129+#
130+# Download twisted from pypi, add our own plugin, and then tar up so
131+# it can be built on each target build.
132+#
133+# When we can just impose twisted >= 12.0 as a dependency and expect a
134+# normal ubuntu to be able to honour that, we can trash this whole
135+# sorry effort. That includes:
136+#
137+# - Killing this file, the twisted egg source file, and the zero-byte
138+# twisted-engage.egg file in this directory that is present just for
139+# dpkg to keep track of.
140+#
141+# - Killing the engage binary and just running "twistd engage" from
142+# etc/init with appropriate options.
143+#
144+# - Remove python-setuptools from the dependencies in debian/control
145+# and add python-twisted-core and python-twisted-web.
146+#
147+# - Moving engage/util/twisted_plugin.py back to twisted/plugins.
148+#
149+# - Adding twisted.plugins back to setup.py.
150+#
151+# Simon C, September 2012
152+
153+set -e
154+src_target=$PWD/twisted-engage-egg-src.tar.bz2
155+tmp=/tmp/engage-$$
156+plugin_file=twisted/plugins/engage_plugin.py
157+plugin_src=$PWD/../src/$plugin_file
158+rm -rf $tmp
159+mkdir $tmp
160+cd $tmp
161+pip install --upgrade --no-install Twisted
162+cd build/Twisted
163+cp $plugin_src $plugin_file
164+(cd ..; tar cf - Twisted | bzip2 -9 > $src_target)
165+cd /
166+rm -rf $tmp
167+ls -l $src_target
168
169=== added file 'lib-depends/twisted-engage-egg-src.tar.bz2'
170Binary 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
171=== removed file 'restart'
172--- restart 2012-09-14 12:24:16 +0000
173+++ restart 1970-01-01 00:00:00 +0000
174@@ -1,2 +0,0 @@
175-./stop $*
176-./start $*
177
178=== modified file 'setup.py'
179--- setup.py 2012-09-18 21:21:43 +0000
180+++ setup.py 2012-09-25 15:49:24 +0000
181@@ -7,10 +7,10 @@
182 from distutils.core import setup
183
184 setup(name='engage',
185- version="0.6",
186+ version="0.6.1",
187 description='EnGage: system/network statistics monitoring and Apple TV control',
188 url='http://open.ensoft.co.uk/EnGage',
189- packages=['engage', 'engage.collector', 'engage.monitor', 'engage.util',
190- 'engage.ui', 'twisted.plugins'],
191+ packages=['engage', 'engage.collector', 'engage.media', 'engage.monitor',
192+ 'engage.util', 'engage.ui'],
193 package_dir={'': 'src' },
194 )
195
196=== added directory 'src/engage/media'
197=== added file 'src/engage/media/__init__.py'
198--- src/engage/media/__init__.py 1970-01-01 00:00:00 +0000
199+++ src/engage/media/__init__.py 2012-09-25 15:49:24 +0000
200@@ -0,0 +1,91 @@
201+# Media base class
202+#
203+# Simon C, September 2012
204+
205+#
206+# Unofficial AirPlay specification:
207+#
208+# http://nto.github.com/AirPlay.html
209+#
210+
211+from zope.interface import Interface, implements
212+from twisted.internet import reactor
213+from twisted.plugin import IPlugin, getPlugins
214+from twisted.application import service
215+from twisted.python import log
216+import engage.media
217+
218+class MediaSource(object):
219+ """
220+ Base classes for a source of media to be displayed on a carousel
221+ """
222+
223+ cfg_defaults = {}
224+
225+ def __init__(self, name, cfg, atv, register_cb):
226+ """
227+ Initialize with the configuration for this media source. Call
228+ back register_cb with the number of items this media source
229+ can render (and call again whenever this updates).
230+ """
231+ self.name = '%s (%s)' % (name, self.__class__.__name__)
232+ self.cfg = cfg.copy()
233+ for k, v in self.cfg_defaults.items():
234+ if k not in self.cfg:
235+ self.cfg[k] = v
236+ log.msg("Starting media source %s with config %s" % (self.name, self.cfg))
237+ self.atv = atv
238+ self.register_cb = register_cb
239+ reactor.callLater(0, self.register_items)
240+
241+ def register_items(self):
242+ """
243+ For documentation only in the base class. Work out how many
244+ different items we could render, and tell the carousel that.
245+ """
246+
247+ def show(self):
248+ """
249+ For documentation only in the base class. Show a random media
250+ object using the provided http_agent. Return a deferred that
251+ fires when done.
252+ """
253+
254+ @classmethod
255+ def config_start(cls, cfg, ip_addr, register_cb):
256+ """
257+ Start all the media sources that are configured
258+ """
259+ media_sources = {}
260+ for media_source in getPlugins(IMediaSource, engage.media):
261+ for source in media_source.config_start(cfg, ip_addr, register_cb):
262+ media_sources[source] = source.name
263+ return media_sources
264+
265+class IMediaSource(Interface):
266+ """
267+ Twisted plugin specification for a config-started MediaSource
268+ """
269+ def config_start(self, cfg, atv, register_cb):
270+ """
271+ Create a MedaSource if configured
272+ """
273+
274+class StartMediaSource(object):
275+ """
276+ Twisted plugin for starting a MediaSource
277+ """
278+ implements(IPlugin, IMediaSource)
279+
280+ def __init__(self, stanza_name, media_source_cls):
281+ self.stanza_name = stanza_name
282+ self.media_source_cls = media_source_cls
283+
284+ def config_start(self, cfg, atv, register_cb):
285+ started = []
286+ for key in cfg:
287+ if key.startswith(self.stanza_name):
288+ log.msg('Starting media source %s from config stanza %s' %
289+ (self.media_source_cls.__name__, key))
290+ started.append(self.media_source_cls(key, cfg[key], atv, register_cb))
291+ return started
292
293=== added file 'src/engage/media/movie.py'
294--- src/engage/media/movie.py 1970-01-01 00:00:00 +0000
295+++ src/engage/media/movie.py 2012-09-25 15:49:24 +0000
296@@ -0,0 +1,21 @@
297+# Static file-drive media source
298+#
299+# Simon C, September 2012
300+
301+from twisted.python import log
302+from engage.media import MediaSource, StartMediaSource
303+
304+#
305+# @@@ still working out structure here, this is probably all wrong
306+#
307+
308+class MovieMediaSource(MediaSource):
309+
310+ def register_items(self):
311+ self.register_cb(self, 1)
312+
313+ def show(self):
314+ d = self.atv.show_movie('http://172.22.22.143:8421/mp4/amy-janet.mp4')
315+ return d
316+
317+start = StartMediaSource('movie', MovieMediaSource)
318
319=== added file 'src/engage/media/static.py'
320--- src/engage/media/static.py 1970-01-01 00:00:00 +0000
321+++ src/engage/media/static.py 2012-09-25 15:49:24 +0000
322@@ -0,0 +1,36 @@
323+# Static file-drive media source
324+#
325+# Simon C, September 2012
326+
327+import os, random, re
328+from twisted.internet import reactor
329+from twisted.python import log
330+from engage.media import MediaSource, StartMediaSource
331+
332+class StaticMediaSource(MediaSource):
333+
334+ cfg_defaults = {
335+ 'refresh': '20',
336+ 'weight': '1.0',
337+ }
338+
339+ def register_items(self):
340+ self.photos = []
341+ for folder, _, files in os.walk(self.cfg['photo_dir'], followlinks=True):
342+ for filename in files:
343+ if re.search('(?i)\.(jpg|png)', filename):
344+ pathname = os.path.join(folder, filename)
345+ self.photos.append(pathname)
346+ log.msg("%s found %d photos to display with weight %s" %
347+ (self.name, len(self.photos), self.cfg['weight']))
348+ self.register_cb(self, len(self.photos) * float(self.cfg['weight']))
349+ # re-scan every 20 minutes by default
350+ reactor.callLater(60 * float(self.cfg['refresh']), self.register_items)
351+
352+ def show(self):
353+ item = random.randint(0, len(self.photos) - 1)
354+ pathname = self.photos[item]
355+ d = self.atv.show_photo(pathname)
356+ return d
357+
358+start = StartMediaSource('static', StaticMediaSource)
359
360=== modified file 'src/engage/monitor/percent_used.py'
361--- src/engage/monitor/percent_used.py 2012-09-18 21:21:43 +0000
362+++ src/engage/monitor/percent_used.py 2012-09-25 15:49:24 +0000
363@@ -32,9 +32,9 @@
364
365 # Memory
366 with open('/proc/meminfo') as f:
367- total, free, buffers = [int(f.readline().split()[1])
368- for i in xrange(0, 3)]
369- mem_used = percent(total - free - buffers, total)
370+ total, free, buffers, cached = \
371+ [int(f.readline().split()[1]) for i in xrange(0, 4)]
372+ mem_used = percent(total - free - buffers - cached, total)
373 mem_buff = percent(buffers, total)
374
375 # Disk
376
377=== modified file 'src/engage/ui/carousel.py'
378--- src/engage/ui/carousel.py 2012-09-18 21:21:43 +0000
379+++ src/engage/ui/carousel.py 2012-09-25 15:49:24 +0000
380@@ -6,9 +6,9 @@
381 from twisted.application import service
382 from twisted.internet import reactor
383 from twisted.python import log
384-from twisted.web.client import Agent, FileBodyProducer, HTTPConnectionPool
385-from twisted.web.http_headers import Headers
386 from engage.monitor.traffic import TrafficMonitor
387+from engage.media import MediaSource
388+from engage.util.atv import ATV
389
390 class Carousel(object):
391 """
392@@ -36,13 +36,11 @@
393 self.cfg = carousel_cfg
394 self.period = int(carousel_cfg.get('period', 7))
395 self.threshold = int(carousel_cfg.get('threshold', 10000))
396- self.url = 'http://%s:7000/photo' % carousel_cfg['atv']
397- self.agent = Agent(reactor, pool=HTTPConnectionPool(reactor))
398- self.headers = Headers({
399- 'User-Agent': ['MediaControl/1.0'],
400- 'X-Apple-Transition': ['Dissolve']})
401- self.scan_photo_dir()
402- self.photo_idx = 0
403+ self.atv = ATV(carousel_cfg['atv'])
404+ self.total_weight = 0
405+ self.media_weights = {}
406+ self.media_source_names = MediaSource.config_start(
407+ carousel_cfg, self.atv, self.register_media_source)
408
409 # Get initial SNMP stats
410 self.snmp_results = (0, 0, 0) # fallback for failure
411@@ -54,6 +52,14 @@
412 # Kick off periodic photo display
413 reactor.callLater(self.period, self.maybe_show_next_photo)
414
415+ def register_media_source(self, media_source, num_items):
416+ self.media_weights[media_source] = num_items
417+ self.total_weight = 0
418+ for weight in self.media_weights.values():
419+ self.total_weight += weight
420+ log.msg("Registered %d out of %d items from %s" %
421+ (num_items, self.total_weight, self.media_source_names[media_source]))
422+
423 def maybe_show_next_photo(self):
424 #
425 # Main workhorse of this class, that is called periodically.
426@@ -76,8 +82,10 @@
427 log.msg("Carousel failure: %s" % fail)
428 failure.printTraceback()
429 d.addErrback(handle_failure)
430- d.addBoth(lambda r:
431- reactor.callLater(self.period, self.maybe_show_next_photo))
432+ def reset(result):
433+ log.msg("FINAL CALLBACK!!!")
434+ reactor.callLater(self.period, self.maybe_show_next_photo)
435+ d.addBoth(reset)
436
437 def check_snmp_results(self, results):
438 #
439@@ -93,18 +101,28 @@
440
441 def conditionally_show_next_photo(self, tx_bps):
442 #
443- # If the tx bps is low enough, pick a photo and send it.
444- # Currently very dumb: round robin
445+ # If the tx bps is low enough, pick a media source at random
446+ # and ask it to do something.
447 #
448 if tx_bps >= self.threshold:
449 log.msg("Skipping ATV photo, tx bps = %d" % tx_bps)
450+ self.atv.drop_connection()
451 else:
452- pathname = self.photos[self.photo_idx]
453- log.msg("Showing photo %s (tx bps = %d)" % (pathname, tx_bps))
454- body = FileBodyProducer(file(pathname))
455- d = self.agent.request('PUT', self.url, self.headers, body)
456- d.addCallbacks(self.display_done, self.display_failed)
457- return d
458+ r = random.randint(0, self.total_weight)
459+ media_source = None
460+ for possible_source in self.media_source_names:
461+ media_source = possible_source
462+ r -= self.media_weights[possible_source]
463+ if r < 0:
464+ break
465+ if media_source is None:
466+ log.msg("No media source")
467+ else:
468+ log.msg("Using media source %s" % (self.media_source_names[media_source]))
469+ d = media_source.show()
470+ d.addCallback(lambda r: "MEDIA SOURCE SHOW CALLBACK!!!")
471+ d.addErrback(self.display_failed)
472+ return d
473
474 def get_second_snmp_results(self, _):
475 #
476@@ -125,35 +143,6 @@
477 log.msg("Display failed: %s" % failure)
478 failure.printTraceback()
479
480- def display_done(self, response):
481- #
482- # Something went wrong sending a photo to the ATV - log it
483- #
484- if response.code != 200:
485- log.msg("Bad display response: key %s, code %s, phrase %s" %
486- (response.version, response.code, response.phrase))
487-
488- # Either way, pick a new photo at random
489- new_photo_idx = self.photo_idx
490- while new_photo_idx == self.photo_idx:
491- new_photo_idx = random.randint(0, len(self.photos) - 1)
492- self.photo_idx = new_photo_idx
493-
494- def scan_photo_dir(self):
495- #
496- # Scan the configured path for any plausible photos. Re-scan
497- # sometimes in case new photos have arrived.
498- #
499- self.photos = []
500- for folder, _, files in os.walk(self.cfg['photo_dir']):
501- for filename in files:
502- if re.search('(?i)\.jpg', filename):
503- pathname = os.path.join(folder, filename)
504- self.photos.append(pathname)
505- log.msg("Found %d photos to display after scan" % len(self.photos))
506- # re-scan every 20 minutes
507- reactor.callLater(60 * 20, self.scan_photo_dir)
508-
509 class CarouselService(service.Service):
510 """
511 Twisted Service encapsulating one carousel
512
513=== modified file 'src/engage/ui/web.py'
514--- src/engage/ui/web.py 2012-09-18 21:21:43 +0000
515+++ src/engage/ui/web.py 2012-09-25 15:49:24 +0000
516@@ -159,6 +159,8 @@
517 site.putChild("", Main(config))
518 site.putChild("graph", Graph(config))
519 site.putChild("png", File('%s/png' % web_config['web_static_dir']))
520+ site.putChild("mp4", File('%s/mp4' % web_config['web_static_dir']))
521+
522
523 web = Site(site)
524 tcp_service = internet.TCPServer(int(web_config['http_port']), web)
525
526=== added file 'src/engage/util/atv.py'
527--- src/engage/util/atv.py 1970-01-01 00:00:00 +0000
528+++ src/engage/util/atv.py 2012-09-25 15:49:24 +0000
529@@ -0,0 +1,94 @@
530+# Apple TV control
531+#
532+# Simon C, September 2012
533+
534+import re
535+from StringIO import StringIO
536+from twisted.internet import defer, reactor
537+from twisted.python import log
538+from twisted.internet.protocol import Protocol
539+from twisted.web.client import Agent, HTTPConnectionPool
540+from twisted.web.client import FileBodyProducer
541+from twisted.web.http_headers import Headers
542+
543+POLL_INTERVAL = 1
544+
545+class ResponseReceiver(Protocol):
546+
547+ def __init__(self, finished, fire_again):
548+ self.finished = finished
549+ self.fire_again = fire_again
550+ self.remaining = POLL_INTERVAL
551+
552+ def dataReceived(self, data):
553+ position, duration = [float(re.search(key + ': ([\d.]+)', data).group(1))
554+ for key in ('position', 'duration')]
555+ if duration > 0:
556+ remaining = duration - position
557+ if remaining < self.remaining:
558+ self.remaining = remaining
559+
560+ log.msg("Video at %f/%f; timer reset to %f" %
561+ (position, duration, self.remaining))
562+
563+ def connectionLost(self, reason):
564+ if self.remaining < POLL_INTERVAL:
565+ log.msg("Last %f seconds, just waiting" % self.remaining)
566+ else:
567+ self.finished.addCallback(self.fire_again)
568+ reactor.callLater(self.remaining, self.finished.callback, None)
569+
570+
571+class ATV(object):
572+
573+ HEADERS = Headers({'User-Agent': ['MediaControl/1.0'],
574+ 'X-Apple-Transition': ['Dissolve']})
575+
576+ def __init__(self, ip_addr):
577+ self.http_agent = None
578+ self.base_url = 'http://%s:7000' % ip_addr
579+
580+ def drop_connection(self):
581+ self.http_agent = None
582+
583+ def _check_connection(self):
584+ if self.http_agent is None:
585+ self.http_agent = Agent(reactor, pool=HTTPConnectionPool(reactor))
586+
587+ def show_photo(self, pathname):
588+ self._check_connection()
589+ body = FileBodyProducer(file(pathname))
590+ d = self.http_agent.request('PUT', self.base_url + '/photo',
591+ self.HEADERS, body)
592+ def display_done(response):
593+ if response.code != 200:
594+ log.msg("Bad response from photo %s: key %s, code %s, phrase %s" %
595+ (pathname, response.version, response.code, response.phrase))
596+ d.addCallback(display_done)
597+ return d
598+
599+ def show_movie(self, url):
600+ self._check_connection()
601+ body_string = 'Content-Location: %s\nStart-Position: 0\n' % url
602+ body = FileBodyProducer(StringIO(body_string))
603+ url = self.base_url + '/play'
604+ d = self.http_agent.request('POST', url, self.HEADERS, body)
605+ d.addCallback(self.get_scrub_soon)
606+ return d
607+
608+ def get_scrub_soon(self, result):
609+ d = defer.Deferred()
610+ d.addCallback(self.get_scrub)
611+ reactor.callLater(POLL_INTERVAL, lambda: d.callback(result))
612+ return d
613+
614+ def get_scrub(self, result):
615+ url = self.base_url + '/scrub'
616+ d = self.http_agent.request('GET', url)
617+ d.addCallback(self.display_scrub)
618+ return d
619+
620+ def display_scrub(self, response):
621+ d = defer.Deferred()
622+ response.deliverBody(ResponseReceiver(d, self.get_scrub))
623+ return d
624
625=== added file 'src/engage/util/twisted_plugin.py'
626--- src/engage/util/twisted_plugin.py 1970-01-01 00:00:00 +0000
627+++ src/engage/util/twisted_plugin.py 2012-09-25 15:49:24 +0000
628@@ -0,0 +1,65 @@
629+# EnGage twisted plugin
630+#
631+# Simon C, September 2012
632+
633+from zope.interface import implements
634+from twisted.python import usage
635+from twisted.plugin import IPlugin
636+from twisted.application import service
637+
638+from engage.collector.rrd import RRDCollector
639+from engage.ui import web, carousel
640+from engage.util import config
641+
642+class Options(usage.Options):
643+
644+ optParameters = [
645+ ('conf', 'c', '/etc/engage.conf', 'EnGage configuration file.'),
646+ ]
647+
648+#
649+# Built-in configuration. We don't really want the admin messing with the first
650+# two; the others are more just handy defaults.
651+#
652+collector_cfg = {
653+ 'interval': 10,
654+ 'rrd_dir': '/var/lib/engage/rrd',
655+}
656+
657+web_cfg = {
658+ 'web_static_dir': '/usr/share/engage/static',
659+ 'http_prefix': '',
660+ 'http_port': '8421',
661+ 'title': 'EnGage',
662+}
663+
664+class EngageServiceMaker(object):
665+
666+ implements(service.IServiceMaker, IPlugin)
667+
668+ tapname = "engage"
669+ description = "EnGage system/network monitoring service."
670+ options = Options
671+
672+ def makeService(self, options):
673+ top_service = service.MultiService()
674+
675+ # Merge the user and built-in configuration
676+ all_config = config.parse(options['conf'])
677+ for k, v in all_config['Main'].items():
678+ web_cfg[k] = v
679+ collector_cfg[k] = v
680+
681+ # Start monitors based on configuration, with data collected by RRD
682+ RRDCollector.config_start_monitors(collector_cfg,
683+ all_config,
684+ top_service)
685+
686+ # Start carousel if configured
687+ if 'Carousel' in all_config:
688+ carousel.start(all_config, top_service)
689+
690+ # Start the web server too
691+ web.start(web_cfg, top_service)
692+
693+ return top_service
694
695=== modified file 'src/twisted/plugins/engage_plugin.py'
696--- src/twisted/plugins/engage_plugin.py 2012-09-18 21:21:43 +0000
697+++ src/twisted/plugins/engage_plugin.py 2012-09-25 15:49:24 +0000
698@@ -1,67 +1,6 @@
699-# EnGage twisted plugin
700-#
701-# Simon C, September 2012
702-
703-from zope.interface import implements
704-from twisted.python import usage
705-from twisted.plugin import IPlugin
706-from twisted.application import service
707-
708-from engage.collector.rrd import RRDCollector
709-from engage.ui import web, carousel
710-from engage.util import config
711-
712-class Options(usage.Options):
713-
714- optParameters = [
715- ('conf', 'c', '/etc/engage.conf', 'EnGage configuration file.'),
716- ]
717-
718-#
719-# Built-in configuration. We don't really want the admin messing with the first
720-# two; the others are more just handy defaults.
721-#
722-collector_cfg = {
723- 'interval': 10,
724- 'rrd_dir': '/var/lib/engage/rrd',
725-}
726-
727-web_cfg = {
728- 'web_static_dir': '/usr/share/engage/static',
729- 'http_prefix': '',
730- 'http_port': '8421',
731- 'title': 'EnGage',
732-}
733-
734-class EngageServiceMaker(object):
735-
736- implements(service.IServiceMaker, IPlugin)
737-
738- tapname = "engage"
739- description = "EnGage system/network monitoring service."
740- options = Options
741-
742- def makeService(self, options):
743- top_service = service.MultiService()
744-
745- # Merge the user and built-in configuration
746- all_config = config.parse(options['conf'])
747- for k, v in all_config['Main'].items():
748- web_cfg[k] = v
749- collector_cfg[k] = v
750-
751- # Start monitors based on configuration, with data collected by RRD
752- RRDCollector.config_start_monitors(collector_cfg,
753- all_config,
754- top_service)
755-
756- # Start carousel if configured
757- if 'Carousel' in all_config:
758- carousel.start(all_config, top_service)
759-
760- # Start the web server too
761- web.start(web_cfg, top_service)
762-
763- return top_service
764-
765+#
766+# Crazy file to allow embedding of Twisted as an egg, for stock Ubuntu
767+# distributions with a pre-12.0.0 version
768+#
769+from engage.util.twisted_plugin import EngageServiceMaker
770 service_maker = EngageServiceMaker()
771
772=== removed file 'start'
773--- start 2012-09-14 12:24:16 +0000
774+++ start 1970-01-01 00:00:00 +0000
775@@ -1,19 +0,0 @@
776-hostname=$(hostname)
777-if [ z$ENGAGE == z ]; then
778- engage=engage
779- instance=$hostname
780-else
781- engage=test_engage
782- instance=test_$hostname
783-fi
784-CFGFILE=$PWD/etc/$instance.conf
785-PIDFILE=$PWD/var/run/$engage.pid
786-LOGFILE=$PWD/var/log/$instance.log
787-
788-if [ ! -f $CFGFILE ]; then
789- echo No specific config file for host $hostname, using default
790- CFGFILE=$PWD/etc/engage.conf
791-fi
792-
793-cd src
794-twistd $* --pidfile $PIDFILE --logfile $LOGFILE engage -c $CFGFILE
795
796=== removed file 'stop'
797--- stop 2012-09-14 12:24:16 +0000
798+++ stop 1970-01-01 00:00:00 +0000
799@@ -1,17 +0,0 @@
800-#!/bin/bash
801-
802-if [ z$ENGAGE == z ]; then
803- engage=engage
804- rrd_dir=var/rrd
805-else
806- engage=test_engage
807- rrd_dir=var/test-rrd
808-fi
809-PIDFILE=$PWD/var/run/$engage.pid
810-
811-if [ -f $PIDFILE ]; then
812- cd $rrd_dir
813- # pause for most recent data collection, to avoid holes in data set
814- perl -e '$| = 1; while (time - (stat((glob("*.rrd"))[0]))[9] != 1) { print "."; sleep 1; }; print "\n"'
815- kill $(cat $PIDFILE)
816-fi

Subscribers

People subscribed via source and target branches

to all changes: