diff --git a/docs/models/dcim/device.md b/docs/models/dcim/device.md
index 33d07e07e..8f97b920b 100644
--- a/docs/models/dcim/device.md
+++ b/docs/models/dcim/device.md
@@ -72,6 +72,10 @@ The device's operational status.
A device may be associated with a particular [platform](./platform.md) to indicate its operating system. Note that only platforms assigned to the associated manufacturer (or to no manufacturer) will be available for selection.
+### Configuration Template
+
+The [configuration template](../extras/configtemplate.md) from which the configuration for this device can be rendered. If set, this will override any config template referenced by the device's role or platform.
+
### Primary IPv4 & IPv6 Addresses
Each device may designate one primary IPv4 address and/or one primary IPv6 address for management purposes.
diff --git a/docs/models/dcim/devicerole.md b/docs/models/dcim/devicerole.md
index e9bdc0fa6..786170f2b 100644
--- a/docs/models/dcim/devicerole.md
+++ b/docs/models/dcim/devicerole.md
@@ -19,3 +19,7 @@ The color used when displaying the role in the NetBox UI.
### VM Role
If selected, this role may be assigned to [virtual machines](../virtualization/virtualmachine.md)
+
+### Configuration Template
+
+The default [configuration template](../extras/configtemplate.md) for devices assigned to this role.
diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py
index ee72a0850..79b48bf65 100644
--- a/netbox/dcim/api/serializers.py
+++ b/netbox/dcim/api/serializers.py
@@ -606,8 +606,8 @@ class DeviceRoleSerializer(NetBoxModelSerializer):
class Meta:
model = DeviceRole
fields = [
- 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'device_count', 'virtualmachine_count',
+ 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py
index 954c6a2a5..484e983f0 100644
--- a/netbox/dcim/api/views.py
+++ b/netbox/dcim/api/views.py
@@ -366,7 +366,7 @@ class InventoryItemTemplateViewSet(NetBoxModelViewSet):
#
class DeviceRoleViewSet(NetBoxModelViewSet):
- queryset = DeviceRole.objects.prefetch_related('tags').annotate(
+ queryset = DeviceRole.objects.prefetch_related('config_template', 'tags').annotate(
device_count=count_related(Device, 'device_role'),
virtualmachine_count=count_related(VirtualMachine, 'role')
)
diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py
index f63ab79ff..9e0963fcc 100644
--- a/netbox/dcim/filtersets.py
+++ b/netbox/dcim/filtersets.py
@@ -777,6 +777,10 @@ class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeCompo
class DeviceRoleFilterSet(OrganizationalModelFilterSet):
+ config_template_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=ConfigTemplate.objects.all(),
+ label=_('Config template (ID)'),
+ )
class Meta:
model = DeviceRole
diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py
index 0edabb1f4..a5d895f66 100644
--- a/netbox/dcim/forms/bulk_edit.py
+++ b/netbox/dcim/forms/bulk_edit.py
@@ -455,6 +455,10 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect,
label=_('VM role')
)
+ config_template = DynamicModelChoiceField(
+ queryset=ConfigTemplate.objects.all(),
+ required=False
+ )
description = forms.CharField(
max_length=200,
required=False
@@ -462,9 +466,9 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
model = DeviceRole
fieldsets = (
- (None, ('color', 'vm_role', 'description')),
+ (None, ('color', 'vm_role', 'config_template', 'description')),
)
- nullable_fields = ('color', 'description')
+ nullable_fields = ('color', 'config_template', 'description')
class PlatformBulkEditForm(NetBoxModelBulkEditForm):
diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py
index 1371ea5d7..a7a21ba51 100644
--- a/netbox/dcim/forms/bulk_import.py
+++ b/netbox/dcim/forms/bulk_import.py
@@ -308,11 +308,17 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
class DeviceRoleImportForm(NetBoxModelImportForm):
+ config_template = CSVModelChoiceField(
+ queryset=ConfigTemplate.objects.all(),
+ to_field_name='name',
+ required=False,
+ help_text=_('Config template')
+ )
slug = SlugField()
class Meta:
model = DeviceRole
- fields = ('name', 'slug', 'color', 'vm_role', 'description', 'tags')
+ fields = ('name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags')
help_texts = {
'color': mark_safe(_('RGB color in hexadecimal (e.g. 00ff00
)')),
}
@@ -438,6 +444,7 @@ class DeviceImportForm(BaseDeviceImportForm):
config_template = CSVModelChoiceField(
queryset=ConfigTemplate.objects.all(),
to_field_name='name',
+ required=False,
help_text=_('Config template')
)
diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py
index 58f44d479..8eeca7484 100644
--- a/netbox/dcim/forms/filtersets.py
+++ b/netbox/dcim/forms/filtersets.py
@@ -569,6 +569,11 @@ class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
model = DeviceRole
+ config_template_id = DynamicModelMultipleChoiceField(
+ queryset=ConfigTemplate.objects.all(),
+ required=False,
+ label=_('Config template')
+ )
tag = TagFilterField(model)
diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py
index e71e772b6..320a45c91 100644
--- a/netbox/dcim/forms/model_forms.py
+++ b/netbox/dcim/forms/model_forms.py
@@ -417,18 +417,22 @@ class ModuleTypeForm(NetBoxModelForm):
class DeviceRoleForm(NetBoxModelForm):
+ config_template = DynamicModelChoiceField(
+ queryset=ConfigTemplate.objects.all(),
+ required=False
+ )
slug = SlugField()
fieldsets = (
('Device Role', (
- 'name', 'slug', 'color', 'vm_role', 'description', 'tags',
+ 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
)),
)
class Meta:
model = DeviceRole
fields = [
- 'name', 'slug', 'color', 'vm_role', 'description', 'tags',
+ 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
]
diff --git a/netbox/dcim/migrations/0170_devicerole_config_template.py b/netbox/dcim/migrations/0170_devicerole_config_template.py
new file mode 100644
index 000000000..95975f3b4
--- /dev/null
+++ b/netbox/dcim/migrations/0170_devicerole_config_template.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.6 on 2023-02-12 20:41
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('extras', '0086_configtemplate'),
+ ('dcim', '0169_device_configtemplate'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='devicerole',
+ name='config_template',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='device_roles', to='extras.configtemplate'),
+ ),
+ ]
diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py
index 59a6bfd3b..8b64fc8c5 100644
--- a/netbox/dcim/models/devices.py
+++ b/netbox/dcim/models/devices.py
@@ -410,6 +410,13 @@ class DeviceRole(OrganizationalModel):
verbose_name='VM Role',
help_text=_('Virtual machines may be assigned to this role')
)
+ config_template = models.ForeignKey(
+ to='extras.ConfigTemplate',
+ on_delete=models.PROTECT,
+ related_name='device_roles',
+ blank=True,
+ null=True
+ )
def get_absolute_url(self):
return reverse('dcim:devicerole', args=[self.pk])
@@ -869,6 +876,12 @@ class Device(PrimaryModel, ConfigContextModel):
def interfaces_count(self):
return self.vc_interfaces().count()
+ def get_config_template(self):
+ """
+ Return the appropriate ConfigTemplate (if any) for this Device.
+ """
+ return self.config_template or self.device_role.config_template
+
def get_vc_master(self):
"""
If this Device is a VirtualChassis member, return the VC master. Otherwise, return None.
diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py
index 1cbc388fb..5aeb64b07 100644
--- a/netbox/dcim/tables/devices.py
+++ b/netbox/dcim/tables/devices.py
@@ -86,6 +86,9 @@ class DeviceRoleTable(NetBoxTable):
)
color = columns.ColorColumn()
vm_role = columns.BooleanColumn()
+ config_template = tables.Column(
+ linkify=True
+ )
tags = columns.TagColumn(
url_name='dcim:devicerole_list'
)
@@ -93,8 +96,8 @@ class DeviceRoleTable(NetBoxTable):
class Meta(NetBoxTable.Meta):
model = models.DeviceRole
fields = (
- 'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'tags',
- 'actions', 'created', 'last_updated',
+ 'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'config_template', 'description',
+ 'slug', 'tags', 'actions', 'created', 'last_updated',
)
default_columns = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description')
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index 30ca6f1b9..164839d57 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -13,7 +13,7 @@ from django.views.generic import View
from circuits.models import Circuit, CircuitTermination
from extras.views import ObjectConfigContextView
-from ipam.models import ASN, IPAddress, Prefix, Service, VLAN, VLANGroup
+from ipam.models import ASN, IPAddress, Prefix, VLAN, VLANGroup
from ipam.tables import InterfaceVLANTable
from netbox.views import generic
from utilities.forms import ConfirmationForm
@@ -2008,16 +2008,20 @@ class DeviceRenderConfigView(generic.ObjectView):
)
def get_extra_context(self, request, instance):
+ # Compile context data
context_data = {
'device': instance,
}
context_data.update(**instance.get_config_context())
- if instance.config_template:
- rendered_config = instance.config_template.render(context=context_data)
+
+ # Render the config template
+ if config_template := instance.get_config_template():
+ rendered_config = config_template.render(context=context_data)
else:
rendered_config = None
return {
+ 'config_template': config_template,
'context_data': context_data,
'rendered_config': rendered_config,
}
diff --git a/netbox/templates/dcim/device/render_config.html b/netbox/templates/dcim/device/render_config.html
index 9ff6fbada..1571b56f3 100644
--- a/netbox/templates/dcim/device/render_config.html
+++ b/netbox/templates/dcim/device/render_config.html
@@ -12,15 +12,15 @@
Config Template | -{{ object.config_template|linkify|placeholder }} | +{{ config_template|linkify|placeholder }} |
---|---|---|
Data Source | -{{ object.config_template.data_file.source|linkify|placeholder }} | +{{ config_template.data_file.source|linkify|placeholder }} |
Data File | -{{ object.config_template.data_file|linkify|placeholder }} | +{{ config_template.data_file|linkify|placeholder }} |