1
2
3
4
5
6
7
8
9
10
11
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
51
52
54 zope.interface.implements(ICollectorPreferences)
55
57 """
58 Constructs a new preferences instance and
59 provides default values for needed attributes.
60 """
61 self.collectorName = "zenrender"
62 self.configCycleInterval = 20
63 self.cycleInterval = 5 * 60
64 self.configurationService = 'Products.ZenHub.services.RenderConfig'
65
66
67 self.options = None
68
70
71 parser.remove_option('--device')
72
73
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
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
95 for name in dir(renderer):
96 if name.startswith('remote_'):
97 func = getattr(renderer, name)
98 setattr(self._daemon, name, func)
99
100
102 isLeaf = True
103
105 self.log = log
106 self._daemon = zope.component.getUtility(ICollector)
107 self.collectorName = collectorName
108 self.rs = RenderServer(collectorName)
109
111 return self.rs.render(*args, **kw)
112
115
118
121
124
127
129 return self.rs.plugin(*args, **kw)
130
132 return self.rs.summary(*args, **kw)
133
136
139
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
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
179 helpText.append("""</body></html>""")
180 return '\n'.join(helpText)
181
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
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