diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index 8a0d32b33..dcd4f3ede 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -4,6 +4,7 @@ from django.db.models import Q from dcim.models import DeviceRole, Platform, Region, Site from tenancy.models import Tenant, TenantGroup +from virtualization.models import Cluster, ClusterGroup from .choices import * from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, Tag @@ -170,6 +171,22 @@ class ConfigContextFilterSet(django_filters.FilterSet): to_field_name='slug', label='Platform (slug)', ) + cluster_group_id = django_filters.ModelMultipleChoiceFilter( + field_name='cluster_groups', + queryset=ClusterGroup.objects.all(), + label='Cluster group', + ) + cluster_group = django_filters.ModelMultipleChoiceFilter( + field_name='cluster_groups__slug', + queryset=ClusterGroup.objects.all(), + to_field_name='slug', + label='Cluster group (slug)', + ) + cluster_id = django_filters.ModelMultipleChoiceFilter( + field_name='clusters', + queryset=Cluster.objects.all(), + label='Cluster', + ) tenant_group_id = django_filters.ModelMultipleChoiceFilter( field_name='tenant_groups', queryset=TenantGroup.objects.all(), diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index edde6c6c5..5c33c7c98 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -254,8 +254,8 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConfigContext fields = [ - 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenant_groups', - 'tenants', 'tags', 'data', + 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'cluster_groups', + 'clusters', 'tenant_groups', 'tenants', 'tags', 'data', ] widgets = { 'regions': APISelectMultiple( @@ -270,6 +270,12 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm): 'platforms': APISelectMultiple( api_url="/api/dcim/platforms/" ), + 'cluster_groups': APISelectMultiple( + api_url="/api/virtualization/cluster-groups/" + ), + 'clusters': APISelectMultiple( + api_url="/api/virtualization/clusters/" + ), 'tenant_groups': APISelectMultiple( api_url="/api/tenancy/tenant-groups/" ), @@ -340,6 +346,21 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form): value_field="slug", ) ) + cluster_group = FilterChoiceField( + queryset=TenantGroup.objects.all(), + to_field_name='slug', + widget=APISelectMultiple( + api_url="/api/virtualization/cluster-groups/", + value_field="slug", + ) + ) + cluster_id = FilterChoiceField( + queryset=Tenant.objects.all(), + label='Cluster', + widget=APISelectMultiple( + api_url="/api/virtualization/clusters/", + ) + ) tenant_group = FilterChoiceField( queryset=TenantGroup.objects.all(), to_field_name='slug', diff --git a/netbox/extras/migrations/0037_configcontexts_clusters.py b/netbox/extras/migrations/0037_configcontexts_clusters.py new file mode 100644 index 000000000..201aed94a --- /dev/null +++ b/netbox/extras/migrations/0037_configcontexts_clusters.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.8 on 2020-01-17 18:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualization', '0013_deterministic_ordering'), + ('extras', '0036_contenttype_filters_to_q_objects'), + ] + + operations = [ + migrations.AddField( + model_name='configcontext', + name='cluster_groups', + field=models.ManyToManyField(blank=True, related_name='_configcontext_cluster_groups_+', to='virtualization.ClusterGroup'), + ), + migrations.AddField( + model_name='configcontext', + name='clusters', + field=models.ManyToManyField(blank=True, related_name='_configcontext_clusters_+', to='virtualization.Cluster'), + ), + ] diff --git a/netbox/extras/models.py b/netbox/extras/models.py index a03494bb2..f247fe1c2 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -694,6 +694,16 @@ class ConfigContext(models.Model): related_name='+', blank=True ) + cluster_groups = models.ManyToManyField( + to='virtualization.ClusterGroup', + related_name='+', + blank=True + ) + clusters = models.ManyToManyField( + to='virtualization.Cluster', + related_name='+', + blank=True + ) tenant_groups = models.ManyToManyField( to='tenancy.TenantGroup', related_name='+', diff --git a/netbox/extras/querysets.py b/netbox/extras/querysets.py index 22ab489bd..812c66714 100644 --- a/netbox/extras/querysets.py +++ b/netbox/extras/querysets.py @@ -29,6 +29,10 @@ class ConfigContextQuerySet(QuerySet): # `device_role` for Device; `role` for VirtualMachine role = getattr(obj, 'device_role', None) or obj.role + # Virtualization cluster for VirtualMachine + cluster = getattr(obj, 'cluster', None) + cluster_group = getattr(cluster, 'group', None) + # Get the group of the assigned tenant, if any tenant_group = obj.tenant.group if obj.tenant else None @@ -44,6 +48,8 @@ class ConfigContextQuerySet(QuerySet): Q(sites=obj.site) | Q(sites=None), Q(roles=role) | Q(roles=None), Q(platforms=obj.platform) | Q(platforms=None), + Q(cluster_groups=cluster_group) | Q(cluster_groups=None), + Q(clusters=cluster) | Q(clusters=None), Q(tenant_groups=tenant_group) | Q(tenant_groups=None), Q(tenants=obj.tenant) | Q(tenants=None), Q(tags__slug__in=obj.tags.slugs()) | Q(tags=None), diff --git a/netbox/extras/tests/test_filters.py b/netbox/extras/tests/test_filters.py index 130f94298..5ef96faa2 100644 --- a/netbox/extras/tests/test_filters.py +++ b/netbox/extras/tests/test_filters.py @@ -7,6 +7,7 @@ from extras.constants import GRAPH_MODELS from extras.filters import * from extras.models import ConfigContext, ExportTemplate, Graph from tenancy.models import Tenant, TenantGroup +from virtualization.models import Cluster, ClusterGroup, ClusterType class GraphTestCase(TestCase): @@ -107,6 +108,21 @@ class ConfigContextTestCase(TestCase): ) Platform.objects.bulk_create(platforms) + cluster_groups = ( + ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'), + ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'), + ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'), + ) + ClusterGroup.objects.bulk_create(cluster_groups) + + cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') + clusters = ( + Cluster(name='Cluster 1', type=cluster_type), + Cluster(name='Cluster 2', type=cluster_type), + Cluster(name='Cluster 3', type=cluster_type), + ) + Cluster.objects.bulk_create(clusters) + tenant_groups = ( TenantGroup(name='Tenant Group 1', slug='tenant-group-1'), TenantGroup(name='Tenant Group 2', slug='tenant-group-2'), @@ -132,6 +148,8 @@ class ConfigContextTestCase(TestCase): c.sites.set([sites[i]]) c.roles.set([device_roles[i]]) c.platforms.set([platforms[i]]) + c.cluster_groups.set([cluster_groups[i]]) + c.clusters.set([clusters[i]]) c.tenant_groups.set([tenant_groups[i]]) c.tenants.set([tenants[i]]) @@ -173,6 +191,18 @@ class ConfigContextTestCase(TestCase): params = {'platform': [platforms[0].slug, platforms[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_cluster_group(self): + cluster_groups = ClusterGroup.objects.all()[:2] + params = {'cluster_group_id': [cluster_groups[0].pk, cluster_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'cluster_group': [cluster_groups[0].slug, cluster_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_cluster(self): + clusters = Cluster.objects.all()[:2] + params = {'cluster_id': [clusters[0].pk, clusters[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_tenant_group(self): tenant_groups = TenantGroup.objects.all()[:2] params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]} diff --git a/netbox/templates/extras/configcontext.html b/netbox/templates/extras/configcontext.html index 7cec3f403..f9ea26c2b 100644 --- a/netbox/templates/extras/configcontext.html +++ b/netbox/templates/extras/configcontext.html @@ -134,6 +134,34 @@ {% endif %} +