Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4c7712c
Ability to specify NFS mount options while adding a primary storage a…
abh1sar Apr 19, 2024
6766115
Pull 8947: Rename all occurrence of nfsopt to nfsMountOpt and added n…
abh1sar Apr 23, 2024
d213df3
Pull 8947: Refactor code - move into separate methods
abh1sar Apr 23, 2024
8fd6b12
Pull 8947: CollectionsUtils.isNotEmpty and switch statement in Libvir…
abh1sar Apr 23, 2024
434496b
Pull 8947: UI - cancel maintainenace will remount the storage pool an…
abh1sar Apr 24, 2024
db4eb24
Pull 8947: UI - moved edit NFS mount options to edit Primary Storage …
abh1sar Apr 24, 2024
e7c7365
Pull 8947: UI - moved 'NFS Mount Options' to below 'Type' in dataview
abh1sar Apr 24, 2024
4405818
Merge branch '4.19' into nfsopts
abh1sar Apr 24, 2024
4ff2f0a
Pull 8947: Fixed message in AddPrimaryStorage.vue
abh1sar Apr 30, 2024
228b6d3
Pull 8947: Convert _nfsmountOpts to Set in libvirtStoragePoolDef
abh1sar Apr 30, 2024
249ffe9
Pull 8947: Throw exception and log error if mount fails due to incorr…
abh1sar Apr 30, 2024
c96de60
Merge remote-tracking branch 'upstream/4.19' into nfsopts
abh1sar Apr 30, 2024
c5de8ec
Merge branch 'nfsopts' of https://github.com/abh1sar/cloudstack into …
abh1sar Apr 30, 2024
830fb0f
Pull 8947: Added UT and moved integration test to component/maint
abh1sar May 2, 2024
0850bbe
Pull 8947: Review comments
abh1sar May 2, 2024
d55a4f1
Pull 8947: Removed password from integration test
abh1sar May 2, 2024
9eaabc7
Pull 8947: move details allocation to inside the if loop in getStorag…
abh1sar May 3, 2024
e73d6dc
Pull 8947: Fixed a bug in AddPrimaryStorage.vue
abh1sar May 3, 2024
96938e0
Pull 8947: Pool should remain in maintenance mode if mount fails
abh1sar May 3, 2024
662aec6
Pull 8947: Removed password from integration test
abh1sar May 3, 2024
b1e2e4e
Merge remote-tracking branch 'upstream/4.19' into nfsopts
abh1sar May 8, 2024
abf1314
Merge branch '4.19' into nfsopts
abh1sar May 8, 2024
e826c94
Pull 8947: Added UT
abh1sar May 9, 2024
1b35b34
Merge branch 'nfsopts' of https://github.com/abh1sar/cloudstack into …
abh1sar May 9, 2024
68873c5
Pull 8875: Fixed a bug in CloudStackPrimaryDataStoreLifeCycleImplTest
abh1sar May 9, 2024
d4c8bfd
Pull 8875: Fixed a bug in LibvirtStoragePoolDefTest
abh1sar May 9, 2024
4689647
Merge remote-tracking branch 'upstream/4.19' into nfsopts
abh1sar May 23, 2024
75eeae3
Merge remote-tracking branch 'upstream/4.19' into nfsopts
abh1sar Jun 17, 2024
427e2e7
Merge branch '4.19' into nfsopts
abh1sar Jun 18, 2024
88fa638
Pull 8947: minor code restructuring
abh1sar Jun 20, 2024
f5e5e61
Merge remote-tracking branch 'upstream/4.19' into nfs
abh1sar Jun 20, 2024
00b9c58
Pull 8947 : added some ut for coverage
abh1sar Jun 20, 2024
eb36d5c
Merge remote-tracking branch 'upstream/4.19' into nfs
abh1sar Jun 24, 2024
f683fc9
Fix LibvirtStorageAdapterTest UT
abh1sar Jun 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,8 @@ public class ApiConstants {

public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not.";

public static final String NFS_MOUNT_OPTIONS = "nfsmountopts";

/**
* This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations {
@Param(description = "the tags for the storage pool")
private String tags;

@SerializedName(ApiConstants.NFS_MOUNT_OPTIONS)
@Param(description = "the nfs mount options for the storage pool", since = "4.19.1")
private String nfsMountOpts;

@SerializedName(ApiConstants.IS_TAG_A_RULE)
@Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE)
private Boolean isTagARule;
Expand Down Expand Up @@ -347,4 +351,12 @@ public String getProvider() {
public void setProvider(String provider) {
this.provider = provider;
}

public String getNfsMountOpts() {
return nfsMountOpts;
}

public void setNfsMountOpts(String nfsMountOpts) {
this.nfsMountOpts = nfsMountOpts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public ModifyStoragePoolCommand(boolean add, StoragePool pool, String localPath,
this.details = details;
}

public ModifyStoragePoolCommand(boolean add, StoragePool pool, Map<String, String> details) {
this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()), details);
}

public ModifyStoragePoolCommand(boolean add, StoragePool pool) {
this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
Expand Down Expand Up @@ -341,6 +342,10 @@ static Boolean getFullCloneConfiguration(Long storeId) {

boolean registerHostListener(String providerUuid, HypervisorHostListener listener);

Pair<Map<String, String>, Boolean> getStoragePoolNFSMountOpts(StoragePool pool, Map<String, String> details);

String getStoragePoolMountFailureReason(String error);

boolean connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException;

void disconnectHostFromSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.StorageService;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;

import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
Expand All @@ -53,7 +55,9 @@
import org.apache.log4j.Logger;

import javax.inject.Inject;

import java.util.List;
import java.util.Map;

public class DefaultHostListener implements HypervisorHostListener {
private static final Logger s_logger = Logger.getLogger(DefaultHostListener.class);
Expand Down Expand Up @@ -125,7 +129,9 @@ private NicTO createNicTOFromNetworkAndOffering(NetworkVO networkVO, NetworkOffe
@Override
public boolean hostConnect(long hostId, long poolId) throws StorageConflictException {
StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool);
Pair<Map<String, String>, Boolean> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null);

ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, nfsMountOpts.first());
cmd.setWait(modifyStoragePoolCommandWait);
s_logger.debug(String.format("Sending modify storage pool command to agent: %d for storage pool: %d with timeout %d seconds",
hostId, poolId, cmd.getWait()));
Expand All @@ -138,7 +144,7 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep
if (!answer.getResult()) {
String msg = "Unable to attach storage pool" + poolId + " to the host" + hostId;
alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg);
throw new CloudRuntimeException("Unable establish connection from storage head to storage pool " + pool.getId() + " due to " + answer.getDetails() +
throw new CloudRuntimeException("Unable to establish connection from storage head to storage pool " + pool.getId() + " due to " + answer.getDetails() +
pool.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
// under the License.
package com.cloud.hypervisor.kvm.resource;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;

public class LibvirtStoragePoolDef {
public enum PoolType {
ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir"), RBD("rbd"), GLUSTERFS("glusterfs"), POWERFLEX("powerflex");
Expand Down Expand Up @@ -55,6 +61,7 @@ public String toString() {
private String _authUsername;
private AuthenticationType _authType;
private String _secretUuid;
private Set<String> _nfsMountOpts = new HashSet<>();

public LibvirtStoragePoolDef(PoolType type, String poolName, String uuid, String host, int port, String dir, String targetPath) {
_poolType = type;
Expand All @@ -75,6 +82,15 @@ public LibvirtStoragePoolDef(PoolType type, String poolName, String uuid, String
_targetPath = targetPath;
}

public LibvirtStoragePoolDef(PoolType type, String poolName, String uuid, String host, String dir, String targetPath, List<String> nfsMountOpts) {
this(type, poolName, uuid, host, dir, targetPath);
if (CollectionUtils.isNotEmpty(nfsMountOpts)) {
for (String nfsMountOpt : nfsMountOpts) {
this._nfsMountOpts.add(nfsMountOpt);
}
}
}

public LibvirtStoragePoolDef(PoolType type, String poolName, String uuid, String sourceHost, int sourcePort, String dir, String authUsername, AuthenticationType authType,
String secretUuid) {
_poolType = type;
Expand Down Expand Up @@ -124,69 +140,98 @@ public AuthenticationType getAuthType() {
return _authType;
}

public Set<String> getNfsMountOpts() {
return _nfsMountOpts;
}

@Override
public String toString() {
StringBuilder storagePoolBuilder = new StringBuilder();
if (_poolType == PoolType.GLUSTERFS) {
/* libvirt mounts a Gluster volume, similar to NFS */
storagePoolBuilder.append("<pool type='netfs'>\n");
} else {
storagePoolBuilder.append("<pool type='");
storagePoolBuilder.append(_poolType);
storagePoolBuilder.append("'>\n");
String poolTypeXML;
switch (_poolType) {
case NETFS:
if (_nfsMountOpts != null) {
poolTypeXML = "netfs' xmlns:fs='http://libvirt.org/schemas/storagepool/fs/1.0";
} else {
poolTypeXML = _poolType.toString();
}
break;
case GLUSTERFS:
/* libvirt mounts a Gluster volume, similar to NFS */
poolTypeXML = "netfs";
break;
default:
poolTypeXML = _poolType.toString();
}

storagePoolBuilder.append("<pool type='");
storagePoolBuilder.append(poolTypeXML);
storagePoolBuilder.append("'>\n");

storagePoolBuilder.append("<name>" + _poolName + "</name>\n");
if (_uuid != null)
storagePoolBuilder.append("<uuid>" + _uuid + "</uuid>\n");
if (_poolType == PoolType.NETFS) {
storagePoolBuilder.append("<source>\n");
storagePoolBuilder.append("<host name='" + _sourceHost + "'/>\n");
storagePoolBuilder.append("<dir path='" + _sourceDir + "'/>\n");
storagePoolBuilder.append("</source>\n");
}
if (_poolType == PoolType.RBD) {
storagePoolBuilder.append("<source>\n");
for (String sourceHost : _sourceHost.split(",")) {

switch (_poolType) {
case NETFS:
storagePoolBuilder.append("<source>\n");
storagePoolBuilder.append("<host name='" + _sourceHost + "'/>\n");
storagePoolBuilder.append("<dir path='" + _sourceDir + "'/>\n");
storagePoolBuilder.append("</source>\n");
break;

case RBD:
storagePoolBuilder.append("<source>\n");
for (String sourceHost : _sourceHost.split(",")) {
storagePoolBuilder.append("<host name='");
storagePoolBuilder.append(sourceHost);
if (_sourcePort != 0) {
storagePoolBuilder.append("' port='");
storagePoolBuilder.append(_sourcePort);
}
storagePoolBuilder.append("'/>\n");
}

storagePoolBuilder.append("<name>" + _sourceDir + "</name>\n");
if (_authUsername != null) {
storagePoolBuilder.append("<auth username='" + _authUsername + "' type='" + _authType + "'>\n");
storagePoolBuilder.append("<secret uuid='" + _secretUuid + "'/>\n");
storagePoolBuilder.append("</auth>\n");
}
storagePoolBuilder.append("</source>\n");
break;

case GLUSTERFS:
storagePoolBuilder.append("<source>\n");
storagePoolBuilder.append("<host name='");
storagePoolBuilder.append(sourceHost);
storagePoolBuilder.append(_sourceHost);
if (_sourcePort != 0) {
storagePoolBuilder.append("' port='");
storagePoolBuilder.append(_sourcePort);
}
storagePoolBuilder.append("'/>\n");
}

storagePoolBuilder.append("<name>" + _sourceDir + "</name>\n");
if (_authUsername != null) {
storagePoolBuilder.append("<auth username='" + _authUsername + "' type='" + _authType + "'>\n");
storagePoolBuilder.append("<secret uuid='" + _secretUuid + "'/>\n");
storagePoolBuilder.append("</auth>\n");
}
storagePoolBuilder.append("</source>\n");
}
if (_poolType == PoolType.GLUSTERFS) {
storagePoolBuilder.append("<source>\n");
storagePoolBuilder.append("<host name='");
storagePoolBuilder.append(_sourceHost);
if (_sourcePort != 0) {
storagePoolBuilder.append("' port='");
storagePoolBuilder.append(_sourcePort);
}
storagePoolBuilder.append("'/>\n");
storagePoolBuilder.append("<dir path='");
storagePoolBuilder.append(_sourceDir);
storagePoolBuilder.append("'/>\n");
storagePoolBuilder.append("<format type='");
storagePoolBuilder.append(_poolType);
storagePoolBuilder.append("'/>\n");
storagePoolBuilder.append("</source>\n");
storagePoolBuilder.append("<dir path='");
storagePoolBuilder.append(_sourceDir);
storagePoolBuilder.append("'/>\n");
storagePoolBuilder.append("<format type='");
storagePoolBuilder.append(_poolType);
storagePoolBuilder.append("'/>\n");
storagePoolBuilder.append("</source>\n");
break;
}

if (_poolType != PoolType.RBD && _poolType != PoolType.POWERFLEX) {
storagePoolBuilder.append("<target>\n");
storagePoolBuilder.append("<path>" + _targetPath + "</path>\n");
storagePoolBuilder.append("</target>\n");
}
if (_poolType == PoolType.NETFS && _nfsMountOpts != null) {
storagePoolBuilder.append("<fs:mount_opts>\n");
for (String options : _nfsMountOpts) {
storagePoolBuilder.append("<fs:option name='" + options + "'/>\n");
}
storagePoolBuilder.append("</fs:mount_opts>\n");
}
storagePoolBuilder.append("</pool>\n");
return storagePoolBuilder.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@
public class LibvirtStoragePoolXMLParser {
private static final Logger s_logger = Logger.getLogger(LibvirtStoragePoolXMLParser.class);

private List<String> getNFSMountOptsFromRootElement(Element rootElement) {
List<String> nfsMountOpts = new ArrayList<>();
Element mountOpts = (Element) rootElement.getElementsByTagName("fs:mount_opts").item(0);
if (mountOpts != null) {
NodeList options = mountOpts.getElementsByTagName("fs:option");
for (int i = 0; i < options.getLength(); i++) {
Element option = (Element) options.item(i);
nfsMountOpts.add(option.getAttribute("name"));
}
}
return nfsMountOpts;
}

public LibvirtStoragePoolDef parseStoragePoolXML(String poolXML) {
DocumentBuilder builder;
try {
Expand Down Expand Up @@ -94,11 +107,15 @@ public LibvirtStoragePoolDef parseStoragePoolXML(String poolXML) {
poolName, uuid, host, port, path, targetPath);
} else {
String path = getAttrValue("dir", "path", source);

Element target = (Element)rootElement.getElementsByTagName("target").item(0);
String targetPath = getTagValue("path", target);

return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath);
if (type.equalsIgnoreCase("netfs")) {
List<String> nfsMountOpts = getNFSMountOptsFromRootElement(rootElement);
return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath, nfsMountOpts);
} else {
return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath);
}
}
} catch (ParserConfigurationException e) {
s_logger.debug(e.toString());
Expand Down
Loading