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 rest_framework import serializers
 
 
+from circuits.constants import CIRCUIT_STATUS_CHOICES
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 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):
 class CircuitSerializer(CustomFieldModelSerializer):
     provider = NestedProviderSerializer()
     provider = NestedProviderSerializer()
+    status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES)
     type = NestedCircuitTypeSerializer()
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
 
 
     class Meta:
     class Meta:
         model = Circuit
         model = Circuit
         fields = [
         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:
     class Meta:
         model = Circuit
         model = Circuit
         fields = [
         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
 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
 # CircuitTermination sides
 TERM_SIDE_A = 'A'
 TERM_SIDE_A = 'A'
 TERM_SIDE_Z = 'Z'
 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 extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.filters import NumericInFilter
 from utilities.filters import NumericInFilter
+from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 
 
 
 
@@ -77,6 +78,10 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Circuit type (slug)',
         label='Circuit type (slug)',
     )
     )
+    status = django_filters.MultipleChoiceFilter(
+        choices=CIRCUIT_STATUS_CHOICES,
+        null_value=None
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         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.forms import TenancyForm
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.forms import (
 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
 from .models import Circuit, CircuitTermination, CircuitType, Provider
 
 
 
 
@@ -105,7 +106,7 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
     class Meta:
     class Meta:
         model = Circuit
         model = Circuit
         fields = [
         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',
             'comments',
         ]
         ]
         help_texts = {
         help_texts = {
@@ -132,6 +133,11 @@ class CircuitCSVForm(forms.ModelForm):
             'invalid_choice': 'Invalid circuit type.'
             'invalid_choice': 'Invalid circuit type.'
         }
         }
     )
     )
+    status = CSVChoiceField(
+        choices=CIRCUIT_STATUS_CHOICES,
+        required=False,
+        help_text='Operational status'
+    )
     tenant = forms.ModelChoiceField(
     tenant = forms.ModelChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         required=False,
         required=False,
@@ -144,13 +150,16 @@ class CircuitCSVForm(forms.ModelForm):
 
 
     class Meta:
     class Meta:
         model = Circuit
         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):
 class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
     pk = forms.ModelMultipleChoiceField(queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput)
     pk = forms.ModelMultipleChoiceField(queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput)
     type = forms.ModelChoiceField(queryset=CircuitType.objects.all(), required=False)
     type = forms.ModelChoiceField(queryset=CircuitType.objects.all(), required=False)
     provider = forms.ModelChoiceField(queryset=Provider.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)
     tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
     commit_rate = forms.IntegerField(required=False, label='Commit rate (Kbps)')
     commit_rate = forms.IntegerField(required=False, label='Commit rate (Kbps)')
     description = forms.CharField(max_length=100, required=False)
     description = forms.CharField(max_length=100, required=False)
@@ -160,6 +169,13 @@ class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
         nullable_fields = ['tenant', 'commit_rate', 'description', 'comments']
         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):
 class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
     model = Circuit
     model = Circuit
     q = forms.CharField(required=False, label='Search')
     q = forms.CharField(required=False, label='Search')
@@ -171,6 +187,7 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
         queryset=Provider.objects.annotate(filter_count=Count('circuits')),
         queryset=Provider.objects.annotate(filter_count=Count('circuits')),
         to_field_name='slug'
         to_field_name='slug'
     )
     )
+    status = forms.MultipleChoiceField(choices=circuit_status_choices, required=False)
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
         queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
         to_field_name='slug',
         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.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.encoding import python_2_unicode_compatible
 
 
+from dcim.constants import STATUS_CLASSES
 from dcim.fields import ASNField
 from dcim.fields import ASNField
 from extras.models import CustomFieldModel, CustomFieldValue
 from extras.models import CustomFieldModel, CustomFieldValue
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.models import CreatedUpdatedModel
 from utilities.models import CreatedUpdatedModel
 from utilities.utils import csv_format
 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
 @python_2_unicode_compatible
@@ -79,6 +80,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
     cid = models.CharField(max_length=50, verbose_name='Circuit ID')
     cid = models.CharField(max_length=50, verbose_name='Circuit ID')
     provider = models.ForeignKey('Provider', related_name='circuits', on_delete=models.PROTECT)
     provider = models.ForeignKey('Provider', related_name='circuits', on_delete=models.PROTECT)
     type = models.ForeignKey('CircuitType', 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)
     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')
     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)')
     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)
     comments = models.TextField(blank=True)
     custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
     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:
     class Meta:
         ordering = ['provider', 'cid']
         ordering = ['provider', 'cid']
@@ -103,12 +105,16 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
             self.cid,
             self.cid,
             self.provider.name,
             self.provider.name,
             self.type.name,
             self.type.name,
+            self.get_status_display(),
             self.tenant.name if self.tenant else None,
             self.tenant.name if self.tenant else None,
             self.install_date.isoformat() if self.install_date else None,
             self.install_date.isoformat() if self.install_date else None,
             self.commit_rate,
             self.commit_rate,
             self.description,
             self.description,
         ])
         ])
 
 
+    def get_status_class(self):
+        return STATUS_CLASSES[self.status]
+
     def _get_termination(self, side):
     def _get_termination(self, side):
         for ct in self.terminations.all():
         for ct in self.terminations.all():
             if ct.term_side == side:
             if ct.term_side == side:

+ 6 - 1
netbox/circuits/tables.py

@@ -14,6 +14,10 @@ CIRCUITTYPE_ACTIONS = """
 {% endif %}
 {% endif %}
 """
 """
 
 
+STATUS_LABEL = """
+<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
+"""
+
 
 
 class CircuitTerminationColumn(tables.Column):
 class CircuitTerminationColumn(tables.Column):
 
 
@@ -76,10 +80,11 @@ class CircuitTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
     cid = tables.LinkColumn(verbose_name='ID')
     cid = tables.LinkColumn(verbose_name='ID')
     provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
     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)
     tenant = tables.TemplateColumn(template_code=COL_TENANT)
     termination_a = CircuitTerminationColumn(orderable=False, verbose_name='A Side')
     termination_a = CircuitTerminationColumn(orderable=False, verbose_name='A Side')
     termination_z = CircuitTerminationColumn(orderable=False, verbose_name='Z Side')
     termination_z = CircuitTerminationColumn(orderable=False, verbose_name='Z Side')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Circuit
         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>
             </div>
             <table class="table table-hover panel-body attr-table">
             <table class="table table-hover panel-body attr-table">
                 <tr>
                 <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>Provider</td>
                     <td>
                     <td>
                         <a href="{% url 'circuits:provider' slug=circuit.provider.slug %}">{{ circuit.provider }}</a>
                         <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.provider %}
             {% render_field form.cid %}
             {% render_field form.cid %}
             {% render_field form.type %}
             {% render_field form.type %}
+            {% render_field form.status %}
             {% render_field form.install_date %}
             {% render_field form.install_date %}
             <div class="form-group">
             <div class="form-group">
                 <label class="col-md-3 control-label" for="id_commit_rate">{{ form.commit_rate.label }}</label>
                 <label class="col-md-3 control-label" for="id_commit_rate">{{ form.commit_rate.label }}</label>