diff --git a/circle/dashboard/templates/dashboard/node-list/column-vm.html b/circle/dashboard/templates/dashboard/node-list/column-vm.html index b5022b2..3f61447 100644 --- a/circle/dashboard/templates/dashboard/node-list/column-vm.html +++ b/circle/dashboard/templates/dashboard/node-list/column-vm.html @@ -1,5 +1,5 @@ {% load i18n %} <div id="node-list-column-vm"> - <a class="real-link" href="{% url "dashboard.views.node-detail" pk=record.pk %}#virtualmachines">{{ record.instance_set.count }}</a> + <a class="real-link" href="{% url "dashboard.views.node-detail" pk=record.pk %}#virtualmachines">{{ value }}</a> </div> diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index b71d184..49103db 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -14,6 +14,7 @@ from django.core.exceptions import ( ) from django.core import signing from django.core.urlresolvers import reverse, reverse_lazy +from django.db.models import Count from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import redirect, render, get_object_or_404 from django.views.decorators.http import require_GET @@ -921,10 +922,13 @@ class VmList(LoginRequiredMixin, SingleTableView): class NodeList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): template_name = "dashboard/node-list.html" - model = Node table_class = NodeListTable table_pagination = False + def get_queryset(self): + return Node.objects.annotate( + number_of_VMs=Count('instance_set')).select_related('host') + class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): template_name = "dashboard/group-list.html" diff --git a/circle/vm/models/node.py b/circle/vm/models/node.py index 10ab925..8a35ad2 100644 --- a/circle/vm/models/node.py +++ b/circle/vm/models/node.py @@ -25,6 +25,17 @@ from django.utils import timezone logger = getLogger(__name__) +def node_available(function): + """Decorate methods to ignore disabled Nodes. + """ + def decorate(self, *args, **kwargs): + if self.enabled and self.online: + return function(self, *args, **kwargs) + else: + return None + return decorate + + class Node(TimeStampedModel): """A VM host machine, a hypervisor. @@ -55,19 +66,22 @@ class Node(TimeStampedModel): def __unicode__(self): return self.name - @method_cache(10, 5) + @method_cache(10) def get_online(self): """Check if the node is online. - Runs a remote ping task if the worker is running. + Check if node is online by queue is available. """ try: - return self.remote_query(vm_tasks.ping, timeout=1, default=False) - except WorkerNotFound: + self.get_remote_queue_name("vm") + except: return False + else: + return True online = property(get_online) + @node_available @method_cache(300) def get_num_cores(self): """Number of CPU threads available to the virtual machines. @@ -106,6 +120,7 @@ class Node(TimeStampedModel): self.get_num_cores(invalidate_cache=True) self.get_ram_size(invalidate_cache=True) + @node_available @method_cache(300) def get_ram_size(self): """Bytes of total memory in the node. @@ -115,6 +130,7 @@ class Node(TimeStampedModel): ram_size = property(get_ram_size) @property + @node_available def ram_size_with_overcommit(self): """Bytes of total memory including overcommit margin. """ @@ -198,6 +214,7 @@ class Node(TimeStampedModel): else: return default + @node_available def get_monitor_info(self): try: handler = GraphiteHandler() @@ -229,17 +246,21 @@ class Node(TimeStampedModel): return collected @property + @node_available def cpu_usage(self): return float(self.get_monitor_info()["cpu.usage"]) / 100 @property + @node_available def ram_usage(self): return float(self.get_monitor_info()["memory.usage"]) / 100 @property + @node_available def byte_ram_usage(self): return self.ram_usage * self.ram_size + @node_available def update_vm_states(self): """Update state of Instances running on this Node. @@ -284,7 +305,8 @@ class Node(TimeStampedModel): @classmethod def get_state_count(cls, online, enabled): - return len([1 for i in cls.objects.filter(enabled=enabled).all() + return len([1 for i in + cls.objects.filter(enabled=enabled).select_related('host') if i.online == online]) @permalink diff --git a/circle/vm/tasks/vm_tasks.py b/circle/vm/tasks/vm_tasks.py index b377a44..bd6bea1 100644 --- a/circle/vm/tasks/vm_tasks.py +++ b/circle/vm/tasks/vm_tasks.py @@ -1,12 +1,22 @@ +from django.core.cache import cache +from logging import getLogger + from manager.mancelery import celery +logger = getLogger(__name__) + def check_queue(node_hostname, queue_id): - drivers = ['vmdriver', 'netdriver', 'agentdriver'] - worker_list = [node_hostname + "." + d for d in drivers] + """True if the queue is alive. + + Example: check_queue('node01', 'vm'): + :param node_hostname: Short hostname of the node. + :param queue_id: Queue identifier (eg. vm). + """ + # drivers = ['vmdriver', 'netdriver', 'agentdriver'] + # worker_list = [node_hostname + "." + d for d in drivers] queue_name = node_hostname + "." + queue_id - inspect = celery.control.inspect(worker_list) - active_queues = inspect.active_queues() + active_queues = get_queues() if active_queues is None: return False # v is List of List of queues dict @@ -18,6 +28,22 @@ def check_queue(node_hostname, queue_id): return False +def get_queues(): + """Get active celery queues. + + Result is cached for 10 seconds! + """ + key = __name__ + u'queues' + result = cache.get(key) + if result is None: + inspect = celery.control.inspect() + inspect.timeout = 0.1 + result = inspect.active_queues() + logger.debug('Queue list of length %d cached.', len(result)) + cache.set(key, result, 10) + return result + + @celery.task(name='vmdriver.create') def deploy(params): pass