mirror of
				https://github.com/netbox-community/netbox.git
				synced 2024-05-10 07:54:54 +00:00 
			
		
		
		
	Reference WirelessLink on both attached Interfaces
This commit is contained in:
		
							
								
								
									
										20
									
								
								netbox/dcim/migrations/0138_interface_wireless_link.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								netbox/dcim/migrations/0138_interface_wireless_link.py
									
									
									
									
									
										Normal file
									
								
							@@ -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'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,3 +3,6 @@ from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
class WirelessConfig(AppConfig):
 | 
			
		||||
    name = 'wireless'
 | 
			
		||||
 | 
			
		||||
    def ready(self):
 | 
			
		||||
        import wireless.signals
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
								netbox/wireless/signals.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								netbox/wireless/signals.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
        )
 | 
			
		||||
		Reference in New Issue
	
	Block a user