VM Interface VLAN availibility when cluster and VLAN group scope is dcim.location #11167

Closed
opened 2025-12-29 21:41:15 +01:00 by adam · 5 comments
Owner

Originally created by @Omripresent on GitHub (May 13, 2025).

Originally assigned to: @Omripresent on GitHub.

Deployment Type

Self-hosted

NetBox Version

v4.2.6

Python Version

3.12

Steps to Reproduce

  1. Create a VLAN group with scope dcim.location and assign VLANs to that group
  2. Create a VM cluster with scope dcim.location matching the VLAN group from step 1
  3. Create a VM in the cluster from step 2
  4. Create a VM interface in the VM from step 3
  5. Attempt to assign a VLAN from step 1 to the VM interface from step 4

Expected Behavior

It is expected that VLANs associated with a VLAN group with a location scope will be able to be assigned to VM interfaces related to the same location

Observed Behavior

The VLANs of the matching location and VLAN group and not listed under either the untagged or tagged VLAN dropdowns

Originally created by @Omripresent on GitHub (May 13, 2025). Originally assigned to: @Omripresent on GitHub. ### Deployment Type Self-hosted ### NetBox Version v4.2.6 ### Python Version 3.12 ### Steps to Reproduce 1. Create a VLAN group with scope dcim.location and assign VLANs to that group 2. Create a VM cluster with scope dcim.location matching the VLAN group from step 1 3. Create a VM in the cluster from step 2 4. Create a VM interface in the VM from step 3 5. Attempt to assign a VLAN from step 1 to the VM interface from step 4 ### Expected Behavior It is expected that VLANs associated with a VLAN group with a location scope will be able to be assigned to VM interfaces related to the same location ### Observed Behavior The VLANs of the matching location and VLAN group and not listed under either the untagged or tagged VLAN dropdowns
adam added the type: bugstatus: acceptedseverity: low labels 2025-12-29 21:41:15 +01:00
adam closed this issue 2025-12-29 21:41:15 +01:00
Author
Owner

@Omripresent commented on GitHub (May 13, 2025):

An update to the function get_for_virtualmachine can fix this issue.
Proposed change:

    def get_for_virtualmachine(self, vm):
        """
        Return all VLANs available to the specified VirtualMachine.
        """
        from .models import VLANGroup

        # Find all relevant VLANGroups
        q = Q()
        if vm.cluster:
            # Add VLANGroups scoped to the assigned cluster (or its group)
            q |= Q(
                scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'),
                scope_id=vm.cluster_id
            )
            if vm.cluster.group:
                q |= Q(
                    scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
                    scope_id=vm.cluster.group_id
                )
            if vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'location'):
                q |= Q(
                    scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'),
                    scope_id__in=vm.cluster.scope.get_ancestors(include_self=True)
                )
        if vm.site:
            # Add VLANGroups scoped to the assigned location (or its site, group or region)
            q |= Q(
                scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
                scope_id=vm.site.pk
            )
            if vm.site.region:
                q |= Q(
                    scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
                    scope_id__in=vm.site.region.get_ancestors(include_self=True)
                )
            if vm.site.group:
                q |= Q(
                    scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
                    scope_id__in=vm.site.group.get_ancestors(include_self=True)
                )
        vlan_groups = VLANGroup.objects.filter(q)

        # Return all applicable VLANs
        q = (
            Q(group__in=vlan_groups) |
            Q(group__scope_id__isnull=True, site__isnull=True) |  # Global group VLANs
            Q(group__isnull=True, site__isnull=True)  # Global VLANs
        )
        if vm.site:
            q |= Q(site=vm.site)

        return self.filter(q)

This doesn't account for VLAN groups scoped to a specific rack, that could be more problematic to solve since the "smallest" scope of a cluster is location. Therefore I think that lookup by rack is not feasible.

@Omripresent commented on GitHub (May 13, 2025): An update to the function [get_for_virtualmachine](https://github.com/netbox-community/netbox/blob/8e13f2a9ec48bc892782e50aa58c691ef1f8b8ec/netbox/ipam/querysets.py#L143) can fix this issue. Proposed change: ```python def get_for_virtualmachine(self, vm): """ Return all VLANs available to the specified VirtualMachine. """ from .models import VLANGroup # Find all relevant VLANGroups q = Q() if vm.cluster: # Add VLANGroups scoped to the assigned cluster (or its group) q |= Q( scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), scope_id=vm.cluster_id ) if vm.cluster.group: q |= Q( scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), scope_id=vm.cluster.group_id ) if vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'location'): q |= Q( scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'), scope_id__in=vm.cluster.scope.get_ancestors(include_self=True) ) if vm.site: # Add VLANGroups scoped to the assigned location (or its site, group or region) q |= Q( scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), scope_id=vm.site.pk ) if vm.site.region: q |= Q( scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), scope_id__in=vm.site.region.get_ancestors(include_self=True) ) if vm.site.group: q |= Q( scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), scope_id__in=vm.site.group.get_ancestors(include_self=True) ) vlan_groups = VLANGroup.objects.filter(q) # Return all applicable VLANs q = ( Q(group__in=vlan_groups) | Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs Q(group__isnull=True, site__isnull=True) # Global VLANs ) if vm.site: q |= Q(site=vm.site) return self.filter(q) ``` This doesn't account for VLAN groups scoped to a specific rack, that could be more problematic to solve since the "smallest" scope of a cluster is location. Therefore I think that lookup by rack is not feasible.
Author
Owner

@jnovinger commented on GitHub (May 13, 2025):

@Omripresent , thanks for the report. I think I agree with your statement about lookup by rack not being feasible.

Would you like to take this on?

@jnovinger commented on GitHub (May 13, 2025): @Omripresent , thanks for the report. I think I agree with your statement about lookup by rack not being feasible. Would you like to take this on?
Author
Owner

@Omripresent commented on GitHub (May 13, 2025):

I'd be happy to. I ran some basic tests for my use case but I'll extend it before submitting a PR

@Omripresent commented on GitHub (May 13, 2025): I'd be happy to. I ran some basic tests for my use case but I'll extend it before submitting a PR
Author
Owner

@bctiemann commented on GitHub (May 22, 2025):

@Omripresent Could you please clarify the reproduction steps? Particularly step 5. What screen/workflow are you using to assign the VLAN from step 1 to the VM interface from step 4? A screenshot would be helpful.

@bctiemann commented on GitHub (May 22, 2025): @Omripresent Could you please clarify the reproduction steps? Particularly step 5. What screen/workflow are you using to assign the VLAN from step 1 to the VM interface from step 4? A screenshot would be helpful.
Author
Owner

@Omripresent commented on GitHub (May 22, 2025):

This is done via the web UI under the VM interface edit view, for example: https://demo.netbox.dev/virtualization/interfaces/1621/edit/
I've setup a similar state in the demo instance.

Image

This is where I'd expect to see vlan 100 as defined under the group here: https://demo.netbox.dev/ipam/vlan-groups/10/vlans/

Image

@Omripresent commented on GitHub (May 22, 2025): This is done via the web UI under the VM interface edit view, for example: https://demo.netbox.dev/virtualization/interfaces/1621/edit/ I've setup a similar state in the demo instance. ![Image](https://github.com/user-attachments/assets/96a7811a-79ce-48f9-aa70-c1c15dae0a22) This is where I'd expect to see vlan 100 as defined under the group here: https://demo.netbox.dev/ipam/vlan-groups/10/vlans/ ![Image](https://github.com/user-attachments/assets/4551d676-d4a2-4a05-8610-de47ddfab890)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11167