mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #971: Implement VLANGroup VLANs view to show available VLANs within a group
This commit is contained in:
committed by
Jeremy Stretch
parent
475e0e6c1e
commit
33e45a5292
@ -528,7 +528,7 @@ class VLANGroup(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "{}?group_id={}".format(reverse('ipam:vlan_list'), self.pk)
|
return reverse('ipam:vlangroup_vlans', args=[self.pk])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return (
|
return (
|
||||||
|
@ -110,6 +110,16 @@ STATUS_LABEL = """
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
VLAN_LINK = """
|
||||||
|
{% if record.pk %}
|
||||||
|
<a href="{{ record.get_absolute_url }}">{{ record.vid }}</a>
|
||||||
|
{% elif perms.ipam.add_vlan %}
|
||||||
|
<a href="{% url 'ipam:vlan_add' %}?vid={{ record.vid }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-xs btn-success">{{ record.available }} VLAN{{ record.available|pluralize }} available</a>
|
||||||
|
{% else %}
|
||||||
|
{{ record.available }} VLAN{{ record.available|pluralize }} available
|
||||||
|
{% endif %}
|
||||||
|
"""
|
||||||
|
|
||||||
VLAN_PREFIXES = """
|
VLAN_PREFIXES = """
|
||||||
{% for prefix in record.prefixes.all %}
|
{% for prefix in record.prefixes.all %}
|
||||||
<a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
|
<a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
|
||||||
@ -375,9 +385,9 @@ class VLANGroupTable(BaseTable):
|
|||||||
|
|
||||||
class VLANTable(BaseTable):
|
class VLANTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
|
vid = tables.TemplateColumn(VLAN_LINK, verbose_name='ID')
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.LinkColumn('ipam:vlangroup_vlans', args=[Accessor('group.pk')], verbose_name='Group')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
status = tables.TemplateColumn(STATUS_LABEL)
|
status = tables.TemplateColumn(STATUS_LABEL)
|
||||||
role = tables.TemplateColumn(VLAN_ROLE_LINK)
|
role = tables.TemplateColumn(VLAN_ROLE_LINK)
|
||||||
@ -385,6 +395,9 @@ class VLANTable(BaseTable):
|
|||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
|
fields = ('pk', 'vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
|
||||||
|
row_attrs = {
|
||||||
|
'class': lambda record: 'success' if not isinstance(record, VLAN) else '',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class VLANDetailTable(VLANTable):
|
class VLANDetailTable(VLANTable):
|
||||||
|
@ -72,6 +72,7 @@ urlpatterns = [
|
|||||||
url(r'^vlan-groups/import/$', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
url(r'^vlan-groups/import/$', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||||
url(r'^vlan-groups/delete/$', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
url(r'^vlan-groups/delete/$', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||||
url(r'^vlan-groups/(?P<pk>\d+)/edit/$', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
url(r'^vlan-groups/(?P<pk>\d+)/edit/$', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||||
|
url(r'^vlan-groups/(?P<pk>\d+)/vlans/$', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
||||||
|
|
||||||
# VLANs
|
# VLANs
|
||||||
url(r'^vlans/$', views.VLANListView.as_view(), name='vlan_list'),
|
url(r'^vlans/$', views.VLANListView.as_view(), name='vlan_list'),
|
||||||
|
@ -84,6 +84,34 @@ def add_available_ipaddresses(prefix, ipaddress_list, is_pool=False):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def add_available_vlans(vlan_group, vlans):
|
||||||
|
"""
|
||||||
|
Create fake records for all gaps between used VLANs
|
||||||
|
"""
|
||||||
|
MIN_VLAN = 1
|
||||||
|
MAX_VLAN = 4094
|
||||||
|
|
||||||
|
if not vlans:
|
||||||
|
return [{'vid': MIN_VLAN, 'available': MAX_VLAN - MIN_VLAN + 1}]
|
||||||
|
|
||||||
|
prev_vid = MAX_VLAN
|
||||||
|
new_vlans = []
|
||||||
|
for vlan in vlans:
|
||||||
|
if vlan.vid - prev_vid > 1:
|
||||||
|
new_vlans.append({'vid': prev_vid + 1, 'available': vlan.vid - prev_vid - 1})
|
||||||
|
prev_vid = vlan.vid
|
||||||
|
|
||||||
|
if vlans[0].vid > MIN_VLAN:
|
||||||
|
new_vlans.append({'vid': MIN_VLAN, 'available': vlans[0].vid - MIN_VLAN})
|
||||||
|
if prev_vid < MAX_VLAN:
|
||||||
|
new_vlans.append({'vid': prev_vid + 1, 'available': MAX_VLAN - prev_vid})
|
||||||
|
|
||||||
|
vlans = list(vlans) + new_vlans
|
||||||
|
vlans.sort(key=lambda v: v.vid if type(v) == VLAN else v['vid'])
|
||||||
|
|
||||||
|
return vlans
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VRFs
|
# VRFs
|
||||||
#
|
#
|
||||||
@ -814,6 +842,41 @@ class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
default_return_url = 'ipam:vlangroup_list'
|
default_return_url = 'ipam:vlangroup_list'
|
||||||
|
|
||||||
|
|
||||||
|
class VLANGroupVLANsView(View):
|
||||||
|
def get(self, request, pk):
|
||||||
|
|
||||||
|
vlan_group = get_object_or_404(VLANGroup.objects.all(), pk=pk)
|
||||||
|
|
||||||
|
vlans = VLAN.objects.filter(group_id=pk)
|
||||||
|
vlans = add_available_vlans(vlan_group, vlans)
|
||||||
|
|
||||||
|
vlan_table = tables.VLANDetailTable(vlans)
|
||||||
|
if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
|
||||||
|
vlan_table.columns.show('pk')
|
||||||
|
vlan_table.columns.hide('site')
|
||||||
|
vlan_table.columns.hide('group')
|
||||||
|
|
||||||
|
paginate = {
|
||||||
|
'klass': EnhancedPaginator,
|
||||||
|
'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
|
||||||
|
}
|
||||||
|
RequestConfig(request, paginate).configure(vlan_table)
|
||||||
|
|
||||||
|
# Compile permissions list for rendering the object table
|
||||||
|
permissions = {
|
||||||
|
'add': request.user.has_perm('ipam.add_vlan'),
|
||||||
|
'change': request.user.has_perm('ipam.change_vlan'),
|
||||||
|
'delete': request.user.has_perm('ipam.delete_vlan'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'ipam/vlangroup_vlans.html', {
|
||||||
|
'vlan_group': vlan_group,
|
||||||
|
'first_available_vlan': vlan_group.get_next_available_vid(),
|
||||||
|
'vlan_table': vlan_table,
|
||||||
|
'permissions': permissions,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VLANs
|
# VLANs
|
||||||
#
|
#
|
||||||
|
14
netbox/templates/ipam/inc/vlangroup_header.html
Normal file
14
netbox/templates/ipam/inc/vlangroup_header.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<div class="pull-right">
|
||||||
|
{% if perms.ipam.add_vlan and first_available_vlan %}
|
||||||
|
<a href="{% url 'ipam:vlan_add' %}?vid={{ first_available_vlan }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-success">
|
||||||
|
<i class="fa fa-plus" aria-hidden="true"></i> Add a VLAN
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.ipam.change_vlangroup %}
|
||||||
|
<a href="{% url 'ipam:vlangroup_edit' pk=vlan_group.pk %}" class="btn btn-warning">
|
||||||
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
|
Edit this VLAN Group
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<h1>{{ vlan_group }}</h1>
|
24
netbox/templates/ipam/vlangroup_vlans.html
Normal file
24
netbox/templates/ipam/vlangroup_vlans.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
|
||||||
|
{% block title %}{{ vlan_group }} - VLANs{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
|
||||||
|
{% if vlan_group.site %}
|
||||||
|
<li><a href="{% url 'dcim:site' slug=vlan_group.site.slug %}">{{ vlan_group.site }}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li>{{ vlan_group }}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'ipam/inc/vlangroup_header.html' %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% include 'utilities/obj_table.html' with table=vlan_table table_template='panel_table.html' heading='VLANs' bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
Reference in New Issue
Block a user