Skip to content

Closes #3892: Robust ContentType filtering #3932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,10 +609,10 @@ class Meta:

class CableSerializer(ValidatedModelSerializer):
termination_a_type = ContentTypeField(
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
)
termination_b_type = ContentTypeField(
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
)
termination_a = serializers.SerializerMethodField(read_only=True)
termination_b = serializers.SerializerMethodField(read_only=True)
Expand Down
54 changes: 31 additions & 23 deletions netbox/dcim/constants.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from django.db.models import Q

from .choices import InterfaceTypeChoices

# BGP ASN bounds
BGP_ASN_MIN = 1
BGP_ASN_MAX = 2**32 - 1

#
# Rack elevation rendering
#

RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230
RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20


#
# Interface type groups
Expand All @@ -23,6 +30,12 @@

NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES


#
# Cabling and connections
#

# TODO: Replace with CableStatusChoices?
# Console/power/interface connection statuses
CONNECTION_STATUS_PLANNED = False
CONNECTION_STATUS_CONNECTED = True
Expand All @@ -32,21 +45,21 @@
]

# Cable endpoint types
CABLE_TERMINATION_TYPES = [
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
'circuittermination', 'powerfeed',
]

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'),
}
CABLE_TERMINATION_MODELS = Q(
Q(app_label='circuits', model__in=(
'circuittermination',
)) |
Q(app_label='dcim', model__in=(
'consoleport',
'consoleserverport',
'frontport',
'interface',
'powerfeed',
'poweroutlet',
'powerport',
'rearport',
))
)

COMPATIBLE_TERMINATION_TYPES = {
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
Expand All @@ -58,8 +71,3 @@
'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
'circuittermination': ['interface', 'frontport', 'rearport'],
}


# Rack Elevation SVG Size
RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230
RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20
7 changes: 5 additions & 2 deletions netbox/dcim/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.db import models
from netaddr import AddrFormatError, EUI, mac_unix_expanded

from .constants import *
from ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN


class ASNField(models.BigIntegerField):
Expand All @@ -14,7 +14,10 @@ class ASNField(models.BigIntegerField):
]

def formfield(self, **kwargs):
defaults = {'min_value': BGP_ASN_MIN, 'max_value': BGP_ASN_MAX}
defaults = {
'min_value': BGP_ASN_MIN,
'max_value': BGP_ASN_MAX,
}
defaults.update(**kwargs)
return super().formfield(**defaults)

Expand Down
13 changes: 5 additions & 8 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from extras.forms import (
AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm, LocalConfigContextFilterForm
)
from ipam.models import IPAddress, VLAN, VLANGroup
from ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN
from ipam.models import IPAddress, VLAN
from tenancy.forms import TenancyFilterForm, TenancyForm
from tenancy.models import Tenant, TenantGroup
from utilities.forms import (
Expand Down Expand Up @@ -103,7 +104,7 @@ def clean(self):
self.cleaned_data['tagged_vlans'] = []

# Validate tagged VLANs; must be a global VLAN or in the same site
elif self.cleaned_data['mode'] == IFACE_MODE_TAGGED:
elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED:
valid_sites = [None, self.cleaned_data['device'].site]
invalid_vlans = [str(v) for v in tagged_vlans if v.site not in valid_sites]

Expand Down Expand Up @@ -3373,9 +3374,7 @@ class CableCSVForm(forms.ModelForm):
)
side_a_type = forms.ModelChoiceField(
queryset=ContentType.objects.all(),
limit_choices_to={
'model__in': CABLE_TERMINATION_TYPES,
},
limit_choices_to=CABLE_TERMINATION_MODELS,
to_field_name='model',
help_text='Side A type'
)
Expand All @@ -3394,9 +3393,7 @@ class CableCSVForm(forms.ModelForm):
)
side_b_type = forms.ModelChoiceField(
queryset=ContentType.objects.all(),
limit_choices_to={
'model__in': CABLE_TERMINATION_TYPES,
},
limit_choices_to=CABLE_TERMINATION_MODELS,
to_field_name='model',
help_text='Side B type'
)
Expand Down
24 changes: 24 additions & 0 deletions netbox/dcim/migrations/0090_cable_termination_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.2.8 on 2020-01-15 20:51

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('dcim', '0089_deterministic_ordering'),
]

operations = [
migrations.AlterField(
model_name='cable',
name='termination_a_type',
field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'),
),
migrations.AlterField(
model_name='cable',
name='termination_b_type',
field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'),
),
]
4 changes: 2 additions & 2 deletions netbox/dcim/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,7 @@ class Cable(ChangeLoggedModel):
"""
termination_a_type = models.ForeignKey(
to=ContentType,
limit_choices_to={'model__in': CABLE_TERMINATION_TYPES},
limit_choices_to=CABLE_TERMINATION_MODELS,
on_delete=models.PROTECT,
related_name='+'
)
Expand All @@ -1950,7 +1950,7 @@ class Cable(ChangeLoggedModel):
)
termination_b_type = models.ForeignKey(
to=ContentType,
limit_choices_to={'model__in': CABLE_TERMINATION_TYPES},
limit_choices_to=CABLE_TERMINATION_MODELS,
on_delete=models.PROTECT,
related_name='+'
)
Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_choices(self):
# Cable
self.assertEqual(choices_to_dict(response.data.get('cable:length_unit')), CableLengthUnitChoices.as_dict())
self.assertEqual(choices_to_dict(response.data.get('cable:status')), CableStatusChoices.as_dict())
content_types = ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
content_types = ContentType.objects.filter(CABLE_TERMINATION_MODELS)
cable_termination_choices = {
"{}.{}".format(ct.app_label, ct.model): ct.name for ct in content_types
}
Expand Down
3 changes: 1 addition & 2 deletions netbox/extras/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
ChoiceField, ContentTypeField, get_serializer_for_model, SerializerNotFound, SerializedPKRelatedField,
ValidatedModelSerializer,
)
from utilities.utils import model_names_to_filter_dict
from .nested_serializers import *


Expand All @@ -30,7 +29,7 @@

class GraphSerializer(ValidatedModelSerializer):
type = ContentTypeField(
queryset=ContentType.objects.filter(**model_names_to_filter_dict(GRAPH_MODELS)),
queryset=ContentType.objects.filter(GRAPH_MODELS),
)

class Meta:
Expand Down
Loading