mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-20 20:32:25 -06:00
Merge v2.5 work
This commit is contained in:
52
netbox/circuits/api/nested_serializers.py
Normal file
52
netbox/circuits/api/nested_serializers.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from utilities.api import WritableNestedSerializer
|
||||
|
||||
__all__ = [
|
||||
'NestedCircuitSerializer',
|
||||
'NestedCircuitTerminationSerializer',
|
||||
'NestedCircuitTypeSerializer',
|
||||
'NestedProviderSerializer',
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Providers
|
||||
#
|
||||
|
||||
class NestedProviderSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Circuits
|
||||
#
|
||||
|
||||
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedCircuitSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
||||
|
||||
class Meta:
|
||||
model = Circuit
|
||||
fields = ['id', 'url', 'cid']
|
||||
|
||||
|
||||
class NestedCircuitTerminationSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
||||
circuit = NestedCircuitSerializer()
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = ['id', 'url', 'circuit', 'term_side']
|
||||
@@ -1,14 +1,13 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import serializers
|
||||
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
|
||||
|
||||
from circuits.constants import CIRCUIT_STATUS_CHOICES
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from dcim.api.serializers import NestedInterfaceSerializer, NestedSiteSerializer
|
||||
from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
|
||||
from dcim.api.serializers import ConnectedEndpointSerializer
|
||||
from extras.api.customfields import CustomFieldModelSerializer
|
||||
from tenancy.api.serializers import NestedTenantSerializer
|
||||
from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from utilities.api import ChoiceField, ValidatedModelSerializer
|
||||
from .nested_serializers import *
|
||||
|
||||
|
||||
#
|
||||
@@ -26,16 +25,8 @@ class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class NestedProviderSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Circuit types
|
||||
# Circuits
|
||||
#
|
||||
|
||||
class CircuitTypeSerializer(ValidatedModelSerializer):
|
||||
@@ -45,18 +36,6 @@ class CircuitTypeSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Circuits
|
||||
#
|
||||
|
||||
class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
provider = NestedProviderSerializer()
|
||||
status = ChoiceField(choices=CIRCUIT_STATUS_CHOICES, required=False)
|
||||
@@ -72,25 +51,14 @@ class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class NestedCircuitSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
||||
|
||||
class Meta:
|
||||
model = Circuit
|
||||
fields = ['id', 'url', 'cid']
|
||||
|
||||
|
||||
#
|
||||
# Circuit Terminations
|
||||
#
|
||||
|
||||
class CircuitTerminationSerializer(ValidatedModelSerializer):
|
||||
class CircuitTerminationSerializer(ConnectedEndpointSerializer):
|
||||
circuit = NestedCircuitSerializer()
|
||||
site = NestedSiteSerializer()
|
||||
interface = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'id', 'circuit', 'term_side', 'site', 'interface', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
|
||||
'id', 'circuit', 'term_side', 'site', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
|
||||
'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
|
||||
]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import routers
|
||||
|
||||
from . import views
|
||||
@@ -17,7 +15,7 @@ router = routers.DefaultRouter()
|
||||
router.APIRootView = CircuitsRootView
|
||||
|
||||
# Field choices
|
||||
router.register(r'_choices', views.CircuitsFieldChoicesViewSet, base_name='field-choice')
|
||||
router.register(r'_choices', views.CircuitsFieldChoicesViewSet, basename='field-choice')
|
||||
|
||||
# Providers
|
||||
router.register(r'providers', views.ProviderViewSet)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
@@ -31,7 +29,7 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
class ProviderViewSet(CustomFieldModelViewSet):
|
||||
queryset = Provider.objects.prefetch_related('tags')
|
||||
serializer_class = serializers.ProviderSerializer
|
||||
filter_class = filters.ProviderFilter
|
||||
filterset_class = filters.ProviderFilter
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk=None):
|
||||
@@ -51,7 +49,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
|
||||
class CircuitTypeViewSet(ModelViewSet):
|
||||
queryset = CircuitType.objects.all()
|
||||
serializer_class = serializers.CircuitTypeSerializer
|
||||
filter_class = filters.CircuitTypeFilter
|
||||
filterset_class = filters.CircuitTypeFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -61,7 +59,7 @@ class CircuitTypeViewSet(ModelViewSet):
|
||||
class CircuitViewSet(CustomFieldModelViewSet):
|
||||
queryset = Circuit.objects.select_related('type', 'tenant', 'provider').prefetch_related('tags')
|
||||
serializer_class = serializers.CircuitSerializer
|
||||
filter_class = filters.CircuitFilter
|
||||
filterset_class = filters.CircuitFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -69,6 +67,8 @@ class CircuitViewSet(CustomFieldModelViewSet):
|
||||
#
|
||||
|
||||
class CircuitTerminationViewSet(ModelViewSet):
|
||||
queryset = CircuitTermination.objects.select_related('circuit', 'site', 'interface__device')
|
||||
queryset = CircuitTermination.objects.select_related(
|
||||
'circuit', 'site', 'connected_endpoint__device', 'cable'
|
||||
)
|
||||
serializer_class = serializers.CircuitTerminationSerializer
|
||||
filter_class = filters.CircuitTerminationFilter
|
||||
filterset_class = filters.CircuitTerminationFilter
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
# Circuit statuses
|
||||
CIRCUIT_STATUS_DEPROVISIONING = 0
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django_filters
|
||||
from django.db.models import Q
|
||||
|
||||
@@ -12,18 +10,21 @@ from .models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
|
||||
|
||||
class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='circuits__terminations__site',
|
||||
field_name='circuits__terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='circuits__terminations__site__slug',
|
||||
field_name='circuits__terminations__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
@@ -54,7 +55,10 @@ class CircuitTypeFilter(django_filters.FilterSet):
|
||||
|
||||
|
||||
class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
@@ -64,7 +68,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Provider (ID)',
|
||||
)
|
||||
provider = django_filters.ModelMultipleChoiceFilter(
|
||||
name='provider__slug',
|
||||
field_name='provider__slug',
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Provider (slug)',
|
||||
@@ -74,7 +78,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Circuit type (ID)',
|
||||
)
|
||||
type = django_filters.ModelMultipleChoiceFilter(
|
||||
name='type__slug',
|
||||
field_name='type__slug',
|
||||
queryset=CircuitType.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Circuit type (slug)',
|
||||
@@ -88,18 +92,18 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='tenant__slug',
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='terminations__site',
|
||||
field_name='terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='terminations__site__slug',
|
||||
field_name='terminations__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
@@ -117,6 +121,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
Q(cid__icontains=value) |
|
||||
Q(terminations__xconnect_id__icontains=value) |
|
||||
Q(terminations__pp_info__icontains=value) |
|
||||
Q(terminations__description__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
).distinct()
|
||||
@@ -136,7 +141,7 @@ class CircuitTerminationFilter(django_filters.FilterSet):
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='site__slug',
|
||||
field_name='site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
@@ -152,5 +157,6 @@ class CircuitTerminationFilter(django_filters.FilterSet):
|
||||
return queryset.filter(
|
||||
Q(circuit__cid__icontains=value) |
|
||||
Q(xconnect_id__icontains=value) |
|
||||
Q(pp_info__icontains=value)
|
||||
Q(pp_info__icontains=value) |
|
||||
Q(description__icontains=value)
|
||||
).distinct()
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.db.models import Count
|
||||
from taggit.forms import TagField
|
||||
|
||||
from dcim.models import Site, Device, Interface, Rack
|
||||
from dcim.models import Site
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
AnnotatedMultipleChoiceField, APISelect, add_blank_choice, BootstrapMixin, ChainedFieldsMixin,
|
||||
ChainedModelChoiceField, CommentField, CSVChoiceField, FilterChoiceField, SmallTextarea, SlugField,
|
||||
AnnotatedMultipleChoiceField, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField, FilterChoiceField,
|
||||
SmallTextarea, SlugField,
|
||||
)
|
||||
from .constants import CIRCUIT_STATUS_CHOICES
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
@@ -23,14 +21,22 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
class ProviderForm(BootstrapMixin, CustomFieldForm):
|
||||
slug = SlugField()
|
||||
comments = CommentField()
|
||||
tags = TagField(required=False)
|
||||
tags = TagField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags']
|
||||
fields = [
|
||||
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'noc_contact': SmallTextarea(attrs={'rows': 5}),
|
||||
'admin_contact': SmallTextarea(attrs={'rows': 5}),
|
||||
'noc_contact': SmallTextarea(
|
||||
attrs={'rows': 5}
|
||||
),
|
||||
'admin_contact': SmallTextarea(
|
||||
attrs={'rows': 5}
|
||||
),
|
||||
}
|
||||
help_texts = {
|
||||
'name': "Full name of the provider",
|
||||
@@ -56,23 +62,57 @@ class ProviderCSVForm(forms.ModelForm):
|
||||
|
||||
|
||||
class ProviderBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(queryset=Provider.objects.all(), widget=forms.MultipleHiddenInput)
|
||||
asn = forms.IntegerField(required=False, label='ASN')
|
||||
account = forms.CharField(max_length=30, required=False, label='Account number')
|
||||
portal_url = forms.URLField(required=False, label='Portal')
|
||||
noc_contact = forms.CharField(required=False, widget=SmallTextarea, label='NOC contact')
|
||||
admin_contact = forms.CharField(required=False, widget=SmallTextarea, label='Admin contact')
|
||||
comments = CommentField(widget=SmallTextarea)
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
asn = forms.IntegerField(
|
||||
required=False,
|
||||
label='ASN'
|
||||
)
|
||||
account = forms.CharField(
|
||||
max_length=30,
|
||||
required=False,
|
||||
label='Account number'
|
||||
)
|
||||
portal_url = forms.URLField(
|
||||
required=False,
|
||||
label='Portal'
|
||||
)
|
||||
noc_contact = forms.CharField(
|
||||
required=False,
|
||||
widget=SmallTextarea,
|
||||
label='NOC contact'
|
||||
)
|
||||
admin_contact = forms.CharField(
|
||||
required=False,
|
||||
widget=SmallTextarea,
|
||||
label='Admin contact'
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
||||
nullable_fields = [
|
||||
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
||||
]
|
||||
|
||||
|
||||
class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Provider
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
site = FilterChoiceField(queryset=Site.objects.all(), to_field_name='slug')
|
||||
asn = forms.IntegerField(required=False, label='ASN')
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug'
|
||||
)
|
||||
asn = forms.IntegerField(
|
||||
required=False,
|
||||
label='ASN'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@@ -84,7 +124,9 @@ class CircuitTypeForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ['name', 'slug']
|
||||
fields = [
|
||||
'name', 'slug',
|
||||
]
|
||||
|
||||
|
||||
class CircuitTypeCSVForm(forms.ModelForm):
|
||||
@@ -104,7 +146,9 @@ class CircuitTypeCSVForm(forms.ModelForm):
|
||||
|
||||
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
comments = CommentField()
|
||||
tags = TagField(required=False)
|
||||
tags = TagField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Circuit
|
||||
@@ -159,28 +203,61 @@ class CircuitCSVForm(forms.ModelForm):
|
||||
|
||||
|
||||
class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, 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)
|
||||
comments = CommentField(widget=SmallTextarea)
|
||||
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
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['tenant', 'commit_rate', 'description', 'comments']
|
||||
nullable_fields = [
|
||||
'tenant', 'commit_rate', 'description', 'comments',
|
||||
]
|
||||
|
||||
|
||||
class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Circuit
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
type = FilterChoiceField(
|
||||
queryset=CircuitType.objects.annotate(filter_count=Count('circuits')),
|
||||
queryset=CircuitType.objects.annotate(
|
||||
filter_count=Count('circuits')
|
||||
),
|
||||
to_field_name='slug'
|
||||
)
|
||||
provider = FilterChoiceField(
|
||||
queryset=Provider.objects.annotate(filter_count=Count('circuits')),
|
||||
queryset=Provider.objects.annotate(
|
||||
filter_count=Count('circuits')
|
||||
),
|
||||
to_field_name='slug'
|
||||
)
|
||||
status = AnnotatedMultipleChoiceField(
|
||||
@@ -190,74 +267,35 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
required=False
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
|
||||
queryset=Tenant.objects.annotate(
|
||||
filter_count=Count('circuits')
|
||||
),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --'
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.annotate(filter_count=Count('circuit_terminations')),
|
||||
queryset=Site.objects.annotate(
|
||||
filter_count=Count('circuit_terminations')
|
||||
),
|
||||
to_field_name='slug'
|
||||
)
|
||||
commit_rate = forms.IntegerField(required=False, min_value=0, label='Commit rate (Kbps)')
|
||||
commit_rate = forms.IntegerField(
|
||||
required=False,
|
||||
min_value=0,
|
||||
label='Commit rate (Kbps)'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Circuit terminations
|
||||
#
|
||||
|
||||
class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
||||
site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
widget=forms.Select(
|
||||
attrs={'filter-for': 'rack'}
|
||||
)
|
||||
)
|
||||
rack = ChainedModelChoiceField(
|
||||
queryset=Rack.objects.all(),
|
||||
chains=(
|
||||
('site', 'site'),
|
||||
),
|
||||
required=False,
|
||||
label='Rack',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/racks/?site_id={{site}}',
|
||||
attrs={'filter-for': 'device', 'nullable': 'true'}
|
||||
)
|
||||
)
|
||||
device = ChainedModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
chains=(
|
||||
('site', 'site'),
|
||||
('rack', 'rack'),
|
||||
),
|
||||
required=False,
|
||||
label='Device',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
|
||||
display_field='display_name',
|
||||
attrs={'filter-for': 'interface'}
|
||||
)
|
||||
)
|
||||
interface = ChainedModelChoiceField(
|
||||
queryset=Interface.objects.connectable().select_related(
|
||||
'circuit_termination', 'connected_as_a', 'connected_as_b'
|
||||
),
|
||||
chains=(
|
||||
('device', 'device'),
|
||||
),
|
||||
required=False,
|
||||
label='Interface',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/interfaces/?device_id={{device}}&type=physical',
|
||||
disabled_indicator='is_connected'
|
||||
)
|
||||
)
|
||||
class CircuitTerminationForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'term_side', 'site', 'rack', 'device', 'interface', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'pp_info',
|
||||
'term_side', 'site', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description',
|
||||
]
|
||||
help_texts = {
|
||||
'port_speed': "Physical circuit speed",
|
||||
@@ -267,25 +305,3 @@ class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm
|
||||
widgets = {
|
||||
'term_side': forms.HiddenInput(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# Initialize helper selectors
|
||||
instance = kwargs.get('instance')
|
||||
if instance and instance.interface is not None:
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
initial['rack'] = instance.interface.device.rack
|
||||
initial['device'] = instance.interface.device
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super(CircuitTerminationForm, self).__init__(*args, **kwargs)
|
||||
|
||||
# Mark connected interfaces as disabled
|
||||
self.fields['interface'].choices = []
|
||||
for iface in self.fields['interface'].queryset:
|
||||
self.fields['interface'].choices.append(
|
||||
(iface.id, {
|
||||
'label': iface.name,
|
||||
'disabled': iface.is_connected and iface.pk != self.initial.get('interface'),
|
||||
})
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-06-22 18:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-06-22 18:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-13 19:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-07-26 21:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-08 20:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-12-13 16:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-17 20:08
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-04-19 17:17
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-05-24 15:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- 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
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.12 on 2018-05-22 19:04
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.12 on 2018-06-13 17:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
89
netbox/circuits/migrations/0013_cables.py
Normal file
89
netbox/circuits/migrations/0013_cables.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import sys
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
||||
|
||||
|
||||
def circuit_terminations_to_cables(apps, schema_editor):
|
||||
"""
|
||||
Copy all existing CircuitTermination Interface associations as Cables
|
||||
"""
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
|
||||
# Load content types
|
||||
circuittermination_type = ContentType.objects.get_for_model(CircuitTermination)
|
||||
interface_type = ContentType.objects.get_for_model(Interface)
|
||||
|
||||
# Create a new Cable instance from each console connection
|
||||
if 'test' not in sys.argv:
|
||||
print("\n Adding circuit terminations... ", end='', flush=True)
|
||||
for circuittermination in CircuitTermination.objects.filter(interface__isnull=False):
|
||||
|
||||
# Create the new Cable
|
||||
cable = Cable.objects.create(
|
||||
termination_a_type=circuittermination_type,
|
||||
termination_a_id=circuittermination.id,
|
||||
termination_b_type=interface_type,
|
||||
termination_b_id=circuittermination.interface_id,
|
||||
status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
# Cache the Cable on its two termination points
|
||||
CircuitTermination.objects.filter(pk=circuittermination.pk).update(
|
||||
cable=cable,
|
||||
connected_endpoint=circuittermination.interface,
|
||||
connection_status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
# Cache the connected Cable on the Interface
|
||||
Interface.objects.filter(pk=circuittermination.interface_id).update(
|
||||
cable=cable,
|
||||
_connected_circuittermination=circuittermination,
|
||||
connection_status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
cable_count = Cable.objects.filter(termination_a_type=circuittermination_type).count()
|
||||
if 'test' not in sys.argv:
|
||||
print("{} cables created".format(cable_count))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0012_change_logging'),
|
||||
('dcim', '0066_cables'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Add new CircuitTermination fields
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='connected_endpoint',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Interface'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='connection_status',
|
||||
field=models.NullBooleanField(),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='cable',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Cable'),
|
||||
),
|
||||
|
||||
# Copy CircuitTermination connections to Interfaces as Cables
|
||||
migrations.RunPython(circuit_terminations_to_cables),
|
||||
|
||||
# Remove interface field from CircuitTermination
|
||||
migrations.RemoveField(
|
||||
model_name='circuittermination',
|
||||
name='interface',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.3 on 2018-11-05 18:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0013_cables'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
]
|
||||
@@ -1,20 +1,17 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from dcim.constants import STATUS_CLASSES
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, STATUS_CLASSES
|
||||
from dcim.fields import ASNField
|
||||
from dcim.models import CableTermination
|
||||
from extras.models import CustomFieldModel, ObjectChange
|
||||
from utilities.models import ChangeLoggedModel
|
||||
from utilities.utils import serialize_object
|
||||
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Provider(ChangeLoggedModel, CustomFieldModel):
|
||||
"""
|
||||
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
|
||||
@@ -84,7 +81,6 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CircuitType(ChangeLoggedModel):
|
||||
"""
|
||||
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
|
||||
@@ -116,12 +112,11 @@ class CircuitType(ChangeLoggedModel):
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Circuit(ChangeLoggedModel, CustomFieldModel):
|
||||
"""
|
||||
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
|
||||
circuits. Each circuit is also assigned a CircuitType and a Site. A Circuit may be terminated to a specific device
|
||||
interface, but this is not required. Circuit port speed and commit rate are measured in Kbps.
|
||||
circuits. Each circuit is also assigned a CircuitType and a Site. Circuit port speed and commit rate are measured
|
||||
in Kbps.
|
||||
"""
|
||||
cid = models.CharField(
|
||||
max_length=50,
|
||||
@@ -217,8 +212,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
||||
return self._get_termination('Z')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CircuitTermination(models.Model):
|
||||
class CircuitTermination(CableTermination):
|
||||
circuit = models.ForeignKey(
|
||||
to='circuits.Circuit',
|
||||
on_delete=models.CASCADE,
|
||||
@@ -234,13 +228,17 @@ class CircuitTermination(models.Model):
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuit_terminations'
|
||||
)
|
||||
interface = models.OneToOneField(
|
||||
connected_endpoint = models.OneToOneField(
|
||||
to='dcim.Interface',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuit_termination',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='+',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
connection_status = models.NullBooleanField(
|
||||
choices=CONNECTION_STATUS_CHOICES,
|
||||
blank=True
|
||||
)
|
||||
port_speed = models.PositiveIntegerField(
|
||||
verbose_name='Port speed (Kbps)'
|
||||
)
|
||||
@@ -260,13 +258,17 @@ class CircuitTermination(models.Model):
|
||||
blank=True,
|
||||
verbose_name='Patch panel/port(s)'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['circuit', 'term_side']
|
||||
unique_together = ['circuit', 'term_side']
|
||||
|
||||
def __str__(self):
|
||||
return '{} (Side {})'.format(self.circuit, self.get_term_side_display())
|
||||
return 'Side {}'.format(self.get_term_side_display())
|
||||
|
||||
def log_change(self, user, request_id, action):
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django_tables2 as tables
|
||||
from django.utils.safestring import mark_safe
|
||||
from django_tables2.utils import Accessor
|
||||
@@ -25,12 +23,6 @@ STATUS_LABEL = """
|
||||
class CircuitTerminationColumn(tables.Column):
|
||||
|
||||
def render(self, value):
|
||||
if value.interface:
|
||||
return mark_safe('<a href="{}" title="{}">{}</a>'.format(
|
||||
value.interface.device.get_absolute_url(),
|
||||
value.site,
|
||||
value.interface.device
|
||||
))
|
||||
return mark_safe('<a href="{}">{}</a>'.format(
|
||||
value.site.get_absolute_url(),
|
||||
value.site
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
|
||||
@@ -15,7 +13,7 @@ class ProviderTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super(ProviderTest, self).setUp()
|
||||
super().setUp()
|
||||
|
||||
self.provider1 = Provider.objects.create(name='Test Provider 1', slug='test-provider-1')
|
||||
self.provider2 = Provider.objects.create(name='Test Provider 2', slug='test-provider-2')
|
||||
@@ -137,7 +135,7 @@ class CircuitTypeTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super(CircuitTypeTest, self).setUp()
|
||||
super().setUp()
|
||||
|
||||
self.circuittype1 = CircuitType.objects.create(name='Test Circuit Type 1', slug='test-circuit-type-1')
|
||||
self.circuittype2 = CircuitType.objects.create(name='Test Circuit Type 2', slug='test-circuit-type-2')
|
||||
@@ -212,7 +210,7 @@ class CircuitTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super(CircuitTest, self).setUp()
|
||||
super().setUp()
|
||||
|
||||
self.provider1 = Provider.objects.create(name='Test Provider 1', slug='test-provider-1')
|
||||
self.provider2 = Provider.objects.create(name='Test Provider 2', slug='test-provider-2')
|
||||
@@ -328,46 +326,26 @@ class CircuitTerminationTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super(CircuitTerminationTest, self).setUp()
|
||||
super().setUp()
|
||||
|
||||
self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||
self.site2 = Site.objects.create(name='Test Site 2', slug='test-site-2')
|
||||
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||
devicetype = DeviceType.objects.create(
|
||||
manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1', is_network_device=True
|
||||
)
|
||||
devicerole = DeviceRole.objects.create(
|
||||
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||
)
|
||||
device1 = Device.objects.create(
|
||||
device_type=devicetype, device_role=devicerole, name='Test Device 1', site=self.site1
|
||||
)
|
||||
device2 = Device.objects.create(
|
||||
device_type=devicetype, device_role=devicerole, name='Test Device 2', site=self.site2
|
||||
)
|
||||
self.interface1 = Interface.objects.create(device=device1, name='Test Interface 1')
|
||||
self.interface2 = Interface.objects.create(device=device2, name='Test Interface 2')
|
||||
self.interface3 = Interface.objects.create(device=device1, name='Test Interface 3')
|
||||
self.interface4 = Interface.objects.create(device=device2, name='Test Interface 4')
|
||||
self.interface5 = Interface.objects.create(device=device1, name='Test Interface 5')
|
||||
self.interface6 = Interface.objects.create(device=device2, name='Test Interface 6')
|
||||
|
||||
provider = Provider.objects.create(name='Test Provider', slug='test-provider')
|
||||
circuittype = CircuitType.objects.create(name='Test Circuit Type', slug='test-circuit-type')
|
||||
self.circuit1 = Circuit.objects.create(cid='TEST0001', provider=provider, type=circuittype)
|
||||
self.circuit2 = Circuit.objects.create(cid='TEST0002', provider=provider, type=circuittype)
|
||||
self.circuit3 = Circuit.objects.create(cid='TEST0003', provider=provider, type=circuittype)
|
||||
self.circuittermination1 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_A, site=self.site1, interface=self.interface1, port_speed=1000000
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
)
|
||||
self.circuittermination2 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_Z, site=self.site2, interface=self.interface2, port_speed=1000000
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_Z, site=self.site2, port_speed=1000000
|
||||
)
|
||||
self.circuittermination3 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_A, site=self.site1, interface=self.interface3, port_speed=1000000
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
)
|
||||
self.circuittermination4 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_Z, site=self.site2, interface=self.interface4, port_speed=1000000
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_Z, site=self.site2, port_speed=1000000
|
||||
)
|
||||
|
||||
def test_get_circuittermination(self):
|
||||
@@ -390,7 +368,6 @@ class CircuitTerminationTest(APITestCase):
|
||||
'circuit': self.circuit3.pk,
|
||||
'term_side': TERM_SIDE_A,
|
||||
'site': self.site1.pk,
|
||||
'interface': self.interface5.pk,
|
||||
'port_speed': 1000000,
|
||||
}
|
||||
|
||||
@@ -403,20 +380,18 @@ class CircuitTerminationTest(APITestCase):
|
||||
self.assertEqual(circuittermination4.circuit_id, data['circuit'])
|
||||
self.assertEqual(circuittermination4.term_side, data['term_side'])
|
||||
self.assertEqual(circuittermination4.site_id, data['site'])
|
||||
self.assertEqual(circuittermination4.interface_id, data['interface'])
|
||||
self.assertEqual(circuittermination4.port_speed, data['port_speed'])
|
||||
|
||||
def test_update_circuittermination(self):
|
||||
|
||||
circuittermination5 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit3, term_side=TERM_SIDE_A, site=self.site1, interface=self.interface5, port_speed=1000000
|
||||
circuit=self.circuit3, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
)
|
||||
|
||||
data = {
|
||||
'circuit': self.circuit3.pk,
|
||||
'term_side': TERM_SIDE_Z,
|
||||
'site': self.site2.pk,
|
||||
'interface': self.interface6.pk,
|
||||
'port_speed': 1000000,
|
||||
}
|
||||
|
||||
@@ -428,7 +403,6 @@ class CircuitTerminationTest(APITestCase):
|
||||
circuittermination1 = CircuitTermination.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(circuittermination1.term_side, data['term_side'])
|
||||
self.assertEqual(circuittermination1.site_id, data['site'])
|
||||
self.assertEqual(circuittermination1.interface_id, data['interface'])
|
||||
self.assertEqual(circuittermination1.port_speed, data['port_speed'])
|
||||
|
||||
def test_delete_circuittermination(self):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from dcim.views import CableCreateView, CableTraceView
|
||||
from extras.views import ObjectChangeLogView
|
||||
from . import views
|
||||
from .models import Circuit, CircuitType, Provider
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
|
||||
app_name = 'circuits'
|
||||
urlpatterns = [
|
||||
@@ -44,5 +43,7 @@ urlpatterns = [
|
||||
url(r'^circuits/(?P<circuit>\d+)/terminations/add/$', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
||||
url(r'^circuit-terminations/(?P<termination_a_id>\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
||||
|
||||
]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
@@ -134,7 +132,7 @@ class CircuitListView(ObjectListView):
|
||||
queryset = Circuit.objects.select_related(
|
||||
'provider', 'type', 'tenant'
|
||||
).prefetch_related(
|
||||
'terminations__site', 'terminations__interface__device'
|
||||
'terminations__site'
|
||||
)
|
||||
filter = filters.CircuitFilter
|
||||
filter_form = forms.CircuitFilterForm
|
||||
@@ -148,12 +146,12 @@ class CircuitView(View):
|
||||
|
||||
circuit = get_object_or_404(Circuit.objects.select_related('provider', 'type', 'tenant__group'), pk=pk)
|
||||
termination_a = CircuitTermination.objects.select_related(
|
||||
'site__region', 'interface__device'
|
||||
'site__region', 'connected_endpoint__device'
|
||||
).filter(
|
||||
circuit=circuit, term_side=TERM_SIDE_A
|
||||
).first()
|
||||
termination_z = CircuitTermination.objects.select_related(
|
||||
'site__region', 'interface__device'
|
||||
'site__region', 'connected_endpoint__device'
|
||||
).filter(
|
||||
circuit=circuit, term_side=TERM_SIDE_Z
|
||||
).first()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework.exceptions import APIException
|
||||
|
||||
|
||||
|
||||
249
netbox/dcim/api/nested_serializers.py
Normal file
249
netbox/dcim/api/nested_serializers.py
Normal file
@@ -0,0 +1,249 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceType, DeviceRole, FrontPort, FrontPortTemplate,
|
||||
Interface, Manufacturer, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackRole, RearPort, RearPortTemplate,
|
||||
Region, Site, VirtualChassis,
|
||||
)
|
||||
from utilities.api import ChoiceField, WritableNestedSerializer
|
||||
|
||||
__all__ = [
|
||||
'NestedCableSerializer',
|
||||
'NestedConsolePortSerializer',
|
||||
'NestedConsoleServerPortSerializer',
|
||||
'NestedDeviceBaySerializer',
|
||||
'NestedDeviceRoleSerializer',
|
||||
'NestedDeviceSerializer',
|
||||
'NestedDeviceTypeSerializer',
|
||||
'NestedFrontPortSerializer',
|
||||
'NestedFrontPortTemplateSerializer',
|
||||
'NestedInterfaceSerializer',
|
||||
'NestedManufacturerSerializer',
|
||||
'NestedPlatformSerializer',
|
||||
'NestedPowerOutletSerializer',
|
||||
'NestedPowerPortSerializer',
|
||||
'NestedRackGroupSerializer',
|
||||
'NestedRackRoleSerializer',
|
||||
'NestedRackSerializer',
|
||||
'NestedRearPortSerializer',
|
||||
'NestedRearPortTemplateSerializer',
|
||||
'NestedRegionSerializer',
|
||||
'NestedSiteSerializer',
|
||||
'NestedVirtualChassisSerializer',
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Regions/sites
|
||||
#
|
||||
|
||||
class NestedRegionSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
||||
|
||||
class Meta:
|
||||
model = Region
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedSiteSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
||||
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Racks
|
||||
#
|
||||
|
||||
class NestedRackGroupSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
|
||||
|
||||
class Meta:
|
||||
model = RackGroup
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedRackRoleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
||||
|
||||
class Meta:
|
||||
model = RackRole
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedRackSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = ['id', 'url', 'name', 'display_name']
|
||||
|
||||
|
||||
#
|
||||
# Device types
|
||||
#
|
||||
|
||||
class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
||||
|
||||
class Meta:
|
||||
model = Manufacturer
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DeviceType
|
||||
fields = ['id', 'url', 'manufacturer', 'model', 'slug']
|
||||
|
||||
|
||||
class NestedRearPortTemplateSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
|
||||
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
|
||||
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
||||
|
||||
class Meta:
|
||||
model = DeviceRole
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedPlatformSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
||||
|
||||
class Meta:
|
||||
model = Platform
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedDeviceSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = ['id', 'url', 'name', 'display_name']
|
||||
|
||||
|
||||
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status']
|
||||
|
||||
|
||||
class NestedConsolePortSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status']
|
||||
|
||||
|
||||
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status']
|
||||
|
||||
|
||||
class NestedPowerPortSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status']
|
||||
|
||||
|
||||
class NestedInterfaceSerializer(WritableNestedSerializer):
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status']
|
||||
|
||||
|
||||
class NestedRearPortSerializer(WritableNestedSerializer):
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||
|
||||
|
||||
class NestedFrontPortSerializer(WritableNestedSerializer):
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
|
||||
|
||||
class Meta:
|
||||
model = FrontPort
|
||||
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||
|
||||
|
||||
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DeviceBay
|
||||
fields = ['id', 'url', 'device', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Cables
|
||||
#
|
||||
|
||||
class NestedCableSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
fields = ['id', 'url', 'label']
|
||||
|
||||
|
||||
#
|
||||
# Virtual chassis
|
||||
#
|
||||
|
||||
class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
||||
master = NestedDeviceSerializer()
|
||||
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'url', 'master']
|
||||
@@ -1,43 +1,58 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.validators import UniqueTogetherValidator
|
||||
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination
|
||||
from dcim.constants import (
|
||||
CONNECTION_STATUS_CHOICES, DEVICE_STATUS_CHOICES, IFACE_FF_CHOICES, IFACE_MODE_CHOICES, IFACE_ORDERING_CHOICES,
|
||||
RACK_FACE_CHOICES, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, SITE_STATUS_CHOICES, SUBDEVICE_ROLE_CHOICES,
|
||||
)
|
||||
from dcim.constants import *
|
||||
from dcim.models import (
|
||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceType, DeviceRole, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
|
||||
RackReservation, RackRole, Region, Site, VirtualChassis,
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
from extras.api.customfields import CustomFieldModelSerializer
|
||||
from ipam.models import IPAddress, VLAN
|
||||
from tenancy.api.serializers import NestedTenantSerializer
|
||||
from users.api.serializers import NestedUserSerializer
|
||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
|
||||
from ipam.models import VLAN
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from users.api.nested_serializers import NestedUserSerializer
|
||||
from utilities.api import (
|
||||
ChoiceField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
|
||||
WritableNestedSerializer,
|
||||
ChoiceField, ContentTypeField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
|
||||
WritableNestedSerializer, get_serializer_for_model,
|
||||
)
|
||||
from virtualization.models import Cluster
|
||||
from virtualization.api.nested_serializers import NestedClusterSerializer
|
||||
from .nested_serializers import *
|
||||
|
||||
|
||||
class ConnectedEndpointSerializer(ValidatedModelSerializer):
|
||||
connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
|
||||
connected_endpoint = serializers.SerializerMethodField(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
def get_connected_endpoint_type(self, obj):
|
||||
if hasattr(obj, 'connected_endpoint') and obj.connected_endpoint is not None:
|
||||
return '{}.{}'.format(
|
||||
obj.connected_endpoint._meta.app_label,
|
||||
obj.connected_endpoint._meta.model_name
|
||||
)
|
||||
return None
|
||||
|
||||
def get_connected_endpoint(self, obj):
|
||||
"""
|
||||
Return the appropriate serializer for the type of connected object.
|
||||
"""
|
||||
if getattr(obj, 'connected_endpoint', None) is None:
|
||||
return None
|
||||
|
||||
serializer = get_serializer_for_model(obj.connected_endpoint, prefix='Nested')
|
||||
context = {'request': self.context['request']}
|
||||
data = serializer(obj.connected_endpoint, context=context).data
|
||||
|
||||
return data
|
||||
|
||||
|
||||
#
|
||||
# Regions
|
||||
# Regions/sites
|
||||
#
|
||||
|
||||
class NestedRegionSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
||||
|
||||
class Meta:
|
||||
model = Region
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
class RegionSerializer(serializers.ModelSerializer):
|
||||
parent = NestedRegionSerializer(required=False, allow_null=True)
|
||||
|
||||
@@ -46,10 +61,6 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||
fields = ['id', 'name', 'slug', 'parent']
|
||||
|
||||
|
||||
#
|
||||
# Sites
|
||||
#
|
||||
|
||||
class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
|
||||
region = NestedRegionSerializer(required=False, allow_null=True)
|
||||
@@ -72,16 +83,8 @@ class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class NestedSiteSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
||||
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Rack groups
|
||||
# Racks
|
||||
#
|
||||
|
||||
class RackGroupSerializer(ValidatedModelSerializer):
|
||||
@@ -92,18 +95,6 @@ class RackGroupSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'name', 'slug', 'site']
|
||||
|
||||
|
||||
class NestedRackGroupSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
|
||||
|
||||
class Meta:
|
||||
model = RackGroup
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Rack roles
|
||||
#
|
||||
|
||||
class RackRoleSerializer(ValidatedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
@@ -111,32 +102,23 @@ class RackRoleSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'name', 'slug', 'color']
|
||||
|
||||
|
||||
class NestedRackRoleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
||||
|
||||
class Meta:
|
||||
model = RackRole
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Racks
|
||||
#
|
||||
|
||||
class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
site = NestedSiteSerializer()
|
||||
group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
status = ChoiceField(choices=RACK_STATUS_CHOICES, required=False)
|
||||
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
|
||||
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
|
||||
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = [
|
||||
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width',
|
||||
'u_height', 'desc_units', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
|
||||
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
|
||||
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
|
||||
# prevents facility_id from being interpreted as a required field.
|
||||
@@ -153,31 +135,11 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
validator(data)
|
||||
|
||||
# Enforce model validation
|
||||
super(RackSerializer, self).validate(data)
|
||||
super().validate(data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class NestedRackSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = ['id', 'url', 'name', 'display_name']
|
||||
|
||||
|
||||
#
|
||||
# Rack units
|
||||
#
|
||||
|
||||
class NestedDeviceSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = ['id', 'url', 'name', 'display_name']
|
||||
|
||||
|
||||
class RackUnitSerializer(serializers.Serializer):
|
||||
"""
|
||||
A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
|
||||
@@ -188,10 +150,6 @@ class RackUnitSerializer(serializers.Serializer):
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
|
||||
|
||||
#
|
||||
# Rack reservations
|
||||
#
|
||||
|
||||
class RackReservationSerializer(ValidatedModelSerializer):
|
||||
rack = NestedRackSerializer()
|
||||
user = NestedUserSerializer()
|
||||
@@ -203,7 +161,7 @@ class RackReservationSerializer(ValidatedModelSerializer):
|
||||
|
||||
|
||||
#
|
||||
# Manufacturers
|
||||
# Device types
|
||||
#
|
||||
|
||||
class ManufacturerSerializer(ValidatedModelSerializer):
|
||||
@@ -213,21 +171,8 @@ class ManufacturerSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
||||
|
||||
class Meta:
|
||||
model = Manufacturer
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Device types
|
||||
#
|
||||
|
||||
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
manufacturer = NestedManufacturerSerializer()
|
||||
interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
|
||||
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
|
||||
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
@@ -235,25 +180,11 @@ class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
class Meta:
|
||||
model = DeviceType
|
||||
fields = [
|
||||
'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
|
||||
'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', 'instance_count',
|
||||
'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
||||
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'instance_count',
|
||||
]
|
||||
|
||||
|
||||
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DeviceType
|
||||
fields = ['id', 'url', 'manufacturer', 'model', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Console port templates
|
||||
#
|
||||
|
||||
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
|
||||
@@ -262,10 +193,6 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Console server port templates
|
||||
#
|
||||
|
||||
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
|
||||
@@ -274,10 +201,6 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Power port templates
|
||||
#
|
||||
|
||||
class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
|
||||
@@ -286,10 +209,6 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Power outlet templates
|
||||
#
|
||||
|
||||
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
|
||||
@@ -298,10 +217,6 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Interface templates
|
||||
#
|
||||
|
||||
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
|
||||
@@ -311,9 +226,24 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
|
||||
|
||||
|
||||
#
|
||||
# Device bay templates
|
||||
#
|
||||
class RearPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
fields = ['id', 'device_type', 'name', 'type', 'positions']
|
||||
|
||||
|
||||
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
rear_port = NestedRearPortTemplateSerializer()
|
||||
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position']
|
||||
|
||||
|
||||
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
@@ -324,7 +254,7 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||
|
||||
|
||||
#
|
||||
# Device roles
|
||||
# Devices
|
||||
#
|
||||
|
||||
class DeviceRoleSerializer(ValidatedModelSerializer):
|
||||
@@ -334,64 +264,12 @@ class DeviceRoleSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'name', 'slug', 'color', 'vm_role']
|
||||
|
||||
|
||||
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
||||
|
||||
class Meta:
|
||||
model = DeviceRole
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Platforms
|
||||
#
|
||||
|
||||
class PlatformSerializer(ValidatedModelSerializer):
|
||||
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Platform
|
||||
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'rpc_client']
|
||||
|
||||
|
||||
class NestedPlatformSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
||||
|
||||
class Meta:
|
||||
model = Platform
|
||||
fields = ['id', 'url', 'name', 'slug']
|
||||
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
|
||||
# Cannot import ipam.api.NestedIPAddressSerializer due to circular dependency
|
||||
class DeviceIPAddressSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
|
||||
|
||||
class Meta:
|
||||
model = IPAddress
|
||||
fields = ['id', 'url', 'family', 'address']
|
||||
|
||||
|
||||
# Cannot import virtualization.api.NestedClusterSerializer due to circular dependency
|
||||
class NestedClusterSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
||||
|
||||
class Meta:
|
||||
model = Cluster
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
# Cannot import NestedVirtualChassisSerializer due to circular dependency
|
||||
class DeviceVirtualChassisSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
||||
master = NestedDeviceSerializer()
|
||||
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'url', 'master']
|
||||
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
|
||||
|
||||
|
||||
class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
@@ -403,12 +281,12 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
rack = NestedRackSerializer(required=False, allow_null=True)
|
||||
face = ChoiceField(choices=RACK_FACE_CHOICES, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=DEVICE_STATUS_CHOICES, required=False)
|
||||
primary_ip = DeviceIPAddressSerializer(read_only=True)
|
||||
primary_ip4 = DeviceIPAddressSerializer(required=False, allow_null=True)
|
||||
primary_ip6 = DeviceIPAddressSerializer(required=False, allow_null=True)
|
||||
primary_ip = NestedIPAddressSerializer(read_only=True)
|
||||
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
parent_device = serializers.SerializerMethodField()
|
||||
cluster = NestedClusterSerializer(required=False, allow_null=True)
|
||||
virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
|
||||
virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
@@ -416,8 +294,8 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
fields = [
|
||||
'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
|
||||
'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
|
||||
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'local_context_data',
|
||||
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data', 'tags',
|
||||
'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
validators = []
|
||||
|
||||
@@ -430,7 +308,7 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
validator(data)
|
||||
|
||||
# Enforce model validation
|
||||
super(DeviceSerializer, self).validate(data)
|
||||
super().validate(data)
|
||||
|
||||
return data
|
||||
|
||||
@@ -452,203 +330,90 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
|
||||
fields = [
|
||||
'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
|
||||
'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
|
||||
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'custom_fields',
|
||||
'config_context', 'created', 'last_updated', 'local_context_data',
|
||||
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data', 'tags',
|
||||
'custom_fields', 'config_context', 'created', 'last_updated',
|
||||
]
|
||||
|
||||
def get_config_context(self, obj):
|
||||
return obj.get_config_context()
|
||||
|
||||
|
||||
#
|
||||
# Console server ports
|
||||
#
|
||||
|
||||
class ConsoleServerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
class ConsoleServerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = ['id', 'device', 'name', 'connected_console', 'tags']
|
||||
read_only_fields = ['connected_console']
|
||||
|
||||
|
||||
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
||||
|
||||
def get_is_connected(self, obj):
|
||||
return hasattr(obj, 'connected_console') and obj.connected_console is not None
|
||||
|
||||
|
||||
#
|
||||
# Console ports
|
||||
#
|
||||
|
||||
class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cs_port = NestedConsoleServerPortSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = ['id', 'device', 'name', 'cs_port', 'connection_status', 'tags']
|
||||
|
||||
|
||||
class NestedConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
||||
|
||||
def get_is_connected(self, obj):
|
||||
return obj.cs_port is not None
|
||||
|
||||
|
||||
#
|
||||
# Power outlets
|
||||
#
|
||||
|
||||
class PowerOutletSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ['id', 'device', 'name', 'connected_port', 'tags']
|
||||
read_only_fields = ['connected_port']
|
||||
|
||||
|
||||
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
||||
|
||||
def get_is_connected(self, obj):
|
||||
return hasattr(obj, 'connected_port') and obj.connected_port is not None
|
||||
|
||||
|
||||
#
|
||||
# Power ports
|
||||
#
|
||||
|
||||
class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
power_outlet = NestedPowerOutletSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = ['id', 'device', 'name', 'power_outlet', 'connection_status', 'tags']
|
||||
|
||||
|
||||
class NestedPowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
||||
|
||||
def get_is_connected(self, obj):
|
||||
return obj.power_outlet is not None
|
||||
|
||||
|
||||
#
|
||||
# Interfaces
|
||||
#
|
||||
|
||||
class IsConnectedMixin(object):
|
||||
"""
|
||||
Provide a method for setting is_connected on Interface serializers.
|
||||
"""
|
||||
def get_is_connected(self, obj):
|
||||
"""
|
||||
Return True if the interface has a connected interface or circuit.
|
||||
"""
|
||||
if obj.connection:
|
||||
return True
|
||||
if hasattr(obj, 'circuit_termination') and obj.circuit_termination is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class NestedInterfaceSerializer(IsConnectedMixin, WritableNestedSerializer):
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
||||
|
||||
|
||||
class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
||||
|
||||
class Meta:
|
||||
model = Circuit
|
||||
fields = ['id', 'url', 'cid']
|
||||
|
||||
|
||||
class InterfaceCircuitTerminationSerializer(WritableNestedSerializer):
|
||||
circuit = InterfaceNestedCircuitSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'id', 'circuit', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
|
||||
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
|
||||
'tags',
|
||||
]
|
||||
|
||||
|
||||
# Cannot import ipam.api.NestedVLANSerializer due to circular dependency
|
||||
class InterfaceVLANSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
||||
class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = VLAN
|
||||
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
||||
model = ConsolePort
|
||||
fields = [
|
||||
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
|
||||
'tags',
|
||||
]
|
||||
|
||||
|
||||
class InterfaceSerializer(TaggitSerializer, IsConnectedMixin, ValidatedModelSerializer):
|
||||
class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
|
||||
'tags',
|
||||
]
|
||||
|
||||
|
||||
class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = [
|
||||
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
|
||||
'tags',
|
||||
]
|
||||
|
||||
|
||||
class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
|
||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
interface_connection = serializers.SerializerMethodField(read_only=True)
|
||||
circuit_termination = InterfaceCircuitTerminationSerializer(read_only=True)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
||||
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
||||
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||
tagged_vlans = SerializedPKRelatedField(
|
||||
queryset=VLAN.objects.all(),
|
||||
serializer=InterfaceVLANSerializer,
|
||||
serializer=NestedVLANSerializer,
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = [
|
||||
'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
|
||||
'is_connected', 'interface_connection', 'circuit_termination', 'mode', 'untagged_vlan', 'tagged_vlans',
|
||||
'tags',
|
||||
'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode', 'untagged_vlan',
|
||||
'tagged_vlans', 'tags', 'count_ipaddresses',
|
||||
]
|
||||
|
||||
# TODO: This validation should be handled by Interface.clean()
|
||||
def validate(self, data):
|
||||
|
||||
# All associated VLANs be global or assigned to the parent device's site.
|
||||
@@ -666,21 +431,42 @@ class InterfaceSerializer(TaggitSerializer, IsConnectedMixin, ValidatedModelSeri
|
||||
"be global.".format(vlan)
|
||||
})
|
||||
|
||||
return super(InterfaceSerializer, self).validate(data)
|
||||
|
||||
def get_interface_connection(self, obj):
|
||||
if obj.connection:
|
||||
context = {
|
||||
'request': self.context['request'],
|
||||
'interface': obj.connected_interface,
|
||||
}
|
||||
return ContextualInterfaceConnectionSerializer(obj.connection, context=context).data
|
||||
return None
|
||||
return super().validate(data)
|
||||
|
||||
|
||||
#
|
||||
# Device bays
|
||||
#
|
||||
class RearPortSerializer(ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['id', 'device', 'name', 'type', 'positions', 'description', 'cable', 'tags']
|
||||
|
||||
|
||||
class FrontPortRearPortSerializer(WritableNestedSerializer):
|
||||
"""
|
||||
NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
|
||||
"""
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
class FrontPortSerializer(ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
rear_port = FrontPortRearPortSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = FrontPort
|
||||
fields = ['id', 'device', 'name', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'tags']
|
||||
|
||||
|
||||
class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
@@ -692,15 +478,6 @@ class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
fields = ['id', 'device', 'name', 'installed_device', 'tags']
|
||||
|
||||
|
||||
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DeviceBay
|
||||
fields = ['id', 'url', 'device', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Inventory items
|
||||
#
|
||||
@@ -720,41 +497,76 @@ class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Cables
|
||||
#
|
||||
|
||||
class CableSerializer(ValidatedModelSerializer):
|
||||
termination_a_type = ContentTypeField()
|
||||
termination_b_type = ContentTypeField()
|
||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
fields = [
|
||||
'id', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type', 'termination_b_id',
|
||||
'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit',
|
||||
]
|
||||
|
||||
def _get_termination(self, obj, side):
|
||||
"""
|
||||
Serialize a nested representation of a termination.
|
||||
"""
|
||||
if side.lower() not in ['a', 'b']:
|
||||
raise ValueError("Termination side must be either A or B.")
|
||||
termination = getattr(obj, 'termination_{}'.format(side.lower()))
|
||||
if termination is None:
|
||||
return None
|
||||
serializer = get_serializer_for_model(termination, prefix='Nested')
|
||||
context = {'request': self.context['request']}
|
||||
data = serializer(termination, context=context).data
|
||||
|
||||
return data
|
||||
|
||||
def get_termination_a(self, obj):
|
||||
return self._get_termination(obj, 'a')
|
||||
|
||||
def get_termination_b(self, obj):
|
||||
return self._get_termination(obj, 'b')
|
||||
|
||||
|
||||
class TracedCableSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Used only while tracing a cable path.
|
||||
"""
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
fields = [
|
||||
'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Interface connections
|
||||
#
|
||||
|
||||
class InterfaceConnectionSerializer(ValidatedModelSerializer):
|
||||
interface_a = NestedInterfaceSerializer()
|
||||
interface_b = NestedInterfaceSerializer()
|
||||
interface_a = serializers.SerializerMethodField()
|
||||
interface_b = NestedInterfaceSerializer(source='connected_endpoint')
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||
|
||||
class Meta:
|
||||
model = InterfaceConnection
|
||||
fields = ['id', 'interface_a', 'interface_b', 'connection_status']
|
||||
model = Interface
|
||||
fields = ['interface_a', 'interface_b', 'connection_status']
|
||||
|
||||
|
||||
class NestedInterfaceConnectionSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
|
||||
|
||||
class Meta:
|
||||
model = InterfaceConnection
|
||||
fields = ['id', 'url', 'connection_status']
|
||||
|
||||
|
||||
class ContextualInterfaceConnectionSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
A read-only representation of an InterfaceConnection from the perspective of either of its two connected Interfaces.
|
||||
"""
|
||||
interface = serializers.SerializerMethodField(read_only=True)
|
||||
connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = InterfaceConnection
|
||||
fields = ['id', 'interface', 'connection_status']
|
||||
|
||||
def get_interface(self, obj):
|
||||
return NestedInterfaceSerializer(self.context['interface'], context=self.context).data
|
||||
def get_interface_a(self, obj):
|
||||
context = {'request': self.context['request']}
|
||||
return NestedInterfaceSerializer(instance=obj, context=context).data
|
||||
|
||||
|
||||
#
|
||||
@@ -768,11 +580,3 @@ class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'master', 'domain', 'tags']
|
||||
|
||||
|
||||
class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
||||
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'url']
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import routers
|
||||
|
||||
from . import views
|
||||
@@ -17,7 +15,7 @@ router = routers.DefaultRouter()
|
||||
router.APIRootView = DCIMRootView
|
||||
|
||||
# Field choices
|
||||
router.register(r'_choices', views.DCIMFieldChoicesViewSet, base_name='field-choice')
|
||||
router.register(r'_choices', views.DCIMFieldChoicesViewSet, basename='field-choice')
|
||||
|
||||
# Sites
|
||||
router.register(r'regions', views.RegionViewSet)
|
||||
@@ -39,6 +37,8 @@ router.register(r'console-server-port-templates', views.ConsoleServerPortTemplat
|
||||
router.register(r'power-port-templates', views.PowerPortTemplateViewSet)
|
||||
router.register(r'power-outlet-templates', views.PowerOutletTemplateViewSet)
|
||||
router.register(r'interface-templates', views.InterfaceTemplateViewSet)
|
||||
router.register(r'front-port-templates', views.FrontPortTemplateViewSet)
|
||||
router.register(r'rear-port-templates', views.RearPortTemplateViewSet)
|
||||
router.register(r'device-bay-templates', views.DeviceBayTemplateViewSet)
|
||||
|
||||
# Devices
|
||||
@@ -52,19 +52,24 @@ router.register(r'console-server-ports', views.ConsoleServerPortViewSet)
|
||||
router.register(r'power-ports', views.PowerPortViewSet)
|
||||
router.register(r'power-outlets', views.PowerOutletViewSet)
|
||||
router.register(r'interfaces', views.InterfaceViewSet)
|
||||
router.register(r'front-ports', views.FrontPortViewSet)
|
||||
router.register(r'rear-ports', views.RearPortViewSet)
|
||||
router.register(r'device-bays', views.DeviceBayViewSet)
|
||||
router.register(r'inventory-items', views.InventoryItemViewSet)
|
||||
|
||||
# Connections
|
||||
router.register(r'console-connections', views.ConsoleConnectionViewSet, base_name='consoleconnections')
|
||||
router.register(r'power-connections', views.PowerConnectionViewSet, base_name='powerconnections')
|
||||
router.register(r'interface-connections', views.InterfaceConnectionViewSet)
|
||||
router.register(r'console-connections', views.ConsoleConnectionViewSet, basename='consoleconnections')
|
||||
router.register(r'power-connections', views.PowerConnectionViewSet, basename='powerconnections')
|
||||
router.register(r'interface-connections', views.InterfaceConnectionViewSet, basename='interfaceconnections')
|
||||
|
||||
# Cables
|
||||
router.register(r'cables', views.CableViewSet)
|
||||
|
||||
# Virtual chassis
|
||||
router.register(r'virtual-chassis', views.VirtualChassisViewSet)
|
||||
|
||||
# Miscellaneous
|
||||
router.register(r'connected-device', views.ConnectedDeviceViewSet, base_name='connected-device')
|
||||
router.register(r'connected-device', views.ConnectedDeviceViewSet, basename='connected-device')
|
||||
|
||||
app_name = 'dcim-api'
|
||||
urlpatterns = router.urls
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import F, Q
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.shortcuts import get_object_or_404
|
||||
from drf_yasg import openapi
|
||||
@@ -15,15 +14,17 @@ from rest_framework.viewsets import GenericViewSet, ViewSet
|
||||
|
||||
from dcim import filters
|
||||
from dcim.models import (
|
||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
|
||||
RackReservation, RackRole, Region, Site, VirtualChassis,
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
from extras.api.serializers import RenderedGraphSerializer
|
||||
from extras.api.views import CustomFieldModelViewSet
|
||||
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
|
||||
from utilities.api import IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable
|
||||
from utilities.api import (
|
||||
get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
|
||||
)
|
||||
from . import serializers
|
||||
from .exceptions import MissingFilterException
|
||||
|
||||
@@ -34,17 +35,51 @@ from .exceptions import MissingFilterException
|
||||
|
||||
class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
fields = (
|
||||
(Cable, ['length_unit']),
|
||||
(Device, ['face', 'status']),
|
||||
(ConsolePort, ['connection_status']),
|
||||
(Interface, ['form_factor', 'mode']),
|
||||
(InterfaceConnection, ['connection_status']),
|
||||
(Interface, ['connection_status', 'form_factor', 'mode']),
|
||||
(InterfaceTemplate, ['form_factor']),
|
||||
(PowerPort, ['connection_status']),
|
||||
(Rack, ['type', 'width']),
|
||||
(Rack, ['outer_unit', 'status', 'type', 'width']),
|
||||
(Site, ['status']),
|
||||
)
|
||||
|
||||
|
||||
# Mixins
|
||||
|
||||
class CableTraceMixin(object):
|
||||
|
||||
@action(detail=True, url_path='trace')
|
||||
def trace(self, request, pk):
|
||||
"""
|
||||
Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).
|
||||
"""
|
||||
obj = get_object_or_404(self.queryset.model, pk=pk)
|
||||
|
||||
# Initialize the path array
|
||||
path = []
|
||||
|
||||
for near_end, cable, far_end in obj.trace():
|
||||
|
||||
# Serialize each object
|
||||
serializer_a = get_serializer_for_model(near_end, prefix='Nested')
|
||||
x = serializer_a(near_end, context={'request': request}).data
|
||||
if cable is not None:
|
||||
y = serializers.TracedCableSerializer(cable, context={'request': request}).data
|
||||
else:
|
||||
y = None
|
||||
if far_end is not None:
|
||||
serializer_b = get_serializer_for_model(far_end, prefix='Nested')
|
||||
z = serializer_b(far_end, context={'request': request}).data
|
||||
else:
|
||||
z = None
|
||||
|
||||
path.append((x, y, z))
|
||||
|
||||
return Response(path)
|
||||
|
||||
|
||||
#
|
||||
# Regions
|
||||
#
|
||||
@@ -52,7 +87,7 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
class RegionViewSet(ModelViewSet):
|
||||
queryset = Region.objects.all()
|
||||
serializer_class = serializers.RegionSerializer
|
||||
filter_class = filters.RegionFilter
|
||||
filterset_class = filters.RegionFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -62,7 +97,7 @@ class RegionViewSet(ModelViewSet):
|
||||
class SiteViewSet(CustomFieldModelViewSet):
|
||||
queryset = Site.objects.select_related('region', 'tenant').prefetch_related('tags')
|
||||
serializer_class = serializers.SiteSerializer
|
||||
filter_class = filters.SiteFilter
|
||||
filterset_class = filters.SiteFilter
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk=None):
|
||||
@@ -82,7 +117,7 @@ class SiteViewSet(CustomFieldModelViewSet):
|
||||
class RackGroupViewSet(ModelViewSet):
|
||||
queryset = RackGroup.objects.select_related('site')
|
||||
serializer_class = serializers.RackGroupSerializer
|
||||
filter_class = filters.RackGroupFilter
|
||||
filterset_class = filters.RackGroupFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -92,7 +127,7 @@ class RackGroupViewSet(ModelViewSet):
|
||||
class RackRoleViewSet(ModelViewSet):
|
||||
queryset = RackRole.objects.all()
|
||||
serializer_class = serializers.RackRoleSerializer
|
||||
filter_class = filters.RackRoleFilter
|
||||
filterset_class = filters.RackRoleFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -102,7 +137,7 @@ class RackRoleViewSet(ModelViewSet):
|
||||
class RackViewSet(CustomFieldModelViewSet):
|
||||
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('tags')
|
||||
serializer_class = serializers.RackSerializer
|
||||
filter_class = filters.RackFilter
|
||||
filterset_class = filters.RackFilter
|
||||
|
||||
@action(detail=True)
|
||||
def units(self, request, pk=None):
|
||||
@@ -132,7 +167,7 @@ class RackViewSet(CustomFieldModelViewSet):
|
||||
class RackReservationViewSet(ModelViewSet):
|
||||
queryset = RackReservation.objects.select_related('rack', 'user', 'tenant')
|
||||
serializer_class = serializers.RackReservationSerializer
|
||||
filter_class = filters.RackReservationFilter
|
||||
filterset_class = filters.RackReservationFilter
|
||||
|
||||
# Assign user from request
|
||||
def perform_create(self, serializer):
|
||||
@@ -146,7 +181,7 @@ class RackReservationViewSet(ModelViewSet):
|
||||
class ManufacturerViewSet(ModelViewSet):
|
||||
queryset = Manufacturer.objects.all()
|
||||
serializer_class = serializers.ManufacturerSerializer
|
||||
filter_class = filters.ManufacturerFilter
|
||||
filterset_class = filters.ManufacturerFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -156,7 +191,7 @@ class ManufacturerViewSet(ModelViewSet):
|
||||
class DeviceTypeViewSet(CustomFieldModelViewSet):
|
||||
queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('tags')
|
||||
serializer_class = serializers.DeviceTypeSerializer
|
||||
filter_class = filters.DeviceTypeFilter
|
||||
filterset_class = filters.DeviceTypeFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -166,37 +201,49 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
|
||||
class ConsolePortTemplateViewSet(ModelViewSet):
|
||||
queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.ConsolePortTemplateSerializer
|
||||
filter_class = filters.ConsolePortTemplateFilter
|
||||
filterset_class = filters.ConsolePortTemplateFilter
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateViewSet(ModelViewSet):
|
||||
queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.ConsoleServerPortTemplateSerializer
|
||||
filter_class = filters.ConsoleServerPortTemplateFilter
|
||||
filterset_class = filters.ConsoleServerPortTemplateFilter
|
||||
|
||||
|
||||
class PowerPortTemplateViewSet(ModelViewSet):
|
||||
queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.PowerPortTemplateSerializer
|
||||
filter_class = filters.PowerPortTemplateFilter
|
||||
filterset_class = filters.PowerPortTemplateFilter
|
||||
|
||||
|
||||
class PowerOutletTemplateViewSet(ModelViewSet):
|
||||
queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.PowerOutletTemplateSerializer
|
||||
filter_class = filters.PowerOutletTemplateFilter
|
||||
filterset_class = filters.PowerOutletTemplateFilter
|
||||
|
||||
|
||||
class InterfaceTemplateViewSet(ModelViewSet):
|
||||
queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.InterfaceTemplateSerializer
|
||||
filter_class = filters.InterfaceTemplateFilter
|
||||
filterset_class = filters.InterfaceTemplateFilter
|
||||
|
||||
|
||||
class FrontPortTemplateViewSet(ModelViewSet):
|
||||
queryset = FrontPortTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.FrontPortTemplateSerializer
|
||||
filterset_class = filters.FrontPortTemplateFilter
|
||||
|
||||
|
||||
class RearPortTemplateViewSet(ModelViewSet):
|
||||
queryset = RearPortTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.RearPortTemplateSerializer
|
||||
filterset_class = filters.RearPortTemplateFilter
|
||||
|
||||
|
||||
class DeviceBayTemplateViewSet(ModelViewSet):
|
||||
queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
|
||||
serializer_class = serializers.DeviceBayTemplateSerializer
|
||||
filter_class = filters.DeviceBayTemplateFilter
|
||||
filterset_class = filters.DeviceBayTemplateFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -206,7 +253,7 @@ class DeviceBayTemplateViewSet(ModelViewSet):
|
||||
class DeviceRoleViewSet(ModelViewSet):
|
||||
queryset = DeviceRole.objects.all()
|
||||
serializer_class = serializers.DeviceRoleSerializer
|
||||
filter_class = filters.DeviceRoleFilter
|
||||
filterset_class = filters.DeviceRoleFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -216,7 +263,7 @@ class DeviceRoleViewSet(ModelViewSet):
|
||||
class PlatformViewSet(ModelViewSet):
|
||||
queryset = Platform.objects.all()
|
||||
serializer_class = serializers.PlatformSerializer
|
||||
filter_class = filters.PlatformFilter
|
||||
filterset_class = filters.PlatformFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -230,7 +277,7 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
).prefetch_related(
|
||||
'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
|
||||
)
|
||||
filter_class = filters.DeviceFilter
|
||||
filterset_class = filters.DeviceFilter
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
@@ -321,34 +368,54 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
# Device components
|
||||
#
|
||||
|
||||
class ConsolePortViewSet(ModelViewSet):
|
||||
queryset = ConsolePort.objects.select_related('device', 'cs_port__device').prefetch_related('tags')
|
||||
class ConsolePortViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = ConsolePort.objects.select_related(
|
||||
'device', 'connected_endpoint__device', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.ConsolePortSerializer
|
||||
filter_class = filters.ConsolePortFilter
|
||||
filterset_class = filters.ConsolePortFilter
|
||||
|
||||
|
||||
class ConsoleServerPortViewSet(ModelViewSet):
|
||||
queryset = ConsoleServerPort.objects.select_related('device', 'connected_console__device').prefetch_related('tags')
|
||||
class ConsoleServerPortViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = ConsoleServerPort.objects.select_related(
|
||||
'device', 'connected_endpoint__device', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.ConsoleServerPortSerializer
|
||||
filter_class = filters.ConsoleServerPortFilter
|
||||
filterset_class = filters.ConsoleServerPortFilter
|
||||
|
||||
|
||||
class PowerPortViewSet(ModelViewSet):
|
||||
queryset = PowerPort.objects.select_related('device', 'power_outlet__device').prefetch_related('tags')
|
||||
class PowerPortViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = PowerPort.objects.select_related(
|
||||
'device', 'connected_endpoint__device', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerPortSerializer
|
||||
filter_class = filters.PowerPortFilter
|
||||
filterset_class = filters.PowerPortFilter
|
||||
|
||||
|
||||
class PowerOutletViewSet(ModelViewSet):
|
||||
queryset = PowerOutlet.objects.select_related('device', 'connected_port__device').prefetch_related('tags')
|
||||
class PowerOutletViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = PowerOutlet.objects.select_related(
|
||||
'device', 'connected_endpoint__device', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerOutletSerializer
|
||||
filter_class = filters.PowerOutletFilter
|
||||
filterset_class = filters.PowerOutletFilter
|
||||
|
||||
|
||||
class InterfaceViewSet(ModelViewSet):
|
||||
queryset = Interface.objects.select_related('device').prefetch_related('tags')
|
||||
class InterfaceViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = Interface.objects.select_related(
|
||||
'device', '_connected_interface', '_connected_circuittermination', 'cable'
|
||||
).prefetch_related(
|
||||
'ip_addresses', 'tags'
|
||||
)
|
||||
serializer_class = serializers.InterfaceSerializer
|
||||
filter_class = filters.InterfaceFilter
|
||||
filterset_class = filters.InterfaceFilter
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk=None):
|
||||
@@ -361,16 +428,36 @@ class InterfaceViewSet(ModelViewSet):
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class FrontPortViewSet(ModelViewSet):
|
||||
queryset = FrontPort.objects.select_related(
|
||||
'device__device_type__manufacturer', 'rear_port', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.FrontPortSerializer
|
||||
filterset_class = filters.FrontPortFilter
|
||||
|
||||
|
||||
class RearPortViewSet(ModelViewSet):
|
||||
queryset = RearPort.objects.select_related(
|
||||
'device__device_type__manufacturer', 'cable'
|
||||
).prefetch_related(
|
||||
'tags'
|
||||
)
|
||||
serializer_class = serializers.RearPortSerializer
|
||||
filterset_class = filters.RearPortFilter
|
||||
|
||||
|
||||
class DeviceBayViewSet(ModelViewSet):
|
||||
queryset = DeviceBay.objects.select_related('installed_device').prefetch_related('tags')
|
||||
serializer_class = serializers.DeviceBaySerializer
|
||||
filter_class = filters.DeviceBayFilter
|
||||
filterset_class = filters.DeviceBayFilter
|
||||
|
||||
|
||||
class InventoryItemViewSet(ModelViewSet):
|
||||
queryset = InventoryItem.objects.select_related('device', 'manufacturer').prefetch_related('tags')
|
||||
serializer_class = serializers.InventoryItemSerializer
|
||||
filter_class = filters.InventoryItemFilter
|
||||
filterset_class = filters.InventoryItemFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -378,21 +465,47 @@ class InventoryItemViewSet(ModelViewSet):
|
||||
#
|
||||
|
||||
class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
queryset = ConsolePort.objects.select_related('device', 'cs_port__device').filter(cs_port__isnull=False)
|
||||
queryset = ConsolePort.objects.select_related(
|
||||
'device', 'connected_endpoint__device'
|
||||
).filter(
|
||||
connected_endpoint__isnull=False
|
||||
)
|
||||
serializer_class = serializers.ConsolePortSerializer
|
||||
filter_class = filters.ConsoleConnectionFilter
|
||||
filterset_class = filters.ConsoleConnectionFilter
|
||||
|
||||
|
||||
class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
queryset = PowerPort.objects.select_related('device', 'power_outlet__device').filter(power_outlet__isnull=False)
|
||||
queryset = PowerPort.objects.select_related(
|
||||
'device', 'connected_endpoint__device'
|
||||
).filter(
|
||||
connected_endpoint__isnull=False
|
||||
)
|
||||
serializer_class = serializers.PowerPortSerializer
|
||||
filter_class = filters.PowerConnectionFilter
|
||||
filterset_class = filters.PowerConnectionFilter
|
||||
|
||||
|
||||
class InterfaceConnectionViewSet(ModelViewSet):
|
||||
queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
|
||||
queryset = Interface.objects.select_related(
|
||||
'device', '_connected_interface', '_connected_circuittermination'
|
||||
).filter(
|
||||
# Avoid duplicate connections by only selecting the lower PK in a connected pair
|
||||
Q(_connected_interface__isnull=False, pk__lt=F('_connected_interface')) |
|
||||
Q(_connected_circuittermination__isnull=False)
|
||||
)
|
||||
serializer_class = serializers.InterfaceConnectionSerializer
|
||||
filter_class = filters.InterfaceConnectionFilter
|
||||
filterset_class = filters.InterfaceConnectionFilter
|
||||
|
||||
|
||||
#
|
||||
# Cables
|
||||
#
|
||||
|
||||
class CableViewSet(ModelViewSet):
|
||||
queryset = Cable.objects.prefetch_related(
|
||||
'termination_a', 'termination_b'
|
||||
)
|
||||
serializer_class = serializers.CableSerializer
|
||||
filterset_class = filters.CableFilter
|
||||
|
||||
|
||||
#
|
||||
@@ -418,32 +531,39 @@ class ConnectedDeviceViewSet(ViewSet):
|
||||
* `peer_interface`: The name of the peer interface
|
||||
"""
|
||||
permission_classes = [IsAuthenticatedOrLoginNotRequired]
|
||||
_device_param = Parameter('peer_device', 'query',
|
||||
description='The name of the peer device', required=True, type=openapi.TYPE_STRING)
|
||||
_interface_param = Parameter('peer_interface', 'query',
|
||||
description='The name of the peer interface', required=True, type=openapi.TYPE_STRING)
|
||||
_device_param = Parameter(
|
||||
name='peer_device',
|
||||
in_='query',
|
||||
description='The name of the peer device',
|
||||
required=True,
|
||||
type=openapi.TYPE_STRING
|
||||
)
|
||||
_interface_param = Parameter(
|
||||
name='peer_interface',
|
||||
in_='query',
|
||||
description='The name of the peer interface',
|
||||
required=True,
|
||||
type=openapi.TYPE_STRING
|
||||
)
|
||||
|
||||
def get_view_name(self):
|
||||
return "Connected Device Locator"
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[_device_param, _interface_param], responses={'200': serializers.DeviceSerializer})
|
||||
manual_parameters=[_device_param, _interface_param],
|
||||
responses={'200': serializers.DeviceSerializer}
|
||||
)
|
||||
def list(self, request):
|
||||
|
||||
peer_device_name = request.query_params.get(self._device_param.name)
|
||||
if not peer_device_name:
|
||||
# TODO: remove this after 2.4 as the switch to using underscores is a breaking change
|
||||
peer_device_name = request.query_params.get('peer-device')
|
||||
peer_interface_name = request.query_params.get(self._interface_param.name)
|
||||
if not peer_interface_name:
|
||||
# TODO: remove this after 2.4 as the switch to using underscores is a breaking change
|
||||
peer_interface_name = request.query_params.get('peer-interface')
|
||||
|
||||
if not peer_device_name or not peer_interface_name:
|
||||
raise MissingFilterException(detail='Request must include "peer_device" and "peer_interface" filters.')
|
||||
|
||||
# Determine local interface from peer interface's connection
|
||||
peer_interface = get_object_or_404(Interface, device__name=peer_device_name, name=peer_interface_name)
|
||||
local_interface = peer_interface.connected_interface
|
||||
local_interface = peer_interface._connected_interface
|
||||
|
||||
if local_interface is None:
|
||||
return Response()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
# Rack types
|
||||
RACK_TYPE_2POST = 100
|
||||
@@ -31,6 +29,20 @@ RACK_FACE_CHOICES = [
|
||||
[RACK_FACE_REAR, 'Rear'],
|
||||
]
|
||||
|
||||
# Rack statuses
|
||||
RACK_STATUS_RESERVED = 0
|
||||
RACK_STATUS_AVAILABLE = 1
|
||||
RACK_STATUS_PLANNED = 2
|
||||
RACK_STATUS_ACTIVE = 3
|
||||
RACK_STATUS_DEPRECATED = 4
|
||||
RACK_STATUS_CHOICES = [
|
||||
[RACK_STATUS_ACTIVE, 'Active'],
|
||||
[RACK_STATUS_PLANNED, 'Planned'],
|
||||
[RACK_STATUS_RESERVED, 'Reserved'],
|
||||
[RACK_STATUS_AVAILABLE, 'Available'],
|
||||
[RACK_STATUS_DEPRECATED, 'Deprecated'],
|
||||
]
|
||||
|
||||
# Parent/child device roles
|
||||
SUBDEVICE_ROLE_PARENT = True
|
||||
SUBDEVICE_ROLE_CHILD = False
|
||||
@@ -233,6 +245,36 @@ IFACE_MODE_CHOICES = [
|
||||
[IFACE_MODE_TAGGED_ALL, 'Tagged All'],
|
||||
]
|
||||
|
||||
# Pass-through port types
|
||||
PORT_TYPE_8P8C = 1000
|
||||
PORT_TYPE_ST = 2000
|
||||
PORT_TYPE_SC = 2100
|
||||
PORT_TYPE_FC = 2200
|
||||
PORT_TYPE_LC = 2300
|
||||
PORT_TYPE_MTRJ = 2400
|
||||
PORT_TYPE_MPO = 2500
|
||||
PORT_TYPE_LSH = 2600
|
||||
PORT_TYPE_CHOICES = [
|
||||
[
|
||||
'Copper',
|
||||
[
|
||||
[PORT_TYPE_8P8C, '8P8C'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Fiber Optic',
|
||||
[
|
||||
[PORT_TYPE_FC, 'FC'],
|
||||
[PORT_TYPE_LC, 'LC'],
|
||||
[PORT_TYPE_LSH, 'LSH'],
|
||||
[PORT_TYPE_MPO, 'MPO'],
|
||||
[PORT_TYPE_MTRJ, 'MTRJ'],
|
||||
[PORT_TYPE_SC, 'SC'],
|
||||
[PORT_TYPE_ST, 'ST'],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
# Device statuses
|
||||
DEVICE_STATUS_OFFLINE = 0
|
||||
DEVICE_STATUS_ACTIVE = 1
|
||||
@@ -259,7 +301,7 @@ SITE_STATUS_CHOICES = [
|
||||
[SITE_STATUS_RETIRED, 'Retired'],
|
||||
]
|
||||
|
||||
# Bootstrap CSS classes for device statuses
|
||||
# Bootstrap CSS classes for device/rack statuses
|
||||
STATUS_CLASSES = {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
@@ -277,12 +319,81 @@ CONNECTION_STATUS_CHOICES = [
|
||||
[CONNECTION_STATUS_CONNECTED, 'Connected'],
|
||||
]
|
||||
|
||||
# Platform -> RPC client mappings
|
||||
RPC_CLIENT_JUNIPER_JUNOS = 'juniper-junos'
|
||||
RPC_CLIENT_CISCO_IOS = 'cisco-ios'
|
||||
RPC_CLIENT_OPENGEAR = 'opengear'
|
||||
RPC_CLIENT_CHOICES = [
|
||||
[RPC_CLIENT_JUNIPER_JUNOS, 'Juniper Junos (NETCONF)'],
|
||||
[RPC_CLIENT_CISCO_IOS, 'Cisco IOS (SSH)'],
|
||||
[RPC_CLIENT_OPENGEAR, 'Opengear (SSH)'],
|
||||
# Cable endpoint types
|
||||
CABLE_TERMINATION_TYPES = [
|
||||
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
|
||||
]
|
||||
|
||||
# Cable types
|
||||
CABLE_TYPE_CAT3 = 1300
|
||||
CABLE_TYPE_CAT5 = 1500
|
||||
CABLE_TYPE_CAT5E = 1510
|
||||
CABLE_TYPE_CAT6 = 1600
|
||||
CABLE_TYPE_CAT6A = 1610
|
||||
CABLE_TYPE_CAT7 = 1700
|
||||
CABLE_TYPE_MMF_OM1 = 3010
|
||||
CABLE_TYPE_MMF_OM2 = 3020
|
||||
CABLE_TYPE_MMF_OM3 = 3030
|
||||
CABLE_TYPE_MMF_OM4 = 3040
|
||||
CABLE_TYPE_SMF = 3500
|
||||
CABLE_TYPE_POWER = 5000
|
||||
CABLE_TYPE_CHOICES = (
|
||||
(
|
||||
'Copper', (
|
||||
(CABLE_TYPE_CAT3, 'CAT3'),
|
||||
(CABLE_TYPE_CAT5, 'CAT5'),
|
||||
(CABLE_TYPE_CAT5E, 'CAT5e'),
|
||||
(CABLE_TYPE_CAT6, 'CAT6'),
|
||||
(CABLE_TYPE_CAT6A, 'CAT6a'),
|
||||
(CABLE_TYPE_CAT7, 'CAT7'),
|
||||
),
|
||||
),
|
||||
(
|
||||
'Fiber', (
|
||||
(CABLE_TYPE_MMF_OM1, 'Multimode Fiber (OM1)'),
|
||||
(CABLE_TYPE_MMF_OM2, 'Multimode Fiber (OM2)'),
|
||||
(CABLE_TYPE_MMF_OM3, 'Multimode Fiber (OM3)'),
|
||||
(CABLE_TYPE_MMF_OM4, 'Multimode Fiber (OM4)'),
|
||||
(CABLE_TYPE_SMF, 'Singlemode Fiber'),
|
||||
),
|
||||
),
|
||||
(CABLE_TYPE_POWER, 'Power'),
|
||||
)
|
||||
|
||||
CABLE_TERMINATION_TYPE_CHOICES = {
|
||||
# (API endpoint, human-friendly name)
|
||||
'consoleport': ('console-ports', 'Console port'),
|
||||
'consoleserverport': ('console-server-ports', 'Console server port'),
|
||||
'powerport': ('power-ports', 'Power port'),
|
||||
'poweroutlet': ('power-outlets', 'Power outlet'),
|
||||
'interface': ('interfaces', 'Interface'),
|
||||
'frontport': ('front-ports', 'Front panel port'),
|
||||
'rearport': ('rear-ports', 'Rear panel port'),
|
||||
}
|
||||
|
||||
COMPATIBLE_TERMINATION_TYPES = {
|
||||
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
|
||||
'consoleserverport': ['consoleport', 'frontport', 'rearport'],
|
||||
'powerport': ['poweroutlet'],
|
||||
'poweroutlet': ['powerport'],
|
||||
'interface': ['interface', 'circuittermination', 'frontport', 'rearport'],
|
||||
'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
|
||||
'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
|
||||
'circuittermination': ['interface', 'frontport', 'rearport'],
|
||||
}
|
||||
|
||||
LENGTH_UNIT_METER = 1200
|
||||
LENGTH_UNIT_CENTIMETER = 1100
|
||||
LENGTH_UNIT_MILLIMETER = 1000
|
||||
LENGTH_UNIT_FOOT = 2100
|
||||
LENGTH_UNIT_INCH = 2000
|
||||
CABLE_LENGTH_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_METER, 'Meters'),
|
||||
(LENGTH_UNIT_CENTIMETER, 'Centimeters'),
|
||||
(LENGTH_UNIT_FOOT, 'Feet'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
RACK_DIMENSION_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from netaddr import AddrFormatError, EUI, mac_unix_expanded
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from django.db import models
|
||||
from netaddr import AddrFormatError, EUI, mac_unix_expanded
|
||||
|
||||
|
||||
class ASNField(models.BigIntegerField):
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django_filters
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@@ -9,17 +7,15 @@ from netaddr.core import AddrFormatError
|
||||
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from utilities.constants import COLOR_CHOICES
|
||||
from utilities.filters import NullableCharFieldFilter, NumericInFilter, TagFilter
|
||||
from virtualization.models import Cluster
|
||||
from .constants import (
|
||||
DEVICE_STATUS_CHOICES, IFACE_FF_LAG, NONCONNECTABLE_IFACE_TYPES, SITE_STATUS_CHOICES, VIRTUAL_IFACE_TYPES,
|
||||
WIRELESS_IFACE_TYPES, IFACE_FF_CHOICES,
|
||||
)
|
||||
from .constants import *
|
||||
from .models import (
|
||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
|
||||
RackReservation, RackRole, Region, Site, VirtualChassis,
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
|
||||
|
||||
@@ -33,7 +29,7 @@ class RegionFilter(django_filters.FilterSet):
|
||||
label='Parent region (ID)',
|
||||
)
|
||||
parent = django_filters.ModelMultipleChoiceFilter(
|
||||
name='parent__slug',
|
||||
field_name='parent__slug',
|
||||
queryset=Region.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Parent region (slug)',
|
||||
@@ -54,7 +50,10 @@ class RegionFilter(django_filters.FilterSet):
|
||||
|
||||
|
||||
class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
@@ -68,7 +67,7 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = django_filters.ModelMultipleChoiceFilter(
|
||||
name='region__slug',
|
||||
field_name='region__slug',
|
||||
queryset=Region.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
@@ -78,7 +77,7 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='tenant__slug',
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
@@ -120,7 +119,7 @@ class RackGroupFilter(django_filters.FilterSet):
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='site__slug',
|
||||
field_name='site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
@@ -148,7 +147,10 @@ class RackRoleFilter(django_filters.FilterSet):
|
||||
|
||||
|
||||
class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
@@ -159,7 +161,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='site__slug',
|
||||
field_name='site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
@@ -169,7 +171,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Group (ID)',
|
||||
)
|
||||
group = django_filters.ModelMultipleChoiceFilter(
|
||||
name='group__slug',
|
||||
field_name='group__slug',
|
||||
queryset=RackGroup.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
@@ -179,26 +181,34 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='tenant__slug',
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=RACK_STATUS_CHOICES,
|
||||
null_value=None
|
||||
)
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=RackRole.objects.all(),
|
||||
label='Role (ID)',
|
||||
)
|
||||
role = django_filters.ModelMultipleChoiceFilter(
|
||||
name='role__slug',
|
||||
field_name='role__slug',
|
||||
queryset=RackRole.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Role (slug)',
|
||||
)
|
||||
asset_tag = NullableCharFieldFilter()
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = ['name', 'serial', 'type', 'width', 'u_height', 'desc_units']
|
||||
fields = [
|
||||
'name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
||||
'outer_unit',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
@@ -207,12 +217,16 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
Q(name__icontains=value) |
|
||||
Q(facility_id__icontains=value) |
|
||||
Q(serial__icontains=value.strip()) |
|
||||
Q(asset_tag__icontains=value.strip()) |
|
||||
Q(comments__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class RackReservationFilter(django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
@@ -222,23 +236,23 @@ class RackReservationFilter(django_filters.FilterSet):
|
||||
label='Rack (ID)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack__site',
|
||||
field_name='rack__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack__site__slug',
|
||||
field_name='rack__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
)
|
||||
group_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack__group',
|
||||
field_name='rack__group',
|
||||
queryset=RackGroup.objects.all(),
|
||||
label='Group (ID)',
|
||||
)
|
||||
group = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack__group__slug',
|
||||
field_name='rack__group__slug',
|
||||
queryset=RackGroup.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
@@ -248,7 +262,7 @@ class RackReservationFilter(django_filters.FilterSet):
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='tenant__slug',
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
@@ -258,7 +272,7 @@ class RackReservationFilter(django_filters.FilterSet):
|
||||
label='User (ID)',
|
||||
)
|
||||
user = django_filters.ModelMultipleChoiceFilter(
|
||||
name='user',
|
||||
field_name='user',
|
||||
queryset=User.objects.all(),
|
||||
to_field_name='username',
|
||||
label='User (name)',
|
||||
@@ -286,8 +300,11 @@ class ManufacturerFilter(django_filters.FilterSet):
|
||||
fields = ['name', 'slug']
|
||||
|
||||
|
||||
class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
class DeviceTypeFilter(CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
@@ -297,18 +314,41 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Manufacturer (ID)',
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
name='manufacturer__slug',
|
||||
field_name='manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
)
|
||||
console_ports = django_filters.BooleanFilter(
|
||||
method='_console_ports',
|
||||
label='Has console ports',
|
||||
)
|
||||
console_server_ports = django_filters.BooleanFilter(
|
||||
method='_console_server_ports',
|
||||
label='Has console server ports',
|
||||
)
|
||||
power_ports = django_filters.BooleanFilter(
|
||||
method='_power_ports',
|
||||
label='Has power ports',
|
||||
)
|
||||
power_outlets = django_filters.BooleanFilter(
|
||||
method='_power_outlets',
|
||||
label='Has power outlets',
|
||||
)
|
||||
interfaces = django_filters.BooleanFilter(
|
||||
method='_interfaces',
|
||||
label='Has interfaces',
|
||||
)
|
||||
pass_through_ports = django_filters.BooleanFilter(
|
||||
method='_pass_through_ports',
|
||||
label='Has pass-through ports',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
model = DeviceType
|
||||
fields = [
|
||||
'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
|
||||
'is_network_device', 'subdevice_role',
|
||||
'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
@@ -321,11 +361,32 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
Q(comments__icontains=value)
|
||||
)
|
||||
|
||||
def _console_ports(self, queryset, name, value):
|
||||
return queryset.exclude(consoleport_templates__isnull=value)
|
||||
|
||||
def _console_server_ports(self, queryset, name, value):
|
||||
return queryset.exclude(consoleserverport_templates__isnull=value)
|
||||
|
||||
def _power_ports(self, queryset, name, value):
|
||||
return queryset.exclude(powerport_templates__isnull=value)
|
||||
|
||||
def _power_outlets(self, queryset, name, value):
|
||||
return queryset.exclude(poweroutlet_templates__isnull=value)
|
||||
|
||||
def _interfaces(self, queryset, name, value):
|
||||
return queryset.exclude(interface_templates__isnull=value)
|
||||
|
||||
def _pass_through_ports(self, queryset, name, value):
|
||||
return queryset.exclude(
|
||||
frontport_templates__isnull=value,
|
||||
rearport_templates__isnull=value
|
||||
)
|
||||
|
||||
|
||||
class DeviceTypeComponentFilterSet(django_filters.FilterSet):
|
||||
devicetype_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=DeviceType.objects.all(),
|
||||
name='device_type_id',
|
||||
field_name='device_type_id',
|
||||
label='Device type (ID)',
|
||||
)
|
||||
|
||||
@@ -365,6 +426,20 @@ class InterfaceTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
fields = ['name', 'form_factor', 'mgmt_only']
|
||||
|
||||
|
||||
class FrontPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = ['name', 'type']
|
||||
|
||||
|
||||
class RearPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
fields = ['name', 'type']
|
||||
|
||||
|
||||
class DeviceBayTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
@@ -381,12 +456,12 @@ class DeviceRoleFilter(django_filters.FilterSet):
|
||||
|
||||
class PlatformFilter(django_filters.FilterSet):
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='manufacturer',
|
||||
field_name='manufacturer',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
label='Manufacturer (ID)',
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
name='manufacturer__slug',
|
||||
field_name='manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
@@ -397,19 +472,22 @@ class PlatformFilter(django_filters.FilterSet):
|
||||
fields = ['name', 'slug']
|
||||
|
||||
|
||||
class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||
class DeviceFilter(CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='device_type__manufacturer',
|
||||
field_name='device_type__manufacturer',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
label='Manufacturer (ID)',
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
name='device_type__manufacturer__slug',
|
||||
field_name='device_type__manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
@@ -419,12 +497,12 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Device type (ID)',
|
||||
)
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='device_role_id',
|
||||
field_name='device_role_id',
|
||||
queryset=DeviceRole.objects.all(),
|
||||
label='Role (ID)',
|
||||
)
|
||||
role = django_filters.ModelMultipleChoiceFilter(
|
||||
name='device_role__slug',
|
||||
field_name='device_role__slug',
|
||||
queryset=DeviceRole.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Role (slug)',
|
||||
@@ -434,7 +512,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='tenant__slug',
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
@@ -444,7 +522,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Platform (ID)',
|
||||
)
|
||||
platform = django_filters.ModelMultipleChoiceFilter(
|
||||
name='platform__slug',
|
||||
field_name='platform__slug',
|
||||
queryset=Platform.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Platform (slug)',
|
||||
@@ -453,12 +531,12 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
asset_tag = NullableCharFieldFilter()
|
||||
region_id = django_filters.NumberFilter(
|
||||
method='filter_region',
|
||||
name='pk',
|
||||
field_name='pk',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = django_filters.CharFilter(
|
||||
method='filter_region',
|
||||
name='slug',
|
||||
field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@@ -466,18 +544,18 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='site__slug',
|
||||
field_name='site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site name (slug)',
|
||||
)
|
||||
rack_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack__group',
|
||||
field_name='rack__group',
|
||||
queryset=RackGroup.objects.all(),
|
||||
label='Rack group (ID)',
|
||||
)
|
||||
rack_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='rack',
|
||||
field_name='rack',
|
||||
queryset=Rack.objects.all(),
|
||||
label='Rack (ID)',
|
||||
)
|
||||
@@ -486,7 +564,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='VM cluster (ID)',
|
||||
)
|
||||
model = django_filters.ModelMultipleChoiceFilter(
|
||||
name='device_type__slug',
|
||||
field_name='device_type__slug',
|
||||
queryset=DeviceType.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Device model (slug)',
|
||||
@@ -496,21 +574,9 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
null_value=None
|
||||
)
|
||||
is_full_depth = django_filters.BooleanFilter(
|
||||
name='device_type__is_full_depth',
|
||||
field_name='device_type__is_full_depth',
|
||||
label='Is full depth',
|
||||
)
|
||||
is_console_server = django_filters.BooleanFilter(
|
||||
name='device_type__is_console_server',
|
||||
label='Is a console server',
|
||||
)
|
||||
is_pdu = django_filters.BooleanFilter(
|
||||
name='device_type__is_pdu',
|
||||
label='Is a PDU',
|
||||
)
|
||||
is_network_device = django_filters.BooleanFilter(
|
||||
name='device_type__is_network_device',
|
||||
label='Is a network device',
|
||||
)
|
||||
mac_address = django_filters.CharFilter(
|
||||
method='_mac_address',
|
||||
label='MAC address',
|
||||
@@ -520,10 +586,34 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Has a primary IP',
|
||||
)
|
||||
virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='virtual_chassis',
|
||||
field_name='virtual_chassis',
|
||||
queryset=VirtualChassis.objects.all(),
|
||||
label='Virtual chassis (ID)',
|
||||
)
|
||||
console_ports = django_filters.BooleanFilter(
|
||||
method='_console_ports',
|
||||
label='Has console ports',
|
||||
)
|
||||
console_server_ports = django_filters.BooleanFilter(
|
||||
method='_console_server_ports',
|
||||
label='Has console server ports',
|
||||
)
|
||||
power_ports = django_filters.BooleanFilter(
|
||||
method='_power_ports',
|
||||
label='Has power ports',
|
||||
)
|
||||
power_outlets = django_filters.BooleanFilter(
|
||||
method='_power_outlets',
|
||||
label='Has power outlets',
|
||||
)
|
||||
interfaces = django_filters.BooleanFilter(
|
||||
method='_interfaces',
|
||||
label='Has interfaces',
|
||||
)
|
||||
pass_through_ports = django_filters.BooleanFilter(
|
||||
method='_pass_through_ports',
|
||||
label='Has pass-through ports',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
@@ -573,6 +663,27 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
Q(primary_ip6__isnull=False)
|
||||
)
|
||||
|
||||
def _console_ports(self, queryset, name, value):
|
||||
return queryset.exclude(consoleports__isnull=value)
|
||||
|
||||
def _console_server_ports(self, queryset, name, value):
|
||||
return queryset.exclude(consoleserverports__isnull=value)
|
||||
|
||||
def _power_ports(self, queryset, name, value):
|
||||
return queryset.exclude(powerports__isnull=value)
|
||||
|
||||
def _power_outlets(self, queryset, name, value):
|
||||
return queryset.exclude(poweroutlets_isnull=value)
|
||||
|
||||
def _interfaces(self, queryset, name, value):
|
||||
return queryset.exclude(interfaces__isnull=value)
|
||||
|
||||
def _pass_through_ports(self, queryset, name, value):
|
||||
return queryset.exclude(
|
||||
frontports__isnull=value,
|
||||
rearports__isnull=value
|
||||
)
|
||||
|
||||
|
||||
class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||
device_id = django_filters.ModelChoiceFilter(
|
||||
@@ -588,54 +699,78 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||
|
||||
|
||||
class ConsolePortFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = ['name']
|
||||
fields = ['name', 'connection_status']
|
||||
|
||||
|
||||
class ConsoleServerPortFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = ['name']
|
||||
fields = ['name', 'connection_status']
|
||||
|
||||
|
||||
class PowerPortFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = ['name']
|
||||
fields = ['name', 'connection_status']
|
||||
|
||||
|
||||
class PowerOutletFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ['name']
|
||||
fields = ['name', 'connection_status']
|
||||
|
||||
|
||||
class InterfaceFilter(django_filters.FilterSet):
|
||||
"""
|
||||
Not using DeviceComponentFilterSet for Interfaces because we need to glean the ordering logic from the parent
|
||||
Device's DeviceType.
|
||||
Not using DeviceComponentFilterSet for Interfaces because we need to check for VirtualChassis membership.
|
||||
"""
|
||||
device = django_filters.CharFilter(
|
||||
method='filter_device',
|
||||
name='name',
|
||||
field_name='name',
|
||||
label='Device',
|
||||
)
|
||||
device_id = django_filters.NumberFilter(
|
||||
method='filter_device',
|
||||
name='pk',
|
||||
field_name='pk',
|
||||
label='Device (ID)',
|
||||
)
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
type = django_filters.CharFilter(
|
||||
method='filter_type',
|
||||
label='Interface type',
|
||||
)
|
||||
lag_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='lag',
|
||||
field_name='lag',
|
||||
queryset=Interface.objects.all(),
|
||||
label='LAG interface (ID)',
|
||||
)
|
||||
@@ -659,14 +794,13 @@ class InterfaceFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['name', 'enabled', 'mtu', 'mgmt_only']
|
||||
fields = ['name', 'connection_status', 'form_factor', 'enabled', 'mtu', 'mgmt_only']
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
try:
|
||||
device = Device.objects.select_related('device_type').get(**{name: value})
|
||||
vc_interface_ids = [i['id'] for i in device.vc_interfaces.values('id')]
|
||||
ordering = device.device_type.interface_ordering
|
||||
return queryset.filter(pk__in=vc_interface_ids).order_naturally(ordering)
|
||||
device = Device.objects.get(**{name: value})
|
||||
vc_interface_ids = device.vc_interfaces.values_list('id', flat=True)
|
||||
return queryset.filter(pk__in=vc_interface_ids)
|
||||
except Device.DoesNotExist:
|
||||
return queryset.none()
|
||||
|
||||
@@ -708,6 +842,30 @@ class InterfaceFilter(django_filters.FilterSet):
|
||||
return queryset.none()
|
||||
|
||||
|
||||
class FrontPortFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = FrontPort
|
||||
fields = ['name', 'type']
|
||||
|
||||
|
||||
class RearPortFilter(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
exclude=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['name', 'type']
|
||||
|
||||
|
||||
class DeviceBayFilter(DeviceComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
@@ -738,7 +896,7 @@ class InventoryItemFilter(DeviceComponentFilterSet):
|
||||
label='Manufacturer (ID)',
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
name='manufacturer__slug',
|
||||
field_name='manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
@@ -768,23 +926,23 @@ class VirtualChassisFilter(django_filters.FilterSet):
|
||||
label='Search',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='master__site',
|
||||
field_name='master__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
name='master__site__slug',
|
||||
field_name='master__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site name (slug)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='master__tenant',
|
||||
field_name='master__tenant',
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
name='master__tenant__slug',
|
||||
field_name='master__tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
@@ -805,6 +963,28 @@ class VirtualChassisFilter(django_filters.FilterSet):
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class CableFilter(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=CABLE_TYPE_CHOICES
|
||||
)
|
||||
color = django_filters.MultipleChoiceFilter(
|
||||
choices=COLOR_CHOICES
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
fields = ['type', 'status', 'color', 'length', 'length_unit']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(label__icontains=value)
|
||||
|
||||
|
||||
class ConsoleConnectionFilter(django_filters.FilterSet):
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
@@ -822,14 +1002,14 @@ class ConsoleConnectionFilter(django_filters.FilterSet):
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(cs_port__device__site__slug=value)
|
||||
return queryset.filter(connected_endpoint__device__site__slug=value)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(device__name__icontains=value) |
|
||||
Q(cs_port__device__name__icontains=value)
|
||||
Q(connected_endpoint__device__name__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
@@ -850,14 +1030,14 @@ class PowerConnectionFilter(django_filters.FilterSet):
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(power_outlet__device__site__slug=value)
|
||||
return queryset.filter(connected_endpoint__device__site__slug=value)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(device__name__icontains=value) |
|
||||
Q(power_outlet__device__name__icontains=value)
|
||||
Q(connected_endpoint__device__name__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
@@ -872,21 +1052,21 @@ class InterfaceConnectionFilter(django_filters.FilterSet):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InterfaceConnection
|
||||
model = Interface
|
||||
fields = ['connection_status']
|
||||
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(interface_a__device__site__slug=value) |
|
||||
Q(interface_b__device__site__slug=value)
|
||||
Q(device__site__slug=value) |
|
||||
Q(_connected_interface__device__site__slug=value)
|
||||
)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(interface_a__device__name__icontains=value) |
|
||||
Q(interface_b__device__name__icontains=value)
|
||||
Q(device__name__icontains=value) |
|
||||
Q(_connected_interface__device__name__icontains=value)
|
||||
)
|
||||
|
||||
@@ -76,10 +76,7 @@
|
||||
"model": "MX960",
|
||||
"slug": "mx960",
|
||||
"u_height": 16,
|
||||
"is_full_depth": true,
|
||||
"is_console_server": false,
|
||||
"is_pdu": false,
|
||||
"is_network_device": true
|
||||
"is_full_depth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -92,10 +89,7 @@
|
||||
"model": "EX9214",
|
||||
"slug": "ex9214",
|
||||
"u_height": 16,
|
||||
"is_full_depth": true,
|
||||
"is_console_server": false,
|
||||
"is_pdu": false,
|
||||
"is_network_device": true
|
||||
"is_full_depth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -108,10 +102,7 @@
|
||||
"model": "QFX5100-24Q",
|
||||
"slug": "qfx5100-24q",
|
||||
"u_height": 1,
|
||||
"is_full_depth": true,
|
||||
"is_console_server": false,
|
||||
"is_pdu": false,
|
||||
"is_network_device": true
|
||||
"is_full_depth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -124,10 +115,7 @@
|
||||
"model": "QFX5100-48S",
|
||||
"slug": "qfx5100-48s",
|
||||
"u_height": 1,
|
||||
"is_full_depth": true,
|
||||
"is_console_server": false,
|
||||
"is_pdu": false,
|
||||
"is_network_device": true
|
||||
"is_full_depth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -140,10 +128,7 @@
|
||||
"model": "CM4148",
|
||||
"slug": "cm4148",
|
||||
"u_height": 1,
|
||||
"is_full_depth": true,
|
||||
"is_console_server": true,
|
||||
"is_pdu": false,
|
||||
"is_network_device": false
|
||||
"is_full_depth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -156,10 +141,7 @@
|
||||
"model": "CWG-24VYM415C9",
|
||||
"slug": "cwg-24vym415c9",
|
||||
"u_height": 0,
|
||||
"is_full_depth": false,
|
||||
"is_console_server": false,
|
||||
"is_pdu": true,
|
||||
"is_network_device": false
|
||||
"is_full_depth": false
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1903,8 +1885,7 @@
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Juniper Junos",
|
||||
"slug": "juniper-junos",
|
||||
"rpc_client": "juniper-junos"
|
||||
"slug": "juniper-junos"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1912,8 +1893,7 @@
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Opengear",
|
||||
"slug": "opengear",
|
||||
"rpc_client": "opengear"
|
||||
"slug": "opengear"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2153,7 +2133,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "Console (RE0)",
|
||||
"cs_port": 27,
|
||||
"connected_endpoint": 27,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2163,7 +2143,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "Console (RE1)",
|
||||
"cs_port": 38,
|
||||
"connected_endpoint": 38,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2173,7 +2153,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "Console (RE0)",
|
||||
"cs_port": 5,
|
||||
"connected_endpoint": 5,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2183,7 +2163,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "Console (RE1)",
|
||||
"cs_port": 16,
|
||||
"connected_endpoint": 16,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2193,7 +2173,7 @@
|
||||
"fields": {
|
||||
"device": 3,
|
||||
"name": "Console",
|
||||
"cs_port": 49,
|
||||
"connected_endpoint": 49,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2203,7 +2183,7 @@
|
||||
"fields": {
|
||||
"device": 4,
|
||||
"name": "Console",
|
||||
"cs_port": 48,
|
||||
"connected_endpoint": 48,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2213,7 +2193,7 @@
|
||||
"fields": {
|
||||
"device": 5,
|
||||
"name": "Console",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2223,7 +2203,7 @@
|
||||
"fields": {
|
||||
"device": 6,
|
||||
"name": "Console",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2233,7 +2213,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "Console (RE0)",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2243,7 +2223,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "Console (RE1)",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2253,7 +2233,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "Console (RE0)",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2263,7 +2243,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "Console (RE1)",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2273,7 +2253,7 @@
|
||||
"fields": {
|
||||
"device": 9,
|
||||
"name": "Console",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2283,7 +2263,7 @@
|
||||
"fields": {
|
||||
"device": 11,
|
||||
"name": "Serial",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2293,7 +2273,7 @@
|
||||
"fields": {
|
||||
"device": 12,
|
||||
"name": "Serial",
|
||||
"cs_port": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2687,7 +2667,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "PEM0",
|
||||
"power_outlet": 25,
|
||||
"connected_endpoint": 25,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2697,7 +2677,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "PEM1",
|
||||
"power_outlet": 49,
|
||||
"connected_endpoint": 49,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2707,7 +2687,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "PEM2",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2717,7 +2697,7 @@
|
||||
"fields": {
|
||||
"device": 1,
|
||||
"name": "PEM3",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2727,7 +2707,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "PEM0",
|
||||
"power_outlet": 26,
|
||||
"connected_endpoint": 26,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2737,7 +2717,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "PEM1",
|
||||
"power_outlet": 50,
|
||||
"connected_endpoint": 50,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2747,7 +2727,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "PEM2",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2757,7 +2737,7 @@
|
||||
"fields": {
|
||||
"device": 2,
|
||||
"name": "PEM3",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2767,7 +2747,7 @@
|
||||
"fields": {
|
||||
"device": 4,
|
||||
"name": "PSU0",
|
||||
"power_outlet": 28,
|
||||
"connected_endpoint": 28,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2777,7 +2757,7 @@
|
||||
"fields": {
|
||||
"device": 4,
|
||||
"name": "PSU1",
|
||||
"power_outlet": 52,
|
||||
"connected_endpoint": 52,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2787,7 +2767,7 @@
|
||||
"fields": {
|
||||
"device": 5,
|
||||
"name": "PSU0",
|
||||
"power_outlet": 56,
|
||||
"connected_endpoint": 56,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2797,7 +2777,7 @@
|
||||
"fields": {
|
||||
"device": 5,
|
||||
"name": "PSU1",
|
||||
"power_outlet": 32,
|
||||
"connected_endpoint": 32,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2807,7 +2787,7 @@
|
||||
"fields": {
|
||||
"device": 3,
|
||||
"name": "PSU0",
|
||||
"power_outlet": 27,
|
||||
"connected_endpoint": 27,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2817,7 +2797,7 @@
|
||||
"fields": {
|
||||
"device": 3,
|
||||
"name": "PSU1",
|
||||
"power_outlet": 51,
|
||||
"connected_endpoint": 51,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2827,7 +2807,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "PEM0",
|
||||
"power_outlet": 53,
|
||||
"connected_endpoint": 53,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2837,7 +2817,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "PEM1",
|
||||
"power_outlet": 29,
|
||||
"connected_endpoint": 29,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2847,7 +2827,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "PEM2",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2857,7 +2837,7 @@
|
||||
"fields": {
|
||||
"device": 7,
|
||||
"name": "PEM3",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2867,7 +2847,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "PEM0",
|
||||
"power_outlet": 54,
|
||||
"connected_endpoint": 54,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2877,7 +2857,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "PEM1",
|
||||
"power_outlet": 30,
|
||||
"connected_endpoint": 30,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2887,7 +2867,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "PEM2",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2897,7 +2877,7 @@
|
||||
"fields": {
|
||||
"device": 8,
|
||||
"name": "PEM3",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2907,7 +2887,7 @@
|
||||
"fields": {
|
||||
"device": 6,
|
||||
"name": "PSU0",
|
||||
"power_outlet": 55,
|
||||
"connected_endpoint": 55,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2917,7 +2897,7 @@
|
||||
"fields": {
|
||||
"device": 6,
|
||||
"name": "PSU1",
|
||||
"power_outlet": 31,
|
||||
"connected_endpoint": 31,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -2927,7 +2907,7 @@
|
||||
"fields": {
|
||||
"device": 9,
|
||||
"name": "PSU",
|
||||
"power_outlet": null,
|
||||
"connected_endpoint": null,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
@@ -5748,158 +5728,5 @@
|
||||
"mgmt_only": true,
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"interface_a": 99,
|
||||
"interface_b": 15,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"interface_a": 100,
|
||||
"interface_b": 153,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"interface_a": 46,
|
||||
"interface_b": 14,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"interface_a": 47,
|
||||
"interface_b": 152,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"interface_a": 91,
|
||||
"interface_b": 144,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"interface_a": 92,
|
||||
"interface_b": 145,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"interface_a": 189,
|
||||
"interface_b": 37,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"interface_a": 192,
|
||||
"interface_b": 175,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"interface_a": 195,
|
||||
"interface_b": 41,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"interface_a": 198,
|
||||
"interface_b": 179,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"interface_a": 191,
|
||||
"interface_b": 197,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"interface_a": 194,
|
||||
"interface_b": 200,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"interface_a": 9,
|
||||
"interface_b": 218,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"interface_a": 8,
|
||||
"interface_b": 206,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"interface_a": 7,
|
||||
"interface_b": 212,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 25,
|
||||
"fields": {
|
||||
"interface_a": 217,
|
||||
"interface_b": 205,
|
||||
"connection_status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.interfaceconnection",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"interface_a": 216,
|
||||
"interface_b": 211,
|
||||
"connection_status": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -149,8 +149,7 @@
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Cisco IOS",
|
||||
"slug": "cisco-ios",
|
||||
"rpc_client": "cisco-ios"
|
||||
"slug": "cisco-ios"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -158,8 +157,7 @@
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Cisco NX-OS",
|
||||
"slug": "cisco-nx-os",
|
||||
"rpc_client": ""
|
||||
"slug": "cisco-nx-os"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -167,8 +165,7 @@
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Juniper Junos",
|
||||
"slug": "juniper-junos",
|
||||
"rpc_client": "juniper-junos"
|
||||
"slug": "juniper-junos"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -176,8 +173,7 @@
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "Arista EOS",
|
||||
"slug": "arista-eos",
|
||||
"rpc_client": ""
|
||||
"slug": "arista-eos"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -185,8 +181,7 @@
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Linux",
|
||||
"slug": "linux",
|
||||
"rpc_client": ""
|
||||
"slug": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -194,8 +189,7 @@
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "Opengear",
|
||||
"slug": "opengear",
|
||||
"rpc_client": "opengear"
|
||||
"slug": "opengear"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
2053
netbox/dcim/forms.py
2053
netbox/dcim/forms.py
File diff suppressed because it is too large
Load Diff
85
netbox/dcim/managers.py
Normal file
85
netbox/dcim/managers.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from django.db.models import Manager, QuerySet
|
||||
from django.db.models.expressions import RawSQL
|
||||
|
||||
from .constants import NONCONNECTABLE_IFACE_TYPES
|
||||
|
||||
# Regular expressions for parsing Interface names
|
||||
TYPE_RE = r"SUBSTRING({} FROM '^([^0-9\.:]+)')"
|
||||
SLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(\d{{1,9}})/') AS integer), NULL)"
|
||||
SUBSLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9\.:]+)?\d{{1,9}}/(\d{{1,9}})') AS integer), NULL)"
|
||||
POSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}/){{2}}(\d{{1,9}})') AS integer), NULL)"
|
||||
SUBPOSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}/){{3}}(\d{{1,9}})') AS integer), NULL)"
|
||||
ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9\.:]+)?(\d{{1,9}})([^/]|$)') AS integer)"
|
||||
CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*:(\d{{1,9}})(\.\d{{1,9}})?$') AS integer), 0)"
|
||||
VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*\.(\d{{1,9}})$') AS integer), 0)"
|
||||
|
||||
|
||||
class DeviceComponentManager(Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
queryset = super().get_queryset()
|
||||
table_name = self.model._meta.db_table
|
||||
sql = r"CONCAT(REGEXP_REPLACE({}.name, '\d+$', ''), LPAD(SUBSTRING({}.name FROM '\d+$'), 8, '0'))"
|
||||
|
||||
# Pad any trailing digits to effect natural sorting
|
||||
return queryset.extra(
|
||||
select={
|
||||
'name_padded': sql.format(table_name, table_name),
|
||||
}
|
||||
).order_by('name_padded')
|
||||
|
||||
|
||||
class InterfaceQuerySet(QuerySet):
|
||||
|
||||
def connectable(self):
|
||||
"""
|
||||
Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or
|
||||
wireless).
|
||||
"""
|
||||
return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES)
|
||||
|
||||
|
||||
class InterfaceManager(Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Naturally order interfaces by their type and numeric position. To order interfaces naturally, the `name` field
|
||||
is split into eight distinct components: leading text (type), slot, subslot, position, subposition, ID, channel,
|
||||
and virtual circuit:
|
||||
|
||||
{type}{slot or ID}/{subslot}/{position}/{subposition}:{channel}.{vc}
|
||||
|
||||
Components absent from the interface name are coalesced to zero or null. For example, an interface named
|
||||
GigabitEthernet1/2/3 would be parsed as follows:
|
||||
|
||||
type = 'GigabitEthernet'
|
||||
slot = 1
|
||||
subslot = 2
|
||||
position = 3
|
||||
subposition = None
|
||||
id = None
|
||||
channel = 0
|
||||
vc = 0
|
||||
|
||||
The original `name` field is considered in its entirety to serve as a fallback in the event interfaces do not
|
||||
match any of the prescribed fields.
|
||||
"""
|
||||
|
||||
sql_col = '{}.name'.format(self.model._meta.db_table)
|
||||
ordering = [
|
||||
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name',
|
||||
]
|
||||
|
||||
fields = {
|
||||
'_type': RawSQL(TYPE_RE.format(sql_col), []),
|
||||
'_id': RawSQL(ID_RE.format(sql_col), []),
|
||||
'_slot': RawSQL(SLOT_RE.format(sql_col), []),
|
||||
'_subslot': RawSQL(SUBSLOT_RE.format(sql_col), []),
|
||||
'_position': RawSQL(POSITION_RE.format(sql_col), []),
|
||||
'_subposition': RawSQL(SUBPOSITION_RE.format(sql_col), []),
|
||||
'_channel': RawSQL(CHANNEL_RE.format(sql_col), []),
|
||||
'_vc': RawSQL(VC_RE.format(sql_col), []),
|
||||
}
|
||||
|
||||
return InterfaceQuerySet(self.model, using=self._db).annotate(**fields).order_by(*ordering)
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-06-22 18:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-06-22 18:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:06
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-06-28 17:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-01 20:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-06 17:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-11 18:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-11 18:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-11 19:01
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-13 19:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-14 21:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-07-26 15:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-07-26 21:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-06 20:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-08 21:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-09 21:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-10 13:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.8 on 2016-08-10 14:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-08-11 15:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-09-13 15:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-10-28 15:01
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-10-31 18:47
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-12-06 16:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-12-16 16:08
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:13
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-29 16:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-06 16:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-16 18:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-16 21:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-16 21:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-16 21:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-27 19:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-28 17:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import mptt.fields
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-03-02 15:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.6 on 2017-03-17 18:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.6 on 2017-03-21 14:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-05-08 15:57
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-05-09 16:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-05-24 15:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dcim.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-06-16 21:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-06-23 17:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-06-23 20:44
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-14 17:26
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-29 21:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-29 21:26
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-31 14:15
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:17
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-09-29 16:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-10-09 17:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-10-09 18:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-10-09 18:50
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-10-31 17:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-11-10 20:10
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-11-15 18:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-11-27 17:27
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-12-19 20:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2018-01-25 18:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import timezone_field.fields
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.9 on 2018-02-21 14:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user