mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 02:48: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.fields import ColorField, NaturalOrderingField
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from utilities.mptt import TreeManager
|
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 .device_components import PowerOutlet, PowerPort
|
||||||
from .devices import Device
|
from .devices import Device
|
||||||
from .power import PowerFeed
|
from .power import PowerFeed
|
||||||
@ -505,9 +505,10 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
return self.devices.filter(position=0)
|
return self.devices.filter(position=0)
|
||||||
|
|
||||||
def get_utilization(self):
|
def get_utilization(self):
|
||||||
"""
|
"""Gets utilization numerator and denominator for racks.
|
||||||
Determine the utilization rate of the rack and return it as a percentage. Occupied and reserved units both count
|
|
||||||
as utilized.
|
Returns:
|
||||||
|
UtilizationData: (numerator=Occupied Unit Count, denominator=U Height of the rack)
|
||||||
"""
|
"""
|
||||||
# Determine unoccupied units
|
# Determine unoccupied units
|
||||||
available_units = self.get_available_units()
|
available_units = self.get_available_units()
|
||||||
@ -517,19 +518,19 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
if u in available_units:
|
if u in available_units:
|
||||||
available_units.remove(u)
|
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 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):
|
def get_power_utilization(self):
|
||||||
"""
|
"""Determine the utilization numerator and denominator for power utilization on the rack.
|
||||||
Determine the utilization rate of power in the rack and return it as a percentage.
|
|
||||||
|
Returns:
|
||||||
|
UtilizationData: (numerator, denominator)
|
||||||
"""
|
"""
|
||||||
powerfeeds = PowerFeed.objects.filter(rack=self)
|
powerfeeds = PowerFeed.objects.filter(rack=self)
|
||||||
available_power_total = sum(pf.available_power for pf in powerfeeds)
|
available_power_total = sum(pf.available_power for pf in powerfeeds)
|
||||||
if not available_power_total:
|
if not available_power_total:
|
||||||
return (0, 0)
|
return UtilizationData(numerator=0, denominator=0)
|
||||||
|
|
||||||
pf_powerports = PowerPort.objects.filter(
|
pf_powerports = PowerPort.objects.filter(
|
||||||
_cable_peer_type=ContentType.objects.get_for_model(PowerFeed),
|
_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)
|
_cable_peer_id__in=poweroutlets.values_list('id', flat=True)
|
||||||
).aggregate(Sum('allocated_draw'))['allocated_draw__sum'] or 0
|
).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')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
|
||||||
|
@ -82,9 +82,10 @@ RACKGROUP_ELEVATIONS = """
|
|||||||
</a>
|
</a>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Value is a namedtuple that takes a numerator and denominator to pass in.
|
||||||
UTILIZATION_GRAPH = """
|
UTILIZATION_GRAPH = """
|
||||||
{% load helpers %}
|
{% 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.models import ChangeLoggedModel, CustomFieldModel, ObjectChange, TaggedItem
|
||||||
from extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
from utilities.querysets import RestrictedQuerySet
|
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 virtualization.models import VirtualMachine, VMInterface
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
@ -308,13 +308,26 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
|
|||||||
return self.prefix.version
|
return self.prefix.version
|
||||||
return None
|
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))
|
queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(self.prefix))
|
||||||
child_prefixes = netaddr.IPSet([p.prefix for p in queryset])
|
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):
|
class Role(ChangeLoggedModel):
|
||||||
@ -595,24 +608,25 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
return '{}/{}'.format(next(available_ips.__iter__()), self.prefix.prefixlen)
|
return '{}/{}'.format(next(available_ips.__iter__()), self.prefix.prefixlen)
|
||||||
|
|
||||||
def get_utilization(self):
|
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
|
if self.status == Prefix.STATUS_CONTAINER:
|
||||||
"container", get the number child prefixes. For all others, count child IP addresses.
|
queryset = Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf)
|
||||||
"""
|
|
||||||
if self.status == PrefixStatusChoices.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])
|
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:
|
else:
|
||||||
# Compile an IPSet to avoid counting duplicate IPs
|
# Compile an IPSet to avoid counting duplicate IPs
|
||||||
child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size
|
child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size
|
||||||
prefix_size = self.prefix.size
|
prefix_size = self.prefix.size
|
||||||
if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
|
if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
|
||||||
prefix_size -= 2
|
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')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
|
||||||
|
@ -225,7 +225,7 @@
|
|||||||
<td>{{ utilization.allocated }}VA</td>
|
<td>{{ utilization.allocated }}VA</td>
|
||||||
{% if powerfeed.available_power %}
|
{% if powerfeed.available_power %}
|
||||||
<td>{{ powerfeed.available_power }}VA</td>
|
<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 %}
|
{% else %}
|
||||||
<td class="text-muted">—</td>
|
<td class="text-muted">—</td>
|
||||||
<td class="text-muted">—</td>
|
<td class="text-muted">—</td>
|
||||||
@ -238,7 +238,7 @@
|
|||||||
<td>{{ leg.allocated }}</td>
|
<td>{{ leg.allocated }}</td>
|
||||||
<td>{{ powerfeed.available_power|divide:3 }}VA</td>
|
<td>{{ powerfeed.available_power|divide:3 }}VA</td>
|
||||||
{% with phase_available=powerfeed.available_power|divide:3 %}
|
{% 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 %}
|
{% endwith %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
<td>
|
<td>
|
||||||
{{ utilization.allocated }}VA / {{ object.available_power }}VA
|
{{ utilization.allocated }}VA / {{ object.available_power }}VA
|
||||||
{% if object.available_power > 0 %}
|
{% if object.available_power > 0 %}
|
||||||
{% utilization_graph utilization.allocated object.available_power %}
|
{% utilization_graph_raw_data utilization.allocated object.available_power %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -242,7 +242,7 @@
|
|||||||
</td>
|
</td>
|
||||||
{% with power_port=powerfeed.connected_endpoint %}
|
{% with power_port=powerfeed.connected_endpoint %}
|
||||||
{% if power_port %}
|
{% 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 %}
|
{% else %}
|
||||||
<td class="text-muted">N/A</td>
|
<td class="text-muted">N/A</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Utilization</td>
|
<td>Utilization</td>
|
||||||
<td>
|
<td>
|
||||||
{{ object.get_utilization }}%
|
{{ object.get_percent_utilized }}%
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<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">
|
{% endif %}class="progress text-center">
|
||||||
{% if utilization < 30 %}<span style="font-size: 12px;">{{ utilization }}%</span>{% endif %}
|
{% 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 %}"
|
<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')
|
@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
|
# Check for possible division by zero error
|
||||||
if total == 0:
|
if denominator == 0:
|
||||||
utilization = 0
|
utilization = 0
|
||||||
else:
|
else:
|
||||||
utilization = int(float(used_count) / total * 100)
|
utilization = int(float(numerator) / denominator * 100)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'utilization': utilization,
|
"utilization": utilization,
|
||||||
'warning_threshold': warning_threshold,
|
"warning_threshold": warning_threshold,
|
||||||
'danger_threshold': danger_threshold,
|
"danger_threshold": danger_threshold,
|
||||||
'utilization_count': used_count,
|
"utilization_count": numerator,
|
||||||
'total_count': total,
|
"total_count": denominator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict, namedtuple
|
||||||
from itertools import count, groupby
|
from itertools import count, groupby
|
||||||
|
|
||||||
from django.core.serializers import serialize
|
from django.core.serializers import serialize
|
||||||
@ -326,3 +326,6 @@ def copy_safe_request(request):
|
|||||||
'path': request.path,
|
'path': request.path,
|
||||||
'id': getattr(request, 'id', None), # UUID assigned by middleware
|
'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