From c4657a914297c627303e7772bdd55126e1d67487 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 17 Feb 2025 17:45:05 +0530 Subject: [PATCH 1/4] engine/orchestration: fix missing vm powerstate update vm state Fixes #10406 VMs were not moving to Stopped state when PowerReportMissing is processed. Signed-off-by: Abhishek Kumar --- .../java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java index 4b344ac42995..50b474a82fdb 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java @@ -83,13 +83,13 @@ private void updateAndPublishVmPowerStates(long hostId, Map vmIds = instancePowerStates.keySet(); - Map notUpdated = _instanceDao.updatePowerState(instancePowerStates, hostId, - updateTime); + Map notUpdated = + _instanceDao.updatePowerState(instancePowerStates, hostId, updateTime); if (notUpdated.size() > vmIds.size()) { return; } for (Long vmId : vmIds) { - if (!notUpdated.isEmpty() && !notUpdated.containsKey(vmId)) { + if (!notUpdated.containsKey(vmId)) { logger.debug("VM state report is updated. {}, {}, power state: {}", () -> hostCache.get(hostId), () -> vmCache.get(vmId), () -> instancePowerStates.get(vmId)); _messageBus.publish(null, VirtualMachineManager.Topics.VM_POWER_STATE, From aba797c6a1efa2b2a47e2679c0c3a19a8544ca2e Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 18 Feb 2025 14:32:35 +0530 Subject: [PATCH 2/4] add unit tests Signed-off-by: Abhishek Kumar --- .../vm/VirtualMachinePowerStateSyncImpl.java | 4 +- .../VirtualMachinePowerStateSyncImplTest.java | 91 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java index 50b474a82fdb..7a1a39ec0f07 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java @@ -77,8 +77,8 @@ public void processHostVmStatePingReport(long hostId, Map instancePowerStates, - Date updateTime) { + protected void updateAndPublishVmPowerStates(long hostId, Map instancePowerStates, + Date updateTime) { if (instancePowerStates.isEmpty()) { return; } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java new file mode 100644 index 000000000000..2f49002b2f1d --- /dev/null +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java @@ -0,0 +1,91 @@ +package com.cloud.vm; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.PublishScope; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.vm.dao.VMInstanceDao; + +@RunWith(MockitoJUnitRunner.class) +public class VirtualMachinePowerStateSyncImplTest { + @Mock + MessageBus messageBus; + @Mock + VMInstanceDao instanceDao; + @Mock + HostDao hostDao; + + @InjectMocks + VirtualMachinePowerStateSyncImpl virtualMachinePowerStateSync = new VirtualMachinePowerStateSyncImpl(); + + @Before + public void setup() { + Mockito.when(instanceDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(VMInstanceVO.class)); + Mockito.when(hostDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostVO.class)); + } + + @Test + public void test_updateAndPublishVmPowerStates_emptyStates() { + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, new HashMap<>(), new Date()); + Mockito.verify(instanceDao, Mockito.never()).updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class)); + } + + @Test + public void test_updateAndPublishVmPowerStates_moreNotUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOff); + Map notUpdated = new HashMap<>(powerStates); + notUpdated.put(2L, VirtualMachine.PowerState.PowerOn); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(notUpdated); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.never()).publish(Mockito.nullable(String.class), Mockito.anyString(), + Mockito.any(PublishScope.class), Mockito.anyLong()); + } + + @Test + public void test_updateAndPublishVmPowerStates_allUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOff); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(new HashMap<>()); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.times(1)).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 1L); + } + + @Test + public void test_updateAndPublishVmPowerStates_partialUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOn); + powerStates.put(2L, VirtualMachine.PowerState.PowerOff); + Map notUpdated = new HashMap<>(); + notUpdated.put(2L, VirtualMachine.PowerState.PowerOff); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(notUpdated); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.times(1)).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 1L); + Mockito.verify(messageBus, Mockito.never()).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 2L); + } +} From bbca682c5ce9328555864e90f17f03036a9f52fa Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 18 Feb 2025 14:41:47 +0530 Subject: [PATCH 3/4] add license Signed-off-by: Abhishek Kumar --- .../vm/VirtualMachinePowerStateSyncImplTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java index 2f49002b2f1d..8b8139ebc22e 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java @@ -1,3 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. package com.cloud.vm; import java.util.Date; From 00a13ca3d48c5b795da3c18bf2cb5e20b3101645 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 18 Feb 2025 15:14:13 +0530 Subject: [PATCH 4/4] add lenient Signed-off-by: Abhishek Kumar --- .../com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java index 8b8139ebc22e..4df14fe22f3b 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java @@ -48,8 +48,8 @@ public class VirtualMachinePowerStateSyncImplTest { @Before public void setup() { - Mockito.when(instanceDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(VMInstanceVO.class)); - Mockito.when(hostDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostVO.class)); + Mockito.lenient().when(instanceDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(VMInstanceVO.class)); + Mockito.lenient().when(hostDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostVO.class)); } @Test