Skip to content

Fixes: #19475 - VM Interface VLAN availibility when cluster and VLAN group scope is dcim.location #19485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion netbox/ipam/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def get_for_virtualmachine(self, vm):

# Find all relevant VLANGroups
q = Q()
site = vm.site or vm.cluster._site
site = vm.site
if vm.cluster:
# Add VLANGroups scoped to the assigned cluster (or its group)
q |= Q(
Expand All @@ -160,6 +160,30 @@ def get_for_virtualmachine(self, vm):
scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
scope_id=vm.cluster.group_id
)
# Looking all possible cluster scopes
if vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'location'):
site = site or vm.cluster.scope.site
q |= Q(
scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'),
scope_id__in=vm.cluster.scope.get_ancestors(include_self=True)
)
elif vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'site'):
site = site or vm.cluster.scope
q |= Q(
scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
scope_id=vm.cluster.scope.pk
)
elif vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'sitegroup'):
q |= Q(
scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
scope_id__in=vm.cluster.scope.get_ancestors(include_self=True)
)
elif vm.cluster.scope_type == ContentType.objects.get_by_natural_key('dcim', 'region'):
q |= Q(
scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
scope_id__in=vm.cluster.scope.get_ancestors(include_self=True)
)
# VM can be assigned to a site without a cluster so checking assigned site independently
if site:
# Add VLANGroups scoped to the assigned site (or its group or region)
q |= Q(
Expand Down
22 changes: 15 additions & 7 deletions netbox/ipam/tests/test_filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,7 @@ def setUpTestData(cls):
Cluster(name='Cluster 1', type=cluster_type, group=cluster_groups[0], scope=sites[0]),
Cluster(name='Cluster 2', type=cluster_type, group=cluster_groups[1], scope=sites[1]),
Cluster(name='Cluster 3', type=cluster_type, group=cluster_groups[2], scope=sites[2]),
Cluster(name='Cluster 4', type=cluster_type, group=cluster_groups[0], scope=locations[0]),
)
for cluster in clusters:
cluster.save()
Expand All @@ -1857,13 +1858,15 @@ def setUpTestData(cls):
VirtualMachine(name='Virtual Machine 1', cluster=clusters[0]),
VirtualMachine(name='Virtual Machine 2', cluster=clusters[1]),
VirtualMachine(name='Virtual Machine 3', cluster=clusters[2]),
VirtualMachine(name='Virtual Machine 4', cluster=clusters[3]),
)
VirtualMachine.objects.bulk_create(virtual_machines)

vm_interfaces = (
VMInterface(virtual_machine=virtual_machines[0], name='VM Interface 1'),
VMInterface(virtual_machine=virtual_machines[1], name='VM Interface 2'),
VMInterface(virtual_machine=virtual_machines[2], name='VM Interface 3'),
VMInterface(virtual_machine=virtual_machines[3], name='VM Interface 4'),
)
VMInterface.objects.bulk_create(vm_interfaces)

Expand All @@ -1890,6 +1893,7 @@ def setUpTestData(cls):
VLANGroup(name='Cluster 1', slug='cluster-1', scope=clusters[0]),
VLANGroup(name='Cluster 2', slug='cluster-2', scope=clusters[1]),
VLANGroup(name='Cluster 3', slug='cluster-3', scope=clusters[2]),
VLANGroup(name='Cluster 4', slug='cluster-4', scope=clusters[3]),

# General purpose VLAN groups
VLANGroup(name='VLAN Group 1', slug='vlan-group-1'),
Expand Down Expand Up @@ -1944,11 +1948,12 @@ def setUpTestData(cls):
VLAN(vid=19, name='Cluster 1', group=groups[18]),
VLAN(vid=20, name='Cluster 2', group=groups[19]),
VLAN(vid=21, name='Cluster 3', group=groups[20]),
VLAN(vid=22, name='Cluster 4', group=groups[21]),
VLAN(
vid=101,
name='VLAN 101',
site=sites[3],
group=groups[21],
group=groups[22],
role=roles[0],
tenant=tenants[0],
status=VLANStatusChoices.STATUS_ACTIVE,
Expand All @@ -1957,7 +1962,7 @@ def setUpTestData(cls):
vid=102,
name='VLAN 102',
site=sites[3],
group=groups[21],
group=groups[22],
role=roles[0],
tenant=tenants[0],
status=VLANStatusChoices.STATUS_ACTIVE,
Expand All @@ -1966,7 +1971,7 @@ def setUpTestData(cls):
vid=201,
name='VLAN 201',
site=sites[4],
group=groups[22],
group=groups[23],
role=roles[1],
tenant=tenants[1],
status=VLANStatusChoices.STATUS_DEPRECATED,
Expand All @@ -1975,7 +1980,7 @@ def setUpTestData(cls):
vid=202,
name='VLAN 202',
site=sites[4],
group=groups[22],
group=groups[23],
role=roles[1],
tenant=tenants[1],
status=VLANStatusChoices.STATUS_DEPRECATED,
Expand All @@ -1984,7 +1989,7 @@ def setUpTestData(cls):
vid=301,
name='VLAN 301',
site=sites[5],
group=groups[23],
group=groups[24],
role=roles[2],
tenant=tenants[2],
status=VLANStatusChoices.STATUS_RESERVED,
Expand All @@ -1993,13 +1998,13 @@ def setUpTestData(cls):
vid=302,
name='VLAN 302',
site=sites[5],
group=groups[23],
group=groups[24],
role=roles[2],
tenant=tenants[2],
status=VLANStatusChoices.STATUS_RESERVED,
),
# Create one globally available VLAN on a VLAN group
VLAN(vid=500, name='VLAN Group 1', group=groups[24]),
VLAN(vid=500, name='VLAN Group 1', group=groups[25]),
# Create one globally available VLAN
VLAN(vid=1000, name='Global VLAN'),
# Create some Q-in-Q service VLANs
Expand Down Expand Up @@ -2130,6 +2135,9 @@ def test_available_on_virtualmachine(self):
vm_id = VirtualMachine.objects.first().pk
params = {'available_on_virtualmachine': vm_id}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7) # 5 scoped + 1 global group + 1 global
vm_id = VirtualMachine.objects.get(name='Virtual Machine 4').pk
params = {'available_on_virtualmachine': vm_id}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8) # 6 scoped + 1 global group + 1 global

def test_available_at_site(self):
site_id = Site.objects.first().pk
Expand Down