From 99d9c27f529333d85f9ef02cfc02acd9e5f48431 Mon Sep 17 00:00:00 2001 From: Pearl1594 Date: Wed, 21 Oct 2026 16:28:21 -0400 Subject: [PATCH] Re-reserve released NICs when a VM resyncs to Running to restore nics_count --- .../service/NetworkOrchestrationService.java | 6 ++++++ .../cloud/vm/VirtualMachineManagerImpl.java | 15 ++++++++++++++ .../orchestration/NetworkOrchestrator.java | 20 +++++++++++++++++++ .../com/cloud/vpc/MockNetworkManagerImpl.java | 4 ++++ 4 files changed, 45 insertions(+) diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index d851867ef136..022679dc3920 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -265,6 +265,12 @@ NicProfile prepareNic(VirtualMachineProfile vmProfile, DeployDestination dest, R throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; + /** + * Re-reserves the Allocated nics of a VM that is Running, e.g. after its nics were released on a power report + * that was later reverted, and restores the active nics count of their networks. + */ + void reconcileNicsForRunningVm(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context); + /** * Removes the provided nic from the given vm * @param vm diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index c98391a654db..996e71dd0602 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5432,6 +5432,7 @@ private void handlePowerOnReportWithNoPendingJobsOnVM(final VMInstanceVO vm) { ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, vm.getDomainId(), EventTypes.EVENT_VM_START, "Out of band VM power on", vm.getId(), getApiCommandResourceTypeForVm(vm).toString()); logger.info("VM {} is sync-ed to at Running state according to power-on report from hypervisor.", vm.getInstanceName()); + reconcileNicsForRunningVm(vm); break; case Destroyed: @@ -5456,6 +5457,20 @@ private void handlePowerOnReportWithNoPendingJobsOnVM(final VMInstanceVO vm) { } } + private void reconcileNicsForRunningVm(final VMInstanceVO vm) { + final VMInstanceVO runningVm = _vmDao.findById(vm.getId()); + if (runningVm == null || runningVm.getType() != VirtualMachine.Type.User || runningVm.getState() != State.Running || runningVm.getHostId() == null + || _nicsDao.listByVmId(runningVm.getId()).stream().noneMatch(nic -> nic.getState() == Nic.State.Allocated)) { + return; + } + final Host host = _hostDao.findById(runningVm.getHostId()); + final DataCenter dc = _entityMgr.findById(DataCenter.class, runningVm.getDataCenterId()); + final User user = _entityMgr.findById(User.class, User.UID_SYSTEM); + final Account account = _entityMgr.findById(Account.class, Account.ACCOUNT_ID_SYSTEM); + final ReservationContext context = new ReservationContextImpl(null, null, user, account); + _networkMgr.reconcileNicsForRunningVm(new VirtualMachineProfileImpl(runningVm), new DeployDestination(dc, null, null, host), context); + } + private void handlePowerOffReportWithNoPendingJobsOnVM(final VMInstanceVO vm) { switch (vm.getState()) { case Starting: diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 9b1609c669a5..219fb5525f6d 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2187,6 +2187,26 @@ public int compare(final NicVO nic1, final NicVO nic2) { } } + @Override + public void reconcileNicsForRunningVm(final VirtualMachineProfile vmProfile, final DeployDestination dest, final ReservationContext context) { + for (final NicVO nic : _nicDao.listByVmId(vmProfile.getId())) { + if (nic.getState() != Nic.State.Allocated) { + continue; + } + try { + final Pair implemented = implementNetwork(nic.getNetworkId(), dest, context); + if (implemented == null || implemented.first() == null) { + logger.warn("Unable to implement Network ID={} to reconcile NIC {} of Running Instance {}", nic.getNetworkId(), nic, vmProfile); + continue; + } + prepareNic(vmProfile, dest, context, nic.getId(), implemented.second()); + logger.info("Re-reserved NIC {} of Running Instance {}", nic, vmProfile); + } catch (final Exception e) { + logger.warn("Failed to reconcile NIC {} of Running Instance {}", nic, vmProfile, e); + } + } + } + @Override public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final DeployDestination dest, final ReservationContext context, final long nicId, final Network network) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index aa27b24e60f5..76db7173d87c 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -758,6 +758,10 @@ public Pair allocateNic(NicProfile requested, Network netwo return null; } + @Override + public void reconcileNicsForRunningVm(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) { + } + @Override public NicProfile prepareNic(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context, long nicId, Network network) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException,