diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py
index 64028dab3..95e98df2f 100644
--- a/netbox/dcim/models.py
+++ b/netbox/dcim/models.py
@@ -360,6 +360,15 @@ class Rack(CreatedUpdatedModel):
def get_0u_devices(self):
return self.devices.filter(position=0)
+ def get_utilization(self):
+ """
+ Determine the utilization rate of the rack and return it as a percentage.
+ """
+ if self.u_consumed is None:
+ self.u_consumed = 0
+ u_available = self.u_height - self.u_consumed
+ return int(float(self.u_height - u_available) / self.u_height * 100)
+
#
# Device Types
diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py
index dc66c3ab1..0fe2c6834 100644
--- a/netbox/dcim/tables.py
+++ b/netbox/dcim/tables.py
@@ -48,6 +48,18 @@ STATUS_ICON = """
{% endif %}
"""
+UTILIZATION_GRAPH = """
+{% with record.get_utilization as percentage %}
+
+ {% if percentage < 15 %}
{{ percentage }}%{% endif %}
+
+ {% if percentage >= 15 %}{{ percentage }}%{% endif %}
+
+
+{% endwith %}
+"""
+
#
# Sites
@@ -97,6 +109,8 @@ class RackTable(BaseTable):
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
facility_id = tables.Column(verbose_name='Facility ID')
u_height = tables.Column(verbose_name='Height (U)')
+ u_consumed = tables.Column(accessor=Accessor('u_consumed'), verbose_name='Used (U)')
+ utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
devices = tables.Column(accessor=Accessor('device_count'), verbose_name='Devices')
class Meta(BaseTable.Meta):
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index d59d5496b..5f8434bcc 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -7,7 +7,7 @@ from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
-from django.db.models import Count, ProtectedError
+from django.db.models import Count, ProtectedError, Sum
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
@@ -144,7 +144,7 @@ class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
#
class RackListView(ObjectListView):
- queryset = Rack.objects.select_related('site', 'group').annotate(device_count=Count('devices', distinct=True))
+ queryset = Rack.objects.select_related('site').prefetch_related('devices__device_type').annotate(device_count=Count('devices', distinct=True), u_consumed=Sum('devices__device_type__u_height'))
filter = filters.RackFilter
filter_form = forms.RackFilterForm
table = tables.RackTable