Просмотр исходного кода

3839: Add airflow field to DeviceType

jeremystretch 4 лет назад
Родитель
Сommit
2c2c2e9060

+ 2 - 1
netbox/dcim/api/serializers.py

@@ -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',
         ]
         ]
 
 

+ 19 - 0
netbox/dcim/choices.py

@@ -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
 #
 #

+ 1 - 1
netbox/dcim/filtersets.py

@@ -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):

+ 5 - 0
netbox/dcim/forms/bulk_edit.py

@@ -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 = []

+ 6 - 1
netbox/dcim/forms/filtersets.py

@@ -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',

+ 5 - 2
netbox/dcim/forms/models.py

@@ -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')),
         )
         )

+ 1 - 1
netbox/dcim/forms/object_import.py

@@ -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',
         ]
         ]
 
 

+ 3 - 0
netbox/dcim/graphql/types.py

@@ -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 - 0
netbox/dcim/migrations/0136_devicetype_airflow.py

@@ -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),
+        ),
+    ]

+ 8 - 1
netbox/dcim/models/devices.py

@@ -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),
         ))
         ))
 
 

+ 1 - 1
netbox/dcim/tables/devicetypes.py

@@ -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',

+ 6 - 2
netbox/dcim/tests/test_filtersets.py

@@ -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[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[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, 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]}

+ 6 - 0
netbox/templates/dcim/devicetype.html

@@ -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>