Package ZenRRD :: Module RenderServer
[hide private]
[frames] | no frames]

Source Code for Module ZenRRD.RenderServer

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  __doc__="""RenderServer 
 15   
 16  Frontend that passes rrd graph options to rrdtool to render.   
 17   
 18  $Id: RenderServer.py,v 1.14 2003/06/04 18:25:58 edahl Exp $""" 
 19   
 20  __version__ = "$Revision: 1.14 $"[11:-2] 
 21   
 22  import os 
 23  import time 
 24  import logging 
 25  import urllib 
 26  import zlib 
 27  import mimetypes 
 28   
 29  from AccessControl import ClassSecurityInfo 
 30  from Globals import InitializeClass 
 31  from Globals import DTMLFile 
 32  from OFS.Image import manage_addFile 
 33   
 34  try: 
 35      import rrdtool 
 36  except ImportError: 
 37      pass 
 38   
 39  try: 
 40      from base64 import urlsafe_b64decode 
 41      raise ImportError 
 42  except ImportError: 
43 - def urlsafe_b64decode(s):
44 import base64 45 return base64.decodestring(s.replace('-','+').replace('_','/'))
46 47 from Products.ZenUtils.PObjectCache import PObjectCache 48 from Products.ZenUtils.PObjectCache import CacheObj 49 from Products.ZenUtils.Utils import zenPath 50 51 from RRDToolItem import RRDToolItem 52 53 from Products.ZenModel.PerformanceConf import performancePath 54 import glob 55 import tarfile 56 57 import utils 58 59 log = logging.getLogger("RenderServer") 60 61
62 -def manage_addRenderServer(context, id, REQUEST = None):
63 """make a RenderServer""" 64 rs = RenderServer(id) 65 context._setObject(id, rs) 66 if REQUEST is not None: 67 REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
68 69 70 addRenderServer = DTMLFile('dtml/addRenderServer',globals()) 71 72
73 -class RenderServer(RRDToolItem):
74 75 meta_type = "RenderServer" 76 77 cacheName = 'RRDRenderCache' 78 79 security = ClassSecurityInfo() 80
81 - def __init__(self, id, tmpdir = '/tmp/renderserver', cachetimeout=300):
82 self.id = id 83 self.tmpdir = tmpdir 84 self.cachetimeout = cachetimeout
85 86 security.declareProtected('View', 'render')
87 - def render(self, gopts=None, start=None, end=None, drange=None, 88 remoteUrl=None, width=None, ftype='PNG', getImage=True, 89 graphid='', comment=None, REQUEST=None):
90 """render a graph and return it""" 91 gopts = zlib.decompress(urlsafe_b64decode(gopts)) 92 gopts = gopts.split('|') 93 gopts.append('--width=%s' % width) 94 if start: 95 gopts.append('--start=%s' % start) 96 if end: 97 gopts.append('--end=%s' % end) 98 drange = int(drange) 99 id = self.graphId(gopts, drange, ftype) 100 graph = self.getGraph(id, ftype, REQUEST) 101 if not graph: 102 if not os.path.exists(self.tmpdir): 103 os.makedirs(self.tmpdir, 0750) 104 filename = "%s/graph-%s" % (self.tmpdir,id) 105 if remoteUrl: 106 f = open(filename, "w") 107 f.write(urllib.urlopen(remoteUrl).read()) 108 f.close() 109 else: 110 if ftype.lower()=='html': 111 imgtype = 'PNG' 112 else: 113 imgtype = ftype 114 gopts.insert(0, "--imgformat=%s" % imgtype) 115 #gopts.insert(0, "--lazy") 116 end = int(time.time())-300 117 start = end - drange 118 gopts.insert(0, 'COMMENT:%s\\c' % comment) 119 gopts.insert(0, '--end=%d' % end) 120 gopts.insert(0, '--start=%d' % start) 121 gopts.insert(0, filename) 122 try: 123 rrdtool.graph(*gopts) 124 except Exception, ex: 125 if ex.args[0].find('No such file or directory') > -1: 126 return None 127 log.exception("failed generating graph") 128 log.warn(" ".join(gopts)) 129 raise 130 self.addGraph(id, filename) 131 graph = self.getGraph(id, ftype, REQUEST) 132 if getImage: 133 return graph 134 else: 135 return """ 136 <script> 137 parent.location.hash = '%s:%s;'; 138 </script> 139 """ % (graphid, str(bool(graph)))
140 141
142 - def deleteRRDFiles(self, device, 143 datasource=None, datapoint=None, 144 remoteUrl=None, REQUEST=None):
145 if datapoint: 146 rrdPath = '/Devices/%s/%s.rrd' % (device, datapoint) 147 try: 148 os.remove(performancePath(rrdPath)) 149 except OSError: 150 log.warn("File %s does not exist" % performancePath(rrdPath)) 151 elif datasource: 152 rrdPath = '/Devices/%s/%s_*.rrd' % (device, datasource) 153 filenames = glob.glob(performancePath(rrdPath)) 154 for filename in filenames: 155 try: 156 os.remove(filename) 157 except OSError: 158 log.warn("File %s does not exist" % filename) 159 if remoteUrl: 160 urllib.urlopen(remoteUrl)
161
162 - def packageRRDFiles(self, device, REQUEST=None):
163 """Tar a package of RRDFiles""" 164 srcdir = performancePath('/Devices/%s' % device) 165 tarfilename = '%s/%s.tgz' % (self.tmpdir, device) 166 tar = tarfile.open(tarfilename, "w:gz") 167 for file in os.listdir(srcdir): 168 tar.add('%s/%s' % (srcdir, file), '/%s' % os.path.basename(file)) 169 tar.close()
170
171 - def unpackageRRDFiles(self, device, REQUEST=None):
172 """Untar a package of RRDFiles""" 173 destdir = performancePath('/Devices/%s' % device) 174 tarfilename = '%s/%s.tgz' % (self.tmpdir, device) 175 tar = tarfile.open(tarfilename, "r:gz") 176 for file in tar.getmembers(): 177 tar.extract(file, destdir) 178 tar.close()
179
180 - def receiveRRDFiles(self, REQUEST=None):
181 """receive a device's RRD Files from another server""" 182 tarfile = REQUEST.get('tarfile') 183 tarfilename = REQUEST.get('tarfilename') 184 f=open('%s/%s' % (self.tmpdir, tarfilename), 'wb') 185 f.write(urllib.unquote(tarfile)) 186 f.close()
187
188 - def sendRRDFiles(self, device, server, REQUEST=None):
189 """Move a package of RRDFiles""" 190 tarfilename = '%s.tgz' % device 191 f=open('%s/%s' % (self.tmpdir, tarfilename), 'rb') 192 tarfilebody=f.read() 193 f.close() 194 # urlencode the id, title and file 195 params = urllib.urlencode({'tarfilename': tarfilename, 196 'tarfile':tarfilebody}) 197 # send the file to zope 198 perfMon = self.dmd.getDmdRoot("Monitors").getPerformanceMonitor(server) 199 if perfMon.renderurl.startswith('http'): 200 remoteUrl = '%s/receiveRRDFiles' % (perfMon.renderurl) 201 urllib.urlopen(remoteUrl, params)
202 203
204 - def moveRRDFiles(self, device, destServer, srcServer=None, REQUEST=None):
205 """send a device's RRD Files to another server""" 206 monitors = self.dmd.getDmdRoot("Monitors") 207 destPerfMon = monitors.getPerformanceMonitor(destServer) 208 if srcServer: 209 srcPerfMon = monitors.getPerformanceMonitor(srcServer) 210 remoteUrl = '%s/moveRRDFiles?device=%s&destServer=%s' % (srcPerfMon.renderurl, device, destServer) 211 urllib.urlopen(remoteUrl) 212 else: 213 self.packageRRDFiles(device, REQUEST) 214 self.sendRRDFiles(device, destServer, REQUEST) 215 if destPerfMon.renderurl.startswith('http'): 216 remoteUrl = '%s/unpackageRRDFiles?device=%s' % (destPerfMon.renderurl, device) 217 urllib.urlopen(remoteUrl) 218 else: 219 self.unpackageRRDFiles(device, REQUEST)
220 221 security.declareProtected('View', 'plugin')
222 - def plugin(self, name, REQUEST=None):
223 "render a custom graph and return it" 224 try: 225 dmd = self.dmd 226 m = zenPath('Products/ZenRRD/plugins/%s.py' % name) 227 graph = None 228 exec open(m) 229 return graph 230 except Exception, ex: 231 log.exception("failed generating graph from plugin %s" % name) 232 raise
233 234 235 security.declareProtected('GenSummary', 'summary')
236 - def summary(self, gopts):
237 """return summary information as a list but no graph""" 238 gopts.insert(0, '/dev/null') #no graph generated 239 try: 240 values = rrdtool.graph(*gopts)[2] 241 except Exception, ex: 242 if ex.args[0].find('No such file or directory') > -1: 243 return None 244 log.exception("failed generating summary") 245 log.warn(" ".join(gopts)) 246 raise 247 return values
248 249 250 security.declareProtected('GenSummary', 'currentValues')
251 - def currentValues(self, paths):
252 """return latest values""" 253 try: 254 def value(p): 255 v = None 256 info = None 257 try: 258 info = rrdtool.info(p) 259 except: 260 log.debug('%s not found' % p) 261 if info: 262 last = info['last_update'] 263 step = info['step'] 264 v = rrdtool.graph('/dev/null', 265 'DEF:x=%s:ds0:AVERAGE' % p, 266 'VDEF:v=x,LAST', 267 'PRINT:v:%.2lf', 268 '--start=%d'%(last-step), 269 '--end=%d'%last) 270 v = float(v[2][0]) 271 if str(v) == 'nan': v = None 272 return v
273 return map(value, paths) 274 except NameError: 275 log.warn("It appears that the rrdtool bindings are not installed properly.") 276 values = [] 277 except Exception, ex: 278 if ex.args[0].find('No such file or directory') > -1: 279 return None 280 log.exception("failed generating summary") 281 raise
282 283
284 - def rrdcmd(self, gopts, ftype='PNG'):
285 filename, gopts = self._setfile(gopts, ftype) 286 return "rrdtool graph " + " ".join(gopts)
287 288
289 - def graphId(self, gopts, drange, ftype):
290 import md5 291 id = md5.new(''.join(gopts)).hexdigest() 292 id += str(drange) + '.' + ftype.lower() 293 return id
294
295 - def _loadfile(self, filename):
296 try: 297 f = open(filename) 298 graph = f.read() 299 f.close() 300 return graph 301 except IOError: 302 log.info("File: %s not created yet." % filename); 303 return None
304 305
306 - def setupCache(self):
307 """make new cache if we need one""" 308 if not hasattr(self, '_v_cache') or not self._v_cache: 309 tmpfolder = self.getPhysicalRoot().temp_folder 310 if not hasattr(tmpfolder, self.cacheName): 311 cache = PObjectCache(self.cacheName, self.cachetimeout) 312 tmpfolder._setObject(self.cacheName, cache) 313 self._v_cache = tmpfolder._getOb(self.cacheName) 314 return self._v_cache
315 316
317 - def addGraph(self, id, filename):
318 """add graph to temporary folder""" 319 cache = self.setupCache() 320 graph = self._loadfile(filename) 321 if graph: 322 cache.addToCache(id, graph) 323 try: 324 os.remove(filename) 325 except OSError, e: 326 if e.errno == 2: 327 log.debug("%s: %s" % (e.strerror, e.filename)) 328 else: 329 raise e 330 cache.cleanCache()
331 332
333 - def getGraph(self, id, ftype, REQUEST):
334 """get a previously generated graph""" 335 cache = self.setupCache() 336 ftype = ftype.lower() 337 mimetype = mimetypes.guess_type('.%s'%ftype)[0] 338 if not mimetype: mimetype = 'image/%s' % ftype 339 if REQUEST: 340 response = REQUEST.RESPONSE 341 response.setHeader('Content-Type', mimetype) 342 return cache.checkCache(id)
343 344 345 InitializeClass(RenderServer) 346