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