diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index db8ff7a85..869d95aa9 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -5,7 +5,7 @@ from django.db.models import Count from dcim.models import Site, Device, Interface from utilities.forms import BootstrapMixin, ConfirmationForm, APISelect, Livesearch, CSVDataField, BulkImportForm -from .models import VRF, RIR, Aggregate, Prefix, IPAddress, VLAN, Status, Role +from .models import VRF, RIR, Aggregate, Role, Status, Prefix, IPAddress, VLAN # @@ -110,6 +110,21 @@ class AggregateFilterForm(forms.Form, BootstrapMixin): widget=forms.SelectMultiple(attrs={'size': 8})) +# +# Roles +# + +class RoleForm(forms.ModelForm, BootstrapMixin): + + class Meta: + model = Role + fields = ['name', 'slug'] + + +class RoleBulkDeleteForm(ConfirmationForm): + pk = forms.ModelMultipleChoiceField(queryset=Role.objects.all(), widget=forms.MultipleHiddenInput) + + # # Prefixes # diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index b00f16104..a3ea25d11 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -44,38 +44,6 @@ class VRF(models.Model): return reverse('ipam:vrf', args=[self.pk]) -class Status(models.Model): - """ - The status of a prefix or VLAN (e.g. allocated, reserved, etc.) - """ - name = models.CharField(max_length=50, unique=True) - slug = models.SlugField(unique=True) - weight = models.PositiveSmallIntegerField(default=1000) - bootstrap_class = models.PositiveSmallIntegerField(choices=BOOTSTRAP_CLASS_CHOICES, default=0) - - class Meta: - ordering = ['weight', 'name'] - verbose_name_plural = 'statuses' - - def __unicode__(self): - return self.name - - -class Role(models.Model): - """ - The role of an address resource (e.g. customer, infrastructure, mgmt, etc.) - """ - name = models.CharField(max_length=50, unique=True) - slug = models.SlugField(unique=True) - weight = models.PositiveSmallIntegerField(default=1000) - - class Meta: - ordering = ['weight', 'name'] - - def __unicode__(self): - return self.name - - class RIR(models.Model): """ A regional Internet registry (e.g. ARIN) or governing standard (e.g. RFC 1918) @@ -149,6 +117,46 @@ class Aggregate(models.Model): return int(children_size / self.prefix.size * 100) +class Status(models.Model): + """ + The status of a prefix or VLAN (e.g. allocated, reserved, etc.) + """ + name = models.CharField(max_length=50, unique=True) + slug = models.SlugField(unique=True) + weight = models.PositiveSmallIntegerField(default=1000) + bootstrap_class = models.PositiveSmallIntegerField(choices=BOOTSTRAP_CLASS_CHOICES, default=0) + + class Meta: + ordering = ['weight', 'name'] + verbose_name_plural = 'statuses' + + def __unicode__(self): + return self.name + + +class Role(models.Model): + """ + The role of an address resource (e.g. customer, infrastructure, mgmt, etc.) + """ + name = models.CharField(max_length=50, unique=True) + slug = models.SlugField(unique=True) + weight = models.PositiveSmallIntegerField(default=1000) + + class Meta: + ordering = ['weight', 'name'] + + def __unicode__(self): + return self.name + + @property + def count_prefixes(self): + return self.prefixes.count() + + @property + def count_vlans(self): + return self.vlans.count() + + class PrefixQuerySet(models.QuerySet): def annotate_depth(self, limit=None): diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index adf8617b2..7862b4233 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from .models import VRF, RIR, Aggregate, Prefix, IPAddress, VLAN +from .models import VRF, RIR, Aggregate, Role, Prefix, IPAddress, VLAN RIR_EDIT_LINK = """ @@ -19,6 +19,10 @@ UTILIZATION_GRAPH = """ {% endwith %} """ +ROLE_EDIT_LINK = """ +{% if perms.ipam.change_role %}Edit{% endif %} +""" + PREFIX_LINK = """ {% if record.has_children %} @@ -105,6 +109,27 @@ class AggregateTable(tables.Table): } +# +# Roles +# + +class RoleTable(tables.Table): + pk = tables.CheckBoxColumn(visible=False, default='') + name = tables.Column(verbose_name='Name') + prefix_count = tables.Column(accessor=Accessor('count_prefixes'), orderable=False, verbose_name='Prefixes') + vlan_count = tables.Column(accessor=Accessor('count_vlans'), orderable=False, verbose_name='VLANs') + slug = tables.Column(verbose_name='Slug') + edit = tables.TemplateColumn(template_code=ROLE_EDIT_LINK, verbose_name='') + + class Meta: + model = Role + fields = ('pk', 'name', 'prefix_count', 'vlan_count', 'slug', 'edit') + empty_text = "No roles were found." + attrs = { + 'class': 'table table-hover', + } + + # # Prefixes # diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 97d7d7543..58f73c247 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -30,6 +30,12 @@ urlpatterns = [ url(r'^aggregates/(?P\d+)/edit/$', views.AggregateEditView.as_view(), name='aggregate_edit'), url(r'^aggregates/(?P\d+)/delete/$', views.AggregateDeleteView.as_view(), name='aggregate_delete'), + # Roles + url(r'^roles/$', views.RoleListView.as_view(), name='role_list'), + url(r'^roles/add/$', views.RoleEditView.as_view(), name='role_add'), + url(r'^roles/delete/$', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'), + url(r'^roles/(?P[\w-]+)/edit/$', views.RoleEditView.as_view(), name='role_edit'), + # Prefixes url(r'^prefixes/$', views.PrefixListView.as_view(), name='prefix_list'), url(r'^prefixes/add/$', views.PrefixEditView.as_view(), name='prefix_add'), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index bec993762..f53bb05ed 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -14,12 +14,12 @@ from utilities.views import BulkImportView, BulkEditView, BulkDeleteView, Object from .filters import AggregateFilter, PrefixFilter, IPAddressFilter, VLANFilter, VRFFilter from .forms import AggregateForm, AggregateImportForm, AggregateBulkEditForm, AggregateBulkDeleteForm,\ - AggregateFilterForm, PrefixForm, PrefixImportForm, PrefixBulkEditForm, PrefixBulkDeleteForm, PrefixFilterForm,\ - IPAddressForm, IPAddressImportForm, IPAddressBulkEditForm, IPAddressBulkDeleteForm, IPAddressFilterForm, VLANForm,\ - VLANImportForm, VLANBulkEditForm, VLANBulkDeleteForm, VRFForm, VRFImportForm, VRFBulkEditForm, VRFBulkDeleteForm,\ - VLANFilterForm, RIRForm, RIRBulkDeleteForm -from .models import VRF, RIR, Aggregate, Prefix, IPAddress, VLAN -from .tables import VRFTable, RIRTable, AggregateTable, PrefixTable, PrefixBriefTable, IPAddressBriefTable,\ + AggregateFilterForm, RoleForm, RoleBulkDeleteForm, PrefixForm, PrefixImportForm, PrefixBulkEditForm,\ + PrefixBulkDeleteForm, PrefixFilterForm, IPAddressForm, IPAddressImportForm, IPAddressBulkEditForm,\ + IPAddressBulkDeleteForm, IPAddressFilterForm, VLANForm, VLANImportForm, VLANBulkEditForm, VLANBulkDeleteForm,\ + VRFForm, VRFImportForm, VRFBulkEditForm, VRFBulkDeleteForm, VLANFilterForm, RIRForm, RIRBulkDeleteForm +from .models import VRF, RIR, Aggregate, Role, Prefix, IPAddress, VLAN +from .tables import VRFTable, RIRTable, AggregateTable, RoleTable, PrefixTable, PrefixBriefTable, IPAddressBriefTable,\ IPAddressTable, VLANTable @@ -217,6 +217,32 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): default_redirect_url = 'ipam:aggregate_list' +# +# Prefix/VLAN roles +# + +class RoleListView(ObjectListView): + queryset = Role.objects.all() + table = RoleTable + edit_permissions = ['ipam.change_role', 'ipam.delete_role'] + template_name = 'ipam/role_list.html' + + +class RoleEditView(PermissionRequiredMixin, ObjectEditView): + permission_required = 'ipam.change_role' + model = Role + form_class = RoleForm + success_url = 'ipam:role_list' + cancel_url = 'ipam:role_list' + + +class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): + permission_required = 'ipam.delete_role' + cls = Role + form = RoleBulkDeleteForm + default_redirect_url = 'ipam:role_list' + + # # Prefixes # diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index c37f8dcce..a357922ec 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -35,9 +35,7 @@
  • Add a Rack
  • Import Racks
  • {% endif %} - {% if perms.dcim.add_rack or perms.dcim.add_rackgroup %} -
  • - {% endif %} +
  • Rack Groups
  • {% if perms.dcim.add_rackgroup %}
  • Add a Rack Group
  • @@ -59,19 +57,21 @@ {% if perms.dcim.add_devicetype %}
  • Add a Device Type
  • {% endif %} - {% if perms.ipam.add_devicetype or perms.ipam.add_devicerole %} -
  • - {% endif %} +
  • Device Roles
  • {% if perms.dcim.add_devicerole %}
  • Add a Device Role
  • {% endif %} -
  • + {% if perms.dcim.add_devicerole or perms.dcim.add_manufacturer %} +
  • + {% endif %}
  • Manufacturers
  • {% if perms.dcim.add_manufacturer %}
  • Add a Manufacturer
  • {% endif %} -
  • + {% if perms.dcim.add_manufacturer or perms.dcim.add_platform %} +
  • + {% endif %}
  • Platforms
  • {% if perms.dcim.add_platform %}
  • Add a Platform
  • @@ -101,7 +101,7 @@ {% endif %} - -