Commit 0d4a0672 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Export Astakos detailed stats from API

Add new 'astakos_admin' service to export basic statistics about Astakos
service. The stats are exported via the '/admin/stats/detail' API endpoint.
Access to this view is only allowed to the users that belong to specific
Astakos groups, as specified in the new setting
'ASTAKOS_ADMIN_STATS_PERMITTED_GROUPS.'
parent 2566bfd7
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from django.conf.urls import url, patterns
from astakos.admin import views
from django.http import Http404
def index(request):
raise Http404
urlpatterns = patterns(
'',
url(r'^$', index),
url(r'^stats$', views.get_public_stats),
url(r'^stats/detail$', views.get_astakos_stats),
)
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import datetime
from django.conf import settings
from django.db.models import Sum
from astakos.im.models import AstakosUser, Resource
from astakos.quotaholder_app.models import Holding
def get_public_stats():
users = AstakosUser.objects.all()
active = users.filter(is_active=True)
return {"users": {"total": users.count(),
"active": active.count()}}
def get_astakos_stats():
stats = {"datetime": datetime.datetime.now().strftime("%c")}
resources = Resource.objects.values_list("name", flat=True)
users = AstakosUser.objects.all()
verified = users.filter(email_verified=True)
active = users.filter(is_active=True)
user_stats = {}
user_stats["total"] = {"total": users.count(),
"verified": verified.count(),
"active": active.count(),
"usage": {}}
for resource in resources:
usage = Holding.objects.filter(resource=resource)\
.aggregate(summ=Sum("usage_max"))
user_stats["total"]["usage"][resource] = int(usage["summ"])
for provider in settings.ASTAKOS_IM_MODULES:
users = AstakosUser.objects.filter(auth_providers__module=provider)
verified = users.filter(email_verified=True)
active = users.filter(is_active=True)
user_stats[provider] = {"total": users.count(),
"verified": verified.count(),
"active": active.count(),
"usage": {}}
users_uuids = users.values_list("uuid", flat=True)
for resource in resources:
usage = Holding.objects\
.filter(holder__in=users_uuids, resource=resource)\
.aggregate(summ=Sum("usage_max"))
user_stats[provider]["usage"][resource] = int(usage["summ"])
stats["users"] = user_stats
return stats
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import logging
from django import http
from django.utils import simplejson as json
from snf_django.lib import api
from astakos.im import settings
from astakos.admin import stats
logger = logging.getLogger(__name__)
PERMITTED_GROUPS = settings.ADMIN_STATS_PERMITTED_GROUPS
@api.api_method(http_method='GET', user_required=False, token_required=False,
logger=logger, serializations=['json'])
@api.allow_jsonp()
def get_public_stats(request):
_stats = stats.get_public_stats()
data = json.dumps(_stats)
return http.HttpResponse(data, status=200, content_type='application/json')
@api.api_method(http_method='GET', user_required=True, token_required=True,
logger=logger, serializations=['json'])
@api.user_in_groups(permitted_groups=PERMITTED_GROUPS,
logger=logger)
def get_astakos_stats(request):
_stats = stats.get_astakos_stats()
data = json.dumps(_stats)
return http.HttpResponse(data, status=200, content_type='application/json')
......@@ -85,4 +85,15 @@ astakos_services = {
'publicURL': None},
],
},
'astakos_admin': {
'type': 'astakos_admin',
'component': 'astakos',
'prefix': 'admin',
'public': False,
'endpoints': [
{'versionId': '',
'publicURL': None},
],
},
}
......@@ -30,21 +30,15 @@
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import datetime
import json
import string
#from optparse import make_option
from django.conf import settings
from snf_django.management.utils import pprint_table
from snf_django.management.commands import SynnefoCommand, CommandError
from astakos.im.models import AstakosUser, Resource
from astakos.quotaholder_app.models import Holding
from django.db.models import Sum
from snf_django.management.utils import pprint_table
#from astakos.im.models import AstakosUser, Resource
#from astakos.quotaholder_app.models import Holding
from astakos.admin import stats as statistics
class Command(SynnefoCommand):
......@@ -55,7 +49,7 @@ class Command(SynnefoCommand):
)
def handle(self, *args, **options):
stats = get_astakos_stats()
stats = statistics.get_astakos_stats()
output_format = options["output_format"]
if output_format == "json":
......@@ -67,49 +61,6 @@ class Command(SynnefoCommand):
output_format)
def get_astakos_stats():
stats = {"datetime": datetime.datetime.now().strftime("%c")}
resources = Resource.objects.values_list("name", flat=True)
users = AstakosUser.objects.all()
verified = users.filter(email_verified=True)
active = users.filter(is_active=True)
user_stats = {}
user_stats["total"] = {"total": users.count(),
"verified": verified.count(),
"active": active.count(),
"usage": {}}
for resource in resources:
usage = Holding.objects.filter(resource=resource)\
.aggregate(summ=Sum("usage_max"))
user_stats["total"]["usage"][resource] = int(usage["summ"])
for provider in settings.ASTAKOS_IM_MODULES:
users = AstakosUser.objects.filter(auth_providers__module=provider)
verified = users.filter(email_verified=True)
active = users.filter(is_active=True)
user_stats[provider] = {"total": users.count(),
"verified": verified.count(),
"active": active.count(),
"usage": {}}
users_uuids = users.values_list("uuid", flat=True)
for resource in resources:
usage = Holding.objects\
.filter(holder__in=users_uuids, resource=resource)\
.aggregate(summ=Sum("usage_max"))
user_stats[provider]["usage"][resource] = int(usage["summ"])
stats["users"] = user_stats
return stats
def columns_from_fields(fields, values):
return zip(map(string.lower, fields), [values.get(f, 0) for f in fields])
......@@ -117,8 +68,8 @@ def columns_from_fields(fields, values):
def pretty_print_stats(stats, stdout):
newline = lambda: stdout.write("\n")
datetime = stats.get("datetime")
stdout.write("datetime: %s\n" % datetime)
_datetime = stats.get("datetime")
stdout.write("datetime: %s\n" % _datetime)
newline()
users = stats.get("users", {})
......
......@@ -21,6 +21,7 @@ ACCOUNTS_PREFIX = get_path(astakos_services, 'astakos_account.prefix')
VIEWS_PREFIX = get_path(astakos_services, 'astakos_ui.prefix')
KEYSTONE_PREFIX = get_path(astakos_services, 'astakos_identity.prefix')
WEBLOGIN_PREFIX = get_path(astakos_services, 'astakos_weblogin.prefix')
ADMIN_PREFIX = get_path(astakos_services, 'astakos_admin.prefix')
# Set the expiration time of newly created auth tokens
# to be this many hours after their creation time.
......@@ -207,3 +208,7 @@ KAMAKI_CONFIG_CLOUD_NAME = getattr(settings,
REDIRECT_ALLOWED_SCHEMES = getattr(settings,
'ASTAKOS_REDIRECT_ALLOWED_SCHEMES',
('pithos', 'pithosdev'))
ADMIN_STATS_PERMITTED_GROUPS = getattr(settings,
'ASTAKOS_ADMIN_STATS_PERMITTED_GROUPS',
['admin-stats'])
......@@ -34,7 +34,8 @@
from django.conf.urls import include, patterns
from astakos.im.settings import (
BASE_PATH, ACCOUNTS_PREFIX, VIEWS_PREFIX, KEYSTONE_PREFIX, WEBLOGIN_PREFIX)
BASE_PATH, ACCOUNTS_PREFIX, VIEWS_PREFIX, KEYSTONE_PREFIX, WEBLOGIN_PREFIX,
ADMIN_PREFIX)
from snf_django.lib.api.utils import prefix_pattern
from snf_django.utils.urls import \
extend_with_root_redirects, extend_endpoint_with_slash
......@@ -52,6 +53,7 @@ astakos_patterns = patterns(
(prefix_pattern(ACCOUNTS_PREFIX), include('astakos.api.urls')),
(prefix_pattern(KEYSTONE_PREFIX), include('astakos.api.keystone_urls')),
(prefix_pattern(WEBLOGIN_PREFIX), include('astakos.im.weblogin_urls')),
(prefix_pattern(ADMIN_PREFIX), include('astakos.admin.admin_urls')),
)
......
......@@ -136,3 +136,6 @@
# Migrate existing shibboleth user entries which where previously associated
# with EPPN instead of the provided value of REMOTE_ID mod_shib2 header.
# ASTAKOS_SHIBBOLETH_MIGRATE_EPPN = False
#
## Astakos groups that have access to '/admin' views.
# ASTAKOS_ADMIN_STATS_PERMITTED_GROUPS = ["admin-stats"]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment