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