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

Closes #5375: Add 'speed' attribute to console port models

Jeremy Stretch 5 лет назад
Родитель
Сommit
8de20fcd1f

+ 1 - 0
docs/release-notes/version-2.11.md

@@ -15,6 +15,7 @@ In addition to the new `mark_connected` boolean field, the REST API representati
 ### Enhancements
 
 * [#5370](https://github.com/netbox-community/netbox/issues/5370) - Extend custom field support to organizational models
+* [#5375](https://github.com/netbox-community/netbox/issues/5375) - Add `speed` attribute to console port models
 * [#5401](https://github.com/netbox-community/netbox/issues/5401) - Extend custom field support to device component models
 * [#5451](https://github.com/netbox-community/netbox/issues/5451) - Add support for multiple-selection custom fields
 * [#5894](https://github.com/netbox-community/netbox/issues/5894) - Use primary keys when filtering object lists by related objects in the UI

+ 16 - 6
netbox/dcim/api/serializers.py

@@ -504,14 +504,19 @@ class ConsoleServerPortSerializer(TaggedObjectSerializer, CableTerminationSerial
         allow_blank=True,
         required=False
     )
+    speed = ChoiceField(
+        choices=ConsolePortSpeedChoices,
+        allow_blank=True,
+        required=False
+    )
     cable = NestedCableSerializer(read_only=True)
 
     class Meta:
         model = ConsoleServerPort
         fields = [
-            'id', 'url', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
-            'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags',
-            'custom_fields', 'created', 'last_updated', '_occupied',
+            'id', 'url', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable',
+            'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
+            'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
         ]
 
 
@@ -523,14 +528,19 @@ class ConsolePortSerializer(TaggedObjectSerializer, CableTerminationSerializer,
         allow_blank=True,
         required=False
     )
+    speed = ChoiceField(
+        choices=ConsolePortSpeedChoices,
+        allow_blank=True,
+        required=False
+    )
     cable = NestedCableSerializer(read_only=True)
 
     class Meta:
         model = ConsolePort
         fields = [
-            'id', 'url', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
-            'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags',
-            'custom_fields', 'created', 'last_updated', '_occupied',
+            'id', 'url', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable',
+            'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
+            'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
         ]
 
 

+ 23 - 0
netbox/dcim/choices.py

@@ -217,6 +217,29 @@ class ConsolePortTypeChoices(ChoiceSet):
     )
 
 
+class ConsolePortSpeedChoices(ChoiceSet):
+
+    SPEED_1200 = 1200
+    SPEED_2400 = 2400
+    SPEED_4800 = 4800
+    SPEED_9600 = 9600
+    SPEED_19200 = 19200
+    SPEED_38400 = 38400
+    SPEED_57600 = 57600
+    SPEED_115200 = 115200
+
+    CHOICES = (
+        (SPEED_1200, '1200 bps'),
+        (SPEED_2400, '2400 bps'),
+        (SPEED_4800, '4800 bps'),
+        (SPEED_9600, '9600 bps'),
+        (SPEED_19200, '19.2 kbps'),
+        (SPEED_38400, '38.4 kbps'),
+        (SPEED_57600, '57.6 kbps'),
+        (SPEED_115200, '115.2 kbps'),
+    )
+
+
 #
 # PowerPorts
 #

+ 47 - 12
netbox/dcim/forms.py

@@ -23,9 +23,10 @@ from tenancy.forms import TenancyFilterForm, TenancyForm
 from tenancy.models import Tenant
 from utilities.forms import (
     APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
-    ColorSelect, CommentField, CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, DynamicModelChoiceField,
-    DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField, NumericArrayField, SelectWithPK,
-    SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
+    ColorSelect, CommentField, CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVTypedChoiceField,
+    DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField,
+    NumericArrayField, SelectWithPK, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField,
+    BOOLEAN_WITH_BLANK_CHOICES,
 )
 from virtualization.models import Cluster, ClusterGroup
 from .choices import *
@@ -2312,6 +2313,11 @@ class ConsolePortFilterForm(DeviceComponentFilterForm):
         required=False,
         widget=StaticSelect2Multiple()
     )
+    speed = forms.MultipleChoiceField(
+        choices=ConsolePortSpeedChoices,
+        required=False,
+        widget=StaticSelect2Multiple()
+    )
     tag = TagFilterField(model)
 
 
@@ -2324,7 +2330,7 @@ class ConsolePortForm(BootstrapMixin, CustomFieldModelForm):
     class Meta:
         model = ConsolePort
         fields = [
-            'device', 'name', 'label', 'type', 'mark_connected', 'description', 'tags',
+            'device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
         ]
         widgets = {
             'device': forms.HiddenInput(),
@@ -2338,11 +2344,16 @@ class ConsolePortCreateForm(ComponentCreateForm):
         required=False,
         widget=StaticSelect2()
     )
-    field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'mark_connected', 'description', 'tags')
+    speed = forms.ChoiceField(
+        choices=add_blank_choice(ConsolePortSpeedChoices),
+        required=False,
+        widget=StaticSelect2()
+    )
+    field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'speed', 'mark_connected', 'description', 'tags')
 
 
 class ConsolePortBulkCreateForm(
-    form_from_model(ConsolePort, ['type', 'mark_connected']),
+    form_from_model(ConsolePort, ['type', 'speed', 'mark_connected']),
     DeviceBulkAddComponentForm
 ):
     model = ConsolePort
@@ -2350,7 +2361,7 @@ class ConsolePortBulkCreateForm(
 
 
 class ConsolePortBulkEditForm(
-    form_from_model(ConsolePort, ['label', 'type', 'mark_connected', 'description']),
+    form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']),
     BootstrapMixin,
     AddRemoveTagsForm,
     CustomFieldBulkEditForm
@@ -2374,6 +2385,13 @@ class ConsolePortCSVForm(CustomFieldModelCSVForm):
         required=False,
         help_text='Port type'
     )
+    speed = CSVTypedChoiceField(
+        choices=ConsolePortSpeedChoices,
+        coerce=int,
+        empty_value=None,
+        required=False,
+        help_text='Port speed in bps'
+    )
 
     class Meta:
         model = ConsolePort
@@ -2392,6 +2410,11 @@ class ConsoleServerPortFilterForm(DeviceComponentFilterForm):
         required=False,
         widget=StaticSelect2Multiple()
     )
+    speed = forms.MultipleChoiceField(
+        choices=ConsolePortSpeedChoices,
+        required=False,
+        widget=StaticSelect2Multiple()
+    )
     tag = TagFilterField(model)
 
 
@@ -2404,7 +2427,7 @@ class ConsoleServerPortForm(BootstrapMixin, CustomFieldModelForm):
     class Meta:
         model = ConsoleServerPort
         fields = [
-            'device', 'name', 'label', 'type', 'description', 'tags',
+            'device', 'name', 'label', 'type', 'speed', 'description', 'tags',
         ]
         widgets = {
             'device': forms.HiddenInput(),
@@ -2418,19 +2441,24 @@ class ConsoleServerPortCreateForm(ComponentCreateForm):
         required=False,
         widget=StaticSelect2()
     )
-    field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'description', 'tags')
+    speed = forms.ChoiceField(
+        choices=add_blank_choice(ConsolePortSpeedChoices),
+        required=False,
+        widget=StaticSelect2()
+    )
+    field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'speed', 'description', 'tags')
 
 
 class ConsoleServerPortBulkCreateForm(
-    form_from_model(ConsoleServerPort, ['type']),
+    form_from_model(ConsoleServerPort, ['type', 'speed']),
     DeviceBulkAddComponentForm
 ):
     model = ConsoleServerPort
-    field_order = ('name_pattern', 'label_pattern', 'type', 'description', 'tags')
+    field_order = ('name_pattern', 'label_pattern', 'type', 'speed', 'description', 'tags')
 
 
 class ConsoleServerPortBulkEditForm(
-    form_from_model(ConsoleServerPort, ['label', 'type', 'description']),
+    form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'description']),
     BootstrapMixin,
     AddRemoveTagsForm,
     CustomFieldBulkEditForm
@@ -2454,6 +2482,13 @@ class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
         required=False,
         help_text='Port type'
     )
+    speed = CSVTypedChoiceField(
+        choices=ConsolePortSpeedChoices,
+        coerce=int,
+        empty_value=None,
+        required=False,
+        help_text='Port speed in bps'
+    )
 
     class Meta:
         model = ConsoleServerPort

+ 21 - 0
netbox/dcim/migrations/0125_console_port_speed.py

@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0124_mark_connected'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='consoleport',
+            name='speed',
+            field=models.PositiveSmallIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='speed',
+            field=models.PositiveSmallIntegerField(blank=True, null=True),
+        ),
+    ]

+ 16 - 2
netbox/dcim/models/device_components.py

@@ -224,9 +224,15 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
         blank=True,
         help_text='Physical port type'
     )
+    speed = models.PositiveSmallIntegerField(
+        choices=ConsolePortSpeedChoices,
+        blank=True,
+        null=True,
+        help_text='Port speed in bits per second'
+    )
     tags = TaggableManager(through=TaggedItem)
 
-    csv_headers = ['device', 'name', 'label', 'type', 'mark_connected', 'description']
+    csv_headers = ['device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description']
 
     class Meta:
         ordering = ('device', '_name')
@@ -241,6 +247,7 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
             self.name,
             self.label,
             self.type,
+            self.speed,
             self.mark_connected,
             self.description,
         )
@@ -261,9 +268,15 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
         blank=True,
         help_text='Physical port type'
     )
+    speed = models.PositiveSmallIntegerField(
+        choices=ConsolePortSpeedChoices,
+        blank=True,
+        null=True,
+        help_text='Port speed in bits per second'
+    )
     tags = TaggableManager(through=TaggedItem)
 
-    csv_headers = ['device', 'name', 'label', 'type', 'mark_connected', 'description']
+    csv_headers = ['device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description']
 
     class Meta:
         ordering = ('device', '_name')
@@ -278,6 +291,7 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
             self.name,
             self.label,
             self.type,
+            self.speed,
             self.mark_connected,
             self.description,
         )

+ 10 - 10
netbox/dcim/tables/devices.py

@@ -249,10 +249,10 @@ class ConsolePortTable(DeviceComponentTable, PathEndpointTable):
     class Meta(DeviceComponentTable.Meta):
         model = ConsolePort
         fields = (
-            'pk', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
+            'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
             'connection', 'tags',
         )
-        default_columns = ('pk', 'device', 'name', 'label', 'type', 'description')
+        default_columns = ('pk', 'device', 'name', 'label', 'type', 'speed', 'description')
 
 
 class DeviceConsolePortTable(ConsolePortTable):
@@ -269,10 +269,10 @@ class DeviceConsolePortTable(ConsolePortTable):
     class Meta(DeviceComponentTable.Meta):
         model = ConsolePort
         fields = (
-            'pk', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer', 'connection',
-            'tags', 'actions'
+            'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
+            'connection', 'tags', 'actions'
         )
-        default_columns = ('pk', 'name', 'label', 'type', 'description', 'cable', 'connection', 'actions')
+        default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection', 'actions')
         row_attrs = {
             'class': lambda record: record.cable.get_status_class() if record.cable else ''
         }
@@ -286,10 +286,10 @@ class ConsoleServerPortTable(DeviceComponentTable, PathEndpointTable):
     class Meta(DeviceComponentTable.Meta):
         model = ConsoleServerPort
         fields = (
-            'pk', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
+            'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
             'connection', 'tags',
         )
-        default_columns = ('pk', 'device', 'name', 'label', 'type', 'description')
+        default_columns = ('pk', 'device', 'name', 'label', 'type', 'speed', 'description')
 
 
 class DeviceConsoleServerPortTable(ConsoleServerPortTable):
@@ -307,10 +307,10 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable):
     class Meta(DeviceComponentTable.Meta):
         model = ConsoleServerPort
         fields = (
-            'pk', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer', 'connection', 'tags',
-            'actions',
+            'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
+            'connection', 'tags', 'actions',
         )
-        default_columns = ('pk', 'name', 'label', 'type', 'description', 'cable', 'connection', 'actions')
+        default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection', 'actions')
         row_attrs = {
             'class': lambda record: record.cable.get_status_class() if record.cable else ''
         }

+ 5 - 1
netbox/templates/dcim/consoleport.html

@@ -26,7 +26,11 @@
                     </tr>
                     <tr>
                         <td>Type</td>
-                        <td>{{ object.get_type_display }}</td>
+                        <td>{{ object.get_type_display|placeholder }}</td>
+                    </tr>
+                    <tr>
+                        <td>Speed</td>
+                        <td>{{ object.get_speed_display|placeholder }}</td>
                     </tr>
                     <tr>
                         <td>Description</td>

+ 5 - 1
netbox/templates/dcim/consoleserverport.html

@@ -26,7 +26,11 @@
                     </tr>
                     <tr>
                         <td>Type</td>
-                        <td>{{ object.get_type_display }}</td>
+                        <td>{{ object.get_type_display|placeholder }}</td>
+                    </tr>
+                    <tr>
+                        <td>Speed</td>
+                        <td>{{ object.get_speed_display|placeholder }}</td>
                     </tr>
                     <tr>
                         <td>Description</td>

+ 5 - 0
netbox/utilities/forms/fields.py

@@ -24,6 +24,7 @@ __all__ = (
     'CSVContentTypeField',
     'CSVDataField',
     'CSVModelChoiceField',
+    'CSVTypedChoiceField',
     'DynamicModelChoiceField',
     'DynamicModelMultipleChoiceField',
     'ExpandableIPAddressField',
@@ -125,6 +126,10 @@ class CSVChoiceField(forms.ChoiceField):
         self.choices = unpack_grouped_choices(choices)
 
 
+class CSVTypedChoiceField(forms.TypedChoiceField):
+    STATIC_CHOICES = True
+
+
 class CSVModelChoiceField(forms.ModelChoiceField):
     """
     Provides additional validation for model choices entered as CSV data.