mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Replace 'is_connected' boolean with Cable attachment
This commit is contained in:
@ -245,7 +245,7 @@ class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm
|
|||||||
label='Interface',
|
label='Interface',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/interfaces/?device_id={{device}}&type=physical',
|
api_url='/api/dcim/interfaces/?device_id={{device}}&type=physical',
|
||||||
disabled_indicator='is_connected'
|
disabled_indicator='cable'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -276,12 +276,12 @@ class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm
|
|||||||
|
|
||||||
super(CircuitTerminationForm, self).__init__(*args, **kwargs)
|
super(CircuitTerminationForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Mark connected interfaces as disabled
|
# Mark occupied interfaces as disabled
|
||||||
self.fields['interface'].choices = []
|
self.fields['interface'].choices = []
|
||||||
for iface in self.fields['interface'].queryset:
|
for iface in self.fields['interface'].queryset:
|
||||||
self.fields['interface'].choices.append(
|
self.fields['interface'].choices.append(
|
||||||
(iface.id, {
|
(iface.id, {
|
||||||
'label': iface.name,
|
'label': iface.name,
|
||||||
'disabled': iface.is_connected and iface.pk != self.initial.get('interface'),
|
'disabled': bool(iface.cable) and iface.pk != self.initial.get('interface'),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -559,14 +559,10 @@ class ConsoleServerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
|||||||
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
return hasattr(obj, 'connected_endpoint') and obj.connected_endpoint is not None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -588,14 +584,10 @@ class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
|||||||
class NestedConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
class NestedConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
return obj.connected_endpoint is not None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -616,14 +608,10 @@ class PowerOutletSerializer(TaggitSerializer, ValidatedModelSerializer):
|
|||||||
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
return hasattr(obj, 'connected_endpoint') and obj.connected_endpoint is not None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -645,43 +633,23 @@ class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
|||||||
class NestedPowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
class NestedPowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
return obj.connected_endpoint is not None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Interfaces
|
# Interfaces
|
||||||
#
|
#
|
||||||
|
|
||||||
class IsConnectedMixin(object):
|
class NestedInterfaceSerializer(WritableNestedSerializer):
|
||||||
"""
|
|
||||||
Provide a method for setting is_connected on Interface serializers.
|
|
||||||
"""
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
"""
|
|
||||||
Return True if the interface has a connected interface or circuit.
|
|
||||||
"""
|
|
||||||
if obj.connected_endpoint:
|
|
||||||
return True
|
|
||||||
if hasattr(obj, 'circuit_termination') and obj.circuit_termination is not None:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class NestedInterfaceSerializer(IsConnectedMixin, WritableNestedSerializer):
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['id', 'url', 'device', 'name', 'is_connected']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
||||||
@ -711,12 +679,11 @@ class InterfaceVLANSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(TaggitSerializer, IsConnectedMixin, ValidatedModelSerializer):
|
class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
|
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
|
||||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
connected_endpoint = NestedInterfaceSerializer(read_only=True)
|
connected_endpoint = NestedInterfaceSerializer(read_only=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
|
||||||
circuit_termination = InterfaceCircuitTerminationSerializer(read_only=True)
|
circuit_termination = InterfaceCircuitTerminationSerializer(read_only=True)
|
||||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
||||||
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
||||||
@ -733,8 +700,7 @@ class InterfaceSerializer(TaggitSerializer, IsConnectedMixin, ValidatedModelSeri
|
|||||||
model = Interface
|
model = Interface
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
|
'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
|
||||||
'is_connected', 'connected_endpoint', 'circuit_termination', 'cable', 'mode', 'untagged_vlan',
|
'connected_endpoint', 'circuit_termination', 'cable', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags',
|
||||||
'tagged_vlans', 'tags',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
@ -778,7 +744,7 @@ class NestedRearPortSerializer(WritableNestedSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = ['id', 'url', 'device', 'name']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -811,7 +777,7 @@ class NestedFrontPortSerializer(WritableNestedSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = ['id', 'url', 'device', 'name']
|
fields = ['id', 'url', 'device', 'name', 'cable']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1402,7 +1402,7 @@ class ConsolePortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelF
|
|||||||
label='Port',
|
label='Port',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/console-server-ports/?device_id={{console_server}}',
|
api_url='/api/dcim/console-server-ports/?device_id={{console_server}}',
|
||||||
disabled_indicator='is_connected',
|
disabled_indicator='cable',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1493,7 +1493,7 @@ class ConsoleServerPortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.
|
|||||||
label='Port',
|
label='Port',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/console-ports/?device_id={{device}}',
|
api_url='/api/dcim/console-ports/?device_id={{device}}',
|
||||||
disabled_indicator='is_connected'
|
disabled_indicator='cable'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
connection_status = forms.BooleanField(
|
connection_status = forms.BooleanField(
|
||||||
@ -1671,7 +1671,7 @@ class PowerPortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor
|
|||||||
label='Outlet',
|
label='Outlet',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/power-outlets/?device_id={{pdu}}',
|
api_url='/api/dcim/power-outlets/?device_id={{pdu}}',
|
||||||
disabled_indicator='is_connected'
|
disabled_indicator='cable'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1762,7 +1762,7 @@ class PowerOutletConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
|
|||||||
label='Port',
|
label='Port',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/power-ports/?device_id={{device}}',
|
api_url='/api/dcim/power-ports/?device_id={{device}}',
|
||||||
disabled_indicator='is_connected'
|
disabled_indicator='cable'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
connection_status = forms.BooleanField(
|
connection_status = forms.BooleanField(
|
||||||
@ -2171,7 +2171,7 @@ class CableCreateForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
|
|||||||
label='Name',
|
label='Name',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/dcim/{{termination_b_type}}s/?device_id={{termination_b_device}}',
|
api_url='/api/dcim/{{termination_b_type}}s/?device_id={{termination_b_device}}',
|
||||||
disabled_indicator='is_connected'
|
disabled_indicator='cable'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1888,7 +1888,9 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
})
|
})
|
||||||
|
|
||||||
# Virtual interfaces cannot be connected
|
# Virtual interfaces cannot be connected
|
||||||
if self.form_factor in NONCONNECTABLE_IFACE_TYPES and self.is_connected:
|
if self.form_factor in NONCONNECTABLE_IFACE_TYPES and (
|
||||||
|
self.cable or getattr(self, 'circuit_termination', False)
|
||||||
|
):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'form_factor': "Virtual and wireless interfaces cannot be connected to another interface or circuit. "
|
'form_factor': "Virtual and wireless interfaces cannot be connected to another interface or circuit. "
|
||||||
"Disconnect the interface or choose a suitable form factor."
|
"Disconnect the interface or choose a suitable form factor."
|
||||||
@ -1977,14 +1979,6 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
def is_lag(self):
|
def is_lag(self):
|
||||||
return self.form_factor == IFACE_FF_LAG
|
return self.form_factor == IFACE_FF_LAG
|
||||||
|
|
||||||
@property
|
|
||||||
def is_connected(self):
|
|
||||||
try:
|
|
||||||
return bool(self.circuit_termination)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
pass
|
|
||||||
return bool(self.connected_endpoint)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Pass-through ports
|
# Pass-through ports
|
||||||
|
@ -1953,7 +1953,7 @@ class ConsolePortTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data['results'][0]),
|
sorted(response.data['results'][0]),
|
||||||
['device', 'id', 'is_connected', 'name', 'url']
|
['cable', 'device', 'id', 'name', 'url']
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_consoleport(self):
|
def test_create_consoleport(self):
|
||||||
@ -2068,7 +2068,7 @@ class ConsoleServerPortTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data['results'][0]),
|
sorted(response.data['results'][0]),
|
||||||
['device', 'id', 'is_connected', 'name', 'url']
|
['cable', 'device', 'id', 'name', 'url']
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_consoleserverport(self):
|
def test_create_consoleserverport(self):
|
||||||
@ -2179,7 +2179,7 @@ class PowerPortTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data['results'][0]),
|
sorted(response.data['results'][0]),
|
||||||
['device', 'id', 'is_connected', 'name', 'url']
|
['cable', 'device', 'id', 'name', 'url']
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_powerport(self):
|
def test_create_powerport(self):
|
||||||
@ -2294,7 +2294,7 @@ class PowerOutletTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data['results'][0]),
|
sorted(response.data['results'][0]),
|
||||||
['device', 'id', 'is_connected', 'name', 'url']
|
['cable', 'device', 'id', 'name', 'url']
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_poweroutlet(self):
|
def test_create_poweroutlet(self):
|
||||||
@ -2431,7 +2431,7 @@ class InterfaceTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data['results'][0]),
|
sorted(response.data['results'][0]),
|
||||||
['device', 'id', 'is_connected', 'name', 'url']
|
['cable', 'device', 'id', 'name', 'url']
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_interface(self):
|
def test_create_interface(self):
|
||||||
|
Reference in New Issue
Block a user