mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #1864: Added a 'status' field to the circuit model
This commit is contained in:
@ -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',
|
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'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',
|
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'comments', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
@ -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)',
|
||||||
|
@ -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,
|
APISelect, add_blank_choice, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField,
|
||||||
SmallTextarea, SlugField,
|
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
netbox/circuits/migrations/0010_circuit_status.py
Normal file
20
netbox/circuits/migrations/0010_circuit_status.py
Normal file
@ -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),
|
||||||
|
),
|
||||||
|
]
|
@ -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:
|
||||||
|
@ -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')
|
||||||
|
@ -46,6 +46,12 @@
|
|||||||
<strong>Circuit</strong>
|
<strong>Circuit</strong>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover panel-body attr-table">
|
<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>
|
<tr>
|
||||||
<td>Provider</td>
|
<td>Provider</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -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>
|
||||||
|
Reference in New Issue
Block a user