Package Products :: Package ZenRRD :: Module zenrender
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenRRD.zenrender

  1  #! /usr/bin/env python  
  2  ########################################################################### 
  3  # 
  4  # This program is part of Zenoss Core, an open source monitoring platform. 
  5  # Copyright (C) 2007, 2011 Zenoss Inc. 
  6  # 
  7  # This program is free software; you can redistribute it and/or modify it 
  8  # under the terms of the GNU General Public License version 2 or (at your 
  9  # option) any later version as published by the Free Software Foundation. 
 10  # 
 11  # For complete information please visit: http://www.zenoss.com/oss/ 
 12  # 
 13  ########################################################################### 
 14  __doc__ = """zenrender 
 15   
 16  Listens in order to process RRD files to generate graphs 
 17  or retrieve data from a remote collector. 
 18  """ 
 19   
 20  import logging 
 21  log = logging.getLogger("zen.zenrender") 
 22   
 23  import mimetypes 
 24  import xmlrpclib 
 25   
 26  import Globals 
 27  import zope.interface 
 28   
 29  from twisted.web import resource, server 
 30  from twisted.internet import reactor 
 31   
 32  from Products.ZenCollector.daemon import CollectorDaemon 
 33  from Products.ZenCollector.interfaces import ICollectorPreferences,\ 
 34                                               ITaskSplitter,\ 
 35                                               ICollector 
 36  from Products.ZenCollector.tasks import NullTaskSplitter 
 37   
 38  from Products.ZenRRD.RenderServer import RenderServer as OrigRenderServer 
 39  from Products.ZenUtils.ObjectCache import ObjectCache 
 40   
 41   
42 -class RenderServer(OrigRenderServer):
43 cache = None 44
45 - def setupCache(self):
46 """make new cache if we need one""" 47 if self.cache is None: 48 self.cache = ObjectCache() 49 self.cache.initCache() 50 return self.cache
51 52
53 -class ZenRenderPreferences(object):
54 zope.interface.implements(ICollectorPreferences) 55
56 - def __init__(self):
57 """ 58 Constructs a new preferences instance and 59 provides default values for needed attributes. 60 """ 61 self.collectorName = "zenrender" 62 self.configCycleInterval = 20 # minutes 63 self.cycleInterval = 5 * 60 # seconds 64 self.configurationService = 'Products.ZenHub.services.RenderConfig' 65 66 # Will be filled in based on buildOptions 67 self.options = None
68
69 - def buildOptions(self, parser):
70 # Remove device option 71 parser.remove_option('--device') 72 73 # Add options 74 parser.add_option('--http-port', type='int', 75 dest='httpport', 76 default= 8091, 77 help='Port zenrender listens on for HTTP' 78 'render requests. Default is %default.')
79
80 - def postStartup(self):
81 """ 82 Listen for HTTP requests for RRD data or graphs. 83 """ 84 self._daemon = zope.component.getUtility(ICollector) 85 86 # Start listening for HTTP requests 87 httpPort = self. _daemon.options.httpport 88 collector = self._daemon.options.monitor 89 log.info("Starting %s zenrender webserver on port %s", 90 collector, httpPort) 91 renderer = HttpRender(collector) 92 reactor.listenTCP(httpPort, server.Site(renderer)) 93 94 # Add remote_ methods from renderer directly to the daemon 95 for name in dir(renderer): 96 if name.startswith('remote_'): 97 func = getattr(renderer, name) 98 setattr(self._daemon, name, func)
99 100
101 -class HttpRender(resource.Resource):
102 isLeaf = True 103
104 - def __init__(self, collectorName):
105 self.log = log 106 self._daemon = zope.component.getUtility(ICollector) 107 self.collectorName = collectorName 108 self.rs = RenderServer(collectorName)
109
110 - def remote_render(self, *args, **kw):
111 return self.rs.render(*args, **kw)
112
113 - def remote_packageRRDFiles(self, *args, **kw):
114 return self.rs.packageRRDFiles(*args, **kw)
115
116 - def remote_unpackageRRDFiles(self, *args, **kw):
117 return self.rs.unpackageRRDFiles(*args, **kw)
118
119 - def remote_receiveRRDFiles(self, *args, **kw):
120 return self.rs.receiveRRDFiles(*args, **kw)
121
122 - def remote_sendRRDFiles(self, *args, **kw):
123 return self.rs.sendRRDFiles(*args, **kw)
124
125 - def remote_moveRRDFiles(self, *args, **kw):
126 return self.rs.moveRRDFiles(*args, **kw)
127
128 - def remote_plugin(self, *args, **kw):
129 return self.rs.plugin(*args, **kw)
130
131 - def remote_summary(self, *args, **kw):
132 return self.rs.summary(*args, **kw)
133
134 - def remote_fetchValues(self, *args, **kw):
135 return self.rs.fetchValues(*args, **kw)
136
137 - def remote_currentValues(self, *args, **kw):
138 return self.rs.currentValues(*args, **kw)
139
140 - def _showHelp(self):
141 """ 142 When someone hits the HTTP port directly, give them 143 something other than a traceback. 144 """ 145 helpText = [ """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 146 <html><title>zenrender Help</title> 147 <body> 148 <h3>This zenrender is for collector: %s</h3> 149 <h3>About zenrender</h3> 150 <p>The zenrender daemon receives calls from zenhub (or in some 151 special cases, by a browser directly) and given a request, 152 creates a graph of RRD data or returns back RRD information. 153 This daemon is not meant to be browsed directly by users.</p> 154 <p>A zenrender daemon should only respond to requests for 155 the remote collector with which it is associated. This 156 zenrender daemon is registered with the '%s' collector.</p> 157 """ % (self.collectorName, self.collectorName)] 158 159 methods = [] 160 for name in dir(self): 161 if not name.startswith('remote_'): 162 continue 163 164 name = name.replace('remote_', '') 165 docs = getattr(self.rs, name).__doc__ 166 docs = docs if docs is not None else '' 167 methods.append( (name, docs) ) 168 169 # Insert table of methods 170 helpText.append("""<table border='1'> 171 <caption>zenrender Methods</caption> 172 <tr><th>Method Name</th><th>Description</th></tr>""") 173 for name, docs in sorted(methods): 174 helpText.append("<tr><td>%s</td> <td><pre>%s</pre></td></tr>" % ( 175 name, docs)) 176 helpText.append("</table>") 177 178 # Drop in the trailer 179 helpText.append("""</body></html>""") 180 return '\n'.join(helpText)
181
182 - def render_GET(self, request):
183 """ 184 Respond to HTTP GET requests 185 """ 186 args = request.args.copy() 187 for k, v in args.items(): 188 if len(v) == 1: 189 args[k] = v[0] 190 command = request.postpath[-1] 191 self.log.debug("Processing %s request from %s", command, 192 request.getClientIP()) 193 if command == '': 194 return self._showHelp() 195 196 args.setdefault('ftype', 'PNG') 197 ftype = args['ftype'] 198 del args['ftype'] 199 mimetype = mimetypes.guess_type('x.%s' % ftype)[0] 200 if mimetype is None: 201 mimetype = 'image/%s' % ftype.lower() 202 request.setHeader('Content-type', mimetype) 203 functor = getattr(self._daemon, 'remote_' + command, None) 204 if functor: 205 return functor(**args) 206 207 # Ignore trash and log error messages 208 if command not in ('favicon.ico',): 209 self.log.error("Received a bad request: %s", command) 210 return ''
211
212 - def render_POST(self, request):
213 """ 214 Respond to HTTP POST requests (eg XML-RPC requests) 215 """ 216 content = request.content.read() 217 args, command = xmlrpclib.loads(content) 218 self.log.debug("Processing %s request from %s" % (command,request.getClientIP())) 219 request.setHeader('Content-type', 'text/xml') 220 functor = getattr(self._daemon, 'remote_' + command, None) 221 if functor and isinstance(args, (tuple, list, dict)): 222 if isinstance(args, (tuple, list)): 223 result = functor(*args) 224 elif isinstance(args, dict): 225 result = functor(**args) 226 response = xmlrpclib.dumps((result,), 227 methodresponse=True, allow_none=True) 228 return response 229 230 self.log.error("Received a bad request: %s", command) 231 return ''
232 233 234 if __name__ == '__main__': 235 myPreferences = ZenRenderPreferences() 236 myTaskSplitter = NullTaskSplitter() 237 daemon = CollectorDaemon(myPreferences, myTaskSplitter) 238 daemon.run() 239