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