Browse Source

Introduced CBVs for IPAM Roles

Jeremy Stretch 9 years ago
parent
commit
ff46970ba9

+ 16 - 1
netbox/ipam/forms.py

@@ -5,7 +5,7 @@ from django.db.models import Count
 
 
 from dcim.models import Site, Device, Interface
 from dcim.models import Site, Device, Interface
 from utilities.forms import BootstrapMixin, ConfirmationForm, APISelect, Livesearch, CSVDataField, BulkImportForm
 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
 
 
 
 
 #
 #
@@ -111,6 +111,21 @@ class AggregateFilterForm(forms.Form, BootstrapMixin):
 
 
 
 
 #
 #
+# 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
 # Prefixes
 #
 #
 
 

+ 40 - 32
netbox/ipam/models.py

@@ -44,38 +44,6 @@ class VRF(models.Model):
         return reverse('ipam:vrf', args=[self.pk])
         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):
 class RIR(models.Model):
     """
     """
     A regional Internet registry (e.g. ARIN) or governing standard (e.g. RFC 1918)
     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)
         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):
 class PrefixQuerySet(models.QuerySet):
 
 
     def annotate_depth(self, limit=None):
     def annotate_depth(self, limit=None):

+ 26 - 1
netbox/ipam/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 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 = """
 RIR_EDIT_LINK = """
@@ -19,6 +19,10 @@ UTILIZATION_GRAPH = """
 {% endwith %}
 {% endwith %}
 """
 """
 
 
+ROLE_EDIT_LINK = """
+{% if perms.ipam.change_role %}<a href="{% url 'ipam:role_edit' slug=record.slug %}">Edit</a>{% endif %}
+"""
+
 PREFIX_LINK = """
 PREFIX_LINK = """
 {% if record.has_children %}
 {% if record.has_children %}
     <span style="padding-left: {{ record.depth }}0px "><i class="fa fa-caret-right"></i></a>
     <span style="padding-left: {{ record.depth }}0px "><i class="fa fa-caret-right"></i></a>
@@ -106,6 +110,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
 # Prefixes
 #
 #
 
 

+ 6 - 0
netbox/ipam/urls.py

@@ -30,6 +30,12 @@ urlpatterns = [
     url(r'^aggregates/(?P<pk>\d+)/edit/$', views.AggregateEditView.as_view(), name='aggregate_edit'),
     url(r'^aggregates/(?P<pk>\d+)/edit/$', views.AggregateEditView.as_view(), name='aggregate_edit'),
     url(r'^aggregates/(?P<pk>\d+)/delete/$', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
     url(r'^aggregates/(?P<pk>\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<slug>[\w-]+)/edit/$', views.RoleEditView.as_view(), name='role_edit'),
+
     # Prefixes
     # Prefixes
     url(r'^prefixes/$', views.PrefixListView.as_view(), name='prefix_list'),
     url(r'^prefixes/$', views.PrefixListView.as_view(), name='prefix_list'),
     url(r'^prefixes/add/$', views.PrefixEditView.as_view(), name='prefix_add'),
     url(r'^prefixes/add/$', views.PrefixEditView.as_view(), name='prefix_add'),

+ 32 - 6
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 .filters import AggregateFilter, PrefixFilter, IPAddressFilter, VLANFilter, VRFFilter
 from .forms import AggregateForm, AggregateImportForm, AggregateBulkEditForm, AggregateBulkDeleteForm,\
 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
     IPAddressTable, VLANTable
 
 
 
 
@@ -218,6 +218,32 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 
 
 
 
 #
 #
+# 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
 # Prefixes
 #
 #
 
 

+ 18 - 13
netbox/templates/_base.html

@@ -35,9 +35,7 @@
                                 <li><a href="{% url 'dcim:rack_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Rack</a></li>
                                 <li><a href="{% url 'dcim:rack_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Rack</a></li>
                                 <li><a href="{% url 'dcim:rack_import' %}"><i class="glyphicon glyphicon-import" aria-hidden="true"></i> Import Racks</a></li>
                                 <li><a href="{% url 'dcim:rack_import' %}"><i class="glyphicon glyphicon-import" aria-hidden="true"></i> Import Racks</a></li>
                             {% endif %}
                             {% endif %}
-                            {% if perms.dcim.add_rack or perms.dcim.add_rackgroup %}
-                                <li class="divider"></li>
-                            {% endif %}
+                            <li class="divider"></li>
                             <li><a href="{% url 'dcim:rackgroup_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Rack Groups</a></li>
                             <li><a href="{% url 'dcim:rackgroup_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Rack Groups</a></li>
                             {% if perms.dcim.add_rackgroup %}
                             {% if perms.dcim.add_rackgroup %}
                                 <li><a href="{% url 'dcim:rackgroup_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Rack Group</a></li>
                                 <li><a href="{% url 'dcim:rackgroup_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Rack Group</a></li>
@@ -59,19 +57,21 @@
                             {% if perms.dcim.add_devicetype %}
                             {% if perms.dcim.add_devicetype %}
                                 <li><a href="{% url 'dcim:devicetype_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Device Type</a></li>
                                 <li><a href="{% url 'dcim:devicetype_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Device Type</a></li>
                             {% endif %}
                             {% endif %}
-                            {% if perms.ipam.add_devicetype or perms.ipam.add_devicerole %}
-                                <li class="divider"></li>
-                            {% endif %}
+                            <li class="divider"></li>
                             <li><a href="{% url 'dcim:devicerole_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Device Roles</a></li>
                             <li><a href="{% url 'dcim:devicerole_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Device Roles</a></li>
                             {% if perms.dcim.add_devicerole %}
                             {% if perms.dcim.add_devicerole %}
                                 <li><a href="{% url 'dcim:devicerole_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Device Role</a></li>
                                 <li><a href="{% url 'dcim:devicerole_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Device Role</a></li>
                             {% endif %}
                             {% endif %}
-                            <li class="divider"></li>
+                            {% if perms.dcim.add_devicerole or perms.dcim.add_manufacturer %}
+                                <li class="divider"></li>
+                            {% endif %}
                             <li><a href="{% url 'dcim:manufacturer_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Manufacturers</a></li>
                             <li><a href="{% url 'dcim:manufacturer_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Manufacturers</a></li>
                             {% if perms.dcim.add_manufacturer %}
                             {% if perms.dcim.add_manufacturer %}
                                 <li><a href="{% url 'dcim:manufacturer_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Manufacturer</a></li>
                                 <li><a href="{% url 'dcim:manufacturer_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Manufacturer</a></li>
                             {% endif %}
                             {% endif %}
-                            <li class="divider"></li>
+                            {% if perms.dcim.add_manufacturer or perms.dcim.add_platform %}
+                                <li class="divider"></li>
+                            {% endif %}
                             <li><a href="{% url 'dcim:platform_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Platforms</a></li>
                             <li><a href="{% url 'dcim:platform_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Platforms</a></li>
                             {% if perms.dcim.add_platform %}
                             {% if perms.dcim.add_platform %}
                                 <li><a href="{% url 'dcim:platform_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Platform</a></li>
                                 <li><a href="{% url 'dcim:platform_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Platform</a></li>
@@ -101,7 +101,7 @@
                             {% endif %}
                             {% endif %}
                         </ul>
                         </ul>
                     </li>
                     </li>
-                    <li class="dropdown{% if '/ipam/ip-addresses/' in request.path or '/prefixes/' in request.path or '/aggregates/' in request.path or '/vrfs/' in request.path or '/rirs/' in request.path %} active{% endif %}">
+                    <li class="dropdown{% if '/ipam/' in request.path and not '/ipam/vlans/' in request.path %} active{% endif %}">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">IP Space <span class="caret"></span></a>
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">IP Space <span class="caret"></span></a>
                         <ul class="dropdown-menu">
                         <ul class="dropdown-menu">
                             <li><a href="{% url 'ipam:ipaddress_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> IP Addresses</a></li>
                             <li><a href="{% url 'ipam:ipaddress_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> IP Addresses</a></li>
@@ -138,9 +138,16 @@
                             {% if perms.ipam.add_rir %}
                             {% if perms.ipam.add_rir %}
                                 <li><a href="{% url 'ipam:rir_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a RIR</a></li>
                                 <li><a href="{% url 'ipam:rir_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a RIR</a></li>
                             {% endif %}
                             {% endif %}
+                            {% if perms.ipam.add_rir or perms.ipam.add_role %}
+                                <li class="divider"></li>
+                            {% endif %}
+                            <li><a href="{% url 'ipam:role_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Prefix/VLAN Roles</a></li>
+                            {% if perms.ipam.add_role %}
+                                <li><a href="{% url 'ipam:role_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Role</a></li>
+                            {% endif %}
                         </ul>
                         </ul>
                     </li>
                     </li>
-                    <li class="dropdown{% if '/vlans/' in request.path %} active{% endif %}">
+                    <li class="dropdown{% if '/ipam/vlans/' in request.path %} active{% endif %}">
                         {% if perms.ipam.add_vlan %}
                         {% if perms.ipam.add_vlan %}
                             <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">VLANs <span class="caret"></span></a>
                             <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">VLANs <span class="caret"></span></a>
                             <ul class="dropdown-menu">
                             <ul class="dropdown-menu">
@@ -168,9 +175,7 @@
                                 <li><a href="{% url 'circuits:circuit_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Circuit</a></li>
                                 <li><a href="{% url 'circuits:circuit_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Circuit</a></li>
                                 <li><a href="{% url 'circuits:circuit_import' %}"><i class="glyphicon glyphicon-import" aria-hidden="true"></i> Import Circuits</a></li>
                                 <li><a href="{% url 'circuits:circuit_import' %}"><i class="glyphicon glyphicon-import" aria-hidden="true"></i> Import Circuits</a></li>
                             {% endif %}
                             {% endif %}
-                            {% if perms.circuits.add_circuit or perms.circuits.add_circuittype %}
-                                <li class="divider"></li>
-                            {% endif %}
+                            <li class="divider"></li>
                             <li><a href="{% url 'circuits:circuittype_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Circuit Types</a></li>
                             <li><a href="{% url 'circuits:circuittype_list' %}"><i class="glyphicon glyphicon-search" aria-hidden="true"></i> Circuit Types</a></li>
                             {% if perms.circuits.add_circuittype %}
                             {% if perms.circuits.add_circuittype %}
                                 <li><a href="{% url 'circuits:circuittype_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Circuit Type</a></li>
                                 <li><a href="{% url 'circuits:circuittype_add' %}"><i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add a Circuit Type</a></li>

+ 1 - 1
netbox/templates/ipam/rir_list.html

@@ -5,7 +5,7 @@
 
 
 {% block content %}
 {% block content %}
 <div class="pull-right">
 <div class="pull-right">
-    {% if perms.dcim.add_devicerole %}
+    {% if perms.ipam.add_rir %}
         <a href="{% url 'ipam:rir_add' %}" class="btn btn-primary">
         <a href="{% url 'ipam:rir_add' %}" class="btn btn-primary">
             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
             Add a RIR
             Add a RIR

+ 21 - 0
netbox/templates/ipam/role_list.html

@@ -0,0 +1,21 @@
+{% extends '_base.html' %}
+{% load helpers %}
+
+{% block title %}Prefix/VLAN Roles{% endblock %}
+
+{% block content %}
+<div class="pull-right">
+    {% if perms.dcim.add_devicerole %}
+        <a href="{% url 'ipam:role_add' %}" class="btn btn-primary">
+            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
+            Add a role
+        </a>
+    {% endif %}
+</div>
+<h1>Prefix/VLAN Roles</h1>
+<div class="row">
+	<div class="col-md-12">
+        {% include 'utilities/obj_table.html' with bulk_delete_url='ipam:role_bulk_delete' %}
+    </div>
+</div>
+{% endblock %}

+ 7 - 4
netbox/utilities/views.py

@@ -116,10 +116,13 @@ class ObjectEditView(View):
             obj = form.save(commit=False)
             obj = form.save(commit=False)
             obj_created = not obj.pk
             obj_created = not obj.pk
             obj.save()
             obj.save()
-            messages.success(request, '{} {} <a href="{}">{}</a>'.format('Created' if obj_created else 'Modified',
-                                                                         self.model._meta.verbose_name,
-                                                                         obj.get_absolute_url(),
-                                                                         obj))
+            msg = 'Created ' if obj_created else 'Modified '
+            msg += self.model._meta.verbose_name
+            if hasattr(obj, 'get_absolute_url'):
+                msg += ' <a href="{}">{}</a>'.format(obj.get_absolute_url(), obj)
+            else:
+                msg += ' {}'.format(obj)
+            messages.success(request, msg)
             if '_addanother' in request.POST:
             if '_addanother' in request.POST:
                 return redirect(request.path)
                 return redirect(request.path)
             elif self.success_url:
             elif self.success_url: