Revision 04733cdb

b/.gitignore
1
settings.py
2
*.pyc
3
rrds/
4
*.db
b/core/admin.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from core.models import *
5

  
6
from django.contrib import admin
7

  
8
admin.site.register(Lun)
b/core/management/__init__.py
1

  
b/core/management/commands/__dbfuncs__.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.conf import settings
5
from core.models import *
6
from graphs.models import *
7
from datetime import datetime, timedelta
8
import time
9
import sys
10

  
11
def expireoldgraphs(node, type):
12
    if node.snmpmanagednode.perm_fail == 1:
13
        graphs = Graph.objects.filter(host=node)
14
        graphs.delete()
15
        return
16
    graphs = Graph.objects.filter(host=node, type=type,
17
                                 date__lt=datetime.now()-timedelta(settings.RG_EXPIRE_DAYS))
18
    graphs.delete()
19

  
20
def dbreg(type, rrdpath, lun, **kwargs):
21
    try:
22
        rrdfile = RrdFile.objects.get(path=rrdpath)
23
    except RrdFile.DoesNotExist, e:
24
        rrdfile = RrdFile(path=rrdpath)
25
        rrdfile.save()
26
    except RrdFile.MultipleObjectsReturned, e:
27
        #This should never happen
28
        sys.stderr.write('Multiple RrdFiles in database. This should never happen\n')
29
        sys.stderr.write('RRD File: %s\n' % rrdpath)
30
        sys.stderr.write('Refusing to proceed. Fix the problem\n')
31
        sys.exit()
32

  
33
    if 'tags' in kwargs:
34
        tags = kwargs['tags']
35
    else:
36
        tags = []
37
    #Making the assumption that an RRDfile will never contain data sources not in the database
38
    #Also since the rrdfiles are created by mrtg datasource names are hardcoded.
39
    if rrdfile.datasource_set.count() == 0:
40
        ds0 = DataSource(name='ds0')
41
        ds0.rrdfile=rrdfile
42
        ds0.save()
43
        ds0.tags.clear()
44
        for tag in tags:
45
            ds0.tags.add(tag) 
46
        # ds0.content_object = content
47
        
48
        ds1 = DataSource(name='ds1')
49
        ds1.rrdfile=rrdfile
50
        ds1.save()
51
        ds1.tags.clear()
52
        for tag in tags:
53
            ds1.tags.add(tag) 
54
        # ds1.content_object = content
55
        
56
        datasources=[ds0,ds1]
57
    else:
58
        datasources=rrdfile.datasource_set.all()
59
        for tempds in datasources:
60
            tempds.tags.clear()
61
            for tag in tags:
62
                tempds.tags.add(tag) 
63
            
64

  
65
    datasourcespk = [ds.pk for ds in datasources]
66
    graphs = Graph.objects.filter(type=type,tags__name='lun:%s'%lun.pk,datasources__pk__in=datasourcespk).distinct()
67
    if len(graphs) == 1:
68
        graph = graphs[0]
69
    if len(graphs) < 1:
70
        graph = Graph(type=type)
71
        graph.save()
72
        graph.tags.add('lun:%s'%lun.pk)
73
        if 'description' in kwargs:
74
            graph.tags.add('graphdescription:%s %s'%(lun.name, kwargs['description']))
75
    elif len(graphs) > 1:
76
        sys.stderr.write('Found more than one possible graph. Using oldest one as an approximation: %s\n' % e)
77
        graph = Graph.objects.filter(type=type,tags__name='lun:%s'%lun.pk,datasources__pk__in=datasourcespk).distinct().order_by('-date')[0]
78

  
79
    # Setting various attributes through kwargs
80
    for i in ('pc95','units','logarithmic','label','description'):
81
        if i in kwargs:
82
            setattr(graph,i,kwargs[i])
83

  
84
    graph.save()
85

  
86
    for datasource in datasources:
87
        try:
88
            grds = GraphDataSource.objects.get(graph=graph,datasource=datasource)
89
        except GraphDataSource.DoesNotExist, e:
90
            grds = GraphDataSource(graph=graph,datasource=datasource)
91
        # Some defaults
92
        grds.args = ''
93
        grds.function = 'LINE1'
94
        grds.color = '#0000bbbb'
95
        # Hardcoded default. Max Datasources in a graph.
96
        for n in range(0,1024):
97
            for i in ( 'function', 'color', 'args', 'legend', 'stackorder' ):
98
                if datasource.name == 'ds%s' % n and 'ds%s%s' % ( n, i ) in kwargs:
99
                    setattr(grds,i,kwargs['ds%s%s' % ( n, i)])
100
        grds.save()
101

  
102
    try:
103
        f = open(rrdpath)
104
    except IOError:
105
        necreation_epoch = int(time.time())
106
        f = rrdtool.create(str(rrdpath), str("--start"), str(1349965501),  [str('DS:ds0:COUNTER:300:U:U'), str('DS:ds1:COUNTER:300:U:U')], str('RRA:AVERAGE:0.5:1:288'), str('RRA:AVERAGE:0.5:3:672'), str('RRA:AVERAGE:0.5:12:744'), str('RRA:AVERAGE:0.5:72:1460'))
107

  
b/core/management/commands/__genrgconfig__.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3
from __dbfuncs__ import *
4
from core.models import *
5
from django.conf import settings
6
import rrdtool
7
import time
8
import datetime
9

  
10

  
11

  
12
def diskio_genconfig(lun):
13
    devfilename = "lun_%s" % lun.pk
14
    seriesfilename = 'blocksiostats'
15
    target = "%s_%s" % (devfilename, seriesfilename)
16
    rrdpath = settings.RRDFILEDIR + '/%s.rrd' % target
17
    
18
    dbreg('diskio', rrdpath, lun, description='Disk IO' , label=target,
19
            ds0legend='Blocks Read', ds1legend='Blocks Write',
20
            ds0function='AREA', ds1function='LINE1',
21
            ds0color='#00bb00bb', ds1color='#0000bbbb',
22
            pc95=False, units='blocks', tags=['lun:%s'%lun.pk, 'ds0:blocksread', "ds1:blockswrite", "type:blocksiostats"])
23

  
24
def requests_genconfig(lun):
25
    devfilename = "lun_%s" % lun.pk
26
    seriesfilename = 'requeststats'
27
    target = "%s_%s" % (devfilename, seriesfilename)
28
    rrdpath = settings.RRDFILEDIR + '/%s.rrd' % target
29
    
30
    dbreg('requests', rrdpath, lun, description='Requests IO' , label=target,
31
            ds0legend='Read Requests', ds1legend='Write Requests',
32
            ds0function='AREA', ds1function='LINE1',
33
            ds0color='#00bb00bb', ds1color='#0000bbbb',
34
            pc95=False, units='reuests', tags=['lun:%s'%lun.pk, 'ds0:readrequests', "ds1:writerequests", "type:requeststats"])
35
    
36

  
37

  
b/core/management/commands/__init__.py
1

  
b/core/management/commands/generate_csv.py
1
# -*- coing: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
# Copyright 2013 Leonidas Poulopoulos
5
#
6
# Licensed under the Apache License, Version 2.0 (the "License");
7
# you may not use this file except in compliance with the License.
8
# You may obtain a copy of the License at
9
#
10
#    http://www.apache.org/licenses/LICENSE-2.0
11
#
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
17

  
18
from django.core.management.base import BaseCommand, CommandError
19
from django.conf import settings
20
from core.models import *
21
import csv
22
import time
23
import random
24

  
25
class Command(BaseCommand):
26
    help = "Populates the lun series measurements from a csv file" \
27
            "It assumes  that the meassurement file is formatted like:\n" \
28
            "<unix_timestamp>\n"\
29
            "LUNID, METRIC_A, METRIC_B, METRIC_C, METRIC_D\n"\
30
            "1, 0, 2, 2, 1\n"\
31
            "2, 0, 0, 0, 0\n" \
32
            "...\n" \
33
            "N, 1, 2, 3, 1 \n" \
34
            
35
    args = ""
36
    label = ''
37
    # HAve that in mind:
38
    # '''{'lun_id': 12, stats'diskio':['blocksread':189202196, 'blockswrite':279338720], 'requests':[readreq':121123, 'writereq': 2121234], 'timestamp':1412341234}'''
39
    def handle(self, *args, **options):
40
        f = open(settings.CSV_FILE, "w")
41
        out = str(int(time.time())) + ",\n"
42
        lun_ids = [10, 20, 21, 22, 12, 42, 32, 31, 144, 11, 233, 321134, 324, 4234, 2343, 631, 3456, 2341, 635, 152345, 12345123, 123496, 65431]
43
        header = ["Blocks Read","Blocks Write","Read Reqs","Write Reqs"]
44
        out = out + "LUN," +",".join(header) + "," + "\n"
45
        for (i,k) in enumerate(lun_ids):
46
            out = out + str(lun_ids[i]) + ","
47
            for j in header:
48
                out = out + str(random.randint(8000, 9000)) + ","
49
            out  = out + "\n"
50
        f.write(out)
51
        f.close()
52

  
53

  
54
    def handle_json(self, *args, **options):
55
        f = open(settings.CSV_FILE, "w")
56
        out = str(int(time.time())) + "\n"
57
        lun_ids = [10, 20, 21, 22, 12, 42, 32, 31, 144, 11, 233, 321134, 324, 4234, 2343, 631, 3456, 2341, 635, 152345, 12345123, 123496, 65431]
58
        out = '{"luns": ['
59
        for id in lun_ids:
60
            out = out + '{"lun_id":%s, "diskio":{"blocksread":%s, "blockswrite":%s}, "requests":{"readreq":%s, "writereq": %s}, "timestamp":%s}' % (id, random.randint(100, 10000), random.randint(100, 10000), random.randint(100, 10000), random.randint(100, 10000), int(time.time()))
61
            out = out + ","
62
        out = out.rstrip(',')
63
        out = out + ']}'
64
        f.write(out)
65
        f.close()
66

  
67
        
68
        
69

  
70

  
b/core/management/commands/parse_csv.py
1
# -*- coing: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
# Copyright 2013 Leonidas Poulopoulos
5
#
6
# Licensed under the Apache License, Version 2.0 (the "License");
7
# you may not use this file except in compliance with the License.
8
# You may obtain a copy of the License at
9
#
10
#    http://www.apache.org/licenses/LICENSE-2.0
11
#
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
17

  
18
from django.core.management.base import BaseCommand, CommandError
19
from django.conf import settings
20
from core.models import *
21
from graphs.models import *
22
import csv
23
import time
24

  
25
import json
26
from __genrgconfig__ import *
27
from __dbfuncs__ import *
28

  
29
# for file listing
30
from stat import S_ISREG, ST_CTIME, ST_MODE
31
import os, sys, time, datetime
32

  
33
class Command(BaseCommand):
34
    help = "Populates the lun series measurements from a csv file" \
35
            "It assumes  that the meassurement file is formatted like:\n" \
36
            "<unix_timestamp>\n"\
37
            "LUNID, METRIC_A, METRIC_B, METRIC_C, METRIC_D\n"\
38
            "1, 0, 2, 2, 1\n"\
39
            "2, 0, 0, 0, 0\n" \
40
            "...\n" \
41
            "N, 1, 2, 3, 1 \n" \
42
            
43
    args = ""
44
    label = ''
45
    
46
    def handle(self, *args, **options):
47
        for statsfile in self.get_files_listing():
48
            self.getgraph(statsfile)
49
        print "Finished!"
50
    
51
    def get_files_listing(self):
52
       
53
        # path to the directory (relative or absolute)
54
        dirpath = '/srv/emc-stats/incoming'
55
        
56
        # get all entries in the directory w/ stats
57
        entries = (os.path.join(dirpath, fn) for fn in os.listdir(dirpath))
58
        entries = ((os.stat(path), path) for path in entries)
59
        
60
        # leave only regular files, insert creation date
61
        entries = ((stat[ST_CTIME], path)
62
                   for stat, path in entries if S_ISREG(stat[ST_MODE]))
63
        #NOTE: on Windows `ST_CTIME` is a creation date 
64
        #  but on Unix it could be something else
65
        #NOTE: use `ST_MTIME` to sort by a modification date
66
        statfiles = []
67
        for cdate, path in sorted(entries):
68
            statfiles.append("%s/%s"%('/srv/emc-stats/incoming',os.path.basename(path)))
69
        return statfiles
70
    
71
    def getgraph(self, statsfile):
72
        # We assume that the following is received
73
        # 1380622204
74
        # LUN,Blocks Read,Blocks Write,Read Reqs,Write Reqs,
75
        # 10,7346,8149,5311,9510,
76
        # 20,4581,3879,2894,9419,
77

  
78
        f = open(statsfile)
79
        #print statsfile
80
        # get first line. This is the timestamp
81
        timestamp = "%s" %f.readline().replace('\n', '')
82
        reader = csv.reader(self.remove_last_char(f))
83
        timestamp = self.round_to_5(int(timestamp))
84
        out = {}
85
        luns = []
86
        for i,serieslist in enumerate(reader):
87
            #print serieslist
88
            lundict = {}
89
            if i == 0:
90
                #those are headers, ignore (for the time)
91
                continue
92
            lundict['lun_name'] = serieslist[0] 
93
            lundict['timestamp'] = timestamp
94
            lundict['blocksread'] = serieslist[1]
95
            lundict['blockswrite'] = serieslist[2]
96
            lundict['readreq'] = serieslist[3]
97
            lundict['writereq'] = serieslist[4]
98
            lun, created = Lun.objects.get_or_create(name="%s"%lundict['lun_name'])
99
            if created:
100
                types = settings.LUN_GRAPH_TYPES
101
                for type in types:
102
                    self.dbupdate(lun, type)
103
            lundict['lun_id'] = lun.pk
104
            luns.append(lundict)
105
        self.update_graphs(luns)
106
        f.close()
107
        print "Done with %s" %statsfile
108

  
109
    def update_graphs(self, lunslist):
110
        for lundict in lunslist:
111
            self.update_graph(lundict)
112

  
113

  
114
    def update_graph(self, lundict):
115
        # Try to automate once proof of concept is done
116
        #print "updating Lun %s"%(lundict['lun_name'])
117
        dss_block = DataSource.objects.values('rrdfile').filter(tags__name="lun:%s"%lundict['lun_id']).filter(tags__name="type:blocksiostats").distinct()
118
        # This should return a single element list
119
        if len(dss_block) != 1:
120
            print "ERROR: It appears that there are %s blocksiostats datasources for lun %s" %(len(dss_block), lundict['lun_name'])
121
        else:
122
            rrdfile = dss_block[0]['rrdfile']
123
            try:
124
                rrdtool.update(
125
                    str('%s'%(RrdFile.objects.get(pk=rrdfile).path)), 
126
                    str('--template'), str('ds0:ds1'),
127
                    str(lundict['timestamp']+":" +lundict['blocksread']+":"+lundict['blockswrite']))
128
            except Exception as e:
129
                #print e
130
                pass
131

  
132
        dss_req = DataSource.objects.values('rrdfile').filter(tags__name="lun:%s"%lundict['lun_id']).filter(tags__name="type:requeststats").distinct()  
133
        # This should return a single element list
134
        if len(dss_req) != 1:
135
            print "ERROR: It appears that there are %s requeststats datasources for lun %s" %(len(dss_req), lundict['lun_name'])
136
        else:
137
            rrdfile = dss_req[0]['rrdfile']
138
            try:
139
                rrdtool.update(
140
                    str('%s'%(RrdFile.objects.get(pk=rrdfile).path)), 
141
                    str('--template'), str('ds0:ds1'),
142
                    str(lundict['timestamp']+":" +lundict['readreq']+":"+lundict['writereq']))
143
            except Exception as e:
144
                #print e
145
                pass
146
        
147
    def round_to_5(self, timestamp):
148
        
149
        tm = datetime.datetime.fromtimestamp(timestamp)
150
        tm = tm - datetime.timedelta(minutes=tm.minute % 5,
151
                             seconds=tm.second,
152
                             microseconds=tm.microsecond)
153
        return tm.strftime("%s")
154
    
155
    def remove_last_char(self, fileobj):
156
        for line in fileobj:
157
            yield line.strip()[:-1]
158

  
159
    def dbupdate(self, device, type):
160
        # Ok. A couple of hacks here. Instead of a if then elif then elif.... for each type
161
        # we get first from the settings dictionnary the confdir and then the function used 
162
        # for this type from the globals() dictionary
163
        try:
164
#            dir = getattr(settings,'RG_%s_CONFDIR' % type.upper())
165
#            self.checkdir(dir)
166
            
167
            func = globals()['%s_genconfig' % type]
168
            func(device)
169
        except AttributeError as e:
170
            print "%s is not going to be configured because %s" % (type, e)
171
            return
172

  
173

  
b/core/models.py
1
from django.db import models
2
# Create your models here.
3

  
4
from taggit.managers import TaggableManager
5

  
6
class Lun(models.Model):
7
    name = models.CharField(max_length=80, null=True, blank=True)
8
    tags = TaggableManager()
9
    
10
    def __unicode__(self):
11
        return 'ID: %s, Name: %s' % (self.pk, self.name)
12
    
13
    def getGraphs(self):
14
        lungraphs = Graph.objects.filter(tags__name='lun:%s'%self.pk)
15
        return lungraphs 
16
    
17
    def graphCounters(self, dataset):
18
        '''dataset format\n'''\
19
        '''{'lun_id': 12, 'diskio':['blocksread':189202196, 'blockswrite':279338720], 'requests':[readreq':121123, 'writereq': 2121234], 'timestamp':1412341234}'''
20
        
21
        if not dataset:
22
            return "Empty data for lun %s" %(self.name)
23
        
24
        reps = {'/':'_', '#':'_','-':'_', ' ':'_'}
25
        
26
        pmpoints_list = res
27
        for pmpoints_meassurement in res:
28
            period_epoch = pmpoints_meassurement['timestamp']
29
            for serie in self.series.all():
30
                counterLink = DeviceSeries.objects.get(device=self, series__name=serie.name)
31
                try:
32
                    rrdfilename = counterLink.getGraphUrl()
33
                except Exception as e:
34
                    print e
35
                    continue
36
                if rrdfilename:
37
                    c_value = pmpoints_meassurement[serie.name]
38
                    try:
39
                        rrdtool.update(str('%s'%(rrdfilename)), str(period_epoch+":" + c_value))
40
                        print "done updating"
41
                    except Exception as e:
42
                        print e
43
                        continue
44
                else:
45
                    print "No rrd file found"
46
                    pass 
47

  
48
from graphs.models import *
49

  
50
            
b/core/tests.py
1
"""
2
This file demonstrates two different styles of tests (one doctest and one
3
unittest). These will both pass when you run "manage.py test".
4

  
5
Replace these with more appropriate tests for your application.
6
"""
7

  
8
from django.test import TestCase
9

  
10
class SimpleTest(TestCase):
11
    def test_basic_addition(self):
12
        """
13
        Tests that 1 + 1 always equals 2.
14
        """
15
        self.failUnlessEqual(1 + 1, 2)
16

  
17
__test__ = {"doctest": """
18
Another way to test that 1 + 1 is equal to 2.
19

  
20
>>> 1 + 1 == 2
21
True
22
"""}
23

  
b/core/views.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from core.models import *
5
from graphs.models import *
6
from django.core.urlresolvers import reverse
7
from django.shortcuts import render_to_response,get_object_or_404
8
from django.http import HttpResponse,HttpResponseRedirect,Http404
9
from django.conf import settings
10

  
11
from django.views.decorators.cache import cache_page
12
from django.views.decorators.cache import never_cache
13
from django.core.cache import cache
14

  
15
from django.template import RequestContext
16

  
17
import json
18

  
19
import datetime
20
import time
21
import rrdtool
22
import tempfile
23
import os
24
import re
25
import random
26
import pprint
27

  
28
from gevent.pool import Pool
29
from gevent.timeout import Timeout
30

  
31
from util.analyze import analyze
32

  
33
def index(request):
34
    luns = Lun.objects.all()
35
    graphs = Graph.objects.all()
36
    nl = cache.get('lun:stats')
37
    if nl is None:
38
        nl = analyze(luns)
39
    urls = []
40
    
41
    for i in nl:
42
        lun = luns.get(pk=i['lunpk'])
43
        graph = graphs.get(pk=i['graph'])
44
        if int(i['avg']) == 0:
45
            continue 
46
        #for graph in lungraphs:
47
        graph_dict = {}
48
        graph.url = reverse(thumb, args=(graph.pk,))
49
        graph_dict['url'] = graph.url
50
        graph_dict['graph'] = graph
51
        graph_dict['lun'] = lun.name
52
        urls.append(graph_dict)
53
    ret = {
54
           'urls' :urls
55
           }
56
    return render_to_response('media.html', ret,
57
                context_instance=RequestContext(request))
58

  
59
def graphjson(request, graph_id):
60
    ret = {"graph_pk": graph_id}
61
    return render_to_response('graph.html', ret,  
62
                context_instance=RequestContext(request))
63

  
64

  
65
def graphperiods(request, graph_id):
66
    graph =  Graph.objects.get(pk=graph_id)
67
    return render_to_response('graphperiods.html', {'graph': graph, },
68
                context_instance=RequestContext(request))
69

  
70

  
71
def get_all_json(lazy=False, legend=False, start='-2d', end='-8m'):
72
    graphs = Graph.objects.all()
73
    kwargs = { 'start': str(start), 'end': str(end), 'lazy': lazy, 'legend': legend, 'jsonexport':True }
74
    results = []
75
    for graph in graphs:
76
        results.extend(graph.create_graph(path=None, **kwargs))
77
#     p = Pool(40)
78
#     
79
#     def _get_graphs_json(graph):
80
#         t = Timeout(5)
81
#         t.start()
82
#         try:
83
#             results.extend(graph.create_graph(path=None, **kwargs))
84
#         except (Timeout):
85
#             pass
86
#         finally:
87
#             t.cancel()
88
#     p.imap(_get_graphs_json, graphs)
89
    return results
90
    
91

  
92
def getpngdata(path,static):
93

  
94
    file = open(path,'r')
95
    data = file.read()
96
    file.close()
97

  
98
    if static == False:
99
        os.remove(path)
100

  
101
    response = HttpResponse(mimetype='image/png')
102
    response.write(data)
103
    return response
104

  
105
def drawpng(request, graph_id, static=True, lazy=False, legend=False, start='-30h', end='-8m', jsonexport=False):
106

  
107
    graph = get_object_or_404(Graph,pk=graph_id)
108
    if static == True:
109
        lazy = True
110
        path = '%s/%s%s%s.png' % (settings.RRD_PNG_STATICPATH, graph_id, start, end)
111
        try:
112
            statinfo = os.stat(path)
113
            if time.time() - statinfo.st_mtime < 300 :
114
                return getpngdata(path,static)
115
        except OSError:
116
            pass
117
    else:
118
        path = tempfile.NamedTemporaryFile().name
119
    '''Removing unicode from strings while constructing kwargs'''
120
    kwargs = { 'start': str(start), 'end': str(end), 'lazy': lazy, 'legend': legend, 'jsonexport':jsonexport }
121
    result = graph.create_graph(path, **kwargs)
122
    if result == None:
123
        raise Http404
124
    if jsonexport:
125
        return HttpResponse(json.dumps(result), mimetype="application/json")
126
    return getpngdata(path, static)
127

  
128

  
129
def thumb(request,graph_id,legend=True,lazy=True,static=True):
130

  
131
    start='-1d'
132
    end='-300'
133

  
134
    graph = get_object_or_404(Graph,pk=graph_id)
135

  
136
    path = '%s/thumb-%s.png' % (settings.RRD_PNG_STATICPATH, graph_id)
137
    try:
138
        statinfo = os.stat(path)
139
        if time.time() - statinfo.st_mtime < 300 :
140
            return getpngdata(path,static)
141
    except OSError:
142
        pass
143

  
144
    '''Removing unicode from strings while constructing kwargs'''
145
    kwargs = { 'start': str(start), 'end': str(end), 'lazy': lazy, 'legend': legend, 'thumb': True, }
146

  
147
    result=graph.create_graph(path,**kwargs)
148
    return getpngdata(path,static)
149

  
b/graphs/admin.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from graphs.models import *
5

  
6
from django.contrib import admin
7

  
8
admin.site.register(Graph)
9
admin.site.register(GraphLabel)
10
admin.site.register(RrdFile)
11
admin.site.register(DataSource)
12
admin.site.register(GraphDataSource)
b/graphs/management/commands/__dbfuncs__.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.conf import settings
5
from core.models import *
6
from graphs.models import *
7
from datetime import datetime, timedelta
8
import time
9
import sys
10

  
11
def expireoldgraphs(node, type):
12
    if node.snmpmanagednode.perm_fail == 1:
13
        graphs = Graph.objects.filter(host=node)
14
        graphs.delete()
15
        return
16
    graphs = Graph.objects.filter(host=node, type=type,
17
                                 date__lt=datetime.now()-timedelta(settings.RG_EXPIRE_DAYS))
18
    graphs.delete()
19

  
20
def dbreg(type, rrdpath, lun, **kwargs):
21
    try:
22
        rrdfile = RrdFile.objects.get(path=rrdpath)
23
    except RrdFile.DoesNotExist, e:
24
        rrdfile = RrdFile(path=rrdpath)
25
        rrdfile.save()
26
    except RrdFile.MultipleObjectsReturned, e:
27
        #This should never happen
28
        sys.stderr.write('Multiple RrdFiles in database. This should never happen\n')
29
        sys.stderr.write('RRD File: %s\n' % rrdpath)
30
        sys.stderr.write('Refusing to proceed. Fix the problem\n')
31
        sys.exit()
32

  
33
    if 'tags' in kwargs:
34
        tags = kwargs['tags']
35
    else:
36
        tags = []
37
    #Making the assumption that an RRDfile will never contain data sources not in the database
38
    #Also since the rrdfiles are created by mrtg datasource names are hardcoded.
39
    if rrdfile.datasource_set.count() == 0:
40
        ds0 = DataSource(name='ds0')
41
        ds0.rrdfile=rrdfile
42
        ds0.save()
43
        ds0.tags.clear()
44
        for tag in tags:
45
            if 'ds1:' in tag:
46
                continue
47
            ds0.tags.add(tag)
48
        
49
        # ds0.content_object = content
50
        
51
        ds1 = DataSource(name='ds1')
52
        ds1.rrdfile=rrdfile
53
        ds1.save()
54
        ds1.tags.clear()
55
        for tag in tags:
56
            if 'ds0:' in tag:
57
                continue
58
            ds1.tags.add(tag) 
59
        # ds1.content_object = content
60
        
61
        datasources=[ds0,ds1]
62
    else:
63
        datasources=rrdfile.datasource_set.all()
64
        for tempds in datasources:
65
            tempds.tags.clear()
66
            for tag in tags:
67
                if 'ds1:' in tag and tempds.name == 'ds0':
68
                    continue
69
                if 'ds0:' in tag and tempds.name == 'ds1':
70
                    continue
71
                tempds.tags.add(tag) 
72
            
73

  
74
    datasourcespk = [ds.pk for ds in datasources]
75
    graphs = Graph.objects.filter(type=type,tags__name='lun:%s'%lun.pk,datasources__pk__in=datasourcespk).distinct()
76
    if len(graphs) == 1:
77
        graph = graphs[0]
78
    if len(graphs) < 1:
79
        graph = Graph(type=type)
80
        graph.save()
81
        graph.tags.add('lun:%s'%lun.pk)
82
        if 'description' in kwargs:
83
            graph.tags.add('graphdescription:%s %s'%(lun.name, kwargs['description']))
84
    elif len(graphs) > 1:
85
        sys.stderr.write('Found more than one possible graph. Using oldest one as an approximation: %s\n' % e)
86
        graph = Graph.objects.filter(type=type,tags__name='lun:%s'%lun.pk,datasources__pk__in=datasourcespk).distinct().order_by('-date')[0]
87

  
88
    # Setting various attributes through kwargs
89
    for i in ('pc95','units','logarithmic','label','description'):
90
        if i in kwargs:
91
            setattr(graph,i,kwargs[i])
92

  
93
    graph.save()
94

  
95
    for datasource in datasources:
96
        try:
97
            grds = GraphDataSource.objects.get(graph=graph,datasource=datasource)
98
        except GraphDataSource.DoesNotExist, e:
99
            grds = GraphDataSource(graph=graph,datasource=datasource)
100
        # Some defaults
101
        grds.args = ''
102
        grds.function = 'LINE1'
103
        grds.color = '#0000bbbb'
104
        # Hardcoded default. Max Datasources in a graph.
105
        for n in range(0,1024):
106
            for i in ( 'function', 'color', 'args', 'legend', 'stackorder' ):
107
                if datasource.name == 'ds%s' % n and 'ds%s%s' % ( n, i ) in kwargs:
108
                    setattr(grds,i,kwargs['ds%s%s' % ( n, i)])
109
        grds.save()
110

  
111
    try:
112
        f = open(rrdpath)
113
    except IOError:
114
        necreation_epoch = int(time.time())
115
        f = rrdtool.create(str(rrdpath), str("--start"), str(1349965501),  [str('DS:ds0:COUNTER:300:U:U'), str('DS:ds1:COUNTER:300:U:U')], str('RRA:AVERAGE:0.5:1:288'), str('RRA:AVERAGE:0.5:3:672'), str('RRA:AVERAGE:0.5:12:744'), str('RRA:AVERAGE:0.5:72:1460'))
116

  
b/graphs/management/commands/__genrgconfig__.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3
from __dbfuncs__ import *
4
from core.models import *
5
from django.conf import settings
6
import rrdtool
7
import time
8
import datetime
9

  
10

  
11

  
12
def diskio_genconfig(lun):
13
    devfilename = "lun_%s" % lun.pk
14
    seriesfilename = 'blocksiostats'
15
    target = "%s_%s" % (devfilename, seriesfilename)
16
    rrdpath = settings.RRDFILEDIR + '/%s.rrd' % target
17
    
18
    dbreg('diskio', rrdpath, lun, description='Disk IO' , label=target,
19
            ds0legend='Blocks Read', ds1legend='Blocks Write',
20
            ds0function='AREA', ds1function='LINE1',
21
            ds0color='#00bb00bb', ds1color='#0000bbbb',
22
            pc95=False, units='blocks', tags=['lun:%s'%lun.pk, 'ds0:blocksread', "ds1:blockswrite", "type:blocksiostats"])
23

  
24
def requests_genconfig(lun):
25
    devfilename = "lun_%s" % lun.pk
26
    seriesfilename = 'requeststats'
27
    target = "%s_%s" % (devfilename, seriesfilename)
28
    rrdpath = settings.RRDFILEDIR + '/%s.rrd' % target
29
    
30
    dbreg('requests', rrdpath, lun, description='Requests IO' , label=target,
31
            ds0legend='Read Requests', ds1legend='Write Requests',
32
            ds0function='AREA', ds1function='LINE1',
33
            ds0color='#00bb00bb', ds1color='#0000bbbb',
34
            pc95=False, units='reuests', tags=['lun:%s'%lun.pk, 'ds0:readrequests', "ds1:writerequests", "type:requeststats"])
35
    
36

  
37

  
b/graphs/management/commands/analyze.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.core.management.base import BaseCommand, CommandError
5
from django.conf import settings
6
from core.models import *
7
from util.analyze import analyze
8

  
9
class Command(BaseCommand):
10

  
11
    def handle(self, *args, **options):
12
        luns = Lun.objects.all()
13
        analyze(luns)
14
        print "Done"
15
        return
16

  
b/graphs/management/commands/clear_rrds.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.core.management.base import BaseCommand, CommandError
5
from django.conf import settings
6
import sys
7
import os
8

  
9

  
10
class Command(BaseCommand):
11
#    help = "Creates the configuration for rg project. Will output mrtg, collectd and munin configuration files as well as populate accordingly the rg database."
12
#    args = "[[device]]"
13
#    label = "Domain of devices to be run for(e.g. example.com) and type of configuration run(or all if you wish everything). An optional device name can also be given to run the program for a single node. Valid types are" + ", ".join(settings.RG_CREATE_TYPES) 
14

  
15
    def handle(self, *args, **options):
16
        dir = os.listdir(settings.RRDFILEDIR)
17
        for fname in dir:
18
            os.remove("%s/%s"%(settings.RRDFILEDIR,fname))
19
            print "Removed %s"%fname
20
        print "done"
b/graphs/management/commands/rgconf.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.core.management.base import BaseCommand, CommandError
5
from django.conf import settings
6
from core.models import *
7
from datetime import datetime, timedelta
8
import sys
9
import os
10
from __genrgconfig__ import *
11
from __dbfuncs__ import *
12

  
13
class Command(BaseCommand):
14

  
15
    def handle(self, *args, **options):
16
        luns = Lun.objects.all()
17
        types = settings.LUN_GRAPH_TYPES
18
        for lun in luns:
19
            for type in types:
20
                self.dbupdate(lun, type)
21
            print 'Node: %s ...done' % lun.pk
22

  
23
    def dbupdate(self, device, type):
24
        # Ok. A couple of hacks here. Instead of a if then elif then elif.... for each type
25
        # we get first from the settings dictionnary the confdir and then the function used 
26
        # for this type from the globals() dictionary
27
        try:
28
#            dir = getattr(settings,'RG_%s_CONFDIR' % type.upper())
29
#            self.checkdir(dir)
30
            
31
            func = globals()['%s_genconfig' % type]
32
            func(device)
33
            print "Done with device"
34
        except AttributeError as e:
35
            print "%s is not going to be configured because %s" % (type, e)
36
            return
37

  
b/graphs/models.py
1
# -*- coding: utf-8 -*- vim:encoding=utf-8:
2
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
3

  
4
from django.db import models
5
from django.conf import settings
6
from django.utils.encoding import iri_to_uri
7
from django.contrib.contenttypes.models import ContentType
8
from django.contrib.contenttypes import generic
9
from django.core.urlresolvers import reverse
10
from taggit.managers import TaggableManager
11
from util.rrd2json import rrd2json
12
import rrdtool, json
13
from core.models import Lun
14

  
15
from numpy import *
16

  
17
class RrdFile(models.Model):
18
    RRDPATH = settings.RRDFILEDIR
19

  
20
    path = models.FilePathField(path=RRDPATH,match='.*\.rrd$', recursive=True,max_length=255)
21

  
22
    def __unicode__(self):
23
        return 'RRD file: %s' % (self.path)
24

  
25
class DataSource(models.Model):
26
    name = models.CharField(max_length=30)
27
    rrdfile = models.ForeignKey(RrdFile)
28
    tags = TaggableManager()
29

  
30
    def __unicode__(self):
31
        return 'Data Source: %s, RRD file: %s' % (self.name,self.rrdfile.path)
32

  
33
    def get_plain_graphs(self):
34
        graphs = self.graph_set.filter(aggrfunction=None)
35
        return graphs
36

  
37
class Graph(models.Model):
38
    AGGREGATE_FUNCTIONS = (
39
        ( u'+', u'Addition' ),
40
        ( u'-', u'Substraction' ),
41
        ( u'*', u'Multiplication' ),
42
        ( u'/', u'Division' ),
43
        ( u'%', u'Modulo' ),
44
        ( u'ADDNAN', u'Nan-aware Addition'),
45
    )
46

  
47
    datasources = models.ManyToManyField(DataSource,through='GraphDataSource')
48
    date = models.DateTimeField(auto_now=True)
49
    type = models.CharField(max_length=35)
50
    label = models.CharField(max_length=60, null=True)
51
    units = models.CharField(max_length=30)
52
    description = models.CharField(max_length=255, null=True)
53
    pc95 = models.BooleanField(verbose_name="95th Percentile Creation",default=False)
54
    logarithmic = models.BooleanField(verbose_name="Logarithmic Y-Axis Scaling",default=False)
55
    aggrfunction = models.CharField(max_length=10, null=True, blank=True, choices=AGGREGATE_FUNCTIONS,
56
                                     verbose_name='Aggregate Function')
57
    aggrlabel = models.CharField(max_length=10, null=True,blank=True, verbose_name='Aggregate Function Label' )
58
    aggrgfunction = models.CharField(max_length=10, null=True, blank=True, verbose_name='Aggregate Graphing Function')
59
    aggrcolor = models.CharField(max_length=9, blank=True)
60
    tags = TaggableManager()
61

  
62
    class Meta:
63
        ordering=('label',)
64

  
65
    def __unicode__(self):
66
        return 'Tags: %s, type: %s, label: %s, description: %s' % (self.tags.all(), self.type,
67
                                                        self.label, self.description)
68

  
69
    def create_graph(self, path, **kwargs):
70
        BOTTOMLOGO = settings.RRD_BOTTOMLOGO
71

  
72
        start = kwargs['start']
73
        end = kwargs['end']
74
        lazy = kwargs['lazy']
75
        legend = kwargs['legend']
76

  
77
        args = list()
78
        tempgraphargs = list()
79

  
80
        args.append(path)
81
        if lazy == True:
82
            args.append('--lazy')
83
        legend = True
84
        if legend == False:
85
            args.append('--no-legend')
86
        args.append('-s %s ' % start )
87
        args.append('-e %s ' % end )
88
        args.append('-W %s' % ( BOTTOMLOGO ))
89
        tags = [t.name for t in self.tags.all() if 'graphdescription' in t.name]
90
        if len(tags) > 0:
91
            description = ",".join([tname.split(':')[1] for tname in tags])
92
            args.append('-t %s' % (description[:60]))
93
#         if self.description != None:
94
#             args.append('-t %s' % (self.description[:60]))
95
#         else:
96
#             args.append('-t %s ' % self.type)
97

  
98
        args.append('-v %s' % ( self.units ))
99
        args.append('--slope-mode')
100
        if 'thumb' in kwargs:
101
            args.append('-h 160')
102
            args.append('-w 360')
103

  
104
#             args.append('-j')
105

  
106
#        # Logarithmic care
107
#        if self.logarithmic == True:
108
#            args.append('--logarithmic')
109
#            args.append('--units=si')
110
#        else:
111
#            args.append('-l %s' % ( '0' ))
112

  
113
        dsnames=list()
114
        if 'thumb' not in kwargs:
115
            args.append('-h 180')
116
            args.append('-w 470')
117
        args.append("-c")
118
        args.append("FONT#333333")
119
        args.append("-c")
120
        args.append("AXIS#333333")
121
        args.append("-c")
122
        args.append("ARROW#3D2828")
123
        args.append("-n")
124
        args.append("DEFAULT:0:Arial")
125
        graphdatasources = self.graphdatasource_set.all()
126
        legends = [grds.legend for grds in graphdatasources]
127
        if self.aggrlabel != None:
128
            legends.append(self.aggrlabel)
129
        maxl = len(max(legends, key=len))
130

  
131
        if self.pc95:
132
            pc95cdef = 'CDEF:pc95bits='
133
        if self.aggrfunction:
134
            aggrfcdef= 'CDEF:aggrf='
135

  
136
        i=0
137
        for grds in graphdatasources:
138
            name = grds.datasource.name
139
            dsnames.append(name)
140
            # Internal variable for usage with rrdtool DEFs,CDEFs. The d at the start is dummy and is
141
            # used because rrdtool seems to want its CDEFs,DEFs starting with a letter in some cases
142
            grname = 'd%s' % grds.pk
143
            filename = grds.datasource.rrdfile.path
144
            args.append('DEF:%s=%s:%s:AVERAGE' % ( grname, filename, name ))
145
            if grds.graphable:
146
                padding = ' ' * (maxl - len(grds.legend))
147
                if grds.legend != '':
148
                    tempstack = '%s:%s%s%s:%s%s\:\g' % ( grds.function, grname, grds.args, grds.color, grds.legend, padding)
149
                else:
150
                    tempstack = '%s:%s%s%s:%s' % ( grds.function, grname, grds.args, grds.color, grds.legend)
151
                if grds.stackorder != 0:
152
                    tempstack = tempstack + ':STACK'
153
                tempgraphargs.append(tempstack)
154
                if grds.legend != '':
155
                    tempgraphargs.append('%s:%s%s:%s:%s' % ( 'GPRINT', grname, grds.args, 'MAX', '\tMax\: %4.2lf%s\g' ))
156
                    tempgraphargs.append('%s:%s%s:%s:%s' % ( 'GPRINT', grname, grds.args, 'MIN', '\tMin\: %4.2lf%s\g' ))
157
                    tempgraphargs.append('%s:%s%s:%s:%s' % ( 'GPRINT', grname, grds.args, 'AVERAGE', '\tAvg\: \t%4.2lf%s\g' ))
158
                    tempgraphargs.append('%s:%s%s:%s:%s' % ( 'GPRINT', grname, grds.args, 'LAST', '\tLast\: %4.2lf%s\\n' ))
159

  
160

  
161
            if self.pc95:
162
                pc95cdef = pc95cdef + grname  + grds.args + ','
163

  
164
            if self.aggrfunction:
165
                aggrfcdef = '%s%s%s,' % ( aggrfcdef, grname, grds.args )
166
                if i > 0:
167
                    aggrfcdef = aggrfcdef + self.aggrfunction + ','
168
            i = i + 1
169

  
170
        if self.pc95:
171
            pc95cdef = pc95cdef + 'MAX'
172
            args.append(pc95cdef)
173
            args.append('VDEF:pc95=pc95bits,95,PERCENT')
174

  
175
        if not self.aggrfunction:
176
            args.extend(tempgraphargs)
177

  
178
        if self.aggrfunction:
179
            padding = ' ' * (maxl - len(self.aggrlabel))
180
            args.append('%s:aggrf%s:%s%s\:\g' % (self.aggrgfunction, self.aggrcolor, self.aggrlabel, padding))
181
            args.append('GPRINT:aggrf:MAX:\tMax\: %4.2lf%s\g')
182
            args.append('GPRINT:aggrf:MIN:\tMin\: %4.2lf%s\g')
183
            args.append('GPRINT:aggrf:AVERAGE:\tAvg\: %4.2lf%s\g')
184
            args.append('GPRINT:aggrf:LAST:\tLast\: %4.2lf%s\\n')
185

  
186
        if self.pc95:
187
            args.append('COMMENT: \\n')
188
            args.append('HRULE:pc95#ff0000a0:95th Percentile\::dashes')
189
            args.append('GPRINT:pc95:%4.2lf%s\\n')
190

  
191
        # This is for rrdtool which hates unicode
192
        args=[str(val) for val in args]
193
        try:
194
            return rrdtool.graphv(*args)
195
        except Exception as e:
196
            print e
197

  
198
            return None
199
    
200
    
201
    def analyze_graph(self, start="-2d", end='-10m', extended=False):
202
        graphdatasources = self.graphdatasource_set.all()
203
        for grds in graphdatasources:
204
            filename = grds.datasource.rrdfile.path
205
            graph_data = rrdtool.fetch(str('%s'%filename), str('AVERAGE'), str('-s'), str("%s"%start), str('-e'), str('%s'%end))
206
            json_ret = []
207
            tojsondict = rrd2json(graph_data)
208
            graphdss = self.datasources.all()
209
            if tojsondict:
210
                for k,v in tojsondict.items():
211
                    tojson = {}
212
                    na = array(map(lambda x:x[1], v))
213
                    tojson['label']= ":".join([t.name for t in graphdss.get(name=k).tags.all()])
214
                    if extended:
215
                        tojson['data'] = v
216
                    tojson['graph'] = self.pk
217
                    tojson['lunpk'] = Lun.objects.get(pk=int(self.tags.get(name__contains='lun').name.replace('lun:',''))).pk
218
                    #tojson['ds'] =  graphdss.get(name=k).tags.get(name__startswith='ds').name
219
                    #tojson['avg'] =  reduce(lambda x, y: x+y, map(lambda x:x[1], v)) / len(v)
220
                    tojson['avg'] =  na.mean()
221
                    tojson['dev'] =  na.std()
222
                    json_ret.append(tojson)
223
        jret = [sorted(json_ret, key=lambda k: k['dev'], reverse=True)[0]]
224
        return jret
225
        
226

  
227
    @models.permalink
228
    def get_absolute_url(self):
229
        return ('core.views.drawpng', [str(self.id)])
230

  
231
    @models.permalink
232
    def get_draw_url(self):
233
        return ('core.views.drawpng', [str(self.id)])
234

  
235
    @models.permalink
236
    def get_thumb_url(self):
237
        return ('core.views.thumb', [str(self.id)])
238

  
239
    @models.permalink
240
    def get_hour_url(self):
241
        return ('core.views.drawpng', [str(self.id),'-1h','-300'])
242

  
243
    @models.permalink
244
    def get_day_url(self):
245
        return ('core.views.drawpng', [str(self.id),'-2d','-30m'])
246

  
247
    @models.permalink
248
    def get_week_url(self):
249
        return ('core.views.drawpng', [str(self.id),'-1w','-30m'])
250

  
251
    @models.permalink
252
    def get_month_url(self):
253
        return ('core.views.drawpng', [str(self.id),'-1m','-30m'])
254

  
255
    @models.permalink
256
    def get_year_url(self):
257
        return ('core.views.drawpng', [str(self.id),'-1y','-30m'])
258

  
259

  
260
        
261
class GraphDataSource(models.Model):
262
    GRAPHFUNCTIONS = (
263
        ( u'PRINT', u'PRINT' ),
264
        ( u'GPRINT', u'GPRINT' ),
265
        ( u'VRULE', u'VRULE' ),
266
        ( u'HRULE', u'HRULE' ),
267
        ( u'LINE',  u'LINE' ),
268
        ( u'AREA', u'AREA' ),
269
        ( u'TICK', u'TICK' ),
270
        ( u'SHIFT', u'SHIFT' ),
271
        ( u'PRINT', u'PRINT' ),
272
        ( u'', u'' ),
273
    )
274

  
275
    graph = models.ForeignKey(Graph)
276
    datasource = models.ForeignKey(DataSource)
277
    graphable = models.BooleanField(default=True)
278
    legend = models.CharField(max_length=35, null=True, blank=True)
279
    function = models.CharField(max_length=5, null=True, choices=GRAPHFUNCTIONS )
280
    color = models.CharField(max_length=9, blank=True)
281
    args = models.CharField(max_length=255, null=True)
282
    stackorder = models.PositiveIntegerField(default=0)
283

  
284
    class Meta:
285
        ordering = ('stackorder',)
286

  
287
    def __unicode__(self):
288
        return 'Graph: %s, %s, Graphing Variables: %s:%s%s:%s' % (self.graph.__unicode__(), self.datasource.__unicode__(), self.function, self.args, self.color, self.legend)
289

  
290

  
291
class GraphLabel(models.Model):
292
    graph = models.ManyToManyField(Graph)
293
    key = models.CharField(max_length=30,db_index=True)
294
    data = models.CharField(max_length=255)
295

  
296
    def __unicode__(self):
297
        return 'Key: %s, data: %s' % (self.key,self.data)
298

  
b/graphs/tests.py
1
"""
2
This file demonstrates two different styles of tests (one doctest and one
3
unittest). These will both pass when you run "manage.py test".
4

  
5
Replace these with more appropriate tests for your application.
6
"""
7

  
8
from django.test import TestCase
9

  
10
class SimpleTest(TestCase):
11
    def test_basic_addition(self):
12
        """
13
        Tests that 1 + 1 always equals 2.
14
        """
15
        self.failUnlessEqual(1 + 1, 2)
16

  
17
__test__ = {"doctest": """
18
Another way to test that 1 + 1 is equal to 2.
19

  
20
>>> 1 + 1 == 2
21
True
22
"""}
23

  
b/graphs/views.py
1
# Create your views here.
b/manage.py
1
#!/usr/bin/python
2
from django.core.management import execute_manager
3
try:
4
    import settings # Assumed to be in the same directory.
5
except ImportError:
6
    import sys
7
    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
8
    sys.exit(1)
9

  
10
if __name__ == "__main__":
11
    execute_manager(settings)
b/settings.py.dist
1
# Django settings for lunstats project.
2

  
3
DEBUG = True
4
TEMPLATE_DEBUG = DEBUG
5

  
6
import os
7
here = lambda x: os.path.join(os.path.abspath(os.path.dirname(__file__)), x)
8

  
9
ADMINS = (
10
    # ('Your Name', 'your_email@domain.com'),
11
)
12

  
13
MANAGERS = ADMINS
14

  
15
DATABASES = {
16
    'default': {
17
        'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
18
        'NAME': ''                      # Or path to database file if using sqlite3.
19
        'USER': '',                      # Not used with sqlite3.
20
        'PASSWORD': '',                  # Not used with sqlite3.
21
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
22
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
23
    }
24
}
25

  
26
# Hosts/domain names that are valid for this site; required if DEBUG is False
27
# See https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#allowed-hosts
28
ALLOWED_HOSTS = []
29

  
30
# Local time zone for this installation. Choices can be found here:
31
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
32
# although not all choices may be available on all operating systems.
33
# On Unix systems, a value of None will cause Django to use the same
34
# timezone as the operating system.
35
# If running in a Windows environment this must be set to the same as your
36
# system time zone.
37
TIME_ZONE = 'Europe/Athens'
38

  
39
# Language code for this installation. All choices can be found here:
40
# http://www.i18nguy.com/unicode/language-identifiers.html
41
LANGUAGE_CODE = 'en-us'
42

  
43
SITE_ID = 1
44

  
45
# If you set this to False, Django will make some optimizations so as not
46
# to load the internationalization machinery.
47
USE_I18N = True
48

  
49
# If you set this to False, Django will not format dates, numbers and
50
# calendars according to the current locale
51
USE_L10N = True
52

  
53
# Absolute path to the directory that holds media.
54
# Example: "/home/media/media.lawrence.com/"
55
MEDIA_ROOT = ''
56

  
57
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
58
# trailing slash if there is a path component (optional in other cases).
59
# Examples: "http://media.lawrence.com", "http://example.com/media/"
60
MEDIA_URL = ''
61

  
62
STATIC_URL = ''
63

  
64
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
65
# trailing slash.
66
# Examples: "http://foo.com/media/", "/media/".
67
ADMIN_MEDIA_PREFIX = '/media/'
68

  
69
# Make this unique, and don't share it with anybody.
70
SECRET_KEY = '#8(lzp_r(u4r6(+_&do44v-)8zrnl=272tob+#vvd378s8%kit'
71

  
72
# List of callables that know how to import templates from various sources.
73
TEMPLATE_LOADERS = (
74
    'django.template.loaders.filesystem.Loader',
75
    'django.template.loaders.app_directories.Loader',
76
#     'django.template.loaders.eggs.Loader',
77
)
78

  
79
MIDDLEWARE_CLASSES = (
80
    'django.middleware.common.CommonMiddleware',
81
    'django.contrib.sessions.middleware.SessionMiddleware',
82
    'django.middleware.csrf.CsrfViewMiddleware',
83
    'django.contrib.auth.middleware.AuthenticationMiddleware',
84
    'django.contrib.messages.middleware.MessageMiddleware',
85
)
86

  
87
ROOT_URLCONF = 'lunstats.urls'
88

  
89
TEMPLATE_DIRS = (
90
    here('templates'),
91
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
92
    # Always use forward slashes, even on Windows.
93
    # Don't forget to use absolute paths, not relative paths.
94
)
95

  
96
INSTALLED_APPS = (
97
    'django.contrib.auth',
98
    'django.contrib.contenttypes',
99
    'django.contrib.sessions',
100
    'django.contrib.sites',
101
    'django.contrib.messages',
102
    'core',
103
    'graphs',
104
    'taggit',
105
    'django_extensions',
106
    'south',
107
    # Uncomment the next line to enable the admin:
108
    'django.contrib.admin',
109
    # Uncomment the next line to enable admin documentation:
110
    # 'django.contrib.admindocs',
111
)
112

  
113
CACHE_BACKEND = 'memcached://127.0.0.1:11211/?timeout=50000'
114

  
115

  
116
RRDFILEDIR = here('rrds')
117
RRD_PNG_STATICPATH = here('rrd_pngs')
118
RRD_BOTTOMLOGO = u''
119

  
120
LUN_COUNTERS = (
121
    ( u'diskio', u'Disk IO' ),
122
    ( u'requests', u'Requests' ),
123
    )
124

  
125
LUN_COUNTER_SERIES = {
126
                      'diskio':['blocksread', 'blockswrite'], 
127
                      'requests':['writereq', 'readreq']
128
                      }
129

  
130
LUN_GRAPH_TYPES = [type[0] for type in LUN_COUNTERS]
b/static/css/isotope.css
1
.isotope,
2
.isotope .isotope-item {
3
  /* change duration value to whatever you like */
4
  -webkit-transition-duration: 0.8s;
5
     -moz-transition-duration: 0.8s;
6
      -ms-transition-duration: 0.8s;
7
       -o-transition-duration: 0.8s;
8
          transition-duration: 0.8s;
9
}
10

  
11
.isotope {
12
  -webkit-transition-property: height, width;
13
     -moz-transition-property: height, width;
14
      -ms-transition-property: height, width;
15
       -o-transition-property: height, width;
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff