Implement periodic tasks 71/19071/1
authormaxbr <maxbr@mi.fu-berlin.de>
Fri, 19 Aug 2016 15:11:58 +0000 (17:11 +0200)
committermaxbr <maxbr@mi.fu-berlin.de>
Fri, 19 Aug 2016 15:11:58 +0000 (17:11 +0200)
JIRA: RELENG-12

The dashboard is now querying jenkins periodically and saving the
results in the database. This fixes delays that were caused by calling
the jenkins API.

Signed-off-by: maxbr <maxbr@mi.fu-berlin.de>
28 files changed:
tools/pharos-dashboard/account/middleware.py
tools/pharos-dashboard/account/migrations/0001_initial.py
tools/pharos-dashboard/booking/migrations/0001_initial.py
tools/pharos-dashboard/booking/tests/test_models.py
tools/pharos-dashboard/booking/tests/test_views.py
tools/pharos-dashboard/celerybeat-schedule [new file with mode: 0644]
tools/pharos-dashboard/dashboard/fixtures/dashboard.json
tools/pharos-dashboard/dashboard/migrations/0001_initial.py
tools/pharos-dashboard/dashboard/migrations/0002_auto_20160815_1511.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/migrations/0003_auto_20160815_1512.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/migrations/0004_resource_slave.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/migrations/0005_remove_resource_slavename.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/models.py
tools/pharos-dashboard/dashboard/templatetags/__init__.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/templatetags/jenkins_filters.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/views.py
tools/pharos-dashboard/jenkins/adapter.py
tools/pharos-dashboard/jenkins/migrations/0001_initial.py [new file with mode: 0644]
tools/pharos-dashboard/jenkins/migrations/0002_auto_20160815_1226.py [new file with mode: 0644]
tools/pharos-dashboard/jenkins/migrations/__init__.py [new file with mode: 0644]
tools/pharos-dashboard/jenkins/models.py [new file with mode: 0644]
tools/pharos-dashboard/jenkins/tasks.py [new file with mode: 0644]
tools/pharos-dashboard/pharos_dashboard/__init__.py
tools/pharos-dashboard/pharos_dashboard/celery.py [new file with mode: 0644]
tools/pharos-dashboard/pharos_dashboard/settings.py
tools/pharos-dashboard/templates/dashboard/ci_pods.html
tools/pharos-dashboard/templates/dashboard/dev_pods.html
tools/pharos-dashboard/templates/dashboard/jenkins_slaves.html

index f5170ba..6f7cac7 100644 (file)
@@ -1,7 +1,8 @@
-from django.core.exceptions import ObjectDoesNotExist
 from django.utils import timezone
 from django.utils.deprecation import MiddlewareMixin
 
 from django.utils import timezone
 from django.utils.deprecation import MiddlewareMixin
 
+from account.models import UserProfile
+
 
 class TimezoneMiddleware(MiddlewareMixin):
     """
 
 class TimezoneMiddleware(MiddlewareMixin):
     """
@@ -10,6 +11,12 @@ class TimezoneMiddleware(MiddlewareMixin):
     """
     def process_request(self, request):
         if request.user.is_authenticated:
     """
     def process_request(self, request):
         if request.user.is_authenticated:
-            timezone.activate(request.user.userprofile.timezone)
+            try:
+                tz = request.user.userprofile.timezone
+                timezone.activate(tz)
+            except UserProfile.DoesNotExist:
+                UserProfile.objects.create(user=request.user)
+                tz = request.user.userprofile.timezone
+                timezone.activate(tz)
         else:
             timezone.deactivate()
         else:
             timezone.deactivate()
index 752d517..4ff9510 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-08-12 09:51
+# Generated by Django 1.10 on 2016-08-15 12:19
 from __future__ import unicode_literals
 
 from django.conf import settings
 from __future__ import unicode_literals
 
 from django.conf import settings
@@ -13,7 +13,6 @@ class Migration(migrations.Migration):
 
     dependencies = [
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 
     dependencies = [
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('dashboard', '0001_initial'),
     ]
 
     operations = [
     ]
 
     operations = [
@@ -21,7 +20,9 @@ class Migration(migrations.Migration):
             name='UserProfile',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
             name='UserProfile',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('sshkey', models.CharField(max_length=1024)),
+                ('timezone', models.CharField(default='UTC', max_length=100)),
+                ('sshkey', models.CharField(max_length=2048)),
+                ('pgpkey', models.CharField(max_length=2048)),
                 ('company', models.CharField(max_length=200)),
                 ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
                 ('company', models.CharField(max_length=200)),
                 ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
@@ -29,15 +30,4 @@ class Migration(migrations.Migration):
                 'db_table': 'user_profile',
             },
         ),
                 'db_table': 'user_profile',
             },
         ),
-        migrations.CreateModel(
-            name='UserResource',
-            fields=[
-                ('id', models.AutoField(primary_key=True, serialize=False)),
-                ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')),
-                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'db_table': 'user_resource',
-            },
-        ),
     ]
     ]
index 57735ee..9706b81 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-08-12 09:51
+# Generated by Django 1.10 on 2016-08-15 12:19
 from __future__ import unicode_literals
 
 from django.conf import settings
 from __future__ import unicode_literals
 
 from django.conf import settings
@@ -12,8 +12,8 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
     initial = True
 
     dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
         ('dashboard', '0001_initial'),
         ('dashboard', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
     ]
 
     operations = [
@@ -21,10 +21,8 @@ class Migration(migrations.Migration):
             name='Booking',
             fields=[
                 ('id', models.AutoField(primary_key=True, serialize=False)),
             name='Booking',
             fields=[
                 ('id', models.AutoField(primary_key=True, serialize=False)),
-                ('deleted', models.BooleanField(default=False)),
                 ('start', models.DateTimeField()),
                 ('end', models.DateTimeField()),
                 ('start', models.DateTimeField()),
                 ('end', models.DateTimeField()),
-                ('status', models.CharField(max_length=20)),
                 ('purpose', models.CharField(max_length=300)),
                 ('resource', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dashboard.Resource')),
                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
                 ('purpose', models.CharField(max_length=300)),
                 ('resource', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dashboard.Resource')),
                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
index e933f6e..00f6b26 100644 (file)
@@ -6,12 +6,15 @@ from django.utils import timezone
 
 from booking.models import Booking
 from dashboard.models import Resource
 
 from booking.models import Booking
 from dashboard.models import Resource
+from jenkins.models import JenkinsSlave
 
 
 class BookingModelTestCase(TestCase):
     def setUp(self):
 
 
 class BookingModelTestCase(TestCase):
     def setUp(self):
-        self.res1 = Resource.objects.create(name='res1', slavename='s1', description='x', url='x')
-        self.res2 = Resource.objects.create(name='res2', slavename='s2', description='x', url='x')
+        self.slave = JenkinsSlave.objects.create(name='test', url='test')
+
+        self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x', url='x')
+        self.res2 = Resource.objects.create(name='res2', slave=self.slave, description='x', url='x')
 
         self.user1 = User.objects.create(username='user1')
 
 
         self.user1 = User.objects.create(username='user1')
 
index f5b75d1..4f5ee8b 100644 (file)
@@ -12,12 +12,14 @@ from registration.forms import User
 from account.models import UserProfile
 from booking.models import Booking
 from dashboard.models import Resource
 from account.models import UserProfile
 from booking.models import Booking
 from dashboard.models import Resource
+from jenkins.models import JenkinsSlave
 
 
 class BookingViewTestCase(TestCase):
     def setUp(self):
         self.client = Client()
 
 
 class BookingViewTestCase(TestCase):
     def setUp(self):
         self.client = Client()
-        self.res1 = Resource.objects.create(name='res1', slavename='s1', description='x', url='x')
+        self.slave = JenkinsSlave.objects.create(name='test', url='test')
+        self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x', url='x')
         self.user1 = User.objects.create(username='user1')
         self.user1.set_password('user1')
         self.user1profile = UserProfile.objects.create(user=self.user1)
         self.user1 = User.objects.create(username='user1')
         self.user1.set_password('user1')
         self.user1profile = UserProfile.objects.create(user=self.user1)
diff --git a/tools/pharos-dashboard/celerybeat-schedule b/tools/pharos-dashboard/celerybeat-schedule
new file mode 100644 (file)
index 0000000..7e5fe85
Binary files /dev/null and b/tools/pharos-dashboard/celerybeat-schedule differ
index f8c1fc1..d90e99b 100644 (file)
@@ -6,9 +6,7 @@
         "name": "Linux Foundation POD 1",
         "slavename": "lf-pod1",
         "description": "Some description",
         "name": "Linux Foundation POD 1",
         "slavename": "lf-pod1",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab"
     }
 },
 {
     }
 },
 {
@@ -18,9 +16,7 @@
         "name": "Linux Foundation POD 2",
         "slavename": "lf-pod2",
         "description": "Some description",
         "name": "Linux Foundation POD 2",
         "slavename": "lf-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab"
     }
 },
 {
     }
 },
 {
@@ -30,9 +26,7 @@
         "name": "Ericsson  POD 2",
         "slavename": "ericsson-pod2",
         "description": "Some description",
         "name": "Ericsson  POD 2",
         "slavename": "ericsson-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process"
     }
 },
 {
     }
 },
 {
@@ -42,9 +36,7 @@
         "name": "Intel POD 2",
         "slavename": "intel-pod2",
         "description": "Some description",
         "name": "Intel POD 2",
         "slavename": "intel-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2"
     }
 },
 {
     }
 },
 {
@@ -54,9 +46,7 @@
         "name": "Intel POD 5",
         "slavename": "intel-pod5",
         "description": "Some description",
         "name": "Intel POD 5",
         "slavename": "intel-pod5",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5"
     }
 },
 {
     }
 },
 {
@@ -66,9 +56,7 @@
         "name": "Intel POD 6",
         "slavename": "intel-pod6",
         "description": "Some description",
         "name": "Intel POD 6",
         "slavename": "intel-pod6",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6"
     }
 },
 {
     }
 },
 {
@@ -78,9 +66,7 @@
         "name": "Intel POD 8",
         "slavename": "intel-pod8",
         "description": "Some description",
         "name": "Intel POD 8",
         "slavename": "intel-pod8",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8"
     }
 },
 {
     }
 },
 {
@@ -90,9 +76,7 @@
         "name": "Huawei POD 1",
         "slavename": "huawei-pod1",
         "description": "Some description",
         "name": "Huawei POD 1",
         "slavename": "huawei-pod1",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting",
-        "bookable": false,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Intel POD 3",
         "slavename": "intel-pod3",
         "description": "Some description",
         "name": "Intel POD 3",
         "slavename": "intel-pod3",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3"
     }
 },
 {
     }
 },
 {
         "name": "Dell POD 1",
         "slavename": "dell-pod1",
         "description": "Some description",
         "name": "Dell POD 1",
         "slavename": "dell-pod1",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Dell POD 2",
         "slavename": "dell-pod2",
         "description": "Some description",
         "name": "Dell POD 2",
         "slavename": "dell-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Orange POD 2",
         "slavename": "orange-pod2",
         "description": "Some description",
         "name": "Orange POD 2",
         "slavename": "orange-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2"
     }
 },
 {
     }
 },
 {
         "name": "Arm POD 1",
         "slavename": "arm-build1",
         "description": "Some description",
         "name": "Arm POD 1",
         "slavename": "arm-build1",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab"
     }
 },
 {
     }
 },
 {
         "name": "Ericsson POD 1",
         "slavename": "ericsson-pod1",
         "description": "Some description",
         "name": "Ericsson POD 1",
         "slavename": "ericsson-pod1",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process"
     }
 },
 {
     }
 },
 {
         "name": "Huawei POD 2",
         "slavename": "huawei-pod2",
         "description": "Some description",
         "name": "Huawei POD 2",
         "slavename": "huawei-pod2",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Huawei POD 3",
         "slavename": "huawei-pod3",
         "description": "Some description",
         "name": "Huawei POD 3",
         "slavename": "huawei-pod3",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Huawei POD 4",
         "slavename": "huawei-pod4",
         "description": "Some description",
         "name": "Huawei POD 4",
         "slavename": "huawei-pod4",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
     }
 },
 {
     }
 },
 {
         "name": "Intel POD 9",
         "slavename": "intel-pod9",
         "description": "Some description",
         "name": "Intel POD 9",
         "slavename": "intel-pod9",
         "description": "Some description",
-        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9",
-        "bookable": true,
-        "active": true
+        "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9"
     }
 }
 ]
     }
 }
 ]
index b93d829..6343b46 100644 (file)
@@ -1,7 +1,8 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-08-12 09:51
+# Generated by Django 1.10 on 2016-08-15 12:19
 from __future__ import unicode_literals
 
 from __future__ import unicode_literals
 
+from django.conf import settings
 from django.db import migrations, models
 
 
 from django.db import migrations, models
 
 
@@ -10,6 +11,7 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
     initial = True
 
     dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
     ]
 
     operations = [
@@ -21,11 +23,18 @@ class Migration(migrations.Migration):
                 ('slavename', models.CharField(blank=True, max_length=50, null=True)),
                 ('description', models.CharField(blank=True, max_length=300, null=True)),
                 ('url', models.CharField(blank=True, max_length=100, null=True)),
                 ('slavename', models.CharField(blank=True, max_length=50, null=True)),
                 ('description', models.CharField(blank=True, max_length=300, null=True)),
                 ('url', models.CharField(blank=True, max_length=100, null=True)),
-                ('bookable', models.BooleanField(default=False)),
-                ('active', models.BooleanField(default=True)),
+                ('owners', models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
             ],
             options={
                 'db_table': 'resource',
             },
         ),
             ],
             options={
                 'db_table': 'resource',
             },
         ),
+        migrations.CreateModel(
+            name='ResourceUtilization',
+            fields=[
+                ('timestamp', models.DateTimeField(auto_created=True)),
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('pod_status', models.IntegerField()),
+            ],
+        ),
     ]
     ]
diff --git a/tools/pharos-dashboard/dashboard/migrations/0002_auto_20160815_1511.py b/tools/pharos-dashboard/dashboard/migrations/0002_auto_20160815_1511.py
new file mode 100644 (file)
index 0000000..67822a7
--- /dev/null
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 15:11
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('jenkins', '0002_auto_20160815_1226'),
+        ('dashboard', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='resource',
+            name='slavename',
+        ),
+        migrations.AddField(
+            model_name='resource',
+            name='slave',
+            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/tools/pharos-dashboard/dashboard/migrations/0003_auto_20160815_1512.py b/tools/pharos-dashboard/dashboard/migrations/0003_auto_20160815_1512.py
new file mode 100644 (file)
index 0000000..53b4fcd
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 15:12
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dashboard', '0002_auto_20160815_1511'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='resource',
+            name='slave',
+        ),
+        migrations.AddField(
+            model_name='resource',
+            name='slavename',
+            field=models.CharField(blank=True, max_length=50, null=True),
+        ),
+    ]
diff --git a/tools/pharos-dashboard/dashboard/migrations/0004_resource_slave.py b/tools/pharos-dashboard/dashboard/migrations/0004_resource_slave.py
new file mode 100644 (file)
index 0000000..82d45f0
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 15:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('jenkins', '0002_auto_20160815_1226'),
+        ('dashboard', '0003_auto_20160815_1512'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='resource',
+            name='slave',
+            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/tools/pharos-dashboard/dashboard/migrations/0005_remove_resource_slavename.py b/tools/pharos-dashboard/dashboard/migrations/0005_remove_resource_slavename.py
new file mode 100644 (file)
index 0000000..339f8c3
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 15:17
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dashboard', '0004_resource_slave'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='resource',
+            name='slavename',
+        ),
+    ]
index 973066b..cb6b92b 100644 (file)
@@ -1,14 +1,17 @@
 from django.contrib.auth.models import User
 from django.db import models
 from django.contrib.auth.models import User
 from django.db import models
+from django.utils import timezone
+
+from jenkins.models import JenkinsSlave
 
 
 class Resource(models.Model):
     id = models.AutoField(primary_key=True)
     name = models.CharField(max_length=100, unique=True)
 
 
 class Resource(models.Model):
     id = models.AutoField(primary_key=True)
     name = models.CharField(max_length=100, unique=True)
-    slavename = models.CharField(max_length=50, blank=True, null=True)
     description = models.CharField(max_length=300, blank=True, null=True)
     url = models.CharField(max_length=100, blank=True, null=True)
     owners = models.ManyToManyField(User)
     description = models.CharField(max_length=300, blank=True, null=True)
     url = models.CharField(max_length=100, blank=True, null=True)
     owners = models.ManyToManyField(User)
+    slave = models.ForeignKey(JenkinsSlave, on_delete=models.DO_NOTHING)
 
     class Meta:
         db_table = 'resource'
 
     class Meta:
         db_table = 'resource'
@@ -16,6 +19,7 @@ class Resource(models.Model):
     def __str__(self):
         return self.name
 
     def __str__(self):
         return self.name
 
+
 class ResourceUtilization(models.Model):
     POD_STATUS = {
         'online': 1,
 class ResourceUtilization(models.Model):
     POD_STATUS = {
         'online': 1,
@@ -25,4 +29,4 @@ class ResourceUtilization(models.Model):
 
     id = models.AutoField(primary_key=True)
     timestamp = models.DateTimeField(auto_created=True)
 
     id = models.AutoField(primary_key=True)
     timestamp = models.DateTimeField(auto_created=True)
-    pod_status = models.IntegerField()
\ No newline at end of file
+    pod_status = models.IntegerField()
diff --git a/tools/pharos-dashboard/dashboard/templatetags/__init__.py b/tools/pharos-dashboard/dashboard/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/pharos-dashboard/dashboard/templatetags/jenkins_filters.py b/tools/pharos-dashboard/dashboard/templatetags/jenkins_filters.py
new file mode 100644 (file)
index 0000000..f7e00a8
--- /dev/null
@@ -0,0 +1,27 @@
+from django.template.defaultfilters import register
+
+
+@register.filter
+def jenkins_job_color(job_result):
+    if job_result == 'SUCCESS':
+        return '#5cb85c'
+    if job_result == 'FAILURE':
+        return '#d9534f'
+    if job_result == 'UNSTABLE':
+        return '#EDD62B'
+    return '#646F73' # job is still building
+
+
+@register.filter
+def jenkins_status_color(slave_status):
+    if slave_status == 'offline':
+        return '#d9534f'
+    if slave_status == 'online':
+        return '#5cb85c'
+    if slave_status == 'online / idle':
+        return '#5bc0de'
+
+@register.filter
+def jenkins_job_blink(job_result):
+    if job_result == '': # job is still building
+        return 'class=blink_me'
\ No newline at end of file
index ed62b35..a4c0c4e 100644 (file)
@@ -1,19 +1,18 @@
+from datetime import timedelta
 from django.utils import timezone
 from django.views.generic import TemplateView
 
 from booking.models import Booking
 from dashboard.models import Resource
 from jenkins import adapter as jenkins
 from django.utils import timezone
 from django.views.generic import TemplateView
 
 from booking.models import Booking
 from dashboard.models import Resource
 from jenkins import adapter as jenkins
+from jenkins.models import JenkinsSlave, JenkinsStatistic
 
 
 class JenkinsSlavesView(TemplateView):
     template_name = "dashboard/jenkins_slaves.html"
 
     def get_context_data(self, **kwargs):
 
 
 class JenkinsSlavesView(TemplateView):
     template_name = "dashboard/jenkins_slaves.html"
 
     def get_context_data(self, **kwargs):
-        slaves = jenkins.get_all_slaves()
-        for slave in slaves:
-            jenkins.parse_slave_data(slave, slave)
-
+        slaves = JenkinsSlave.objects.all()
         context = super(JenkinsSlavesView, self).get_context_data(**kwargs)
         context.update({'title': "Jenkins Slaves", 'slaves': slaves})
         return context
         context = super(JenkinsSlavesView, self).get_context_data(**kwargs)
         context.update({'title': "Jenkins Slaves", 'slaves': slaves})
         return context
@@ -23,15 +22,7 @@ class CIPodsView(TemplateView):
     template_name = "dashboard/ci_pods.html"
 
     def get_context_data(self, **kwargs):
     template_name = "dashboard/ci_pods.html"
 
     def get_context_data(self, **kwargs):
-        resources = Resource.objects.filter().values()  # get resources as a set of dicts
-        ci_pods = []
-        for resource in resources:
-            if not jenkins.is_ci_slave(resource['slavename']):
-                continue
-            ci_slave = jenkins.get_slave(resource['slavename'])
-            jenkins.parse_slave_data(resource, ci_slave)
-            ci_pods.append(resource)
-
+        ci_pods = Resource.objects.filter(slave__ci_slave=True)
         context = super(CIPodsView, self).get_context_data(**kwargs)
         context.update({'title': "CI Pods", 'ci_pods': ci_pods})
         return context
         context = super(CIPodsView, self).get_context_data(**kwargs)
         context.update({'title': "CI Pods", 'ci_pods': ci_pods})
         return context
@@ -41,21 +32,18 @@ class DevelopmentPodsView(TemplateView):
     template_name = "dashboard/dev_pods.html"
 
     def get_context_data(self, **kwargs):
     template_name = "dashboard/dev_pods.html"
 
     def get_context_data(self, **kwargs):
-        resources = Resource.objects.filter().values()  # get resources as a set of dicts
-        dev_pods = []
+        resources = Resource.objects.filter(slave__dev_pod=True)
 
 
-        current_bookings = Booking.objects.filter(start__lte=timezone.now())
-        current_bookings = current_bookings.filter(end__gt=timezone.now())
+        bookings = Booking.objects.filter(start__lte=timezone.now())
+        bookings = bookings.filter(end__gt=timezone.now())
 
 
+        dev_pods = []
         for resource in resources:
         for resource in resources:
-            if not jenkins.is_dev_pod(resource['slavename']):
-                continue
-            dev_pod = jenkins.get_slave(resource['slavename'])
-            jenkins.parse_slave_data(resource, dev_pod)
-            for booking in current_bookings:
-                if booking.resource.slavename == resource['slavename']:
-                    resource['current_booking'] = booking
-            dev_pods.append(resource)
+            dev_pod = (resource, None)
+            for booking in bookings:
+                if booking.resource == resource:
+                    dev_pod = (resource, booking)
+            dev_pods.append(dev_pod)
 
         context = super(DevelopmentPodsView, self).get_context_data(**kwargs)
         context.update({'title': "Development Pods", 'dev_pods': dev_pods})
 
         context = super(DevelopmentPodsView, self).get_context_data(**kwargs)
         context.update({'title': "Development Pods", 'dev_pods': dev_pods})
index 06233af..fabd535 100644 (file)
@@ -14,7 +14,7 @@ def get_json(url):
             response = requests.get(url)
             json = response.json()
             cache.set(url, json, 180)  # cache result for 180 seconds
             response = requests.get(url)
             json = response.json()
             cache.set(url, json, 180)  # cache result for 180 seconds
-            return response.json()
+            return json
         except requests.exceptions.RequestException as e:
             logger.exception(e)
         except ValueError as e:
         except requests.exceptions.RequestException as e:
             logger.exception(e)
         except ValueError as e:
@@ -85,26 +85,22 @@ def is_dev_pod(slavename):
         return True
     return False
 
         return True
     return False
 
-def parse_slave_data(slave_dict, slave):
-    slave_dict['status'] = get_slave_status(slave)
-    slave_dict['status_color'] = get_status_color(slave)
-    slave_dict['slaveurl'] = get_slave_url(slave)
-    job = get_jenkins_job(slave['displayName'])
-    if job is not None:
-        slave_dict['last_job'] = parse_job(job)
-
 
 def parse_job(job):
     result = parse_job_string(job['lastBuild']['fullDisplayName'])
 
 def parse_job(job):
     result = parse_job_string(job['lastBuild']['fullDisplayName'])
+    result['building'] = job['lastBuild']['building']
+    result['result'] = ''
+    if not job['lastBuild']['building']:
+        result['result'] = job['lastBuild']['result']
     result['url'] = job['url']
     result['url'] = job['url']
-    result['color'] = get_job_color(job)
-    if job['lastBuild']['building']:
-        result['blink'] = 'class=blink_me'
     return result
 
 
 def parse_job_string(full_displayname):
     job = {}
     return result
 
 
 def parse_job_string(full_displayname):
     job = {}
+    job['scenario'] = ''
+    job['installer'] = ''
+    job['branch'] = ''
     tokens = re.split(r'[ -]', full_displayname)
     for i in range(len(tokens)):
         if tokens[i] == 'os':
     tokens = re.split(r'[ -]', full_displayname)
     for i in range(len(tokens)):
         if tokens[i] == 'os':
@@ -113,34 +109,10 @@ def parse_job_string(full_displayname):
             job['installer'] = tokens[i]
         elif tokens[i] in ['master', 'arno', 'brahmaputra', 'colorado']:
             job['branch'] = tokens[i]
             job['installer'] = tokens[i]
         elif tokens[i] in ['master', 'arno', 'brahmaputra', 'colorado']:
             job['branch'] = tokens[i]
-
     tokens = full_displayname.split(' ')
     job['name'] = tokens[0]
     return job
 
     tokens = full_displayname.split(' ')
     job['name'] = tokens[0]
     return job
 
-
-# TODO: use css
-def get_job_color(job):
-    if job['lastBuild']['building'] is True:
-        return '#646F73'
-    result = job['lastBuild']['result']
-    if result == 'SUCCESS':
-        return '#5cb85c'
-    if result == 'FAILURE':
-        return '#d9534f'
-    if result == 'UNSTABLE':
-        return '#EDD62B'
-
-
-# TODO: use css
-def get_status_color(slave):
-    if not slave['offline'] and slave['idle']:
-        return '#5bc0de'
-    if not slave['offline']:
-        return '#5cb85c'
-    return '#d9534f'
-
-
 def get_slave_url(slave):
     return 'https://build.opnfv.org/ci/computer/' + slave['displayName']
 
 def get_slave_url(slave):
     return 'https://build.opnfv.org/ci/computer/' + slave['displayName']
 
diff --git a/tools/pharos-dashboard/jenkins/migrations/0001_initial.py b/tools/pharos-dashboard/jenkins/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..a9bb8d5
--- /dev/null
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 12:19
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='JenkinsSlave',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('status', models.CharField(default='offline', max_length=30)),
+                ('url', models.CharField(max_length=1024)),
+                ('ci_slave', models.BooleanField(default=False)),
+                ('dev_pod', models.BooleanField(default=False)),
+                ('building', models.BooleanField(default=False)),
+                ('last_job_name', models.CharField(default='', max_length=1024)),
+                ('last_job_url', models.CharField(default='', max_length=1024)),
+                ('last_job_scenario', models.CharField(default='', max_length=50)),
+                ('last_job_branch', models.CharField(default='', max_length=50)),
+                ('last_job_installer', models.CharField(default='', max_length=50)),
+                ('last_job_result', models.CharField(default='', max_length=30)),
+            ],
+            options={
+                'db_table': 'jenkins_slave',
+            },
+        ),
+        migrations.CreateModel(
+            name='JenkinsStatistic',
+            fields=[
+                ('timestamp', models.DateTimeField(auto_created=True)),
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('offline', models.BooleanField(default=False)),
+                ('idle', models.BooleanField(default=False)),
+                ('online', models.BooleanField(default=False)),
+                ('slave', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='jenkins.JenkinsSlave')),
+            ],
+            options={
+                'db_table': 'jenkins_statistic',
+            },
+        ),
+    ]
diff --git a/tools/pharos-dashboard/jenkins/migrations/0002_auto_20160815_1226.py b/tools/pharos-dashboard/jenkins/migrations/0002_auto_20160815_1226.py
new file mode 100644 (file)
index 0000000..f1cf7f9
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-08-15 12:26
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('jenkins', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='jenkinsstatistic',
+            name='timestamp',
+            field=models.DateTimeField(auto_now_add=True),
+        ),
+    ]
diff --git a/tools/pharos-dashboard/jenkins/migrations/__init__.py b/tools/pharos-dashboard/jenkins/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/pharos-dashboard/jenkins/models.py b/tools/pharos-dashboard/jenkins/models.py
new file mode 100644 (file)
index 0000000..f7d54c9
--- /dev/null
@@ -0,0 +1,34 @@
+from django.db import models
+
+
+class JenkinsSlave(models.Model):
+    id = models.AutoField(primary_key=True)
+    name = models.CharField(max_length=100, unique=True)
+    status = models.CharField(max_length=30, default='offline')
+    url = models.CharField(max_length=1024)
+    ci_slave = models.BooleanField(default=False)
+    dev_pod = models.BooleanField(default=False)
+
+    building = models.BooleanField(default=False)
+
+    last_job_name = models.CharField(max_length=1024, default='')
+    last_job_url = models.CharField(max_length=1024, default='')
+    last_job_scenario = models.CharField(max_length=50, default='')
+    last_job_branch = models.CharField(max_length=50, default='')
+    last_job_installer = models.CharField(max_length=50, default='')
+    last_job_result = models.CharField(max_length=30, default='')
+
+    class Meta:
+        db_table = 'jenkins_slave'
+
+
+class JenkinsStatistic(models.Model):
+    id = models.AutoField(primary_key=True)
+    slave = models.ForeignKey(JenkinsSlave, on_delete=models.CASCADE)
+    offline = models.BooleanField(default=False)
+    idle = models.BooleanField(default=False)
+    online = models.BooleanField(default=False)
+    timestamp = models.DateTimeField(auto_now_add=True)
+
+    class Meta:
+        db_table = 'jenkins_statistic'
diff --git a/tools/pharos-dashboard/jenkins/tasks.py b/tools/pharos-dashboard/jenkins/tasks.py
new file mode 100644 (file)
index 0000000..6998cf3
--- /dev/null
@@ -0,0 +1,39 @@
+from celery import shared_task
+
+from jenkins.models import JenkinsSlave, JenkinsStatistic
+from .adapter import *
+
+
+@shared_task
+def sync_jenkins():
+    update_jenkins_slaves()
+
+
+def update_jenkins_slaves():
+    jenkins_slaves = get_all_slaves()
+    for slave in jenkins_slaves:
+        jenkins_slave, created = JenkinsSlave.objects.get_or_create(name=slave['displayName'],
+                                                                    url=get_slave_url(slave))
+        jenkins_slave.ci_slave = is_ci_slave(slave['displayName'])
+        jenkins_slave.dev_pod = is_dev_pod(slave['displayName'])
+        jenkins_slave.status = get_slave_status(slave)
+
+        last_job = get_jenkins_job(jenkins_slave.name)
+        if last_job is not None:
+            last_job = parse_job(last_job)
+            jenkins_slave.last_job_name = last_job['name']
+            jenkins_slave.last_job_url = last_job['url']
+            jenkins_slave.last_job_scenario = last_job['scenario']
+            jenkins_slave.last_job_branch = last_job['branch']
+            jenkins_slave.last_job_installer = last_job['installer']
+            jenkins_slave.last_job_result = last_job['result']
+        jenkins_slave.save()
+
+        jenkins_statistic = JenkinsStatistic(slave=jenkins_slave)
+        if jenkins_slave.status == 'online' or jenkins_slave.status == 'building':
+            jenkins_statistic.online = True
+        if jenkins_slave.status == 'offline':
+            jenkins_statistic.offline = True
+        if jenkins_slave.status == 'online / idle':
+            jenkins_statistic.idle = True
+        jenkins_statistic.save()
index e69de29..b6fc817 100644 (file)
@@ -0,0 +1,3 @@
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from .celery import app as celery_app  # noqa
diff --git a/tools/pharos-dashboard/pharos_dashboard/celery.py b/tools/pharos-dashboard/pharos_dashboard/celery.py
new file mode 100644 (file)
index 0000000..4cf6a7a
--- /dev/null
@@ -0,0 +1,20 @@
+import os
+
+from celery import Celery
+
+# set the default Django settings module for the 'celery' program.
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pharos_dashboard.settings')
+
+from django.conf import settings  # noqa
+
+app = Celery('pharos_dashboard')
+
+# Using a string here means the worker will not have to
+# pickle the object when using Windows.
+app.config_from_object('django.conf:settings')
+app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
+
+
+@app.task(bind=True)
+def debug_task(self):
+    print('Request: {0!r}'.format(self.request))
\ No newline at end of file
index b6e9899..7717501 100644 (file)
@@ -15,7 +15,6 @@ import os
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
-
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
 
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
 
@@ -27,7 +26,6 @@ DEBUG = True
 
 ALLOWED_HOSTS = []
 
 
 ALLOWED_HOSTS = []
 
-
 # Application definition
 
 INSTALLED_APPS = [
 # Application definition
 
 INSTALLED_APPS = [
@@ -43,6 +41,8 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
     'django.contrib.humanize',
     'bootstrap3',
     'django.contrib.staticfiles',
     'django.contrib.humanize',
     'bootstrap3',
+    'djcelery',
+    'kombu.transport.django',
 ]
 
 MIDDLEWARE = [
 ]
 
 MIDDLEWARE = [
@@ -77,7 +77,6 @@ TEMPLATES = [
 
 WSGI_APPLICATION = 'pharos_dashboard.wsgi.application'
 
 
 WSGI_APPLICATION = 'pharos_dashboard.wsgi.application'
 
-
 # Database
 # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
 
 # Database
 # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
 
@@ -92,7 +91,6 @@ DATABASES = {
     }
 }
 
     }
 }
 
-
 # Password validation
 # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
 
 # Password validation
 # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
 
@@ -124,7 +122,6 @@ USE_L10N = True
 
 USE_TZ = True
 
 
 USE_TZ = True
 
-
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/1.10/howto/static-files/
 
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/1.10/howto/static-files/
 
@@ -139,3 +136,11 @@ BOOTSTRAP3 = {
 }
 
 LOGIN_REDIRECT_URL = '/'
 }
 
 LOGIN_REDIRECT_URL = '/'
+
+import djcelery
+
+djcelery.setup_loader()
+# django broker, NOT SAFE FOR PRODUCTION
+BROKER_URL = 'django://'
+CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
+
index d3e5ff6..2982a6f 100644 (file)
@@ -1,5 +1,6 @@
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
+{% load jenkins_filters %}
 
 {% block table %}
     <thead>
 
 {% block table %}
     <thead>
                 <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
             </th>
             <th>
                 <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
             </th>
             <th>
-                <a target='_blank' href={{ pod.slaveurl }}>{{ pod.slavename }}</a>
+                <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
             </th>
             </th>
-            <th style="background-color:{{ pod.status_color }}">
-                {{ pod.status }}
+            <th style="background-color:{{ pod.slave.status | jenkins_status_color }}">
+                {{ pod.slave.status }}
             </th>
             </th>
-            <th {{ pod.last_job.blink }}>
-                {{ pod.last_job.installer }}
+            <th {{ pod.slave.last_job_result | jenkins_job_blink }}>
+                {{ pod.slave.last_job_installer }}
             </th>
             </th>
-            <th {{ pod.last_job.blink }}>
-                {{ pod.last_job.scenario }}
+            <th {{ pod.slave.last_job_result | jenkins_job_blink }}>
+                {{ pod.slave.last_job_scenario }}
             </th>
             </th>
-            <th {{ pod.last_job.blink }}>
-                {{ pod.last_job.branch }}
+            <th {{ pod.slave.last_job_result | jenkins_job_blink }}>
+                {{ pod.slave.last_job_branch }}
             </th>
             </th>
-            <th><a {{ pod.last_job.blink }} style="color:{{ pod.last_job.color }}"
-                                            target='_blank'
-                                            href={{ pod.last_job.url }}>{{ pod.last_job.name }}</a>
+            <th><a {{ pod.slave.last_job_result | jenkins_job_blink }}
+                    style="color:{{ pod.slave.last_job_result | jenkins_job_color }}"
+                    target='_blank'
+                    href={{ pod.slave.last_job_url }}>{{ pod.slave.last_job_name }}</a>
             </th>
         </tr>
     {% endfor %}`
             </th>
         </tr>
     {% endfor %}`
index f08e1d1..532a3a1 100644 (file)
@@ -1,5 +1,6 @@
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
+{% load jenkins_filters %}
 
 {% block table %}
     <thead>
 
 {% block table %}
     <thead>
     </tr>
     </thead>
     <tbody>
     </tr>
     </thead>
     <tbody>
-    {% for resource in dev_pods %}
+    {% for pod, booking in dev_pods %}
         <tr>
             <th>
         <tr>
             <th>
-                <a target='_blank' href={{ resource.url }}>{{ resource.name }}</a>
+                <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
             </th>
             <th>
             </th>
             <th>
-                <a target='_blank' href={{ resource.slaveurl }}>{{ resource.slavename }}</a>
+                <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
             </th>
             <th>
             </th>
             <th>
-                {{ resource.current_booking.user.username }}
+                {{ booking.user.username }}
             </th>
             <th>
             </th>
             <th>
-                {{ resource.current_booking.end }}
+                {{ booking.end }}
             </th>
             <th>
             </th>
             <th>
-                {{ resource.current_booking.purpose }}
+                {{ booking.purpose }}
             </th>
             </th>
-            <th style="background-color:{{ resource.status_color }}">
-                {{ resource.status }}
+            <th style="background-color:{{ pod.slave.status | jenkins_status_color }}">
+                {{ pod.slave.status }}
             </th>
             <th>
             </th>
             <th>
-                <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn btn-primary">
+                <a href="{% url 'booking:create' resource_id=pod.id %}" class="btn btn-primary">
                     Book
                 </a>
             </th>
                     Book
                 </a>
             </th>
index 2d011b4..830ed19 100644 (file)
@@ -1,6 +1,8 @@
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
 
 {% extends "dashboard/table.html" %}
 {% load staticfiles %}
 
+{% load jenkins_filters %}
+
 {% block table %}
     <thead>
     <tr>
 {% block table %}
     <thead>
     <tr>
     {% for slave in slaves %}
         <tr>
             <th><a target='_blank'
     {% for slave in slaves %}
         <tr>
             <th><a target='_blank'
-                   href={{ slave.slaveurl }}>{{ slave.displayName }}</a>
+                   href={{ slave.url }}>{{ slave.name }}</a>
             </th>
             </th>
-            <th style="background-color:{{ slave.status_color }}">
+            <th style="background-color:{{ slave.status | jenkins_status_color }}">
                 {{ slave.status }}
             </th>
                 {{ slave.status }}
             </th>
-            <th><a {{ slave.last_job.blink }} style="color:{{ slave.last_job.color }}"
-                                              target="_blank" href={{ slave.last_job.url }}>
-                {{ slave.last_job.name }}</a>
+            <th><a {{ slave.last_job_result | jenkins_job_blink }}
+                    style="color:{{ slave.last_job_result | jenkins_job_color }}"
+                    target="_blank" href={{ slave.last_job_url }}>
+                {{ slave.last_job_name }}</a>
             </th>
         </tr>
     {% endfor %}
             </th>
         </tr>
     {% endfor %}