1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #7784: Support cluster type assignment for config contexts

This commit is contained in:
jeremystretch
2021-12-23 14:20:03 -05:00
parent bffd22038b
commit 77dd684916
13 changed files with 115 additions and 45 deletions

View File

@ -43,6 +43,7 @@ FIELD_CHOICES = {
* [#7650](https://github.com/netbox-community/netbox/issues/7650) - Add support for local account password validation
* [#7681](https://github.com/netbox-community/netbox/issues/7681) - Add `service_id` field for provider networks
* [#7759](https://github.com/netbox-community/netbox/issues/7759) - Improved the user preferences form
* [#7784](https://github.com/netbox-community/netbox/issues/7784) - Support cluster type assignment for config contexts
* [#8168](https://github.com/netbox-community/netbox/issues/8168) - Add `min_vid` and `max_vid` fields to VLAN group
### Other Changes
@ -77,6 +78,8 @@ FIELD_CHOICES = {
* Added `module` field
* dcim.Site
* Removed the `asn`, `contact_name`, `contact_phone`, and `contact_email` fields
* extras.ConfigContext
* Add `cluster_types` field
* ipam.VLANGroup
* Added the `/availables-vlans/` endpoint
* Added the `min_vid` and `max_vid` fields

View File

@ -19,8 +19,10 @@ from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantG
from tenancy.models import Tenant, TenantGroup
from users.api.nested_serializers import NestedUserSerializer
from utilities.api import get_serializer_for_model
from virtualization.api.nested_serializers import NestedClusterGroupSerializer, NestedClusterSerializer
from virtualization.models import Cluster, ClusterGroup
from virtualization.api.nested_serializers import (
NestedClusterGroupSerializer, NestedClusterSerializer, NestedClusterTypeSerializer,
)
from virtualization.models import Cluster, ClusterGroup, ClusterType
from .nested_serializers import *
__all__ = (
@ -267,6 +269,12 @@ class ConfigContextSerializer(ValidatedModelSerializer):
required=False,
many=True
)
cluster_types = SerializedPKRelatedField(
queryset=ClusterType.objects.all(),
serializer=NestedClusterTypeSerializer,
required=False,
many=True
)
cluster_groups = SerializedPKRelatedField(
queryset=ClusterGroup.objects.all(),
serializer=NestedClusterGroupSerializer,
@ -302,8 +310,8 @@ class ConfigContextSerializer(ValidatedModelSerializer):
model = ConfigContext
fields = [
'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites',
'device_types', 'roles', 'platforms', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags',
'data', 'created', 'last_updated',
'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups',
'tenants', 'tags', 'data', 'created', 'last_updated',
]

View File

@ -7,7 +7,7 @@ from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGrou
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet
from tenancy.models import Tenant, TenantGroup
from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
from virtualization.models import Cluster, ClusterGroup
from virtualization.models import Cluster, ClusterGroup, ClusterType
from .choices import *
from .models import *
@ -279,6 +279,17 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
to_field_name='slug',
label='Platform (slug)',
)
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_types',
queryset=ClusterType.objects.all(),
label='Cluster type',
)
cluster_type = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_types__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
label='Cluster type (slug)',
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),

View File

@ -12,7 +12,7 @@ from utilities.forms import (
add_blank_choice, APISelectMultiple, ContentTypeChoiceField, ContentTypeMultipleChoiceField, DateTimePicker,
DynamicModelMultipleChoiceField, FilterForm, StaticSelect, StaticSelectMultiple, BOOLEAN_WITH_BLANK_CHOICES,
)
from virtualization.models import Cluster, ClusterGroup
from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = (
'ConfigContextFilterForm',
@ -158,7 +158,7 @@ class ConfigContextFilterForm(FilterForm):
['q', 'tag'],
['region_id', 'site_group_id', 'site_id'],
['device_type_id', 'platform_id', 'role_id'],
['cluster_group_id', 'cluster_id'],
['cluster_type_id', 'cluster_group_id', 'cluster_id'],
['tenant_group_id', 'tenant_id']
]
region_id = DynamicModelMultipleChoiceField(
@ -197,6 +197,12 @@ class ConfigContextFilterForm(FilterForm):
label=_('Platforms'),
fetch_trigger='open'
)
cluster_type_id = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(),
required=False,
label=_('Cluster types'),
fetch_trigger='open'
)
cluster_group_id = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(),
required=False,

View File

@ -10,7 +10,7 @@ from utilities.forms import (
add_blank_choice, BootstrapMixin, CommentField, ContentTypeChoiceField,
ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect,
)
from virtualization.models import Cluster, ClusterGroup
from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = (
'AddRemoveTagsForm',
@ -165,6 +165,10 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
queryset=Platform.objects.all(),
required=False
)
cluster_types = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(),
required=False
)
cluster_groups = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(),
required=False
@ -193,7 +197,7 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
model = ConfigContext
fields = (
'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites', 'roles', 'device_types',
'platforms', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
)

View File

@ -0,0 +1,17 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('virtualization', '0026_vminterface_bridge'),
('extras', '0066_customfield_name_validation'),
]
operations = [
migrations.AddField(
model_name='configcontext',
name='cluster_types',
field=models.ManyToManyField(blank=True, related_name='_extras_configcontext_cluster_types_+', to='virtualization.ClusterType'),
),
]

View File

@ -71,6 +71,11 @@ class ConfigContext(ChangeLoggedModel):
related_name='+',
blank=True
)
cluster_types = models.ManyToManyField(
to='virtualization.ClusterType',
related_name='+',
blank=True
)
cluster_groups = models.ManyToManyField(
to='virtualization.ClusterGroup',
related_name='+',

View File

@ -22,8 +22,9 @@ class ConfigContextQuerySet(RestrictedQuerySet):
# Device type assignment is relevant only for Devices
device_type = getattr(obj, 'device_type', None)
# Get assigned Cluster and ClusterGroup, if any
# Get assigned cluster, group, and type (if any)
cluster = getattr(obj, 'cluster', None)
cluster_type = getattr(cluster, 'type', None)
cluster_group = getattr(cluster, 'group', None)
# Get the group of the assigned tenant, if any
@ -44,6 +45,7 @@ class ConfigContextQuerySet(RestrictedQuerySet):
Q(device_types=device_type) | Q(device_types=None),
Q(roles=role) | Q(roles=None),
Q(platforms=obj.platform) | Q(platforms=None),
Q(cluster_types=cluster_type) | Q(cluster_types=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),
@ -93,6 +95,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
}
base_query = Q(
Q(platforms=OuterRef('platform')) | Q(platforms=None),
Q(cluster_types=OuterRef('cluster__type')) | Q(cluster_types=None),
Q(cluster_groups=OuterRef('cluster__group')) | Q(cluster_groups=None),
Q(clusters=OuterRef('cluster')) | Q(clusters=None),
Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None),

View File

@ -193,7 +193,7 @@ class ConfigContextTable(BaseTable):
model = ConfigContext
fields = (
'pk', 'id', 'name', 'weight', 'is_active', 'description', 'regions', 'sites', 'roles',
'platforms', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants',
'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants',
)
default_columns = ('pk', 'name', 'weight', 'is_active', 'description')

View File

@ -399,6 +399,13 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
)
Platform.objects.bulk_create(platforms)
cluster_types = (
ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
)
ClusterType.objects.bulk_create(cluster_types)
cluster_groups = (
ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
@ -406,11 +413,10 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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(name='Cluster 1', type=cluster_types[0]),
Cluster(name='Cluster 2', type=cluster_types[1]),
Cluster(name='Cluster 3', type=cluster_types[2]),
)
Cluster.objects.bulk_create(clusters)
@ -442,6 +448,7 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
c.device_types.set([device_types[i]])
c.roles.set([device_roles[i]])
c.platforms.set([platforms[i]])
c.cluster_types.set([cluster_types[i]])
c.cluster_groups.set([cluster_groups[i]])
c.clusters.set([clusters[i]])
c.tenant_groups.set([tenant_groups[i]])
@ -504,6 +511,13 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'cluster_group': [cluster_groups[0].slug, cluster_groups[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_cluster_type(self):
cluster_types = ClusterType.objects.all()[:2]
params = {'cluster_type_id': [cluster_types[0].pk, cluster_types[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'cluster_type': [cluster_types[0].slug, cluster_types[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]}

View File

@ -216,80 +216,77 @@ class ConfigContextTest(TestCase):
self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())
def test_annotation_same_as_get_for_object_virtualmachine_relations(self):
cluster_type = ClusterType.objects.create(name="Cluster Type")
cluster_group = ClusterGroup.objects.create(name="Cluster Group")
cluster = Cluster.objects.create(name="Cluster", group=cluster_group, type=cluster_type)
site_context = ConfigContext.objects.create(
name="site",
weight=100,
data={
"site": 1
}
data={"site": 1}
)
site_context.sites.add(self.site)
region_context = ConfigContext.objects.create(
name="region",
weight=100,
data={
"region": 1
}
data={"region": 1}
)
region_context.regions.add(self.region)
sitegroup_context = ConfigContext.objects.create(
name="sitegroup",
weight=100,
data={
"sitegroup": 1
}
data={"sitegroup": 1}
)
sitegroup_context.site_groups.add(self.sitegroup)
platform_context = ConfigContext.objects.create(
name="platform",
weight=100,
data={
"platform": 1
}
data={"platform": 1}
)
platform_context.platforms.add(self.platform)
tenant_group_context = ConfigContext.objects.create(
name="tenant group",
weight=100,
data={
"tenant_group": 1
}
data={"tenant_group": 1}
)
tenant_group_context.tenant_groups.add(self.tenantgroup)
tenant_context = ConfigContext.objects.create(
name="tenant",
weight=100,
data={
"tenant": 1
}
data={"tenant": 1}
)
tenant_context.tenants.add(self.tenant)
tag_context = ConfigContext.objects.create(
name="tag",
weight=100,
data={
"tag": 1
}
data={"tag": 1}
)
tag_context.tags.add(self.tag)
cluster_group = ClusterGroup.objects.create(name="Cluster Group")
cluster_type_context = ConfigContext.objects.create(
name="cluster type",
weight=100,
data={"cluster_type": 1}
)
cluster_type_context.cluster_types.add(cluster_type)
cluster_group_context = ConfigContext.objects.create(
name="cluster group",
weight=100,
data={
"cluster_group": 1
}
data={"cluster_group": 1}
)
cluster_group_context.cluster_groups.add(cluster_group)
cluster_type = ClusterType.objects.create(name="Cluster Type 1")
cluster = Cluster.objects.create(name="Cluster", group=cluster_group, type=cluster_type)
cluster_context = ConfigContext.objects.create(
name="cluster",
weight=100,
data={
"cluster": 1
}
data={"cluster": 1}
)
cluster_context.clusters.add(cluster)

View File

@ -285,6 +285,7 @@ class ConfigContextView(generic.ObjectView):
('Device Types', instance.device_types.all),
('Roles', instance.roles.all),
('Platforms', instance.platforms.all),
('Cluster Types', instance.cluster_types.all),
('Cluster Groups', instance.cluster_groups.all),
('Clusters', instance.clusters.all),
('Tenant Groups', instance.tenant_groups.all),

View File

@ -20,6 +20,7 @@
{% 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 %}