Просмотр исходного кода

Reference WirelessLink on both attached Interfaces

jeremystretch 4 лет назад
Родитель
Сommit
445e16f668

+ 20 - 0
netbox/dcim/migrations/0138_interface_wireless_link.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.8 on 2021-10-13 15:29
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wireless', '0001_wireless'),
+        ('dcim', '0137_wireless'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='wireless_link',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wireless.wirelesslink'),
+        ),
+    ]

+ 13 - 6
netbox/dcim/models/device_components.py

@@ -529,6 +529,13 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
         null=True,
         verbose_name='Channel width'
     )
+    wireless_link = models.ForeignKey(
+        to='wireless.WirelessLink',
+        on_delete=models.SET_NULL,
+        related_name='+',
+        blank=True,
+        null=True
+    )
     wireless_lans = models.ManyToManyField(
         to='wireless.WirelessLAN',
         related_name='interfaces',
@@ -568,14 +575,14 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
     def clean(self):
         super().clean()
 
-        # Virtual interfaces cannot be connected
-        if not self.is_connectable and self.cable:
+        # Virtual Interfaces cannot have a Cable attached
+        if self.is_virtual and self.cable:
             raise ValidationError({
                 'type': f"{self.get_type_display()} interfaces cannot have a cable attached."
             })
 
-        # Non-connectable interfaces cannot be marked as connected
-        if not self.is_connectable and self.mark_connected:
+        # Virtual Interfaces cannot be marked as connected
+        if self.is_virtual and self.mark_connected:
             raise ValidationError({
                 'mark_connected': f"{self.get_type_display()} interfaces cannot be marked as connected."
             })
@@ -635,8 +642,8 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
             })
 
     @property
-    def is_connectable(self):
-        return self.type not in NONCONNECTABLE_IFACE_TYPES
+    def is_wired(self):
+        return not self.is_virtual and not self.is_wireless
 
     @property
     def is_virtual(self):

+ 1 - 1
netbox/dcim/tables/template_code.py

@@ -203,7 +203,7 @@ INTERFACE_BUTTONS = """
             <i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i>
         </a>
     {% endif %}
-{% elif record.is_connectable and perms.dcim.add_cable %}
+{% elif record.is_wired and perms.dcim.add_cable %}
     <a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i></a>
     <a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-lan-connect" aria-hidden="true"></i></a>
     {% if not record.mark_connected %}

+ 17 - 2
netbox/templates/dcim/interface.html

@@ -117,7 +117,7 @@
             {% plugin_left_page object %}
         </div>
         <div class="col col-md-6">
-            {% if object.is_connectable %}
+            {% if not object.is_virtual %}
                 <div class="card">
                     <h5 class="card-header">
                         Connection
@@ -221,10 +221,19 @@
                                 </td>
                             </tr>
                         </table>
+                    {% elif object.wireless_link %}
+                        <table class="table table-hover">
+                            <tr>
+                                <th scope="row">Wireless Link</th>
+                                <td>
+                                    <a href="{{ object.wireless_link.get_absolute_url }}">{{ object.wireless_link }}</a>
+                                </td>
+                            </tr>
+                        </table>
                     {% else %}
                         <div class="text-muted">
                             Not Connected
-                            {% if perms.dcim.add_cable %}
+                            {% if object.is_wired and perms.dcim.add_cable %}
                                 <div class="dropdown float-end">
                                     <button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                         <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
@@ -252,6 +261,12 @@
                                         </li>
                                     </ul>
                                 </div>
+                            {% elif object.is_wireless and perms.wireless.add_wirelesslink %}
+                                <div class="dropdown float-end">
+                                    <a href="{% url 'wireless:wirelesslink_add' %}?interface_a={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary btn-sm">
+                                        <span class="mdi mdi-wifi-plus" aria-hidden="true"></span> Connect
+                                    </a>
+                                </div>
                             {% endif %}
                         </div>
                     {% endif %}

+ 3 - 0
netbox/wireless/apps.py

@@ -3,3 +3,6 @@ from django.apps import AppConfig
 
 class WirelessConfig(AppConfig):
     name = 'wireless'
+
+    def ready(self):
+        import wireless.signals

+ 4 - 2
netbox/wireless/forms/models.py

@@ -37,13 +37,15 @@ class WirelessLinkForm(BootstrapMixin, CustomFieldModelForm):
         queryset=Interface.objects.all(),
         query_params={
             'kind': 'wireless'
-        }
+        },
+        label='Interface A'
     )
     interface_b = DynamicModelChoiceField(
         queryset=Interface.objects.all(),
         query_params={
             'kind': 'wireless'
-        }
+        },
+        label='Interface B'
     )
     tags = DynamicModelMultipleChoiceField(
         queryset=Tag.objects.all(),

+ 58 - 0
netbox/wireless/signals.py

@@ -0,0 +1,58 @@
+import logging
+
+from django.db.models.signals import post_save, post_delete
+from django.dispatch import receiver
+
+from dcim.models import Interface
+from .models import WirelessLink
+
+
+#
+# Wireless links
+#
+
+@receiver(post_save, sender=WirelessLink)
+def update_connected_interfaces(instance, raw=False, **kwargs):
+    """
+    When a WirelessLink is saved, save a reference to it on each connected interface.
+    """
+    print('update_connected_interfaces')
+    logger = logging.getLogger('netbox.wireless.wirelesslink')
+    if raw:
+        logger.debug(f"Skipping endpoint updates for imported wireless link {instance}")
+        return
+
+    if instance.interface_a.wireless_link != instance:
+        logger.debug(f"Updating interface A for wireless link {instance}")
+        instance.interface_a.wireless_link = instance
+        # instance.interface_a._cable_peer = instance.interface_b  # TODO: Rename _cable_peer field
+        instance.interface_a.save()
+    if instance.interface_b.cable != instance:
+        logger.debug(f"Updating interface B for wireless link {instance}")
+        instance.interface_b.wireless_link = instance
+        # instance.interface_b._cable_peer = instance.interface_a
+        instance.interface_b.save()
+
+
+@receiver(post_delete, sender=WirelessLink)
+def nullify_connected_interfaces(instance, **kwargs):
+    """
+    When a WirelessLink is deleted, update its two connected Interfaces
+    """
+    print('nullify_connected_interfaces')
+    logger = logging.getLogger('netbox.wireless.wirelesslink')
+
+    if instance.interface_a is not None:
+        logger.debug(f"Nullifying interface A for wireless link {instance}")
+        Interface.objects.filter(pk=instance.interface_a.pk).update(
+            wireless_link=None,
+            _cable_peer_type=None,
+            _cable_peer_id=None
+        )
+    if instance.interface_b is not None:
+        logger.debug(f"Nullifying interface B for wireless link {instance}")
+        Interface.objects.filter(pk=instance.interface_b.pk).update(
+            wireless_link=None,
+            _cable_peer_type=None,
+            _cable_peer_id=None
+        )