2 Commits

Author SHA1 Message Date
4feb802c4f fix(ipam): filter unlinked allocations to VMs with IPs inside active IPAM networks
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 46s
Deploy to Production / Trigger production update (push) Successful in 8s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 02:53:50 +05:00
64924e50ed fix(instances): persist server filter selection across navigation and page reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 02:53:45 +05:00
2 changed files with 25 additions and 3 deletions

View File

@@ -766,6 +766,7 @@ def get_unlinked_allocations(
):
"""
Return VM instances that have no IPAM allocation linked to them (by vmid+server_id).
Only VMs whose cached ip_address falls within an active IPAM network are included.
"""
unlinked = []
@@ -782,13 +783,32 @@ def get_unlinked_allocations(
servers_map = {s.id: s for s in db.query(ProxmoxServer).all()}
# Parse active IPAM networks to filter by IP membership
ipam_nets = []
for net in db.query(IPAMNetwork).filter(IPAMNetwork.is_active == True).all():
try:
ipam_nets.append(ipaddress_module.ip_network(net.network, strict=False))
except ValueError:
pass
for vm in vms:
if (vm.server_id, vm.vmid) in linked_pairs:
continue
# Skip VMs with no IP or an IP outside all IPAM networks
if not vm.ip_address:
continue
try:
vm_ip = ipaddress_module.ip_address(vm.ip_address)
if not any(vm_ip in net for net in ipam_nets):
continue
except ValueError:
continue
server = servers_map.get(vm.server_id)
unlinked.append({
'id': None,
'ip_address': vm.ip_address or '?',
'ip_address': vm.ip_address,
'resource_name': vm.name,
'resource_type': vm.vm_type,
'can_link': True,

View File

@@ -263,7 +263,9 @@ export default function InstancesPage() {
const [search, setSearch] = useState('');
const [statusFilter, setStatusFilter] = useState<string>('all');
const [serverFilter, setServerFilter] = useState<string>('all');
const [serverFilter, setServerFilter] = useState<string>(() => {
return localStorage.getItem('instances-server-filter') ?? 'all';
});
const [nodeFilter, setNodeFilter] = useState<string>(() => {
return localStorage.getItem('instances-node-filter') ?? 'all';
});
@@ -721,7 +723,7 @@ export default function InstancesPage() {
</Select>
{servers.length > 1 && (
<Select value={serverFilter} onValueChange={v => { if (v !== null) { setServerFilter(v); setNodeFilter('all'); } }}>
<Select value={serverFilter} onValueChange={v => { if (v !== null) { setServerFilter(v); localStorage.setItem('instances-server-filter', v); setNodeFilter('all'); localStorage.setItem('instances-node-filter', 'all'); } }}>
<SelectTrigger className="w-[150px]">
<SelectValue placeholder={t('backups.select_server', 'Server')} />
</SelectTrigger>