mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-29 11:56:25 -06:00
Fix E501 errors
This commit is contained in:
parent
d39dc6d45c
commit
3b7d8dd5db
@ -351,9 +351,9 @@ class InventoryItemSerializer(NetBoxModelSerializer):
|
||||
class Meta:
|
||||
model = InventoryItem
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role', 'manufacturer',
|
||||
'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type', 'component_id',
|
||||
'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
||||
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role',
|
||||
'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type',
|
||||
'component_id', 'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|
||||
|
||||
|
@ -312,8 +312,8 @@ class RackTypeFilterSet(NetBoxModelFilterSet):
|
||||
class Meta:
|
||||
model = RackType
|
||||
fields = (
|
||||
'id', 'model', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
|
||||
'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
|
||||
'id', 'model', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth',
|
||||
'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
|
||||
)
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
|
@ -428,7 +428,10 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
|
||||
|
||||
class Meta:
|
||||
model = ModuleType
|
||||
fields = ['manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'comments', 'tags']
|
||||
fields = [
|
||||
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'comments',
|
||||
'tags',
|
||||
]
|
||||
|
||||
|
||||
class DeviceRoleImportForm(NetBoxModelImportForm):
|
||||
@ -800,7 +803,10 @@ class PowerOutletImportForm(NetBoxModelImportForm):
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ('device', 'name', 'label', 'type', 'color', 'mark_connected', 'power_port', 'feed_leg', 'description', 'tags')
|
||||
fields = (
|
||||
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'power_port', 'feed_leg', 'description',
|
||||
'tags',
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -1114,8 +1120,8 @@ class InventoryItemImportForm(NetBoxModelImportForm):
|
||||
class Meta:
|
||||
model = InventoryItem
|
||||
fields = (
|
||||
'device', 'name', 'label', 'status', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag', 'discovered',
|
||||
'description', 'tags', 'component_type', 'component_name',
|
||||
'device', 'name', 'label', 'status', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag',
|
||||
'discovered', 'description', 'tags', 'component_type', 'component_name',
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -136,7 +136,10 @@ class ModuleCommonForm(forms.Form):
|
||||
|
||||
if len(module_bays) != template.name.count(MODULE_TOKEN):
|
||||
raise forms.ValidationError(
|
||||
_("Cannot install module with placeholder values in a module bay tree {level} in tree but {tokens} placeholders given.").format(
|
||||
_(
|
||||
"Cannot install module with placeholder values in a module bay tree {level} in tree "
|
||||
"but {tokens} placeholders given."
|
||||
).format(
|
||||
level=len(module_bays), tokens=template.name.count(MODULE_TOKEN)
|
||||
)
|
||||
)
|
||||
|
@ -111,9 +111,15 @@ def get_cable_form(a_type, b_type):
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
# Initialize A/B terminations when modifying an existing Cable instance
|
||||
if a_type and self.instance.a_terminations and a_ct == ContentType.objects.get_for_model(self.instance.a_terminations[0]):
|
||||
if (
|
||||
a_type and self.instance.a_terminations and
|
||||
a_ct == ContentType.objects.get_for_model(self.instance.a_terminations[0])
|
||||
):
|
||||
self.initial['a_terminations'] = self.instance.a_terminations
|
||||
if b_type and self.instance.b_terminations and b_ct == ContentType.objects.get_for_model(self.instance.b_terminations[0]):
|
||||
if (
|
||||
b_type and self.instance.b_terminations and
|
||||
b_ct == ContentType.objects.get_for_model(self.instance.b_terminations[0])
|
||||
):
|
||||
self.initial['b_terminations'] = self.instance.b_terminations
|
||||
else:
|
||||
# Need to clear terminations if swapped type - but need to do it only
|
||||
|
@ -266,7 +266,10 @@ class RackForm(TenancyForm, NetBoxModelForm):
|
||||
comments = CommentField()
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('site', 'location', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags', name=_('Rack')),
|
||||
FieldSet(
|
||||
'site', 'location', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags',
|
||||
name=_('Rack')
|
||||
),
|
||||
FieldSet('facility_id', 'serial', 'asset_tag', name=_('Inventory Control')),
|
||||
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
||||
)
|
||||
@ -1007,7 +1010,8 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
|
||||
class Meta:
|
||||
model = InterfaceTemplate
|
||||
fields = [
|
||||
'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type', 'bridge', 'rf_role',
|
||||
'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode',
|
||||
'poe_type', 'bridge', 'rf_role',
|
||||
]
|
||||
|
||||
|
||||
@ -1189,7 +1193,10 @@ class InventoryItemTemplateForm(ComponentTemplateForm):
|
||||
break
|
||||
elif component_type and component_id:
|
||||
# When adding the InventoryItem from a component page
|
||||
if content_type := ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS).filter(pk=component_type).first():
|
||||
content_type = ContentType.objects.filter(
|
||||
MODULAR_COMPONENT_TEMPLATE_MODELS
|
||||
).filter(pk=component_type).first()
|
||||
if content_type:
|
||||
if component := content_type.model_class().objects.filter(pk=component_id).first():
|
||||
initial[content_type.model] = component
|
||||
|
||||
@ -1301,16 +1308,16 @@ class PowerOutletForm(ModularDeviceComponentForm):
|
||||
|
||||
fieldsets = (
|
||||
FieldSet(
|
||||
'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description',
|
||||
'tags',
|
||||
'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected',
|
||||
'description', 'tags',
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description',
|
||||
'tags',
|
||||
'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected',
|
||||
'description', 'tags',
|
||||
]
|
||||
|
||||
|
||||
@ -1611,7 +1618,10 @@ class InventoryItemForm(DeviceComponentForm):
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('device', 'parent', 'name', 'label', 'status', 'role', 'description', 'tags', name=_('Inventory Item')),
|
||||
FieldSet(
|
||||
'device', 'parent', 'name', 'label', 'status', 'role', 'description', 'tags',
|
||||
name=_('Inventory Item')
|
||||
),
|
||||
FieldSet('manufacturer', 'part_id', 'serial', 'asset_tag', name=_('Hardware')),
|
||||
FieldSet(
|
||||
TabbedGroups(
|
||||
|
@ -416,7 +416,8 @@ class VirtualChassisCreateForm(NetBoxModelForm):
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = [
|
||||
'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags',
|
||||
'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position',
|
||||
'tags',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
|
@ -136,7 +136,8 @@ class FrontPortTemplateImportForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = [
|
||||
'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label', 'description',
|
||||
'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label',
|
||||
'description',
|
||||
]
|
||||
|
||||
|
||||
|
@ -482,7 +482,9 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Organi
|
||||
return self.cluster_set.all()
|
||||
|
||||
@strawberry_django.field
|
||||
def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
|
||||
def circuit_terminations(self) -> List[
|
||||
Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
|
||||
]:
|
||||
return self.circuit_terminations.all()
|
||||
|
||||
|
||||
@ -728,7 +730,9 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
|
||||
return self.cluster_set.all()
|
||||
|
||||
@strawberry_django.field
|
||||
def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
|
||||
def circuit_terminations(self) -> List[
|
||||
Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
|
||||
]:
|
||||
return self.circuit_terminations.all()
|
||||
|
||||
|
||||
@ -760,7 +764,9 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje
|
||||
return self.cluster_set.all()
|
||||
|
||||
@strawberry_django.field
|
||||
def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
|
||||
def circuit_terminations(self) -> List[
|
||||
Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
|
||||
]:
|
||||
return self.circuit_terminations.all()
|
||||
|
||||
|
||||
@ -784,7 +790,9 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
|
||||
return self.cluster_set.all()
|
||||
|
||||
@strawberry_django.field
|
||||
def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
|
||||
def circuit_terminations(self) -> List[
|
||||
Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
|
||||
]:
|
||||
return self.circuit_terminations.all()
|
||||
|
||||
|
||||
|
@ -912,7 +912,10 @@ class Device(
|
||||
})
|
||||
if self.primary_ip4.assigned_object in vc_interfaces:
|
||||
pass
|
||||
elif self.primary_ip4.nat_inside is not None and self.primary_ip4.nat_inside.assigned_object in vc_interfaces:
|
||||
elif (
|
||||
self.primary_ip4.nat_inside is not None and
|
||||
self.primary_ip4.nat_inside.assigned_object in vc_interfaces
|
||||
):
|
||||
pass
|
||||
else:
|
||||
raise ValidationError({
|
||||
@ -927,7 +930,10 @@ class Device(
|
||||
})
|
||||
if self.primary_ip6.assigned_object in vc_interfaces:
|
||||
pass
|
||||
elif self.primary_ip6.nat_inside is not None and self.primary_ip6.nat_inside.assigned_object in vc_interfaces:
|
||||
elif (
|
||||
self.primary_ip6.nat_inside is not None and
|
||||
self.primary_ip6.nat_inside.assigned_object in vc_interfaces
|
||||
):
|
||||
pass
|
||||
else:
|
||||
raise ValidationError({
|
||||
|
@ -153,7 +153,10 @@ class RackElevationSVG:
|
||||
if self.rack.desc_units:
|
||||
y += int((position - self.rack.starting_unit) * self.unit_height)
|
||||
else:
|
||||
y += int((self.rack.u_height - position + self.rack.starting_unit) * self.unit_height) - int(height * self.unit_height)
|
||||
y += (
|
||||
int((self.rack.u_height - position + self.rack.starting_unit) * self.unit_height) -
|
||||
int(height * self.unit_height)
|
||||
)
|
||||
|
||||
return x, y
|
||||
|
||||
|
@ -53,7 +53,8 @@ class Command(BaseCommand):
|
||||
|
||||
else:
|
||||
raise CommandError(
|
||||
f"Invalid model: {label}. Model names must be in the format <app_label> or <app_label>.<model_name>."
|
||||
f"Invalid model: {label}. Model names must be in the format <app_label> or "
|
||||
f"<app_label>.<model_name>."
|
||||
)
|
||||
|
||||
return indexers
|
||||
|
@ -27,13 +27,15 @@ class Migration(migrations.Migration):
|
||||
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
'ALTER TABLE IF EXISTS extras_customfield_content_types_id_seq RENAME TO extras_customfield_object_types_id_seq'
|
||||
),
|
||||
migrations.RunSQL((
|
||||
'ALTER TABLE IF EXISTS extras_customfield_content_types_id_seq '
|
||||
'RENAME TO extras_customfield_object_types_id_seq'
|
||||
)),
|
||||
# Pre-v2.10 sequence name (see #15605)
|
||||
migrations.RunSQL(
|
||||
'ALTER TABLE IF EXISTS extras_customfield_obj_type_id_seq RENAME TO extras_customfield_object_types_id_seq'
|
||||
),
|
||||
migrations.RunSQL((
|
||||
'ALTER TABLE IF EXISTS extras_customfield_obj_type_id_seq '
|
||||
'RENAME TO extras_customfield_object_types_id_seq'
|
||||
)),
|
||||
# Custom links
|
||||
migrations.RenameField(
|
||||
model_name='customlink',
|
||||
|
@ -704,7 +704,10 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat
|
||||
|
||||
def __str__(self):
|
||||
created = timezone.localtime(self.created)
|
||||
return f"{created.date().isoformat()} {created.time().isoformat(timespec='minutes')} ({self.get_kind_display()})"
|
||||
return (
|
||||
f"{created.date().isoformat()} {created.time().isoformat(timespec='minutes')} "
|
||||
f"({self.get_kind_display()})"
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('extras:journalentry', args=[self.pk])
|
||||
|
@ -637,15 +637,51 @@ class CustomFieldAPITest(APITestCase):
|
||||
)
|
||||
|
||||
custom_fields = (
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo'),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_LONGTEXT, name='longtext_field', default='ABC'),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_INTEGER, name='integer_field', default=123),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_DECIMAL, name='decimal_field', default=123.45),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_BOOLEAN, name='boolean_field', default=False),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_DATE, name='date_field', default='2020-01-01'),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_DATETIME, name='datetime_field', default='2020-01-01T01:23:45'),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_URL, name='url_field', default='http://example.com/1'),
|
||||
CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_TEXT,
|
||||
name='text_field',
|
||||
default='foo'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_LONGTEXT,
|
||||
name='longtext_field',
|
||||
default='ABC'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_INTEGER,
|
||||
name='integer_field',
|
||||
default=123
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_DECIMAL,
|
||||
name='decimal_field',
|
||||
default=123.45
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
||||
name='boolean_field',
|
||||
default=False)
|
||||
,
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_DATE,
|
||||
name='date_field',
|
||||
default='2020-01-01'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_DATETIME,
|
||||
name='datetime_field',
|
||||
default='2020-01-01T01:23:45'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_URL,
|
||||
name='url_field',
|
||||
default='http://example.com/1'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_JSON,
|
||||
name='json_field',
|
||||
default='{"x": "y"}'
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_SELECT,
|
||||
name='select_field',
|
||||
@ -656,7 +692,7 @@ class CustomFieldAPITest(APITestCase):
|
||||
type=CustomFieldTypeChoices.TYPE_MULTISELECT,
|
||||
name='multiselect_field',
|
||||
default=['foo'],
|
||||
choice_set=choice_set
|
||||
choice_set=choice_set,
|
||||
),
|
||||
CustomField(
|
||||
type=CustomFieldTypeChoices.TYPE_OBJECT,
|
||||
@ -1273,9 +1309,18 @@ class CustomFieldImportTest(TestCase):
|
||||
Import a Site in CSV format, including a value for each CustomField.
|
||||
"""
|
||||
data = (
|
||||
('name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date', 'cf_datetime', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect'),
|
||||
('Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01', '2020-01-01 12:00:00', 'http://example.com/1', '{"foo": 123}', 'a', '"a,b"'),
|
||||
('Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02', '2020-01-02 12:00:00', 'http://example.com/2', '{"bar": 456}', 'b', '"b,c"'),
|
||||
(
|
||||
'name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date',
|
||||
'cf_datetime', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect',
|
||||
),
|
||||
(
|
||||
'Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01',
|
||||
'2020-01-01 12:00:00', 'http://example.com/1', '{"foo": 123}', 'a', '"a,b"',
|
||||
),
|
||||
(
|
||||
'Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02',
|
||||
'2020-01-02 12:00:00', 'http://example.com/2', '{"bar": 456}', 'b', '"b,c"',
|
||||
),
|
||||
('Site 3', 'site-3', 'active', '', '', '', '', '', '', '', '', '', '', ''),
|
||||
)
|
||||
csv_data = '\n'.join(','.join(row) for row in data)
|
||||
@ -1616,7 +1661,10 @@ class CustomFieldModelFilterTest(TestCase):
|
||||
self.assertEqual(self.filterset({'cf_cf6__lte': ['2016-06-27']}, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_filter_url_strict(self):
|
||||
self.assertEqual(self.filterset({'cf_cf7': ['http://a.example.com', 'http://b.example.com']}, self.queryset).qs.count(), 2)
|
||||
self.assertEqual(
|
||||
self.filterset({'cf_cf7': ['http://a.example.com', 'http://b.example.com']}, self.queryset).qs.count(),
|
||||
2
|
||||
)
|
||||
self.assertEqual(self.filterset({'cf_cf7__n': ['http://b.example.com']}, self.queryset).qs.count(), 2)
|
||||
self.assertEqual(self.filterset({'cf_cf7__ic': ['b']}, self.queryset).qs.count(), 1)
|
||||
self.assertEqual(self.filterset({'cf_cf7__nic': ['b']}, self.queryset).qs.count(), 2)
|
||||
@ -1640,9 +1688,18 @@ class CustomFieldModelFilterTest(TestCase):
|
||||
|
||||
def test_filter_object(self):
|
||||
manufacturer_ids = Manufacturer.objects.values_list('id', flat=True)
|
||||
self.assertEqual(self.filterset({'cf_cf11': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), 2)
|
||||
self.assertEqual(
|
||||
self.filterset({'cf_cf11': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(),
|
||||
2
|
||||
)
|
||||
|
||||
def test_filter_multiobject(self):
|
||||
manufacturer_ids = Manufacturer.objects.values_list('id', flat=True)
|
||||
self.assertEqual(self.filterset({'cf_cf12': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), 2)
|
||||
self.assertEqual(self.filterset({'cf_cf12': [manufacturer_ids[3]]}, self.queryset).qs.count(), 3)
|
||||
self.assertEqual(
|
||||
self.filterset({'cf_cf12': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(),
|
||||
2
|
||||
)
|
||||
self.assertEqual(
|
||||
self.filterset({'cf_cf12': [manufacturer_ids[3]]}, self.queryset).qs.count(),
|
||||
3
|
||||
)
|
||||
|
@ -387,7 +387,12 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
|
||||
})
|
||||
elif selected_objects:
|
||||
assigned_object = self.cleaned_data[selected_objects[0]]
|
||||
if self.instance.pk and self.instance.assigned_object and self.cleaned_data['primary_for_parent'] and assigned_object != self.instance.assigned_object:
|
||||
if (
|
||||
self.instance.pk and
|
||||
self.instance.assigned_object and
|
||||
self.cleaned_data['primary_for_parent'] and
|
||||
assigned_object != self.instance.assigned_object
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
|
||||
)
|
||||
|
@ -295,7 +295,10 @@ class VLANTranslationPolicyType(NetBoxObjectType):
|
||||
filters=VLANTranslationRuleFilter
|
||||
)
|
||||
class VLANTranslationRuleType(NetBoxObjectType):
|
||||
policy: Annotated["VLANTranslationPolicyType", strawberry.lazy('ipam.graphql.types')] = strawberry_django.field(select_related=["policy"])
|
||||
policy: Annotated[
|
||||
"VLANTranslationPolicyType",
|
||||
strawberry.lazy('ipam.graphql.types')
|
||||
] = strawberry_django.field(select_related=["policy"])
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
|
@ -57,7 +57,10 @@ class Migration(migrations.Migration):
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
code='invalid',
|
||||
message='Only alphanumeric characters, asterisks, hyphens, periods, and underscores are allowed in DNS names',
|
||||
message=(
|
||||
'Only alphanumeric characters, asterisks, hyphens, periods, and underscores are '
|
||||
'allowed in DNS names'
|
||||
),
|
||||
regex='^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$',
|
||||
)
|
||||
],
|
||||
|
@ -418,7 +418,9 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
available_ips = prefix - child_ips - netaddr.IPSet(child_ranges)
|
||||
|
||||
# IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable
|
||||
if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31):
|
||||
if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (
|
||||
self.family == 4 and self.prefix.prefixlen >= 31
|
||||
):
|
||||
return available_ips
|
||||
|
||||
if self.family == 4:
|
||||
@ -561,10 +563,26 @@ class IPRange(ContactsMixin, PrimaryModel):
|
||||
})
|
||||
|
||||
# Check for overlapping ranges
|
||||
overlapping_ranges = IPRange.objects.exclude(pk=self.pk).filter(vrf=self.vrf).filter(
|
||||
Q(start_address__host__inet__gte=self.start_address.ip, start_address__host__inet__lte=self.end_address.ip) | # Starts inside
|
||||
Q(end_address__host__inet__gte=self.start_address.ip, end_address__host__inet__lte=self.end_address.ip) | # Ends inside
|
||||
Q(start_address__host__inet__lte=self.start_address.ip, end_address__host__inet__gte=self.end_address.ip) # Starts & ends outside
|
||||
overlapping_ranges = (
|
||||
IPRange.objects.exclude(pk=self.pk)
|
||||
.filter(vrf=self.vrf)
|
||||
.filter(
|
||||
# Starts inside
|
||||
Q(
|
||||
start_address__host__inet__gte=self.start_address.ip,
|
||||
start_address__host__inet__lte=self.end_address.ip,
|
||||
) |
|
||||
# Ends inside
|
||||
Q(
|
||||
end_address__host__inet__gte=self.start_address.ip,
|
||||
end_address__host__inet__lte=self.end_address.ip,
|
||||
) |
|
||||
# Starts & ends outside
|
||||
Q(
|
||||
start_address__host__inet__lte=self.start_address.ip,
|
||||
end_address__host__inet__gte=self.end_address.ip,
|
||||
)
|
||||
)
|
||||
)
|
||||
if overlapping_ranges.exists():
|
||||
raise ValidationError(
|
||||
@ -866,9 +884,11 @@ class IPAddress(ContactsMixin, PrimaryModel):
|
||||
|
||||
# can't use is_primary_ip as self.assigned_object might be changed
|
||||
is_primary = False
|
||||
if self.family == 4 and hasattr(original_parent, 'primary_ip4') and original_parent.primary_ip4_id == self.pk:
|
||||
if self.family == 4 and hasattr(original_parent, 'primary_ip4'):
|
||||
if original_parent.primary_ip4_id == self.pk:
|
||||
is_primary = True
|
||||
if self.family == 6 and hasattr(original_parent, 'primary_ip6') and original_parent.primary_ip6_id == self.pk:
|
||||
if self.family == 6 and hasattr(original_parent, 'primary_ip6'):
|
||||
if original_parent.primary_ip6_id == self.pk:
|
||||
is_primary = True
|
||||
|
||||
if is_primary and (parent != original_parent):
|
||||
|
@ -732,10 +732,19 @@ class FHRPGroupTest(APIViewTestCases.APIViewTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
fhrp_groups = (
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'),
|
||||
FHRPGroup(
|
||||
protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2,
|
||||
group_id=10,
|
||||
auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT,
|
||||
auth_key='foobar123',
|
||||
),
|
||||
FHRPGroup(
|
||||
protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3,
|
||||
group_id=20,
|
||||
auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5,
|
||||
auth_key='foobar123',
|
||||
),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30),
|
||||
)
|
||||
FHRPGroup.objects.bulk_create(fhrp_groups)
|
||||
|
@ -496,8 +496,12 @@ class AggregateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
aggregates = (
|
||||
Aggregate(prefix='10.1.0.0/16', rir=rirs[0], tenant=tenants[0], date_added='2020-01-01', description='foobar1'),
|
||||
Aggregate(prefix='10.2.0.0/16', rir=rirs[0], tenant=tenants[1], date_added='2020-01-02', description='foobar2'),
|
||||
Aggregate(
|
||||
prefix='10.1.0.0/16', rir=rirs[0], tenant=tenants[0], date_added='2020-01-01', description='foobar1'
|
||||
),
|
||||
Aggregate(
|
||||
prefix='10.2.0.0/16', rir=rirs[0], tenant=tenants[1], date_added='2020-01-02', description='foobar2'
|
||||
),
|
||||
Aggregate(prefix='10.3.0.0/16', rir=rirs[1], tenant=tenants[2], date_added='2020-01-03'),
|
||||
Aggregate(prefix='2001:db8:1::/48', rir=rirs[1], tenant=tenants[0], date_added='2020-01-04'),
|
||||
Aggregate(prefix='2001:db8:2::/48', rir=rirs[2], tenant=tenants[1], date_added='2020-01-05'),
|
||||
@ -656,14 +660,80 @@ class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
prefixes = (
|
||||
Prefix(prefix='10.0.0.0/24', tenant=None, scope=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True, description='foobar1'),
|
||||
Prefix(prefix='10.0.1.0/24', tenant=tenants[0], scope=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0], description='foobar2'),
|
||||
Prefix(prefix='10.0.2.0/24', tenant=tenants[1], scope=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='10.0.3.0/24', tenant=tenants[2], scope=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(prefix='2001:db8::/64', tenant=None, scope=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True),
|
||||
Prefix(prefix='2001:db8:0:1::/64', tenant=tenants[0], scope=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
|
||||
Prefix(prefix='2001:db8:0:2::/64', tenant=tenants[1], scope=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='2001:db8:0:3::/64', tenant=tenants[2], scope=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(
|
||||
prefix='10.0.0.0/24',
|
||||
tenant=None,
|
||||
scope=None,
|
||||
vrf=None,
|
||||
vlan=None,
|
||||
role=None,
|
||||
is_pool=True,
|
||||
mark_utilized=True,
|
||||
description='foobar1',
|
||||
),
|
||||
Prefix(
|
||||
prefix='10.0.1.0/24',
|
||||
tenant=tenants[0],
|
||||
scope=sites[0],
|
||||
vrf=vrfs[0],
|
||||
vlan=vlans[0],
|
||||
role=roles[0],
|
||||
description='foobar2',
|
||||
),
|
||||
Prefix(
|
||||
prefix='10.0.2.0/24',
|
||||
tenant=tenants[1],
|
||||
scope=sites[1],
|
||||
vrf=vrfs[1],
|
||||
vlan=vlans[1],
|
||||
role=roles[1],
|
||||
status=PrefixStatusChoices.STATUS_DEPRECATED,
|
||||
),
|
||||
Prefix(
|
||||
prefix='10.0.3.0/24',
|
||||
tenant=tenants[2],
|
||||
scope=sites[2],
|
||||
vrf=vrfs[2],
|
||||
vlan=vlans[2],
|
||||
role=roles[2],
|
||||
status=PrefixStatusChoices.STATUS_RESERVED,
|
||||
),
|
||||
Prefix(
|
||||
prefix='2001:db8::/64',
|
||||
tenant=None,
|
||||
scope=None,
|
||||
vrf=None,
|
||||
vlan=None,
|
||||
role=None,
|
||||
is_pool=True,
|
||||
mark_utilized=True,
|
||||
),
|
||||
Prefix(
|
||||
prefix='2001:db8:0:1::/64',
|
||||
tenant=tenants[0],
|
||||
scope=sites[0],
|
||||
vrf=vrfs[0],
|
||||
vlan=vlans[0],
|
||||
role=roles[0]
|
||||
),
|
||||
Prefix(
|
||||
prefix='2001:db8:0:2::/64',
|
||||
tenant=tenants[1],
|
||||
scope=sites[1],
|
||||
vrf=vrfs[1],
|
||||
vlan=vlans[1],
|
||||
role=roles[1],
|
||||
status=PrefixStatusChoices.STATUS_DEPRECATED,
|
||||
),
|
||||
Prefix(
|
||||
prefix='2001:db8:0:3::/64',
|
||||
tenant=tenants[2],
|
||||
scope=sites[2],
|
||||
vrf=vrfs[2],
|
||||
vlan=vlans[2],
|
||||
role=roles[2],
|
||||
status=PrefixStatusChoices.STATUS_RESERVED,
|
||||
),
|
||||
Prefix(prefix='10.0.0.0/16'),
|
||||
Prefix(prefix='2001:db8::/32'),
|
||||
)
|
||||
@ -1365,7 +1435,10 @@ class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_auth_type(self):
|
||||
params = {'auth_type': [FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5]}
|
||||
params = {'auth_type': [
|
||||
FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT,
|
||||
FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5,
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_auth_key(self):
|
||||
@ -1653,9 +1726,15 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
|
||||
role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
devices = (
|
||||
Device(name='Device 1', site=sites[0], location=locations[0], rack=racks[0], device_type=device_type, role=role),
|
||||
Device(name='Device 2', site=sites[1], location=locations[1], rack=racks[1], device_type=device_type, role=role),
|
||||
Device(name='Device 3', site=sites[2], location=locations[2], rack=racks[2], device_type=device_type, role=role),
|
||||
Device(
|
||||
name='Device 1', site=sites[0], location=locations[0], rack=racks[0], device_type=device_type, role=role
|
||||
),
|
||||
Device(
|
||||
name='Device 2', site=sites[1], location=locations[1], rack=racks[1], device_type=device_type, role=role
|
||||
),
|
||||
Device(
|
||||
name='Device 3', site=sites[2], location=locations[2], rack=racks[2], device_type=device_type, role=role
|
||||
),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
@ -1773,20 +1852,64 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
VLAN(vid=19, name='Cluster 1', group=groups[18]),
|
||||
VLAN(vid=20, name='Cluster 2', group=groups[19]),
|
||||
VLAN(vid=21, name='Cluster 3', group=groups[20]),
|
||||
|
||||
VLAN(vid=101, name='VLAN 101', site=sites[3], group=groups[21], role=roles[0], tenant=tenants[0], status=VLANStatusChoices.STATUS_ACTIVE),
|
||||
VLAN(vid=102, name='VLAN 102', site=sites[3], group=groups[21], role=roles[0], tenant=tenants[0], status=VLANStatusChoices.STATUS_ACTIVE),
|
||||
VLAN(vid=201, name='VLAN 201', site=sites[4], group=groups[22], role=roles[1], tenant=tenants[1], status=VLANStatusChoices.STATUS_DEPRECATED),
|
||||
VLAN(vid=202, name='VLAN 202', site=sites[4], group=groups[22], role=roles[1], tenant=tenants[1], status=VLANStatusChoices.STATUS_DEPRECATED),
|
||||
VLAN(vid=301, name='VLAN 301', site=sites[5], group=groups[23], role=roles[2], tenant=tenants[2], status=VLANStatusChoices.STATUS_RESERVED),
|
||||
VLAN(vid=302, name='VLAN 302', site=sites[5], group=groups[23], role=roles[2], tenant=tenants[2], status=VLANStatusChoices.STATUS_RESERVED),
|
||||
|
||||
VLAN(
|
||||
vid=101,
|
||||
name='VLAN 101',
|
||||
site=sites[3],
|
||||
group=groups[21],
|
||||
role=roles[0],
|
||||
tenant=tenants[0],
|
||||
status=VLANStatusChoices.STATUS_ACTIVE,
|
||||
),
|
||||
VLAN(
|
||||
vid=102,
|
||||
name='VLAN 102',
|
||||
site=sites[3],
|
||||
group=groups[21],
|
||||
role=roles[0],
|
||||
tenant=tenants[0],
|
||||
status=VLANStatusChoices.STATUS_ACTIVE,
|
||||
),
|
||||
VLAN(
|
||||
vid=201,
|
||||
name='VLAN 201',
|
||||
site=sites[4],
|
||||
group=groups[22],
|
||||
role=roles[1],
|
||||
tenant=tenants[1],
|
||||
status=VLANStatusChoices.STATUS_DEPRECATED,
|
||||
),
|
||||
VLAN(
|
||||
vid=202,
|
||||
name='VLAN 202',
|
||||
site=sites[4],
|
||||
group=groups[22],
|
||||
role=roles[1],
|
||||
tenant=tenants[1],
|
||||
status=VLANStatusChoices.STATUS_DEPRECATED,
|
||||
),
|
||||
VLAN(
|
||||
vid=301,
|
||||
name='VLAN 301',
|
||||
site=sites[5],
|
||||
group=groups[23],
|
||||
role=roles[2],
|
||||
tenant=tenants[2],
|
||||
status=VLANStatusChoices.STATUS_RESERVED,
|
||||
),
|
||||
VLAN(
|
||||
vid=302,
|
||||
name='VLAN 302',
|
||||
site=sites[5],
|
||||
group=groups[23],
|
||||
role=roles[2],
|
||||
tenant=tenants[2],
|
||||
status=VLANStatusChoices.STATUS_RESERVED,
|
||||
),
|
||||
# Create one globally available VLAN on a VLAN group
|
||||
VLAN(vid=500, name='VLAN Group 1', group=groups[24]),
|
||||
|
||||
# Create one globally available VLAN
|
||||
VLAN(vid=1000, name='Global VLAN'),
|
||||
|
||||
# Create some Q-in-Q service VLANs
|
||||
VLAN(vid=2001, name='SVLAN 1', site=sites[6], qinq_role=VLANQinQRoleChoices.ROLE_SERVICE),
|
||||
VLAN(vid=2002, name='SVLAN 2', site=sites[6], qinq_role=VLANQinQRoleChoices.ROLE_SERVICE),
|
||||
@ -1795,11 +1918,31 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
VLAN.objects.bulk_create(vlans)
|
||||
|
||||
# Create Q-in-Q customer VLANs
|
||||
VLAN.objects.bulk_create([
|
||||
VLAN(vid=3001, name='CVLAN 1', site=sites[6], qinq_svlan=vlans[29], qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER),
|
||||
VLAN(vid=3002, name='CVLAN 2', site=sites[6], qinq_svlan=vlans[30], qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER),
|
||||
VLAN(vid=3003, name='CVLAN 3', site=sites[6], qinq_svlan=vlans[31], qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER),
|
||||
])
|
||||
VLAN.objects.bulk_create(
|
||||
[
|
||||
VLAN(
|
||||
vid=3001,
|
||||
name='CVLAN 1',
|
||||
site=sites[6],
|
||||
qinq_svlan=vlans[29],
|
||||
qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER,
|
||||
),
|
||||
VLAN(
|
||||
vid=3002,
|
||||
name='CVLAN 2',
|
||||
site=sites[6],
|
||||
qinq_svlan=vlans[30],
|
||||
qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER,
|
||||
),
|
||||
VLAN(
|
||||
vid=3003,
|
||||
name='CVLAN 3',
|
||||
site=sites[6],
|
||||
qinq_svlan=vlans[31],
|
||||
qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# Assign VLANs to device interfaces
|
||||
interfaces[0].untagged_vlan = vlans[0]
|
||||
@ -2125,12 +2268,39 @@ class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
VirtualMachine.objects.bulk_create(virtual_machines)
|
||||
|
||||
services = (
|
||||
Service(device=devices[0], name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1001], description='foobar1'),
|
||||
Service(device=devices[1], name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1002], description='foobar2'),
|
||||
Service(
|
||||
device=devices[0],
|
||||
name='Service 1',
|
||||
protocol=ServiceProtocolChoices.PROTOCOL_TCP,
|
||||
ports=[1001],
|
||||
description='foobar1',
|
||||
),
|
||||
Service(
|
||||
device=devices[1],
|
||||
name='Service 2',
|
||||
protocol=ServiceProtocolChoices.PROTOCOL_TCP,
|
||||
ports=[1002],
|
||||
description='foobar2',
|
||||
),
|
||||
Service(device=devices[2], name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_UDP, ports=[1003]),
|
||||
Service(virtual_machine=virtual_machines[0], name='Service 4', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[2001]),
|
||||
Service(virtual_machine=virtual_machines[1], name='Service 5', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[2002]),
|
||||
Service(virtual_machine=virtual_machines[2], name='Service 6', protocol=ServiceProtocolChoices.PROTOCOL_UDP, ports=[2003]),
|
||||
Service(
|
||||
virtual_machine=virtual_machines[0],
|
||||
name='Service 4',
|
||||
protocol=ServiceProtocolChoices.PROTOCOL_TCP,
|
||||
ports=[2001],
|
||||
),
|
||||
Service(
|
||||
virtual_machine=virtual_machines[1],
|
||||
name='Service 5',
|
||||
protocol=ServiceProtocolChoices.PROTOCOL_TCP,
|
||||
ports=[2002],
|
||||
),
|
||||
Service(
|
||||
virtual_machine=virtual_machines[2],
|
||||
name='Service 6',
|
||||
protocol=ServiceProtocolChoices.PROTOCOL_UDP,
|
||||
ports=[2003],
|
||||
),
|
||||
)
|
||||
Service.objects.bulk_create(services)
|
||||
services[0].ipaddresses.add(ip_addresses[0])
|
||||
|
@ -39,29 +39,50 @@ class TestAggregate(TestCase):
|
||||
class TestIPRange(TestCase):
|
||||
|
||||
def test_overlapping_range(self):
|
||||
iprange_192_168 = IPRange.objects.create(start_address=IPNetwork('192.168.0.1/22'), end_address=IPNetwork('192.168.0.49/22'))
|
||||
iprange_192_168 = IPRange.objects.create(
|
||||
start_address=IPNetwork('192.168.0.1/22'), end_address=IPNetwork('192.168.0.49/22')
|
||||
)
|
||||
iprange_192_168.clean()
|
||||
iprange_3_1_99 = IPRange.objects.create(start_address=IPNetwork('1.2.3.1/24'), end_address=IPNetwork('1.2.3.99/24'))
|
||||
iprange_3_1_99 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.3.1/24'), end_address=IPNetwork('1.2.3.99/24')
|
||||
)
|
||||
iprange_3_1_99.clean()
|
||||
iprange_3_100_199 = IPRange.objects.create(start_address=IPNetwork('1.2.3.100/24'), end_address=IPNetwork('1.2.3.199/24'))
|
||||
iprange_3_100_199 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.3.100/24'), end_address=IPNetwork('1.2.3.199/24')
|
||||
)
|
||||
iprange_3_100_199.clean()
|
||||
iprange_3_200_255 = IPRange.objects.create(start_address=IPNetwork('1.2.3.200/24'), end_address=IPNetwork('1.2.3.255/24'))
|
||||
iprange_3_200_255 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.3.200/24'), end_address=IPNetwork('1.2.3.255/24')
|
||||
)
|
||||
iprange_3_200_255.clean()
|
||||
iprange_4_1_99 = IPRange.objects.create(start_address=IPNetwork('1.2.4.1/24'), end_address=IPNetwork('1.2.4.99/24'))
|
||||
iprange_4_1_99 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.4.1/24'), end_address=IPNetwork('1.2.4.99/24')
|
||||
)
|
||||
iprange_4_1_99.clean()
|
||||
iprange_4_200 = IPRange.objects.create(start_address=IPNetwork('1.2.4.200/24'), end_address=IPNetwork('1.2.4.255/24'))
|
||||
iprange_4_200 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.4.200/24'), end_address=IPNetwork('1.2.4.255/24')
|
||||
)
|
||||
iprange_4_200.clean()
|
||||
|
||||
# Overlapping range entirely within existing
|
||||
with self.assertRaises(ValidationError):
|
||||
iprange_3_123_124 = IPRange.objects.create(start_address=IPNetwork('1.2.3.123/26'), end_address=IPNetwork('1.2.3.124/26'))
|
||||
iprange_3_123_124 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.3.123/26'), end_address=IPNetwork('1.2.3.124/26')
|
||||
)
|
||||
iprange_3_123_124.clean()
|
||||
|
||||
# Overlapping range starting within existing
|
||||
with self.assertRaises(ValidationError):
|
||||
iprange_4_98_101 = IPRange.objects.create(start_address=IPNetwork('1.2.4.98/24'), end_address=IPNetwork('1.2.4.101/24'))
|
||||
iprange_4_98_101 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.4.98/24'), end_address=IPNetwork('1.2.4.101/24')
|
||||
)
|
||||
iprange_4_98_101.clean()
|
||||
|
||||
# Overlapping range ending within existing
|
||||
with self.assertRaises(ValidationError):
|
||||
iprange_4_198_201 = IPRange.objects.create(start_address=IPNetwork('1.2.4.198/24'), end_address=IPNetwork('1.2.4.201/24'))
|
||||
iprange_4_198_201 = IPRange.objects.create(
|
||||
start_address=IPNetwork('1.2.4.198/24'), end_address=IPNetwork('1.2.4.201/24')
|
||||
)
|
||||
iprange_4_198_201.clean()
|
||||
|
||||
|
||||
@ -105,13 +126,30 @@ class TestPrefix(TestCase):
|
||||
def test_get_child_ranges(self):
|
||||
prefix = Prefix(prefix='192.168.0.16/28')
|
||||
prefix.save()
|
||||
ranges = IPRange.objects.bulk_create((
|
||||
IPRange(start_address=IPNetwork('192.168.0.1/24'), end_address=IPNetwork('192.168.0.10/24'), size=10), # No overlap
|
||||
IPRange(start_address=IPNetwork('192.168.0.11/24'), end_address=IPNetwork('192.168.0.17/24'), size=7), # Partial overlap
|
||||
IPRange(start_address=IPNetwork('192.168.0.18/24'), end_address=IPNetwork('192.168.0.23/24'), size=6), # Full overlap
|
||||
IPRange(start_address=IPNetwork('192.168.0.24/24'), end_address=IPNetwork('192.168.0.30/24'), size=7), # Full overlap
|
||||
IPRange(start_address=IPNetwork('192.168.0.31/24'), end_address=IPNetwork('192.168.0.40/24'), size=10), # Partial overlap
|
||||
))
|
||||
ranges = IPRange.objects.bulk_create(
|
||||
(
|
||||
# No overlap
|
||||
IPRange(
|
||||
start_address=IPNetwork('192.168.0.1/24'), end_address=IPNetwork('192.168.0.10/24'), size=10
|
||||
),
|
||||
# Partial overlap
|
||||
IPRange(
|
||||
start_address=IPNetwork('192.168.0.11/24'), end_address=IPNetwork('192.168.0.17/24'), size=7
|
||||
),
|
||||
# Full overlap
|
||||
IPRange(
|
||||
start_address=IPNetwork('192.168.0.18/24'), end_address=IPNetwork('192.168.0.23/24'), size=6
|
||||
),
|
||||
# Full overlap
|
||||
IPRange(
|
||||
start_address=IPNetwork('192.168.0.24/24'), end_address=IPNetwork('192.168.0.30/24'), size=7
|
||||
),
|
||||
# Partial overlap
|
||||
IPRange(
|
||||
start_address=IPNetwork('192.168.0.31/24'), end_address=IPNetwork('192.168.0.40/24'), size=10
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
child_ranges = prefix.get_child_ranges()
|
||||
|
||||
|
@ -92,8 +92,8 @@ class PrefixOrderingTestCase(OrderingTestBase):
|
||||
|
||||
def test_prefix_complex_ordering(self):
|
||||
"""
|
||||
This function tests a complex ordering of interwoven prefixes and vrfs. This is the current expected ordering of VRFs
|
||||
This includes the testing of the Container status.
|
||||
This function tests a complex ordering of interwoven prefixes and vrfs. This is the current expected ordering
|
||||
of VRFs. This includes the testing of the Container status.
|
||||
|
||||
The proper ordering, to get proper containerization should be:
|
||||
None:10.0.0.0/8
|
||||
@ -125,7 +125,6 @@ class PrefixOrderingTestCase(OrderingTestBase):
|
||||
|
||||
|
||||
class IPAddressOrderingTestCase(OrderingTestBase):
|
||||
|
||||
def test_address_vrf_ordering(self):
|
||||
"""
|
||||
This function tests ordering with the inclusion of vrfs
|
||||
@ -147,24 +146,54 @@ class IPAddressOrderingTestCase(OrderingTestBase):
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.2.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.3.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.4.1/24')),
|
||||
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.0.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.1.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.2.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.3.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.4.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.0.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.1.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.2.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.3.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.4.1/24')),
|
||||
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.0.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.1.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.2.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.3.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.4.1/24')),
|
||||
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.5.1/24')),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.0.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.1.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.2.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.3.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.4.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.0.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.1.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.2.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.3.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.4.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.0.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.1.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.2.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.3.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.4.1/24')
|
||||
),
|
||||
IPAddress(
|
||||
status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.5.1/24')
|
||||
),
|
||||
)
|
||||
IPAddress.objects.bulk_create(addresses)
|
||||
|
||||
|
@ -707,11 +707,23 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
fhrp_groups = (
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30),
|
||||
FHRPGroup(
|
||||
protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2,
|
||||
group_id=10,
|
||||
auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT,
|
||||
auth_key='foobar123',
|
||||
),
|
||||
FHRPGroup(
|
||||
protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3,
|
||||
group_id=20,
|
||||
auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5,
|
||||
auth_key='foobar123',
|
||||
),
|
||||
FHRPGroup(
|
||||
protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP,
|
||||
group_id=30
|
||||
),
|
||||
)
|
||||
FHRPGroup.objects.bulk_create(fhrp_groups)
|
||||
|
||||
|
@ -264,7 +264,9 @@ class ChangeLoggedModelFilterSet(BaseFilterSet):
|
||||
action = {
|
||||
'created_by_request': Q(action=ObjectChangeActionChoices.ACTION_CREATE),
|
||||
'updated_by_request': Q(action=ObjectChangeActionChoices.ACTION_UPDATE),
|
||||
'modified_by_request': Q(action__in=[ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE]),
|
||||
'modified_by_request': Q(
|
||||
action__in=[ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE]
|
||||
),
|
||||
}.get(name)
|
||||
request_id = value
|
||||
pks = ObjectChange.objects.filter(
|
||||
|
@ -995,7 +995,8 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
form.add_error(field, '{}: {}'.format(obj, ', '.join(e)))
|
||||
|
||||
# Enforce object-level permissions
|
||||
if self.queryset.filter(pk__in=[obj.pk for obj in new_components]).count() != len(new_components):
|
||||
component_ids = [obj.pk for obj in new_components]
|
||||
if self.queryset.filter(pk__in=component_ids).count() != len(new_components):
|
||||
raise PermissionsViolation
|
||||
|
||||
except IntegrityError:
|
||||
|
@ -143,7 +143,12 @@ class ObjectJobsView(ConditionalLoginRequiredMixin, View):
|
||||
"""
|
||||
Render a list of all Job assigned to an object. For example:
|
||||
|
||||
path('data-sources/<int:pk>/jobs/', ObjectJobsView.as_view(), name='datasource_jobs', kwargs={'model': DataSource}),
|
||||
path(
|
||||
'data-sources/<int:pk>/jobs/',
|
||||
ObjectJobsView.as_view(),
|
||||
name='datasource_jobs',
|
||||
kwargs={'model': DataSource}
|
||||
)
|
||||
|
||||
Attributes:
|
||||
base_template: The name of the template to extend. If not provided, "{app}/{model}.html" will be used.
|
||||
|
Loading…
Reference in New Issue
Block a user