mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-24 08:25:17 -06:00
9654 add weight fields to devices
This commit is contained in:
parent
148c6a6c23
commit
81f0992ace
@ -1314,6 +1314,24 @@ class CableLengthUnitChoices(ChoiceSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceWeightUnitChoices(ChoiceSet):
|
||||||
|
|
||||||
|
# Metric
|
||||||
|
UNIT_KILOGRAM = 'kg'
|
||||||
|
UNIT_GRAM = 'g'
|
||||||
|
|
||||||
|
# Imperial
|
||||||
|
UNIT_POUND = 'lb'
|
||||||
|
UNIT_OUNCE = 'oz'
|
||||||
|
|
||||||
|
CHOICES = (
|
||||||
|
(UNIT_KILOGRAM, 'Kilograms'),
|
||||||
|
(UNIT_GRAM, 'Grams'),
|
||||||
|
(UNIT_POUND, 'Pounds'),
|
||||||
|
(UNIT_OUNCE, 'Ounce'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# CableTerminations
|
# CableTerminations
|
||||||
#
|
#
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
# Generated by Django 4.0.7 on 2022-09-23 01:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0161_cabling_cleanup'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='_abs_weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='weight_unit',
|
||||||
|
field=models.CharField(blank=True, max_length=50),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='moduletype',
|
||||||
|
name='_abs_weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='moduletype',
|
||||||
|
name='weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='moduletype',
|
||||||
|
name='weight_unit',
|
||||||
|
field=models.CharField(blank=True, max_length=50),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='_abs_weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='weight',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='weight_unit',
|
||||||
|
field=models.CharField(blank=True, max_length=50),
|
||||||
|
),
|
||||||
|
]
|
@ -20,6 +20,7 @@ from netbox.models import OrganizationalModel, NetBoxModel
|
|||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
from utilities.fields import ColorField, NaturalOrderingField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
from .device_components import *
|
from .device_components import *
|
||||||
|
from .mixins import DeviceWeightMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -70,7 +71,7 @@ class Manufacturer(OrganizationalModel):
|
|||||||
return reverse('dcim:manufacturer', args=[self.pk])
|
return reverse('dcim:manufacturer', args=[self.pk])
|
||||||
|
|
||||||
|
|
||||||
class DeviceType(NetBoxModel):
|
class DeviceType(NetBoxModel, DeviceWeightMixin):
|
||||||
"""
|
"""
|
||||||
A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as
|
A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as
|
||||||
well as high-level functional role(s).
|
well as high-level functional role(s).
|
||||||
@ -308,7 +309,7 @@ class DeviceType(NetBoxModel):
|
|||||||
return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
|
return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
|
||||||
|
|
||||||
|
|
||||||
class ModuleType(NetBoxModel):
|
class ModuleType(NetBoxModel, DeviceWeightMixin):
|
||||||
"""
|
"""
|
||||||
A ModuleType represents a hardware element that can be installed within a device and which houses additional
|
A ModuleType represents a hardware element that can be installed within a device and which houses additional
|
||||||
components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a
|
components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a
|
||||||
|
37
netbox/dcim/models/mixins.py
Normal file
37
netbox/dcim/models/mixins.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from django.db import models
|
||||||
|
from dcim.choices import *
|
||||||
|
from utilities.utils import to_kilograms
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceWeightMixin(models.Model):
|
||||||
|
weight = models.DecimalField(
|
||||||
|
max_digits=8,
|
||||||
|
decimal_places=2,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
weight_unit = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=DeviceWeightUnitChoices,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
# Stores the normalized length (in meters) for database ordering
|
||||||
|
_abs_weight = models.DecimalField(
|
||||||
|
max_digits=10,
|
||||||
|
decimal_places=4,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
|
# Store the given weight (if any) in meters for use in database ordering
|
||||||
|
if self.weight and self.weight_unit:
|
||||||
|
self._abs_weight = to_kilograms(self.length, self.length_unit)
|
||||||
|
else:
|
||||||
|
self._abs_weight = None
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
@ -20,6 +20,7 @@ from utilities.fields import ColorField, NaturalOrderingField
|
|||||||
from utilities.utils import array_to_string, drange
|
from utilities.utils import array_to_string, drange
|
||||||
from .device_components import PowerOutlet, PowerPort
|
from .device_components import PowerOutlet, PowerPort
|
||||||
from .devices import Device
|
from .devices import Device
|
||||||
|
from .mixins import DeviceWeightMixin
|
||||||
from .power import PowerFeed
|
from .power import PowerFeed
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -63,7 +64,7 @@ class RackRole(OrganizationalModel):
|
|||||||
return reverse('dcim:rackrole', args=[self.pk])
|
return reverse('dcim:rackrole', args=[self.pk])
|
||||||
|
|
||||||
|
|
||||||
class Rack(NetBoxModel):
|
class Rack(NetBoxModel, DeviceWeightMixin):
|
||||||
"""
|
"""
|
||||||
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
|
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
|
||||||
Each Rack is assigned to a Site and (optionally) a Location.
|
Each Rack is assigned to a Site and (optionally) a Location.
|
||||||
@ -449,6 +450,11 @@ class Rack(NetBoxModel):
|
|||||||
|
|
||||||
return int(allocated_draw / available_power_total * 100)
|
return int(allocated_draw / available_power_total * 100)
|
||||||
|
|
||||||
|
def get_total_weight(self):
|
||||||
|
total_weight = sum(device._abs_weight for device in self.devices.exclude(_abs_weight__isnull=True))
|
||||||
|
total_weight += self._abs_weight
|
||||||
|
return total_weight
|
||||||
|
|
||||||
|
|
||||||
class RackReservation(NetBoxModel):
|
class RackReservation(NetBoxModel):
|
||||||
"""
|
"""
|
||||||
|
@ -270,6 +270,38 @@ def to_meters(length, unit):
|
|||||||
raise ValueError(f"Unknown unit {unit}. Must be 'km', 'm', 'cm', 'mi', 'ft', or 'in'.")
|
raise ValueError(f"Unknown unit {unit}. Must be 'km', 'm', 'cm', 'mi', 'ft', or 'in'.")
|
||||||
|
|
||||||
|
|
||||||
|
def to_kilograms(weight, unit):
|
||||||
|
"""
|
||||||
|
Convert the given length to kilograms.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if weight < 0:
|
||||||
|
raise ValueError("Weight must be a positive number")
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError(f"Invalid value '{weight}' for weight (must be a number)")
|
||||||
|
|
||||||
|
valid_units = DeviceWeightUnitChoices.values()
|
||||||
|
if unit not in valid_units:
|
||||||
|
raise ValueError(f"Unknown unit {unit}. Must be one of the following: {', '.join(valid_units)}")
|
||||||
|
|
||||||
|
UNIT_KILOGRAM = 'kg'
|
||||||
|
UNIT_GRAM = 'g'
|
||||||
|
|
||||||
|
# Imperial
|
||||||
|
UNIT_POUND = 'lb'
|
||||||
|
UNIT_OUNCE = 'oz'
|
||||||
|
|
||||||
|
if unit == DeviceWeightUnitChoices.UNIT_KILOGRAM:
|
||||||
|
return weight
|
||||||
|
if unit == DeviceWeightUnitChoices.UNIT_GRAM:
|
||||||
|
return weight * 1000
|
||||||
|
if unit == DeviceWeightUnitChoices.UNIT_POUND:
|
||||||
|
return weight * Decimal(0.453592)
|
||||||
|
if unit == DeviceWeightUnitChoices.UNIT_OUNCE:
|
||||||
|
return weight * Decimal(0.0283495)
|
||||||
|
raise ValueError(f"Unknown unit {unit}. Must be 'kg', 'g', 'lb', 'oz'.")
|
||||||
|
|
||||||
|
|
||||||
def render_jinja2(template_code, context):
|
def render_jinja2(template_code, context):
|
||||||
"""
|
"""
|
||||||
Render a Jinja2 template with the provided context. Return the rendered content.
|
Render a Jinja2 template with the provided context. Return the rendered content.
|
||||||
|
Loading…
Reference in New Issue
Block a user