=== modified file 'lava_scheduler_app/models.py'
@@ -55,6 +55,10 @@
health_check_job = models.TextField(
null=True, blank=True, default=None, validators=[validate_job_json])
+ @models.permalink
+ def get_absolute_url(self):
+ return ("lava.scheduler.device_type.detail", [self.pk])
+
class Device(models.Model):
"""
=== added file 'lava_scheduler_app/templates/lava_scheduler_app/alldevices.html'
@@ -0,0 +1,11 @@
+{% extends "lava_scheduler_app/_content.html" %}
+
+{% load django_tables2 %}
+
+{% block content %}
+
+<h2>All Devices</h2>
+
+{% render_table devices_table %}
+
+{% endblock %}
=== added file 'lava_scheduler_app/templates/lava_scheduler_app/device_type.html'
@@ -0,0 +1,16 @@
+{% extends "lava_scheduler_app/_content.html" %}
+
+{% load django_tables2 %}
+
+{% block content %}
+<h2>{{device_type}} Status</h2>
+
+{{running_jobs_num}} jobs running / {{queued_jobs_num}} jobs queued
+
+<h3>Health Job Summary</h3>
+{% render_table health_job_summary_table %}
+
+<h3>Devices Overview</h3>
+{% render_table devices_table_no_dt %}
+
+{% endblock %}
=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/index.html'
@@ -3,11 +3,16 @@
{% load django_tables2 %}
{% block content %}
-<h2>Devices</h2>
-
-{% render_table devices_table %}
-
-<a href="{% url lava.scheduler.labhealth %}">All Devices Health</a>
+
+<h2>Device Type Overview</h2>
+<p>{{device_status}} devices online</p>
+<p>{{health_check_status}} health check jobs passed in 24 hours</p>
+
+{% render_table device_type_table %}
+
+<p><a href="{% url lava.scheduler.alldevices %}">All Devices</a></p>
+
+<p><a href="{% url lava.scheduler.labhealth %}">All Devices Health</a></p>
<h2>Active Jobs</h2>
=== modified file 'lava_scheduler_app/urls.py'
@@ -18,6 +18,18 @@
url(r'^alljobs_json$',
'alljobs_json',
name='lava.scheduler.job.list_json'),
+ url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)$',
+ 'device_type_detail',
+ name='lava.scheduler.device_type.detail'),
+ url(r'^device_type_json$',
+ 'device_type_json',
+ name='lava.scheduler.device_type.device_type_json'),
+ url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)/index_nodt_devices_json$',
+ 'index_nodt_devices_json',
+ name='lava.scheduler.device_type.index_nodt_devices_json'),
+ url(r'^alldevices$',
+ 'device_list',
+ name='lava.scheduler.alldevices'),
url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)$',
'device_detail',
name='lava.scheduler.device.detail'),
=== modified file 'lava_scheduler_app/views.py'
@@ -3,6 +3,8 @@
import os
import simplejson
import StringIO
+import datetime
+from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.urlresolvers import reverse
@@ -38,13 +40,14 @@
getDispatcherLogMessages
)
from lava_scheduler_app.models import (
+ Tag,
Device,
+ DeviceType,
DeviceStateTransition,
TestJob,
)
-
def post_only(func):
def decorated(request, *args, **kwargs):
if request.method != 'POST':
@@ -69,6 +72,7 @@
record.get_absolute_url(),
escape(record.pk)))
+
class IDLinkColumn(Column):
def __init__(self, verbose_name="ID", **kw):
@@ -97,6 +101,7 @@
}).all()
+
class JobTable(DataTablesTable):
def render_device(self, record):
@@ -157,12 +162,23 @@
def index_devices_json(request):
return DeviceTable.json(request)
+def health_jobs_in_hr(hr=-24):
+ return TestJob.objects.filter(health_check=True,
+ start_time__gte=(datetime.datetime.now() + relativedelta(hours=hr)))
@BreadCrumb("Scheduler", parent=lava_index)
def index(request):
return render_to_response(
"lava_scheduler_app/index.html",
{
+ 'device_status': "%s/%s" % (
+ Device.objects.filter(
+ status__in=[Device.IDLE, Device.RUNNING]).count(),
+ Device.objects.count()),
+ 'health_check_status': "%s/%s" % (
+ health_jobs_in_hr().filter(status=TestJob.COMPLETE).count(),
+ health_jobs_in_hr().count()),
+ 'device_type_table': DeviceTypeTable('devicetype', reverse(device_type_json)),
'devices_table': DeviceTable('devices', reverse(index_devices_json)),
'active_jobs_table': IndexJobTable(
'active_jobs', reverse(index_active_jobs_json)),
@@ -170,11 +186,107 @@
},
RequestContext(request))
+@BreadCrumb("All Devices", parent=index)
+def device_list(request):
+ return render_to_response(
+ "lava_scheduler_app/alldevices.html",
+ {
+ 'devices_table': DeviceTable('devices', reverse(index_devices_json)),
+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(device_list),
+ },
+ RequestContext(request))
def get_restricted_job(user, pk):
return get_object_or_404(
TestJob.objects.accessible_by_principal(user), pk=pk)
+class DeviceTypeTable(DataTablesTable):
+
+ def get_queryset(self):
+ return DeviceType.objects.all()
+
+ def render_status(self, record):
+ idle_num = Device.objects.filter(device_type=record.name,
+ status=Device.IDLE).count()
+ offline_num = Device.objects.filter(device_type=record.name,
+ status__in=[Device.OFFLINE, Device.OFFLINING]).count()
+ running_num = Device.objects.filter(device_type=record.name,
+ status=Device.RUNNING).count()
+ return "%s idle, %s offline, %s busy" % (idle_num, offline_num,
+ running_num)
+
+ name = IDLinkColumn("name")
+ status = Column()
+
+ searchable_columns = ['name']
+
+
+class HealthJobSummaryTable(DataTablesTable):
+ """
+ The Table will return 1 day, 1 week, 1 month offset health job count.
+ The value is defined when table instance is created in device_type_detail()
+ """
+
+ def render_name(self, record):
+ matrix = {-24:"24hours", -24*7:"Week", -24*7*30:"Month"}
+ return matrix[record]
+
+ def render_Complete(self, record):
+ device_type = self.params[0]
+ num = health_jobs_in_hr(record).filter(
+ actual_device__in=Device.objects.filter(
+ device_type=device_type), status=TestJob.COMPLETE).count()
+ return num
+
+ def render_Failed(self, record):
+ device_type = self.params[0]
+ num = health_jobs_in_hr(record).filter(
+ actual_device__in=Device.objects.filter(
+ device_type=device_type), status=TestJob.INCOMPLETE).count()
+ return num
+
+ name = Column()
+ Complete = Column()
+ Failed = Column()
+
+def device_type_json(request):
+ return DeviceTypeTable.json(request)
+
+class NoDTDeviceTable(DeviceTable):
+ def get_queryset(self, device_type):
+ return Device.objects.filter(device_type=device_type)
+
+ class Meta:
+ exclude = ('device_type',)
+
+def index_nodt_devices_json(request, pk):
+ device_type = get_object_or_404(DeviceType, pk=pk)
+ return NoDTDeviceTable.json(request, params=(device_type,))
+
+@BreadCrumb("Device Type {pk}", parent=index, needs=['pk'])
+def device_type_detail(request, pk):
+ dt = get_object_or_404(DeviceType, pk=pk)
+ return render_to_response(
+ "lava_scheduler_app/device_type.html",
+ {
+ 'device_type': dt,
+ 'running_jobs_num': TestJob.objects.filter(
+ actual_device__in=Device.objects.filter(device_type=dt),
+ status=TestJob.RUNNING).count(),
+ # Fix me: doesn't count actual_device not set but requested
+ # device type jobs.
+ 'queued_jobs_num': TestJob.objects.filter(
+ actual_device__in=Device.objects.filter(device_type=dt),
+ status=TestJob.SUBMITTED).count(),
+ # data return 1 day, 1 week, 1 month offset
+ 'health_job_summary_table': HealthJobSummaryTable(
+ 'device_type', params=(dt,), data=[-24, -24*7, -24*7*30]),
+ 'devices_table_no_dt': NoDTDeviceTable('devices',
+ reverse(index_nodt_devices_json, kwargs=dict(pk=pk)), params=(dt,)),
+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(device_type_detail, pk=pk),
+ },
+ RequestContext(request))
+
class DeviceHealthTable(DataTablesTable):