Parcourir la source

Closes #87: Added status field to IP addresses

Jeremy Stretch il y a 8 ans
Parent
commit
13243785f1

+ 2 - 2
netbox/ipam/api/serializers.py

@@ -159,8 +159,8 @@ class IPAddressSerializer(CustomFieldSerializer, serializers.ModelSerializer):
 
     class Meta:
         model = IPAddress
-        fields = ['id', 'family', 'address', 'vrf', 'tenant', 'interface', 'description', 'nat_inside', 'nat_outside',
-                  'custom_fields']
+        fields = ['id', 'family', 'address', 'vrf', 'tenant', 'status', 'interface', 'description', 'nat_inside',
+                  'nat_outside', 'custom_fields']
 
 
 class IPAddressNestedSerializer(IPAddressSerializer):

+ 1 - 1
netbox/ipam/filters.py

@@ -232,7 +232,7 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
 
     class Meta:
         model = IPAddress
-        fields = ['q', 'family', 'device_id', 'device', 'interface_id']
+        fields = ['q', 'family', 'status', 'device_id', 'device', 'interface_id']
 
     def search(self, queryset, value):
         qs_filter = Q(description__icontains=value)

+ 17 - 8
netbox/ipam/forms.py

@@ -5,16 +5,15 @@ from dcim.models import Site, Device, Interface
 from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from tenancy.models import Tenant
 from utilities.forms import (
-    APISelect, BootstrapMixin, CSVDataField, BulkImportForm, FilterChoiceField, Livesearch, SlugField,
+    APISelect, BootstrapMixin, CSVDataField, BulkImportForm, FilterChoiceField, Livesearch, SlugField, add_blank_choice,
 )
 
 from .models import (
-    Aggregate, IPAddress, Prefix, PREFIX_STATUS_CHOICES, RIR, Role, VLAN, VLANGroup, VLAN_STATUS_CHOICES, VRF,
+    Aggregate, IPAddress, IPADDRESS_STATUS_CHOICES, Prefix, PREFIX_STATUS_CHOICES, RIR, Role, VLAN, VLANGroup,
+    VLAN_STATUS_CHOICES, VRF,
 )
 
 
-FORM_PREFIX_STATUS_CHOICES = (('', '---------'),) + PREFIX_STATUS_CHOICES
-FORM_VLAN_STATUS_CHOICES = (('', '---------'),) + VLAN_STATUS_CHOICES
 IP_FAMILY_CHOICES = [
     ('', 'All'),
     (4, 'IPv4'),
@@ -248,7 +247,7 @@ class PrefixBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
     site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False)
     vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF')
     tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
-    status = forms.ChoiceField(choices=FORM_PREFIX_STATUS_CHOICES, required=False)
+    status = forms.ChoiceField(choices=add_blank_choice(PREFIX_STATUS_CHOICES), required=False)
     role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False)
     description = forms.CharField(max_length=100, required=False)
 
@@ -301,7 +300,7 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm):
 
     class Meta:
         model = IPAddress
-        fields = ['address', 'vrf', 'tenant', 'nat_device', 'nat_inside', 'description']
+        fields = ['address', 'vrf', 'tenant', 'status', 'nat_device', 'nat_inside', 'description']
         help_texts = {
             'address': "IPv4 or IPv6 address and mask",
             'vrf': "VRF (if applicable)",
@@ -352,6 +351,7 @@ class IPAddressFromCSVForm(forms.ModelForm):
                                  error_messages={'invalid_choice': 'VRF 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 IPADDRESS_STATUS_CHOICES])
     device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
                                     error_messages={'invalid_choice': 'Device not found.'})
     interface_name = forms.CharField(required=False)
@@ -359,7 +359,7 @@ class IPAddressFromCSVForm(forms.ModelForm):
 
     class Meta:
         model = IPAddress
-        fields = ['address', 'vrf', 'tenant', 'device', 'interface_name', 'is_primary', 'description']
+        fields = ['address', 'vrf', 'tenant', 'status_name', 'device', 'interface_name', 'is_primary', 'description']
 
     def clean(self):
 
@@ -406,12 +406,20 @@ class IPAddressBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
     pk = forms.ModelMultipleChoiceField(queryset=IPAddress.objects.all(), widget=forms.MultipleHiddenInput)
     vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF')
     tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
+    status = forms.ChoiceField(choices=add_blank_choice(IPADDRESS_STATUS_CHOICES), required=False)
     description = forms.CharField(max_length=100, required=False)
 
     class Meta:
         nullable_fields = ['vrf', 'tenant', 'description']
 
 
+def ipaddress_status_choices():
+    status_counts = {}
+    for status in IPAddress.objects.values('status').annotate(count=Count('status')).order_by('status'):
+        status_counts[status['status']] = status['count']
+    return [(s[0], u'{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in IPADDRESS_STATUS_CHOICES]
+
+
 class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
     model = IPAddress
     parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={
@@ -422,6 +430,7 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
                             label='VRF', null_option=(0, 'Global'))
     tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('ip_addresses')),
                                to_field_name='slug', null_option=(0, 'None'))
+    status = forms.MultipleChoiceField(choices=ipaddress_status_choices, required=False)
 
 
 #
@@ -510,7 +519,7 @@ class VLANBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
     site = forms.ModelChoiceField(queryset=Site.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=add_blank_choice(VLAN_STATUS_CHOICES), required=False)
     role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False)
     description = forms.CharField(max_length=100, required=False)
 

+ 20 - 0
netbox/ipam/migrations/0009_ipaddress_add_status.py

@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-10-21 15:44
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0008_prefix_change_order'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='ipaddress',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[(1, b'Active'), (2, b'Reserved'), (5, b'DHCP')], default=1, verbose_name=b'Status'),
+        ),
+    ]

+ 13 - 0
netbox/ipam/models.py

@@ -29,6 +29,12 @@ PREFIX_STATUS_CHOICES = (
     (3, 'Deprecated')
 )
 
+IPADDRESS_STATUS_CHOICES = (
+    (1, 'Active'),
+    (2, 'Reserved'),
+    (5, 'DHCP')
+)
+
 VLAN_STATUS_CHOICES = (
     (1, 'Active'),
     (2, 'Reserved'),
@@ -40,6 +46,8 @@ STATUS_CHOICE_CLASSES = {
     1: 'primary',
     2: 'info',
     3: 'danger',
+    4: 'warning',
+    5: 'success',
 }
 
 
@@ -333,6 +341,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
     vrf = models.ForeignKey('VRF', related_name='ip_addresses', on_delete=models.PROTECT, blank=True, null=True,
                             verbose_name='VRF')
     tenant = models.ForeignKey(Tenant, related_name='ip_addresses', blank=True, null=True, on_delete=models.PROTECT)
+    status = models.PositiveSmallIntegerField('Status', choices=IPADDRESS_STATUS_CHOICES, default=1)
     interface = models.ForeignKey(Interface, related_name='ip_addresses', on_delete=models.CASCADE, blank=True,
                                   null=True)
     nat_inside = models.OneToOneField('self', related_name='nat_outside', on_delete=models.SET_NULL, blank=True,
@@ -387,6 +396,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
             str(self.address),
             self.vrf.rd if self.vrf else '',
             self.tenant.name if self.tenant else '',
+            self.get_status_display(),
             self.device.identifier if self.device else '',
             self.interface.name if self.interface else '',
             'True' if is_primary else '',
@@ -399,6 +409,9 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
             return self.interface.device
         return None
 
+    def get_status_class(self):
+        return STATUS_CHOICE_CLASSES[self.status]
+
 
 class VLANGroup(models.Model):
     """

+ 2 - 1
netbox/ipam/tables.py

@@ -193,6 +193,7 @@ class PrefixBriefTable(BaseTable):
 class IPAddressTable(BaseTable):
     pk = ToggleColumn()
     address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
+    status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
     vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
     tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant')
     device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
@@ -202,7 +203,7 @@ class IPAddressTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = IPAddress
-        fields = ('pk', 'address', 'vrf', 'tenant', 'device', 'interface', 'description')
+        fields = ('pk', 'address', 'status', 'vrf', 'tenant', 'device', 'interface', 'description')
         row_attrs = {
             'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
         }

+ 6 - 0
netbox/templates/ipam/ipaddress.html

@@ -77,6 +77,12 @@
                     </td>
                 </tr>
                 <tr>
+                    <td>Status</td>
+                    <td>
+                        <span class="label label-{{ ipaddress.get_status_class }}">{{ ipaddress.get_status_display }}</span>
+                    </td>
+                </tr>
+                <tr>
                     <td>Description</td>
                     <td>
                         {% if ipaddress.description %}

+ 2 - 0
netbox/templates/ipam/ipaddress_bulk_edit.html

@@ -8,6 +8,7 @@
         <th>IP Address</th>
         <th>VRF</th>
         <th>Tenant</th>
+        <th>Status</th>
         <th>Assigned</th>
         <th>Description</th>
     </tr>
@@ -16,6 +17,7 @@
             <td><a href="{% url 'ipam:ipaddress' pk=ipaddress.pk %}">{{ ipaddress }}</a></td>
             <td>{{ ipaddress.vrf|default:"Global" }}</td>
             <td>{{ ipaddress.tenant }}</td>
+            <td>{{ ipaddress.get_status_display }}</td>
             <td>{% if ipaddress.interface %}<i class="glyphicon glyphicon-ok text-success" title="{{ ipaddress.interface.device }} {{ ipaddress.interface }}"></i>{% endif %}</td>
             <td>{{ ipaddress.description }}</td>
         </tr>

+ 1 - 0
netbox/templates/ipam/ipaddress_edit.html

@@ -9,6 +9,7 @@
             {% render_field form.address %}
             {% render_field form.vrf %}
             {% render_field form.tenant %}
+            {% render_field form.status %}
             {% if obj %}
                 <div class="form-group">
                     <label class="col-md-3 control-label">Device</label>

+ 6 - 1
netbox/templates/ipam/ipaddress_import.html

@@ -44,6 +44,11 @@
 					<td>ABC01</td>
 				</tr>
 				<tr>
+					<td>Status</td>
+					<td>Current status</td>
+					<td>Active</td>
+				</tr>
+				<tr>
 					<td>Device</td>
 					<td>Device name (optional)</td>
 					<td>switch12</td>
@@ -66,7 +71,7 @@
 			</tbody>
 		</table>
 		<h4>Example</h4>
-		<pre>192.0.2.42/24,65000:123,ABC01,switch12,ge-0/0/31,True,Management IP</pre>
+		<pre>192.0.2.42/24,65000:123,ABC01,Active,switch12,ge-0/0/31,True,Management IP</pre>
 	</div>
 </div>
 {% endblock %}