Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions netbox/dcim/api/serializers_/devicetype_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
InventoryItemTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
)
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from netbox.api.serializers import ChangeLogMessageSerializer, CustomFieldModelSerializer, ValidatedModelSerializer
from utilities.api import get_serializer_for_model
from wireless.choices import *
from .devicetypes import DeviceTypeSerializer, ModuleTypeSerializer
Expand All @@ -31,7 +31,7 @@
)


class ComponentTemplateSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
class ComponentTemplateSerializer(ChangeLogMessageSerializer, CustomFieldModelSerializer, ValidatedModelSerializer):
pass


Expand All @@ -58,7 +58,7 @@ class Meta:
model = ConsolePortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
'description', 'created', 'last_updated',
'description', 'created', 'custom_fields', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand Down Expand Up @@ -86,7 +86,7 @@ class Meta:
model = ConsoleServerPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
'description', 'created', 'last_updated',
'description', 'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand Down Expand Up @@ -115,7 +115,7 @@ class Meta:
model = PowerPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
'maximum_draw', 'allocated_draw', 'description', 'created', 'last_updated',
'maximum_draw', 'allocated_draw', 'description', 'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand Down Expand Up @@ -155,7 +155,7 @@ class Meta:
model = PowerOutletTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
'power_port', 'feed_leg', 'description', 'created', 'last_updated',
'power_port', 'feed_leg', 'description', 'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand Down Expand Up @@ -202,6 +202,7 @@ class Meta:
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled',
'mgmt_only', 'description', 'bridge', 'poe_mode', 'poe_type', 'rf_role', 'created', 'last_updated',
'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand All @@ -225,7 +226,7 @@ class Meta:
model = RearPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color',
'positions', 'description', 'created', 'last_updated',
'positions', 'description', 'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand All @@ -250,7 +251,7 @@ class Meta:
model = FrontPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color',
'rear_port', 'rear_port_position', 'description', 'created', 'last_updated',
'rear_port', 'rear_port_position', 'description', 'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand All @@ -273,7 +274,7 @@ class Meta:
model = ModuleBayTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'position', 'description',
'created', 'last_updated',
'created', 'last_updated', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

Expand Down Expand Up @@ -321,7 +322,7 @@ class Meta:
fields = [
'id', 'url', 'display', 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer',
'part_id', 'description', 'component_type', 'component_id', 'component', 'created', 'last_updated',
'_depth',
'_depth', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', '_depth')

Expand Down
3 changes: 1 addition & 2 deletions netbox/dcim/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
from netbox.forms import NetBoxModelForm
from netbox.forms.mixins import ChangelogMessageMixin
from tenancy.forms import TenancyForm
from users.models import User
from utilities.forms import add_blank_choice, get_field_value
Expand Down Expand Up @@ -985,7 +984,7 @@ def clean_device(self):
# Device component templates
#

class ComponentTemplateForm(ChangelogMessageMixin, forms.ModelForm):
class ComponentTemplateForm(NetBoxModelForm):
device_type = DynamicModelChoiceField(
label=_('Device type'),
queryset=DeviceType.objects.all(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import utilities.json
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dcim', '0215_rackreservation_status'),
]

operations = [
migrations.AddField(
model_name='consoleporttemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='consoleserverporttemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='devicebaytemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='frontporttemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='interfacetemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='inventoryitemtemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='modulebaytemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='poweroutlettemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='powerporttemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='rearporttemplate',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
]
64 changes: 53 additions & 11 deletions netbox/dcim/models/device_component_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from dcim.constants import *
from dcim.models.mixins import InterfaceValidationMixin
from netbox.models import ChangeLoggedModel
from netbox.models.features import CustomFieldsMixin
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
from utilities.ordering import naturalize_interface
Expand All @@ -34,7 +35,7 @@
)


class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
class ComponentTemplateModel(CustomFieldsMixin, ChangeLoggedModel, TrackingModelMixin):
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
Expand Down Expand Up @@ -81,6 +82,19 @@ def instantiate(self, device):
"""
raise NotImplementedError()

def get_cloneable_custom_field_data(self):
"""
Return a dictionary of cloneable custom field values from this template.
"""
if not hasattr(self, 'custom_fields'):
return {}

cloneable_data = {}
for field in self.custom_fields:
if field.is_cloneable and field.name in self.custom_field_data:
cloneable_data[field.name] = self.custom_field_data[field.name]
return cloneable_data

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down Expand Up @@ -211,12 +225,14 @@ class Meta(ModularComponentTemplateModel.Meta):
verbose_name_plural = _('console port templates')

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

def to_yaml(self):
return {
Expand Down Expand Up @@ -246,12 +262,14 @@ class Meta(ModularComponentTemplateModel.Meta):
verbose_name_plural = _('console server port templates')

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component
instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand Down Expand Up @@ -296,14 +314,17 @@ class Meta(ModularComponentTemplateModel.Meta):
verbose_name_plural = _('power port templates')

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
maximum_draw=self.maximum_draw,
allocated_draw=self.allocated_draw,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def clean(self):
Expand Down Expand Up @@ -385,14 +406,17 @@ def instantiate(self, **kwargs):
power_port = PowerPort.objects.get(name=power_port_name, **kwargs)
else:
power_port = None
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
power_port=power_port,
feed_leg=self.feed_leg,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand Down Expand Up @@ -484,7 +508,7 @@ def clean(self):
})

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
Expand All @@ -495,6 +519,9 @@ def instantiate(self, **kwargs):
rf_role=self.rf_role,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand Down Expand Up @@ -589,7 +616,7 @@ def instantiate(self, **kwargs):
rear_port = RearPort.objects.get(name=rear_port_name, **kwargs)
else:
rear_port = None
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
Expand All @@ -598,6 +625,9 @@ def instantiate(self, **kwargs):
rear_port_position=self.rear_port_position,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand Down Expand Up @@ -641,14 +671,17 @@ class Meta(ModularComponentTemplateModel.Meta):
verbose_name_plural = _('rear port templates')

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
color=self.color,
positions=self.positions,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand Down Expand Up @@ -680,12 +713,15 @@ class Meta(ModularComponentTemplateModel.Meta):
verbose_name_plural = _('module bay templates')

def instantiate(self, **kwargs):
return self.component_model(
component = self.component_model(
name=self.name,
label=self.label,
position=self.position,
**kwargs
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def to_yaml(self):
Expand All @@ -708,11 +744,14 @@ class Meta(ComponentTemplateModel.Meta):
verbose_name_plural = _('device bay templates')

def instantiate(self, device):
return self.component_model(
component = self.component_model(
device=device,
name=self.name,
label=self.label
)
component.custom_field_data.update(self.get_cloneable_custom_field_data())
return component

instantiate.do_not_call_in_templates = True

def clean(self):
Expand Down Expand Up @@ -803,7 +842,7 @@ def instantiate(self, **kwargs):
component = model.objects.get(name=self.component.name, **kwargs)
else:
component = None
return self.component_model(
component_item = self.component_model(
parent=parent,
name=self.name,
label=self.label,
Expand All @@ -813,4 +852,7 @@ def instantiate(self, **kwargs):
part_id=self.part_id,
**kwargs
)
component_item.custom_field_data.update(self.get_cloneable_custom_field_data())
return component_item

instantiate.do_not_call_in_templates = True