From patchwork Thu Feb 16 07:24:11 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Spring Zhang X-Patchwork-Id: 6816 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 0B3E323EB0 for ; Thu, 16 Feb 2012 07:24:15 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 7DC22A18A8F for ; Thu, 16 Feb 2012 07:24:14 +0000 (UTC) Received: by iabz7 with SMTP id z7so3546279iab.11 for ; Wed, 15 Feb 2012 23:24:13 -0800 (PST) Received: by 10.50.203.6 with SMTP id km6mr1586397igc.25.1329377053918; Wed, 15 Feb 2012 23:24:13 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.80.19 with SMTP id r19cs8551ibk; Wed, 15 Feb 2012 23:24:12 -0800 (PST) Received: by 10.216.136.200 with SMTP id w50mr588344wei.2.1329377051854; Wed, 15 Feb 2012 23:24:11 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id fu1si2957064wib.35.2012.02.15.23.24.11 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 15 Feb 2012 23:24:11 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1RxvhL-0006un-6s for ; Thu, 16 Feb 2012 07:24:11 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 2824EE0283 for ; Thu, 16 Feb 2012 07:24:11 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-scheduler X-Launchpad-Branch: ~linaro-validation/lava-scheduler/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 132 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-scheduler/trunk] Rev 132: Add device health page for scheduler Message-Id: <20120216072411.23714.46781.launchpad@ackee.canonical.com> Date: Thu, 16 Feb 2012 07:24:11 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14781"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: c12a863e53fd31f27025f9c8e529df54d078b038 X-Gm-Message-State: ALoCoQmaeBdeb/sP/36A+9myQyVcnwafs/Qg2zshd6Kl6FbvqtG4gWD1YSAlqbHJHgbVZIl0/8xu Merge authors: Spring Zhang (qzhang) Related merge proposals: https://code.launchpad.net/~qzhang/lava-scheduler/add-labhealth-page/+merge/89254 proposed by: Paul Larson (pwlars) review: Approve - Michael Hudson-Doyle (mwhudson) review: Resubmit - Spring Zhang (qzhang) review: Needs Fixing - Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 132 [merge] committer: Spring Zhang branch nick: lava-scheduler timestamp: Thu 2012-02-16 15:21:24 +0800 message: Add device health page for scheduler added: lava_scheduler_app/migrations/0014_auto__add_field_device_health_status__add_field_device_last_health_rep.py lava_scheduler_app/migrations/0015_auto__add_field_testjob_health_check.py lava_scheduler_app/templates/lava_scheduler_app/health_jobs.html lava_scheduler_app/templates/lava_scheduler_app/labhealth.html modified: lava_scheduler_app/models.py lava_scheduler_app/templates/lava_scheduler_app/device.html lava_scheduler_app/templates/lava_scheduler_app/index.html lava_scheduler_app/urls.py lava_scheduler_app/views.py lava_scheduler_daemon/dbjobsource.py --- lp:lava-scheduler https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk You are subscribed to branch lp:lava-scheduler. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk/+edit-subscription === added file 'lava_scheduler_app/migrations/0014_auto__add_field_device_health_status__add_field_device_last_health_rep.py' --- lava_scheduler_app/migrations/0014_auto__add_field_device_health_status__add_field_device_last_health_rep.py 1970-01-01 00:00:00 +0000 +++ lava_scheduler_app/migrations/0014_auto__add_field_device_health_status__add_field_device_last_health_rep.py 2012-02-10 01:53:49 +0000 @@ -0,0 +1,124 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Device.health_status' + db.add_column('lava_scheduler_app_device', 'health_status', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) + + # Adding field 'Device.last_health_report_job' + db.add_column('lava_scheduler_app_device', 'last_health_report_job', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name=u'Health Report Job', unique=True, null=True, to=orm['lava_scheduler_app.TestJob']), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Device.health_status' + db.delete_column('lava_scheduler_app_device', 'health_status') + + # Deleting field 'Device.last_health_report_job' + db.delete_column('lava_scheduler_app_device', 'last_health_report_job_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'lava_scheduler_app.device': { + 'Meta': {'object_name': 'Device'}, + 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'device_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.DeviceType']"}), + 'health_status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'primary_key': 'True'}), + 'last_health_report_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'Health Report Job'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'lava_scheduler_app.devicestatetransition': { + 'Meta': {'object_name': 'DeviceStateTransition'}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'device': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['lava_scheduler_app.Device']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'null': 'True', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'new_state': ('django.db.models.fields.IntegerField', [], {}), + 'old_state': ('django.db.models.fields.IntegerField', [], {}) + }, + 'lava_scheduler_app.devicetype': { + 'Meta': {'object_name': 'DeviceType'}, + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'primary_key': 'True', 'db_index': 'True'}) + }, + 'lava_scheduler_app.tag': { + 'Meta': {'object_name': 'Tag'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) + }, + 'lava_scheduler_app.testjob': { + 'Meta': {'object_name': 'TestJob'}, + 'actual_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), + 'definition': ('django.db.models.fields.TextField', [], {}), + 'description': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_file': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'requested_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), + 'requested_device_type': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.DeviceType']"}), + 'results_link': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '400', 'null': 'True', 'blank': 'True'}), + 'start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'submit_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'submit_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['linaro_django_xmlrpc.AuthToken']", 'null': 'True', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'linaro_django_xmlrpc.authtoken': { + 'Meta': {'object_name': 'AuthToken'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_used_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'secret': ('django.db.models.fields.CharField', [], {'default': "'wxahi36scmitudt29p0u6qx6ktiq2d0od7uuq0ptklddiiayy1g1yf24p51qlbgbsns74dfdq0bwzz1uh0o4ps5981c2eu0hdrix0smnz4sftf9uros2z002t7gttc5h'", 'unique': 'True', 'max_length': '128'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['lava_scheduler_app'] === added file 'lava_scheduler_app/migrations/0015_auto__add_field_testjob_health_check.py' --- lava_scheduler_app/migrations/0015_auto__add_field_testjob_health_check.py 1970-01-01 00:00:00 +0000 +++ lava_scheduler_app/migrations/0015_auto__add_field_testjob_health_check.py 2012-02-10 01:53:49 +0000 @@ -0,0 +1,119 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'TestJob.health_check' + db.add_column('lava_scheduler_app_testjob', 'health_check', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'TestJob.health_check' + db.delete_column('lava_scheduler_app_testjob', 'health_check') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'lava_scheduler_app.device': { + 'Meta': {'object_name': 'Device'}, + 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'Current Job'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}), + 'device_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.DeviceType']"}), + 'health_status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'primary_key': 'True'}), + 'last_health_report_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'Health Report Job'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'lava_scheduler_app.devicestatetransition': { + 'Meta': {'object_name': 'DeviceStateTransition'}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'device': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['lava_scheduler_app.Device']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'null': 'True', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'new_state': ('django.db.models.fields.IntegerField', [], {}), + 'old_state': ('django.db.models.fields.IntegerField', [], {}) + }, + 'lava_scheduler_app.devicetype': { + 'Meta': {'object_name': 'DeviceType'}, + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'primary_key': 'True', 'db_index': 'True'}) + }, + 'lava_scheduler_app.tag': { + 'Meta': {'object_name': 'Tag'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) + }, + 'lava_scheduler_app.testjob': { + 'Meta': {'object_name': 'TestJob'}, + 'actual_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), + 'definition': ('django.db.models.fields.TextField', [], {}), + 'description': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'health_check': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'log_file': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'requested_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), + 'requested_device_type': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.DeviceType']"}), + 'results_link': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '400', 'null': 'True', 'blank': 'True'}), + 'start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'submit_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'submit_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['linaro_django_xmlrpc.AuthToken']", 'null': 'True', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'linaro_django_xmlrpc.authtoken': { + 'Meta': {'object_name': 'AuthToken'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_used_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'secret': ('django.db.models.fields.CharField', [], {'default': "'ezum04q7hfcefuclm2hfdaeh9n5mljd2r6uo6ua440j2p2grb6aspla2o0p0t1jl7udoqehyk2qgy50zryucuj1mwtttdqhceg00dfy0gdcmsbexhf0e7o2ndd73ndjb'", 'unique': 'True', 'max_length': '128'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['lava_scheduler_app'] === modified file 'lava_scheduler_app/models.py' --- lava_scheduler_app/models.py 2012-02-09 19:18:18 +0000 +++ lava_scheduler_app/models.py 2012-02-16 01:22:23 +0000 @@ -50,6 +50,14 @@ (OFFLINING, 'Going offline'), ) + # A device health shows a device is ready to test or not + HEALTH_UNKNOWN, HEALTH_HEALTHY, HEALTH_SICK = range(3) + HEALTH_CHOICES = ( + (HEALTH_UNKNOWN, 'Unknown'), + (HEALTH_HEALTHY, 'Healthy'), + (HEALTH_SICK, 'Sick'), + ) + hostname = models.CharField( verbose_name = _(u"Hostname"), max_length = 200, @@ -60,7 +68,7 @@ DeviceType, verbose_name=_(u"Device type")) current_job = models.ForeignKey( - "TestJob", blank=True, unique=True, null=True) + "TestJob", blank=True, unique=True, null=True, related_name='+') tags = models.ManyToManyField(Tag, blank=True) @@ -70,6 +78,15 @@ verbose_name = _(u"Device status"), ) + health_status = models.IntegerField( + choices = HEALTH_CHOICES, + default = HEALTH_UNKNOWN, + verbose_name = _(u"Device Health"), + ) + + last_health_report_job = models.ForeignKey( + "TestJob", blank=True, unique=True, null=True, related_name='+') + def __unicode__(self): return self.hostname @@ -77,6 +94,10 @@ def get_absolute_url(self): return ("lava.scheduler.device.detail", [self.pk]) + @models.permalink + def get_device_health_url(self): + return ("lava.scheduler.labhealth.detail", [self.pk]) + def recent_jobs(self): return TestJob.objects.select_related( "actual_device", @@ -156,6 +177,8 @@ default = None ) + health_check = models.BooleanField(default=False) + # Only one of these two should be non-null. requested_device = models.ForeignKey( Device, null=True, default=None, related_name='+', blank=True) @@ -229,6 +252,9 @@ raise JSONDataError( "Neither 'target' nor 'device_type' found in job data.") job_name = job_data.get('job_name', '') + + is_check = job_data.get('health_check', False) + tags = [] for tag_name in job_data.get('device_tags', []): try: @@ -237,7 +263,8 @@ raise JSONDataError("tag %r does not exist" % tag_name) job = TestJob( definition=json_data, submitter=user, requested_device=target, - requested_device_type=device_type, description=job_name) + requested_device_type=device_type, description=job_name, + health_check=is_check) job.save() for tag in tags: job.tags.add(tag) === modified file 'lava_scheduler_app/templates/lava_scheduler_app/device.html' --- lava_scheduler_app/templates/lava_scheduler_app/device.html 2012-02-09 18:37:46 +0000 +++ lava_scheduler_app/templates/lava_scheduler_app/device.html 2012-02-16 02:27:05 +0000 @@ -76,6 +76,10 @@ (reason: {{ transition }}) {% endif %} +
Health Status:
+
+ {{ device.get_health_status_display }} +
{% if device.current_job %}
Currently running:
+.column { + position: relative; + float: left; + padding-right: 2em; + padding-bottom: 1em; +} + +{% endblock %} + +{% block content %} +

Device {{ device }} Health Job List

+ +{% if show_maintenance %} +
+ {% csrf_token %} + +
+{% endif %} +{% if show_online %} +
+ {% csrf_token %} + +
+{% endif %} + +
+
+
Hostname:
+
{{ device.hostname }}
+ +
Device type:
+
{{ device.device_type }}
+ +
Device Tags
+ {% for tag in device.tags.all %} +
{{ tag.name }}
+ {% empty %} +
None
+ {% endfor %} + +
+
+ + + + + + + + + + + + + + + {% for job in recent_job_list %} + + + + + + + + + {% endfor %} + +
IDStatusDeviceSubmitterStart TimeFinish Time
{{ job.id }}{{ job.get_status_display }}{{ job.actual_device|default:'' }}{{ job.submitter }}{{ job.start_time }}{{ job.end_time|default:"not finished" }}
+ + + +{% endblock %} === modified file 'lava_scheduler_app/templates/lava_scheduler_app/index.html' --- lava_scheduler_app/templates/lava_scheduler_app/index.html 2011-10-28 00:24:13 +0000 +++ lava_scheduler_app/templates/lava_scheduler_app/index.html 2012-02-16 02:20:50 +0000 @@ -9,6 +9,7 @@ Hostname Type Status + Health Status @@ -19,11 +20,14 @@ {{ device.device_type }} {{ device.get_status_display }} + {{ device.get_health_status_display }} {% endfor %} +All Devices Health +

Active Jobs

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/labhealth.html' --- lava_scheduler_app/templates/lava_scheduler_app/labhealth.html 1970-01-01 00:00:00 +0000 +++ lava_scheduler_app/templates/lava_scheduler_app/labhealth.html 2012-02-10 11:15:14 +0000 @@ -0,0 +1,43 @@ +{% extends "lava_scheduler_app/_content.html" %} + + +{% block content %} +

Lab Health

+ +
+ + + + + + + + + + {% for device_health in device_health_list %} + + + + + + + {% endfor %} + +
HostnameHealth StatusLast Report TimeLast Report Job
{{ device_health.hostname }}{{ device_health.get_health_status_display }}{{ device_health.last_health_report_job.end_time }}{{ device_health.last_health_report_job.id }}
+ + + +{% endblock %} === modified file 'lava_scheduler_app/urls.py' --- lava_scheduler_app/urls.py 2012-02-10 01:24:02 +0000 +++ lava_scheduler_app/urls.py 2012-02-16 05:41:14 +0000 @@ -21,6 +21,12 @@ url(r'^device/(?P[-_a-zA-Z0-9]+)/online$', 'device_online', name='lava.scheduler.device.online'), + url(r'^labhealth/$', + 'lab_health', + name='lava.scheduler.labhealth'), + url(r'^labhealth/device/(?P[-_a-zA-Z0-9]+)$', + 'health_job_list', + name='lava.scheduler.labhealth.detail'), url(r'^job/(?P[0-9]+)$', 'job_detail', name='lava.scheduler.job.detail'), === modified file 'lava_scheduler_app/views.py' --- lava_scheduler_app/views.py 2012-02-10 19:50:40 +0000 +++ lava_scheduler_app/views.py 2012-02-16 05:41:14 +0000 @@ -74,6 +74,44 @@ }, RequestContext(request)) +@BreadCrumb("All Device Health", parent=index) +def lab_health(request): + device_health_list = Device.objects.select_related( + "hostname", "health_status").all() + return render_to_response( + "lava_scheduler_app/labhealth.html", + { + 'device_health_list': device_health_list, + 'bread_crumb_trail': BreadCrumbTrail.leading_to(lab_health), + }, + RequestContext(request)) + +@BreadCrumb("All Health Jobs on Device {pk}", parent=index, needs=['pk']) +def health_job_list(request, pk): + device = get_object_or_404(Device, pk=pk) + recent_health_jobs = TestJob.objects.select_related( + "actual_device", + "health_check", + "end_time", + ).filter( + actual_device=device, + health_check=True + ).order_by( + '-end_time' + ) + + return render_to_response( + "lava_scheduler_app/health_jobs.html", + { + 'device': device, + 'recent_job_list': recent_health_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], + 'bread_crumb_trail': BreadCrumbTrail.leading_to(health_job_list, pk=pk), + }, + RequestContext(request)) def device_callback(job): if job.actual_device: === modified file 'lava_scheduler_daemon/dbjobsource.py' --- lava_scheduler_daemon/dbjobsource.py 2012-02-09 18:20:33 +0000 +++ lava_scheduler_daemon/dbjobsource.py 2012-02-10 20:08:25 +0000 @@ -214,6 +214,15 @@ DeviceStateTransition.objects.create( created_by=None, device=device, old_state=old_device_status, new_state=device.status, message=None, job=job).save() + + if job.health_check is True: + device.last_health_report_job = job + if job.status == TestJob.INCOMPLETE: + device.health_status = Device.HEALTH_SICK + device.put_into_maintenance_mode(None, "Health Check Job Failed") + elif job.status == TestJob.COMPLETE: + device.health_status = Device.HEALTH_HEALTHY + job.end_time = datetime.datetime.utcnow() token = job.submit_token job.submit_token = None