mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-04 06:38:16 -06:00
16547 add distance to API, forms, tables
This commit is contained in:
parent
db83a676fd
commit
de87763572
@ -36,6 +36,12 @@ The operational status of the circuit. By default, the following statuses are av
|
|||||||
!!! tip "Custom circuit statuses"
|
!!! tip "Custom circuit statuses"
|
||||||
Additional circuit statuses may be defined by setting `Circuit.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
Additional circuit statuses may be defined by setting `Circuit.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||||
|
|
||||||
|
### Distance
|
||||||
|
|
||||||
|
!!! info "This field was introduced in NetBox v4.2."
|
||||||
|
|
||||||
|
The distance between the circuit's two endpoints, including a unit designation (e.g. 100 meters or 25 feet).
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
A brief description of the circuit.
|
A brief description of the circuit.
|
||||||
|
@ -4,6 +4,7 @@ from dcim.api.serializers_.cables import CabledObjectSerializer
|
|||||||
from dcim.api.serializers_.sites import SiteSerializer
|
from dcim.api.serializers_.sites import SiteSerializer
|
||||||
from netbox.api.fields import ChoiceField, RelatedObjectCountField
|
from netbox.api.fields import ChoiceField, RelatedObjectCountField
|
||||||
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
||||||
|
from netbox.choices import DistanceUnitChoices
|
||||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||||
|
|
||||||
from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer
|
from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer
|
||||||
@ -80,13 +81,14 @@ class CircuitSerializer(NetBoxModelSerializer):
|
|||||||
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
||||||
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
||||||
assignments = CircuitGroupAssignmentSerializer_(nested=True, many=True, required=False)
|
assignments = CircuitGroupAssignmentSerializer_(nested=True, many=True, required=False)
|
||||||
|
distance_unit = ChoiceField(choices=DistanceUnitChoices, allow_blank=True, required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant',
|
'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant',
|
||||||
'install_date', 'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z',
|
'install_date', 'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z',
|
||||||
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'assignments',
|
'distance', 'distance_unit', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'assignments',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'provider', 'cid', 'description')
|
brief_fields = ('id', 'url', 'display', 'provider', 'cid', 'description')
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = ('id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate')
|
fields = ('id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit')
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
|
@ -5,6 +5,7 @@ from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, C
|
|||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
|
from netbox.choices import DistanceUnitChoices
|
||||||
from netbox.forms import NetBoxModelBulkEditForm
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import add_blank_choice
|
from utilities.forms import add_blank_choice
|
||||||
@ -160,6 +161,17 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
options=CircuitCommitRateChoices
|
options=CircuitCommitRateChoices
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
distance = forms.DecimalField(
|
||||||
|
label=_('Distance'),
|
||||||
|
min_value=0,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
distance_unit = forms.ChoiceField(
|
||||||
|
label=_('Distance unit'),
|
||||||
|
choices=add_blank_choice(DistanceUnitChoices),
|
||||||
|
required=False,
|
||||||
|
initial=''
|
||||||
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
label=_('Description'),
|
label=_('Description'),
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -171,6 +183,7 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('provider', 'type', 'status', 'description', name=_('Circuit')),
|
FieldSet('provider', 'type', 'status', 'description', name=_('Circuit')),
|
||||||
FieldSet('provider_account', 'install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
|
FieldSet('provider_account', 'install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
|
||||||
|
FieldSet('distance', 'distance_unit', name=_('Attributes')),
|
||||||
FieldSet('tenant', name=_('Tenancy')),
|
FieldSet('tenant', name=_('Tenancy')),
|
||||||
)
|
)
|
||||||
nullable_fields = (
|
nullable_fields = (
|
||||||
|
@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from circuits.choices import *
|
from circuits.choices import *
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
|
from netbox.choices import DistanceUnitChoices
|
||||||
from netbox.forms import NetBoxModelImportForm
|
from netbox.forms import NetBoxModelImportForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
|
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
@ -95,6 +96,12 @@ class CircuitImportForm(NetBoxModelImportForm):
|
|||||||
choices=CircuitStatusChoices,
|
choices=CircuitStatusChoices,
|
||||||
help_text=_('Operational status')
|
help_text=_('Operational status')
|
||||||
)
|
)
|
||||||
|
distance_unit = CSVChoiceField(
|
||||||
|
label=_('Distance unit'),
|
||||||
|
choices=DistanceUnitChoices,
|
||||||
|
required=False,
|
||||||
|
help_text=_('Distance unit')
|
||||||
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
label=_('Tenant'),
|
label=_('Tenant'),
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
@ -107,7 +114,7 @@ class CircuitImportForm(NetBoxModelImportForm):
|
|||||||
model = Circuit
|
model = Circuit
|
||||||
fields = [
|
fields = [
|
||||||
'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date',
|
'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date',
|
||||||
'commit_rate', 'description', 'comments', 'tags'
|
'commit_rate', 'distance', 'distance_unit', 'description', 'comments', 'tags'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from ipam.models import ASN
|
|||||||
from netbox.forms import NetBoxModelForm
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
|
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
|
||||||
from utilities.forms.rendering import FieldSet, TabbedGroups
|
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
|
||||||
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -108,7 +108,17 @@ class CircuitForm(TenancyForm, NetBoxModelForm):
|
|||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('provider', 'provider_account', 'cid', 'type', 'status', 'description', 'tags', name=_('Circuit')),
|
FieldSet(
|
||||||
|
'provider',
|
||||||
|
'provider_account',
|
||||||
|
'cid',
|
||||||
|
'type',
|
||||||
|
'status',
|
||||||
|
InlineFields('distance', 'distance_unit', label=_('Distance')),
|
||||||
|
'description',
|
||||||
|
'tags',
|
||||||
|
name=_('Circuit')
|
||||||
|
),
|
||||||
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
|
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
|
||||||
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
|
|||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
|
|
||||||
from .columns import CommitRateColumn
|
from .columns import CommitRateColumn
|
||||||
|
from .template_code import CIRCUIT_DISTANCE
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'CircuitGroupAssignmentTable',
|
'CircuitGroupAssignmentTable',
|
||||||
@ -76,6 +77,10 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
commit_rate = CommitRateColumn(
|
commit_rate = CommitRateColumn(
|
||||||
verbose_name=_('Commit Rate')
|
verbose_name=_('Commit Rate')
|
||||||
)
|
)
|
||||||
|
distance = columns.TemplateColumn(
|
||||||
|
template_code=CIRCUIT_DISTANCE,
|
||||||
|
order_by=('_abs_distance')
|
||||||
|
)
|
||||||
comments = columns.MarkdownColumn(
|
comments = columns.MarkdownColumn(
|
||||||
verbose_name=_('Comments')
|
verbose_name=_('Comments')
|
||||||
)
|
)
|
||||||
|
4
netbox/circuits/tables/template_code.py
Normal file
4
netbox/circuits/tables/template_code.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CIRCUIT_DISTANCE = """
|
||||||
|
{% load helpers %}
|
||||||
|
{% if record.distance %}{{ record.distance|floatformat:"-2" }} {{ record.distance_unit }}{% endif %}
|
||||||
|
"""
|
@ -5,6 +5,7 @@ from circuits.filtersets import *
|
|||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Cable, Region, Site, SiteGroup
|
from dcim.models import Cable, Region, Site, SiteGroup
|
||||||
from ipam.models import ASN, RIR
|
from ipam.models import ASN, RIR
|
||||||
|
from netbox.choices import DistanceUnitChoices
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.testing import ChangeLoggedFilterSetTests
|
from utilities.testing import ChangeLoggedFilterSetTests
|
||||||
|
|
||||||
@ -222,9 +223,9 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
ProviderNetwork.objects.bulk_create(provider_networks)
|
ProviderNetwork.objects.bulk_create(provider_networks)
|
||||||
|
|
||||||
circuits = (
|
circuits = (
|
||||||
Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', termination_date='2021-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar1'),
|
Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', termination_date='2021-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar1', distance=10, distance_unit=DistanceUnitChoices.UNIT_FOOT),
|
||||||
Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', termination_date='2021-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar2'),
|
Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', termination_date='2021-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar2', distance=20, distance_unit=DistanceUnitChoices.UNIT_METER),
|
||||||
Circuit(provider=providers[0], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', termination_date='2021-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED),
|
Circuit(provider=providers[0], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', termination_date='2021-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED, description='foobar2', distance=30, distance_unit=DistanceUnitChoices.UNIT_METER),
|
||||||
Circuit(provider=providers[1], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', termination_date='2021-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED),
|
Circuit(provider=providers[1], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', termination_date='2021-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED),
|
||||||
Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', termination_date='2021-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', termination_date='2021-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
||||||
Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', termination_date='2021-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', termination_date='2021-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
||||||
@ -289,6 +290,14 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'status': [CircuitStatusChoices.STATUS_ACTIVE, CircuitStatusChoices.STATUS_PLANNED]}
|
params = {'status': [CircuitStatusChoices.STATUS_ACTIVE, CircuitStatusChoices.STATUS_PLANNED]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_distance(self):
|
||||||
|
params = {'distance': [10, 20]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_distance_unit(self):
|
||||||
|
params = {'distance_unit': DistanceUnitChoices.UNIT_FOOT}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
def test_description(self):
|
def test_description(self):
|
||||||
params = {'description': ['foobar1', 'foobar2']}
|
params = {'description': ['foobar1', 'foobar2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
@ -34,6 +34,20 @@
|
|||||||
<th scope="row">{% trans "Status" %}</th>
|
<th scope="row">{% trans "Status" %}</th>
|
||||||
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans "Description" %}</th>
|
||||||
|
<td>{{ object.description|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans "Distance" %}</th>
|
||||||
|
<td>
|
||||||
|
{% if object.distance is not None %}
|
||||||
|
{{ object.distance|floatformat }} {{ object.get_distance_unit_display }}
|
||||||
|
{% else %}
|
||||||
|
{{ ''|placeholder }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Tenant" %}</th>
|
<th scope="row">{% trans "Tenant" %}</th>
|
||||||
<td>
|
<td>
|
||||||
|
Loading…
Reference in New Issue
Block a user