mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Merge pull request #3572 from frelon/cluster-tenant
Add tenancy to cluster
This commit is contained in:
@ -142,6 +142,10 @@
|
||||
<h2><a href="{% url 'virtualization:virtualmachine_list' %}?tenant={{ tenant.slug }}" class="btn {% if stats.virtualmachine_count %}btn-primary{% else %}btn-default{% endif %} btn-lg">{{ stats.virtualmachine_count }}</a></h2>
|
||||
<p>Virtual machines</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<h2><a href="{% url 'virtualization:cluster_list' %}?tenant={{ tenant.slug }}" class="btn {% if stats.cluster_count %}btn-primary{% else %}btn-default{% endif %} btn-lg">{{ stats.cluster_count }}</a></h2>
|
||||
<p>Clusters</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -83,6 +83,16 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tenant</td>
|
||||
<td>
|
||||
{% if cluster.tenant %}
|
||||
<a href="{{ cluster.tenant.get_absolute_url }}">{{ cluster.tenant }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Site</td>
|
||||
<td>
|
||||
|
@ -8,6 +8,7 @@
|
||||
{% render_field form.name %}
|
||||
{% render_field form.type %}
|
||||
{% render_field form.group %}
|
||||
{% render_field form.tenant %}
|
||||
{% render_field form.site %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,11 +31,12 @@ class TenantSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
||||
vlan_count = serializers.IntegerField(read_only=True)
|
||||
vrf_count = serializers.IntegerField(read_only=True)
|
||||
cluster_count = serializers.IntegerField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Tenant
|
||||
fields = [
|
||||
'id', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'circuit_count', 'device_count', 'ipaddress_count', 'prefix_count', 'rack_count',
|
||||
'site_count', 'virtualmachine_count', 'vlan_count', 'vrf_count',
|
||||
'site_count', 'virtualmachine_count', 'vlan_count', 'vrf_count', 'cluster_count',
|
||||
]
|
||||
|
@ -9,7 +9,7 @@ from ipam.models import IPAddress, Prefix, VLAN, VRF
|
||||
from utilities.views import (
|
||||
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
)
|
||||
from virtualization.models import VirtualMachine
|
||||
from virtualization.models import VirtualMachine, Cluster
|
||||
from . import filters, forms, tables
|
||||
from .models import Tenant, TenantGroup
|
||||
|
||||
@ -80,6 +80,7 @@ class TenantView(PermissionRequiredMixin, View):
|
||||
'vlan_count': VLAN.objects.filter(tenant=tenant).count(),
|
||||
'circuit_count': Circuit.objects.filter(tenant=tenant).count(),
|
||||
'virtualmachine_count': VirtualMachine.objects.filter(tenant=tenant).count(),
|
||||
'cluster_count': Cluster.objects.filter(tenant=tenant).count(),
|
||||
}
|
||||
|
||||
return render(request, 'tenancy/tenant.html', {
|
||||
|
@ -38,6 +38,7 @@ class ClusterGroupSerializer(ValidatedModelSerializer):
|
||||
class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
type = NestedClusterTypeSerializer()
|
||||
group = NestedClusterGroupSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
device_count = serializers.IntegerField(read_only=True)
|
||||
@ -46,7 +47,7 @@ class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
class Meta:
|
||||
model = Cluster
|
||||
fields = [
|
||||
'id', 'name', 'type', 'group', 'site', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'device_count', 'virtualmachine_count',
|
||||
]
|
||||
|
||||
|
@ -41,7 +41,7 @@ class ClusterGroupViewSet(ModelViewSet):
|
||||
|
||||
class ClusterViewSet(CustomFieldModelViewSet):
|
||||
queryset = Cluster.objects.prefetch_related(
|
||||
'type', 'group', 'site', 'tags'
|
||||
'type', 'group', 'tenant', 'site', 'tags'
|
||||
).annotate(
|
||||
device_count=get_subquery(Device, 'cluster'),
|
||||
virtualmachine_count=get_subquery(VirtualMachine, 'cluster')
|
||||
|
@ -4,6 +4,7 @@ from netaddr import EUI
|
||||
from netaddr.core import AddrFormatError
|
||||
|
||||
from dcim.models import DeviceRole, Interface, Platform, Region, Site
|
||||
from tenancy.models import Tenant
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from utilities.filters import (
|
||||
@ -56,6 +57,10 @@ class ClusterFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Cluster type (slug)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label="Tenant (ID)"
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
|
@ -86,7 +86,7 @@ class ClusterForm(BootstrapMixin, CustomFieldForm):
|
||||
class Meta:
|
||||
model = Cluster
|
||||
fields = [
|
||||
'name', 'type', 'group', 'site', 'comments', 'tags',
|
||||
'name', 'type', 'group', 'tenant', 'site', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'type': APISelect(
|
||||
@ -128,6 +128,15 @@ class ClusterCSVForm(forms.ModelForm):
|
||||
'invalid_choice': 'Invalid site name.',
|
||||
}
|
||||
)
|
||||
tenant = forms.ModelChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False,
|
||||
help_text='Name of assigned tenant',
|
||||
error_messages={
|
||||
'invalid_choice': 'Invalid tenant name'
|
||||
}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Cluster
|
||||
@ -153,6 +162,10 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
||||
api_url="/api/virtualization/cluster-groups/"
|
||||
)
|
||||
)
|
||||
tenant = forms.ModelChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False
|
||||
)
|
||||
site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False,
|
||||
@ -166,7 +179,7 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'group', 'site', 'comments',
|
||||
'group', 'site', 'comments', 'tenant',
|
||||
]
|
||||
|
||||
|
||||
@ -193,6 +206,15 @@ class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
null_label='-- None --',
|
||||
required=False,
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
|
18
netbox/virtualization/migrations/0010_cluster_add_tenant.py
Normal file
18
netbox/virtualization/migrations/0010_cluster_add_tenant.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tenancy', '0001_initial'),
|
||||
('virtualization', '0009_custom_tag_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='tenancy.Tenant'),
|
||||
),
|
||||
]
|
@ -103,6 +103,13 @@ class Cluster(ChangeLoggedModel, CustomFieldModel):
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='tenants',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
@ -150,6 +157,7 @@ class Cluster(ChangeLoggedModel, CustomFieldModel):
|
||||
self.type.name,
|
||||
self.group.name if self.group else None,
|
||||
self.site.name if self.site else None,
|
||||
self.tenant.name if self.tenant else None,
|
||||
self.comments,
|
||||
)
|
||||
|
||||
|
@ -84,13 +84,14 @@ class ClusterGroupTable(BaseTable):
|
||||
class ClusterTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||
device_count = tables.Column(accessor=Accessor('devices.count'), orderable=False, verbose_name='Devices')
|
||||
vm_count = tables.Column(accessor=Accessor('virtual_machines.count'), orderable=False, verbose_name='VMs')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Cluster
|
||||
fields = ('pk', 'name', 'type', 'group', 'site', 'device_count', 'vm_count')
|
||||
fields = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
|
||||
|
||||
|
||||
#
|
||||
|
@ -96,7 +96,7 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
|
||||
class ClusterListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'virtualization.view_cluster'
|
||||
queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
|
||||
queryset = Cluster.objects.prefetch_related('type', 'group', 'site', 'tenant')
|
||||
table = tables.ClusterTable
|
||||
filter = filters.ClusterFilter
|
||||
filter_form = forms.ClusterFilterForm
|
||||
|
Reference in New Issue
Block a user