This is an automated email from the ASF dual-hosted git repository.

weizhou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 70a4503ea16db57dbcd2535a55f90dbad538a52f
Merge: b46e29dc677 38006b2e03d
Author: Wei Zhou <weiz...@apache.org>
AuthorDate: Thu Sep 11 14:04:52 2025 +0200

    Merge remote-tracking branch 'apache/4.20'

 .../main/java/com/cloud/hypervisor/Hypervisor.java |  6 ++-
 .../kubernetes/cluster/KubernetesCluster.java      |  4 ++
 .../com/cloud/agent/manager/AgentManagerImpl.java  |  2 +-
 .../kvm/resource/LibvirtComputingResource.java     | 14 ++++++
 ...LibvirtGetUnmanagedInstancesCommandWrapper.java |  8 +++-
 .../KubernetesClusterScaleWorker.java              |  5 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  3 +-
 .../resource/NfsSecondaryStorageResource.java      | 22 +++++++--
 .../resource/NfsSecondaryStorageResourceTest.java  | 56 +++++++++++++++++++---
 systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py    |  2 +
 .../views/compute/wizard/MultiNetworkSelection.vue | 16 ++++++-
 11 files changed, 119 insertions(+), 19 deletions(-)

diff --cc api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
index 8b5551d6a8e,379a0db2ccd..571d993c7a1
--- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
+++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
@@@ -60,9 -58,8 +60,10 @@@ public interface KubernetesCluster exte
          Stopping("Resources for the Kubernetes cluster are being destroyed"),
          Stopped("All resources for the Kubernetes cluster are destroyed, 
Kubernetes cluster may still have ephemeral resource like persistent volumes 
provisioned"),
          Scaling("Transient state in which resources are either getting scaled 
up/down"),
+         ScalingStoppedCluster("Transient state in which the service offerings 
of stopped clusters are getting scaled"),
          Upgrading("Transient state in which cluster is getting upgraded"),
 +        Importing("Transient state in which additional nodes are added as 
worker nodes to a cluster"),
 +        RemovingNodes("Transient state in which additional nodes are removed 
from a cluster"),
          Alert("State to represent Kubernetes clusters which are not in 
expected desired state (operationally in active control place, stopped cluster 
VM's etc)."),
          Recovering("State in which Kubernetes cluster is recovering from 
alert state"),
          Destroyed("End state of Kubernetes cluster in which all resources are 
destroyed, cluster will not be usable further"),
diff --cc 
plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
index 3461d5c0634,f6828e3b203..0938fd44554
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
@@@ -355,10 -282,10 +355,11 @@@ public class KubernetesClusterScaleWork
          }
      }
  
 -    private void scaleKubernetesClusterOffering() throws 
CloudRuntimeException {
 +    private void scaleKubernetesClusterOffering(KubernetesClusterNodeType 
nodeType, ServiceOffering serviceOffering,
 +                                                boolean updateNodeOffering, 
boolean updateClusterOffering) throws CloudRuntimeException {
          validateKubernetesClusterScaleOfferingParameters();
-         if 
(!kubernetesCluster.getState().equals(KubernetesCluster.State.Scaling)) {
+         List<KubernetesCluster.State> scalingStates = 
List.of(KubernetesCluster.State.Scaling, 
KubernetesCluster.State.ScalingStoppedCluster);
+         if (!scalingStates.contains(kubernetesCluster.getState())) {
              stateTransitTo(kubernetesCluster.getId(), 
KubernetesCluster.Event.ScaleUpRequested);
          }
          if (KubernetesCluster.State.Created.equals(originalState)) {
@@@ -532,58 -447,38 +533,60 @@@
          }
          scaleTimeoutTime = System.currentTimeMillis() + 
KubernetesClusterService.KubernetesClusterScaleTimeout.value() * 1000;
          final long originalClusterSize = kubernetesCluster.getNodeCount();
 -        final ServiceOffering existingServiceOffering = 
serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId());
 -        if (existingServiceOffering == null) {
 -            logAndThrow(Level.ERROR, String.format("Scaling Kubernetes 
cluster %s failed, service offering for the Kubernetes cluster not found!", 
kubernetesCluster));
 +
 +        // DEFAULT node type means only the global service offering has been 
set for the Kubernetes cluster
 +        boolean scaleClusterDefaultOffering = 
serviceOfferingNodeTypeMap.containsKey(DEFAULT.name());
 +        if (scaleClusterDefaultOffering) {
 +            final ServiceOffering existingServiceOffering = 
serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId());
 +            final ServiceOffering existingControlOffering = 
serviceOfferingDao.findById(kubernetesCluster.getControlNodeServiceOfferingId());
 +            final ServiceOffering existingWorkerOffering = 
serviceOfferingDao.findById(kubernetesCluster.getWorkerNodeServiceOfferingId());
 +            if (existingServiceOffering == null && 
ObjectUtils.anyNull(existingControlOffering, existingWorkerOffering)) {
 +                logAndThrow(Level.ERROR, String.format("Scaling Kubernetes 
cluster : %s failed, service offering for the Kubernetes cluster not found!", 
kubernetesCluster.getName()));
 +            }
          }
 +
          final boolean autoscalingChanged = isAutoscalingChanged();
 -        final boolean serviceOfferingScalingNeeded = serviceOffering != null 
&& serviceOffering.getId() != existingServiceOffering.getId();
 +        ServiceOffering defaultServiceOffering = 
serviceOfferingNodeTypeMap.getOrDefault(DEFAULT.name(), null);
 +
 +        for (KubernetesClusterNodeType nodeType : Arrays.asList(CONTROL, 
ETCD, WORKER)) {
 +            boolean isWorkerNode = WORKER == nodeType;
 +            final long newVMRequired = (!isWorkerNode || clusterSize == null) 
? 0 : clusterSize - originalClusterSize;
 +            if (!scaleClusterDefaultOffering && 
!serviceOfferingNodeTypeMap.containsKey(nodeType.name()) && newVMRequired == 0) 
{
 +                continue;
 +            }
 +
 +            ServiceOffering existingServiceOffering = 
getExistingServiceOfferingForNodeType(nodeType, kubernetesCluster);
 +            ServiceOffering scalingServiceOffering = 
serviceOfferingNodeTypeMap.getOrDefault(nodeType.name(), 
defaultServiceOffering);
 +            boolean isNodeOfferingScalingNeeded = 
isServiceOfferingScalingNeededForNodeType(existingServiceOffering, 
scalingServiceOffering);
 +            boolean updateNodeOffering = 
serviceOfferingNodeTypeMap.containsKey(nodeType.name()) || 
isNodeOfferingScalingNeeded;
  
 -        if (autoscalingChanged) {
 -            boolean autoScaled = autoscaleCluster(this.isAutoscalingEnabled, 
minSize, maxSize);
 -            if (autoScaled && serviceOfferingScalingNeeded) {
 -                scaleKubernetesClusterOffering();
 +            boolean updateClusterOffering = isWorkerNode && 
scaleClusterDefaultOffering;
 +            if (isWorkerNode && autoscalingChanged) {
 +                boolean autoScaled = 
autoscaleCluster(this.isAutoscalingEnabled, minSize, maxSize);
 +                if (autoScaled && isNodeOfferingScalingNeeded) {
 +                    scaleKubernetesClusterOffering(nodeType, 
scalingServiceOffering, updateNodeOffering, updateClusterOffering);
 +                }
 +                stateTransitTo(kubernetesCluster.getId(), 
KubernetesCluster.Event.OperationSucceeded);
 +                return autoScaled;
              }
 -            stateTransitTo(kubernetesCluster.getId(), 
KubernetesCluster.Event.OperationSucceeded);
 -            return autoScaled;
 -        }
 -        final boolean clusterSizeScalingNeeded = clusterSize != null && 
clusterSize != originalClusterSize;
 -        final long newVMRequired = clusterSize == null ? 0 : clusterSize - 
originalClusterSize;
 -        if (serviceOfferingScalingNeeded && clusterSizeScalingNeeded) {
 -            if (newVMRequired > 0) {
 -                scaleKubernetesClusterOffering();
 -                scaleKubernetesClusterSize();
 +            final boolean clusterSizeScalingNeeded = isWorkerNode && 
clusterSize != null && clusterSize != originalClusterSize;
 +            if (isNodeOfferingScalingNeeded && clusterSizeScalingNeeded) {
 +                if (newVMRequired > 0) {
 +                    scaleKubernetesClusterOffering(nodeType, 
scalingServiceOffering, updateNodeOffering, updateClusterOffering);
 +                    scaleKubernetesClusterSize(nodeType);
 +                } else {
 +                    scaleKubernetesClusterSize(nodeType);
 +                    scaleKubernetesClusterOffering(nodeType, 
scalingServiceOffering, updateNodeOffering, updateClusterOffering);
 +                }
 +            } else if (isNodeOfferingScalingNeeded) {
 +                scaleKubernetesClusterOffering(nodeType, 
scalingServiceOffering, updateNodeOffering, updateClusterOffering);
 +            } else if (clusterSizeScalingNeeded) {
 +                scaleKubernetesClusterSize(nodeType);
+             } else {
 -                scaleKubernetesClusterSize();
 -                scaleKubernetesClusterOffering();
++                return true;
              }
 -        } else if (serviceOfferingScalingNeeded) {
 -            scaleKubernetesClusterOffering();
 -        } else if (clusterSizeScalingNeeded) {
 -            scaleKubernetesClusterSize();
 -        } else {
 -            return true;
          }
 +
          stateTransitTo(kubernetesCluster.getId(), 
KubernetesCluster.Event.OperationSucceeded);
          return true;
      }
diff --cc 
services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
index 6d474ddcc92,9f510f25b12..17414c60f20
--- 
a/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
+++ 
b/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
@@@ -18,10 -18,10 +18,9 @@@
   */
  package org.apache.cloudstack.storage.resource;
  
- import org.apache.logging.log4j.Logger;
  import static org.mockito.ArgumentMatchers.any;
- import org.mockito.Mock;
  import static org.mockito.Mockito.doThrow;
 -import static org.mockito.Mockito.spy;
+ import static org.mockito.Mockito.times;
  
  import java.io.File;
  import java.nio.file.Files;

Reply via email to