Add hypervisor type to CKS cluster creation to fix CKS cluster creation when External hosts added

This commit is contained in:
Nicolas Vazquez 2024-05-20 18:41:15 -03:00 committed by nvazquez
parent 44991773b9
commit 98389a116d
No known key found for this signature in database
GPG Key ID: 656E1BCC8CB54F84
6 changed files with 86 additions and 17 deletions

View File

@ -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<String, Pair<HostVO, Integer>> hosts_with_resevered_capacity = new ConcurrentHashMap<String, Pair<HostVO, Integer>>();
for (HostVO h : hosts) {
hosts_with_resevered_capacity.put(h.getUuid(), new Pair<HostVO, Integer>(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<String, Long> 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<String, Long> serviceOfferingNodeTypeMap,
Long defaultServiceOfferingId,
Map<String, Long> 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) {

View File

@ -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<String, Pair<HostVO, Integer>> hosts_with_resevered_capacity = new ConcurrentHashMap<String, Pair<HostVO, Integer>>();
for (HostVO h : hosts) {
hosts_with_resevered_capacity.put(h.getUuid(), new Pair<HostVO, Integer>(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);
}

View File

@ -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()));

View File

@ -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);
}

View File

@ -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///////////////////
/////////////////////////////////////////////////////

View File

@ -65,6 +65,25 @@
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="hypervisor" name="hypervisor">
<template #label>
<tooltip-label :title="$t('label.hypervisor')" :tooltip="apiParams.hypervisor.description"/>
</template>
<a-select
v-model:value="form.hypervisor"
:loading="hypervisorLoading"
:placeholder="apiParams.hypervisor.description"
showSearch
optionFilterProp="label"
@change="val => { handleZoneHypervisorChange(val) }">
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="(opt, optIndex) in selectedZoneHypervisors" :key="optIndex" :label="opt.name || opt.description">
{{ opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="kubernetesversionid" ref="kubernetesversionid">
<template #label>
<tooltip-label :title="$t('label.kubernetesversionid')" :tooltip="apiParams.kubernetesversionid.description"/>
@ -386,7 +405,9 @@ export default {
keyPairLoading: false,
loading: false,
templates: [],
templateLoading: false
templateLoading: false,
selectedZoneHypervisors: [],
hypervisorLoading: false
}
},
beforeCreate () {
@ -408,7 +429,8 @@ export default {
this.form = reactive({
controlnodes: 3,
size: 1,
noderootdisksize: 8
noderootdisksize: 8,
hypervisor: null
})
this.rules = reactive({
name: [{ required: true, message: this.$t('message.error.kubecluster.name') }],
@ -483,6 +505,7 @@ export default {
this.selectedZone = zone
this.fetchKubernetesVersionData()
this.fetchNetworkData()
this.fetchZoneHypervisors()
},
fetchKubernetesVersionData () {
this.kubernetesVersions = []
@ -595,6 +618,24 @@ export default {
}
})
},
fetchZoneHypervisors () {
const params = {
zoneid: this.selectedZone.id
}
this.hypervisorLoading = true
api('listHypervisors', params).then(json => {
const listResponse = json.listhypervisorsresponse.hypervisor || []
if (listResponse) {
this.selectedZoneHypervisors = listResponse
}
}).finally(() => {
this.hypervisorLoading = false
})
},
handleZoneHypervisorChange (index) {
this.form.hypervisor = index
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
@ -611,6 +652,9 @@ export default {
size: values.size,
clustertype: 'CloudManaged'
}
if (values.hypervisor !== null) {
params.hypervisor = this.selectedZoneHypervisors[values.hypervisor].name.toLowerCase()
}
var advancedOfferings = 0
if (this.isValidValueForKey(values, 'advancedmode') && values.advancedmode && this.isValidValueForKey(values, 'controlofferingid') && this.arrayHasItems(this.serviceOfferings) && this.serviceOfferings[values.controlofferingid].id != null) {
params['nodeofferings[' + advancedOfferings + '].node'] = 'control'