Add config_template FK to Device

This commit is contained in:
Jeremy Stretch 2023-02-12 16:31:56 -05:00 committed by jeremystretch
parent 72536d28a7
commit 15be44d06b
15 changed files with 92 additions and 16 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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',
]

View File

@ -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')
)

View File

@ -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

View File

@ -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):

View File

@ -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. <code>00ff00</code>)')),
}
@ -438,6 +444,7 @@ class DeviceImportForm(BaseDeviceImportForm):
config_template = CSVModelChoiceField(
queryset=ConfigTemplate.objects.all(),
to_field_name='name',
required=False,
help_text=_('Config template')
)

View File

@ -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)

View File

@ -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',
]

View File

@ -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'),
),
]

View File

@ -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.

View File

@ -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')

View File

@ -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,
}

View File

@ -12,15 +12,15 @@
<table class="table table-hover attr-table">
<tr>
<th scope="row">Config Template</th>
<td>{{ object.config_template|linkify|placeholder }}</td>
<td>{{ config_template|linkify|placeholder }}</td>
</tr>
<tr>
<th scope="row">Data Source</th>
<td>{{ object.config_template.data_file.source|linkify|placeholder }}</td>
<td>{{ config_template.data_file.source|linkify|placeholder }}</td>
</tr>
<tr>
<th scope="row">Data File</th>
<td>{{ object.config_template.data_file|linkify|placeholder }}</td>
<td>{{ config_template.data_file|linkify|placeholder }}</td>
</tr>
</table>
</div>

View File

@ -42,6 +42,10 @@
<th scope="row">VM Role</th>
<td>{% checkmark object.vm_role %}</td>
</tr>
<tr>
<th scope="row">Config Template</th>
<td>{{ object.config_template|linkify|placeholder }}</td>
</tr>
</table>
</div>
</div>