Revision 592bfc3b

b/snf-stats-app/synnefo_stats/grapher.py
32 32
# or implied, of GRNET S.A.
33 33

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

  
37 36
import gd
38 37
import os
......
41 40

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

  
46 44
import rrdtool
47 45

  
48 46
from synnefo_stats import settings
49 47

  
50
class InternalError(Exception):
51
    pass
48
from synnefo.util.text import uenc
49
from snf_django.lib.api import faults, api_method
50

  
51
from logging import getLogger
52
log = getLogger(__name__)
52 53

  
53
class NotFound(Exception):
54
    pass
55 54

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

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

  
69 63
    return data
70 64

  
71 65

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

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

  
81 74
    v = [x[0] for x in values if x[0] is not None]
......
98 91
    else:
99 92
        line_color = image.colorAllocate((0x00, 0xa1, 0x00))
100 93

  
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)
94
    image.rectangle((0, 0),
95
                    (settings.WIDTH - 1, settings.HEIGHT - 1),
96
                    border_color, background_color)
97
    image.rectangle((1, 1),
98
                    (int(value / 100.0 * (settings.WIDTH - 2)),
99
                     settings.HEIGHT - 2),
100
                    line_color, line_color)
101
    image.string_ttf(settings.FONT, 8.0, 0.0,
102
                     (settings.WIDTH + 1, settings.HEIGHT - 1),
103
                     "CPU: %.1f%%" % value, white)
104 104

  
105 105
    io = StringIO()
106 106
    image.writePng(io)
......
110 110
    return data
111 111

  
112 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")
113
def draw_net_bar(fname, outfname=None):
114
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
116 115

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

  
123 121
    v = [x for x in values if x[0] is not None and x[1] is not None]
......
128 126
    rx_value, tx_value = v[-1]
129 127

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

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

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

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

  
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)
143
    image.rectangle((0, 0),
144
                    (settings.WIDTH - 1, settings.HEIGHT - 1),
145
                    border_color, background_color)
146
    image.rectangle((1, 1),
147
                    (int(tx_value / max_value * (settings.WIDTH - 2)),
148
                     settings.HEIGHT / 2 - 1),
149
                    tx_line_color, tx_line_color)
150
    image.rectangle((1, settings.HEIGHT / 2),
151
                    (int(rx_value / max_value * (settings.WIDTH - 2)),
152
                     settings.HEIGHT - 2),
153
                    rx_line_color, rx_line_color)
154
    image.string_ttf(settings.FONT, 8.0, 0.0,
155
                     (settings.WIDTH + 1, settings.HEIGHT - 1),
156
                     "TX/RX: %.2f/%.2f Mbps" % (tx_value, rx_value), white)
149 157

  
150 158
    io = StringIO()
151 159
    image.writePng(io)
......
155 163
    return data
156 164

  
157 165

  
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")
166
def draw_cpu_ts(fname, outfname):
167
    fname = os.path.join(fname, "cpu", "virt_cpu_total.rrd")
168
    outfname += "-cpu.png"
161 169

  
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))
170
    rrdtool.graph(outfname, "-s", "-1d", "-e", "-20s",
171
                  #"-t", "CPU usage",
172
                  "-v", "%",
173
                  #"--lazy",
174
                  "DEF:cpu=%s:ns:AVERAGE" % fname,
175
                  "LINE1:cpu#00ff00:")
171 176

  
172 177
    return read_file(outfname)
173 178

  
174 179

  
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")
180
def draw_cpu_ts_w(fname, outfname):
181
    fname = os.path.join(fname, "cpu", "virt_cpu_total.rrd")
182
    outfname += "-cpu-weekly.png"
178 183

  
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))
184
    rrdtool.graph(outfname, "-s", "-1w", "-e", "-20s",
185
                  #"-t", "CPU usage",
186
                  "-v", "%",
187
                  #"--lazy",
188
                  "DEF:cpu=%s:ns:AVERAGE" % fname,
189
                  "LINE1:cpu#00ff00:")
188 190

  
189 191
    return read_file(outfname)
190 192

  
191 193

  
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))
194
def draw_net_ts(fname, outfname):
195
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
196
    outfname += "-net.png"
197

  
198
    rrdtool.graph(outfname, "-s", "-1d", "-e", "-20s",
199
              #"-t", "Network traffic",
200
              "--units", "si",
201
              "-v", "Bits/s",
202
              #"--lazy",
203
              "COMMENT:\t\t\tAverage network traffic\\n",
204
              "DEF:rx=%s:rx:AVERAGE" % fname,
205
              "DEF:tx=%s:tx:AVERAGE" % fname,
206
              "CDEF:rxbits=rx,8,*",
207
              "CDEF:txbits=tx,8,*",
208
              "LINE1:rxbits#00ff00:Incoming",
209
              "GPRINT:rxbits:AVERAGE:\t%4.0lf%sbps\t\g",
210
              "LINE1:txbits#0000ff:Outgoing",
211
              "GPRINT:txbits:AVERAGE:\t%4.0lf%sbps\\n")
212 212

  
213 213
    return read_file(outfname)
214 214

  
215 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))
216
def draw_net_ts_w(fname, outfname):
217
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
218
    outfname += "-net-weekly.png"
219

  
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")
236 234

  
237 235
    return read_file(outfname)
238 236

  
239 237

  
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
238
available_graph_types = {
239
        'cpu-bar': draw_cpu_bar,
240
        'net-bar': draw_net_bar,
241
        'cpu-ts': draw_cpu_ts,
242
        'net-ts': draw_net_ts,
243
        'cpu-ts-w': draw_cpu_ts_w,
244
        'net-ts-w': draw_net_ts_w,
245
        }
246 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
247

  
248
@api_method(http_method='GET', token_required=False, user_required=False,
249
            format_allowed=False, logger=log)
250
def grapher(request, graph_type, hostname):
251
    fname = uenc(os.path.join(settings.RRD_PREFIX, hostname))
252
    if not os.path.isdir(fname):
253
        raise faults.ItemNotFound('No such instance')
254

  
255
    outfname = uenc(os.path.join(settings.GRAPH_PREFIX, hostname))
256
    draw_func = available_graph_types[graph_type]
257

  
258
    response = HttpResponse(draw_func(fname, outfname),
259
                            status=200, content_type="image/png")
260
    response.override_serialization = True
270 261

  
271 262
    return response
b/snf-stats-app/synnefo_stats/settings.py
11 11
GRAPH_PREFIX = getattr(settings, 'GRAPH_PREFIX', "/var/cache/snf-stats-app/")
12 12

  
13 13
# Font settings
14
FONT = getattr(settings, 'FONT', "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf")
15
FONT = getattr(settings, 'FONT', "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf")
14
FONT = getattr(settings, 'FONT',
15
               "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf")
16 16

  
17 17
# Bar settings
18 18
BAR_BORDER_COLOR = getattr(settings, 'BAR_BORDER_COLOR', (0x5c, 0xa1, 0xc0))
b/snf-stats-app/synnefo_stats/urls.py
1 1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
from django.conf.urls.defaults import patterns, include
35

  
35 36
from snf_django.lib.api.utils import prefix_pattern
37
from snf_django.lib.api import api_endpoint_not_found
38

  
36 39
from synnefo_stats.stats_settings import BASE_PATH
37 40
from synnefo_stats.grapher import grapher
38 41

  
42
graph_types_re = '((cpu|net)-(bar|(ts(-w)?)))'
43
stats_v1_patterns = patterns('',
44
    (r'^(?P<graph_type>%s)/(?P<hostname>[^ /]+)$' % graph_types_re, grapher),
45
)
46

  
39 47
stats_patterns = patterns('',
40
    (r'^(?P<hostname>\S+)/(?P<graph_type>\S+)/$', grapher),
48
    (r'^v1.0/', include(stats_v1_patterns)),
49
    (r'^.*', api_endpoint_not_found),
41 50
)
42 51

  
43 52
urlpatterns = patterns('',

Also available in: Unified diff