From ffc6eec48393f94fb9c2a95ccd6928eeba74b30c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 24 Sep 2019 15:07:54 -0400 Subject: [PATCH] Fixes #3519: Prevent cables from being terminated to virtual/wireless interfaces --- CHANGELOG.md | 3 ++- netbox/dcim/models.py | 28 ++++++++++++++-------------- netbox/dcim/tests/test_models.py | 23 ++++++++++++++++------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d9124f3..d4c72299a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ v2.6.5 (FUTURE) ## Bug Fixes -* [#3521](https://github.com/netbox-community/netbox/issues/3521) - Fixed error in parseURL related to {{variables}} in API url +* [#3519](https://github.com/netbox-community/netbox/issues/3519) - Prevent cables from being terminated to virtual/wireless interfaces via API +* [#3521](https://github.com/netbox-community/netbox/issues/3521) - Fix error in `parseURL` related to variables in API URL v2.6.4 (2019-09-19) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 88d2aee1d..302056edc 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2817,6 +2817,20 @@ class Cable(ChangeLoggedModel): type_a = self.termination_a_type.model type_b = self.termination_b_type.model + # Validate interface types + if type_a == 'interface' and self.termination_a.type in NONCONNECTABLE_IFACE_TYPES: + raise ValidationError({ + 'termination_a_id': 'Cables cannot be terminated to {} interfaces'.format( + self.termination_a.get_type_display() + ) + }) + if type_b == 'interface' and self.termination_b.type in NONCONNECTABLE_IFACE_TYPES: + raise ValidationError({ + 'termination_b_id': 'Cables cannot be terminated to {} interfaces'.format( + self.termination_b.get_type_display() + ) + }) + # Check that termination types are compatible if type_b not in COMPATIBLE_TERMINATION_TYPES.get(type_a): raise ValidationError("Incompatible termination types: {} and {}".format( @@ -2858,20 +2872,6 @@ class Cable(ChangeLoggedModel): self.termination_b, self.termination_b.cable_id )) - # Virtual interfaces cannot be connected - endpoint_a, endpoint_b, _ = self.get_path_endpoints() - if ( - ( - isinstance(endpoint_a, Interface) and - endpoint_a.type == IFACE_TYPE_VIRTUAL - ) or - ( - isinstance(endpoint_b, Interface) and - endpoint_b.type == IFACE_TYPE_VIRTUAL - ) - ): - raise ValidationError("Cannot connect to a virtual interface") - # Validate length and length_unit if self.length is not None and self.length_unit is None: raise ValidationError("Must specify a unit when setting a cable length") diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index 2135aba66..2b5bed283 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -343,7 +343,7 @@ class CableTestCase(TestCase): def test_cable_validates_compatibale_types(self): """ - The clean method should have a check to ensure only compatiable port types can be connected by a cable + The clean method should have a check to ensure only compatible port types can be connected by a cable """ # An interface cannot be connected to a power port cable = Cable(termination_a=self.interface1, termination_b=self.power_port1) @@ -360,30 +360,39 @@ class CableTestCase(TestCase): def test_cable_front_port_cannot_connect_to_corresponding_rear_port(self): """ - A cable cannot connect a front port to its sorresponding rear port + A cable cannot connect a front port to its corresponding rear port """ cable = Cable(termination_a=self.front_port, termination_b=self.rear_port) with self.assertRaises(ValidationError): cable.clean() - def test_cable_cannot_be_connected_to_an_existing_connection(self): + def test_cable_cannot_terminate_to_an_existing_connection(self): """ - Either side of a cable cannot be terminated when that side aready has a connection + Either side of a cable cannot be terminated when that side already has a connection """ # Try to create a cable with the same interface terminations cable = Cable(termination_a=self.interface2, termination_b=self.interface1) with self.assertRaises(ValidationError): cable.clean() - def test_cable_cannot_connect_to_a_virtual_inteface(self): + def test_cable_cannot_terminate_to_a_virtual_inteface(self): """ - A cable connection cannot include a virtual interface + A cable cannot terminate to a virtual interface """ - virtual_interface = Interface(device=self.device1, name="V1", type=0) + virtual_interface = Interface(device=self.device1, name="V1", type=IFACE_TYPE_VIRTUAL) cable = Cable(termination_a=self.interface2, termination_b=virtual_interface) with self.assertRaises(ValidationError): cable.clean() + def test_cable_cannot_terminate_to_a_wireless_inteface(self): + """ + A cable cannot terminate to a wireless interface + """ + wireless_interface = Interface(device=self.device1, name="W1", type=IFACE_TYPE_80211A) + cable = Cable(termination_a=self.interface2, termination_b=wireless_interface) + with self.assertRaises(ValidationError): + cable.clean() + class CablePathTestCase(TestCase):