Package flumotion :: Package admin :: Package rrdmon :: Module rrdmon
[hide private]

Source Code for Module flumotion.admin.rrdmon.rrdmon

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """RRD resource poller daemon for Flumotion. 
 23   
 24  Makes periodic observations on components' UI states, recording them to 
 25  RRD files. One can then extract graphs using rrdtool graph. For example, 
 26  to show a stream bandwidth graph for the last 30 minutes with the 
 27  example configuration file, in the source tree as 
 28  conf/rrdmon/default.xml, the following command makes a graph: 
 29   
 30    rrdtool graph --end now --start end-30min --width 400 out.png \ 
 31       DEF:ds0=/tmp/stream-bitrate.rrd:http-streamer:AVERAGE \ 
 32       AREA:ds0#0000FF:"Stream bandwidth (bytes/sec)" 
 33   
 34  It would be possible to expose these graphs via HTTP, but I don't know 
 35  how useful this might be. 
 36   
 37  See L{flumotion.admin.rrdmon.config} for information on how to configure 
 38  the RRD resource poller. 
 39  """ 
 40   
 41  import os 
 42  import random 
 43  import rrdtool 
 44  import datetime 
 45  import time 
 46   
 47  from flumotion.admin import multi 
 48  from flumotion.common import log, common 
 49  from flumotion.common import eventcalendar 
 50  from flumotion.component.base import scheduler 
 51   
 52  # register the unjellyable 
 53  from flumotion.common import componentui 
 54   
 55  componentui # pyflakes 
 56   
 57  __version__ = "$Rev: 7587 $" 
 58   
 59   
60 -def sourceGetFileName(source):
61 return source['rrd-file']
62 63
64 -def sourceGetName(source):
65 return source['name']
66 67
68 -def sourceGetSampleFrequency(source):
69 return source['sample-frequency']
70 71
72 -def sourceGetDS(source):
73 74 def makeDS(): 75 if source['is-gauge']: 76 return 'DS:%s:GAUGE:%d:U:U' % (source['name'], 77 2*source['sample-frequency']) 78 else: 79 return 'DS:%s:DERIVE:%d:0:U' % (source['name'], 80 2*source['sample-frequency'])
81 return source['rrd-ds-spec'] or makeDS() 82 83
84 -def sourceGetRRAList(source):
85 86 def archiveGetRRA(archive): 87 return 'RRA:' + archive['rra-spec']
88 return [archiveGetRRA(archive) for archive in source['archives']] 89 90
91 -def sourceGetConnectionInfo(source):
92 return source['manager']
93 94
95 -def sourceGetComponentId(source):
96 return source['component-id']
97 98
99 -def sourceGetUIStateKey(source):
100 return source['ui-state-key']
101 102
103 -class RRDMonitor(log.Loggable):
104 logName = 'rrdmon' 105
106 - def __init__(self, sources):
107 self.debug('started rrd monitor') 108 self.multi = multi.MultiAdminModel() 109 self.scheduler = scheduler.Scheduler() 110 self.ensureRRDFiles(sources) 111 self.connectToManagers(sources) 112 self.startScheduler(sources)
113
114 - def ensureRRDFiles(self, sources):
115 for source in sources: 116 rrdfile = sourceGetFileName(source) 117 if not os.path.exists(rrdfile): 118 try: 119 self.info('Creating RRD file %s', rrdfile) 120 rrdtool.create(rrdfile, 121 "-s", str(sourceGetSampleFrequency(source)), 122 sourceGetDS(source), 123 *sourceGetRRAList(source)) 124 except rrdtool.error, e: 125 self.warning('Could not create RRD file %s', 126 rrdfile) 127 self.debug('Failure reason: %s', 128 log.getExceptionMessage(e))
129
130 - def connectToManagers(self, sources):
131 for source in sources: 132 connectionInfo = sourceGetConnectionInfo(source) 133 self.multi.addManager(connectionInfo, tenacious=True)
134
135 - def startScheduler(self, sources):
136 r = random.Random() 137 now = datetime.datetime.now(eventcalendar.LOCAL) 138 139 def eventInstanceStarted(eventInstance): 140 self.pollData(*eventInstance.event.content)
141 142 def eventStopped(eventInstance): 143 pass
144 145 self.scheduler.subscribe(eventInstanceStarted, eventInstanceStopped) 146 147 for source in sources: 148 freq = sourceGetSampleFrequency(source) 149 150 # randomly offset the polling 151 offset = datetime.timedelta(seconds=r.randint(0, freq)) 152 153 data = (str(sourceGetConnectionInfo(source)), 154 sourceGetComponentId(source), 155 sourceGetUIStateKey(source), 156 sourceGetName(source), 157 sourceGetFileName(source)) 158 159 # FIXME: Event never actually allowed a timedelta as rrule, 160 # so I doubt this refactoring of scheduler ever worked 161 calendar = eventcalendar.Calendar() 162 calendar.addEvent(now.isoformat(), 163 now + offset, now + offset + datetime.timedelta(seconds=1), 164 data, rrule=datetime.timedelta(seconds=freq)) 165 self.scheduler.setCalendar(calendar) 166
167 - def pollData(self, managerId, componentId, uiStateKey, dsName, 168 rrdFile):
169 170 def stateListToDict(l): 171 return dict([(x.get('name'), x) for x in l])
172 173 if managerId in self.multi.admins: 174 admin = self.multi.admins[managerId] 175 176 flowName, componentName = common.parseComponentId(componentId) 177 178 flows = stateListToDict(admin.planet.get('flows')) 179 if flowName not in flows: 180 self.warning('not polling %s%s:%s: no such flow %s', 181 managerId, componentId, uiStateKey, 182 flowName) 183 return 184 185 components = stateListToDict(flows[flowName].get('components')) 186 if componentName not in components: 187 self.warning('not polling %s%s:%s: no such component %s', 188 managerId, componentId, uiStateKey, 189 componentId) 190 return 191 192 state = components[componentName] 193 194 def gotUIState(uiState): 195 if not uiState.hasKey(uiStateKey): 196 self.warning('while polling %s%s:%s: uiState has no ' 197 'key %s', managerId, componentId, 198 uiStateKey, uiStateKey) 199 else: 200 try: 201 value = '%d:%s' % (int(time.time()), 202 uiState.get(uiStateKey)) 203 self.log("polled %s%s:%s, updating ds %s = %s", 204 managerId, componentId, uiStateKey, 205 dsName, value) 206 rrdtool.update(rrdFile, "-t", dsName, value) 207 except rrdtool.error, e: 208 self.warning('error updating rrd file %s for ' 209 '%s%s:%s', rrdFile, managerId, 210 componentId, uiStateKey) 211 self.debug('error reason: %s', 212 log.getExceptionMessage(e)) 213 214 def errback(failure): 215 self.warning('not polling %s%s:%s: failed to get ui ' 216 'state') 217 self.debug('reason: %s', log.getFailureMessage(failure)) 218 219 d = admin.componentCallRemote(state, 'getUIState') 220 d.addCallbacks(gotUIState, errback) 221