Updates for efficiency and consistency

This commit is contained in:
Josh VanDeraa 2021-03-13 00:53:36 +00:00
parent b88c4079bb
commit b6adfa1a17
10 changed files with 94 additions and 42 deletions

View File

@ -22,7 +22,7 @@ from utilities.choices import ColorChoices
from utilities.fields import ColorField, NaturalOrderingField
from utilities.querysets import RestrictedQuerySet
from utilities.mptt import TreeManager
from utilities.utils import array_to_string, serialize_object
from utilities.utils import array_to_string, serialize_object, UtilizationData
from .device_components import PowerOutlet, PowerPort
from .devices import Device
from .power import PowerFeed
@ -505,9 +505,10 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
return self.devices.filter(position=0)
def get_utilization(self):
"""
Determine the utilization rate of the rack and return it as a percentage. Occupied and reserved units both count
as utilized.
"""Gets utilization numerator and denominator for racks.
Returns:
UtilizationData: (numerator=Occupied Unit Count, denominator=U Height of the rack)
"""
# Determine unoccupied units
available_units = self.get_available_units()
@ -517,19 +518,19 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
if u in available_units:
available_units.remove(u)
occupied_unit_count = self.u_height - len(available_units)
# Return the numerator and denominator as percentage is to be calculated later where needed
return (occupied_unit_count, self.u_height)
return UtilizationData(numerator=self.u_height - len(available_units), denominator=self.u_height)
def get_power_utilization(self):
"""
Determine the utilization rate of power in the rack and return it as a percentage.
"""Determine the utilization numerator and denominator for power utilization on the rack.
Returns:
UtilizationData: (numerator, denominator)
"""
powerfeeds = PowerFeed.objects.filter(rack=self)
available_power_total = sum(pf.available_power for pf in powerfeeds)
if not available_power_total:
return (0, 0)
return UtilizationData(numerator=0, denominator=0)
pf_powerports = PowerPort.objects.filter(
_cable_peer_type=ContentType.objects.get_for_model(PowerFeed),
@ -541,7 +542,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
_cable_peer_id__in=poweroutlets.values_list('id', flat=True)
).aggregate(Sum('allocated_draw'))['allocated_draw__sum'] or 0
return (allocated_draw_total, available_power_total)
return UtilizationData(numerator=allocated_draw_total, denominator=available_power_total)
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')

View File

@ -82,9 +82,10 @@ RACKGROUP_ELEVATIONS = """
</a>
"""
# Value is a namedtuple that takes a numerator and denominator to pass in.
UTILIZATION_GRAPH = """
{% load helpers %}
{% utilization_graph value.0 value.1 %}
{% utilization_graph value %}
"""
#

View File

@ -14,7 +14,7 @@ from dcim.models import Device, Interface
from extras.models import ChangeLoggedModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.querysets import RestrictedQuerySet
from utilities.utils import array_to_string, serialize_object
from utilities.utils import array_to_string, serialize_object, UtilizationData
from virtualization.models import VirtualMachine, VMInterface
from .choices import *
from .constants import *
@ -308,13 +308,26 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
return self.prefix.version
return None
def get_utilization(self):
def get_percent_utilized(self):
"""Gets the percentage utilized from the get_utilization method.
Returns
float: Percentage utilization
"""
Determine the prefix utilization of the aggregate and return it as a percentage.
utilization = self.get_utilization()
return int(utilization.numerator / float(utilization.denominator) * 100)
def get_utilization(self):
"""Gets the numerator and denominator for calculating utilization of an Aggregrate.
Returns:
UtilizationData: Aggregate utilization (numerator=size of child prefixes, denominator=prefix size)
"""
queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(self.prefix))
child_prefixes = netaddr.IPSet([p.prefix for p in queryset])
return (child_prefixes.size, self.prefix.size)
return UtilizationData(numerator=child_prefixes.size, denominator=self.prefix.size)
class Role(ChangeLoggedModel):
@ -595,24 +608,25 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
return '{}/{}'.format(next(available_ips.__iter__()), self.prefix.prefixlen)
def get_utilization(self):
"""Get the child prefix size and parent size.
For Prefixes with a status of "container", get the number child prefixes. For all others, count child IP addresses.
Returns:
UtilizationData (namedtuple): (numerator, denominator)
"""
Get the child prefix size and parent prefix size return them as a tuple. For Prefixes with a status of
"container", get the number child prefixes. For all others, count child IP addresses.
"""
if self.status == PrefixStatusChoices.STATUS_CONTAINER:
queryset = Prefix.objects.filter(
prefix__net_contained=str(self.prefix),
vrf=self.vrf
)
if self.status == Prefix.STATUS_CONTAINER:
queryset = Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf)
child_prefixes = netaddr.IPSet([p.prefix for p in queryset])
return (child_prefixes.size, self.prefix.size)
return UtilizationData(numerator=child_prefixes.size, denominator=self.prefix.size)
else:
# Compile an IPSet to avoid counting duplicate IPs
child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size
prefix_size = self.prefix.size
if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
prefix_size -= 2
return (child_count, prefix_size)
return UtilizationData(numerator=child_count, denominator=prefix_size)
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')

View File

@ -225,7 +225,7 @@
<td>{{ utilization.allocated }}VA</td>
{% if powerfeed.available_power %}
<td>{{ powerfeed.available_power }}VA</td>
<td>{% utilization_graph utilization.allocated powerfeed.available_power %}</td>
<td>{% utilization_graph_raw_data utilization.allocated powerfeed.available_power %}</td>
{% else %}
<td class="text-muted">&mdash;</td>
<td class="text-muted">&mdash;</td>
@ -238,7 +238,7 @@
<td>{{ leg.allocated }}</td>
<td>{{ powerfeed.available_power|divide:3 }}VA</td>
{% with phase_available=powerfeed.available_power|divide:3 %}
<td>{% utilization_graph leg.allocated phase_available %}</td>
<td>{% utilization_graph_raw_data leg.allocated phase_available %}</td>
{% endwith %}
</tr>
{% endfor %}

View File

@ -113,7 +113,7 @@
<td>
{{ utilization.allocated }}VA / {{ object.available_power }}VA
{% if object.available_power > 0 %}
{% utilization_graph utilization.allocated object.available_power %}
{% utilization_graph_raw_data utilization.allocated object.available_power %}
{% endif %}
</td>
{% else %}

View File

@ -242,7 +242,7 @@
</td>
{% with power_port=powerfeed.connected_endpoint %}
{% if power_port %}
<td>{% utilization_graph power_port.get_power_draw.allocated powerfeed.available_power %}</td>
<td>{% utilization_graph power_port.get_power_draw %}</td>
{% else %}
<td class="text-muted">N/A</td>
{% endif %}

View File

@ -77,7 +77,7 @@
<tr>
<td>Utilization</td>
<td>
{{ object.get_utilization }}%
{{ object.get_percent_utilized }}%
</td>
</tr>
<tr>

View File

@ -1,4 +1,4 @@
<div {% if utilization_count and total_count %}title="Used: {{ utilization_count }}&#013Total Count: {{ total_count }}"
<div {% if utilization_count and total_count %}title="Used: {{ utilization_count }}&#13Total Count: {{ total_count }}"
{% endif %}class="progress text-center">
{% if utilization < 30 %}<span style="font-size: 12px;">{{ utilization }}%</span>{% endif %}
<div class="progress-bar progress-bar-{% if utilization >= danger_threshold %}danger{% elif utilization >= warning_threshold %}warning{% else %}success{% endif %}"

View File

@ -243,22 +243,55 @@ def querystring(request, **kwargs):
@register.inclusion_tag('utilities/templatetags/utilization_graph.html')
def utilization_graph(used_count, total, warning_threshold=75, danger_threshold=90):
def utilization_graph(utilization_data, warning_threshold=75, danger_threshold=90):
"""Wrapper for a horizontal bar graph indicating a percentage of utilization from a tuple of data.
Takes the utilization_data that is a namedtuple with numerator and denominator field names and passes them into
the utilization_graph_raw_data to handle the generation graph data.
Args:
utilization_data (UtilizationData): Namedtuple with numerator and denominator keys
warning_threshold (int, optional): Warning Threshold Value. Defaults to 75.
danger_threshold (int, optional): Danger Threshold Value. Defaults to 90.
Returns:
dict: Dictionary with utilization, warning threshold, danger threshold, utilization count, and total count for
display
"""
Display a horizontal bar graph indicating a percentage of utilization.
return utilization_graph_raw_data(
numerator=utilization_data.numerator,
denominator=utilization_data.denominator,
warning_threshold=warning_threshold,
danger_threshold=danger_threshold,
)
@register.inclusion_tag("utilities/templatetags/utilization_graph_raw_data.html")
def utilization_graph_raw_data(numerator, denominator, warning_threshold=75, danger_threshold=90):
"""Display a horizontal bar graph indicating a percentage of utilization.
Args:
numerator (int): Numerator for creating a percentage
denominator (int): Denominator for creating a percentage
warning_threshold (int, optional): Warning Threshold Value. Defaults to 75.
danger_threshold (int, optional): Danger Threshold Value. Defaults to 90.
Returns:
dict: Dictionary with utilization, warning threshold, danger threshold, utilization count, and total count for
display
"""
# Check for possible division by zero error
if total == 0:
if denominator == 0:
utilization = 0
else:
utilization = int(float(used_count) / total * 100)
utilization = int(float(numerator) / denominator * 100)
return {
'utilization': utilization,
'warning_threshold': warning_threshold,
'danger_threshold': danger_threshold,
'utilization_count': used_count,
'total_count': total,
"utilization": utilization,
"warning_threshold": warning_threshold,
"danger_threshold": danger_threshold,
"utilization_count": numerator,
"total_count": denominator,
}

View File

@ -1,6 +1,6 @@
import datetime
import json
from collections import OrderedDict
from collections import OrderedDict, namedtuple
from itertools import count, groupby
from django.core.serializers import serialize
@ -326,3 +326,6 @@ def copy_safe_request(request):
'path': request.path,
'id': getattr(request, 'id', None), # UUID assigned by middleware
})
# Setup UtilizationData named tuple for use by multiple methods
UtilizationData = namedtuple("UtilizationData", ["numerator", "denominator"])