mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
DeviceType.subdevice_role to slug (#3569)
This commit is contained in:
@ -187,7 +187,7 @@ class ManufacturerSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer()
|
||||||
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
|
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, required=False, allow_null=True)
|
||||||
tags = TagListSerializerField(required=False)
|
tags = TagListSerializerField(required=False)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
@ -66,6 +66,26 @@ class RackStatusChoices(ChoiceSet):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# DeviceTypes
|
||||||
|
#
|
||||||
|
|
||||||
|
class SubdeviceRoleChoices(ChoiceSet):
|
||||||
|
|
||||||
|
ROLE_PARENT = 'parent'
|
||||||
|
ROLE_CHILD = 'child'
|
||||||
|
|
||||||
|
CHOICES = (
|
||||||
|
(ROLE_PARENT, 'Parent'),
|
||||||
|
(ROLE_CHILD, 'Child'),
|
||||||
|
)
|
||||||
|
|
||||||
|
LEGACY_MAP = {
|
||||||
|
ROLE_PARENT: True,
|
||||||
|
ROLE_CHILD: False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Devices
|
# Devices
|
||||||
#
|
#
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
# Parent/child device roles
|
|
||||||
SUBDEVICE_ROLE_PARENT = True
|
|
||||||
SUBDEVICE_ROLE_CHILD = False
|
|
||||||
SUBDEVICE_ROLE_CHOICES = (
|
|
||||||
(None, 'None'),
|
|
||||||
(SUBDEVICE_ROLE_PARENT, 'Parent'),
|
|
||||||
(SUBDEVICE_ROLE_CHILD, 'Child'),
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Numeric interface types
|
# Numeric interface types
|
||||||
#
|
#
|
||||||
|
@ -879,12 +879,10 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subdevice_role = forms.NullBooleanField(
|
subdevice_role = forms.MultipleChoiceField(
|
||||||
|
choices=add_blank_choice(SubdeviceRoleChoices),
|
||||||
required=False,
|
required=False,
|
||||||
label='Subdevice role',
|
widget=StaticSelect2Multiple()
|
||||||
widget=StaticSelect2(
|
|
||||||
choices=add_blank_choice(SUBDEVICE_ROLE_CHOICES)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
console_ports = forms.NullBooleanField(
|
console_ports = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -3382,7 +3380,7 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
|
|||||||
rack=device_bay.device.rack,
|
rack=device_bay.device.rack,
|
||||||
parent_bay__isnull=True,
|
parent_bay__isnull=True,
|
||||||
device_type__u_height=0,
|
device_type__u_height=0,
|
||||||
device_type__subdevice_role=SUBDEVICE_ROLE_CHILD
|
device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD
|
||||||
).exclude(pk=device_bay.device.pk)
|
).exclude(pk=device_bay.device.pk)
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
SUBDEVICE_ROLE_CHOICES = (
|
||||||
|
('true', 'parent'),
|
||||||
|
('false', 'child'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def devicetype_subdevicerole_to_slug(apps, schema_editor):
|
||||||
|
DeviceType = apps.get_model('dcim', 'DeviceType')
|
||||||
|
for boolean, slug in SUBDEVICE_ROLE_CHOICES:
|
||||||
|
DeviceType.objects.filter(subdevice_role=boolean).update(subdevice_role=slug)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
atomic = False
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0080_device_face_to_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='subdevice_role',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=50),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=devicetype_subdevicerole_to_slug
|
||||||
|
),
|
||||||
|
]
|
@ -919,12 +919,12 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
verbose_name='Is full depth',
|
verbose_name='Is full depth',
|
||||||
help_text='Device consumes both front and rear rack faces'
|
help_text='Device consumes both front and rear rack faces'
|
||||||
)
|
)
|
||||||
subdevice_role = models.NullBooleanField(
|
subdevice_role = models.CharField(
|
||||||
default=None,
|
max_length=50,
|
||||||
|
choices=SubdeviceRoleChoices,
|
||||||
verbose_name='Parent/child status',
|
verbose_name='Parent/child status',
|
||||||
choices=SUBDEVICE_ROLE_CHOICES,
|
help_text='Parent devices house child devices in device bays. Leave blank '
|
||||||
help_text='Parent devices house child devices in device bays. Select '
|
'if this device type is neither a parent nor a child.'
|
||||||
'"None" if this device type is neither a parent nor a child.'
|
|
||||||
)
|
)
|
||||||
comments = models.TextField(
|
comments = models.TextField(
|
||||||
blank=True
|
blank=True
|
||||||
@ -968,7 +968,7 @@ 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.get_subdevice_role_display() if self.subdevice_role else None,
|
self.get_subdevice_role_display(),
|
||||||
self.comments,
|
self.comments,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -988,13 +988,15 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
"{}U".format(d, d.rack, self.u_height)
|
"{}U".format(d, d.rack, self.u_height)
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.subdevice_role != SUBDEVICE_ROLE_PARENT and self.device_bay_templates.count():
|
if (
|
||||||
|
self.subdevice_role != SubdeviceRoleChoices.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 "
|
||||||
"declassifying it as a parent device."
|
"declassifying it as a parent device."
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.u_height and self.subdevice_role == SUBDEVICE_ROLE_CHILD:
|
if self.u_height and self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'u_height': "Child device types must be 0U."
|
'u_height': "Child device types must be 0U."
|
||||||
})
|
})
|
||||||
@ -1005,11 +1007,11 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_parent_device(self):
|
def is_parent_device(self):
|
||||||
return bool(self.subdevice_role)
|
return self.subdevice_role == SubdeviceRoleChoices.ROLE_PARENT
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_child_device(self):
|
def is_child_device(self):
|
||||||
return bool(self.subdevice_role is False)
|
return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplate(ComponentTemplateModel):
|
class ConsolePortTemplate(ComponentTemplateModel):
|
||||||
|
@ -156,10 +156,6 @@ DEVICE_PRIMARY_IP = """
|
|||||||
{{ record.primary_ip4.address.ip|default:"" }}
|
{{ record.primary_ip4.address.ip|default:"" }}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUBDEVICE_ROLE_TEMPLATE = """
|
|
||||||
{% if record.subdevice_role == True %}Parent{% elif record.subdevice_role == False %}Child{% else %}—{% endif %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
DEVICETYPE_INSTANCES_TEMPLATE = """
|
DEVICETYPE_INSTANCES_TEMPLATE = """
|
||||||
<a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
|
<a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
|
||||||
"""
|
"""
|
||||||
@ -391,10 +387,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')
|
||||||
subdevice_role = tables.TemplateColumn(
|
|
||||||
template_code=SUBDEVICE_ROLE_TEMPLATE,
|
|
||||||
verbose_name='Subdevice Role'
|
|
||||||
)
|
|
||||||
instance_count = tables.TemplateColumn(
|
instance_count = tables.TemplateColumn(
|
||||||
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
||||||
verbose_name='Instances'
|
verbose_name='Instances'
|
||||||
|
@ -3,6 +3,7 @@ from netaddr import IPNetwork
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||||
|
from dcim.choices import SubdeviceRoleChoices
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import (
|
from dcim.models import (
|
||||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||||
@ -2590,11 +2591,11 @@ class DeviceBayTest(APITestCase):
|
|||||||
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||||
self.devicetype1 = DeviceType.objects.create(
|
self.devicetype1 = DeviceType.objects.create(
|
||||||
manufacturer=manufacturer, model='Parent Device Type', slug='parent-device-type',
|
manufacturer=manufacturer, model='Parent Device Type', slug='parent-device-type',
|
||||||
subdevice_role=SUBDEVICE_ROLE_PARENT
|
subdevice_role=SubdeviceRoleChoices.ROLE_PARENT
|
||||||
)
|
)
|
||||||
self.devicetype2 = DeviceType.objects.create(
|
self.devicetype2 = DeviceType.objects.create(
|
||||||
manufacturer=manufacturer, model='Child Device Type', slug='child-device-type',
|
manufacturer=manufacturer, model='Child Device Type', slug='child-device-type',
|
||||||
subdevice_role=SUBDEVICE_ROLE_CHILD
|
subdevice_role=SubdeviceRoleChoices.ROLE_CHILD
|
||||||
)
|
)
|
||||||
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'
|
||||||
|
Reference in New Issue
Block a user