Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.6 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

    
36
import gd
37
import os
38

    
39
from cStringIO import StringIO
40

    
41
import rrdtool
42

    
43
from Crypto.Cipher import AES
44
from base64 import urlsafe_b64decode
45
from hashlib import sha256
46

    
47
from synnefo_stats import settings
48

    
49
from synnefo.util.text import uenc
50
from snf_django.lib.api import faults, api_method
51

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

    
55

    
56
def read_file(filepath):
57
    f = open(filepath, "r")
58

    
59
    try:
60
        data = f.read()
61
    finally:
62
        f.close()
63

    
64
    return data
65

    
66

    
67
def draw_cpu_bar(fname, outfname=None):
68
    fname = os.path.join(fname, "cpu", "virt_cpu_total.rrd")
69

    
70
    try:
71
        values = rrdtool.fetch(fname, "AVERAGE")[2][-20:]
72
    except rrdtool.error:
73
        values = [(0.0, )]
74

    
75
    v = [x[0] for x in values if x[0] is not None]
76
    if not v:
77
        # Fallback in case we only get NaNs
78
        v = [0.0]
79
    # Pick the last value
80
    value = v[-1]
81

    
82
    image = gd.image((settings.IMAGE_WIDTH, settings.HEIGHT))
83

    
84
    border_color = image.colorAllocate(settings.BAR_BORDER_COLOR)
85
    white = image.colorAllocate((0xff, 0xff, 0xff))
86
    background_color = image.colorAllocate(settings.BAR_BG_COLOR)
87

    
88
    if value >= 90.0:
89
        line_color = image.colorAllocate((0xff, 0x00, 0x00))
90
    elif value >= 75.0:
91
        line_color = image.colorAllocate((0xda, 0xaa, 0x00))
92
    else:
93
        line_color = image.colorAllocate((0x00, 0xa1, 0x00))
94

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

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

    
113

    
114
def draw_net_bar(fname, outfname=None):
115
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
116

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

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

    
127
    rx_value, tx_value = v[-1]
128

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

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

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

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

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

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

    
159
    io = StringIO()
160
    image.writePng(io)
161
    io.seek(0)
162
    data = io.getvalue()
163
    io.close()
164
    return data
165

    
166

    
167
def draw_cpu_ts(fname, outfname):
168
    fname = os.path.join(fname, "cpu", "virt_cpu_total.rrd")
169
    outfname += "-cpu.png"
170

    
171
    rrdtool.graph(outfname, "-s", "-1d", "-e", "-20s",
172
                  #"-t", "CPU usage",
173
                  "-v", "%",
174
                  #"--lazy",
175
                  "DEF:cpu=%s:value:AVERAGE" % fname,
176
                  "LINE1:cpu#00ff00:")
177

    
178
    return read_file(outfname)
179

    
180

    
181
def draw_cpu_ts_w(fname, outfname):
182
    fname = os.path.join(fname, "cpu", "virt_cpu_total.rrd")
183
    outfname += "-cpu-weekly.png"
184

    
185
    rrdtool.graph(outfname, "-s", "-1w", "-e", "-20s",
186
                  #"-t", "CPU usage",
187
                  "-v", "%",
188
                  #"--lazy",
189
                  "DEF:cpu=%s:value:AVERAGE" % fname,
190
                  "LINE1:cpu#00ff00:")
191

    
192
    return read_file(outfname)
193

    
194

    
195
def draw_net_ts(fname, outfname):
196
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
197
    outfname += "-net.png"
198

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

    
212
    return read_file(outfname)
213

    
214

    
215
def draw_net_ts_w(fname, outfname):
216
    fname = os.path.join(fname, "interface", "if_octets-eth0.rrd")
217
    outfname += "-net-weekly.png"
218

    
219
    rrdtool.graph(outfname, "-s", "-1w", "-e", "-20s",
220
                  "--units", "si",
221
                  "-v", "Bits/s",
222
                  "COMMENT:\t\t\tAverage network traffic\\n",
223
                  "DEF:rx=%s:rx:AVERAGE" % fname,
224
                  "DEF:tx=%s:tx:AVERAGE" % fname,
225
                  "CDEF:rxbits=rx,8,*",
226
                  "CDEF:txbits=tx,8,*",
227
                  "LINE1:rxbits#00ff00:Incoming",
228
                  "GPRINT:rxbits:AVERAGE:\t%4.0lf%sbps\t\g",
229
                  "LINE1:txbits#0000ff:Outgoing",
230
                  "GPRINT:txbits:AVERAGE:\t%4.0lf%sbps\\n")
231

    
232
    return read_file(outfname)
233

    
234

    
235
def decrypt(secret):
236
    # Make sure key is 32 bytes long
237
    key = sha256(settings.STATS_SECRET_KEY).digest()
238

    
239
    aes = AES.new(key)
240
    return aes.decrypt(urlsafe_b64decode(secret)).rstrip('\x00')
241

    
242

    
243
available_graph_types = {'cpu-bar': draw_cpu_bar,
244
                         'net-bar': draw_net_bar,
245
                         'cpu-ts': draw_cpu_ts,
246
                         'net-ts': draw_net_ts,
247
                         'cpu-ts-w': draw_cpu_ts_w,
248
                         'net-ts-w': draw_net_ts_w
249
                         }
250

    
251

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

    
260
    outfname = uenc(os.path.join(settings.GRAPH_PREFIX, hostname))
261
    draw_func = available_graph_types[graph_type]
262

    
263
    response = HttpResponse(draw_func(fname, outfname),
264
                            status=200, content_type="image/png")
265
    response.override_serialization = True
266

    
267
    return response