=== modified file 'lava_scheduler_app/models.py'
@@ -26,11 +26,13 @@
OFFLINE = 0
IDLE = 1
RUNNING = 2
+ OFFLINING = 3
STATUS_CHOICES = (
(OFFLINE, 'Offline'),
(IDLE, 'Idle'),
(RUNNING, 'Running'),
+ (OFFLINING, 'Going offline'),
)
hostname = models.CharField(
@@ -51,6 +53,20 @@
verbose_name = _(u"Device status"),
)
+ def can_admin(self, user):
+ return user.has_perm('lava_scheduler_app.change_device')
+
+ def put_into_maintenance_mode(self):
+ if self.status == self.RUNNING:
+ self.status = self.OFFLINING
+ else:
+ self.status = self.OFFLINE
+ self.save()
+
+ def put_into_online_mode(self):
+ self.status = self.IDLE
+ self.save()
+
def __unicode__(self):
return self.hostname
=== added file 'lava_scheduler_app/templates/lava_scheduler_app/device.html'
@@ -0,0 +1,98 @@
+{% extends "lava_scheduler_app/_content.html" %}
+
+{% block extrahead %}
+{{ block.super }}
+<style type="text/css">
+.column {
+ position: relative;
+ float: left;
+ padding-right: 2em;
+ padding-bottom: 1em;
+}
+</style>
+{% endblock %}
+
+{% block content %}
+<h2>Device {{ device.hostname }}</h2>
+
+{% if show_maintenance %}
+<form style="display:inline; float:right" method="POST"
+ action="{% url lava_scheduler_app.views.device_maintenance_mode device.pk %}">
+ {% csrf_token %}
+ <button id="maintenance-button">Put into maintenance mode</button>
+</form>
+{% endif %}
+{% if show_online %}
+<form style="display:inline; float:right" method="POST"
+ action="{% url lava_scheduler_app.views.device_online device.pk %}">
+ {% csrf_token %}
+ <button id="online-button">Put online</button>
+</form>
+{% endif %}
+
+<div id="columns">
+ <div class="column">
+ <dt>Hostname:</dt>
+ <dd>{{ device.hostname }}</dd>
+
+ <dt>Device type:</dt>
+ <dd>{{ device.device_type }}</dd>
+ </div>
+ <div class="column">
+ <dt>Status:</dt>
+ <dd>{{ device.get_status_display }}</dd>
+
+{% if device.current_job %}
+ <dt>Currently running:</dt>
+ <dd>
+ <a href="{% url lava_scheduler_app.views.job pk=device.current_job.pk %}">
+ Job {{ device.current_job.id }}
+ </a>
+ </dd>
+{% endif %}
+ </div>
+ <div style="clear: both"></div>
+</div>
+
+<table class="data display">
+ <thead>
+ <tr>
+ <th class="id">ID</th>
+ <th>Status</th>
+ <th>Device</th>
+ <th>Submitter</th>
+ <th>Start Time</th>
+ <th>Finish Time</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for job in recent_jobs %}
+ <tr>
+ <td><a href="{% url lava_scheduler_app.views.job pk=job.pk %}">{{ job.id }}</a></td>
+ <td>{{ job.get_status_display }}</td>
+ <td>{{ job.actual_device }}</td>
+ <td>{{ job.submitter }}</td>
+ <td>{{ job.start_time }}</td>
+ <td>{{ job.end_time|default:"not finished" }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+
+<script>
+$(document).ready(
+ function() {
+ $("table.data").dataTable({
+ "bJQueryUI": true,
+ "aoColumnDefs": [
+ { "sType": "num-html", "aTargets": [ "id" ] }
+ ],
+ "aaSorting": [[4, 'desc', 4]]
+ });
+ $("#maintenance-button").button();
+ $("#online-button").button();
+ }
+);
+</script>
+
+{% endblock %}
=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/index.html'
@@ -15,7 +15,11 @@
{% for device in devices %}
<tr>
<td>{{ device.device_type }}</td>
- <td>{{ device.hostname }}</td>
+ <td>
+ <a href="{% url lava_scheduler_app.views.device pk=device.pk %}">
+ {{ device.hostname }}
+ </a>
+ </td>
<td>{{ device.get_status_display }}</td>
</tr>
{% endfor %}
=== modified file 'lava_scheduler_app/urls.py'
@@ -4,6 +4,9 @@
'lava_scheduler_app.views',
url(r'^$', 'index'),
url(r'^alljobs$', 'alljobs'),
+ url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)$', 'device'),
+ url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)/maintenance$', 'device_maintenance_mode'),
+ url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)/online$', 'device_online'),
url(r'^job/(?P<pk>[0-9]+)$', 'job'),
url(r'^job/(?P<pk>[0-9]+)/output$', 'job_output'),
url(r'^job/(?P<pk>[0-9]+)/cancel$', 'job_cancel'),
=== modified file 'lava_scheduler_app/views.py'
@@ -94,3 +94,42 @@
else:
return HttpResponseForbidden(
"you cannot cancel this job", content_type="text/plain")
+
+
+def device(request, pk):
+ device = Device.objects.get(pk=pk)
+ recent_jobs = TestJob.objects.all().filter(
+ actual_device=device).order_by('-start_time')
+ return render_to_response(
+ "lava_scheduler_app/device.html",
+ {
+ 'device': device,
+ 'recent_jobs': recent_jobs,
+ 'show_maintenance': device.can_admin(request.user) and \
+ device.status in [Device.IDLE, Device.RUNNING],
+ 'show_online': device.can_admin(request.user) and \
+ device.status in [Device.OFFLINE, Device.OFFLINING],
+ },
+ RequestContext(request))
+
+
+@post_only
+def device_maintenance_mode(request, pk):
+ device = Device.objects.get(pk=pk)
+ if device.can_admin(request.user):
+ device.put_into_maintenance_mode()
+ return redirect('lava_scheduler_app.views.device', pk=device.pk)
+ else:
+ return HttpResponseForbidden(
+ "you cannot administer this device", content_type="text/plain")
+
+
+@post_only
+def device_online(request, pk):
+ device = Device.objects.get(pk=pk)
+ if device.can_admin(request.user):
+ device.put_into_online_mode()
+ return redirect('lava_scheduler_app.views.device', pk=device.pk)
+ else:
+ return HttpResponseForbidden(
+ "you cannot administer this device", content_type="text/plain")
=== modified file 'lava_scheduler_daemon/dbjobsource.py'
@@ -124,7 +124,14 @@
def jobCompleted_impl(self, board_name):
self.logger.debug('marking job as complete on %s', board_name)
device = Device.objects.get(hostname=board_name)
- device.status = Device.IDLE
+ if device.status == Device.RUNNING:
+ device.status = Device.IDLE
+ elif device.status == Device.OFFLINING:
+ device.status = Device.OFFLINE
+ else:
+ self.logger.error(
+ "Unexpected device state in jobCompleted: %s" % device.status)
+ device.status = Device.IDLE
job = device.current_job
device.current_job = None
if job.status == TestJob.RUNNING: