Add a Resource detail view 67/19567/1
authormaxbr <maxbr@mi.fu-berlin.de>
Thu, 25 Aug 2016 10:10:55 +0000 (12:10 +0200)
committermaxbr <maxbr@mi.fu-berlin.de>
Thu, 25 Aug 2016 10:10:55 +0000 (12:10 +0200)
JIRA: RELENG-12

The resource page contains an utilization diagram, future bookings with
their jira tickets and a list of servers.

Change-Id: I2123ccbe96cde29a56af32b933ebbf6ba2668ed1
Signed-off-by: maxbr <maxbr@mi.fu-berlin.de>
14 files changed:
tools/pharos-dashboard/dashboard/admin.py
tools/pharos-dashboard/dashboard/models.py
tools/pharos-dashboard/dashboard/templatetags/jenkins_filters.py
tools/pharos-dashboard/dashboard/templatetags/jira_filters.py [new file with mode: 0644]
tools/pharos-dashboard/dashboard/urls.py
tools/pharos-dashboard/dashboard/views.py
tools/pharos-dashboard/jenkins/models.py
tools/pharos-dashboard/templates/booking/booking_table.html [new file with mode: 0644]
tools/pharos-dashboard/templates/dashboard/dev_pods.html
tools/pharos-dashboard/templates/dashboard/lab_owner.html [deleted file]
tools/pharos-dashboard/templates/dashboard/resource.html [new file with mode: 0644]
tools/pharos-dashboard/templates/dashboard/resource_all.html [moved from tools/pharos-dashboard/templates/dashboard/resource_utilization.html with 50% similarity]
tools/pharos-dashboard/templates/dashboard/resource_detail.html [new file with mode: 0644]
tools/pharos-dashboard/templates/dashboard/server_table.html [new file with mode: 0644]

index 990e63e..71a1e7d 100644 (file)
@@ -1,5 +1,6 @@
 from django.contrib import admin
 
-from dashboard.models import Resource
+from dashboard.models import *
 
 admin.site.register(Resource)
+admin.site.register(Server)
index 971af6a..d645cd5 100644 (file)
@@ -1,6 +1,5 @@
 from django.contrib.auth.models import User
 from django.db import models
-from django.utils import timezone
 
 from jenkins.models import JenkinsSlave
 
@@ -17,4 +16,20 @@ class Resource(models.Model):
         db_table = 'resource'
 
     def __str__(self):
-        return self.name
\ No newline at end of file
+        return self.name
+
+
+class Server(models.Model):
+    id = models.AutoField(primary_key=True)
+    resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
+    name = models.CharField(max_length=100, blank=True)
+    model = models.CharField(max_length=100, blank=True)
+    cpu = models.CharField(max_length=100, blank=True)
+    ram = models.CharField(max_length=100, blank=True)
+    storage = models.CharField(max_length=100, blank=True)
+
+    class Meta:
+        db_table = 'server'
+
+    def __str__(self):
+        return self.name
index f7e00a8..f5038ea 100644 (file)
@@ -9,7 +9,7 @@ def jenkins_job_color(job_result):
         return '#d9534f'
     if job_result == 'UNSTABLE':
         return '#EDD62B'
-    return '#646F73' # job is still building
+    return '#646F73'  # job is still building
 
 
 @register.filter
@@ -21,7 +21,8 @@ def jenkins_status_color(slave_status):
     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
+    if job_result == '':  # job is still building
+        return 'class=blink_me'
diff --git a/tools/pharos-dashboard/dashboard/templatetags/jira_filters.py b/tools/pharos-dashboard/dashboard/templatetags/jira_filters.py
new file mode 100644 (file)
index 0000000..d9c2761
--- /dev/null
@@ -0,0 +1,8 @@
+from django.template.defaultfilters import register
+
+from pharos_dashboard import settings
+
+
+@register.filter
+def jira_issue_url(issue):
+    return settings.JIRA_URL + '/browse/' + str(issue)
index 51d764c..809204c 100644 (file)
@@ -21,8 +21,8 @@ urlpatterns = [
     url(r'^ci_pods/$', CIPodsView.as_view(), name='ci_pods'),
     url(r'^dev_pods/$', DevelopmentPodsView.as_view(), name='dev_pods'),
     url(r'^jenkins_slaves/$', JenkinsSlavesView.as_view(), name='jenkins_slaves'),
-    url(r'^resource/all/', LabOwnerView.as_view(),
-        name='resources'),
+    url(r'^resource/all/$', LabOwnerView.as_view(), name='resources'),
+    url(r'^resource/(?P<resource_id>[0-9]+)/$', ResourceView.as_view(), name='resource'),
 
     url(r'^$', DevelopmentPodsView.as_view(), name="index"),
 ]
index 56b3a51..ef1845c 100644 (file)
@@ -1,12 +1,12 @@
 from datetime import timedelta
 
-from django.contrib.auth.models import User
+from django.shortcuts import get_object_or_404
 from django.utils import timezone
 from django.views.generic import TemplateView
 
 from booking.models import Booking
 from dashboard.models import Resource
-from jenkins.models import JenkinsSlave, JenkinsStatistic
+from jenkins.models import JenkinsSlave
 
 
 class JenkinsSlavesView(TemplateView):
@@ -51,25 +51,27 @@ class DevelopmentPodsView(TemplateView):
         return context
 
 
+class ResourceView(TemplateView):
+    template_name = "dashboard/resource.html"
+
+    def get_context_data(self, **kwargs):
+        resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
+        utilization = resource.slave.get_utilization(timedelta(days=7))
+        bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
+        context = super(ResourceView, self).get_context_data(**kwargs)
+        context.update({'title': str(resource), 'resource': resource, 'utilization': utilization, 'bookings': bookings})
+        return context
+
+
 class LabOwnerView(TemplateView):
-    template_name = "dashboard/lab_owner.html"
+    template_name = "dashboard/resource_all.html"
 
     def get_context_data(self, **kwargs):
         resources = Resource.objects.filter(slave__dev_pod=True)
         pods = []
         for resource in resources:
-            utilization = {'idle': 0, 'online': 0, 'offline': 0}
-            # query measurement points for the last week
-            statistics = JenkinsStatistic.objects.filter(slave=resource.slave,
-                                                         timestamp__gte=timezone.now() - timedelta(
-                                                             days=7))
-
-            utilization['idle'] = statistics.filter(idle=True).count()
-            utilization['online'] = statistics.filter(online=True).count()
-            utilization['offline'] = statistics.filter(offline=True).count()
-
+            utilization = resource.slave.get_utilization(timedelta(days=7))
             bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
-
             pods.append((resource, utilization, bookings))
         context = super(LabOwnerView, self).get_context_data(**kwargs)
         context.update({'title': "Overview", 'pods': pods})
index 438a882..354887a 100644 (file)
@@ -1,4 +1,5 @@
 from django.db import models
+from django.utils import timezone
 
 
 class JenkinsSlave(models.Model):
@@ -18,6 +19,18 @@ class JenkinsSlave(models.Model):
     last_job_installer = models.CharField(max_length=50, default='')
     last_job_result = models.CharField(max_length=30, default='')
 
+    def get_utilization(self, timedelta):
+        """
+        Return a dictionary containing the count of idle, online and offline measurements in the time from
+        now-timedelta to now
+        """
+        utilization = {'idle': 0, 'online': 0, 'offline': 0}
+        statistics = self.jenkinsstatistic_set.filter(timestamp__gte=timezone.now() - timedelta)
+        utilization['idle'] = statistics.filter(idle=True).count()
+        utilization['online'] = statistics.filter(online=True).count()
+        utilization['offline'] = statistics.filter(offline=True).count()
+        return utilization
+
     class Meta:
         db_table = 'jenkins_slave'
 
diff --git a/tools/pharos-dashboard/templates/booking/booking_table.html b/tools/pharos-dashboard/templates/booking/booking_table.html
new file mode 100644 (file)
index 0000000..3d0b757
--- /dev/null
@@ -0,0 +1,33 @@
+{% load jira_filters %}
+
+
+<thead>
+<tr>
+    <th>User</th>
+    <th>Purpose</th>
+    <th>Start</th>
+    <th>End</th>
+    <th>Jira</th>
+</tr>
+</thead>
+<tbody>
+{% for booking in bookings %}
+    <tr>
+        <th>
+            {{ booking.user.username }}
+        </th>
+        <th>
+            {{ booking.purpose }}
+        </th>
+        <th>
+            {{ booking.start }}
+        </th>
+        <th>
+            {{ booking.end }}
+        </th>
+        <th><a target='_blank'
+               href={{ booking.get_jira_issue | jira_issue_url }}>{{ booking.get_jira_issue }}</a>
+        </th>
+    </tr>
+{% endfor %}
+</tbody>
\ No newline at end of file
index 532a3a1..9c84bb9 100644 (file)
@@ -18,7 +18,7 @@
     {% for pod, booking in dev_pods %}
         <tr>
             <th>
-                <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
+                <a href={% url 'dashboard:resource' resource_id=pod.id %}>{{ pod.name }}</a>
             </th>
             <th>
                 <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
diff --git a/tools/pharos-dashboard/templates/dashboard/lab_owner.html b/tools/pharos-dashboard/templates/dashboard/lab_owner.html
deleted file mode 100644 (file)
index a4f428c..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
-    <!-- Morris Charts CSS -->
-    <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
-
-    <!-- DataTables CSS -->
-    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
-          rel="stylesheet">
-
-    <!-- DataTables Responsive CSS -->
-    <link href="{% static "bower_components/datatables-responsive/css/dataTables.responsive.css" %}"
-          rel="stylesheet">
-{% endblock extrahead %}
-
-
-{% block content %}
-    {% for resource, utilization, bookings in pods %}
-        <div class="row">
-            <div class="col-lg-3">
-                <div class="panel panel-default">
-                    <div class="panel-heading">
-                        {{ resource.name }}
-                    </div>
-                    <div class="panel-body">
-                        <div class="flot-chart">
-                            <div class="flot-chart-content" id="{{ resource.slave.name }}"></div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="col-lg-6">
-                <div class="panel panel-default">
-                    <div class="panel-heading">
-                        {{ resource.name }} Bookings
-                    </div>
-                    <div class="panel-body">
-                        <div class="dataTables_wrapper">
-                            <table class="table table-striped table-bordered table-hover"
-                                   id="{{ resource.slave.name }}_bookings" cellspacing="0"
-                                   width="100%">
-                                <thead>
-                                <tr>
-                                    <th>User</th>
-                                    <th>Purpose</th>
-                                    <th>Start</th>
-                                    <th>End</th>
-                                    <th>Status</th>
-                                </tr>
-                                </thead>
-                                <tbody>
-                                {% for booking in bookings %}
-                                    <tr>
-                                        <th>
-                                            {{ booking.user.username }}
-                                        </th>
-                                        <th>
-                                            {{ booking.purpose }}
-                                        </th>
-                                        <th>
-                                            {{ booking.start }}
-                                        </th>
-                                        <th>
-                                            {{ booking.end }}
-                                        </th>
-                                        <th>
-                                            Jira Status
-                                        </th>
-                                    </tr>
-                                {% endfor %}`
-                                </tbody>
-                            </table>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-    {% endfor %}
-
-{% endblock content %}
-
-
-{% block extrajs %}
-    <!-- DataTables JavaScript -->
-    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
-          rel="stylesheet">
-
-
-    <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
-    <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
-
-
-
-    <!-- Flot Charts JavaScript -->
-    <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script>
-    <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script>
-    <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script>
-    <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script>
-    <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script>
-    <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script>
-
-    <script type="text/javascript">
-        $(document).ready(function () {
-
-
-            {% for resource, utilization, bookings in pods %}
-                $('#{{ resource.slave.name }}_bookings').DataTable({});
-
-                $(function () {
-                    var data = [{
-                        label: "Offline",
-                        data: {{ utilization.offline }},
-                        color: '#d9534f'
-                    }, {
-                        label: "Online",
-                        data: {{ utilization.online }},
-                        color: '#5cb85c'
-                    }, {
-                        label: "Idle",
-                        data: {{ utilization.idle }},
-                        color: '#5bc0de'
-                    }];
-
-                    var plotObj = $.plot($("#{{ resource.slave.name }}"), data, {
-                        series: {
-                            pie: {
-                                show: true
-                            }
-                        },
-                        grid: {
-                            hoverable: false
-                        },
-                        tooltip: true,
-                        tooltipOpts: {
-                            content: "%p.0%, %s", // show percentages, rounding to 2 decimal places
-                            shifts: {
-                                x: 20,
-                                y: 0
-                            },
-                            defaultTheme: false
-                        }
-                    });
-
-                });
-            {% endfor %}
-
-        });
-    </script>
-
-{% endblock extrajs %}
\ No newline at end of file
diff --git a/tools/pharos-dashboard/templates/dashboard/resource.html b/tools/pharos-dashboard/templates/dashboard/resource.html
new file mode 100644 (file)
index 0000000..92d02f6
--- /dev/null
@@ -0,0 +1,58 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+    <!-- Morris Charts CSS -->
+    <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
+
+    <!-- DataTables CSS -->
+    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+          rel="stylesheet">
+
+    <!-- DataTables Responsive CSS -->
+    <link href="{% static "bower_components/datatables-responsive/css/dataTables.responsive.css" %}"
+          rel="stylesheet">
+{% endblock extrahead %}
+
+
+{% block content %}
+    {% include "dashboard/resource_detail.html" %}
+{% endblock content %}
+
+
+{% block extrajs %}
+    <!-- DataTables JavaScript -->
+    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+          rel="stylesheet">
+
+    <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
+    <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+
+
+
+    <!-- Flot Charts JavaScript -->
+    <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script>
+    <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script>
+    <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script>
+    <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script>
+    <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script>
+    <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script>
+
+    <script type="text/javascript">
+        $(document).ready(function () {
+            $('#{{ resource.id }}_server_table').DataTable({});
+            $('#{{ resource.id }}_bookings_table').DataTable({});
+
+            $(function () {
+                var plotObj = $.plot($("#{{ resource.id }}_slave_utilization"), data_{{ resource.id }}, {
+                    series: {
+                        pie: {
+                            show: true
+                        }
+                    }
+                });
+
+            });
+        });
+    </script>
+{% endblock extrajs %}
\ No newline at end of file
@@ -5,31 +5,43 @@
     <!-- Morris Charts CSS -->
     <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
 
+    <!-- DataTables CSS -->
+    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+          rel="stylesheet">
+
+    <!-- DataTables Responsive CSS -->
+    <link href="{% static "bower_components/datatables-responsive/css/dataTables.responsive.css" %}"
+          rel="stylesheet">
 {% endblock extrahead %}
 
 
 {% block content %}
-    <div class="row">
-        {% for resource, utilization in pods %}
-            <div class="col-lg-3">
+    {% for resource, utilization, bookings in pods %}
+        <div class="row">
+            <div class="col-lg-12">
                 <div class="panel panel-default">
                     <div class="panel-heading">
                         {{ resource.name }}
                     </div>
                     <div class="panel-body">
-                        <div class="flot-chart">
-                            <div class="flot-chart-content" id="{{ resource.slave.name }}"></div>
-                        </div>
+                        {% include "dashboard/resource_detail.html" %}
                     </div>
                 </div>
             </div>
-        {% endfor %}
-    </div>
-
+        </div>
+    {% endfor %}
 {% endblock content %}
 
 
 {% block extrajs %}
+    <!-- DataTables JavaScript -->
+    <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+          rel="stylesheet">
+
+    <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
+    <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+
+
 
     <!-- Flot Charts JavaScript -->
     <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script>
 
     <script type="text/javascript">
         $(document).ready(function () {
-            {% for resource, utilization in pods %}
-                $(function () {
-                    var data = [{
-                        label: "Offline",
-                        data: {{ utilization.offline }},
-                        color: '#d9534f'
-                    }, {
-                        label: "Online",
-                        data: {{ utilization.online }},
-                        color: '#5cb85c'
-                    }, {
-                        label: "Idle",
-                        data: {{ utilization.idle }},
-                        color: '#5bc0de'
-                    }];
+            {% for resource, utilization, bookings in pods %}
+
+                $('#{{ resource.id }}_server_table').DataTable({});
+                $('#{{ resource.id }}_bookings_table').DataTable({});
 
-                    var plotObj = $.plot($("#{{ resource.slave.name }}"), data, {
+                $(function () {
+                    var plotObj = $.plot($("#{{ resource.id }}_slave_utilization"), data_{{ resource.id }}, {
                         series: {
                             pie: {
                                 show: true
                             }
-                        },
-                        grid: {
-                            hoverable: true
-                        },
-                        tooltip: true,
-                        tooltipOpts: {
-                            content: "%p.0%, %s", // show percentages, rounding to 2 decimal places
-                            shifts: {
-                                x: 20,
-                                y: 0
-                            },
-                            defaultTheme: false
                         }
                     });
 
                 });
             {% endfor %}
-
         });
     </script>
-
 {% endblock extrajs %}
\ No newline at end of file
diff --git a/tools/pharos-dashboard/templates/dashboard/resource_detail.html b/tools/pharos-dashboard/templates/dashboard/resource_detail.html
new file mode 100644 (file)
index 0000000..4fba476
--- /dev/null
@@ -0,0 +1,64 @@
+<div class="row">
+    <div class="col-lg-3">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                Utilization
+            </div>
+            <div class="panel-body">
+                <div class="flot-chart">
+                    <div class="flot-chart-content" id="{{ resource.id }}_slave_utilization"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="col-lg-9">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                Servers
+            </div>
+            <div class="panel-body">
+                <div class="dataTables_wrapper">
+                    <table class="table table-striped table-bordered table-hover"
+                           id="{{ resource.id }}_server_table" cellspacing="0"
+                           width="100%">
+                        {% include "dashboard/server_table.html" %}
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="row">
+    <div class="col-lg-6">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                Bookings
+            </div>
+            <div class="panel-body">
+                <div class="dataTables_wrapper">
+                    <table class="table table-striped table-bordered table-hover"
+                           id="{{ resource.id }}_bookings_table" cellspacing="0"
+                           width="100%">
+                        {% include "booking/booking_table.html" %}
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript">
+    var data_{{ resource.id }} = [{
+        label: "Offline",
+        data: {{ utilization.offline }},
+        color: '#d9534f'
+    }, {
+        label: "Online",
+        data: {{ utilization.online }},
+        color: '#5cb85c'
+    }, {
+        label: "Idle",
+        data: {{ utilization.idle }},
+        color: '#5bc0de'
+    }];
+</script>
\ No newline at end of file
diff --git a/tools/pharos-dashboard/templates/dashboard/server_table.html b/tools/pharos-dashboard/templates/dashboard/server_table.html
new file mode 100644 (file)
index 0000000..d47e520
--- /dev/null
@@ -0,0 +1,30 @@
+<thead>
+<tr>
+    <th>Server</th>
+    <th>Model</th>
+    <th>CPU</th>
+    <th>RAM</th>
+    <th>Storage</th>
+</tr>
+</thead>
+<tbody>
+{% for server in resource.server_set.all %}
+    <tr>
+        <th>
+            {{ server.name }}
+        </th>
+        <th>
+            {{ server.model }}
+        </th>
+        <th>
+            {{ server.cpu }}
+        </th>
+        <th>
+            {{ server.ram }}
+        </th>
+        <th>
+            {{ server.storage }}
+        </th>
+    </tr>
+{% endfor %}`
+</tbody>
\ No newline at end of file