Browse Source

Closes #1864: Added a 'status' field to the circuit model

Jeremy Stretch 7 years ago
parent
commit
69f921aea9

+ 7 - 5
netbox/circuits/api/serializers.py

@@ -2,11 +2,12 @@ from __future__ import unicode_literals
 
 from rest_framework import serializers
 
+from circuits.constants import CIRCUIT_STATUS_CHOICES
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import ValidatedModelSerializer
+from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
 
 
 #
@@ -66,14 +67,15 @@ class NestedCircuitTypeSerializer(serializers.ModelSerializer):
 
 class CircuitSerializer(CustomFieldModelSerializer):
     provider = NestedProviderSerializer()
+    status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES)
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer()
 
     class Meta:
         model = Circuit
         fields = [
-            'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
+            'comments', 'custom_fields', 'created', 'last_updated',
         ]
 
 
@@ -90,8 +92,8 @@ class WritableCircuitSerializer(CustomFieldModelSerializer):
     class Meta:
         model = Circuit
         fields = [
-            'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
+            'comments', 'custom_fields', 'created', 'last_updated',
         ]
 
 

+ 16 - 0
netbox/circuits/constants.py

@@ -1,6 +1,22 @@
 from __future__ import unicode_literals
 
 
+# Circuit statuses
+CIRCUIT_STATUS_DEPROVISIONING = 0
+CIRCUIT_STATUS_ACTIVE = 1
+CIRCUIT_STATUS_PLANNED = 2
+CIRCUIT_STATUS_PROVISIONING = 3
+CIRCUIT_STATUS_OFFLINE = 4
+CIRCUIT_STATUS_DECOMMISSIONED = 5
+CIRCUIT_STATUS_CHOICES = [
+    [CIRCUIT_STATUS_PLANNED, 'Planned'],
+    [CIRCUIT_STATUS_PROVISIONING, 'Provisioning'],
+    [CIRCUIT_STATUS_ACTIVE, 'Active'],
+    [CIRCUIT_STATUS_OFFLINE, 'Offline'],
+    [CIRCUIT_STATUS_DEPROVISIONING, 'Deprovisioning'],
+    [CIRCUIT_STATUS_DECOMMISSIONED, 'Decommissioned'],
+]
+
 # CircuitTermination sides
 TERM_SIDE_A = 'A'
 TERM_SIDE_Z = 'Z'

+ 5 - 0
netbox/circuits/filters.py

@@ -7,6 +7,7 @@ from dcim.models import Site
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from utilities.filters import NumericInFilter
+from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 
 
@@ -77,6 +78,10 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Circuit type (slug)',
     )
+    status = django_filters.MultipleChoiceFilter(
+        choices=CIRCUIT_STATUS_CHOICES,
+        null_value=None
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',

+ 21 - 4
netbox/circuits/forms.py

@@ -8,9 +8,10 @@ from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFi
 from tenancy.forms import TenancyForm
 from tenancy.models import Tenant
 from utilities.forms import (
-    APISelect, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, FilterChoiceField,
-    SmallTextarea, SlugField,
+    APISelect, add_blank_choice, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField,
+    CSVChoiceField, FilterChoiceField, SmallTextarea, SlugField,
 )
+from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Circuit, CircuitTermination, CircuitType, Provider
 
 
@@ -105,7 +106,7 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
     class Meta:
         model = Circuit
         fields = [
-            'cid', 'type', 'provider', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
+            'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
             'comments',
         ]
         help_texts = {
@@ -132,6 +133,11 @@ class CircuitCSVForm(forms.ModelForm):
             'invalid_choice': 'Invalid circuit type.'
         }
     )
+    status = CSVChoiceField(
+        choices=CIRCUIT_STATUS_CHOICES,
+        required=False,
+        help_text='Operational status'
+    )
     tenant = forms.ModelChoiceField(
         queryset=Tenant.objects.all(),
         required=False,
@@ -144,13 +150,16 @@ class CircuitCSVForm(forms.ModelForm):
 
     class Meta:
         model = Circuit
-        fields = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments']
+        fields = [
+            'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
+        ]
 
 
 class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
     pk = forms.ModelMultipleChoiceField(queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput)
     type = forms.ModelChoiceField(queryset=CircuitType.objects.all(), required=False)
     provider = forms.ModelChoiceField(queryset=Provider.objects.all(), required=False)
+    status = forms.ChoiceField(choices=add_blank_choice(CIRCUIT_STATUS_CHOICES), required=False, initial='')
     tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
     commit_rate = forms.IntegerField(required=False, label='Commit rate (Kbps)')
     description = forms.CharField(max_length=100, required=False)
@@ -160,6 +169,13 @@ class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
         nullable_fields = ['tenant', 'commit_rate', 'description', 'comments']
 
 
+def circuit_status_choices():
+    status_counts = {}
+    for status in Circuit.objects.values('status').annotate(count=Count('status')).order_by('status'):
+        status_counts[status['status']] = status['count']
+    return [(s[0], '{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in CIRCUIT_STATUS_CHOICES]
+
+
 class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
     model = Circuit
     q = forms.CharField(required=False, label='Search')
@@ -171,6 +187,7 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
         queryset=Provider.objects.annotate(filter_count=Count('circuits')),
         to_field_name='slug'
     )
+    status = forms.MultipleChoiceField(choices=circuit_status_choices, required=False)
     tenant = FilterChoiceField(
         queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
         to_field_name='slug',

+ 20 - 0
netbox/circuits/migrations/0010_circuit_status.py

@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.9 on 2018-02-06 18:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0009_unicode_literals'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circuit',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[[2, 'Planned'], [3, 'Provisioning'], [1, 'Active'], [4, 'Offline'], [0, 'Deprovisioning'], [5, 'Decommissioned']], default=1),
+        ),
+    ]

+ 8 - 2
netbox/circuits/models.py

@@ -5,12 +5,13 @@ from django.db import models
 from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
 
+from dcim.constants import STATUS_CLASSES
 from dcim.fields import ASNField
 from extras.models import CustomFieldModel, CustomFieldValue
 from tenancy.models import Tenant
 from utilities.models import CreatedUpdatedModel
 from utilities.utils import csv_format
-from .constants import *
+from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
 
 
 @python_2_unicode_compatible
@@ -79,6 +80,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
     cid = models.CharField(max_length=50, verbose_name='Circuit ID')
     provider = models.ForeignKey('Provider', related_name='circuits', on_delete=models.PROTECT)
     type = models.ForeignKey('CircuitType', related_name='circuits', on_delete=models.PROTECT)
+    status = models.PositiveSmallIntegerField(choices=CIRCUIT_STATUS_CHOICES, default=CIRCUIT_STATUS_ACTIVE)
     tenant = models.ForeignKey(Tenant, related_name='circuits', blank=True, null=True, on_delete=models.PROTECT)
     install_date = models.DateField(blank=True, null=True, verbose_name='Date installed')
     commit_rate = models.PositiveIntegerField(blank=True, null=True, verbose_name='Commit rate (Kbps)')
@@ -86,7 +88,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
     comments = models.TextField(blank=True)
     custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
 
-    csv_headers = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description']
+    csv_headers = ['cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description']
 
     class Meta:
         ordering = ['provider', 'cid']
@@ -103,12 +105,16 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
             self.cid,
             self.provider.name,
             self.type.name,
+            self.get_status_display(),
             self.tenant.name if self.tenant else None,
             self.install_date.isoformat() if self.install_date else None,
             self.commit_rate,
             self.description,
         ])
 
+    def get_status_class(self):
+        return STATUS_CLASSES[self.status]
+
     def _get_termination(self, side):
         for ct in self.terminations.all():
             if ct.term_side == side:

+ 6 - 1
netbox/circuits/tables.py

@@ -14,6 +14,10 @@ CIRCUITTYPE_ACTIONS = """
 {% endif %}
 """
 
+STATUS_LABEL = """
+<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
+"""
+
 
 class CircuitTerminationColumn(tables.Column):
 
@@ -76,10 +80,11 @@ class CircuitTable(BaseTable):
     pk = ToggleColumn()
     cid = tables.LinkColumn(verbose_name='ID')
     provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
+    status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
     tenant = tables.TemplateColumn(template_code=COL_TENANT)
     termination_a = CircuitTerminationColumn(orderable=False, verbose_name='A Side')
     termination_z = CircuitTerminationColumn(orderable=False, verbose_name='Z Side')
 
     class Meta(BaseTable.Meta):
         model = Circuit
-        fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'termination_a', 'termination_z', 'description')
+        fields = ('pk', 'cid', 'status', 'type', 'provider', 'tenant', 'termination_a', 'termination_z', 'description')

+ 6 - 0
netbox/templates/circuits/circuit.html

@@ -47,6 +47,12 @@
             </div>
             <table class="table table-hover panel-body attr-table">
                 <tr>
+                    <td>Status</td>
+                    <td>
+                        <span class="label label-{{ circuit.get_status_class }}">{{ circuit.get_status_display }}</span>
+                    </td>
+                </tr>
+                <tr>
                     <td>Provider</td>
                     <td>
                         <a href="{% url 'circuits:provider' slug=circuit.provider.slug %}">{{ circuit.provider }}</a>

+ 1 - 0
netbox/templates/circuits/circuit_edit.html

@@ -8,6 +8,7 @@
             {% render_field form.provider %}
             {% render_field form.cid %}
             {% render_field form.type %}
+            {% render_field form.status %}
             {% render_field form.install_date %}
             <div class="form-group">
                 <label class="col-md-3 control-label" for="id_commit_rate">{{ form.commit_rate.label }}</label>