You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2023/11/16 15:43:49 UTC
(cloudstack) branch main updated: multi local storage handling for kvm (#6699)
This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 60017723357 multi local storage handling for kvm (#6699)
60017723357 is described below
commit 60017723357c5c5d3f81931d2b1be286aa986ad8
Author: DK101010 <57...@users.noreply.github.com>
AuthorDate: Thu Nov 16 16:43:42 2023 +0100
multi local storage handling for kvm (#6699)
Co-authored-by: DK101010 <di...@itelligence.de>
Co-authored-by: João Jandre <48...@users.noreply.github.com>
---
.../java/com/cloud/agent/api/StoragePoolInfo.java | 7 ++
.../api/command/admin/host/ListHostsCmd.java | 4 +
.../command/admin/storage/ListStoragePoolsCmd.java | 29 +++++-
.../CloudStackPrimaryDataStoreLifeCycleImpl.java | 83 +++++-----------
.../java/com/cloud/api/query/QueryManagerImpl.java | 27 ++++-
.../java/com/cloud/storage/StorageManagerImpl.java | 110 ++++++++++++++++++++-
ui/src/views/infra/AddPrimaryStorage.vue | 32 +++++-
7 files changed, 220 insertions(+), 72 deletions(-)
diff --git a/api/src/main/java/com/cloud/agent/api/StoragePoolInfo.java b/api/src/main/java/com/cloud/agent/api/StoragePoolInfo.java
index d923694a854..eb7e8813ecd 100644
--- a/api/src/main/java/com/cloud/agent/api/StoragePoolInfo.java
+++ b/api/src/main/java/com/cloud/agent/api/StoragePoolInfo.java
@@ -52,6 +52,13 @@ public class StoragePoolInfo {
this.details = details;
}
+ public StoragePoolInfo(String uuid, String host, String hostPath, String localPath, StoragePoolType poolType, long capacityBytes, long availableBytes,
+ Map<String, String> details, String name) {
+ this(uuid, host, hostPath, localPath, poolType, capacityBytes, availableBytes);
+ this.details = details;
+ this.name = name;
+ }
+
public long getCapacityBytes() {
return capacityBytes;
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java
index 75fe339b710..b8668f61ca4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java
@@ -119,6 +119,10 @@ public class ListHostsCmd extends BaseListCmd {
return id;
}
+ public void setId(Long id) {
+ this.id = id;
+ }
+
public String getHostName() {
return hostName;
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
index 209aaac279c..6923353b3bf 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
@@ -24,6 +24,7 @@ import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ClusterResponse;
+import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.StoragePoolResponse;
@@ -63,16 +64,25 @@ public class ListStoragePoolsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "the ID of the storage pool")
private Long id;
- @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "the ID of the storage pool")
+ @Parameter(name = ApiConstants.SCOPE, type = CommandType.STRING, entityType = StoragePoolResponse.class, description = "the scope of the storage pool")
private String scope;
+
@Parameter(name = ApiConstants.STATUS, type = CommandType.STRING, description = "the status of the storage pool")
private String status;
+ @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "host ID of the storage pools")
+ private Long hostId;
+
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
+ public Long getHostId() {
+ return hostId;
+ }
+
public Long getClusterId() {
return clusterId;
}
@@ -81,6 +91,10 @@ public class ListStoragePoolsCmd extends BaseListCmd {
return ipAddress;
}
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
public String getStoragePoolName() {
return storagePoolName;
}
@@ -108,6 +122,15 @@ public class ListStoragePoolsCmd extends BaseListCmd {
public void setId(Long id) {
this.id = id;
}
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@@ -123,8 +146,4 @@ public class ListStoragePoolsCmd extends BaseListCmd {
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
-
- public String getScope() {
- return scope;
- }
}
diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
index 213e5620553..6e178c119e3 100644
--- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
+++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
@@ -27,6 +27,7 @@ import com.cloud.agent.api.ValidateVcenterDetailsCommand;
import com.cloud.alert.AlertManager;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.StorageConflictException;
+import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
@@ -44,7 +45,6 @@ import com.cloud.storage.dao.StoragePoolWorkDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.UriUtils;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachineManager;
@@ -67,10 +67,6 @@ import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
import org.apache.log4j.Logger;
import javax.inject.Inject;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -138,64 +134,26 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
- UriUtils.UriInfo uriInfo = UriUtils.getUriInfo(url);
-
- String scheme = uriInfo.getScheme();
- String storageHost = uriInfo.getStorageHost();
- String storagePath = uriInfo.getStoragePath();
- try {
- if (scheme == null) {
- throw new InvalidParameterValueException("scheme is null " + url + ", add nfs:// (or cifs://) as a prefix");
- } else if (scheme.equalsIgnoreCase("nfs")) {
- if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) {
- throw new InvalidParameterValueException("host or path is null, should be nfs://hostname/path");
- }
- } else if (scheme.equalsIgnoreCase("cifs")) {
- // Don't validate against a URI encoded URI.
- URI cifsUri = new URI(url);
- String warnMsg = UriUtils.getCifsUriParametersProblems(cifsUri);
- if (warnMsg != null) {
- throw new InvalidParameterValueException(warnMsg);
- }
- } else if (scheme.equalsIgnoreCase("sharedMountPoint")) {
- if (storagePath == null) {
- throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path");
- }
- } else if (scheme.equalsIgnoreCase("rbd")) {
- if (storagePath == null) {
- throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool");
- }
- } else if (scheme.equalsIgnoreCase("gluster")) {
- if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) {
- throw new InvalidParameterValueException("host or path is null, should be gluster://hostname/volume");
- }
- }
- } catch (URISyntaxException e) {
- throw new InvalidParameterValueException(url + " is not a valid uri");
- }
-
String tags = (String)dsInfos.get("tags");
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
parameters.setTags(tags);
parameters.setDetails(details);
- String hostPath = null;
- try {
- hostPath = URLDecoder.decode(storagePath, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- s_logger.error("[ignored] we are on a platform not supporting \"UTF-8\"!?!", e);
- }
- if (hostPath == null) { // if decoding fails, use getPath() anyway
- hostPath = storagePath;
- }
+ String scheme = dsInfos.get("scheme").toString();
+ String storageHost = dsInfos.get("host").toString();
+ String hostPath = dsInfos.get("hostPath").toString();
+ String uri = String.format("%s://%s%s", scheme, storageHost, hostPath);
+
Object localStorage = dsInfos.get("localStorage");
- if (localStorage != null) {
- hostPath = hostPath.replaceFirst("/", "");
+ if (localStorage != null) {
+ hostPath = hostPath.contains("//") ? hostPath.replaceFirst("/", "") : hostPath;
hostPath = hostPath.replace("+", " ");
}
- String userInfo = uriInfo.getUserInfo();
- int port = uriInfo.getPort();
+
+ String userInfo = dsInfos.get("userInfo") != null ? dsInfos.get("userInfo").toString() : null;
+ int port = dsInfos.get("port") != null ? Integer.parseInt(dsInfos.get("port").toString()) : -1;
+
if (s_logger.isDebugEnabled()) {
s_logger.debug("createPool Params @ scheme - " + scheme + " storageHost - " + storageHost + " hostPath - " + hostPath + " port - " + port);
}
@@ -312,8 +270,8 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
parameters.setPort(0);
parameters.setPath(hostPath);
} else {
- s_logger.warn("Unable to figure out the scheme for URI: " + uriInfo);
- throw new IllegalArgumentException("Unable to figure out the scheme for URI: " + uriInfo);
+ s_logger.warn("Unable to figure out the scheme for URI: " + scheme);
+ throw new IllegalArgumentException("Unable to figure out the scheme for URI: " + scheme);
}
}
@@ -321,7 +279,7 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
List<StoragePoolVO> pools = primaryDataStoreDao.listPoolByHostPath(storageHost, hostPath);
if (!pools.isEmpty() && !scheme.equalsIgnoreCase("sharedmountpoint")) {
Long oldPodId = pools.get(0).getPodId();
- throw new CloudRuntimeException("Storage pool " + uriInfo + " already in use by another pod (id=" + oldPodId + ")");
+ throw new CloudRuntimeException("Storage pool " + hostPath + " already in use by another pod (id=" + oldPodId + ")");
}
}
@@ -550,7 +508,16 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
@Override
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
- dataStoreHelper.attachHost(store, scope, existingInfo);
+ DataStore dataStore = dataStoreHelper.attachHost(store, scope, existingInfo);
+ if(existingInfo.getCapacityBytes() == 0){
+ try {
+ storageMgr.connectHostToSharedPool(scope.getScopeId(), dataStore.getId());
+ } catch (StorageUnavailableException ex) {
+ s_logger.error("Storage unavailable ",ex);
+ } catch (StorageConflictException ex) {
+ s_logger.error("Storage already exists ",ex);
+ }
+ }
return true;
}
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index 0056be5fce9..7a7e88ef449 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -2793,10 +2793,29 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
@Override
public ListResponse<StoragePoolResponse> searchForStoragePools(ListStoragePoolsCmd cmd) {
- Pair<List<StoragePoolJoinVO>, Integer> result = searchForStoragePoolsInternal(cmd);
- ListResponse<StoragePoolResponse> response = new ListResponse<StoragePoolResponse>();
+ Pair<List<StoragePoolJoinVO>, Integer> result = cmd.getHostId() != null ? searchForLocalStorages(cmd) : searchForStoragePoolsInternal(cmd);
+ return createStoragesPoolResponse(result);
+ }
+
+ private Pair<List<StoragePoolJoinVO>, Integer> searchForLocalStorages(ListStoragePoolsCmd cmd) {
+ long id = cmd.getHostId();
+ String scope = ScopeType.HOST.toString();
+ Pair<List<StoragePoolJoinVO>, Integer> localStorages;
+
+ ListHostsCmd listHostsCmd = new ListHostsCmd();
+ listHostsCmd.setId(id);
+ Pair<List<HostJoinVO>, Integer> hosts = searchForServersInternal(listHostsCmd);
+
+ cmd.setScope(scope);
+ localStorages = searchForStoragePoolsInternal(cmd);
- List<StoragePoolResponse> poolResponses = ViewResponseHelper.createStoragePoolResponse(result.first().toArray(new StoragePoolJoinVO[result.first().size()]));
+ return localStorages;
+ }
+
+ private ListResponse<StoragePoolResponse> createStoragesPoolResponse(Pair<List<StoragePoolJoinVO>, Integer> storagePools) {
+ ListResponse<StoragePoolResponse> response = new ListResponse<>();
+
+ List<StoragePoolResponse> poolResponses = ViewResponseHelper.createStoragePoolResponse(storagePools.first().toArray(new StoragePoolJoinVO[storagePools.first().size()]));
for (StoragePoolResponse poolResponse : poolResponses) {
DataStore store = dataStoreManager.getPrimaryDataStore(poolResponse.getId());
if (store != null) {
@@ -2816,7 +2835,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
}
}
- response.setResponses(poolResponses, result.second());
+ response.setResponses(poolResponses, storagePools.second());
return response;
}
diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 82a6c159565..494855b253d 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -237,6 +237,9 @@ import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.common.collect.Sets;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
@Component
public class StorageManagerImpl extends ManagerBase implements StorageManager, ClusterManagerListener, Configurable {
@@ -653,6 +656,41 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
return true;
}
+ private DataStore createLocalStorage(Map<String, Object> poolInfos) throws ConnectionException{
+ Object existingUuid = poolInfos.get("uuid");
+ if( existingUuid == null ){
+ poolInfos.put("uuid", UUID.randomUUID().toString());
+ }
+ String hostAddress = poolInfos.get("host").toString();
+ Host host = _hostDao.findByName(hostAddress);
+
+ if( host == null ) {
+ host = _hostDao.findByIp(hostAddress);
+
+ if( host == null ) {
+ host = _hostDao.findByPublicIp(hostAddress);
+
+ if( host == null ) {
+ throw new InvalidParameterValueException(String.format("host %s not found",hostAddress));
+ }
+ }
+ }
+
+ long capacityBytes = poolInfos.get("capacityBytes") != null ? Long.parseLong(poolInfos.get("capacityBytes").toString()) : 0;
+
+ StoragePoolInfo pInfo = new StoragePoolInfo(poolInfos.get("uuid").toString(),
+ host.getPrivateIpAddress(),
+ poolInfos.get("hostPath").toString(),
+ poolInfos.get("hostPath").toString(),
+ StoragePoolType.Filesystem,
+ capacityBytes,
+ 0,
+ (Map<String,String>)poolInfos.get("details"),
+ poolInfos.get("name").toString());
+
+ return createLocalStorage(host, pInfo);
+ }
+
@DB
@Override
public DataStore createLocalStorage(Host host, StoragePoolInfo pInfo) throws ConnectionException {
@@ -698,17 +736,19 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
DataStoreLifeCycle lifeCycle = provider.getDataStoreLifeCycle();
if (pool == null) {
Map<String, Object> params = new HashMap<String, Object>();
- String name = createLocalStoragePoolName(host, pInfo);
+ String name = pInfo.getName() != null ? pInfo.getName() : createLocalStoragePoolName(host, pInfo);
params.put("zoneId", host.getDataCenterId());
params.put("clusterId", host.getClusterId());
params.put("podId", host.getPodId());
params.put("hypervisorType", host.getHypervisorType());
- params.put("url", pInfo.getPoolType().toString() + "://" + pInfo.getHost() + "/" + pInfo.getHostPath());
params.put("name", name);
params.put("localStorage", true);
params.put("details", pInfo.getDetails());
params.put("uuid", pInfo.getUuid());
params.put("providerName", provider.getName());
+ params.put("scheme", pInfo.getPoolType().toString());
+ params.put("host", pInfo.getHost());
+ params.put("hostPath", pInfo.getHostPath());
store = lifeCycle.initialize(params);
} else {
@@ -740,6 +780,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
@Override
public PrimaryDataStoreInfo createPool(CreateStoragePoolCmd cmd) throws ResourceInUseException, IllegalArgumentException, UnknownHostException, ResourceUnavailableException {
String providerName = cmd.getStorageProviderName();
+ Map<String,String> uriParams = extractUriParamsAsMap(cmd.getUrl());
DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(providerName);
if (storeProvider == null) {
@@ -753,7 +794,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
Long podId = cmd.getPodId();
Long zoneId = cmd.getZoneId();
- ScopeType scopeType = ScopeType.CLUSTER;
+ ScopeType scopeType = uriParams.get("scheme").toString().equals("file") ? ScopeType.HOST : ScopeType.CLUSTER;
String scope = cmd.getScope();
if (scope != null) {
try {
@@ -819,11 +860,16 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
params.put("managed", cmd.isManaged());
params.put("capacityBytes", cmd.getCapacityBytes());
params.put("capacityIops", cmd.getCapacityIops());
+ params.putAll(uriParams);
DataStoreLifeCycle lifeCycle = storeProvider.getDataStoreLifeCycle();
DataStore store = null;
try {
- store = lifeCycle.initialize(params);
+ if (params.get("scheme").toString().equals("file")) {
+ store = createLocalStorage(params);
+ } else {
+ store = lifeCycle.initialize(params);
+ }
if (scopeType == ScopeType.CLUSTER) {
ClusterScope clusterScope = new ClusterScope(clusterId, podId, zoneId);
lifeCycle.attachCluster(store, clusterScope);
@@ -848,6 +894,62 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
return (PrimaryDataStoreInfo)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Primary);
}
+ private Map<String,String> extractUriParamsAsMap(String url){
+ Map<String,String> uriParams = new HashMap<>();
+ UriUtils.UriInfo uriInfo = UriUtils.getUriInfo(url);
+
+ String scheme = uriInfo.getScheme();
+ String storageHost = uriInfo.getStorageHost();
+ String storagePath = uriInfo.getStoragePath();
+ try {
+ if (scheme == null) {
+ throw new InvalidParameterValueException("scheme is null " + url + ", add nfs:// (or cifs://) as a prefix");
+ } else if (scheme.equalsIgnoreCase("nfs")) {
+ if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) {
+ throw new InvalidParameterValueException("host or path is null, should be nfs://hostname/path");
+ }
+ } else if (scheme.equalsIgnoreCase("cifs")) {
+ // Don't validate against a URI encoded URI.
+ URI cifsUri = new URI(url);
+ String warnMsg = UriUtils.getCifsUriParametersProblems(cifsUri);
+ if (warnMsg != null) {
+ throw new InvalidParameterValueException(warnMsg);
+ }
+ } else if (scheme.equalsIgnoreCase("sharedMountPoint")) {
+ if (storagePath == null) {
+ throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path");
+ }
+ } else if (scheme.equalsIgnoreCase("rbd")) {
+ if (storagePath == null) {
+ throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool");
+ }
+ } else if (scheme.equalsIgnoreCase("gluster")) {
+ if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) {
+ throw new InvalidParameterValueException("host or path is null, should be gluster://hostname/volume");
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new InvalidParameterValueException(url + " is not a valid uri");
+ }
+
+ String hostPath = null;
+ try {
+ hostPath = URLDecoder.decode(storagePath, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ s_logger.error("[ignored] we are on a platform not supporting \"UTF-8\"!?!", e);
+ }
+ if (hostPath == null) { // if decoding fails, use getPath() anyway
+ hostPath = storagePath;
+ }
+
+ uriParams.put("scheme", scheme);
+ uriParams.put("host", storageHost);
+ uriParams.put("hostPath", hostPath);
+ uriParams.put("userInfo", uriInfo.getUserInfo());
+ uriParams.put("port", uriInfo.getPort() + "");
+ return uriParams;
+ }
+
private Map<String, String> extractApiParamAsMap(Map ds) {
Map<String, String> details = new HashMap<String, String>();
if (ds != null) {
diff --git a/ui/src/views/infra/AddPrimaryStorage.vue b/ui/src/views/infra/AddPrimaryStorage.vue
index b30b0341ca4..91c0dcbf42e 100644
--- a/ui/src/views/infra/AddPrimaryStorage.vue
+++ b/ui/src/views/infra/AddPrimaryStorage.vue
@@ -32,6 +32,7 @@
<a-select
v-model:value="form.scope"
v-focus="true"
+ @change="val => changeScope(val)"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
@@ -40,6 +41,7 @@
:placeholder="apiParams.scope.description" >
<a-select-option :value="'cluster'" :label="$t('label.clusterid')"> {{ $t('label.clusterid') }} </a-select-option>
<a-select-option :value="'zone'" :label="$t('label.zoneid')"> {{ $t('label.zoneid') }} </a-select-option>
+ <a-select-option :value="'host'" :label="$t('label.hostid')"> {{ $t('label.hostid') }} </a-select-option>
</a-select>
</a-form-item>
<div v-if="form.scope === 'zone'">
@@ -168,7 +170,7 @@
<a-input v-model:value="form.server" :placeholder="$t('message.server.description')" />
</a-form-item>
</div>
- <div v-if="form.protocol === 'nfs' || form.protocol === 'SMB' || form.protocol === 'ocfs2' || (form.protocol === 'PreSetup' && hypervisorType !== 'VMware') || form.protocol === 'SharedMountPoint'">
+ <div v-if="form.protocol === 'nfs' || form.protocol === 'SMB' || form.protocol === 'ocfs2' || form.protocol === 'Filesystem' || (form.protocol === 'PreSetup' && hypervisorType !== 'VMware') || form.protocol === 'SharedMountPoint'">
<a-form-item name="path" ref="path">
<template #label>
<tooltip-label :title="$t('label.path')" :tooltip="$t('message.path.description')"/>
@@ -438,6 +440,15 @@ export default {
this.loading = false
})
},
+ changeScope (value) {
+ if (value === 'host') {
+ const cluster = this.clusters.find(cluster => cluster.id === this.form.cluster)
+ this.hypervisorType = cluster.hypervisortype
+ if (this.hypervisorType === 'KVM') {
+ this.protocols.push('Filesystem')
+ }
+ }
+ },
changeZone (value) {
this.form.zone = value
if (this.form.zone === '') {
@@ -504,6 +515,9 @@ export default {
this.hypervisorType = cluster.hypervisortype
if (this.hypervisorType === 'KVM') {
this.protocols = ['nfs', 'SharedMountPoint', 'RBD', 'CLVM', 'Gluster', 'Linstor', 'custom']
+ if (this.form.scope === 'host') {
+ this.protocols.push('Filesystem')
+ }
} else if (this.hypervisorType === 'XenServer') {
this.protocols = ['nfs', 'PreSetup', 'iscsi', 'custom']
} else if (this.hypervisorType === 'VMware') {
@@ -524,6 +538,20 @@ export default {
this.form.protocol = this.protocols[0]
}
},
+ filesystemURL (hostId, path) {
+ var url
+ if (path.substring(0, 1) !== '/') {
+ path = '/' + path
+ }
+ var hostName
+ this.hosts.forEach(host => {
+ if (host.id === hostId) {
+ hostName = host.name
+ }
+ })
+ url = 'file://' + hostName + path
+ return url
+ },
nfsURL (server, path) {
var url
if (path.substring(0, 1) !== '/') {
@@ -756,6 +784,8 @@ export default {
if (values.capacityIops && values.capacityIops.length > 0) {
params.capacityIops = values.capacityIops.split(',').join('')
}
+ } else if (values.protocol === 'Filesystem') {
+ url = this.filesystemURL(values.host, path)
}
params.url = url
if (values.provider !== 'DefaultPrimary' && values.provider !== 'PowerFlex') {