Revision 7fcec5d7

b/.gitignore
33 33
snf-quotaholder-app/quotaholder_django/version.py
34 34
*.iml
35 35
*.graffle
36
snf-stats/synnefo_stats/version.py
b/snf-stats/COPYRIGHT
1
Copyright 2011 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.
b/snf-stats/Changelog
1
Changelog
2
---------
3

  
4
v0.1.0
5
*******
6
	* Initial release
b/snf-stats/MANIFEST.in
1
include README Changelog
2
include distribute_setup.py
b/snf-stats/README
1
README
2
=======
3

  
4
snf-stats
b/snf-stats/conf/20-snf-stats-settings.conf
1
## -*- coding: utf-8 -*-
2
##
3
## Image properties
4
#IMAGE_WIDTH = 210
5
#WIDTH = 68
6
#HEIGHT = 10
7

  
8
## Path settings
9
#RRD_PREFIX = "/var/lib/collectd/rrd/"
10
#GRAPH_PREFIX = "/var/cache/snf-stats/"
11

  
12
## Font settings
13
#FONT = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf"
14
#FONT = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
15

  
16
## Bar settings
17
#BAR_BORDER_COLOR = (0x5c, 0xa1, 0xc0)
18
#BAR_BG_COLOR = (0xea, 0xea, 0xea)
b/snf-stats/distribute_setup.py
1
#!python
2
"""Bootstrap distribute installation
3

  
4
If you want to use setuptools in your package's setup.py, just include this
5
file in the same directory with it, and add this to the top of your setup.py::
6

  
7
    from distribute_setup import use_setuptools
8
    use_setuptools()
9

  
10
If you want to require a specific version of setuptools, set a download
11
mirror, or use an alternate download directory, you can do so by supplying
12
the appropriate options to ``use_setuptools()``.
13

  
14
This file can also be run as a script to install or upgrade setuptools.
15
"""
16
import os
17
import sys
18
import time
19
import fnmatch
20
import tempfile
21
import tarfile
22
from distutils import log
23

  
24
try:
25
    from site import USER_SITE
26
except ImportError:
27
    USER_SITE = None
28

  
29
try:
30
    import subprocess
31

  
32
    def _python_cmd(*args):
33
        args = (sys.executable,) + args
34
        return subprocess.call(args) == 0
35

  
36
except ImportError:
37
    # will be used for python 2.3
38
    def _python_cmd(*args):
39
        args = (sys.executable,) + args
40
        # quoting arguments if windows
41
        if sys.platform == 'win32':
42
            def quote(arg):
43
                if ' ' in arg:
44
                    return '"%s"' % arg
45
                return arg
46
            args = [quote(arg) for arg in args]
47
        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
48

  
49
DEFAULT_VERSION = "0.6.10"
50
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
51
SETUPTOOLS_FAKED_VERSION = "0.6c11"
52

  
53
SETUPTOOLS_PKG_INFO = """\
54
Metadata-Version: 1.0
55
Name: setuptools
56
Version: %s
57
Summary: xxxx
58
Home-page: xxx
59
Author: xxx
60
Author-email: xxx
61
License: xxx
62
Description: xxx
63
""" % SETUPTOOLS_FAKED_VERSION
64

  
65

  
66
def _install(tarball):
67
    # extracting the tarball
68
    tmpdir = tempfile.mkdtemp()
69
    log.warn('Extracting in %s', tmpdir)
70
    old_wd = os.getcwd()
71
    try:
72
        os.chdir(tmpdir)
73
        tar = tarfile.open(tarball)
74
        _extractall(tar)
75
        tar.close()
76

  
77
        # going in the directory
78
        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
79
        os.chdir(subdir)
80
        log.warn('Now working in %s', subdir)
81

  
82
        # installing
83
        log.warn('Installing Distribute')
84
        if not _python_cmd('setup.py', 'install'):
85
            log.warn('Something went wrong during the installation.')
86
            log.warn('See the error message above.')
87
    finally:
88
        os.chdir(old_wd)
89

  
90

  
91
def _build_egg(egg, tarball, to_dir):
92
    # extracting the tarball
93
    tmpdir = tempfile.mkdtemp()
94
    log.warn('Extracting in %s', tmpdir)
95
    old_wd = os.getcwd()
96
    try:
97
        os.chdir(tmpdir)
98
        tar = tarfile.open(tarball)
99
        _extractall(tar)
100
        tar.close()
101

  
102
        # going in the directory
103
        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
104
        os.chdir(subdir)
105
        log.warn('Now working in %s', subdir)
106

  
107
        # building an egg
108
        log.warn('Building a Distribute egg in %s', to_dir)
109
        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
110

  
111
    finally:
112
        os.chdir(old_wd)
113
    # returning the result
114
    log.warn(egg)
115
    if not os.path.exists(egg):
116
        raise IOError('Could not build the egg.')
117

  
118

  
119
def _do_download(version, download_base, to_dir, download_delay):
120
    egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
121
                       % (version, sys.version_info[0], sys.version_info[1]))
122
    if not os.path.exists(egg):
123
        tarball = download_setuptools(version, download_base,
124
                                      to_dir, download_delay)
125
        _build_egg(egg, tarball, to_dir)
126
    sys.path.insert(0, egg)
127
    import setuptools
128
    setuptools.bootstrap_install_from = egg
129

  
130

  
131
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
132
                   to_dir=os.curdir, download_delay=15, no_fake=True):
133
    # making sure we use the absolute path
134
    to_dir = os.path.abspath(to_dir)
135
    was_imported = 'pkg_resources' in sys.modules or \
136
        'setuptools' in sys.modules
137
    try:
138
        try:
139
            import pkg_resources
140
            if not hasattr(pkg_resources, '_distribute'):
141
                if not no_fake:
142
                    _fake_setuptools()
143
                raise ImportError
144
        except ImportError:
145
            return _do_download(version, download_base, to_dir, download_delay)
146
        try:
147
            pkg_resources.require("distribute>="+version)
148
            return
149
        except pkg_resources.VersionConflict:
150
            e = sys.exc_info()[1]
151
            if was_imported:
152
                sys.stderr.write(
153
                "The required version of distribute (>=%s) is not available,\n"
154
                "and can't be installed while this script is running. Please\n"
155
                "install a more recent version first, using\n"
156
                "'easy_install -U distribute'."
157
                "\n\n(Currently using %r)\n" % (version, e.args[0]))
158
                sys.exit(2)
159
            else:
160
                del pkg_resources, sys.modules['pkg_resources']    # reload ok
161
                return _do_download(version, download_base, to_dir,
162
                                    download_delay)
163
        except pkg_resources.DistributionNotFound:
164
            return _do_download(version, download_base, to_dir,
165
                                download_delay)
166
    finally:
167
        if not no_fake:
168
            _create_fake_setuptools_pkg_info(to_dir)
169

  
170
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
171
                        to_dir=os.curdir, delay=15):
172
    """Download distribute from a specified location and return its filename
173

  
174
    `version` should be a valid distribute version number that is available
175
    as an egg for download under the `download_base` URL (which should end
176
    with a '/'). `to_dir` is the directory where the egg will be downloaded.
177
    `delay` is the number of seconds to pause before an actual download
178
    attempt.
179
    """
180
    # making sure we use the absolute path
181
    to_dir = os.path.abspath(to_dir)
182
    try:
183
        from urllib.request import urlopen
184
    except ImportError:
185
        from urllib2 import urlopen
186
    tgz_name = "distribute-%s.tar.gz" % version
187
    url = download_base + tgz_name
188
    saveto = os.path.join(to_dir, tgz_name)
189
    src = dst = None
190
    if not os.path.exists(saveto):  # Avoid repeated downloads
191
        try:
192
            log.warn("Downloading %s", url)
193
            src = urlopen(url)
194
            # Read/write all in one block, so we don't create a corrupt file
195
            # if the download is interrupted.
196
            data = src.read()
197
            dst = open(saveto, "wb")
198
            dst.write(data)
199
        finally:
200
            if src:
201
                src.close()
202
            if dst:
203
                dst.close()
204
    return os.path.realpath(saveto)
205

  
206
def _no_sandbox(function):
207
    def __no_sandbox(*args, **kw):
208
        try:
209
            from setuptools.sandbox import DirectorySandbox
210
            if not hasattr(DirectorySandbox, '_old'):
211
                def violation(*args):
212
                    pass
213
                DirectorySandbox._old = DirectorySandbox._violation
214
                DirectorySandbox._violation = violation
215
                patched = True
216
            else:
217
                patched = False
218
        except ImportError:
219
            patched = False
220

  
221
        try:
222
            return function(*args, **kw)
223
        finally:
224
            if patched:
225
                DirectorySandbox._violation = DirectorySandbox._old
226
                del DirectorySandbox._old
227

  
228
    return __no_sandbox
229

  
230
def _patch_file(path, content):
231
    """Will backup the file then patch it"""
232
    existing_content = open(path).read()
233
    if existing_content == content:
234
        # already patched
235
        log.warn('Already patched.')
236
        return False
237
    log.warn('Patching...')
238
    _rename_path(path)
239
    f = open(path, 'w')
240
    try:
241
        f.write(content)
242
    finally:
243
        f.close()
244
    return True
245

  
246
_patch_file = _no_sandbox(_patch_file)
247

  
248
def _same_content(path, content):
249
    return open(path).read() == content
250

  
251
def _rename_path(path):
252
    new_name = path + '.OLD.%s' % time.time()
253
    log.warn('Renaming %s into %s', path, new_name)
254
    os.rename(path, new_name)
255
    return new_name
256

  
257
def _remove_flat_installation(placeholder):
258
    if not os.path.isdir(placeholder):
259
        log.warn('Unkown installation at %s', placeholder)
260
        return False
261
    found = False
262
    for file in os.listdir(placeholder):
263
        if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
264
            found = True
265
            break
266
    if not found:
267
        log.warn('Could not locate setuptools*.egg-info')
268
        return
269

  
270
    log.warn('Removing elements out of the way...')
271
    pkg_info = os.path.join(placeholder, file)
272
    if os.path.isdir(pkg_info):
273
        patched = _patch_egg_dir(pkg_info)
274
    else:
275
        patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
276

  
277
    if not patched:
278
        log.warn('%s already patched.', pkg_info)
279
        return False
280
    # now let's move the files out of the way
281
    for element in ('setuptools', 'pkg_resources.py', 'site.py'):
282
        element = os.path.join(placeholder, element)
283
        if os.path.exists(element):
284
            _rename_path(element)
285
        else:
286
            log.warn('Could not find the %s element of the '
287
                     'Setuptools distribution', element)
288
    return True
289

  
290
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
291

  
292
def _after_install(dist):
293
    log.warn('After install bootstrap.')
294
    placeholder = dist.get_command_obj('install').install_purelib
295
    _create_fake_setuptools_pkg_info(placeholder)
296

  
297
def _create_fake_setuptools_pkg_info(placeholder):
298
    if not placeholder or not os.path.exists(placeholder):
299
        log.warn('Could not find the install location')
300
        return
301
    pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
302
    setuptools_file = 'setuptools-%s-py%s.egg-info' % \
303
            (SETUPTOOLS_FAKED_VERSION, pyver)
304
    pkg_info = os.path.join(placeholder, setuptools_file)
305
    if os.path.exists(pkg_info):
306
        log.warn('%s already exists', pkg_info)
307
        return
308

  
309
    log.warn('Creating %s', pkg_info)
310
    f = open(pkg_info, 'w')
311
    try:
312
        f.write(SETUPTOOLS_PKG_INFO)
313
    finally:
314
        f.close()
315

  
316
    pth_file = os.path.join(placeholder, 'setuptools.pth')
317
    log.warn('Creating %s', pth_file)
318
    f = open(pth_file, 'w')
319
    try:
320
        f.write(os.path.join(os.curdir, setuptools_file))
321
    finally:
322
        f.close()
323

  
324
_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
325

  
326
def _patch_egg_dir(path):
327
    # let's check if it's already patched
328
    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
329
    if os.path.exists(pkg_info):
330
        if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
331
            log.warn('%s already patched.', pkg_info)
332
            return False
333
    _rename_path(path)
334
    os.mkdir(path)
335
    os.mkdir(os.path.join(path, 'EGG-INFO'))
336
    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
337
    f = open(pkg_info, 'w')
338
    try:
339
        f.write(SETUPTOOLS_PKG_INFO)
340
    finally:
341
        f.close()
342
    return True
343

  
344
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
345

  
346
def _before_install():
347
    log.warn('Before install bootstrap.')
348
    _fake_setuptools()
349

  
350

  
351
def _under_prefix(location):
352
    if 'install' not in sys.argv:
353
        return True
354
    args = sys.argv[sys.argv.index('install')+1:]
355
    for index, arg in enumerate(args):
356
        for option in ('--root', '--prefix'):
357
            if arg.startswith('%s=' % option):
358
                top_dir = arg.split('root=')[-1]
359
                return location.startswith(top_dir)
360
            elif arg == option:
361
                if len(args) > index:
362
                    top_dir = args[index+1]
363
                    return location.startswith(top_dir)
364
        if arg == '--user' and USER_SITE is not None:
365
            return location.startswith(USER_SITE)
366
    return True
367

  
368

  
369
def _fake_setuptools():
370
    log.warn('Scanning installed packages')
371
    try:
372
        import pkg_resources
373
    except ImportError:
374
        # we're cool
375
        log.warn('Setuptools or Distribute does not seem to be installed.')
376
        return
377
    ws = pkg_resources.working_set
378
    try:
379
        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
380
                                  replacement=False))
381
    except TypeError:
382
        # old distribute API
383
        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
384

  
385
    if setuptools_dist is None:
386
        log.warn('No setuptools distribution found')
387
        return
388
    # detecting if it was already faked
389
    setuptools_location = setuptools_dist.location
390
    log.warn('Setuptools installation detected at %s', setuptools_location)
391

  
392
    # if --root or --preix was provided, and if
393
    # setuptools is not located in them, we don't patch it
394
    if not _under_prefix(setuptools_location):
395
        log.warn('Not patching, --root or --prefix is installing Distribute'
396
                 ' in another location')
397
        return
398

  
399
    # let's see if its an egg
400
    if not setuptools_location.endswith('.egg'):
401
        log.warn('Non-egg installation')
402
        res = _remove_flat_installation(setuptools_location)
403
        if not res:
404
            return
405
    else:
406
        log.warn('Egg installation')
407
        pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
408
        if (os.path.exists(pkg_info) and
409
            _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
410
            log.warn('Already patched.')
411
            return
412
        log.warn('Patching...')
413
        # let's create a fake egg replacing setuptools one
414
        res = _patch_egg_dir(setuptools_location)
415
        if not res:
416
            return
417
    log.warn('Patched done.')
418
    _relaunch()
419

  
420

  
421
def _relaunch():
422
    log.warn('Relaunching...')
423
    # we have to relaunch the process
424
    # pip marker to avoid a relaunch bug
425
    if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
426
        sys.argv[0] = 'setup.py'
427
    args = [sys.executable] + sys.argv
428
    sys.exit(subprocess.call(args))
429

  
430

  
431
def _extractall(self, path=".", members=None):
432
    """Extract all members from the archive to the current working
433
       directory and set owner, modification time and permissions on
434
       directories afterwards. `path' specifies a different directory
435
       to extract to. `members' is optional and must be a subset of the
436
       list returned by getmembers().
437
    """
438
    import copy
439
    import operator
440
    from tarfile import ExtractError
441
    directories = []
442

  
443
    if members is None:
444
        members = self
445

  
446
    for tarinfo in members:
447
        if tarinfo.isdir():
448
            # Extract directories with a safe mode.
449
            directories.append(tarinfo)
450
            tarinfo = copy.copy(tarinfo)
451
            tarinfo.mode = 448 # decimal for oct 0700
452
        self.extract(tarinfo, path)
453

  
454
    # Reverse sort directories.
455
    if sys.version_info < (2, 4):
456
        def sorter(dir1, dir2):
457
            return cmp(dir1.name, dir2.name)
458
        directories.sort(sorter)
459
        directories.reverse()
460
    else:
461
        directories.sort(key=operator.attrgetter('name'), reverse=True)
462

  
463
    # Set correct owner, mtime and filemode on directories.
464
    for tarinfo in directories:
465
        dirpath = os.path.join(path, tarinfo.name)
466
        try:
467
            self.chown(tarinfo, dirpath)
468
            self.utime(tarinfo, dirpath)
469
            self.chmod(tarinfo, dirpath)
470
        except ExtractError:
471
            e = sys.exc_info()[1]
472
            if self.errorlevel > 1:
473
                raise
474
            else:
475
                self._dbg(1, "tarfile: %s" % e)
476

  
477

  
478
def main(argv, version=DEFAULT_VERSION):
479
    """Install or upgrade setuptools and EasyInstall"""
480
    tarball = download_setuptools()
481
    _install(tarball)
482

  
483

  
484
if __name__ == '__main__':
485
    main(sys.argv[1:])
b/snf-stats/examples/stats.gunicorn
1
CONFIG = {
2
 'mode': 'django',
3
 'environment': {
4
   'DJANGO_SETTINGS_MODULE': 'synnefo.settings',
5
 },
6
 'working_dir': '/etc/synnefo',
7
 'user': 'www-data',
8
 'group': 'www-data',
9
 'args': (
10
   '--bind=127.0.0.1:8080',
11
   '--workers=12',
12
   '--workers=1',
13
   '--worker-class=gevent',
14
   '--log-level=DEBUG',
15
   '--timeout=90'
16
   ),
17
}
b/snf-stats/setup.py
1
# Copyright 2011 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

  
35
import distribute_setup
36
distribute_setup.use_setuptools()
37

  
38
import os
39

  
40
from distutils.util import convert_path
41
from fnmatch import fnmatchcase
42
from setuptools import setup, find_packages
43

  
44
HERE = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
45

  
46
try:
47
    # try to update the version file
48
    from synnefo.util.version import update_version
49
    update_version('synnefo_stats', 'version', HERE)
50
except ImportError:
51
    pass
52

  
53
from synnefo_stats.version import __version__
54

  
55
# Package info
56
VERSION = __version__
57
README = open(os.path.join(HERE, 'README')).read()
58
CHANGES = open(os.path.join(HERE, 'Changelog')).read()
59
SHORT_DESCRIPTION = 'synnefo stats grapher'
60

  
61
PACKAGES_ROOT = '.'
62
PACKAGES = find_packages(PACKAGES_ROOT)
63

  
64
# Package meta
65
CLASSIFIERS = []
66

  
67
# Package requirements
68
INSTALL_REQUIRES = [
69
    'gdmodule', 
70
    'py-rrdtool',
71
    'Django>=1.2, <1.3',
72
    'snf-common>=0.9.0',
73
]
74

  
75
setup(
76
    name = 'snf-stats',
77
    version = VERSION,
78
    license = 'BSD',
79
    url = 'http://code.grnet.gr/',
80
    description = SHORT_DESCRIPTION,
81
    long_description=README + '\n\n' +  CHANGES,
82
    classifiers = CLASSIFIERS,
83

  
84
    author = 'Stratos Psomadakis',
85
    author_email = 'psomas@grnet.gr',
86
    maintainer = 'Stratos Psomadakis',
87
    maintainer_email = 'psomas@grnet.gr',
88

  
89
    packages = PACKAGES,
90
    package_dir= {'': PACKAGES_ROOT},
91
    include_package_data = True,
92
    zip_safe = False,
93

  
94
    install_requires = INSTALL_REQUIRES,
95

  
96
    dependency_links = ['http://docs.dev.grnet.gr/pypi'],
97
    entry_points={
98
        'synnefo': [
99
             'default_settings = synnefo_stats.synnefo_settings',
100
             'web_apps = synnefo_stats.synnefo_settings:installed_apps',
101
             'urls = synnefo_stats.urls:urlpatterns',
102
             'loggers = synnefo_stats.synnefo_settings:loggers'
103
        ]
104
    }
105
)
b/snf-stats/synnefo_stats/grapher.py
1
# Copyright 2011-2012 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
b/snf-stats/synnefo_stats/settings.py
1
## -*- coding: utf-8 -*-
2
from django.conf import settings
3

  
4
# Image properties
5
IMAGE_WIDTH = getattr(settings, 'IMAGE_WIDTH', 210)
6
WIDTH = getattr(settings, 'WIDTH', 68)
7
HEIGHT = getattr(settings, 'HEIGHT', 10)
8

  
9
# Path settings
10
RRD_PREFIX = getattr(settings, 'RRD_PREFIX', "/var/lib/collectd/rrd/")
11
GRAPH_PREFIX = getattr(settings, 'GRAPH_PREFIX', "/var/cache/snf-stats/")
12

  
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")
16

  
17
# Bar settings
18
BAR_BORDER_COLOR = getattr(settings, 'BAR_BORDER_COLOR', (0x5c, 0xa1, 0xc0))
19
BAR_BG_COLOR = getattr(settings, 'BAR_BG_COLOR', (0xea, 0xea, 0xea))
b/snf-stats/synnefo_stats/synnefo_settings.py
1
# Copyright 2011-2012 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

  
35
"""
36
Django settings metadata. To be used in setup.py snf-webproject entry points.
37
"""
38

  
39
installed_apps = ['synnefo_stats']
40

  
41
loggers = {
42
    'synnefo_stats': {
43
        'handlers': ['console'],
44
        'level': 'INFO',
45
    },
46
}
b/snf-stats/synnefo_stats/urls.py
1
# Copyright 2011-2012 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.conf.urls.defaults import patterns
35
from synnefo_stats.grapher import grapher
36

  
37
urlpatterns = patterns('',
38
    (r'^(?P<hostname>\S+)/(?P<graph_type>\S+)/$', grapher),
39
)

Also available in: Unified diff