This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.18 by this push:
new d0f3233fda3 edge-zone,kvm,iso,cks: allow k8s deployment with
direct-download iso (#8142)
d0f3233fda3 is described below
commit d0f3233fda33dbd3a7e2fd52dd3d42831cc1f1d4
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Nov 10 18:26:05 2023 +0530
edge-zone,kvm,iso,cks: allow k8s deployment with direct-download iso (#8142)
Signed-off-by: Abhishek Kumar <[email protected]>
---
.../api/command/user/iso/RegisterIsoCmd.java | 7 +++
.../storage/image/TemplateDataFactoryImpl.java | 49 +++++++++---------
.../kvm/storage/KVMStoragePoolManager.java | 6 ++-
.../kvm/storage/KVMStorageProcessor.java | 59 +++++++++++++---------
.../cluster/KubernetesClusterManagerImpl.java | 15 +++++-
.../version/KubernetesVersionManagerImpl.java | 20 ++++++--
.../version/AddKubernetesSupportedVersionCmd.java | 10 +++-
.../KubernetesSupportedVersionResponse.java | 8 +++
.../com/cloud/network/router/NetworkHelper.java | 3 ++
.../cloud/network/router/NetworkHelperImpl.java | 7 ++-
ui/src/views/compute/CreateKubernetesCluster.vue | 8 ++-
.../views/image/AddKubernetesSupportedVersion.vue | 56 +++++++++++++++-----
12 files changed, 174 insertions(+), 74 deletions(-)
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
index 47018b3b38d..bdb51e849e6 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
@@ -177,6 +177,9 @@ public class RegisterIsoCmd extends BaseCmd implements
UserCmd {
}
public Long getZoneId() {
+ if (zoneId == null || zoneId == -1) {
+ return null;
+ }
return zoneId;
}
@@ -220,6 +223,10 @@ public class RegisterIsoCmd extends BaseCmd implements
UserCmd {
return directDownload == null ? false : directDownload;
}
+ public void setDirectDownload(Boolean directDownload) {
+ this.directDownload = directDownload;
+ }
+
public boolean isPasswordEnabled() {
return passwordEnabled == null ? false : passwordEnabled;
}
diff --git
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
index 492ec74382b..8951b9d7c24 100644
---
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
+++
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
@@ -20,11 +20,10 @@ package org.apache.cloudstack.storage.image;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
import javax.inject.Inject;
-import com.cloud.hypervisor.Hypervisor;
-import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.direct.download.DirectDownloadManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@@ -36,18 +35,21 @@ import
org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.image.store.TemplateObject;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplatePoolDao;
+import com.cloud.utils.exception.CloudRuntimeException;
@Component
public class TemplateDataFactoryImpl implements TemplateDataFactory {
@@ -203,12 +205,7 @@ public class TemplateDataFactoryImpl implements
TemplateDataFactory {
* Given existing spool refs, return one pool id existing on pools and refs
*/
private Long getOneMatchingPoolIdFromRefs(List<VMTemplateStoragePoolVO>
existingRefs, List<StoragePoolVO> pools) {
- if (pools.isEmpty()) {
- throw new CloudRuntimeException("No storage pools found");
- }
- if (existingRefs.isEmpty()) {
- return pools.get(0).getId();
- } else {
+ if (!existingRefs.isEmpty()) {
for (VMTemplateStoragePoolVO ref : existingRefs) {
for (StoragePoolVO p : pools) {
if (ref.getPoolId() == p.getId()) {
@@ -217,45 +214,51 @@ public class TemplateDataFactoryImpl implements
TemplateDataFactory {
}
}
}
- return null;
+ return pools.get(0).getId();
}
/**
- * Retrieve storage pools with scope = cluster or zone matching clusterId
or dataCenterId depending on their scope
+ * Retrieve storage pools with scope = cluster or zone or local matching
clusterId or dataCenterId or hostId depending on their scope
*/
- private List<StoragePoolVO> getStoragePoolsFromClusterOrZone(Long
clusterId, long dataCenterId, Hypervisor.HypervisorType hypervisorType) {
+ private List<StoragePoolVO> getStoragePoolsForScope(long dataCenterId,
Long clusterId, long hostId, Hypervisor.HypervisorType hypervisorType) {
List<StoragePoolVO> pools = new ArrayList<>();
if (clusterId != null) {
List<StoragePoolVO> clusterPools =
primaryDataStoreDao.listPoolsByCluster(clusterId);
+ clusterPools = clusterPools.stream().filter(p ->
!p.isLocal()).collect(Collectors.toList());
pools.addAll(clusterPools);
}
List<StoragePoolVO> zonePools =
primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(dataCenterId,
hypervisorType);
pools.addAll(zonePools);
+ List<StoragePoolVO> localPools =
primaryDataStoreDao.findLocalStoragePoolsByHostAndTags(hostId, null);
+ pools.addAll(localPools);
return pools;
}
+ protected Long getBypassedTemplateExistingOrNewPoolId(VMTemplateVO
templateVO, Long hostId) {
+ HostVO host = hostDao.findById(hostId);
+ List<StoragePoolVO> pools =
getStoragePoolsForScope(host.getDataCenterId(), host.getClusterId(), hostId,
host.getHypervisorType());
+ if (CollectionUtils.isEmpty(pools)) {
+ throw new CloudRuntimeException(String.format("No storage pool
found to download template: %s", templateVO.getName()));
+ }
+ List<VMTemplateStoragePoolVO> existingRefs =
templatePoolDao.listByTemplateId(templateVO.getId());
+ return getOneMatchingPoolIdFromRefs(existingRefs, pools);
+ }
+
@Override
public TemplateInfo getReadyBypassedTemplateOnPrimaryStore(long
templateId, Long poolId, Long hostId) {
VMTemplateVO templateVO = imageDataDao.findById(templateId);
if (templateVO == null || !templateVO.isDirectDownload()) {
return null;
}
- Long pool = poolId;
+ Long templatePoolId = poolId;
if (poolId == null) {
- //Get ISO from existing pool ref
- HostVO host = hostDao.findById(hostId);
- List<StoragePoolVO> pools =
getStoragePoolsFromClusterOrZone(host.getClusterId(), host.getDataCenterId(),
host.getHypervisorType());
- List<VMTemplateStoragePoolVO> existingRefs =
templatePoolDao.listByTemplateId(templateId);
- pool = getOneMatchingPoolIdFromRefs(existingRefs, pools);
- }
- if (pool == null) {
- throw new CloudRuntimeException("No storage pool found where to
download template: " + templateId);
+ templatePoolId =
getBypassedTemplateExistingOrNewPoolId(templateVO, hostId);
}
- VMTemplateStoragePoolVO spoolRef =
templatePoolDao.findByPoolTemplate(pool, templateId, null);
+ VMTemplateStoragePoolVO spoolRef =
templatePoolDao.findByPoolTemplate(templatePoolId, templateId, null);
if (spoolRef == null) {
- directDownloadManager.downloadTemplate(templateId, pool, hostId);
+ directDownloadManager.downloadTemplate(templateId, templatePoolId,
hostId);
}
- DataStore store = storeMgr.getDataStore(pool, DataStoreRole.Primary);
+ DataStore store = storeMgr.getDataStore(templatePoolId,
DataStoreRole.Primary);
return this.getTemplate(templateId, store);
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
index bfaa799e134..79c7e2a488a 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
@@ -294,12 +294,14 @@ public class KVMStoragePoolManager {
String uuid = null;
String sourceHost = "";
StoragePoolType protocol = null;
- if (storageUri.getScheme().equalsIgnoreCase("nfs") ||
storageUri.getScheme().equalsIgnoreCase("NetworkFilesystem")) {
+ final String scheme = storageUri.getScheme().toLowerCase();
+ List<String> acceptedSchemes = List.of("nfs", "networkfilesystem",
"filesystem");
+ if (acceptedSchemes.contains(scheme)) {
sourcePath = storageUri.getPath();
sourcePath = sourcePath.replace("//", "/");
sourceHost = storageUri.getHost();
uuid = UUID.nameUUIDFromBytes(new String(sourceHost +
sourcePath).getBytes()).toString();
- protocol = StoragePoolType.NetworkFilesystem;
+ protocol = scheme.equals("filesystem") ?
StoragePoolType.Filesystem: StoragePoolType.NetworkFilesystem;
}
// secondary storage registers itself through here
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index f7ec09ca50f..dd31025d35f 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -25,23 +25,26 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
import javax.naming.ConfigurationException;
-import org.apache.cloudstack.direct.download.DirectDownloadHelper;
-import org.apache.cloudstack.direct.download.DirectTemplateDownloader;
-import com.cloud.storage.ScopeType;
-import com.cloud.storage.Volume;
import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
+import org.apache.cloudstack.direct.download.DirectDownloadHelper;
+import org.apache.cloudstack.direct.download.DirectTemplateDownloader;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.storage.command.AttachAnswer;
import org.apache.cloudstack.storage.command.AttachCommand;
@@ -71,7 +74,10 @@ import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.cloudstack.utils.qemu.QemuObject;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
-
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.Domain;
@@ -110,9 +116,11 @@ import
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.DiskProtocol;
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtUtilitiesHelper;
import com.cloud.storage.JavaStorageLayer;
import com.cloud.storage.MigrationOptions;
+import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StorageLayer;
+import com.cloud.storage.Volume;
import com.cloud.storage.resource.StorageProcessor;
import com.cloud.storage.template.Processor;
import com.cloud.storage.template.Processor.FormatInfo;
@@ -126,16 +134,6 @@ import com.cloud.utils.script.Script;
import com.cloud.utils.storage.S3.S3Utils;
import com.cloud.vm.VmDetailConstants;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
public class KVMStorageProcessor implements StorageProcessor {
private static final Logger s_logger =
Logger.getLogger(KVMStorageProcessor.class);
private final KVMStoragePoolManager storagePoolMgr;
@@ -1074,7 +1072,8 @@ public class KVMStorageProcessor implements
StorageProcessor {
s_logger.debug(String.format("This backup is temporary, not
deleting snapshot [%s] on primary storage [%s]", snapshotPath,
primaryPool.getUuid()));
}
}
- protected synchronized void attachOrDetachISO(final Connect conn, final
String vmName, String isoPath, final boolean isAttach, Map<String, String>
params) throws
+
+ protected synchronized void attachOrDetachISO(final Connect conn, final
String vmName, String isoPath, final boolean isAttach, Map<String, String>
params, DataStoreTO store) throws
LibvirtException, InternalErrorException {
DiskDef iso = new DiskDef();
boolean isUefiEnabled = MapUtils.isNotEmpty(params) &&
params.containsKey("UEFI");
@@ -1082,8 +1081,14 @@ public class KVMStorageProcessor implements
StorageProcessor {
final int index = isoPath.lastIndexOf("/");
final String path = isoPath.substring(0, index);
final String name = isoPath.substring(index + 1);
- final KVMStoragePool secondaryPool =
storagePoolMgr.getStoragePoolByURI(path);
- final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
+ KVMStoragePool storagePool;
+ if (store instanceof PrimaryDataStoreTO) {
+ PrimaryDataStoreTO primaryDataStoreTO =
(PrimaryDataStoreTO)store;
+ storagePool =
storagePoolMgr.getStoragePool(primaryDataStoreTO.getPoolType(),
store.getUuid());
+ } else {
+ storagePool = storagePoolMgr.getStoragePoolByURI(path);
+ }
+ final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
isoPath = isoVol.getPath();
iso.defISODisk(isoPath, isUefiEnabled);
@@ -1112,7 +1117,7 @@ public class KVMStorageProcessor implements
StorageProcessor {
try {
String dataStoreUrl = getDataStoreUrlFromStore(store);
final Connect conn =
LibvirtConnection.getConnectionByVmName(cmd.getVmName());
- attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl +
File.separator + isoTO.getPath(), true, cmd.getControllerInfo());
+ attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl +
File.separator + isoTO.getPath(), true, cmd.getControllerInfo(), store);
} catch (final LibvirtException e) {
return new Answer(cmd, false, e.toString());
} catch (final InternalErrorException e) {
@@ -1133,7 +1138,7 @@ public class KVMStorageProcessor implements
StorageProcessor {
try {
String dataStoreUrl = getDataStoreUrlFromStore(store);
final Connect conn =
LibvirtConnection.getConnectionByVmName(cmd.getVmName());
- attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl +
File.separator + isoTO.getPath(), false, cmd.getParams());
+ attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl +
File.separator + isoTO.getPath(), false, cmd.getParams(), store);
} catch (final LibvirtException e) {
return new Answer(cmd, false, e.toString());
} catch (final InternalErrorException e) {
@@ -1149,19 +1154,25 @@ public class KVMStorageProcessor implements
StorageProcessor {
* Return data store URL from store
*/
private String getDataStoreUrlFromStore(DataStoreTO store) {
- if (!(store instanceof NfsTO) && (!(store instanceof
PrimaryDataStoreTO) ||
- store instanceof PrimaryDataStoreTO && !((PrimaryDataStoreTO)
store).getPoolType().equals(StoragePoolType.NetworkFilesystem))) {
+ List<StoragePoolType> supportedPoolType =
List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.Filesystem);
+ if (!(store instanceof NfsTO) && (!(store instanceof
PrimaryDataStoreTO) || !supportedPoolType.contains(((PrimaryDataStoreTO)
store).getPoolType()))) {
+ s_logger.error(String.format("Unsupported protocol, store: %s",
store.getUuid()));
throw new InvalidParameterValueException("unsupported protocol");
}
if (store instanceof NfsTO) {
NfsTO nfsStore = (NfsTO)store;
return nfsStore.getUrl();
- } else if (store instanceof PrimaryDataStoreTO &&
((PrimaryDataStoreTO)
store).getPoolType().equals(StoragePoolType.NetworkFilesystem)) {
+ } else if (store instanceof PrimaryDataStoreTO) {
//In order to support directly downloaded ISOs
+ StoragePoolType poolType =
((PrimaryDataStoreTO)store).getPoolType();
String psHost = ((PrimaryDataStoreTO) store).getHost();
String psPath = ((PrimaryDataStoreTO) store).getPath();
- return "nfs://" + psHost + File.separator + psPath;
+ if (StoragePoolType.NetworkFilesystem.equals(poolType)) {
+ return "nfs://" + psHost + File.separator + psPath;
+ } else if (StoragePoolType.Filesystem.equals(poolType)) {
+ return StoragePoolType.Filesystem.toString().toLowerCase() +
"://" + psHost + File.separator + psPath;
+ }
}
return store.getUrl();
}
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 f0fa335d22c..41ad7981e5d 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
@@ -118,6 +118,7 @@ import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PhysicalNetworkDao;
+import com.cloud.network.router.NetworkHelper;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.security.SecurityGroupManager;
@@ -243,6 +244,8 @@ public class KubernetesClusterManagerImpl extends
ManagerBase implements Kuberne
private SecurityGroupManager securityGroupManager;
@Inject
public SecurityGroupService securityGroupService;
+ @Inject
+ public NetworkHelper networkHelper;
private void logMessage(final Level logLevel, final String message, final
Exception e) {
if (logLevel == Level.WARN) {
@@ -347,8 +350,12 @@ public class KubernetesClusterManagerImpl extends
ManagerBase implements Kuberne
public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter,
Hypervisor.HypervisorType hypervisorType) {
VMTemplateVO template =
templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType);
+ if (DataCenter.Type.Edge.equals(dataCenter.getType()) && template !=
null && !template.isDirectDownload()) {
+ LOGGER.debug(String.format("Template %s can not be used for edge
zone %s", template, dataCenter));
+ template = templateDao.findRoutingTemplate(hypervisorType,
networkHelper.getHypervisorRouterTemplateConfigMap().get(hypervisorType).valueIn(dataCenter.getId()));
+ }
if (template == null) {
- throw new CloudRuntimeException("Not able to find the System
templates or not downloaded in zone " + dataCenter.getId());
+ throw new CloudRuntimeException("Not able to find the System or
Routing template in ready state for the zone " + dataCenter.getUuid());
}
return template;
}
@@ -650,7 +657,11 @@ public class KubernetesClusterManagerImpl extends
ManagerBase implements Kuberne
}
if (Grouping.AllocationState.Disabled == zone.getAllocationState()) {
- throw new PermissionDeniedException(String.format("Cannot perform
this operation, zone ID: %s is currently disabled", zone.getUuid()));
+ throw new PermissionDeniedException(String.format("Zone ID: %s is
currently disabled", zone.getUuid()));
+ }
+
+ if (DataCenter.Type.Edge.equals(zone.getType()) && networkId == null) {
+ throw new PermissionDeniedException("Kubernetes clusters cannot be
created on an edge zone without an existing network");
}
if (!isKubernetesServiceConfigured(zone)) {
diff --git
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
index 643044f78d8..3ea30291f43 100644
---
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
+++
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
@@ -31,10 +31,12 @@ import
org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
import
org.apache.cloudstack.api.command.user.kubernetes.version.ListKubernetesSupportedVersionsCmd;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO;
+import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.event.ActionEvent;
@@ -56,7 +58,6 @@ import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException;
-import org.apache.commons.lang3.StringUtils;
public class KubernetesVersionManagerImpl extends ManagerBase implements
KubernetesVersionService {
public static final Logger LOGGER =
Logger.getLogger(KubernetesVersionManagerImpl.class.getName());
@@ -104,6 +105,7 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
response.setIsoId(template.getUuid());
response.setIsoName(template.getName());
response.setIsoState(template.getState().toString());
+ response.setDirectDownload(template.isDirectDownload());
}
response.setCreated(kubernetesSupportedVersion.getCreated());
return response;
@@ -147,7 +149,7 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
return versions;
}
- private VirtualMachineTemplate registerKubernetesVersionIso(final Long
zoneId, final String versionName, final String isoUrl, final String
isoChecksum)throws IllegalAccessException, NoSuchFieldException,
+ private VirtualMachineTemplate registerKubernetesVersionIso(final Long
zoneId, final String versionName, final String isoUrl, final String
isoChecksum, final boolean directDownload) throws IllegalAccessException,
NoSuchFieldException,
IllegalArgumentException, ResourceAllocationException {
String isoName = String.format("%s-Kubernetes-Binaries-ISO",
versionName);
RegisterIsoCmd registerIsoCmd = new RegisterIsoCmd();
@@ -163,6 +165,7 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
if (StringUtils.isNotEmpty(isoChecksum)) {
registerIsoCmd.setChecksum(isoChecksum);
}
+ registerIsoCmd.setDirectDownload(directDownload);
registerIsoCmd.setAccountName(accountManager.getSystemAccount().getAccountName());
registerIsoCmd.setDomainId(accountManager.getSystemAccount().getDomainId());
return templateService.registerIso(registerIsoCmd);
@@ -288,6 +291,7 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
final String isoChecksum = cmd.getChecksum();
final Integer minimumCpu = cmd.getMinimumCpu();
final Integer minimumRamSize = cmd.getMinimumRamSize();
+ final boolean isDirectDownload = cmd.isDirectDownload();
if (minimumCpu == null || minimumCpu <
KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU) {
throw new InvalidParameterValueException(String.format("Invalid
value for %s parameter. Minimum %d vCPUs required.",
ApiConstants.MIN_CPU_NUMBER,
KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU));
}
@@ -297,8 +301,14 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
if (compareSemanticVersions(semanticVersion, MIN_KUBERNETES_VERSION) <
0) {
throw new InvalidParameterValueException(String.format("New
supported Kubernetes version cannot be added as %s is minimum version supported
by Kubernetes Service", MIN_KUBERNETES_VERSION));
}
- if (zoneId != null && dataCenterDao.findById(zoneId) == null) {
- throw new InvalidParameterValueException("Invalid zone specified");
+ if (zoneId != null) {
+ DataCenter zone = dataCenterDao.findById(zoneId);
+ if (zone == null) {
+ throw new InvalidParameterValueException("Invalid zone
specified");
+ }
+ if (DataCenter.Type.Edge.equals(zone.getType()) &&
!isDirectDownload) {
+ throw new InvalidParameterValueException(String.format("Zone:
%s supports only direct download Kubernetes versions", zone.getName()));
+ }
}
if (StringUtils.isEmpty(isoUrl)) {
throw new InvalidParameterValueException(String.format("Invalid
URL for ISO specified, %s", isoUrl));
@@ -312,7 +322,7 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
VMTemplateVO template = null;
try {
- VirtualMachineTemplate vmTemplate =
registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum);
+ VirtualMachineTemplate vmTemplate =
registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum,
isDirectDownload);
template = templateDao.findById(vmTemplate.getId());
} catch (IllegalAccessException | NoSuchFieldException |
IllegalArgumentException | ResourceAllocationException ex) {
LOGGER.error(String.format("Unable to register binaries ISO for
supported kubernetes version, %s, with url: %s", name, isoUrl), ex);
diff --git
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
index 48bb26c8d44..380c93cca20 100644
---
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
+++
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
@@ -31,6 +31,7 @@ import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.exception.ConcurrentOperationException;
@@ -38,7 +39,6 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
import com.cloud.kubernetes.version.KubernetesVersionService;
import com.cloud.utils.exception.CloudRuntimeException;
-import org.apache.commons.lang3.StringUtils;
@APICommand(name = "addKubernetesSupportedVersion",
description = "Add a supported Kubernetes version",
@@ -84,6 +84,10 @@ public class AddKubernetesSupportedVersionCmd extends
BaseCmd implements AdminCm
description = "the minimum RAM size in MB to be set with the
Kubernetes version")
private Integer minimumRamSize;
+ @Parameter(name=ApiConstants.DIRECT_DOWNLOAD, type = CommandType.BOOLEAN,
since="4.18.2",
+ description = "If set to true the Kubernetes supported version ISO
will bypass Secondary Storage and be downloaded to Primary Storage on
deployment. Default is false")
+ private Boolean directDownload;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -123,6 +127,10 @@ public class AddKubernetesSupportedVersionCmd extends
BaseCmd implements AdminCm
return minimumRamSize;
}
+ public boolean isDirectDownload() {
+ return (directDownload != null) ? directDownload : false;
+ }
+
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccountId();
diff --git
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesSupportedVersionResponse.java
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesSupportedVersionResponse.java
index 72a52682364..188328ac008 100644
---
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesSupportedVersionResponse.java
+++
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesSupportedVersionResponse.java
@@ -86,6 +86,10 @@ public class KubernetesSupportedVersionResponse extends
BaseResponse {
@Param(description = "the date when this Kubernetes supported version was
created")
private Date created;
+ @SerializedName(ApiConstants.DIRECT_DOWNLOAD)
+ @Param(description = "KVM Only: true if the ISO for the Kubernetes
supported version is directly downloaded to Primary Storage bypassing Secondary
Storage", since = "4.18.2")
+ private Boolean directDownload;
+
public String getId() {
return id;
}
@@ -193,4 +197,8 @@ public class KubernetesSupportedVersionResponse extends
BaseResponse {
public void setCreated(Date created) {
this.created = created;
}
+
+ public void setDirectDownload(Boolean directDownload) {
+ this.directDownload = directDownload;
+ }
}
diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelper.java
b/server/src/main/java/com/cloud/network/router/NetworkHelper.java
index ea008e4c4ca..c9daa5eedb4 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelper.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelper.java
@@ -20,6 +20,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import org.apache.cloudstack.framework.config.ConfigKey;
import
org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import com.cloud.agent.api.to.NicTO;
@@ -93,4 +94,6 @@ public interface NetworkHelper {
throws ConcurrentOperationException,
InsufficientAddressCapacityException;
public boolean validateHAProxyLBRule(final LoadBalancingRule rule);
+
+ public Map<HypervisorType, ConfigKey<String>>
getHypervisorRouterTemplateConfigMap();
}
diff --git
a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index 56860499ae0..f0fa50bb913 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -28,7 +28,6 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
-import com.cloud.utils.validation.ChecksumUtil;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.context.CallContext;
import
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -102,6 +101,7 @@ import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
+import com.cloud.utils.validation.ChecksumUtil;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
@@ -968,4 +968,9 @@ public class NetworkHelperImpl implements NetworkHelper {
public String acquireGuestIpAddressForVrouterRedundant(Network network) {
return _ipAddrMgr.acquireGuestIpAddressByPlacement(network, null);
}
+
+ @Override
+ public Map<HypervisorType, ConfigKey<String>>
getHypervisorRouterTemplateConfigMap() {
+ return hypervisorsMap;
+ }
}
diff --git a/ui/src/views/compute/CreateKubernetesCluster.vue
b/ui/src/views/compute/CreateKubernetesCluster.vue
index 9ca7b0aa7c8..1c70dece6e1 100644
--- a/ui/src/views/compute/CreateKubernetesCluster.vue
+++ b/ui/src/views/compute/CreateKubernetesCluster.vue
@@ -343,7 +343,6 @@ export default {
const listZones = json.listzonesresponse.zone
if (listZones) {
this.zones = this.zones.concat(listZones)
- this.zones = this.zones.filter(zone => zone.type !== 'Edge')
}
}).finally(() => {
this.zoneLoading = false
@@ -356,6 +355,7 @@ export default {
handleZoneChange (zone) {
this.selectedZone = zone
this.fetchKubernetesVersionData()
+ this.fetchNetworkData()
},
fetchKubernetesVersionData () {
this.kubernetesVersions = []
@@ -414,10 +414,14 @@ export default {
},
fetchNetworkData () {
const params = {}
+ if (!this.isObjectEmpty(this.selectedZone)) {
+ params.zoneid = this.selectedZone.id
+ }
this.networkLoading = true
api('listNetworks', params).then(json => {
- const listNetworks = json.listnetworksresponse.network
+ var listNetworks = json.listnetworksresponse.network
if (this.arrayHasItems(listNetworks)) {
+ listNetworks = listNetworks.filter(n => n.type !== 'L2')
this.networks = this.networks.concat(listNetworks)
}
}).finally(() => {
diff --git a/ui/src/views/image/AddKubernetesSupportedVersion.vue
b/ui/src/views/image/AddKubernetesSupportedVersion.vue
index ad4a9490a37..c88fc34695d 100644
--- a/ui/src/views/image/AddKubernetesSupportedVersion.vue
+++ b/ui/src/views/image/AddKubernetesSupportedVersion.vue
@@ -54,7 +54,8 @@
return option.label.toLowerCase().indexOf(input.toLowerCase())
>= 0
}"
:loading="zoneLoading"
- :placeholder="apiParams.zoneid.description">
+ :placeholder="apiParams.zoneid.description"
+ @change="handleZoneChange">
<a-select-option v-for="(opt, optIndex) in this.zones"
:key="optIndex" :label="opt.name || opt.description">
<span>
<resource-icon v-if="opt.icon" :image="opt.icon.base64image"
size="1x" style="margin-right: 5px"/>
@@ -96,6 +97,15 @@
v-model:value="form.minmemory"
:placeholder="apiParams.minmemory.description"/>
</a-form-item>
+ <a-form-item ref="directdownload" name="directdownload">
+ <template #label>
+ <tooltip-label :title="$t('label.directdownload')"
:tooltip="apiParams.directdownload.description"/>
+ </template>
+ <a-switch
+ :disabled="directDownloadDisabled"
+ v-model:checked="form.directdownload"
+ :placeholder="apiParams.directdownload.description"/>
+ </a-form-item>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@@ -122,7 +132,10 @@ export default {
return {
zones: [],
zoneLoading: false,
- loading: false
+ loading: false,
+ selectedZone: {},
+ directDownloadDisabled: false,
+ lastNonEdgeDirectDownloadUserSelection: false
}
},
beforeCreate () {
@@ -143,7 +156,8 @@ export default {
this.formRef = ref()
this.form = reactive({
mincpunumber: 2,
- minmemory: 2048
+ minmemory: 2048,
+ directdownload: false
})
this.rules = reactive({
semanticversion: [{ required: true, message:
this.$t('message.error.kuberversion') }],
@@ -198,7 +212,6 @@ export default {
const listZones = json.listzonesresponse.zone
if (listZones) {
this.zones = this.zones.concat(listZones)
- this.zones = this.zones.filter(zone => zone.type !== 'Edge')
}
}).finally(() => {
this.zoneLoading = false
@@ -207,23 +220,38 @@ export default {
}
})
},
+ handleZoneChange (zoneIdx) {
+ const zone = this.zones[zoneIdx]
+ if (this.selectedZone.type === zone.type) {
+ return
+ }
+ var lastZoneType = this.selectedZone?.type || ''
+ if (lastZoneType !== 'Edge') {
+ this.nonEdgeDirectDownloadUserSelection = this.form.directdownload
+ }
+ this.selectedZone = zone
+ if (zone.type && zone.type === 'Edge') {
+ this.form.directdownload = true
+ this.directDownloadDisabled = true
+ return
+ }
+ this.directDownloadDisabled = false
+ this.form.directdownload = this.nonEdgeDirectDownloadUserSelection
+ },
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
this.formRef.value.validate().then(() => {
const values = toRaw(this.form)
this.loading = true
- const params = {
- semanticversion: values.semanticversion,
- url: values.url
- }
- if (this.isValidValueForKey(values, 'name')) {
- params.name = values.name
- }
- if (this.isValidValueForKey(values, 'checksum')) {
- params.checksum = values.checksum
+ const params = {}
+ const customCheckParams = ['mincpunumber', 'minmemory', 'zoneid']
+ for (const key in values) {
+ if (!customCheckParams.includes(key) && values[key]) {
+ params[key] = values[key]
+ }
}
- if (this.isValidValueForKey(values, 'zoneid')) {
+ if (this.isValidValueForKey(values, 'zoneid') && values.zoneid > 0) {
params.zoneid = this.zones[values.zoneid].id
}
if (this.isValidValueForKey(values, 'mincpunumber') &&
values.mincpunumber > 0) {