pedro - r7234 - in flumotion/branches/ticket-2058: .
flumotion/monitor/nagios
flumotion-commit at lists.fluendo.com
flumotion-commit at lists.fluendo.com
Tue Aug 12 23:27:07 CEST 2008
Author: pedro
Date: Tue Aug 12 23:27:06 2008
New Revision: 7234
Log:
* flumotion/monitor/nagios/stream.py:
Playlist detected with gstreamer too.
Better nagios error messages.
General code clean.
Modified:
flumotion/branches/ticket-2058/ChangeLog
flumotion/branches/ticket-2058/flumotion/monitor/nagios/stream.py
Modified: flumotion/branches/ticket-2058/ChangeLog
==============================================================================
--- flumotion/branches/ticket-2058/ChangeLog (original)
+++ flumotion/branches/ticket-2058/ChangeLog Tue Aug 12 23:27:06 2008
@@ -1,6 +1,13 @@
2008-08-12 Pedro Gracia <pedro at flumotion.com>
* flumotion/monitor/nagios/stream.py:
+ Playlist detected with gstreamer too.
+ Better nagios error messages.
+ General code clean.
+
+2008-08-12 Pedro Gracia <pedro at flumotion.com>
+
+ * flumotion/monitor/nagios/stream.py:
Nagios critical messages in one line.
Simple playlist autodetect.
Modified: flumotion/branches/ticket-2058/flumotion/monitor/nagios/stream.py
==============================================================================
--- flumotion/branches/ticket-2058/flumotion/monitor/nagios/stream.py (original)
+++ flumotion/branches/ticket-2058/flumotion/monitor/nagios/stream.py Tue Aug 12 23:27:06 2008
@@ -36,37 +36,38 @@
"""Main class to perform the stream checks"""
description = 'Check stream.'
usage = 'check [options] url'
- # variables to check streams and a counter to track the playing state
def __init__(self, parentCommand=None, **kwargs):
"""Initial values and pipeline setup"""
- self._hasaudio = False
- self._hasvideo = False
- self._isaudio = False
- self._isvideo = False
- self.options = None
- self.url = ""
+ self._hasAudio = False
+ self._hasVideo = False
+ self._isAudio = False
+ self._isVideo = False
+ self._isPlaylist = True
+ self._options = None
+ self._url = ''
util.LogCommand.__init__(self, parentCommand, **kwargs)
def addOptions(self):
"""Command line options"""
# video options
- self.parser.add_option('-w', '--videowidth',
+ self.parser.add_option('-W', '--videowidth',
action="store", dest="videowidth",
help='Video width')
self.parser.add_option('-H', '--videoheight',
action="store", dest="videoheight",
help='Video height')
- self.parser.add_option('-f', '--videoframerate',
+ self.parser.add_option('-F', '--videoframerate',
action="store", dest="videoframerate",
help='Video framerate (fraction)')
- self.parser.add_option('-p', '--videopixelratio',
+ self.parser.add_option('-P', '--videopixelratio',
action="store", dest="videopixelratio",
help='Video pixel aspect ratio (fraction)')
- self.parser.add_option('-m', '--videomimetype',
+ self.parser.add_option('-M', '--videomimetype',
action="store", dest="videomimetype",
help='Video mimetype')
+
# audio options
self.parser.add_option('-s', '--audiosamplerate',
action="store", dest="audiosamplerate",
@@ -74,205 +75,233 @@
self.parser.add_option('-c', '--audiochannels',
action="store", dest="audiochannels",
help='Audio channels')
- self.parser.add_option('-W', '--audiowidth',
+ self.parser.add_option('-w', '--audiowidth',
action="store", dest="audiowidth",
help='Audio width')
- self.parser.add_option('-D', '--audiodepth',
+ self.parser.add_option('-d', '--audiodepth',
action="store", dest="audiodepth",
help='Audio depth')
- self.parser.add_option('-M', '--audiomimetype',
+ self.parser.add_option('-m', '--audiomimetype',
action="store", dest="audiomimetype",
help='Audio mimetype')
+
# general option
- self.parser.add_option('-d', '--duration',
+ self.parser.add_option('-D', '--duration',
action="store", dest="duration", default='5',
help='Check duration (default 5 seconds)')
self.parser.add_option('-t', '--timeout',
action="store", dest="timeout", default='10',
help='Number of tries (default 10 try)')
- self.parser.add_option('-P', '--playlist',
+ self.parser.add_option('-p', '--playlist',
action="store_true", dest="playlist",
help='is a playlist')
def handleOptions(self, options):
- """Determine if this stream would have audio/video"""
+ #Determine if this stream would have audio/video
self.options = options
if options.videowidth or options.videoheight or \
options.videoframerate or options.videopixelratio or \
options.videomimetype:
- self._hasvideo = True
+ self._hasVideo = True
if options.audiosamplerate or options.audiochannels or \
options.audiowidth or options.audiodepth or \
options.audiomimetype:
- self._hasaudio = True
+ self._hasAudio = True
+
+ def critical(self, message):
+ return util.critical('%s: %s' % (self._url, message))
+
+ def ok(self, message):
+ return util.ok('%s: %s' % (self._url, message))
def do(self, args):
"""Check URL and perfom the stream check"""
if not args:
- return util.critical('Please specify the url to check the '\
- 'stream/playlist.\n.')
+ return self.critical('Please specify the url to check the '\
+ 'stream/playlist.\n.')
- self.url = args[0]
+ self._url = args[0]
# check url and get path from it
try:
- parse = urlparse.urlparse(self.url)
+ parse = urlparse.urlparse(self._url)
except ValueError:
- return util.critical("URL isn't valid.\n")
+ return self.critical("URL isn't valid.\n")
if parse[0] != 'http':
- return util.critical('URL type is not valid.\n')
+ return self.critical('URL type is not valid.\n')
# Simple playlist detection.
- # TODO: detect it using the mime-type with gstreamer
- if self.url.endswith('.m3u') or self.url.endswith('.asx'):
+ if self._url.endswith('.m3u') or self._url.endswith('.asx'):
self.options.playlist = True
if self.options.playlist:
- playlist = urllib.urlopen(self.url).read()
- if playlist.startswith('404'):
- return util.critical(playlist)
- urls = re.findall(URLFINDER, playlist)
- self.url = urls[-1] # new (the last) url to check
+ self._url = self.getURLFromPlaylist(self._url)
result = self.checkStream()
return result
+ def getURLFromPlaylist(self, url):
+ playlist = urllib.urlopen(self._url).read()
+ if playlist.startswith('404'):
+ return util.critical(playlist)
+ urls = re.findall(URLFINDER, playlist)
+ return urls[-1] # new (the last) url to check
+
def checkStream(self):
"""Launch the pipeline and compare values with the expecteds"""
- gstinfo = GSTInfo(int(self.options.timeout),
- int(self.options.duration), self.url)
- elements = gstinfo.getElements()
+ while self._isPlaylist: #use gstreamer to detect the playlist
+ gstinfo = GSTInfo(int(self.options.timeout),
+ int(self.options.duration), self._url)
+ elements = gstinfo.getElements()
+ self._isPlaylist = gstinfo.isPlaylist()
+ if self._isPlaylist:
+ self._url = self.getURLFromPlaylist(self._url) #replace the url
for i in elements['typefinder'].src_pads():
caps = i.get_caps()
if caps == 'ANY' or not caps:
if gstinfo.getError():
- return util.critical(gstinfo.getError())
+ return self.critical(gstinfo.getError())
else:
- return util.critical("There aren't caps in this stream.")
+ return self.critical("There aren't caps in this stream.")
for i in elements['decoder'].src_pads():
caps = i.get_caps()
mime = caps.to_string().split(', ')[0]
# tests for audio
if 'audio' in caps.to_string():
- self._isaudio = True
+ self._isAudio = True
if self.options.audiomimetype:
if self.options.audiomimetype != mime:
- return util.critical('Audio mime type fail: '\
- '%s [%s]' % (mime, self.url))
+ return self.critical('Audio mime type fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.audiomimetype, mime))
if self.options.audiosamplerate:
if int(self.options.audiosamplerate) != caps[0]['rate']:
- return util.critical('Audio sample rate fail: '\
- '%s [%s]' % (caps[0]['rate'], self.url))
+ return self.critical('Audio sample rate fail: '\
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.audiosamplerate, caps[0]['rate']))
if self.options.audiowidth:
if int(self.options.audiowidth) != caps[0]['width']:
- return util.critical('Audio width fail: %s [%s]' % \
- (caps[0]['width'], self.url))
+ return self.critical('Audio width fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.audiowidth, caps[0]['width']))
if self.options.audiodepth:
if int(self.options.audiodepth) != caps[0]['depth']:
- return util.critical('Audio depth fail: %s [%s]' % \
- (caps[0]['depth'], self.url))
+ return self.critical('Audio depth fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.audiodepth, caps[0]['depth']))
if self.options.audiochannels:
if int(self.options.audiochannels) != caps[0]['channels']:
- return util.critical('Audio channels fail: %s [%s]' % \
- (caps[0]['channels'], self.url))
+ return self.critical('Audio channels fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.audiochannels, caps[0]['channels']))
# tests for video
if 'video' in caps.to_string():
- self._isvideo = True
+ self._isVideo = True
if self.options.videomimetype:
if self.options.videomimetype != mime:
- return util.critical('Video mime type fail: '\
- '%s [%s]' % (mime, self.url))
+ return self.critical('Video mime type fail: '\
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.videomimetype, mime))
if self.options.videowidth:
if int(self.options.videowidth) != caps[0]['width']:
- return util.critical('Video width fail: %s [%s]' % \
- (caps[0]['width'], self.url))
+ return self.critical('Video width fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.videowidth, caps[0]['width']))
if self.options.videoheight:
if int(self.options.videoheight) != caps[0]['height']:
- return util.critical('Video height fail: %s [%s]' % \
- (caps[0]['height'], self.url))
+ return self.critical('Video height fail: ' \
+ 'EXPECTED: %s, ACTUAL: %s' % \
+ (self.options.videoheight, caps[0]['height']))
if self.options.videoframerate:
value = caps[0]['framerate']
if self.options.videoframerate != '%i/%i' % \
(value.num, value.denom):
- return util.critical('Video framerate fail: ' % \
- 'i/%i [%s]' % (value.num, value.denom,
- self.url))
+ return self.critical('Video framerate fail: ' \
+ 'EXPECTED: %s, ACTUAL: %i/%i' % \
+ (self.options.videoframerate, value.num, value.denom))
if self.options.videopixelratio:
value = caps[0]['pixel-aspect-ratio']
- if self.options.videopixelratio != "%i/%i [%s]" % \
+ if self.options.videopixelratio != '%i/%i' % \
(value.num, value.denom):
- return util.critical('Video height fail: ' \
- '%i/%i [%s]' % (value.num, value.denom,
- self.url))
+ return self.critical('Video height fail: ' \
+ 'EXPECTED: %s, ACTUAL: %i/%i' % \
+ (self.options.videopixelratio, value.num, value.denom))
# is audio and/or video missing?
- if self._hasvideo != self._isvideo or \
- self._hasaudio != self._isaudio:
- return util.critical('Stream type mismatch. Please add ' \
- 'more parameters to check [%s].' % self.url)
+ if self._hasVideo != self._isVideo or \
+ self._hasAudio != self._isAudio:
+ return self.critical('Stream type mismatch. Please add ' \
+ 'more parameters to check.')
gstinfo.setState(gst.STATE_NULL)
- return util.ok('Done! [%s]' % self.url)
+ return self.ok('Done!')
class GSTInfo:
"""Get info from stream using a gstreamer pipeline"""
def __init__(self, timeout, duration, url):
- self.mainloop = gobject.MainLoop()
- self._counter = 0
+ self._mainloop = gobject.MainLoop()
+ self._counter = 0 # counter to track the playing state
_pipeline = 'souphttpsrc name=httpsrc ! ' \
'typefind name=typefinder ! ' \
'decodebin name=decoder ! ' \
'fakesink'
- self.duration = duration
- self.url = url
- self.error = ''
- self.pipeline = gst.parse_launch(_pipeline)
- self.elements = dict((e.get_name(), e) for e in \
- self.pipeline.elements())
- bus = self.pipeline.get_bus()
+ self._duration = duration
+ self._url = url
+ self._error = ''
+ self._playlist = False
+ self._pipeline = gst.parse_launch(_pipeline)
+ self._elements = dict((e.get_name(), e) for e in \
+ self._pipeline.elements())
+ bus = self._pipeline.get_bus()
bus.add_watch(self.busWatch)
- self.elements['httpsrc'].set_property('location', url)
- self.pipeline.set_state(gst.STATE_PLAYING)
+ self._elements['httpsrc'].set_property('location', url)
+ self._pipeline.set_state(gst.STATE_PLAYING)
gobject.timeout_add(timeout * 1000, self.endTimeout)
- self.mainloop.run()
+ self._mainloop.run()
def setState(self, state):
- self.pipeline.set_state(state)
+ self._pipeline.set_state(state)
def getElements(self):
- return self.elements
+ return self._elements
def getError(self):
- return self.error
+ return self._error
+
+ def isPlaylist(self):
+ return self._playlist
def busWatch(self, bus, message):
"""Capture messages in pipeline bus"""
- typus = message.type
- if typus == gst.MESSAGE_EOS:
- self.pipeline.set_state(gst.STATE_NULL)
- elif typus == gst.MESSAGE_ERROR:
- self.error = message.parse_error()[0].message
- self.pipeline.set_state(gst.STATE_NULL)
- self.mainloop.quit()
- elif typus == gst.MESSAGE_STATE_CHANGED:
+ if message.type == gst.MESSAGE_EOS:
+ self._pipeline.set_state(gst.STATE_NULL)
+ elif message.type == gst.MESSAGE_ERROR:
+ self._error = message.parse_error()[0].message
+ self._pipeline.set_state(gst.STATE_NULL)
+ code = message.parse_error()[0].code
+ if code == gst.STREAM_ERROR_CODEC_NOT_FOUND:
+ if 'text/uri-list' in message.parse_error()[0].message:
+ self._playlist = True
+ self._mainloop.quit()
+ elif message.type == gst.MESSAGE_STATE_CHANGED:
new = message.parse_state_changed()[1]
- if message.src == self.pipeline and new == gst.STATE_PLAYING:
+ if message.src == self._pipeline and new == gst.STATE_PLAYING:
gobject.timeout_add(1000, self.isPlaying)
return True
def isPlaying(self):
"""Check the stream is playing every second"""
- ret, cur, pen = self.pipeline.get_state()
+ ret, cur, pen = self._pipeline.get_state()
if ret == gst.STATE_CHANGE_SUCCESS and cur == gst.STATE_PLAYING:
self._counter += 1
- if self._counter == self.duration:
- self.mainloop.quit()
+ if self._counter == self._duration:
+ self._mainloop.quit()
return True
def endTimeout(self):
"""End of time to do the check"""
- self.mainloop.quit()
+ self._mainloop.quit()
More information about the flumotion-commit
mailing list