Przeglądaj źródła

Merge pull request #3675 from netbox-community/792-power-types

Introduce power port and outlet types
Jeremy Stretch 6 lat temu
rodzic
commit
48dcd2d2b9

+ 21 - 4
netbox/dcim/api/serializers.py

@@ -4,6 +4,7 @@ from rest_framework import serializers
 from rest_framework.validators import UniqueTogetherValidator
 from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
+from dcim.choices import PowerOutletTypes, PowerPortTypes
 from dcim.constants import *
 from dcim.models import (
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
@@ -218,14 +219,22 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
 
 class PowerPortTemplateSerializer(ValidatedModelSerializer):
     device_type = NestedDeviceTypeSerializer()
+    type = ChoiceField(
+        choices=PowerPortTypes.CHOICES,
+        required=False
+    )
 
     class Meta:
         model = PowerPortTemplate
-        fields = ['id', 'device_type', 'name', 'maximum_draw', 'allocated_draw']
+        fields = ['id', 'device_type', 'name', 'type', 'maximum_draw', 'allocated_draw']
 
 
 class PowerOutletTemplateSerializer(ValidatedModelSerializer):
     device_type = NestedDeviceTypeSerializer()
+    type = ChoiceField(
+        choices=PowerOutletTypes.CHOICES,
+        required=False
+    )
     power_port = PowerPortTemplateSerializer(
         required=False
     )
@@ -237,7 +246,7 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
 
     class Meta:
         model = PowerOutletTemplate
-        fields = ['id', 'device_type', 'name', 'power_port', 'feed_leg']
+        fields = ['id', 'device_type', 'name', 'type', 'power_port', 'feed_leg']
 
 
 class InterfaceTemplateSerializer(ValidatedModelSerializer):
@@ -400,6 +409,10 @@ class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
 
 class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
     device = NestedDeviceSerializer()
+    type = ChoiceField(
+        choices=PowerOutletTypes.CHOICES,
+        required=False
+    )
     power_port = NestedPowerPortSerializer(
         required=False
     )
@@ -418,20 +431,24 @@ class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
     class Meta:
         model = PowerOutlet
         fields = [
-            'id', 'device', 'name', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
+            'id', 'device', 'name', 'type', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
             'connected_endpoint', 'connection_status', 'cable', 'tags',
         ]
 
 
 class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
     device = NestedDeviceSerializer()
+    type = ChoiceField(
+        choices=PowerPortTypes.CHOICES,
+        required=False
+    )
     cable = NestedCableSerializer(read_only=True)
     tags = TagListSerializerField(required=False)
 
     class Meta:
         model = PowerPort
         fields = [
-            'id', 'device', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
+            'id', 'device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
             'connected_endpoint', 'connection_status', 'cable', 'tags',
         ]
 

+ 4 - 3
netbox/dcim/api/views.py

@@ -52,9 +52,10 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
         (FrontPortTemplate, ['type']),
         (Interface, ['type', 'mode']),
         (InterfaceTemplate, ['type']),
-        (PowerOutlet, ['feed_leg']),
-        (PowerOutletTemplate, ['feed_leg']),
-        (PowerPort, ['connection_status']),
+        (PowerOutlet, ['type', 'feed_leg']),
+        (PowerOutletTemplate, ['type', 'feed_leg']),
+        (PowerPort, ['type', 'connection_status']),
+        (PowerPortTemplate, ['type']),
         (Rack, ['outer_unit', 'status', 'type', 'width']),
         (RearPort, ['type']),
         (RearPortTemplate, ['type']),

+ 174 - 0
netbox/dcim/choices.py

@@ -62,6 +62,180 @@ class ConsolePortTypes:
         }.get(slug)
 
 
+#
+# Power port types
+#
+
+class PowerPortTypes:
+    # TODO: Add more power port types
+    # IEC 60320
+    TYPE_IEC_C6 = 'iec-60320-c6'
+    TYPE_IEC_C8 = 'iec-60320-c8'
+    TYPE_IEC_C14 = 'iec-60320-c14'
+    TYPE_IEC_C16 = 'iec-60320-c16'
+    TYPE_IEC_C20 = 'iec-60320-c20'
+    # IEC 60309
+    TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h'
+    TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h'
+    TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h'
+    TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h'
+    TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h'
+    TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h'
+    TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h'
+    TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h'
+    TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h'
+    TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h'
+    TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h'
+    TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h'
+    # NEMA non-locking
+    TYPE_NEMA_515P = 'nema-5-15p'
+    TYPE_NEMA_520P = 'nema-5-20p'
+    TYPE_NEMA_530P = 'nema-5-30p'
+    TYPE_NEMA_550P = 'nema-5-50p'
+    TYPE_NEMA_615P = 'nema-6-15p'
+    TYPE_NEMA_620P = 'nema-6-20p'
+    TYPE_NEMA_630P = 'nema-6-30p'
+    TYPE_NEMA_650P = 'nema-6-50p'
+    # NEMA locking
+    TYPE_NEMA_L515P = 'nema-l5-15p'
+    TYPE_NEMA_L520P = 'nema-l5-20p'
+    TYPE_NEMA_L530P = 'nema-l5-30p'
+    TYPE_NEMA_L615P = 'nema-l5-50p'
+    TYPE_NEMA_L620P = 'nema-l6-20p'
+    TYPE_NEMA_L630P = 'nema-l6-30p'
+    TYPE_NEMA_L650P = 'nema-l6-50p'
+
+    CHOICES = (
+        ('IEC 60320', (
+            (TYPE_IEC_C6, 'C6'),
+            (TYPE_IEC_C8, 'C8'),
+            (TYPE_IEC_C14, 'C14'),
+            (TYPE_IEC_C16, 'C16'),
+            (TYPE_IEC_C20, 'C20'),
+        )),
+        ('IEC 60309', (
+            (TYPE_IEC_PNE4H, 'P+N+E 4H'),
+            (TYPE_IEC_PNE6H, 'P+N+E 6H'),
+            (TYPE_IEC_PNE9H, 'P+N+E 9H'),
+            (TYPE_IEC_2PE4H, '2P+E 4H'),
+            (TYPE_IEC_2PE6H, '2P+E 6H'),
+            (TYPE_IEC_2PE9H, '2P+E 9H'),
+            (TYPE_IEC_3PE4H, '3P+E 4H'),
+            (TYPE_IEC_3PE6H, '3P+E 6H'),
+            (TYPE_IEC_3PE9H, '3P+E 9H'),
+            (TYPE_IEC_3PNE4H, '3P+N+E 4H'),
+            (TYPE_IEC_3PNE6H, '3P+N+E 6H'),
+            (TYPE_IEC_3PNE9H, '3P+N+E 9H'),
+        )),
+        ('NEMA (Non-locking)', (
+            (TYPE_NEMA_515P, 'NEMA 5-15P'),
+            (TYPE_NEMA_520P, 'NEMA 5-20P'),
+            (TYPE_NEMA_530P, 'NEMA 5-30P'),
+            (TYPE_NEMA_550P, 'NEMA 5-50P'),
+            (TYPE_NEMA_615P, 'NEMA 6-15P'),
+            (TYPE_NEMA_620P, 'NEMA 6-20P'),
+            (TYPE_NEMA_630P, 'NEMA 6-30P'),
+            (TYPE_NEMA_650P, 'NEMA 6-50P'),
+        )),
+        ('NEMA (Locking)', (
+            (TYPE_NEMA_L515P, 'NEMA L5-15P'),
+            (TYPE_NEMA_L520P, 'NEMA L5-20P'),
+            (TYPE_NEMA_L530P, 'NEMA L5-30P'),
+            (TYPE_NEMA_L615P, 'NEMA L6-15P'),
+            (TYPE_NEMA_L620P, 'NEMA L6-20P'),
+            (TYPE_NEMA_L630P, 'NEMA L6-30P'),
+            (TYPE_NEMA_L650P, 'NEMA L6-50P'),
+        )),
+    )
+
+
+#
+# Power outlet types
+#
+
+class PowerOutletTypes:
+    # TODO: Add more power outlet types
+    # IEC 60320
+    TYPE_IEC_C5 = 'iec-60320-c5'
+    TYPE_IEC_C7 = 'iec-60320-c7'
+    TYPE_IEC_C13 = 'iec-60320-c13'
+    TYPE_IEC_C15 = 'iec-60320-c15'
+    TYPE_IEC_C19 = 'iec-60320-c19'
+    # IEC 60309
+    TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h'
+    TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h'
+    TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h'
+    TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h'
+    TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h'
+    TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h'
+    TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h'
+    TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h'
+    TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h'
+    TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h'
+    TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h'
+    TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h'
+    # NEMA non-locking
+    TYPE_NEMA_515R = 'nema-5-15r'
+    TYPE_NEMA_520R = 'nema-5-20r'
+    TYPE_NEMA_530R = 'nema-5-30r'
+    TYPE_NEMA_550R = 'nema-5-50r'
+    TYPE_NEMA_615R = 'nema-6-15r'
+    TYPE_NEMA_620R = 'nema-6-20r'
+    TYPE_NEMA_630R = 'nema-6-30r'
+    TYPE_NEMA_650R = 'nema-6-50r'
+    # NEMA locking
+    TYPE_NEMA_L515R = 'nema-l5-15r'
+    TYPE_NEMA_L520R = 'nema-l5-20r'
+    TYPE_NEMA_L530R = 'nema-l5-30r'
+    TYPE_NEMA_L615R = 'nema-l5-50r'
+    TYPE_NEMA_L620R = 'nema-l6-20r'
+    TYPE_NEMA_L630R = 'nema-l6-30r'
+    TYPE_NEMA_L650R = 'nema-l6-50r'
+
+    CHOICES = (
+        ('IEC 60320', (
+            (TYPE_IEC_C5, 'C5'),
+            (TYPE_IEC_C7, 'C7'),
+            (TYPE_IEC_C13, 'C13'),
+            (TYPE_IEC_C15, 'C15'),
+            (TYPE_IEC_C19, 'C19'),
+        )),
+        ('IEC 60309', (
+            (TYPE_IEC_PNE4H, 'P+N+E 4H'),
+            (TYPE_IEC_PNE6H, 'P+N+E 6H'),
+            (TYPE_IEC_PNE9H, 'P+N+E 9H'),
+            (TYPE_IEC_2PE4H, '2P+E 4H'),
+            (TYPE_IEC_2PE6H, '2P+E 6H'),
+            (TYPE_IEC_2PE9H, '2P+E 9H'),
+            (TYPE_IEC_3PE4H, '3P+E 4H'),
+            (TYPE_IEC_3PE6H, '3P+E 6H'),
+            (TYPE_IEC_3PE9H, '3P+E 9H'),
+            (TYPE_IEC_3PNE4H, '3P+N+E 4H'),
+            (TYPE_IEC_3PNE6H, '3P+N+E 6H'),
+            (TYPE_IEC_3PNE9H, '3P+N+E 9H'),
+        )),
+        ('NEMA (Non-locking)', (
+            (TYPE_NEMA_515R, 'NEMA 5-15R'),
+            (TYPE_NEMA_520R, 'NEMA 5-20R'),
+            (TYPE_NEMA_530R, 'NEMA 5-30R'),
+            (TYPE_NEMA_550R, 'NEMA 5-50R'),
+            (TYPE_NEMA_615R, 'NEMA 6-15R'),
+            (TYPE_NEMA_620R, 'NEMA 6-20R'),
+            (TYPE_NEMA_630R, 'NEMA 6-30R'),
+            (TYPE_NEMA_650R, 'NEMA 6-50R'),
+        )),
+        ('NEMA (Locking)', (
+            (TYPE_NEMA_L515R, 'NEMA L5-15R'),
+            (TYPE_NEMA_L520R, 'NEMA L5-20R'),
+            (TYPE_NEMA_L530R, 'NEMA L5-30R'),
+            (TYPE_NEMA_L615R, 'NEMA L6-15R'),
+            (TYPE_NEMA_L620R, 'NEMA L6-20R'),
+            (TYPE_NEMA_L630R, 'NEMA L6-30R'),
+            (TYPE_NEMA_L650R, 'NEMA L6-50R'),
+        )),
+    )
+
+
 #
 # Interface type values
 #

+ 4 - 4
netbox/dcim/filters.py

@@ -360,14 +360,14 @@ class PowerPortTemplateFilter(DeviceTypeComponentFilterSet):
 
     class Meta:
         model = PowerPortTemplate
-        fields = ['id', 'name', 'maximum_draw', 'allocated_draw']
+        fields = ['id', 'name', 'type', 'maximum_draw', 'allocated_draw']
 
 
 class PowerOutletTemplateFilter(DeviceTypeComponentFilterSet):
 
     class Meta:
         model = PowerOutletTemplate
-        fields = ['id', 'name', 'feed_leg']
+        fields = ['id', 'name', 'type', 'feed_leg']
 
 
 class InterfaceTemplateFilter(DeviceTypeComponentFilterSet):
@@ -681,7 +681,7 @@ class PowerPortFilter(DeviceComponentFilterSet):
 
     class Meta:
         model = PowerPort
-        fields = ['id', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connection_status']
+        fields = ['id', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description', 'connection_status']
 
 
 class PowerOutletFilter(DeviceComponentFilterSet):
@@ -693,7 +693,7 @@ class PowerOutletFilter(DeviceComponentFilterSet):
 
     class Meta:
         model = PowerOutlet
-        fields = ['id', 'name', 'feed_leg', 'description', 'connection_status']
+        fields = ['id', 'name', 'type', 'feed_leg', 'description', 'connection_status']
 
 
 class InterfaceFilter(django_filters.FilterSet):

+ 27 - 7
netbox/dcim/forms.py

@@ -986,7 +986,7 @@ class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm):
     class Meta:
         model = PowerPortTemplate
         fields = [
-            'device_type', 'name', 'maximum_draw', 'allocated_draw',
+            'device_type', 'name', 'type', 'maximum_draw', 'allocated_draw',
         ]
         widgets = {
             'device_type': forms.HiddenInput(),
@@ -997,6 +997,10 @@ class PowerPortTemplateCreateForm(ComponentForm):
     name_pattern = ExpandableNameField(
         label='Name'
     )
+    type = forms.ChoiceField(
+        choices=add_blank_choice(PowerPortTypes.CHOICES),
+        required=False
+    )
     maximum_draw = forms.IntegerField(
         min_value=1,
         required=False,
@@ -1014,7 +1018,7 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
     class Meta:
         model = PowerOutletTemplate
         fields = [
-            'device_type', 'name', 'power_port', 'feed_leg',
+            'device_type', 'name', 'type', 'power_port', 'feed_leg',
         ]
         widgets = {
             'device_type': forms.HiddenInput(),
@@ -1035,6 +1039,10 @@ class PowerOutletTemplateCreateForm(ComponentForm):
     name_pattern = ExpandableNameField(
         label='Name'
     )
+    type = forms.ChoiceField(
+        choices=add_blank_choice(PowerOutletTypes.CHOICES),
+        required=False
+    )
     power_port = forms.ModelChoiceField(
         queryset=PowerPortTemplate.objects.all(),
         required=False
@@ -1295,7 +1303,7 @@ class PowerPortTemplateImportForm(ComponentTemplateImportForm):
     class Meta:
         model = PowerPortTemplate
         fields = [
-            'device_type', 'name', 'maximum_draw', 'allocated_draw',
+            'device_type', 'name', 'type', 'maximum_draw', 'allocated_draw',
         ]
 
 
@@ -1309,7 +1317,7 @@ class PowerOutletTemplateImportForm(ComponentTemplateImportForm):
     class Meta:
         model = PowerOutletTemplate
         fields = [
-            'device_type', 'name', 'power_port', 'feed_leg',
+            'device_type', 'name', 'type', 'power_port', 'feed_leg',
         ]
 
 
@@ -2186,7 +2194,7 @@ class PowerPortForm(BootstrapMixin, forms.ModelForm):
     class Meta:
         model = PowerPort
         fields = [
-            'device', 'name', 'maximum_draw', 'allocated_draw', 'description', 'tags',
+            'device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description', 'tags',
         ]
         widgets = {
             'device': forms.HiddenInput(),
@@ -2197,6 +2205,10 @@ class PowerPortCreateForm(ComponentForm):
     name_pattern = ExpandableNameField(
         label='Name'
     )
+    type = forms.ChoiceField(
+        choices=add_blank_choice(PowerPortTypes.CHOICES),
+        required=False
+    )
     maximum_draw = forms.IntegerField(
         min_value=1,
         required=False,
@@ -2232,7 +2244,7 @@ class PowerOutletForm(BootstrapMixin, forms.ModelForm):
     class Meta:
         model = PowerOutlet
         fields = [
-            'device', 'name', 'power_port', 'feed_leg', 'description', 'tags',
+            'device', 'name', 'type', 'power_port', 'feed_leg', 'description', 'tags',
         ]
         widgets = {
             'device': forms.HiddenInput(),
@@ -2252,6 +2264,10 @@ class PowerOutletCreateForm(ComponentForm):
     name_pattern = ExpandableNameField(
         label='Name'
     )
+    type = forms.ChoiceField(
+        choices=add_blank_choice(PowerOutletTypes.CHOICES),
+        required=False
+    )
     power_port = forms.ModelChoiceField(
         queryset=PowerPort.objects.all(),
         required=False
@@ -2281,6 +2297,10 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
         queryset=PowerOutlet.objects.all(),
         widget=forms.MultipleHiddenInput()
     )
+    type = forms.ChoiceField(
+        choices=PowerOutletTypes.CHOICES,
+        required=False
+    )
     feed_leg = forms.ChoiceField(
         choices=add_blank_choice(POWERFEED_LEG_CHOICES),
         required=False,
@@ -2296,7 +2316,7 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 
     class Meta:
         nullable_fields = [
-            'feed_leg', 'power_port', 'description',
+            'type', 'feed_leg', 'power_port', 'description',
         ]
 
     def __init__(self, *args, **kwargs):

+ 33 - 0
netbox/dcim/migrations/0077_power_types.py

@@ -0,0 +1,33 @@
+# Generated by Django 2.2.6 on 2019-11-06 19:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0076_console_port_types'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='poweroutlettemplate',
+            name='type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='powerporttemplate',
+            name='type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+    ]

+ 25 - 2
netbox/dcim/models.py

@@ -20,6 +20,7 @@ from utilities.fields import ColorField
 from utilities.managers import NaturalOrderingManager
 from utilities.models import ChangeLoggedModel
 from utilities.utils import serialize_object, to_meters
+from .choices import PowerOutletTypes, PowerPortTypes
 from .constants import *
 from .exceptions import LoopDetected
 from .fields import ASNField, MACAddressField
@@ -1084,6 +1085,11 @@ class PowerPortTemplate(ComponentTemplateModel):
     name = models.CharField(
         max_length=50
     )
+    type = models.CharField(
+        max_length=50,
+        choices=PowerPortTypes.CHOICES,
+        blank=True
+    )
     maximum_draw = models.PositiveSmallIntegerField(
         blank=True,
         null=True,
@@ -1127,6 +1133,11 @@ class PowerOutletTemplate(ComponentTemplateModel):
     name = models.CharField(
         max_length=50
     )
+    type = models.CharField(
+        max_length=50,
+        choices=PowerOutletTypes.CHOICES,
+        blank=True
+    )
     power_port = models.ForeignKey(
         to='dcim.PowerPortTemplate',
         on_delete=models.SET_NULL,
@@ -1964,6 +1975,11 @@ class PowerPort(CableTermination, ComponentModel):
     name = models.CharField(
         max_length=50
     )
+    type = models.CharField(
+        max_length=50,
+        choices=PowerPortTypes.CHOICES,
+        blank=True
+    )
     maximum_draw = models.PositiveSmallIntegerField(
         blank=True,
         null=True,
@@ -1998,7 +2014,7 @@ class PowerPort(CableTermination, ComponentModel):
     objects = NaturalOrderingManager()
     tags = TaggableManager(through=TaggedItem)
 
-    csv_headers = ['device', 'name', 'maximum_draw', 'allocated_draw', 'description']
+    csv_headers = ['device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description']
 
     class Meta:
         ordering = ['device', 'name']
@@ -2014,6 +2030,7 @@ class PowerPort(CableTermination, ComponentModel):
         return (
             self.device.identifier,
             self.name,
+            self.get_type_display(),
             self.maximum_draw,
             self.allocated_draw,
             self.description,
@@ -2101,6 +2118,11 @@ class PowerOutlet(CableTermination, ComponentModel):
     name = models.CharField(
         max_length=50
     )
+    type = models.CharField(
+        max_length=50,
+        choices=PowerOutletTypes.CHOICES,
+        blank=True
+    )
     power_port = models.ForeignKey(
         to='dcim.PowerPort',
         on_delete=models.SET_NULL,
@@ -2122,7 +2144,7 @@ class PowerOutlet(CableTermination, ComponentModel):
     objects = NaturalOrderingManager()
     tags = TaggableManager(through=TaggedItem)
 
-    csv_headers = ['device', 'name', 'power_port', 'feed_leg', 'description']
+    csv_headers = ['device', 'name', 'type', 'power_port', 'feed_leg', 'description']
 
     class Meta:
         unique_together = ['device', 'name']
@@ -2137,6 +2159,7 @@ class PowerOutlet(CableTermination, ComponentModel):
         return (
             self.device.identifier,
             self.name,
+            self.get_type_display(),
             self.power_port.name if self.power_port else None,
             self.get_feed_leg_display(),
             self.description,

+ 4 - 4
netbox/dcim/tables.py

@@ -450,7 +450,7 @@ class PowerPortTemplateTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = PowerPortTemplate
-        fields = ('pk', 'name', 'maximum_draw', 'allocated_draw', 'actions')
+        fields = ('pk', 'name', 'type', 'maximum_draw', 'allocated_draw', 'actions')
         empty_text = "None"
 
 
@@ -464,7 +464,7 @@ class PowerOutletTemplateTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = PowerOutletTemplate
-        fields = ('pk', 'name', 'power_port', 'feed_leg', 'actions')
+        fields = ('pk', 'name', 'type', 'power_port', 'feed_leg', 'actions')
         empty_text = "None"
 
 
@@ -661,14 +661,14 @@ class PowerPortTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = PowerPort
-        fields = ('name',)
+        fields = ('name', 'type')
 
 
 class PowerOutletTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = PowerOutlet
-        fields = ('name', 'description')
+        fields = ('name', 'type', 'description')
 
 
 class InterfaceTable(BaseTable):

+ 1 - 0
netbox/templates/dcim/device.html

@@ -688,6 +688,7 @@
                                     <th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
                                 {% endif %}
                                 <th>Name</th>
+                            <th>Type</th>
                                 <th>Input/Leg</th>
                                 <th>Description</th>
                                 <th>Cable</th>

+ 2 - 1
netbox/templates/dcim/inc/consoleport.html

@@ -4,13 +4,14 @@
     <td>
         <i class="fa fa-fw fa-keyboard-o"></i> {{ cp }}
     </td>
-    <td></td>
 
     {# Type #}
     <td>
         {% if cp.type %}{{ cp.get_type_display }}{% else %}&mdash;{% endif %}
     </td>
 
+    <td></td>
+
     {# Description #}
     <td>
         {{ cp.description }}

+ 5 - 0
netbox/templates/dcim/inc/poweroutlet.html

@@ -14,6 +14,11 @@
         <i class="fa fa-fw fa-bolt"></i> {{ po }}
     </td>
 
+    {# Type #}
+    <td>
+        {{ po.get_type_display }}
+    </td>
+
     {# Input/leg #}
     <td>
         {% if po.power_port %}

+ 5 - 0
netbox/templates/dcim/inc/powerport.html

@@ -5,6 +5,11 @@
         <i class="fa fa-fw fa-bolt"></i> {{ pp }}
     </td>
 
+    {# Type #}
+    <td>
+        {{ pp.get_type_display }}
+    </td>
+
     {# Current draw #}
     <td>
         {% if pp.allocated_draw %}