Statistics
| Branch: | Tag: | Revision:

root / snf-stats-app / synnefo_stats / grapher.py @ e6ad6878

History | View | Annotate | Download (9.2 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from django.http import HttpResponse
35
from django.views.decorators.http import require_http_methods
36

    
37
import gd
38
import os
39
import sys
40
import subprocess
41

    
42
from cgi import escape
43
from cStringIO import StringIO
44
#from flup.server.fcgi import WSGIServer
45

    
46
import rrdtool
47

    
48
from synnefo_stats import settings
49

    
50
class InternalError(Exception):
51
    pass
52

    
53
class NotFound(Exception):
54
    pass
55

    
56
def read_file(filepath):
57
    try:
58
        f = open(filepath, "r")
59
    except EnvironmentError, e:
60
        raise InternalError(str(e))
61

    
62
    try:
63
        data = f.read()
64
    except EnvironmentError, e:
65
        raise InternalError(str(e))
66
    finally:
67
        f.close()
68

    
69
    return data
70

    
71

    
72
def draw_cpu_bar(hostname):
73
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname), "cpu", "virt_cpu_total.rrd")
74

    
75
    try:
76
        values = rrdtool.fetch(fname, "AVERAGE")[2][-20:]
77
    except rrdtool.error, e:
78
        #raise InternalError(str(e))
79
        values = [(0.0, )]
80

    
81
    v = [x[0] for x in values if x[0] is not None]
82
    if not v:
83
        # Fallback in case we only get NaNs
84
        v = [0.0]
85
    # Pick the last value
86
    value = v[-1]
87

    
88
    image = gd.image((settings.IMAGE_WIDTH, settings.HEIGHT))
89

    
90
    border_color = image.colorAllocate(settings.BAR_BORDER_COLOR)
91
    white = image.colorAllocate((0xff, 0xff, 0xff))
92
    background_color = image.colorAllocate(settings.BAR_BG_COLOR)
93

    
94
    if value >= 90.0:
95
        line_color = image.colorAllocate((0xff, 0x00, 0x00))
96
    elif value >= 75.0:
97
        line_color = image.colorAllocate((0xda, 0xaa, 0x00))
98
    else:
99
        line_color = image.colorAllocate((0x00, 0xa1, 0x00))
100

    
101
    image.rectangle((0,0), (settings.WIDTH-1, settings.HEIGHT-1), border_color, background_color)
102
    image.rectangle((1,1), (int(value/100.0 * (settings.WIDTH - 2)), settings.HEIGHT - 2), line_color, line_color)
103
    image.string_ttf(settings.FONT, 8.0, 0.0, (settings.WIDTH + 1, settings.HEIGHT - 1), "CPU: %.1f%%" % value, white)
104

    
105
    io = StringIO()
106
    image.writePng(io)
107
    io.seek(0)
108
    data = io.getvalue()
109
    io.close()
110
    return data
111

    
112

    
113
def draw_net_bar(hostname):
114
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname),
115
                         "interface", "if_octets-eth0.rrd")
116

    
117
    try:
118
        values = rrdtool.fetch(fname, "AVERAGE")[2][-20:]
119
    except rrdtool.error, e:
120
        #raise InternalError(str(e))
121
        values = [(0.0, 0.0)]
122

    
123
    v = [x for x in values if x[0] is not None and x[1] is not None]
124
    if not v:
125
        # Fallback in case we only get NaNs
126
        v = [(0.0, 0.0)]
127

    
128
    rx_value, tx_value = v[-1]
129

    
130
    # Convert to bits
131
    rx_value = rx_value * 8 / 10**6
132
    tx_value = tx_value * 8 / 10**6
133

    
134
    max_value = (int(max(rx_value, tx_value)/50) + 1) * 50.0
135

    
136
    image = gd.image((settings.IMAGE_WIDTH, settings.HEIGHT))
137

    
138
    border_color = image.colorAllocate(settings.BAR_BORDER_COLOR)
139
    white = image.colorAllocate((0xff, 0xff, 0xff))
140
    background_color = image.colorAllocate(settings.BAR_BG_COLOR)
141

    
142
    tx_line_color = image.colorAllocate((0x00, 0xa1, 0x00))
143
    rx_line_color = image.colorAllocate((0x00, 0x00, 0xa1))
144

    
145
    image.rectangle((0,0), (settings.WIDTH-1, settings.HEIGHT-1), border_color, background_color)
146
    image.rectangle((1,1), (int(tx_value/max_value * (settings.WIDTH-2)), settings.HEIGHT/2 - 1), tx_line_color, tx_line_color)
147
    image.rectangle((1,settings.HEIGHT/2), (int(rx_value/max_value * (settings.WIDTH-2)), settings.HEIGHT - 2), rx_line_color, rx_line_color)
148
    image.string_ttf(settings.FONT, 8.0, 0.0, (settings.WIDTH + 1, settings.HEIGHT - 1), "TX/RX: %.2f/%.2f Mbps" % (tx_value, rx_value), white)
149

    
150
    io = StringIO()
151
    image.writePng(io)
152
    io.seek(0)
153
    data = io.getvalue()
154
    io.close()
155
    return data
156

    
157

    
158
def draw_cpu_ts(hostname):
159
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname), "cpu", "virt_cpu_total.rrd")
160
    outfname = os.path.join(settings.GRAPH_PREFIX, os.path.basename(hostname) + "-cpu.png")
161

    
162
    try:
163
        rrdtool.graph(outfname, "-s", "-1d", "-e", "-20s",
164
                      #"-t", "CPU usage",
165
                      "-v", "%",
166
                      #"--lazy",
167
                      "DEF:cpu=%s:ns:AVERAGE" % fname,
168
                      "LINE1:cpu#00ff00:")
169
    except rrdtool.error, e:
170
        raise InternalError(str(e))
171

    
172
    return read_file(outfname)
173

    
174

    
175
def draw_cpu_ts_w(hostname):
176
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname), "cpu", "virt_cpu_total.rrd")
177
    outfname = os.path.join(settings.GRAPH_PREFIX, os.path.basename(hostname) + "-cpu-weekly.png")
178

    
179
    try:
180
        rrdtool.graph(outfname, "-s", "-1w", "-e", "-20s",
181
                      #"-t", "CPU usage",
182
                      "-v", "%",
183
                      #"--lazy",
184
                      "DEF:cpu=%s:ns:AVERAGE" % fname,
185
                      "LINE1:cpu#00ff00:")
186
    except rrdtool.error, e:
187
        raise InternalError(str(e))
188

    
189
    return read_file(outfname)
190

    
191

    
192
def draw_net_ts(hostname):
193
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname), "interface", "if_octets-eth0.rrd")
194
    outfname = os.path.join(settings.GRAPH_PREFIX, os.path.basename(hostname) + "-net.png")
195
    try:
196
        rrdtool.graph(outfname, "-s", "-1d", "-e", "-20s",
197
                  #"-t", "Network traffic",
198
                  "--units", "si",
199
                  "-v", "Bits/s",
200
                  #"--lazy",
201
                  "COMMENT:\t\t\tAverage network traffic\\n",
202
                  "DEF:rx=%s:rx:AVERAGE" % fname,
203
                  "DEF:tx=%s:tx:AVERAGE" % fname,
204
                  "CDEF:rxbits=rx,8,*",
205
                  "CDEF:txbits=tx,8,*",
206
                  "LINE1:rxbits#00ff00:Incoming",
207
                  "GPRINT:rxbits:AVERAGE:\t%4.0lf%sbps\t\g",
208
                  "LINE1:txbits#0000ff:Outgoing",
209
                  "GPRINT:txbits:AVERAGE:\t%4.0lf%sbps\\n")
210
    except rrdtool.error, e:
211
        raise InternalError(str(e))
212

    
213
    return read_file(outfname)
214

    
215

    
216
def draw_net_ts_w(hostname):
217
    fname = os.path.join(settings.RRD_PREFIX, os.path.basename(hostname), "interface", "if_octets-eth0.rrd")
218
    outfname = os.path.join(settings.GRAPH_PREFIX, os.path.basename(hostname) + "-net-weekly.png")
219
    try:
220
        rrdtool.graph(outfname, "-s", "-1w", "-e", "-20s",
221
                  #"-t", "Network traffic",
222
                  "--units", "si",
223
                  "-v", "Bits/s",
224
                  #"--lazy",
225
                  "COMMENT:\t\t\tAverage network traffic\\n",
226
                  "DEF:rx=%s:rx:AVERAGE" % fname,
227
                  "DEF:tx=%s:tx:AVERAGE" % fname,
228
                  "CDEF:rxbits=rx,8,*",
229
                  "CDEF:txbits=tx,8,*",
230
                  "LINE1:rxbits#00ff00:Incoming",
231
                  "GPRINT:rxbits:AVERAGE:\t%4.0lf%sbps\t\g",
232
                  "LINE1:txbits#0000ff:Outgoing",
233
                  "GPRINT:txbits:AVERAGE:\t%4.0lf%sbps\\n")
234
    except rrdtool.error, e:
235
        raise InternalError(str(e))
236

    
237
    return read_file(outfname)
238

    
239

    
240
@require_http_methods(["GET"])
241
def grapher(request, hostname, graph_type):
242
    hostname = str(hostname)
243
    graph = ""
244
    ctype = "text/plain"
245
    code = 200
246

    
247
    try:
248
        if graph_type == "cpu-bar":
249
            graph = draw_cpu_bar(hostname)
250
        elif graph_type == "cpu-ts":
251
            graph = draw_cpu_ts(hostname)
252
        elif graph_type == "net-bar":
253
            graph = draw_net_bar(hostname)
254
        elif graph_type == "net-ts":
255
            graph = draw_net_ts(hostname)
256
        elif graph_type == "cpu-ts-w":
257
            graph = draw_cpu_ts_w(hostname)
258
        elif graph_type == "net-ts-w":
259
            graph = draw_net_ts_w(hostname)
260
        else:
261
            raise NotFound
262
        ctype = "image/png"
263
    except InternalError:
264
        code = 500
265
    except NotFound:
266
        code = 404
267

    
268
    response = HttpResponse(graph, content_type=ctype)
269
    response.status_code = code
270

    
271
    return response