mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-18 13:06:30 -06:00
3839: Add airflow field to DeviceType
This commit is contained in:
parent
176bd2396b
commit
2c2c2e9060
@ -288,13 +288,14 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
|
|||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer()
|
||||||
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
|
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
|
||||||
|
airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
|
'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
|
||||||
'subdevice_role', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created',
|
'subdevice_role', 'airflow', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created',
|
||||||
'last_updated', 'device_count',
|
'last_updated', 'device_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -174,6 +174,25 @@ class DeviceStatusChoices(ChoiceSet):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceAirflowChoices(ChoiceSet):
|
||||||
|
|
||||||
|
AIRFLOW_FRONT_TO_REAR = 'front-to-rear'
|
||||||
|
AIRFLOW_REAR_TO_FRONT = 'rear-to-front'
|
||||||
|
AIRFLOW_LEFT_TO_RIGHT = 'left-to-right'
|
||||||
|
AIRFLOW_RIGHT_TO_LEFT = 'right-to-left'
|
||||||
|
AIRFLOW_SIDE_TO_REAR = 'side-to-rear'
|
||||||
|
AIRFLOW_PASSIVE = 'passive'
|
||||||
|
|
||||||
|
CHOICES = (
|
||||||
|
(AIRFLOW_FRONT_TO_REAR, 'Front to rear'),
|
||||||
|
(AIRFLOW_REAR_TO_FRONT, 'Rear to front'),
|
||||||
|
(AIRFLOW_LEFT_TO_RIGHT, 'Left to right'),
|
||||||
|
(AIRFLOW_RIGHT_TO_LEFT, 'Right to left'),
|
||||||
|
(AIRFLOW_SIDE_TO_REAR, 'Side to rear'),
|
||||||
|
(AIRFLOW_PASSIVE, 'Passive'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# ConsolePorts
|
# ConsolePorts
|
||||||
#
|
#
|
||||||
|
@ -441,7 +441,7 @@ class DeviceTypeFilterSet(PrimaryModelFilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
'id', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
]
|
]
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
|
@ -335,6 +335,11 @@ class DeviceTypeBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModel
|
|||||||
widget=BulkEditNullBooleanSelect(),
|
widget=BulkEditNullBooleanSelect(),
|
||||||
label='Is full depth'
|
label='Is full depth'
|
||||||
)
|
)
|
||||||
|
airflow = forms.ChoiceField(
|
||||||
|
choices=add_blank_choice(DeviceAirflowChoices),
|
||||||
|
required=False,
|
||||||
|
widget=StaticSelect()
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = []
|
nullable_fields = []
|
||||||
|
@ -385,7 +385,7 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
|||||||
model = DeviceType
|
model = DeviceType
|
||||||
field_groups = [
|
field_groups = [
|
||||||
['q', 'tag'],
|
['q', 'tag'],
|
||||||
['manufacturer_id', 'subdevice_role'],
|
['manufacturer_id', 'subdevice_role', 'airflow'],
|
||||||
['console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports'],
|
['console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports'],
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
@ -404,6 +404,11 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelectMultiple()
|
widget=StaticSelectMultiple()
|
||||||
)
|
)
|
||||||
|
airflow = forms.MultipleChoiceField(
|
||||||
|
choices=add_blank_choice(DeviceAirflowChoices),
|
||||||
|
required=False,
|
||||||
|
widget=StaticSelectMultiple()
|
||||||
|
)
|
||||||
console_ports = forms.NullBooleanField(
|
console_ports = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Has console ports',
|
label='Has console ports',
|
||||||
|
@ -367,12 +367,15 @@ class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
'front_image', 'rear_image', 'comments', 'tags',
|
'front_image', 'rear_image', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Device Type', (
|
('Device Type', (
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'tags',
|
'manufacturer', 'model', 'slug', 'part_number', 'tags',
|
||||||
|
)),
|
||||||
|
('Chassis', (
|
||||||
|
'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
)),
|
)),
|
||||||
('Images', ('front_image', 'rear_image')),
|
('Images', ('front_image', 'rear_image')),
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
'comments',
|
'comments',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -179,6 +179,9 @@ class DeviceTypeType(PrimaryObjectType):
|
|||||||
def resolve_subdevice_role(self, info):
|
def resolve_subdevice_role(self, info):
|
||||||
return self.subdevice_role or None
|
return self.subdevice_role or None
|
||||||
|
|
||||||
|
def resolve_airflow(self, info):
|
||||||
|
return self.airflow or None
|
||||||
|
|
||||||
|
|
||||||
class FrontPortType(ComponentObjectType):
|
class FrontPortType(ComponentObjectType):
|
||||||
|
|
||||||
|
18
netbox/dcim/migrations/0136_devicetype_airflow.py
Normal file
18
netbox/dcim/migrations/0136_devicetype_airflow.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.2.8 on 2021-10-14 19:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0135_location_tenant'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='airflow',
|
||||||
|
field=models.CharField(blank=True, max_length=50),
|
||||||
|
),
|
||||||
|
]
|
@ -115,6 +115,12 @@ class DeviceType(PrimaryModel):
|
|||||||
help_text='Parent devices house child devices in device bays. Leave blank '
|
help_text='Parent devices house child devices in device bays. Leave blank '
|
||||||
'if this device type is neither a parent nor a child.'
|
'if this device type is neither a parent nor a child.'
|
||||||
)
|
)
|
||||||
|
airflow = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=DeviceAirflowChoices,
|
||||||
|
blank=True,
|
||||||
|
verbose_name='Airflow direction'
|
||||||
|
)
|
||||||
front_image = models.ImageField(
|
front_image = models.ImageField(
|
||||||
upload_to='devicetype-images',
|
upload_to='devicetype-images',
|
||||||
blank=True
|
blank=True
|
||||||
@ -130,7 +136,7 @@ class DeviceType(PrimaryModel):
|
|||||||
objects = RestrictedQuerySet.as_manager()
|
objects = RestrictedQuerySet.as_manager()
|
||||||
|
|
||||||
clone_fields = [
|
clone_fields = [
|
||||||
'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role',
|
'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -165,6 +171,7 @@ class DeviceType(PrimaryModel):
|
|||||||
('u_height', self.u_height),
|
('u_height', self.u_height),
|
||||||
('is_full_depth', self.is_full_depth),
|
('is_full_depth', self.is_full_depth),
|
||||||
('subdevice_role', self.subdevice_role),
|
('subdevice_role', self.subdevice_role),
|
||||||
|
('airflow', self.airflow),
|
||||||
('comments', self.comments),
|
('comments', self.comments),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class DeviceTypeTable(BaseTable):
|
|||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
'pk', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
||||||
'comments', 'instance_count', 'tags',
|
'airflow', 'comments', 'instance_count', 'tags',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
|
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
|
||||||
|
@ -638,8 +638,8 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
|
|
||||||
device_types = (
|
device_types = (
|
||||||
DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True),
|
DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True),
|
||||||
DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
|
DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT, airflow=DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR),
|
||||||
DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD),
|
DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD, airflow=DeviceAirflowChoices.AIRFLOW_REAR_TO_FRONT),
|
||||||
)
|
)
|
||||||
DeviceType.objects.bulk_create(device_types)
|
DeviceType.objects.bulk_create(device_types)
|
||||||
|
|
||||||
@ -704,6 +704,10 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'subdevice_role': SubdeviceRoleChoices.ROLE_PARENT}
|
params = {'subdevice_role': SubdeviceRoleChoices.ROLE_PARENT}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
|
def test_airflow(self):
|
||||||
|
params = {'airflow': DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
def test_manufacturer(self):
|
def test_manufacturer(self):
|
||||||
manufacturers = Manufacturer.objects.all()[:2]
|
manufacturers = Manufacturer.objects.all()[:2]
|
||||||
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
||||||
|
@ -90,6 +90,12 @@
|
|||||||
{{ object.get_subdevice_role_display|placeholder }}
|
{{ object.get_subdevice_role_display|placeholder }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Airflow direction</td>
|
||||||
|
<td>
|
||||||
|
{{ object.get_airflow_display|placeholder }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Front Image</td>
|
<td>Front Image</td>
|
||||||
<td>
|
<td>
|
||||||
|
Loading…
Reference in New Issue
Block a user