Просмотр исходного кода

Extend VLANGroup to support cluster/cluster group assignment

Jeremy Stretch 4 лет назад
Родитель
Сommit
c0c4eed3a8

+ 7 - 1
netbox/ipam/filters.py

@@ -552,6 +552,12 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
     rack = django_filters.NumberFilter(
         method='filter_scope'
     )
+    clustergroup = django_filters.NumberFilter(
+        method='filter_scope'
+    )
+    cluster = django_filters.NumberFilter(
+        method='filter_scope'
+    )
 
     class Meta:
         model = VLANGroup
@@ -559,7 +565,7 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
 
     def filter_scope(self, queryset, name, value):
         return queryset.filter(
-            scope_type=ContentType.objects.get(app_label='dcim', model=name),
+            scope_type=ContentType.objects.get(model=name),
             scope_id=value
         )
 

+ 25 - 9
netbox/ipam/forms.py

@@ -1,5 +1,4 @@
 from django import forms
-from django.contrib.contenttypes.models import ContentType
 from django.utils.translation import gettext as _
 
 from dcim.models import Device, Interface, Location, Rack, Region, Site, SiteGroup
@@ -14,7 +13,7 @@ from utilities.forms import (
     DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableIPAddressField, NumericArrayField,
     ReturnURLForm, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
 )
-from virtualization.models import Cluster, VirtualMachine, VMInterface
+from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
 from .choices import *
 from .constants import *
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
@@ -1153,17 +1152,28 @@ class VLANGroupForm(BootstrapMixin, CustomFieldModelForm):
             'location_id': '$location',
         }
     )
+    cluster_group = DynamicModelChoiceField(
+        queryset=ClusterGroup.objects.all(),
+        required=False,
+        initial_params={
+            'clusters': '$cluster'
+        }
+    )
+    cluster = DynamicModelChoiceField(
+        queryset=Cluster.objects.all(),
+        required=False,
+        query_params={
+            'group_id': '$cluster_group',
+        }
+    )
     slug = SlugField()
 
     class Meta:
         model = VLANGroup
         fields = [
-            'name', 'slug', 'description', 'region', 'site_group', 'site', 'location', 'rack',
+            'name', 'slug', 'description', 'region', 'site_group', 'site', 'location', 'rack', 'cluster_group',
+            'cluster',
         ]
-        fieldsets = (
-            ('VLAN Group', ('name', 'slug', 'description')),
-            ('Scope', ('region', 'site_group', 'site', 'location', 'rack')),
-        )
 
     def __init__(self, *args, **kwargs):
         instance = kwargs.get('instance')
@@ -1180,6 +1190,10 @@ class VLANGroupForm(BootstrapMixin, CustomFieldModelForm):
                 initial['site_group'] = instance.scope
             elif type(instance.scope) is Region:
                 initial['region'] = instance.scope
+            elif type(instance.scope) is Cluster:
+                initial['cluster'] = instance.scope
+            elif type(instance.scope) is ClusterGroup:
+                initial['cluster_group'] = instance.scope
 
             kwargs['initial'] = initial
 
@@ -1189,8 +1203,10 @@ class VLANGroupForm(BootstrapMixin, CustomFieldModelForm):
         super().clean()
 
         # Assign scope object
-        self.instance.scope = self.cleaned_data['rack'] or self.cleaned_data['location'] or self.cleaned_data['site'] \
-            or self.cleaned_data['site_group'] or self.cleaned_data['region'] or None
+        self.instance.scope = self.cleaned_data['rack'] or self.cleaned_data['location'] or \
+            self.cleaned_data['site'] or self.cleaned_data['site_group'] or \
+            self.cleaned_data['region'] or self.cleaned_data['cluster'] or \
+            self.cleaned_data['cluster_group'] or None
 
 
 class VLANGroupCSVForm(CustomFieldModelCSVForm):

+ 1 - 1
netbox/ipam/migrations/0045_vlangroup_scope.py

@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name='vlangroup',
             name='scope_type',
-            field=models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ['region', 'sitegroup', 'site', 'location', 'rack'])), null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'),
+            field=models.ForeignKey(blank=True, limit_choices_to=models.Q(model__in=['region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster']), null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'),
         ),
         migrations.AlterModelOptions(
             name='vlangroup',

+ 1 - 2
netbox/ipam/models/vlans.py

@@ -35,8 +35,7 @@ class VLANGroup(OrganizationalModel):
         to=ContentType,
         on_delete=models.CASCADE,
         limit_choices_to=Q(
-            app_label='dcim',
-            model__in=['region', 'sitegroup', 'site', 'location', 'rack']
+            model__in=['region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster']
         ),
         blank=True,
         null=True

+ 2 - 1
netbox/ipam/views.py

@@ -642,6 +642,7 @@ class VLANGroupListView(generic.ObjectListView):
 class VLANGroupEditView(generic.ObjectEditView):
     queryset = VLANGroup.objects.all()
     model_form = forms.VLANGroupForm
+    template_name = 'ipam/vlangroup_edit.html'
 
 
 class VLANGroupDeleteView(generic.ObjectDeleteView):
@@ -655,7 +656,7 @@ class VLANGroupBulkImportView(generic.BulkImportView):
 
 
 class VLANGroupBulkDeleteView(generic.BulkDeleteView):
-    queryset = VLANGroup.objects.prefetch_related('site').annotate(
+    queryset = VLANGroup.objects.annotate(
         vlan_count=count_related(VLAN, 'group')
     )
     filterset = filters.VLANGroupFilterSet

+ 49 - 0
netbox/templates/ipam/vlangroup_edit.html

@@ -0,0 +1,49 @@
+{% extends 'generic/object_edit.html' %}
+{% load form_helpers %}
+{% load helpers %}
+
+{% block form %}
+    <div class="panel panel-default">
+        <div class="panel-heading"><strong>VLAN Group</strong></div>
+        <div class="panel-body">
+            {% render_field form.name %}
+            {% render_field form.slug %}
+            {% render_field form.description %}
+        </div>
+    </div>
+    <div class="panel panel-default">
+        <div class="panel-heading">
+            <strong>Scope</strong>
+        </div>
+        <div class="panel-body">
+            {% with virtual_tab_active=form.initial.cluster %}
+                <ul class="nav nav-tabs" role="tablist">
+                    <li role="presentation"{% if not virtual_tab_active %} class="active"{% endif %}><a href="#physical" role="tab" data-toggle="tab">Physical</a></li>
+                    <li role="presentation"{% if virtual_tab_active %} class="active"{% endif %}><a href="#virtual" role="tab" data-toggle="tab">Virtual</a></li>
+                </ul>
+                <div class="tab-content">
+                    <div class="tab-pane{% if not virtual_tab_active %} active{% endif %}" id="physical">
+                        {% render_field form.region %}
+                        {% render_field form.site_group %}
+                        {% render_field form.site %}
+                        {% render_field form.location %}
+                        {% render_field form.rack %}
+                    </div>
+                    <div class="tab-pane{% if virtual_tab_active %} active{% endif %}" id="virtual">
+                        {% render_field form.cluster_group %}
+                        {% render_field form.cluster %}
+                    </div>
+                </div>
+                <span class="help-block">The VLAN group will be limited in scope to the most-specific object selected above.</span>
+            {% endwith %}
+        </div>
+    </div>
+    {% if form.custom_fields %}
+        <div class="panel panel-default">
+            <div class="panel-heading"><strong>Custom Fields</strong></div>
+            <div class="panel-body">
+                {% render_custom_fields form %}
+            </div>
+        </div>
+    {% endif %}
+{% endblock %}