Closes #9582: Enable assigning config contexts based on device location

This commit is contained in:
jeremystretch 2022-06-22 16:10:48 -04:00
parent 341615668b
commit 379880cd84
15 changed files with 138 additions and 86 deletions

View File

@ -5,9 +5,11 @@ Sometimes it is desirable to associate additional data with a group of devices o
* Region * Region
* Site group * Site group
* Site * Site
* Location (devices only)
* Device type (devices only) * Device type (devices only)
* Role * Role
* Platform * Platform
* Cluster type (VMs only)
* Cluster group (VMs only) * Cluster group (VMs only)
* Cluster (VMs only) * Cluster (VMs only)
* Tenant group * Tenant group

View File

@ -25,6 +25,7 @@
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping * [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
* [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results * [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results
* [#9166](https://github.com/netbox-community/netbox/issues/9166) - Add UI visibility toggle for custom fields * [#9166](https://github.com/netbox-community/netbox/issues/9166) - Add UI visibility toggle for custom fields
* [#9582](https://github.com/netbox-community/netbox/issues/9582) - Enable assigning config contexts based on device location
### Other Changes ### Other Changes
@ -45,6 +46,8 @@
* Added required `status` field (default value: `active`) * Added required `status` field (default value: `active`)
* dcim.Rack * dcim.Rack
* The `elevation` endpoint now includes half-height rack units, and utilizes decimal values for the ID and name of each unit * The `elevation` endpoint now includes half-height rack units, and utilizes decimal values for the ID and name of each unit
* extras.ConfigContext
* Added the `locations` many-to-many field to track the assignment of ConfigContexts to Locations
* extras.CustomField * extras.CustomField
* Added `group_name` and `ui_visibility` fields * Added `group_name` and `ui_visibility` fields
* ipam.IPAddress * ipam.IPAddress

View File

@ -5,10 +5,10 @@ from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers from rest_framework import serializers
from dcim.api.nested_serializers import ( from dcim.api.nested_serializers import (
NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedPlatformSerializer, NestedRegionSerializer, NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedLocationSerializer, NestedPlatformSerializer,
NestedSiteSerializer, NestedSiteGroupSerializer, NestedRegionSerializer, NestedSiteSerializer, NestedSiteGroupSerializer,
) )
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.choices import * from extras.choices import *
from extras.models import * from extras.models import *
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
@ -272,6 +272,12 @@ class ConfigContextSerializer(ValidatedModelSerializer):
required=False, required=False,
many=True many=True
) )
locations = SerializedPKRelatedField(
queryset=Location.objects.all(),
serializer=NestedLocationSerializer,
required=False,
many=True
)
device_types = SerializedPKRelatedField( device_types = SerializedPKRelatedField(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
serializer=NestedDeviceTypeSerializer, serializer=NestedDeviceTypeSerializer,
@ -331,8 +337,8 @@ class ConfigContextSerializer(ValidatedModelSerializer):
model = ConfigContext model = ConfigContext
fields = [ fields = [
'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites', 'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites',
'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'locations', 'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
'tenants', 'tags', 'data', 'created', 'last_updated', 'tenant_groups', 'tenants', 'tags', 'data', 'created', 'last_updated',
] ]

View File

@ -138,7 +138,7 @@ class JournalEntryViewSet(NetBoxModelViewSet):
class ConfigContextViewSet(NetBoxModelViewSet): class ConfigContextViewSet(NetBoxModelViewSet):
queryset = ConfigContext.objects.prefetch_related( queryset = ConfigContext.objects.prefetch_related(
'regions', 'site_groups', 'sites', 'roles', 'platforms', 'tenant_groups', 'tenants', 'regions', 'site_groups', 'sites', 'locations', 'roles', 'platforms', 'tenant_groups', 'tenants',
) )
serializer_class = serializers.ConfigContextSerializer serializer_class = serializers.ConfigContextSerializer
filterset_class = filtersets.ConfigContextFilterSet filterset_class = filtersets.ConfigContextFilterSet

View File

@ -3,7 +3,7 @@ from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
@ -255,6 +255,17 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
to_field_name='slug', to_field_name='slug',
label='Site (slug)', label='Site (slug)',
) )
location_id = django_filters.ModelMultipleChoiceFilter(
field_name='locations',
queryset=Location.objects.all(),
label='Location',
)
location = django_filters.ModelMultipleChoiceFilter(
field_name='locations__slug',
queryset=Location.objects.all(),
to_field_name='slug',
label='Location (slug)',
)
device_type_id = django_filters.ModelMultipleChoiceFilter( device_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_types', field_name='device_types',
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),

View File

@ -3,7 +3,7 @@ from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.choices import * from extras.choices import *
from extras.models import * from extras.models import *
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
@ -170,7 +170,7 @@ class TagFilterForm(FilterForm):
class ConfigContextFilterForm(FilterForm): class ConfigContextFilterForm(FilterForm):
fieldsets = ( fieldsets = (
(None, ('q', 'tag_id')), (None, ('q', 'tag_id')),
('Location', ('region_id', 'site_group_id', 'site_id')), ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id')),
('Device', ('device_type_id', 'platform_id', 'role_id')), ('Device', ('device_type_id', 'platform_id', 'role_id')),
('Cluster', ('cluster_type_id', 'cluster_group_id', 'cluster_id')), ('Cluster', ('cluster_type_id', 'cluster_group_id', 'cluster_id')),
('Tenant', ('tenant_group_id', 'tenant_id')) ('Tenant', ('tenant_group_id', 'tenant_id'))
@ -190,6 +190,11 @@ class ConfigContextFilterForm(FilterForm):
required=False, required=False,
label=_('Sites') label=_('Sites')
) )
location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(),
required=False,
label=_('Locations')
)
device_type_id = DynamicModelMultipleChoiceField( device_type_id = DynamicModelMultipleChoiceField(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
required=False, required=False,

View File

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.choices import * from extras.choices import *
from extras.models import * from extras.models import *
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
@ -166,6 +166,10 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
queryset=Site.objects.all(), queryset=Site.objects.all(),
required=False required=False
) )
locations = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(),
required=False
)
device_types = DynamicModelMultipleChoiceField( device_types = DynamicModelMultipleChoiceField(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
required=False required=False
@ -202,15 +206,22 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
queryset=Tag.objects.all(), queryset=Tag.objects.all(),
required=False required=False
) )
data = JSONField( data = JSONField()
label=''
fieldsets = (
('Config Context', ('name', 'weight', 'description', 'data', 'is_active')),
('Assignment', (
'regions', 'site_groups', 'sites', 'locations', 'device_types', 'roles', 'platforms', 'cluster_types',
'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags',
)),
) )
class Meta: class Meta:
model = ConfigContext model = ConfigContext
fields = ( fields = (
'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites', 'roles', 'device_types', 'name', 'weight', 'description', 'data', 'is_active', 'regions', 'site_groups', 'sites', 'locations',
'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data', 'roles', 'device_types', 'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups',
'tenants', 'tags',
) )

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.5 on 2022-06-22 19:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0156_location_status'),
('extras', '0075_customfield_ui_visibility'),
]
operations = [
migrations.AddField(
model_name='configcontext',
name='locations',
field=models.ManyToManyField(blank=True, related_name='+', to='dcim.location'),
),
]

View File

@ -1,5 +1,3 @@
from collections import OrderedDict
from django.core.validators import ValidationError from django.core.validators import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
@ -55,6 +53,11 @@ class ConfigContext(WebhooksMixin, ChangeLoggedModel):
related_name='+', related_name='+',
blank=True blank=True
) )
locations = models.ManyToManyField(
to='dcim.Location',
related_name='+',
blank=True
)
device_types = models.ManyToManyField( device_types = models.ManyToManyField(
to='dcim.DeviceType', to='dcim.DeviceType',
related_name='+', related_name='+',
@ -138,11 +141,10 @@ class ConfigContextModel(models.Model):
def get_config_context(self): def get_config_context(self):
""" """
Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs.
Return the rendered configuration context for a device or VM. Return the rendered configuration context for a device or VM.
""" """
data = {}
# Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs
data = OrderedDict()
if not hasattr(self, 'config_context_data'): if not hasattr(self, 'config_context_data'):
# The annotation is not available, so we fall back to manually querying for the config context objects # The annotation is not available, so we fall back to manually querying for the config context objects

View File

@ -19,8 +19,9 @@ class ConfigContextQuerySet(RestrictedQuerySet):
# `device_role` for Device; `role` for VirtualMachine # `device_role` for Device; `role` for VirtualMachine
role = getattr(obj, 'device_role', None) or obj.role role = getattr(obj, 'device_role', None) or obj.role
# Device type assignment is relevant only for Devices # Device type and location assignment is relevant only for Devices
device_type = getattr(obj, 'device_type', None) device_type = getattr(obj, 'device_type', None)
location = getattr(obj, 'location', None)
# Get assigned cluster, group, and type (if any) # Get assigned cluster, group, and type (if any)
cluster = getattr(obj, 'cluster', None) cluster = getattr(obj, 'cluster', None)
@ -42,6 +43,7 @@ class ConfigContextQuerySet(RestrictedQuerySet):
Q(regions__in=regions) | Q(regions=None), Q(regions__in=regions) | Q(regions=None),
Q(site_groups__in=sitegroups) | Q(site_groups=None), Q(site_groups__in=sitegroups) | Q(site_groups=None),
Q(sites=obj.site) | Q(sites=None), Q(sites=obj.site) | Q(sites=None),
Q(locations=location) | Q(locations=None),
Q(device_types=device_type) | Q(device_types=None), Q(device_types=device_type) | Q(device_types=None),
Q(roles=role) | Q(roles=None), Q(roles=role) | Q(roles=None),
Q(platforms=obj.platform) | Q(platforms=None), Q(platforms=obj.platform) | Q(platforms=None),
@ -114,6 +116,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
) )
if self.model._meta.model_name == 'device': if self.model._meta.model_name == 'device':
base_query.add((Q(locations=OuterRef('location')) | Q(locations=None)), Q.AND)
base_query.add((Q(device_types=OuterRef('device_type')) | Q(device_types=None)), Q.AND) base_query.add((Q(device_types=OuterRef('device_type')) | Q(device_types=None)), Q.AND)
base_query.add((Q(roles=OuterRef('device_role')) | Q(roles=None)), Q.AND) base_query.add((Q(roles=OuterRef('device_role')) | Q(roles=None)), Q.AND)
base_query.add((Q(sites=OuterRef('site')) | Q(sites=None)), Q.AND) base_query.add((Q(sites=OuterRef('site')) | Q(sites=None)), Q.AND)

View File

@ -167,8 +167,9 @@ class ConfigContextTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = ConfigContext model = ConfigContext
fields = ( fields = (
'pk', 'id', 'name', 'weight', 'is_active', 'description', 'regions', 'sites', 'roles', 'platforms', 'pk', 'id', 'name', 'weight', 'is_active', 'description', 'regions', 'sites', 'locations', 'roles',
'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'created', 'last_updated', 'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'created',
'last_updated',
) )
default_columns = ('pk', 'name', 'weight', 'is_active', 'description') default_columns = ('pk', 'name', 'weight', 'is_active', 'description')

View File

@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.test import TestCase from django.test import TestCase
from circuits.models import Provider from circuits.models import Provider
from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup from dcim.models import DeviceRole, DeviceType, Location, Manufacturer, Platform, Rack, Region, Site, SiteGroup
from extras.choices import JournalEntryKindChoices, ObjectChangeActionChoices from extras.choices import JournalEntryKindChoices, ObjectChangeActionChoices
from extras.filtersets import * from extras.filtersets import *
from extras.models import * from extras.models import *
@ -368,9 +368,9 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
def setUpTestData(cls): def setUpTestData(cls):
regions = ( regions = (
Region(name='Test Region 1', slug='test-region-1'), Region(name='Region 1', slug='region-1'),
Region(name='Test Region 2', slug='test-region-2'), Region(name='Region 2', slug='region-2'),
Region(name='Test Region 3', slug='test-region-3'), Region(name='Region 3', slug='region-3'),
) )
for r in regions: for r in regions:
r.save() r.save()
@ -384,12 +384,20 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
site_group.save() site_group.save()
sites = ( sites = (
Site(name='Test Site 1', slug='test-site-1'), Site(name='Site 1', slug='site-1'),
Site(name='Test Site 2', slug='test-site-2'), Site(name='Site 2', slug='site-2'),
Site(name='Test Site 3', slug='test-site-3'), Site(name='Site 3', slug='site-3'),
) )
Site.objects.bulk_create(sites) Site.objects.bulk_create(sites)
locations = (
Location(name='Location 1', slug='location-1', site=sites[0]),
Location(name='Location 2', slug='location-2', site=sites[1]),
Location(name='Location 3', slug='location-3', site=sites[2]),
)
for location in locations:
location.save()
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
device_types = ( device_types = (
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'), DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
@ -460,6 +468,7 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
c.regions.set([regions[i]]) c.regions.set([regions[i]])
c.site_groups.set([site_groups[i]]) c.site_groups.set([site_groups[i]])
c.sites.set([sites[i]]) c.sites.set([sites[i]])
c.locations.set([locations[i]])
c.device_types.set([device_types[i]]) c.device_types.set([device_types[i]])
c.roles.set([device_roles[i]]) c.roles.set([device_roles[i]])
c.platforms.set([platforms[i]]) c.platforms.set([platforms[i]])
@ -501,6 +510,13 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'site': [sites[0].slug, sites[1].slug]} params = {'site': [sites[0].slug, sites[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_location(self):
locations = Location.objects.all()[:2]
params = {'location_id': [locations[0].pk, locations[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'location': [locations[0].slug, locations[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_device_type(self): def test_device_type(self):
device_types = DeviceType.objects.all()[:2] device_types = DeviceType.objects.all()[:2]
params = {'device_type_id': [device_types[0].pk, device_types[1].pk]} params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}

View File

@ -1,6 +1,6 @@
from django.test import TestCase from django.test import TestCase
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site, SiteGroup from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
from extras.models import ConfigContext, Tag from extras.models import ConfigContext, Tag
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -29,7 +29,8 @@ class ConfigContextTest(TestCase):
self.devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') self.devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
self.region = Region.objects.create(name="Region") self.region = Region.objects.create(name="Region")
self.sitegroup = SiteGroup.objects.create(name="Site Group") self.sitegroup = SiteGroup.objects.create(name="Site Group")
self.site = Site.objects.create(name='Site-1', slug='site-1', region=self.region, group=self.sitegroup) self.site = Site.objects.create(name='Site 1', slug='site-1', region=self.region, group=self.sitegroup)
self.location = Location.objects.create(name='Location 1', slug='location-1', site=self.site)
self.platform = Platform.objects.create(name="Platform") self.platform = Platform.objects.create(name="Platform")
self.tenantgroup = TenantGroup.objects.create(name="Tenant Group") self.tenantgroup = TenantGroup.objects.create(name="Tenant Group")
self.tenant = Tenant.objects.create(name="Tenant", group=self.tenantgroup) self.tenant = Tenant.objects.create(name="Tenant", group=self.tenantgroup)
@ -40,7 +41,8 @@ class ConfigContextTest(TestCase):
name='Device 1', name='Device 1',
device_type=self.devicetype, device_type=self.devicetype,
device_role=self.devicerole, device_role=self.devicerole,
site=self.site site=self.site,
location=self.location
) )
def test_higher_weight_wins(self): def test_higher_weight_wins(self):
@ -144,15 +146,6 @@ class ConfigContextTest(TestCase):
self.assertEqual(self.device.get_config_context(), annotated_queryset[0].get_config_context()) self.assertEqual(self.device.get_config_context(), annotated_queryset[0].get_config_context())
def test_annotation_same_as_get_for_object_device_relations(self): def test_annotation_same_as_get_for_object_device_relations(self):
site_context = ConfigContext.objects.create(
name="site",
weight=100,
data={
"site": 1
}
)
site_context.sites.add(self.site)
region_context = ConfigContext.objects.create( region_context = ConfigContext.objects.create(
name="region", name="region",
weight=100, weight=100,
@ -169,6 +162,22 @@ class ConfigContextTest(TestCase):
} }
) )
sitegroup_context.site_groups.add(self.sitegroup) sitegroup_context.site_groups.add(self.sitegroup)
site_context = ConfigContext.objects.create(
name="site",
weight=100,
data={
"site": 1
}
)
site_context.sites.add(self.site)
location_context = ConfigContext.objects.create(
name="location",
weight=100,
data={
"location": 1
}
)
location_context.locations.add(self.location)
platform_context = ConfigContext.objects.create( platform_context = ConfigContext.objects.create(
name="platform", name="platform",
weight=100, weight=100,
@ -205,6 +214,7 @@ class ConfigContextTest(TestCase):
device = Device.objects.create( device = Device.objects.create(
name="Device 2", name="Device 2",
site=self.site, site=self.site,
location=self.location,
tenant=self.tenant, tenant=self.tenant,
platform=self.platform, platform=self.platform,
device_role=self.devicerole, device_role=self.devicerole,
@ -220,13 +230,6 @@ class ConfigContextTest(TestCase):
cluster_group = ClusterGroup.objects.create(name="Cluster Group") cluster_group = ClusterGroup.objects.create(name="Cluster Group")
cluster = Cluster.objects.create(name="Cluster", group=cluster_group, type=cluster_type) cluster = Cluster.objects.create(name="Cluster", group=cluster_group, type=cluster_type)
site_context = ConfigContext.objects.create(
name="site",
weight=100,
data={"site": 1}
)
site_context.sites.add(self.site)
region_context = ConfigContext.objects.create( region_context = ConfigContext.objects.create(
name="region", name="region",
weight=100, weight=100,
@ -241,6 +244,13 @@ class ConfigContextTest(TestCase):
) )
sitegroup_context.site_groups.add(self.sitegroup) sitegroup_context.site_groups.add(self.sitegroup)
site_context = ConfigContext.objects.create(
name="site",
weight=100,
data={"site": 1}
)
site_context.sites.add(self.site)
platform_context = ConfigContext.objects.create( platform_context = ConfigContext.objects.create(
name="platform", name="platform",
weight=100, weight=100,

View File

@ -281,6 +281,7 @@ class ConfigContextView(generic.ObjectView):
('Regions', instance.regions.all), ('Regions', instance.regions.all),
('Site Groups', instance.site_groups.all), ('Site Groups', instance.site_groups.all),
('Sites', instance.sites.all), ('Sites', instance.sites.all),
('Locations', instance.locations.all),
('Device Types', instance.device_types.all), ('Device Types', instance.device_types.all),
('Roles', instance.roles.all), ('Roles', instance.roles.all),
('Platforms', instance.platforms.all), ('Platforms', instance.platforms.all),
@ -311,7 +312,6 @@ class ConfigContextView(generic.ObjectView):
class ConfigContextEditView(generic.ObjectEditView): class ConfigContextEditView(generic.ObjectEditView):
queryset = ConfigContext.objects.all() queryset = ConfigContext.objects.all()
form = forms.ConfigContextForm form = forms.ConfigContextForm
template_name = 'extras/configcontext_edit.html'
class ConfigContextBulkEditView(generic.BulkEditView): class ConfigContextBulkEditView(generic.BulkEditView):

View File

@ -1,37 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load form_helpers %}
{% block form %}
<div class="card">
<h5 class="card-header">Config Context</h5>
<div class="card-body">
{% render_field form.name %}
{% render_field form.weight %}
{% render_field form.description %}
{% render_field form.is_active %}
</div>
</div>
<div class="card">
<h5 class="card-header">Assignment</h5>
<div class="card-body">
{% render_field form.regions %}
{% render_field form.site_groups %}
{% render_field form.sites %}
{% render_field form.device_types %}
{% render_field form.roles %}
{% render_field form.platforms %}
{% render_field form.cluster_types %}
{% render_field form.cluster_groups %}
{% render_field form.clusters %}
{% render_field form.tenant_groups %}
{% render_field form.tenants %}
{% render_field form.tags %}
</div>
</div>
<div class="card">
<h5 class="card-header">Data</h5>
<div class="card-body">
{% render_field form.data %}
</div>
</div>
{% endblock %}