mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-26 18:38:38 -06:00
Updates for efficiency and consistency
This commit is contained in:
parent
b88c4079bb
commit
b6adfa1a17
@ -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')
|
||||
|
@ -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 %}
|
||||
"""
|
||||
|
||||
#
|
||||
|
@ -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')
|
||||
|
@ -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">—</td>
|
||||
<td class="text-muted">—</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 %}
|
||||
|
@ -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 %}
|
||||
|
@ -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 %}
|
||||
|
@ -77,7 +77,7 @@
|
||||
<tr>
|
||||
<td>Utilization</td>
|
||||
<td>
|
||||
{{ object.get_utilization }}%
|
||||
{{ object.get_percent_utilized }}%
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div {% if utilization_count and total_count %}title="Used: {{ utilization_count }}
Total Count: {{ total_count }}"
|
||||
<div {% if utilization_count and total_count %}title="Used: {{ utilization_count }}
Total 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 %}"
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -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"])
|
Loading…
Reference in New Issue
Block a user