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

Remove is_console_server, is_pdu, and is_network_device from DeviceType

Jeremy Stretch 7 лет назад
Родитель
Сommit
8573c8b8cb

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

@@ -226,8 +226,7 @@ class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
             'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
             'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
-            'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated', 'instance_count',
+            'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'instance_count',
         ]
         ]
 
 
 
 

+ 15 - 15
netbox/dcim/filters.py

@@ -1,6 +1,6 @@
 import django_filters
 import django_filters
 from django.contrib.auth.models import User
 from django.contrib.auth.models import User
-from django.db.models import Q
+from django.db.models import Count, Q
 from netaddr import EUI
 from netaddr import EUI
 from netaddr.core import AddrFormatError
 from netaddr.core import AddrFormatError
 
 
@@ -310,8 +310,7 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
     class Meta:
     class Meta:
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
-            'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
-            'is_network_device', 'subdevice_role',
+            'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
         ]
         ]
 
 
     def search(self, queryset, name, value):
     def search(self, queryset, name, value):
@@ -506,18 +505,19 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
         name='device_type__is_full_depth',
         name='device_type__is_full_depth',
         label='Is full depth',
         label='Is full depth',
     )
     )
-    is_console_server = django_filters.BooleanFilter(
-        name='device_type__is_console_server',
-        label='Is a console server',
-    )
-    is_pdu = django_filters.BooleanFilter(
-        name='device_type__is_pdu',
-        label='Is a PDU',
-    )
-    is_network_device = django_filters.BooleanFilter(
-        name='device_type__is_network_device',
-        label='Is a network device',
-    )
+    # TODO: Replace these filters
+    # is_console_server = django_filters.BooleanFilter(
+    #     name='device_type__is_console_server',
+    #     label='Is a console server',
+    # )
+    # is_pdu = django_filters.BooleanFilter(
+    #     name='device_type__is_pdu',
+    #     label='Is a PDU',
+    # )
+    # is_network_device = django_filters.BooleanFilter(
+    #     name='device_type__is_network_device',
+    #     label='Is a network device',
+    # )
     mac_address = django_filters.CharFilter(
     mac_address = django_filters.CharFilter(
         method='_mac_address',
         method='_mac_address',
         label='MAC address',
         label='MAC address',

+ 6 - 24
netbox/dcim/fixtures/dcim.json

@@ -76,10 +76,7 @@
         "model": "MX960",
         "model": "MX960",
         "slug": "mx960",
         "slug": "mx960",
         "u_height": 16,
         "u_height": 16,
-        "is_full_depth": true,
-        "is_console_server": false,
-        "is_pdu": false,
-        "is_network_device": true
+        "is_full_depth": true
     }
     }
 },
 },
 {
 {
@@ -92,10 +89,7 @@
         "model": "EX9214",
         "model": "EX9214",
         "slug": "ex9214",
         "slug": "ex9214",
         "u_height": 16,
         "u_height": 16,
-        "is_full_depth": true,
-        "is_console_server": false,
-        "is_pdu": false,
-        "is_network_device": true
+        "is_full_depth": true
     }
     }
 },
 },
 {
 {
@@ -108,10 +102,7 @@
         "model": "QFX5100-24Q",
         "model": "QFX5100-24Q",
         "slug": "qfx5100-24q",
         "slug": "qfx5100-24q",
         "u_height": 1,
         "u_height": 1,
-        "is_full_depth": true,
-        "is_console_server": false,
-        "is_pdu": false,
-        "is_network_device": true
+        "is_full_depth": true
     }
     }
 },
 },
 {
 {
@@ -124,10 +115,7 @@
         "model": "QFX5100-48S",
         "model": "QFX5100-48S",
         "slug": "qfx5100-48s",
         "slug": "qfx5100-48s",
         "u_height": 1,
         "u_height": 1,
-        "is_full_depth": true,
-        "is_console_server": false,
-        "is_pdu": false,
-        "is_network_device": true
+        "is_full_depth": true
     }
     }
 },
 },
 {
 {
@@ -140,10 +128,7 @@
         "model": "CM4148",
         "model": "CM4148",
         "slug": "cm4148",
         "slug": "cm4148",
         "u_height": 1,
         "u_height": 1,
-        "is_full_depth": true,
-        "is_console_server": true,
-        "is_pdu": false,
-        "is_network_device": false
+        "is_full_depth": true
     }
     }
 },
 },
 {
 {
@@ -156,10 +141,7 @@
         "model": "CWG-24VYM415C9",
         "model": "CWG-24VYM415C9",
         "slug": "cwg-24vym415c9",
         "slug": "cwg-24vym415c9",
         "u_height": 0,
         "u_height": 0,
-        "is_full_depth": false,
-        "is_console_server": false,
-        "is_pdu": true,
-        "is_network_device": false
+        "is_full_depth": false
     }
     }
 },
 },
 {
 {

+ 7 - 22
netbox/dcim/forms.py

@@ -531,8 +531,8 @@ class DeviceTypeForm(BootstrapMixin, CustomFieldForm):
     class Meta:
     class Meta:
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
-            'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
-            'is_network_device', 'subdevice_role', 'interface_ordering', 'comments', 'tags',
+            'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
+            'interface_ordering', 'comments', 'tags',
         ]
         ]
         labels = {
         labels = {
             'interface_ordering': 'Order interfaces by',
             'interface_ordering': 'Order interfaces by',
@@ -575,13 +575,6 @@ class DeviceTypeBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkE
     u_height = forms.IntegerField(min_value=1, required=False)
     u_height = forms.IntegerField(min_value=1, required=False)
     is_full_depth = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect, label='Is full depth')
     is_full_depth = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect, label='Is full depth')
     interface_ordering = forms.ChoiceField(choices=add_blank_choice(IFACE_ORDERING_CHOICES), required=False)
     interface_ordering = forms.ChoiceField(choices=add_blank_choice(IFACE_ORDERING_CHOICES), required=False)
-    is_console_server = forms.NullBooleanField(
-        required=False, widget=BulkEditNullBooleanSelect, label='Is a console server'
-    )
-    is_pdu = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect, label='Is a PDU')
-    is_network_device = forms.NullBooleanField(
-        required=False, widget=BulkEditNullBooleanSelect, label='Is a network device'
-    )
 
 
     class Meta:
     class Meta:
         nullable_fields = []
         nullable_fields = []
@@ -594,14 +587,6 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
         queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')),
         queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')),
         to_field_name='slug'
         to_field_name='slug'
     )
     )
-    is_console_server = forms.BooleanField(
-        required=False, label='Is a console server', widget=forms.CheckboxInput(attrs={'value': 'True'}))
-    is_pdu = forms.BooleanField(
-        required=False, label='Is a PDU', widget=forms.CheckboxInput(attrs={'value': 'True'})
-    )
-    is_network_device = forms.BooleanField(
-        required=False, label='Is a network device', widget=forms.CheckboxInput(attrs={'value': 'True'})
-    )
     subdevice_role = forms.NullBooleanField(
     subdevice_role = forms.NullBooleanField(
         required=False, label='Subdevice role', widget=forms.Select(choices=(
         required=False, label='Subdevice role', widget=forms.Select(choices=(
             ('', '---------'),
             ('', '---------'),
@@ -1288,7 +1273,7 @@ class ConsolePortCreateForm(ComponentForm):
 
 
 class ConsoleConnectionCSVForm(forms.ModelForm):
 class ConsoleConnectionCSVForm(forms.ModelForm):
     console_server = FlexibleModelChoiceField(
     console_server = FlexibleModelChoiceField(
-        queryset=Device.objects.filter(device_type__is_console_server=True),
+        queryset=Device.objects.all(),
         to_field_name='name',
         to_field_name='name',
         help_text='Console server name or ID',
         help_text='Console server name or ID',
         error_messages={
         error_messages={
@@ -1387,7 +1372,7 @@ class ConsolePortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelF
         )
         )
     )
     )
     console_server = ChainedModelChoiceField(
     console_server = ChainedModelChoiceField(
-        queryset=Device.objects.filter(device_type__is_console_server=True),
+        queryset=Device.objects.all(),
         chains=(
         chains=(
             ('site', 'site'),
             ('site', 'site'),
             ('rack', 'rack'),
             ('rack', 'rack'),
@@ -1395,7 +1380,7 @@ class ConsolePortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelF
         label='Console Server',
         label='Console Server',
         required=False,
         required=False,
         widget=APISelect(
         widget=APISelect(
-            api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}&is_console_server=True',
+            api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
             display_field='display_name',
             display_field='display_name',
             attrs={'filter-for': 'connected_endpoint'}
             attrs={'filter-for': 'connected_endpoint'}
         )
         )
@@ -1557,7 +1542,7 @@ class PowerPortCreateForm(ComponentForm):
 
 
 class PowerConnectionCSVForm(forms.ModelForm):
 class PowerConnectionCSVForm(forms.ModelForm):
     pdu = FlexibleModelChoiceField(
     pdu = FlexibleModelChoiceField(
-        queryset=Device.objects.filter(device_type__is_pdu=True),
+        queryset=Device.objects.all(),
         to_field_name='name',
         to_field_name='name',
         help_text='PDU name or ID',
         help_text='PDU name or ID',
         error_messages={
         error_messages={
@@ -1664,7 +1649,7 @@ class PowerPortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor
         label='PDU',
         label='PDU',
         required=False,
         required=False,
         widget=APISelect(
         widget=APISelect(
-            api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}&is_pdu=True',
+            api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
             display_field='display_name',
             display_field='display_name',
             attrs={'filter-for': 'connected_endpoint'}
             attrs={'filter-for': 'connected_endpoint'}
         )
         )

+ 25 - 0
netbox/dcim/migrations/0067_device_type_remove_qualifiers.py

@@ -0,0 +1,25 @@
+# Generated by Django 2.0.8 on 2018-10-26 17:49
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0066_cables'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='devicetype',
+            name='is_console_server',
+        ),
+        migrations.RemoveField(
+            model_name='devicetype',
+            name='is_network_device',
+        ),
+        migrations.RemoveField(
+            model_name='devicetype',
+            name='is_pdu',
+        ),
+    ]

+ 2 - 68
netbox/dcim/models.py

@@ -770,21 +770,6 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
         choices=IFACE_ORDERING_CHOICES,
         choices=IFACE_ORDERING_CHOICES,
         default=IFACE_ORDERING_POSITION
         default=IFACE_ORDERING_POSITION
     )
     )
-    is_console_server = models.BooleanField(
-        default=False,
-        verbose_name='Is a console server',
-        help_text='This type of device has console server ports'
-    )
-    is_pdu = models.BooleanField(
-        default=False,
-        verbose_name='Is a PDU',
-        help_text='This type of device has power outlets'
-    )
-    is_network_device = models.BooleanField(
-        default=True,
-        verbose_name='Is a network device',
-        help_text='This type of device has network interfaces'
-    )
     subdevice_role = models.NullBooleanField(
     subdevice_role = models.NullBooleanField(
         default=None,
         default=None,
         verbose_name='Parent/child status',
         verbose_name='Parent/child status',
@@ -804,8 +789,8 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
     tags = TaggableManager()
     tags = TaggableManager()
 
 
     csv_headers = [
     csv_headers = [
-        'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
-        'is_network_device', 'subdevice_role', 'interface_ordering', 'comments',
+        'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
+        'interface_ordering', 'comments',
     ]
     ]
 
 
     class Meta:
     class Meta:
@@ -835,9 +820,6 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
             self.part_number,
             self.part_number,
             self.u_height,
             self.u_height,
             self.is_full_depth,
             self.is_full_depth,
-            self.is_console_server,
-            self.is_pdu,
-            self.is_network_device,
             self.get_subdevice_role_display() if self.subdevice_role else None,
             self.get_subdevice_role_display() if self.subdevice_role else None,
             self.get_interface_ordering_display(),
             self.get_interface_ordering_display(),
             self.comments,
             self.comments,
@@ -859,24 +841,6 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
                                     "{}U".format(d, d.rack, self.u_height)
                                     "{}U".format(d, d.rack, self.u_height)
                     })
                     })
 
 
-        if not self.is_console_server and self.cs_port_templates.count():
-            raise ValidationError({
-                'is_console_server': "Must delete all console server port templates associated with this device before "
-                                     "declassifying it as a console server."
-            })
-
-        if not self.is_pdu and self.power_outlet_templates.count():
-            raise ValidationError({
-                'is_pdu': "Must delete all power outlet templates associated with this device before declassifying it "
-                          "as a PDU."
-            })
-
-        if not self.is_network_device and self.interface_templates.filter(mgmt_only=False).count():
-            raise ValidationError({
-                'is_network_device': "Must delete all non-management-only interface templates associated with this "
-                                     "device before declassifying it as a network device."
-            })
-
         if self.subdevice_role != SUBDEVICE_ROLE_PARENT and self.device_bay_templates.count():
         if self.subdevice_role != SUBDEVICE_ROLE_PARENT and self.device_bay_templates.count():
             raise ValidationError({
             raise ValidationError({
                 'subdevice_role': "Must delete all device bay templates associated with this device before "
                 'subdevice_role': "Must delete all device bay templates associated with this device before "
@@ -1705,17 +1669,6 @@ class ConsoleServerPort(CableTermination, ComponentModel):
     def get_absolute_url(self):
     def get_absolute_url(self):
         return self.device.get_absolute_url()
         return self.device.get_absolute_url()
 
 
-    def clean(self):
-
-        # Check that the parent device's DeviceType is a console server
-        if self.device is None:
-            raise ValidationError("Console server ports must be assigned to devices.")
-        device_type = self.device.device_type
-        if not device_type.is_console_server:
-            raise ValidationError("The {} {} device type does not support assignment of console server ports.".format(
-                device_type.manufacturer, device_type
-            ))
-
 
 
 #
 #
 # Power ports
 # Power ports
@@ -1808,17 +1761,6 @@ class PowerOutlet(CableTermination, ComponentModel):
     def get_absolute_url(self):
     def get_absolute_url(self):
         return self.device.get_absolute_url()
         return self.device.get_absolute_url()
 
 
-    def clean(self):
-
-        # Check that the parent device's DeviceType is a PDU
-        if self.device is None:
-            raise ValidationError("Power outlets must be assigned to devices.")
-        device_type = self.device.device_type
-        if not device_type.is_pdu:
-            raise ValidationError("The {} {} device type does not support assignment of power outlets.".format(
-                device_type.manufacturer, device_type
-            ))
-
 
 
 #
 #
 # Interfaces
 # Interfaces
@@ -1927,14 +1869,6 @@ class Interface(CableTermination, ComponentModel):
 
 
     def clean(self):
     def clean(self):
 
 
-        # Check that the parent device's DeviceType is a network device
-        if self.device is not None:
-            device_type = self.device.device_type
-            if not device_type.is_network_device:
-                raise ValidationError("The {} {} device type does not support assignment of network interfaces.".format(
-                    device_type.manufacturer, device_type
-                ))
-
         # An Interface must belong to a Device *or* to a VirtualMachine
         # An Interface must belong to a Device *or* to a VirtualMachine
         if self.device and self.virtual_machine:
         if self.device and self.virtual_machine:
             raise ValidationError("An interface cannot belong to both a device and a virtual machine.")
             raise ValidationError("An interface cannot belong to both a device and a virtual machine.")

+ 2 - 5
netbox/dcim/tables.py

@@ -349,9 +349,6 @@ class DeviceTypeTable(BaseTable):
         verbose_name='Device Type'
         verbose_name='Device Type'
     )
     )
     is_full_depth = BooleanColumn(verbose_name='Full Depth')
     is_full_depth = BooleanColumn(verbose_name='Full Depth')
-    is_console_server = BooleanColumn(verbose_name='CS')
-    is_pdu = BooleanColumn(verbose_name='PDU')
-    is_network_device = BooleanColumn(verbose_name='Net')
     subdevice_role = tables.TemplateColumn(
     subdevice_role = tables.TemplateColumn(
         template_code=SUBDEVICE_ROLE_TEMPLATE,
         template_code=SUBDEVICE_ROLE_TEMPLATE,
         verbose_name='Subdevice Role'
         verbose_name='Subdevice Role'
@@ -364,8 +361,8 @@ class DeviceTypeTable(BaseTable):
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = DeviceType
         model = DeviceType
         fields = (
         fields = (
-            'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
-            'is_network_device', 'subdevice_role', 'instance_count',
+            'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
+            'instance_count',
         )
         )
 
 
 
 

+ 3 - 3
netbox/dcim/tests/test_api.py

@@ -2035,7 +2035,7 @@ class ConsoleServerPortTest(APITestCase):
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         devicetype = DeviceType.objects.create(
         devicetype = DeviceType.objects.create(
-            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1', is_console_server=True
+            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
         )
         )
         devicerole = DeviceRole.objects.create(
         devicerole = DeviceRole.objects.create(
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
@@ -2261,7 +2261,7 @@ class PowerOutletTest(APITestCase):
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         devicetype = DeviceType.objects.create(
         devicetype = DeviceType.objects.create(
-            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1', is_pdu=True
+            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
         )
         )
         devicerole = DeviceRole.objects.create(
         devicerole = DeviceRole.objects.create(
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
@@ -2372,7 +2372,7 @@ class InterfaceTest(APITestCase):
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         site = Site.objects.create(name='Test Site 1', slug='test-site-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         devicetype = DeviceType.objects.create(
         devicetype = DeviceType.objects.create(
-            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1', is_network_device=True
+            manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
         )
         )
         devicerole = DeviceRole.objects.create(
         devicerole = DeviceRole.objects.create(
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'

+ 48 - 73
netbox/templates/dcim/device.html

@@ -35,6 +35,21 @@
     </div>
     </div>
     <div class="pull-right">
     <div class="pull-right">
         {% if perms.dcim.change_device %}
         {% if perms.dcim.change_device %}
+            <div class="btn-group">
+                <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu">
+                    {% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:consoleport_add' pk=device.pk %}">Console Ports</a></li>{% endif %}
+                    {% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:consoleserverport_add' pk=device.pk %}">Console Server Ports</a></li>{% endif %}
+                    {% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:powerport_add' pk=device.pk %}">Power Ports</a></li>{% endif %}
+                    {% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:poweroutlet_add' pk=device.pk %}">Power Outlets</a></li>{% endif %}
+                    {% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:interface_add' pk=device.pk %}">Interfaces</a></li>{% endif %}
+                    {% if perms.dcim.add_frontport %}<li><a href="{% url 'dcim:frontport_add' pk=device.pk %}">Front Ports</a></li>{% endif %}
+                    {% if perms.dcim.add_rearport %}<li><a href="{% url 'dcim:rearport_add' pk=device.pk %}">Rear Ports</a></li>{% endif %}
+                    {% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:devicebay_add' pk=device.pk %}">Device Bays</a></li>{% endif %}
+                </ul>
+            </div>
             <a href="{% url 'dcim:device_edit' pk=device.pk %}" class="btn btn-warning">
             <a href="{% url 'dcim:device_edit' pk=device.pk %}" class="btn btn-warning">
                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                 Edit this device
                 Edit this device
@@ -300,55 +315,35 @@
             </div>
             </div>
         </div>
         </div>
         <div class="col-md-6">
         <div class="col-md-6">
-            <div class="panel panel-default">
-                <div class="panel-heading">
-                    <strong>Console / Power</strong>
-                </div>
-                <table class="table table-hover panel-body component-list">
-                    {% for cp in console_ports %}
-                        {% include 'dcim/inc/consoleport.html' %}
-                    {% empty %}
-                        {% if device.device_type.console_port_templates.exists %}
-                            <tr>
-                                <td colspan="6" class="alert-warning">
-                                    <i class="fa fa-fw fa-warning"></i> No console ports defined
-                                    {% if perms.dcim.add_consoleport %}
-                                        <a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></a>
-                                    {% endif %}
-                                </td>
-                            </tr>
-                        {% endif %}
-                    {% endfor %}
-                    {% for pp in power_ports %}
-                        {% include 'dcim/inc/powerport.html' %}
-                    {% empty %}
-                        {% if device.device_type.power_port_templates.exists %}
-                            <tr>
-                                <td colspan="6" class="alert-warning">
-                                    <i class="fa fa-fw fa-warning"></i> No power ports defined
-                                    {% if perms.dcim.add_powerport %}
-                                        <a href="{% url 'dcim:powerport_add' pk=device.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></a>
-                                    {% endif %}
-                                </td>
-                            </tr>
-                        {% endif %}
-                    {% endfor %}
-                </table>
-                {% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
-                    <div class="panel-footer text-right">
-                        {% if perms.dcim.add_consoleport %}
-                            <a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
-                                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
-                            </a>
-                        {% endif %}
-                        {% if perms.dcim.add_powerport %}
-                            <a href="{% url 'dcim:powerport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
-                                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
-                            </a>
-                        {% endif %}
+            {% if console_ports or power_ports %}
+                <div class="panel panel-default">
+                    <div class="panel-heading">
+                        <strong>Console / Power</strong>
                     </div>
                     </div>
-                {% endif %}
-            </div>
+                    <table class="table table-hover panel-body component-list">
+                        {% for cp in console_ports %}
+                            {% include 'dcim/inc/consoleport.html' %}
+                        {% endfor %}
+                        {% for pp in power_ports %}
+                            {% include 'dcim/inc/powerport.html' %}
+                        {% endfor %}
+                    </table>
+                    {% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
+                        <div class="panel-footer text-right">
+                            {% if perms.dcim.add_consoleport %}
+                                <a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
+                                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
+                                </a>
+                            {% endif %}
+                            {% if perms.dcim.add_powerport %}
+                                <a href="{% url 'dcim:powerport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
+                                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
+                                </a>
+                            {% endif %}
+                        </div>
+                    {% endif %}
+                </div>
+            {% endif %}
             {% if request.user.is_authenticated %}
             {% if request.user.is_authenticated %}
                 <div class="panel panel-default">
                 <div class="panel panel-default">
                     <div class="panel-heading">
                     <div class="panel-heading">
@@ -501,7 +496,7 @@
                     </form>
                     </form>
                 {% endif %}
                 {% endif %}
             {% endif %}
             {% endif %}
-            {% if interfaces or device.device_type.is_network_device %}
+            {% if interfaces %}
                 {% if perms.dcim.change_interface or perms.dcim.delete_interface %}
                 {% if perms.dcim.change_interface or perms.dcim.delete_interface %}
                     <form method="post">
                     <form method="post">
                     {% csrf_token %}
                     {% csrf_token %}
@@ -534,10 +529,6 @@
                         <tbody>
                         <tbody>
                             {% for iface in interfaces %}
                             {% for iface in interfaces %}
                                 {% include 'dcim/inc/interface.html' %}
                                 {% include 'dcim/inc/interface.html' %}
-                            {% empty %}
-                                <tr>
-                                    <td colspan="9" class="text-center text-muted">&mdash; No interfaces defined &mdash;</td>
-                                </tr>
                             {% endfor %}
                             {% endfor %}
                         </tbody>
                         </tbody>
                     </table>
                     </table>
@@ -574,7 +565,7 @@
                     </form>
                     </form>
                 {% endif %}
                 {% endif %}
             {% endif %}
             {% endif %}
-            {% if consoleserverports or device.device_type.is_console_server %}
+            {% if consoleserverports %}
                 {% if perms.dcim.delete_consoleserverport %}
                 {% if perms.dcim.delete_consoleserverport %}
                     <form method="post">
                     <form method="post">
                     {% csrf_token %}
                     {% csrf_token %}
@@ -598,10 +589,6 @@
                         <tbody>
                         <tbody>
                             {% for csp in consoleserverports %}
                             {% for csp in consoleserverports %}
                                 {% include 'dcim/inc/consoleserverport.html' %}
                                 {% include 'dcim/inc/consoleserverport.html' %}
-                            {% empty %}
-                                <tr>
-                                    <td colspan="5" class="text-center text-muted">&mdash; No console server ports defined &mdash;</td>
-                                </tr>
                             {% endfor %}
                             {% endfor %}
                         </tbody>
                         </tbody>
                     </table>
                     </table>
@@ -633,7 +620,7 @@
                     </form>
                     </form>
                 {% endif %}
                 {% endif %}
             {% endif %}
             {% endif %}
-            {% if poweroutlets or device.device_type.is_pdu %}
+            {% if poweroutlets %}
                 {% if perms.dcim.delete_poweroutlet %}
                 {% if perms.dcim.delete_poweroutlet %}
                     <form method="post">
                     <form method="post">
                     {% csrf_token %}
                     {% csrf_token %}
@@ -657,10 +644,6 @@
                         <tbody>
                         <tbody>
                             {% for po in poweroutlets %}
                             {% for po in poweroutlets %}
                                 {% include 'dcim/inc/poweroutlet.html' %}
                                 {% include 'dcim/inc/poweroutlet.html' %}
-                            {% empty %}
-                                <tr>
-                                    <td colspan="5" class="text-center text-muted">&mdash; No power outlets defined &mdash;</td>
-                                </tr>
                             {% endfor %}
                             {% endfor %}
                         </tbody>
                         </tbody>
                     </table>
                     </table>
@@ -716,10 +699,6 @@
                             <tbody>
                             <tbody>
                                 {% for frontport in front_ports %}
                                 {% for frontport in front_ports %}
                                     {% include 'dcim/inc/frontport.html' %}
                                     {% include 'dcim/inc/frontport.html' %}
-                                {% empty %}
-                                    <tr>
-                                        <td colspan="5" class="text-center text-muted">&mdash; No front panel ports defined &mdash;</td>
-                                    </tr>
                                 {% endfor %}
                                 {% endfor %}
                             </tbody>
                             </tbody>
                         </table>
                         </table>
@@ -737,7 +716,7 @@
                             {% if perms.dcim.add_frontport %}
                             {% if perms.dcim.add_frontport %}
                                 <div class="pull-right">
                                 <div class="pull-right">
                                     <a href="{% url 'dcim:frontport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
                                     <a href="{% url 'dcim:frontport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
-                                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front panel ports
+                                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front ports
                                     </a>
                                     </a>
                                 </div>
                                 </div>
                                 <div class="clearfix"></div>
                                 <div class="clearfix"></div>
@@ -769,10 +748,6 @@
                             <tbody>
                             <tbody>
                                 {% for rearport in rear_ports %}
                                 {% for rearport in rear_ports %}
                                     {% include 'dcim/inc/rearport.html' %}
                                     {% include 'dcim/inc/rearport.html' %}
-                                {% empty %}
-                                    <tr>
-                                        <td colspan="5" class="text-center text-muted">&mdash; No rear panel ports defined &mdash;</td>
-                                    </tr>
                                 {% endfor %}
                                 {% endfor %}
                             </tbody>
                             </tbody>
                         </table>
                         </table>
@@ -790,7 +765,7 @@
                             {% if perms.dcim.add_rearport %}
                             {% if perms.dcim.add_rearport %}
                                 <div class="pull-right">
                                 <div class="pull-right">
                                     <a href="{% url 'dcim:rearport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
                                     <a href="{% url 'dcim:rearport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
-                                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear panel ports
+                                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear ports
                                     </a>
                                     </a>
                                 </div>
                                 </div>
                                 <div class="clearfix"></div>
                                 <div class="clearfix"></div>

+ 60 - 83
netbox/templates/dcim/devicetype.html

@@ -15,12 +15,27 @@
     </div>
     </div>
     {% if perms.dcim.change_devicetype or perms.dcim.delete_devicetype %}
     {% if perms.dcim.change_devicetype or perms.dcim.delete_devicetype %}
         <div class="pull-right">
         <div class="pull-right">
-          {% if perms.dcim.change_devicetype %}
+            {% if perms.dcim.change_devicetype %}
+                <div class="btn-group">
+                    <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
+                    </button>
+                    <ul class="dropdown-menu">
+                        {% if perms.dcim.add_consoleporttemplate %}<li><a href="{% url 'dcim:devicetype_add_consoleport' pk=devicetype.pk %}">Console Ports</a></li>{% endif %}
+                        {% if perms.dcim.add_consoleserverporttemplate %}<li><a href="{% url 'dcim:devicetype_add_consoleserverport' pk=devicetype.pk %}">Console Server Ports</a></li>{% endif %}
+                        {% if perms.dcim.add_powerporttemplate %}<li><a href="{% url 'dcim:devicetype_add_powerport' pk=devicetype.pk %}">Power Ports</a></li>{% endif %}
+                        {% if perms.dcim.add_poweroutlettemplate %}<li><a href="{% url 'dcim:devicetype_add_poweroutlet' pk=devicetype.pk %}">Power Outlets</a></li>{% endif %}
+                        {% if perms.dcim.add_interfacetemplate %}<li><a href="{% url 'dcim:devicetype_add_interface' pk=devicetype.pk %}">Interfaces</a></li>{% endif %}
+                        {% if perms.dcim.add_frontporttemplate %}<li><a href="{% url 'dcim:devicetype_add_frontport' pk=devicetype.pk %}">Front Ports</a></li>{% endif %}
+                        {% if perms.dcim.add_rearporttemplate %}<li><a href="{% url 'dcim:devicetype_add_rearport' pk=devicetype.pk %}">Rear Ports</a></li>{% endif %}
+                        {% if perms.dcim.add_devicebaytemplate %}<li><a href="{% url 'dcim:devicetype_add_devicebay' pk=devicetype.pk %}">Device Bays</a></li>{% endif %}
+                    </ul>
+                </div>
                 <a href="{% url 'dcim:devicetype_edit' pk=devicetype.pk %}" class="btn btn-warning">
                 <a href="{% url 'dcim:devicetype_edit' pk=devicetype.pk %}" class="btn btn-warning">
-                  <span class="fa fa-pencil" aria-hidden="true"></span>
-                  Edit this device type
+                    <span class="fa fa-pencil" aria-hidden="true"></span>
+                    Edit this device type
                 </a>
                 </a>
-          {% endif %}
+            {% endif %}
           {% if perms.dcim.delete_devicetype %}
           {% if perms.dcim.delete_devicetype %}
               <a href="{% url 'dcim:devicetype_delete' pk=devicetype.pk %}" class="btn btn-danger">
               <a href="{% url 'dcim:devicetype_delete' pk=devicetype.pk %}" class="btn btn-danger">
                 <span class="fa fa-trash" aria-hidden="true"></span>
                 <span class="fa fa-trash" aria-hidden="true"></span>
@@ -43,7 +58,7 @@
 
 
 {% block content %}
 {% block content %}
 <div class="row">
 <div class="row">
-    <div class="col-md-5">
+    <div class="col-md-6">
         <div class="panel panel-default">
         <div class="panel panel-default">
             <div class="panel-heading">
             <div class="panel-heading">
                 <strong>Chassis</strong>
                 <strong>Chassis</strong>
@@ -82,82 +97,29 @@
                     </td>
                     </td>
                 </tr>
                 </tr>
                 <tr>
                 <tr>
-                    <td>Interface Ordering</td>
-                    <td>{{ devicetype.get_interface_ordering_display }}</td>
-                </tr>
-                <tr>
-                    <td>Instances</td>
-                    <td><a href="{% url 'dcim:device_list' %}?device_type_id={{ devicetype.pk }}">{{ devicetype.instances.count }}</a></td>
-                </tr>
-            </table>
-        </div>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                <strong>Function</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                <tr>
-                    <td class="text-right">
-                        {% if devicetype.is_console_server %}
-                            <i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
-                        {% else %}
-                            <i class="glyphicon glyphicon-remove text-danger" title="No"></i>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <strong>Console Server</strong><br />
-                        <small class="text-muted">This device {% if devicetype.is_console_server %}has{% else %}does not have{% endif %} console server ports</small>
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-right">
-                        {% if devicetype.is_pdu %}
-                            <i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
-                        {% else %}
-                            <i class="glyphicon glyphicon-remove text-danger" title="No"></i>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <strong>PDU</strong><br />
-                        <small class="text-muted">This device {% if devicetype.is_pdu %}has{% else %}does not have{% endif %} power outlets</small>
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-right">
-                        {% if devicetype.is_network_device %}
-                            <i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
-                        {% else %}
-                            <i class="glyphicon glyphicon-remove text-danger" title="No"></i>
-                        {% endif %}
-                    </td>
+                    <td>Parent/Child</td>
                     <td>
                     <td>
-                        <strong>Network Device</strong><br />
-                        <small class="text-muted">This device {% if devicetype.is_network_device %}has{% else %}does not have{% endif %} network interfaces</small>
-                    </td>
-                </tr>
-                <tr>
-                    <td class="text-right">
                         {% if devicetype.subdevice_role == True %}
                         {% if devicetype.subdevice_role == True %}
                             <label class="label label-primary">Parent</label>
                             <label class="label label-primary">Parent</label>
                         {% elif devicetype.subdevice_role == False %}
                         {% elif devicetype.subdevice_role == False %}
                             <label class="label label-info">Child</label>
                             <label class="label label-info">Child</label>
                         {% else %}
                         {% else %}
-                            <label class="label label-default">None</label>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <strong>Parent/Child</strong><br />
-                        {% if devicetype.subdevice_role == True %}
-                            <small class="text-muted">This device has device bays for mounting child devices</small>
-                        {% elif devicetype.subdevice_role == False %}
-                            <small class="text-muted">This device can only be mounted in a parent device</small>
-                        {% else %}
-                            <small class="text-muted">This device does not have device bays</small>
+                            <span class="text-muted">N/A</span>
                         {% endif %}
                         {% endif %}
                     </td>
                     </td>
                 </tr>
                 </tr>
+                <tr>
+                    <td>Interface Ordering</td>
+                    <td>{{ devicetype.get_interface_ordering_display }}</td>
+                </tr>
+                <tr>
+                    <td>Instances</td>
+                    <td><a href="{% url 'dcim:device_list' %}?device_type_id={{ devicetype.pk }}">{{ devicetype.instances.count }}</a></td>
+                </tr>
             </table>
             </table>
         </div>
         </div>
+    </div>
+    <div class="col-md-6">
         {% include 'inc/custom_fields_panel.html' with obj=devicetype %}
         {% include 'inc/custom_fields_panel.html' with obj=devicetype %}
         {% include 'extras/inc/tags_panel.html' with tags=devicetype.tags.all url='dcim:devicetype_list' %}
         {% include 'extras/inc/tags_panel.html' with tags=devicetype.tags.all url='dcim:devicetype_list' %}
         <div class="panel panel-default">
         <div class="panel panel-default">
@@ -173,23 +135,38 @@
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>
-    <div class="col-md-7">
-        {% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:devicetype_add_consoleport' delete_url='dcim:devicetype_delete_consoleport' %}
-        {% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:devicetype_add_powerport' delete_url='dcim:devicetype_delete_powerport' %}
-        {% if devicetype.is_parent_device or devicebay_table.rows %}
+</div>
+{% if devicetype.console_port_templates.exists or devicetype.power_port_templates.exists %}
+    <div class="row">
+        <div class="col-md-6">
+            {% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:devicetype_add_consoleport' delete_url='dcim:devicetype_delete_consoleport' %}
+        </div>
+        <div class="col-md-6">
+             {% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:devicetype_add_powerport' delete_url='dcim:devicetype_delete_powerport' %}
+        </div>
+    </div>
+{% endif %}
+{% if devicetype.is_parent_device or devicebay_table.rows %}
+    <div class="row">
+        <div class="col-md-12">
             {% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' add_url='dcim:devicetype_add_devicebay' delete_url='dcim:devicetype_delete_devicebay' %}
             {% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' add_url='dcim:devicetype_add_devicebay' delete_url='dcim:devicetype_delete_devicebay' %}
-        {% endif %}
-        {% if devicetype.is_network_device or interface_table.rows %}
-            {% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' add_url='dcim:devicetype_add_interface' edit_url='dcim:devicetype_bulkedit_interface' delete_url='dcim:devicetype_delete_interface' %}
-        {% endif %}
-        {% if devicetype.is_console_server or consoleserverport_table.rows %}
+        </div>
+    </div>
+{% endif %}
+{% if devicetype.cs_port_templates.exists %}
+    <div class="row">
+        <div class="col-md-12">
             {% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:devicetype_add_consoleserverport' delete_url='dcim:devicetype_delete_consoleserverport' %}
             {% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:devicetype_add_consoleserverport' delete_url='dcim:devicetype_delete_consoleserverport' %}
-        {% endif %}
-        {% if devicetype.is_pdu or poweroutlet_table.rows %}
+        </div>
+    </div>
+{% endif %}
+{% if devicetype.power_outlet_templates.exists %}
+    <div class="row">
+        <div class="col-md-12">
             {% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:devicetype_add_poweroutlet' delete_url='dcim:devicetype_delete_poweroutlet' %}
             {% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:devicetype_add_poweroutlet' delete_url='dcim:devicetype_delete_poweroutlet' %}
-        {% endif %}
+        </div>
     </div>
     </div>
-</div>
+{% endif %}
 {% if devicetype.front_port_templates.exists or devicetype.rear_port_templates.exists %}
 {% if devicetype.front_port_templates.exists or devicetype.rear_port_templates.exists %}
     <div class="row">
     <div class="row">
         <div class="col-md-6">
         <div class="col-md-6">

+ 0 - 8
netbox/templates/dcim/devicetype_edit.html

@@ -12,14 +12,6 @@
             {% render_field form.u_height %}
             {% render_field form.u_height %}
             {% render_field form.is_full_depth %}
             {% render_field form.is_full_depth %}
             {% render_field form.interface_ordering %}
             {% render_field form.interface_ordering %}
-        </div>
-    </div>
-    <div class="panel panel-default">
-        <div class="panel-heading"><strong>Function</strong></div>
-        <div class="panel-body">
-            {% render_field form.is_console_server %}
-            {% render_field form.is_pdu %}
-            {% render_field form.is_network_device %}
             {% render_field form.subdevice_role %}
             {% render_field form.subdevice_role %}
         </div>
         </div>
     </div>
     </div>