Commit bd5473c7 by Őry Máté

lazy loading of fields

parent c126e61f
...@@ -88,3 +88,4 @@ for i in LOCAL_APPS: ...@@ -88,3 +88,4 @@ for i in LOCAL_APPS:
LOGGING['loggers'][i] = {'handlers': ['syslog'], 'level': level} LOGGING['loggers'][i] = {'handlers': ['syslog'], 'level': level}
LOGGING['loggers']['djangosaml2'] = {'handlers': ['syslog'], 'level': level} LOGGING['loggers']['djangosaml2'] = {'handlers': ['syslog'], 'level': level}
LOGGING['loggers']['django'] = {'handlers': ['syslog'], 'level': level} LOGGING['loggers']['django'] = {'handlers': ['syslog'], 'level': level}
DEBUG=True
...@@ -16,16 +16,54 @@ ...@@ -16,16 +16,54 @@
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import from __future__ import absolute_import
from timeit import default_timer
from django.contrib.auth.models import Group, User 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 import Table, A
from django_tables2.columns import TemplateColumn, Column, LinkColumn from django_tables2.columns import TemplateColumn, Column, LinkColumn
from celery.exceptions import TimeoutError
from vm.models import Node, InstanceTemplate, Lease from vm.models import Node, InstanceTemplate, Lease
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django_sshkey.models import UserKey from django_sshkey.models import UserKey
from dashboard.models import ConnectCommand 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__,
self.__args,
self.__kwargs,
kwargs],
queue="localhost.man")
queued = default_timer()
def get_result():
remains = max(0, self.timeout - (default_timer() - queued))
try:
return task.get(timeout=remains)
except TimeoutError:
pass
from .views import LazyLoadView
return LazyLoadView.get_link(task.id)
return lazy(get_result, six.text_type)()
class LazyTemplateColumn(LazyColumnMixin, TemplateColumn):
pass
class NodeListTable(Table): class NodeListTable(Table):
...@@ -60,7 +98,7 @@ class NodeListTable(Table): ...@@ -60,7 +98,7 @@ class NodeListTable(Table):
attrs={'th': {'class': 'node-list-table-thin'}}, attrs={'th': {'class': 'node-list-table-thin'}},
) )
monitor = TemplateColumn( monitor = LazyTemplateColumn(
verbose_name=_("Monitor"), verbose_name=_("Monitor"),
template_name='dashboard/node-list/column-monitor.html', template_name='dashboard/node-list/column-monitor.html',
attrs={'th': {'class': 'node-list-table-monitor'}}, 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 <http://www.gnu.org/licenses/>.
from manager.mancelery import celery
@celery.task(bind=True)
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
else:
call = False
obj = getattr(obj, prop)
if call:
obj = obj()
return obj
@celery.task(bind=True)
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 @@ ...@@ -8,7 +8,7 @@
<dt>{% trans "Architecture" %}:</dt><dd>{{ node.info.architecture }}</dd> <dt>{% trans "Architecture" %}:</dt><dd>{{ node.info.architecture }}</dd>
<dt>{% trans "Host IP" %}:</dt><dd>{{ node.host.ipv4 }}</dd> <dt>{% trans "Host IP" %}:</dt><dd>{{ node.host.ipv4 }}</dd>
<dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd> <dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{ node.online }}</dd> <dt>{% trans "Host online" %}:</dt><dd> {{ node_online }}</dd>
<dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd> <dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd>
<dt>{% trans "Driver Version:" %}</dt> <dt>{% trans "Driver Version:" %}</dt>
<dd> <dd>
......
...@@ -50,7 +50,7 @@ from .views import ( ...@@ -50,7 +50,7 @@ from .views import (
VmGraphView, NodeGraphView, NodeListGraphView, VmGraphView, NodeGraphView, NodeListGraphView,
TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView, TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView,
TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView, TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView,
OpenSearchDescriptionView, OpenSearchDescriptionView, LazyLoadView,
) )
from .views.vm import vm_ops, vm_mass_ops from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops from .views.node import node_ops
...@@ -224,6 +224,8 @@ urlpatterns = patterns( ...@@ -224,6 +224,8 @@ urlpatterns = patterns(
name="dashboard.views.token-login"), name="dashboard.views.token-login"),
url(r'^vm/opensearch.xml$', OpenSearchDescriptionView.as_view(), url(r'^vm/opensearch.xml$', OpenSearchDescriptionView.as_view(),
name="dashboard.views.vm-opensearch"), name="dashboard.views.vm-opensearch"),
url(r'^lazyload/(?P<token>.*)/$', LazyLoadView.as_view(),
name=LazyLoadView.url_name),
) )
urlpatterns += patterns( urlpatterns += patterns(
......
...@@ -38,7 +38,7 @@ from vm.models import Node, NodeActivity, Trait ...@@ -38,7 +38,7 @@ from vm.models import Node, NodeActivity, Trait
from ..forms import TraitForm, HostForm, NodeForm from ..forms import TraitForm, HostForm, NodeForm
from ..tables import NodeListTable from ..tables import NodeListTable
from .util import AjaxOperationMixin, OperationView, GraphMixin from .util import AjaxOperationMixin, OperationView, GraphMixin, lazy
def get_operations(instance, user): def get_operations(instance, user):
...@@ -100,6 +100,7 @@ class NodeDetailView(LoginRequiredMixin, ...@@ -100,6 +100,7 @@ class NodeDetailView(LoginRequiredMixin,
context['trait_form'] = form context['trait_form'] = form
context['graphite_enabled'] = ( context['graphite_enabled'] = (
settings.GRAPHITE_URL is not None) settings.GRAPHITE_URL is not None)
context['node_online'] = lazy(self.object, "online", 0.5)
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
......
...@@ -20,18 +20,23 @@ import json ...@@ -20,18 +20,23 @@ import json
import logging import logging
import re import re
from collections import OrderedDict from collections import OrderedDict
from timeit import default_timer
from urlparse import urljoin from urlparse import urljoin
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.core import signing from django.core import signing
from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.signing import dumps, loads
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse, Http404, HttpResponseRedirect from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import redirect, render 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.utils.translation import ugettext_lazy as _, ugettext_noop
from django.views.generic import DetailView, View from django.views.generic import DetailView, View
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
...@@ -43,6 +48,7 @@ from celery.exceptions import TimeoutError ...@@ -43,6 +48,7 @@ from celery.exceptions import TimeoutError
from common.models import HumanReadableException, HumanReadableObject from common.models import HumanReadableException, HumanReadableObject
from ..models import GroupProfile, Profile from ..models import GroupProfile, Profile
from ..forms import TransferOwnershipForm from ..forms import TransferOwnershipForm
from ..tasks.local_tasks import lazy_evaluator
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
saml_available = hasattr(settings, "SAML_CONFIG") saml_available = hasattr(settings, "SAML_CONFIG")
...@@ -694,3 +700,54 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View): ...@@ -694,3 +700,54 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
unicode(user), user.pk, new_owner, key) unicode(user), user.pk, new_owner, key)
raise PermissionDenied() raise PermissionDenied()
return (instance, new_owner) return (instance, new_owner)
class LazyLoadView(View):
url_name = "dashboard.view.lazyload"
@classmethod
def get_url(cls, id):
return reverse(cls.url_name, kwargs={
'token': dumps(id, salt=cls.url_name, compress=True)})
@classmethod
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)
try:
value = lazy_evaluator.AsyncResult(id).get(timeout=10)
except TimeoutError:
raise Http404()
else:
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__, obj.pk, prop],
queue="localhost.man")
queued = default_timer()
def get_result():
if timeout is not None:
remains = timeout - (default_timer() - queued)
else:
remains = None
if not force:
try:
return task.get(timeout=remains)
except TimeoutError:
pass
return LazyLoadView.get_link(task.id)
if types is None:
types = [six.text_type]
return functional_lazy(get_result, *types)()
...@@ -35,6 +35,7 @@ celery = Celery('manager', ...@@ -35,6 +35,7 @@ celery = Celery('manager',
'storage.tasks.periodic_tasks', 'storage.tasks.periodic_tasks',
'firewall.tasks.local_tasks', 'firewall.tasks.local_tasks',
'dashboard.tasks.local_periodic_tasks', 'dashboard.tasks.local_periodic_tasks',
'dashboard.tasks.local_tasks',
]) ])
celery.conf.update( celery.conf.update(
......
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