diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 8e2b72529df..d54c81ba7ff 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -558,7 +558,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } private DeployDestination plan(final long nodesCount, final DataCenter zone, final ServiceOffering offering, - final Long domainId, final Long accountId) throws InsufficientServerCapacityException { + final Long domainId, final Long accountId, Hypervisor.HypervisorType hypervisorType) throws InsufficientServerCapacityException { final int cpu_requested = offering.getCpu() * offering.getSpeed(); final long ram_requested = offering.getRamSize() * 1024L * 1024L; boolean useDedicatedHosts = false; @@ -579,6 +579,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne if (hosts.isEmpty()) { hosts = resourceManager.listAllHostsInOneZoneByType(Host.Type.Routing, zone.getId()); } + if (hypervisorType != null) { + hosts = hosts.stream().filter(x -> x.getHypervisorType() == hypervisorType).collect(Collectors.toList()); + } final Map> hosts_with_resevered_capacity = new ConcurrentHashMap>(); for (HostVO h : hosts) { hosts_with_resevered_capacity.put(h.getUuid(), new Pair(h, 0)); @@ -1412,6 +1415,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne CONTROL.name(), controlNodeCount, ETCD.name(), etcdNodes); final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId()); final KubernetesSupportedVersion clusterKubernetesVersion = kubernetesSupportedVersionDao.findById(cmd.getKubernetesVersionId()); + final Hypervisor.HypervisorType hypervisor = cmd.getHypervisorType(); Map serviceOfferingNodeTypeMap = cmd.getServiceOfferingNodeTypeMap(); Long defaultServiceOfferingId = cmd.getServiceOfferingId(); @@ -1424,7 +1428,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne accountId = account.getId(); } } - Hypervisor.HypervisorType hypervisorType = getHypervisorTypeAndValidateNodeDeployments(serviceOfferingNodeTypeMap, defaultServiceOfferingId, nodeTypeCount, zone, domainId, accountId); + Hypervisor.HypervisorType hypervisorType = getHypervisorTypeAndValidateNodeDeployments(serviceOfferingNodeTypeMap, defaultServiceOfferingId, nodeTypeCount, zone, domainId, accountId, hypervisor); SecurityGroup securityGroup = null; if (zone.isSecurityGroupEnabled()) { @@ -1510,8 +1514,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne protected Hypervisor.HypervisorType getHypervisorTypeAndValidateNodeDeployments(Map serviceOfferingNodeTypeMap, Long defaultServiceOfferingId, Map nodeTypeCount, - DataCenter zone, Long domainId, Long accountId) { - Hypervisor.HypervisorType hypervisorType = null; + DataCenter zone, Long domainId, Long accountId, + Hypervisor.HypervisorType hypervisorType) { + Hypervisor.HypervisorType deploymentHypervisor = null; for (String nodeType : CLUSTER_NODES_TYPES_LIST) { if (!nodeTypeCount.containsKey(nodeType)) { continue; @@ -1524,18 +1529,23 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne (!serviceOfferingNodeTypeMap.containsKey(ETCD.name()) || nodes == 0)) { continue; } - DeployDestination deployDestination = plan(nodes, zone, serviceOffering, domainId, accountId); + DeployDestination deployDestination = plan(nodes, zone, serviceOffering, domainId, accountId, hypervisorType); if (deployDestination.getCluster() == null) { logAndThrow(Level.ERROR, String.format("Creating Kubernetes cluster failed due to error while finding suitable deployment plan for cluster in zone : %s", zone.getName())); } - if (hypervisorType == null) { - hypervisorType = deployDestination.getCluster().getHypervisorType(); + if (deploymentHypervisor == null) { + deploymentHypervisor = deployDestination.getCluster().getHypervisorType(); + if (hypervisorType != deploymentHypervisor) { + String msg = String.format("The hypervisor type planned for the CKS cluster deployment %s is different " + + "from the selected hypervisor %s", deployDestination.getCluster().getHypervisorType(), hypervisorType); + LOGGER.warn(msg); + } } } catch (InsufficientCapacityException e) { logAndThrow(Level.ERROR, String.format("Creating Kubernetes cluster failed due to insufficient capacity for %d nodes cluster in zone : %s with service offering : %s", nodes, zone.getName(), serviceOffering.getName())); } } - return hypervisorType; + return deploymentHypervisor; } private SecurityGroup getOrCreateSecurityGroupForAccount(Account owner) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 07195b04c1a..fcd04716cc3 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -174,7 +174,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu } protected DeployDestination plan(final long nodesCount, final DataCenter zone, final ServiceOffering offering, - final Long domainId, final Long accountId) throws InsufficientServerCapacityException { + final Long domainId, final Long accountId, final Hypervisor.HypervisorType hypervisorType) throws InsufficientServerCapacityException { final int cpu_requested = offering.getCpu() * offering.getSpeed(); final long ram_requested = offering.getRamSize() * 1024L * 1024L; boolean useDedicatedHosts = false; @@ -195,6 +195,9 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu if (hosts.isEmpty()) { hosts = resourceManager.listAllHostsInOneZoneByType(Host.Type.Routing, zone.getId()); } + if (hypervisorType != null) { + hosts = hosts.stream().filter(x -> x.getHypervisorType() == hypervisorType).collect(Collectors.toList()); + } final Map> hosts_with_resevered_capacity = new ConcurrentHashMap>(); for (HostVO h : hosts) { hosts_with_resevered_capacity.put(h.getUuid(), new Pair(h, 0)); @@ -257,13 +260,13 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId()); } - protected DeployDestination plan(Long domainId, Long accountId) throws InsufficientServerCapacityException { + protected DeployDestination plan(Long domainId, Long accountId, Hypervisor.HypervisorType hypervisorType) throws InsufficientServerCapacityException { ServiceOffering offering = serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId()); DataCenter zone = dataCenterDao.findById(kubernetesCluster.getZoneId()); if (logger.isDebugEnabled()) { logger.debug(String.format("Checking deployment destination for Kubernetes cluster : %s in zone : %s", kubernetesCluster.getName(), zone.getName())); } - return plan(kubernetesCluster.getTotalNodeCount(), zone, offering, domainId, accountId); + return plan(kubernetesCluster.getTotalNodeCount(), zone, offering, domainId, accountId, hypervisorType); } protected void resizeNodeVolume(final UserVm vm) throws ManagementServerException { @@ -300,7 +303,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu if (Objects.nonNull(domainId) && !listDedicatedHostsInDomain(domainId).isEmpty()) { DeployDestination dest = null; try { - dest = plan(domainId, accountId); + dest = plan(domainId, accountId, vm.getHypervisorType()); } catch (InsufficientCapacityException e) { logTransitStateAndThrow(Level.ERROR, String.format("Provisioning the cluster failed due to insufficient capacity in the Kubernetes cluster: %s", kubernetesCluster.getUuid()), kubernetesCluster.getId(), KubernetesCluster.Event.CreateFailed, e); } diff --git 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 index 61d54e7dcf7..c8e13328333 100644 --- 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 @@ -30,6 +30,7 @@ import javax.inject.Inject; import com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType; import com.cloud.service.ServiceOfferingVO; +import com.cloud.storage.VMTemplateVO; import org.apache.cloudstack.api.InternalIdentity; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -331,11 +332,12 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif } if (newVmRequiredCount > 0) { final DataCenter zone = dataCenterDao.findById(kubernetesCluster.getZoneId()); + VMTemplateVO clusterTemplate = templateDao.findById(kubernetesCluster.getTemplateId()); try { if (originalState.equals(KubernetesCluster.State.Running)) { - plan(newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId()); + plan(newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType()); } else { - plan(kubernetesCluster.getTotalNodeCount() + newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId()); + plan(kubernetesCluster.getTotalNodeCount() + newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType()); } } catch (InsufficientCapacityException e) { logTransitStateToFailedIfNeededAndThrow(Level.WARN, String.format("Scaling failed for Kubernetes cluster : %s in zone : %s, insufficient capacity", kubernetesCluster.getName(), zone.getName())); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index e2949aaec37..855c4446a1e 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.stream.Collectors; import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.storage.VMTemplateVO; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.framework.ca.Certificate; @@ -685,7 +686,8 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif stateTransitTo(kubernetesCluster.getId(), KubernetesCluster.Event.StartRequested); DeployDestination dest = null; try { - dest = plan(domainId, accountId); + VMTemplateVO clusterTemplate = templateDao.findById(kubernetesCluster.getTemplateId()); + dest = plan(domainId, accountId, clusterTemplate.getHypervisorType()); } catch (InsufficientCapacityException e) { logTransitStateAndThrow(Level.ERROR, String.format("Provisioning the cluster failed due to insufficient capacity in the Kubernetes cluster: %s", kubernetesCluster.getUuid()), kubernetesCluster.getId(), KubernetesCluster.Event.CreateFailed, e); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java index f3e557579f2..0c5bca9405d 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java +++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java @@ -25,6 +25,7 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ManagementServerException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; import com.cloud.kubernetes.cluster.KubernetesClusterHelper; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -170,6 +171,9 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, description = "type of the cluster: CloudManaged, ExternalManaged. The default value is CloudManaged.", since="4.19.0") private String clusterType; + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "(optional) the hypervisor on which to deploy the CKS cluster nodes.") + private String hypervisor; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -278,6 +282,10 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd { return kubernetesClusterHelper.getTemplateNodeTypeMap(templateNodeTypeMap); } + public Hypervisor.HypervisorType getHypervisorType() { + return hypervisor == null ? null : Hypervisor.HypervisorType.getType(hypervisor); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/ui/src/views/compute/CreateKubernetesCluster.vue b/ui/src/views/compute/CreateKubernetesCluster.vue index 12e49f955d5..4f11816089b 100644 --- a/ui/src/views/compute/CreateKubernetesCluster.vue +++ b/ui/src/views/compute/CreateKubernetesCluster.vue @@ -65,6 +65,25 @@ + + + + :filterOption="(input, option) => { + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }" > + + {{ opt.name || opt.description }} + + +