Commit bd5473c7 by Őry Máté

lazy loading of fields

parent c126e61f
......@@ -88,3 +88,4 @@ for i in LOCAL_APPS:
LOGGING['loggers'][i] = {'handlers': ['syslog'], 'level': level}
LOGGING['loggers']['djangosaml2'] = {'handlers': ['syslog'], 'level': level}
LOGGING['loggers']['django'] = {'handlers': ['syslog'], 'level': level}
......@@ -16,16 +16,54 @@
# with CIRCLE. If not, see <>.
from __future__ import absolute_import
from timeit import default_timer
from django.contrib.auth.models import Group, User
from django.utils import six
from django.utils.functional import lazy
from django_tables2 import Table, A
from django_tables2.columns import TemplateColumn, Column, LinkColumn
from celery.exceptions import TimeoutError
from vm.models import Node, InstanceTemplate, Lease
from django.utils.translation import ugettext_lazy as _
from django_sshkey.models import UserKey
from dashboard.models import ConnectCommand
from .tasks.local_tasks import lazy_column
class LazyColumnMixin(object):
timeout = 0.5
def __init__(self, *args, **kwargs):
self.__args = args
self.__kwargs = kwargs
super(LazyColumnMixin, self).__init__(*args, **kwargs)
def render(self, **kwargs):
task = lazy_column.apply_async(args=[self.__class__,
queued = default_timer()
def get_result():
remains = max(0, self.timeout - (default_timer() - queued))
return task.get(timeout=remains)
except TimeoutError:
from .views import LazyLoadView
return LazyLoadView.get_link(
return lazy(get_result, six.text_type)()
class LazyTemplateColumn(LazyColumnMixin, TemplateColumn):
class NodeListTable(Table):
......@@ -60,7 +98,7 @@ class NodeListTable(Table):
attrs={'th': {'class': 'node-list-table-thin'}},
monitor = TemplateColumn(
monitor = LazyTemplateColumn(
attrs={'th': {'class': 'node-list-table-monitor'}},
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
# This file is part of CIRCLE Cloud.
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <>.
from manager.mancelery import celery
def lazy_evaluator(task, cls, pk, prop):
obj = cls.objects.get(pk=pk)
for i in prop.split("."):
if i.endswith("()"):
prop = prop[:-2]
call = True
call = False
obj = getattr(obj, prop)
if call:
obj = obj()
return obj
def lazy_column(task, cls, clsargs, clskwargs, kwargs):
obj = cls(*clsargs, **clskwargs)
# skip over LazyColumnMixin.render
assert cls.__mro__[0].__name__ == "LazyColumnMixin"
return cls.__mro__[1].render(obj, **kwargs)
......@@ -8,7 +8,7 @@
<dt>{% trans "Architecture" %}:</dt><dd>{{ }}</dd>
<dt>{% trans "Host IP" %}:</dt><dd>{{ }}</dd>
<dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{ }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{ node_online }}</dd>
<dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd>
<dt>{% trans "Driver Version:" %}</dt>
......@@ -50,7 +50,7 @@ from .views import (
VmGraphView, NodeGraphView, NodeListGraphView,
TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView,
TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView,
OpenSearchDescriptionView, LazyLoadView,
from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops
......@@ -224,6 +224,8 @@ urlpatterns = patterns(
url(r'^vm/opensearch.xml$', OpenSearchDescriptionView.as_view(),
url(r'^lazyload/(?P<token>.*)/$', LazyLoadView.as_view(),
urlpatterns += patterns(
......@@ -38,7 +38,7 @@ from vm.models import Node, NodeActivity, Trait
from ..forms import TraitForm, HostForm, NodeForm
from ..tables import NodeListTable
from .util import AjaxOperationMixin, OperationView, GraphMixin
from .util import AjaxOperationMixin, OperationView, GraphMixin, lazy
def get_operations(instance, user):
......@@ -100,6 +100,7 @@ class NodeDetailView(LoginRequiredMixin,
context['trait_form'] = form
context['graphite_enabled'] = (
settings.GRAPHITE_URL is not None)
context['node_online'] = lazy(self.object, "online", 0.5)
return context
def post(self, request, *args, **kwargs):
......@@ -20,18 +20,23 @@ import json
import logging
import re
from collections import OrderedDict
from timeit import default_timer
from urlparse import urljoin
from django.conf import settings
from django.contrib.auth.models import User, Group
from django.core import signing
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.signing import dumps, loads
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.views import redirect_to_login
from django.db.models import Q
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import redirect, render
from django.utils import six
from django.utils.functional import lazy as functional_lazy
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from django.views.generic import DetailView, View
from django.views.generic.detail import SingleObjectMixin
......@@ -43,6 +48,7 @@ from celery.exceptions import TimeoutError
from common.models import HumanReadableException, HumanReadableObject
from ..models import GroupProfile, Profile
from ..forms import TransferOwnershipForm
from ..tasks.local_tasks import lazy_evaluator
logger = logging.getLogger(__name__)
saml_available = hasattr(settings, "SAML_CONFIG")
......@@ -694,3 +700,54 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
unicode(user),, new_owner, key)
raise PermissionDenied()
return (instance, new_owner)
class LazyLoadView(View):
url_name = "dashboard.view.lazyload"
def get_url(cls, id):
return reverse(cls.url_name, kwargs={
'token': dumps(id, salt=cls.url_name, compress=True)})
def get_link(cls, id):
return format_html(
'<a href="{0}" class="lazy-loading">{1}</a>',
cls.get_url(id), _("Loading..."))
def get(self, request, token):
id = loads(token, salt=self.url_name, max_age=60*60)
value = lazy_evaluator.AsyncResult(id).get(timeout=10)
except TimeoutError:
raise Http404()
return HttpResponse(value)
def lazy(obj, prop, timeout=None, force=False, types=None):
"""Evaluate callable in the background and return value or html token.
timeout: how much to wait for the result (in secs, counted from queuing)
force: don't wait for result
task = lazy_evaluator.apply_async(args=[obj.__class__,, prop],
queued = default_timer()
def get_result():
if timeout is not None:
remains = timeout - (default_timer() - queued)
remains = None
if not force:
return task.get(timeout=remains)
except TimeoutError:
return LazyLoadView.get_link(
if types is None:
types = [six.text_type]
return functional_lazy(get_result, *types)()
......@@ -35,6 +35,7 @@ celery = Celery('manager',
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 sign in to comment