mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 17:08:41 -06:00
Closes #6715: Add tenant assignment for cables
This commit is contained in:
parent
ba7361bdc7
commit
0afd3e6189
@ -3,11 +3,16 @@
|
|||||||
!!! warning "PostgreSQL 10 Required"
|
!!! warning "PostgreSQL 10 Required"
|
||||||
NetBox v3.1 requires PostgreSQL 10 or later.
|
NetBox v3.1 requires PostgreSQL 10 or later.
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* The `tenant` and `tenant_id` filters for the Cable model now filter on the tenant assigned directly to each cable, rather than on the parent object of either termination.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
|
||||||
* [#1337](https://github.com/netbox-community/netbox/issues/1337) - Add WWN field to interfaces
|
* [#1337](https://github.com/netbox-community/netbox/issues/1337) - Add WWN field to interfaces
|
||||||
* [#3839](https://github.com/netbox-community/netbox/issues/3839) - Add `airflow` field for devices types and devices
|
* [#3839](https://github.com/netbox-community/netbox/issues/3839) - Add `airflow` field for devices types and devices
|
||||||
* [#6711](https://github.com/netbox-community/netbox/issues/6711) - Add `longtext` custom field type with Markdown support
|
* [#6711](https://github.com/netbox-community/netbox/issues/6711) - Add `longtext` custom field type with Markdown support
|
||||||
|
* [#6715](https://github.com/netbox-community/netbox/issues/6715) - Add tenant assignment for cables
|
||||||
* [#6874](https://github.com/netbox-community/netbox/issues/6874) - Add tenant assignment for locations
|
* [#6874](https://github.com/netbox-community/netbox/issues/6874) - Add tenant assignment for locations
|
||||||
|
|
||||||
### Other Changes
|
### Other Changes
|
||||||
|
@ -758,14 +758,15 @@ class CableSerializer(PrimaryModelSerializer):
|
|||||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||||
status = ChoiceField(choices=CableStatusChoices, required=False)
|
status = ChoiceField(choices=CableStatusChoices, required=False)
|
||||||
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
|
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
|
'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
|
||||||
'termination_b_id', 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
|
'termination_b_id', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
|
||||||
'custom_fields',
|
'tags', 'custom_fields',
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_termination(self, obj, side):
|
def _get_termination(self, obj, side):
|
||||||
|
@ -1189,7 +1189,7 @@ class VirtualChassisFilterSet(PrimaryModelFilterSet):
|
|||||||
return queryset.filter(qs_filter).distinct()
|
return queryset.filter(qs_filter).distinct()
|
||||||
|
|
||||||
|
|
||||||
class CableFilterSet(PrimaryModelFilterSet):
|
class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label='Search',
|
||||||
@ -1230,14 +1230,6 @@ class CableFilterSet(PrimaryModelFilterSet):
|
|||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='device__site__slug'
|
field_name='device__site__slug'
|
||||||
)
|
)
|
||||||
tenant_id = MultiValueNumberFilter(
|
|
||||||
method='filter_device',
|
|
||||||
field_name='device__tenant_id'
|
|
||||||
)
|
|
||||||
tenant = MultiValueNumberFilter(
|
|
||||||
method='filter_device',
|
|
||||||
field_name='device__tenant__slug'
|
|
||||||
)
|
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -468,6 +468,10 @@ class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkE
|
|||||||
widget=StaticSelect(),
|
widget=StaticSelect(),
|
||||||
initial=''
|
initial=''
|
||||||
)
|
)
|
||||||
|
tenant = DynamicModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
label = forms.CharField(
|
label = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
required=False
|
required=False
|
||||||
@ -488,7 +492,7 @@ class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkE
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = [
|
nullable_fields = [
|
||||||
'type', 'status', 'label', 'color', 'length',
|
'type', 'status', 'tenant', 'label', 'color', 'length',
|
||||||
]
|
]
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -821,6 +821,12 @@ class CableCSVForm(CustomFieldModelCSVForm):
|
|||||||
required=False,
|
required=False,
|
||||||
help_text='Physical medium classification'
|
help_text='Physical medium classification'
|
||||||
)
|
)
|
||||||
|
tenant = CSVModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned tenant'
|
||||||
|
)
|
||||||
length_unit = CSVChoiceField(
|
length_unit = CSVChoiceField(
|
||||||
choices=CableLengthUnitChoices,
|
choices=CableLengthUnitChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -831,7 +837,7 @@ class CableCSVForm(CustomFieldModelCSVForm):
|
|||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type',
|
'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type',
|
||||||
'status', 'label', 'color', 'length', 'length_unit',
|
'status', 'tenant', 'label', 'color', 'length', 'length_unit',
|
||||||
]
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
|
'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
|
||||||
|
@ -2,6 +2,7 @@ from circuits.models import Circuit, CircuitTermination, Provider
|
|||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelForm
|
from extras.forms import CustomFieldModelForm
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
|
from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -17,7 +18,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToDeviceForm(BootstrapMixin, CustomFieldModelForm):
|
class ConnectCableToDeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
"""
|
"""
|
||||||
Base form for connecting a Cable to a Device component
|
Base form for connecting a Cable to a Device component
|
||||||
"""
|
"""
|
||||||
@ -78,7 +79,8 @@ class ConnectCableToDeviceForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'termination_b_region', 'termination_b_site', 'termination_b_rack', 'termination_b_device',
|
'termination_b_region', 'termination_b_site', 'termination_b_rack', 'termination_b_device',
|
||||||
'termination_b_id', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
|
'termination_b_id', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color', 'length', 'length_unit',
|
||||||
|
'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect,
|
'status': StaticSelect,
|
||||||
@ -169,7 +171,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToCircuitTerminationForm(BootstrapMixin, CustomFieldModelForm):
|
class ConnectCableToCircuitTerminationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
termination_b_provider = DynamicModelChoiceField(
|
termination_b_provider = DynamicModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
label='Provider',
|
label='Provider',
|
||||||
@ -219,7 +221,8 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, CustomFieldModelForm)
|
|||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'termination_b_provider', 'termination_b_region', 'termination_b_site', 'termination_b_circuit',
|
'termination_b_provider', 'termination_b_region', 'termination_b_site', 'termination_b_circuit',
|
||||||
'termination_b_id', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
|
'termination_b_id', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color', 'length', 'length_unit',
|
||||||
|
'tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
def clean_termination_b_id(self):
|
def clean_termination_b_id(self):
|
||||||
@ -227,7 +230,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, CustomFieldModelForm)
|
|||||||
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
|
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToPowerFeedForm(BootstrapMixin, CustomFieldModelForm):
|
class ConnectCableToPowerFeedForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
termination_b_region = DynamicModelChoiceField(
|
termination_b_region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
label='Region',
|
label='Region',
|
||||||
@ -280,8 +283,8 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'termination_b_location', 'termination_b_powerpanel', 'termination_b_id', 'type', 'status', 'label',
|
'termination_b_location', 'termination_b_powerpanel', 'termination_b_id', 'type', 'status', 'tenant_group',
|
||||||
'color', 'length', 'length_unit', 'tags',
|
'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
def clean_termination_b_id(self):
|
def clean_termination_b_id(self):
|
||||||
|
@ -691,13 +691,13 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
class CableFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterForm):
|
||||||
model = Cable
|
model = Cable
|
||||||
field_groups = [
|
field_groups = [
|
||||||
['q', 'tag'],
|
['q', 'tag'],
|
||||||
['site_id', 'rack_id', 'device_id'],
|
['site_id', 'rack_id', 'device_id'],
|
||||||
['type', 'status', 'color'],
|
['type', 'status', 'color'],
|
||||||
['tenant_id'],
|
['tenant_group_id', 'tenant_id'],
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -719,12 +719,6 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
|||||||
label=_('Site'),
|
label=_('Site'),
|
||||||
fetch_trigger='open'
|
fetch_trigger='open'
|
||||||
)
|
)
|
||||||
tenant_id = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Tenant.objects.all(),
|
|
||||||
required=False,
|
|
||||||
label=_('Tenant'),
|
|
||||||
fetch_trigger='open'
|
|
||||||
)
|
|
||||||
rack_id = DynamicModelMultipleChoiceField(
|
rack_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -601,7 +601,7 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
self.fields['position'].widget.choices = [(position, f'U{position}')]
|
self.fields['position'].widget.choices = [(position, f'U{position}')]
|
||||||
|
|
||||||
|
|
||||||
class CableForm(BootstrapMixin, CustomFieldModelForm):
|
class CableForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -610,7 +610,7 @@ class CableForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
|
'type', 'status', 'tenant_group', 'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect,
|
'status': StaticSelect,
|
||||||
|
@ -15,4 +15,9 @@ class Migration(migrations.Migration):
|
|||||||
name='tenant',
|
name='tenant',
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locations', to='tenancy.tenant'),
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locations', to='tenancy.tenant'),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='cable',
|
||||||
|
name='tenant',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cables', to='tenancy.tenant'),
|
||||||
|
),
|
||||||
]
|
]
|
@ -4,7 +4,7 @@ from django.db import migrations, models
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('dcim', '0135_location_tenant'),
|
('dcim', '0135_tenancy_extensions'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -67,6 +67,13 @@ class Cable(PrimaryModel):
|
|||||||
choices=CableStatusChoices,
|
choices=CableStatusChoices,
|
||||||
default=CableStatusChoices.STATUS_CONNECTED
|
default=CableStatusChoices.STATUS_CONNECTED
|
||||||
)
|
)
|
||||||
|
tenant = models.ForeignKey(
|
||||||
|
to='tenancy.Tenant',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='cables',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
blank=True
|
blank=True
|
||||||
|
@ -2,6 +2,7 @@ import django_tables2 as tables
|
|||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from dcim.models import Cable
|
from dcim.models import Cable
|
||||||
|
from tenancy.tables import TenantColumn
|
||||||
from utilities.tables import BaseTable, ChoiceFieldColumn, ColorColumn, TagColumn, TemplateColumn, ToggleColumn
|
from utilities.tables import BaseTable, ChoiceFieldColumn, ColorColumn, TagColumn, TemplateColumn, ToggleColumn
|
||||||
from .template_code import CABLE_LENGTH, CABLE_TERMINATION_PARENT
|
from .template_code import CABLE_LENGTH, CABLE_TERMINATION_PARENT
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class CableTable(BaseTable):
|
|||||||
verbose_name='Termination B'
|
verbose_name='Termination B'
|
||||||
)
|
)
|
||||||
status = ChoiceFieldColumn()
|
status = ChoiceFieldColumn()
|
||||||
|
tenant = TenantColumn()
|
||||||
length = TemplateColumn(
|
length = TemplateColumn(
|
||||||
template_code=CABLE_LENGTH,
|
template_code=CABLE_LENGTH,
|
||||||
order_by='_abs_length'
|
order_by='_abs_length'
|
||||||
@ -58,7 +60,7 @@ class CableTable(BaseTable):
|
|||||||
model = Cable
|
model = Cable
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
|
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
|
||||||
'status', 'type', 'color', 'length', 'tags',
|
'status', 'type', 'tenant', 'color', 'length', 'tags',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
|
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
|
||||||
|
@ -2819,6 +2819,7 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
tenants = (
|
tenants = (
|
||||||
Tenant(name='Tenant 1', slug='tenant-1'),
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
Tenant(name='Tenant 2', slug='tenant-2'),
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||||
)
|
)
|
||||||
Tenant.objects.bulk_create(tenants)
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
@ -2834,9 +2835,9 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||||
|
|
||||||
devices = (
|
devices = (
|
||||||
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=1, tenant=tenants[0]),
|
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=1),
|
||||||
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=2, tenant=tenants[0]),
|
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=2),
|
||||||
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=1, tenant=tenants[1]),
|
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=1),
|
||||||
Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=2),
|
Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=2),
|
||||||
Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=1),
|
Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=1),
|
||||||
Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=2),
|
Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=2),
|
||||||
@ -2863,12 +2864,12 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
console_server_port = ConsoleServerPort.objects.create(device=devices[0], name='Console Server Port 1')
|
console_server_port = ConsoleServerPort.objects.create(device=devices[0], name='Console Server Port 1')
|
||||||
|
|
||||||
# Cables
|
# Cables
|
||||||
Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=10, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CableTypeChoices.TYPE_CAT3, tenant=tenants[0], status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=10, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||||
Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=20, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CableTypeChoices.TYPE_CAT3, tenant=tenants[0], status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=20, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||||
Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_CONNECTED, color='f44336', length=30, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CableTypeChoices.TYPE_CAT5E, tenant=tenants[1], status=CableStatusChoices.STATUS_CONNECTED, color='f44336', length=30, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||||
Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_PLANNED, color='f44336', length=40, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CableTypeChoices.TYPE_CAT5E, tenant=tenants[1], status=CableStatusChoices.STATUS_PLANNED, color='f44336', length=40, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||||
Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, tenant=tenants[2], status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||||
Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, tenant=tenants[2], status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||||
Cable(termination_a=console_port, termination_b=console_server_port, label='Cable 7').save()
|
Cable(termination_a=console_port, termination_b=console_server_port, label='Cable 7').save()
|
||||||
|
|
||||||
def test_label(self):
|
def test_label(self):
|
||||||
@ -2921,9 +2922,9 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
def test_tenant(self):
|
def test_tenant(self):
|
||||||
tenant = Tenant.objects.all()[:2]
|
tenant = Tenant.objects.all()[:2]
|
||||||
params = {'tenant_id': [tenant[0].pk, tenant[1].pk]}
|
params = {'tenant_id': [tenant[0].pk, tenant[1].pk]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
params = {'tenant': [tenant[0].slug, tenant[1].slug]}
|
params = {'tenant': [tenant[0].slug, tenant[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
def test_termination_types(self):
|
def test_termination_types(self):
|
||||||
params = {'termination_a_type': 'dcim.consoleport'}
|
params = {'termination_a_type': 'dcim.consoleport'}
|
||||||
|
@ -23,6 +23,19 @@
|
|||||||
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
|
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Tenant</th>
|
||||||
|
<td>
|
||||||
|
{% if object.tenant %}
|
||||||
|
{% if object.tenant.group %}
|
||||||
|
<a href="{{ object.tenant.group.get_absolute_url }}">{{ object.tenant.group }}</a> /
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ object.tenant.get_absolute_url }}">{{ object.tenant }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Label</th>
|
<th scope="row">Label</th>
|
||||||
<td>{{ object.label|placeholder }}</td>
|
<td>{{ object.label|placeholder }}</td>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
{% render_field form.status %}
|
{% render_field form.status %}
|
||||||
{% render_field form.type %}
|
{% render_field form.type %}
|
||||||
|
{% render_field form.tenant_group %}
|
||||||
|
{% render_field form.tenant %}
|
||||||
{% render_field form.label %}
|
{% render_field form.label %}
|
||||||
{% render_field form.color %}
|
{% render_field form.color %}
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
@ -17,7 +19,7 @@
|
|||||||
{% render_field form.tags %}
|
{% render_field form.tags %}
|
||||||
{% if form.custom_fields %}
|
{% if form.custom_fields %}
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<div class="row mb-2">
|
<div class="row mb-3">
|
||||||
<h5 class="offset-sm-3">Custom Fields</h5>
|
<h5 class="offset-sm-3">Custom Fields</h5>
|
||||||
</div>
|
</div>
|
||||||
{% render_custom_fields form %}
|
{% render_custom_fields form %}
|
||||||
|
Loading…
Reference in New Issue
Block a user