Skip to content

Commit

Permalink
Closes #3648: Mark cable termination models as connected without atta…
Browse files Browse the repository at this point in the history
…ching a cable
  • Loading branch information
jeremystretch committed Mar 2, 2021
1 parent 6ed2e7b commit 8dd7123
Show file tree
Hide file tree
Showing 26 changed files with 281 additions and 108 deletions.
24 changes: 24 additions & 0 deletions docs/release-notes/version-2.11.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

**WARNING:** This is a beta release and is not suitable for production use. It is intended for development and evaluation purposes only. No upgrade path to the final v2.11 release will be provided from this beta, and users should assume that all data entered into the application will be lost.

### New Features

#### Mark as Connected Without a Cable ([#3648](https://github.com/netbox-community/netbox/issues/3648))

Cable termination objects (circuit terminations, power feeds, and most device components) can now be marked as "connected" without actually attaching a cable. This helps simplify the process of modeling an infrastructure boundary where you don't necessarily know or care what is connected to the far end of a cable, but still need to designate the near end termination.

In addition to the new `mark_connected` boolean field, the REST API representation of these objects now also includes a read-only boolean field named `_occupied`. This conveniently returns true if either a cable is attached or `mark_connected` is true.

### Enhancements

* [#5370](https://github.com/netbox-community/netbox/issues/5370) - Extend custom field support to organizational models
Expand All @@ -16,3 +24,19 @@

* [#1638](https://github.com/netbox-community/netbox/issues/1638) - Migrate all primary keys to 64-bit integers
* [#5873](https://github.com/netbox-community/netbox/issues/5873) - Use numeric IDs in all object URLs

### REST API Changes

* All primary keys are now 64-bit integers
* All device components
* Added support for custom fields
* Added `created` and `last_updated` fields to track object creation and modification
* All device component templates
* Added `created` and `last_updated` fields to track object creation and modification
* All organizational models
* Added support for custom fields
* All cable termination models (cabled device components, power feeds, and circuit terminations)
* Added `mark_connected` boolean field to force connection status
* Added `_occupied` read-only boolean field as common attribute for determining whether an object is occupied
* extras.CustomField
* Added new custom field type: `multi-select`
2 changes: 1 addition & 1 deletion netbox/circuits/api/nested_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ class NestedCircuitTerminationSerializer(WritableNestedSerializer):

class Meta:
model = CircuitTermination
fields = ['id', 'url', 'circuit', 'term_side', 'cable']
fields = ['id', 'url', 'circuit', 'term_side', 'cable', '_occupied']
4 changes: 2 additions & 2 deletions netbox/circuits/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,6 @@ class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'circuit', 'term_side', 'site', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
'description', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable'
'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint',
'connected_endpoint_type', 'connected_endpoint_reachable', '_occupied',
]
3 changes: 2 additions & 1 deletion netbox/circuits/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ class CircuitTerminationForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = CircuitTermination
fields = [
'term_side', 'region', 'site', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description',
'term_side', 'region', 'site', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
'description',
]
help_texts = {
'port_speed': "Physical circuit speed",
Expand Down
16 changes: 16 additions & 0 deletions netbox/circuits/migrations/0026_mark_connected.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('circuits', '0025_standardize_models'),
]

operations = [
migrations.AddField(
model_name='circuittermination',
name='mark_connected',
field=models.BooleanField(default=False),
),
]
2 changes: 1 addition & 1 deletion netbox/circuits/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def setUpTestData(cls):

class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
model = CircuitTermination
brief_fields = ['cable', 'circuit', 'id', 'term_side', 'url']
brief_fields = ['_occupied', 'cable', 'circuit', 'id', 'term_side', 'url']

@classmethod
def setUpTestData(cls):
Expand Down
16 changes: 8 additions & 8 deletions netbox/dcim/api/nested_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class NestedConsoleServerPortSerializer(WritableNestedSerializer):

class Meta:
model = models.ConsoleServerPort
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedConsolePortSerializer(WritableNestedSerializer):
Expand All @@ -239,7 +239,7 @@ class NestedConsolePortSerializer(WritableNestedSerializer):

class Meta:
model = models.ConsolePort
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedPowerOutletSerializer(WritableNestedSerializer):
Expand All @@ -248,7 +248,7 @@ class NestedPowerOutletSerializer(WritableNestedSerializer):

class Meta:
model = models.PowerOutlet
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedPowerPortSerializer(WritableNestedSerializer):
Expand All @@ -257,7 +257,7 @@ class NestedPowerPortSerializer(WritableNestedSerializer):

class Meta:
model = models.PowerPort
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedInterfaceSerializer(WritableNestedSerializer):
Expand All @@ -266,7 +266,7 @@ class NestedInterfaceSerializer(WritableNestedSerializer):

class Meta:
model = models.Interface
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedRearPortSerializer(WritableNestedSerializer):
Expand All @@ -275,7 +275,7 @@ class NestedRearPortSerializer(WritableNestedSerializer):

class Meta:
model = models.RearPort
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedFrontPortSerializer(WritableNestedSerializer):
Expand All @@ -284,7 +284,7 @@ class NestedFrontPortSerializer(WritableNestedSerializer):

class Meta:
model = models.FrontPort
fields = ['id', 'url', 'device', 'name', 'cable']
fields = ['id', 'url', 'device', 'name', 'cable', '_occupied']


class NestedDeviceBaySerializer(WritableNestedSerializer):
Expand Down Expand Up @@ -350,4 +350,4 @@ class NestedPowerFeedSerializer(WritableNestedSerializer):

class Meta:
model = models.PowerFeed
fields = ['id', 'url', 'name', 'cable']
fields = ['id', 'url', 'name', 'cable', '_occupied']
45 changes: 23 additions & 22 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,9 @@ class ConsoleServerPortSerializer(TaggedObjectSerializer, CableTerminationSerial
class Meta:
model = ConsoleServerPort
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'description', 'cable', 'cable_peer', 'cable_peer_type',
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags',
'custom_fields', 'created', 'last_updated', '_occupied',
]


Expand All @@ -528,9 +528,9 @@ class ConsolePortSerializer(TaggedObjectSerializer, CableTerminationSerializer,
class Meta:
model = ConsolePort
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'description', 'cable', 'cable_peer', 'cable_peer_type',
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'cable', 'cable_peer',
'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags',
'custom_fields', 'created', 'last_updated', '_occupied',
]


Expand All @@ -557,9 +557,9 @@ class PowerOutletSerializer(TaggedObjectSerializer, CableTerminationSerializer,
class Meta:
model = PowerOutlet
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'cable',
'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'mark_connected',
'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]


Expand All @@ -576,9 +576,9 @@ class PowerPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Co
class Meta:
model = PowerPort
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'cable',
'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description',
'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]


Expand All @@ -602,9 +602,9 @@ class Meta:
model = Interface
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only',
'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'cable', 'cable_peer', 'cable_peer_type',
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
'created', 'last_updated', 'count_ipaddresses',
'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'cable_peer',
'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags',
'custom_fields', 'created', 'last_updated', 'count_ipaddresses', '_occupied',
]

def validate(self, data):
Expand All @@ -630,8 +630,8 @@ class RearPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Cus
class Meta:
model = RearPort
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'positions', 'description', 'cable', 'cable_peer',
'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'cable',
'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]


Expand All @@ -656,8 +656,9 @@ class FrontPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Cu
class Meta:
model = FrontPort
fields = [
'id', 'url', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable',
'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description',
'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created',
'last_updated', '_occupied',
]


Expand Down Expand Up @@ -892,7 +893,7 @@ class Meta:
model = PowerFeed
fields = [
'id', 'url', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'comments', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint',
'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
'last_updated',
'max_utilization', 'comments', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type',
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
'created', 'last_updated', '_occupied',
]
38 changes: 19 additions & 19 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2324,7 +2324,7 @@ class ConsolePortForm(BootstrapMixin, CustomFieldModelForm):
class Meta:
model = ConsolePort
fields = [
'device', 'name', 'label', 'type', 'description', 'tags',
'device', 'name', 'label', 'type', 'mark_connected', 'description', 'tags',
]
widgets = {
'device': forms.HiddenInput(),
Expand All @@ -2338,19 +2338,19 @@ class ConsolePortCreateForm(ComponentCreateForm):
required=False,
widget=StaticSelect2()
)
field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'description', 'tags')
field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'mark_connected', 'description', 'tags')


class ConsolePortBulkCreateForm(
form_from_model(ConsolePort, ['type']),
form_from_model(ConsolePort, ['type', 'mark_connected']),
DeviceBulkAddComponentForm
):
model = ConsolePort
field_order = ('name_pattern', 'label_pattern', 'type', 'description', 'tags')
field_order = ('name_pattern', 'label_pattern', 'type', 'mark_connected', 'description', 'tags')


class ConsolePortBulkEditForm(
form_from_model(ConsolePort, ['label', 'type', 'description']),
form_from_model(ConsolePort, ['label', 'type', 'mark_connected', 'description']),
BootstrapMixin,
AddRemoveTagsForm,
CustomFieldBulkEditForm
Expand Down Expand Up @@ -2772,8 +2772,8 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
class Meta:
model = Interface
fields = [
'device', 'name', 'label', 'type', 'enabled', 'lag', 'mac_address', 'mtu', 'mgmt_only', 'description',
'mode', 'untagged_vlan', 'tagged_vlans', 'tags',
'device', 'name', 'label', 'type', 'enabled', 'lag', 'mac_address', 'mtu', 'mgmt_only', 'mark_connected',
'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags',
]
widgets = {
'device': forms.HiddenInput(),
Expand Down Expand Up @@ -3625,7 +3625,7 @@ class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=ConsolePort.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand All @@ -3636,7 +3636,7 @@ class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=ConsoleServerPort.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand All @@ -3647,7 +3647,7 @@ class ConnectCableToPowerPortForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=PowerPort.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand All @@ -3658,7 +3658,7 @@ class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=PowerOutlet.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand All @@ -3669,7 +3669,7 @@ class ConnectCableToInterfaceForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device',
'kind': 'physical',
Expand All @@ -3681,7 +3681,7 @@ class ConnectCableToFrontPortForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=FrontPort.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand All @@ -3692,7 +3692,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
termination_b_id = DynamicModelChoiceField(
queryset=RearPort.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'device_id': '$termination_b_device'
}
Expand Down Expand Up @@ -3731,7 +3731,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, CustomFieldModelForm)
queryset=CircuitTermination.objects.all(),
label='Side',
display_field='term_side',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'circuit_id': '$termination_b_circuit'
}
Expand Down Expand Up @@ -3788,7 +3788,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, CustomFieldModelForm):
termination_b_id = DynamicModelChoiceField(
queryset=PowerFeed.objects.all(),
label='Name',
disabled_indicator='cable',
disabled_indicator='_occupied',
query_params={
'power_panel_id': '$termination_b_powerpanel'
}
Expand Down Expand Up @@ -4538,12 +4538,12 @@ class PowerFeedForm(BootstrapMixin, CustomFieldModelForm):
class Meta:
model = PowerFeed
fields = [
'region', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'comments', 'tags',
'region', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase',
'voltage', 'amperage', 'max_utilization', 'comments', 'tags',
]
fieldsets = (
('Power Panel', ('region', 'site', 'power_panel')),
('Power Feed', ('rack', 'name', 'status', 'type', 'tags')),
('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
)
widgets = {
Expand Down
Loading

0 comments on commit 8dd7123

Please sign in to comment.