mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Implemented tenancy for VRFs and VLANs
This commit is contained in:
@ -7,7 +7,12 @@ from .models import (
|
|||||||
|
|
||||||
@admin.register(VRF)
|
@admin.register(VRF)
|
||||||
class VRFAdmin(admin.ModelAdmin):
|
class VRFAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name', 'rd']
|
list_display = ['name', 'rd', 'tenant', 'enforce_unique']
|
||||||
|
list_filter = ['tenant']
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
qs = super(VRFAdmin, self).get_queryset(request)
|
||||||
|
return qs.select_related('tenant')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Role)
|
@admin.register(Role)
|
||||||
@ -67,10 +72,10 @@ class VLANGroupAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(VLAN)
|
@admin.register(VLAN)
|
||||||
class VLANAdmin(admin.ModelAdmin):
|
class VLANAdmin(admin.ModelAdmin):
|
||||||
list_display = ['site', 'vid', 'name', 'status', 'role']
|
list_display = ['site', 'vid', 'name', 'tenant', 'status', 'role']
|
||||||
list_filter = ['site', 'status', 'role']
|
list_filter = ['site', 'tenant', 'status', 'role']
|
||||||
search_fields = ['vid', 'name']
|
search_fields = ['vid', 'name']
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(VLANAdmin, self).get_queryset(request)
|
qs = super(VLANAdmin, self).get_queryset(request)
|
||||||
return qs.select_related('site', 'role')
|
return qs.select_related('site', 'tenant', 'role')
|
||||||
|
@ -3,6 +3,7 @@ from netaddr import IPNetwork
|
|||||||
from netaddr.core import AddrFormatError
|
from netaddr.core import AddrFormatError
|
||||||
|
|
||||||
from dcim.models import Site, Device, Interface
|
from dcim.models import Site, Device, Interface
|
||||||
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
from .models import RIR, Aggregate, VRF, Prefix, IPAddress, VLAN, VLANGroup, Role
|
from .models import RIR, Aggregate, VRF, Prefix, IPAddress, VLAN, VLANGroup, Role
|
||||||
|
|
||||||
@ -13,6 +14,17 @@ class VRFFilter(django_filters.FilterSet):
|
|||||||
lookup_type='icontains',
|
lookup_type='icontains',
|
||||||
label='Name',
|
label='Name',
|
||||||
)
|
)
|
||||||
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='tenant',
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
label='Tenant (ID)',
|
||||||
|
)
|
||||||
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='tenant',
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Tenant (slug)',
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
@ -226,6 +238,17 @@ class VLANFilter(django_filters.FilterSet):
|
|||||||
name='vid',
|
name='vid',
|
||||||
label='VLAN number (1-4095)',
|
label='VLAN number (1-4095)',
|
||||||
)
|
)
|
||||||
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='tenant',
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
label='Tenant (ID)',
|
||||||
|
)
|
||||||
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='tenant',
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Tenant (slug)',
|
||||||
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
name='role',
|
name='role',
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
|
@ -4,6 +4,7 @@ from django import forms
|
|||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from dcim.models import Site, Device, Interface
|
from dcim.models import Site, Device, Interface
|
||||||
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import BootstrapMixin, APISelect, Livesearch, CSVDataField, BulkImportForm, SlugField
|
from utilities.forms import BootstrapMixin, APISelect, Livesearch, CSVDataField, BulkImportForm, SlugField
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
@ -23,7 +24,7 @@ class VRFForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
fields = ['name', 'rd', 'enforce_unique', 'description']
|
fields = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
||||||
labels = {
|
labels = {
|
||||||
'rd': "RD",
|
'rd': "RD",
|
||||||
}
|
}
|
||||||
@ -33,10 +34,12 @@ class VRFForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
|
|
||||||
class VRFFromCSVForm(forms.ModelForm):
|
class VRFFromCSVForm(forms.ModelForm):
|
||||||
|
tenant = forms.ModelChoiceField(Tenant.objects.all(), to_field_name='name', required=False,
|
||||||
|
error_messages={'invalid_choice': 'Tenant not found.'})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
fields = ['name', 'rd', 'enforce_unique', 'description']
|
fields = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
||||||
|
|
||||||
|
|
||||||
class VRFImportForm(BulkImportForm, BootstrapMixin):
|
class VRFImportForm(BulkImportForm, BootstrapMixin):
|
||||||
@ -45,9 +48,20 @@ class VRFImportForm(BulkImportForm, BootstrapMixin):
|
|||||||
|
|
||||||
class VRFBulkEditForm(forms.Form, BootstrapMixin):
|
class VRFBulkEditForm(forms.Form, BootstrapMixin):
|
||||||
pk = forms.ModelMultipleChoiceField(queryset=VRF.objects.all(), widget=forms.MultipleHiddenInput)
|
pk = forms.ModelMultipleChoiceField(queryset=VRF.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
|
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
||||||
description = forms.CharField(max_length=100, required=False)
|
description = forms.CharField(max_length=100, required=False)
|
||||||
|
|
||||||
|
|
||||||
|
def vrf_tenant_choices():
|
||||||
|
tenant_choices = Tenant.objects.annotate(vrf_count=Count('vrfs'))
|
||||||
|
return [(t.slug, u'{} ({})'.format(t.name, t.vrf_count)) for t in tenant_choices]
|
||||||
|
|
||||||
|
|
||||||
|
class VRFFilterForm(forms.Form, BootstrapMixin):
|
||||||
|
tenant = forms.MultipleChoiceField(required=False, choices=vrf_tenant_choices,
|
||||||
|
widget=forms.SelectMultiple(attrs={'size': 8}))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
@ -444,7 +458,7 @@ class VLANForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ['site', 'group', 'vid', 'name', 'description', 'status', 'role']
|
fields = ['site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'site': "The site at which this VLAN exists",
|
'site': "The site at which this VLAN exists",
|
||||||
'group': "VLAN group (optional)",
|
'group': "VLAN group (optional)",
|
||||||
@ -475,13 +489,15 @@ class VLANFromCSVForm(forms.ModelForm):
|
|||||||
error_messages={'invalid_choice': 'Device not found.'})
|
error_messages={'invalid_choice': 'Device not found.'})
|
||||||
group = forms.ModelChoiceField(queryset=VLANGroup.objects.all(), required=False, to_field_name='name',
|
group = forms.ModelChoiceField(queryset=VLANGroup.objects.all(), required=False, to_field_name='name',
|
||||||
error_messages={'invalid_choice': 'VLAN group not found.'})
|
error_messages={'invalid_choice': 'VLAN group not found.'})
|
||||||
|
tenant = forms.ModelChoiceField(Tenant.objects.all(), to_field_name='name', required=False,
|
||||||
|
error_messages={'invalid_choice': 'Tenant not found.'})
|
||||||
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in VLAN_STATUS_CHOICES])
|
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in VLAN_STATUS_CHOICES])
|
||||||
role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False, to_field_name='name',
|
role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False, to_field_name='name',
|
||||||
error_messages={'invalid_choice': 'Invalid role.'})
|
error_messages={'invalid_choice': 'Invalid role.'})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ['site', 'group', 'vid', 'name', 'status_name', 'role', 'description']
|
fields = ['site', 'group', 'vid', 'name', 'tenant', 'status_name', 'role', 'description']
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
m = super(VLANFromCSVForm, self).save(commit=False)
|
m = super(VLANFromCSVForm, self).save(commit=False)
|
||||||
@ -500,6 +516,7 @@ class VLANBulkEditForm(forms.Form, BootstrapMixin):
|
|||||||
pk = forms.ModelMultipleChoiceField(queryset=VLAN.objects.all(), widget=forms.MultipleHiddenInput)
|
pk = forms.ModelMultipleChoiceField(queryset=VLAN.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False)
|
site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False)
|
||||||
group = forms.ModelChoiceField(queryset=VLANGroup.objects.all(), required=False)
|
group = forms.ModelChoiceField(queryset=VLANGroup.objects.all(), required=False)
|
||||||
|
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
||||||
status = forms.ChoiceField(choices=FORM_VLAN_STATUS_CHOICES, required=False)
|
status = forms.ChoiceField(choices=FORM_VLAN_STATUS_CHOICES, required=False)
|
||||||
role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False)
|
role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False)
|
||||||
description = forms.CharField(max_length=100, required=False)
|
description = forms.CharField(max_length=100, required=False)
|
||||||
@ -515,6 +532,11 @@ def vlan_group_choices():
|
|||||||
return [(g.pk, u'{} ({})'.format(g, g.vlan_count)) for g in group_choices]
|
return [(g.pk, u'{} ({})'.format(g, g.vlan_count)) for g in group_choices]
|
||||||
|
|
||||||
|
|
||||||
|
def vlan_tenant_choices():
|
||||||
|
tenant_choices = Tenant.objects.annotate(vrf_count=Count('vlans'))
|
||||||
|
return [(t.slug, u'{} ({})'.format(t.name, t.vrf_count)) for t in tenant_choices]
|
||||||
|
|
||||||
|
|
||||||
def vlan_status_choices():
|
def vlan_status_choices():
|
||||||
status_counts = {}
|
status_counts = {}
|
||||||
for status in VLAN.objects.values('status').annotate(count=Count('status')).order_by('status'):
|
for status in VLAN.objects.values('status').annotate(count=Count('status')).order_by('status'):
|
||||||
@ -532,6 +554,8 @@ class VLANFilterForm(forms.Form, BootstrapMixin):
|
|||||||
widget=forms.SelectMultiple(attrs={'size': 8}))
|
widget=forms.SelectMultiple(attrs={'size': 8}))
|
||||||
group_id = forms.MultipleChoiceField(required=False, choices=vlan_group_choices, label='VLAN Group',
|
group_id = forms.MultipleChoiceField(required=False, choices=vlan_group_choices, label='VLAN Group',
|
||||||
widget=forms.SelectMultiple(attrs={'size': 8}))
|
widget=forms.SelectMultiple(attrs={'size': 8}))
|
||||||
|
tenant = forms.MultipleChoiceField(required=False, choices=vlan_tenant_choices,
|
||||||
|
widget=forms.SelectMultiple(attrs={'size': 8}))
|
||||||
status = forms.MultipleChoiceField(required=False, choices=vlan_status_choices)
|
status = forms.MultipleChoiceField(required=False, choices=vlan_status_choices)
|
||||||
role = forms.MultipleChoiceField(required=False, choices=vlan_role_choices,
|
role = forms.MultipleChoiceField(required=False, choices=vlan_role_choices,
|
||||||
widget=forms.SelectMultiple(attrs={'size': 8}))
|
widget=forms.SelectMultiple(attrs={'size': 8}))
|
||||||
|
27
netbox/ipam/migrations/0006_vrf_vlan_add_tenant.py
Normal file
27
netbox/ipam/migrations/0006_vrf_vlan_add_tenant.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.8 on 2016-07-27 14:39
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tenancy', '0001_initial'),
|
||||||
|
('ipam', '0005_auto_20160725_1842'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vlan',
|
||||||
|
name='tenant',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='tenancy.Tenant'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vrf',
|
||||||
|
name='tenant',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vrfs', to='tenancy.Tenant'),
|
||||||
|
),
|
||||||
|
]
|
@ -7,6 +7,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
|
from tenancy.models import Tenant
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
|
|
||||||
from .fields import IPNetworkField, IPAddressField
|
from .fields import IPNetworkField, IPAddressField
|
||||||
@ -46,6 +47,7 @@ class VRF(CreatedUpdatedModel):
|
|||||||
"""
|
"""
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
rd = models.CharField(max_length=21, unique=True, verbose_name='Route distinguisher')
|
rd = models.CharField(max_length=21, unique=True, verbose_name='Route distinguisher')
|
||||||
|
tenant = models.ForeignKey(Tenant, related_name='vrfs', blank=True, null=True, on_delete=models.PROTECT)
|
||||||
enforce_unique = models.BooleanField(default=True, verbose_name='Enforce unique space',
|
enforce_unique = models.BooleanField(default=True, verbose_name='Enforce unique space',
|
||||||
help_text="Prevent duplicate prefixes/IP addresses within this VRF")
|
help_text="Prevent duplicate prefixes/IP addresses within this VRF")
|
||||||
description = models.CharField(max_length=100, blank=True)
|
description = models.CharField(max_length=100, blank=True)
|
||||||
@ -65,6 +67,8 @@ class VRF(CreatedUpdatedModel):
|
|||||||
return ','.join([
|
return ','.join([
|
||||||
self.name,
|
self.name,
|
||||||
self.rd,
|
self.rd,
|
||||||
|
self.tenant.name if self.tenant else '',
|
||||||
|
'True' if self.enforce_unique else '',
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -291,7 +295,7 @@ class Prefix(CreatedUpdatedModel):
|
|||||||
|
|
||||||
class IPAddress(CreatedUpdatedModel):
|
class IPAddress(CreatedUpdatedModel):
|
||||||
"""
|
"""
|
||||||
An IPAddress represents an individual IPV4 or IPv6 address and its mask. The mask length should match what is
|
An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
|
||||||
configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like
|
configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like
|
||||||
Prefixes, IPAddresses can optionally be assigned to a VRF. An IPAddress can optionally be assigned to an Interface.
|
Prefixes, IPAddresses can optionally be assigned to a VRF. An IPAddress can optionally be assigned to an Interface.
|
||||||
Interfaces can have zero or more IPAddresses assigned to them.
|
Interfaces can have zero or more IPAddresses assigned to them.
|
||||||
@ -407,9 +411,10 @@ class VLAN(CreatedUpdatedModel):
|
|||||||
MaxValueValidator(4094)
|
MaxValueValidator(4094)
|
||||||
])
|
])
|
||||||
name = models.CharField(max_length=64)
|
name = models.CharField(max_length=64)
|
||||||
description = models.CharField(max_length=100, blank=True)
|
tenant = models.ForeignKey(Tenant, related_name='vlans', blank=True, null=True, on_delete=models.PROTECT)
|
||||||
status = models.PositiveSmallIntegerField('Status', choices=VLAN_STATUS_CHOICES, default=1)
|
status = models.PositiveSmallIntegerField('Status', choices=VLAN_STATUS_CHOICES, default=1)
|
||||||
role = models.ForeignKey('Role', related_name='vlans', on_delete=models.SET_NULL, blank=True, null=True)
|
role = models.ForeignKey('Role', related_name='vlans', on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
description = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['site', 'group', 'vid']
|
ordering = ['site', 'group', 'vid']
|
||||||
@ -438,6 +443,7 @@ class VLAN(CreatedUpdatedModel):
|
|||||||
self.group.name if self.group else '',
|
self.group.name if self.group else '',
|
||||||
str(self.vid),
|
str(self.vid),
|
||||||
self.name,
|
self.name,
|
||||||
|
self.tenant.name if self.tenant else '',
|
||||||
self.get_status_display(),
|
self.get_status_display(),
|
||||||
self.role.name if self.role else '',
|
self.role.name if self.role else '',
|
||||||
self.description,
|
self.description,
|
||||||
|
@ -58,11 +58,12 @@ class VRFTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name')
|
name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name')
|
||||||
rd = tables.Column(verbose_name='RD')
|
rd = tables.Column(verbose_name='RD')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
||||||
description = tables.Column(orderable=False, verbose_name='Description')
|
description = tables.Column(orderable=False, verbose_name='Description')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VRF
|
model = VRF
|
||||||
fields = ('pk', 'name', 'rd', 'description')
|
fields = ('pk', 'name', 'rd', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -203,9 +204,10 @@ class VLANTable(BaseTable):
|
|||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
name = tables.Column(verbose_name='Name')
|
name = tables.Column(verbose_name='Name')
|
||||||
|
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
|
||||||
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
|
||||||
role = tables.Column(verbose_name='Role')
|
role = tables.Column(verbose_name='Role')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'status', 'role')
|
fields = ('pk', 'vid', 'site', 'group', 'name', 'tenant', 'status', 'role')
|
||||||
|
@ -36,8 +36,9 @@ def add_available_prefixes(parent, prefix_list):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class VRFListView(ObjectListView):
|
class VRFListView(ObjectListView):
|
||||||
queryset = VRF.objects.all()
|
queryset = VRF.objects.select_related('tenant')
|
||||||
filter = filters.VRFFilter
|
filter = filters.VRFFilter
|
||||||
|
filter_form = forms.VRFFilterForm
|
||||||
table = tables.VRFTable
|
table = tables.VRFTable
|
||||||
edit_permissions = ['ipam.change_vrf', 'ipam.delete_vrf']
|
edit_permissions = ['ipam.change_vrf', 'ipam.delete_vrf']
|
||||||
template_name = 'ipam/vrf_list.html'
|
template_name = 'ipam/vrf_list.html'
|
||||||
@ -85,7 +86,7 @@ class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|||||||
def update_objects(self, pk_list, form):
|
def update_objects(self, pk_list, form):
|
||||||
|
|
||||||
fields_to_update = {}
|
fields_to_update = {}
|
||||||
for field in ['description']:
|
for field in ['tenant', 'description']:
|
||||||
if form.cleaned_data[field]:
|
if form.cleaned_data[field]:
|
||||||
fields_to_update[field] = form.cleaned_data[field]
|
fields_to_update[field] = form.cleaned_data[field]
|
||||||
|
|
||||||
@ -558,7 +559,7 @@ class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|||||||
def update_objects(self, pk_list, form):
|
def update_objects(self, pk_list, form):
|
||||||
|
|
||||||
fields_to_update = {}
|
fields_to_update = {}
|
||||||
for field in ['site', 'group', 'status', 'role', 'description']:
|
for field in ['site', 'group', 'tenant', 'status', 'role', 'description']:
|
||||||
if form.cleaned_data[field]:
|
if form.cleaned_data[field]:
|
||||||
fields_to_update[field] = form.cleaned_data[field]
|
fields_to_update[field] = form.cleaned_data[field]
|
||||||
|
|
||||||
|
@ -70,10 +70,10 @@
|
|||||||
<td>{{ vlan.name }}</td>
|
<td>{{ vlan.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Description</td>
|
<td>Tenant</td>
|
||||||
<td>
|
<td>
|
||||||
{% if vlan.description %}
|
{% if vlan.tenant %}
|
||||||
{{ vlan.description }}
|
<a href="{{ vlan.tenant.get_absolute_url }}">{{ vlan.tenant }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">None</span>
|
<span class="text-muted">None</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -89,6 +89,16 @@
|
|||||||
<td>Role</td>
|
<td>Role</td>
|
||||||
<td>{{ vlan.role }}</td>
|
<td>{{ vlan.role }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Description</td>
|
||||||
|
<td>
|
||||||
|
{% if vlan.description %}
|
||||||
|
{{ vlan.description }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Created</td>
|
<td>Created</td>
|
||||||
<td>{{ vlan.created }}</td>
|
<td>{{ vlan.created }}</td>
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
<td><a href="{% url 'ipam:vlan' pk=vlan.pk %}">{{ vlan.vid }}</a></td>
|
<td><a href="{% url 'ipam:vlan' pk=vlan.pk %}">{{ vlan.vid }}</a></td>
|
||||||
<td>{{ vlan.name }}</td>
|
<td>{{ vlan.name }}</td>
|
||||||
<td>{{ vlan.site }}</td>
|
<td>{{ vlan.site }}</td>
|
||||||
<td>{{ vlan.status }}</td>
|
<td>{{ vlan.tenant }}</td>
|
||||||
|
<td>{{ vlan.get_status_display }}</td>
|
||||||
<td>{{ vlan.role }}</td>
|
<td>{{ vlan.role }}</td>
|
||||||
<td>{{ vlan.description }}</td>
|
<td>{{ vlan.description }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -48,6 +48,11 @@
|
|||||||
<td>Configured VLAN name</td>
|
<td>Configured VLAN name</td>
|
||||||
<td>Cameras</td>
|
<td>Cameras</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tenant</td>
|
||||||
|
<td>Name of tenant (optional)</td>
|
||||||
|
<td>Internal</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Status</td>
|
<td>Status</td>
|
||||||
<td>Current status</td>
|
<td>Current status</td>
|
||||||
@ -66,7 +71,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h4>Example</h4>
|
<h4>Example</h4>
|
||||||
<pre>LAS2,Backend Network,1400,Cameras,Active,Security,Security team only</pre>
|
<pre>LAS2,Backend Network,1400,Cameras,Internal,Active,Security,Security team only</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -30,6 +30,16 @@
|
|||||||
<td>Route Distinguisher</td>
|
<td>Route Distinguisher</td>
|
||||||
<td>{{ vrf.rd }}</td>
|
<td>{{ vrf.rd }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tenant</td>
|
||||||
|
<td>
|
||||||
|
{% if vrf.tenant %}
|
||||||
|
<a href="{{ vrf.tenant.get_absolute_url }}">{{ vrf.tenant }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Enforce Uniqueness</td>
|
<td>Enforce Uniqueness</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><a href="{% url 'ipam:vrf' pk=vrf.pk %}">{{ vrf.name }}</a></td>
|
<td><a href="{% url 'ipam:vrf' pk=vrf.pk %}">{{ vrf.name }}</a></td>
|
||||||
<td>{{ vrf.rd }}</td>
|
<td>{{ vrf.rd }}</td>
|
||||||
|
<td>{{ vrf.tenant }}</td>
|
||||||
<td>{{ vrf.description }}</td>
|
<td>{{ vrf.description }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -38,6 +38,11 @@
|
|||||||
<td>Route distinguisher</td>
|
<td>Route distinguisher</td>
|
||||||
<td>65000:123456</td>
|
<td>65000:123456</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tenant</td>
|
||||||
|
<td>Name of tenant (optional)</td>
|
||||||
|
<td>ABC01</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Enforce uniqueness</td>
|
<td>Enforce uniqueness</td>
|
||||||
<td>Prevent duplicate prefixes/IP addresses</td>
|
<td>Prevent duplicate prefixes/IP addresses</td>
|
||||||
@ -51,7 +56,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h4>Example</h4>
|
<h4>Example</h4>
|
||||||
<pre>Customer_ABC,65000:123456,True,Native VRF for customer ABC</pre>
|
<pre>Customer_ABC,65000:123456,ABC01,True,Native VRF for customer ABC</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% include 'inc/filter_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
{% render_field form.name %}
|
{% render_field form.name %}
|
||||||
{% render_field form.slug %}
|
{% render_field form.slug %}
|
||||||
{% render_field form.group %}
|
{% render_field form.group %}
|
||||||
|
{% render_field form.description %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -46,4 +46,5 @@ class Tenant(CreatedUpdatedModel):
|
|||||||
self.name,
|
self.name,
|
||||||
self.slug,
|
self.slug,
|
||||||
self.group.name,
|
self.group.name,
|
||||||
|
self.description,
|
||||||
])
|
])
|
||||||
|
Reference in New Issue
Block a user